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