VirtualBox

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

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

Fe/Qt4: Adding platform information function into VBoxGlobal.

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