VirtualBox

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

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

FE/Qt4: Fixed memory leak.

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

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