VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox4/src/VBoxGlobal.cpp@ 13090

Last change on this file since 13090 was 13090, checked in by vboxsync, 16 years ago

FE/Qt4: pass the language ID as property to the guest

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 158.3 KB
Line 
1/** @file
2 *
3 * VBox frontends: Qt GUI ("VirtualBox"):
4 * VBoxGlobal class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include "VBoxGlobal.h"
24#include <VBox/VBoxHDD-new.h>
25
26#include "VBoxDefs.h"
27#include "VBoxSelectorWnd.h"
28#include "VBoxConsoleWnd.h"
29#include "VBoxProblemReporter.h"
30#include "QIHotKeyEdit.h"
31#include "QIMessageBox.h"
32#include "QIDialogButtonBox.h"
33
34#ifdef VBOX_WITH_REGISTRATION
35#include "VBoxRegistrationDlg.h"
36#endif
37#include "VBoxUpdateDlg.h"
38
39/* Qt includes */
40#include <QLibraryInfo>
41#include <QFileDialog>
42#include <QToolTip>
43#include <QTranslator>
44#include <QDesktopWidget>
45#include <QMutex>
46#include <QToolButton>
47#include <QProcess>
48#include <QThread>
49#include <QPainter>
50
51#ifdef Q_WS_X11
52#ifndef VBOX_OSE
53# include "VBoxLicenseViewer.h"
54#endif /* VBOX_OSE */
55
56#include <QTextBrowser>
57#include <QScrollBar>
58#include <QX11Info>
59#endif
60
61
62#if defined (Q_WS_MAC)
63#include "VBoxUtils.h"
64#include <Carbon/Carbon.h> // for HIToolbox/InternetConfig
65#endif
66
67#if defined (Q_WS_WIN)
68#include "shlobj.h"
69#include <QEventLoop>
70#endif
71
72#if defined (Q_WS_X11)
73#undef BOOL /* typedef CARD8 BOOL in Xmd.h conflicts with #define BOOL PRBool
74 * in COMDefs.h. A better fix would be to isolate X11-specific
75 * stuff by placing XX* helpers below to a separate source file. */
76#include <X11/X.h>
77#include <X11/Xmd.h>
78#include <X11/Xlib.h>
79#include <X11/Xatom.h>
80#define BOOL PRBool
81#endif
82
83#include <iprt/asm.h>
84#include <iprt/err.h>
85#include <iprt/param.h>
86#include <iprt/path.h>
87#include <iprt/env.h>
88#include <iprt/file.h>
89#include <iprt/ldr.h>
90
91#if defined (Q_WS_X11)
92#include <iprt/mem.h>
93#endif
94
95//#warning "port me: check this"
96/// @todo bird: Use (U)INT_PTR, (U)LONG_PTR, DWORD_PTR, or (u)intptr_t.
97#if defined(Q_OS_WIN64)
98typedef __int64 Q_LONG; /* word up to 64 bit signed */
99typedef unsigned __int64 Q_ULONG; /* word up to 64 bit unsigned */
100#else
101typedef long Q_LONG; /* word up to 64 bit signed */
102typedef unsigned long Q_ULONG; /* word up to 64 bit unsigned */
103#endif
104
105#if defined (VBOX_GUI_DEBUG)
106uint64_t VMCPUTimer::ticks_per_msec = (uint64_t) -1LL; // declared in VBoxDefs.h
107uint64_t VMCPUTimer::ticks()
108{
109 return ASMReadTSC();
110}
111#endif
112
113// VBoxEnumerateMediaEvent
114/////////////////////////////////////////////////////////////////////////////
115
116class VBoxEnumerateMediaEvent : public QEvent
117{
118public:
119
120 /** Constructs a regular enum event */
121 VBoxEnumerateMediaEvent (const VBoxMedia &aMedia, int aIndex)
122 : QEvent ((QEvent::Type) VBoxDefs::EnumerateMediaEventType)
123 , mMedia (aMedia), mLast (false), mIndex (aIndex)
124 {}
125 /** Constructs the last enum event */
126 VBoxEnumerateMediaEvent()
127 : QEvent ((QEvent::Type) VBoxDefs::EnumerateMediaEventType)
128 , mLast (true), mIndex (-1)
129 {}
130
131 /** the last enumerated media (not valid when #last is true) */
132 const VBoxMedia mMedia;
133 /** whether this is the last event for the given enumeration or not */
134 const bool mLast;
135 /** last enumerated media index (-1 when #last is true) */
136 const int mIndex;
137};
138
139#if defined (Q_WS_WIN)
140class VBoxShellExecuteEvent : public QEvent
141{
142public:
143
144 /** Constructs a regular enum event */
145 VBoxShellExecuteEvent (QThread *aThread, const QString &aURL,
146 bool aOk)
147 : QEvent ((QEvent::Type) VBoxDefs::ShellExecuteEventType)
148 , mThread (aThread), mURL (aURL), mOk (aOk)
149 {}
150
151 QThread *mThread;
152 QString mURL;
153 bool mOk;
154};
155#endif
156
157// VirtualBox callback class
158/////////////////////////////////////////////////////////////////////////////
159
160class VBoxCallback : public IVirtualBoxCallback
161{
162public:
163
164 VBoxCallback (VBoxGlobal &aGlobal)
165 : mGlobal (aGlobal)
166 , mIsRegDlgOwner (false), mIsUpdDlgOwner (false)
167 {
168#if defined (Q_OS_WIN32)
169 refcnt = 0;
170#endif
171 }
172
173 virtual ~VBoxCallback() {}
174
175 NS_DECL_ISUPPORTS
176
177#if defined (Q_OS_WIN32)
178 STDMETHOD_(ULONG, AddRef)()
179 {
180 return ::InterlockedIncrement (&refcnt);
181 }
182 STDMETHOD_(ULONG, Release)()
183 {
184 long cnt = ::InterlockedDecrement (&refcnt);
185 if (cnt == 0)
186 delete this;
187 return cnt;
188 }
189 STDMETHOD(QueryInterface) (REFIID riid , void **ppObj)
190 {
191 if (riid == IID_IUnknown) {
192 *ppObj = this;
193 AddRef();
194 return S_OK;
195 }
196 if (riid == IID_IVirtualBoxCallback) {
197 *ppObj = this;
198 AddRef();
199 return S_OK;
200 }
201 *ppObj = NULL;
202 return E_NOINTERFACE;
203 }
204#endif
205
206 // IVirtualBoxCallback methods
207
208 // Note: we need to post custom events to the GUI event queue
209 // instead of doing what we need directly from here because on Win32
210 // these callback methods are never called on the main GUI thread.
211 // Another reason to handle events asynchronously is that internally
212 // most callback interface methods are called from under the initiator
213 // object's lock, so accessing the initiator object (for example, reading
214 // some property) directly from the callback method will definitely cause
215 // a deadlock.
216
217 STDMETHOD(OnMachineStateChange) (IN_GUIDPARAM id, MachineState_T state)
218 {
219 postEvent (new VBoxMachineStateChangeEvent (COMBase::ToQUuid (id),
220 (KMachineState) state));
221 return S_OK;
222 }
223
224 STDMETHOD(OnMachineDataChange) (IN_GUIDPARAM id)
225 {
226 postEvent (new VBoxMachineDataChangeEvent (COMBase::ToQUuid (id)));
227 return S_OK;
228 }
229
230 STDMETHOD(OnExtraDataCanChange)(IN_GUIDPARAM id,
231 IN_BSTRPARAM key, IN_BSTRPARAM value,
232 BSTR *error, BOOL *allowChange)
233 {
234 if (!error || !allowChange)
235 return E_INVALIDARG;
236
237 if (COMBase::ToQUuid (id).isNull())
238 {
239 /* it's a global extra data key someone wants to change */
240 QString sKey = QString::fromUtf16 (key);
241 QString sVal = QString::fromUtf16 (value);
242 if (sKey.startsWith ("GUI/"))
243 {
244 if (sKey == VBoxDefs::GUI_RegistrationDlgWinID)
245 {
246 if (mIsRegDlgOwner)
247 {
248 if (sVal.isEmpty() ||
249 sVal == QString ("%1")
250 .arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
251 *allowChange = TRUE;
252 else
253 *allowChange = FALSE;
254 }
255 else
256 *allowChange = TRUE;
257 return S_OK;
258 }
259
260 if (sKey == VBoxDefs::GUI_UpdateDlgWinID)
261 {
262 if (mIsUpdDlgOwner)
263 {
264 if (sVal.isEmpty() ||
265 sVal == QString ("%1")
266 .arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
267 *allowChange = TRUE;
268 else
269 *allowChange = FALSE;
270 }
271 else
272 *allowChange = TRUE;
273 return S_OK;
274 }
275
276 /* try to set the global setting to check its syntax */
277 VBoxGlobalSettings gs (false /* non-null */);
278 if (gs.setPublicProperty (sKey, sVal))
279 {
280 /* this is a known GUI property key */
281 if (!gs)
282 {
283 /* disallow the change when there is an error*/
284 *error = SysAllocString ((const OLECHAR *)
285 (gs.lastError().isNull() ? 0 : gs.lastError().utf16()));
286 *allowChange = FALSE;
287 }
288 else
289 *allowChange = TRUE;
290 return S_OK;
291 }
292 }
293 }
294
295 /* not interested in this key -- never disagree */
296 *allowChange = TRUE;
297 return S_OK;
298 }
299
300 STDMETHOD(OnExtraDataChange) (IN_GUIDPARAM id,
301 IN_BSTRPARAM key, IN_BSTRPARAM value)
302 {
303 if (COMBase::ToQUuid (id).isNull())
304 {
305 QString sKey = QString::fromUtf16 (key);
306 QString sVal = QString::fromUtf16 (value);
307 if (sKey.startsWith ("GUI/"))
308 {
309 if (sKey == VBoxDefs::GUI_RegistrationDlgWinID)
310 {
311 if (sVal.isEmpty())
312 {
313 mIsRegDlgOwner = false;
314 QApplication::postEvent (&mGlobal, new VBoxCanShowRegDlgEvent (true));
315 }
316 else if (sVal == QString ("%1")
317 .arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
318 {
319 mIsRegDlgOwner = true;
320 QApplication::postEvent (&mGlobal, new VBoxCanShowRegDlgEvent (true));
321 }
322 else
323 QApplication::postEvent (&mGlobal, new VBoxCanShowRegDlgEvent (false));
324 }
325 if (sKey == VBoxDefs::GUI_UpdateDlgWinID)
326 {
327 if (sVal.isEmpty())
328 {
329 mIsUpdDlgOwner = false;
330 QApplication::postEvent (&mGlobal, new VBoxCanShowUpdDlgEvent (true));
331 }
332 else if (sVal == QString ("%1")
333 .arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
334 {
335 mIsUpdDlgOwner = true;
336 QApplication::postEvent (&mGlobal, new VBoxCanShowUpdDlgEvent (true));
337 }
338 else
339 QApplication::postEvent (&mGlobal, new VBoxCanShowUpdDlgEvent (false));
340 }
341 if (sKey == "GUI/LanguageID")
342 QApplication::postEvent (&mGlobal, new VBoxChangeGUILanguageEvent (sVal));
343
344 mMutex.lock();
345 mGlobal.gset.setPublicProperty (sKey, sVal);
346 mMutex.unlock();
347 Assert (!!mGlobal.gset);
348 }
349 }
350 return S_OK;
351 }
352
353 STDMETHOD(OnMediaRegistered) (IN_GUIDPARAM id, DeviceType_T type,
354 BOOL registered)
355 {
356 /** @todo */
357 Q_UNUSED (id);
358 Q_UNUSED (type);
359 Q_UNUSED (registered);
360 return S_OK;
361 }
362
363 STDMETHOD(OnMachineRegistered) (IN_GUIDPARAM id, BOOL registered)
364 {
365 postEvent (new VBoxMachineRegisteredEvent (COMBase::ToQUuid (id),
366 registered));
367 return S_OK;
368 }
369
370 STDMETHOD(OnSessionStateChange) (IN_GUIDPARAM id, SessionState_T state)
371 {
372 postEvent (new VBoxSessionStateChangeEvent (COMBase::ToQUuid (id),
373 (KSessionState) state));
374 return S_OK;
375 }
376
377 STDMETHOD(OnSnapshotTaken) (IN_GUIDPARAM aMachineId, IN_GUIDPARAM aSnapshotId)
378 {
379 postEvent (new VBoxSnapshotEvent (COMBase::ToQUuid (aMachineId),
380 COMBase::ToQUuid (aSnapshotId),
381 VBoxSnapshotEvent::Taken));
382 return S_OK;
383 }
384
385 STDMETHOD(OnSnapshotDiscarded) (IN_GUIDPARAM aMachineId, IN_GUIDPARAM aSnapshotId)
386 {
387 postEvent (new VBoxSnapshotEvent (COMBase::ToQUuid (aMachineId),
388 COMBase::ToQUuid (aSnapshotId),
389 VBoxSnapshotEvent::Discarded));
390 return S_OK;
391 }
392
393 STDMETHOD(OnSnapshotChange) (IN_GUIDPARAM aMachineId, IN_GUIDPARAM aSnapshotId)
394 {
395 postEvent (new VBoxSnapshotEvent (COMBase::ToQUuid (aMachineId),
396 COMBase::ToQUuid (aSnapshotId),
397 VBoxSnapshotEvent::Changed));
398 return S_OK;
399 }
400
401 STDMETHOD(OnGuestPropertyChange) (IN_GUIDPARAM /* id */,
402 IN_BSTRPARAM /* key */,
403 IN_BSTRPARAM /* value */,
404 IN_BSTRPARAM /* flags */)
405 {
406 return S_OK;
407 }
408
409private:
410
411 void postEvent (QEvent *e)
412 {
413 // currently, we don't post events if we are in the VM execution
414 // console mode, to save some CPU ticks (so far, there was no need
415 // to handle VirtualBox callback events in the execution console mode)
416
417 if (!mGlobal.isVMConsoleProcess())
418 QApplication::postEvent (&mGlobal, e);
419 }
420
421 VBoxGlobal &mGlobal;
422
423 /** protects #OnExtraDataChange() */
424 QMutex mMutex;
425
426 bool mIsRegDlgOwner;
427 bool mIsUpdDlgOwner;
428
429#if defined (Q_OS_WIN32)
430private:
431 long refcnt;
432#endif
433};
434
435#if !defined (Q_OS_WIN32)
436NS_DECL_CLASSINFO (VBoxCallback)
437NS_IMPL_THREADSAFE_ISUPPORTS1_CI (VBoxCallback, IVirtualBoxCallback)
438#endif
439
440// Helpers for VBoxGlobal::getOpenFileName() & getExistingDirectory()
441/////////////////////////////////////////////////////////////////////////////
442
443#if defined Q_WS_WIN
444
445extern void qt_enter_modal (QWidget*);
446extern void qt_leave_modal (QWidget*);
447
448static QString extractFilter (const QString &aRawFilter)
449{
450 static const char qt_file_dialog_filter_reg_exp[] =
451 "([a-zA-Z0-9 ]*)\\(([a-zA-Z0-9_.*? +;#\\[\\]]*)\\)$";
452
453 QString result = aRawFilter;
454 QRegExp r (QString::fromLatin1 (qt_file_dialog_filter_reg_exp));
455 int index = r.indexIn (result);
456 if (index >= 0)
457 result = r.cap (2);
458 return result.replace (QChar (' '), QChar (';'));
459}
460
461/**
462 * Converts QFileDialog filter list to Win32 API filter list.
463 */
464static QString winFilter (const QString &aFilter)
465{
466 QStringList filterLst;
467
468 if (!aFilter.isEmpty())
469 {
470 int i = aFilter.indexOf (";;", 0);
471 QString sep (";;");
472 if (i == -1)
473 {
474 if (aFilter.indexOf ("\n", 0) != -1)
475 {
476 sep = "\n";
477 i = aFilter.indexOf (sep, 0);
478 }
479 }
480
481 filterLst = aFilter.split (sep);
482 }
483
484 QStringList::Iterator it = filterLst.begin();
485 QString winfilters;
486 for (; it != filterLst.end(); ++it)
487 {
488 winfilters += *it;
489 winfilters += QChar::Null;
490 winfilters += extractFilter (*it);
491 winfilters += QChar::Null;
492 }
493 winfilters += QChar::Null;
494 return winfilters;
495}
496
497/*
498 * Callback function to control the native Win32 API file dialog
499 */
500UINT_PTR CALLBACK OFNHookProc (HWND aHdlg, UINT aUiMsg, WPARAM aWParam, LPARAM aLParam)
501{
502 if (aUiMsg == WM_NOTIFY)
503 {
504 OFNOTIFY *notif = (OFNOTIFY*) aLParam;
505 if (notif->hdr.code == CDN_TYPECHANGE)
506 {
507 /* locate native dialog controls */
508 HWND parent = GetParent (aHdlg);
509 HWND button = GetDlgItem (parent, IDOK);
510 HWND textfield = ::GetDlgItem (parent, cmb13);
511 if (textfield == NULL)
512 textfield = ::GetDlgItem (parent, edt1);
513 if (textfield == NULL)
514 return FALSE;
515 HWND selector = ::GetDlgItem (parent, cmb1);
516
517 /* simulate filter change by pressing apply-key */
518 int size = 256;
519 TCHAR *buffer = (TCHAR*)malloc (size);
520 SendMessage (textfield, WM_GETTEXT, size, (LPARAM)buffer);
521 SendMessage (textfield, WM_SETTEXT, 0, (LPARAM)"\0");
522 SendMessage (button, BM_CLICK, 0, 0);
523 SendMessage (textfield, WM_SETTEXT, 0, (LPARAM)buffer);
524 free (buffer);
525
526 /* make request for focus moving to filter selector combo-box */
527 HWND curFocus = GetFocus();
528 PostMessage (curFocus, WM_KILLFOCUS, (WPARAM)selector, 0);
529 PostMessage (selector, WM_SETFOCUS, (WPARAM)curFocus, 0);
530 WPARAM wParam = MAKEWPARAM (WA_ACTIVE, 0);
531 PostMessage (selector, WM_ACTIVATE, wParam, (LPARAM)curFocus);
532 }
533 }
534 return FALSE;
535}
536
537/*
538 * Callback function to control the native Win32 API folders dialog
539 */
540static int __stdcall winGetExistDirCallbackProc (HWND hwnd, UINT uMsg,
541 LPARAM lParam, LPARAM lpData)
542{
543 if (uMsg == BFFM_INITIALIZED && lpData != 0)
544 {
545 QString *initDir = (QString *)(lpData);
546 if (!initDir->isEmpty())
547 {
548 SendMessage (hwnd, BFFM_SETSELECTION, TRUE, Q_ULONG (
549 initDir->isNull() ? 0 : initDir->utf16()));
550 //SendMessage (hwnd, BFFM_SETEXPANDED, TRUE, Q_ULONG (initDir->utf16()));
551 }
552 }
553 else if (uMsg == BFFM_SELCHANGED)
554 {
555 TCHAR path [MAX_PATH];
556 SHGetPathFromIDList (LPITEMIDLIST (lParam), path);
557 QString tmpStr = QString::fromUtf16 ((ushort*)path);
558 if (!tmpStr.isEmpty())
559 SendMessage (hwnd, BFFM_ENABLEOK, 1, 1);
560 else
561 SendMessage (hwnd, BFFM_ENABLEOK, 0, 0);
562 SendMessage (hwnd, BFFM_SETSTATUSTEXT, 1, Q_ULONG (path));
563 }
564 return 0;
565}
566
567/**
568 * QEvent class to carry Win32 API native dialog's result information
569 */
570class OpenNativeDialogEvent : public QEvent
571{
572public:
573
574 OpenNativeDialogEvent (const QString &aResult, QEvent::Type aType)
575 : QEvent (aType), mResult (aResult) {}
576
577 const QString& result() { return mResult; }
578
579private:
580
581 QString mResult;
582};
583
584/**
585 * QObject class reimplementation which is the target for OpenNativeDialogEvent
586 * event. It receives OpenNativeDialogEvent event from another thread,
587 * stores result information and exits the given local event loop.
588 */
589class LoopObject : public QObject
590{
591public:
592
593 LoopObject (QEvent::Type aType, QEventLoop &aLoop)
594 : mType (aType), mLoop (aLoop), mResult (QString::null) {}
595 const QString& result() { return mResult; }
596
597private:
598
599 bool event (QEvent *aEvent)
600 {
601 if (aEvent->type() == mType)
602 {
603 OpenNativeDialogEvent *ev = (OpenNativeDialogEvent*) aEvent;
604 mResult = ev->result();
605 mLoop.quit();
606 return true;
607 }
608 return QObject::event (aEvent);
609 }
610
611 QEvent::Type mType;
612 QEventLoop &mLoop;
613 QString mResult;
614};
615
616#endif /* Q_WS_WIN */
617
618
619// VBoxGlobal
620////////////////////////////////////////////////////////////////////////////////
621
622static bool sVBoxGlobalInited = false;
623static bool sVBoxGlobalInCleanup = false;
624
625/** @internal
626 *
627 * Special routine to do VBoxGlobal cleanup when the application is being
628 * terminated. It is called before some essential Qt functionality (for
629 * instance, QThread) becomes unavailable, allowing us to use it from
630 * VBoxGlobal::cleanup() if necessary.
631 */
632static void vboxGlobalCleanup()
633{
634 Assert (!sVBoxGlobalInCleanup);
635 sVBoxGlobalInCleanup = true;
636 vboxGlobal().cleanup();
637}
638
639/** @internal
640 *
641 * Determines the rendering mode from the argument. Sets the appropriate
642 * default rendering mode if the argumen is NULL.
643 */
644static VBoxDefs::RenderMode vboxGetRenderMode (const char *aModeStr)
645{
646 VBoxDefs::RenderMode mode = VBoxDefs::InvalidRenderMode;
647
648#if defined (Q_WS_MAC) && defined (VBOX_GUI_USE_QUARTZ2D)
649 mode = VBoxDefs::Quartz2DMode;
650#elif (defined (Q_WS_WIN32) || defined (Q_WS_PM)) && defined (VBOX_GUI_USE_QIMAGE)
651 mode = VBoxDefs::QImageMode;
652#elif defined (Q_WS_X11) && defined (VBOX_GUI_USE_SDL)
653 mode = VBoxDefs::SDLMode;
654#elif defined (VBOX_GUI_USE_QIMAGE)
655 mode = VBoxDefs::QImageMode;
656#else
657# error "Cannot determine the default render mode!"
658#endif
659
660 if (aModeStr)
661 {
662 if (0) ;
663#if defined (VBOX_GUI_USE_QIMAGE)
664 else if (::strcmp (aModeStr, "image") == 0)
665 mode = VBoxDefs::QImageMode;
666#endif
667#if defined (VBOX_GUI_USE_SDL)
668 else if (::strcmp (aModeStr, "sdl") == 0)
669 mode = VBoxDefs::SDLMode;
670#endif
671#if defined (VBOX_GUI_USE_DDRAW)
672 else if (::strcmp (aModeStr, "ddraw") == 0)
673 mode = VBoxDefs::DDRAWMode;
674#endif
675#if defined (VBOX_GUI_USE_QUARTZ2D)
676 else if (::strcmp (aModeStr, "quartz2d") == 0)
677 mode = VBoxDefs::Quartz2DMode;
678#endif
679 }
680
681 return mode;
682}
683
684/** @class VBoxGlobal
685 *
686 * The VBoxGlobal class incapsulates the global VirtualBox data.
687 *
688 * There is only one instance of this class per VirtualBox application,
689 * the reference to it is returned by the static instance() method, or by
690 * the global vboxGlobal() function, that is just an inlined shortcut.
691 */
692
693VBoxGlobal::VBoxGlobal()
694 : mValid (false)
695 , mSelectorWnd (NULL), mConsoleWnd (NULL)
696 , mMainWindow (NULL)
697#ifdef VBOX_WITH_REGISTRATION
698 , mRegDlg (NULL)
699#endif
700 , mUpdDlg (NULL)
701 , media_enum_thread (NULL)
702 , verString ("1.0")
703 , vm_state_color (KMachineState_COUNT)
704 , machineStates (KMachineState_COUNT)
705 , sessionStates (KSessionState_COUNT)
706 , deviceTypes (KDeviceType_COUNT)
707 , storageBuses (KStorageBus_COUNT)
708 , storageBusDevices (2)
709 , storageBusChannels (3)
710 , diskTypes (KHardDiskType_COUNT)
711 , diskStorageTypes (KHardDiskStorageType_COUNT)
712 , vrdpAuthTypes (KVRDPAuthType_COUNT)
713 , portModeTypes (KPortMode_COUNT)
714 , usbFilterActionTypes (KUSBDeviceFilterAction_COUNT)
715 , audioDriverTypes (KAudioDriverType_COUNT)
716 , audioControllerTypes (KAudioControllerType_COUNT)
717 , networkAdapterTypes (KNetworkAdapterType_COUNT)
718 , networkAttachmentTypes (KNetworkAttachmentType_COUNT)
719 , clipboardTypes (KClipboardMode_COUNT)
720 , ideControllerTypes (KIDEControllerType_COUNT)
721 , USBDeviceStates (KUSBDeviceState_COUNT)
722 , detailReportTemplatesReady (false)
723{
724}
725
726//
727// Public members
728/////////////////////////////////////////////////////////////////////////////
729
730/**
731 * Returns a reference to the global VirtualBox data, managed by this class.
732 *
733 * The main() function of the VBox GUI must call this function soon after
734 * creating a QApplication instance but before opening any of the main windows
735 * (to let the VBoxGlobal initialization procedure use various Qt facilities),
736 * and continue execution only when the isValid() method of the returned
737 * instancereturns true, i.e. do something like:
738 *
739 * @code
740 * if ( !VBoxGlobal::instance().isValid() )
741 * return 1;
742 * @endcode
743 * or
744 * @code
745 * if ( !vboxGlobal().isValid() )
746 * return 1;
747 * @endcode
748 *
749 * @note Some VBoxGlobal methods can be used on a partially constructed
750 * VBoxGlobal instance, i.e. from constructors and methods called
751 * from the VBoxGlobal::init() method, which obtain the instance
752 * using this instance() call or the ::vboxGlobal() function. Currently, such
753 * methods are:
754 * #vmStateText, #vmTypeIcon, #vmTypeText, #vmTypeTextList, #vmTypeFromText.
755 *
756 * @see ::vboxGlobal
757 */
758VBoxGlobal &VBoxGlobal::instance()
759{
760 static VBoxGlobal vboxGlobal_instance;
761
762 if (!sVBoxGlobalInited)
763 {
764 /* check that a QApplication instance is created */
765 if (qApp)
766 {
767 sVBoxGlobalInited = true;
768 vboxGlobal_instance.init();
769 /* add our cleanup handler to the list of Qt post routines */
770 qAddPostRoutine (vboxGlobalCleanup);
771 }
772 else
773 AssertMsgFailed (("Must construct a QApplication first!"));
774 }
775 return vboxGlobal_instance;
776}
777
778VBoxGlobal::~VBoxGlobal()
779{
780 qDeleteAll (vm_os_type_icons);
781 qDeleteAll (mStateIcons);
782 qDeleteAll (vm_state_color);
783}
784
785/**
786 * Sets the new global settings and saves them to the VirtualBox server.
787 */
788bool VBoxGlobal::setSettings (const VBoxGlobalSettings &gs)
789{
790 gs.save (mVBox);
791
792 if (!mVBox.isOk())
793 {
794 vboxProblem().cannotSaveGlobalConfig (mVBox);
795 return false;
796 }
797
798 /* We don't assign gs to our gset member here, because VBoxCallback
799 * will update gset as necessary when new settings are successfullly
800 * sent to the VirtualBox server by gs.save(). */
801
802 return true;
803}
804
805/**
806 * Returns a reference to the main VBox VM Selector window.
807 * The reference is valid until application termination.
808 *
809 * There is only one such a window per VirtualBox application.
810 */
811VBoxSelectorWnd &VBoxGlobal::selectorWnd()
812{
813#if defined (VBOX_GUI_SEPARATE_VM_PROCESS)
814 AssertMsg (!vboxGlobal().isVMConsoleProcess(),
815 ("Must NOT be a VM console process"));
816#endif
817
818 Assert (mValid);
819
820 if (!mSelectorWnd)
821 {
822 /*
823 * We pass the address of mSelectorWnd to the constructor to let it be
824 * initialized right after the constructor is called. It is necessary
825 * to avoid recursion, since this method may be (and will be) called
826 * from the below constructor or from constructors/methods it calls.
827 */
828 VBoxSelectorWnd *w = new VBoxSelectorWnd (&mSelectorWnd, 0);
829 Assert (w == mSelectorWnd);
830 NOREF(w);
831 }
832
833 return *mSelectorWnd;
834}
835
836/**
837 * Returns a reference to the main VBox VM Console window.
838 * The reference is valid until application termination.
839 *
840 * There is only one such a window per VirtualBox application.
841 */
842VBoxConsoleWnd &VBoxGlobal::consoleWnd()
843{
844#if defined (VBOX_GUI_SEPARATE_VM_PROCESS)
845 AssertMsg (vboxGlobal().isVMConsoleProcess(),
846 ("Must be a VM console process"));
847#endif
848
849 Assert (mValid);
850
851 if (!mConsoleWnd)
852 {
853 /*
854 * We pass the address of mConsoleWnd to the constructor to let it be
855 * initialized right after the constructor is called. It is necessary
856 * to avoid recursion, since this method may be (and will be) called
857 * from the below constructor or from constructors/methods it calls.
858 */
859 VBoxConsoleWnd *w = new VBoxConsoleWnd (&mConsoleWnd, 0);
860 Assert (w == mConsoleWnd);
861 NOREF(w);
862 }
863
864 return *mConsoleWnd;
865}
866
867/**
868 * Returns the list of all guest OS type descriptions, queried from
869 * IVirtualBox.
870 */
871QStringList VBoxGlobal::vmGuestOSTypeDescriptions() const
872{
873 static QStringList list;
874 if (list.empty()) {
875 for (int i = 0; i < vm_os_types.count(); i++) {
876 list += vm_os_types [i].GetDescription();
877 }
878 }
879 return list;
880}
881
882QList<QPixmap> VBoxGlobal::vmGuestOSTypeIcons (int aHorizonalMargin, int aVerticalMargin) const
883{
884 static QList<QPixmap> list;
885 if (list.empty())
886 {
887 for (int i = 0; i < vm_os_types.count(); i++)
888 {
889 QPixmap image (32 + 2 * aHorizonalMargin, 32 + 2 * aVerticalMargin);
890 image.fill (Qt::transparent);
891 QPainter p (&image);
892 p.drawPixmap (aHorizonalMargin, aVerticalMargin, *vm_os_type_icons.value (vm_os_types [i].GetId()));
893 p.end();
894 list << image;
895 }
896 }
897 return list;
898}
899
900/**
901 * Returns the guest OS type object corresponding to the given index.
902 * The index argument corresponds to the index in the list of OS type
903 * descriptions as returnded by #vmGuestOSTypeDescriptions().
904 *
905 * If the index is invalid a null object is returned.
906 */
907CGuestOSType VBoxGlobal::vmGuestOSType (int aIndex) const
908{
909 CGuestOSType type;
910 if (aIndex >= 0 && aIndex < (int) vm_os_types.count())
911 type = vm_os_types.value (aIndex);
912 AssertMsg (!type.isNull(), ("Index for OS type must be valid: %d", aIndex));
913 return type;
914}
915
916/**
917 * Returns the index corresponding to the given guest OS type ID.
918 * The returned index corresponds to the index in the list of OS type
919 * descriptions as returnded by #vmGuestOSTypeDescriptions().
920 *
921 * If the guest OS type ID is invalid, -1 is returned.
922 */
923int VBoxGlobal::vmGuestOSTypeIndex (const QString &aId) const
924{
925 for (int i = 0; i < (int) vm_os_types.count(); i++) {
926 if (!vm_os_types [i].GetId().compare (aId))
927 return i;
928 }
929 return -1;
930}
931
932/**
933 * Returns the icon corresponding to the given guest OS type ID.
934 */
935QPixmap VBoxGlobal::vmGuestOSTypeIcon (const QString &aId) const
936{
937 static const QPixmap none;
938 QPixmap *p = vm_os_type_icons.value (aId);
939 AssertMsg (p, ("Icon for type `%s' must be defined", aId.toLatin1().constData()));
940 return p ? *p : none;
941}
942
943/**
944 * Returns the description corresponding to the given guest OS type ID.
945 */
946QString VBoxGlobal::vmGuestOSTypeDescription (const QString &aId) const
947{
948 for (int i = 0; i < (int) vm_os_types.count(); i++) {
949 if (!vm_os_types [i].GetId().compare (aId))
950 return vm_os_types [i].GetDescription();
951 }
952 return QString::null;
953}
954
955/**
956 * Returns a string representation of the given channel number on the given
957 * storage bus. Complementary to #toStorageChannel (KStorageBus, const
958 * QString &) const.
959 */
960QString VBoxGlobal::toString (KStorageBus aBus, LONG aChannel) const
961{
962 Assert (storageBusChannels.count() == 3);
963 QString channel;
964
965 switch (aBus)
966 {
967 case KStorageBus_IDE:
968 {
969 if (aChannel == 0 || aChannel == 1)
970 {
971 channel = storageBusChannels [aChannel];
972 break;
973 }
974
975 AssertMsgFailedBreak (("Invalid channel %d\n", aChannel));
976 }
977 case KStorageBus_SATA:
978 {
979 channel = storageBusChannels [2].arg (aChannel);
980 break;
981 }
982 default:
983 AssertFailedBreak();
984 }
985
986 return channel;
987}
988
989/**
990 * Returns a channel number on the given storage bus corresponding to the given
991 * string representation. Complementary to #toString (KStorageBus, LONG) const.
992 */
993LONG VBoxGlobal::toStorageChannel (KStorageBus aBus, const QString &aChannel) const
994{
995 LONG channel = 0;
996
997 switch (aBus)
998 {
999 case KStorageBus_IDE:
1000 {
1001 QStringVector::const_iterator it =
1002 qFind (storageBusChannels.begin(), storageBusChannels.end(),
1003 aChannel);
1004 AssertMsgBreak (it != storageBusChannels.end(),
1005 ("No value for {%s}\n", aChannel.toLatin1().constData()));
1006 channel = (LONG) (it - storageBusChannels.begin());
1007 break;
1008 }
1009 case KStorageBus_SATA:
1010 {
1011 /// @todo use regexp to properly extract the %1 text
1012 QString tpl = storageBusChannels [2].arg ("");
1013 if (aChannel.startsWith (tpl))
1014 {
1015 channel = aChannel.right (aChannel.length() - tpl.length()).toLong();
1016 break;
1017 }
1018
1019 AssertMsgFailedBreak (("Invalid channel {%s}\n", aChannel.toLatin1().constData()));
1020 break;
1021 }
1022 default:
1023 AssertFailedBreak();
1024 }
1025
1026 return channel;
1027}
1028
1029/**
1030 * Returns a string representation of the given device number of the given
1031 * channel on the given storage bus. Complementary to #toStorageDevice
1032 * (KStorageBus, LONG, const QString &) const.
1033 */
1034QString VBoxGlobal::toString (KStorageBus aBus, LONG aChannel, LONG aDevice) const
1035{
1036 NOREF (aChannel);
1037
1038 Assert (storageBusDevices.count() == 2);
1039 QString device;
1040
1041 switch (aBus)
1042 {
1043 case KStorageBus_IDE:
1044 {
1045 if (aDevice == 0 || aDevice == 1)
1046 {
1047 device = storageBusDevices [aDevice];
1048 break;
1049 }
1050
1051 AssertMsgFailedBreak (("Invalid device %d\n", aDevice));
1052 }
1053 case KStorageBus_SATA:
1054 {
1055 AssertMsgBreak (aDevice == 0, ("Invalid device %d\n", aDevice));
1056 /* always zero so far for SATA */
1057 break;
1058 }
1059 default:
1060 AssertFailedBreak();
1061 }
1062
1063 return device;
1064}
1065
1066/**
1067 * Returns a device number of the given channel on the given storage bus
1068 * corresponding to the given string representation. Complementary to #toString
1069 * (KStorageBus, LONG, LONG) const.
1070 */
1071LONG VBoxGlobal::toStorageDevice (KStorageBus aBus, LONG aChannel,
1072 const QString &aDevice) const
1073{
1074 NOREF (aChannel);
1075
1076 LONG device = 0;
1077
1078 switch (aBus)
1079 {
1080 case KStorageBus_IDE:
1081 {
1082 QStringVector::const_iterator it =
1083 qFind (storageBusDevices.begin(), storageBusDevices.end(),
1084 aDevice);
1085 AssertMsg (it != storageBusDevices.end(),
1086 ("No value for {%s}", aDevice.toLatin1().constData()));
1087 device = (LONG) (it - storageBusDevices.begin());
1088 break;
1089 }
1090 case KStorageBus_SATA:
1091 {
1092 AssertMsgBreak(aDevice.isEmpty(), ("Invalid device {%s}\n", aDevice.toLatin1().constData()));
1093 /* always zero for SATA so far. */
1094 break;
1095 }
1096 default:
1097 AssertFailedBreak();
1098 }
1099
1100 return device;
1101}
1102
1103/**
1104 * Returns a full string representation of the given device of the given channel
1105 * on the given storage bus. Complementary to #toStorageParams (KStorageBus,
1106 * LONG, LONG) const.
1107 */
1108QString VBoxGlobal::toFullString (KStorageBus aBus, LONG aChannel,
1109 LONG aDevice) const
1110{
1111 QString device;
1112
1113 switch (aBus)
1114 {
1115 case KStorageBus_IDE:
1116 {
1117 device = QString ("%1 %2 %3")
1118 .arg (vboxGlobal().toString (aBus))
1119 .arg (vboxGlobal().toString (aBus, aChannel))
1120 .arg (vboxGlobal().toString (aBus, aChannel, aDevice));
1121 break;
1122 }
1123 case KStorageBus_SATA:
1124 {
1125 /* we only have one SATA device so far which is always zero */
1126 device = QString ("%1 %2")
1127 .arg (vboxGlobal().toString (aBus))
1128 .arg (vboxGlobal().toString (aBus, aChannel));
1129 break;
1130 }
1131 default:
1132 AssertFailedBreak();
1133 }
1134
1135 return device;
1136}
1137
1138/**
1139 * Returns the list of all device types (VirtualBox::DeviceType COM enum).
1140 */
1141QStringList VBoxGlobal::deviceTypeStrings() const
1142{
1143 static QStringList list;
1144 if (list.empty())
1145 for (int i = 0; i < deviceTypes.count() - 1 /* usb=n/a */; i++)
1146 list += deviceTypes [i];
1147 return list;
1148}
1149
1150struct PortConfig
1151{
1152 const char *name;
1153 const ulong IRQ;
1154 const ulong IOBase;
1155};
1156
1157static const PortConfig comKnownPorts[] =
1158{
1159 { "COM1", 4, 0x3F8 },
1160 { "COM2", 3, 0x2F8 },
1161 { "COM3", 4, 0x3E8 },
1162 { "COM4", 3, 0x2E8 },
1163 /* must not contain an element with IRQ=0 and IOBase=0 used to cause
1164 * toCOMPortName() to return the "User-defined" string for these values. */
1165};
1166
1167static const PortConfig lptKnownPorts[] =
1168{
1169 { "LPT1", 7, 0x3BC },
1170 { "LPT2", 5, 0x378 },
1171 { "LPT3", 5, 0x278 },
1172 /* must not contain an element with IRQ=0 and IOBase=0 used to cause
1173 * toLPTPortName() to return the "User-defined" string for these values. */
1174};
1175
1176/**
1177 * Returns the list of the standard COM port names (i.e. "COMx").
1178 */
1179QStringList VBoxGlobal::COMPortNames() const
1180{
1181 QStringList list;
1182 for (size_t i = 0; i < ELEMENTS (comKnownPorts); ++ i)
1183 list << comKnownPorts [i].name;
1184
1185 return list;
1186}
1187
1188/**
1189 * Returns the list of the standard LPT port names (i.e. "LPTx").
1190 */
1191QStringList VBoxGlobal::LPTPortNames() const
1192{
1193 QStringList list;
1194 for (size_t i = 0; i < ELEMENTS (lptKnownPorts); ++ i)
1195 list << lptKnownPorts [i].name;
1196
1197 return list;
1198}
1199
1200/**
1201 * Returns the name of the standard COM port corresponding to the given
1202 * parameters, or "User-defined" (which is also returned when both
1203 * @a aIRQ and @a aIOBase are 0).
1204 */
1205QString VBoxGlobal::toCOMPortName (ulong aIRQ, ulong aIOBase) const
1206{
1207 for (size_t i = 0; i < ELEMENTS (comKnownPorts); ++ i)
1208 if (comKnownPorts [i].IRQ == aIRQ &&
1209 comKnownPorts [i].IOBase == aIOBase)
1210 return comKnownPorts [i].name;
1211
1212 return mUserDefinedPortName;
1213}
1214
1215/**
1216 * Returns the name of the standard LPT port corresponding to the given
1217 * parameters, or "User-defined" (which is also returned when both
1218 * @a aIRQ and @a aIOBase are 0).
1219 */
1220QString VBoxGlobal::toLPTPortName (ulong aIRQ, ulong aIOBase) const
1221{
1222 for (size_t i = 0; i < ELEMENTS (lptKnownPorts); ++ i)
1223 if (lptKnownPorts [i].IRQ == aIRQ &&
1224 lptKnownPorts [i].IOBase == aIOBase)
1225 return lptKnownPorts [i].name;
1226
1227 return mUserDefinedPortName;
1228}
1229
1230/**
1231 * Returns port parameters corresponding to the given standard COM name.
1232 * Returns @c true on success, or @c false if the given port name is not one
1233 * of the standard names (i.e. "COMx").
1234 */
1235bool VBoxGlobal::toCOMPortNumbers (const QString &aName, ulong &aIRQ,
1236 ulong &aIOBase) const
1237{
1238 for (size_t i = 0; i < ELEMENTS (comKnownPorts); ++ i)
1239 if (strcmp (comKnownPorts [i].name, aName.toUtf8().data()) == 0)
1240 {
1241 aIRQ = comKnownPorts [i].IRQ;
1242 aIOBase = comKnownPorts [i].IOBase;
1243 return true;
1244 }
1245
1246 return false;
1247}
1248
1249/**
1250 * Returns port parameters corresponding to the given standard LPT name.
1251 * Returns @c true on success, or @c false if the given port name is not one
1252 * of the standard names (i.e. "LPTx").
1253 */
1254bool VBoxGlobal::toLPTPortNumbers (const QString &aName, ulong &aIRQ,
1255 ulong &aIOBase) const
1256{
1257 for (size_t i = 0; i < ELEMENTS (lptKnownPorts); ++ i)
1258 if (strcmp (lptKnownPorts [i].name, aName.toUtf8().data()) == 0)
1259 {
1260 aIRQ = lptKnownPorts [i].IRQ;
1261 aIOBase = lptKnownPorts [i].IOBase;
1262 return true;
1263 }
1264
1265 return false;
1266}
1267
1268/**
1269 * Returns the details of the given hard disk as a single-line string
1270 * to be used in the VM details view.
1271 *
1272 * The details include the type and the virtual size of the hard disk.
1273 * Note that for differencing hard disks based on immutable hard disks,
1274 * the Immutable hard disk type is returned.
1275 *
1276 * @param aHD hard disk image (when predict = true, must be a top-level image)
1277 * @param aPredict when true, the function predicts the type of the resulting
1278 * image after attaching the given image to the machine.
1279 * Otherwise, a real type of the given image is returned
1280 * (with the exception mentioned above).
1281 *
1282 * @note The hard disk object may become uninitialized by a third party
1283 * while this method is reading its properties. In this case, the method will
1284 * return an empty string.
1285 */
1286QString VBoxGlobal::details (const CHardDisk &aHD, bool aPredict /* = false */,
1287 bool aDoRefresh)
1288{
1289 Assert (!aPredict || aHD.GetParent().isNull());
1290
1291 VBoxMedia media;
1292 if (!aDoRefresh)
1293 media = VBoxMedia (CUnknown (aHD), VBoxDefs::HD, VBoxMedia::Ok);
1294 else if (!findMedia (CUnknown (aHD), media))
1295 {
1296 /* media may be new and not alredy in the media list, request refresh */
1297 startEnumeratingMedia();
1298 if (!findMedia (CUnknown (aHD), media))
1299 AssertFailed();
1300 }
1301
1302 CHardDisk root = aHD.GetRoot();
1303
1304 // @todo *** this check is rough; if aHD becomes uninitialized, any of aHD
1305 // getters called afterwards will also fail. The same relates to the root
1306 // object (that will be aHD itself in case of non-differencing
1307 // disks). However, this check was added to fix a particular use case:
1308 // when aHD is a differencing hard disk and it happens to be discarded
1309 // (and uninitialized) after this method is called but before we read all
1310 // its properties (yes, it's possible!), the root object will be null and
1311 // calling methods on it will assert in the debug builds. This check seems
1312 // to be enough as a quick solution (fresh hard disk attachments will be
1313 // re-read by a state change signal after the discard operation is
1314 // finished, so the user will eventually see correct data), but in order
1315 // to solve the problem properly we need to use exceptions everywhere (or
1316 // check the result after every method call). See also Comment #17 and
1317 // below in Defect #2126.
1318 if (!aHD.isOk())
1319 return QString::null;
1320
1321 QString details;
1322
1323 KHardDiskType type = root.GetType();
1324
1325 if (type == KHardDiskType_Normal &&
1326 (aHD != root || (aPredict && root.GetChildren().GetCount() != 0)))
1327 details = tr ("Differencing", "hard disk");
1328 else
1329 details = hardDiskTypeString (root);
1330
1331 details += ", ";
1332
1333 /// @todo prepend the details with the warning/error
1334 // icon when not accessible
1335
1336 switch (media.status)
1337 {
1338 case VBoxMedia::Unknown:
1339 details += tr ("<i>Checking...</i>", "hard disk");
1340 break;
1341 case VBoxMedia::Ok:
1342 details += formatSize (root.GetSize() * _1M);
1343 break;
1344 case VBoxMedia::Error:
1345 case VBoxMedia::Inaccessible:
1346 details += tr ("<i>Inaccessible</i>", "hard disk");
1347 break;
1348 }
1349
1350 return details;
1351}
1352
1353/**
1354 * Returns the details of the given USB device as a single-line string.
1355 */
1356QString VBoxGlobal::details (const CUSBDevice &aDevice) const
1357{
1358 QString details;
1359 QString m = aDevice.GetManufacturer().trimmed();
1360 QString p = aDevice.GetProduct().trimmed();
1361 if (m.isEmpty() && p.isEmpty())
1362 {
1363 details =
1364 tr ("Unknown device %1:%2", "USB device details")
1365 .arg (QString().sprintf ("%04hX", aDevice.GetVendorId()))
1366 .arg (QString().sprintf ("%04hX", aDevice.GetProductId()));
1367 }
1368 else
1369 {
1370 if (p.toUpper().startsWith (m.toUpper()))
1371 details = p;
1372 else
1373 details = m + " " + p;
1374 }
1375 ushort r = aDevice.GetRevision();
1376 if (r != 0)
1377 details += QString().sprintf (" [%04hX]", r);
1378
1379 return details.trimmed();
1380}
1381
1382/**
1383 * Returns the multi-line description of the given USB device.
1384 */
1385QString VBoxGlobal::toolTip (const CUSBDevice &aDevice) const
1386{
1387 QString tip =
1388 tr ("<nobr>Vendor ID: %1</nobr><br>"
1389 "<nobr>Product ID: %2</nobr><br>"
1390 "<nobr>Revision: %3</nobr>", "USB device tooltip")
1391 .arg (QString().sprintf ("%04hX", aDevice.GetVendorId()))
1392 .arg (QString().sprintf ("%04hX", aDevice.GetProductId()))
1393 .arg (QString().sprintf ("%04hX", aDevice.GetRevision()));
1394
1395 QString ser = aDevice.GetSerialNumber();
1396 if (!ser.isEmpty())
1397 tip += QString (tr ("<br><nobr>Serial No. %1</nobr>", "USB device tooltip"))
1398 .arg (ser);
1399
1400 /* add the state field if it's a host USB device */
1401 CHostUSBDevice hostDev = CUnknown (aDevice);
1402 if (!hostDev.isNull())
1403 {
1404 tip += QString (tr ("<br><nobr>State: %1</nobr>", "USB device tooltip"))
1405 .arg (vboxGlobal().toString (hostDev.GetState()));
1406 }
1407
1408 return tip;
1409}
1410
1411/**
1412 * Returns the multi-line description of the given USB filter
1413 */
1414QString VBoxGlobal::toolTip (const CUSBDeviceFilter &aFilter) const
1415{
1416 QString tip;
1417
1418 QString vendorId = aFilter.GetVendorId();
1419 if (!vendorId.isEmpty())
1420 tip += tr ("<nobr>Vendor ID: %1</nobr>", "USB filter tooltip")
1421 .arg (vendorId);
1422
1423 QString productId = aFilter.GetProductId();
1424 if (!productId.isEmpty())
1425 tip += tip.isEmpty() ? "":"<br/>" + tr ("<nobr>Product ID: %2</nobr>", "USB filter tooltip")
1426 .arg (productId);
1427
1428 QString revision = aFilter.GetRevision();
1429 if (!revision.isEmpty())
1430 tip += tip.isEmpty() ? "":"<br/>" + tr ("<nobr>Revision: %3</nobr>", "USB filter tooltip")
1431 .arg (revision);
1432
1433 QString product = aFilter.GetProduct();
1434 if (!product.isEmpty())
1435 tip += tip.isEmpty() ? "":"<br/>" + tr ("<nobr>Product: %4</nobr>", "USB filter tooltip")
1436 .arg (product);
1437
1438 QString manufacturer = aFilter.GetManufacturer();
1439 if (!manufacturer.isEmpty())
1440 tip += tip.isEmpty() ? "":"<br/>" + tr ("<nobr>Manufacturer: %5</nobr>", "USB filter tooltip")
1441 .arg (manufacturer);
1442
1443 QString serial = aFilter.GetSerialNumber();
1444 if (!serial.isEmpty())
1445 tip += tip.isEmpty() ? "":"<br/>" + tr ("<nobr>Serial No.: %1</nobr>", "USB filter tooltip")
1446 .arg (serial);
1447
1448 QString port = aFilter.GetPort();
1449 if (!port.isEmpty())
1450 tip += tip.isEmpty() ? "":"<br/>" + tr ("<nobr>Port: %1</nobr>", "USB filter tooltip")
1451 .arg (port);
1452
1453 /* add the state field if it's a host USB device */
1454 CHostUSBDevice hostDev = CUnknown (aFilter);
1455 if (!hostDev.isNull())
1456 {
1457 tip += tip.isEmpty() ? "":"<br/>" + tr ("<nobr>State: %1</nobr>", "USB filter tooltip")
1458 .arg (vboxGlobal().toString (hostDev.GetState()));
1459 }
1460
1461 return tip;
1462}
1463
1464/**
1465 * Puts soft hyphens after every path component in the given file name.
1466 * @param fn file name (must be a full path name)
1467 */
1468QString VBoxGlobal::prepareFileNameForHTML (const QString &fn) const
1469{
1470/// @todo (dmik) remove?
1471// QString result = QDir::convertSeparators (fn);
1472//#ifdef Q_OS_LINUX
1473// result.replace ('/', "/<font color=red>&shy;</font>");
1474//#else
1475// result.replace ('\\', "\\<font color=red>&shy;</font>");
1476//#endif
1477// return result;
1478 QFileInfo fi (fn);
1479 return fi.fileName();
1480}
1481
1482/**
1483 * Returns a details report on a given VM enclosed in a HTML table.
1484 *
1485 * @param m machine to create a report for
1486 * @param isNewVM true when called by the New VM Wizard
1487 * @param withLinks true if section titles should be hypertext links
1488 */
1489QString VBoxGlobal::detailsReport (const CMachine &m, bool isNewVM,
1490 bool withLinks, bool aDoRefresh)
1491{
1492 static const char *sTableTpl =
1493 "<table border=0 cellspacing=1 cellpadding=0>%1</table>";
1494 static const char *sSectionHrefTpl =
1495 "<tr><td width=22 rowspan=%1 align=left><img src='%2'></td>"
1496 "<td colspan=2><b><a href='%3'><nobr>%4</nobr></a></b></td></tr>"
1497 "%5"
1498 "<tr><td colspan=2><font size=1>&nbsp;</font></td></tr>";
1499 static const char *sSectionBoldTpl =
1500 "<tr><td width=22 rowspan=%1 align=left><img src='%2'></td>"
1501 "<td colspan=2><!-- %3 --><b><nobr>%4</nobr></b></td></tr>"
1502 "%5"
1503 "<tr><td colspan=2><font size=1>&nbsp;</font></td></tr>";
1504 static const char *sSectionItemTpl =
1505 "<tr><td width=40%><nobr>%1</nobr></td><td>%2</td></tr>";
1506
1507 static QString sGeneralBasicHrefTpl, sGeneralBasicBoldTpl;
1508 static QString sGeneralFullHrefTpl, sGeneralFullBoldTpl;
1509
1510 /* generate templates after every language change */
1511
1512 if (!detailReportTemplatesReady)
1513 {
1514 detailReportTemplatesReady = true;
1515
1516 QString generalItems
1517 = QString (sSectionItemTpl).arg (tr ("Name", "details report"), "%1")
1518 + QString (sSectionItemTpl).arg (tr ("OS Type", "details report"), "%2")
1519 + QString (sSectionItemTpl).arg (tr ("Base Memory", "details report"),
1520 tr ("<nobr>%3 MB</nobr>", "details report"));
1521 sGeneralBasicHrefTpl = QString (sSectionHrefTpl)
1522 .arg (2 + 3) /* rows */
1523 .arg (":/machine_16px.png", /* icon */
1524 "#general", /* link */
1525 tr ("General", "details report"), /* title */
1526 generalItems); /* items */
1527 sGeneralBasicBoldTpl = QString (sSectionBoldTpl)
1528 .arg (2 + 3) /* rows */
1529 .arg (":/machine_16px.png", /* icon */
1530 "#general", /* link */
1531 tr ("General", "details report"), /* title */
1532 generalItems); /* items */
1533
1534 generalItems
1535 += QString (sSectionItemTpl).arg (tr ("Video Memory", "details report"),
1536 tr ("<nobr>%4 MB</nobr>", "details report"))
1537 + QString (sSectionItemTpl).arg (tr ("Boot Order", "details report"), "%5")
1538 + QString (sSectionItemTpl).arg (tr ("ACPI", "details report"), "%6")
1539 + QString (sSectionItemTpl).arg (tr ("IO APIC", "details report"), "%7")
1540 + QString (sSectionItemTpl).arg (tr ("VT-x/AMD-V", "details report"), "%8")
1541 + QString (sSectionItemTpl).arg (tr ("PAE/NX", "details report"), "%9");
1542
1543 sGeneralFullHrefTpl = QString (sSectionHrefTpl)
1544 .arg (2 + 9) /* rows */
1545 .arg (":/machine_16px.png", /* icon */
1546 "#general", /* link */
1547 tr ("General", "details report"), /* title */
1548 generalItems); /* items */
1549 sGeneralFullBoldTpl = QString (sSectionBoldTpl)
1550 .arg (2 + 9) /* rows */
1551 .arg (":/machine_16px.png", /* icon */
1552 "#general", /* link */
1553 tr ("General", "details report"), /* title */
1554 generalItems); /* items */
1555 }
1556
1557 /* common generated content */
1558
1559 const QString &sectionTpl = withLinks
1560 ? sSectionHrefTpl
1561 : sSectionBoldTpl;
1562
1563 QString hardDisks;
1564 {
1565 int rows = 2; /* including section header and footer */
1566
1567 CHardDiskAttachmentEnumerator aen = m.GetHardDiskAttachments().Enumerate();
1568 while (aen.HasMore())
1569 {
1570 CHardDiskAttachment hda = aen.GetNext();
1571 CHardDisk hd = hda.GetHardDisk();
1572 /// @todo for the explaination of the below isOk() checks, see
1573 /// @todo *** in #details (const CHardDisk &, bool).
1574 if (hda.isOk())
1575 {
1576 CHardDisk root = hd.GetRoot();
1577 if (hd.isOk())
1578 {
1579 QString src = root.GetLocation();
1580 KStorageBus bus = hda.GetBus();
1581 LONG channel = hda.GetChannel();
1582 LONG device = hda.GetDevice();
1583 hardDisks += QString (sSectionItemTpl)
1584 .arg (toFullString (bus, channel, device))
1585 .arg (QString ("%1 [<nobr>%2</nobr>]")
1586 .arg (prepareFileNameForHTML (src))
1587 .arg (details (hd, isNewVM /* predict */, aDoRefresh)));
1588 ++ rows;
1589 }
1590 }
1591 }
1592
1593 if (hardDisks.isNull())
1594 {
1595 hardDisks = QString (sSectionItemTpl)
1596 .arg (tr ("Not Attached", "details report (HDDs)")).arg ("");
1597 ++ rows;
1598 }
1599
1600 hardDisks = sectionTpl
1601 .arg (rows) /* rows */
1602 .arg (":/hd_16px.png", /* icon */
1603 "#hdds", /* link */
1604 tr ("Hard Disks", "details report"), /* title */
1605 hardDisks); /* items */
1606 }
1607
1608 /* compose details report */
1609
1610 const QString &generalBasicTpl = withLinks
1611 ? sGeneralBasicHrefTpl
1612 : sGeneralBasicBoldTpl;
1613
1614 const QString &generalFullTpl = withLinks
1615 ? sGeneralFullHrefTpl
1616 : sGeneralFullBoldTpl;
1617
1618 QString detailsReport;
1619
1620 if (isNewVM)
1621 {
1622 detailsReport
1623 = generalBasicTpl
1624 .arg (m.GetName())
1625 .arg (vmGuestOSTypeDescription (m.GetOSTypeId()))
1626 .arg (m.GetMemorySize())
1627 + hardDisks;
1628 }
1629 else
1630 {
1631 /* boot order */
1632 QString bootOrder;
1633 for (ulong i = 1; i <= mVBox.GetSystemProperties().GetMaxBootPosition(); i++)
1634 {
1635 KDeviceType device = m.GetBootOrder (i);
1636 if (device == KDeviceType_Null)
1637 continue;
1638 if (!bootOrder.isEmpty())
1639 bootOrder += ", ";
1640 bootOrder += toString (device);
1641 }
1642 if (bootOrder.isEmpty())
1643 bootOrder = toString (KDeviceType_Null);
1644
1645 CBIOSSettings biosSettings = m.GetBIOSSettings();
1646
1647 /* ACPI */
1648 QString acpi = biosSettings.GetACPIEnabled()
1649 ? tr ("Enabled", "details report (ACPI)")
1650 : tr ("Disabled", "details report (ACPI)");
1651
1652 /* IO APIC */
1653 QString ioapic = biosSettings.GetIOAPICEnabled()
1654 ? tr ("Enabled", "details report (IO APIC)")
1655 : tr ("Disabled", "details report (IO APIC)");
1656
1657 /* VT-x/AMD-V */
1658 QString virt = m.GetHWVirtExEnabled() == KTSBool_True ?
1659 tr ("Enabled", "details report (VT-x/AMD-V)") :
1660 tr ("Disabled", "details report (VT-x/AMD-V)");
1661
1662 /* PAE/NX */
1663 QString pae = m.GetPAEEnabled()
1664 ? tr ("Enabled", "details report (PAE/NX)")
1665 : tr ("Disabled", "details report (PAE/NX)");
1666
1667 /* General + Hard Disks */
1668 detailsReport
1669 = generalFullTpl
1670 .arg (m.GetName())
1671 .arg (vmGuestOSTypeDescription (m.GetOSTypeId()))
1672 .arg (m.GetMemorySize())
1673 .arg (m.GetVRAMSize())
1674 .arg (bootOrder)
1675 .arg (acpi)
1676 .arg (ioapic)
1677 .arg (virt)
1678 .arg (pae)
1679 + hardDisks;
1680
1681 QString item;
1682
1683 /* DVD */
1684 CDVDDrive dvd = m.GetDVDDrive();
1685 item = QString (sSectionItemTpl);
1686 switch (dvd.GetState())
1687 {
1688 case KDriveState_NotMounted:
1689 item = item.arg (tr ("Not mounted", "details report (DVD)"), "");
1690 break;
1691 case KDriveState_ImageMounted:
1692 {
1693 CDVDImage img = dvd.GetImage();
1694 item = item.arg (tr ("Image", "details report (DVD)"),
1695 prepareFileNameForHTML (img.GetFilePath()));
1696 break;
1697 }
1698 case KDriveState_HostDriveCaptured:
1699 {
1700 CHostDVDDrive drv = dvd.GetHostDrive();
1701 QString drvName = drv.GetName();
1702 QString description = drv.GetDescription();
1703 QString fullName = description.isEmpty() ?
1704 drvName :
1705 QString ("%1 (%2)").arg (description, drvName);
1706 item = item.arg (tr ("Host Drive", "details report (DVD)"),
1707 fullName);
1708 break;
1709 }
1710 default:
1711 AssertMsgFailed (("Invalid DVD state: %d", dvd.GetState()));
1712 }
1713 detailsReport += sectionTpl
1714 .arg (2 + 1) /* rows */
1715 .arg (":/cd_16px.png", /* icon */
1716 "#dvd", /* link */
1717 tr ("CD/DVD-ROM", "details report"), /* title */
1718 item); // items
1719
1720 /* Floppy */
1721 CFloppyDrive floppy = m.GetFloppyDrive();
1722 item = QString (sSectionItemTpl);
1723 switch (floppy.GetState())
1724 {
1725 case KDriveState_NotMounted:
1726 item = item.arg (tr ("Not mounted", "details report (floppy)"), "");
1727 break;
1728 case KDriveState_ImageMounted:
1729 {
1730 CFloppyImage img = floppy.GetImage();
1731 item = item.arg (tr ("Image", "details report (floppy)"),
1732 prepareFileNameForHTML (img.GetFilePath()));
1733 break;
1734 }
1735 case KDriveState_HostDriveCaptured:
1736 {
1737 CHostFloppyDrive drv = floppy.GetHostDrive();
1738 QString drvName = drv.GetName();
1739 QString description = drv.GetDescription();
1740 QString fullName = description.isEmpty() ?
1741 drvName :
1742 QString ("%1 (%2)").arg (description, drvName);
1743 item = item.arg (tr ("Host Drive", "details report (floppy)"),
1744 fullName);
1745 break;
1746 }
1747 default:
1748 AssertMsgFailed (("Invalid floppy state: %d", floppy.GetState()));
1749 }
1750 detailsReport += sectionTpl
1751 .arg (2 + 1) /* rows */
1752 .arg (":/fd_16px.png", /* icon */
1753 "#floppy", /* link */
1754 tr ("Floppy", "details report"), /* title */
1755 item); /* items */
1756
1757 /* audio */
1758 {
1759 CAudioAdapter audio = m.GetAudioAdapter();
1760 int rows = audio.GetEnabled() ? 3 : 2;
1761 if (audio.GetEnabled())
1762 item = QString (sSectionItemTpl)
1763 .arg (tr ("Host Driver", "details report (audio)"),
1764 toString (audio.GetAudioDriver())) +
1765 QString (sSectionItemTpl)
1766 .arg (tr ("Controller", "details report (audio)"),
1767 toString (audio.GetAudioController()));
1768 else
1769 item = QString (sSectionItemTpl)
1770 .arg (tr ("Disabled", "details report (audio)"), "");
1771
1772 detailsReport += sectionTpl
1773 .arg (rows + 1) /* rows */
1774 .arg (":/sound_16px.png", /* icon */
1775 "#audio", /* link */
1776 tr ("Audio", "details report"), /* title */
1777 item); /* items */
1778 }
1779 /* network */
1780 {
1781 item = QString::null;
1782 ulong count = mVBox.GetSystemProperties().GetNetworkAdapterCount();
1783 int rows = 2; /* including section header and footer */
1784 for (ulong slot = 0; slot < count; slot ++)
1785 {
1786 CNetworkAdapter adapter = m.GetNetworkAdapter (slot);
1787 if (adapter.GetEnabled())
1788 {
1789 KNetworkAttachmentType type = adapter.GetAttachmentType();
1790 QString attType = toString (adapter.GetAdapterType())
1791 .replace (QRegExp ("\\s\\(.+\\)"), " (%1)");
1792 /* don't use the adapter type string for types that have
1793 * an additional symbolic network/interface name field, use
1794 * this name instead */
1795 if (type == KNetworkAttachmentType_HostInterface)
1796 attType = attType.arg (tr ("host interface, %1",
1797 "details report (network)").arg (adapter.GetHostInterface()));
1798 else if (type == KNetworkAttachmentType_Internal)
1799 attType = attType.arg (tr ("internal network, '%1'",
1800 "details report (network)").arg (adapter.GetInternalNetwork()));
1801 else
1802 attType = attType.arg (vboxGlobal().toString (type));
1803
1804 item += QString (sSectionItemTpl)
1805 .arg (tr ("Adapter %1", "details report (network)")
1806 .arg (adapter.GetSlot() + 1))
1807 .arg (attType);
1808 ++ rows;
1809 }
1810 }
1811 if (item.isNull())
1812 {
1813 item = QString (sSectionItemTpl)
1814 .arg (tr ("Disabled", "details report (network)"), "");
1815 ++ rows;
1816 }
1817
1818 detailsReport += sectionTpl
1819 .arg (rows) /* rows */
1820 .arg (":/nw_16px.png", /* icon */
1821 "#network", /* link */
1822 tr ("Network", "details report"), /* title */
1823 item); /* items */
1824 }
1825 /* serial ports */
1826 {
1827 item = QString::null;
1828 ulong count = mVBox.GetSystemProperties().GetSerialPortCount();
1829 int rows = 2; /* including section header and footer */
1830 for (ulong slot = 0; slot < count; slot ++)
1831 {
1832 CSerialPort port = m.GetSerialPort (slot);
1833 if (port.GetEnabled())
1834 {
1835 KPortMode mode = port.GetHostMode();
1836 QString data =
1837 toCOMPortName (port.GetIRQ(), port.GetIOBase()) + ", ";
1838 if (mode == KPortMode_HostPipe ||
1839 mode == KPortMode_HostDevice)
1840 data += QString ("%1 (<nobr>%2</nobr>)")
1841 .arg (vboxGlobal().toString (mode))
1842 .arg (QDir::convertSeparators (port.GetPath()));
1843 else
1844 data += toString (mode);
1845
1846 item += QString (sSectionItemTpl)
1847 .arg (tr ("Port %1", "details report (serial ports)")
1848 .arg (port.GetSlot() + 1))
1849 .arg (data);
1850 ++ rows;
1851 }
1852 }
1853 if (item.isNull())
1854 {
1855 item = QString (sSectionItemTpl)
1856 .arg (tr ("Disabled", "details report (serial ports)"), "");
1857 ++ rows;
1858 }
1859
1860 detailsReport += sectionTpl
1861 .arg (rows) /* rows */
1862 .arg (":/serial_port_16px.png", /* icon */
1863 "#serialPorts", /* link */
1864 tr ("Serial Ports", "details report"), /* title */
1865 item); /* items */
1866 }
1867 /* parallel ports */
1868 {
1869 item = QString::null;
1870 ulong count = mVBox.GetSystemProperties().GetParallelPortCount();
1871 int rows = 2; /* including section header and footer */
1872 for (ulong slot = 0; slot < count; slot ++)
1873 {
1874 CParallelPort port = m.GetParallelPort (slot);
1875 if (port.GetEnabled())
1876 {
1877 QString data =
1878 toLPTPortName (port.GetIRQ(), port.GetIOBase()) +
1879 QString (" (<nobr>%1</nobr>)")
1880 .arg (QDir::convertSeparators (port.GetPath()));
1881
1882 item += QString (sSectionItemTpl)
1883 .arg (tr ("Port %1", "details report (parallel ports)")
1884 .arg (port.GetSlot() + 1))
1885 .arg (data);
1886 ++ rows;
1887 }
1888 }
1889 if (item.isNull())
1890 {
1891 item = QString (sSectionItemTpl)
1892 .arg (tr ("Disabled", "details report (parallel ports)"), "");
1893 ++ rows;
1894 }
1895
1896 /* Temporary disabled */
1897 QString dummy = sectionTpl /* detailsReport += sectionTpl */
1898 .arg (rows) /* rows */
1899 .arg (":/parallel_port_16px.png", /* icon */
1900 "#parallelPorts", /* link */
1901 tr ("Parallel Ports", "details report"), /* title */
1902 item); /* items */
1903 }
1904 /* USB */
1905 {
1906 CUSBController ctl = m.GetUSBController();
1907 if (!ctl.isNull())
1908 {
1909 /* the USB controller may be unavailable (i.e. in VirtualBox OSE) */
1910
1911 if (ctl.GetEnabled())
1912 {
1913 CUSBDeviceFilterCollection coll = ctl.GetDeviceFilters();
1914 CUSBDeviceFilterEnumerator en = coll.Enumerate();
1915 uint active = 0;
1916 while (en.HasMore())
1917 if (en.GetNext().GetActive())
1918 active ++;
1919
1920 item = QString (sSectionItemTpl)
1921 .arg (tr ("Device Filters", "details report (USB)"),
1922 tr ("%1 (%2 active)", "details report (USB)")
1923 .arg (coll.GetCount()).arg (active));
1924 }
1925 else
1926 item = QString (sSectionItemTpl)
1927 .arg (tr ("Disabled", "details report (USB)"), "");
1928
1929 detailsReport += sectionTpl
1930 .arg (2 + 1) /* rows */
1931 .arg (":/usb_16px.png", /* icon */
1932 "#usb", /* link */
1933 tr ("USB", "details report"), /* title */
1934 item); /* items */
1935 }
1936 }
1937 /* Shared folders */
1938 {
1939 ulong count = m.GetSharedFolders().GetCount();
1940 if (count > 0)
1941 {
1942 item = QString (sSectionItemTpl)
1943 .arg (tr ("Shared Folders", "details report (shared folders)"),
1944 tr ("%1", "details report (shadef folders)")
1945 .arg (count));
1946 }
1947 else
1948 item = QString (sSectionItemTpl)
1949 .arg (tr ("None", "details report (shared folders)"), "");
1950
1951 detailsReport += sectionTpl
1952 .arg (2 + 1) /* rows */
1953 .arg (":/shared_folder_16px.png", /* icon */
1954 "#sfolders", /* link */
1955 tr ("Shared Folders", "details report"), /* title */
1956 item); /* items */
1957 }
1958 /* VRDP */
1959 {
1960 CVRDPServer srv = m.GetVRDPServer();
1961 if (!srv.isNull())
1962 {
1963 /* the VRDP server may be unavailable (i.e. in VirtualBox OSE) */
1964
1965 if (srv.GetEnabled())
1966 item = QString (sSectionItemTpl)
1967 .arg (tr ("VRDP Server Port", "details report (VRDP)"),
1968 tr ("%1", "details report (VRDP)")
1969 .arg (srv.GetPort()));
1970 else
1971 item = QString (sSectionItemTpl)
1972 .arg (tr ("Disabled", "details report (VRDP)"), "");
1973
1974 detailsReport += sectionTpl
1975 .arg (2 + 1) /* rows */
1976 .arg (":/vrdp_16px.png", /* icon */
1977 "#vrdp", /* link */
1978 tr ("Remote Display", "details report"), /* title */
1979 item); /* items */
1980 }
1981 }
1982 }
1983
1984 return QString (sTableTpl). arg (detailsReport);
1985}
1986
1987QString VBoxGlobal::platformInfo()
1988{
1989 QString platform;
1990
1991#if defined (Q_OS_WIN)
1992 platform = "win";
1993#elif defined (Q_OS_LINUX)
1994 platform = "linux";
1995#elif defined (Q_OS_MACX)
1996 platform = "macosx";
1997#elif defined (Q_OS_OS2)
1998 platform = "os2";
1999#elif defined (Q_OS_FREEBSD)
2000 platform = "freebsd";
2001#elif defined (Q_OS_SOLARIS)
2002 platform = "solaris";
2003#else
2004 platform = "unknown";
2005#endif
2006
2007 /* The format is <system>.<bitness> */
2008 platform += QString (".%1").arg (ARCH_BITS);
2009
2010 /* Add more system information */
2011#if defined (Q_OS_WIN)
2012 OSVERSIONINFO versionInfo;
2013 ZeroMemory (&versionInfo, sizeof (OSVERSIONINFO));
2014 versionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
2015 GetVersionEx (&versionInfo);
2016 int major = versionInfo.dwMajorVersion;
2017 int minor = versionInfo.dwMinorVersion;
2018 int build = versionInfo.dwBuildNumber;
2019 QString sp = QString::fromUtf16 ((ushort*)versionInfo.szCSDVersion);
2020
2021 QString distrib;
2022 if (major == 6)
2023 distrib = QString ("Windows Vista %1");
2024 else if (major == 5)
2025 {
2026 if (minor == 2)
2027 distrib = QString ("Windows Server 2003 %1");
2028 else if (minor == 1)
2029 distrib = QString ("Windows XP %1");
2030 else if (minor == 0)
2031 distrib = QString ("Windows 2000 %1");
2032 else
2033 distrib = QString ("Unknown %1");
2034 }
2035 else if (major == 4)
2036 {
2037 if (minor == 90)
2038 distrib = QString ("Windows Me %1");
2039 else if (minor == 10)
2040 distrib = QString ("Windows 98 %1");
2041 else if (minor == 0)
2042 distrib = QString ("Windows 95 %1");
2043 else
2044 distrib = QString ("Unknown %1");
2045 }
2046 else
2047 distrib = QString ("Unknown %1");
2048 distrib = distrib.arg (sp);
2049 QString version = QString ("%1.%2").arg (major).arg (minor);
2050 QString kernel = QString ("%1").arg (build);
2051 platform += QString (" [Distribution: %1 | Version: %2 | Build: %3]")
2052 .arg (distrib).arg (version).arg (kernel);
2053#elif defined (Q_OS_OS2)
2054 // TODO: add sys info for os2 if any...
2055#elif defined (Q_OS_LINUX) || defined (Q_OS_MACX) || defined (Q_OS_FREEBSD) || defined (Q_OS_SOLARIS)
2056 /* Get script path */
2057 char szAppPrivPath [RTPATH_MAX];
2058 int rc = RTPathAppPrivateNoArch (szAppPrivPath, sizeof (szAppPrivPath)); NOREF(rc);
2059 AssertRC (rc);
2060 /* Run script */
2061 QByteArray result =
2062 Process::singleShot (QString (szAppPrivPath) + "/VBoxSysInfo.sh");
2063 if (!result.isNull())
2064 platform += QString (" [%1]").arg (QString (result).trimmed());
2065#endif
2066
2067 return platform;
2068}
2069
2070#if defined(Q_WS_X11) && !defined(VBOX_OSE)
2071bool VBoxGlobal::showVirtualBoxLicense()
2072{
2073 /* get the apps doc path */
2074 int size = 256;
2075 char *buffer = (char*) RTMemTmpAlloc (size);
2076 RTPathAppDocs (buffer, size);
2077 QString path (buffer);
2078 RTMemTmpFree (buffer);
2079 QDir docDir (path);
2080 docDir.setFilter (QDir::Files);
2081 docDir.setNameFilters (QStringList ("License-*.html"));
2082
2083 /* get the license files list and search for the latest license */
2084 QStringList filesList = docDir.entryList();
2085 double maxVersionNumber = 0;
2086 for (int index = 0; index < filesList.count(); ++ index)
2087 {
2088 QRegExp regExp ("License-([\\d\\.]+).html");
2089 regExp.indexIn (filesList [index]);
2090 QString version = regExp.cap (1);
2091 if (maxVersionNumber < version.toDouble())
2092 maxVersionNumber = version.toDouble();
2093 }
2094 if (!maxVersionNumber)
2095 {
2096 vboxProblem().cannotFindLicenseFiles (path);
2097 return false;
2098 }
2099
2100 /* compose the latest license file full path */
2101 QString latestVersion = QString::number (maxVersionNumber);
2102 QString latestFilePath = docDir.absoluteFilePath (
2103 QString ("License-%1.html").arg (latestVersion));
2104
2105 /* check for the agreed license version */
2106 QString licenseAgreed = virtualBox().GetExtraData (VBoxDefs::GUI_LicenseKey);
2107 if (licenseAgreed == latestVersion)
2108 return true;
2109
2110 VBoxLicenseViewer licenseDialog (latestFilePath);
2111 bool result = licenseDialog.exec() == QDialog::Accepted;
2112 if (result)
2113 virtualBox().SetExtraData (VBoxDefs::GUI_LicenseKey, latestVersion);
2114 return result;
2115}
2116#endif /* defined(Q_WS_X11) && !defined(VBOX_OSE) */
2117
2118/**
2119 * Checks if any of the settings files were auto-converted and informs the user
2120 * if so.
2121 */
2122void VBoxGlobal::checkForAutoConvertedSettings()
2123{
2124 QString formatVersion = mVBox.GetSettingsFormatVersion();
2125
2126 bool isGlobalConverted = false;
2127 QList <CMachine> machines;
2128 QString fileList;
2129 QString version;
2130
2131 CMachineVector vec = mVBox.GetMachines2();
2132 for (CMachineVector::ConstIterator m = vec.begin();
2133 m != vec.end(); ++ m)
2134 {
2135 if (!m->GetAccessible())
2136 continue;
2137
2138 version = m->GetSettingsFileVersion();
2139 if (version != formatVersion)
2140 {
2141 machines.append (*m);
2142 fileList += QString ("<nobr>%1&nbsp;&nbsp;&nbsp;(<i>%2</i>)</nobr><br>")
2143 .arg (m->GetSettingsFilePath())
2144 .arg (version);
2145 }
2146 }
2147
2148 version = mVBox.GetSettingsFileVersion();
2149 if (version != formatVersion)
2150 {
2151 isGlobalConverted = true;
2152 fileList += QString ("<nobr>%1&nbsp;&nbsp;&nbsp;(<i>%2</i>)</nobr><br>")
2153 .arg (mVBox.GetSettingsFilePath())
2154 .arg (version);
2155 }
2156
2157
2158 if (!fileList.isNull())
2159 {
2160 int rc = vboxProblem()
2161 .warnAboutAutoConvertedSettings (formatVersion, fileList);
2162
2163 if (rc == QIMessageBox::No || rc == QIMessageBox::Yes)
2164 {
2165 /* backup (optionally) and save all settings files
2166 * (QIMessageBox::No = Backup, QIMessageBox::Yes = Save) */
2167
2168 foreach (CMachine m, machines)
2169 {
2170 CSession session = openSession (m.GetId());
2171 if (!session.isNull())
2172 {
2173 CMachine sm = session.GetMachine();
2174 if (rc == QIMessageBox::No)
2175 sm.SaveSettingsWithBackup();
2176 else
2177 sm.SaveSettings();
2178
2179 if (!sm.isOk())
2180 vboxProblem().cannotSaveMachineSettings (sm);
2181 session.Close();
2182 }
2183 }
2184
2185 if (isGlobalConverted)
2186 {
2187 if (rc == QIMessageBox::No)
2188 mVBox.SaveSettingsWithBackup();
2189 else
2190 mVBox.SaveSettings();
2191
2192 if (!mVBox.isOk())
2193 vboxProblem().cannotSaveGlobalSettings (mVBox);
2194 }
2195 }
2196 }
2197}
2198
2199/**
2200 * Opens a direct session for a machine with the given ID.
2201 * This method does user-friendly error handling (display error messages, etc.).
2202 * and returns a null CSession object in case of any error.
2203 * If this method succeeds, don't forget to close the returned session when
2204 * it is no more necessary.
2205 *
2206 * @param aId Machine ID.
2207 * @param aExisting @c true to open an existing session with the machine
2208 * which is already running, @c false to open a new direct
2209 * session.
2210 */
2211CSession VBoxGlobal::openSession (const QUuid &aId, bool aExisting /* = false */)
2212{
2213 CSession session;
2214 session.createInstance (CLSID_Session);
2215 if (session.isNull())
2216 {
2217 vboxProblem().cannotOpenSession (session);
2218 return session;
2219 }
2220
2221 if (aExisting)
2222 mVBox.OpenExistingSession (session, aId);
2223 else
2224 {
2225 mVBox.OpenSession (session, aId);
2226 CMachine machine = session.GetMachine ();
2227 machine.SetGuestPropertyValue ("VBox/LanguageID", VBoxGlobal::languageId());
2228 }
2229
2230 if (!mVBox.isOk())
2231 {
2232 CMachine machine = CVirtualBox (mVBox).GetMachine (aId);
2233 vboxProblem().cannotOpenSession (mVBox, machine);
2234 session.detach();
2235 }
2236
2237 return session;
2238}
2239
2240/**
2241 * Starts a machine with the given ID.
2242 */
2243bool VBoxGlobal::startMachine (const QUuid &id)
2244{
2245 AssertReturn (mValid, false);
2246
2247 CSession session = vboxGlobal().openSession (id);
2248 if (session.isNull())
2249 return false;
2250
2251 return consoleWnd().openView (session);
2252}
2253
2254/**
2255 * Appends the disk object and all its children to the media list.
2256 */
2257static
2258void addMediaToList (VBoxMediaList &aList,
2259 const CUnknown &aDisk,
2260 VBoxDefs::DiskType aType)
2261{
2262 VBoxMedia media (aDisk, aType, VBoxMedia::Unknown);
2263 aList += media;
2264 /* append all vdi children */
2265 if (aType == VBoxDefs::HD)
2266 {
2267 CHardDisk hd = aDisk;
2268 CHardDiskEnumerator enumerator = hd.GetChildren().Enumerate();
2269 while (enumerator.HasMore())
2270 {
2271 CHardDisk subHd = enumerator.GetNext();
2272 addMediaToList (aList, CUnknown (subHd), VBoxDefs::HD);
2273 }
2274 }
2275}
2276
2277/**
2278 * Starts a thread that asynchronously enumerates all currently registered
2279 * media, checks for its accessibility and posts VBoxEnumerateMediaEvent
2280 * events to the VBoxGlobal object until all media is enumerated.
2281 *
2282 * If the enumeration is already in progress, no new thread is started.
2283 *
2284 * @sa #currentMediaList()
2285 * @sa #isMediaEnumerationStarted()
2286 */
2287void VBoxGlobal::startEnumeratingMedia()
2288{
2289 Assert (mValid);
2290
2291 /* check if already started but not yet finished */
2292 if (media_enum_thread)
2293 return;
2294
2295 /* ignore the request during application termination */
2296 if (sVBoxGlobalInCleanup)
2297 return;
2298
2299 /* composes a list of all currently known media & their children */
2300 media_list.clear();
2301 {
2302 CHardDiskEnumerator enHD = mVBox.GetHardDisks().Enumerate();
2303 while (enHD.HasMore())
2304 addMediaToList (media_list, CUnknown (enHD.GetNext()), VBoxDefs::HD);
2305
2306 CDVDImageEnumerator enCD = mVBox.GetDVDImages().Enumerate();
2307 while (enCD.HasMore())
2308 addMediaToList (media_list, CUnknown (enCD.GetNext()), VBoxDefs::CD);
2309
2310 CFloppyImageEnumerator enFD = mVBox.GetFloppyImages().Enumerate();
2311 while (enFD.HasMore())
2312 addMediaToList (media_list, CUnknown (enFD.GetNext()), VBoxDefs::FD);
2313 }
2314
2315 /* enumeration thread class */
2316 class Thread : public QThread
2317 {
2318 public:
2319
2320 Thread (const VBoxMediaList &aList) : mList (aList) {}
2321
2322 virtual void run()
2323 {
2324 LogFlow (("MediaEnumThread started.\n"));
2325 COMBase::InitializeCOM();
2326
2327 CVirtualBox mVBox = vboxGlobal().virtualBox();
2328 QObject *target = &vboxGlobal();
2329
2330 /* enumerating list */
2331 int index = 0;
2332 VBoxMediaList::const_iterator it;
2333 for (it = mList.begin();
2334 it != mList.end() && !sVBoxGlobalInCleanup;
2335 ++ it, ++ index)
2336 {
2337 VBoxMedia media = *it;
2338 switch (media.type)
2339 {
2340 case VBoxDefs::HD:
2341 {
2342 CHardDisk hd = media.disk;
2343 media.status =
2344 hd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2345 hd.isOk() ? VBoxMedia::Inaccessible :
2346 VBoxMedia::Error;
2347 /* assign back to store error info if any */
2348 media.disk = hd;
2349 if (media.status == VBoxMedia::Inaccessible)
2350 {
2351 QUuid machineId = hd.GetMachineId();
2352 if (!machineId.isNull())
2353 {
2354 CMachine machine = mVBox.GetMachine (machineId);
2355 if (!machine.isNull() && (machine.GetState() >= KMachineState_Running))
2356 media.status = VBoxMedia::Ok;
2357 }
2358 }
2359 QApplication::postEvent (target,
2360 new VBoxEnumerateMediaEvent (media, index));
2361 break;
2362 }
2363 case VBoxDefs::CD:
2364 {
2365 CDVDImage cd = media.disk;
2366 media.status =
2367 cd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2368 cd.isOk() ? VBoxMedia::Inaccessible :
2369 VBoxMedia::Error;
2370 /* assign back to store error info if any */
2371 media.disk = cd;
2372 QApplication::postEvent (target,
2373 new VBoxEnumerateMediaEvent (media, index));
2374 break;
2375 }
2376 case VBoxDefs::FD:
2377 {
2378 CFloppyImage fd = media.disk;
2379 media.status =
2380 fd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2381 fd.isOk() ? VBoxMedia::Inaccessible :
2382 VBoxMedia::Error;
2383 /* assign back to store error info if any */
2384 media.disk = fd;
2385 QApplication::postEvent (target,
2386 new VBoxEnumerateMediaEvent (media, index));
2387 break;
2388 }
2389 default:
2390 {
2391 AssertMsgFailed (("Invalid aMedia type\n"));
2392 break;
2393 }
2394 }
2395 }
2396
2397 /* post the last message to indicate the end of enumeration */
2398 if (!sVBoxGlobalInCleanup)
2399 QApplication::postEvent (target, new VBoxEnumerateMediaEvent());
2400
2401 COMBase::CleanupCOM();
2402 LogFlow (("MediaEnumThread finished.\n"));
2403 }
2404
2405 private:
2406
2407 const VBoxMediaList &mList;
2408 };
2409
2410 media_enum_thread = new Thread (media_list);
2411 AssertReturnVoid (media_enum_thread);
2412
2413 /* emit mediaEnumStarted() after we set media_enum_thread to != NULL
2414 * to cause isMediaEnumerationStarted() to return TRUE from slots */
2415 emit mediaEnumStarted();
2416
2417 media_enum_thread->start();
2418}
2419
2420/**
2421 * Adds a new media to the current media list.
2422 * @note Currently, this method does nothing but emits the mediaAdded() signal.
2423 * Later, it will be used to synchronize the current media list with
2424 * the actial media list on the server after a single media opetartion
2425 * performed from within one of our UIs.
2426 * @sa #currentMediaList()
2427 */
2428void VBoxGlobal::addMedia (const VBoxMedia &aMedia)
2429{
2430 emit mediaAdded (aMedia);
2431}
2432
2433/**
2434 * Updates the media in the current media list.
2435 * @note Currently, this method does nothing but emits the mediaUpdated() signal.
2436 * Later, it will be used to synchronize the current media list with
2437 * the actial media list on the server after a single media opetartion
2438 * performed from within one of our UIs.
2439 * @sa #currentMediaList()
2440 */
2441void VBoxGlobal::updateMedia (const VBoxMedia &aMedia)
2442{
2443 emit mediaUpdated (aMedia);
2444}
2445
2446/**
2447 * Removes the media from the current media list.
2448 * @note Currently, this method does nothing but emits the mediaRemoved() signal.
2449 * Later, it will be used to synchronize the current media list with
2450 * the actial media list on the server after a single media opetartion
2451 * performed from within one of our UIs.
2452 * @sa #currentMediaList()
2453 */
2454void VBoxGlobal::removeMedia (VBoxDefs::DiskType aType, const QUuid &aId)
2455{
2456 emit mediaRemoved (aType, aId);
2457}
2458
2459/**
2460 * Searches for a VBoxMedia object representing the given COM media object.
2461 *
2462 * @return true if found and false otherwise.
2463 */
2464bool VBoxGlobal::findMedia (const CUnknown &aObj, VBoxMedia &aMedia) const
2465{
2466 for (VBoxMediaList::ConstIterator it = media_list.begin();
2467 it != media_list.end(); ++ it)
2468 {
2469 if ((*it).disk == aObj)
2470 {
2471 aMedia = (*it);
2472 return true;
2473 }
2474 }
2475
2476 return false;
2477}
2478
2479/**
2480 * Native language name of the currently installed translation.
2481 * Returns "English" if no translation is installed
2482 * or if the translation file is invalid.
2483 */
2484QString VBoxGlobal::languageName() const
2485{
2486
2487 return qApp->translate ("@@@", "English",
2488 "Native language name");
2489}
2490
2491/**
2492 * Native language country name of the currently installed translation.
2493 * Returns "--" if no translation is installed or if the translation file is
2494 * invalid, or if the language is independent on the country.
2495 */
2496QString VBoxGlobal::languageCountry() const
2497{
2498 return qApp->translate ("@@@", "--",
2499 "Native language country name "
2500 "(empty if this language is for all countries)");
2501}
2502
2503/**
2504 * Language name of the currently installed translation, in English.
2505 * Returns "English" if no translation is installed
2506 * or if the translation file is invalid.
2507 */
2508QString VBoxGlobal::languageNameEnglish() const
2509{
2510
2511 return qApp->translate ("@@@", "English",
2512 "Language name, in English");
2513}
2514
2515/**
2516 * Language country name of the currently installed translation, in English.
2517 * Returns "--" if no translation is installed or if the translation file is
2518 * invalid, or if the language is independent on the country.
2519 */
2520QString VBoxGlobal::languageCountryEnglish() const
2521{
2522 return qApp->translate ("@@@", "--",
2523 "Language country name, in English "
2524 "(empty if native country name is empty)");
2525}
2526
2527/**
2528 * Comma-separated list of authors of the currently installed translation.
2529 * Returns "Sun Microsystems, Inc." if no translation is installed or if the
2530 * translation file is invalid, or if the translation is supplied by Sun
2531 * Microsystems, inc.
2532 */
2533QString VBoxGlobal::languageTranslators() const
2534{
2535 return qApp->translate ("@@@", "Sun Microsystems, Inc.",
2536 "Comma-separated list of translators");
2537}
2538
2539/**
2540 * Changes the language of all global string constants according to the
2541 * currently installed translations tables.
2542 */
2543void VBoxGlobal::retranslateUi()
2544{
2545 machineStates [KMachineState_PoweredOff] = tr ("Powered Off", "MachineState");
2546 machineStates [KMachineState_Saved] = tr ("Saved", "MachineState");
2547 machineStates [KMachineState_Aborted] = tr ("Aborted", "MachineState");
2548 machineStates [KMachineState_Running] = tr ("Running", "MachineState");
2549 machineStates [KMachineState_Paused] = tr ("Paused", "MachineState");
2550 machineStates [KMachineState_Stuck] = tr ("Stuck", "MachineState");
2551 machineStates [KMachineState_Starting] = tr ("Starting", "MachineState");
2552 machineStates [KMachineState_Stopping] = tr ("Stopping", "MachineState");
2553 machineStates [KMachineState_Saving] = tr ("Saving", "MachineState");
2554 machineStates [KMachineState_Restoring] = tr ("Restoring", "MachineState");
2555 machineStates [KMachineState_Discarding] = tr ("Discarding", "MachineState");
2556
2557 sessionStates [KSessionState_Closed] = tr ("Closed", "SessionState");
2558 sessionStates [KSessionState_Open] = tr ("Open", "SessionState");
2559 sessionStates [KSessionState_Spawning] = tr ("Spawning", "SessionState");
2560 sessionStates [KSessionState_Closing] = tr ("Closing", "SessionState");
2561
2562 deviceTypes [KDeviceType_Null] = tr ("None", "DeviceType");
2563 deviceTypes [KDeviceType_Floppy] = tr ("Floppy", "DeviceType");
2564 deviceTypes [KDeviceType_DVD] = tr ("CD/DVD-ROM", "DeviceType");
2565 deviceTypes [KDeviceType_HardDisk] = tr ("Hard Disk", "DeviceType");
2566 deviceTypes [KDeviceType_Network] = tr ("Network", "DeviceType");
2567 deviceTypes [KDeviceType_USB] = tr ("USB", "DeviceType");
2568 deviceTypes [KDeviceType_SharedFolder] = tr ("Shared Folder", "DeviceType");
2569
2570 storageBuses [KStorageBus_IDE] =
2571 tr ("IDE", "StorageBus");
2572 storageBuses [KStorageBus_SATA] =
2573 tr ("SATA", "StorageBus");
2574
2575 Assert (storageBusChannels.count() == 3);
2576 storageBusChannels [0] =
2577 tr ("Primary", "StorageBusChannel");
2578 storageBusChannels [1] =
2579 tr ("Secondary", "StorageBusChannel");
2580 storageBusChannels [2] =
2581 tr ("Port %1", "StorageBusChannel");
2582
2583 Assert (storageBusDevices.count() == 2);
2584 storageBusDevices [0] = tr ("Master", "StorageBusDevice");
2585 storageBusDevices [1] = tr ("Slave", "StorageBusDevice");
2586
2587 diskTypes [KHardDiskType_Normal] =
2588 tr ("Normal", "DiskType");
2589 diskTypes [KHardDiskType_Immutable] =
2590 tr ("Immutable", "DiskType");
2591 diskTypes [KHardDiskType_Writethrough] =
2592 tr ("Writethrough", "DiskType");
2593
2594 diskStorageTypes [KHardDiskStorageType_VirtualDiskImage] =
2595 tr ("Virtual Disk Image", "DiskStorageType");
2596 diskStorageTypes [KHardDiskStorageType_ISCSIHardDisk] =
2597 tr ("iSCSI", "DiskStorageType");
2598 diskStorageTypes [KHardDiskStorageType_VMDKImage] =
2599 tr ("VMDK Image", "DiskStorageType");
2600 diskStorageTypes [KHardDiskStorageType_CustomHardDisk] =
2601 tr ("Custom Hard Disk", "DiskStorageType");
2602 diskStorageTypes [KHardDiskStorageType_VHDImage] =
2603 tr ("VHD Image", "DiskStorageType");
2604
2605 vrdpAuthTypes [KVRDPAuthType_Null] =
2606 tr ("Null", "VRDPAuthType");
2607 vrdpAuthTypes [KVRDPAuthType_External] =
2608 tr ("External", "VRDPAuthType");
2609 vrdpAuthTypes [KVRDPAuthType_Guest] =
2610 tr ("Guest", "VRDPAuthType");
2611
2612 portModeTypes [KPortMode_Disconnected] =
2613 tr ("Disconnected", "PortMode");
2614 portModeTypes [KPortMode_HostPipe] =
2615 tr ("Host Pipe", "PortMode");
2616 portModeTypes [KPortMode_HostDevice] =
2617 tr ("Host Device", "PortMode");
2618
2619 usbFilterActionTypes [KUSBDeviceFilterAction_Ignore] =
2620 tr ("Ignore", "USBFilterActionType");
2621 usbFilterActionTypes [KUSBDeviceFilterAction_Hold] =
2622 tr ("Hold", "USBFilterActionType");
2623
2624 audioDriverTypes [KAudioDriverType_Null] =
2625 tr ("Null Audio Driver", "AudioDriverType");
2626 audioDriverTypes [KAudioDriverType_WinMM] =
2627 tr ("Windows Multimedia", "AudioDriverType");
2628 audioDriverTypes [KAudioDriverType_SolAudio] =
2629 tr ("Solaris Audio", "AudioDriverType");
2630 audioDriverTypes [KAudioDriverType_OSS] =
2631 tr ("OSS Audio Driver", "AudioDriverType");
2632 audioDriverTypes [KAudioDriverType_ALSA] =
2633 tr ("ALSA Audio Driver", "AudioDriverType");
2634 audioDriverTypes [KAudioDriverType_DirectSound] =
2635 tr ("Windows DirectSound", "AudioDriverType");
2636 audioDriverTypes [KAudioDriverType_CoreAudio] =
2637 tr ("CoreAudio", "AudioDriverType");
2638 audioDriverTypes [KAudioDriverType_Pulse] =
2639 tr ("PulseAudio", "AudioDriverType");
2640
2641 audioControllerTypes [KAudioControllerType_AC97] =
2642 tr ("ICH AC97", "AudioControllerType");
2643 audioControllerTypes [KAudioControllerType_SB16] =
2644 tr ("SoundBlaster 16", "AudioControllerType");
2645
2646 networkAdapterTypes [KNetworkAdapterType_Am79C970A] =
2647 tr ("PCnet-PCI II (Am79C970A)", "NetworkAdapterType");
2648 networkAdapterTypes [KNetworkAdapterType_Am79C973] =
2649 tr ("PCnet-FAST III (Am79C973)", "NetworkAdapterType");
2650 networkAdapterTypes [KNetworkAdapterType_I82540EM] =
2651 tr ("Intel PRO/1000 MT Desktop (82540EM)", "NetworkAdapterType");
2652 networkAdapterTypes [KNetworkAdapterType_I82543GC] =
2653 tr ("Intel PRO/1000 T Server (82543GC)", "NetworkAdapterType");
2654
2655 networkAttachmentTypes [KNetworkAttachmentType_Null] =
2656 tr ("Not attached", "NetworkAttachmentType");
2657 networkAttachmentTypes [KNetworkAttachmentType_NAT] =
2658 tr ("NAT", "NetworkAttachmentType");
2659 networkAttachmentTypes [KNetworkAttachmentType_HostInterface] =
2660 tr ("Host Interface", "NetworkAttachmentType");
2661 networkAttachmentTypes [KNetworkAttachmentType_Internal] =
2662 tr ("Internal Network", "NetworkAttachmentType");
2663
2664 clipboardTypes [KClipboardMode_Disabled] =
2665 tr ("Disabled", "ClipboardType");
2666 clipboardTypes [KClipboardMode_HostToGuest] =
2667 tr ("Host To Guest", "ClipboardType");
2668 clipboardTypes [KClipboardMode_GuestToHost] =
2669 tr ("Guest To Host", "ClipboardType");
2670 clipboardTypes [KClipboardMode_Bidirectional] =
2671 tr ("Bidirectional", "ClipboardType");
2672
2673 ideControllerTypes [KIDEControllerType_PIIX3] =
2674 tr ("PIIX3", "IDEControllerType");
2675 ideControllerTypes [KIDEControllerType_PIIX4] =
2676 tr ("PIIX4", "IDEControllerType");
2677
2678 USBDeviceStates [KUSBDeviceState_NotSupported] =
2679 tr ("Not supported", "USBDeviceState");
2680 USBDeviceStates [KUSBDeviceState_Unavailable] =
2681 tr ("Unavailable", "USBDeviceState");
2682 USBDeviceStates [KUSBDeviceState_Busy] =
2683 tr ("Busy", "USBDeviceState");
2684 USBDeviceStates [KUSBDeviceState_Available] =
2685 tr ("Available", "USBDeviceState");
2686 USBDeviceStates [KUSBDeviceState_Held] =
2687 tr ("Held", "USBDeviceState");
2688 USBDeviceStates [KUSBDeviceState_Captured] =
2689 tr ("Captured", "USBDeviceState");
2690
2691 mUserDefinedPortName = tr ("User-defined", "serial port");
2692
2693 detailReportTemplatesReady = false;
2694
2695#if defined (Q_WS_PM) || defined (Q_WS_X11)
2696 /* As PM and X11 do not (to my knowledge) have functionality for providing
2697 * human readable key names, we keep a table of them, which must be
2698 * updated when the language is changed. */
2699 QIHotKeyEdit::retranslateUi();
2700#endif
2701}
2702
2703// public static stuff
2704////////////////////////////////////////////////////////////////////////////////
2705
2706/* static */
2707bool VBoxGlobal::isDOSType (const QString &aOSTypeId)
2708{
2709 if (aOSTypeId.left (3) == "dos" ||
2710 aOSTypeId.left (3) == "win" ||
2711 aOSTypeId.left (3) == "os2")
2712 return true;
2713
2714 return false;
2715}
2716
2717/**
2718 * Sets the QLabel background and frame colors according tho the pixmap
2719 * contents. The bottom right pixel of the label pixmap defines the
2720 * background color of the label, the top right pixel defines the color of
2721 * the one-pixel frame around it. This function also sets the alignment of
2722 * the pixmap to AlignVTop (to correspond to the color choosing logic).
2723 *
2724 * This method is useful to provide nice scaling of pixmal labels without
2725 * scaling pixmaps themselves. To see th eeffect, the size policy of the
2726 * label in the corresponding direction (vertical, for now) should be set to
2727 * something like MinimumExpanding.
2728 *
2729 * @todo Parametrize corners to select pixels from and set the alignment
2730 * accordingly.
2731 */
2732/* static */
2733void VBoxGlobal::adoptLabelPixmap (QLabel *aLabel)
2734{
2735 AssertReturnVoid (aLabel);
2736
2737 aLabel->setAlignment (Qt::AlignTop);
2738 aLabel->setFrameShape (QFrame::Box);
2739 aLabel->setFrameShadow (QFrame::Plain);
2740
2741 const QPixmap *pix = aLabel->pixmap();
2742 QImage img = pix->toImage();
2743 QRgb rgbBack = img.pixel (img.width() - 1, img.height() - 1);
2744 QRgb rgbFrame = img.pixel (img.width() - 1, 0);
2745
2746 QPalette pal = aLabel->palette();
2747 pal.setColor (QPalette::Window, rgbBack);
2748 pal.setColor (QPalette::WindowText, rgbFrame);
2749 aLabel->setPalette (pal);
2750}
2751
2752extern const char *gVBoxLangSubDir = "/nls";
2753extern const char *gVBoxLangFileBase = "VirtualBox_";
2754extern const char *gVBoxLangFileExt = ".qm";
2755extern const char *gVBoxLangIDRegExp = "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
2756extern const char *gVBoxBuiltInLangName = "C";
2757
2758class VBoxTranslator : public QTranslator
2759{
2760public:
2761
2762 VBoxTranslator (QObject *aParent = 0)
2763 : QTranslator (aParent) {}
2764
2765 bool loadFile (const QString &aFileName)
2766 {
2767 QFile file (aFileName);
2768 if (!file.open (QIODevice::ReadOnly))
2769 return false;
2770 mData = file.readAll();
2771 return load ((uchar*) mData.data(), mData.size());
2772 }
2773
2774private:
2775
2776 QByteArray mData;
2777};
2778
2779static VBoxTranslator *sTranslator = 0;
2780static QString sLoadedLangId = gVBoxBuiltInLangName;
2781
2782/**
2783 * Returns the loaded (active) language ID.
2784 * Note that it may not match with VBoxGlobalSettings::languageId() if the
2785 * specified language cannot be loaded.
2786 * If the built-in language is active, this method returns "C".
2787 *
2788 * @note "C" is treated as the built-in language for simplicity -- the C
2789 * locale is used in unix environments as a fallback when the requested
2790 * locale is invalid. This way we don't need to process both the "built_in"
2791 * language and the "C" language (which is a valid environment setting)
2792 * separately.
2793 */
2794/* static */
2795QString VBoxGlobal::languageId()
2796{
2797 return sLoadedLangId;
2798}
2799
2800/**
2801 * Loads the language by language ID.
2802 *
2803 * @param aLangId Language ID in in form of xx_YY. QString::null means the
2804 * system default language.
2805 */
2806/* static */
2807void VBoxGlobal::loadLanguage (const QString &aLangId)
2808{
2809 QString langId = aLangId.isNull() ?
2810 VBoxGlobal::systemLanguageId() : aLangId;
2811 QString languageFileName;
2812 QString selectedLangId = gVBoxBuiltInLangName;
2813
2814 char szNlsPath[RTPATH_MAX];
2815 int rc;
2816
2817 rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
2818 AssertRC (rc);
2819
2820 QString nlsPath = QString(szNlsPath) + gVBoxLangSubDir;
2821 QDir nlsDir (nlsPath);
2822
2823 Assert (!langId.isEmpty());
2824 if (!langId.isEmpty() && langId != gVBoxBuiltInLangName)
2825 {
2826 QRegExp regExp (gVBoxLangIDRegExp);
2827 int pos = regExp.indexIn (langId);
2828 /* the language ID should match the regexp completely */
2829 AssertReturnVoid (pos == 0);
2830
2831 QString lang = regExp.cap (2);
2832
2833 if (nlsDir.exists (gVBoxLangFileBase + langId + gVBoxLangFileExt))
2834 {
2835 languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + langId +
2836 gVBoxLangFileExt);
2837 selectedLangId = langId;
2838 }
2839 else if (nlsDir.exists (gVBoxLangFileBase + lang + gVBoxLangFileExt))
2840 {
2841 languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + lang +
2842 gVBoxLangFileExt);
2843 selectedLangId = lang;
2844 }
2845 else
2846 {
2847 /* Never complain when the default language is requested. In any
2848 * case, if no explicit language file exists, we will simply
2849 * fall-back to English (built-in). */
2850 if (!aLangId.isNull())
2851 vboxProblem().cannotFindLanguage (langId, nlsPath);
2852 /* selectedLangId remains built-in here */
2853 AssertReturnVoid (selectedLangId == gVBoxBuiltInLangName);
2854 }
2855 }
2856
2857 /* delete the old translator if there is one */
2858 if (sTranslator)
2859 {
2860 /* QTranslator destructor will call qApp->removeTranslator() for
2861 * us. It will also delete all its child translations we attach to it
2862 * below, so we don't have to care about them specially. */
2863 delete sTranslator;
2864 }
2865
2866 /* load new language files */
2867 sTranslator = new VBoxTranslator (qApp);
2868 Assert (sTranslator);
2869 bool loadOk = true;
2870 if (sTranslator)
2871 {
2872 if (selectedLangId != gVBoxBuiltInLangName)
2873 {
2874 Assert (!languageFileName.isNull());
2875 loadOk = sTranslator->loadFile (languageFileName);
2876 }
2877 /* we install the translator in any case: on failure, this will
2878 * activate an empty translator that will give us English
2879 * (built-in) */
2880 qApp->installTranslator (sTranslator);
2881 }
2882 else
2883 loadOk = false;
2884
2885 if (loadOk)
2886 sLoadedLangId = selectedLangId;
2887 else
2888 {
2889 vboxProblem().cannotLoadLanguage (languageFileName);
2890 sLoadedLangId = gVBoxBuiltInLangName;
2891 }
2892
2893 /* Try to load the corresponding Qt translation */
2894 if (sLoadedLangId != gVBoxBuiltInLangName)
2895 {
2896#ifdef Q_OS_UNIX
2897 /* We use system installations of Qt on Linux systems, so first, try
2898 * to load the Qt translation from the system location. */
2899 languageFileName = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" +
2900 sLoadedLangId + gVBoxLangFileExt;
2901 QTranslator *qtSysTr = new QTranslator (sTranslator);
2902 Assert (qtSysTr);
2903 if (qtSysTr && qtSysTr->load (languageFileName))
2904 qApp->installTranslator (qtSysTr);
2905 /* Note that the Qt translation supplied by Sun is always loaded
2906 * afterwards to make sure it will take precedence over the system
2907 * translation (it may contain more decent variants of translation
2908 * that better correspond to VirtualBox UI). We need to load both
2909 * because a newer version of Qt may be installed on the user computer
2910 * and the Sun version may not fully support it. We don't do it on
2911 * Win32 because we supply a Qt library there and therefore the
2912 * Sun translation is always the best one. */
2913#endif
2914 languageFileName = nlsDir.absoluteFilePath (QString ("qt_") +
2915 sLoadedLangId +
2916 gVBoxLangFileExt);
2917 QTranslator *qtTr = new QTranslator (sTranslator);
2918 Assert (qtTr);
2919 if (qtTr && (loadOk = qtTr->load (languageFileName)))
2920 qApp->installTranslator (qtTr);
2921 /* The below message doesn't fit 100% (because it's an additonal
2922 * language and the main one won't be reset to built-in on failure)
2923 * but the load failure is so rare here that it's not worth a separate
2924 * message (but still, having something is better than having none) */
2925 if (!loadOk && !aLangId.isNull())
2926 vboxProblem().cannotLoadLanguage (languageFileName);
2927 }
2928}
2929
2930QString VBoxGlobal::helpFile() const
2931{
2932#if defined (Q_WS_WIN32) || defined (Q_WS_X11)
2933 const QString name = "VirtualBox";
2934 const QString suffix = "chm";
2935#elif defined (Q_WS_MAC)
2936 const QString name = "UserManual";
2937 const QString suffix = "pdf";
2938#endif
2939 /* Where are the docs located? */
2940 char szDocsPath[RTPATH_MAX];
2941 int rc = RTPathAppDocs (szDocsPath, sizeof (szDocsPath));
2942 AssertRC (rc);
2943
2944 /* Construct the path and the filename */
2945 QString manual = QString ("%1/%2_%3.%4").arg (szDocsPath)
2946 .arg (name)
2947 .arg (vboxGlobal().languageId())
2948 .arg (suffix);
2949 /* Check if a help file with that name exists */
2950 QFileInfo fi (manual);
2951 if (fi.exists())
2952 return manual;
2953
2954 /* Fall back to the standard */
2955 manual = QString ("%1/%2.%4").arg (szDocsPath)
2956 .arg (name)
2957 .arg (suffix);
2958 return manual;
2959}
2960
2961/* static */
2962QIcon VBoxGlobal::iconSet (const char *aNormal,
2963 const char *aDisabled /* = NULL */,
2964 const char *aActive /* = NULL */)
2965{
2966 QIcon iconSet;
2967
2968 iconSet.addFile (aNormal, QSize(),
2969 QIcon::Normal);
2970 if (aDisabled != NULL)
2971 iconSet.addFile (aDisabled, QSize(),
2972 QIcon::Disabled);
2973 if (aActive != NULL)
2974 iconSet.addFile (aActive, QSize(),
2975 QIcon::Active);
2976 return iconSet;
2977}
2978
2979/* static */
2980QIcon VBoxGlobal::iconSetFull (const QSize &aNormalSize, const QSize &aSmallSize,
2981 const char *aNormal, const char *aSmallNormal,
2982 const char *aDisabled /* = NULL */,
2983 const char *aSmallDisabled /* = NULL */,
2984 const char *aActive /* = NULL */,
2985 const char *aSmallActive /* = NULL */)
2986{
2987 QIcon iconSet;
2988
2989 iconSet.addFile (aNormal, aNormalSize, QIcon::Normal);
2990 iconSet.addFile (aSmallNormal, aSmallSize, QIcon::Normal);
2991 if (aSmallDisabled != NULL)
2992 {
2993 iconSet.addFile (aDisabled, aNormalSize, QIcon::Disabled);
2994 iconSet.addFile (aSmallDisabled, aSmallSize, QIcon::Disabled);
2995 }
2996 if (aSmallActive != NULL)
2997 {
2998 iconSet.addFile (aActive, aNormalSize, QIcon::Active);
2999 iconSet.addFile (aSmallActive, aSmallSize, QIcon::Active);
3000 }
3001
3002 return iconSet;
3003}
3004
3005QIcon VBoxGlobal::standardIcon (QStyle::StandardPixmap aStandard, QWidget *aWidget /* = NULL */)
3006{
3007 QStyle *style = aWidget ? aWidget->style(): QApplication::style();
3008#ifdef Q_WS_MAC
3009 /* At least in Qt 4.3.4/4.4 RC1 SP_MessageBoxWarning is the application
3010 * icon. So change this to the critical icon. (Maybe this would be
3011 * fixed in a later Qt version) */
3012 if (aStandard == QStyle::SP_MessageBoxWarning)
3013 return style->standardIcon (QStyle::SP_MessageBoxCritical, 0, aWidget);
3014#endif /* Q_WS_MAC */
3015 return style->standardIcon (aStandard, 0, aWidget);
3016}
3017
3018/**
3019 * Replacement for QToolButton::setTextLabel() that handles the shortcut
3020 * letter (if it is present in the argument string) as if it were a setText()
3021 * call: the shortcut letter is used to automatically assign an "Alt+<letter>"
3022 * accelerator key sequence to the given tool button.
3023 *
3024 * @note This method preserves the icon set if it was assigned before. Only
3025 * the text label and the accelerator are changed.
3026 *
3027 * @param aToolButton Tool button to set the text label on.
3028 * @param aTextLabel Text label to set.
3029 */
3030/* static */
3031void VBoxGlobal::setTextLabel (QToolButton *aToolButton,
3032 const QString &aTextLabel)
3033{
3034 AssertReturnVoid (aToolButton != NULL);
3035
3036 /* remember the icon set as setText() will kill it */
3037 QIcon iset = aToolButton->icon();
3038 /* re-use the setText() method to detect and set the accelerator */
3039 aToolButton->setText (aTextLabel);
3040 QKeySequence accel = aToolButton->shortcut();
3041 aToolButton->setText (aTextLabel);
3042 aToolButton->setIcon (iset);
3043 /* set the accel last as setIconSet() would kill it */
3044 aToolButton->setShortcut (accel);
3045}
3046
3047/**
3048 * Ensures that the given rectangle \a aRect is fully contained within the
3049 * rectangle \a aBoundRect by moving \a aRect if necessary. If \a aRect is
3050 * larger than \a aBoundRect, its top left corner is simply aligned with the
3051 * top left corner of \a aRect and, if \a aCanResize is true, \a aRect is
3052 * shrinked to become fully visible.
3053 */
3054/* static */
3055QRect VBoxGlobal::normalizeGeometry (const QRect &aRect, const QRect &aBoundRect,
3056 bool aCanResize /* = true */)
3057{
3058 QRect fr = aRect;
3059
3060 /* make the bottom right corner visible */
3061 int rd = aBoundRect.right() - fr.right();
3062 int bd = aBoundRect.bottom() - fr.bottom();
3063 fr.translate (rd < 0 ? rd : 0, bd < 0 ? bd : 0);
3064
3065 /* ensure the top left corner is visible */
3066 int ld = fr.left() - aBoundRect.left();
3067 int td = fr.top() - aBoundRect.top();
3068 fr.translate (ld < 0 ? -ld : 0, td < 0 ? -td : 0);
3069
3070 if (aCanResize)
3071 {
3072 /* adjust the size to make the rectangle fully contained */
3073 rd = aBoundRect.right() - fr.right();
3074 bd = aBoundRect.bottom() - fr.bottom();
3075 if (rd < 0)
3076 fr.setRight (fr.right() + rd);
3077 if (bd < 0)
3078 fr.setBottom (fr.bottom() + bd);
3079 }
3080
3081 return fr;
3082}
3083
3084/**
3085 * Aligns the center of \a aWidget with the center of \a aRelative.
3086 *
3087 * If necessary, \a aWidget's position is adjusted to make it fully visible
3088 * within the available desktop area. If \a aWidget is bigger then this area,
3089 * it will also be resized unless \a aCanResize is false or there is an
3090 * inappropriate minimum size limit (in which case the top left corner will be
3091 * simply aligned with the top left corner of the available desktop area).
3092 *
3093 * \a aWidget must be a top-level widget. \a aRelative may be any widget, but
3094 * if it's not top-level itself, its top-level widget will be used for
3095 * calculations. \a aRelative can also be NULL, in which case \a aWidget will
3096 * be centered relative to the available desktop area.
3097 */
3098/* static */
3099void VBoxGlobal::centerWidget (QWidget *aWidget, QWidget *aRelative,
3100 bool aCanResize /* = true */)
3101{
3102 AssertReturnVoid (aWidget);
3103 AssertReturnVoid (aWidget->isTopLevel());
3104
3105 QRect deskGeo, parentGeo;
3106 QWidget *w = aRelative;
3107 if (w)
3108 {
3109 w = w->window();
3110 deskGeo = QApplication::desktop()->availableGeometry (w);
3111 parentGeo = w->frameGeometry();
3112 /* On X11/Gnome, geo/frameGeo.x() and y() are always 0 for top level
3113 * widgets with parents, what a shame. Use mapToGlobal() to workaround. */
3114 QPoint d = w->mapToGlobal (QPoint (0, 0));
3115 d.rx() -= w->geometry().x() - w->x();
3116 d.ry() -= w->geometry().y() - w->y();
3117 parentGeo.moveTopLeft (d);
3118 }
3119 else
3120 {
3121 deskGeo = QApplication::desktop()->availableGeometry();
3122 parentGeo = deskGeo;
3123 }
3124
3125 /* On X11, there is no way to determine frame geometry (including WM
3126 * decorations) before the widget is shown for the first time. Stupidly
3127 * enumerate other top level widgets to find the thickest frame. The code
3128 * is based on the idea taken from QDialog::adjustPositionInternal(). */
3129
3130 int extraw = 0, extrah = 0;
3131
3132 QWidgetList list = QApplication::topLevelWidgets();
3133 QListIterator<QWidget*> it (list);
3134 while ((extraw == 0 || extrah == 0) && it.hasNext())
3135 {
3136 int framew, frameh;
3137 QWidget *current = it.next();
3138 if (!current->isVisible())
3139 continue;
3140
3141 framew = current->frameGeometry().width() - current->width();
3142 frameh = current->frameGeometry().height() - current->height();
3143
3144 extraw = qMax (extraw, framew);
3145 extrah = qMax (extrah, frameh);
3146 }
3147
3148 /// @todo (r=dmik) not sure if we really need this
3149#if 0
3150 /* sanity check for decoration frames. With embedding, we
3151 * might get extraordinary values */
3152 if (extraw == 0 || extrah == 0 || extraw > 20 || extrah > 50)
3153 {
3154 extrah = 50;
3155 extraw = 20;
3156 }
3157#endif
3158
3159 /* On non-X11 platforms, the following would be enough instead of the
3160 * above workaround: */
3161 // QRect geo = frameGeometry();
3162 QRect geo = QRect (0, 0, aWidget->width() + extraw,
3163 aWidget->height() + extrah);
3164
3165 geo.moveCenter (QPoint (parentGeo.x() + (parentGeo.width() - 1) / 2,
3166 parentGeo.y() + (parentGeo.height() - 1) / 2));
3167
3168 /* ensure the widget is within the available desktop area */
3169 QRect newGeo = normalizeGeometry (geo, deskGeo, aCanResize);
3170
3171 aWidget->move (newGeo.topLeft());
3172
3173 if (aCanResize &&
3174 (geo.width() != newGeo.width() || geo.height() != newGeo.height()))
3175 aWidget->resize (newGeo.width() - extraw, newGeo.height() - extrah);
3176}
3177
3178/**
3179 * Returns the decimal separator for the current locale.
3180 */
3181/* static */
3182QChar VBoxGlobal::decimalSep()
3183{
3184 return QLocale::system().decimalPoint();
3185}
3186
3187/**
3188 * Returns the regexp string that defines the format of the human-readable
3189 * size representation, <tt>####[.##] B|KB|MB|GB|TB|PB</tt>.
3190 *
3191 * This regexp will capture 5 groups of text:
3192 * - cap(1): integer number in case when no decimal point is present
3193 * (if empty, it means that decimal point is present)
3194 * - cap(2): size suffix in case when no decimal point is present (may be empty)
3195 * - cap(3): integer number in case when decimal point is present (may be empty)
3196 * - cap(4): fraction number (hundredth) in case when decimal point is present
3197 * - cap(5): size suffix in case when decimal point is present (note that
3198 * B cannot appear there)
3199 */
3200/* static */
3201QString VBoxGlobal::sizeRegexp()
3202{
3203 QString regexp =
3204 QString ("^(?:(?:(\\d+)(?:\\s?([KMGTP]?B))?)|(?:(\\d*)%1(\\d{1,2})(?:\\s?([KMGTP]B))))$")
3205 .arg (decimalSep());
3206 return regexp;
3207}
3208
3209/**
3210 * Parses the given size string that should be in form of
3211 * <tt>####[.##] B|KB|MB|GB|TB|PB</tt> and returns the size value
3212 * in bytes. Zero is returned on error.
3213 */
3214/* static */
3215quint64 VBoxGlobal::parseSize (const QString &aText)
3216{
3217 QRegExp regexp (sizeRegexp());
3218 int pos = regexp.indexIn (aText);
3219 if (pos != -1)
3220 {
3221 QString intgS = regexp.cap (1);
3222 QString hundS;
3223 QString suff = regexp.cap (2);
3224 if (intgS.isEmpty())
3225 {
3226 intgS = regexp.cap (3);
3227 hundS = regexp.cap (4);
3228 suff = regexp.cap (5);
3229 }
3230
3231 quint64 denom = 0;
3232 if (suff.isEmpty() || suff == "B")
3233 denom = 1;
3234 else if (suff == "KB")
3235 denom = _1K;
3236 else if (suff == "MB")
3237 denom = _1M;
3238 else if (suff == "GB")
3239 denom = _1G;
3240 else if (suff == "TB")
3241 denom = _1T;
3242 else if (suff == "PB")
3243 denom = _1P;
3244
3245 quint64 intg = intgS.toULongLong();
3246 if (denom == 1)
3247 return intg;
3248
3249 quint64 hund = hundS.leftJustified (2, '0').toULongLong();
3250 hund = hund * denom / 100;
3251 intg = intg * denom + hund;
3252 return intg;
3253 }
3254 else
3255 return 0;
3256}
3257
3258/**
3259 * Formats the given \a size value in bytes to a human readable string
3260 * in form of <tt>####[.##] B|KB|MB|GB|TB|PB</tt>.
3261 *
3262 * The \a mode parameter is used for resulting numbers that get a fractional
3263 * part after converting the \a size to KB, MB etc:
3264 * <ul>
3265 * <li>When \a mode is 0, the result is rounded to the closest number
3266 * containing two decimal digits.
3267 * </li>
3268 * <li>When \a mode is -1, the result is rounded to the largest two decimal
3269 * digit number that is not greater than the result. This guarantees that
3270 * converting the resulting string back to the integer value in bytes
3271 * will not produce a value greater that the initial \a size parameter.
3272 * </li>
3273 * <li>When \a mode is 1, the result is rounded to the smallest two decimal
3274 * digit number that is not less than the result. This guarantees that
3275 * converting the resulting string back to the integer value in bytes
3276 * will not produce a value less that the initial \a size parameter.
3277 * </li>
3278 * </ul>
3279 *
3280 * @param aSize size value in bytes
3281 * @param aMode convertion mode (-1, 0 or 1)
3282 * @return human-readable size string
3283 */
3284/* static */
3285QString VBoxGlobal::formatSize (quint64 aSize, int aMode /* = 0 */)
3286{
3287 static const char *Suffixes [] = { "B", "KB", "MB", "GB", "TB", "PB", NULL };
3288
3289 quint64 denom = 0;
3290 int suffix = 0;
3291
3292 if (aSize < _1K)
3293 {
3294 denom = 1;
3295 suffix = 0;
3296 }
3297 else if (aSize < _1M)
3298 {
3299 denom = _1K;
3300 suffix = 1;
3301 }
3302 else if (aSize < _1G)
3303 {
3304 denom = _1M;
3305 suffix = 2;
3306 }
3307 else if (aSize < _1T)
3308 {
3309 denom = _1G;
3310 suffix = 3;
3311 }
3312 else if (aSize < _1P)
3313 {
3314 denom = _1T;
3315 suffix = 4;
3316 }
3317 else
3318 {
3319 denom = _1P;
3320 suffix = 5;
3321 }
3322
3323 quint64 intg = aSize / denom;
3324 quint64 hund = aSize % denom;
3325
3326 QString number;
3327 if (denom > 1)
3328 {
3329 if (hund)
3330 {
3331 hund *= 100;
3332 /* not greater */
3333 if (aMode < 0) hund = hund / denom;
3334 /* not less */
3335 else if (aMode > 0) hund = (hund + denom - 1) / denom;
3336 /* nearest */
3337 else hund = (hund + denom / 2) / denom;
3338 }
3339 /* check for the fractional part overflow due to rounding */
3340 if (hund == 100)
3341 {
3342 hund = 0;
3343 ++ intg;
3344 /* check if we've got 1024 XB after rounding and scale down if so */
3345 if (intg == 1024 && Suffixes [suffix + 1] != NULL)
3346 {
3347 intg /= 1024;
3348 ++ suffix;
3349 }
3350 }
3351 number = QString ("%1%2%3").arg (intg).arg (decimalSep())
3352 .arg (QString::number (hund).rightJustified (2, '0'));
3353 }
3354 else
3355 {
3356 number = QString::number (intg);
3357 }
3358
3359 return QString ("%1 %2").arg (number).arg (Suffixes [suffix]);
3360}
3361
3362/**
3363 * Reformats the input string @a aStr so that:
3364 * - strings in single quotes will be put inside <nobr> and marked
3365 * with blue color;
3366 * - UUIDs be put inside <nobr> and marked
3367 * with green color;
3368 * - replaces new line chars with </p><p> constructs to form paragraphs
3369 * (note that <p> and </p> are not appended to the beginnign and to the
3370 * end of the string respectively, to allow the result be appended
3371 * or prepended to the existing paragraph).
3372 *
3373 * If @a aToolTip is true, colouring is not applied, only the <nobr> tag
3374 * is added. Also, new line chars are replaced with <br> instead of <p>.
3375 */
3376/* static */
3377QString VBoxGlobal::highlight (const QString &aStr, bool aToolTip /* = false */)
3378{
3379 QString strFont;
3380 QString uuidFont;
3381 QString endFont;
3382 if (!aToolTip)
3383 {
3384 strFont = "<font color=#0000CC>";
3385 uuidFont = "<font color=#008000>";
3386 endFont = "</font>";
3387 }
3388
3389 QString text = aStr;
3390
3391 /* replace special entities, '&' -- first! */
3392 text.replace ('&', "&amp;");
3393 text.replace ('<', "&lt;");
3394 text.replace ('>', "&gt;");
3395 text.replace ('\"', "&quot;");
3396
3397 /* mark strings in single quotes with color */
3398 QRegExp rx = QRegExp ("((?:^|\\s)[(]?)'([^']*)'(?=[:.-!);]?(?:\\s|$))");
3399 rx.setMinimal (true);
3400 text.replace (rx,
3401 QString ("\\1%1<nobr>'\\2'</nobr>%2")
3402 .arg (strFont). arg (endFont));
3403
3404 /* mark UUIDs with color */
3405 text.replace (QRegExp (
3406 "((?:^|\\s)[(]?)"
3407 "(\\{[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}\\})"
3408 "(?=[:.-!);]?(?:\\s|$))"),
3409 QString ("\\1%1<nobr>\\2</nobr>%2")
3410 .arg (uuidFont). arg (endFont));
3411
3412 /* split to paragraphs at \n chars */
3413 if (!aToolTip)
3414 text.replace ('\n', "</p><p>");
3415 else
3416 text.replace ('\n', "<br>");
3417
3418 return text;
3419}
3420
3421/**
3422 * This does exactly the same as QLocale::system().name() but corrects its
3423 * wrong behavior on Linux systems (LC_NUMERIC for some strange reason takes
3424 * precedence over any other locale setting in the QLocale::system()
3425 * implementation). This implementation first looks at LC_ALL (as defined by
3426 * SUS), then looks at LC_MESSAGES which is designed to define a language for
3427 * program messages in case if it differs from the language for other locale
3428 * categories. Then it looks for LANG and finally falls back to
3429 * QLocale::system().name().
3430 *
3431 * The order of precedence is well defined here:
3432 * http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
3433 *
3434 * @note This method will return "C" when the requested locale is invalid or
3435 * when the "C" locale is set explicitly.
3436 */
3437/* static */
3438QString VBoxGlobal::systemLanguageId()
3439{
3440#if defined (Q_WS_MAC)
3441 /* QLocale return the right id only if the user select the format of the
3442 * language also. So we use our own implementation */
3443 return ::darwinSystemLanguage();
3444#elif defined (Q_OS_UNIX)
3445 const char *s = RTEnvGet ("LC_ALL");
3446 if (s == 0)
3447 s = RTEnvGet ("LC_MESSAGES");
3448 if (s == 0)
3449 s = RTEnvGet ("LANG");
3450 if (s != 0)
3451 return QLocale (s).name();
3452#endif
3453 return QLocale::system().name();
3454}
3455
3456/**
3457 * Reimplementation of QFileDialog::getExistingDirectory() that removes some
3458 * oddities and limitations.
3459 *
3460 * On Win32, this function makes sure a native dialog is launched in
3461 * another thread to avoid dialog visualization errors occuring due to
3462 * multi-threaded COM apartment initialization on the main UI thread while
3463 * the appropriate native dialog function expects a single-threaded one.
3464 *
3465 * On all other platforms, this function is equivalent to
3466 * QFileDialog::getExistingDirectory().
3467 */
3468QString VBoxGlobal::getExistingDirectory (const QString &aDir,
3469 QWidget *aParent,
3470 const QString &aCaption,
3471 bool aDirOnly,
3472 bool aResolveSymlinks)
3473{
3474#if defined Q_WS_WIN
3475
3476 /**
3477 * QEvent class reimplementation to carry Win32 API native dialog's
3478 * result folder information
3479 */
3480 class GetExistDirectoryEvent : public OpenNativeDialogEvent
3481 {
3482 public:
3483
3484 enum { TypeId = QEvent::User + 300 };
3485
3486 GetExistDirectoryEvent (const QString &aResult)
3487 : OpenNativeDialogEvent (aResult, (QEvent::Type) TypeId) {}
3488 };
3489
3490 /**
3491 * QThread class reimplementation to open Win32 API native folder's dialog
3492 */
3493 class Thread : public QThread
3494 {
3495 public:
3496
3497 Thread (QWidget *aParent, QObject *aTarget,
3498 const QString &aDir, const QString &aCaption)
3499 : mParent (aParent), mTarget (aTarget), mDir (aDir), mCaption (aCaption) {}
3500
3501 virtual void run()
3502 {
3503 QString result;
3504
3505 QWidget *topParent = mParent ? mParent->window() : vboxGlobal().mainWindow();
3506 QString title = mCaption.isNull() ? tr ("Select a directory") : mCaption;
3507
3508 TCHAR path[MAX_PATH];
3509 path [0] = 0;
3510 TCHAR initPath [MAX_PATH];
3511 initPath [0] = 0;
3512
3513 BROWSEINFO bi;
3514 bi.hwndOwner = topParent ? topParent->winId() : 0;
3515 bi.pidlRoot = NULL;
3516 bi.lpszTitle = (TCHAR*)(title.isNull() ? 0 : title.utf16());
3517 bi.pszDisplayName = initPath;
3518 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
3519 bi.lpfn = winGetExistDirCallbackProc;
3520 bi.lParam = Q_ULONG (&mDir);
3521
3522 /* Qt is uncapable to properly handle modal state if the modal
3523 * window is not a QWidget. For example, if we have the W1->W2->N
3524 * ownership where Wx are QWidgets (W2 is modal), and N is a
3525 * native modal window, cliking on the title bar of W1 will still
3526 * activate W2 and redirect keyboard/mouse to it. The dirty hack
3527 * to prevent it is to disable the entire widget... */
3528 if (mParent)
3529 mParent->setEnabled (false);
3530
3531 LPITEMIDLIST itemIdList = SHBrowseForFolder (&bi);
3532 if (itemIdList)
3533 {
3534 SHGetPathFromIDList (itemIdList, path);
3535 IMalloc *pMalloc;
3536 if (SHGetMalloc (&pMalloc) != NOERROR)
3537 result = QString::null;
3538 else
3539 {
3540 pMalloc->Free (itemIdList);
3541 pMalloc->Release();
3542 result = QString::fromUtf16 ((ushort*)path);
3543 }
3544 }
3545 else
3546 result = QString::null;
3547 QApplication::postEvent (mTarget, new GetExistDirectoryEvent (result));
3548
3549 /* Enable the parent widget again. */
3550 if (mParent)
3551 mParent->setEnabled (true);
3552 }
3553
3554 private:
3555
3556 QWidget *mParent;
3557 QObject *mTarget;
3558 QString mDir;
3559 QString mCaption;
3560 };
3561
3562 /* Local event loop to run while waiting for the result from another
3563 * thread */
3564 QEventLoop loop;
3565
3566 QString dir = QDir::convertSeparators (aDir);
3567 LoopObject loopObject ((QEvent::Type) GetExistDirectoryEvent::TypeId, loop);
3568
3569 Thread openDirThread (aParent, &loopObject, dir, aCaption);
3570 openDirThread.start();
3571 loop.exec();
3572 openDirThread.wait();
3573
3574 return loopObject.result();
3575
3576#elif defined (Q_WS_X11) && (QT_VERSION < 0x040400)
3577
3578 /* Here is workaround for Qt4.3 bug with QFileDialog which crushes when
3579 * gets initial path as hidden directory if no hidden files are shown.
3580 * See http://trolltech.com/developer/task-tracker/index_html?method=entry&id=193483
3581 * for details */
3582 QFileDialog dlg (aParent);
3583 dlg.setWindowTitle (aCaption);
3584 dlg.setDirectory (aDir);
3585 dlg.setResolveSymlinks (aResolveSymlinks);
3586 dlg.setFileMode (aDirOnly ? QFileDialog::DirectoryOnly : QFileDialog::Directory);
3587 QAction *hidden = dlg.findChild <QAction*> ("qt_show_hidden_action");
3588 if (hidden)
3589 {
3590 hidden->trigger();
3591 hidden->setVisible (false);
3592 }
3593 return dlg.exec() ? dlg.selectedFiles() [0] : QString::null;
3594
3595#else
3596
3597 QFileDialog::Options o;
3598 if (aDirOnly)
3599 o = QFileDialog::ShowDirsOnly;
3600 if (!aResolveSymlinks)
3601 o |= QFileDialog::DontResolveSymlinks;
3602 return QFileDialog::getExistingDirectory (aParent, aCaption, aDir, o);
3603
3604#endif
3605}
3606
3607/**
3608 * Reimplementation of QFileDialog::getOpenFileName() that removes some
3609 * oddities and limitations.
3610 *
3611 * On Win32, this function makes sure a file filter is applied automatically
3612 * right after it is selected from the drop-down list, to conform to common
3613 * experience in other applications. Note that currently, @a selectedFilter
3614 * is always set to null on return.
3615 *
3616 * On all other platforms, this function is equivalent to
3617 * QFileDialog::getOpenFileName().
3618 */
3619/* static */
3620QString VBoxGlobal::getOpenFileName (const QString &aStartWith,
3621 const QString &aFilters,
3622 QWidget *aParent,
3623 const QString &aCaption,
3624 QString *aSelectedFilter,
3625 bool aResolveSymlinks)
3626{
3627#if defined Q_WS_WIN
3628
3629 /**
3630 * QEvent class reimplementation to carry Win32 API native dialog's
3631 * result folder information
3632 */
3633 class GetOpenFileNameEvent : public OpenNativeDialogEvent
3634 {
3635 public:
3636
3637 enum { TypeId = QEvent::User + 301 };
3638
3639 GetOpenFileNameEvent (const QString &aResult)
3640 : OpenNativeDialogEvent (aResult, (QEvent::Type) TypeId) {}
3641 };
3642
3643 /**
3644 * QThread class reimplementation to open Win32 API native file dialog
3645 */
3646 class Thread : public QThread
3647 {
3648 public:
3649
3650 Thread (QWidget *aParent, QObject *aTarget,
3651 const QString &aStartWith, const QString &aFilters,
3652 const QString &aCaption) :
3653 mParent (aParent), mTarget (aTarget),
3654 mStartWith (aStartWith), mFilters (aFilters),
3655 mCaption (aCaption) {}
3656
3657 virtual void run()
3658 {
3659 QString result;
3660
3661 QString workDir;
3662 QString initSel;
3663 QFileInfo fi (mStartWith);
3664
3665 if (fi.isDir())
3666 workDir = mStartWith;
3667 else
3668 {
3669 workDir = fi.absolutePath();
3670 initSel = fi.fileName();
3671 }
3672
3673 workDir = QDir::convertSeparators (workDir);
3674 if (!workDir.endsWith ("\\"))
3675 workDir += "\\";
3676
3677 QString title = mCaption.isNull() ? tr ("Select a file") : mCaption;
3678
3679 QWidget *topParent = mParent ? mParent->window() : vboxGlobal().mainWindow();
3680 QString winFilters = winFilter (mFilters);
3681 AssertCompile (sizeof (TCHAR) == sizeof (QChar));
3682 TCHAR buf [1024];
3683 if (initSel.length() > 0 && initSel.length() < sizeof (buf))
3684 memcpy (buf, initSel.isNull() ? 0 : initSel.utf16(),
3685 (initSel.length() + 1) * sizeof (TCHAR));
3686 else
3687 buf [0] = 0;
3688
3689 OPENFILENAME ofn;
3690 memset (&ofn, 0, sizeof (OPENFILENAME));
3691
3692 ofn.lStructSize = sizeof (OPENFILENAME);
3693 ofn.hwndOwner = topParent ? topParent->winId() : 0;
3694 ofn.lpstrFilter = (TCHAR *) winFilters.isNull() ? 0 : winFilters.utf16();
3695 ofn.lpstrFile = buf;
3696 ofn.nMaxFile = sizeof (buf) - 1;
3697 ofn.lpstrInitialDir = (TCHAR *) workDir.isNull() ? 0 : workDir.utf16();
3698 ofn.lpstrTitle = (TCHAR *) title.isNull() ? 0 : title.utf16();
3699 ofn.Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY |
3700 OFN_EXPLORER | OFN_ENABLEHOOK |
3701 OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
3702 ofn.lpfnHook = OFNHookProc;
3703
3704 if (GetOpenFileName (&ofn))
3705 {
3706 result = QString::fromUtf16 ((ushort *) ofn.lpstrFile);
3707 }
3708
3709 // qt_win_eatMouseMove();
3710 MSG msg = {0, 0, 0, 0, 0, 0, 0};
3711 while (PeekMessage (&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
3712 if (msg.message == WM_MOUSEMOVE)
3713 PostMessage (msg.hwnd, msg.message, 0, msg.lParam);
3714
3715 result = result.isEmpty() ? result : QFileInfo (result).absoluteFilePath();
3716
3717 QApplication::postEvent (mTarget, new GetOpenFileNameEvent (result));
3718 }
3719
3720 private:
3721
3722 QWidget *mParent;
3723 QObject *mTarget;
3724 QString mStartWith;
3725 QString mFilters;
3726 QString mCaption;
3727 };
3728
3729 if (aSelectedFilter)
3730 *aSelectedFilter = QString::null;
3731
3732 /* Local event loop to run while waiting for the result from another
3733 * thread */
3734 QEventLoop loop;
3735
3736 QString startWith = QDir::convertSeparators (aStartWith);
3737 LoopObject loopObject ((QEvent::Type) GetOpenFileNameEvent::TypeId, loop);
3738
3739//#warning check me!
3740 if (aParent)
3741 aParent->setWindowModality (Qt::WindowModal);
3742
3743 Thread openDirThread (aParent, &loopObject, startWith, aFilters, aCaption);
3744 openDirThread.start();
3745 loop.exec();
3746 openDirThread.wait();
3747
3748//#warning check me!
3749 if (aParent)
3750 aParent->setWindowModality (Qt::NonModal);
3751
3752 return loopObject.result();
3753
3754#elif defined (Q_WS_X11) && (QT_VERSION < 0x040400)
3755
3756 /* Here is workaround for Qt4.3 bug with QFileDialog which crushes when
3757 * gets initial path as hidden directory if no hidden files are shown.
3758 * See http://trolltech.com/developer/task-tracker/index_html?method=entry&id=193483
3759 * for details */
3760 QFileDialog dlg (aParent);
3761 dlg.setWindowTitle (aCaption);
3762 dlg.setDirectory (aStartWith);
3763 dlg.setFilter (aFilters);
3764 if (aSelectedFilter)
3765 dlg.selectFilter (*aSelectedFilter);
3766 dlg.setResolveSymlinks (aResolveSymlinks);
3767 QAction *hidden = dlg.findChild <QAction*> ("qt_show_hidden_action");
3768 if (hidden)
3769 {
3770 hidden->trigger();
3771 hidden->setVisible (false);
3772 }
3773 return dlg.exec() ? dlg.selectedFiles() [0] : QString::null;
3774
3775#else
3776
3777 QFileDialog::Options o;
3778 if (!aResolveSymlinks)
3779 o |= QFileDialog::DontResolveSymlinks;
3780 return QFileDialog::getOpenFileName (aParent, aCaption, aStartWith,
3781 aFilters, aSelectedFilter, o);
3782#endif
3783}
3784
3785/**
3786 * Search for the first directory that exists starting from the passed one
3787 * and going up through its parents. In case if none of the directories
3788 * exist (except the root one), the function returns QString::null.
3789 */
3790/* static */
3791QString VBoxGlobal::getFirstExistingDir (const QString &aStartDir)
3792{
3793 QString result = QString::null;
3794 QDir dir (aStartDir);
3795 while (!dir.exists() && !dir.isRoot())
3796 {
3797 QFileInfo dirInfo (dir.absolutePath());
3798 dir = dirInfo.absolutePath();
3799 }
3800 if (dir.exists() && !dir.isRoot())
3801 result = dir.absolutePath();
3802 return result;
3803}
3804
3805#if defined (Q_WS_X11)
3806
3807static char *XXGetProperty (Display *aDpy, Window aWnd,
3808 Atom aPropType, const char *aPropName)
3809{
3810 Atom propNameAtom = XInternAtom (aDpy, aPropName,
3811 True /* only_if_exists */);
3812 if (propNameAtom == None)
3813 return NULL;
3814
3815 Atom actTypeAtom = None;
3816 int actFmt = 0;
3817 unsigned long nItems = 0;
3818 unsigned long nBytesAfter = 0;
3819 unsigned char *propVal = NULL;
3820 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
3821 0, LONG_MAX, False /* delete */,
3822 aPropType, &actTypeAtom, &actFmt,
3823 &nItems, &nBytesAfter, &propVal);
3824 if (rc != Success)
3825 return NULL;
3826
3827 return reinterpret_cast <char *> (propVal);
3828}
3829
3830static Bool XXSendClientMessage (Display *aDpy, Window aWnd, const char *aMsg,
3831 unsigned long aData0 = 0, unsigned long aData1 = 0,
3832 unsigned long aData2 = 0, unsigned long aData3 = 0,
3833 unsigned long aData4 = 0)
3834{
3835 Atom msgAtom = XInternAtom (aDpy, aMsg, True /* only_if_exists */);
3836 if (msgAtom == None)
3837 return False;
3838
3839 XEvent ev;
3840
3841 ev.xclient.type = ClientMessage;
3842 ev.xclient.serial = 0;
3843 ev.xclient.send_event = True;
3844 ev.xclient.display = aDpy;
3845 ev.xclient.window = aWnd;
3846 ev.xclient.message_type = msgAtom;
3847
3848 /* always send as 32 bit for now */
3849 ev.xclient.format = 32;
3850 ev.xclient.data.l [0] = aData0;
3851 ev.xclient.data.l [1] = aData1;
3852 ev.xclient.data.l [2] = aData2;
3853 ev.xclient.data.l [3] = aData3;
3854 ev.xclient.data.l [4] = aData4;
3855
3856 return XSendEvent (aDpy, DefaultRootWindow (aDpy), False,
3857 SubstructureRedirectMask, &ev) != 0;
3858}
3859
3860#endif
3861
3862/**
3863 * Activates the specified window. If necessary, the window will be
3864 * de-iconified activation.
3865 *
3866 * @note On X11, it is implied that @a aWid represents a window of the same
3867 * display the application was started on.
3868 *
3869 * @param aWId Window ID to activate.
3870 * @param aSwitchDesktop @c true to switch to the window's desktop before
3871 * activation.
3872 *
3873 * @return @c true on success and @c false otherwise.
3874 */
3875/* static */
3876bool VBoxGlobal::activateWindow (WId aWId, bool aSwitchDesktop /* = true */)
3877{
3878 bool result = true;
3879
3880#if defined (Q_WS_WIN32)
3881
3882 if (IsIconic (aWId))
3883 result &= !!ShowWindow (aWId, SW_RESTORE);
3884 else if (!IsWindowVisible (aWId))
3885 result &= !!ShowWindow (aWId, SW_SHOW);
3886
3887 result &= !!SetForegroundWindow (aWId);
3888
3889#elif defined (Q_WS_X11)
3890
3891 Display *dpy = QX11Info::display();
3892
3893 if (aSwitchDesktop)
3894 {
3895 /* try to find the desktop ID using the NetWM property */
3896 CARD32 *desktop = (CARD32 *) XXGetProperty (dpy, aWId, XA_CARDINAL,
3897 "_NET_WM_DESKTOP");
3898 if (desktop == NULL)
3899 /* if the NetWM propery is not supported try to find the desktop
3900 * ID using the GNOME WM property */
3901 desktop = (CARD32 *) XXGetProperty (dpy, aWId, XA_CARDINAL,
3902 "_WIN_WORKSPACE");
3903
3904 if (desktop != NULL)
3905 {
3906 Bool ok = XXSendClientMessage (dpy, DefaultRootWindow (dpy),
3907 "_NET_CURRENT_DESKTOP",
3908 *desktop);
3909 if (!ok)
3910 {
3911 LogWarningFunc (("Couldn't switch to desktop=%08X\n",
3912 desktop));
3913 result = false;
3914 }
3915 XFree (desktop);
3916 }
3917 else
3918 {
3919 LogWarningFunc (("Couldn't find a desktop ID for aWId=%08X\n",
3920 aWId));
3921 result = false;
3922 }
3923 }
3924
3925 Bool ok = XXSendClientMessage (dpy, aWId, "_NET_ACTIVE_WINDOW");
3926 result &= !!ok;
3927
3928 XRaiseWindow (dpy, aWId);
3929
3930#else
3931
3932 NOREF (aSwitchDesktop);
3933 AssertFailed();
3934 result = false;
3935
3936#endif
3937
3938 if (!result)
3939 LogWarningFunc (("Couldn't activate aWId=%08X\n", aWId));
3940
3941 return result;
3942}
3943
3944/**
3945 * Removes the acceletartor mark (the ampersand symbol) from the given string
3946 * and returns the result. The string is supposed to be a menu item's text
3947 * that may (or may not) contain the accelerator mark.
3948 *
3949 * In order to support accelerators used in non-alphabet languages
3950 * (e.g. Japanese) that has a form of "(&<L>)" (where <L> is a latin letter),
3951 * this method first searches for this pattern and, if found, removes it as a
3952 * whole. If such a pattern is not found, then the '&' character is simply
3953 * removed from the string.
3954 *
3955 * @note This function removes only the first occurense of the accelerator
3956 * mark.
3957 *
3958 * @param aText Menu item's text to remove the acceletaror mark from.
3959 *
3960 * @return The resulting string.
3961 */
3962/* static */
3963QString VBoxGlobal::removeAccelMark (const QString &aText)
3964{
3965 QString result = aText;
3966
3967 QRegExp accel ("\\(&[a-zA-Z]\\)");
3968 int pos = accel.indexIn (result);
3969 if (pos >= 0)
3970 result.remove (pos, accel.cap().length());
3971 else
3972 {
3973 pos = result.indexOf ('&');
3974 if (pos >= 0)
3975 result.remove (pos, 1);
3976 }
3977
3978 return result;
3979}
3980
3981/* static */
3982QString VBoxGlobal::insertKeyToActionText (const QString &aText, const QString &aKey)
3983{
3984#ifdef Q_WS_MAC
3985 QString key ("%1 (Host+%2)");
3986#else
3987 QString key ("%1 \tHost+%2");
3988#endif
3989 return key.arg (aText).arg (QKeySequence (aKey).toString (QKeySequence::NativeText));
3990}
3991
3992/* static */
3993QString VBoxGlobal::extractKeyFromActionText (const QString &aText)
3994{
3995 QString key;
3996#ifdef Q_WS_MAC
3997 QRegExp re (".* \\(Host\\+(.+)\\)");
3998#else
3999 QRegExp re (".* \\t\\Host\\+(.+)");
4000#endif
4001 if (re.exactMatch (aText))
4002 key = re.cap (1);
4003 return key;
4004}
4005
4006/**
4007 * Searches for a widget that with @a aName (if it is not NULL) which inherits
4008 * @a aClassName (if it is not NULL) and among children of @a aParent. If @a
4009 * aParent is NULL, all top-level widgets are searched. If @a aRecursive is
4010 * true, child widgets are recursively searched as well.
4011 */
4012/* static */
4013QWidget *VBoxGlobal::findWidget (QWidget *aParent, const char *aName,
4014 const char *aClassName /* = NULL */,
4015 bool aRecursive /* = false */)
4016{
4017 if (aParent == NULL)
4018 {
4019 QWidgetList list = QApplication::topLevelWidgets();
4020 QWidget* w = NULL;
4021 foreach(w, list)
4022 {
4023 if ((!aName || strcmp (w->objectName().toAscii().constData(), aName) == 0) &&
4024 (!aClassName || strcmp (w->metaObject()->className(), aClassName) == 0))
4025 break;
4026 if (aRecursive)
4027 {
4028 w = findWidget (w, aName, aClassName, aRecursive);
4029 if (w)
4030 break;
4031 }
4032 }
4033 return w;
4034 }
4035
4036 /* Find the first children of aParent with the appropriate properties.
4037 * Please note that this call is recursivly. */
4038 QList<QWidget *> list = qFindChildren<QWidget *> (aParent, aName);
4039 QWidget *child = NULL;
4040 foreach(child, list)
4041 {
4042 if (!aClassName || strcmp (child->metaObject()->className(), aClassName) == 0)
4043 break;
4044 }
4045 return child;
4046}
4047
4048/**
4049 * Automatically figure out which hdd backends are currently support by vbox.
4050 * Returned is a list of pairs with the form
4051 * <"Backend Name", "*.suffix1 *.suffix2 ...">.
4052 */
4053/* static */
4054QList< QPair<QString, QString> > VBoxGlobal::HDDBackends()
4055{
4056 int rc;
4057 VDBACKENDINFO aVDInfo[100];
4058 unsigned cEntries;
4059
4060 QStringList all;
4061 QStringList filterList;
4062 QList< QPair<QString, QString> > backendPropList;
4063 /* Ask for all supported backends */
4064 rc = VDBackendInfo (RT_ELEMENTS(aVDInfo), aVDInfo, &cEntries);
4065 if (VBOX_SUCCESS (rc))
4066 {
4067 for (unsigned i=0; i < cEntries; i++)
4068 {
4069 QStringList f;
4070 /* One backend could have more than one suffix. Get them all. */
4071 if (aVDInfo[i].papszFileExtensions)
4072 {
4073 const char *const *papsz = aVDInfo[i].papszFileExtensions;
4074 while (*papsz != NULL)
4075 {
4076 f << QString ("*.%1").arg (*papsz);
4077 papsz++;
4078 }
4079 }
4080 /* Create a pair out of the backend name and all suffix's. */
4081 if (!f.isEmpty())
4082 backendPropList << QPair<QString, QString> (aVDInfo[i].pszBackend, f.join(" "));
4083 }
4084 }
4085 return backendPropList;
4086}
4087
4088// Public slots
4089////////////////////////////////////////////////////////////////////////////////
4090
4091/**
4092 * Opens the specified URL using OS/Desktop capabilities.
4093 *
4094 * @param aURL URL to open
4095 *
4096 * @return true on success and false otherwise
4097 */
4098bool VBoxGlobal::openURL (const QString &aURL)
4099{
4100#if defined (Q_WS_WIN)
4101 /* We cannot use ShellExecute() on the main UI thread because we've
4102 * initialized COM with CoInitializeEx(COINIT_MULTITHREADED). See
4103 * http://support.microsoft.com/default.aspx?scid=kb;en-us;287087
4104 * for more details. */
4105 class Thread : public QThread
4106 {
4107 public:
4108
4109 Thread (const QString &aURL, QObject *aObject)
4110 : mObject (aObject), mURL (aURL) {}
4111
4112 void run()
4113 {
4114 int rc = (int) ShellExecute (NULL, NULL, mURL.isNull() ? 0 : mURL.utf16(),
4115 NULL, NULL, SW_SHOW);
4116 bool ok = rc > 32;
4117 QApplication::postEvent
4118 (mObject,
4119 new VBoxShellExecuteEvent (this, mURL, ok));
4120 }
4121
4122 QString mURL;
4123 QObject *mObject;
4124 };
4125
4126 Thread *thread = new Thread (aURL, this);
4127 thread->start();
4128 /* thread will be deleted in the VBoxShellExecuteEvent handler */
4129
4130 return true;
4131
4132#elif defined (Q_WS_X11)
4133
4134 static const char * const commands[] =
4135 { "kfmclient:exec", "gnome-open", "x-www-browser", "firefox", "konqueror" };
4136
4137 for (size_t i = 0; i < ELEMENTS (commands); ++ i)
4138 {
4139 QStringList args = QString(commands [i]).split (':');
4140 args += aURL;
4141 QString command = args.takeFirst();
4142 if (QProcess::startDetached (command, args))
4143 return true;
4144 }
4145
4146#elif defined (Q_WS_MAC)
4147
4148 /* The code below is taken from Psi 0.10 sources
4149 * (http://www.psi-im.org) */
4150
4151 /* Use Internet Config to hand the URL to the appropriate application, as
4152 * set by the user in the Internet Preferences pane.
4153 * NOTE: ICStart could be called once at Psi startup, saving the
4154 * ICInstance in a global variable, as a minor optimization.
4155 * ICStop should then be called at Psi shutdown if ICStart
4156 * succeeded. */
4157 ICInstance icInstance;
4158 OSType psiSignature = 'psi ';
4159 OSStatus error = ::ICStart (&icInstance, psiSignature);
4160 if (error == noErr)
4161 {
4162 ConstStr255Param hint (0x0);
4163 QByteArray cs = aURL.toLocal8Bit();
4164 const char* data = cs.data();
4165 long length = cs.length();
4166 long start (0);
4167 long end (length);
4168 /* Don't bother testing return value (error); launched application
4169 * will report problems. */
4170 ::ICLaunchURL (icInstance, hint, data, length, &start, &end);
4171 ICStop (icInstance);
4172 return true;
4173 }
4174
4175#else
4176 vboxProblem().message
4177 (NULL, VBoxProblemReporter::Error,
4178 tr ("Opening URLs is not implemented yet."));
4179 return false;
4180#endif
4181
4182 /* if we go here it means we couldn't open the URL */
4183 vboxProblem().cannotOpenURL (aURL);
4184
4185 return false;
4186}
4187
4188/**
4189 * Shows the VirtualBox registration dialog.
4190 *
4191 * @note that this method is not part of VBoxProblemReporter (like e.g.
4192 * VBoxProblemReporter::showHelpAboutDialog()) because it is tied to
4193 * VBoxCallback::OnExtraDataChange() handling performed by VBoxGlobal.
4194 *
4195 * @param aForce
4196 */
4197void VBoxGlobal::showRegistrationDialog (bool aForce)
4198{
4199#ifdef VBOX_WITH_REGISTRATION
4200 if (!aForce && !VBoxRegistrationDlg::hasToBeShown())
4201 return;
4202
4203 if (mRegDlg)
4204 {
4205 /* Show the already opened registration dialog */
4206 mRegDlg->setWindowState (mRegDlg->windowState() & ~Qt::WindowMinimized);
4207 mRegDlg->raise();
4208 mRegDlg->activateWindow();
4209 }
4210 else
4211 {
4212 /* Store the ID of the main window to ensure that only one
4213 * registration dialog is shown at a time. Due to manipulations with
4214 * OnExtraDataCanChange() and OnExtraDataChange() signals, this extra
4215 * data item acts like an inter-process mutex, so the first process
4216 * that attempts to set it will win, the rest will get a failure from
4217 * the SetExtraData() call. */
4218 mVBox.SetExtraData (VBoxDefs::GUI_RegistrationDlgWinID,
4219 QString ("%1").arg ((qulonglong) mMainWindow->winId()));
4220
4221 if (mVBox.isOk())
4222 {
4223 /* We've got the "mutex", create a new registration dialog */
4224 VBoxRegistrationDlg *dlg =
4225 new VBoxRegistrationDlg (&mRegDlg, 0);
4226 dlg->setAttribute (Qt::WA_DeleteOnClose);
4227 Assert (dlg == mRegDlg);
4228 mRegDlg->show();
4229 }
4230 }
4231#endif
4232}
4233
4234/**
4235 * Shows the VirtualBox version check & update dialog.
4236 *
4237 * @note that this method is not part of VBoxProblemReporter (like e.g.
4238 * VBoxProblemReporter::showHelpAboutDialog()) because it is tied to
4239 * VBoxCallback::OnExtraDataChange() handling performed by VBoxGlobal.
4240 *
4241 * @param aForce
4242 */
4243void VBoxGlobal::showUpdateDialog (bool aForce)
4244{
4245 bool isNecessary = VBoxUpdateDlg::isNecessary();
4246
4247 if (!aForce && !isNecessary)
4248 return;
4249
4250 if (mUpdDlg)
4251 {
4252 if (!mUpdDlg->isHidden())
4253 {
4254 mUpdDlg->setWindowState (mUpdDlg->windowState() & ~Qt::WindowMinimized);
4255 mUpdDlg->raise();
4256 mUpdDlg->activateWindow();
4257 }
4258 }
4259 else
4260 {
4261 /* Store the ID of the main window to ensure that only one
4262 * update dialog is shown at a time. Due to manipulations with
4263 * OnExtraDataCanChange() and OnExtraDataChange() signals, this extra
4264 * data item acts like an inter-process mutex, so the first process
4265 * that attempts to set it will win, the rest will get a failure from
4266 * the SetExtraData() call. */
4267 mVBox.SetExtraData (VBoxDefs::GUI_UpdateDlgWinID,
4268 QString ("%1").arg ((qulonglong) mMainWindow->winId()));
4269
4270 if (mVBox.isOk())
4271 {
4272 /* We've got the "mutex", create a new update dialog */
4273 VBoxUpdateDlg *dlg = new VBoxUpdateDlg (&mUpdDlg, aForce, 0);
4274 dlg->setAttribute (Qt::WA_DeleteOnClose);
4275 Assert (dlg == mUpdDlg);
4276
4277 /* Update dialog always in background mode for now.
4278 * if (!aForce && isAutomatic) */
4279 mUpdDlg->search();
4280 /* else mUpdDlg->show(); */
4281 }
4282 }
4283}
4284
4285// Protected members
4286////////////////////////////////////////////////////////////////////////////////
4287
4288bool VBoxGlobal::event (QEvent *e)
4289{
4290 switch (e->type())
4291 {
4292#if defined (Q_WS_WIN)
4293 case VBoxDefs::ShellExecuteEventType:
4294 {
4295 VBoxShellExecuteEvent *ev = (VBoxShellExecuteEvent *) e;
4296 if (!ev->mOk)
4297 vboxProblem().cannotOpenURL (ev->mURL);
4298 /* wait for the thread and free resources */
4299 ev->mThread->wait();
4300 delete ev->mThread;
4301 return true;
4302 }
4303#endif
4304
4305 case VBoxDefs::AsyncEventType:
4306 {
4307 VBoxAsyncEvent *ev = (VBoxAsyncEvent *) e;
4308 ev->handle();
4309 return true;
4310 }
4311
4312 case VBoxDefs::EnumerateMediaEventType:
4313 {
4314 VBoxEnumerateMediaEvent *ev = (VBoxEnumerateMediaEvent *) e;
4315
4316 if (!ev->mLast)
4317 {
4318 if (ev->mMedia.status == VBoxMedia::Error)
4319 vboxProblem().cannotGetMediaAccessibility (ev->mMedia.disk);
4320 media_list [ev->mIndex] = ev->mMedia;
4321 emit mediaEnumerated (media_list [ev->mIndex], ev->mIndex);
4322 }
4323 else
4324 {
4325 /* the thread has posted the last message, wait for termination */
4326 media_enum_thread->wait();
4327 delete media_enum_thread;
4328 media_enum_thread = 0;
4329
4330 emit mediaEnumFinished (media_list);
4331 }
4332
4333 return true;
4334 }
4335
4336 /* VirtualBox callback events */
4337
4338 case VBoxDefs::MachineStateChangeEventType:
4339 {
4340 emit machineStateChanged (*(VBoxMachineStateChangeEvent *) e);
4341 return true;
4342 }
4343 case VBoxDefs::MachineDataChangeEventType:
4344 {
4345 emit machineDataChanged (*(VBoxMachineDataChangeEvent *) e);
4346 return true;
4347 }
4348 case VBoxDefs::MachineRegisteredEventType:
4349 {
4350 emit machineRegistered (*(VBoxMachineRegisteredEvent *) e);
4351 return true;
4352 }
4353 case VBoxDefs::SessionStateChangeEventType:
4354 {
4355 emit sessionStateChanged (*(VBoxSessionStateChangeEvent *) e);
4356 return true;
4357 }
4358 case VBoxDefs::SnapshotEventType:
4359 {
4360 emit snapshotChanged (*(VBoxSnapshotEvent *) e);
4361 return true;
4362 }
4363 case VBoxDefs::CanShowRegDlgEventType:
4364 {
4365 emit canShowRegDlg (((VBoxCanShowRegDlgEvent *) e)->mCanShow);
4366 return true;
4367 }
4368 case VBoxDefs::CanShowUpdDlgEventType:
4369 {
4370 emit canShowUpdDlg (((VBoxCanShowUpdDlgEvent *) e)->mCanShow);
4371 return true;
4372 }
4373 case VBoxDefs::ChangeGUILanguageEventType:
4374 {
4375 loadLanguage (static_cast<VBoxChangeGUILanguageEvent*> (e)->mLangId);
4376 return true;
4377 }
4378
4379 default:
4380 break;
4381 }
4382
4383 return QObject::event (e);
4384}
4385
4386bool VBoxGlobal::eventFilter (QObject *aObject, QEvent *aEvent)
4387{
4388 if (aEvent->type() == QEvent::LanguageChange &&
4389 aObject->isWidgetType() &&
4390 static_cast <QWidget *> (aObject)->isTopLevel())
4391 {
4392 /* Catch the language change event before any other widget gets it in
4393 * order to invalidate cached string resources (like the details view
4394 * templates) that may be used by other widgets. */
4395 QWidgetList list = QApplication::topLevelWidgets();
4396 if (list.first() == aObject)
4397 {
4398 /* call this only once per every language change (see
4399 * QApplication::installTranslator() for details) */
4400 retranslateUi();
4401 }
4402 }
4403
4404 return QObject::eventFilter (aObject, aEvent);
4405}
4406
4407// Private members
4408////////////////////////////////////////////////////////////////////////////////
4409
4410void VBoxGlobal::init()
4411{
4412#ifdef DEBUG
4413 verString += " [DEBUG]";
4414#endif
4415
4416#ifdef Q_WS_WIN
4417 /* COM for the main thread is initialized in main() */
4418#else
4419 HRESULT rc = COMBase::InitializeCOM();
4420 if (FAILED (rc))
4421 {
4422 vboxProblem().cannotInitCOM (rc);
4423 return;
4424 }
4425#endif
4426
4427 mVBox.createInstance (CLSID_VirtualBox);
4428 if (!mVBox.isOk())
4429 {
4430 vboxProblem().cannotCreateVirtualBox (mVBox);
4431 return;
4432 }
4433
4434 /* initialize guest OS type vector */
4435 CGuestOSTypeCollection coll = mVBox.GetGuestOSTypes();
4436 int osTypeCount = coll.GetCount();
4437 AssertMsg (osTypeCount > 0, ("Number of OS types must not be zero"));
4438 if (osTypeCount > 0)
4439 {
4440 vm_os_types.resize (osTypeCount);
4441 int i = 0;
4442 CGuestOSTypeEnumerator en = coll.Enumerate();
4443 while (en.HasMore())
4444 vm_os_types [i++] = en.GetNext();
4445 }
4446
4447 /* fill in OS type icon dictionary */
4448 static const char *kOSTypeIcons [][2] =
4449 {
4450 {"unknown", ":/os_unknown.png"},
4451 {"dos", ":/os_dos.png"},
4452 {"win31", ":/os_win31.png"},
4453 {"win95", ":/os_win95.png"},
4454 {"win98", ":/os_win98.png"},
4455 {"winme", ":/os_winme.png"},
4456 {"winnt4", ":/os_winnt4.png"},
4457 {"win2k", ":/os_win2k.png"},
4458 {"winxp", ":/os_winxp.png"},
4459 {"win2k3", ":/os_win2k3.png"},
4460 {"winvista", ":/os_winvista.png"},
4461 {"win2k8", ":/os_win2k8.png"},
4462 {"os2warp3", ":/os_os2warp3.png"},
4463 {"os2warp4", ":/os_os2warp4.png"},
4464 {"os2warp45", ":/os_os2warp45.png"},
4465 {"ecs", ":/os_ecs.png"},
4466 {"linux22", ":/os_linux22.png"},
4467 {"linux24", ":/os_linux24.png"},
4468 {"linux26", ":/os_linux26.png"},
4469 {"archlinux", ":/os_archlinux.png"},
4470 {"debian", ":/os_debian.png"},
4471 {"opensolaris", ":/os_opensolaris.png"},
4472 {"opensuse", ":/os_opensuse.png"},
4473 {"fedoracore", ":/os_fedoracore.png"},
4474 {"gentoo", ":/os_gentoo.png"},
4475 {"mandriva", ":/os_mandriva.png"},
4476 {"redhat", ":/os_redhat.png"},
4477 {"ubuntu", ":/os_ubuntu.png"},
4478 {"xandros", ":/os_xandros.png"},
4479 {"freebsd", ":/os_freebsd.png"},
4480 {"openbsd", ":/os_openbsd.png"},
4481 {"netbsd", ":/os_netbsd.png"},
4482 {"netware", ":/os_netware.png"},
4483 {"solaris", ":/os_solaris.png"},
4484 {"l4", ":/os_l4.png"},
4485 };
4486 for (uint n = 0; n < SIZEOF_ARRAY (kOSTypeIcons); n ++)
4487 {
4488 vm_os_type_icons.insert (kOSTypeIcons [n][0],
4489 new QPixmap (kOSTypeIcons [n][1]));
4490 }
4491
4492 /* fill in VM state icon dictionary */
4493 static struct
4494 {
4495 KMachineState state;
4496 const char *name;
4497 }
4498 vmStateIcons[] =
4499 {
4500 {KMachineState_Null, NULL},
4501 {KMachineState_PoweredOff, ":/state_powered_off_16px.png"},
4502 {KMachineState_Saved, ":/state_saved_16px.png"},
4503 {KMachineState_Aborted, ":/state_aborted_16px.png"},
4504 {KMachineState_Running, ":/state_running_16px.png"},
4505 {KMachineState_Paused, ":/state_paused_16px.png"},
4506 {KMachineState_Stuck, ":/state_stuck_16px.png"},
4507 {KMachineState_Starting, ":/state_running_16px.png"}, /// @todo (dmik) separate icon?
4508 {KMachineState_Stopping, ":/state_running_16px.png"}, /// @todo (dmik) separate icon?
4509 {KMachineState_Saving, ":/state_saving_16px.png"},
4510 {KMachineState_Restoring, ":/state_restoring_16px.png"},
4511 {KMachineState_Discarding, ":/state_discarding_16px.png"},
4512 };
4513 for (uint n = 0; n < SIZEOF_ARRAY (vmStateIcons); n ++)
4514 {
4515 mStateIcons.insert (vmStateIcons [n].state,
4516 new QPixmap (vmStateIcons [n].name));
4517 }
4518
4519 /* online/offline snapshot icons */
4520 mOfflineSnapshotIcon = QPixmap (":/offline_snapshot_16px.png");
4521 mOnlineSnapshotIcon = QPixmap (":/online_snapshot_16px.png");
4522
4523 /* initialize state colors vector */
4524 vm_state_color.insert (KMachineState_Null, new QColor(Qt::red));
4525 vm_state_color.insert (KMachineState_PoweredOff, new QColor(Qt::gray));
4526 vm_state_color.insert (KMachineState_Saved, new QColor(Qt::yellow));
4527 vm_state_color.insert (KMachineState_Aborted, new QColor(Qt::darkRed));
4528 vm_state_color.insert (KMachineState_Running, new QColor(Qt::green));
4529 vm_state_color.insert (KMachineState_Paused, new QColor(Qt::darkGreen));
4530 vm_state_color.insert (KMachineState_Stuck, new QColor(Qt::darkMagenta));
4531 vm_state_color.insert (KMachineState_Starting, new QColor(Qt::green));
4532 vm_state_color.insert (KMachineState_Stopping, new QColor(Qt::green));
4533 vm_state_color.insert (KMachineState_Saving, new QColor(Qt::green));
4534 vm_state_color.insert (KMachineState_Restoring, new QColor(Qt::green));
4535 vm_state_color.insert (KMachineState_Discarding, new QColor(Qt::green));
4536
4537 qApp->installEventFilter (this);
4538
4539 /* create default non-null global settings */
4540 gset = VBoxGlobalSettings (false);
4541
4542 /* try to load global settings */
4543 gset.load (mVBox);
4544 if (!mVBox.isOk() || !gset)
4545 {
4546 vboxProblem().cannotLoadGlobalConfig (mVBox, gset.lastError());
4547 return;
4548 }
4549
4550 /* Load customized language if any */
4551 QString languageId = gset.languageId();
4552 if (!languageId.isNull())
4553 loadLanguage (languageId);
4554
4555 retranslateUi();
4556
4557 /* process command line */
4558
4559 vm_render_mode_str = 0;
4560#ifdef VBOX_WITH_DEBUGGER_GUI
4561# ifdef VBOX_WITH_DEBUGGER_GUI_MENU
4562 mDbgEnabled = true;
4563# else
4564 mDbgEnabled = RTEnvExist("VBOX_GUI_DBG_ENABLED");
4565# endif
4566 mDbgAutoShow = RTEnvExist("VBOX_GUI_DBG_AUTO_SHOW");
4567#endif
4568
4569 int argc = qApp->argc();
4570 int i = 1;
4571 while (i < argc)
4572 {
4573 const char *arg = qApp->argv() [i];
4574 if ( !::strcmp (arg, "-startvm"))
4575 {
4576 if (++i < argc)
4577 {
4578 QString param = QString (qApp->argv() [i]);
4579 QUuid uuid = QUuid (param);
4580 if (!uuid.isNull())
4581 {
4582 vmUuid = uuid;
4583 }
4584 else
4585 {
4586 CMachine m = mVBox.FindMachine (param);
4587 if (m.isNull())
4588 {
4589 vboxProblem().cannotFindMachineByName (mVBox, param);
4590 return;
4591 }
4592 vmUuid = m.GetId();
4593 }
4594 }
4595 }
4596 else if (!::strcmp (arg, "-comment"))
4597 {
4598 ++i;
4599 }
4600 else if (!::strcmp (arg, "-rmode"))
4601 {
4602 if (++i < argc)
4603 vm_render_mode_str = qApp->argv() [i];
4604 }
4605#ifdef VBOX_WITH_DEBUGGER_GUI
4606 else if (!::strcmp (arg, "-dbg") || !::strcmp (arg, "--dbg"))
4607 {
4608 mDbgEnabled = true;
4609 }
4610 else if (!::strcmp( arg, "-debug") || !::strcmp( arg, "--debug"))
4611 {
4612 mDbgEnabled = true;
4613 mDbgAutoShow = true;
4614 }
4615 else if (!::strcmp (arg, "-no-debug") || !::strcmp (arg, "--no-debug"))
4616 {
4617 mDbgEnabled = false;
4618 mDbgAutoShow = false;
4619 }
4620#endif
4621 /** @todo add an else { msgbox(syntax error); exit(1); } here, pretty please... */
4622 i++;
4623 }
4624
4625 vm_render_mode = vboxGetRenderMode( vm_render_mode_str );
4626
4627 /* setup the callback */
4628 callback = CVirtualBoxCallback (new VBoxCallback (*this));
4629 mVBox.RegisterCallback (callback);
4630 AssertWrapperOk (mVBox);
4631 if (!mVBox.isOk())
4632 return;
4633
4634#ifdef VBOX_WITH_DEBUGGER_GUI
4635 /* setup the debugger gui. */
4636 if (RTEnvExist("VBOX_GUI_NO_DEBUGGER"))
4637 mDbgEnabled = mDbgAutoShow = false;
4638 if (mDbgEnabled)
4639 {
4640 int rc = SUPR3HardenedLdrLoadAppPriv("VBoxDbg", &mhVBoxDbg);
4641 if (RT_FAILURE(rc))
4642 {
4643 mhVBoxDbg = NIL_RTLDRMOD;
4644 mDbgAutoShow = false;
4645 LogRel(("Failed to load VBoxDbg, rc=%Rrc\n", rc));
4646 }
4647 }
4648#endif
4649
4650 mValid = true;
4651}
4652
4653/** @internal
4654 *
4655 * This method should be never called directly. It is called automatically
4656 * when the application terminates.
4657 */
4658void VBoxGlobal::cleanup()
4659{
4660 /* sanity check */
4661 if (!sVBoxGlobalInCleanup)
4662 {
4663 AssertMsgFailed (("Should never be called directly\n"));
4664 return;
4665 }
4666
4667 if (!callback.isNull())
4668 {
4669 mVBox.UnregisterCallback (callback);
4670 AssertWrapperOk (mVBox);
4671 callback.detach();
4672 }
4673
4674 if (media_enum_thread)
4675 {
4676 /* sVBoxGlobalInCleanup is true here, so just wait for the thread */
4677 media_enum_thread->wait();
4678 delete media_enum_thread;
4679 media_enum_thread = 0;
4680 }
4681
4682#ifdef VBOX_WITH_REGISTRATION
4683 if (mRegDlg)
4684 mRegDlg->close();
4685#endif
4686
4687 if (mConsoleWnd)
4688 delete mConsoleWnd;
4689 if (mSelectorWnd)
4690 delete mSelectorWnd;
4691
4692 /* ensure CGuestOSType objects are no longer used */
4693 vm_os_types.clear();
4694 /* media list contains a lot of CUUnknown, release them */
4695 media_list.clear();
4696 /* the last step to ensure we don't use COM any more */
4697 mVBox.detach();
4698
4699 /* There may be VBoxEnumerateMediaEvent instances still in the message
4700 * queue which reference COM objects. Remove them to release those objects
4701 * before uninitializing the COM subsystem. */
4702 QApplication::removePostedEvents (this);
4703
4704#ifdef Q_WS_WIN
4705 /* COM for the main thread is shutdown in main() */
4706#else
4707 COMBase::CleanupCOM();
4708#endif
4709
4710 mValid = false;
4711}
4712
4713/** @fn vboxGlobal
4714 *
4715 * Shortcut to the static VBoxGlobal::instance() method, for convenience.
4716 */
4717
4718
4719/**
4720 * USB Popup Menu class methods
4721 * This class provides the list of USB devices attached to the host.
4722 */
4723VBoxUSBMenu::VBoxUSBMenu (QWidget *aParent) : QMenu (aParent)
4724{
4725 connect (this, SIGNAL (aboutToShow()),
4726 this, SLOT (processAboutToShow()));
4727// connect (this, SIGNAL (hovered (QAction *)),
4728// this, SLOT (processHighlighted (QAction *)));
4729}
4730
4731const CUSBDevice& VBoxUSBMenu::getUSB (QAction *aAction)
4732{
4733 return mUSBDevicesMap [aAction];
4734}
4735
4736void VBoxUSBMenu::setConsole (const CConsole &aConsole)
4737{
4738 mConsole = aConsole;
4739}
4740
4741void VBoxUSBMenu::processAboutToShow()
4742{
4743 clear();
4744 mUSBDevicesMap.clear();
4745
4746 CHost host = vboxGlobal().virtualBox().GetHost();
4747
4748 bool isUSBEmpty = host.GetUSBDevices().GetCount() == 0;
4749 if (isUSBEmpty)
4750 {
4751 QAction *action = addAction (tr ("<no available devices>", "USB devices"));
4752 action->setEnabled (false);
4753 action->setToolTip (tr ("No supported devices connected to the host PC",
4754 "USB device tooltip"));
4755 }
4756 else
4757 {
4758 CHostUSBDeviceEnumerator en = host.GetUSBDevices().Enumerate();
4759 while (en.HasMore())
4760 {
4761 CHostUSBDevice iterator = en.GetNext();
4762 CUSBDevice usb = CUnknown (iterator);
4763 QAction *action = addAction (vboxGlobal().details (usb));
4764 action->setCheckable (true);
4765 mUSBDevicesMap [action] = usb;
4766 /* check if created item was alread attached to this session */
4767 if (!mConsole.isNull())
4768 {
4769 CUSBDevice attachedUSB =
4770 mConsole.GetUSBDevices().FindById (usb.GetId());
4771 action->setChecked (!attachedUSB.isNull());
4772 action->setEnabled (iterator.GetState() !=
4773 KUSBDeviceState_Unavailable);
4774 }
4775 }
4776 }
4777}
4778
4779bool VBoxUSBMenu::event(QEvent *aEvent)
4780{
4781 /* We provide dynamic tooltips for the usb devices */
4782 if (aEvent->type() == QEvent::ToolTip)
4783 {
4784 QHelpEvent *helpEvent = static_cast<QHelpEvent *> (aEvent);
4785 QAction *action = actionAt (helpEvent->pos());
4786 if (action)
4787 {
4788 CUSBDevice usb = mUSBDevicesMap [action];
4789 if (!usb.isNull())
4790 {
4791 QToolTip::showText (helpEvent->globalPos(), vboxGlobal().toolTip (usb));
4792 return true;
4793 }
4794 }
4795 }
4796 return QMenu::event (aEvent);
4797}
4798
4799/**
4800 * Enable/Disable Menu class.
4801 * This class provides enable/disable menu items.
4802 */
4803VBoxSwitchMenu::VBoxSwitchMenu (QWidget *aParent, QAction *aAction,
4804 bool aInverted)
4805 : QMenu (aParent), mAction (aAction), mInverted (aInverted)
4806{
4807 /* this menu works only with toggle action */
4808 Assert (aAction->isCheckable());
4809 addAction(aAction);
4810 connect (this, SIGNAL (aboutToShow()),
4811 this, SLOT (processAboutToShow()));
4812}
4813
4814void VBoxSwitchMenu::setToolTip (const QString &aTip)
4815{
4816 mAction->setToolTip (aTip);
4817}
4818
4819void VBoxSwitchMenu::processAboutToShow()
4820{
4821 QString text = mAction->isChecked() ^ mInverted ? tr ("Disable") : tr ("Enable");
4822 mAction->setText (text);
4823}
4824
Note: See TracBrowser for help on using the repository browser.

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