VirtualBox

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

Last change on this file since 100347 was 100094, checked in by vboxsync, 20 months ago

FE/Qt: bugref:9080. Some more refactoring.

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