VirtualBox

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

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

FE/Qt4: Use translated manual of the current selected language if it exists.

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