VirtualBox

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

Last change on this file since 10167 was 10167, checked in by vboxsync, 17 years ago

Fe/Qt4: New VM&Global settings api based on one default class (+ui).
One interface for both Settings dialog & one interface for settings pages.

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