VirtualBox

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

Last change on this file since 82781 was 82470, checked in by vboxsync, 4 years ago

FE/Qt: UICommon: NLS fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 169.0 KB
Line 
1/* $Id: UICommon.cpp 82470 2019-12-06 17:27:03Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UICommon class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QDesktopServices>
20#include <QDir>
21#include <QFileDialog>
22#include <QGraphicsWidget>
23#include <QLocale>
24#include <QMenu>
25#include <QMutex>
26#include <QPainter>
27#include <QProcess>
28#include <QSpinBox>
29#include <QStandardPaths>
30#include <QThread>
31#include <QTimer>
32#include <QToolButton>
33#include <QToolTip>
34#include <QTranslator>
35#include <QLibraryInfo>
36#include <QProgressDialog>
37#include <QSettings>
38#include <QStyleOptionSpinBox>
39#include <QSessionManager>
40#ifdef VBOX_WS_WIN
41# include <QEventLoop>
42#endif /* VBOX_WS_WIN */
43#ifdef VBOX_WS_X11
44# include <QScrollBar>
45# include <QTextBrowser>
46# include <QX11Info>
47#endif /* VBOX_WS_X11 */
48#ifdef VBOX_GUI_WITH_PIDFILE
49# include <QTextStream>
50#endif /* VBOX_GUI_WITH_PIDFILE */
51#ifdef VBOX_WS_X11
52# include <QScreen>
53#endif
54
55/* GUI includes: */
56#include "QIWithRestorableGeometry.h"
57#include "UICommon.h"
58#include "VBoxLicenseViewer.h"
59#include "UIMessageCenter.h"
60#include "UIPopupCenter.h"
61#include "QIMessageBox.h"
62#include "QIDialogButtonBox.h"
63#include "UIFDCreationDialog.h"
64#include "UIIconPool.h"
65#include "UIThreadPool.h"
66#include "UIShortcutPool.h"
67#include "UIExtraDataManager.h"
68#include "QIFileDialog.h"
69#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
70# include "UINetworkManager.h"
71# include "UIUpdateManager.h"
72#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
73#include "UIConverter.h"
74#include "UIMediumEnumerator.h"
75#include "UIMedium.h"
76#include "UIMediumSelector.h"
77#include "UIModalWindowManager.h"
78#include "UIIconPool.h"
79#include "UIVirtualBoxEventHandler.h"
80#include "UIDesktopWidgetWatchdog.h"
81#include "UIVisoCreator.h"
82#include "UIWizardNewVD.h"
83#ifdef VBOX_WS_X11
84# include "UIHostComboEditor.h"
85# include "VBoxX11Helper.h"
86#endif
87#ifdef VBOX_WS_MAC
88# include "VBoxUtils-darwin.h"
89# include "UIMachineWindowFullscreen.h"
90# include "UIMachineWindowSeamless.h"
91#endif /* VBOX_WS_MAC */
92
93/* COM includes: */
94#include "CAudioAdapter.h"
95#include "CBIOSSettings.h"
96#include "CConsole.h"
97#include "CExtPack.h"
98#include "CExtPackFile.h"
99#include "CExtPackManager.h"
100#include "CHostUSBDevice.h"
101#include "CHostVideoInputDevice.h"
102#include "CMachine.h"
103#include "CMediumAttachment.h"
104#include "CNetworkAdapter.h"
105#include "CSerialPort.h"
106#include "CSharedFolder.h"
107#include "CSnapshot.h"
108#include "CStorageController.h"
109#include "CSystemProperties.h"
110#include "CUSBController.h"
111#include "CUSBDevice.h"
112#include "CUSBDeviceFilter.h"
113#include "CUSBDeviceFilters.h"
114#include "CVRDEServer.h"
115
116/* Other VBox includes: */
117#include <iprt/asm.h>
118#include <iprt/ctype.h>
119#include <iprt/err.h>
120#include <iprt/env.h>
121#include <iprt/file.h>
122#include <iprt/getopt.h>
123#include <iprt/ldr.h>
124#include <iprt/param.h>
125#include <iprt/path.h>
126#include <iprt/system.h>
127#include <iprt/stream.h>
128#ifdef VBOX_WS_X11
129# include <iprt/mem.h>
130#endif /* VBOX_WS_X11 */
131#include <VBox/sup.h>
132#include <VBox/VBoxOGL.h>
133#include <VBox/vd.h>
134#include <VBox/com/Guid.h>
135
136/* VirtualBox interface declarations: */
137#include <VBox/com/VirtualBox.h>
138
139/* External includes: */
140#ifdef VBOX_WS_WIN
141# include <iprt/win/shlobj.h>
142#endif
143#ifdef VBOX_WS_X11
144# include <xcb/xcb.h>
145#endif
146
147/* External includes: */
148# include <math.h>
149#ifdef VBOX_WS_MAC
150# include <sys/utsname.h>
151#endif /* VBOX_WS_MAC */
152#ifdef VBOX_WS_X11
153// WORKAROUND:
154// typedef CARD8 BOOL in Xmd.h conflicts with #define BOOL PRBool
155// in COMDefs.h. A better fix would be to isolate X11-specific
156// stuff by placing XX* helpers below to a separate source file.
157# undef BOOL
158# include <X11/X.h>
159# include <X11/Xmd.h>
160# include <X11/Xlib.h>
161# include <X11/Xatom.h>
162# include <X11/Xutil.h>
163# include <X11/extensions/Xinerama.h>
164# define BOOL PRBool
165#endif /* VBOX_WS_X11 */
166
167//#define VBOX_WITH_FULL_DETAILS_REPORT /* hidden for now */
168
169/* Namespaces: */
170using namespace UIExtraDataDefs;
171using namespace UIMediumDefs;
172
173
174/** QTranslator subclass for VBox needs. */
175class VBoxTranslator : public QTranslator
176{
177public:
178
179 /** Constructs translator passing @a pParent to the base-class. */
180 VBoxTranslator(QObject *pParent = 0)
181 : QTranslator(pParent)
182 {}
183
184 /** Loads language file with gained @a strFileName. */
185 bool loadFile(const QString &strFileName)
186 {
187 QFile file(strFileName);
188 if (!file.open(QIODevice::ReadOnly))
189 return false;
190 m_data = file.readAll();
191 return load((uchar*)m_data.data(), m_data.size());
192 }
193
194private:
195
196 /** Holds the loaded data. */
197 QByteArray m_data;
198};
199
200/** Holds the static #VBoxTranslator instance. */
201static VBoxTranslator *sTranslator = 0;
202
203
204/** Port config cache. */
205struct PortConfig
206{
207 const char *name;
208 const ulong IRQ;
209 const ulong IOBase;
210};
211
212/** Known port config COM ports. */
213static const PortConfig kComKnownPorts[] =
214{
215 { "COM1", 4, 0x3F8 },
216 { "COM2", 3, 0x2F8 },
217 { "COM3", 4, 0x3E8 },
218 { "COM4", 3, 0x2E8 },
219 /* Must not contain an element with IRQ=0 and IOBase=0 used to cause
220 * toCOMPortName() to return the "User-defined" string for these values. */
221};
222
223/** Known port config LPT ports. */
224static const PortConfig kLptKnownPorts[] =
225{
226 { "LPT1", 7, 0x378 },
227 { "LPT2", 5, 0x278 },
228 { "LPT1", 2, 0x3BC },
229 /* Must not contain an element with IRQ=0 and IOBase=0 used to cause
230 * toLPTPortName() to return the "User-defined" string for these values. */
231};
232
233
234/* static */
235UICommon *UICommon::s_pInstance = 0;
236bool UICommon::s_fCleaningUp = false;
237QString UICommon::s_strLoadedLanguageId = vboxBuiltInLanguageName();
238QString UICommon::s_strUserDefinedPortName = QString();
239
240/* static */
241void UICommon::create(UIType enmType)
242{
243 /* Make sure instance is NOT created yet: */
244 if (s_pInstance)
245 {
246 AssertMsgFailed(("UICommon instance is already created!"));
247 return;
248 }
249
250 /* Create instance: */
251 new UICommon(enmType);
252 /* Prepare instance: */
253 s_pInstance->prepare();
254}
255
256/* static */
257void UICommon::destroy()
258{
259 /* Make sure instance is NOT destroyed yet: */
260 if (!s_pInstance)
261 {
262 AssertMsgFailed(("UICommon instance is already destroyed!"));
263 return;
264 }
265
266 /* Cleanup instance:
267 * 1. By default, automatically on QApplication::aboutToQuit() signal.
268 * 2. But if QApplication was not started at all and we perform
269 * early shutdown, we should do cleanup ourselves. */
270 if (s_pInstance->isValid())
271 s_pInstance->cleanup();
272 /* Destroy instance: */
273 delete s_pInstance;
274}
275
276UICommon::UICommon(UIType enmType)
277 : m_enmType(enmType)
278 , m_fValid(false)
279#ifdef VBOX_WS_MAC
280 , m_enmMacOSVersion(MacOSXRelease_Old)
281#endif
282#ifdef VBOX_WS_X11
283 , m_enmWindowManagerType(X11WMType_Unknown)
284 , m_fCompositingManagerRunning(false)
285#endif
286 , m_fSeparateProcess(false)
287 , m_fShowStartVMErrors(true)
288#if defined(DEBUG_bird)
289 , m_fAgressiveCaching(false)
290#else
291 , m_fAgressiveCaching(true)
292#endif
293 , m_fRestoreCurrentSnapshot(false)
294 , m_fDisablePatm(false)
295 , m_fDisableCsam(false)
296 , m_fRecompileSupervisor(false)
297 , m_fRecompileUser(false)
298 , m_fExecuteAllInIem(false)
299 , m_uWarpPct(100)
300#ifdef VBOX_WITH_DEBUGGER_GUI
301 , m_fDbgEnabled(0)
302 , m_fDbgAutoShow(0)
303 , m_fDbgAutoShowCommandLine(0)
304 , m_fDbgAutoShowStatistics(0)
305 , m_hVBoxDbg(NIL_RTLDRMOD)
306 , m_enmLaunchRunning(LaunchRunning_Default)
307#endif
308 , m_fSettingsPwSet(false)
309 , m_fWrappersValid(false)
310 , m_fVBoxSVCAvailable(true)
311 , m_i3DAvailable(-1)
312 , m_pThreadPool(0)
313 , m_pIconPool(0)
314 , m_pMediumEnumerator(0)
315{
316 /* Assign instance: */
317 s_pInstance = this;
318}
319
320UICommon::~UICommon()
321{
322 /* Unassign instance: */
323 s_pInstance = 0;
324}
325
326/* static */
327QString UICommon::qtRTVersionString()
328{
329 return QString::fromLatin1(qVersion());
330}
331
332/* static */
333uint UICommon::qtRTVersion()
334{
335 const QString strVersionRT = UICommon::qtRTVersionString();
336 return (strVersionRT.section('.', 0, 0).toInt() << 16) +
337 (strVersionRT.section('.', 1, 1).toInt() << 8) +
338 strVersionRT.section('.', 2, 2).toInt();
339}
340
341/* static */
342uint UICommon::qtRTMajorVersion()
343{
344 return UICommon::qtRTVersionString().section('.', 0, 0).toInt();
345}
346
347/* static */
348uint UICommon::qtRTMinorVersion()
349{
350 return UICommon::qtRTVersionString().section('.', 1, 1).toInt();
351}
352
353/* static */
354uint UICommon::qtRTRevisionNumber()
355{
356 return UICommon::qtRTVersionString().section('.', 2, 2).toInt();
357}
358
359/* static */
360QString UICommon::qtCTVersionString()
361{
362 return QString::fromLatin1(QT_VERSION_STR);
363}
364
365/* static */
366uint UICommon::qtCTVersion()
367{
368 const QString strVersionCompiled = UICommon::qtCTVersionString();
369 return (strVersionCompiled.section('.', 0, 0).toInt() << 16) +
370 (strVersionCompiled.section('.', 1, 1).toInt() << 8) +
371 strVersionCompiled.section('.', 2, 2).toInt();
372}
373
374QString UICommon::vboxVersionString() const
375{
376 return m_comVBox.GetVersion();
377}
378
379QString UICommon::vboxVersionStringNormalized() const
380{
381 return m_comVBox.GetVersionNormalized();
382}
383
384bool UICommon::isBeta() const
385{
386 return vboxVersionString().contains("BETA", Qt::CaseInsensitive);
387}
388
389#ifdef VBOX_WS_MAC
390/* static */
391MacOSXRelease UICommon::determineOsRelease()
392{
393 /* Prepare 'utsname' struct: */
394 utsname info;
395 if (uname(&info) != -1)
396 {
397 /* Compose map of known releases: */
398 QMap<int, MacOSXRelease> release;
399 release[10] = MacOSXRelease_SnowLeopard;
400 release[11] = MacOSXRelease_Lion;
401 release[12] = MacOSXRelease_MountainLion;
402 release[13] = MacOSXRelease_Mavericks;
403 release[14] = MacOSXRelease_Yosemite;
404 release[15] = MacOSXRelease_ElCapitan;
405
406 /* Cut the major release index of the string we have, s.a. 'man uname': */
407 const int iRelease = QString(info.release).section('.', 0, 0).toInt();
408
409 /* Return release if determined, return 'New' if version more recent than latest, return 'Old' otherwise: */
410 return release.value(iRelease, iRelease > release.keys().last() ? MacOSXRelease_New : MacOSXRelease_Old);
411 }
412 /* Return 'Old' by default: */
413 return MacOSXRelease_Old;
414}
415#endif /* VBOX_WS_MAC */
416
417bool UICommon::brandingIsActive(bool fForce /* = false */)
418{
419 if (fForce)
420 return true;
421
422 if (m_strBrandingConfigFilePath.isEmpty())
423 {
424 m_strBrandingConfigFilePath = QDir(QApplication::applicationDirPath()).absolutePath();
425 m_strBrandingConfigFilePath += "/custom/custom.ini";
426 }
427
428 return QFile::exists(m_strBrandingConfigFilePath);
429}
430
431QString UICommon::brandingGetKey(QString strKey)
432{
433 QSettings settings(m_strBrandingConfigFilePath, QSettings::IniFormat);
434 return settings.value(QString("%1").arg(strKey)).toString();
435}
436
437/*static */
438QString UICommon::findUniqueFileName(const QString &strFullFolderPath, const QString &strBaseFileName)
439{
440 QDir folder(strFullFolderPath);
441 if (!folder.exists())
442 return strBaseFileName;
443 QFileInfoList folderContent = folder.entryInfoList();
444 QSet<QString> fileNameSet;
445 foreach (const QFileInfo &fileInfo, folderContent)
446 {
447 /* Remove the extension : */
448 fileNameSet.insert(fileInfo.completeBaseName());
449 }
450 int iSuffix = 0;
451 QString strNewName(strBaseFileName);
452 while (fileNameSet.contains(strNewName))
453 {
454 strNewName = strBaseFileName + QString("_") + QString::number(++iSuffix);
455 }
456 return strNewName;
457}
458
459/* static */
460bool UICommon::hasAllowedExtension(const QString &strExt, const QStringList &extList)
461{
462 for (int i = 0; i < extList.size(); ++i)
463 if (strExt.endsWith(extList.at(i), Qt::CaseInsensitive))
464 return true;
465 return false;
466}
467
468bool UICommon::processArgs()
469{
470 /* Among those arguments: */
471 bool fResult = false;
472 const QStringList args = qApp->arguments();
473
474 /* We are looking for a list of file URLs passed to the executable: */
475 QList<QUrl> listArgUrls;
476 for (int i = 1; i < args.size(); ++i)
477 {
478 /* But we break out after the first parameter, cause there
479 * could be parameters with arguments (e.g. --comment comment). */
480 if (args.at(i).startsWith("-"))
481 break;
482
483#ifdef VBOX_WS_MAC
484 const QString strArg = ::darwinResolveAlias(args.at(i));
485#else
486 const QString strArg = args.at(i);
487#endif
488
489 /* So if the argument file exists, we add it to URL list: */
490 if ( !strArg.isEmpty()
491 && QFile::exists(strArg))
492 listArgUrls << QUrl::fromLocalFile(QFileInfo(strArg).absoluteFilePath());
493 }
494
495 /* If there are file URLs: */
496 if (!listArgUrls.isEmpty())
497 {
498 /* We enumerate them and: */
499 for (int i = 0; i < listArgUrls.size(); ++i)
500 {
501 /* Check which of them has allowed VM extensions: */
502 const QUrl url = listArgUrls.at(i);
503 const QString strFile = url.toLocalFile();
504 if (UICommon::hasAllowedExtension(strFile, VBoxFileExts))
505 {
506 /* So that we could run existing VMs: */
507 CVirtualBox comVBox = virtualBox();
508 CMachine comMachine = comVBox.FindMachine(strFile);
509 if (!comMachine.isNull())
510 {
511 fResult = true;
512 launchMachine(comMachine);
513 /* And remove their URLs from the ULR list: */
514 listArgUrls.removeAll(url);
515 }
516 }
517 }
518 }
519
520 /* And if there are *still* URLs: */
521 if (!listArgUrls.isEmpty())
522 {
523 /* We store them, they will be handled later: */
524 m_listArgUrls = listArgUrls;
525 }
526
527 return fResult;
528}
529
530bool UICommon::argumentUrlsPresent() const
531{
532 return !m_listArgUrls.isEmpty();
533}
534
535QList<QUrl> UICommon::takeArgumentUrls()
536{
537 const QList<QUrl> result = m_listArgUrls;
538 m_listArgUrls.clear();
539 return result;
540}
541
542#ifdef VBOX_WITH_DEBUGGER_GUI
543
544bool UICommon::isDebuggerEnabled() const
545{
546 return isDebuggerWorker(&m_fDbgEnabled, GUI_Dbg_Enabled);
547}
548
549bool UICommon::isDebuggerAutoShowEnabled() const
550{
551 return isDebuggerWorker(&m_fDbgAutoShow, GUI_Dbg_AutoShow);
552}
553
554bool UICommon::isDebuggerAutoShowCommandLineEnabled() const
555{
556 return isDebuggerWorker(&m_fDbgAutoShowCommandLine, GUI_Dbg_AutoShow);
557}
558
559bool UICommon::isDebuggerAutoShowStatisticsEnabled() const
560{
561 return isDebuggerWorker(&m_fDbgAutoShowStatistics, GUI_Dbg_AutoShow);
562}
563
564#endif /* VBOX_WITH_DEBUGGER_GUI */
565
566bool UICommon::shouldStartPaused() const
567{
568#ifdef VBOX_WITH_DEBUGGER_GUI
569 return m_enmLaunchRunning == LaunchRunning_Default ? isDebuggerAutoShowEnabled() : m_enmLaunchRunning == LaunchRunning_No;
570#else
571 return false;
572#endif
573}
574
575#ifdef VBOX_GUI_WITH_PIDFILE
576
577void UICommon::createPidfile()
578{
579 if (!m_strPidFile.isEmpty())
580 {
581 const qint64 iPid = qApp->applicationPid();
582 QFile file(m_strPidFile);
583 if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
584 {
585 QTextStream out(&file);
586 out << iPid << endl;
587 }
588 else
589 LogRel(("Failed to create pid file %s\n", m_strPidFile.toUtf8().constData()));
590 }
591}
592
593void UICommon::deletePidfile()
594{
595 if ( !m_strPidFile.isEmpty()
596 && QFile::exists(m_strPidFile))
597 QFile::remove(m_strPidFile);
598}
599
600#endif /* VBOX_GUI_WITH_PIDFILE */
601
602/* static */
603QString UICommon::languageName()
604{
605 /* Returns "English" if no translation is installed
606 * or if the translation file is invalid. */
607 return QApplication::translate("@@@", "English",
608 "Native language name");
609}
610
611/* static */
612QString UICommon::languageCountry()
613{
614 /* Returns "--" if no translation is installed or if the translation file
615 * is invalid, or if the language is independent on the country. */
616 return QApplication::translate("@@@", "--",
617 "Native language country name "
618 "(empty if this language is for all countries)");
619}
620
621/* static */
622QString UICommon::languageNameEnglish()
623{
624 /* Returns "English" if no translation is installed
625 * or if the translation file is invalid. */
626 return QApplication::translate("@@@", "English",
627 "Language name, in English");
628}
629
630/* static */
631QString UICommon::languageCountryEnglish()
632{
633 /* Returns "--" if no translation is installed or if the translation file
634 * is invalid, or if the language is independent on the country. */
635 return QApplication::translate("@@@", "--",
636 "Language country name, in English "
637 "(empty if native country name is empty)");
638}
639
640/* static */
641QString UICommon::languageTranslators()
642{
643 /* Returns "Oracle Corporation" if no translation is installed or if the translation file
644 * is invalid, or if the translation is supplied by Oracle Corporation. */
645 return QApplication::translate("@@@", "Oracle Corporation",
646 "Comma-separated list of translators");
647}
648
649/* static */
650QString UICommon::vboxLanguageSubDirectory()
651{
652 return "/nls";
653}
654
655/* static */
656QString UICommon::vboxLanguageFileBase()
657{
658 return "VirtualBox_";
659}
660
661/* static */
662QString UICommon::vboxLanguageFileExtension()
663{
664 return ".qm";
665}
666
667/* static */
668QString UICommon::vboxLanguageIdRegExp()
669{
670 return "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
671}
672
673/* static */
674QString UICommon::vboxBuiltInLanguageName()
675{
676 return "C";
677}
678
679/* static */
680QString UICommon::languageId()
681{
682 /* Note that it may not match with UIExtraDataManager::languageId() if the specified language cannot be loaded.
683 *
684 * If the built-in language is active, this method returns "C". "C" is treated as the built-in language for
685 * simplicity -- the C locale is used in unix environments as a fallback when the requested locale is invalid.
686 * This way we don't need to process both the "built_in" language and the "C" language (which is a valid
687 * environment setting) separately. */
688
689 return s_strLoadedLanguageId;
690}
691
692/* static */
693QString UICommon::systemLanguageId()
694{
695 /* This does exactly the same as QLocale::system().name() but corrects its wrong behavior on Linux systems
696 * (LC_NUMERIC for some strange reason takes precedence over any other locale setting in the QLocale::system()
697 * implementation). This implementation first looks at LC_ALL (as defined by SUS), then looks at LC_MESSAGES
698 * which is designed to define a language for program messages in case if it differs from the language for
699 * other locale categories. Then it looks for LANG and finally falls back to QLocale::system().name().
700 *
701 * The order of precedence is well defined here:
702 * http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
703 *
704 * This method will return "C" when the requested locale is invalid or when the "C" locale is set explicitly. */
705
706#if defined(VBOX_WS_MAC)
707 /* QLocale return the right id only if the user select the format
708 * of the language also. So we use our own implementation */
709 return ::darwinSystemLanguage();
710#elif defined(Q_OS_UNIX)
711 const char *pszValue = RTEnvGet("LC_ALL");
712 if (pszValue == 0)
713 pszValue = RTEnvGet("LC_MESSAGES");
714 if (pszValue == 0)
715 pszValue = RTEnvGet("LANG");
716 if (pszValue != 0)
717 return QLocale(pszValue).name();
718#endif
719 return QLocale::system().name();
720}
721
722/* static */
723void UICommon::loadLanguage(const QString &strLangId)
724{
725 QString strEffectiveLangId = strLangId.isEmpty()
726 ? UICommon::systemLanguageId()
727 : strLangId;
728 QString strLanguageFileName;
729 QString strSelectedLangId = vboxBuiltInLanguageName();
730
731 /* If C is selected we change it temporary to en. This makes sure any extra
732 * "en" translation file will be loaded. This is necessary for loading the
733 * plural forms of some of our translations. */
734 bool fResetToC = false;
735 if (strEffectiveLangId == "C")
736 {
737 strEffectiveLangId = "en";
738 fResetToC = true;
739 }
740
741 char szNlsPath[RTPATH_MAX];
742 int rc;
743
744 rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
745 AssertRC(rc);
746
747 QString strNlsPath = QString(szNlsPath) + vboxLanguageSubDirectory();
748 QDir nlsDir(strNlsPath);
749
750 Assert(!strEffectiveLangId.isEmpty());
751 if (!strEffectiveLangId.isEmpty() && strEffectiveLangId != vboxBuiltInLanguageName())
752 {
753 QRegExp regExp(vboxLanguageIdRegExp());
754 int iPos = regExp.indexIn(strEffectiveLangId);
755 /* The language ID should match the regexp completely: */
756 AssertReturnVoid(iPos == 0);
757
758 QString strStrippedLangId = regExp.cap(2);
759
760 if (nlsDir.exists(vboxLanguageFileBase() + strEffectiveLangId + vboxLanguageFileExtension()))
761 {
762 strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
763 strEffectiveLangId +
764 vboxLanguageFileExtension());
765 strSelectedLangId = strEffectiveLangId;
766 }
767 else if (nlsDir.exists(vboxLanguageFileBase() + strStrippedLangId + vboxLanguageFileExtension()))
768 {
769 strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
770 strStrippedLangId +
771 vboxLanguageFileExtension());
772 strSelectedLangId = strStrippedLangId;
773 }
774 else
775 {
776 /* Never complain when the default language is requested. In any
777 * case, if no explicit language file exists, we will simply
778 * fall-back to English (built-in). */
779 if (!strLangId.isNull() && strEffectiveLangId != "en")
780 msgCenter().cannotFindLanguage(strEffectiveLangId, strNlsPath);
781 /* strSelectedLangId remains built-in here: */
782 AssertReturnVoid(strSelectedLangId == vboxBuiltInLanguageName());
783 }
784 }
785
786 /* Delete the old translator if there is one: */
787 if (sTranslator)
788 {
789 /* QTranslator destructor will call qApp->removeTranslator() for
790 * us. It will also delete all its child translations we attach to it
791 * below, so we don't have to care about them specially. */
792 delete sTranslator;
793 }
794
795 /* Load new language files: */
796 sTranslator = new VBoxTranslator(qApp);
797 Assert(sTranslator);
798 bool fLoadOk = true;
799 if (sTranslator)
800 {
801 if (strSelectedLangId != vboxBuiltInLanguageName())
802 {
803 Assert(!strLanguageFileName.isNull());
804 fLoadOk = sTranslator->loadFile(strLanguageFileName);
805 }
806 /* We install the translator in any case: on failure, this will
807 * activate an empty translator that will give us English (built-in): */
808 qApp->installTranslator(sTranslator);
809 }
810 else
811 fLoadOk = false;
812
813 if (fLoadOk)
814 s_strLoadedLanguageId = strSelectedLangId;
815 else
816 {
817 msgCenter().cannotLoadLanguage(strLanguageFileName);
818 s_strLoadedLanguageId = vboxBuiltInLanguageName();
819 }
820
821 /* Try to load the corresponding Qt translation: */
822 if (languageId() != vboxBuiltInLanguageName() && languageId() != "en")
823 {
824#ifdef Q_OS_UNIX
825 /* We use system installations of Qt on Linux systems, so first, try
826 * to load the Qt translation from the system location. */
827 strLanguageFileName = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" +
828 languageId() + vboxLanguageFileExtension();
829 QTranslator *pQtSysTr = new QTranslator(sTranslator);
830 Assert(pQtSysTr);
831 if (pQtSysTr && pQtSysTr->load(strLanguageFileName))
832 qApp->installTranslator(pQtSysTr);
833 /* Note that the Qt translation supplied by Oracle is always loaded
834 * afterwards to make sure it will take precedence over the system
835 * translation (it may contain more decent variants of translation
836 * that better correspond to VirtualBox UI). We need to load both
837 * because a newer version of Qt may be installed on the user computer
838 * and the Oracle version may not fully support it. We don't do it on
839 * Win32 because we supply a Qt library there and therefore the
840 * Oracle translation is always the best one. */
841#endif
842 strLanguageFileName = nlsDir.absoluteFilePath(QString("qt_") +
843 languageId() +
844 vboxLanguageFileExtension());
845 QTranslator *pQtTr = new QTranslator(sTranslator);
846 Assert(pQtTr);
847 if (pQtTr && (fLoadOk = pQtTr->load(strLanguageFileName)))
848 qApp->installTranslator(pQtTr);
849 /* The below message doesn't fit 100% (because it's an additional
850 * language and the main one won't be reset to built-in on failure)
851 * but the load failure is so rare here that it's not worth a separate
852 * message (but still, having something is better than having none) */
853 if (!fLoadOk && !strLangId.isNull())
854 msgCenter().cannotLoadLanguage(strLanguageFileName);
855 }
856 if (fResetToC)
857 s_strLoadedLanguageId = vboxBuiltInLanguageName();
858#ifdef VBOX_WS_MAC
859 /* Qt doesn't translate the items in the Application menu initially.
860 * Manually trigger an update. */
861 ::darwinRetranslateAppMenu();
862#endif
863}
864
865/* static */
866QString UICommon::yearsToString(uint32_t cVal)
867{
868 return QApplication::translate("UICommon", "%n year(s)", "", cVal);
869}
870
871/* static */
872QString UICommon::monthsToString(uint32_t cVal)
873{
874 return QApplication::translate("UICommon", "%n month(s)", "", cVal);
875}
876
877/* static */
878QString UICommon::daysToString(uint32_t cVal)
879{
880 return QApplication::translate("UICommon", "%n day(s)", "", cVal);
881}
882
883/* static */
884QString UICommon::hoursToString(uint32_t cVal)
885{
886 return QApplication::translate("UICommon", "%n hour(s)", "", cVal);
887}
888
889/* static */
890QString UICommon::minutesToString(uint32_t cVal)
891{
892 return QApplication::translate("UICommon", "%n minute(s)", "", cVal);
893}
894
895/* static */
896QString UICommon::secondsToString(uint32_t cVal)
897{
898 return QApplication::translate("UICommon", "%n second(s)", "", cVal);
899}
900
901/* static */
902QChar UICommon::decimalSep()
903{
904 return QLocale::system().decimalPoint();
905}
906
907/* static */
908QString UICommon::sizeRegexp()
909{
910 /* This regexp will capture 5 groups of text:
911 * - cap(1): integer number in case when no decimal point is present
912 * (if empty, it means that decimal point is present)
913 * - cap(2): size suffix in case when no decimal point is present (may be empty)
914 * - cap(3): integer number in case when decimal point is present (may be empty)
915 * - cap(4): fraction number (hundredth) in case when decimal point is present
916 * - cap(5): size suffix in case when decimal point is present (note that
917 * B cannot appear there). */
918
919 const QString strRegexp =
920 QString("^(?:(?:(\\d+)(?:\\s?(%2|%3|%4|%5|%6|%7))?)|(?:(\\d*)%1(\\d{1,2})(?:\\s?(%3|%4|%5|%6|%7))))$")
921 .arg(decimalSep())
922 .arg(tr("B", "size suffix Bytes"))
923 .arg(tr("KB", "size suffix KBytes=1024 Bytes"))
924 .arg(tr("MB", "size suffix MBytes=1024 KBytes"))
925 .arg(tr("GB", "size suffix GBytes=1024 MBytes"))
926 .arg(tr("TB", "size suffix TBytes=1024 GBytes"))
927 .arg(tr("PB", "size suffix PBytes=1024 TBytes"));
928 return strRegexp;
929}
930
931/* static */
932quint64 UICommon::parseSize(const QString &strText)
933{
934 /* Text should be in form of B|KB|MB|GB|TB|PB. */
935 QRegExp regexp(sizeRegexp());
936 int iPos = regexp.indexIn(strText);
937 if (iPos != -1)
938 {
939 QString strInteger = regexp.cap(1);
940 QString strHundred;
941 QString strSuff = regexp.cap(2);
942 if (strInteger.isEmpty())
943 {
944 strInteger = regexp.cap(3);
945 strHundred = regexp.cap(4);
946 strSuff = regexp.cap(5);
947 }
948
949 quint64 uDenominator = 0;
950 if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
951 uDenominator = 1;
952 else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
953 uDenominator = _1K;
954 else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
955 uDenominator = _1M;
956 else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
957 uDenominator = _1G;
958 else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
959 uDenominator = _1T;
960 else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
961 uDenominator = _1P;
962
963 quint64 iInteger = strInteger.toULongLong();
964 if (uDenominator == 1)
965 return iInteger;
966
967 quint64 iHundred = strHundred.leftJustified(2, '0').toULongLong();
968 iHundred = iHundred * uDenominator / 100;
969 iInteger = iInteger * uDenominator + iHundred;
970 return iInteger;
971 }
972 else
973 return 0;
974}
975
976/* static */
977SizeSuffix UICommon::parseSizeSuffix(const QString &strText)
978{
979 /* Text should be in form of B|KB|MB|GB|TB|PB. */
980 QRegExp regexp(sizeRegexp());
981 int iPos = regexp.indexIn(strText);
982 if (iPos != -1)
983 {
984 QString strInteger = regexp.cap(1);
985 QString strSuff = regexp.cap(2);
986 if (strInteger.isEmpty())
987 {
988 strInteger = regexp.cap(3);
989 strSuff = regexp.cap(5);
990 }
991
992 SizeSuffix enmSizeSuffix = SizeSuffix_Byte;
993
994 if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
995 enmSizeSuffix = SizeSuffix_Byte;
996 else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
997 enmSizeSuffix = SizeSuffix_KiloByte;
998 else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
999 enmSizeSuffix = SizeSuffix_MegaByte;
1000 else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
1001 enmSizeSuffix = SizeSuffix_GigaByte;
1002 else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
1003 enmSizeSuffix = SizeSuffix_TeraByte;
1004 else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
1005 enmSizeSuffix = SizeSuffix_PetaByte;
1006 return enmSizeSuffix;
1007 }
1008 else
1009 return SizeSuffix_Byte;
1010}
1011
1012/* static */
1013bool UICommon::hasSizeSuffix(const QString &strText)
1014{
1015 /* Text should be in form of B|KB|MB|GB|TB|PB. */
1016 QRegExp regexp(sizeRegexp());
1017 int iPos = regexp.indexIn(strText);
1018 if (iPos != -1)
1019 {
1020 QString strInteger = regexp.cap(1);
1021 QString strSuff = regexp.cap(2);
1022 if (strInteger.isEmpty())
1023 {
1024 strInteger = regexp.cap(3);
1025 strSuff = regexp.cap(5);
1026 }
1027
1028 if (strSuff.isEmpty())
1029 return false;
1030 if (strSuff == tr("B", "size suffix Bytes") ||
1031 strSuff == tr("KB", "size suffix KBytes=1024 Bytes") ||
1032 strSuff == tr("MB", "size suffix MBytes=1024 KBytes") ||
1033 strSuff == tr("GB", "size suffix GBytes=1024 MBytes") ||
1034 strSuff == tr("TB", "size suffix TBytes=1024 GBytes") ||
1035 strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
1036 return true;
1037 return false;
1038 }
1039 else
1040 return false;
1041}
1042
1043/* static */
1044QString UICommon::formatSize(quint64 uSize, uint cDecimal /* = 2 */,
1045 FormatSize enmMode /* = FormatSize_Round */)
1046{
1047 /* Text will be in form of B|KB|MB|GB|TB|PB.
1048 *
1049 * When enmMode is FormatSize_Round, the result is rounded to the
1050 * closest number containing @a aDecimal decimal digits.
1051 * When enmMode is FormatSize_RoundDown, the result is rounded to the
1052 * largest number with @a aDecimal decimal digits that is not greater than
1053 * the result. This guarantees that converting the resulting string back to
1054 * the integer value in bytes will not produce a value greater that the
1055 * initial size parameter.
1056 * When enmMode is FormatSize_RoundUp, the result is rounded to the
1057 * smallest number with @a aDecimal decimal digits that is not less than the
1058 * result. This guarantees that converting the resulting string back to the
1059 * integer value in bytes will not produce a value less that the initial
1060 * size parameter. */
1061
1062 quint64 uDenominator = 0;
1063 int iSuffix = 0;
1064
1065 if (uSize < _1K)
1066 {
1067 uDenominator = 1;
1068 iSuffix = 0;
1069 }
1070 else if (uSize < _1M)
1071 {
1072 uDenominator = _1K;
1073 iSuffix = 1;
1074 }
1075 else if (uSize < _1G)
1076 {
1077 uDenominator = _1M;
1078 iSuffix = 2;
1079 }
1080 else if (uSize < _1T)
1081 {
1082 uDenominator = _1G;
1083 iSuffix = 3;
1084 }
1085 else if (uSize < _1P)
1086 {
1087 uDenominator = _1T;
1088 iSuffix = 4;
1089 }
1090 else
1091 {
1092 uDenominator = _1P;
1093 iSuffix = 5;
1094 }
1095
1096 quint64 uInteger = uSize / uDenominator;
1097 quint64 uDecimal = uSize % uDenominator;
1098 quint64 uMult = 1;
1099 for (uint i = 0; i < cDecimal; ++i)
1100 uMult *= 10;
1101
1102 QString strNumber;
1103 if (uDenominator > 1)
1104 {
1105 if (uDecimal)
1106 {
1107 uDecimal *= uMult;
1108 /* Not greater: */
1109 if (enmMode == FormatSize_RoundDown)
1110 uDecimal = uDecimal / uDenominator;
1111 /* Not less: */
1112 else if (enmMode == FormatSize_RoundUp)
1113 uDecimal = (uDecimal + uDenominator - 1) / uDenominator;
1114 /* Nearest: */
1115 else
1116 uDecimal = (uDecimal + uDenominator / 2) / uDenominator;
1117 }
1118 /* Check for the fractional part overflow due to rounding: */
1119 if (uDecimal == uMult)
1120 {
1121 uDecimal = 0;
1122 ++uInteger;
1123 /* Check if we've got 1024 XB after rounding and scale down if so: */
1124 if (uInteger == 1024 && iSuffix + 1 < (int)SizeSuffix_Max)
1125 {
1126 uInteger /= 1024;
1127 ++iSuffix;
1128 }
1129 }
1130 strNumber = QString::number(uInteger);
1131 if (cDecimal)
1132 strNumber += QString("%1%2").arg(decimalSep())
1133 .arg(QString::number(uDecimal).rightJustified(cDecimal, '0'));
1134 }
1135 else
1136 {
1137 strNumber = QString::number(uInteger);
1138 }
1139
1140 return QString("%1 %2").arg(strNumber).arg(gpConverter->toString(static_cast<SizeSuffix>(iSuffix)));
1141}
1142
1143/* static */
1144QString UICommon::addMetricSuffixToNumber(quint64 uNumber)
1145{
1146 if (uNumber <= 0)
1147 return QString();
1148 /* See https://en.wikipedia.org/wiki/Metric_prefix for metric suffixes:*/
1149 char suffixes[] = {'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
1150 int zeroCount = (int)log10((long double)uNumber);
1151 if (zeroCount < 3)
1152 return QString::number(uNumber);
1153 int h = 3 * (zeroCount / 3);
1154 char result[128];
1155 sprintf(result, "%.2f", uNumber / (float)pow((double)10, h));
1156 return QString("%1%2").arg(result).arg(suffixes[h / 3 - 1]);
1157}
1158
1159/* static */
1160QStringList UICommon::COMPortNames()
1161{
1162 QStringList list;
1163 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
1164 list << kComKnownPorts[i].name;
1165
1166 return list;
1167}
1168
1169/* static */
1170QString UICommon::toCOMPortName(ulong uIRQ, ulong uIOBase)
1171{
1172 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
1173 if (kComKnownPorts[i].IRQ == uIRQ &&
1174 kComKnownPorts[i].IOBase == uIOBase)
1175 return kComKnownPorts[i].name;
1176
1177 return s_strUserDefinedPortName;
1178}
1179
1180/* static */
1181bool UICommon::toCOMPortNumbers(const QString &strName, ulong &uIRQ, ulong &uIOBase)
1182{
1183 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
1184 if (strcmp(kComKnownPorts[i].name, strName.toUtf8().data()) == 0)
1185 {
1186 uIRQ = kComKnownPorts[i].IRQ;
1187 uIOBase = kComKnownPorts[i].IOBase;
1188 return true;
1189 }
1190
1191 return false;
1192}
1193
1194/* static */
1195QStringList UICommon::LPTPortNames()
1196{
1197 QStringList list;
1198 for (size_t i = 0; i < RT_ELEMENTS(kLptKnownPorts); ++i)
1199 list << kLptKnownPorts[i].name;
1200
1201 return list;
1202}
1203
1204/* static */
1205QString UICommon::toLPTPortName(ulong uIRQ, ulong uIOBase)
1206{
1207 for (size_t i = 0; i < RT_ELEMENTS(kLptKnownPorts); ++i)
1208 if (kLptKnownPorts[i].IRQ == uIRQ &&
1209 kLptKnownPorts[i].IOBase == uIOBase)
1210 return kLptKnownPorts[i].name;
1211
1212 return s_strUserDefinedPortName;
1213}
1214
1215/* static */
1216bool UICommon::toLPTPortNumbers(const QString &strName, ulong &uIRQ, ulong &uIOBase)
1217{
1218 for (size_t i = 0; i < RT_ELEMENTS(kLptKnownPorts); ++i)
1219 if (strcmp(kLptKnownPorts[i].name, strName.toUtf8().data()) == 0)
1220 {
1221 uIRQ = kLptKnownPorts[i].IRQ;
1222 uIOBase = kLptKnownPorts[i].IOBase;
1223 return true;
1224 }
1225
1226 return false;
1227}
1228
1229/* static */
1230QString UICommon::highlight(QString strText, bool fToolTip /* = false */)
1231{
1232 /* We should reformat the input strText so that:
1233 * - strings in single quotes will be put inside <nobr> and marked
1234 * with blue color;
1235 * - UUIDs be put inside <nobr> and marked
1236 * with green color;
1237 * - replaces new line chars with </p><p> constructs to form paragraphs
1238 * (note that <p\> and </p> are not appended to the beginning and to the
1239 * end of the string respectively, to allow the result be appended
1240 * or prepended to the existing paragraph).
1241 *
1242 * If @a fToolTip is true, colouring is not applied, only the <nobr> tag
1243 * is added. Also, new line chars are replaced with <br> instead of <p>. */
1244
1245 QString strFont;
1246 QString uuidFont;
1247 QString endFont;
1248 if (!fToolTip)
1249 {
1250 strFont = "<font color=#0000CC>";
1251 uuidFont = "<font color=#008000>";
1252 endFont = "</font>";
1253 }
1254
1255 /* Replace special entities, '&' -- first! */
1256 strText.replace('&', "&amp;");
1257 strText.replace('<', "&lt;");
1258 strText.replace('>', "&gt;");
1259 strText.replace('\"', "&quot;");
1260
1261 /* Mark strings in single quotes with color: */
1262 QRegExp rx = QRegExp("((?:^|\\s)[(]?)'([^']*)'(?=[:.-!);]?(?:\\s|$))");
1263 rx.setMinimal(true);
1264 strText.replace(rx, QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strFont).arg(endFont));
1265
1266 /* Mark UUIDs with color: */
1267 strText.replace(QRegExp(
1268 "((?:^|\\s)[(]?)"
1269 "(\\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\})"
1270 "(?=[:.-!);]?(?:\\s|$))"),
1271 QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidFont).arg(endFont));
1272
1273 /* Split to paragraphs at \n chars: */
1274 if (!fToolTip)
1275 strText.replace('\n', "</p><p>");
1276 else
1277 strText.replace('\n', "<br>");
1278
1279 return strText;
1280}
1281
1282/* static */
1283QString UICommon::emphasize(QString strText)
1284{
1285 /* We should reformat the input string @a strText so that:
1286 * - strings in single quotes will be put inside \<nobr\> and marked
1287 * with bold style;
1288 * - UUIDs be put inside \<nobr\> and marked
1289 * with italic style;
1290 * - replaces new line chars with \</p\>\<p\> constructs to form paragraphs
1291 * (note that \<p\> and \</p\> are not appended to the beginning and to the
1292 * end of the string respectively, to allow the result be appended
1293 * or prepended to the existing paragraph). */
1294
1295 QString strEmphStart("<b>");
1296 QString strEmphEnd("</b>");
1297 QString uuidEmphStart("<i>");
1298 QString uuidEmphEnd("</i>");
1299
1300 /* Replace special entities, '&' -- first! */
1301 strText.replace('&', "&amp;");
1302 strText.replace('<', "&lt;");
1303 strText.replace('>', "&gt;");
1304 strText.replace('\"', "&quot;");
1305
1306 /* Mark strings in single quotes with bold style: */
1307 QRegExp rx = QRegExp("((?:^|\\s)[(]?)'([^']*)'(?=[:.-!);]?(?:\\s|$))");
1308 rx.setMinimal(true);
1309 strText.replace(rx, QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strEmphStart).arg(strEmphEnd));
1310
1311 /* Mark UUIDs with italic style: */
1312 strText.replace(QRegExp(
1313 "((?:^|\\s)[(]?)"
1314 "(\\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\})"
1315 "(?=[:.-!);]?(?:\\s|$))"),
1316 QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidEmphStart).arg(uuidEmphEnd));
1317
1318 /* Split to paragraphs at \n chars: */
1319 strText.replace('\n', "</p><p>");
1320
1321 return strText;
1322}
1323
1324/* static */
1325QString UICommon::removeAccelMark(QString strText)
1326{
1327 /* In order to support accelerators used in non-alphabet languages
1328 * (e.g. Japanese) that has a form of "(&<L>)" (where <L> is a latin letter),
1329 * this method first searches for this pattern and, if found, removes it as a
1330 * whole. If such a pattern is not found, then the '&' character is simply
1331 * removed from the string. */
1332
1333 QRegExp accel("\\(&[a-zA-Z]\\)");
1334 int iPos = accel.indexIn(strText);
1335 if (iPos >= 0)
1336 strText.remove(iPos, accel.cap().length());
1337 else
1338 {
1339 iPos = strText.indexOf('&');
1340 if (iPos >= 0)
1341 strText.remove(iPos, 1);
1342 }
1343
1344 return strText;
1345}
1346
1347/* static */
1348QString UICommon::insertKeyToActionText(const QString &strText, const QString &strKey)
1349{
1350#ifdef VBOX_WS_MAC
1351 QString strPattern("%1 (Host+%2)");
1352#else
1353 QString strPattern("%1 \tHost+%2");
1354#endif
1355 if ( strKey.isEmpty()
1356 || strKey.compare("None", Qt::CaseInsensitive) == 0)
1357 return strText;
1358 else
1359 return strPattern.arg(strText).arg(QKeySequence(strKey).toString(QKeySequence::NativeText));
1360}
1361
1362/* static */
1363QString UICommon::helpFile()
1364{
1365#if defined(VBOX_WS_WIN)
1366 const QString strName = "VirtualBox";
1367 const QString strSuffix = "chm";
1368#elif defined(VBOX_WS_MAC)
1369 const QString strName = "UserManual";
1370 const QString strSuffix = "pdf";
1371#elif defined(VBOX_WS_X11)
1372# if defined VBOX_OSE
1373 const QString strName = "UserManual";
1374 const QString strSuffix = "pdf";
1375# else
1376 const QString strName = "VirtualBox";
1377 const QString strSuffix = "chm";
1378# endif
1379#endif
1380
1381 /* Where are the docs located? */
1382 char szDocsPath[RTPATH_MAX];
1383 int rc = RTPathAppDocs(szDocsPath, sizeof(szDocsPath));
1384 AssertRC(rc);
1385
1386 /* Make sure that the language is in two letter code.
1387 * Note: if languageId() returns an empty string lang.name() will
1388 * return "C" which is an valid language code. */
1389 QLocale lang(UICommon::languageId());
1390
1391 /* Construct the path and the filename: */
1392 QString strManual = QString("%1/%2_%3.%4").arg(szDocsPath)
1393 .arg(strName)
1394 .arg(lang.name())
1395 .arg(strSuffix);
1396
1397 /* Check if a help file with that name exists: */
1398 QFileInfo fi(strManual);
1399 if (fi.exists())
1400 return strManual;
1401
1402 /* Fall back to the standard: */
1403 strManual = QString("%1/%2.%4").arg(szDocsPath)
1404 .arg(strName)
1405 .arg(strSuffix);
1406 return strManual;
1407}
1408
1409/* static */
1410QString UICommon::documentsPath()
1411{
1412 QString strPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
1413 QDir dir(strPath);
1414 if (dir.exists())
1415 return QDir::cleanPath(dir.canonicalPath());
1416 else
1417 {
1418 dir.setPath(QDir::homePath() + "/Documents");
1419 if (dir.exists())
1420 return QDir::cleanPath(dir.canonicalPath());
1421 else
1422 return QDir::homePath();
1423 }
1424}
1425
1426/* static */
1427QRect UICommon::normalizeGeometry(const QRect &rectangle, const QRegion &boundRegion, bool fCanResize /* = true */)
1428{
1429 /* Perform direct and flipped search of position for @a rectangle to make sure it is fully contained
1430 * inside @a boundRegion region by moving & resizing (if @a fCanResize is specified) @a rectangle if
1431 * necessary. Selects the minimum shifted result between direct and flipped variants. */
1432
1433 /* Direct search for normalized rectangle: */
1434 QRect var1(getNormalized(rectangle, boundRegion, fCanResize));
1435
1436 /* Flipped search for normalized rectangle: */
1437 QRect var2(flip(getNormalized(flip(rectangle).boundingRect(),
1438 flip(boundRegion), fCanResize)).boundingRect());
1439
1440 /* Calculate shift from starting position for both variants: */
1441 double dLength1 = sqrt(pow((double)(var1.x() - rectangle.x()), (double)2) +
1442 pow((double)(var1.y() - rectangle.y()), (double)2));
1443 double dLength2 = sqrt(pow((double)(var2.x() - rectangle.x()), (double)2) +
1444 pow((double)(var2.y() - rectangle.y()), (double)2));
1445
1446 /* Return minimum shifted variant: */
1447 return dLength1 > dLength2 ? var2 : var1;
1448}
1449
1450/* static */
1451QRect UICommon::getNormalized(const QRect &rectangle, const QRegion &boundRegion, bool /* fCanResize = true */)
1452{
1453 /* Ensures that the given rectangle @a rectangle is fully contained within the region @a boundRegion
1454 * by moving @a rectangle if necessary. If @a rectangle is larger than @a boundRegion, top left
1455 * corner of @a rectangle is aligned with the top left corner of maximum available rectangle and,
1456 * if @a fCanResize is true, @a rectangle is shrinked to become fully visible. */
1457
1458 /* Storing available horizontal sub-rectangles & vertical shifts: */
1459 const int iWindowVertical = rectangle.center().y();
1460 QList<QRect> rectanglesList;
1461 QList<int> shiftsList;
1462 foreach (QRect currentItem, boundRegion.rects())
1463 {
1464 const int iCurrentDelta = qAbs(iWindowVertical - currentItem.center().y());
1465 const int iShift2Top = currentItem.top() - rectangle.top();
1466 const int iShift2Bot = currentItem.bottom() - rectangle.bottom();
1467
1468 int iTtemPosition = 0;
1469 foreach (QRect item, rectanglesList)
1470 {
1471 const int iDelta = qAbs(iWindowVertical - item.center().y());
1472 if (iDelta > iCurrentDelta)
1473 break;
1474 else
1475 ++iTtemPosition;
1476 }
1477 rectanglesList.insert(iTtemPosition, currentItem);
1478
1479 int iShift2TopPos = 0;
1480 foreach (int iShift, shiftsList)
1481 if (qAbs(iShift) > qAbs(iShift2Top))
1482 break;
1483 else
1484 ++iShift2TopPos;
1485 shiftsList.insert(iShift2TopPos, iShift2Top);
1486
1487 int iShift2BotPos = 0;
1488 foreach (int iShift, shiftsList)
1489 if (qAbs(iShift) > qAbs(iShift2Bot))
1490 break;
1491 else
1492 ++iShift2BotPos;
1493 shiftsList.insert(iShift2BotPos, iShift2Bot);
1494 }
1495
1496 /* Trying to find the appropriate place for window: */
1497 QRect result;
1498 for (int i = -1; i < shiftsList.size(); ++i)
1499 {
1500 /* Move to appropriate vertical: */
1501 QRect newRectangle(rectangle);
1502 if (i >= 0)
1503 newRectangle.translate(0, shiftsList[i]);
1504
1505 /* Search horizontal shift: */
1506 int iMaxShift = 0;
1507 foreach (QRect item, rectanglesList)
1508 {
1509 QRect trectangle(newRectangle.translated(item.left() - newRectangle.left(), 0));
1510 if (!item.intersects(trectangle))
1511 continue;
1512
1513 if (newRectangle.left() < item.left())
1514 {
1515 const int iShift = item.left() - newRectangle.left();
1516 iMaxShift = qAbs(iShift) > qAbs(iMaxShift) ? iShift : iMaxShift;
1517 }
1518 else if (newRectangle.right() > item.right())
1519 {
1520 const int iShift = item.right() - newRectangle.right();
1521 iMaxShift = qAbs(iShift) > qAbs(iMaxShift) ? iShift : iMaxShift;
1522 }
1523 }
1524
1525 /* Shift across the horizontal direction: */
1526 newRectangle.translate(iMaxShift, 0);
1527
1528 /* Check the translated rectangle to feat the rules: */
1529 if (boundRegion.united(newRectangle) == boundRegion)
1530 result = newRectangle;
1531
1532 if (!result.isNull())
1533 break;
1534 }
1535
1536 if (result.isNull())
1537 {
1538 /* Resize window to feat desirable size
1539 * using max of available rectangles: */
1540 QRect maxRectangle;
1541 quint64 uMaxSquare = 0;
1542 foreach (QRect item, rectanglesList)
1543 {
1544 const quint64 uSquare = item.width() * item.height();
1545 if (uSquare > uMaxSquare)
1546 {
1547 uMaxSquare = uSquare;
1548 maxRectangle = item;
1549 }
1550 }
1551
1552 result = rectangle;
1553 result.moveTo(maxRectangle.x(), maxRectangle.y());
1554 if (maxRectangle.right() < result.right())
1555 result.setRight(maxRectangle.right());
1556 if (maxRectangle.bottom() < result.bottom())
1557 result.setBottom(maxRectangle.bottom());
1558 }
1559
1560 return result;
1561}
1562
1563/* static */
1564QRegion UICommon::flip(const QRegion &region)
1565{
1566 QRegion result;
1567 QVector<QRect> rectangles(region.rects());
1568 foreach (QRect rectangle, rectangles)
1569 result += QRect(rectangle.y(), rectangle.x(),
1570 rectangle.height(), rectangle.width());
1571 return result;
1572}
1573
1574/* static */
1575void UICommon::centerWidget(QWidget *pWidget, QWidget *pRelative, bool fCanResize /* = true */)
1576{
1577 /* If necessary, pWidget's position is adjusted to make it fully visible within
1578 * the available desktop area. If pWidget is bigger then this area, it will also
1579 * be resized unless fCanResize is false or there is an inappropriate minimum
1580 * size limit (in which case the top left corner will be simply aligned with the top
1581 * left corner of the available desktop area). pWidget must be a top-level widget.
1582 * pRelative may be any widget, but if it's not top-level itself, its top-level
1583 * widget will be used for calculations. pRelative can also be NULL, in which case
1584 * pWidget will be centered relative to the available desktop area. */
1585
1586 AssertReturnVoid(pWidget);
1587 AssertReturnVoid(pWidget->isTopLevel());
1588
1589 QRect deskGeo, parentGeo;
1590 if (pRelative)
1591 {
1592 pRelative = pRelative->window();
1593 deskGeo = gpDesktop->availableGeometry(pRelative);
1594 parentGeo = pRelative->frameGeometry();
1595 // WORKAROUND:
1596 // On X11/Gnome, geo/frameGeo.x() and y() are always 0 for top level
1597 // widgets with parents, what a shame. Use mapToGlobal() to workaround.
1598 QPoint d = pRelative->mapToGlobal(QPoint(0, 0));
1599 d.rx() -= pRelative->geometry().x() - pRelative->x();
1600 d.ry() -= pRelative->geometry().y() - pRelative->y();
1601 parentGeo.moveTopLeft(d);
1602 }
1603 else
1604 {
1605 deskGeo = gpDesktop->availableGeometry();
1606 parentGeo = deskGeo;
1607 }
1608
1609 // WORKAROUND:
1610 // On X11, there is no way to determine frame geometry (including WM
1611 // decorations) before the widget is shown for the first time. Stupidly
1612 // enumerate other top level widgets to find the thickest frame. The code
1613 // is based on the idea taken from QDialog::adjustPositionInternal().
1614
1615 int iExtraW = 0;
1616 int iExtraH = 0;
1617
1618 QWidgetList list = QApplication::topLevelWidgets();
1619 QListIterator<QWidget*> it(list);
1620 while ((iExtraW == 0 || iExtraH == 0) && it.hasNext())
1621 {
1622 int iFrameW, iFrameH;
1623 QWidget *pCurrent = it.next();
1624 if (!pCurrent->isVisible())
1625 continue;
1626
1627 iFrameW = pCurrent->frameGeometry().width() - pCurrent->width();
1628 iFrameH = pCurrent->frameGeometry().height() - pCurrent->height();
1629
1630 iExtraW = qMax(iExtraW, iFrameW);
1631 iExtraH = qMax(iExtraH, iFrameH);
1632 }
1633
1634 /* On non-X11 platforms, the following would be enough instead of the above workaround: */
1635 // QRect geo = frameGeometry();
1636 QRect geo = QRect(0, 0, pWidget->width() + iExtraW,
1637 pWidget->height() + iExtraH);
1638
1639 geo.moveCenter(QPoint(parentGeo.x() + (parentGeo.width() - 1) / 2,
1640 parentGeo.y() + (parentGeo.height() - 1) / 2));
1641
1642 /* Ensure the widget is within the available desktop area: */
1643 QRect newGeo = normalizeGeometry(geo, deskGeo, fCanResize);
1644#ifdef VBOX_WS_MAC
1645 // WORKAROUND:
1646 // No idea why, but Qt doesn't respect if there is a unified toolbar on the
1647 // ::move call. So manually add the height of the toolbar before setting
1648 // the position.
1649 if (pRelative)
1650 newGeo.translate(0, ::darwinWindowToolBarHeight(pWidget));
1651#endif /* VBOX_WS_MAC */
1652
1653 pWidget->move(newGeo.topLeft());
1654
1655 if ( fCanResize
1656 && (geo.width() != newGeo.width() || geo.height() != newGeo.height()))
1657 pWidget->resize(newGeo.width() - iExtraW, newGeo.height() - iExtraH);
1658}
1659
1660#ifdef VBOX_WS_X11
1661typedef struct {
1662/** User specified flags */
1663uint32_t flags;
1664/** User-specified position */
1665int32_t x, y;
1666/** User-specified size */
1667int32_t width, height;
1668/** Program-specified minimum size */
1669int32_t min_width, min_height;
1670/** Program-specified maximum size */
1671int32_t max_width, max_height;
1672/** Program-specified resize increments */
1673int32_t width_inc, height_inc;
1674/** Program-specified minimum aspect ratios */
1675int32_t min_aspect_num, min_aspect_den;
1676/** Program-specified maximum aspect ratios */
1677int32_t max_aspect_num, max_aspect_den;
1678/** Program-specified base size */
1679int32_t base_width, base_height;
1680/** Program-specified window gravity */
1681uint32_t win_gravity;
1682} xcb_size_hints_t;
1683#endif /* VBOX_WS_X11 */
1684
1685/* static */
1686void UICommon::setTopLevelGeometry(QWidget *pWidget, int x, int y, int w, int h)
1687{
1688 AssertPtrReturnVoid(pWidget);
1689#ifdef VBOX_WS_X11
1690# define QWINDOWSIZE_MAX ((1<<24)-1)
1691 if (pWidget->isWindow() && pWidget->isVisible())
1692 {
1693 // WORKAROUND:
1694 // X11 window managers are not required to accept geometry changes on
1695 // the top-level window. Unfortunately, current at Qt 5.6 and 5.7, Qt
1696 // assumes that the change will succeed, and resizes all sub-windows
1697 // unconditionally. By calling ConfigureWindow directly, Qt will see
1698 // our change request as an externally triggered one on success and not
1699 // at all if it is rejected.
1700 const double dDPR = gpDesktop->devicePixelRatio(pWidget);
1701 uint16_t fMask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
1702 | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
1703 uint32_t values[] = { (uint32_t)(x * dDPR), (uint32_t)(y * dDPR), (uint32_t)(w * dDPR), (uint32_t)(h * dDPR) };
1704 xcb_configure_window(QX11Info::connection(), (xcb_window_t)pWidget->winId(),
1705 fMask, values);
1706 xcb_size_hints_t hints;
1707 hints.flags = 1 /* XCB_ICCCM_SIZE_HINT_US_POSITION */
1708 | 2 /* XCB_ICCCM_SIZE_HINT_US_SIZE */
1709 | 512 /* XCB_ICCCM_SIZE_P_WIN_GRAVITY */;
1710 hints.x = x * dDPR;
1711 hints.y = y * dDPR;
1712 hints.width = w * dDPR;
1713 hints.height = h * dDPR;
1714 hints.min_width = pWidget->minimumSize().width() * dDPR;
1715 hints.min_height = pWidget->minimumSize().height() * dDPR;
1716 hints.max_width = pWidget->maximumSize().width() * dDPR;
1717 hints.max_height = pWidget->maximumSize().height() * dDPR;
1718 hints.width_inc = pWidget->sizeIncrement().width() * dDPR;
1719 hints.height_inc = pWidget->sizeIncrement().height() * dDPR;
1720 hints.base_width = pWidget->baseSize().width() * dDPR;
1721 hints.base_height = pWidget->baseSize().height() * dDPR;
1722 hints.win_gravity = XCB_GRAVITY_STATIC;
1723 if (hints.min_width > 0 || hints.min_height > 0)
1724 hints.flags |= 16 /* XCB_ICCCM_SIZE_HINT_P_MIN_SIZE */;
1725 if (hints.max_width < QWINDOWSIZE_MAX || hints.max_height < QWINDOWSIZE_MAX)
1726 hints.flags |= 32 /* XCB_ICCCM_SIZE_HINT_P_MAX_SIZE */;
1727 if (hints.width_inc > 0 || hints.height_inc)
1728 hints.flags |= 64 /* XCB_ICCCM_SIZE_HINT_P_MIN_SIZE */
1729 | 256 /* XCB_ICCCM_SIZE_HINT_BASE_SIZE */;
1730 xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE,
1731 (xcb_window_t)pWidget->winId(), XCB_ATOM_WM_NORMAL_HINTS,
1732 XCB_ATOM_WM_SIZE_HINTS, 32, sizeof(hints) >> 2, &hints);
1733 xcb_flush(QX11Info::connection());
1734 }
1735 else
1736 // WORKAROUND:
1737 // Call the Qt method if the window is not visible as otherwise no
1738 // Configure event will arrive to tell Qt what geometry we want.
1739 pWidget->setGeometry(x, y, w, h);
1740# else /* !VBOX_WS_X11 */
1741 pWidget->setGeometry(x, y, w, h);
1742# endif /* !VBOX_WS_X11 */
1743}
1744
1745/* static */
1746void UICommon::setTopLevelGeometry(QWidget *pWidget, const QRect &rect)
1747{
1748 UICommon::setTopLevelGeometry(pWidget, rect.x(), rect.y(), rect.width(), rect.height());
1749}
1750
1751#if defined(VBOX_WS_X11)
1752
1753static char *XXGetProperty(Display *pDpy, Window windowHandle, Atom propType, const char *pszPropName)
1754{
1755 Atom propNameAtom = XInternAtom(pDpy, pszPropName, True /* only_if_exists */);
1756 if (propNameAtom == None)
1757 return NULL;
1758
1759 Atom actTypeAtom = None;
1760 int actFmt = 0;
1761 unsigned long nItems = 0;
1762 unsigned long nBytesAfter = 0;
1763 unsigned char *propVal = NULL;
1764 int rc = XGetWindowProperty(pDpy, windowHandle, propNameAtom,
1765 0, LONG_MAX, False /* delete */,
1766 propType, &actTypeAtom, &actFmt,
1767 &nItems, &nBytesAfter, &propVal);
1768 if (rc != Success)
1769 return NULL;
1770
1771 return reinterpret_cast<char*>(propVal);
1772}
1773
1774static Bool XXSendClientMessage(Display *pDpy, Window windowHandle, const char *pszMsg,
1775 unsigned long aData0 = 0, unsigned long aData1 = 0,
1776 unsigned long aData2 = 0, unsigned long aData3 = 0,
1777 unsigned long aData4 = 0)
1778{
1779 Atom msgAtom = XInternAtom(pDpy, pszMsg, True /* only_if_exists */);
1780 if (msgAtom == None)
1781 return False;
1782
1783 XEvent ev;
1784
1785 ev.xclient.type = ClientMessage;
1786 ev.xclient.serial = 0;
1787 ev.xclient.send_event = True;
1788 ev.xclient.display = pDpy;
1789 ev.xclient.window = windowHandle;
1790 ev.xclient.message_type = msgAtom;
1791
1792 /* Always send as 32 bit for now: */
1793 ev.xclient.format = 32;
1794 ev.xclient.data.l[0] = aData0;
1795 ev.xclient.data.l[1] = aData1;
1796 ev.xclient.data.l[2] = aData2;
1797 ev.xclient.data.l[3] = aData3;
1798 ev.xclient.data.l[4] = aData4;
1799
1800 return XSendEvent(pDpy, DefaultRootWindow(pDpy), False,
1801 SubstructureRedirectMask, &ev) != 0;
1802}
1803
1804#endif
1805
1806/* static */
1807bool UICommon::activateWindow(WId wId, bool fSwitchDesktop /* = true */)
1808{
1809 RT_NOREF(fSwitchDesktop);
1810 bool fResult = true;
1811
1812#if defined(VBOX_WS_WIN)
1813
1814 HWND handle = (HWND)wId;
1815
1816 if (IsIconic(handle))
1817 fResult &= !!ShowWindow(handle, SW_RESTORE);
1818 else if (!IsWindowVisible(handle))
1819 fResult &= !!ShowWindow(handle, SW_SHOW);
1820
1821 fResult &= !!SetForegroundWindow(handle);
1822
1823#elif defined(VBOX_WS_X11)
1824
1825 Display *pDisplay = QX11Info::display();
1826
1827 if (fSwitchDesktop)
1828 {
1829 /* try to find the desktop ID using the NetWM property */
1830 CARD32 *pDesktop = (CARD32 *) XXGetProperty(pDisplay, wId, XA_CARDINAL,
1831 "_NET_WM_DESKTOP");
1832 if (pDesktop == NULL)
1833 // WORKAROUND:
1834 // if the NetWM properly is not supported try to find
1835 // the desktop ID using the GNOME WM property.
1836 pDesktop = (CARD32 *) XXGetProperty(pDisplay, wId, XA_CARDINAL,
1837 "_WIN_WORKSPACE");
1838
1839 if (pDesktop != NULL)
1840 {
1841 Bool ok = XXSendClientMessage(pDisplay, DefaultRootWindow(pDisplay),
1842 "_NET_CURRENT_DESKTOP",
1843 *pDesktop);
1844 if (!ok)
1845 {
1846 Log1WarningFunc(("Couldn't switch to pDesktop=%08X\n", pDesktop));
1847 fResult = false;
1848 }
1849 XFree(pDesktop);
1850 }
1851 else
1852 {
1853 Log1WarningFunc(("Couldn't find a pDesktop ID for wId=%08X\n", wId));
1854 fResult = false;
1855 }
1856 }
1857
1858 Bool ok = XXSendClientMessage(pDisplay, wId, "_NET_ACTIVE_WINDOW");
1859 fResult &= !!ok;
1860
1861 XRaiseWindow(pDisplay, wId);
1862
1863#else
1864
1865 NOREF(wId);
1866 NOREF(fSwitchDesktop);
1867 AssertFailed();
1868 fResult = false;
1869
1870#endif
1871
1872 if (!fResult)
1873 Log1WarningFunc(("Couldn't activate wId=%08X\n", wId));
1874
1875 return fResult;
1876}
1877
1878/* static */
1879void UICommon::setCursor(QWidget *pWidget, const QCursor &cursor)
1880{
1881 if (!pWidget)
1882 return;
1883
1884#ifdef VBOX_WS_X11
1885 /* As reported in https://www.virtualbox.org/ticket/16348,
1886 * in X11 QWidget::setCursor(..) call uses RENDER
1887 * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
1888 * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
1889 if ((UICommon::qtRTMajorVersion() < 5) ||
1890 (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
1891 {
1892 if (X11CheckExtension("RENDER"))
1893 pWidget->setCursor(cursor);
1894 }
1895 else
1896 {
1897 pWidget->setCursor(cursor);
1898 }
1899#else
1900 pWidget->setCursor(cursor);
1901#endif
1902}
1903
1904/* static */
1905void UICommon::setCursor(QGraphicsWidget *pWidget, const QCursor &cursor)
1906{
1907 if (!pWidget)
1908 return;
1909
1910#ifdef VBOX_WS_X11
1911 /* As reported in https://www.virtualbox.org/ticket/16348,
1912 * in X11 QGraphicsWidget::setCursor(..) call uses RENDER
1913 * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
1914 * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
1915 if ((UICommon::qtRTMajorVersion() < 5) ||
1916 (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
1917 {
1918 if (X11CheckExtension("RENDER"))
1919 pWidget->setCursor(cursor);
1920 }
1921 else
1922 {
1923 pWidget->setCursor(cursor);
1924 }
1925#else
1926 pWidget->setCursor(cursor);
1927#endif
1928}
1929
1930/* static */
1931void UICommon::unsetCursor(QWidget *pWidget)
1932{
1933 if (!pWidget)
1934 return;
1935
1936#ifdef VBOX_WS_X11
1937 /* As reported in https://www.virtualbox.org/ticket/16348,
1938 * in X11 QWidget::unsetCursor(..) call uses RENDER
1939 * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
1940 * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
1941 if ((UICommon::qtRTMajorVersion() < 5) ||
1942 (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
1943 {
1944 if (X11CheckExtension("RENDER"))
1945 pWidget->unsetCursor();
1946 }
1947 else
1948 {
1949 pWidget->unsetCursor();
1950 }
1951#else
1952 pWidget->unsetCursor();
1953#endif
1954}
1955
1956/* static */
1957void UICommon::unsetCursor(QGraphicsWidget *pWidget)
1958{
1959 if (!pWidget)
1960 return;
1961
1962#ifdef VBOX_WS_X11
1963 /* As reported in https://www.virtualbox.org/ticket/16348,
1964 * in X11 QGraphicsWidget::unsetCursor(..) call uses RENDER
1965 * extension. Qt (before 5.11) fails to handle the case where the mentioned extension
1966 * is missing. Please see https://codereview.qt-project.org/#/c/225665/ for Qt patch: */
1967 if ((UICommon::qtRTMajorVersion() < 5) ||
1968 (UICommon::qtRTMajorVersion() == 5 && UICommon::qtRTMinorVersion() < 11))
1969 {
1970 if (X11CheckExtension("RENDER"))
1971 pWidget->unsetCursor();
1972 }
1973 else
1974 {
1975 pWidget->unsetCursor();
1976 }
1977#else
1978 pWidget->unsetCursor();
1979#endif
1980}
1981
1982
1983#if defined(VBOX_WS_X11)
1984
1985/* static */
1986bool UICommon::supportsFullScreenMonitorsProtocolX11()
1987{
1988 /* This method tests whether the current X11 window manager supports full-screen mode as we need it.
1989 * Unfortunately the EWMH specification was not fully clear about whether we can expect to find
1990 * all of these atoms on the _NET_SUPPORTED root window property, so we have to test with all
1991 * interesting window managers. If this fails for a user when you think it should succeed
1992 * they should try executing:
1993 * xprop -root | egrep -w '_NET_WM_FULLSCREEN_MONITORS|_NET_WM_STATE|_NET_WM_STATE_FULLSCREEN'
1994 * in an X11 terminal window.
1995 * All three strings should be found under a property called "_NET_SUPPORTED(ATOM)". */
1996
1997 /* Using a global to get at the display does not feel right, but that is how it is done elsewhere in the code. */
1998 Display *pDisplay = QX11Info::display();
1999 Atom atomSupported = XInternAtom(pDisplay, "_NET_SUPPORTED",
2000 True /* only_if_exists */);
2001 Atom atomWMFullScreenMonitors = XInternAtom(pDisplay,
2002 "_NET_WM_FULLSCREEN_MONITORS",
2003 True /* only_if_exists */);
2004 Atom atomWMState = XInternAtom(pDisplay,
2005 "_NET_WM_STATE",
2006 True /* only_if_exists */);
2007 Atom atomWMStateFullScreen = XInternAtom(pDisplay,
2008 "_NET_WM_STATE_FULLSCREEN",
2009 True /* only_if_exists */);
2010 bool fFoundFullScreenMonitors = false;
2011 bool fFoundState = false;
2012 bool fFoundStateFullScreen = false;
2013 Atom atomType;
2014 int cFormat;
2015 unsigned long cItems;
2016 unsigned long cbLeft;
2017 Atom *pAtomHints;
2018 int rc;
2019 unsigned i;
2020
2021 if ( atomSupported == None || atomWMFullScreenMonitors == None
2022 || atomWMState == None || atomWMStateFullScreen == None)
2023 return false;
2024 /* Get atom value: */
2025 rc = XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
2026 atomSupported, 0, 0x7fffffff /*LONG_MAX*/,
2027 False /* delete */, XA_ATOM, &atomType,
2028 &cFormat, &cItems, &cbLeft,
2029 (unsigned char **)&pAtomHints);
2030 if (rc != Success)
2031 return false;
2032 if (pAtomHints == NULL)
2033 return false;
2034 if (atomType == XA_ATOM && cFormat == 32 && cbLeft == 0)
2035 for (i = 0; i < cItems; ++i)
2036 {
2037 if (pAtomHints[i] == atomWMFullScreenMonitors)
2038 fFoundFullScreenMonitors = true;
2039 if (pAtomHints[i] == atomWMState)
2040 fFoundState = true;
2041 if (pAtomHints[i] == atomWMStateFullScreen)
2042 fFoundStateFullScreen = true;
2043 }
2044 XFree(pAtomHints);
2045 return fFoundFullScreenMonitors && fFoundState && fFoundStateFullScreen;
2046}
2047
2048/* static */
2049bool UICommon::setFullScreenMonitorX11(QWidget *pWidget, ulong uScreenId)
2050{
2051 return XXSendClientMessage(QX11Info::display(),
2052 pWidget->window()->winId(),
2053 "_NET_WM_FULLSCREEN_MONITORS",
2054 uScreenId, uScreenId, uScreenId, uScreenId,
2055 1 /* Source indication (1 = normal application) */);
2056}
2057
2058/* static */
2059QVector<Atom> UICommon::flagsNetWmState(QWidget *pWidget)
2060{
2061 /* Get display: */
2062 Display *pDisplay = QX11Info::display();
2063
2064 /* Prepare atoms: */
2065 QVector<Atom> resultNetWmState;
2066 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
2067
2068 /* Get the size of the property data: */
2069 Atom actual_type;
2070 int iActualFormat;
2071 ulong uPropertyLength;
2072 ulong uBytesLeft;
2073 uchar *pPropertyData = 0;
2074 if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
2075 net_wm_state, 0, 0, False, XA_ATOM, &actual_type, &iActualFormat,
2076 &uPropertyLength, &uBytesLeft, &pPropertyData) == Success &&
2077 actual_type == XA_ATOM && iActualFormat == 32)
2078 {
2079 resultNetWmState.resize(uBytesLeft / 4);
2080 XFree((char*)pPropertyData);
2081 pPropertyData = 0;
2082
2083 /* Fetch all data: */
2084 if (XGetWindowProperty(pDisplay, pWidget->window()->winId(),
2085 net_wm_state, 0, resultNetWmState.size(), False, XA_ATOM, &actual_type, &iActualFormat,
2086 &uPropertyLength, &uBytesLeft, &pPropertyData) != Success)
2087 resultNetWmState.clear();
2088 else if (uPropertyLength != (ulong)resultNetWmState.size())
2089 resultNetWmState.resize(uPropertyLength);
2090
2091 /* Put it into resultNetWmState: */
2092 if (!resultNetWmState.isEmpty())
2093 memcpy(resultNetWmState.data(), pPropertyData, resultNetWmState.size() * sizeof(Atom));
2094 if (pPropertyData)
2095 XFree((char*)pPropertyData);
2096 }
2097
2098 /* Return result: */
2099 return resultNetWmState;
2100}
2101
2102/* static */
2103bool UICommon::isFullScreenFlagSet(QWidget *pWidget)
2104{
2105 /* Get display: */
2106 Display *pDisplay = QX11Info::display();
2107
2108 /* Prepare atoms: */
2109 Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
2110
2111 /* Check if flagsNetWmState(pWidget) contains full-screen flag: */
2112 return flagsNetWmState(pWidget).contains(net_wm_state_fullscreen);
2113}
2114
2115/* static */
2116void UICommon::setFullScreenFlag(QWidget *pWidget)
2117{
2118 /* Get display: */
2119 Display *pDisplay = QX11Info::display();
2120
2121 /* Prepare atoms: */
2122 QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
2123 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
2124 Atom net_wm_state_fullscreen = XInternAtom(pDisplay, "_NET_WM_STATE_FULLSCREEN", True /* only if exists */);
2125
2126 /* Append resultNetWmState with fullscreen flag if necessary: */
2127 if (!resultNetWmState.contains(net_wm_state_fullscreen))
2128 {
2129 resultNetWmState.append(net_wm_state_fullscreen);
2130 /* Apply property to widget again: */
2131 XChangeProperty(pDisplay, pWidget->window()->winId(),
2132 net_wm_state, XA_ATOM, 32, PropModeReplace,
2133 (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
2134 }
2135}
2136
2137/* static */
2138void UICommon::setSkipTaskBarFlag(QWidget *pWidget)
2139{
2140 /* Get display: */
2141 Display *pDisplay = QX11Info::display();
2142
2143 /* Prepare atoms: */
2144 QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
2145 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
2146 Atom net_wm_state_skip_taskbar = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_TASKBAR", True /* only if exists */);
2147
2148 /* Append resultNetWmState with skip-taskbar flag if necessary: */
2149 if (!resultNetWmState.contains(net_wm_state_skip_taskbar))
2150 {
2151 resultNetWmState.append(net_wm_state_skip_taskbar);
2152 /* Apply property to widget again: */
2153 XChangeProperty(pDisplay, pWidget->window()->winId(),
2154 net_wm_state, XA_ATOM, 32, PropModeReplace,
2155 (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
2156 }
2157}
2158
2159/* static */
2160void UICommon::setSkipPagerFlag(QWidget *pWidget)
2161{
2162 /* Get display: */
2163 Display *pDisplay = QX11Info::display();
2164
2165 /* Prepare atoms: */
2166 QVector<Atom> resultNetWmState = flagsNetWmState(pWidget);
2167 Atom net_wm_state = XInternAtom(pDisplay, "_NET_WM_STATE", True /* only if exists */);
2168 Atom net_wm_state_skip_pager = XInternAtom(pDisplay, "_NET_WM_STATE_SKIP_PAGER", True /* only if exists */);
2169
2170 /* Append resultNetWmState with skip-pager flag if necessary: */
2171 if (!resultNetWmState.contains(net_wm_state_skip_pager))
2172 {
2173 resultNetWmState.append(net_wm_state_skip_pager);
2174 /* Apply property to widget again: */
2175 XChangeProperty(pDisplay, pWidget->window()->winId(),
2176 net_wm_state, XA_ATOM, 32, PropModeReplace,
2177 (unsigned char*)resultNetWmState.data(), resultNetWmState.size());
2178 }
2179}
2180
2181/* static */
2182void UICommon::setWMClass(QWidget *pWidget, const QString &strNameString, const QString &strClassString)
2183{
2184 /* Make sure all arguments set: */
2185 AssertReturnVoid(pWidget && !strNameString.isNull() && !strClassString.isNull());
2186
2187 /* Define QByteArray objects to make sure data is alive within the scope: */
2188 QByteArray nameByteArray;
2189 /* Check the existence of RESOURCE_NAME env. variable and override name string if necessary: */
2190 const char resourceName[] = "RESOURCE_NAME";
2191 if (qEnvironmentVariableIsSet(resourceName))
2192 nameByteArray = qgetenv(resourceName);
2193 else
2194 nameByteArray = strNameString.toLatin1();
2195 QByteArray classByteArray = strClassString.toLatin1();
2196
2197 AssertReturnVoid(nameByteArray.data() && classByteArray.data());
2198
2199 XClassHint windowClass;
2200 windowClass.res_name = nameByteArray.data();
2201 windowClass.res_class = classByteArray.data();
2202 /* Set WM_CLASS of the window to passed name and class strings: */
2203 XSetClassHint(QX11Info::display(), pWidget->window()->winId(), &windowClass);
2204}
2205
2206#endif /* VBOX_WS_X11 */
2207
2208/* static */
2209void UICommon::setMinimumWidthAccordingSymbolCount(QSpinBox *pSpinBox, int cCount)
2210{
2211 /* Shame on Qt it hasn't stuff for tuning
2212 * widget size suitable for reflecting content of desired size.
2213 * For example QLineEdit, QSpinBox and similar widgets should have a methods
2214 * to strict the minimum width to reflect at least [n] symbols. */
2215
2216 /* Load options: */
2217 QStyleOptionSpinBox option;
2218 option.initFrom(pSpinBox);
2219
2220 /* Acquire edit-field rectangle: */
2221 QRect rect = pSpinBox->style()->subControlRect(QStyle::CC_SpinBox,
2222 &option,
2223 QStyle::SC_SpinBoxEditField,
2224 pSpinBox);
2225
2226 /* Calculate minimum-width magic: */
2227 const int iSpinBoxWidth = pSpinBox->width();
2228 const int iSpinBoxEditFieldWidth = rect.width();
2229 const int iSpinBoxDelta = qMax(0, iSpinBoxWidth - iSpinBoxEditFieldWidth);
2230 QFontMetrics metrics(pSpinBox->font(), pSpinBox);
2231 const QString strDummy(cCount, '0');
2232 const int iTextWidth = metrics.width(strDummy);
2233
2234 /* Tune spin-box minimum-width: */
2235 pSpinBox->setMinimumWidth(iTextWidth + iSpinBoxDelta);
2236}
2237
2238QString UICommon::vmGuestOSFamilyDescription(const QString &strFamilyId) const
2239{
2240 AssertMsg(m_guestOSFamilyDescriptions.contains(strFamilyId),
2241 ("Family ID incorrect: '%s'.", strFamilyId.toLatin1().constData()));
2242 return m_guestOSFamilyDescriptions.value(strFamilyId);
2243}
2244
2245QList<CGuestOSType> UICommon::vmGuestOSTypeList(const QString &strFamilyId) const
2246{
2247 AssertMsg(m_guestOSFamilyIDs.contains(strFamilyId),
2248 ("Family ID incorrect: '%s'.", strFamilyId.toLatin1().constData()));
2249 return m_guestOSFamilyIDs.contains(strFamilyId) ?
2250 m_guestOSTypes[m_guestOSFamilyIDs.indexOf(strFamilyId)] : QList<CGuestOSType>();
2251}
2252
2253CGuestOSType UICommon::vmGuestOSType(const QString &strTypeId,
2254 const QString &strFamilyId /* = QString() */) const
2255{
2256 QList<CGuestOSType> list;
2257 if (m_guestOSFamilyIDs.contains(strFamilyId))
2258 {
2259 list = m_guestOSTypes.at(m_guestOSFamilyIDs.indexOf(strFamilyId));
2260 }
2261 else
2262 {
2263 for (int i = 0; i < m_guestOSFamilyIDs.size(); ++i)
2264 list += m_guestOSTypes.at(i);
2265 }
2266 for (int j = 0; j < list.size(); ++j)
2267 if (!list.at(j).GetId().compare(strTypeId))
2268 return list.at(j);
2269 return CGuestOSType();
2270}
2271
2272QString UICommon::vmGuestOSTypeDescription(const QString &strTypeId) const
2273{
2274 for (int i = 0; i < m_guestOSFamilyIDs.size(); ++i)
2275 {
2276 QList<CGuestOSType> list(m_guestOSTypes[i]);
2277 for (int j = 0; j < list.size(); ++j)
2278 if (!list.at(j).GetId().compare(strTypeId))
2279 return list.at(j).GetDescription();
2280 }
2281 return QString();
2282}
2283
2284/* static */
2285bool UICommon::isDOSType(const QString &strOSTypeId)
2286{
2287 if ( strOSTypeId.left(3) == "dos"
2288 || strOSTypeId.left(3) == "win"
2289 || strOSTypeId.left(3) == "os2")
2290 return true;
2291
2292 return false;
2293}
2294
2295/* static */
2296bool UICommon::switchToMachine(CMachine &comMachine)
2297{
2298#ifdef VBOX_WS_MAC
2299 ULONG64 id = comMachine.ShowConsoleWindow();
2300#else
2301 WId id = (WId)comMachine.ShowConsoleWindow();
2302#endif
2303
2304 AssertWrapperOk(comMachine);
2305 if (!comMachine.isOk())
2306 return false;
2307
2308 // WORKAROUND:
2309 // winId = 0 it means the console window has already done everything
2310 // necessary to implement the "show window" semantics.
2311 if (id == 0)
2312 return true;
2313
2314#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
2315
2316 return activateWindow(id, true);
2317
2318#elif defined(VBOX_WS_MAC)
2319
2320 // WORKAROUND:
2321 // This is just for the case were the other process cannot steal
2322 // the focus from us. It will send us a PSN so we can try.
2323 ProcessSerialNumber psn;
2324 psn.highLongOfPSN = id >> 32;
2325 psn.lowLongOfPSN = (UInt32)id;
2326# ifdef __clang__
2327# pragma GCC diagnostic push
2328# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2329 OSErr rc = ::SetFrontProcess(&psn);
2330# pragma GCC diagnostic pop
2331# else
2332 OSErr rc = ::SetFrontProcess(&psn);
2333# endif
2334 if (!rc)
2335 Log(("GUI: %#RX64 couldn't do SetFrontProcess on itself, the selector (we) had to do it...\n", id));
2336 else
2337 Log(("GUI: Failed to bring %#RX64 to front. rc=%#x\n", id, rc));
2338 return !rc;
2339
2340#else
2341
2342 return false;
2343
2344#endif
2345}
2346
2347bool UICommon::launchMachine(CMachine &comMachine, LaunchMode enmLaunchMode /* = LaunchMode_Default */)
2348{
2349 /* Switch to machine window(s) if possible: */
2350 if ( comMachine.GetSessionState() == KSessionState_Locked /* precondition for CanShowConsoleWindow() */
2351 && comMachine.CanShowConsoleWindow())
2352 {
2353 switch (uiType())
2354 {
2355 /* For Selector UI: */
2356 case UIType_SelectorUI:
2357 {
2358 /* Just switch to existing VM window: */
2359 return switchToMachine(comMachine);
2360 }
2361 /* For Runtime UI: */
2362 case UIType_RuntimeUI:
2363 {
2364 /* Only separate UI process can reach that place.
2365 * Switch to existing VM window and exit. */
2366 switchToMachine(comMachine);
2367 return false;
2368 }
2369 }
2370 }
2371
2372 if (enmLaunchMode != LaunchMode_Separate)
2373 {
2374 /* Make sure machine-state is one of required: */
2375 KMachineState enmState = comMachine.GetState(); NOREF(enmState);
2376 AssertMsg( enmState == KMachineState_PoweredOff
2377 || enmState == KMachineState_Saved
2378 || enmState == KMachineState_Teleported
2379 || enmState == KMachineState_Aborted
2380 , ("Machine must be PoweredOff/Saved/Teleported/Aborted (%d)", enmState));
2381 }
2382
2383 /* Create empty session instance: */
2384 CSession comSession;
2385 comSession.createInstance(CLSID_Session);
2386 if (comSession.isNull())
2387 {
2388 msgCenter().cannotOpenSession(comSession);
2389 return false;
2390 }
2391
2392 /* Configure environment: */
2393 QVector<QString> astrEnv;
2394#ifdef Q_OS_WIN
2395 /* Allow started VM process to be foreground window: */
2396 AllowSetForegroundWindow(ASFW_ANY);
2397#endif
2398#ifdef VBOX_WS_X11
2399 /* Make sure VM process will start on the same display as the VM selector: */
2400 const char *pDisplay = RTEnvGet("DISPLAY");
2401 if (pDisplay)
2402 astrEnv.append(QString("DISPLAY=%1").arg(pDisplay));
2403 const char *pXauth = RTEnvGet("XAUTHORITY");
2404 if (pXauth)
2405 astrEnv.append(QString("XAUTHORITY=%1").arg(pXauth));
2406#endif
2407 QString strType;
2408 switch (enmLaunchMode)
2409 {
2410 case LaunchMode_Default: strType = ""; break;
2411 case LaunchMode_Separate: strType = isSeparateProcess() ? "headless" : "separate"; break;
2412 case LaunchMode_Headless: strType = "headless"; break;
2413 default: AssertFailedReturn(false);
2414 }
2415
2416 /* Prepare "VM spawning" progress: */
2417 CProgress comProgress = comMachine.LaunchVMProcess(comSession, strType, astrEnv);
2418 if (!comMachine.isOk())
2419 {
2420 /* If the VM is started separately and the VM process is already running, then it is OK. */
2421 if (enmLaunchMode == LaunchMode_Separate)
2422 {
2423 KMachineState enmState = comMachine.GetState();
2424 if ( enmState >= KMachineState_FirstOnline
2425 && enmState <= KMachineState_LastOnline)
2426 {
2427 /* Already running. */
2428 return true;
2429 }
2430 }
2431
2432 msgCenter().cannotOpenSession(comMachine);
2433 return false;
2434 }
2435
2436 /* Postpone showing "VM spawning" progress.
2437 * Hope 1 minute will be enough to spawn any running VM silently,
2438 * otherwise we better show the progress...
2439 * If starting separately, then show the progress now. */
2440 int iSpawningDuration = enmLaunchMode == LaunchMode_Separate ? 0 : 60000;
2441 msgCenter().showModalProgressDialog(comProgress, comMachine.GetName(),
2442 ":/progress_start_90px.png", 0, iSpawningDuration);
2443 if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
2444 msgCenter().cannotOpenSession(comProgress, comMachine.GetName());
2445
2446 /* Unlock machine, close session: */
2447 comSession.UnlockMachine();
2448
2449 /* True finally: */
2450 return true;
2451}
2452
2453CSession UICommon::openSession(const QUuid &uId, KLockType lockType /* = KLockType_Shared */)
2454{
2455 /* Prepare session: */
2456 CSession comSession;
2457
2458 /* Simulate try-catch block: */
2459 bool fSuccess = false;
2460 do
2461 {
2462 /* Create empty session instance: */
2463 comSession.createInstance(CLSID_Session);
2464 if (comSession.isNull())
2465 {
2466 msgCenter().cannotOpenSession(comSession);
2467 break;
2468 }
2469
2470 /* Search for the corresponding machine: */
2471 CMachine comMachine = m_comVBox.FindMachine(uId.toString());
2472 if (comMachine.isNull())
2473 {
2474 msgCenter().cannotFindMachineById(m_comVBox, uId);
2475 break;
2476 }
2477
2478 if (lockType == KLockType_VM)
2479 comSession.SetName("GUI/Qt");
2480
2481 /* Lock found machine to session: */
2482 comMachine.LockMachine(comSession, lockType);
2483 if (!comMachine.isOk())
2484 {
2485 msgCenter().cannotOpenSession(comMachine);
2486 break;
2487 }
2488
2489 /* Pass the language ID as the property to the guest: */
2490 if (comSession.GetType() == KSessionType_Shared)
2491 {
2492 CMachine comStartedMachine = comSession.GetMachine();
2493 /* Make sure that the language is in two letter code.
2494 * Note: if languageId() returns an empty string lang.name() will
2495 * return "C" which is an valid language code. */
2496 QLocale lang(UICommon::languageId());
2497 comStartedMachine.SetGuestPropertyValue("/VirtualBox/HostInfo/GUI/LanguageID", lang.name());
2498 }
2499
2500 /* Success finally: */
2501 fSuccess = true;
2502 }
2503 while (0);
2504 /* Cleanup try-catch block: */
2505 if (!fSuccess)
2506 comSession.detach();
2507
2508 /* Return session: */
2509 return comSession;
2510}
2511
2512CSession UICommon::tryToOpenSessionFor(CMachine &comMachine)
2513{
2514 /* Prepare session: */
2515 CSession comSession;
2516
2517 /* Session state unlocked? */
2518 if (comMachine.GetSessionState() == KSessionState_Unlocked)
2519 {
2520 /* Open own 'write' session: */
2521 comSession = openSession(comMachine.GetId());
2522 AssertReturn(!comSession.isNull(), CSession());
2523 comMachine = comSession.GetMachine();
2524 }
2525 /* Is this a Selector UI call? */
2526 else if (uiType() == UIType_SelectorUI)
2527 {
2528 /* Open existing 'shared' session: */
2529 comSession = openExistingSession(comMachine.GetId());
2530 AssertReturn(!comSession.isNull(), CSession());
2531 comMachine = comSession.GetMachine();
2532 }
2533 /* Else this is Runtime UI call
2534 * which has session locked for itself. */
2535
2536 /* Return session: */
2537 return comSession;
2538}
2539
2540void UICommon::enumerateMedia(const CMediumVector &comMedia /* = CMediumVector() */)
2541{
2542 /* Make sure UICommon is already valid: */
2543 AssertReturnVoid(m_fValid);
2544 /* Ignore the request during UICommon cleanup: */
2545 if (s_fCleaningUp)
2546 return;
2547 /* Ignore the request during startup snapshot restoring: */
2548 if (shouldRestoreCurrentSnapshot())
2549 return;
2550
2551 /* Make sure medium-enumerator is already created: */
2552 if (!m_pMediumEnumerator)
2553 return;
2554
2555 /* Redirect request to medium-enumerator under proper lock: */
2556 if (m_meCleanupProtectionToken.tryLockForRead())
2557 {
2558 if (m_pMediumEnumerator)
2559 m_pMediumEnumerator->enumerateMedia(comMedia);
2560 m_meCleanupProtectionToken.unlock();
2561 }
2562}
2563
2564void UICommon::refreshMedia()
2565{
2566 /* Make sure UICommon is already valid: */
2567 AssertReturnVoid(m_fValid);
2568 /* Ignore the request during UICommon cleanup: */
2569 if (s_fCleaningUp)
2570 return;
2571 /* Ignore the request during startup snapshot restoring: */
2572 if (shouldRestoreCurrentSnapshot())
2573 return;
2574
2575 /* Make sure medium-enumerator is already created: */
2576 if (!m_pMediumEnumerator)
2577 return;
2578 /* Make sure enumeration is not already started: */
2579 if (m_pMediumEnumerator->isMediumEnumerationInProgress())
2580 return;
2581
2582 /* We assume it's safe to call it without locking,
2583 * since we are performing blocking operation here. */
2584 m_pMediumEnumerator->refreshMedia();
2585}
2586
2587bool UICommon::isFullMediumEnumerationRequested() const
2588{
2589 /* Redirect request to medium-enumerator: */
2590 return m_pMediumEnumerator
2591 && m_pMediumEnumerator->isFullMediumEnumerationRequested();
2592}
2593
2594bool UICommon::isMediumEnumerationInProgress() const
2595{
2596 /* Redirect request to medium-enumerator: */
2597 return m_pMediumEnumerator
2598 && m_pMediumEnumerator->isMediumEnumerationInProgress();
2599}
2600
2601UIMedium UICommon::medium(const QUuid &uMediumID) const
2602{
2603 if (m_meCleanupProtectionToken.tryLockForRead())
2604 {
2605 /* Redirect call to medium-enumerator: */
2606 UIMedium guiMedium;
2607 if (m_pMediumEnumerator)
2608 guiMedium = m_pMediumEnumerator->medium(uMediumID);
2609 m_meCleanupProtectionToken.unlock();
2610 return guiMedium;
2611 }
2612 return UIMedium();
2613}
2614
2615QList<QUuid> UICommon::mediumIDs() const
2616{
2617 if (m_meCleanupProtectionToken.tryLockForRead())
2618 {
2619 /* Redirect call to medium-enumerator: */
2620 QList<QUuid> listOfMedia;
2621 if (m_pMediumEnumerator)
2622 listOfMedia = m_pMediumEnumerator->mediumIDs();
2623 m_meCleanupProtectionToken.unlock();
2624 return listOfMedia;
2625 }
2626 return QList<QUuid>();
2627}
2628
2629void UICommon::createMedium(const UIMedium &guiMedium)
2630{
2631 if (m_meCleanupProtectionToken.tryLockForRead())
2632 {
2633 /* Create medium in medium-enumerator: */
2634 if (m_pMediumEnumerator)
2635 m_pMediumEnumerator->createMedium(guiMedium);
2636 m_meCleanupProtectionToken.unlock();
2637 }
2638}
2639
2640QUuid UICommon::openMedium(UIMediumDeviceType enmMediumType, QString strMediumLocation, QWidget *pParent /* = 0 */)
2641{
2642 /* Convert to native separators: */
2643 strMediumLocation = QDir::toNativeSeparators(strMediumLocation);
2644
2645 /* Initialize variables: */
2646 CVirtualBox comVBox = virtualBox();
2647
2648 /* Open corresponding medium: */
2649 CMedium comMedium = comVBox.OpenMedium(strMediumLocation, mediumTypeToGlobal(enmMediumType), KAccessMode_ReadWrite, false);
2650
2651 if (comVBox.isOk())
2652 {
2653 /* Prepare vbox medium wrapper: */
2654 UIMedium guiMedium = medium(comMedium.GetId());
2655
2656 /* First of all we should test if that medium already opened: */
2657 if (guiMedium.isNull())
2658 {
2659 /* And create new otherwise: */
2660 guiMedium = UIMedium(comMedium, enmMediumType, KMediumState_Created);
2661 createMedium(guiMedium);
2662 }
2663
2664 /* Return guiMedium id: */
2665 return guiMedium.id();
2666 }
2667 else
2668 msgCenter().cannotOpenMedium(comVBox, strMediumLocation, pParent);
2669
2670 return QUuid();
2671}
2672
2673QUuid UICommon::openMediumWithFileOpenDialog(UIMediumDeviceType enmMediumType, QWidget *pParent,
2674 const QString &strDefaultFolder /* = QString() */,
2675 bool fUseLastFolder /* = false */)
2676{
2677 /* Initialize variables: */
2678 QList<QPair <QString, QString> > filters;
2679 QStringList backends;
2680 QStringList prefixes;
2681 QString strFilter;
2682 QString strTitle;
2683 QString allType;
2684 QString strLastFolder = defaultFolderPathForType(enmMediumType);
2685
2686 /* For DVDs and Floppies always check first the last recently used medium folder. For hard disk use
2687 the caller's setting: */
2688 fUseLastFolder = (enmMediumType == UIMediumDeviceType_DVD) || (enmMediumType == UIMediumDeviceType_Floppy);
2689
2690 switch (enmMediumType)
2691 {
2692 case UIMediumDeviceType_HardDisk:
2693 {
2694 filters = HDDBackends(virtualBox());
2695 strTitle = tr("Please choose a virtual hard disk file");
2696 allType = tr("All virtual hard disk files (%1)");
2697 break;
2698 }
2699 case UIMediumDeviceType_DVD:
2700 {
2701 filters = DVDBackends(virtualBox());
2702 strTitle = tr("Please choose a virtual optical disk file");
2703 allType = tr("All virtual optical disk files (%1)");
2704 break;
2705 }
2706 case UIMediumDeviceType_Floppy:
2707 {
2708 filters = FloppyBackends(virtualBox());
2709 strTitle = tr("Please choose a virtual floppy disk file");
2710 allType = tr("All virtual floppy disk files (%1)");
2711 break;
2712 }
2713 default:
2714 break;
2715 }
2716 QString strHomeFolder = fUseLastFolder && !strLastFolder.isEmpty() ? strLastFolder :
2717 strDefaultFolder.isEmpty() ? homeFolder() : strDefaultFolder;
2718
2719 /* Prepare filters and backends: */
2720 for (int i = 0; i < filters.count(); ++i)
2721 {
2722 /* Get iterated filter: */
2723 QPair<QString, QString> item = filters.at(i);
2724 /* Create one backend filter string: */
2725 backends << QString("%1 (%2)").arg(item.first).arg(item.second);
2726 /* Save the suffix's for the "All" entry: */
2727 prefixes << item.second;
2728 }
2729 if (!prefixes.isEmpty())
2730 backends.insert(0, allType.arg(prefixes.join(" ").trimmed()));
2731 backends << tr("All files (*)");
2732 strFilter = backends.join(";;").trimmed();
2733
2734 /* Create open file dialog: */
2735 QStringList files = QIFileDialog::getOpenFileNames(strHomeFolder, strFilter, pParent, strTitle, 0, true, true);
2736
2737 /* If dialog has some result: */
2738 if (!files.empty() && !files[0].isEmpty())
2739 {
2740 QUuid uMediumId = openMedium(enmMediumType, files[0], pParent);
2741 if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy ||
2742 (enmMediumType == UIMediumDeviceType_HardDisk && fUseLastFolder))
2743 updateRecentlyUsedMediumListAndFolder(enmMediumType, medium(uMediumId).location());
2744 return uMediumId;
2745 }
2746 return QUuid();
2747}
2748
2749
2750/**
2751 * Helper for createVisoMediumWithVisoCreator.
2752 * @returns IPRT status code.
2753 * @param pStrmDst Where to write the quoted string.
2754 * @param pszPrefix Stuff to put in front of it.
2755 * @param rStr The string to quote and write out.
2756 * @param pszPrefix Stuff to put after it.
2757 */
2758DECLINLINE(int) visoWriteQuotedString(PRTSTREAM pStrmDst, const char *pszPrefix, QString const &rStr, const char *pszPostFix)
2759{
2760 QByteArray const utf8Array = rStr.toUtf8();
2761 const char *apszArgv[2] = { utf8Array.constData(), NULL };
2762 char *pszQuoted;
2763 int vrc = RTGetOptArgvToString(&pszQuoted, apszArgv, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH);
2764 if (RT_SUCCESS(vrc))
2765 {
2766 if (pszPrefix)
2767 vrc = RTStrmPutStr(pStrmDst, pszPrefix);
2768 if (RT_SUCCESS(vrc))
2769 {
2770 vrc = RTStrmPutStr(pStrmDst, pszQuoted);
2771 if (pszPostFix && RT_SUCCESS(vrc))
2772 vrc = RTStrmPutStr(pStrmDst, pszPostFix);
2773 }
2774 RTStrFree(pszQuoted);
2775 }
2776
2777 return vrc;
2778}
2779
2780
2781QUuid UICommon::openMediumCreatorDialog(QWidget *pParent, UIMediumDeviceType enmMediumType,
2782 const QString &strDefaultFolder /* = QString() */, const QString &strMachineName /* = QString() */,
2783 const QString &strMachineGuestOSTypeId /*= QString() */)
2784{
2785 QUuid uMediumId;
2786
2787 switch (enmMediumType)
2788 {
2789 case UIMediumDeviceType_Floppy:
2790 uMediumId = showCreateFloppyDiskDialog(pParent, strDefaultFolder, strMachineName);
2791 break;
2792 case UIMediumDeviceType_HardDisk:
2793 uMediumId = createHDWithNewHDWizard(pParent, strDefaultFolder, strMachineName, strMachineGuestOSTypeId);
2794 break;
2795 case UIMediumDeviceType_DVD:
2796 uMediumId = createVisoMediumWithVisoCreator(pParent, strDefaultFolder, strMachineName);
2797 break;
2798 default:
2799 break;
2800 }
2801 if (!uMediumId.isNull())
2802 {
2803 /* Update the recent medium list only if the mdium type is DVD or floppy. In case of hard disk
2804 update those only if there is no vm context: */
2805 if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy ||
2806 (enmMediumType == UIMediumDeviceType_HardDisk && strMachineName.isEmpty()))
2807 updateRecentlyUsedMediumListAndFolder(enmMediumType, medium(uMediumId).location());
2808 }
2809 return uMediumId;
2810}
2811
2812QUuid UICommon::createVisoMediumWithVisoCreator(QWidget *pParent, const QString &strDefaultFolder /* = QString */,
2813 const QString &strMachineName /* = QString */)
2814{
2815 QString strVisoSaveFolder(strDefaultFolder);
2816 if (strVisoSaveFolder.isEmpty())
2817 strVisoSaveFolder = defaultFolderPathForType(UIMediumDeviceType_DVD);
2818
2819 QWidget *pDialogParent = windowManager().realParentWindow(pParent);
2820 UIVisoCreator *pVisoCreator = new UIVisoCreator(pDialogParent, strMachineName);
2821
2822 if (!pVisoCreator)
2823 return QString();
2824 windowManager().registerNewParent(pVisoCreator, pDialogParent);
2825 pVisoCreator->setCurrentPath(gEDataManager->visoCreatorRecentFolder());
2826
2827 if (pVisoCreator->exec(false /* not application modal */))
2828 {
2829 QStringList files = pVisoCreator->entryList();
2830 QString strVisoName = pVisoCreator->visoName();
2831 if (strVisoName.isEmpty())
2832 strVisoName = strMachineName;
2833
2834 if (files.empty() || files[0].isEmpty())
2835 {
2836 delete pVisoCreator;
2837 return QUuid();
2838 }
2839
2840 gEDataManager->setVISOCreatorRecentFolder(pVisoCreator->currentPath());
2841
2842 /* Produce the VISO. */
2843 char szVisoPath[RTPATH_MAX];
2844 QString strFileName = QString("%1%2").arg(strVisoName).arg(".viso");
2845 int vrc = RTPathJoin(szVisoPath, sizeof(szVisoPath), strVisoSaveFolder.toUtf8().constData(), strFileName.toUtf8().constData());
2846 if (RT_SUCCESS(vrc))
2847 {
2848 PRTSTREAM pStrmViso;
2849 vrc = RTStrmOpen(szVisoPath, "w", &pStrmViso);
2850 if (RT_SUCCESS(vrc))
2851 {
2852 RTUUID Uuid;
2853 vrc = RTUuidCreate(&Uuid);
2854 if (RT_SUCCESS(vrc))
2855 {
2856 RTStrmPrintf(pStrmViso, "--iprt-iso-maker-file-marker-bourne-sh %RTuuid\n", &Uuid);
2857 vrc = visoWriteQuotedString(pStrmViso, "--volume-id=", strVisoName, "\n");
2858
2859 for (int iFile = 0; iFile < files.size() && RT_SUCCESS(vrc); iFile++)
2860 vrc = visoWriteQuotedString(pStrmViso, NULL, files[iFile], "\n");
2861
2862 /* Append custom options if any to the file: */
2863 const QStringList &customOptions = pVisoCreator->customOptions();
2864 foreach (QString strLine, customOptions)
2865 RTStrmPrintf(pStrmViso, "%s\n", strLine.toUtf8().constData());
2866
2867 RTStrmFlush(pStrmViso);
2868 if (RT_SUCCESS(vrc))
2869 vrc = RTStrmError(pStrmViso);
2870 }
2871
2872 RTStrmClose(pStrmViso);
2873 }
2874 }
2875
2876 /* Done. */
2877 if (RT_SUCCESS(vrc))
2878 {
2879 delete pVisoCreator;
2880 return openMedium(UIMediumDeviceType_DVD, QString(szVisoPath), pParent);
2881 }
2882 /** @todo error message. */
2883 else
2884 {
2885 delete pVisoCreator;
2886 return QUuid();
2887 }
2888 }
2889 delete pVisoCreator;
2890 return QUuid();
2891}
2892
2893QUuid UICommon::showCreateFloppyDiskDialog(QWidget *pParent, const QString &strDefaultFolder /* QString() */,
2894 const QString &strMachineName /* = QString() */ )
2895{
2896 QString strStartPath(strDefaultFolder);
2897
2898 if (strStartPath.isEmpty())
2899 strStartPath = defaultFolderPathForType(UIMediumDeviceType_Floppy);
2900
2901 QWidget *pDialogParent = windowManager().realParentWindow(pParent);
2902
2903 UIFDCreationDialog *pDialog = new UIFDCreationDialog(pParent, strStartPath, strMachineName);
2904 if (!pDialog)
2905 return QUuid();
2906 windowManager().registerNewParent(pDialog, pDialogParent);
2907
2908 if (pDialog->exec())
2909 {
2910 delete pDialog;
2911 return pDialog->mediumID();
2912 }
2913 delete pDialog;
2914 return QUuid();
2915}
2916
2917int UICommon::openMediumSelectorDialog(QWidget *pParent, UIMediumDeviceType enmMediumType, QUuid &outUuid,
2918 const QString &strMachineFolder, const QString &strMachineName,
2919 const QString &strMachineGuestOSTypeId, bool fEnableCreate, const QUuid &uMachineID /* = QUuid() */)
2920{
2921 QUuid uMachineOrGlobalId = uMachineID == QUuid() ? gEDataManager->GlobalID : uMachineID;
2922
2923 QWidget *pDialogParent = windowManager().realParentWindow(pParent);
2924 QPointer<UIMediumSelector> pSelector = new UIMediumSelector(enmMediumType, strMachineName,
2925 strMachineFolder, strMachineGuestOSTypeId,
2926 uMachineOrGlobalId, pDialogParent);
2927
2928 if (!pSelector)
2929 return static_cast<int>(UIMediumSelector::ReturnCode_Rejected);
2930 pSelector->setEnableCreateAction(fEnableCreate);
2931 windowManager().registerNewParent(pSelector, pDialogParent);
2932
2933 int iResult = pSelector->exec(false);
2934 UIMediumSelector::ReturnCode returnCode;
2935
2936 if (iResult >= static_cast<int>(UIMediumSelector::ReturnCode_Max) || iResult < 0)
2937 returnCode = UIMediumSelector::ReturnCode_Rejected;
2938 else
2939 returnCode = static_cast<UIMediumSelector::ReturnCode>(iResult);
2940
2941 if (returnCode == UIMediumSelector::ReturnCode_Accepted)
2942 {
2943 QList<QUuid> selectedMediumIds = pSelector->selectedMediumIds();
2944
2945 /* Currently we only care about the 0th since we support single selection by intention: */
2946 if (selectedMediumIds.isEmpty())
2947 returnCode = UIMediumSelector::ReturnCode_Rejected;
2948 else
2949 {
2950 outUuid = selectedMediumIds[0];
2951 updateRecentlyUsedMediumListAndFolder(enmMediumType, medium(outUuid).location());
2952 }
2953 }
2954 delete pSelector;
2955 return static_cast<int>(returnCode);
2956}
2957
2958QUuid UICommon::createHDWithNewHDWizard(QWidget *pParent, const QString &strMachineFolder /* = QString() */,
2959 const QString &strMachineName /* = QString() */,
2960 const QString &strMachineGuestOSTypeId /* = QString() */)
2961{
2962 /* Initialize variables: */
2963 QString strDefaultFolder(strMachineFolder);
2964 if (strDefaultFolder.isEmpty())
2965 strDefaultFolder = defaultFolderPathForType(UIMediumDeviceType_HardDisk);
2966
2967 /* In case we dont have a 'guest os type id' default back to 'Other': */
2968 const CGuestOSType comGuestOSType = virtualBox().GetGuestOSType(!strMachineGuestOSTypeId.isEmpty() ? strMachineGuestOSTypeId : "Other");
2969 QString strDiskName = findUniqueFileName(strDefaultFolder,
2970 !strMachineName.isEmpty() ? strMachineName : "NewVirtualDisk");
2971 /* Show New VD wizard: */
2972 UISafePointerWizardNewVD pWizard = new UIWizardNewVD(pParent, strDiskName, strDefaultFolder, comGuestOSType.GetRecommendedHDD());
2973
2974 if (!pWizard)
2975 return QUuid();
2976 QWidget *pDialogParent = windowManager().realParentWindow(pParent);
2977 windowManager().registerNewParent(pWizard, pDialogParent);
2978 pWizard->prepare();
2979
2980 const QUuid uResult = pWizard->exec() == QDialog::Accepted ? pWizard->virtualDisk().GetId() : QUuid();
2981 if (pWizard)
2982 delete pWizard;
2983 return uResult;
2984}
2985
2986void UICommon::prepareStorageMenu(QMenu &menu,
2987 QObject *pListener, const char *pszSlotName,
2988 const CMachine &comMachine, const QString &strControllerName, const StorageSlot &storageSlot)
2989{
2990 /* Current attachment attributes: */
2991 const CMediumAttachment comCurrentAttachment = comMachine.GetMediumAttachment(strControllerName,
2992 storageSlot.port,
2993 storageSlot.device);
2994 const CMedium comCurrentMedium = comCurrentAttachment.GetMedium();
2995 const QUuid uCurrentID = comCurrentMedium.isNull() ? QUuid() : comCurrentMedium.GetId();
2996 const QString strCurrentLocation = comCurrentMedium.isNull() ? QString() : comCurrentMedium.GetLocation();
2997
2998 /* Other medium-attachments of same machine: */
2999 const CMediumAttachmentVector comAttachments = comMachine.GetMediumAttachments();
3000
3001 /* Determine device & medium types: */
3002 const UIMediumDeviceType enmMediumType = mediumTypeToLocal(comCurrentAttachment.GetType());
3003 AssertMsgReturnVoid(enmMediumType != UIMediumDeviceType_Invalid, ("Incorrect storage medium type!\n"));
3004
3005 /* Prepare open-existing-medium action: */
3006 QAction *pActionOpenExistingMedium = menu.addAction(UIIconPool::iconSet(":/select_file_16px.png"),
3007 QString(), pListener, pszSlotName);
3008 pActionOpenExistingMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
3009 comCurrentAttachment.GetDevice(), enmMediumType)));
3010 pActionOpenExistingMedium->setText(QApplication::translate("UIMachineSettingsStorage", "Choose/Create a disk image..."));
3011
3012
3013 /* Prepare open medium file action: */
3014 QAction *pActionFileSelector = menu.addAction(UIIconPool::iconSet(":/select_file_16px.png"),
3015 QString(), pListener, pszSlotName);
3016 pActionFileSelector->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
3017 comCurrentAttachment.GetDevice(), enmMediumType,
3018 UIMediumTarget::UIMediumTargetType_WithFileDialog)));
3019 pActionFileSelector->setText(QApplication::translate("UIMachineSettingsStorage", "Choose a disk file..."));
3020
3021
3022 /* Insert separator: */
3023 menu.addSeparator();
3024
3025 /* Get existing-host-drive vector: */
3026 CMediumVector comMedia;
3027 switch (enmMediumType)
3028 {
3029 case UIMediumDeviceType_DVD: comMedia = host().GetDVDDrives(); break;
3030 case UIMediumDeviceType_Floppy: comMedia = host().GetFloppyDrives(); break;
3031 default: break;
3032 }
3033 /* Prepare choose-existing-host-drive actions: */
3034 foreach (const CMedium &comMedium, comMedia)
3035 {
3036 /* Make sure host-drive usage is unique: */
3037 bool fIsHostDriveUsed = false;
3038 foreach (const CMediumAttachment &comOtherAttachment, comAttachments)
3039 {
3040 if (comOtherAttachment != comCurrentAttachment)
3041 {
3042 const CMedium &comOtherMedium = comOtherAttachment.GetMedium();
3043 if (!comOtherMedium.isNull() && comOtherMedium.GetId() == comMedium.GetId())
3044 {
3045 fIsHostDriveUsed = true;
3046 break;
3047 }
3048 }
3049 }
3050 /* If host-drives usage is unique: */
3051 if (!fIsHostDriveUsed)
3052 {
3053 QAction *pActionChooseHostDrive = menu.addAction(UIMedium(comMedium, enmMediumType).name(), pListener, pszSlotName);
3054 pActionChooseHostDrive->setCheckable(true);
3055 pActionChooseHostDrive->setChecked(!comCurrentMedium.isNull() && comMedium.GetId() == uCurrentID);
3056 pActionChooseHostDrive->setData(QVariant::fromValue(UIMediumTarget(strControllerName,
3057 comCurrentAttachment.GetPort(),
3058 comCurrentAttachment.GetDevice(),
3059 enmMediumType,
3060 UIMediumTarget::UIMediumTargetType_WithID,
3061 comMedium.GetId().toString())));
3062 }
3063 }
3064
3065 /* Get recent-medium list: */
3066 QStringList recentMediumList;
3067 QStringList recentMediumListUsed;
3068 switch (enmMediumType)
3069 {
3070 case UIMediumDeviceType_HardDisk: recentMediumList = gEDataManager->recentListOfHardDrives(); break;
3071 case UIMediumDeviceType_DVD: recentMediumList = gEDataManager->recentListOfOpticalDisks(); break;
3072 case UIMediumDeviceType_Floppy: recentMediumList = gEDataManager->recentListOfFloppyDisks(); break;
3073 default: break;
3074 }
3075 /* Prepare choose-recent-medium actions: */
3076 foreach (const QString &strRecentMediumLocationBase, recentMediumList)
3077 {
3078 /* Confirm medium uniqueness: */
3079 if (recentMediumListUsed.contains(strRecentMediumLocationBase))
3080 continue;
3081 /* Mark medium as known: */
3082 recentMediumListUsed << strRecentMediumLocationBase;
3083 /* Convert separators to native: */
3084 const QString strRecentMediumLocation = QDir::toNativeSeparators(strRecentMediumLocationBase);
3085 /* Confirm medium presence: */
3086 if (!QFile::exists(strRecentMediumLocation))
3087 continue;
3088 /* Make sure recent-medium usage is unique: */
3089 bool fIsRecentMediumUsed = false;
3090 foreach (const CMediumAttachment &otherAttachment, comAttachments)
3091 {
3092 if (otherAttachment != comCurrentAttachment)
3093 {
3094 const CMedium &comOtherMedium = otherAttachment.GetMedium();
3095 if (!comOtherMedium.isNull() && comOtherMedium.GetLocation() == strRecentMediumLocation)
3096 {
3097 fIsRecentMediumUsed = true;
3098 break;
3099 }
3100 }
3101 }
3102 /* If recent-medium usage is unique: */
3103 if (!fIsRecentMediumUsed)
3104 {
3105 QAction *pActionChooseRecentMedium = menu.addAction(QFileInfo(strRecentMediumLocation).fileName(),
3106 pListener, pszSlotName);
3107 pActionChooseRecentMedium->setCheckable(true);
3108 pActionChooseRecentMedium->setChecked(!comCurrentMedium.isNull() && strRecentMediumLocation == strCurrentLocation);
3109 pActionChooseRecentMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName,
3110 comCurrentAttachment.GetPort(),
3111 comCurrentAttachment.GetDevice(),
3112 enmMediumType,
3113 UIMediumTarget::UIMediumTargetType_WithLocation,
3114 strRecentMediumLocation)));
3115 pActionChooseRecentMedium->setToolTip(strRecentMediumLocation);
3116 }
3117 }
3118
3119 /* Last action for optical/floppy attachments only: */
3120 if (enmMediumType == UIMediumDeviceType_DVD || enmMediumType == UIMediumDeviceType_Floppy)
3121 {
3122 /* Insert separator: */
3123 menu.addSeparator();
3124
3125 /* Prepare unmount-current-medium action: */
3126 QAction *pActionUnmountMedium = menu.addAction(QString(), pListener, pszSlotName);
3127 pActionUnmountMedium->setEnabled(!comCurrentMedium.isNull());
3128 pActionUnmountMedium->setData(QVariant::fromValue(UIMediumTarget(strControllerName, comCurrentAttachment.GetPort(),
3129 comCurrentAttachment.GetDevice())));
3130 pActionUnmountMedium->setText(QApplication::translate("UIMachineSettingsStorage", "Remove disk from virtual drive"));
3131 if (enmMediumType == UIMediumDeviceType_DVD)
3132 pActionUnmountMedium->setIcon(UIIconPool::iconSet(":/cd_unmount_16px.png", ":/cd_unmount_disabled_16px.png"));
3133 else if (enmMediumType == UIMediumDeviceType_Floppy)
3134 pActionUnmountMedium->setIcon(UIIconPool::iconSet(":/fd_unmount_16px.png", ":/fd_unmount_disabled_16px.png"));
3135 }
3136}
3137
3138void UICommon::updateMachineStorage(const CMachine &comConstMachine, const UIMediumTarget &target)
3139{
3140 /* Mount (by default): */
3141 bool fMount = true;
3142 /* Null medium (by default): */
3143 CMedium comMedium;
3144 /* With null ID (by default): */
3145 QUuid uActualID;
3146
3147 /* Current mount-target attributes: */
3148 const CStorageController comCurrentController = comConstMachine.GetStorageControllerByName(target.name);
3149 const KStorageBus enmCurrentStorageBus = comCurrentController.GetBus();
3150 const CMediumAttachment comCurrentAttachment = comConstMachine.GetMediumAttachment(target.name, target.port, target.device);
3151 const CMedium comCurrentMedium = comCurrentAttachment.GetMedium();
3152 const QUuid uCurrentID = comCurrentMedium.isNull() ? QUuid() : comCurrentMedium.GetId();
3153 const QString strCurrentLocation = comCurrentMedium.isNull() ? QString() : comCurrentMedium.GetLocation();
3154
3155 /* Which additional info do we have? */
3156 switch (target.type)
3157 {
3158 /* Do we have an exact ID or do we let the user open a medium? */
3159 case UIMediumTarget::UIMediumTargetType_WithID:
3160 case UIMediumTarget::UIMediumTargetType_WithFileDialog:
3161 case UIMediumTarget::UIMediumTargetType_CreateAdHocVISO:
3162 case UIMediumTarget::UIMediumTargetType_CreateFloppyDisk:
3163 {
3164 /* New mount-target attributes: */
3165 QUuid uNewID;
3166
3167 /* Invoke file-open dialog to choose medium ID: */
3168 if (target.mediumType != UIMediumDeviceType_Invalid && target.data.isNull())
3169 {
3170 /* Keyboard can be captured by machine-view.
3171 * So we should clear machine-view focus to let file-open dialog get it.
3172 * That way the keyboard will be released too.. */
3173 QWidget *pLastFocusedWidget = 0;
3174 if (QApplication::focusWidget())
3175 {
3176 pLastFocusedWidget = QApplication::focusWidget();
3177 pLastFocusedWidget->clearFocus();
3178 }
3179 /* Call for file-open dialog: */
3180 const QString strMachineFolder(QFileInfo(comConstMachine.GetSettingsFilePath()).absolutePath());
3181 QUuid uMediumID;
3182 if (target.type == UIMediumTarget::UIMediumTargetType_WithID)
3183 {
3184 int iDialogReturn = openMediumSelectorDialog(windowManager().mainWindowShown(), target.mediumType, uMediumID,
3185 strMachineFolder, comConstMachine.GetName(),
3186 comConstMachine.GetOSTypeId(), true /*fEnableCreate */, comConstMachine.GetId());
3187 if (iDialogReturn == UIMediumSelector::ReturnCode_LeftEmpty &&
3188 (target.mediumType == UIMediumDeviceType_DVD || target.mediumType == UIMediumDeviceType_Floppy))
3189 fMount = false;
3190 }
3191 else if (target.type == UIMediumTarget::UIMediumTargetType_WithFileDialog)
3192 {
3193 uMediumID = openMediumWithFileOpenDialog(target.mediumType, windowManager().mainWindowShown(),
3194 strMachineFolder, false /* fUseLastFolder */);
3195 }
3196 else if(target.type == UIMediumTarget::UIMediumTargetType_CreateAdHocVISO)
3197 uMediumID = createVisoMediumWithVisoCreator(windowManager().mainWindowShown(), strMachineFolder, comConstMachine.GetName());
3198
3199 else if(target.type == UIMediumTarget::UIMediumTargetType_CreateFloppyDisk)
3200 uMediumID = showCreateFloppyDiskDialog(windowManager().mainWindowShown(), strMachineFolder, comConstMachine.GetName());
3201
3202 /* Return focus back: */
3203 if (pLastFocusedWidget)
3204 pLastFocusedWidget->setFocus();
3205 /* Accept new medium ID: */
3206 if (!uMediumID.isNull())
3207 uNewID = uMediumID;
3208 else
3209 /* Else just exit in case left empty is not chosen in medium selector dialog: */
3210 if (fMount)
3211 return;
3212 }
3213 /* Use medium ID which was passed: */
3214 else if (!target.data.isNull() && target.data != uCurrentID.toString())
3215 uNewID = target.data;
3216
3217 /* Should we mount or unmount? */
3218 fMount = !uNewID.isNull();
3219
3220 /* Prepare target medium: */
3221 const UIMedium guiMedium = medium(uNewID);
3222 comMedium = guiMedium.medium();
3223 uActualID = fMount ? uNewID : uCurrentID;
3224 break;
3225 }
3226 /* Do we have a recent location? */
3227 case UIMediumTarget::UIMediumTargetType_WithLocation:
3228 {
3229 /* Open medium by location and get new medium ID if any: */
3230 const QUuid uNewID = openMedium(target.mediumType, target.data);
3231 /* Else just exit: */
3232 if (uNewID.isNull())
3233 return;
3234
3235 /* Should we mount or unmount? */
3236 fMount = uNewID != uCurrentID;
3237
3238 /* Prepare target medium: */
3239 const UIMedium guiMedium = fMount ? medium(uNewID) : UIMedium();
3240 comMedium = fMount ? guiMedium.medium() : CMedium();
3241 uActualID = fMount ? uNewID : uCurrentID;
3242 break;
3243 }
3244 }
3245
3246 /* Do not unmount hard-drives: */
3247 if (target.mediumType == UIMediumDeviceType_HardDisk && !fMount)
3248 return;
3249
3250 /* Get editable machine & session: */
3251 CMachine comMachine = comConstMachine;
3252 CSession comSession = tryToOpenSessionFor(comMachine);
3253
3254 /* Remount medium to the predefined port/device: */
3255 bool fWasMounted = false;
3256 /* Hard drive case: */
3257 if (target.mediumType == UIMediumDeviceType_HardDisk)
3258 {
3259 /* Detaching: */
3260 comMachine.DetachDevice(target.name, target.port, target.device);
3261 fWasMounted = comMachine.isOk();
3262 if (!fWasMounted)
3263 msgCenter().cannotDetachDevice(comMachine, UIMediumDeviceType_HardDisk, strCurrentLocation,
3264 StorageSlot(enmCurrentStorageBus, target.port, target.device));
3265 else
3266 {
3267 /* Attaching: */
3268 comMachine.AttachDevice(target.name, target.port, target.device, KDeviceType_HardDisk, comMedium);
3269 fWasMounted = comMachine.isOk();
3270 if (!fWasMounted)
3271 msgCenter().cannotAttachDevice(comMachine, UIMediumDeviceType_HardDisk, strCurrentLocation,
3272 StorageSlot(enmCurrentStorageBus, target.port, target.device));
3273 }
3274 }
3275 /* Optical/floppy drive case: */
3276 else
3277 {
3278 /* Remounting: */
3279 comMachine.MountMedium(target.name, target.port, target.device, comMedium, false /* force? */);
3280 fWasMounted = comMachine.isOk();
3281 if (!fWasMounted)
3282 {
3283 /* Ask for force remounting: */
3284 if (msgCenter().cannotRemountMedium(comMachine, medium(uActualID),
3285 fMount, true /* retry? */))
3286 {
3287 /* Force remounting: */
3288 comMachine.MountMedium(target.name, target.port, target.device, comMedium, true /* force? */);
3289 fWasMounted = comMachine.isOk();
3290 if (!fWasMounted)
3291 msgCenter().cannotRemountMedium(comMachine, medium(uActualID),
3292 fMount, false /* retry? */);
3293 }
3294 }
3295 /* If mounting was successful: */
3296 if (fWasMounted)
3297 {
3298 /* Disable First RUN Wizard: */
3299 if (gEDataManager->machineFirstTimeStarted(comMachine.GetId()))
3300 gEDataManager->setMachineFirstTimeStarted(false, comMachine.GetId());
3301 }
3302 }
3303
3304 /* Save settings: */
3305 if (fWasMounted)
3306 {
3307 comMachine.SaveSettings();
3308 if (!comMachine.isOk())
3309 msgCenter().cannotSaveMachineSettings(comMachine, windowManager().mainWindowShown());
3310 }
3311
3312 /* Close session to editable comMachine if necessary: */
3313 if (!comSession.isNull())
3314 comSession.UnlockMachine();
3315}
3316
3317QString UICommon::details(const CMedium &comMedium, bool fPredictDiff, bool fUseHtml /* = true */)
3318{
3319 /* Search for corresponding UI medium: */
3320 const QUuid uMediumID = comMedium.isNull() ? UIMedium::nullID() : comMedium.GetId();
3321 UIMedium guiMedium = medium(uMediumID);
3322 if (!comMedium.isNull() && guiMedium.isNull())
3323 {
3324 /* UI medium may be new and not among cached media, request enumeration: */
3325 enumerateMedia(CMediumVector() << comMedium);
3326
3327 /* Search for corresponding UI medium again: */
3328 guiMedium = medium(uMediumID);
3329 if (guiMedium.isNull())
3330 {
3331 /* Medium might be deleted already, return null string: */
3332 return QString();
3333 }
3334 }
3335
3336 /* For differencing hard-disk we have to request
3337 * enumeration of whole tree based in it's root item: */
3338 if ( comMedium.isNotNull()
3339 && comMedium.GetDeviceType() == KDeviceType_HardDisk)
3340 {
3341 /* Traverse through parents to root to catch it: */
3342 CMedium comRootMedium;
3343 CMedium comParentMedium = comMedium.GetParent();
3344 while (comParentMedium.isNotNull())
3345 {
3346 comRootMedium = comParentMedium;
3347 comParentMedium = comParentMedium.GetParent();
3348 }
3349 /* Enumerate root if it's found and wasn't cached: */
3350 if (comRootMedium.isNotNull())
3351 {
3352 const QUuid uRootId = comRootMedium.GetId();
3353 if (medium(uRootId).isNull())
3354 enumerateMedia(CMediumVector() << comRootMedium);
3355 }
3356 }
3357
3358 /* Return UI medium details: */
3359 return fUseHtml ? guiMedium.detailsHTML(true /* no diffs? */, fPredictDiff) :
3360 guiMedium.details(true /* no diffs? */, fPredictDiff);
3361}
3362
3363void UICommon::updateRecentlyUsedMediumListAndFolder(UIMediumDeviceType enmMediumType, QString strMediumLocation)
3364{
3365 /** Don't add the medium to extra data if its name is in exclude list, m_recentMediaExcludeList: */
3366 foreach (QString strExcludeName, m_recentMediaExcludeList)
3367 {
3368 if (strMediumLocation.contains(strExcludeName))
3369 return;
3370 }
3371
3372 /* Remember the path of the last chosen medium: */
3373 switch (enmMediumType)
3374 {
3375 case UIMediumDeviceType_HardDisk: gEDataManager->setRecentFolderForHardDrives(QFileInfo(strMediumLocation).absolutePath()); break;
3376 case UIMediumDeviceType_DVD: gEDataManager->setRecentFolderForOpticalDisks(QFileInfo(strMediumLocation).absolutePath()); break;
3377 case UIMediumDeviceType_Floppy: gEDataManager->setRecentFolderForFloppyDisks(QFileInfo(strMediumLocation).absolutePath()); break;
3378 default: break;
3379 }
3380
3381 /* Update recently used list: */
3382 QStringList recentMediumList;
3383 switch (enmMediumType)
3384 {
3385 case UIMediumDeviceType_HardDisk: recentMediumList = gEDataManager->recentListOfHardDrives(); break;
3386 case UIMediumDeviceType_DVD: recentMediumList = gEDataManager->recentListOfOpticalDisks(); break;
3387 case UIMediumDeviceType_Floppy: recentMediumList = gEDataManager->recentListOfFloppyDisks(); break;
3388 default: break;
3389 }
3390 if (recentMediumList.contains(strMediumLocation))
3391 recentMediumList.removeAll(strMediumLocation);
3392 recentMediumList.prepend(strMediumLocation);
3393 while(recentMediumList.size() > 5)
3394 recentMediumList.removeLast();
3395 switch (enmMediumType)
3396 {
3397 case UIMediumDeviceType_HardDisk: gEDataManager->setRecentListOfHardDrives(recentMediumList); break;
3398 case UIMediumDeviceType_DVD: gEDataManager->setRecentListOfOpticalDisks(recentMediumList); break;
3399 case UIMediumDeviceType_Floppy: gEDataManager->setRecentListOfFloppyDisks(recentMediumList); break;
3400 default: break;
3401 }
3402}
3403
3404QString UICommon::defaultFolderPathForType(UIMediumDeviceType enmMediumType)
3405{
3406 QString strLastFolder;
3407 switch (enmMediumType)
3408 {
3409 case UIMediumDeviceType_HardDisk:
3410 strLastFolder = gEDataManager->recentFolderForHardDrives();
3411 if (strLastFolder.isEmpty())
3412 strLastFolder = gEDataManager->recentFolderForOpticalDisks();
3413 if (strLastFolder.isEmpty())
3414 strLastFolder = gEDataManager->recentFolderForFloppyDisks();
3415 break;
3416 case UIMediumDeviceType_DVD:
3417 strLastFolder = gEDataManager->recentFolderForOpticalDisks();
3418 if (strLastFolder.isEmpty())
3419 strLastFolder = gEDataManager->recentFolderForFloppyDisks();
3420 if (strLastFolder.isEmpty())
3421 strLastFolder = gEDataManager->recentFolderForHardDrives();
3422 break;
3423 case UIMediumDeviceType_Floppy:
3424 strLastFolder = gEDataManager->recentFolderForFloppyDisks();
3425 if (strLastFolder.isEmpty())
3426 strLastFolder = gEDataManager->recentFolderForOpticalDisks();
3427 if (strLastFolder.isEmpty())
3428 strLastFolder = gEDataManager->recentFolderForHardDrives();
3429 break;
3430 default:
3431 break;
3432 }
3433
3434 if (strLastFolder.isEmpty())
3435 return virtualBox().GetSystemProperties().GetDefaultMachineFolder();
3436
3437 return strLastFolder;
3438}
3439
3440#ifdef RT_OS_LINUX
3441/* static */
3442void UICommon::checkForWrongUSBMounted()
3443{
3444 /* Make sure '/proc/mounts' exists and can be opened: */
3445 QFile file("/proc/mounts");
3446 if (!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text))
3447 return;
3448
3449 /* Fetch contents: */
3450 QStringList contents;
3451 for (;;)
3452 {
3453 QByteArray line = file.readLine();
3454 if (line.isEmpty())
3455 break;
3456 contents << line;
3457 }
3458 /* Grep contents for usbfs presence: */
3459 QStringList grep1(contents.filter("/sys/bus/usb/drivers"));
3460 QStringList grep2(grep1.filter("usbfs"));
3461 if (grep2.isEmpty())
3462 return;
3463
3464 /* Show corresponding warning: */
3465 msgCenter().warnAboutWrongUSBMounted();
3466}
3467#endif /* RT_OS_LINUX */
3468
3469/* static */
3470QString UICommon::details(const CUSBDevice &comDevice)
3471{
3472 QString strDetails;
3473 if (comDevice.isNull())
3474 strDetails = tr("Unknown device", "USB device details");
3475 else
3476 {
3477 QVector<QString> devInfoVector = comDevice.GetDeviceInfo();
3478 QString strManufacturer;
3479 QString strProduct;
3480
3481 if (devInfoVector.size() >= 1)
3482 strManufacturer = devInfoVector[0].trimmed();
3483 if (devInfoVector.size() >= 2)
3484 strProduct = devInfoVector[1].trimmed();
3485
3486 if (strManufacturer.isEmpty() && strProduct.isEmpty())
3487 {
3488 strDetails =
3489 tr("Unknown device %1:%2", "USB device details")
3490 .arg(QString().sprintf("%04hX", comDevice.GetVendorId()))
3491 .arg(QString().sprintf("%04hX", comDevice.GetProductId()));
3492 }
3493 else
3494 {
3495 if (strProduct.toUpper().startsWith(strManufacturer.toUpper()))
3496 strDetails = strProduct;
3497 else
3498 strDetails = strManufacturer + " " + strProduct;
3499 }
3500 ushort iRev = comDevice.GetRevision();
3501 if (iRev != 0)
3502 strDetails += QString().sprintf(" [%04hX]", iRev);
3503 }
3504
3505 return strDetails.trimmed();
3506}
3507
3508/* static */
3509QString UICommon::toolTip(const CUSBDevice &comDevice)
3510{
3511 QString strTip =
3512 tr("<nobr>Vendor ID: %1</nobr><br>"
3513 "<nobr>Product ID: %2</nobr><br>"
3514 "<nobr>Revision: %3</nobr>", "USB device tooltip")
3515 .arg(QString().sprintf("%04hX", comDevice.GetVendorId()))
3516 .arg(QString().sprintf("%04hX", comDevice.GetProductId()))
3517 .arg(QString().sprintf("%04hX", comDevice.GetRevision()));
3518
3519 const QString strSerial = comDevice.GetSerialNumber();
3520 if (!strSerial.isEmpty())
3521 strTip += QString(tr("<br><nobr>Serial No. %1</nobr>", "USB device tooltip"))
3522 .arg(strSerial);
3523
3524 /* Add the state field if it's a host USB device: */
3525 CHostUSBDevice hostDev(comDevice);
3526 if (!hostDev.isNull())
3527 {
3528 strTip += QString(tr("<br><nobr>State: %1</nobr>", "USB device tooltip"))
3529 .arg(gpConverter->toString(hostDev.GetState()));
3530 }
3531
3532 return strTip;
3533}
3534
3535/* static */
3536QString UICommon::toolTip(const CUSBDeviceFilter &comFilter)
3537{
3538 QString strTip;
3539
3540 const QString strVendorId = comFilter.GetVendorId();
3541 if (!strVendorId.isEmpty())
3542 strTip += tr("<nobr>Vendor ID: %1</nobr>", "USB filter tooltip")
3543 .arg(strVendorId);
3544
3545 const QString strProductId = comFilter.GetProductId();
3546 if (!strProductId.isEmpty())
3547 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Product ID: %2</nobr>", "USB filter tooltip")
3548 .arg(strProductId);
3549
3550 const QString strRevision = comFilter.GetRevision();
3551 if (!strRevision.isEmpty())
3552 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Revision: %3</nobr>", "USB filter tooltip")
3553 .arg(strRevision);
3554
3555 const QString strProduct = comFilter.GetProduct();
3556 if (!strProduct.isEmpty())
3557 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Product: %4</nobr>", "USB filter tooltip")
3558 .arg(strProduct);
3559
3560 const QString strManufacturer = comFilter.GetManufacturer();
3561 if (!strManufacturer.isEmpty())
3562 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Manufacturer: %5</nobr>", "USB filter tooltip")
3563 .arg(strManufacturer);
3564
3565 const QString strSerial = comFilter.GetSerialNumber();
3566 if (!strSerial.isEmpty())
3567 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Serial No.: %1</nobr>", "USB filter tooltip")
3568 .arg(strSerial);
3569
3570 const QString strPort = comFilter.GetPort();
3571 if (!strPort.isEmpty())
3572 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>Port: %1</nobr>", "USB filter tooltip")
3573 .arg(strPort);
3574
3575 /* Add the state field if it's a host USB device: */
3576 CHostUSBDevice hostDev(comFilter);
3577 if (!hostDev.isNull())
3578 {
3579 strTip += strTip.isEmpty() ? "":"<br/>" + tr("<nobr>State: %1</nobr>", "USB filter tooltip")
3580 .arg(gpConverter->toString(hostDev.GetState()));
3581 }
3582
3583 return strTip;
3584}
3585
3586/* static */
3587QString UICommon::toolTip(const CHostVideoInputDevice &comWebcam)
3588{
3589 QStringList records;
3590
3591 const QString strName = comWebcam.GetName();
3592 if (!strName.isEmpty())
3593 records << strName;
3594
3595 const QString strPath = comWebcam.GetPath();
3596 if (!strPath.isEmpty())
3597 records << strPath;
3598
3599 return records.join("<br>");
3600}
3601
3602/* static */
3603void UICommon::doExtPackInstallation(QString const &strFilePath, QString const &strDigest,
3604 QWidget *pParent, QString *pstrExtPackName) const
3605{
3606 /* Open the extpack tarball via IExtPackManager: */
3607 CExtPackManager comManager = virtualBox().GetExtensionPackManager();
3608 CExtPackFile comExtPackFile;
3609 if (strDigest.isEmpty())
3610 comExtPackFile = comManager.OpenExtPackFile(strFilePath);
3611 else
3612 {
3613 QString strFileAndHash = QString("%1::SHA-256=%2").arg(strFilePath).arg(strDigest);
3614 comExtPackFile = comManager.OpenExtPackFile(strFileAndHash);
3615 }
3616 if (!comManager.isOk())
3617 {
3618 msgCenter().cannotOpenExtPack(strFilePath, comManager, pParent);
3619 return;
3620 }
3621
3622 if (!comExtPackFile.GetUsable())
3623 {
3624 msgCenter().warnAboutBadExtPackFile(strFilePath, comExtPackFile, pParent);
3625 return;
3626 }
3627
3628 const QString strPackName = comExtPackFile.GetName();
3629 const QString strPackDescription = comExtPackFile.GetDescription();
3630 const QString strPackVersion = QString("%1r%2%3").arg(comExtPackFile.GetVersion()).arg(comExtPackFile.GetRevision()).arg(comExtPackFile.GetEdition());
3631
3632 /* Check if there is a version of the extension pack already
3633 * installed on the system and let the user decide what to do about it. */
3634 CExtPack comExtPackCur = comManager.Find(strPackName);
3635 bool fReplaceIt = comExtPackCur.isOk();
3636 if (fReplaceIt)
3637 {
3638 QString strPackVersionCur = QString("%1r%2%3").arg(comExtPackCur.GetVersion()).arg(comExtPackCur.GetRevision()).arg(comExtPackCur.GetEdition());
3639 if (!msgCenter().confirmReplaceExtensionPack(strPackName, strPackVersion, strPackVersionCur, strPackDescription, pParent))
3640 return;
3641 }
3642 /* If it's a new package just ask for general confirmation. */
3643 else
3644 {
3645 if (!msgCenter().confirmInstallExtensionPack(strPackName, strPackVersion, strPackDescription, pParent))
3646 return;
3647 }
3648
3649 /* Display the license dialog if required by the extension pack. */
3650 if (comExtPackFile.GetShowLicense())
3651 {
3652 QString strLicense = comExtPackFile.GetLicense();
3653 VBoxLicenseViewer licenseViewer(pParent);
3654 if (licenseViewer.showLicenseFromString(strLicense) != QDialog::Accepted)
3655 return;
3656 }
3657
3658 /* Install the selected package.
3659 * Set the package name return value before doing
3660 * this as the caller should do a refresh even on failure. */
3661 QString strDisplayInfo;
3662#ifdef VBOX_WS_WIN
3663 if (pParent)
3664 strDisplayInfo.sprintf("hwnd=%#llx", (uint64_t)(uintptr_t)pParent->winId());
3665#endif
3666 /* Prepare installation progress: */
3667 CProgress comProgress = comExtPackFile.Install(fReplaceIt, strDisplayInfo);
3668 if (comExtPackFile.isOk())
3669 {
3670 /* Show installation progress: */
3671 /** @todo move this tr into UIUpdateManager context */
3672 msgCenter().showModalProgressDialog(comProgress, QApplication::translate("UIGlobalSettingsExtension",
3673 "Extensions"),
3674 ":/progress_install_guest_additions_90px.png", pParent);
3675 if (!comProgress.GetCanceled())
3676 {
3677 if (comProgress.isOk() && comProgress.GetResultCode() == 0)
3678 msgCenter().warnAboutExtPackInstalled(strPackName, pParent);
3679 else
3680 msgCenter().cannotInstallExtPack(comProgress, strFilePath, pParent);
3681 }
3682 }
3683 else
3684 msgCenter().cannotInstallExtPack(comExtPackFile, strFilePath, pParent);
3685
3686 if (pstrExtPackName)
3687 *pstrExtPackName = strPackName;
3688}
3689
3690bool UICommon::is3DAvailableWorker() const
3691{
3692 /* Rational is that when starting a text mode guest (like DOS) that does not
3693 * have 3D enabled, don't wast the developer's or user's time on launching the
3694 * test application when starting the VM or editing it's settings.
3695 *
3696 * Keep in mind that if we ever end up checking this concurrently on multiple threads,
3697 * use a RTONCE construct to serialize the efforts. */
3698
3699#ifdef VBOX_WITH_3D_ACCELERATION
3700 bool fSupported = VBoxOglIs3DAccelerationSupported();
3701#else
3702 bool fSupported = false;
3703#endif
3704 m_i3DAvailable = fSupported;
3705 return fSupported;
3706}
3707
3708bool UICommon::is3DAvailable() const
3709{
3710 if (m_i3DAvailable < 0)
3711 return is3DAvailableWorker();
3712 return m_i3DAvailable != 0;
3713}
3714
3715#ifdef VBOX_WITH_3D_ACCELERATION
3716/* static */
3717bool UICommon::isWddmCompatibleOsType(const QString &strGuestOSTypeId)
3718{
3719 return strGuestOSTypeId.startsWith("WindowsVista")
3720 || strGuestOSTypeId.startsWith("Windows7")
3721 || strGuestOSTypeId.startsWith("Windows8")
3722 || strGuestOSTypeId.startsWith("Windows81")
3723 || strGuestOSTypeId.startsWith("Windows10")
3724 || strGuestOSTypeId.startsWith("Windows2008")
3725 || strGuestOSTypeId.startsWith("Windows2012");
3726}
3727#endif /* VBOX_WITH_3D_ACCELERATION */
3728
3729/* static */
3730quint64 UICommon::requiredVideoMemory(const QString &strGuestOSTypeId, int cMonitors /* = 1 */)
3731{
3732 /* We create a list of the size of all available host monitors. This list
3733 * is sorted by value and by starting with the biggest one, we calculate
3734 * the memory requirements for every guest screen. This is of course not
3735 * correct, but as we can't predict on which host screens the user will
3736 * open the guest windows, this is the best assumption we can do, cause it
3737 * is the worst case. */
3738 const int cHostScreens = gpDesktop->screenCount();
3739 QVector<int> screenSize(qMax(cMonitors, cHostScreens), 0);
3740 for (int i = 0; i < cHostScreens; ++i)
3741 {
3742 QRect r = gpDesktop->screenGeometry(i);
3743 screenSize[i] = r.width() * r.height();
3744 }
3745 /* Now sort the vector: */
3746 qSort(screenSize.begin(), screenSize.end(), qGreater<int>());
3747 /* For the case that there are more guest screens configured then host
3748 * screens available, replace all zeros with the greatest value in the
3749 * vector. */
3750 for (int i = 0; i < screenSize.size(); ++i)
3751 if (screenSize.at(i) == 0)
3752 screenSize.replace(i, screenSize.at(0));
3753
3754 quint64 uNeedBits = 0;
3755 for (int i = 0; i < cMonitors; ++i)
3756 {
3757 /* Calculate summary required memory amount in bits: */
3758 uNeedBits += (screenSize.at(i) * /* with x height */
3759 32 + /* we will take the maximum possible bpp for now */
3760 8 * _1M) + /* current cache per screen - may be changed in future */
3761 8 * 4096; /* adapter info */
3762 }
3763 /* Translate value into megabytes with rounding to highest side: */
3764 quint64 uNeedMBytes = uNeedBits % (8 * _1M)
3765 ? uNeedBits / (8 * _1M) + 1
3766 : uNeedBits / (8 * _1M) /* convert to megabytes */;
3767
3768 if (strGuestOSTypeId.startsWith("Windows"))
3769 {
3770 /* Windows guests need offscreen VRAM too for graphics acceleration features: */
3771#ifdef VBOX_WITH_3D_ACCELERATION
3772 if (isWddmCompatibleOsType(strGuestOSTypeId))
3773 {
3774 /* WDDM mode, there are two surfaces for each screen: shadow & primary: */
3775 uNeedMBytes *= 3;
3776 }
3777 else
3778#endif /* VBOX_WITH_3D_ACCELERATION */
3779 {
3780 uNeedMBytes *= 2;
3781 }
3782 }
3783
3784 return uNeedMBytes * _1M;
3785}
3786
3787QIcon UICommon::vmUserIcon(const CMachine &comMachine) const
3788{
3789 /* Prepare fallback icon: */
3790 static QIcon nullIcon;
3791
3792 /* Make sure general icon-pool initialized: */
3793 AssertReturn(m_pIconPool, nullIcon);
3794
3795 /* Redirect to general icon-pool: */
3796 return m_pIconPool->userMachineIcon(comMachine);
3797}
3798
3799QPixmap UICommon::vmUserPixmap(const CMachine &comMachine, const QSize &size) const
3800{
3801 /* Prepare fallback pixmap: */
3802 static QPixmap nullPixmap;
3803
3804 /* Make sure general icon-pool initialized: */
3805 AssertReturn(m_pIconPool, nullPixmap);
3806
3807 /* Redirect to general icon-pool: */
3808 return m_pIconPool->userMachinePixmap(comMachine, size);
3809}
3810
3811QPixmap UICommon::vmUserPixmapDefault(const CMachine &comMachine, QSize *pLogicalSize /* = 0 */) const
3812{
3813 /* Prepare fallback pixmap: */
3814 static QPixmap nullPixmap;
3815
3816 /* Make sure general icon-pool initialized: */
3817 AssertReturn(m_pIconPool, nullPixmap);
3818
3819 /* Redirect to general icon-pool: */
3820 return m_pIconPool->userMachinePixmapDefault(comMachine, pLogicalSize);
3821}
3822
3823QIcon UICommon::vmGuestOSTypeIcon(const QString &strOSTypeID) const
3824{
3825 /* Prepare fallback icon: */
3826 static QIcon nullIcon;
3827
3828 /* Make sure general icon-pool initialized: */
3829 AssertReturn(m_pIconPool, nullIcon);
3830
3831 /* Redirect to general icon-pool: */
3832 return m_pIconPool->guestOSTypeIcon(strOSTypeID);
3833}
3834
3835QPixmap UICommon::vmGuestOSTypePixmap(const QString &strOSTypeID, const QSize &size) const
3836{
3837 /* Prepare fallback pixmap: */
3838 static QPixmap nullPixmap;
3839
3840 /* Make sure general icon-pool initialized: */
3841 AssertReturn(m_pIconPool, nullPixmap);
3842
3843 /* Redirect to general icon-pool: */
3844 return m_pIconPool->guestOSTypePixmap(strOSTypeID, size);
3845}
3846
3847QPixmap UICommon::vmGuestOSTypePixmapDefault(const QString &strOSTypeID, QSize *pLogicalSize /* = 0 */) const
3848{
3849 /* Prepare fallback pixmap: */
3850 static QPixmap nullPixmap;
3851
3852 /* Make sure general icon-pool initialized: */
3853 AssertReturn(m_pIconPool, nullPixmap);
3854
3855 /* Redirect to general icon-pool: */
3856 return m_pIconPool->guestOSTypePixmapDefault(strOSTypeID, pLogicalSize);
3857}
3858
3859/* static */
3860QPixmap UICommon::joinPixmaps(const QPixmap &pixmap1, const QPixmap &pixmap2)
3861{
3862 if (pixmap1.isNull())
3863 return pixmap2;
3864 if (pixmap2.isNull())
3865 return pixmap1;
3866
3867 QPixmap result(pixmap1.width() + pixmap2.width() + 2,
3868 qMax(pixmap1.height(), pixmap2.height()));
3869 result.fill(Qt::transparent);
3870
3871 QPainter painter(&result);
3872 painter.drawPixmap(0, 0, pixmap1);
3873 painter.drawPixmap(pixmap1.width() + 2, result.height() - pixmap2.height(), pixmap2);
3874 painter.end();
3875
3876 return result;
3877}
3878
3879bool UICommon::openURL(const QString &strUrl) const
3880{
3881 /** Service event. */
3882 class ServiceEvent : public QEvent
3883 {
3884 public:
3885
3886 /** Constructs service event on th basis of passed @a fResult. */
3887 ServiceEvent(bool fResult)
3888 : QEvent(QEvent::User)
3889 , m_fResult(fResult)
3890 {}
3891
3892 /** Returns the result which event brings. */
3893 bool result() const { return m_fResult; }
3894
3895 private:
3896
3897 /** Holds the result which event brings. */
3898 bool m_fResult;
3899 };
3900
3901 /** Service client object. */
3902 class ServiceClient : public QEventLoop
3903 {
3904 public:
3905
3906 /** Constructs service client on the basis of passed @a fResult. */
3907 ServiceClient()
3908 : m_fResult(false)
3909 {}
3910
3911 /** Returns the result which event brings. */
3912 bool result() const { return m_fResult; }
3913
3914 private:
3915
3916 /** Handles any Qt @a pEvent. */
3917 bool event(QEvent *pEvent)
3918 {
3919 /* Handle service event: */
3920 if (pEvent->type() == QEvent::User)
3921 {
3922 ServiceEvent *pServiceEvent = static_cast<ServiceEvent*>(pEvent);
3923 m_fResult = pServiceEvent->result();
3924 pServiceEvent->accept();
3925 quit();
3926 return true;
3927 }
3928 return false;
3929 }
3930
3931 bool m_fResult;
3932 };
3933
3934 /** Service server object. */
3935 class ServiceServer : public QThread
3936 {
3937 public:
3938
3939 /** Constructs service server on the basis of passed @a client and @a strUrl. */
3940 ServiceServer(ServiceClient &client, const QString &strUrl)
3941 : m_client(client), m_strUrl(strUrl) {}
3942
3943 private:
3944
3945 /** Executes thread task. */
3946 void run()
3947 {
3948 QApplication::postEvent(&m_client, new ServiceEvent(QDesktopServices::openUrl(m_strUrl)));
3949 }
3950
3951 /** Holds the client reference. */
3952 ServiceClient &m_client;
3953 /** Holds the URL to be processed. */
3954 const QString &m_strUrl;
3955 };
3956
3957 /* Create client & server: */
3958 ServiceClient client;
3959 ServiceServer server(client, strUrl);
3960 server.start();
3961 client.exec();
3962 server.wait();
3963
3964 /* Acquire client result: */
3965 bool fResult = client.result();
3966
3967 if (!fResult)
3968 msgCenter().cannotOpenURL(strUrl);
3969
3970 return fResult;
3971}
3972
3973void UICommon::sltGUILanguageChange(QString strLanguage)
3974{
3975 /* Make sure medium-enumeration is not in progress! */
3976 AssertReturnVoid(!isMediumEnumerationInProgress());
3977 /* Load passed language: */
3978 loadLanguage(strLanguage);
3979}
3980
3981bool UICommon::eventFilter(QObject *pObject, QEvent *pEvent)
3982{
3983 /** @todo Just use the QIWithRetranslateUI3 template wrapper. */
3984
3985 if ( pEvent->type() == QEvent::LanguageChange
3986 && pObject->isWidgetType()
3987 && static_cast<QWidget*>(pObject)->isTopLevel())
3988 {
3989 /* Catch the language change event before any other widget gets it in
3990 * order to invalidate cached string resources (like the details view
3991 * templates) that may be used by other widgets. */
3992 QWidgetList list = QApplication::topLevelWidgets();
3993 if (list.first() == pObject)
3994 {
3995 /* Call this only once per every language change (see
3996 * QApplication::installTranslator() for details): */
3997 retranslateUi();
3998 }
3999 }
4000
4001 /* Call to base-class: */
4002 return QObject::eventFilter(pObject, pEvent);
4003}
4004
4005void UICommon::retranslateUi()
4006{
4007 s_strUserDefinedPortName = tr("User-defined", "serial port");
4008
4009 m_pixWarning = UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_MessageBoxWarning).pixmap(16, 16);
4010 Assert(!m_pixWarning.isNull());
4011
4012 m_pixError = UIIconPool::defaultIcon(UIIconPool::UIDefaultIconType_MessageBoxCritical).pixmap(16, 16);
4013 Assert(!m_pixError.isNull());
4014
4015 /* Re-enumerate uimedium since they contain some translations too: */
4016 if (m_fValid)
4017 refreshMedia();
4018
4019#ifdef VBOX_WS_X11
4020 // WORKAROUND:
4021 // As X11 do not have functionality for providing human readable key names,
4022 // we keep a table of them, which must be updated when the language is changed.
4023 UINativeHotKey::retranslateKeyNames();
4024#endif
4025}
4026
4027void UICommon::prepare()
4028{
4029 /* Make sure QApplication cleanup us on exit: */
4030 connect(qApp, &QGuiApplication::aboutToQuit, this, &UICommon::cleanup);
4031 /* Make sure we handle host OS session shutdown as well: */
4032 connect(qApp, &QGuiApplication::commitDataRequest,
4033 this, &UICommon::sltHandleCommitDataRequest);
4034
4035#ifdef VBOX_WS_MAC
4036 /* Determine OS release early: */
4037 m_enmMacOSVersion = determineOsRelease();
4038#endif /* VBOX_WS_MAC */
4039
4040 /* Prepare converter: */
4041 UIConverter::prepare();
4042
4043 /* Create desktop-widget watchdog: */
4044 UIDesktopWidgetWatchdog::create();
4045
4046 /* Create message-center: */
4047 UIMessageCenter::create();
4048 /* Create popup-center: */
4049 UIPopupCenter::create();
4050
4051 /* Prepare general icon-pool: */
4052 m_pIconPool = new UIIconPoolGeneral;
4053
4054 /* Load translation based on the current locale: */
4055 loadLanguage();
4056
4057 HRESULT rc = COMBase::InitializeCOM(true);
4058 if (FAILED(rc))
4059 {
4060#ifdef VBOX_WITH_XPCOM
4061 if (rc == NS_ERROR_FILE_ACCESS_DENIED)
4062 {
4063 char szHome[RTPATH_MAX] = "";
4064 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
4065 msgCenter().cannotInitUserHome(QString(szHome));
4066 }
4067 else
4068#endif
4069 msgCenter().cannotInitCOM(rc);
4070 return;
4071 }
4072
4073#ifdef VBOX_WITH_SDS
4074 // setup Client COM Security to enable impersonation required by VBOX_SDS
4075 HRESULT hrGUICoInitializeSecurity = CoInitializeSecurity(NULL,
4076 -1,
4077 NULL,
4078 NULL,
4079 RPC_C_AUTHN_LEVEL_DEFAULT,
4080 RPC_C_IMP_LEVEL_IMPERSONATE,
4081 NULL,
4082 EOAC_NONE,
4083 NULL);
4084 NOREF(hrGUICoInitializeSecurity);
4085 Assert(RPC_E_TOO_LATE != hrGUICoInitializeSecurity);
4086 Assert(hrGUICoInitializeSecurity == S_OK);
4087#endif
4088
4089 /* Make sure VirtualBoxClient instance created: */
4090 m_comVBoxClient.createInstance(CLSID_VirtualBoxClient);
4091 if (!m_comVBoxClient.isOk())
4092 {
4093 msgCenter().cannotCreateVirtualBoxClient(m_comVBoxClient);
4094 return;
4095 }
4096 /* Make sure VirtualBox instance acquired: */
4097 m_comVBox = m_comVBoxClient.GetVirtualBox();
4098 if (!m_comVBoxClient.isOk())
4099 {
4100 msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
4101 return;
4102 }
4103 /* Init wrappers: */
4104 comWrappersReinit();
4105
4106 /* Watch for the VBoxSVC availability changes: */
4107 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigVBoxSVCAvailabilityChange,
4108 this, &UICommon::sltHandleVBoxSVCAvailabilityChange);
4109
4110 /* Prepare thread-pool instance: */
4111 m_pThreadPool = new UIThreadPool(3 /* worker count */, 5000 /* worker timeout */);
4112
4113 /* Load translation based on the user settings: */
4114 QString sLanguageId = gEDataManager->languageId();
4115 if (!sLanguageId.isNull())
4116 loadLanguage(sLanguageId);
4117
4118 retranslateUi();
4119
4120 connect(gEDataManager, &UIExtraDataManager::sigLanguageChange,
4121 this, &UICommon::sltGUILanguageChange);
4122
4123 qApp->installEventFilter(this);
4124
4125 /* process command line */
4126
4127 UIVisualStateType visualStateType = UIVisualStateType_Invalid;
4128
4129#ifdef VBOX_WS_X11
4130 /* Check whether we have compositing manager running: */
4131 m_fCompositingManagerRunning = X11IsCompositingManagerRunning();
4132
4133 /* Acquire current Window Manager type: */
4134 m_enmWindowManagerType = X11WindowManagerType();
4135#endif /* VBOX_WS_X11 */
4136
4137#ifdef VBOX_WITH_DEBUGGER_GUI
4138# ifdef VBOX_WITH_DEBUGGER_GUI_MENU
4139 initDebuggerVar(&m_fDbgEnabled, "VBOX_GUI_DBG_ENABLED", GUI_Dbg_Enabled, true);
4140# else
4141 initDebuggerVar(&m_fDbgEnabled, "VBOX_GUI_DBG_ENABLED", GUI_Dbg_Enabled, false);
4142# endif
4143 initDebuggerVar(&m_fDbgAutoShow, "VBOX_GUI_DBG_AUTO_SHOW", GUI_Dbg_AutoShow, false);
4144 m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = m_fDbgAutoShow;
4145#endif
4146
4147 /*
4148 * Parse the command line options.
4149 *
4150 * This is a little sloppy but we're trying to tighten it up. Unfortuately,
4151 * both on X11 and darwin (IIRC) there might be additional arguments aimed
4152 * for client libraries with GUI processes. So, using RTGetOpt or similar
4153 * is a bit hard since we have to cope with unknown options.
4154 */
4155 m_fShowStartVMErrors = true;
4156 bool startVM = false;
4157 bool fSeparateProcess = false;
4158 QString vmNameOrUuid;
4159
4160 const QStringList &arguments = QCoreApplication::arguments();
4161 const int argc = arguments.size();
4162 int i = 1;
4163 while (i < argc)
4164 {
4165 const QByteArray &argBytes = arguments.at(i).toUtf8();
4166 const char *arg = argBytes.constData();
4167 enum { OptType_Unknown, OptType_VMRunner, OptType_VMSelector, OptType_MaybeBoth } enmOptType = OptType_Unknown;
4168 /* NOTE: the check here must match the corresponding check for the
4169 * options to start a VM in main.cpp and hardenedmain.cpp exactly,
4170 * otherwise there will be weird error messages. */
4171 if ( !::strcmp(arg, "--startvm")
4172 || !::strcmp(arg, "-startvm"))
4173 {
4174 enmOptType = OptType_VMRunner;
4175 if (++i < argc)
4176 {
4177 vmNameOrUuid = arguments.at(i);
4178 startVM = true;
4179 }
4180 }
4181 else if (!::strcmp(arg, "-separate") || !::strcmp(arg, "--separate"))
4182 {
4183 enmOptType = OptType_VMRunner;
4184 fSeparateProcess = true;
4185 }
4186#ifdef VBOX_GUI_WITH_PIDFILE
4187 else if (!::strcmp(arg, "-pidfile") || !::strcmp(arg, "--pidfile"))
4188 {
4189 enmOptType = OptType_MaybeBoth;
4190 if (++i < argc)
4191 m_strPidFile = arguments.at(i);
4192 }
4193#endif /* VBOX_GUI_WITH_PIDFILE */
4194 /* Visual state type options: */
4195 else if (!::strcmp(arg, "-normal") || !::strcmp(arg, "--normal"))
4196 {
4197 enmOptType = OptType_MaybeBoth;
4198 visualStateType = UIVisualStateType_Normal;
4199 }
4200 else if (!::strcmp(arg, "-fullscreen") || !::strcmp(arg, "--fullscreen"))
4201 {
4202 enmOptType = OptType_MaybeBoth;
4203 visualStateType = UIVisualStateType_Fullscreen;
4204 }
4205 else if (!::strcmp(arg, "-seamless") || !::strcmp(arg, "--seamless"))
4206 {
4207 enmOptType = OptType_MaybeBoth;
4208 visualStateType = UIVisualStateType_Seamless;
4209 }
4210 else if (!::strcmp(arg, "-scale") || !::strcmp(arg, "--scale"))
4211 {
4212 enmOptType = OptType_MaybeBoth;
4213 visualStateType = UIVisualStateType_Scale;
4214 }
4215 /* Passwords: */
4216 else if (!::strcmp(arg, "--settingspw"))
4217 {
4218 enmOptType = OptType_MaybeBoth;
4219 if (++i < argc)
4220 {
4221 RTStrCopy(m_astrSettingsPw, sizeof(m_astrSettingsPw), arguments.at(i).toLocal8Bit().constData());
4222 m_fSettingsPwSet = true;
4223 }
4224 }
4225 else if (!::strcmp(arg, "--settingspwfile"))
4226 {
4227 enmOptType = OptType_MaybeBoth;
4228 if (++i < argc)
4229 {
4230 const QByteArray &argFileBytes = arguments.at(i).toLocal8Bit();
4231 const char *pszFile = argFileBytes.constData();
4232 bool fStdIn = !::strcmp(pszFile, "stdin");
4233 int vrc = VINF_SUCCESS;
4234 PRTSTREAM pStrm;
4235 if (!fStdIn)
4236 vrc = RTStrmOpen(pszFile, "r", &pStrm);
4237 else
4238 pStrm = g_pStdIn;
4239 if (RT_SUCCESS(vrc))
4240 {
4241 size_t cbFile;
4242 vrc = RTStrmReadEx(pStrm, m_astrSettingsPw, sizeof(m_astrSettingsPw) - 1, &cbFile);
4243 if (RT_SUCCESS(vrc))
4244 {
4245 if (cbFile >= sizeof(m_astrSettingsPw) - 1)
4246 cbFile = sizeof(m_astrSettingsPw) - 1;
4247 unsigned i;
4248 for (i = 0; i < cbFile && !RT_C_IS_CNTRL(m_astrSettingsPw[i]); i++)
4249 ;
4250 m_astrSettingsPw[i] = '\0';
4251 m_fSettingsPwSet = true;
4252 }
4253 if (!fStdIn)
4254 RTStrmClose(pStrm);
4255 }
4256 }
4257 }
4258 /* Misc options: */
4259 else if (!::strcmp(arg, "-comment") || !::strcmp(arg, "--comment"))
4260 {
4261 enmOptType = OptType_MaybeBoth;
4262 ++i;
4263 }
4264 else if (!::strcmp(arg, "--no-startvm-errormsgbox"))
4265 {
4266 enmOptType = OptType_VMRunner;
4267 m_fShowStartVMErrors = false;
4268 }
4269 else if (!::strcmp(arg, "--aggressive-caching"))
4270 {
4271 enmOptType = OptType_MaybeBoth;
4272 m_fAgressiveCaching = true;
4273 }
4274 else if (!::strcmp(arg, "--no-aggressive-caching"))
4275 {
4276 enmOptType = OptType_MaybeBoth;
4277 m_fAgressiveCaching = false;
4278 }
4279 else if (!::strcmp(arg, "--restore-current"))
4280 {
4281 enmOptType = OptType_VMRunner;
4282 m_fRestoreCurrentSnapshot = true;
4283 }
4284 /* Ad hoc VM reconfig options: */
4285 else if (!::strcmp(arg, "--fda"))
4286 {
4287 enmOptType = OptType_VMRunner;
4288 if (++i < argc)
4289 m_strFloppyImage = arguments.at(i);
4290 }
4291 else if (!::strcmp(arg, "--dvd") || !::strcmp(arg, "--cdrom"))
4292 {
4293 enmOptType = OptType_VMRunner;
4294 if (++i < argc)
4295 m_strDvdImage = arguments.at(i);
4296 }
4297 /* VMM Options: */
4298 else if (!::strcmp(arg, "--disable-patm"))
4299 {
4300 enmOptType = OptType_VMRunner;
4301 m_fDisablePatm = true;
4302 }
4303 else if (!::strcmp(arg, "--disable-csam"))
4304 {
4305 enmOptType = OptType_VMRunner;
4306 m_fDisableCsam = true;
4307 }
4308 else if (!::strcmp(arg, "--recompile-supervisor"))
4309 {
4310 enmOptType = OptType_VMRunner;
4311 m_fRecompileSupervisor = true;
4312 }
4313 else if (!::strcmp(arg, "--recompile-user"))
4314 {
4315 enmOptType = OptType_VMRunner;
4316 m_fRecompileUser = true;
4317 }
4318 else if (!::strcmp(arg, "--recompile-all"))
4319 {
4320 enmOptType = OptType_VMRunner;
4321 m_fDisablePatm = m_fDisableCsam = m_fRecompileSupervisor = m_fRecompileUser = true;
4322 }
4323 else if (!::strcmp(arg, "--execute-all-in-iem"))
4324 {
4325 enmOptType = OptType_VMRunner;
4326 m_fDisablePatm = m_fDisableCsam = m_fExecuteAllInIem = true;
4327 }
4328 else if (!::strcmp(arg, "--warp-pct"))
4329 {
4330 enmOptType = OptType_VMRunner;
4331 if (++i < argc)
4332 m_uWarpPct = RTStrToUInt32(arguments.at(i).toLocal8Bit().constData());
4333 }
4334#ifdef VBOX_WITH_DEBUGGER_GUI
4335 /* Debugger/Debugging options: */
4336 else if (!::strcmp(arg, "-dbg") || !::strcmp(arg, "--dbg"))
4337 {
4338 enmOptType = OptType_VMRunner;
4339 setDebuggerVar(&m_fDbgEnabled, true);
4340 }
4341 else if (!::strcmp( arg, "-debug") || !::strcmp(arg, "--debug"))
4342 {
4343 enmOptType = OptType_VMRunner;
4344 setDebuggerVar(&m_fDbgEnabled, true);
4345 setDebuggerVar(&m_fDbgAutoShow, true);
4346 setDebuggerVar(&m_fDbgAutoShowCommandLine, true);
4347 setDebuggerVar(&m_fDbgAutoShowStatistics, true);
4348 }
4349 else if (!::strcmp(arg, "--debug-command-line"))
4350 {
4351 enmOptType = OptType_VMRunner;
4352 setDebuggerVar(&m_fDbgEnabled, true);
4353 setDebuggerVar(&m_fDbgAutoShow, true);
4354 setDebuggerVar(&m_fDbgAutoShowCommandLine, true);
4355 }
4356 else if (!::strcmp(arg, "--debug-statistics"))
4357 {
4358 enmOptType = OptType_VMRunner;
4359 setDebuggerVar(&m_fDbgEnabled, true);
4360 setDebuggerVar(&m_fDbgAutoShow, true);
4361 setDebuggerVar(&m_fDbgAutoShowStatistics, true);
4362 }
4363 else if (!::strcmp(arg, "-no-debug") || !::strcmp(arg, "--no-debug"))
4364 {
4365 enmOptType = OptType_VMRunner;
4366 setDebuggerVar(&m_fDbgEnabled, false);
4367 setDebuggerVar(&m_fDbgAutoShow, false);
4368 setDebuggerVar(&m_fDbgAutoShowCommandLine, false);
4369 setDebuggerVar(&m_fDbgAutoShowStatistics, false);
4370 }
4371 /* Not quite debug options, but they're only useful with the debugger bits. */
4372 else if (!::strcmp(arg, "--start-paused"))
4373 {
4374 enmOptType = OptType_VMRunner;
4375 m_enmLaunchRunning = LaunchRunning_No;
4376 }
4377 else if (!::strcmp(arg, "--start-running"))
4378 {
4379 enmOptType = OptType_VMRunner;
4380 m_enmLaunchRunning = LaunchRunning_Yes;
4381 }
4382#endif
4383 if (enmOptType == OptType_VMRunner && m_enmType != UIType_RuntimeUI)
4384 msgCenter().warnAboutUnrelatedOptionType(arg);
4385
4386 i++;
4387 }
4388
4389 if (m_enmType == UIType_RuntimeUI && startVM)
4390 {
4391 /* m_fSeparateProcess makes sense only if a VM is started. */
4392 m_fSeparateProcess = fSeparateProcess;
4393
4394 /* Search for corresponding VM: */
4395 QUuid uuid = QUuid(vmNameOrUuid);
4396 const CMachine machine = m_comVBox.FindMachine(vmNameOrUuid);
4397 if (!uuid.isNull())
4398 {
4399 if (machine.isNull() && showStartVMErrors())
4400 return msgCenter().cannotFindMachineById(m_comVBox, vmNameOrUuid);
4401 }
4402 else
4403 {
4404 if (machine.isNull() && showStartVMErrors())
4405 return msgCenter().cannotFindMachineByName(m_comVBox, vmNameOrUuid);
4406 }
4407 m_strManagedVMId = machine.GetId();
4408 }
4409
4410 /* For Selector UI: */
4411 if (uiType() == UIType_SelectorUI)
4412 {
4413 /* We should create separate logging file for VM selector: */
4414 char szLogFile[RTPATH_MAX];
4415 const char *pszLogFile = NULL;
4416 com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
4417 RTPathAppend(szLogFile, sizeof(szLogFile), "selectorwindow.log");
4418 pszLogFile = szLogFile;
4419 /* Create release logger, to file: */
4420 com::VBoxLogRelCreate("GUI VM Selector Window",
4421 pszLogFile,
4422 RTLOGFLAGS_PREFIX_TIME_PROG,
4423 "all",
4424 "VBOX_GUI_SELECTORWINDOW_RELEASE_LOG",
4425 RTLOGDEST_FILE | RTLOGDEST_F_NO_DENY,
4426 UINT32_MAX,
4427 10,
4428 60 * 60,
4429 _1M,
4430 NULL /*pErrInfo*/);
4431
4432 LogRel(("Qt version: %s\n", qtRTVersionString().toUtf8().constData()));
4433 }
4434
4435 if (m_fSettingsPwSet)
4436 m_comVBox.SetSettingsSecret(m_astrSettingsPw);
4437
4438 if (visualStateType != UIVisualStateType_Invalid && !m_strManagedVMId.isNull())
4439 gEDataManager->setRequestedVisualState(visualStateType, m_strManagedVMId);
4440
4441#ifdef VBOX_WITH_DEBUGGER_GUI
4442 /* For Runtime UI: */
4443 if (uiType() == UIType_RuntimeUI)
4444 {
4445 /* Setup the debugger GUI: */
4446 if (RTEnvExist("VBOX_GUI_NO_DEBUGGER"))
4447 m_fDbgEnabled = m_fDbgAutoShow = m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = false;
4448 if (m_fDbgEnabled)
4449 {
4450 RTERRINFOSTATIC ErrInfo;
4451 RTErrInfoInitStatic(&ErrInfo);
4452 int vrc = SUPR3HardenedLdrLoadAppPriv("VBoxDbg", &m_hVBoxDbg, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
4453 if (RT_FAILURE(vrc))
4454 {
4455 m_hVBoxDbg = NIL_RTLDRMOD;
4456 m_fDbgAutoShow = m_fDbgAutoShowCommandLine = m_fDbgAutoShowStatistics = false;
4457 LogRel(("Failed to load VBoxDbg, rc=%Rrc - %s\n", vrc, ErrInfo.Core.pszMsg));
4458 }
4459 }
4460 }
4461#endif
4462
4463 m_fValid = true;
4464
4465 /* Create medium-enumerator but don't do any immediate caching: */
4466 m_pMediumEnumerator = new UIMediumEnumerator;
4467 {
4468 /* Prepare medium-enumerator: */
4469 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumCreated,
4470 this, &UICommon::sigMediumCreated);
4471 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumDeleted,
4472 this, &UICommon::sigMediumDeleted);
4473 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerationStarted,
4474 this, &UICommon::sigMediumEnumerationStarted);
4475 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerated,
4476 this, &UICommon::sigMediumEnumerated);
4477 connect(m_pMediumEnumerator, &UIMediumEnumerator::sigMediumEnumerationFinished,
4478 this, &UICommon::sigMediumEnumerationFinished);
4479 }
4480
4481 /* Create shortcut pool: */
4482 UIShortcutPool::create();
4483
4484#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
4485 /* Create network manager: */
4486 UINetworkManager::create();
4487
4488 /* Schedule update manager: */
4489 UIUpdateManager::schedule();
4490#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
4491
4492#ifdef RT_OS_LINUX
4493 /* Make sure no wrong USB mounted: */
4494 checkForWrongUSBMounted();
4495#endif /* RT_OS_LINUX */
4496
4497 /* Populate the list of medium names to be excluded from the
4498 recently used media extra data: */
4499#if 0 /* bird: This is counter productive as it is _frequently_ necessary to re-insert the
4500 viso to refresh the files (like after you rebuilt them on the host).
4501 The guest caches ISOs aggressively and files sizes may change. */
4502 m_recentMediaExcludeList << "ad-hoc.viso";
4503#endif
4504}
4505
4506void UICommon::cleanup()
4507{
4508 /// @todo Shouldn't that be protected with a mutex or something?
4509 /* Remember that the cleanup is in progress preventing any unwanted
4510 * stuff which could be called from the other threads: */
4511 s_fCleaningUp = true;
4512
4513#ifdef VBOX_WITH_DEBUGGER_GUI
4514 /* For Runtime UI: */
4515 if ( uiType() == UIType_RuntimeUI
4516 && m_hVBoxDbg != NIL_RTLDRMOD)
4517 {
4518 RTLdrClose(m_hVBoxDbg);
4519 m_hVBoxDbg = NIL_RTLDRMOD;
4520 }
4521#endif
4522
4523#ifdef VBOX_GUI_WITH_NETWORK_MANAGER
4524 /* Shutdown update manager: */
4525 UIUpdateManager::shutdown();
4526
4527 /* Destroy network manager: */
4528 UINetworkManager::destroy();
4529#endif /* VBOX_GUI_WITH_NETWORK_MANAGER */
4530
4531 /* Destroy shortcut pool: */
4532 UIShortcutPool::destroy();
4533
4534#ifdef VBOX_GUI_WITH_PIDFILE
4535 deletePidfile();
4536#endif /* VBOX_GUI_WITH_PIDFILE */
4537
4538 /* Starting medium-enumerator cleanup: */
4539 m_meCleanupProtectionToken.lockForWrite();
4540 {
4541 /* Destroy medium-enumerator: */
4542 delete m_pMediumEnumerator;
4543 m_pMediumEnumerator = 0;
4544 }
4545 /* Finishing medium-enumerator cleanup: */
4546 m_meCleanupProtectionToken.unlock();
4547
4548 /* Destroy the global (VirtualBox) Main event handler
4549 * which is used in both Selector and Runtime UI. */
4550 UIVirtualBoxEventHandler::destroy();
4551
4552 /* Destroy the extra-data manager finally after everything
4553 * above which could use it already destroyed: */
4554 UIExtraDataManager::destroy();
4555
4556 /* Cleanup converter: */
4557 UIConverter::cleanup();
4558
4559 /* Cleanup thread-pool: */
4560 delete m_pThreadPool;
4561 m_pThreadPool = 0;
4562 /* Cleanup general icon-pool: */
4563 delete m_pIconPool;
4564 m_pIconPool = 0;
4565
4566 /* Ensure CGuestOSType objects are no longer used: */
4567 m_guestOSFamilyIDs.clear();
4568 m_guestOSTypes.clear();
4569
4570 /* Starting COM cleanup: */
4571 m_comCleanupProtectionToken.lockForWrite();
4572 {
4573 /* First, make sure we don't use COM any more: */
4574 m_comHost.detach();
4575 m_comVBox.detach();
4576 m_comVBoxClient.detach();
4577
4578 /* There may be UIMedium(s)EnumeratedEvent instances still in the message
4579 * queue which reference COM objects. Remove them to release those objects
4580 * before uninitializing the COM subsystem. */
4581 QApplication::removePostedEvents(this);
4582
4583 /* Finally cleanup COM itself: */
4584 COMBase::CleanupCOM();
4585 }
4586 /* Finishing COM cleanup: */
4587 m_comCleanupProtectionToken.unlock();
4588
4589 /* Destroy popup-center: */
4590 UIPopupCenter::destroy();
4591 /* Destroy message-center: */
4592 UIMessageCenter::destroy();
4593
4594 /* Destroy desktop-widget watchdog: */
4595 UIDesktopWidgetWatchdog::destroy();
4596
4597 m_fValid = false;
4598}
4599
4600void UICommon::sltHandleCommitDataRequest(QSessionManager &manager)
4601{
4602 LogRel(("GUI: UICommon::sltHandleCommitDataRequest: Emergency shutdown initiated\n"));
4603
4604 /* Ask listener to commit data: */
4605 emit sigAskToCommitData();
4606
4607 /* Ask session manager to postpone shutdown until we done: */
4608 manager.cancel();
4609
4610#ifdef VBOX_WS_WIN
4611 // WORKAROUND:
4612 // In theory that's Qt5 who should allow us to provide postponing reason as well,
4613 // but that functionality seems missed in Windows platform plugin, so we are making that ourselves.
4614 // That also implies that since we had postponed a shutdown process, host will send us WM_QUIT to
4615 // allow to properly do an application cleanup first. That signal will cause QApplication to quit().
4616 ShutdownBlockReasonCreateAPI((HWND)windowManager().mainWindowShown()->winId(), L"Shutdown in progress...");
4617#endif
4618}
4619
4620void UICommon::sltHandleVBoxSVCAvailabilityChange(bool fAvailable)
4621{
4622 /* Make sure the VBoxSVC availability changed: */
4623 if (m_fVBoxSVCAvailable == fAvailable)
4624 return;
4625
4626 /* Cache the new VBoxSVC availability value: */
4627 m_fVBoxSVCAvailable = fAvailable;
4628
4629 /* If VBoxSVC is not available: */
4630 if (!m_fVBoxSVCAvailable)
4631 {
4632 /* Mark wrappers invalid: */
4633 m_fWrappersValid = false;
4634 /* Re-fetch corresponding CVirtualBox to restart VBoxSVC: */
4635 m_comVBox = m_comVBoxClient.GetVirtualBox();
4636 if (!m_comVBoxClient.isOk())
4637 {
4638 // The proper behavior would be to show the message and to exit the app, e.g.:
4639 // msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
4640 // return QApplication::quit();
4641 // But CVirtualBox is still NULL in current Main implementation,
4642 // and this call do not restart anything, so we are waiting
4643 // for subsequent event about VBoxSVC is available again.
4644 }
4645 }
4646 /* If VBoxSVC is available: */
4647 else
4648 {
4649 if (!m_fWrappersValid)
4650 {
4651 /* Re-fetch corresponding CVirtualBox: */
4652 m_comVBox = m_comVBoxClient.GetVirtualBox();
4653 if (!m_comVBoxClient.isOk())
4654 {
4655 msgCenter().cannotAcquireVirtualBox(m_comVBoxClient);
4656 return QApplication::quit();
4657 }
4658 /* Re-init wrappers: */
4659 comWrappersReinit();
4660
4661 /* For Selector UI: */
4662 if (uiType() == UIType_SelectorUI)
4663 {
4664 /* Recreate Main event listeners: */
4665 UIVirtualBoxEventHandler::destroy();
4666 UIExtraDataManager::destroy();
4667 UIExtraDataManager::instance();
4668 UIVirtualBoxEventHandler::instance();
4669 /* Ask UIStarter to restart UI: */
4670 emit sigAskToRestartUI();
4671 }
4672 }
4673 }
4674
4675 /* Notify listeners about the VBoxSVC availability change: */
4676 emit sigVBoxSVCAvailabilityChange();
4677}
4678
4679#ifdef VBOX_WS_WIN
4680/* static */
4681BOOL UICommon::ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
4682{
4683 BOOL fResult = FALSE;
4684 typedef BOOL(WINAPI *PFNSHUTDOWNBLOCKREASONCREATE)(HWND hWnd, LPCWSTR pwszReason);
4685
4686 PFNSHUTDOWNBLOCKREASONCREATE pfn = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(
4687 GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
4688 _ASSERTE(pfn);
4689 if (pfn)
4690 fResult = pfn(hWnd, pwszReason);
4691 return fResult;
4692}
4693#endif
4694
4695#ifdef VBOX_WITH_DEBUGGER_GUI
4696
4697# define UICOMMON_DBG_CFG_VAR_FALSE (0)
4698# define UICOMMON_DBG_CFG_VAR_TRUE (1)
4699# define UICOMMON_DBG_CFG_VAR_MASK (1)
4700# define UICOMMON_DBG_CFG_VAR_CMD_LINE RT_BIT(3)
4701# define UICOMMON_DBG_CFG_VAR_DONE RT_BIT(4)
4702
4703void UICommon::initDebuggerVar(int *piDbgCfgVar, const char *pszEnvVar, const char *pszExtraDataName, bool fDefault)
4704{
4705 QString strEnvValue;
4706 char szEnvValue[256];
4707 int rc = RTEnvGetEx(RTENV_DEFAULT, pszEnvVar, szEnvValue, sizeof(szEnvValue), NULL);
4708 if (RT_SUCCESS(rc))
4709 {
4710 strEnvValue = QString::fromUtf8(&szEnvValue[0]).toLower().trimmed();
4711 if (strEnvValue.isEmpty())
4712 strEnvValue = "yes";
4713 }
4714 else if (rc != VERR_ENV_VAR_NOT_FOUND)
4715 strEnvValue = "veto";
4716
4717 QString strExtraValue = m_comVBox.GetExtraData(pszExtraDataName).toLower().trimmed();
4718 if (strExtraValue.isEmpty())
4719 strExtraValue = QString();
4720
4721 if ( strEnvValue.contains("veto") || strExtraValue.contains("veto"))
4722 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
4723 else if (strEnvValue.isNull() && strExtraValue.isNull())
4724 *piDbgCfgVar = fDefault ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE;
4725 else
4726 {
4727 QString *pStr = !strEnvValue.isEmpty() ? &strEnvValue : &strExtraValue;
4728 if ( pStr->startsWith("y") // yes
4729 || pStr->startsWith("e") // enabled
4730 || pStr->startsWith("t") // true
4731 || pStr->startsWith("on")
4732 || pStr->toLongLong() != 0)
4733 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_TRUE;
4734 else if ( pStr->startsWith("n") // o
4735 || pStr->startsWith("d") // disable
4736 || pStr->startsWith("f") // false
4737 || pStr->startsWith("off")
4738 || pStr->contains("veto") /* paranoia */
4739 || pStr->toLongLong() == 0)
4740 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_FALSE;
4741 else
4742 {
4743 LogFunc(("Ignoring unknown value '%s' for '%s'\n", pStr->toUtf8().constData(), pStr == &strEnvValue ? pszEnvVar : pszExtraDataName));
4744 *piDbgCfgVar = fDefault ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE;
4745 }
4746 }
4747}
4748
4749void UICommon::setDebuggerVar(int *piDbgCfgVar, bool fState)
4750{
4751 if (!(*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_DONE))
4752 *piDbgCfgVar = (fState ? UICOMMON_DBG_CFG_VAR_TRUE : UICOMMON_DBG_CFG_VAR_FALSE)
4753 | UICOMMON_DBG_CFG_VAR_CMD_LINE;
4754}
4755
4756bool UICommon::isDebuggerWorker(int *piDbgCfgVar, const char *pszExtraDataName) const
4757{
4758 if (!(*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_DONE))
4759 {
4760 const QString str = gEDataManager->debugFlagValue(pszExtraDataName);
4761 if (str.contains("veto"))
4762 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
4763 else if (str.isEmpty() || (*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_CMD_LINE))
4764 *piDbgCfgVar |= UICOMMON_DBG_CFG_VAR_DONE;
4765 else if ( str.startsWith("y") // yes
4766 || str.startsWith("e") // enabled
4767 || str.startsWith("t") // true
4768 || str.startsWith("on")
4769 || str.toLongLong() != 0)
4770 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_TRUE;
4771 else if ( str.startsWith("n") // no
4772 || str.startsWith("d") // disable
4773 || str.startsWith("f") // false
4774 || str.toLongLong() == 0)
4775 *piDbgCfgVar = UICOMMON_DBG_CFG_VAR_DONE | UICOMMON_DBG_CFG_VAR_FALSE;
4776 else
4777 *piDbgCfgVar |= UICOMMON_DBG_CFG_VAR_DONE;
4778 }
4779
4780 return (*piDbgCfgVar & UICOMMON_DBG_CFG_VAR_MASK) == UICOMMON_DBG_CFG_VAR_TRUE;
4781}
4782
4783#endif /* VBOX_WITH_DEBUGGER_GUI */
4784
4785void UICommon::comWrappersReinit()
4786{
4787 /* Re-fetch corresponding objects/values: */
4788 m_comHost = virtualBox().GetHost();
4789 m_strHomeFolder = virtualBox().GetHomeFolder();
4790
4791 /* Re-initialize guest OS Type list: */
4792 m_guestOSFamilyIDs.clear();
4793 m_guestOSTypes.clear();
4794 const CGuestOSTypeVector guestOSTypes = m_comVBox.GetGuestOSTypes();
4795 const int cGuestOSTypeCount = guestOSTypes.size();
4796 AssertMsg(cGuestOSTypeCount > 0, ("Number of OS types must not be zero"));
4797 if (cGuestOSTypeCount > 0)
4798 {
4799 /* Here we ASSUME the 'Other' types are always the first,
4800 * so we remember them and will append them to the list when finished.
4801 * We do a two pass, first adding the specific types, then the two 'Other' types. */
4802 for (int j = 0; j < 2; ++j)
4803 {
4804 int cMax = j == 0 ? cGuestOSTypeCount : RT_MIN(2, cGuestOSTypeCount);
4805 for (int i = j == 0 ? 2 : 0; i < cMax; ++i)
4806 {
4807 const CGuestOSType os = guestOSTypes.at(i);
4808 const QString strFamilyID = os.GetFamilyId();
4809 const QString strFamilyDescription = os.GetFamilyDescription();
4810 if (!m_guestOSFamilyIDs.contains(strFamilyID))
4811 {
4812 m_guestOSFamilyIDs << strFamilyID;
4813 m_guestOSFamilyDescriptions[strFamilyID] = strFamilyDescription;
4814 m_guestOSTypes << QList<CGuestOSType>();
4815 }
4816 m_guestOSTypes[m_guestOSFamilyIDs.indexOf(strFamilyID)].append(os);
4817 }
4818 }
4819 }
4820
4821 /* Mark wrappers valid: */
4822 m_fWrappersValid = true;
4823}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use