VirtualBox

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

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

The Big Sun Rebranding Header Change

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