VirtualBox

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

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

FE/Qt4: Made it build on win.x86.

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