VirtualBox

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

Last change on this file since 104158 was 104158, checked in by vboxsync, 6 months ago

FE/Qt: UICommon: Round font down-scaling up cause there can be too small values otherwise.

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

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