VirtualBox

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

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

Fe/Qt4: VBoxGlobal::detailsReport() now use minimum 16+10=26 pixels for 16px icons to separate icons from the text items with 10 px space.

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