VirtualBox

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

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

FE/Qt4: fix compilation for release builds

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