VirtualBox

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

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

Fe/Qt4: Little code styling.

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