VirtualBox

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

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

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

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

© 2023 Oracle
ContactPrivacy policyTerms of Use