VirtualBox

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

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

FE/Qt4: VM USB settings face lift.

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

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