VirtualBox

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

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

Qt3->Qt4:
Update QLabel picture decoration mechanism.

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