VirtualBox

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

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

Fe/Qt4: 'Nested Paging' VM attribute added to Details Page. Little relayouting of Session Information Dialog to make Details & Runtime pages more proportional and feat the containing data.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 152.9 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.215389.xyz. 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=4 cellpadding=0>%1</table>";
1486 static const char *sSectionHrefTpl =
1487 "<tr><td width=22 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=22 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=40%><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 ("Nested Paging", "details report"), "%9")
1534 + QString (sSectionItemTpl).arg (tr ("PAE/NX", "details report"), "%10");
1535
1536 sGeneralFullHrefTpl = QString (sSectionHrefTpl)
1537 .arg (2 + 10) /* rows */
1538 .arg (":/machine_16px.png", /* icon */
1539 "#general", /* link */
1540 tr ("General", "details report"), /* title */
1541 generalItems); /* items */
1542 sGeneralFullBoldTpl = QString (sSectionBoldTpl)
1543 .arg (2 + 10) /* rows */
1544 .arg (":/machine_16px.png", /* icon */
1545 "#general", /* link */
1546 tr ("General", "details report"), /* title */
1547 generalItems); /* items */
1548 }
1549
1550 /* common generated content */
1551
1552 const QString &sectionTpl = withLinks
1553 ? sSectionHrefTpl
1554 : sSectionBoldTpl;
1555
1556 QString hardDisks;
1557 {
1558 int rows = 2; /* including section header and footer */
1559
1560 CHardDiskAttachmentEnumerator aen = m.GetHardDiskAttachments().Enumerate();
1561 while (aen.HasMore())
1562 {
1563 CHardDiskAttachment hda = aen.GetNext();
1564 CHardDisk hd = hda.GetHardDisk();
1565 /// @todo for the explaination of the below isOk() checks, see
1566 /// @todo *** in #details (const CHardDisk &, bool).
1567 if (hda.isOk())
1568 {
1569 CHardDisk root = hd.GetRoot();
1570 if (hd.isOk())
1571 {
1572 QString src = root.GetLocation();
1573 KStorageBus bus = hda.GetBus();
1574 LONG channel = hda.GetChannel();
1575 LONG device = hda.GetDevice();
1576 hardDisks += QString (sSectionItemTpl)
1577 .arg (toFullString (bus, channel, device))
1578 .arg (QString ("%1 [<nobr>%2</nobr>]")
1579 .arg (prepareFileNameForHTML (src))
1580 .arg (details (hd, isNewVM /* predict */, aDoRefresh)));
1581 ++ rows;
1582 }
1583 }
1584 }
1585
1586 if (hardDisks.isNull())
1587 {
1588 hardDisks = QString (sSectionItemTpl)
1589 .arg (tr ("Not Attached", "details report (HDDs)")).arg ("");
1590 ++ rows;
1591 }
1592
1593 hardDisks = sectionTpl
1594 .arg (rows) /* rows */
1595 .arg (":/hd_16px.png", /* icon */
1596 "#hdds", /* link */
1597 tr ("Hard Disks", "details report"), /* title */
1598 hardDisks); /* items */
1599 }
1600
1601 /* compose details report */
1602
1603 const QString &generalBasicTpl = withLinks
1604 ? sGeneralBasicHrefTpl
1605 : sGeneralBasicBoldTpl;
1606
1607 const QString &generalFullTpl = withLinks
1608 ? sGeneralFullHrefTpl
1609 : sGeneralFullBoldTpl;
1610
1611 QString detailsReport;
1612
1613 if (isNewVM)
1614 {
1615 detailsReport
1616 = generalBasicTpl
1617 .arg (m.GetName())
1618 .arg (vmGuestOSTypeDescription (m.GetOSTypeId()))
1619 .arg (m.GetMemorySize())
1620 + hardDisks;
1621 }
1622 else
1623 {
1624 /* boot order */
1625 QString bootOrder;
1626 for (ulong i = 1; i <= mVBox.GetSystemProperties().GetMaxBootPosition(); i++)
1627 {
1628 KDeviceType device = m.GetBootOrder (i);
1629 if (device == KDeviceType_Null)
1630 continue;
1631 if (!bootOrder.isEmpty())
1632 bootOrder += ", ";
1633 bootOrder += toString (device);
1634 }
1635 if (bootOrder.isEmpty())
1636 bootOrder = toString (KDeviceType_Null);
1637
1638 CBIOSSettings biosSettings = m.GetBIOSSettings();
1639
1640 /* ACPI */
1641 QString acpi = biosSettings.GetACPIEnabled()
1642 ? tr ("Enabled", "details report (ACPI)")
1643 : tr ("Disabled", "details report (ACPI)");
1644
1645 /* IO APIC */
1646 QString ioapic = biosSettings.GetIOAPICEnabled()
1647 ? tr ("Enabled", "details report (IO APIC)")
1648 : tr ("Disabled", "details report (IO APIC)");
1649
1650 /* VT-x/AMD-V */
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 /* Nested paging status */
1656 QString nested = m.GetHWVirtExNestedPagingEnabled() ?
1657 tr ("Enabled", "details report (Nested Paging)") :
1658 tr ("Disabled", "details report (Nested Paging)");
1659
1660 /* PAE/NX */
1661 QString pae = m.GetPAEEnabled()
1662 ? tr ("Enabled", "details report (PAE/NX)")
1663 : tr ("Disabled", "details report (PAE/NX)");
1664
1665 /* General + Hard Disks */
1666 detailsReport
1667 = generalFullTpl
1668 .arg (m.GetName())
1669 .arg (vmGuestOSTypeDescription (m.GetOSTypeId()))
1670 .arg (m.GetMemorySize())
1671 .arg (m.GetVRAMSize())
1672 .arg (bootOrder)
1673 .arg (acpi)
1674 .arg (ioapic)
1675 .arg (virt)
1676 .arg (nested)
1677 .arg (pae)
1678 + hardDisks;
1679
1680 QString item;
1681
1682 /* DVD */
1683 CDVDDrive dvd = m.GetDVDDrive();
1684 item = QString (sSectionItemTpl);
1685 switch (dvd.GetState())
1686 {
1687 case KDriveState_NotMounted:
1688 item = item.arg (tr ("Not mounted", "details report (DVD)"), "");
1689 break;
1690 case KDriveState_ImageMounted:
1691 {
1692 CDVDImage img = dvd.GetImage();
1693 item = item.arg (tr ("Image", "details report (DVD)"),
1694 prepareFileNameForHTML (img.GetFilePath()));
1695 break;
1696 }
1697 case KDriveState_HostDriveCaptured:
1698 {
1699 CHostDVDDrive drv = dvd.GetHostDrive();
1700 QString drvName = drv.GetName();
1701 QString description = drv.GetDescription();
1702 QString fullName = description.isEmpty() ?
1703 drvName :
1704 QString ("%1 (%2)").arg (description, drvName);
1705 item = item.arg (tr ("Host Drive", "details report (DVD)"),
1706 fullName);
1707 break;
1708 }
1709 default:
1710 AssertMsgFailed (("Invalid DVD state: %d", dvd.GetState()));
1711 }
1712 detailsReport += sectionTpl
1713 .arg (2 + 1) /* rows */
1714 .arg (":/cd_16px.png", /* icon */
1715 "#dvd", /* link */
1716 tr ("CD/DVD-ROM", "details report"), /* title */
1717 item); // items
1718
1719 /* Floppy */
1720 CFloppyDrive floppy = m.GetFloppyDrive();
1721 item = QString (sSectionItemTpl);
1722 switch (floppy.GetState())
1723 {
1724 case KDriveState_NotMounted:
1725 item = item.arg (tr ("Not mounted", "details report (floppy)"), "");
1726 break;
1727 case KDriveState_ImageMounted:
1728 {
1729 CFloppyImage img = floppy.GetImage();
1730 item = item.arg (tr ("Image", "details report (floppy)"),
1731 prepareFileNameForHTML (img.GetFilePath()));
1732 break;
1733 }
1734 case KDriveState_HostDriveCaptured:
1735 {
1736 CHostFloppyDrive drv = floppy.GetHostDrive();
1737 QString drvName = drv.GetName();
1738 QString description = drv.GetDescription();
1739 QString fullName = description.isEmpty() ?
1740 drvName :
1741 QString ("%1 (%2)").arg (description, drvName);
1742 item = item.arg (tr ("Host Drive", "details report (floppy)"),
1743 fullName);
1744 break;
1745 }
1746 default:
1747 AssertMsgFailed (("Invalid floppy state: %d", floppy.GetState()));
1748 }
1749 detailsReport += sectionTpl
1750 .arg (2 + 1) /* rows */
1751 .arg (":/fd_16px.png", /* icon */
1752 "#floppy", /* link */
1753 tr ("Floppy", "details report"), /* title */
1754 item); /* items */
1755
1756 /* audio */
1757 {
1758 CAudioAdapter audio = m.GetAudioAdapter();
1759 int rows = audio.GetEnabled() ? 3 : 2;
1760 if (audio.GetEnabled())
1761 item = QString (sSectionItemTpl)
1762 .arg (tr ("Host Driver", "details report (audio)"),
1763 toString (audio.GetAudioDriver())) +
1764 QString (sSectionItemTpl)
1765 .arg (tr ("Controller", "details report (audio)"),
1766 toString (audio.GetAudioController()));
1767 else
1768 item = QString (sSectionItemTpl)
1769 .arg (tr ("Disabled", "details report (audio)"), "");
1770
1771 detailsReport += sectionTpl
1772 .arg (rows + 1) /* rows */
1773 .arg (":/sound_16px.png", /* icon */
1774 "#audio", /* link */
1775 tr ("Audio", "details report"), /* title */
1776 item); /* items */
1777 }
1778 /* network */
1779 {
1780 item = QString::null;
1781 ulong count = mVBox.GetSystemProperties().GetNetworkAdapterCount();
1782 int rows = 2; /* including section header and footer */
1783 for (ulong slot = 0; slot < count; slot ++)
1784 {
1785 CNetworkAdapter adapter = m.GetNetworkAdapter (slot);
1786 if (adapter.GetEnabled())
1787 {
1788 KNetworkAttachmentType type = adapter.GetAttachmentType();
1789 QString attType = toString (adapter.GetAdapterType())
1790 .replace (QRegExp ("\\s\\(.+\\)"), " (%1)");
1791 /* don't use the adapter type string for types that have
1792 * an additional symbolic network/interface name field, use
1793 * this name instead */
1794 if (type == KNetworkAttachmentType_HostInterface)
1795 attType = attType.arg (tr ("host interface, %1",
1796 "details report (network)").arg (adapter.GetHostInterface()));
1797 else if (type == KNetworkAttachmentType_Internal)
1798 attType = attType.arg (tr ("internal network, '%1'",
1799 "details report (network)").arg (adapter.GetInternalNetwork()));
1800 else
1801 attType = attType.arg (vboxGlobal().toString (type));
1802
1803 item += QString (sSectionItemTpl)
1804 .arg (tr ("Adapter %1", "details report (network)")
1805 .arg (adapter.GetSlot()))
1806 .arg (attType);
1807 ++ rows;
1808 }
1809 }
1810 if (item.isNull())
1811 {
1812 item = QString (sSectionItemTpl)
1813 .arg (tr ("Disabled", "details report (network)"), "");
1814 ++ rows;
1815 }
1816
1817 detailsReport += sectionTpl
1818 .arg (rows) /* rows */
1819 .arg (":/nw_16px.png", /* icon */
1820 "#network", /* link */
1821 tr ("Network", "details report"), /* title */
1822 item); /* items */
1823 }
1824 /* serial ports */
1825 {
1826 item = QString::null;
1827 ulong count = mVBox.GetSystemProperties().GetSerialPortCount();
1828 int rows = 2; /* including section header and footer */
1829 for (ulong slot = 0; slot < count; slot ++)
1830 {
1831 CSerialPort port = m.GetSerialPort (slot);
1832 if (port.GetEnabled())
1833 {
1834 KPortMode mode = port.GetHostMode();
1835 QString data =
1836 toCOMPortName (port.GetIRQ(), port.GetIOBase()) + ", ";
1837 if (mode == KPortMode_HostPipe ||
1838 mode == KPortMode_HostDevice)
1839 data += QString ("%1 (<nobr>%2</nobr>)")
1840 .arg (vboxGlobal().toString (mode))
1841 .arg (QDir::convertSeparators (port.GetPath()));
1842 else
1843 data += toString (mode);
1844
1845 item += QString (sSectionItemTpl)
1846 .arg (tr ("Port %1", "details report (serial ports)")
1847 .arg (port.GetSlot()))
1848 .arg (data);
1849 ++ rows;
1850 }
1851 }
1852 if (item.isNull())
1853 {
1854 item = QString (sSectionItemTpl)
1855 .arg (tr ("Disabled", "details report (serial ports)"), "");
1856 ++ rows;
1857 }
1858
1859 detailsReport += sectionTpl
1860 .arg (rows) /* rows */
1861 .arg (":/serial_port_16px.png", /* icon */
1862 "#serialPorts", /* link */
1863 tr ("Serial Ports", "details report"), /* title */
1864 item); /* items */
1865 }
1866 /* parallel ports */
1867 {
1868 item = QString::null;
1869 ulong count = mVBox.GetSystemProperties().GetParallelPortCount();
1870 int rows = 2; /* including section header and footer */
1871 for (ulong slot = 0; slot < count; slot ++)
1872 {
1873 CParallelPort port = m.GetParallelPort (slot);
1874 if (port.GetEnabled())
1875 {
1876 QString data =
1877 toLPTPortName (port.GetIRQ(), port.GetIOBase()) +
1878 QString (" (<nobr>%1</nobr>)")
1879 .arg (QDir::convertSeparators (port.GetPath()));
1880
1881 item += QString (sSectionItemTpl)
1882 .arg (tr ("Port %1", "details report (parallel ports)")
1883 .arg (port.GetSlot()))
1884 .arg (data);
1885 ++ rows;
1886 }
1887 }
1888 if (item.isNull())
1889 {
1890 item = QString (sSectionItemTpl)
1891 .arg (tr ("Disabled", "details report (parallel ports)"), "");
1892 ++ rows;
1893 }
1894
1895 /* Temporary disabled */
1896 QString dummy = sectionTpl /* detailsReport += sectionTpl */
1897 .arg (rows) /* rows */
1898 .arg (":/parallel_port_16px.png", /* icon */
1899 "#parallelPorts", /* link */
1900 tr ("Parallel Ports", "details report"), /* title */
1901 item); /* items */
1902 }
1903 /* USB */
1904 {
1905 CUSBController ctl = m.GetUSBController();
1906 if (!ctl.isNull())
1907 {
1908 /* the USB controller may be unavailable (i.e. in VirtualBox OSE) */
1909
1910 if (ctl.GetEnabled())
1911 {
1912 CUSBDeviceFilterCollection coll = ctl.GetDeviceFilters();
1913 CUSBDeviceFilterEnumerator en = coll.Enumerate();
1914 uint active = 0;
1915 while (en.HasMore())
1916 if (en.GetNext().GetActive())
1917 active ++;
1918
1919 item = QString (sSectionItemTpl)
1920 .arg (tr ("Device Filters", "details report (USB)"),
1921 tr ("%1 (%2 active)", "details report (USB)")
1922 .arg (coll.GetCount()).arg (active));
1923 }
1924 else
1925 item = QString (sSectionItemTpl)
1926 .arg (tr ("Disabled", "details report (USB)"), "");
1927
1928 detailsReport += sectionTpl
1929 .arg (2 + 1) /* rows */
1930 .arg (":/usb_16px.png", /* icon */
1931 "#usb", /* link */
1932 tr ("USB", "details report"), /* title */
1933 item); /* items */
1934 }
1935 }
1936 /* Shared folders */
1937 {
1938 ulong count = m.GetSharedFolders().GetCount();
1939 if (count > 0)
1940 {
1941 item = QString (sSectionItemTpl)
1942 .arg (tr ("Shared Folders", "details report (shared folders)"),
1943 tr ("%1", "details report (shadef folders)")
1944 .arg (count));
1945 }
1946 else
1947 item = QString (sSectionItemTpl)
1948 .arg (tr ("None", "details report (shared folders)"), "");
1949
1950 detailsReport += sectionTpl
1951 .arg (2 + 1) /* rows */
1952 .arg (":/shared_folder_16px.png", /* icon */
1953 "#sfolders", /* link */
1954 tr ("Shared Folders", "details report"), /* title */
1955 item); /* items */
1956 }
1957 /* VRDP */
1958 {
1959 CVRDPServer srv = m.GetVRDPServer();
1960 if (!srv.isNull())
1961 {
1962 /* the VRDP server may be unavailable (i.e. in VirtualBox OSE) */
1963
1964 if (srv.GetEnabled())
1965 item = QString (sSectionItemTpl)
1966 .arg (tr ("VRDP Server Port", "details report (VRDP)"),
1967 tr ("%1", "details report (VRDP)")
1968 .arg (srv.GetPort()));
1969 else
1970 item = QString (sSectionItemTpl)
1971 .arg (tr ("Disabled", "details report (VRDP)"), "");
1972
1973 detailsReport += sectionTpl
1974 .arg (2 + 1) /* rows */
1975 .arg (":/vrdp_16px.png", /* icon */
1976 "#vrdp", /* link */
1977 tr ("Remote Display", "details report"), /* title */
1978 item); /* items */
1979 }
1980 }
1981 }
1982
1983 return QString (sTableTpl). arg (detailsReport);
1984}
1985
1986QString VBoxGlobal::platformInfo()
1987{
1988 QString platform;
1989
1990#if defined (Q_OS_WIN)
1991 platform = "win";
1992#elif defined (Q_OS_LINUX)
1993 platform = "linux";
1994#elif defined (Q_OS_MACX)
1995 platform = "macosx";
1996#elif defined (Q_OS_OS2)
1997 platform = "os2";
1998#elif defined (Q_OS_FREEBSD)
1999 platform = "freebsd";
2000#elif defined (Q_OS_SOLARIS)
2001 platform = "solaris";
2002#else
2003 platform = "unknown";
2004#endif
2005
2006 /* The format is <system>.<bitness> */
2007 platform += QString (".%1").arg (ARCH_BITS);
2008
2009 /* Add more system information */
2010#if defined (Q_OS_WIN)
2011 OSVERSIONINFO versionInfo;
2012 ZeroMemory (&versionInfo, sizeof (OSVERSIONINFO));
2013 versionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
2014 GetVersionEx (&versionInfo);
2015 int major = versionInfo.dwMajorVersion;
2016 int minor = versionInfo.dwMinorVersion;
2017 int build = versionInfo.dwBuildNumber;
2018 QString sp = QString::fromUtf16 ((ushort*)versionInfo.szCSDVersion);
2019
2020 QString distrib;
2021 if (major == 6)
2022 distrib = QString ("Windows Vista %1");
2023 else if (major == 5)
2024 {
2025 if (minor == 2)
2026 distrib = QString ("Windows Server 2003 %1");
2027 else if (minor == 1)
2028 distrib = QString ("Windows XP %1");
2029 else if (minor == 0)
2030 distrib = QString ("Windows 2000 %1");
2031 else
2032 distrib = QString ("Unknown %1");
2033 }
2034 else if (major == 4)
2035 {
2036 if (minor == 90)
2037 distrib = QString ("Windows Me %1");
2038 else if (minor == 10)
2039 distrib = QString ("Windows 98 %1");
2040 else if (minor == 0)
2041 distrib = QString ("Windows 95 %1");
2042 else
2043 distrib = QString ("Unknown %1");
2044 }
2045 else
2046 distrib = QString ("Unknown %1");
2047 distrib = distrib.arg (sp);
2048 QString version = QString ("%1.%2").arg (major).arg (minor);
2049 QString kernel = QString ("%1").arg (build);
2050 platform += QString (" [Distribution: %1 | Version: %2 | Build: %3]")
2051 .arg (distrib).arg (version).arg (kernel);
2052#elif defined (Q_OS_OS2)
2053 // TODO: add sys info for os2 if any...
2054#elif defined (Q_OS_LINUX) || defined (Q_OS_MACX) || defined (Q_OS_FREEBSD) || defined (Q_OS_SOLARIS)
2055 /* Get script path */
2056 char szAppPrivPath [RTPATH_MAX];
2057 int rc = RTPathAppPrivateNoArch (szAppPrivPath, sizeof (szAppPrivPath)); NOREF(rc);
2058 Assert (RT_SUCCESS (rc));
2059 /* Run script */
2060 QByteArray result =
2061 Process::singleShot (QString (szAppPrivPath) + "/VBoxSysInfo.sh");
2062 if (!result.isNull())
2063 platform += QString (" [%1]").arg (QString (result).trimmed());
2064#endif
2065
2066 return platform;
2067}
2068
2069#if defined(Q_WS_X11) && !defined(VBOX_OSE)
2070bool VBoxGlobal::showVirtualBoxLicense()
2071{
2072 /* get the apps doc path */
2073 int size = 256;
2074 char *buffer = (char*) RTMemTmpAlloc (size);
2075 RTPathAppDocs (buffer, size);
2076 QString path (buffer);
2077 RTMemTmpFree (buffer);
2078 QDir docDir (path);
2079 docDir.setFilter (QDir::Files);
2080 docDir.setNameFilters (QStringList ("License-*.html"));
2081
2082 /* get the license files list and search for the latest license */
2083 QStringList filesList = docDir.entryList();
2084 double maxVersionNumber = 0;
2085 for (int index = 0; index < filesList.count(); ++ index)
2086 {
2087 QRegExp regExp ("License-([\\d\\.]+).html");
2088 regExp.indexIn (filesList [index]);
2089 QString version = regExp.cap (1);
2090 if (maxVersionNumber < version.toDouble())
2091 maxVersionNumber = version.toDouble();
2092 }
2093 if (!maxVersionNumber)
2094 {
2095 vboxProblem().cannotFindLicenseFiles (path);
2096 return false;
2097 }
2098
2099 /* compose the latest license file full path */
2100 QString latestVersion = QString::number (maxVersionNumber);
2101 QString latestFilePath = docDir.absoluteFilePath (
2102 QString ("License-%1.html").arg (latestVersion));
2103
2104 /* check for the agreed license version */
2105 QString licenseAgreed = virtualBox().GetExtraData (VBoxDefs::GUI_LicenseKey);
2106 if (licenseAgreed == latestVersion)
2107 return true;
2108
2109 VBoxLicenseViewer licenseDialog (latestFilePath);
2110 bool result = licenseDialog.exec() == QDialog::Accepted;
2111 if (result)
2112 virtualBox().SetExtraData (VBoxDefs::GUI_LicenseKey, latestVersion);
2113 return result;
2114}
2115#endif /* defined(Q_WS_X11) && !defined(VBOX_OSE) */
2116
2117/**
2118 * Checks if any of the settings files were auto-converted and informs the user
2119 * if so.
2120 */
2121void VBoxGlobal::checkForAutoConvertedSettings()
2122{
2123 QString formatVersion = mVBox.GetSettingsFormatVersion();
2124
2125 bool isGlobalConverted = false;
2126 QList <CMachine> machines;
2127 QString fileList;
2128 QString version;
2129
2130 CMachineVector vec = mVBox.GetMachines2();
2131 for (CMachineVector::ConstIterator m = vec.begin();
2132 m != vec.end(); ++ m)
2133 {
2134 if (!m->GetAccessible())
2135 continue;
2136
2137 version = m->GetSettingsFileVersion();
2138 if (version != formatVersion)
2139 {
2140 machines.append (*m);
2141 fileList += QString ("<nobr>%1&nbsp;&nbsp;&nbsp;(<i>%2</i>)</nobr><br>")
2142 .arg (m->GetSettingsFilePath())
2143 .arg (version);
2144 }
2145 }
2146
2147 version = mVBox.GetSettingsFileVersion();
2148 if (version != formatVersion)
2149 {
2150 isGlobalConverted = true;
2151 fileList += QString ("<nobr>%1&nbsp;&nbsp;&nbsp;(<i>%2</i>)</nobr><br>")
2152 .arg (mVBox.GetSettingsFilePath())
2153 .arg (version);
2154 }
2155
2156
2157 if (!fileList.isNull())
2158 {
2159 int rc = vboxProblem()
2160 .warnAboutAutoConvertedSettings (formatVersion, fileList);
2161
2162 if (rc == QIMessageBox::No || rc == QIMessageBox::Yes)
2163 {
2164 /* backup (optionally) and save all settings files
2165 * (QIMessageBox::No = Backup, QIMessageBox::Yes = Save) */
2166
2167 foreach (CMachine m, machines)
2168 {
2169 CSession session = openSession (m.GetId());
2170 if (!session.isNull())
2171 {
2172 CMachine sm = session.GetMachine();
2173 if (rc == QIMessageBox::No)
2174 sm.SaveSettingsWithBackup();
2175 else
2176 sm.SaveSettings();
2177
2178 if (!sm.isOk())
2179 vboxProblem().cannotSaveMachineSettings (sm);
2180 session.Close();
2181 }
2182 }
2183
2184 if (isGlobalConverted)
2185 {
2186 if (rc == QIMessageBox::No)
2187 mVBox.SaveSettingsWithBackup();
2188 else
2189 mVBox.SaveSettings();
2190
2191 if (!mVBox.isOk())
2192 vboxProblem().cannotSaveGlobalSettings (mVBox);
2193 }
2194 }
2195 }
2196}
2197
2198/**
2199 * Opens a direct session for a machine with the given ID.
2200 * This method does user-friendly error handling (display error messages, etc.).
2201 * and returns a null CSession object in case of any error.
2202 * If this method succeeds, don't forget to close the returned session when
2203 * it is no more necessary.
2204 *
2205 * @param aId Machine ID.
2206 * @param aExisting @c true to open an existing session with the machine
2207 * which is already running, @c false to open a new direct
2208 * session.
2209 */
2210CSession VBoxGlobal::openSession (const QUuid &aId, bool aExisting /* = false */)
2211{
2212 CSession session;
2213 session.createInstance (CLSID_Session);
2214 if (session.isNull())
2215 {
2216 vboxProblem().cannotOpenSession (session);
2217 return session;
2218 }
2219
2220 aExisting ? mVBox.OpenExistingSession (session, aId) :
2221 mVBox.OpenSession (session, aId);
2222
2223 if (!mVBox.isOk())
2224 {
2225 CMachine machine = CVirtualBox (mVBox).GetMachine (aId);
2226 vboxProblem().cannotOpenSession (mVBox, machine);
2227 session.detach();
2228 }
2229
2230 return session;
2231}
2232
2233/**
2234 * Starts a machine with the given ID.
2235 */
2236bool VBoxGlobal::startMachine (const QUuid &id)
2237{
2238 AssertReturn (mValid, false);
2239
2240 CSession session = vboxGlobal().openSession (id);
2241 if (session.isNull())
2242 return false;
2243
2244 return consoleWnd().openView (session);
2245}
2246
2247/**
2248 * Appends the disk object and all its children to the media list.
2249 */
2250static
2251void addMediaToList (VBoxMediaList &aList,
2252 const CUnknown &aDisk,
2253 VBoxDefs::DiskType aType)
2254{
2255 VBoxMedia media (aDisk, aType, VBoxMedia::Unknown);
2256 aList += media;
2257 /* append all vdi children */
2258 if (aType == VBoxDefs::HD)
2259 {
2260 CHardDisk hd = aDisk;
2261 CHardDiskEnumerator enumerator = hd.GetChildren().Enumerate();
2262 while (enumerator.HasMore())
2263 {
2264 CHardDisk subHd = enumerator.GetNext();
2265 addMediaToList (aList, CUnknown (subHd), VBoxDefs::HD);
2266 }
2267 }
2268}
2269
2270/**
2271 * Starts a thread that asynchronously enumerates all currently registered
2272 * media, checks for its accessibility and posts VBoxEnumerateMediaEvent
2273 * events to the VBoxGlobal object until all media is enumerated.
2274 *
2275 * If the enumeration is already in progress, no new thread is started.
2276 *
2277 * @sa #currentMediaList()
2278 * @sa #isMediaEnumerationStarted()
2279 */
2280void VBoxGlobal::startEnumeratingMedia()
2281{
2282 Assert (mValid);
2283
2284 /* check if already started but not yet finished */
2285 if (media_enum_thread)
2286 return;
2287
2288 /* ignore the request during application termination */
2289 if (sVBoxGlobalInCleanup)
2290 return;
2291
2292 /* composes a list of all currently known media & their children */
2293 media_list.clear();
2294 {
2295 CHardDiskEnumerator enHD = mVBox.GetHardDisks().Enumerate();
2296 while (enHD.HasMore())
2297 addMediaToList (media_list, CUnknown (enHD.GetNext()), VBoxDefs::HD);
2298
2299 CDVDImageEnumerator enCD = mVBox.GetDVDImages().Enumerate();
2300 while (enCD.HasMore())
2301 addMediaToList (media_list, CUnknown (enCD.GetNext()), VBoxDefs::CD);
2302
2303 CFloppyImageEnumerator enFD = mVBox.GetFloppyImages().Enumerate();
2304 while (enFD.HasMore())
2305 addMediaToList (media_list, CUnknown (enFD.GetNext()), VBoxDefs::FD);
2306 }
2307
2308 /* enumeration thread class */
2309 class Thread : public QThread
2310 {
2311 public:
2312
2313 Thread (const VBoxMediaList &aList) : mList (aList) {}
2314
2315 virtual void run()
2316 {
2317 LogFlow (("MediaEnumThread started.\n"));
2318 COMBase::InitializeCOM();
2319
2320 CVirtualBox mVBox = vboxGlobal().virtualBox();
2321 QObject *target = &vboxGlobal();
2322
2323 /* enumerating list */
2324 int index = 0;
2325 VBoxMediaList::const_iterator it;
2326 for (it = mList.begin();
2327 it != mList.end() && !sVBoxGlobalInCleanup;
2328 ++ it, ++ index)
2329 {
2330 VBoxMedia media = *it;
2331 switch (media.type)
2332 {
2333 case VBoxDefs::HD:
2334 {
2335 CHardDisk hd = media.disk;
2336 media.status =
2337 hd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2338 hd.isOk() ? VBoxMedia::Inaccessible :
2339 VBoxMedia::Error;
2340 /* assign back to store error info if any */
2341 media.disk = hd;
2342 if (media.status == VBoxMedia::Inaccessible)
2343 {
2344 QUuid machineId = hd.GetMachineId();
2345 if (!machineId.isNull())
2346 {
2347 CMachine machine = mVBox.GetMachine (machineId);
2348 if (!machine.isNull() && (machine.GetState() >= KMachineState_Running))
2349 media.status = VBoxMedia::Ok;
2350 }
2351 }
2352 QApplication::postEvent (target,
2353 new VBoxEnumerateMediaEvent (media, index));
2354 break;
2355 }
2356 case VBoxDefs::CD:
2357 {
2358 CDVDImage cd = media.disk;
2359 media.status =
2360 cd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2361 cd.isOk() ? VBoxMedia::Inaccessible :
2362 VBoxMedia::Error;
2363 /* assign back to store error info if any */
2364 media.disk = cd;
2365 QApplication::postEvent (target,
2366 new VBoxEnumerateMediaEvent (media, index));
2367 break;
2368 }
2369 case VBoxDefs::FD:
2370 {
2371 CFloppyImage fd = media.disk;
2372 media.status =
2373 fd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2374 fd.isOk() ? VBoxMedia::Inaccessible :
2375 VBoxMedia::Error;
2376 /* assign back to store error info if any */
2377 media.disk = fd;
2378 QApplication::postEvent (target,
2379 new VBoxEnumerateMediaEvent (media, index));
2380 break;
2381 }
2382 default:
2383 {
2384 AssertMsgFailed (("Invalid aMedia type\n"));
2385 break;
2386 }
2387 }
2388 }
2389
2390 /* post the last message to indicate the end of enumeration */
2391 if (!sVBoxGlobalInCleanup)
2392 QApplication::postEvent (target, new VBoxEnumerateMediaEvent());
2393
2394 COMBase::CleanupCOM();
2395 LogFlow (("MediaEnumThread finished.\n"));
2396 }
2397
2398 private:
2399
2400 const VBoxMediaList &mList;
2401 };
2402
2403 media_enum_thread = new Thread (media_list);
2404 AssertReturnVoid (media_enum_thread);
2405
2406 /* emit mediaEnumStarted() after we set media_enum_thread to != NULL
2407 * to cause isMediaEnumerationStarted() to return TRUE from slots */
2408 emit mediaEnumStarted();
2409
2410 media_enum_thread->start();
2411}
2412
2413/**
2414 * Adds a new media to the current media list.
2415 * @note Currently, this method does nothing but emits the mediaAdded() signal.
2416 * Later, it will be used to synchronize the current media list with
2417 * the actial media list on the server after a single media opetartion
2418 * performed from within one of our UIs.
2419 * @sa #currentMediaList()
2420 */
2421void VBoxGlobal::addMedia (const VBoxMedia &aMedia)
2422{
2423 emit mediaAdded (aMedia);
2424}
2425
2426/**
2427 * Updates the media in the current media list.
2428 * @note Currently, this method does nothing but emits the mediaUpdated() signal.
2429 * Later, it will be used to synchronize the current media list with
2430 * the actial media list on the server after a single media opetartion
2431 * performed from within one of our UIs.
2432 * @sa #currentMediaList()
2433 */
2434void VBoxGlobal::updateMedia (const VBoxMedia &aMedia)
2435{
2436 emit mediaUpdated (aMedia);
2437}
2438
2439/**
2440 * Removes the media from the current media list.
2441 * @note Currently, this method does nothing but emits the mediaRemoved() signal.
2442 * Later, it will be used to synchronize the current media list with
2443 * the actial media list on the server after a single media opetartion
2444 * performed from within one of our UIs.
2445 * @sa #currentMediaList()
2446 */
2447void VBoxGlobal::removeMedia (VBoxDefs::DiskType aType, const QUuid &aId)
2448{
2449 emit mediaRemoved (aType, aId);
2450}
2451
2452/**
2453 * Searches for a VBoxMedia object representing the given COM media object.
2454 *
2455 * @return true if found and false otherwise.
2456 */
2457bool VBoxGlobal::findMedia (const CUnknown &aObj, VBoxMedia &aMedia) const
2458{
2459 for (VBoxMediaList::ConstIterator it = media_list.begin();
2460 it != media_list.end(); ++ it)
2461 {
2462 if ((*it).disk == aObj)
2463 {
2464 aMedia = (*it);
2465 return true;
2466 }
2467 }
2468
2469 return false;
2470}
2471
2472/**
2473 * Native language name of the currently installed translation.
2474 * Returns "English" if no translation is installed
2475 * or if the translation file is invalid.
2476 */
2477QString VBoxGlobal::languageName() const
2478{
2479
2480 return qApp->translate ("@@@", "English",
2481 "Native language name");
2482}
2483
2484/**
2485 * Native language country name of the currently installed translation.
2486 * Returns "--" if no translation is installed or if the translation file is
2487 * invalid, or if the language is independent on the country.
2488 */
2489QString VBoxGlobal::languageCountry() const
2490{
2491 return qApp->translate ("@@@", "--",
2492 "Native language country name "
2493 "(empty if this language is for all countries)");
2494}
2495
2496/**
2497 * Language name of the currently installed translation, in English.
2498 * Returns "English" if no translation is installed
2499 * or if the translation file is invalid.
2500 */
2501QString VBoxGlobal::languageNameEnglish() const
2502{
2503
2504 return qApp->translate ("@@@", "English",
2505 "Language name, in English");
2506}
2507
2508/**
2509 * Language country name of the currently installed translation, in English.
2510 * Returns "--" if no translation is installed or if the translation file is
2511 * invalid, or if the language is independent on the country.
2512 */
2513QString VBoxGlobal::languageCountryEnglish() const
2514{
2515 return qApp->translate ("@@@", "--",
2516 "Language country name, in English "
2517 "(empty if native country name is empty)");
2518}
2519
2520/**
2521 * Comma-separated list of authors of the currently installed translation.
2522 * Returns "Sun Microsystems, Inc." if no translation is installed or if the
2523 * translation file is invalid, or if the translation is supplied by Sun
2524 * Microsystems, inc.
2525 */
2526QString VBoxGlobal::languageTranslators() const
2527{
2528 return qApp->translate ("@@@", "Sun Microsystems, Inc.",
2529 "Comma-separated list of translators");
2530}
2531
2532/**
2533 * Changes the language of all global string constants according to the
2534 * currently installed translations tables.
2535 */
2536void VBoxGlobal::retranslateUi()
2537{
2538 machineStates [KMachineState_PoweredOff] = tr ("Powered Off", "MachineState");
2539 machineStates [KMachineState_Saved] = tr ("Saved", "MachineState");
2540 machineStates [KMachineState_Aborted] = tr ("Aborted", "MachineState");
2541 machineStates [KMachineState_Running] = tr ("Running", "MachineState");
2542 machineStates [KMachineState_Paused] = tr ("Paused", "MachineState");
2543 machineStates [KMachineState_Stuck] = tr ("Stuck", "MachineState");
2544 machineStates [KMachineState_Starting] = tr ("Starting", "MachineState");
2545 machineStates [KMachineState_Stopping] = tr ("Stopping", "MachineState");
2546 machineStates [KMachineState_Saving] = tr ("Saving", "MachineState");
2547 machineStates [KMachineState_Restoring] = tr ("Restoring", "MachineState");
2548 machineStates [KMachineState_Discarding] = tr ("Discarding", "MachineState");
2549
2550 sessionStates [KSessionState_Closed] = tr ("Closed", "SessionState");
2551 sessionStates [KSessionState_Open] = tr ("Open", "SessionState");
2552 sessionStates [KSessionState_Spawning] = tr ("Spawning", "SessionState");
2553 sessionStates [KSessionState_Closing] = tr ("Closing", "SessionState");
2554
2555 deviceTypes [KDeviceType_Null] = tr ("None", "DeviceType");
2556 deviceTypes [KDeviceType_Floppy] = tr ("Floppy", "DeviceType");
2557 deviceTypes [KDeviceType_DVD] = tr ("CD/DVD-ROM", "DeviceType");
2558 deviceTypes [KDeviceType_HardDisk] = tr ("Hard Disk", "DeviceType");
2559 deviceTypes [KDeviceType_Network] = tr ("Network", "DeviceType");
2560 deviceTypes [KDeviceType_USB] = tr ("USB", "DeviceType");
2561 deviceTypes [KDeviceType_SharedFolder] = tr ("Shared Folder", "DeviceType");
2562
2563 storageBuses [KStorageBus_IDE] =
2564 tr ("IDE", "StorageBus");
2565 storageBuses [KStorageBus_SATA] =
2566 tr ("SATA", "StorageBus");
2567
2568 Assert (storageBusChannels.count() == 3);
2569 storageBusChannels [0] =
2570 tr ("Primary", "StorageBusChannel");
2571 storageBusChannels [1] =
2572 tr ("Secondary", "StorageBusChannel");
2573 storageBusChannels [2] =
2574 tr ("Port %1", "StorageBusChannel");
2575
2576 Assert (storageBusDevices.count() == 2);
2577 storageBusDevices [0] = tr ("Master", "StorageBusDevice");
2578 storageBusDevices [1] = tr ("Slave", "StorageBusDevice");
2579
2580 diskTypes [KHardDiskType_Normal] =
2581 tr ("Normal", "DiskType");
2582 diskTypes [KHardDiskType_Immutable] =
2583 tr ("Immutable", "DiskType");
2584 diskTypes [KHardDiskType_Writethrough] =
2585 tr ("Writethrough", "DiskType");
2586
2587 diskStorageTypes [KHardDiskStorageType_VirtualDiskImage] =
2588 tr ("Virtual Disk Image", "DiskStorageType");
2589 diskStorageTypes [KHardDiskStorageType_ISCSIHardDisk] =
2590 tr ("iSCSI", "DiskStorageType");
2591 diskStorageTypes [KHardDiskStorageType_VMDKImage] =
2592 tr ("VMDK Image", "DiskStorageType");
2593 diskStorageTypes [KHardDiskStorageType_CustomHardDisk] =
2594 tr ("Custom Hard Disk", "DiskStorageType");
2595 diskStorageTypes [KHardDiskStorageType_VHDImage] =
2596 tr ("VHD Image", "DiskStorageType");
2597
2598 vrdpAuthTypes [KVRDPAuthType_Null] =
2599 tr ("Null", "VRDPAuthType");
2600 vrdpAuthTypes [KVRDPAuthType_External] =
2601 tr ("External", "VRDPAuthType");
2602 vrdpAuthTypes [KVRDPAuthType_Guest] =
2603 tr ("Guest", "VRDPAuthType");
2604
2605 portModeTypes [KPortMode_Disconnected] =
2606 tr ("Disconnected", "PortMode");
2607 portModeTypes [KPortMode_HostPipe] =
2608 tr ("Host Pipe", "PortMode");
2609 portModeTypes [KPortMode_HostDevice] =
2610 tr ("Host Device", "PortMode");
2611
2612 usbFilterActionTypes [KUSBDeviceFilterAction_Ignore] =
2613 tr ("Ignore", "USBFilterActionType");
2614 usbFilterActionTypes [KUSBDeviceFilterAction_Hold] =
2615 tr ("Hold", "USBFilterActionType");
2616
2617 audioDriverTypes [KAudioDriverType_Null] =
2618 tr ("Null Audio Driver", "AudioDriverType");
2619 audioDriverTypes [KAudioDriverType_WinMM] =
2620 tr ("Windows Multimedia", "AudioDriverType");
2621 audioDriverTypes [KAudioDriverType_SolAudio] =
2622 tr ("Solaris Audio", "AudioDriverType");
2623 audioDriverTypes [KAudioDriverType_OSS] =
2624 tr ("OSS Audio Driver", "AudioDriverType");
2625 audioDriverTypes [KAudioDriverType_ALSA] =
2626 tr ("ALSA Audio Driver", "AudioDriverType");
2627 audioDriverTypes [KAudioDriverType_DirectSound] =
2628 tr ("Windows DirectSound", "AudioDriverType");
2629 audioDriverTypes [KAudioDriverType_CoreAudio] =
2630 tr ("CoreAudio", "AudioDriverType");
2631 audioDriverTypes [KAudioDriverType_Pulse] =
2632 tr ("PulseAudio", "AudioDriverType");
2633
2634 audioControllerTypes [KAudioControllerType_AC97] =
2635 tr ("ICH AC97", "AudioControllerType");
2636 audioControllerTypes [KAudioControllerType_SB16] =
2637 tr ("SoundBlaster 16", "AudioControllerType");
2638
2639 networkAdapterTypes [KNetworkAdapterType_Am79C970A] =
2640 tr ("PCnet-PCI II (Am79C970A)", "NetworkAdapterType");
2641 networkAdapterTypes [KNetworkAdapterType_Am79C973] =
2642 tr ("PCnet-FAST III (Am79C973)", "NetworkAdapterType");
2643 networkAdapterTypes [KNetworkAdapterType_I82540EM] =
2644 tr ("Intel PRO/1000 MT Desktop (82540EM)", "NetworkAdapterType");
2645 networkAdapterTypes [KNetworkAdapterType_I82543GC] =
2646 tr ("Intel PRO/1000 T Server (82543GC)", "NetworkAdapterType");
2647
2648 networkAttachmentTypes [KNetworkAttachmentType_Null] =
2649 tr ("Not attached", "NetworkAttachmentType");
2650 networkAttachmentTypes [KNetworkAttachmentType_NAT] =
2651 tr ("NAT", "NetworkAttachmentType");
2652 networkAttachmentTypes [KNetworkAttachmentType_HostInterface] =
2653 tr ("Host Interface", "NetworkAttachmentType");
2654 networkAttachmentTypes [KNetworkAttachmentType_Internal] =
2655 tr ("Internal Network", "NetworkAttachmentType");
2656
2657 clipboardTypes [KClipboardMode_Disabled] =
2658 tr ("Disabled", "ClipboardType");
2659 clipboardTypes [KClipboardMode_HostToGuest] =
2660 tr ("Host To Guest", "ClipboardType");
2661 clipboardTypes [KClipboardMode_GuestToHost] =
2662 tr ("Guest To Host", "ClipboardType");
2663 clipboardTypes [KClipboardMode_Bidirectional] =
2664 tr ("Bidirectional", "ClipboardType");
2665
2666 ideControllerTypes [KIDEControllerType_PIIX3] =
2667 tr ("PIIX3", "IDEControllerType");
2668 ideControllerTypes [KIDEControllerType_PIIX4] =
2669 tr ("PIIX4", "IDEControllerType");
2670
2671 USBDeviceStates [KUSBDeviceState_NotSupported] =
2672 tr ("Not supported", "USBDeviceState");
2673 USBDeviceStates [KUSBDeviceState_Unavailable] =
2674 tr ("Unavailable", "USBDeviceState");
2675 USBDeviceStates [KUSBDeviceState_Busy] =
2676 tr ("Busy", "USBDeviceState");
2677 USBDeviceStates [KUSBDeviceState_Available] =
2678 tr ("Available", "USBDeviceState");
2679 USBDeviceStates [KUSBDeviceState_Held] =
2680 tr ("Held", "USBDeviceState");
2681 USBDeviceStates [KUSBDeviceState_Captured] =
2682 tr ("Captured", "USBDeviceState");
2683
2684 mUserDefinedPortName = tr ("User-defined", "serial port");
2685
2686 detailReportTemplatesReady = false;
2687
2688#if defined (Q_WS_PM) || defined (Q_WS_X11)
2689 /* As PM and X11 do not (to my knowledge) have functionality for providing
2690 * human readable key names, we keep a table of them, which must be
2691 * updated when the language is changed. */
2692 QIHotKeyEdit::retranslateUi();
2693#endif
2694}
2695
2696// public static stuff
2697////////////////////////////////////////////////////////////////////////////////
2698
2699/* static */
2700bool VBoxGlobal::isDOSType (const QString &aOSTypeId)
2701{
2702 if (aOSTypeId.left (3) == "dos" ||
2703 aOSTypeId.left (3) == "win" ||
2704 aOSTypeId.left (3) == "os2")
2705 return true;
2706
2707 return false;
2708}
2709
2710/**
2711 * Sets the QLabel background and frame colors according tho the pixmap
2712 * contents. The bottom right pixel of the label pixmap defines the
2713 * background color of the label, the top right pixel defines the color of
2714 * the one-pixel frame around it. This function also sets the alignment of
2715 * the pixmap to AlignVTop (to correspond to the color choosing logic).
2716 *
2717 * This method is useful to provide nice scaling of pixmal labels without
2718 * scaling pixmaps themselves. To see th eeffect, the size policy of the
2719 * label in the corresponding direction (vertical, for now) should be set to
2720 * something like MinimumExpanding.
2721 *
2722 * @todo Parametrize corners to select pixels from and set the alignment
2723 * accordingly.
2724 */
2725/* static */
2726void VBoxGlobal::adoptLabelPixmap (QLabel *aLabel)
2727{
2728 AssertReturnVoid (aLabel);
2729
2730 aLabel->setAlignment (Qt::AlignTop);
2731 aLabel->setFrameShape (QFrame::Box);
2732 aLabel->setFrameShadow (QFrame::Plain);
2733
2734 const QPixmap *pix = aLabel->pixmap();
2735 QImage img = pix->toImage();
2736 QRgb rgbBack = img.pixel (img.width() - 1, img.height() - 1);
2737 QRgb rgbFrame = img.pixel (img.width() - 1, 0);
2738
2739 QPalette pal = aLabel->palette();
2740 pal.setColor (QPalette::Window, rgbBack);
2741 pal.setColor (QPalette::WindowText, rgbFrame);
2742 aLabel->setPalette (pal);
2743}
2744
2745extern const char *gVBoxLangSubDir = "/nls";
2746extern const char *gVBoxLangFileBase = "VirtualBox_";
2747extern const char *gVBoxLangFileExt = ".qm";
2748extern const char *gVBoxLangIDRegExp = "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
2749extern const char *gVBoxBuiltInLangName = "C";
2750
2751class VBoxTranslator : public QTranslator
2752{
2753public:
2754
2755 VBoxTranslator (QObject *aParent = 0)
2756 : QTranslator (aParent) {}
2757
2758 bool loadFile (const QString &aFileName)
2759 {
2760 QFile file (aFileName);
2761 if (!file.open (QIODevice::ReadOnly))
2762 return false;
2763 mData = file.readAll();
2764 return load ((uchar*) mData.data(), mData.size());
2765 }
2766
2767private:
2768
2769 QByteArray mData;
2770};
2771
2772static VBoxTranslator *sTranslator = 0;
2773static QString sLoadedLangId = gVBoxBuiltInLangName;
2774
2775/**
2776 * Returns the loaded (active) language ID.
2777 * Note that it may not match with VBoxGlobalSettings::languageId() if the
2778 * specified language cannot be loaded.
2779 * If the built-in language is active, this method returns "C".
2780 *
2781 * @note "C" is treated as the built-in language for simplicity -- the C
2782 * locale is used in unix environments as a fallback when the requested
2783 * locale is invalid. This way we don't need to process both the "built_in"
2784 * language and the "C" language (which is a valid environment setting)
2785 * separately.
2786 */
2787/* static */
2788QString VBoxGlobal::languageId()
2789{
2790 return sLoadedLangId;
2791}
2792
2793/**
2794 * Loads the language by language ID.
2795 *
2796 * @param aLangId Language ID in in form of xx_YY. QString::null means the
2797 * system default language.
2798 */
2799/* static */
2800void VBoxGlobal::loadLanguage (const QString &aLangId)
2801{
2802 QString langId = aLangId.isNull() ?
2803 VBoxGlobal::systemLanguageId() : aLangId;
2804 QString languageFileName;
2805 QString selectedLangId = gVBoxBuiltInLangName;
2806
2807 char szNlsPath[RTPATH_MAX];
2808 int rc;
2809
2810 rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
2811 Assert(RT_SUCCESS(rc));
2812
2813 QString nlsPath = QString(szNlsPath) + gVBoxLangSubDir;
2814 QDir nlsDir (nlsPath);
2815
2816 Assert (!langId.isEmpty());
2817 if (!langId.isEmpty() && langId != gVBoxBuiltInLangName)
2818 {
2819 QRegExp regExp (gVBoxLangIDRegExp);
2820 int pos = regExp.indexIn (langId);
2821 /* the language ID should match the regexp completely */
2822 AssertReturnVoid (pos == 0);
2823
2824 QString lang = regExp.cap (2);
2825
2826 if (nlsDir.exists (gVBoxLangFileBase + langId + gVBoxLangFileExt))
2827 {
2828 languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + langId +
2829 gVBoxLangFileExt);
2830 selectedLangId = langId;
2831 }
2832 else if (nlsDir.exists (gVBoxLangFileBase + lang + gVBoxLangFileExt))
2833 {
2834 languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + lang +
2835 gVBoxLangFileExt);
2836 selectedLangId = lang;
2837 }
2838 else
2839 {
2840 /* Never complain when the default language is requested. In any
2841 * case, if no explicit language file exists, we will simply
2842 * fall-back to English (built-in). */
2843 if (!aLangId.isNull())
2844 vboxProblem().cannotFindLanguage (langId, nlsPath);
2845 /* selectedLangId remains built-in here */
2846 AssertReturnVoid (selectedLangId == gVBoxBuiltInLangName);
2847 }
2848 }
2849
2850 /* delete the old translator if there is one */
2851 if (sTranslator)
2852 {
2853 /* QTranslator destructor will call qApp->removeTranslator() for
2854 * us. It will also delete all its child translations we attach to it
2855 * below, so we don't have to care about them specially. */
2856 delete sTranslator;
2857 }
2858
2859 /* load new language files */
2860 sTranslator = new VBoxTranslator (qApp);
2861 Assert (sTranslator);
2862 bool loadOk = true;
2863 if (sTranslator)
2864 {
2865 if (selectedLangId != gVBoxBuiltInLangName)
2866 {
2867 Assert (!languageFileName.isNull());
2868 loadOk = sTranslator->loadFile (languageFileName);
2869 }
2870 /* we install the translator in any case: on failure, this will
2871 * activate an empty translator that will give us English
2872 * (built-in) */
2873 qApp->installTranslator (sTranslator);
2874 }
2875 else
2876 loadOk = false;
2877
2878 if (loadOk)
2879 sLoadedLangId = selectedLangId;
2880 else
2881 {
2882 vboxProblem().cannotLoadLanguage (languageFileName);
2883 sLoadedLangId = gVBoxBuiltInLangName;
2884 }
2885
2886 /* Try to load the corresponding Qt translation */
2887 if (sLoadedLangId != gVBoxBuiltInLangName)
2888 {
2889#ifdef Q_OS_UNIX
2890 /* We use system installations of Qt on Linux systems, so first, try
2891 * to load the Qt translation from the system location. */
2892 languageFileName = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" +
2893 sLoadedLangId + gVBoxLangFileExt;
2894 QTranslator *qtSysTr = new QTranslator (sTranslator);
2895 Assert (qtSysTr);
2896 if (qtSysTr && qtSysTr->load (languageFileName))
2897 qApp->installTranslator (qtSysTr);
2898 /* Note that the Qt translation supplied by Sun is always loaded
2899 * afterwards to make sure it will take precedence over the system
2900 * translation (it may contain more decent variants of translation
2901 * that better correspond to VirtualBox UI). We need to load both
2902 * because a newer version of Qt may be installed on the user computer
2903 * and the Sun version may not fully support it. We don't do it on
2904 * Win32 because we supply a Qt library there and therefore the
2905 * Sun translation is always the best one. */
2906#endif
2907 languageFileName = nlsDir.absoluteFilePath (QString ("qt_") +
2908 sLoadedLangId +
2909 gVBoxLangFileExt);
2910 QTranslator *qtTr = new QTranslator (sTranslator);
2911 Assert (qtTr);
2912 if (qtTr && (loadOk = qtTr->load (languageFileName)))
2913 qApp->installTranslator (qtTr);
2914 /* The below message doesn't fit 100% (because it's an additonal
2915 * language and the main one won't be reset to built-in on failure)
2916 * but the load failure is so rare here that it's not worth a separate
2917 * message (but still, having something is better than having none) */
2918 if (!loadOk && !aLangId.isNull())
2919 vboxProblem().cannotLoadLanguage (languageFileName);
2920 }
2921}
2922
2923/* static */
2924QIcon VBoxGlobal::iconSet (const char *aNormal,
2925 const char *aDisabled /* = NULL */,
2926 const char *aActive /* = NULL */)
2927{
2928 QIcon iconSet;
2929
2930 iconSet.addFile (aNormal, QSize(),
2931 QIcon::Normal);
2932 if (aDisabled != NULL)
2933 iconSet.addFile (aDisabled, QSize(),
2934 QIcon::Disabled);
2935 if (aActive != NULL)
2936 iconSet.addFile (aActive, QSize(),
2937 QIcon::Active);
2938 return iconSet;
2939}
2940
2941/* static */
2942QIcon VBoxGlobal::
2943iconSetEx (const char *aNormal, const char *aSmallNormal,
2944 const char *aDisabled /* = NULL */,
2945 const char *aSmallDisabled /* = NULL */,
2946 const char *aActive /* = NULL */,
2947 const char *aSmallActive /* = NULL */)
2948{
2949 QIcon iconSet;
2950
2951 iconSet.addFile (aNormal, QSize(), QIcon::Normal);
2952 iconSet.addFile (aSmallNormal, QSize(), QIcon::Normal);
2953 if (aSmallDisabled != NULL)
2954 {
2955 iconSet.addFile (aDisabled, QSize(), QIcon::Disabled);
2956 iconSet.addFile (aSmallDisabled, QSize(), QIcon::Disabled);
2957 }
2958 if (aSmallActive != NULL)
2959 {
2960 iconSet.addFile (aActive, QSize(), QIcon::Active);
2961 iconSet.addFile (aSmallActive, QSize(), QIcon::Active);
2962 }
2963
2964 return iconSet;
2965}
2966
2967QIcon VBoxGlobal::standardIcon (QStyle::StandardPixmap aStandard, QWidget *aWidget /* = NULL */)
2968{
2969 QStyle *style = aWidget ? aWidget->style(): QApplication::style();
2970#ifdef Q_WS_MAC
2971 /* At least in Qt 4.3.4/4.4 RC1 SP_MessageBoxWarning is the application
2972 * icon. So change this to the critical icon. (Maybe this would be
2973 * fixed in a later Qt version) */
2974 if (aStandard == QStyle::SP_MessageBoxWarning)
2975 return style->standardIcon (QStyle::SP_MessageBoxCritical, 0, aWidget);
2976#endif /* Q_WS_MAC */
2977 return style->standardIcon (aStandard, 0, aWidget);
2978}
2979
2980/**
2981 * Replacement for QToolButton::setTextLabel() that handles the shortcut
2982 * letter (if it is present in the argument string) as if it were a setText()
2983 * call: the shortcut letter is used to automatically assign an "Alt+<letter>"
2984 * accelerator key sequence to the given tool button.
2985 *
2986 * @note This method preserves the icon set if it was assigned before. Only
2987 * the text label and the accelerator are changed.
2988 *
2989 * @param aToolButton Tool button to set the text label on.
2990 * @param aTextLabel Text label to set.
2991 */
2992/* static */
2993void VBoxGlobal::setTextLabel (QToolButton *aToolButton,
2994 const QString &aTextLabel)
2995{
2996 AssertReturnVoid (aToolButton != NULL);
2997
2998 /* remember the icon set as setText() will kill it */
2999 QIcon iset = aToolButton->icon();
3000 /* re-use the setText() method to detect and set the accelerator */
3001 aToolButton->setText (aTextLabel);
3002 QKeySequence accel = aToolButton->shortcut();
3003 aToolButton->setText (aTextLabel);
3004 aToolButton->setIcon (iset);
3005 /* set the accel last as setIconSet() would kill it */
3006 aToolButton->setShortcut (accel);
3007}
3008
3009/**
3010 * Ensures that the given rectangle \a aRect is fully contained within the
3011 * rectangle \a aBoundRect by moving \a aRect if necessary. If \a aRect is
3012 * larger than \a aBoundRect, its top left corner is simply aligned with the
3013 * top left corner of \a aRect and, if \a aCanResize is true, \a aRect is
3014 * shrinked to become fully visible.
3015 */
3016/* static */
3017QRect VBoxGlobal::normalizeGeometry (const QRect &aRect, const QRect &aBoundRect,
3018 bool aCanResize /* = true */)
3019{
3020 QRect fr = aRect;
3021
3022 /* make the bottom right corner visible */
3023 int rd = aBoundRect.right() - fr.right();
3024 int bd = aBoundRect.bottom() - fr.bottom();
3025 fr.translate (rd < 0 ? rd : 0, bd < 0 ? bd : 0);
3026
3027 /* ensure the top left corner is visible */
3028 int ld = fr.left() - aBoundRect.left();
3029 int td = fr.top() - aBoundRect.top();
3030 fr.translate (ld < 0 ? -ld : 0, td < 0 ? -td : 0);
3031
3032 if (aCanResize)
3033 {
3034 /* adjust the size to make the rectangle fully contained */
3035 rd = aBoundRect.right() - fr.right();
3036 bd = aBoundRect.bottom() - fr.bottom();
3037 if (rd < 0)
3038 fr.setRight (fr.right() + rd);
3039 if (bd < 0)
3040 fr.setBottom (fr.bottom() + bd);
3041 }
3042
3043 return fr;
3044}
3045
3046/**
3047 * Aligns the center of \a aWidget with the center of \a aRelative.
3048 *
3049 * If necessary, \a aWidget's position is adjusted to make it fully visible
3050 * within the available desktop area. If \a aWidget is bigger then this area,
3051 * it will also be resized unless \a aCanResize is false or there is an
3052 * inappropriate minimum size limit (in which case the top left corner will be
3053 * simply aligned with the top left corner of the available desktop area).
3054 *
3055 * \a aWidget must be a top-level widget. \a aRelative may be any widget, but
3056 * if it's not top-level itself, its top-level widget will be used for
3057 * calculations. \a aRelative can also be NULL, in which case \a aWidget will
3058 * be centered relative to the available desktop area.
3059 */
3060/* static */
3061void VBoxGlobal::centerWidget (QWidget *aWidget, QWidget *aRelative,
3062 bool aCanResize /* = true */)
3063{
3064 AssertReturnVoid (aWidget);
3065 AssertReturnVoid (aWidget->isTopLevel());
3066
3067 QRect deskGeo, parentGeo;
3068 QWidget *w = aRelative;
3069 if (w)
3070 {
3071 w = w->window();
3072 deskGeo = QApplication::desktop()->availableGeometry (w);
3073 parentGeo = w->frameGeometry();
3074 /* On X11/Gnome, geo/frameGeo.x() and y() are always 0 for top level
3075 * widgets with parents, what a shame. Use mapToGlobal() to workaround. */
3076 QPoint d = w->mapToGlobal (QPoint (0, 0));
3077 d.rx() -= w->geometry().x() - w->x();
3078 d.ry() -= w->geometry().y() - w->y();
3079 parentGeo.moveTopLeft (d);
3080 }
3081 else
3082 {
3083 deskGeo = QApplication::desktop()->availableGeometry();
3084 parentGeo = deskGeo;
3085 }
3086
3087 /* On X11, there is no way to determine frame geometry (including WM
3088 * decorations) before the widget is shown for the first time. Stupidly
3089 * enumerate other top level widgets to find the thickest frame. The code
3090 * is based on the idea taken from QDialog::adjustPositionInternal(). */
3091
3092 int extraw = 0, extrah = 0;
3093
3094 QWidgetList list = QApplication::topLevelWidgets();
3095 QListIterator<QWidget*> it (list);
3096 while ((extraw == 0 || extrah == 0) && it.hasNext())
3097 {
3098 int framew, frameh;
3099 QWidget *current = it.next();
3100 if (!current->isVisible())
3101 continue;
3102
3103 framew = current->frameGeometry().width() - current->width();
3104 frameh = current->frameGeometry().height() - current->height();
3105
3106 extraw = qMax (extraw, framew);
3107 extrah = qMax (extrah, frameh);
3108 }
3109
3110 /// @todo (r=dmik) not sure if we really need this
3111#if 0
3112 /* sanity check for decoration frames. With embedding, we
3113 * might get extraordinary values */
3114 if (extraw == 0 || extrah == 0 || extraw > 20 || extrah > 50)
3115 {
3116 extrah = 50;
3117 extraw = 20;
3118 }
3119#endif
3120
3121 /* On non-X11 platforms, the following would be enough instead of the
3122 * above workaround: */
3123 // QRect geo = frameGeometry();
3124 QRect geo = QRect (0, 0, aWidget->width() + extraw,
3125 aWidget->height() + extrah);
3126
3127 geo.moveCenter (QPoint (parentGeo.x() + (parentGeo.width() - 1) / 2,
3128 parentGeo.y() + (parentGeo.height() - 1) / 2));
3129
3130 /* ensure the widget is within the available desktop area */
3131 QRect newGeo = normalizeGeometry (geo, deskGeo, aCanResize);
3132
3133 aWidget->move (newGeo.topLeft());
3134
3135 if (aCanResize &&
3136 (geo.width() != newGeo.width() || geo.height() != newGeo.height()))
3137 aWidget->resize (newGeo.width() - extraw, newGeo.height() - extrah);
3138}
3139
3140/**
3141 * Returns the decimal separator for the current locale.
3142 */
3143/* static */
3144QChar VBoxGlobal::decimalSep()
3145{
3146 return QLocale::system().decimalPoint();
3147}
3148
3149/**
3150 * Returns the regexp string that defines the format of the human-readable
3151 * size representation, <tt>####[.##] B|KB|MB|GB|TB|PB</tt>.
3152 *
3153 * This regexp will capture 5 groups of text:
3154 * - cap(1): integer number in case when no decimal point is present
3155 * (if empty, it means that decimal point is present)
3156 * - cap(2): size suffix in case when no decimal point is present (may be empty)
3157 * - cap(3): integer number in case when decimal point is present (may be empty)
3158 * - cap(4): fraction number (hundredth) in case when decimal point is present
3159 * - cap(5): size suffix in case when decimal point is present (note that
3160 * B cannot appear there)
3161 */
3162/* static */
3163QString VBoxGlobal::sizeRegexp()
3164{
3165 QString regexp =
3166 QString ("^(?:(?:(\\d+)(?:\\s?([KMGTP]?B))?)|(?:(\\d*)%1(\\d{1,2})(?:\\s?([KMGTP]B))))$")
3167 .arg (decimalSep());
3168 return regexp;
3169}
3170
3171/**
3172 * Parses the given size string that should be in form of
3173 * <tt>####[.##] B|KB|MB|GB|TB|PB</tt> and returns the size value
3174 * in bytes. Zero is returned on error.
3175 */
3176/* static */
3177quint64 VBoxGlobal::parseSize (const QString &aText)
3178{
3179 QRegExp regexp (sizeRegexp());
3180 int pos = regexp.indexIn (aText);
3181 if (pos != -1)
3182 {
3183 QString intgS = regexp.cap (1);
3184 QString hundS;
3185 QString suff = regexp.cap (2);
3186 if (intgS.isEmpty())
3187 {
3188 intgS = regexp.cap (3);
3189 hundS = regexp.cap (4);
3190 suff = regexp.cap (5);
3191 }
3192
3193 quint64 denom = 0;
3194 if (suff.isEmpty() || suff == "B")
3195 denom = 1;
3196 else if (suff == "KB")
3197 denom = _1K;
3198 else if (suff == "MB")
3199 denom = _1M;
3200 else if (suff == "GB")
3201 denom = _1G;
3202 else if (suff == "TB")
3203 denom = _1T;
3204 else if (suff == "PB")
3205 denom = _1P;
3206
3207 quint64 intg = intgS.toULongLong();
3208 if (denom == 1)
3209 return intg;
3210
3211 quint64 hund = hundS.leftJustified (2, '0').toULongLong();
3212 hund = hund * denom / 100;
3213 intg = intg * denom + hund;
3214 return intg;
3215 }
3216 else
3217 return 0;
3218}
3219
3220/**
3221 * Formats the given \a size value in bytes to a human readable string
3222 * in form of <tt>####[.##] B|KB|MB|GB|TB|PB</tt>.
3223 *
3224 * The \a mode parameter is used for resulting numbers that get a fractional
3225 * part after converting the \a size to KB, MB etc:
3226 * <ul>
3227 * <li>When \a mode is 0, the result is rounded to the closest number
3228 * containing two decimal digits.
3229 * </li>
3230 * <li>When \a mode is -1, the result is rounded to the largest two decimal
3231 * digit number that is not greater than the result. This guarantees that
3232 * converting the resulting string back to the integer value in bytes
3233 * will not produce a value greater that the initial \a size parameter.
3234 * </li>
3235 * <li>When \a mode is 1, the result is rounded to the smallest two decimal
3236 * digit number that is not less than the result. This guarantees that
3237 * converting the resulting string back to the integer value in bytes
3238 * will not produce a value less that the initial \a size parameter.
3239 * </li>
3240 * </ul>
3241 *
3242 * @param aSize size value in bytes
3243 * @param aMode convertion mode (-1, 0 or 1)
3244 * @return human-readable size string
3245 */
3246/* static */
3247QString VBoxGlobal::formatSize (quint64 aSize, int aMode /* = 0 */)
3248{
3249 static const char *Suffixes [] = { "B", "KB", "MB", "GB", "TB", "PB", NULL };
3250
3251 quint64 denom = 0;
3252 int suffix = 0;
3253
3254 if (aSize < _1K)
3255 {
3256 denom = 1;
3257 suffix = 0;
3258 }
3259 else if (aSize < _1M)
3260 {
3261 denom = _1K;
3262 suffix = 1;
3263 }
3264 else if (aSize < _1G)
3265 {
3266 denom = _1M;
3267 suffix = 2;
3268 }
3269 else if (aSize < _1T)
3270 {
3271 denom = _1G;
3272 suffix = 3;
3273 }
3274 else if (aSize < _1P)
3275 {
3276 denom = _1T;
3277 suffix = 4;
3278 }
3279 else
3280 {
3281 denom = _1P;
3282 suffix = 5;
3283 }
3284
3285 quint64 intg = aSize / denom;
3286 quint64 hund = aSize % denom;
3287
3288 QString number;
3289 if (denom > 1)
3290 {
3291 if (hund)
3292 {
3293 hund *= 100;
3294 /* not greater */
3295 if (aMode < 0) hund = hund / denom;
3296 /* not less */
3297 else if (aMode > 0) hund = (hund + denom - 1) / denom;
3298 /* nearest */
3299 else hund = (hund + denom / 2) / denom;
3300 }
3301 /* check for the fractional part overflow due to rounding */
3302 if (hund == 100)
3303 {
3304 hund = 0;
3305 ++ intg;
3306 /* check if we've got 1024 XB after rounding and scale down if so */
3307 if (intg == 1024 && Suffixes [suffix + 1] != NULL)
3308 {
3309 intg /= 1024;
3310 ++ suffix;
3311 }
3312 }
3313 number = QString ("%1%2%3").arg (intg).arg (decimalSep())
3314 .arg (QString::number (hund).rightJustified (2, '0'));
3315 }
3316 else
3317 {
3318 number = QString::number (intg);
3319 }
3320
3321 return QString ("%1 %2").arg (number).arg (Suffixes [suffix]);
3322}
3323
3324/**
3325 * Reformats the input string @a aStr so that:
3326 * - strings in single quotes will be put inside <nobr> and marked
3327 * with blue color;
3328 * - UUIDs be put inside <nobr> and marked
3329 * with green color;
3330 * - replaces new line chars with </p><p> constructs to form paragraphs
3331 * (note that <p> and </p> are not appended to the beginnign and to the
3332 * end of the string respectively, to allow the result be appended
3333 * or prepended to the existing paragraph).
3334 *
3335 * If @a aToolTip is true, colouring is not applied, only the <nobr> tag
3336 * is added. Also, new line chars are replaced with <br> instead of <p>.
3337 */
3338/* static */
3339QString VBoxGlobal::highlight (const QString &aStr, bool aToolTip /* = false */)
3340{
3341 QString strFont;
3342 QString uuidFont;
3343 QString endFont;
3344 if (!aToolTip)
3345 {
3346 strFont = "<font color=#0000CC>";
3347 uuidFont = "<font color=#008000>";
3348 endFont = "</font>";
3349 }
3350
3351 QString text = aStr;
3352
3353 /* replace special entities, '&' -- first! */
3354 text.replace ('&', "&amp;");
3355 text.replace ('<', "&lt;");
3356 text.replace ('>', "&gt;");
3357 text.replace ('\"', "&quot;");
3358
3359 /* mark strings in single quotes with color */
3360 QRegExp rx = QRegExp ("((?:^|\\s)[(]?)'([^']*)'(?=[:.-!);]?(?:\\s|$))");
3361 rx.setMinimal (true);
3362 text.replace (rx,
3363 QString ("\\1%1<nobr>'\\2'</nobr>%2")
3364 .arg (strFont). arg (endFont));
3365
3366 /* mark UUIDs with color */
3367 text.replace (QRegExp (
3368 "((?:^|\\s)[(]?)"
3369 "(\\{[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}\\})"
3370 "(?=[:.-!);]?(?:\\s|$))"),
3371 QString ("\\1%1<nobr>\\2</nobr>%2")
3372 .arg (uuidFont). arg (endFont));
3373
3374 /* split to paragraphs at \n chars */
3375 if (!aToolTip)
3376 text.replace ('\n', "</p><p>");
3377 else
3378 text.replace ('\n', "<br>");
3379
3380 return text;
3381}
3382
3383/**
3384 * This does exactly the same as QLocale::system().name() but corrects its
3385 * wrong behavior on Linux systems (LC_NUMERIC for some strange reason takes
3386 * precedence over any other locale setting in the QLocale::system()
3387 * implementation). This implementation first looks at LC_ALL (as defined by
3388 * SUS), then looks at LC_MESSAGES which is designed to define a language for
3389 * program messages in case if it differs from the language for other locale
3390 * categories. Then it looks for LANG and finally falls back to
3391 * QLocale::system().name().
3392 *
3393 * The order of precedence is well defined here:
3394 * http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
3395 *
3396 * @note This method will return "C" when the requested locale is invalid or
3397 * when the "C" locale is set explicitly.
3398 */
3399/* static */
3400QString VBoxGlobal::systemLanguageId()
3401{
3402#ifdef Q_OS_UNIX
3403 const char *s = RTEnvGet ("LC_ALL");
3404 if (s == 0)
3405 s = RTEnvGet ("LC_MESSAGES");
3406 if (s == 0)
3407 s = RTEnvGet ("LANG");
3408 if (s != 0)
3409 return QLocale (s).name();
3410#endif
3411 return QLocale::system().name();
3412}
3413
3414/**
3415 * Reimplementation of QFileDialog::getExistingDirectory() that removes some
3416 * oddities and limitations.
3417 *
3418 * On Win32, this function makes sure a native dialog is launched in
3419 * another thread to avoid dialog visualization errors occuring due to
3420 * multi-threaded COM apartment initialization on the main UI thread while
3421 * the appropriate native dialog function expects a single-threaded one.
3422 *
3423 * On all other platforms, this function is equivalent to
3424 * QFileDialog::getExistingDirectory().
3425 */
3426QString VBoxGlobal::getExistingDirectory (const QString &aDir,
3427 QWidget *aParent,
3428 const QString &aCaption,
3429 bool aDirOnly,
3430 bool aResolveSymlinks)
3431{
3432#if defined Q_WS_WIN
3433
3434 /**
3435 * QEvent class reimplementation to carry Win32 API native dialog's
3436 * result folder information
3437 */
3438 class GetExistDirectoryEvent : public OpenNativeDialogEvent
3439 {
3440 public:
3441
3442 enum { TypeId = QEvent::User + 300 };
3443
3444 GetExistDirectoryEvent (const QString &aResult)
3445 : OpenNativeDialogEvent (aResult, (QEvent::Type) TypeId) {}
3446 };
3447
3448 /**
3449 * QThread class reimplementation to open Win32 API native folder's dialog
3450 */
3451 class Thread : public QThread
3452 {
3453 public:
3454
3455 Thread (QWidget *aParent, QObject *aTarget,
3456 const QString &aDir, const QString &aCaption)
3457 : mParent (aParent), mTarget (aTarget), mDir (aDir), mCaption (aCaption) {}
3458
3459 virtual void run()
3460 {
3461 QString result;
3462
3463 QWidget *topParent = mParent ? mParent->window() : vboxGlobal().mainWindow();
3464 QString title = mCaption.isNull() ? tr ("Select a directory") : mCaption;
3465
3466 TCHAR path[MAX_PATH];
3467 path [0] = 0;
3468 TCHAR initPath [MAX_PATH];
3469 initPath [0] = 0;
3470
3471 BROWSEINFO bi;
3472 bi.hwndOwner = topParent ? topParent->winId() : 0;
3473 bi.pidlRoot = NULL;
3474 bi.lpszTitle = (TCHAR*)(title.isNull() ? 0 : title.utf16());
3475 bi.pszDisplayName = initPath;
3476 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
3477 bi.lpfn = winGetExistDirCallbackProc;
3478 bi.lParam = Q_ULONG (&mDir);
3479
3480 /* Qt is uncapable to properly handle modal state if the modal
3481 * window is not a QWidget. For example, if we have the W1->W2->N
3482 * ownership where Wx are QWidgets (W2 is modal), and N is a
3483 * native modal window, cliking on the title bar of W1 will still
3484 * activate W2 and redirect keyboard/mouse to it. The dirty hack
3485 * to prevent it is to disable the entire widget... */
3486 if (mParent)
3487 mParent->setEnabled (false);
3488
3489 LPITEMIDLIST itemIdList = SHBrowseForFolder (&bi);
3490 if (itemIdList)
3491 {
3492 SHGetPathFromIDList (itemIdList, path);
3493 IMalloc *pMalloc;
3494 if (SHGetMalloc (&pMalloc) != NOERROR)
3495 result = QString::null;
3496 else
3497 {
3498 pMalloc->Free (itemIdList);
3499 pMalloc->Release();
3500 result = QString::fromUtf16 ((ushort*)path);
3501 }
3502 }
3503 else
3504 result = QString::null;
3505 QApplication::postEvent (mTarget, new GetExistDirectoryEvent (result));
3506
3507 /* Enable the parent widget again. */
3508 if (mParent)
3509 mParent->setEnabled (true);
3510 }
3511
3512 private:
3513
3514 QWidget *mParent;
3515 QObject *mTarget;
3516 QString mDir;
3517 QString mCaption;
3518 };
3519
3520 /* Local event loop to run while waiting for the result from another
3521 * thread */
3522 QEventLoop loop;
3523
3524 QString dir = QDir::convertSeparators (aDir);
3525 LoopObject loopObject ((QEvent::Type) GetExistDirectoryEvent::TypeId, loop);
3526
3527 Thread openDirThread (aParent, &loopObject, dir, aCaption);
3528 openDirThread.start();
3529 loop.exec();
3530 openDirThread.wait();
3531
3532 return loopObject.result();
3533
3534#else
3535
3536 QFileDialog::Options o;
3537 if (aDirOnly)
3538 o = QFileDialog::ShowDirsOnly;
3539 if (!aResolveSymlinks)
3540 o |= QFileDialog::DontResolveSymlinks;
3541 return QFileDialog::getExistingDirectory (aParent, aCaption, aDir, o);
3542
3543#endif
3544}
3545
3546/**
3547 * Reimplementation of QFileDialog::getOpenFileName() that removes some
3548 * oddities and limitations.
3549 *
3550 * On Win32, this function makes sure a file filter is applied automatically
3551 * right after it is selected from the drop-down list, to conform to common
3552 * experience in other applications. Note that currently, @a selectedFilter
3553 * is always set to null on return.
3554 *
3555 * On all other platforms, this function is equivalent to
3556 * QFileDialog::getOpenFileName().
3557 */
3558/* static */
3559QString VBoxGlobal::getOpenFileName (const QString &aStartWith,
3560 const QString &aFilters,
3561 QWidget *aParent,
3562 const QString &aCaption,
3563 QString *aSelectedFilter,
3564 bool aResolveSymlinks)
3565{
3566#if defined Q_WS_WIN
3567
3568 /**
3569 * QEvent class reimplementation to carry Win32 API native dialog's
3570 * result folder information
3571 */
3572 class GetOpenFileNameEvent : public OpenNativeDialogEvent
3573 {
3574 public:
3575
3576 enum { TypeId = QEvent::User + 301 };
3577
3578 GetOpenFileNameEvent (const QString &aResult)
3579 : OpenNativeDialogEvent (aResult, (QEvent::Type) TypeId) {}
3580 };
3581
3582 /**
3583 * QThread class reimplementation to open Win32 API native file dialog
3584 */
3585 class Thread : public QThread
3586 {
3587 public:
3588
3589 Thread (QWidget *aParent, QObject *aTarget,
3590 const QString &aStartWith, const QString &aFilters,
3591 const QString &aCaption) :
3592 mParent (aParent), mTarget (aTarget),
3593 mStartWith (aStartWith), mFilters (aFilters),
3594 mCaption (aCaption) {}
3595
3596 virtual void run()
3597 {
3598 QString result;
3599
3600 QString workDir;
3601 QString initSel;
3602 QFileInfo fi (mStartWith);
3603
3604 if (fi.isDir())
3605 workDir = mStartWith;
3606 else
3607 {
3608 workDir = fi.absolutePath();
3609 initSel = fi.fileName();
3610 }
3611
3612 workDir = QDir::convertSeparators (workDir);
3613 if (!workDir.endsWith ("\\"))
3614 workDir += "\\";
3615
3616 QString title = mCaption.isNull() ? tr ("Select a file") : mCaption;
3617
3618 QWidget *topParent = mParent ? mParent->window() : vboxGlobal().mainWindow();
3619 QString winFilters = winFilter (mFilters);
3620 AssertCompile (sizeof (TCHAR) == sizeof (QChar));
3621 TCHAR buf [1024];
3622 if (initSel.length() > 0 && initSel.length() < sizeof (buf))
3623 memcpy (buf, initSel.isNull() ? 0 : initSel.utf16(),
3624 (initSel.length() + 1) * sizeof (TCHAR));
3625 else
3626 buf [0] = 0;
3627
3628 OPENFILENAME ofn;
3629 memset (&ofn, 0, sizeof (OPENFILENAME));
3630
3631 ofn.lStructSize = sizeof (OPENFILENAME);
3632 ofn.hwndOwner = topParent ? topParent->winId() : 0;
3633 ofn.lpstrFilter = (TCHAR *) winFilters.isNull() ? 0 : winFilters.utf16();
3634 ofn.lpstrFile = buf;
3635 ofn.nMaxFile = sizeof (buf) - 1;
3636 ofn.lpstrInitialDir = (TCHAR *) workDir.isNull() ? 0 : workDir.utf16();
3637 ofn.lpstrTitle = (TCHAR *) title.isNull() ? 0 : title.utf16();
3638 ofn.Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY |
3639 OFN_EXPLORER | OFN_ENABLEHOOK |
3640 OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
3641 ofn.lpfnHook = OFNHookProc;
3642
3643 if (GetOpenFileName (&ofn))
3644 {
3645 result = QString::fromUtf16 ((ushort *) ofn.lpstrFile);
3646 }
3647
3648 // qt_win_eatMouseMove();
3649 MSG msg = {0, 0, 0, 0, 0, 0, 0};
3650 while (PeekMessage (&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
3651 if (msg.message == WM_MOUSEMOVE)
3652 PostMessage (msg.hwnd, msg.message, 0, msg.lParam);
3653
3654 result = result.isEmpty() ? result : QFileInfo (result).absoluteFilePath();
3655
3656 QApplication::postEvent (mTarget, new GetOpenFileNameEvent (result));
3657 }
3658
3659 private:
3660
3661 QWidget *mParent;
3662 QObject *mTarget;
3663 QString mStartWith;
3664 QString mFilters;
3665 QString mCaption;
3666 };
3667
3668 if (aSelectedFilter)
3669 *aSelectedFilter = QString::null;
3670
3671 /* Local event loop to run while waiting for the result from another
3672 * thread */
3673 QEventLoop loop;
3674
3675 QString startWith = QDir::convertSeparators (aStartWith);
3676 LoopObject loopObject ((QEvent::Type) GetOpenFileNameEvent::TypeId, loop);
3677
3678//#warning check me!
3679 if (aParent)
3680 aParent->setWindowModality (Qt::WindowModal);
3681
3682 Thread openDirThread (aParent, &loopObject, startWith, aFilters, aCaption);
3683 openDirThread.start();
3684 loop.exec();
3685 openDirThread.wait();
3686
3687//#warning check me!
3688 if (aParent)
3689 aParent->setWindowModality (Qt::NonModal);
3690
3691 return loopObject.result();
3692
3693#else
3694
3695 QFileDialog::Options o;
3696 if (!aResolveSymlinks)
3697 o |= QFileDialog::DontResolveSymlinks;
3698 return QFileDialog::getOpenFileName (aParent, aCaption, aStartWith,
3699 aFilters, aSelectedFilter, o);
3700#endif
3701}
3702
3703/**
3704 * Search for the first directory that exists starting from the passed one
3705 * and going up through its parents. In case if none of the directories
3706 * exist (except the root one), the function returns QString::null.
3707 */
3708/* static */
3709QString VBoxGlobal::getFirstExistingDir (const QString &aStartDir)
3710{
3711 QString result = QString::null;
3712 QDir dir (aStartDir);
3713 while (!dir.exists() && !dir.isRoot())
3714 {
3715 QFileInfo dirInfo (dir.absolutePath());
3716 dir = dirInfo.absolutePath();
3717 }
3718 if (dir.exists() && !dir.isRoot())
3719 result = dir.absolutePath();
3720 return result;
3721}
3722
3723#if defined (Q_WS_X11)
3724
3725static char *XXGetProperty (Display *aDpy, Window aWnd,
3726 Atom aPropType, const char *aPropName)
3727{
3728 Atom propNameAtom = XInternAtom (aDpy, aPropName,
3729 True /* only_if_exists */);
3730 if (propNameAtom == None)
3731 return NULL;
3732
3733 Atom actTypeAtom = None;
3734 int actFmt = 0;
3735 unsigned long nItems = 0;
3736 unsigned long nBytesAfter = 0;
3737 unsigned char *propVal = NULL;
3738 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
3739 0, LONG_MAX, False /* delete */,
3740 aPropType, &actTypeAtom, &actFmt,
3741 &nItems, &nBytesAfter, &propVal);
3742 if (rc != Success)
3743 return NULL;
3744
3745 return reinterpret_cast <char *> (propVal);
3746}
3747
3748static Bool XXSendClientMessage (Display *aDpy, Window aWnd, const char *aMsg,
3749 unsigned long aData0 = 0, unsigned long aData1 = 0,
3750 unsigned long aData2 = 0, unsigned long aData3 = 0,
3751 unsigned long aData4 = 0)
3752{
3753 Atom msgAtom = XInternAtom (aDpy, aMsg, True /* only_if_exists */);
3754 if (msgAtom == None)
3755 return False;
3756
3757 XEvent ev;
3758
3759 ev.xclient.type = ClientMessage;
3760 ev.xclient.serial = 0;
3761 ev.xclient.send_event = True;
3762 ev.xclient.display = aDpy;
3763 ev.xclient.window = aWnd;
3764 ev.xclient.message_type = msgAtom;
3765
3766 /* always send as 32 bit for now */
3767 ev.xclient.format = 32;
3768 ev.xclient.data.l [0] = aData0;
3769 ev.xclient.data.l [1] = aData1;
3770 ev.xclient.data.l [2] = aData2;
3771 ev.xclient.data.l [3] = aData3;
3772 ev.xclient.data.l [4] = aData4;
3773
3774 return XSendEvent (aDpy, DefaultRootWindow (aDpy), False,
3775 SubstructureRedirectMask, &ev) != 0;
3776}
3777
3778#endif
3779
3780/**
3781 * Activates the specified window. If necessary, the window will be
3782 * de-iconified activation.
3783 *
3784 * @note On X11, it is implied that @a aWid represents a window of the same
3785 * display the application was started on.
3786 *
3787 * @param aWId Window ID to activate.
3788 * @param aSwitchDesktop @c true to switch to the window's desktop before
3789 * activation.
3790 *
3791 * @return @c true on success and @c false otherwise.
3792 */
3793/* static */
3794bool VBoxGlobal::activateWindow (WId aWId, bool aSwitchDesktop /* = true */)
3795{
3796 bool result = true;
3797
3798#if defined (Q_WS_WIN32)
3799
3800 if (IsIconic (aWId))
3801 result &= !!ShowWindow (aWId, SW_RESTORE);
3802 else if (!IsWindowVisible (aWId))
3803 result &= !!ShowWindow (aWId, SW_SHOW);
3804
3805 result &= !!SetForegroundWindow (aWId);
3806
3807#elif defined (Q_WS_X11)
3808
3809 Display *dpy = QX11Info::display();
3810
3811 if (aSwitchDesktop)
3812 {
3813 /* try to find the desktop ID using the NetWM property */
3814 CARD32 *desktop = (CARD32 *) XXGetProperty (dpy, aWId, XA_CARDINAL,
3815 "_NET_WM_DESKTOP");
3816 if (desktop == NULL)
3817 /* if the NetWM propery is not supported try to find the desktop
3818 * ID using the GNOME WM property */
3819 desktop = (CARD32 *) XXGetProperty (dpy, aWId, XA_CARDINAL,
3820 "_WIN_WORKSPACE");
3821
3822 if (desktop != NULL)
3823 {
3824 Bool ok = XXSendClientMessage (dpy, DefaultRootWindow (dpy),
3825 "_NET_CURRENT_DESKTOP",
3826 *desktop);
3827 if (!ok)
3828 {
3829 LogWarningFunc (("Couldn't switch to desktop=%08X\n",
3830 desktop));
3831 result = false;
3832 }
3833 XFree (desktop);
3834 }
3835 else
3836 {
3837 LogWarningFunc (("Couldn't find a desktop ID for aWId=%08X\n",
3838 aWId));
3839 result = false;
3840 }
3841 }
3842
3843 Bool ok = XXSendClientMessage (dpy, aWId, "_NET_ACTIVE_WINDOW");
3844 result &= !!ok;
3845
3846 XRaiseWindow (dpy, aWId);
3847
3848#else
3849
3850 NOREF (aSwitchDesktop);
3851 AssertFailed();
3852 result = false;
3853
3854#endif
3855
3856 if (!result)
3857 LogWarningFunc (("Couldn't activate aWId=%08X\n", aWId));
3858
3859 return result;
3860}
3861
3862/**
3863 * Removes the acceletartor mark (the ampersand symbol) from the given string
3864 * and returns the result. The string is supposed to be a menu item's text
3865 * that may (or may not) contain the accelerator mark.
3866 *
3867 * In order to support accelerators used in non-alphabet languages
3868 * (e.g. Japanese) that has a form of "(&<L>)" (where <L> is a latin letter),
3869 * this method first searches for this pattern and, if found, removes it as a
3870 * whole. If such a pattern is not found, then the '&' character is simply
3871 * removed from the string.
3872 *
3873 * @note This function removes only the first occurense of the accelerator
3874 * mark.
3875 *
3876 * @param aText Menu item's text to remove the acceletaror mark from.
3877 *
3878 * @return The resulting string.
3879 */
3880/* static */
3881QString VBoxGlobal::removeAccelMark (const QString &aText)
3882{
3883 QString result = aText;
3884
3885 QRegExp accel ("\\(&[a-zA-Z]\\)");
3886 int pos = accel.indexIn (result);
3887 if (pos >= 0)
3888 result.remove (pos, accel.cap().length());
3889 else
3890 {
3891 pos = result.indexOf ('&');
3892 if (pos >= 0)
3893 result.remove (pos, 1);
3894 }
3895
3896 return result;
3897}
3898
3899/* static */
3900QString VBoxGlobal::insertKeyToActionText (const QString &aText, const QString &aKey)
3901{
3902#ifdef Q_WS_MAC
3903 QString key ("%1 (Host+%2)");
3904#else
3905 QString key ("%1 \tHost+%2");
3906#endif
3907 return key.arg (aText).arg (QKeySequence (aKey).toString (QKeySequence::NativeText));
3908}
3909
3910/* static */
3911QString VBoxGlobal::extractKeyFromActionText (const QString &aText)
3912{
3913 QString key;
3914#ifdef Q_WS_MAC
3915 QRegExp re (".* \\(Host\\+(.+)\\)");
3916#else
3917 QRegExp re (".* \\t\\Host\\+(.+)");
3918#endif
3919 if (re.exactMatch (aText))
3920 key = re.cap (1);
3921 return key;
3922}
3923
3924/**
3925 * Searches for a widget that with @a aName (if it is not NULL) which inherits
3926 * @a aClassName (if it is not NULL) and among children of @a aParent. If @a
3927 * aParent is NULL, all top-level widgets are searched. If @a aRecursive is
3928 * true, child widgets are recursively searched as well.
3929 */
3930/* static */
3931QWidget *VBoxGlobal::findWidget (QWidget *aParent, const char *aName,
3932 const char *aClassName /* = NULL */,
3933 bool aRecursive /* = false */)
3934{
3935 if (aParent == NULL)
3936 {
3937 QWidgetList list = QApplication::topLevelWidgets();
3938 QWidget* w = NULL;
3939 foreach(w, list)
3940 {
3941 if ((!aName || strcmp (w->objectName().toAscii().constData(), aName) == 0) &&
3942 (!aClassName || strcmp (w->metaObject()->className(), aClassName) == 0))
3943 break;
3944 if (aRecursive)
3945 {
3946 w = findWidget (w, aName, aClassName, aRecursive);
3947 if (w)
3948 break;
3949 }
3950 }
3951 return w;
3952 }
3953
3954 /* Find the first children of aParent with the appropriate properties.
3955 * Please note that this call is recursivly. */
3956 QList<QWidget *> list = qFindChildren<QWidget *> (aParent, aName);
3957 QWidget *child = NULL;
3958 foreach(child, list)
3959 {
3960 if (!aClassName || strcmp (child->metaObject()->className(), aClassName) == 0)
3961 break;
3962 }
3963 return child;
3964}
3965
3966// Public slots
3967////////////////////////////////////////////////////////////////////////////////
3968
3969/**
3970 * Opens the specified URL using OS/Desktop capabilities.
3971 *
3972 * @param aURL URL to open
3973 *
3974 * @return true on success and false otherwise
3975 */
3976bool VBoxGlobal::openURL (const QString &aURL)
3977{
3978#if defined (Q_WS_WIN)
3979 /* We cannot use ShellExecute() on the main UI thread because we've
3980 * initialized COM with CoInitializeEx(COINIT_MULTITHREADED). See
3981 * http://support.microsoft.com/default.aspx?scid=kb;en-us;287087
3982 * for more details. */
3983 class Thread : public QThread
3984 {
3985 public:
3986
3987 Thread (const QString &aURL, QObject *aObject)
3988 : mObject (aObject), mURL (aURL) {}
3989
3990 void run()
3991 {
3992 int rc = (int) ShellExecute (NULL, NULL, mURL.isNull() ? 0 : mURL.utf16(),
3993 NULL, NULL, SW_SHOW);
3994 bool ok = rc > 32;
3995 QApplication::postEvent
3996 (mObject,
3997 new VBoxShellExecuteEvent (this, mURL, ok));
3998 }
3999
4000 QString mURL;
4001 QObject *mObject;
4002 };
4003
4004 Thread *thread = new Thread (aURL, this);
4005 thread->start();
4006 /* thread will be deleted in the VBoxShellExecuteEvent handler */
4007
4008 return true;
4009
4010#elif defined (Q_WS_X11)
4011
4012 static const char * const commands[] =
4013 { "kfmclient:exec", "gnome-open", "x-www-browser", "firefox", "konqueror" };
4014
4015 for (size_t i = 0; i < ELEMENTS (commands); ++ i)
4016 {
4017 QStringList args = QString(commands [i]).split (':');
4018 args += aURL;
4019 QString command = args.takeFirst();
4020 if (QProcess::startDetached (command, args))
4021 return true;
4022 }
4023
4024#elif defined (Q_WS_MAC)
4025
4026 /* The code below is taken from Psi 0.10 sources
4027 * (http://www.psi-im.org) */
4028
4029 /* Use Internet Config to hand the URL to the appropriate application, as
4030 * set by the user in the Internet Preferences pane.
4031 * NOTE: ICStart could be called once at Psi startup, saving the
4032 * ICInstance in a global variable, as a minor optimization.
4033 * ICStop should then be called at Psi shutdown if ICStart
4034 * succeeded. */
4035 ICInstance icInstance;
4036 OSType psiSignature = 'psi ';
4037 OSStatus error = ::ICStart (&icInstance, psiSignature);
4038 if (error == noErr)
4039 {
4040 ConstStr255Param hint (0x0);
4041 QByteArray cs = aURL.toLocal8Bit();
4042 const char* data = cs.data();
4043 long length = cs.length();
4044 long start (0);
4045 long end (length);
4046 /* Don't bother testing return value (error); launched application
4047 * will report problems. */
4048 ::ICLaunchURL (icInstance, hint, data, length, &start, &end);
4049 ICStop (icInstance);
4050 return true;
4051 }
4052
4053#else
4054 vboxProblem().message
4055 (NULL, VBoxProblemReporter::Error,
4056 tr ("Opening URLs is not implemented yet."));
4057 return false;
4058#endif
4059
4060 /* if we go here it means we couldn't open the URL */
4061 vboxProblem().cannotOpenURL (aURL);
4062
4063 return false;
4064}
4065
4066void VBoxGlobal::showRegistrationDialog (bool aForce)
4067{
4068#ifdef VBOX_WITH_REGISTRATION
4069 if (!aForce && !VBoxRegistrationDlg::hasToBeShown())
4070 return;
4071
4072 if (mRegDlg)
4073 {
4074 /* Show the already opened registration dialog */
4075 mRegDlg->setWindowState (mRegDlg->windowState() & ~Qt::WindowMinimized);
4076 mRegDlg->raise();
4077 mRegDlg->activateWindow();
4078 }
4079 else
4080 {
4081 /* Store the ID of the main window to ensure that only one
4082 * registration dialog is shown at a time. Due to manipulations with
4083 * OnExtraDataCanChange() and OnExtraDataChange() signals, this extra
4084 * data item acts like an inter-process mutex, so the first process
4085 * that attempts to set it will win, the rest will get a failure from
4086 * the SetExtraData() call. */
4087 mVBox.SetExtraData (VBoxDefs::GUI_RegistrationDlgWinID,
4088 QString ("%1").arg ((qulonglong) mMainWindow->winId()));
4089
4090 if (mVBox.isOk())
4091 {
4092 /* We've got the "mutex", create a new registration dialog */
4093 VBoxRegistrationDlg *dlg =
4094 new VBoxRegistrationDlg (&mRegDlg, 0);
4095 dlg->setAttribute (Qt::WA_DeleteOnClose);
4096 Assert (dlg == mRegDlg);
4097 mRegDlg->show();
4098 }
4099 }
4100#endif
4101}
4102
4103void VBoxGlobal::showUpdateDialog (bool aForce)
4104{
4105 bool isNecessary = VBoxUpdateDlg::isNecessary();
4106
4107 if (!aForce && !isNecessary)
4108 return;
4109
4110 if (mUpdDlg)
4111 {
4112 if (!mUpdDlg->isHidden())
4113 {
4114 mUpdDlg->setWindowState (mUpdDlg->windowState() & ~Qt::WindowMinimized);
4115 mUpdDlg->raise();
4116 mUpdDlg->activateWindow();
4117 }
4118 }
4119 else
4120 {
4121 /* Store the ID of the main window to ensure that only one
4122 * update dialog is shown at a time. Due to manipulations with
4123 * OnExtraDataCanChange() and OnExtraDataChange() signals, this extra
4124 * data item acts like an inter-process mutex, so the first process
4125 * that attempts to set it will win, the rest will get a failure from
4126 * the SetExtraData() call. */
4127 mVBox.SetExtraData (VBoxDefs::GUI_UpdateDlgWinID,
4128 QString ("%1").arg ((qulonglong) mMainWindow->winId()));
4129
4130 if (mVBox.isOk())
4131 {
4132 /* We've got the "mutex", create a new update dialog */
4133 VBoxUpdateDlg *dlg = new VBoxUpdateDlg (&mUpdDlg, aForce, 0);
4134 dlg->setAttribute (Qt::WA_DeleteOnClose);
4135 Assert (dlg == mUpdDlg);
4136
4137 /* Update dialog always in background mode for now.
4138 * if (!aForce && isAutomatic) */
4139 mUpdDlg->search();
4140 /* else mUpdDlg->show(); */
4141 }
4142 }
4143}
4144
4145// Protected members
4146////////////////////////////////////////////////////////////////////////////////
4147
4148bool VBoxGlobal::event (QEvent *e)
4149{
4150 switch (e->type())
4151 {
4152#if defined (Q_WS_WIN)
4153 case VBoxDefs::ShellExecuteEventType:
4154 {
4155 VBoxShellExecuteEvent *ev = (VBoxShellExecuteEvent *) e;
4156 if (!ev->mOk)
4157 vboxProblem().cannotOpenURL (ev->mURL);
4158 /* wait for the thread and free resources */
4159 ev->mThread->wait();
4160 delete ev->mThread;
4161 return true;
4162 }
4163#endif
4164
4165 case VBoxDefs::AsyncEventType:
4166 {
4167 VBoxAsyncEvent *ev = (VBoxAsyncEvent *) e;
4168 ev->handle();
4169 return true;
4170 }
4171
4172 case VBoxDefs::EnumerateMediaEventType:
4173 {
4174 VBoxEnumerateMediaEvent *ev = (VBoxEnumerateMediaEvent *) e;
4175
4176 if (!ev->mLast)
4177 {
4178 if (ev->mMedia.status == VBoxMedia::Error)
4179 vboxProblem().cannotGetMediaAccessibility (ev->mMedia.disk);
4180 media_list [ev->mIndex] = ev->mMedia;
4181 emit mediaEnumerated (media_list [ev->mIndex], ev->mIndex);
4182 }
4183 else
4184 {
4185 /* the thread has posted the last message, wait for termination */
4186 media_enum_thread->wait();
4187 delete media_enum_thread;
4188 media_enum_thread = 0;
4189
4190 emit mediaEnumFinished (media_list);
4191 }
4192
4193 return true;
4194 }
4195
4196 /* VirtualBox callback events */
4197
4198 case VBoxDefs::MachineStateChangeEventType:
4199 {
4200 emit machineStateChanged (*(VBoxMachineStateChangeEvent *) e);
4201 return true;
4202 }
4203 case VBoxDefs::MachineDataChangeEventType:
4204 {
4205 emit machineDataChanged (*(VBoxMachineDataChangeEvent *) e);
4206 return true;
4207 }
4208 case VBoxDefs::MachineRegisteredEventType:
4209 {
4210 emit machineRegistered (*(VBoxMachineRegisteredEvent *) e);
4211 return true;
4212 }
4213 case VBoxDefs::SessionStateChangeEventType:
4214 {
4215 emit sessionStateChanged (*(VBoxSessionStateChangeEvent *) e);
4216 return true;
4217 }
4218 case VBoxDefs::SnapshotEventType:
4219 {
4220 emit snapshotChanged (*(VBoxSnapshotEvent *) e);
4221 return true;
4222 }
4223 case VBoxDefs::CanShowRegDlgEventType:
4224 {
4225 emit canShowRegDlg (((VBoxCanShowRegDlgEvent *) e)->mCanShow);
4226 return true;
4227 }
4228 case VBoxDefs::CanShowUpdDlgEventType:
4229 {
4230 emit canShowUpdDlg (((VBoxCanShowUpdDlgEvent *) e)->mCanShow);
4231 return true;
4232 }
4233 case VBoxDefs::ChangeGUILanguageEventType:
4234 {
4235 loadLanguage (static_cast<VBoxChangeGUILanguageEvent*> (e)->mLangId);
4236 return true;
4237 }
4238
4239 default:
4240 break;
4241 }
4242
4243 return QObject::event (e);
4244}
4245
4246bool VBoxGlobal::eventFilter (QObject *aObject, QEvent *aEvent)
4247{
4248 if (aEvent->type() == QEvent::LanguageChange &&
4249 aObject->isWidgetType() &&
4250 static_cast <QWidget *> (aObject)->isTopLevel())
4251 {
4252 /* Catch the language change event before any other widget gets it in
4253 * order to invalidate cached string resources (like the details view
4254 * templates) that may be used by other widgets. */
4255 QWidgetList list = QApplication::topLevelWidgets();
4256 if (list.first() == aObject)
4257 {
4258 /* call this only once per every language change (see
4259 * QApplication::installTranslator() for details) */
4260 retranslateUi();
4261 }
4262 }
4263
4264 return QObject::eventFilter (aObject, aEvent);
4265}
4266
4267// Private members
4268////////////////////////////////////////////////////////////////////////////////
4269
4270void VBoxGlobal::init()
4271{
4272#ifdef DEBUG
4273 verString += " [DEBUG]";
4274#endif
4275
4276#ifdef Q_WS_WIN
4277 /* COM for the main thread is initialized in main() */
4278#else
4279 HRESULT rc = COMBase::InitializeCOM();
4280 if (FAILED (rc))
4281 {
4282 vboxProblem().cannotInitCOM (rc);
4283 return;
4284 }
4285#endif
4286
4287 mVBox.createInstance (CLSID_VirtualBox);
4288 if (!mVBox.isOk())
4289 {
4290 vboxProblem().cannotCreateVirtualBox (mVBox);
4291 return;
4292 }
4293
4294 /* initialize guest OS type vector */
4295 CGuestOSTypeCollection coll = mVBox.GetGuestOSTypes();
4296 int osTypeCount = coll.GetCount();
4297 AssertMsg (osTypeCount > 0, ("Number of OS types must not be zero"));
4298 if (osTypeCount > 0)
4299 {
4300 vm_os_types.resize (osTypeCount);
4301 int i = 0;
4302 CGuestOSTypeEnumerator en = coll.Enumerate();
4303 while (en.HasMore())
4304 vm_os_types [i++] = en.GetNext();
4305 }
4306
4307 /* fill in OS type icon dictionary */
4308 static const char *kOSTypeIcons [][2] =
4309 {
4310 {"unknown", ":/os_unknown.png"},
4311 {"dos", ":/os_dos.png"},
4312 {"win31", ":/os_win31.png"},
4313 {"win95", ":/os_win95.png"},
4314 {"win98", ":/os_win98.png"},
4315 {"winme", ":/os_winme.png"},
4316 {"winnt4", ":/os_winnt4.png"},
4317 {"win2k", ":/os_win2k.png"},
4318 {"winxp", ":/os_winxp.png"},
4319 {"win2k3", ":/os_win2k3.png"},
4320 {"winvista", ":/os_winvista.png"},
4321 {"win2k8", ":/os_win2k8.png"},
4322 {"os2warp3", ":/os_os2warp3.png"},
4323 {"os2warp4", ":/os_os2warp4.png"},
4324 {"os2warp45", ":/os_os2warp45.png"},
4325 {"ecs", ":/os_ecs.png"},
4326 {"linux22", ":/os_linux22.png"},
4327 {"linux24", ":/os_linux24.png"},
4328 {"linux26", ":/os_linux26.png"},
4329 {"archlinux", ":/os_archlinux.png"},
4330 {"debian", ":/os_debian.png"},
4331 {"opensolaris", ":/os_opensolaris.png"},
4332 {"opensuse", ":/os_opensuse.png"},
4333 {"fedoracore", ":/os_fedoracore.png"},
4334 {"gentoo", ":/os_gentoo.png"},
4335 {"mandriva", ":/os_mandriva.png"},
4336 {"redhat", ":/os_redhat.png"},
4337 {"ubuntu", ":/os_ubuntu.png"},
4338 {"xandros", ":/os_xandros.png"},
4339 {"freebsd", ":/os_freebsd.png"},
4340 {"openbsd", ":/os_openbsd.png"},
4341 {"netbsd", ":/os_netbsd.png"},
4342 {"netware", ":/os_netware.png"},
4343 {"solaris", ":/os_solaris.png"},
4344 {"l4", ":/os_l4.png"},
4345 };
4346 for (uint n = 0; n < SIZEOF_ARRAY (kOSTypeIcons); n ++)
4347 {
4348 vm_os_type_icons.insert (kOSTypeIcons [n][0],
4349 new QPixmap (kOSTypeIcons [n][1]));
4350 }
4351
4352 /* fill in VM state icon dictionary */
4353 static struct
4354 {
4355 KMachineState state;
4356 const char *name;
4357 }
4358 vmStateIcons[] =
4359 {
4360 {KMachineState_Null, NULL},
4361 {KMachineState_PoweredOff, ":/state_powered_off_16px.png"},
4362 {KMachineState_Saved, ":/state_saved_16px.png"},
4363 {KMachineState_Aborted, ":/state_aborted_16px.png"},
4364 {KMachineState_Running, ":/state_running_16px.png"},
4365 {KMachineState_Paused, ":/state_paused_16px.png"},
4366 {KMachineState_Stuck, ":/state_stuck_16px.png"},
4367 {KMachineState_Starting, ":/state_running_16px.png"}, /// @todo (dmik) separate icon?
4368 {KMachineState_Stopping, ":/state_running_16px.png"}, /// @todo (dmik) separate icon?
4369 {KMachineState_Saving, ":/state_saving_16px.png"},
4370 {KMachineState_Restoring, ":/state_restoring_16px.png"},
4371 {KMachineState_Discarding, ":/state_discarding_16px.png"},
4372 };
4373 for (uint n = 0; n < SIZEOF_ARRAY (vmStateIcons); n ++)
4374 {
4375 mStateIcons.insert (vmStateIcons [n].state,
4376 new QPixmap (vmStateIcons [n].name));
4377 }
4378
4379 /* online/offline snapshot icons */
4380 mOfflineSnapshotIcon = QPixmap (":/offline_snapshot_16px.png");
4381 mOnlineSnapshotIcon = QPixmap (":/online_snapshot_16px.png");
4382
4383 /* initialize state colors vector */
4384 vm_state_color.insert (KMachineState_Null, new QColor(Qt::red));
4385 vm_state_color.insert (KMachineState_PoweredOff, new QColor(Qt::gray));
4386 vm_state_color.insert (KMachineState_Saved, new QColor(Qt::yellow));
4387 vm_state_color.insert (KMachineState_Aborted, new QColor(Qt::darkRed));
4388 vm_state_color.insert (KMachineState_Running, new QColor(Qt::green));
4389 vm_state_color.insert (KMachineState_Paused, new QColor(Qt::darkGreen));
4390 vm_state_color.insert (KMachineState_Stuck, new QColor(Qt::darkMagenta));
4391 vm_state_color.insert (KMachineState_Starting, new QColor(Qt::green));
4392 vm_state_color.insert (KMachineState_Stopping, new QColor(Qt::green));
4393 vm_state_color.insert (KMachineState_Saving, new QColor(Qt::green));
4394 vm_state_color.insert (KMachineState_Restoring, new QColor(Qt::green));
4395 vm_state_color.insert (KMachineState_Discarding, new QColor(Qt::green));
4396
4397 qApp->installEventFilter (this);
4398
4399 /* create default non-null global settings */
4400 gset = VBoxGlobalSettings (false);
4401
4402 /* try to load global settings */
4403 gset.load (mVBox);
4404 if (!mVBox.isOk() || !gset)
4405 {
4406 vboxProblem().cannotLoadGlobalConfig (mVBox, gset.lastError());
4407 return;
4408 }
4409
4410 /* Load customized language if any */
4411 QString languageId = gset.languageId();
4412 if (!languageId.isNull())
4413 loadLanguage (languageId);
4414
4415 retranslateUi();
4416
4417 /* process command line */
4418
4419 vm_render_mode_str = 0;
4420#ifdef VBOX_WITH_DEBUGGER_GUI
4421#ifdef VBOX_WITH_DEBUGGER_GUI_MENU
4422 dbg_enabled = true;
4423#else
4424 dbg_enabled = false;
4425#endif
4426 dbg_visible_at_startup = false;
4427#endif
4428
4429 int argc = qApp->argc();
4430 int i = 1;
4431 while (i < argc)
4432 {
4433 const char *arg = qApp->argv() [i];
4434 if ( !::strcmp (arg, "-startvm"))
4435 {
4436 if (++i < argc)
4437 {
4438 QString param = QString (qApp->argv() [i]);
4439 QUuid uuid = QUuid (param);
4440 if (!uuid.isNull())
4441 {
4442 vmUuid = uuid;
4443 }
4444 else
4445 {
4446 CMachine m = mVBox.FindMachine (param);
4447 if (m.isNull())
4448 {
4449 vboxProblem().cannotFindMachineByName (mVBox, param);
4450 return;
4451 }
4452 vmUuid = m.GetId();
4453 }
4454 }
4455 }
4456 else if (!::strcmp (arg, "-comment"))
4457 {
4458 ++i;
4459 }
4460 else if (!::strcmp (arg, "-rmode"))
4461 {
4462 if (++i < argc)
4463 vm_render_mode_str = qApp->argv() [i];
4464 }
4465#ifdef VBOX_WITH_DEBUGGER_GUI
4466 else if (!::strcmp (arg, "-dbg"))
4467 {
4468 dbg_enabled = true;
4469 }
4470#ifdef DEBUG
4471 else if (!::strcmp (arg, "-nodebug"))
4472 {
4473 dbg_enabled = false;
4474 dbg_visible_at_startup = false;
4475 }
4476#else
4477 else if (!::strcmp( arg, "-debug"))
4478 {
4479 dbg_enabled = true;
4480 dbg_visible_at_startup = true;
4481 }
4482#endif
4483#endif
4484 i++;
4485 }
4486
4487 vm_render_mode = vboxGetRenderMode( vm_render_mode_str );
4488
4489 /* setup the callback */
4490 callback = CVirtualBoxCallback (new VBoxCallback (*this));
4491 mVBox.RegisterCallback (callback);
4492 AssertWrapperOk (mVBox);
4493 if (!mVBox.isOk())
4494 return;
4495
4496 mValid = true;
4497}
4498
4499/** @internal
4500 *
4501 * This method should be never called directly. It is called automatically
4502 * when the application terminates.
4503 */
4504void VBoxGlobal::cleanup()
4505{
4506 /* sanity check */
4507 if (!sVBoxGlobalInCleanup)
4508 {
4509 AssertMsgFailed (("Should never be called directly\n"));
4510 return;
4511 }
4512
4513 if (!callback.isNull())
4514 {
4515 mVBox.UnregisterCallback (callback);
4516 AssertWrapperOk (mVBox);
4517 callback.detach();
4518 }
4519
4520 if (media_enum_thread)
4521 {
4522 /* sVBoxGlobalInCleanup is true here, so just wait for the thread */
4523 media_enum_thread->wait();
4524 delete media_enum_thread;
4525 media_enum_thread = 0;
4526 }
4527
4528#ifdef VBOX_WITH_REGISTRATION
4529 if (mRegDlg)
4530 mRegDlg->close();
4531#endif
4532
4533 if (mConsoleWnd)
4534 delete mConsoleWnd;
4535 if (mSelectorWnd)
4536 delete mSelectorWnd;
4537
4538 /* ensure CGuestOSType objects are no longer used */
4539 vm_os_types.clear();
4540 /* media list contains a lot of CUUnknown, release them */
4541 media_list.clear();
4542 /* the last step to ensure we don't use COM any more */
4543 mVBox.detach();
4544
4545 /* There may be VBoxEnumerateMediaEvent instances still in the message
4546 * queue which reference COM objects. Remove them to release those objects
4547 * before uninitializing the COM subsystem. */
4548 QApplication::removePostedEvents (this);
4549
4550#ifdef Q_WS_WIN
4551 /* COM for the main thread is shutdown in main() */
4552#else
4553 COMBase::CleanupCOM();
4554#endif
4555
4556 mValid = false;
4557}
4558
4559/** @fn vboxGlobal
4560 *
4561 * Shortcut to the static VBoxGlobal::instance() method, for convenience.
4562 */
4563
4564
4565/**
4566 * USB Popup Menu class methods
4567 * This class provides the list of USB devices attached to the host.
4568 */
4569VBoxUSBMenu::VBoxUSBMenu (QWidget *aParent) : QMenu (aParent)
4570{
4571 connect (this, SIGNAL (aboutToShow()),
4572 this, SLOT (processAboutToShow()));
4573// connect (this, SIGNAL (hovered (QAction *)),
4574// this, SLOT (processHighlighted (QAction *)));
4575}
4576
4577const CUSBDevice& VBoxUSBMenu::getUSB (QAction *aAction)
4578{
4579 return mUSBDevicesMap [aAction];
4580}
4581
4582void VBoxUSBMenu::setConsole (const CConsole &aConsole)
4583{
4584 mConsole = aConsole;
4585}
4586
4587void VBoxUSBMenu::processAboutToShow()
4588{
4589 clear();
4590 mUSBDevicesMap.clear();
4591
4592 CHost host = vboxGlobal().virtualBox().GetHost();
4593
4594 bool isUSBEmpty = host.GetUSBDevices().GetCount() == 0;
4595 if (isUSBEmpty)
4596 {
4597 QAction *action = addAction (tr ("<no available devices>", "USB devices"));
4598 action->setEnabled (false);
4599 action->setToolTip (tr ("No supported devices connected to the host PC",
4600 "USB device tooltip"));
4601 }
4602 else
4603 {
4604 CHostUSBDeviceEnumerator en = host.GetUSBDevices().Enumerate();
4605 while (en.HasMore())
4606 {
4607 CHostUSBDevice iterator = en.GetNext();
4608 CUSBDevice usb = CUnknown (iterator);
4609 QAction *action = addAction (vboxGlobal().details (usb));
4610 action->setCheckable (true);
4611 mUSBDevicesMap [action] = usb;
4612 /* check if created item was alread attached to this session */
4613 if (!mConsole.isNull())
4614 {
4615 CUSBDevice attachedUSB =
4616 mConsole.GetUSBDevices().FindById (usb.GetId());
4617 action->setChecked (!attachedUSB.isNull());
4618 action->setEnabled (iterator.GetState() !=
4619 KUSBDeviceState_Unavailable);
4620 }
4621 }
4622 }
4623}
4624
4625bool VBoxUSBMenu::event(QEvent *aEvent)
4626{
4627 /* We provide dynamic tooltips for the usb devices */
4628 if (aEvent->type() == QEvent::ToolTip)
4629 {
4630 QHelpEvent *helpEvent = static_cast<QHelpEvent *> (aEvent);
4631 QAction *action = actionAt (helpEvent->pos());
4632 if (action)
4633 {
4634 CUSBDevice usb = mUSBDevicesMap [action];
4635 if (!usb.isNull())
4636 {
4637 QToolTip::showText (helpEvent->globalPos(), vboxGlobal().toolTip (usb));
4638 return true;
4639 }
4640 }
4641 }
4642 return QMenu::event (aEvent);
4643}
4644
4645/**
4646 * Enable/Disable Menu class.
4647 * This class provides enable/disable menu items.
4648 */
4649VBoxSwitchMenu::VBoxSwitchMenu (QWidget *aParent, QAction *aAction,
4650 bool aInverted)
4651 : QMenu (aParent), mAction (aAction), mInverted (aInverted)
4652{
4653 /* this menu works only with toggle action */
4654 Assert (aAction->isCheckable());
4655 addAction(aAction);
4656 connect (this, SIGNAL (aboutToShow()),
4657 this, SLOT (processAboutToShow()));
4658}
4659
4660void VBoxSwitchMenu::setToolTip (const QString &aTip)
4661{
4662 mAction->setToolTip (aTip);
4663}
4664
4665void VBoxSwitchMenu::processAboutToShow()
4666{
4667 QString text = mAction->isChecked() ^ mInverted ? tr ("Disable") : tr ("Enable");
4668 mAction->setText (text);
4669}
4670
Note: See TracBrowser for help on using the repository browser.

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