VirtualBox

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

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

Guest properties: initial commit of new interface

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