VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/globals/UICommon.cpp@ 103131

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

FE/Qt: macOS: A bit of fixes for AlternateBase color required since we are using hardcoded native cocoa colors now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 113.0 KB
Line 
1/* $Id: UICommon.cpp 102877 2024-01-15 14:42:35Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UICommon class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QDesktopServices>
30#include <QDir>
31#include <QFileDialog>
32#include <QGraphicsWidget>
33#include <QLibraryInfo>
34#include <QLocale>
35#include <QMenu>
36#include <QMutex>
37#include <QProcess>
38#include <QProgressDialog>
39#include <QRegularExpression>
40#include <QSessionManager>
41#include <QSettings>
42#include <QSpinBox>
43#include <QStandardPaths>
44#include <QStyleOptionSpinBox>
45#include <QThread>
46#include <QTimer>
47#include <QToolButton>
48#include <QToolTip>
49#include <QTranslator>
50#ifdef VBOX_WS_WIN
51# include <QStyleFactory>
52#endif
53#ifdef VBOX_GUI_WITH_PIDFILE
54# include <QTextStream>
55#endif
56
57/* GUI includes: */
58#include "QIDialogButtonBox.h"
59#include "QIFileDialog.h"
60#include "QIMessageBox.h"
61#include "QIWithRestorableGeometry.h"
62#include "UICommon.h"
63#include "UIConverter.h"
64#include "UIDesktopWidgetWatchdog.h"
65#include "UIGuestOSType.h"
66#include "UIExtraDataDefs.h"
67#include "UIExtraDataManager.h"
68#include "UIFDCreationDialog.h"
69#include "UIIconPool.h"
70#include "UIMedium.h"
71#include "UIMediumEnumerator.h"
72#include "UIMediumSelector.h"
73#include "UIMessageCenter.h"
74#include "UIModalWindowManager.h"
75#include "UINotificationCenter.h"
76#include "UIPopupCenter.h"
77#include "UIShortcutPool.h"
78#include "UIThreadPool.h"
79#include "UITranslator.h"
80#include "UIVirtualBoxClientEventHandler.h"
81#include "UIVirtualBoxEventHandler.h"
82#include "UIVisoCreator.h"
83#include "UIWizardNewVD.h"
84#ifdef VBOX_WS_MAC
85# include "UICocoaApplication.h"
86# include "UIMachineWindowFullscreen.h"
87# include "UIMachineWindowSeamless.h"
88#endif
89#ifdef VBOX_WS_WIN
90# include "VBoxUtils-win.h"
91#endif
92#ifdef VBOX_WS_NIX
93# include "UIHostComboEditor.h"
94#endif
95#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
96# include "UINetworkRequestManager.h"
97# include "UIUpdateManager.h"
98#endif
99
100/* COM includes: */
101#include "CAudioAdapter.h"
102#include "CCloudMachine.h"
103#include "CConsole.h"
104#include "CExtPack.h"
105#include "CExtPackFile.h"
106#include "CExtPackManager.h"
107#include "CHostUSBDevice.h"
108#include "CHostVideoInputDevice.h"
109#include "CMachine.h"
110#include "CMediumAttachment.h"
111#include "CNetworkAdapter.h"
112#include "CSerialPort.h"
113#include "CSharedFolder.h"
114#include "CSnapshot.h"
115#include "CStorageController.h"
116#include "CSystemProperties.h"
117#include "CUSBController.h"
118#include "CUSBDevice.h"
119#include "CUSBDeviceFilter.h"
120#include "CUSBDeviceFilters.h"
121#include "CVRDEServer.h"
122#include <VBox/com/VirtualBox.h> /* For GUEST_OS_ID_STR_PARTIAL. */
123
124/* Other VBox includes: */
125#include <iprt/asm.h>
126#include <iprt/ctype.h>
127#include <iprt/env.h>
128#include <iprt/err.h>
129#include <iprt/file.h>
130#include <iprt/ldr.h>
131#include <iprt/param.h>
132#include <iprt/path.h>
133#include <iprt/system.h>
134#include <VBox/sup.h>
135#include <VBox/VBoxOGL.h>
136#include <VBox/vd.h>
137#include <VBox/com/Guid.h>
138
139/* VirtualBox interface declarations: */
140#include <VBox/com/VirtualBox.h>
141
142/* External includes: */
143#ifdef VBOX_WS_MAC
144# include <sys/utsname.h>
145#endif
146#ifdef VBOX_WS_NIX
147# include <xcb/xcb.h>
148#endif
149
150/* Namespaces: */
151using namespace UIExtraDataDefs;
152using namespace UIMediumDefs;
153
154
155/* static */
156UICommon *UICommon::s_pInstance = 0;
157
158/* static */
159void UICommon::create(UIType enmType)
160{
161 /* Make sure instance is NOT created yet: */
162 AssertReturnVoid(!s_pInstance);
163
164 /* Create instance: */
165 new UICommon(enmType);
166 /* Prepare instance: */
167 s_pInstance->prepare();
168}
169
170/* static */
171void UICommon::destroy()
172{
173 /* Make sure instance is NOT destroyed yet: */
174 AssertPtrReturnVoid(s_pInstance);
175
176 /* Cleanup instance:
177 * 1. By default, automatically on QApplication::aboutToQuit() signal.
178 * 2. But if QApplication was not started at all and we perform
179 * early shutdown, we should do cleanup ourselves. */
180 if (s_pInstance->isValid())
181 s_pInstance->cleanup();
182 /* Destroy instance: */
183 delete s_pInstance;
184}
185
186UICommon::UICommon(UIType enmType)
187 : m_enmType(enmType)
188 , m_fValid(false)
189 , m_fCleaningUp(false)
190#ifdef VBOX_WS_WIN
191 , m_fDataCommitted(false)
192#endif
193#ifdef VBOX_WS_MAC
194 , m_enmMacOSVersion(MacOSXRelease_Old)
195#endif
196#ifdef VBOX_WS_NIX
197 , m_enmWindowManagerType(X11WMType_Unknown)
198 , m_fCompositingManagerRunning(false)
199 , m_enmDisplayServerType(VBGHDISPLAYSERVERTYPE_NONE)
200#endif
201 , m_fDarkMode(false)
202 , m_fSeparateProcess(false)
203 , m_fShowStartVMErrors(true)
204#if defined(DEBUG_bird)
205 , m_fAgressiveCaching(false)
206#else
207 , m_fAgressiveCaching(true)
208#endif
209 , m_fRestoreCurrentSnapshot(false)
210 , m_fNoKeyboardGrabbing(false)
211 , m_fExecuteAllInIem(false)
212 , m_uWarpPct(100)
213#ifdef VBOX_WITH_DEBUGGER_GUI
214 , m_fDbgEnabled(0)
215 , m_fDbgAutoShow(0)
216 , m_fDbgAutoShowCommandLine(0)
217 , m_fDbgAutoShowStatistics(0)
218 , m_hVBoxDbg(NIL_RTLDRMOD)
219 , m_enmLaunchRunning(LaunchRunning_Default)
220#endif
221 , m_fSettingsPwSet(false)
222 , m_fWrappersValid(false)
223 , m_fVBoxSVCAvailable(true)
224 , m_pThreadPool(0)
225 , m_pThreadPoolCloud(0)
226 , m_pGuestOSTypeManager(0)
227 , m_pMediumEnumerator(0)
228{
229 /* Assign instance: */
230 s_pInstance = this;
231}
232
233UICommon::~UICommon()
234{
235 /* Unassign instance: */
236 s_pInstance = 0;
237}
238
239void UICommon::prepare()
240{
241 /* Make sure QApplication cleanup us on exit: */
242#ifndef VBOX_IS_QT6_OR_LATER /** @todo qt6: ... */
243 qApp->setFallbackSessionManagementEnabled(false);
244#endif
245 connect(qApp, &QGuiApplication::aboutToQuit,
246 this, &UICommon::sltCleanup);
247#ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
248 /* Make sure we handle host OS session shutdown as well: */
249 connect(qApp, &QGuiApplication::commitDataRequest,
250 this, &UICommon::sltHandleCommitDataRequest);
251#endif /* VBOX_GUI_WITH_CUSTOMIZATIONS1 */
252
253#ifdef VBOX_WS_MAC
254 /* Determine OS release early: */
255 m_enmMacOSVersion = determineOsRelease();
256#endif
257
258#ifdef VBOX_WS_NIX
259 /* Detect display server type: */
260 m_enmDisplayServerType = VBGHDisplayServerTypeDetect();
261#endif
262
263 /* Create converter: */
264 UIConverter::create();
265
266 /* Create desktop-widget watchdog: */
267 UIDesktopWidgetWatchdog::create();
268
269 /* Create message-center: */
270 UIMessageCenter::create();
271 /* Create popup-center: */
272 UIPopupCenter::create();
273
274 /* Prepare general icon-pool: */
275 UIIconPoolGeneral::create();
276
277 /* Load translation based on the current locale: */
278 UITranslator::loadLanguage();
279
280 /* Prepare guest OS type manager before COM stuff: */
281 m_pGuestOSTypeManager = new UIGuestOSTypeManager;
282
283 HRESULT rc = COMBase::InitializeCOM(true);
284 if (FAILED(rc))
285 {
286#ifdef VBOX_WITH_XPCOM
287 if (rc == NS_ERROR_FILE_ACCESS_DENIED)
288 {
289 char szHome[RTPATH_MAX] = "";
290 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
291 msgCenter().cannotInitUserHome(QString(szHome));
292 }
293 else
294#endif
295 msgCenter().cannotInitCOM(rc);
296 return;
297 }
298
299 /* Make sure VirtualBoxClient instance created: */
300 m_comVBoxClient.createInstance(CLSID_VirtualBoxClient);
301 if (!m_comVBoxClient.isOk())
302 {
303 msgCenter().cannotCreateVirtualBoxClient(m_comVBoxClient);
304 return;
305 }
306 /* Make sure VirtualBox instance acquired: */
307 m_comVBox = m_comVBoxClient.GetVirtualBox();
308 if (!m_comVBoxClient.isOk())
309 {
310 msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
311 return;
312 }
313 /* Init wrappers: */
314 comWrappersReinit();
315
316 /* Watch for the VBoxSVC availability changes: */
317 connect(gVBoxClientEvents, &UIVirtualBoxClientEventHandler::sigVBoxSVCAvailabilityChange,
318 this, &UICommon::sltHandleVBoxSVCAvailabilityChange);
319
320 /* Prepare thread-pool instances: */
321 m_pThreadPool = new UIThreadPool(3 /* worker count */, 5000 /* worker timeout */);
322 m_pThreadPoolCloud = new UIThreadPool(2 /* worker count */, 1000 /* worker timeout */);
323
324 /* Load whether host OS is in Dark mode: */
325#if defined(VBOX_WS_MAC)
326 m_fDarkMode = UICocoaApplication::instance()->isDarkMode();
327#elif defined(VBOX_WS_WIN)
328 m_fDarkMode = isWindowsInDarkMode();
329#else /* Linux, BSD, Solaris */
330 m_fDarkMode = isPaletteInDarkMode();
331#endif /* Linux, BSD, Solaris */
332 /* Load color theme: */
333 loadColorTheme();
334
335 /* Load translation based on the user settings: */
336 QString strLanguageId = gEDataManager->languageId();
337 if (!strLanguageId.isNull())
338 UITranslator::loadLanguage(strLanguageId);
339
340 retranslateUi();
341
342 connect(gEDataManager, &UIExtraDataManager::sigLanguageChange,
343 this, &UICommon::sltGUILanguageChange);
344 connect(gEDataManager, &UIExtraDataManager::sigFontScaleFactorChanged,
345 this, &UICommon::sltHandleFontScaleFactorChanged);
346
347 qApp->installEventFilter(this);
348
349 /* process command line */
350
351 UIVisualStateType visualStateType = UIVisualStateType_Invalid;
352
353#ifdef VBOX_WS_NIX
354 /* Check whether we have compositing manager running: */
355 m_fCompositingManagerRunning = NativeWindowSubsystem::isCompositingManagerRunning(X11ServerAvailable());
356
357 /* Acquire current Window Manager type: */
358 m_enmWindowManagerType = NativeWindowSubsystem::windowManagerType(X11ServerAvailable());
359#endif /* VBOX_WS_NIX */
360
361#ifdef VBOX_WITH_DEBUGGER_GUI
362# ifdef VBOX_WITH_DEBUGGER_GUI_MENU
363 initDebuggerVar(&m_fDbgEnabled, "VBOX_GUI_DBG_ENABLED", GUI_Dbg_Enabled, true);
364# else
365 initDebuggerVar(&m_fDbgEnabled, "VBOX_GUI_DBG_ENABLED", GUI_Dbg_Enabled, false);
366# endif
367 initDebuggerVar(&m_fDbgAutoShow, "VBOX_GUI_DBG_AUTO_SHOW", GUI_Dbg_AutoShow, false);
368 m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = m_fDbgAutoShow;
369#endif
370
371 /*
372 * Parse the command line options.
373 *
374 * This is a little sloppy but we're trying to tighten it up. Unfortuately,
375 * both on X11 and darwin (IIRC) there might be additional arguments aimed
376 * for client libraries with GUI processes. So, using RTGetOpt or similar
377 * is a bit hard since we have to cope with unknown options.
378 */
379 m_fShowStartVMErrors = true;
380 bool startVM = false;
381 bool fSeparateProcess = false;
382 QString vmNameOrUuid;
383
384 const QStringList &arguments = QCoreApplication::arguments();
385 const int argc = arguments.size();
386 int i = 1;
387 while (i < argc)
388 {
389 const QByteArray &argBytes = arguments.at(i).toUtf8();
390 const char *arg = argBytes.constData();
391 enum { OptType_Unknown, OptType_VMRunner, OptType_VMSelector, OptType_MaybeBoth } enmOptType = OptType_Unknown;
392 /* NOTE: the check here must match the corresponding check for the
393 * options to start a VM in main.cpp and hardenedmain.cpp exactly,
394 * otherwise there will be weird error messages. */
395 if ( !::strcmp(arg, "--startvm")
396 || !::strcmp(arg, "-startvm"))
397 {
398 enmOptType = OptType_VMRunner;
399 if (++i < argc)
400 {
401 vmNameOrUuid = arguments.at(i);
402 startVM = true;
403 }
404 }
405 else if (!::strcmp(arg, "-separate") || !::strcmp(arg, "--separate"))
406 {
407 enmOptType = OptType_VMRunner;
408 fSeparateProcess = true;
409 }
410#ifdef VBOX_GUI_WITH_PIDFILE
411 else if (!::strcmp(arg, "-pidfile") || !::strcmp(arg, "--pidfile"))
412 {
413 enmOptType = OptType_MaybeBoth;
414 if (++i < argc)
415 m_strPidFile = arguments.at(i);
416 }
417#endif /* VBOX_GUI_WITH_PIDFILE */
418 /* Visual state type options: */
419 else if (!::strcmp(arg, "-normal") || !::strcmp(arg, "--normal"))
420 {
421 enmOptType = OptType_MaybeBoth;
422 visualStateType = UIVisualStateType_Normal;
423 }
424 else if (!::strcmp(arg, "-fullscreen") || !::strcmp(arg, "--fullscreen"))
425 {
426 enmOptType = OptType_MaybeBoth;
427 visualStateType = UIVisualStateType_Fullscreen;
428 }
429 else if (!::strcmp(arg, "-seamless") || !::strcmp(arg, "--seamless"))
430 {
431 enmOptType = OptType_MaybeBoth;
432 visualStateType = UIVisualStateType_Seamless;
433 }
434 else if (!::strcmp(arg, "-scale") || !::strcmp(arg, "--scale"))
435 {
436 enmOptType = OptType_MaybeBoth;
437 visualStateType = UIVisualStateType_Scale;
438 }
439 /* Passwords: */
440 else if (!::strcmp(arg, "--settingspw"))
441 {
442 enmOptType = OptType_MaybeBoth;
443 if (++i < argc)
444 {
445 RTStrCopy(m_astrSettingsPw, sizeof(m_astrSettingsPw), arguments.at(i).toLocal8Bit().constData());
446 m_fSettingsPwSet = true;
447 }
448 }
449 else if (!::strcmp(arg, "--settingspwfile"))
450 {
451 enmOptType = OptType_MaybeBoth;
452 if (++i < argc)
453 {
454 const QByteArray &argFileBytes = arguments.at(i).toLocal8Bit();
455 const char *pszFile = argFileBytes.constData();
456 bool fStdIn = !::strcmp(pszFile, "stdin");
457 int vrc = VINF_SUCCESS;
458 PRTSTREAM pStrm;
459 if (!fStdIn)
460 vrc = RTStrmOpen(pszFile, "r", &pStrm);
461 else
462 pStrm = g_pStdIn;
463 if (RT_SUCCESS(vrc))
464 {
465 size_t cbFile;
466 vrc = RTStrmReadEx(pStrm, m_astrSettingsPw, sizeof(m_astrSettingsPw) - 1, &cbFile);
467 if (RT_SUCCESS(vrc))
468 {
469 if (cbFile >= sizeof(m_astrSettingsPw) - 1)
470 cbFile = sizeof(m_astrSettingsPw) - 1;
471 unsigned i;
472 for (i = 0; i < cbFile && !RT_C_IS_CNTRL(m_astrSettingsPw[i]); i++)
473 ;
474 m_astrSettingsPw[i] = '\0';
475 m_fSettingsPwSet = true;
476 }
477 if (!fStdIn)
478 RTStrmClose(pStrm);
479 }
480 }
481 }
482 /* Misc options: */
483 else if (!::strcmp(arg, "-comment") || !::strcmp(arg, "--comment"))
484 {
485 enmOptType = OptType_MaybeBoth;
486 ++i;
487 }
488 else if (!::strcmp(arg, "--no-startvm-errormsgbox"))
489 {
490 enmOptType = OptType_VMRunner;
491 m_fShowStartVMErrors = false;
492 }
493 else if (!::strcmp(arg, "--aggressive-caching"))
494 {
495 enmOptType = OptType_MaybeBoth;
496 m_fAgressiveCaching = true;
497 }
498 else if (!::strcmp(arg, "--no-aggressive-caching"))
499 {
500 enmOptType = OptType_MaybeBoth;
501 m_fAgressiveCaching = false;
502 }
503 else if (!::strcmp(arg, "--restore-current"))
504 {
505 enmOptType = OptType_VMRunner;
506 m_fRestoreCurrentSnapshot = true;
507 }
508 else if (!::strcmp(arg, "--no-keyboard-grabbing"))
509 {
510 enmOptType = OptType_VMRunner;
511 m_fNoKeyboardGrabbing = true;
512 }
513 /* Ad hoc VM reconfig options: */
514 else if (!::strcmp(arg, "--fda"))
515 {
516 enmOptType = OptType_VMRunner;
517 if (++i < argc)
518 m_uFloppyImage = QUuid(arguments.at(i));
519 }
520 else if (!::strcmp(arg, "--dvd") || !::strcmp(arg, "--cdrom"))
521 {
522 enmOptType = OptType_VMRunner;
523 if (++i < argc)
524 m_uDvdImage = QUuid(arguments.at(i));
525 }
526 /* VMM Options: */
527 else if (!::strcmp(arg, "--execute-all-in-iem"))
528 {
529 enmOptType = OptType_VMRunner;
530 m_fExecuteAllInIem = true;
531 }
532 else if (!::strcmp(arg, "--driverless"))
533 enmOptType = OptType_VMRunner;
534 else if (!::strcmp(arg, "--warp-pct"))
535 {
536 enmOptType = OptType_VMRunner;
537 if (++i < argc)
538 m_uWarpPct = RTStrToUInt32(arguments.at(i).toLocal8Bit().constData());
539 }
540#ifdef VBOX_WITH_DEBUGGER_GUI
541 /* Debugger/Debugging options: */
542 else if (!::strcmp(arg, "-dbg") || !::strcmp(arg, "--dbg"))
543 {
544 enmOptType = OptType_VMRunner;
545 setDebuggerVar(&m_fDbgEnabled, true);
546 }
547 else if (!::strcmp( arg, "-debug") || !::strcmp(arg, "--debug"))
548 {
549 enmOptType = OptType_VMRunner;
550 setDebuggerVar(&m_fDbgEnabled, true);
551 setDebuggerVar(&m_fDbgAutoShow, true);
552 setDebuggerVar(&m_fDbgAutoShowCommandLine, true);
553 setDebuggerVar(&m_fDbgAutoShowStatistics, true);
554 }
555 else if (!::strcmp(arg, "--debug-command-line"))
556 {
557 enmOptType = OptType_VMRunner;
558 setDebuggerVar(&m_fDbgEnabled, true);
559 setDebuggerVar(&m_fDbgAutoShow, true);
560 setDebuggerVar(&m_fDbgAutoShowCommandLine, true);
561 }
562 else if (!::strcmp(arg, "--debug-statistics"))
563 {
564 enmOptType = OptType_VMRunner;
565 setDebuggerVar(&m_fDbgEnabled, true);
566 setDebuggerVar(&m_fDbgAutoShow, true);
567 setDebuggerVar(&m_fDbgAutoShowStatistics, true);
568 }
569 else if (!::strcmp(arg, "--statistics-expand") || !::strcmp(arg, "--stats-expand"))
570 {
571 enmOptType = OptType_VMRunner;
572 if (++i < argc)
573 {
574 if (!m_strDbgStatisticsExpand.isEmpty())
575 m_strDbgStatisticsExpand.append('|');
576 m_strDbgStatisticsExpand.append(arguments.at(i));
577 }
578 }
579 else if (!::strncmp(arg, RT_STR_TUPLE("--statistics-expand=")) || !::strncmp(arg, RT_STR_TUPLE("--stats-expand=")))
580 {
581 enmOptType = OptType_VMRunner;
582 if (!m_strDbgStatisticsExpand.isEmpty())
583 m_strDbgStatisticsExpand.append('|');
584 m_strDbgStatisticsExpand.append(arguments.at(i).section('=', 1));
585 }
586 else if (!::strcmp(arg, "--statistics-filter") || !::strcmp(arg, "--stats-filter"))
587 {
588 enmOptType = OptType_VMRunner;
589 if (++i < argc)
590 m_strDbgStatisticsFilter = arguments.at(i);
591 }
592 else if (!::strncmp(arg, RT_STR_TUPLE("--statistics-filter=")) || !::strncmp(arg, RT_STR_TUPLE("--stats-filter=")))
593 {
594 enmOptType = OptType_VMRunner;
595 m_strDbgStatisticsFilter = arguments.at(i).section('=', 1);
596 }
597 else if (!::strcmp(arg, "-no-debug") || !::strcmp(arg, "--no-debug"))
598 {
599 enmOptType = OptType_VMRunner;
600 setDebuggerVar(&m_fDbgEnabled, false);
601 setDebuggerVar(&m_fDbgAutoShow, false);
602 setDebuggerVar(&m_fDbgAutoShowCommandLine, false);
603 setDebuggerVar(&m_fDbgAutoShowStatistics, false);
604 }
605 /* Not quite debug options, but they're only useful with the debugger bits. */
606 else if (!::strcmp(arg, "--start-paused"))
607 {
608 enmOptType = OptType_VMRunner;
609 m_enmLaunchRunning = LaunchRunning_No;
610 }
611 else if (!::strcmp(arg, "--start-running"))
612 {
613 enmOptType = OptType_VMRunner;
614 m_enmLaunchRunning = LaunchRunning_Yes;
615 }
616#endif
617 if (enmOptType == OptType_VMRunner && m_enmType != UIType_RuntimeUI)
618 msgCenter().cannotHandleRuntimeOption(arg);
619
620 i++;
621 }
622
623 if (m_enmType == UIType_RuntimeUI && startVM)
624 {
625 /* m_fSeparateProcess makes sense only if a VM is started. */
626 m_fSeparateProcess = fSeparateProcess;
627
628 /* Search for corresponding VM: */
629 QUuid uuid = QUuid(vmNameOrUuid);
630 const CMachine machine = m_comVBox.FindMachine(vmNameOrUuid);
631 if (!uuid.isNull())
632 {
633 if (machine.isNull() && showStartVMErrors())
634 return msgCenter().cannotFindMachineById(m_comVBox, uuid);
635 }
636 else
637 {
638 if (machine.isNull() && showStartVMErrors())
639 return msgCenter().cannotFindMachineByName(m_comVBox, vmNameOrUuid);
640 }
641 m_uManagedVMId = machine.GetId();
642
643 if (m_fSeparateProcess)
644 {
645 /* Create a log file for VirtualBoxVM process. */
646 QString str = machine.GetLogFolder();
647 com::Utf8Str logDir(str.toUtf8().constData());
648
649 /* make sure the Logs folder exists */
650 if (!RTDirExists(logDir.c_str()))
651 RTDirCreateFullPath(logDir.c_str(), 0700);
652
653 com::Utf8Str logFile = com::Utf8StrFmt("%s%cVBoxUI.log",
654 logDir.c_str(), RTPATH_DELIMITER);
655
656 com::VBoxLogRelCreate("GUI (separate)", logFile.c_str(),
657 RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS,
658 "all all.restrict -default.restrict",
659 "VBOX_RELEASE_LOG", RTLOGDEST_FILE,
660 32768 /* cMaxEntriesPerGroup */,
661 0 /* cHistory */, 0 /* uHistoryFileTime */,
662 0 /* uHistoryFileSize */, NULL);
663 }
664 }
665
666 /* For Selector UI: */
667 if (uiType() == UIType_SelectorUI)
668 {
669 /* We should create separate logging file for VM selector: */
670 char szLogFile[RTPATH_MAX];
671 const char *pszLogFile = NULL;
672 com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
673 RTPathAppend(szLogFile, sizeof(szLogFile), "selectorwindow.log");
674 pszLogFile = szLogFile;
675 /* Create release logger, to file: */
676 com::VBoxLogRelCreate("GUI VM Selector Window",
677 pszLogFile,
678 RTLOGFLAGS_PREFIX_TIME_PROG,
679 "all",
680 "VBOX_GUI_SELECTORWINDOW_RELEASE_LOG",
681 RTLOGDEST_FILE | RTLOGDEST_F_NO_DENY,
682 UINT32_MAX,
683 10,
684 60 * 60,
685 _1M,
686 NULL /*pErrInfo*/);
687
688 LogRel(("Qt version: %s\n", qtRTVersionString().toUtf8().constData()));
689 }
690
691 if (m_fSettingsPwSet)
692 m_comVBox.SetSettingsSecret(m_astrSettingsPw);
693
694 if (visualStateType != UIVisualStateType_Invalid && !m_uManagedVMId.isNull())
695 gEDataManager->setRequestedVisualState(visualStateType, m_uManagedVMId);
696
697#ifdef VBOX_WITH_DEBUGGER_GUI
698 /* For Runtime UI: */
699 if (uiType() == UIType_RuntimeUI)
700 {
701 /* Setup the debugger GUI: */
702 if (RTEnvExist("VBOX_GUI_NO_DEBUGGER"))
703 m_fDbgEnabled = m_fDbgAutoShow = m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = false;
704 if (m_fDbgEnabled)
705 {
706 RTERRINFOSTATIC ErrInfo;
707 RTErrInfoInitStatic(&ErrInfo);
708 int vrc = SUPR3HardenedLdrLoadAppPriv("VBoxDbg", &m_hVBoxDbg, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
709 if (RT_FAILURE(vrc))
710 {
711 m_hVBoxDbg = NIL_RTLDRMOD;
712 m_fDbgAutoShow = m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = false;
713 LogRel(("Failed to load VBoxDbg, rc=%Rrc - %s\n", vrc, ErrInfo.Core.pszMsg));
714 }
715 }
716 }
717#endif
718
719 m_fValid = true;
720
721 /* Create medium-enumerator but don't do any immediate caching: */
722 m_pMediumEnumerator = new UIMediumEnumerator;
723 {
724 /* Prepare medium-enumerator: */
725 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumCreated,
726 this, &UICommon::sigMediumCreated);
727 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumDeleted,
728 this, &UICommon::sigMediumDeleted);
729 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerationStarted,
730 this, &UICommon::sigMediumEnumerationStarted);
731 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerated,
732 this, &UICommon::sigMediumEnumerated);
733 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerationFinished,
734 this, &UICommon::sigMediumEnumerationFinished);
735 }
736
737 /* Create shortcut pool: */
738 UIShortcutPool::create();
739
740#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
741 /* Create network manager: */
742 UINetworkRequestManager::create();
743
744 /* Schedule update manager: */
745 UIUpdateManager::schedule();
746#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
747
748#ifdef RT_OS_LINUX
749 /* Make sure no wrong USB mounted: */
750 checkForWrongUSBMounted();
751#endif /* RT_OS_LINUX */
752
753 /* Populate the list of medium names to be excluded from the
754 recently used media extra data: */
755#if 0 /* bird: This is counter productive as it is _frequently_ necessary to re-insert the
756 viso to refresh the files (like after you rebuilt them on the host).
757 The guest caches ISOs aggressively and files sizes may change. */
758 m_recentMediaExcludeList << "ad-hoc.viso";
759#endif
760
761
762 iOriginalFontPixelSize = qApp->font().pixelSize();
763 iOriginalFontPointSize = qApp->font().pointSize();
764 sltHandleFontScaleFactorChanged(gEDataManager->fontScaleFactor());
765}
766
767void UICommon::cleanup()
768{
769 LogRel(("GUI: UICommon: Handling aboutToQuit request..\n"));
770
771 /// @todo Shouldn't that be protected with a mutex or something?
772 /* Remember that the cleanup is in progress preventing any unwanted
773 * stuff which could be called from the other threads: */
774 m_fCleaningUp = true;
775
776#ifdef VBOX_WS_WIN
777 /* Ask listeners to commit data if haven't yet: */
778 if (!m_fDataCommitted)
779 {
780 emit sigAskToCommitData();
781 m_fDataCommitted = true;
782 }
783#else
784 /* Ask listeners to commit data: */
785 emit sigAskToCommitData();
786#endif
787
788#ifdef VBOX_WITH_DEBUGGER_GUI
789 /* For Runtime UI: */
790 if ( uiType() == UIType_RuntimeUI
791 && m_hVBoxDbg != NIL_RTLDRMOD)
792 {
793 RTLdrClose(m_hVBoxDbg);
794 m_hVBoxDbg = NIL_RTLDRMOD;
795 }
796#endif
797
798#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
799 /* Shutdown update manager: */
800 UIUpdateManager::shutdown();
801
802 /* Destroy network manager: */
803 UINetworkRequestManager::destroy();
804#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
805
806 /* Destroy shortcut pool: */
807 UIShortcutPool::destroy();
808
809#ifdef VBOX_GUI_WITH_PIDFILE
810 deletePidfile();
811#endif /* VBOX_GUI_WITH_PIDFILE */
812
813 /* Starting medium-enumerator cleanup: */
814 m_meCleanupProtectionToken.lockForWrite();
815 {
816 /* Destroy medium-enumerator: */
817 delete m_pMediumEnumerator;
818 m_pMediumEnumerator = 0;
819 }
820 /* Finishing medium-enumerator cleanup: */
821 m_meCleanupProtectionToken.unlock();
822
823 /* Destroy the global (VirtualBox and VirtualBoxClient) Main event
824 * handlers which are used in both Manager and Runtime UIs. */
825 UIVirtualBoxEventHandler::destroy();
826 UIVirtualBoxClientEventHandler::destroy();
827
828 /* Destroy the extra-data manager finally after everything
829 * above which could use it already destroyed: */
830 UIExtraDataManager::destroy();
831
832 /* Destroy converter: */
833 UIConverter::destroy();
834
835 /* Cleanup thread-pools: */
836 delete m_pThreadPool;
837 m_pThreadPool = 0;
838 delete m_pThreadPoolCloud;
839 m_pThreadPoolCloud = 0;
840
841 /* Cleanup guest OS type manager before COM stuff: */
842 delete m_pGuestOSTypeManager;
843 m_pGuestOSTypeManager = 0;
844
845 /* Starting COM cleanup: */
846 m_comCleanupProtectionToken.lockForWrite();
847 {
848 /* First, make sure we don't use COM any more: */
849 emit sigAskToDetachCOM();
850 m_comHost.detach();
851 m_comVBox.detach();
852 m_comVBoxClient.detach();
853
854 /* There may be UIMedium(s)EnumeratedEvent instances still in the message
855 * queue which reference COM objects. Remove them to release those objects
856 * before uninitializing the COM subsystem. */
857 QApplication::removePostedEvents(this);
858
859 /* Finally cleanup COM itself: */
860 COMBase::CleanupCOM();
861 }
862 /* Finishing COM cleanup: */
863 m_comCleanupProtectionToken.unlock();
864
865 /* Notify listener it can close UI now: */
866 emit sigAskToCloseUI();
867
868 /* Cleanup general icon-pool: */
869 UIIconPoolGeneral::destroy();
870
871 /* Destroy popup-center: */
872 UIPopupCenter::destroy();
873 /* Destroy message-center: */
874 UIMessageCenter::destroy();
875
876 /* Destroy desktop-widget watchdog: */
877 UIDesktopWidgetWatchdog::destroy();
878
879 m_fValid = false;
880
881 LogRel(("GUI: UICommon: aboutToQuit request handled!\n"));
882}
883
884/* static */
885QString UICommon::qtRTVersionString()
886{
887 return QString::fromLatin1(qVersion());
888}
889
890/* static */
891uint UICommon::qtRTVersion()
892{
893 const QString strVersionRT = UICommon::qtRTVersionString();
894 return (strVersionRT.section('.', 0, 0).toInt() << 16) +
895 (strVersionRT.section('.', 1, 1).toInt() << 8) +
896 strVersionRT.section('.', 2, 2).toInt();
897}
898
899/* static */
900uint UICommon::qtRTMajorVersion()
901{
902 return UICommon::qtRTVersionString().section('.', 0, 0).toInt();
903}
904
905/* static */
906uint UICommon::qtRTMinorVersion()
907{
908 return UICommon::qtRTVersionString().section('.', 1, 1).toInt();
909}
910
911/* static */
912uint UICommon::qtRTRevisionNumber()
913{
914 return UICommon::qtRTVersionString().section('.', 2, 2).toInt();
915}
916
917/* static */
918QString UICommon::qtCTVersionString()
919{
920 return QString::fromLatin1(QT_VERSION_STR);
921}
922
923/* static */
924uint UICommon::qtCTVersion()
925{
926 const QString strVersionCompiled = UICommon::qtCTVersionString();
927 return (strVersionCompiled.section('.', 0, 0).toInt() << 16) +
928 (strVersionCompiled.section('.', 1, 1).toInt() << 8) +
929 strVersionCompiled.section('.', 2, 2).toInt();
930}
931
932QString UICommon::vboxVersionString() const
933{
934 return m_comVBox.GetVersion();
935}
936
937QString UICommon::vboxVersionStringNormalized() const
938{
939 return m_comVBox.GetVersionNormalized();
940}
941
942bool UICommon::isBeta() const
943{
944 return vboxVersionString().contains(QRegularExpression("BETA|ALPHA", QRegularExpression::CaseInsensitiveOption));
945}
946
947bool UICommon::showBetaLabel() const
948{
949 return isBeta()
950 && !gEDataManager->preventBetaBuildLavel();
951}
952
953bool UICommon::brandingIsActive(bool fForce /* = false */)
954{
955 if (fForce)
956 return true;
957
958 if (m_strBrandingConfigFilePath.isEmpty())
959 {
960 m_strBrandingConfigFilePath = QDir(QApplication::applicationDirPath()).absolutePath();
961 m_strBrandingConfigFilePath += "/custom/custom.ini";
962 }
963
964 return QFile::exists(m_strBrandingConfigFilePath);
965}
966
967QString UICommon::brandingGetKey(QString strKey) const
968{
969 QSettings settings(m_strBrandingConfigFilePath, QSettings::IniFormat);
970 return settings.value(QString("%1").arg(strKey)).toString();
971}
972
973#ifdef VBOX_WS_MAC
974/* static */
975MacOSXRelease UICommon::determineOsRelease()
976{
977 /* Prepare 'utsname' struct: */
978 utsname info;
979 if (uname(&info) != -1)
980 {
981 /* Cut the major release index of the string we have, s.a. 'man uname': */
982 const int iRelease = QString(info.release).section('.', 0, 0).toInt();
983 /* Check boundaries: */
984 if (iRelease <= MacOSXRelease_FirstUnknown)
985 return MacOSXRelease_Old;
986 else if (iRelease >= MacOSXRelease_LastUnknown)
987 return MacOSXRelease_New;
988 else
989 return (MacOSXRelease)iRelease;
990 }
991 /* Return 'Old' by default: */
992 return MacOSXRelease_Old;
993}
994#endif /* VBOX_WS_MAC */
995
996#ifdef VBOX_WS_NIX
997bool UICommon::X11ServerAvailable() const
998{
999 return VBGHDisplayServerTypeIsXAvailable(m_enmDisplayServerType);
1000}
1001
1002VBGHDISPLAYSERVERTYPE UICommon::displayServerType() const
1003{
1004 return m_enmDisplayServerType;
1005}
1006#endif
1007
1008QString UICommon::hostOperatingSystem() const
1009{
1010 if (!m_comHost.isOk())
1011 return QString();
1012 return m_comHost.GetOperatingSystem();
1013}
1014
1015#if defined(VBOX_WS_MAC)
1016// Provided by UICocoaApplication ..
1017
1018#elif defined(VBOX_WS_WIN)
1019
1020bool UICommon::isWindowsInDarkMode() const
1021{
1022 /* Load saved color theme: */
1023 UIColorThemeType enmColorTheme = gEDataManager->colorTheme();
1024
1025 /* Check whether we have dark system theme requested: */
1026 if (enmColorTheme == UIColorThemeType_Auto)
1027 {
1028 QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
1029 QSettings::NativeFormat);
1030 if (settings.value("AppsUseLightTheme") == 0)
1031 enmColorTheme = UIColorThemeType_Dark;
1032 }
1033
1034 /* Return result: */
1035 return enmColorTheme == UIColorThemeType_Dark;
1036}
1037
1038#else /* Linux, BSD, Solaris */
1039
1040bool UICommon::isPaletteInDarkMode() const
1041{
1042 const QPalette pal = qApp->palette();
1043 const QColor background = pal.color(QPalette::Active, QPalette::Window);
1044 const double dLuminance = (0.299 * background.red() + 0.587 * background.green() + 0.114 * background.blue()) / 255;
1045 return dLuminance < 0.5;
1046}
1047#endif /* Linux, BSD, Solaris */
1048
1049void UICommon::loadColorTheme()
1050{
1051#if defined (VBOX_WS_MAC)
1052 /* macOS has Window color hardcoded somewhere inside, Qt has no access to it,
1053 * moreover these colors are influenced by window background blending,
1054 * making Qt default colors incredibly inconsistent with native macOS apps. */
1055
1056 /* Redefine colors for known OS types: */
1057 enum ColorSlot
1058 {
1059 ColorSlot_DarkActive,
1060 ColorSlot_DarkInactive,
1061 ColorSlot_DarkAlternate,
1062 ColorSlot_LightActive,
1063 ColorSlot_LightInactive,
1064 ColorSlot_LightAlternate,
1065 };
1066 QMap<ColorSlot, QColor> colors;
1067 switch (osRelease())
1068 {
1069 case MacOSXRelease_BigSur:
1070 {
1071 colors[ColorSlot_DarkActive] = QColor("#282628");
1072 colors[ColorSlot_DarkInactive] = QColor("#2E292E");
1073 colors[ColorSlot_LightActive] = QColor("#E7E2E3");
1074 colors[ColorSlot_LightInactive] = QColor("#EEE9EA");
1075 break;
1076 }
1077 case MacOSXRelease_Monterey:
1078 {
1079 colors[ColorSlot_DarkActive] = QColor("#252328");
1080 colors[ColorSlot_DarkInactive] = QColor("#2A2630");
1081 colors[ColorSlot_LightActive] = QColor("#E1DEE4");
1082 colors[ColorSlot_LightInactive] = QColor("#EEE8E9");
1083 break;
1084 }
1085 case MacOSXRelease_Ventura:
1086 {
1087 colors[ColorSlot_DarkActive] = QColor("#322827");
1088 colors[ColorSlot_DarkInactive] = QColor("#332A28");
1089 colors[ColorSlot_LightActive] = QColor("#E5E0DF");
1090 colors[ColorSlot_LightInactive] = QColor("#ECE7E5");
1091 break;
1092 }
1093 default:
1094 break;
1095 }
1096 /* Redefine colors common for various OS types: */
1097 colors[ColorSlot_DarkAlternate] = QColor("#2F2A2F");
1098 colors[ColorSlot_LightAlternate] = QColor("#F4F5F5");
1099
1100 /* Do we have redefined colors? */
1101 if (!colors.isEmpty())
1102 {
1103 QPalette pal = qApp->palette();
1104 if (isInDarkMode())
1105 {
1106 pal.setColor(QPalette::Active, QPalette::Window, colors.value(ColorSlot_DarkActive));
1107 pal.setColor(QPalette::Inactive, QPalette::Window, colors.value(ColorSlot_DarkInactive));
1108 pal.setColor(QPalette::Active, QPalette::AlternateBase, colors.value(ColorSlot_DarkAlternate));
1109 pal.setColor(QPalette::Inactive, QPalette::AlternateBase, colors.value(ColorSlot_DarkAlternate));
1110 }
1111 else
1112 {
1113 pal.setColor(QPalette::Active, QPalette::Window, colors.value(ColorSlot_LightActive));
1114 pal.setColor(QPalette::Inactive, QPalette::Window, colors.value(ColorSlot_LightInactive));
1115 pal.setColor(QPalette::Active, QPalette::AlternateBase, colors.value(ColorSlot_LightAlternate));
1116 pal.setColor(QPalette::Inactive, QPalette::AlternateBase, colors.value(ColorSlot_LightAlternate));
1117 }
1118 qApp->setPalette(pal);
1119 }
1120
1121#elif defined(VBOX_WS_WIN)
1122
1123 /* For the Dark mode! */
1124 if (isInDarkMode())
1125 {
1126 qApp->setStyle(QStyleFactory::create("Fusion"));
1127 QPalette darkPalette;
1128 QColor windowColor1 = QColor(59, 60, 61);
1129 QColor windowColor2 = QColor(63, 64, 65);
1130 QColor baseColor1 = QColor(46, 47, 48);
1131 QColor baseColor2 = QColor(56, 57, 58);
1132 QColor disabledColor = QColor(113, 114, 115);
1133 darkPalette.setColor(QPalette::Window, windowColor1);
1134 darkPalette.setColor(QPalette::WindowText, Qt::white);
1135 darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, disabledColor);
1136 darkPalette.setColor(QPalette::Base, baseColor1);
1137 darkPalette.setColor(QPalette::AlternateBase, baseColor2);
1138 darkPalette.setColor(QPalette::PlaceholderText, disabledColor);
1139 darkPalette.setColor(QPalette::Text, Qt::white);
1140 darkPalette.setColor(QPalette::Disabled, QPalette::Text, disabledColor);
1141 darkPalette.setColor(QPalette::Button, windowColor2);
1142 darkPalette.setColor(QPalette::ButtonText, Qt::white);
1143 darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledColor);
1144 darkPalette.setColor(QPalette::BrightText, Qt::red);
1145 darkPalette.setColor(QPalette::Link, QColor(179, 214, 242));
1146 darkPalette.setColor(QPalette::Highlight, QColor(29, 84, 92));
1147 darkPalette.setColor(QPalette::HighlightedText, Qt::white);
1148 darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, disabledColor);
1149 qApp->setPalette(darkPalette);
1150 qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2b2b2b; border: 1px solid #737373; }");
1151 }
1152
1153#else /* Linux, BSD, Solaris */
1154
1155 /* For the Dark mode! */
1156 if (isInDarkMode())
1157 {
1158 // WORKAROUND:
1159 // Have seen it on Linux with Qt5 but still see it with Qt6.
1160 // In Dark themes on KDE (at least) PlaceholderText foreground
1161 // is indistinguishable on Base background.
1162
1163 /* Acquire global palette: */
1164 QPalette darkPalette = qApp->palette();
1165
1166 /* Get text base color: */
1167 const QColor base = darkPalette.color(QPalette::Active, QPalette::Base);
1168
1169 /* Get possible foreground colors: */
1170 const QColor simpleText = darkPalette.color(QPalette::Active, QPalette::Text);
1171 const QColor placeholderText = darkPalette.color(QPalette::Active, QPalette::PlaceholderText);
1172 QColor lightText = simpleText.black() < placeholderText.black() ? simpleText : placeholderText;
1173 QColor darkText = simpleText.black() > placeholderText.black() ? simpleText : placeholderText;
1174 if (lightText.black() > 128)
1175 lightText = QColor(Qt::white);
1176 lightText = lightText.darker(150);
1177 if (darkText.black() < 128)
1178 darkText = QColor(Qt::black);
1179 darkText = darkText.lighter(150);
1180
1181 /* Measure base luminance: */
1182 double dLuminance = (0.299 * base.red() + 0.587 * base.green() + 0.114 * base.blue()) / 255;
1183
1184 /* Adjust color accordingly: */
1185 darkPalette.setColor(QPalette::Active, QPalette::PlaceholderText,
1186 dLuminance > 0.5 ? darkText : lightText);
1187
1188 /* Put palette back: */
1189 qApp->setPalette(darkPalette);
1190 }
1191
1192#endif /* Linux, BSD, Solaris */
1193}
1194
1195bool UICommon::processArgs()
1196{
1197 /* Among those arguments: */
1198 bool fResult = false;
1199 const QStringList args = qApp->arguments();
1200
1201 /* We are looking for a list of file URLs passed to the executable: */
1202 QList<QUrl> listArgUrls;
1203 for (int i = 1; i < args.size(); ++i)
1204 {
1205 /* But we break out after the first parameter, cause there
1206 * could be parameters with arguments (e.g. --comment comment). */
1207 if (args.at(i).startsWith("-"))
1208 break;
1209
1210#ifdef VBOX_WS_MAC
1211 const QString strArg = ::darwinResolveAlias(args.at(i));
1212#else
1213 const QString strArg = args.at(i);
1214#endif
1215
1216 /* So if the argument file exists, we add it to URL list: */
1217 if ( !strArg.isEmpty()
1218 && QFile::exists(strArg))
1219 listArgUrls << QUrl::fromLocalFile(QFileInfo(strArg).absoluteFilePath());
1220 }
1221
1222 /* If there are file URLs: */
1223 if (!listArgUrls.isEmpty())
1224 {
1225 /* We enumerate them and: */
1226 for (int i = 0; i < listArgUrls.size(); ++i)
1227 {
1228 /* Check which of them has allowed VM extensions: */
1229 const QUrl url = listArgUrls.at(i);
1230 const QString strFile = url.toLocalFile();
1231 if (UICommon::hasAllowedExtension(strFile, VBoxFileExts))
1232 {
1233 /* So that we could run existing VMs: */
1234 CVirtualBox comVBox = virtualBox();
1235 CMachine comMachine = comVBox.FindMachine(strFile);
1236 if (!comMachine.isNull())
1237 {
1238 fResult = true;
1239 launchMachine(comMachine);
1240 /* And remove their URLs from the ULR list: */
1241 listArgUrls.removeAll(url);
1242 }
1243 }
1244 }
1245 }
1246
1247 /* And if there are *still* URLs: */
1248 if (!listArgUrls.isEmpty())
1249 {
1250 /* We store them, they will be handled later: */
1251 m_listArgUrls = listArgUrls;
1252 }
1253
1254 return fResult;
1255}
1256
1257bool UICommon::argumentUrlsPresent() const
1258{
1259 return !m_listArgUrls.isEmpty();
1260}
1261
1262QList<QUrl> UICommon::takeArgumentUrls()
1263{
1264 const QList<QUrl> result = m_listArgUrls;
1265 m_listArgUrls.clear();
1266 return result;
1267}
1268
1269#ifdef VBOX_WITH_DEBUGGER_GUI
1270
1271bool UICommon::isDebuggerEnabled() const
1272{
1273 return isDebuggerWorker(&m_fDbgEnabled, GUI_Dbg_Enabled);
1274}
1275
1276bool UICommon::isDebuggerAutoShowEnabled() const
1277{
1278 return isDebuggerWorker(&m_fDbgAutoShow, GUI_Dbg_AutoShow);
1279}
1280
1281bool UICommon::isDebuggerAutoShowCommandLineEnabled() const
1282{
1283 return isDebuggerWorker(&m_fDbgAutoShowCommandLine, GUI_Dbg_AutoShow);
1284}
1285
1286bool UICommon::isDebuggerAutoShowStatisticsEnabled() const
1287{
1288 return isDebuggerWorker(&m_fDbgAutoShowStatistics, GUI_Dbg_AutoShow);
1289}
1290
1291#endif /* VBOX_WITH_DEBUGGER_GUI */
1292
1293bool UICommon::shouldStartPaused() const
1294{
1295#ifdef VBOX_WITH_DEBUGGER_GUI
1296 return m_enmLaunchRunning == LaunchRunning_Default ? isDebuggerAutoShowEnabled() : m_enmLaunchRunning == LaunchRunning_No;
1297#else
1298 return false;
1299#endif
1300}
1301
1302#ifdef VBOX_GUI_WITH_PIDFILE
1303
1304void UICommon::createPidfile()
1305{
1306 if (!m_strPidFile.isEmpty())
1307 {
1308 const qint64 iPid = qApp->applicationPid();
1309 QFile file(m_strPidFile);
1310 if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
1311 {
1312 QTextStream out(&file);
1313 out << iPid << endl;
1314 }
1315 else
1316 LogRel(("Failed to create pid file %s\n", m_strPidFile.toUtf8().constData()));
1317 }
1318}
1319
1320void UICommon::deletePidfile()
1321{
1322 if ( !m_strPidFile.isEmpty()
1323 && QFile::exists(m_strPidFile))
1324 QFile::remove(m_strPidFile);
1325}
1326
1327#endif /* VBOX_GUI_WITH_PIDFILE */
1328
1329/* static */
1330bool UICommon::switchToMachine(CMachine &comMachine)
1331{
1332#ifdef VBOX_WS_MAC
1333 const ULONG64 id = comMachine.ShowConsoleWindow();
1334#else
1335 const WId id = (WId)comMachine.ShowConsoleWindow();
1336#endif
1337 AssertWrapperOk(comMachine);
1338 if (!comMachine.isOk())
1339 return false;
1340
1341 // WORKAROUND:
1342 // id == 0 means the console window has already done everything
1343 // necessary to implement the "show window" semantics.
1344 if (id == 0)
1345 return true;
1346
1347#if defined(VBOX_WS_WIN) || defined(VBOX_WS_NIX)
1348
1349 return UIDesktopWidgetWatchdog::activateWindow(id, true);
1350
1351#elif defined(VBOX_WS_MAC)
1352
1353 // WORKAROUND:
1354 // This is just for the case were the other process cannot steal
1355 // the focus from us. It will send us a PSN so we can try.
1356 ProcessSerialNumber psn;
1357 psn.highLongOfPSN = id >> 32;
1358 psn.lowLongOfPSN = (UInt32)id;
1359# ifdef __clang__
1360# pragma GCC diagnostic push
1361# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1362 OSErr rc = ::SetFrontProcess(&psn);
1363# pragma GCC diagnostic pop
1364# else
1365 OSErr rc = ::SetFrontProcess(&psn);
1366# endif
1367 if (!rc)
1368 Log(("GUI: %#RX64 couldn't do SetFrontProcess on itself, the selector (we) had to do it...\n", id));
1369 else
1370 Log(("GUI: Failed to bring %#RX64 to front. rc=%#x\n", id, rc));
1371 return !rc;
1372
1373#else
1374
1375 return false;
1376
1377#endif
1378}
1379
1380/* static */
1381bool UICommon::launchMachine(CMachine &comMachine, UILaunchMode enmLaunchMode /* = UILaunchMode_Default */)
1382{
1383 /* Switch to machine window(s) if possible: */
1384 if ( comMachine.GetSessionState() == KSessionState_Locked /* precondition for CanShowConsoleWindow() */
1385 && comMachine.CanShowConsoleWindow())
1386 {
1387 switch (uiCommon().uiType())
1388 {
1389 /* For Selector UI: */
1390 case UIType_SelectorUI:
1391 {
1392 /* Just switch to existing VM window: */
1393 return switchToMachine(comMachine);
1394 }
1395 /* For Runtime UI: */
1396 case UIType_RuntimeUI:
1397 {
1398 /* Only separate UI process can reach that place.
1399 * Switch to existing VM window and exit. */
1400 switchToMachine(comMachine);
1401 return false;
1402 }
1403 }
1404 }
1405
1406 /* Not for separate UI (which can connect to machine in any state): */
1407 if (enmLaunchMode != UILaunchMode_Separate)
1408 {
1409 /* Make sure machine-state is one of required: */
1410 const KMachineState enmState = comMachine.GetState(); NOREF(enmState);
1411 AssertMsg( enmState == KMachineState_PoweredOff
1412 || enmState == KMachineState_Saved
1413 || enmState == KMachineState_Teleported
1414 || enmState == KMachineState_Aborted
1415 || enmState == KMachineState_AbortedSaved
1416 , ("Machine must be PoweredOff/Saved/Teleported/Aborted (%d)", enmState));
1417 }
1418
1419 /* Create empty session instance: */
1420 CSession comSession;
1421 comSession.createInstance(CLSID_Session);
1422 if (comSession.isNull())
1423 {
1424 msgCenter().cannotOpenSession(comSession);
1425 return false;
1426 }
1427
1428 /* Configure environment: */
1429 QVector<QString> astrEnv;
1430#ifdef VBOX_WS_WIN
1431 /* Allow started VM process to be foreground window: */
1432 AllowSetForegroundWindow(ASFW_ANY);
1433#endif
1434#ifdef VBOX_WS_NIX
1435 /* Make sure VM process will start on the same
1436 * display as window this wrapper is called from: */
1437 const char *pDisplay = RTEnvGet("DISPLAY");
1438 if (pDisplay)
1439 astrEnv.append(QString("DISPLAY=%1").arg(pDisplay));
1440 const char *pXauth = RTEnvGet("XAUTHORITY");
1441 if (pXauth)
1442 astrEnv.append(QString("XAUTHORITY=%1").arg(pXauth));
1443#endif
1444 QString strType;
1445 switch (enmLaunchMode)
1446 {
1447 case UILaunchMode_Default: strType = ""; break;
1448 case UILaunchMode_Separate: strType = uiCommon().isSeparateProcess() ? "headless" : "separate"; break;
1449 case UILaunchMode_Headless: strType = "headless"; break;
1450 default: AssertFailedReturn(false);
1451 }
1452
1453 /* Prepare "VM spawning" progress: */
1454 CProgress comProgress = comMachine.LaunchVMProcess(comSession, strType, astrEnv);
1455 if (!comMachine.isOk())
1456 {
1457 /* If the VM is started separately and the VM process is already running, then it is OK. */
1458 if (enmLaunchMode == UILaunchMode_Separate)
1459 {
1460 const KMachineState enmState = comMachine.GetState();
1461 if ( enmState >= KMachineState_FirstOnline
1462 && enmState <= KMachineState_LastOnline)
1463 {
1464 /* Already running: */
1465 return true;
1466 }
1467 }
1468
1469 msgCenter().cannotOpenSession(comMachine);
1470 return false;
1471 }
1472
1473 /* Show "VM spawning" progress: */
1474 msgCenter().showModalProgressDialog(comProgress, comMachine.GetName(),
1475 ":/progress_start_90px.png", 0, 0);
1476 if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
1477 msgCenter().cannotOpenSession(comProgress, comMachine.GetName());
1478
1479 /* Unlock machine, close session: */
1480 comSession.UnlockMachine();
1481
1482 /* True finally: */
1483 return true;
1484}
1485
1486CSession UICommon::openSession(QUuid uId, KLockType enmLockType /* = KLockType_Write */)
1487{
1488 /* Prepare session: */
1489 CSession comSession;
1490
1491 /* Make sure uId isn't null: */
1492 if (uId.isNull())
1493 uId = managedVMUuid();
1494 if (uId.isNull())
1495 return comSession;
1496
1497 /* Simulate try-catch block: */
1498 bool fSuccess = false;
1499 do
1500 {
1501 /* Create empty session instance: */
1502 comSession.createInstance(CLSID_Session);
1503 if (comSession.isNull())
1504 {
1505 msgCenter().cannotOpenSession(comSession);
1506 break;
1507 }
1508
1509 /* Search for the corresponding machine: */
1510 CMachine comMachine = m_comVBox.FindMachine(uId.toString());
1511 if (comMachine.isNull())
1512 {
1513 msgCenter().cannotFindMachineById(m_comVBox, uId);
1514 break;
1515 }
1516
1517 if (enmLockType == KLockType_VM)
1518 comSession.SetName("GUI/Qt");
1519
1520 /* Lock found machine to session: */
1521 comMachine.LockMachine(comSession, enmLockType);
1522 if (!comMachine.isOk())
1523 {
1524 msgCenter().cannotOpenSession(comMachine);
1525 break;
1526 }
1527
1528 /* Pass the language ID as the property to the guest: */
1529 if (comSession.GetType() == KSessionType_Shared)
1530 {
1531 CMachine comStartedMachine = comSession.GetMachine();
1532 /* Make sure that the language is in two letter code.
1533 * Note: if languageId() returns an empty string lang.name() will
1534 * return "C" which is an valid language code. */
1535 QLocale lang(UITranslator::languageId());
1536 comStartedMachine.SetGuestPropertyValue("/VirtualBox/HostInfo/GUI/LanguageID", lang.name());
1537 }
1538
1539 /* Success finally: */
1540 fSuccess = true;
1541 }
1542 while (0);
1543 /* Cleanup try-catch block: */
1544 if (!fSuccess)
1545 comSession.detach();
1546
1547 /* Return session: */
1548 return comSession;
1549}
1550
1551CSession UICommon::openSession(KLockType enmLockType /* = KLockType_Write */)
1552{
1553 /* Pass to function above: */
1554 return openSession(managedVMUuid(), enmLockType);
1555}
1556
1557CSession UICommon::tryToOpenSessionFor(CMachine &comMachine)
1558{
1559 /* Prepare session: */
1560 CSession comSession;
1561
1562 /* Session state unlocked? */
1563 if (comMachine.GetSessionState() == KSessionState_Unlocked)
1564 {
1565 /* Open own 'write' session: */
1566 comSession = openSession(comMachine.GetId());
1567 AssertReturn(!comSession.isNull(), CSession());
1568 comMachine = comSession.GetMachine();
1569 }
1570 /* Is this a Selector UI call? */
1571 else if (uiType() == UIType_SelectorUI)
1572 {
1573 /* Open existing 'shared' session: */
1574 comSession = openExistingSession(comMachine.GetId());
1575 AssertReturn(!comSession.isNull(), CSession());
1576 comMachine = comSession.GetMachine();
1577 }
1578 /* Else this is Runtime UI call
1579 * which has session locked for itself. */
1580
1581 /* Return session: */
1582 return comSession;
1583}
1584
1585void UICommon::notifyCloudMachineUnregistered(const QString &strProviderShortName,
1586 const QString &strProfileName,
1587 const QUuid &uId)
1588{
1589 emit sigCloudMachineUnregistered(strProviderShortName, strProfileName, uId);
1590}
1591
1592void UICommon::notifyCloudMachineRegistered(const QString &strProviderShortName,
1593 const QString &strProfileName,
1594 const CCloudMachine &comMachine)
1595{
1596 emit sigCloudMachineRegistered(strProviderShortName, strProfileName, comMachine);
1597}
1598
1599const UIGuestOSTypeManager &UICommon::guestOSTypeManager()
1600{
1601 /* Handle exceptional and undesired case!
1602 * This object is created and destroyed within own timeframe.
1603 * If pointer isn't yet initialized or already cleaned up,
1604 * something is definitely wrong. */
1605 AssertPtr(m_pGuestOSTypeManager);
1606 if (!m_pGuestOSTypeManager)
1607 {
1608 m_pGuestOSTypeManager = new UIGuestOSTypeManager;
1609 m_pGuestOSTypeManager->reCacheGuestOSTypes();
1610 }
1611
1612 /* Return an object instance: */
1613 return *m_pGuestOSTypeManager;
1614}
1615
1616void UICommon::enumerateMedia(const CMediumVector &comMedia /* = CMediumVector() */)
1617{
1618 /* Make sure UICommon is already valid: */
1619 AssertReturnVoid(m_fValid);
1620 /* Ignore the request during UICommon cleanup: */
1621 if (m_fCleaningUp)
1622 return;
1623 /* Ignore the request during startup snapshot restoring: */
1624 if (shouldRestoreCurrentSnapshot())
1625 return;
1626
1627 /* Make sure medium-enumerator is already created: */
1628 if (!m_pMediumEnumerator)
1629 return;
1630
1631 /* Redirect request to medium-enumerator under proper lock: */
1632 if (m_meCleanupProtectionToken.tryLockForRead())
1633 {
1634 if (m_pMediumEnumerator)
1635 m_pMediumEnumerator->enumerateMedia(comMedia);
1636 m_meCleanupProtectionToken.unlock();
1637 }
1638}
1639
1640void UICommon::refreshMedia()
1641{
1642 /* Make sure UICommon is already valid: */
1643 AssertReturnVoid(m_fValid);
1644 /* Ignore the request during UICommon cleanup: */
1645 if (m_fCleaningUp)
1646 return;
1647 /* Ignore the request during startup snapshot restoring: */
1648 if (shouldRestoreCurrentSnapshot())
1649 return;
1650
1651 /* Make sure medium-enumerator is already created: */
1652 if (!m_pMediumEnumerator)
1653 return;
1654 /* Make sure enumeration is not already started: */
1655 if (m_pMediumEnumerator->isMediumEnumerationInProgress())
1656 return;
1657
1658 /* We assume it's safe to call it without locking,
1659 * since we are performing blocking operation here. */
1660 m_pMediumEnumerator->refreshMedia();
1661}
1662
1663bool UICommon::isFullMediumEnumerationRequested() const
1664{
1665 /* Redirect request to medium-enumerator: */
1666 return m_pMediumEnumerator
1667 && m_pMediumEnumerator->isFullMediumEnumerationRequested();
1668}
1669
1670bool UICommon::isMediumEnumerationInProgress() const
1671{
1672 /* Redirect request to medium-enumerator: */
1673 return m_pMediumEnumerator
1674 && m_pMediumEnumerator->isMediumEnumerationInProgress();
1675}
1676
1677UIMedium UICommon::medium(const QUuid &uMediumID) const
1678{
1679 if (m_meCleanupProtectionToken.tryLockForRead())
1680 {
1681 /* Redirect call to medium-enumerator: */
1682 UIMedium guiMedium;
1683 if (m_pMediumEnumerator)
1684 guiMedium = m_pMediumEnumerator->medium(uMediumID);
1685 m_meCleanupProtectionToken.unlock();
1686 return guiMedium;
1687 }
1688 return UIMedium();
1689}
1690
1691QList<QUuid> UICommon::mediumIDs() const
1692{
1693 if (m_meCleanupProtectionToken.tryLockForRead())
1694 {
1695 /* Redirect call to medium-enumerator: */
1696 QList<QUuid> listOfMedia;
1697 if (m_pMediumEnumerator)
1698 listOfMedia = m_pMediumEnumerator->mediumIDs();
1699 m_meCleanupProtectionToken.unlock();
1700 return listOfMedia;
1701 }
1702 return QList<QUuid>();
1703}
1704
1705void UICommon::createMedium(const UIMedium &guiMedium)
1706{
1707 if (m_meCleanupProtectionToken.tryLockForRead())
1708 {
1709 /* Create medium in medium-enumerator: */
1710 if (m_pMediumEnumerator)
1711 m_pMediumEnumerator->createMedium(guiMedium);
1712 m_meCleanupProtectionToken.unlock();
1713 }
1714}
1715
1716QUuid UICommon::openMedium(UIMediumDeviceType enmMediumType, QString strMediumLocation, QWidget *pParent /* = 0 */)
1717{
1718 /* Convert to native separators: */
1719 strMediumLocation = QDir::toNativeSeparators(strMediumLocation);
1720
1721 /* Initialize variables: */
1722 CVirtualBox comVBox = virtualBox();
1723
1724 /* Open corresponding medium: */
1725 CMedium comMedium = comVBox.OpenMedium(strMediumLocation, mediumTypeToGlobal(enmMediumType), KAccessMode_ReadWrite, false);
1726
1727 if (comVBox.isOk())
1728 {
1729 /* Prepare vbox medium wrapper: */
1730 UIMedium guiMedium = medium(comMedium.GetId());
1731
1732 /* First of all we should test if that medium already opened: */
1733 if (guiMedium.isNull())
1734 {
1735 /* And create new otherwise: */
1736 guiMedium = UIMedium(comMedium, enmMediumType, KMediumState_Created);
1737 createMedium(guiMedium);
1738 }
1739
1740 /* Return guiMedium id: */
1741 return guiMedium.id();
1742 }
1743 else
1744 msgCenter().cannotOpenMedium(comVBox, strMediumLocation, pParent);
1745
1746 return QUuid();
1747}
1748
1749QUuid UICommon::openMediumWithFileOpenDialog(UIMediumDeviceType enmMediumType, QWidget *pParent,
1750 const QString &strDefaultFolder /* = QString() */,
1751 bool fUseLastFolder /* = false */)
1752{
1753 /* Initialize variables: */
1754 QList<QPair <QString, QString> > filters;
1755 QStringList backends;
1756 QStringList prefixes;
1757 QString strFilter;
1758 QString strTitle;
1759 QString allType;
1760 QString strLastFolder = defaultFolderPathForType(enmMediumType);
1761
1762 /* For DVDs and Floppies always check first the last recently used medium folder. For hard disk use
1763 the caller's setting: */
1764 fUseLastFolder = (enmMediumType == UIMediumDeviceType_DVD) || (enmMediumType == UIMediumDeviceType_Floppy);
1765
1766 switch (enmMediumType)
1767 {
1768 case UIMediumDeviceType_HardDisk:
1769 {
1770 filters = HDDBackends(virtualBox());
1771 strTitle = tr("Please choose a virtual hard disk file");
1772 allType = tr("All virtual hard disk files (%1)");
1773 break;
1774 }
1775 case UIMediumDeviceType_DVD:
1776 {
1777 filters = DVDBackends(virtualBox());
1778 strTitle = tr("Please choose a virtual optical disk file");
1779 allType = tr("All virtual optical disk files (%1)");
1780 break;
1781 }
1782 case UIMediumDeviceType_Floppy:
1783 {
1784 filters = FloppyBackends(virtualBox());
1785 strTitle = tr("Please choose a virtual floppy disk file");
1786 allType = tr("All virtual floppy disk files (%1)");
1787 break;
1788 }
1789 default:
1790 break;
1791 }
1792 QString strHomeFolder = fUseLastFolder && !strLastFolder.isEmpty() ? strLastFolder :
1793 strDefaultFolder.isEmpty() ? homeFolder() : strDefaultFolder;
1794
1795 /* Prepare filters and backends: */
1796 for (int i = 0; i < filters.count(); ++i)
1797 {
1798 /* Get iterated filter: */
1799 QPair<QString, QString> item = filters.at(i);
1800 /* Create one backend filter string: */
1801 backends << QString("%1 (%2)").arg(item.first).arg(item.second);
1802 /* Save the suffix's for the "All" entry: */
1803 prefixes << item.second;
1804 }
1805 if (!prefixes.isEmpty())
1806 backends.insert(0, allType.arg(prefixes.join(" ").trimmed()));
1807 backends << tr("All files (*)");
1808 strFilter = backends.join(";;").trimmed();
1809
1810 /* Create open file dialog: */
1811 QStringList files = QIFileDialog::getOpenFileNames(strHomeFolder, strFilter, pParent, strTitle, 0, true, true);
1812
1813 /* If dialog has some result: */
1814 if (!files.empty() && !files[0].isEmpty())
1815 {
1816 QUuid uMediumId = openMedium(enmMediumType, files[0], pParent);
1817 if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy ||
1818 (enmMediumType == UIMediumDeviceType_HardDisk && fUseLastFolder))
1819 updateRecentlyUsedMediumListAndFolder(enmMediumType, medium(uMediumId).location());
1820 return uMediumId;
1821 }
1822 return QUuid();
1823}
1824
1825QUuid UICommon::openMediumCreatorDialog(UIActionPool *pActionPool, QWidget *pParent, UIMediumDeviceType enmMediumType,
1826 const QString &strDefaultFolder /* = QString() */,
1827 const QString &strMachineName /* = QString() */,
1828 const QString &strMachineGuestOSTypeId /*= QString() */)
1829{
1830 /* Depending on medium-type: */
1831 QUuid uMediumId;
1832 switch (enmMediumType)
1833 {
1834 case UIMediumDeviceType_HardDisk:
1835 uMediumId = UIWizardNewVD::createVDWithWizard(pParent, strDefaultFolder, strMachineName, strMachineGuestOSTypeId);
1836 break;
1837 case UIMediumDeviceType_DVD:
1838 uMediumId = UIVisoCreatorDialog::createViso(pActionPool, pParent, strDefaultFolder, strMachineName);
1839 break;
1840 case UIMediumDeviceType_Floppy:
1841 uMediumId = UIFDCreationDialog::createFloppyDisk(pParent, strDefaultFolder, strMachineName);
1842 break;
1843 default:
1844 break;
1845 }
1846 if (uMediumId.isNull())
1847 return QUuid();
1848
1849 /* Update the recent medium list only if the medium type is floppy since updating when a VISO is created is not optimal: */
1850 if (enmMediumType == UIMediumDeviceType_Floppy)
1851 updateRecentlyUsedMediumListAndFolder(enmMediumType, medium(uMediumId).location());
1852 return uMediumId;
1853}
1854
1855void UICommon::prepareStorageMenu(QMenu *pMenu,
1856 QObject *pListener, const char *pszSlotName,
1857 const CMachine &comMachine, const QString &strControllerName, const StorageSlot &storageSlot)
1858{
1859 /* Current attachment attributes: */
1860 const CMediumAttachment comCurrentAttachment = comMachine.GetMediumAttachment(strControllerName,
1861 storageSlot.port,
1862 storageSlot.device);
1863 const CMedium comCurrentMedium = comCurrentAttachment.GetMedium();
1864 const QUuid uCurrentID = comCurrentMedium.isNull() ? QUuid() : comCurrentMedium.GetId();
1865 const QString strCurrentLocation = comCurrentMedium.isNull() ? QString() : comCurrentMedium.GetLocation();
1866
1867 /* Other medium-attachments of same machine: */
1868 const CMediumAttachmentVector comAttachments = comMachine.GetMediumAttachments();
1869
1870 /* Determine device & medium types: */
1871 const UIMediumDeviceType enmMediumType = mediumTypeToLocal(comCurrentAttachment.GetType());
1872 AssertMsgReturnVoid(enmMediumType != UIMediumDeviceType_Invalid, ("Incorrect storage medium type!\n"));
1873
1874 /* Prepare open-existing-medium action: */
1875 QAction *pActionOpenExistingMedium = pMenu->addAction(UIIconPool::iconSet(":/select_file_16px.png"),
1876 QString(), pListener, pszSlotName);
1877 pActionOpenExistingMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
1878 comCurrentAttachment.GetDevice(), enmMediumType)));
1879 pActionOpenExistingMedium->setText(QApplication::translate("UIMachineSettingsStorage", "Choose/Create a disk image..."));
1880
1881
1882 /* Prepare open medium file action: */
1883 QAction *pActionFileSelector = pMenu->addAction(UIIconPool::iconSet(":/select_file_16px.png"),
1884 QString(), pListener, pszSlotName);
1885 pActionFileSelector->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
1886 comCurrentAttachment.GetDevice(), enmMediumType,
1887 UIMediumTarget::UIMediumTargetType_WithFileDialog)));
1888 pActionFileSelector->setText(QApplication::translate("UIMachineSettingsStorage", "Choose a disk file..."));
1889
1890
1891 /* Insert separator: */
1892 pMenu->addSeparator();
1893
1894 /* Get existing-host-drive vector: */
1895 CMediumVector comMedia;
1896 switch (enmMediumType)
1897 {
1898 case UIMediumDeviceType_DVD: comMedia = host().GetDVDDrives(); break;
1899 case UIMediumDeviceType_Floppy: comMedia = host().GetFloppyDrives(); break;
1900 default: break;
1901 }
1902 /* Prepare choose-existing-host-drive actions: */
1903 foreach (const CMedium &comMedium, comMedia)
1904 {
1905 /* Make sure host-drive usage is unique: */
1906 bool fIsHostDriveUsed = false;
1907 foreach (const CMediumAttachment &comOtherAttachment, comAttachments)
1908 {
1909 if (comOtherAttachment != comCurrentAttachment)
1910 {
1911 const CMedium &comOtherMedium = comOtherAttachment.GetMedium();
1912 if (!comOtherMedium.isNull() && comOtherMedium.GetId() == comMedium.GetId())
1913 {
1914 fIsHostDriveUsed = true;
1915 break;
1916 }
1917 }
1918 }
1919 /* If host-drives usage is unique: */
1920 if (!fIsHostDriveUsed)
1921 {
1922 QAction *pActionChooseHostDrive = pMenu->addAction(UIMedium(comMedium, enmMediumType).name(), pListener, pszSlotName);
1923 pActionChooseHostDrive->setCheckable(true);
1924 pActionChooseHostDrive->setChecked(!comCurrentMedium.isNull() && comMedium.GetId() == uCurrentID);
1925 pActionChooseHostDrive->setData(QVariant::fromValue(UIMediumTarget(strControllerName,
1926 comCurrentAttachment.GetPort(),
1927 comCurrentAttachment.GetDevice(),
1928 enmMediumType,
1929 UIMediumTarget::UIMediumTargetType_WithID,
1930 comMedium.GetId().toString())));
1931 }
1932 }
1933
1934 /* Get recent-medium list: */
1935 QStringList recentMediumList;
1936 QStringList recentMediumListUsed;
1937 switch (enmMediumType)
1938 {
1939 case UIMediumDeviceType_HardDisk: recentMediumList = gEDataManager->recentListOfHardDrives(); break;
1940 case UIMediumDeviceType_DVD: recentMediumList = gEDataManager->recentListOfOpticalDisks(); break;
1941 case UIMediumDeviceType_Floppy: recentMediumList = gEDataManager->recentListOfFloppyDisks(); break;
1942 default: break;
1943 }
1944 /* Prepare choose-recent-medium actions: */
1945 foreach (const QString &strRecentMediumLocationBase, recentMediumList)
1946 {
1947 /* Confirm medium uniqueness: */
1948 if (recentMediumListUsed.contains(strRecentMediumLocationBase))
1949 continue;
1950 /* Mark medium as known: */
1951 recentMediumListUsed << strRecentMediumLocationBase;
1952 /* Convert separators to native: */
1953 const QString strRecentMediumLocation = QDir::toNativeSeparators(strRecentMediumLocationBase);
1954 /* Confirm medium presence: */
1955 if (!QFile::exists(strRecentMediumLocation))
1956 continue;
1957 /* Make sure recent-medium usage is unique: */
1958 bool fIsRecentMediumUsed = false;
1959 if (enmMediumType != UIMediumDeviceType_DVD)
1960 {
1961 foreach (const CMediumAttachment &otherAttachment, comAttachments)
1962 {
1963 if (otherAttachment != comCurrentAttachment)
1964 {
1965 const CMedium &comOtherMedium = otherAttachment.GetMedium();
1966 if (!comOtherMedium.isNull() && comOtherMedium.GetLocation() == strRecentMediumLocation)
1967 {
1968 fIsRecentMediumUsed = true;
1969 break;
1970 }
1971 }
1972 }
1973 }
1974 /* If recent-medium usage is unique: */
1975 if (!fIsRecentMediumUsed)
1976 {
1977 QAction *pActionChooseRecentMedium = pMenu->addAction(QFileInfo(strRecentMediumLocation).fileName(),
1978 pListener, pszSlotName);
1979 pActionChooseRecentMedium->setCheckable(true);
1980 pActionChooseRecentMedium->setChecked(!comCurrentMedium.isNull() && strRecentMediumLocation == strCurrentLocation);
1981 pActionChooseRecentMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName,
1982 comCurrentAttachment.GetPort(),
1983 comCurrentAttachment.GetDevice(),
1984 enmMediumType,
1985 UIMediumTarget::UIMediumTargetType_WithLocation,
1986 strRecentMediumLocation)));
1987 pActionChooseRecentMedium->setToolTip(strRecentMediumLocation);
1988 }
1989 }
1990
1991 /* Last action for optical/floppy attachments only: */
1992 if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy)
1993 {
1994 /* Insert separator: */
1995 pMenu->addSeparator();
1996
1997 /* Prepare unmount-current-medium action: */
1998 QAction *pActionUnmountMedium = pMenu->addAction(QString(), pListener, pszSlotName);
1999 pActionUnmountMedium->setEnabled(!comCurrentMedium.isNull());
2000 pActionUnmountMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
2001 comCurrentAttachment.GetDevice())));
2002 pActionUnmountMedium->setText(QApplication::translate("UIMachineSettingsStorage", "Remove disk from virtual drive"));
2003 if (enmMediumType == UIMediumDeviceType_DVD)
2004 pActionUnmountMedium->setIcon(UIIconPool::iconSet(":/cd_unmount_16px.png", ":/cd_unmount_disabled_16px.png"));
2005 else if (enmMediumType == UIMediumDeviceType_Floppy)
2006 pActionUnmountMedium->setIcon(UIIconPool::iconSet(":/fd_unmount_16px.png", ":/fd_unmount_disabled_16px.png"));
2007 }
2008}
2009
2010void UICommon::updateMachineStorage(const CMachine &comConstMachine, const UIMediumTarget &target, UIActionPool *pActionPool)
2011{
2012 /* Mount (by default): */
2013 bool fMount = true;
2014 /* Null medium (by default): */
2015 CMedium comMedium;
2016 /* With null ID (by default): */
2017 QUuid uActualID;
2018
2019 /* Current mount-target attributes: */
2020 const CStorageController comCurrentController = comConstMachine.GetStorageControllerByName(target.name);
2021 const KStorageBus enmCurrentStorageBus = comCurrentController.GetBus();
2022 const CMediumAttachment comCurrentAttachment = comConstMachine.GetMediumAttachment(target.name, target.port, target.device);
2023 const CMedium comCurrentMedium = comCurrentAttachment.GetMedium();
2024 const QUuid uCurrentID = comCurrentMedium.isNull() ? QUuid() : comCurrentMedium.GetId();
2025 const QString strCurrentLocation = comCurrentMedium.isNull() ? QString() : comCurrentMedium.GetLocation();
2026
2027 /* Which additional info do we have? */
2028 switch (target.type)
2029 {
2030 /* Do we have an exact ID or do we let the user open a medium? */
2031 case UIMediumTarget::UIMediumTargetType_WithID:
2032 case UIMediumTarget::UIMediumTargetType_WithFileDialog:
2033 case UIMediumTarget::UIMediumTargetType_CreateAdHocVISO:
2034 case UIMediumTarget::UIMediumTargetType_CreateFloppyDisk:
2035 {
2036 /* New mount-target attributes: */
2037 QUuid uNewID;
2038
2039 /* Invoke file-open dialog to choose medium ID: */
2040 if (target.mediumType != UIMediumDeviceType_Invalid && target.data.isNull())
2041 {
2042 /* Keyboard can be captured by machine-view.
2043 * So we should clear machine-view focus to let file-open dialog get it.
2044 * That way the keyboard will be released too.. */
2045 QWidget *pLastFocusedWidget = 0;
2046 if (QApplication::focusWidget())
2047 {
2048 pLastFocusedWidget = QApplication::focusWidget();
2049 pLastFocusedWidget->clearFocus();
2050 }
2051 /* Call for file-open dialog: */
2052 const QString strMachineFolder(QFileInfo(comConstMachine.GetSettingsFilePath()).absolutePath());
2053 QUuid uMediumID;
2054 if (target.type == UIMediumTarget::UIMediumTargetType_WithID)
2055 {
2056 int iDialogReturn = UIMediumSelector::openMediumSelectorDialog(windowManager().mainWindowShown(), target.mediumType,
2057 uCurrentID, uMediumID,
2058 strMachineFolder, comConstMachine.GetName(),
2059 comConstMachine.GetOSTypeId(), true /*fEnableCreate */,
2060 comConstMachine.GetId(), pActionPool);
2061 if (iDialogReturn == UIMediumSelector::ReturnCode_LeftEmpty &&
2062 (target.mediumType == UIMediumDeviceType_DVD || target.mediumType == UIMediumDeviceType_Floppy))
2063 fMount = false;
2064 }
2065 else if (target.type == UIMediumTarget::UIMediumTargetType_WithFileDialog)
2066 {
2067 uMediumID = openMediumWithFileOpenDialog(target.mediumType, windowManager().mainWindowShown(),
2068 strMachineFolder, false /* fUseLastFolder */);
2069 }
2070 else if(target.type == UIMediumTarget::UIMediumTargetType_CreateAdHocVISO)
2071 UIVisoCreatorDialog::createViso(pActionPool, windowManager().mainWindowShown(),
2072 strMachineFolder, comConstMachine.GetName());
2073
2074 else if(target.type == UIMediumTarget::UIMediumTargetType_CreateFloppyDisk)
2075 uMediumID = UIFDCreationDialog::createFloppyDisk(windowManager().mainWindowShown(), strMachineFolder, comConstMachine.GetName());
2076
2077 /* Return focus back: */
2078 if (pLastFocusedWidget)
2079 pLastFocusedWidget->setFocus();
2080 /* Accept new medium ID: */
2081 if (!uMediumID.isNull())
2082 uNewID = uMediumID;
2083 else
2084 /* Else just exit in case left empty is not chosen in medium selector dialog: */
2085 if (fMount)
2086 return;
2087 }
2088 /* Use medium ID which was passed: */
2089 else if (!target.data.isNull() && target.data != uCurrentID.toString())
2090 uNewID = QUuid(target.data);
2091
2092 /* Should we mount or unmount? */
2093 fMount = !uNewID.isNull();
2094
2095 /* Prepare target medium: */
2096 const UIMedium guiMedium = medium(uNewID);
2097 comMedium = guiMedium.medium();
2098 uActualID = fMount ? uNewID : uCurrentID;
2099 break;
2100 }
2101 /* Do we have a recent location? */
2102 case UIMediumTarget::UIMediumTargetType_WithLocation:
2103 {
2104 /* Open medium by location and get new medium ID if any: */
2105 const QUuid uNewID = openMedium(target.mediumType, target.data);
2106 /* Else just exit: */
2107 if (uNewID.isNull())
2108 return;
2109
2110 /* Should we mount or unmount? */
2111 fMount = uNewID != uCurrentID;
2112
2113 /* Prepare target medium: */
2114 const UIMedium guiMedium = fMount ? medium(uNewID) : UIMedium();
2115 comMedium = fMount ? guiMedium.medium() : CMedium();
2116 uActualID = fMount ? uNewID : uCurrentID;
2117 break;
2118 }
2119 }
2120
2121 /* Do not unmount hard-drives: */
2122 if (target.mediumType == UIMediumDeviceType_HardDisk && !fMount)
2123 return;
2124
2125 /* Get editable machine & session: */
2126 CMachine comMachine = comConstMachine;
2127 CSession comSession = tryToOpenSessionFor(comMachine);
2128
2129 /* Remount medium to the predefined port/device: */
2130 bool fWasMounted = false;
2131 /* Hard drive case: */
2132 if (target.mediumType == UIMediumDeviceType_HardDisk)
2133 {
2134 /* Detaching: */
2135 comMachine.DetachDevice(target.name, target.port, target.device);
2136 fWasMounted = comMachine.isOk();
2137 if (!fWasMounted)
2138 msgCenter().cannotDetachDevice(comMachine, UIMediumDeviceType_HardDisk, strCurrentLocation,
2139 StorageSlot(enmCurrentStorageBus, target.port, target.device));
2140 else
2141 {
2142 /* Attaching: */
2143 comMachine.AttachDevice(target.name, target.port, target.device, KDeviceType_HardDisk, comMedium);
2144 fWasMounted = comMachine.isOk();
2145 if (!fWasMounted)
2146 msgCenter().cannotAttachDevice(comMachine, UIMediumDeviceType_HardDisk, strCurrentLocation,
2147 StorageSlot(enmCurrentStorageBus, target.port, target.device));
2148 }
2149 }
2150 /* Optical/floppy drive case: */
2151 else
2152 {
2153 /* Remounting: */
2154 comMachine.MountMedium(target.name, target.port, target.device, comMedium, false /* force? */);
2155 fWasMounted = comMachine.isOk();
2156 if (!fWasMounted)
2157 {
2158 /* Ask for force remounting: */
2159 if (msgCenter().cannotRemountMedium(comMachine, medium(uActualID),
2160 fMount, true /* retry? */))
2161 {
2162 /* Force remounting: */
2163 comMachine.MountMedium(target.name, target.port, target.device, comMedium, true /* force? */);
2164 fWasMounted = comMachine.isOk();
2165 if (!fWasMounted)
2166 msgCenter().cannotRemountMedium(comMachine, medium(uActualID),
2167 fMount, false /* retry? */);
2168 }
2169 }
2170 }
2171
2172 /* Save settings: */
2173 if (fWasMounted)
2174 {
2175 comMachine.SaveSettings();
2176 if (!comMachine.isOk())
2177 msgCenter().cannotSaveMachineSettings(comMachine, windowManager().mainWindowShown());
2178 }
2179
2180 /* Close session to editable comMachine if necessary: */
2181 if (!comSession.isNull())
2182 comSession.UnlockMachine();
2183}
2184
2185QString UICommon::storageDetails(const CMedium &comMedium, bool fPredictDiff, bool fUseHtml /* = true */)
2186{
2187 /* Search for corresponding UI medium: */
2188 const QUuid uMediumID = comMedium.isNull() ? UIMedium::nullID() : comMedium.GetId();
2189 UIMedium guiMedium = medium(uMediumID);
2190 if (!comMedium.isNull() && guiMedium.isNull())
2191 {
2192 /* UI medium may be new and not among cached media, request enumeration: */
2193 enumerateMedia(CMediumVector() << comMedium);
2194
2195 /* Search for corresponding UI medium again: */
2196 guiMedium = medium(uMediumID);
2197 if (guiMedium.isNull())
2198 {
2199 /* Medium might be deleted already, return null string: */
2200 return QString();
2201 }
2202 }
2203
2204 /* For differencing hard-disk we have to request
2205 * enumeration of whole tree based in it's root item: */
2206 if ( comMedium.isNotNull()
2207 && comMedium.GetDeviceType() == KDeviceType_HardDisk)
2208 {
2209 /* Traverse through parents to root to catch it: */
2210 CMedium comRootMedium;
2211 CMedium comParentMedium = comMedium.GetParent();
2212 while (comParentMedium.isNotNull())
2213 {
2214 comRootMedium = comParentMedium;
2215 comParentMedium = comParentMedium.GetParent();
2216 }
2217 /* Enumerate root if it's found and wasn't cached: */
2218 if (comRootMedium.isNotNull())
2219 {
2220 const QUuid uRootId = comRootMedium.GetId();
2221 if (medium(uRootId).isNull())
2222 enumerateMedia(CMediumVector() << comRootMedium);
2223 }
2224 }
2225
2226 /* Return UI medium details: */
2227 return fUseHtml ? guiMedium.detailsHTML(true /* no diffs? */, fPredictDiff) :
2228 guiMedium.details(true /* no diffs? */, fPredictDiff);
2229}
2230
2231void UICommon::updateRecentlyUsedMediumListAndFolder(UIMediumDeviceType enmMediumType, QString strMediumLocation)
2232{
2233 /** Don't add the medium to extra data if its name is in exclude list, m_recentMediaExcludeList: */
2234 foreach (QString strExcludeName, m_recentMediaExcludeList)
2235 {
2236 if (strMediumLocation.contains(strExcludeName))
2237 return;
2238 }
2239
2240 /* Remember the path of the last chosen medium: */
2241 switch (enmMediumType)
2242 {
2243 case UIMediumDeviceType_HardDisk: gEDataManager->setRecentFolderForHardDrives(QFileInfo(strMediumLocation).absolutePath()); break;
2244 case UIMediumDeviceType_DVD: gEDataManager->setRecentFolderForOpticalDisks(QFileInfo(strMediumLocation).absolutePath()); break;
2245 case UIMediumDeviceType_Floppy: gEDataManager->setRecentFolderForFloppyDisks(QFileInfo(strMediumLocation).absolutePath()); break;
2246 default: break;
2247 }
2248
2249 /* Update recently used list: */
2250 QStringList recentMediumList;
2251 switch (enmMediumType)
2252 {
2253 case UIMediumDeviceType_HardDisk: recentMediumList = gEDataManager->recentListOfHardDrives(); break;
2254 case UIMediumDeviceType_DVD: recentMediumList = gEDataManager->recentListOfOpticalDisks(); break;
2255 case UIMediumDeviceType_Floppy: recentMediumList = gEDataManager->recentListOfFloppyDisks(); break;
2256 default: break;
2257 }
2258 if (recentMediumList.contains(strMediumLocation))
2259 recentMediumList.removeAll(strMediumLocation);
2260 recentMediumList.prepend(strMediumLocation);
2261 while(recentMediumList.size() > 5)
2262 recentMediumList.removeLast();
2263 switch (enmMediumType)
2264 {
2265 case UIMediumDeviceType_HardDisk: gEDataManager->setRecentListOfHardDrives(recentMediumList); break;
2266 case UIMediumDeviceType_DVD: gEDataManager->setRecentListOfOpticalDisks(recentMediumList); break;
2267 case UIMediumDeviceType_Floppy: gEDataManager->setRecentListOfFloppyDisks(recentMediumList); break;
2268 default: break;
2269 }
2270 emit sigRecentMediaListUpdated(enmMediumType);
2271}
2272
2273QString UICommon::defaultFolderPathForType(UIMediumDeviceType enmMediumType)
2274{
2275 QString strLastFolder;
2276 switch (enmMediumType)
2277 {
2278 case UIMediumDeviceType_HardDisk:
2279 strLastFolder = gEDataManager->recentFolderForHardDrives();
2280 if (strLastFolder.isEmpty())
2281 strLastFolder = gEDataManager->recentFolderForOpticalDisks();
2282 if (strLastFolder.isEmpty())
2283 strLastFolder = gEDataManager->recentFolderForFloppyDisks();
2284 break;
2285 case UIMediumDeviceType_DVD:
2286 strLastFolder = gEDataManager->recentFolderForOpticalDisks();
2287 if (strLastFolder.isEmpty())
2288 strLastFolder = gEDataManager->recentFolderForFloppyDisks();
2289 if (strLastFolder.isEmpty())
2290 strLastFolder = gEDataManager->recentFolderForHardDrives();
2291 break;
2292 case UIMediumDeviceType_Floppy:
2293 strLastFolder = gEDataManager->recentFolderForFloppyDisks();
2294 if (strLastFolder.isEmpty())
2295 strLastFolder = gEDataManager->recentFolderForOpticalDisks();
2296 if (strLastFolder.isEmpty())
2297 strLastFolder = gEDataManager->recentFolderForHardDrives();
2298 break;
2299 default:
2300 break;
2301 }
2302
2303 if (strLastFolder.isEmpty())
2304 return virtualBox().GetSystemProperties().GetDefaultMachineFolder();
2305
2306 return strLastFolder;
2307}
2308
2309/* static */
2310bool UICommon::acquireAmountOfImmutableImages(const CMachine &comMachine, ulong &cAmount)
2311{
2312 /* Acquire state: */
2313 ulong cAmountOfImmutableImages = 0;
2314 const KMachineState enmState = comMachine.GetState();
2315 bool fSuccess = comMachine.isOk();
2316 if (!fSuccess)
2317 UINotificationMessage::cannotAcquireMachineParameter(comMachine);
2318 else
2319 {
2320 /// @todo Who knows why 13 years ago this condition was added ..
2321 if (enmState == KMachineState_Paused)
2322 {
2323 const CMediumAttachmentVector comAttachments = comMachine.GetMediumAttachments();
2324 fSuccess = comMachine.isOk();
2325 if (!fSuccess)
2326 UINotificationMessage::cannotAcquireMachineParameter(comMachine);
2327 else
2328 {
2329 /* Calculate the amount of immutable attachments: */
2330 foreach (const CMediumAttachment &comAttachment, comAttachments)
2331 {
2332 /* Get the medium: */
2333 const CMedium comMedium = comAttachment.GetMedium();
2334 if ( comMedium.isNull() /* Null medium is valid case as well */
2335 || comMedium.GetParent().isNull() /* Null parent is valid case as well */)
2336 continue;
2337 /* Get the base medium: */
2338 const CMedium comBaseMedium = comMedium.GetBase();
2339 fSuccess = comMedium.isOk();
2340 if (!fSuccess)
2341 UINotificationMessage::cannotAcquireMediumParameter(comMedium);
2342 else
2343 {
2344 const KMediumType enmType = comBaseMedium.GetType();
2345 fSuccess = comBaseMedium.isOk();
2346 if (!fSuccess)
2347 UINotificationMessage::cannotAcquireMediumParameter(comBaseMedium);
2348 else if (enmType == KMediumType_Immutable)
2349 ++cAmountOfImmutableImages;
2350 }
2351 if (!fSuccess)
2352 break;
2353 }
2354 }
2355 }
2356 }
2357 if (fSuccess)
2358 cAmount = cAmountOfImmutableImages;
2359 return fSuccess;
2360}
2361
2362#ifdef RT_OS_LINUX
2363/* static */
2364void UICommon::checkForWrongUSBMounted()
2365{
2366 /* Make sure '/proc/mounts' exists and can be opened: */
2367 QFile file("/proc/mounts");
2368 if (!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text))
2369 return;
2370
2371 /* Fetch contents: */
2372 QStringList contents;
2373 for (;;)
2374 {
2375 QByteArray line = file.readLine();
2376 if (line.isEmpty())
2377 break;
2378 contents << line;
2379 }
2380 /* Grep contents for usbfs presence: */
2381 QStringList grep1(contents.filter("/sys/bus/usb/drivers"));
2382 QStringList grep2(grep1.filter("usbfs"));
2383 if (grep2.isEmpty())
2384 return;
2385
2386 /* Show corresponding warning: */
2387 msgCenter().warnAboutWrongUSBMounted();
2388}
2389#endif /* RT_OS_LINUX */
2390
2391/* static */
2392QString UICommon::usbDetails(const CUSBDevice &comDevice)
2393{
2394 QString strDetails;
2395 if (comDevice.isNull())
2396 strDetails = tr("Unknown device", "USB device details");
2397 else
2398 {
2399 QVector<QString> devInfoVector = comDevice.GetDeviceInfo();
2400 QString strManufacturer;
2401 QString strProduct;
2402
2403 if (devInfoVector.size() >= 1)
2404 strManufacturer = devInfoVector[0].trimmed();
2405 if (devInfoVector.size() >= 2)
2406 strProduct = devInfoVector[1].trimmed();
2407
2408 if (strManufacturer.isEmpty() && strProduct.isEmpty())
2409 {
2410 strDetails =
2411 tr("Unknown device %1:%2", "USB device details")
2412 .arg(QString::number(comDevice.GetVendorId(), 16).toUpper().rightJustified(4, '0'))
2413 .arg(QString::number(comDevice.GetProductId(), 16).toUpper().rightJustified(4, '0'));
2414 }
2415 else
2416 {
2417 if (strProduct.toUpper().startsWith(strManufacturer.toUpper()))
2418 strDetails = strProduct;
2419 else
2420 strDetails = strManufacturer + " " + strProduct;
2421 }
2422 ushort iRev = comDevice.GetRevision();
2423 if (iRev != 0)
2424 {
2425 strDetails += " [";
2426 strDetails += QString::number(iRev, 16).toUpper().rightJustified(4, '0');
2427 strDetails += "]";
2428 }
2429 }
2430
2431 return strDetails.trimmed();
2432}
2433
2434/* static */
2435QString UICommon::usbToolTip(const CUSBDevice &comDevice)
2436{
2437 QString strTip =
2438 tr("<nobr>Vendor ID: %1</nobr><br>"
2439 "<nobr>Product ID: %2</nobr><br>"
2440 "<nobr>Revision: %3</nobr>", "USB device tooltip")
2441 .arg(QString::number(comDevice.GetVendorId(), 16).toUpper().rightJustified(4, '0'))
2442 .arg(QString::number(comDevice.GetProductId(), 16).toUpper().rightJustified(4, '0'))
2443 .arg(QString::number(comDevice.GetRevision(), 16).toUpper().rightJustified(4, '0'));
2444
2445 const QString strSerial = comDevice.GetSerialNumber();
2446 if (!strSerial.isEmpty())
2447 strTip += QString(tr("<br><nobr>Serial No. %1</nobr>", "USB device tooltip"))
2448 .arg(strSerial);
2449
2450 /* Add the state field if it's a host USB device: */
2451 CHostUSBDevice hostDev(comDevice);
2452 if (!hostDev.isNull())
2453 {
2454 strTip += QString(tr("<br><nobr>State: %1</nobr>", "USB device tooltip"))
2455 .arg(gpConverter->toString(hostDev.GetState()));
2456 }
2457
2458 return strTip;
2459}
2460
2461/* static */
2462QString UICommon::usbToolTip(const CUSBDeviceFilter &comFilter)
2463{
2464 QString strTip;
2465
2466 const QString strVendorId = comFilter.GetVendorId();
2467 if (!strVendorId.isEmpty())
2468 strTip += tr("<nobr>Vendor ID: %1</nobr>", "USB filter tooltip")
2469 .arg(strVendorId);
2470
2471 const QString strProductId = comFilter.GetProductId();
2472 if (!strProductId.isEmpty())
2473 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Product ID: %2</nobr>", "USB filter tooltip")
2474 .arg(strProductId);
2475
2476 const QString strRevision = comFilter.GetRevision();
2477 if (!strRevision.isEmpty())
2478 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Revision: %3</nobr>", "USB filter tooltip")
2479 .arg(strRevision);
2480
2481 const QString strProduct = comFilter.GetProduct();
2482 if (!strProduct.isEmpty())
2483 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Product: %4</nobr>", "USB filter tooltip")
2484 .arg(strProduct);
2485
2486 const QString strManufacturer = comFilter.GetManufacturer();
2487 if (!strManufacturer.isEmpty())
2488 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Manufacturer: %5</nobr>", "USB filter tooltip")
2489 .arg(strManufacturer);
2490
2491 const QString strSerial = comFilter.GetSerialNumber();
2492 if (!strSerial.isEmpty())
2493 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Serial No.: %1</nobr>", "USB filter tooltip")
2494 .arg(strSerial);
2495
2496 const QString strPort = comFilter.GetPort();
2497 if (!strPort.isEmpty())
2498 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Port: %1</nobr>", "USB filter tooltip")
2499 .arg(strPort);
2500
2501 /* Add the state field if it's a host USB device: */
2502 CHostUSBDevice hostDev(comFilter);
2503 if (!hostDev.isNull())
2504 {
2505 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>State: %1</nobr>", "USB filter tooltip")
2506 .arg(gpConverter->toString(hostDev.GetState()));
2507 }
2508
2509 return strTip;
2510}
2511
2512/* static */
2513QString UICommon::usbToolTip(const CHostVideoInputDevice &comWebcam)
2514{
2515 QStringList records;
2516
2517 const QString strName = comWebcam.GetName();
2518 if (!strName.isEmpty())
2519 records << strName;
2520
2521 const QString strPath = comWebcam.GetPath();
2522 if (!strPath.isEmpty())
2523 records << strPath;
2524
2525 return records.join("<br>");
2526}
2527
2528int UICommon::supportedRecordingFeatures() const
2529{
2530 int iSupportedFlag = 0;
2531 CSystemProperties comProperties = virtualBox().GetSystemProperties();
2532 foreach (const KRecordingFeature &enmFeature, comProperties.GetSupportedRecordingFeatures())
2533 iSupportedFlag |= enmFeature;
2534 return iSupportedFlag;
2535}
2536
2537/* static */
2538QString UICommon::helpFile()
2539{
2540 const QString strName = "UserManual";
2541 const QString strSuffix = "qhc";
2542
2543 /* Where are the docs located? */
2544 char szDocsPath[RTPATH_MAX];
2545 int rc = RTPathAppDocs(szDocsPath, sizeof(szDocsPath));
2546 AssertRC(rc);
2547
2548 /* Make sure that the language is in two letter code.
2549 * Note: if languageId() returns an empty string lang.name() will
2550 * return "C" which is an valid language code. */
2551 QLocale lang(UITranslator::languageId());
2552
2553 /* Construct the path and the filename: */
2554 QString strManual = QString("%1/%2_%3.%4").arg(szDocsPath)
2555 .arg(strName)
2556 .arg(lang.name())
2557 .arg(strSuffix);
2558
2559 /* Check if a help file with that name exists: */
2560 QFileInfo fi(strManual);
2561 if (fi.exists())
2562 return strManual;
2563
2564 /* Fall back to the standard: */
2565 strManual = QString("%1/%2.%4").arg(szDocsPath)
2566 .arg(strName)
2567 .arg(strSuffix);
2568 return strManual;
2569}
2570
2571/* static */
2572QString UICommon::documentsPath()
2573{
2574 QString strPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
2575 QDir dir(strPath);
2576 if (dir.exists())
2577 return QDir::cleanPath(dir.canonicalPath());
2578 else
2579 {
2580 dir.setPath(QDir::homePath() + "/Documents");
2581 if (dir.exists())
2582 return QDir::cleanPath(dir.canonicalPath());
2583 else
2584 return QDir::homePath();
2585 }
2586}
2587
2588/* static */
2589bool UICommon::hasAllowedExtension(const QString &strFileName, const QStringList &extensions)
2590{
2591 foreach (const QString &strExtension, extensions)
2592 if (strFileName.endsWith(strExtension, Qt::CaseInsensitive))
2593 return true;
2594 return false;
2595}
2596
2597/* static */
2598QString UICommon::findUniqueFileName(const QString &strFullFolderPath, const QString &strBaseFileName)
2599{
2600 QDir folder(strFullFolderPath);
2601 if (!folder.exists())
2602 return strBaseFileName;
2603 QFileInfoList folderContent = folder.entryInfoList();
2604 QSet<QString> fileNameSet;
2605 foreach (const QFileInfo &fileInfo, folderContent)
2606 {
2607 /* Remove the extension : */
2608 fileNameSet.insert(fileInfo.completeBaseName());
2609 }
2610 int iSuffix = 0;
2611 QString strNewName(strBaseFileName);
2612 while (fileNameSet.contains(strNewName))
2613 {
2614 strNewName = strBaseFileName + QString("_") + QString::number(++iSuffix);
2615 }
2616 return strNewName;
2617}
2618
2619/* static */
2620void UICommon::setMinimumWidthAccordingSymbolCount(QSpinBox *pSpinBox, int cCount)
2621{
2622 /* Shame on Qt it hasn't stuff for tuning
2623 * widget size suitable for reflecting content of desired size.
2624 * For example QLineEdit, QSpinBox and similar widgets should have a methods
2625 * to strict the minimum width to reflect at least [n] symbols. */
2626
2627 /* Load options: */
2628 QStyleOptionSpinBox option;
2629 option.initFrom(pSpinBox);
2630
2631 /* Acquire edit-field rectangle: */
2632 QRect rect = pSpinBox->style()->subControlRect(QStyle::CC_SpinBox,
2633 &option,
2634 QStyle::SC_SpinBoxEditField,
2635 pSpinBox);
2636
2637 /* Calculate minimum-width magic: */
2638 const int iSpinBoxWidth = pSpinBox->width();
2639 const int iSpinBoxEditFieldWidth = rect.width();
2640 const int iSpinBoxDelta = qMax(0, iSpinBoxWidth - iSpinBoxEditFieldWidth);
2641 QFontMetrics metrics(pSpinBox->font(), pSpinBox);
2642 const QString strDummy(cCount, '0');
2643#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
2644 const int iTextWidth = metrics.horizontalAdvance(strDummy);
2645#else
2646 const int iTextWidth = metrics.width(strDummy);
2647#endif
2648
2649 /* Tune spin-box minimum-width: */
2650 pSpinBox->setMinimumWidth(iTextWidth + iSpinBoxDelta);
2651}
2652
2653#ifdef VBOX_WITH_3D_ACCELERATION
2654/* static */
2655bool UICommon::isWddmCompatibleOsType(const QString &strGuestOSTypeId)
2656{
2657 return strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("WindowsVista"))
2658 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows7"))
2659 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows8"))
2660 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows81"))
2661 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows10"))
2662 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows11"))
2663 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows2008"))
2664 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows2012"))
2665 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows2016"))
2666 || strGuestOSTypeId.startsWith(GUEST_OS_ID_STR_PARTIAL("Windows2019"));
2667}
2668#endif /* VBOX_WITH_3D_ACCELERATION */
2669
2670/* static */
2671quint64 UICommon::requiredVideoMemory(const QString &strGuestOSTypeId, int cMonitors /* = 1 */)
2672{
2673 /* We create a list of the size of all available host monitors. This list
2674 * is sorted by value and by starting with the biggest one, we calculate
2675 * the memory requirements for every guest screen. This is of course not
2676 * correct, but as we can't predict on which host screens the user will
2677 * open the guest windows, this is the best assumption we can do, cause it
2678 * is the worst case. */
2679 const int cHostScreens = UIDesktopWidgetWatchdog::screenCount();
2680 QVector<int> screenSize(qMax(cMonitors, cHostScreens), 0);
2681 for (int i = 0; i < cHostScreens; ++i)
2682 {
2683 QRect r = gpDesktop->screenGeometry(i);
2684 screenSize[i] = r.width() * r.height();
2685 }
2686 /* Now sort the vector: */
2687 std::sort(screenSize.begin(), screenSize.end(), std::greater<int>());
2688 /* For the case that there are more guest screens configured then host
2689 * screens available, replace all zeros with the greatest value in the
2690 * vector. */
2691 for (int i = 0; i < screenSize.size(); ++i)
2692 if (screenSize.at(i) == 0)
2693 screenSize.replace(i, screenSize.at(0));
2694
2695 quint64 uNeedBits = 0;
2696 for (int i = 0; i < cMonitors; ++i)
2697 {
2698 /* Calculate summary required memory amount in bits: */
2699 uNeedBits += (screenSize.at(i) * /* with x height */
2700 32 + /* we will take the maximum possible bpp for now */
2701 8 * _1M) + /* current cache per screen - may be changed in future */
2702 8 * 4096; /* adapter info */
2703 }
2704 /* Translate value into megabytes with rounding to highest side: */
2705 quint64 uNeedMBytes = uNeedBits % (8 * _1M)
2706 ? uNeedBits / (8 * _1M) + 1
2707 : uNeedBits / (8 * _1M) /* convert to megabytes */;
2708
2709 if (strGuestOSTypeId.startsWith("Windows"))
2710 {
2711 /* Windows guests need offscreen VRAM too for graphics acceleration features: */
2712#ifdef VBOX_WITH_3D_ACCELERATION
2713 if (isWddmCompatibleOsType(strGuestOSTypeId))
2714 {
2715 /* WDDM mode, there are two surfaces for each screen: shadow & primary: */
2716 uNeedMBytes *= 3;
2717 }
2718 else
2719#endif /* VBOX_WITH_3D_ACCELERATION */
2720 {
2721 uNeedMBytes *= 2;
2722 }
2723 }
2724
2725 return uNeedMBytes * _1M;
2726}
2727
2728KGraphicsControllerType UICommon::getRecommendedGraphicsController(const QString &strGuestOSTypeId) const
2729{
2730 return m_pGuestOSTypeManager
2731 ? m_pGuestOSTypeManager->getRecommendedGraphicsController(strGuestOSTypeId)
2732 : KGraphicsControllerType_Null;
2733}
2734
2735/* static */
2736void UICommon::setHelpKeyword(QObject *pObject, const QString &strHelpKeyword)
2737{
2738 if (pObject)
2739 pObject->setProperty("helpkeyword", strHelpKeyword);
2740}
2741
2742/* static */
2743QString UICommon::helpKeyword(const QObject *pObject)
2744{
2745 if (!pObject)
2746 return QString();
2747 return pObject->property("helpkeyword").toString();
2748}
2749
2750bool UICommon::isExtentionPackInstalled() const
2751{
2752 const CExtPackManager comEPManager = virtualBox().GetExtensionPackManager();
2753
2754 if (!virtualBox().isOk())
2755 return false;
2756
2757 const QVector<CExtPack> extensionPacks = comEPManager.GetInstalledExtPacks();
2758 if (!comEPManager.isOk())
2759 return false;
2760 foreach (const CExtPack &comExtensionPack, extensionPacks)
2761 {
2762 if (!comExtensionPack.isOk())
2763 continue;
2764 bool fUsable = comExtensionPack.GetUsable();
2765 if (comExtensionPack.isOk() && fUsable)
2766 return true;
2767 }
2768 return false;
2769}
2770
2771bool UICommon::openURL(const QString &strUrl) const
2772{
2773 /** Service event. */
2774 class ServiceEvent : public QEvent
2775 {
2776 public:
2777
2778 /** Constructs service event on th basis of passed @a fResult. */
2779 ServiceEvent(bool fResult)
2780 : QEvent(QEvent::User)
2781 , m_fResult(fResult)
2782 {}
2783
2784 /** Returns the result which event brings. */
2785 bool result() const { return m_fResult; }
2786
2787 private:
2788
2789 /** Holds the result which event brings. */
2790 bool m_fResult;
2791 };
2792
2793 /** Service client object. */
2794 class ServiceClient : public QEventLoop
2795 {
2796 public:
2797
2798 /** Constructs service client on the basis of passed @a fResult. */
2799 ServiceClient()
2800 : m_fResult(false)
2801 {}
2802
2803 /** Returns the result which event brings. */
2804 bool result() const { return m_fResult; }
2805
2806 private:
2807
2808 /** Handles any Qt @a pEvent. */
2809 bool event(QEvent *pEvent)
2810 {
2811 /* Handle service event: */
2812 if (pEvent->type() == QEvent::User)
2813 {
2814 ServiceEvent *pServiceEvent = static_cast<ServiceEvent*>(pEvent);
2815 m_fResult = pServiceEvent->result();
2816 pServiceEvent->accept();
2817 quit();
2818 return true;
2819 }
2820 return false;
2821 }
2822
2823 bool m_fResult;
2824 };
2825
2826 /** Service server object. */
2827 class ServiceServer : public QThread
2828 {
2829 public:
2830
2831 /** Constructs service server on the basis of passed @a client and @a strUrl. */
2832 ServiceServer(ServiceClient &client, const QString &strUrl)
2833 : m_client(client), m_strUrl(strUrl) {}
2834
2835 private:
2836
2837 /** Executes thread task. */
2838 void run()
2839 {
2840 QApplication::postEvent(&m_client, new ServiceEvent(QDesktopServices::openUrl(m_strUrl)));
2841 }
2842
2843 /** Holds the client reference. */
2844 ServiceClient &m_client;
2845 /** Holds the URL to be processed. */
2846 const QString &m_strUrl;
2847 };
2848
2849 /* Create client & server: */
2850 ServiceClient client;
2851 ServiceServer server(client, strUrl);
2852 server.start();
2853 client.exec();
2854 server.wait();
2855
2856 /* Acquire client result: */
2857 bool fResult = client.result();
2858 if (!fResult)
2859 UINotificationMessage::cannotOpenURL(strUrl);
2860
2861 return fResult;
2862}
2863
2864void UICommon::sltGUILanguageChange(QString strLanguage)
2865{
2866 /* Make sure medium-enumeration is not in progress! */
2867 AssertReturnVoid(!isMediumEnumerationInProgress());
2868 /* Load passed language: */
2869 UITranslator::loadLanguage(strLanguage);
2870}
2871
2872void UICommon::sltHandleMediumCreated(const CMedium &comMedium)
2873{
2874 /* Acquire device type: */
2875 const KDeviceType enmDeviceType = comMedium.GetDeviceType();
2876 if (!comMedium.isOk())
2877 UINotificationMessage::cannotAcquireMediumParameter(comMedium);
2878 else
2879 {
2880 /* Convert to medium type: */
2881 const UIMediumDeviceType enmMediumType = mediumTypeToLocal(enmDeviceType);
2882
2883 /* Make sure we cached created medium in GUI: */
2884 createMedium(UIMedium(comMedium, enmMediumType, KMediumState_Created));
2885 }
2886}
2887
2888void UICommon::sltHandleMachineCreated(const CMachine &comMachine)
2889{
2890 /* Register created machine. */
2891 CVirtualBox comVBox = virtualBox();
2892 comVBox.RegisterMachine(comMachine);
2893 if (!comVBox.isOk())
2894 UINotificationMessage::cannotRegisterMachine(comVBox, comMachine.GetName());
2895}
2896
2897void UICommon::sltHandleCloudMachineAdded(const QString &strProviderShortName,
2898 const QString &strProfileName,
2899 const CCloudMachine &comMachine)
2900{
2901 /* Make sure we cached added cloud VM in GUI: */
2902 notifyCloudMachineRegistered(strProviderShortName,
2903 strProfileName,
2904 comMachine);
2905}
2906
2907bool UICommon::eventFilter(QObject *pObject, QEvent *pEvent)
2908{
2909 /** @todo Just use the QIWithRetranslateUI3 template wrapper. */
2910
2911 if ( pEvent->type() == QEvent::LanguageChange
2912 && pObject->isWidgetType()
2913 && qobject_cast<QWidget*>(pObject)->isWindow())
2914 {
2915 /* Catch the language change event before any other widget gets it in
2916 * order to invalidate cached string resources (like the details view
2917 * templates) that may be used by other widgets. */
2918 QWidgetList list = QApplication::topLevelWidgets();
2919 if (list.first() == pObject)
2920 {
2921 /* Call this only once per every language change (see
2922 * QApplication::installTranslator() for details): */
2923 retranslateUi();
2924 }
2925 }
2926
2927 /* Handle application palette change event: */
2928 if ( pEvent->type() == QEvent::ApplicationPaletteChange
2929 && pObject == windowManager().mainWindowShown())
2930 {
2931#if defined(VBOX_WS_MAC)
2932 const bool fDarkMode = UICocoaApplication::instance()->isDarkMode();
2933#elif defined(VBOX_WS_WIN)
2934 const bool fDarkMode = isWindowsInDarkMode();
2935#else /* Linux, BSD, Solaris */
2936 const bool fDarkMode = isPaletteInDarkMode();
2937#endif /* Linux, BSD, Solaris */
2938 if (m_fDarkMode != fDarkMode)
2939 {
2940 m_fDarkMode = fDarkMode;
2941 loadColorTheme();
2942 emit sigThemeChange();
2943 }
2944 }
2945
2946 /* Call to base-class: */
2947 return QObject::eventFilter(pObject, pEvent);
2948}
2949
2950void UICommon::sltHandleFontScaleFactorChanged(int iFontScaleFactor)
2951{
2952 QFont appFont = qApp->font();
2953
2954 if (iOriginalFontPixelSize != -1)
2955 appFont.setPixelSize(iFontScaleFactor / 100.f * iOriginalFontPixelSize);
2956 else
2957 appFont.setPointSize(iFontScaleFactor / 100.f * iOriginalFontPointSize);
2958 qApp->setFont(appFont);
2959}
2960
2961void UICommon::retranslateUi()
2962{
2963 /* Re-enumerate uimedium since they contain some translations too: */
2964 if (m_fValid)
2965 refreshMedia();
2966
2967#ifdef VBOX_WS_NIX
2968 // WORKAROUND:
2969 // As X11 do not have functionality for providing human readable key names,
2970 // we keep a table of them, which must be updated when the language is changed.
2971 UINativeHotKey::retranslateKeyNames();
2972#endif
2973}
2974
2975#ifndef VBOX_GUI_WITH_CUSTOMIZATIONS1
2976void UICommon::sltHandleCommitDataRequest(QSessionManager &manager)
2977{
2978 LogRel(("GUI: UICommon: Commit data request...\n"));
2979
2980 /* Ask listener to commit data: */
2981 emit sigAskToCommitData();
2982# ifdef VBOX_WS_WIN
2983 m_fDataCommitted = true;
2984# endif
2985
2986 /* Depending on UI type: */
2987 switch (uiType())
2988 {
2989 /* For Runtime UI: */
2990 case UIType_RuntimeUI:
2991 {
2992 /* Thin clients will be able to shutdown properly,
2993 * but for fat clients: */
2994 if (!isSeparateProcess())
2995 {
2996# if defined(VBOX_WS_MAC) && defined(VBOX_IS_QT6_OR_LATER) /** @todo qt6: ... */
2997 Q_UNUSED(manager);
2998 /* This code prevents QWindowSystemInterface::handleApplicationTermination
2999 for running, so among other things QApplication::closeAllWindows isn't
3000 called and we're somehow stuck in a half closed down state. That said,
3001 just disabling this isn't sufficent, there we also have to accept() the
3002 QCloseEvent in UIMachineWindow. */
3003 /** @todo qt6: This isn't quite the right fix, I bet... I'm sure I haven't
3004 * quite understood all that's going on here. So, leaving this for
3005 * the real GUI experts to look into... :-) */
3006# else
3007 // WORKAROUND:
3008 // We can't save VM state in one go for fat clients, so we have to ask session manager to cancel shutdown.
3009 // To next major release this should be removed in any case, since there will be no fat clients after all.
3010 manager.cancel();
3011
3012# ifdef VBOX_WS_WIN
3013 // WORKAROUND:
3014 // In theory that's Qt5 who should allow us to provide canceling reason as well, but that functionality
3015 // seems to be missed in Windows platform plugin, so we are making that ourselves.
3016 NativeWindowSubsystem::ShutdownBlockReasonCreateAPI((HWND)windowManager().mainWindowShown()->winId(), L"VM is still running.");
3017# endif
3018# endif
3019 }
3020
3021 break;
3022 }
3023 default:
3024 break;
3025 }
3026}
3027#endif /* !VBOX_GUI_WITH_CUSTOMIZATIONS1 */
3028
3029void UICommon::sltHandleVBoxSVCAvailabilityChange(bool fAvailable)
3030{
3031 /* Make sure the VBoxSVC availability changed: */
3032 if (m_fVBoxSVCAvailable == fAvailable)
3033 return;
3034
3035 /* Cache the new VBoxSVC availability value: */
3036 m_fVBoxSVCAvailable = fAvailable;
3037
3038 /* If VBoxSVC is not available: */
3039 if (!m_fVBoxSVCAvailable)
3040 {
3041 /* Mark wrappers invalid: */
3042 m_fWrappersValid = false;
3043 /* Re-fetch corresponding CVirtualBox to restart VBoxSVC: */
3044 m_comVBox = m_comVBoxClient.GetVirtualBox();
3045 if (!m_comVBoxClient.isOk())
3046 {
3047 // The proper behavior would be to show the message and to exit the app, e.g.:
3048 // msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
3049 // return QApplication::quit();
3050 // But CVirtualBox is still NULL in current Main implementation,
3051 // and this call do not restart anything, so we are waiting
3052 // for subsequent event about VBoxSVC is available again.
3053 }
3054 }
3055 /* If VBoxSVC is available: */
3056 else
3057 {
3058 if (!m_fWrappersValid)
3059 {
3060 /* Re-fetch corresponding CVirtualBox: */
3061 m_comVBox = m_comVBoxClient.GetVirtualBox();
3062 if (!m_comVBoxClient.isOk())
3063 {
3064 msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
3065 return QApplication::quit();
3066 }
3067 /* Re-init wrappers: */
3068 comWrappersReinit();
3069
3070 /* For Selector UI: */
3071 if (uiType() == UIType_SelectorUI)
3072 {
3073 /* Recreate Main event listeners: */
3074 UIVirtualBoxEventHandler::destroy();
3075 UIVirtualBoxClientEventHandler::destroy();
3076 UIExtraDataManager::destroy();
3077 UIExtraDataManager::instance();
3078 UIVirtualBoxEventHandler::instance();
3079 UIVirtualBoxClientEventHandler::instance();
3080 /* Ask UIStarter to restart UI: */
3081 emit sigAskToRestartUI();
3082 }
3083 }
3084 }
3085
3086 /* Notify listeners about the VBoxSVC availability change: */
3087 emit sigVBoxSVCAvailabilityChange();
3088}
3089
3090#ifdef VBOX_WITH_DEBUGGER_GUI
3091
3092# define UICOMMON_DBG_CFG_VAR_FALSE (0)
3093# define UICOMMON_DBG_CFG_VAR_TRUE (1)
3094# define UICOMMON_DBG_CFG_VAR_MASK (1)
3095# define UICOMMON_DBG_CFG_VAR_CMD_LINE RT_BIT(3)
3096# define UICOMMON_DBG_CFG_VAR_DONE RT_BIT(4)
3097
3098void UICommon::initDebuggerVar(int *piDbgCfgVar, const char *pszEnvVar, const char *pszExtraDataName, bool fDefault)
3099{
3100 QString strEnvValue;
3101 char szEnvValue[256];
3102 int rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, szEnvValue, sizeof(szEnvValue), NULL);
3103 if (RT_SUCCESS(rc))
3104 {
3105 strEnvValue = QString::fromUtf8(&szEnvValue[0]).toLower().trimmed();
3106 if (strEnvValue.isEmpty())
3107 strEnvValue = "yes";
3108 }
3109 else if (rc != VERR_ENV_VAR_NOT_FOUND)
3110 strEnvValue = "veto";
3111
3112 QString strExtraValue = m_comVBox.GetExtraData(pszExtraDataName).toLower().trimmed();
3113 if (strExtraValue.isEmpty())
3114 strExtraValue = QString();
3115
3116 if ( strEnvValue.contains("veto") || strExtraValue.contains("veto"))
3117 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
3118 else if (strEnvValue.isNull() && strExtraValue.isNull())
3119 *piDbgCfgVar = fDefault ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE;
3120 else
3121 {
3122 QString *pStr = !strEnvValue.isEmpty() ? &strEnvValue : &strExtraValue;
3123 if ( pStr->startsWith("y") // yes
3124 || pStr->startsWith("e") // enabled
3125 || pStr->startsWith("t") // true
3126 || pStr->startsWith("on")
3127 || pStr->toLongLong() != 0)
3128 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_TRUE;
3129 else if ( pStr->startsWith("n") // o
3130 || pStr->startsWith("d") // disable
3131 || pStr->startsWith("f") // false
3132 || pStr->startsWith("off")
3133 || pStr->contains("veto") /* paranoia */
3134 || pStr->toLongLong() == 0)
3135 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_FALSE;
3136 else
3137 {
3138 LogFunc(("Ignoring unknown value '%s' for '%s'\n", pStr->toUtf8().constData(), pStr == &strEnvValue ? pszEnvVar : pszExtraDataName));
3139 *piDbgCfgVar = fDefault ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE;
3140 }
3141 }
3142}
3143
3144void UICommon::setDebuggerVar(int *piDbgCfgVar, bool fState)
3145{
3146 if (!(*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_DONE))
3147 *piDbgCfgVar = (fState ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE)
3148 | UICOMMON_DBG_CFG_VAR_CMD_LINE;
3149}
3150
3151bool UICommon::isDebuggerWorker(int *piDbgCfgVar, const char *pszExtraDataName) const
3152{
3153 if (!(*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_DONE))
3154 {
3155 const QString str = gEDataManager->debugFlagValue(pszExtraDataName);
3156 if (str.contains("veto"))
3157 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
3158 else if (str.isEmpty() || (*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_CMD_LINE))
3159 *piDbgCfgVar |= UICOMMON_DBG_CFG_VAR_DONE;
3160 else if ( str.startsWith("y") // yes
3161 || str.startsWith("e") // enabled
3162 || str.startsWith("t") // true
3163 || str.startsWith("on")
3164 || str.toLongLong() != 0)
3165 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_TRUE;
3166 else if ( str.startsWith("n") // no
3167 || str.startsWith("d") // disable
3168 || str.startsWith("f") // false
3169 || str.toLongLong() == 0)
3170 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
3171 else
3172 *piDbgCfgVar |= UICOMMON_DBG_CFG_VAR_DONE;
3173 }
3174
3175 return (*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_MASK) == UICOMMON_DBG_CFG_VAR_TRUE;
3176}
3177
3178#endif /* VBOX_WITH_DEBUGGER_GUI */
3179
3180void UICommon::comWrappersReinit()
3181{
3182 /* Re-fetch corresponding objects/values: */
3183 m_comHost = virtualBox().GetHost();
3184 m_strHomeFolder = virtualBox().GetHomeFolder();
3185
3186 /* Re-initialize guest OS type database: */
3187 if (m_pGuestOSTypeManager)
3188 m_pGuestOSTypeManager->reCacheGuestOSTypes();
3189
3190 /* Mark wrappers valid: */
3191 m_fWrappersValid = true;
3192}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette