VirtualBox

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

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

FE/Qt4: use new QIDialogButtonBox

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 140.8 KB
Line 
1/** @file
2 *
3 * VBox frontends: Qt GUI ("VirtualBox"):
4 * VBoxGlobal class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include "VBoxGlobal.h"
24
25#include "VBoxDefs.h"
26#include "VBoxSelectorWnd.h"
27#include "VBoxConsoleWnd.h"
28#include "VBoxProblemReporter.h"
29#include "QIHotKeyEdit.h"
30#include "QIMessageBox.h"
31#include "QIDialogButtonBox.h"
32
33#ifdef VBOX_WITH_REGISTRATION
34#include "VBoxRegistrationDlg.h"
35#endif
36
37/* Qt includes */
38#include <QLibraryInfo>
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 QIDialogButtonBox (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 + QString (sSectionItemTpl).arg (tr ("PAE/NX", "details report"), "%9");
1365
1366 sGeneralFullHrefTpl = QString (sSectionHrefTpl)
1367 .arg (2 + 9) /* rows */
1368 .arg (":/machine_16px.png", /* icon */
1369 "#general", /* link */
1370 tr ("General", "details report"), /* title */
1371 generalItems); /* items */
1372 sGeneralFullBoldTpl = QString (sSectionBoldTpl)
1373 .arg (2 + 9) /* rows */
1374 .arg (":/machine_16px.png", /* icon */
1375 "#general", /* link */
1376 tr ("General", "details report"), /* title */
1377 generalItems); /* items */
1378 }
1379
1380 /* common generated content */
1381
1382 const QString &sectionTpl = withLinks
1383 ? sSectionHrefTpl
1384 : sSectionBoldTpl;
1385
1386 QString hardDisks;
1387 {
1388 int rows = 2; /* including section header and footer */
1389
1390 CHardDiskAttachmentEnumerator aen = m.GetHardDiskAttachments().Enumerate();
1391 while (aen.HasMore())
1392 {
1393 CHardDiskAttachment hda = aen.GetNext();
1394 CHardDisk hd = hda.GetHardDisk();
1395 /// @todo for the explaination of the below isOk() checks, see
1396 /// @todo *** in #details (const CHardDisk &, bool).
1397 if (hda.isOk())
1398 {
1399 CHardDisk root = hd.GetRoot();
1400 if (hd.isOk())
1401 {
1402 QString src = root.GetLocation();
1403 hardDisks += QString (sSectionItemTpl)
1404 .arg (QString ("%1 %2")
1405 .arg (toString (hda.GetBus(), hda.GetChannel()))
1406 .arg (toString (hda.GetBus(), hda.GetChannel(),
1407 hda.GetDevice())))
1408 .arg (QString ("%1 [<nobr>%2</nobr>]")
1409 .arg (prepareFileNameForHTML (src))
1410 .arg (details (hd, isNewVM /* predict */, aDoRefresh)));
1411 ++ rows;
1412 }
1413 }
1414 }
1415
1416 if (hardDisks.isNull())
1417 {
1418 hardDisks = QString (sSectionItemTpl)
1419 .arg (tr ("Not Attached", "details report (HDDs)")).arg ("");
1420 ++ rows;
1421 }
1422
1423 hardDisks = sectionTpl
1424 .arg (rows) /* rows */
1425 .arg (":/hd_16px.png", /* icon */
1426 "#hdds", /* link */
1427 tr ("Hard Disks", "details report"), /* title */
1428 hardDisks); /* items */
1429 }
1430
1431 /* compose details report */
1432
1433 const QString &generalBasicTpl = withLinks
1434 ? sGeneralBasicHrefTpl
1435 : sGeneralBasicBoldTpl;
1436
1437 const QString &generalFullTpl = withLinks
1438 ? sGeneralFullHrefTpl
1439 : sGeneralFullBoldTpl;
1440
1441 QString detailsReport;
1442
1443 if (isNewVM)
1444 {
1445 detailsReport
1446 = generalBasicTpl
1447 .arg (m.GetName())
1448 .arg (vmGuestOSTypeDescription (m.GetOSTypeId()))
1449 .arg (m.GetMemorySize())
1450 + hardDisks;
1451 }
1452 else
1453 {
1454 /* boot order */
1455 QString bootOrder;
1456 for (ulong i = 1; i <= mVBox.GetSystemProperties().GetMaxBootPosition(); i++)
1457 {
1458 KDeviceType device = m.GetBootOrder (i);
1459 if (device == KDeviceType_Null)
1460 continue;
1461 if (!bootOrder.isEmpty())
1462 bootOrder += ", ";
1463 bootOrder += toString (device);
1464 }
1465 if (bootOrder.isEmpty())
1466 bootOrder = toString (KDeviceType_Null);
1467
1468 CBIOSSettings biosSettings = m.GetBIOSSettings();
1469
1470 /* ACPI */
1471 QString acpi = biosSettings.GetACPIEnabled()
1472 ? tr ("Enabled", "details report (ACPI)")
1473 : tr ("Disabled", "details report (ACPI)");
1474
1475 /* IO APIC */
1476 QString ioapic = biosSettings.GetIOAPICEnabled()
1477 ? tr ("Enabled", "details report (IO APIC)")
1478 : tr ("Disabled", "details report (IO APIC)");
1479
1480 /* VT-x/AMD-V */
1481 CSystemProperties props = vboxGlobal().virtualBox().GetSystemProperties();
1482 QString virt = m.GetHWVirtExEnabled() == KTSBool_True ?
1483 tr ("Enabled", "details report (VT-x/AMD-V)") :
1484 m.GetHWVirtExEnabled() == KTSBool_False ?
1485 tr ("Disabled", "details report (VT-x/AMD-V)") :
1486 props.GetHWVirtExEnabled() ?
1487 tr ("Enabled", "details report (VT-x/AMD-V)") :
1488 tr ("Disabled", "details report (VT-x/AMD-V)");
1489
1490 /* PAE/NX */
1491 QString pae = m.GetPAEEnabled()
1492 ? tr ("Enabled", "details report (PAE/NX)")
1493 : tr ("Disabled", "details report (PAE/NX)");
1494
1495 /* General + Hard Disks */
1496 detailsReport
1497 = generalFullTpl
1498 .arg (m.GetName())
1499 .arg (vmGuestOSTypeDescription (m.GetOSTypeId()))
1500 .arg (m.GetMemorySize())
1501 .arg (m.GetVRAMSize())
1502 .arg (bootOrder)
1503 .arg (acpi)
1504 .arg (ioapic)
1505 .arg (virt)
1506 .arg (pae)
1507 + hardDisks;
1508
1509 QString item;
1510
1511 /* DVD */
1512 CDVDDrive dvd = m.GetDVDDrive();
1513 item = QString (sSectionItemTpl);
1514 switch (dvd.GetState())
1515 {
1516 case KDriveState_NotMounted:
1517 item = item.arg (tr ("Not mounted", "details report (DVD)"), "");
1518 break;
1519 case KDriveState_ImageMounted:
1520 {
1521 CDVDImage img = dvd.GetImage();
1522 item = item.arg (tr ("Image", "details report (DVD)"),
1523 prepareFileNameForHTML (img.GetFilePath()));
1524 break;
1525 }
1526 case KDriveState_HostDriveCaptured:
1527 {
1528 CHostDVDDrive drv = dvd.GetHostDrive();
1529 QString drvName = drv.GetName();
1530 QString description = drv.GetDescription();
1531 QString fullName = description.isEmpty() ?
1532 drvName :
1533 QString ("%1 (%2)").arg (description, drvName);
1534 item = item.arg (tr ("Host Drive", "details report (DVD)"),
1535 fullName);
1536 break;
1537 }
1538 default:
1539 AssertMsgFailed (("Invalid DVD state: %d", dvd.GetState()));
1540 }
1541 detailsReport += sectionTpl
1542 .arg (2 + 1) /* rows */
1543 .arg (":/cd_16px.png", /* icon */
1544 "#dvd", /* link */
1545 tr ("CD/DVD-ROM", "details report"), /* title */
1546 item); // items
1547
1548 /* Floppy */
1549 CFloppyDrive floppy = m.GetFloppyDrive();
1550 item = QString (sSectionItemTpl);
1551 switch (floppy.GetState())
1552 {
1553 case KDriveState_NotMounted:
1554 item = item.arg (tr ("Not mounted", "details report (floppy)"), "");
1555 break;
1556 case KDriveState_ImageMounted:
1557 {
1558 CFloppyImage img = floppy.GetImage();
1559 item = item.arg (tr ("Image", "details report (floppy)"),
1560 prepareFileNameForHTML (img.GetFilePath()));
1561 break;
1562 }
1563 case KDriveState_HostDriveCaptured:
1564 {
1565 CHostFloppyDrive drv = floppy.GetHostDrive();
1566 QString drvName = drv.GetName();
1567 QString description = drv.GetDescription();
1568 QString fullName = description.isEmpty() ?
1569 drvName :
1570 QString ("%1 (%2)").arg (description, drvName);
1571 item = item.arg (tr ("Host Drive", "details report (floppy)"),
1572 fullName);
1573 break;
1574 }
1575 default:
1576 AssertMsgFailed (("Invalid floppy state: %d", floppy.GetState()));
1577 }
1578 detailsReport += sectionTpl
1579 .arg (2 + 1) /* rows */
1580 .arg (":/fd_16px.png", /* icon */
1581 "#floppy", /* link */
1582 tr ("Floppy", "details report"), /* title */
1583 item); /* items */
1584
1585 /* audio */
1586 {
1587 CAudioAdapter audio = m.GetAudioAdapter();
1588 int rows = audio.GetEnabled() ? 3 : 2;
1589 if (audio.GetEnabled())
1590 item = QString (sSectionItemTpl)
1591 .arg (tr ("Host Driver", "details report (audio)"),
1592 toString (audio.GetAudioDriver())) +
1593 QString (sSectionItemTpl)
1594 .arg (tr ("Controller", "details report (audio)"),
1595 toString (audio.GetAudioController()));
1596 else
1597 item = QString (sSectionItemTpl)
1598 .arg (tr ("Disabled", "details report (audio)"), "");
1599
1600 detailsReport += sectionTpl
1601 .arg (rows + 1) /* rows */
1602 .arg (":/sound_16px.png", /* icon */
1603 "#audio", /* link */
1604 tr ("Audio", "details report"), /* title */
1605 item); /* items */
1606 }
1607 /* network */
1608 {
1609 item = QString::null;
1610 ulong count = mVBox.GetSystemProperties().GetNetworkAdapterCount();
1611 int rows = 2; /* including section header and footer */
1612 for (ulong slot = 0; slot < count; slot ++)
1613 {
1614 CNetworkAdapter adapter = m.GetNetworkAdapter (slot);
1615 if (adapter.GetEnabled())
1616 {
1617 KNetworkAttachmentType type = adapter.GetAttachmentType();
1618 QString attType = toString (adapter.GetAdapterType())
1619 .replace (QRegExp ("\\s\\(.+\\)"), " (%1)");
1620 /* don't use the adapter type string for types that have
1621 * an additional symbolic network/interface name field, use
1622 * this name instead */
1623 if (type == KNetworkAttachmentType_HostInterface)
1624 attType = attType.arg (tr ("host interface, %1",
1625 "details report (network)").arg (adapter.GetHostInterface()));
1626 else if (type == KNetworkAttachmentType_Internal)
1627 attType = attType.arg (tr ("internal network, '%1'",
1628 "details report (network)").arg (adapter.GetInternalNetwork()));
1629 else
1630 attType = attType.arg (vboxGlobal().toString (type));
1631
1632 item += QString (sSectionItemTpl)
1633 .arg (tr ("Adapter %1", "details report (network)")
1634 .arg (adapter.GetSlot()))
1635 .arg (attType);
1636 ++ rows;
1637 }
1638 }
1639 if (item.isNull())
1640 {
1641 item = QString (sSectionItemTpl)
1642 .arg (tr ("Disabled", "details report (network)"), "");
1643 ++ rows;
1644 }
1645
1646 detailsReport += sectionTpl
1647 .arg (rows) /* rows */
1648 .arg (":/nw_16px.png", /* icon */
1649 "#network", /* link */
1650 tr ("Network", "details report"), /* title */
1651 item); /* items */
1652 }
1653 /* serial ports */
1654 {
1655 item = QString::null;
1656 ulong count = mVBox.GetSystemProperties().GetSerialPortCount();
1657 int rows = 2; /* including section header and footer */
1658 for (ulong slot = 0; slot < count; slot ++)
1659 {
1660 CSerialPort port = m.GetSerialPort (slot);
1661 if (port.GetEnabled())
1662 {
1663 KPortMode mode = port.GetHostMode();
1664 QString data =
1665 toCOMPortName (port.GetIRQ(), port.GetIOBase()) + ", ";
1666 if (mode == KPortMode_HostPipe ||
1667 mode == KPortMode_HostDevice)
1668 data += QString ("%1 (<nobr>%2</nobr>)")
1669 .arg (vboxGlobal().toString (mode))
1670 .arg (QDir::convertSeparators (port.GetPath()));
1671 else
1672 data += toString (mode);
1673
1674 item += QString (sSectionItemTpl)
1675 .arg (tr ("Port %1", "details report (serial ports)")
1676 .arg (port.GetSlot()))
1677 .arg (data);
1678 ++ rows;
1679 }
1680 }
1681 if (item.isNull())
1682 {
1683 item = QString (sSectionItemTpl)
1684 .arg (tr ("Disabled", "details report (serial ports)"), "");
1685 ++ rows;
1686 }
1687
1688 detailsReport += sectionTpl
1689 .arg (rows) /* rows */
1690 .arg (":/serial_port_16px.png", /* icon */
1691 "#serialPorts", /* link */
1692 tr ("Serial Ports", "details report"), /* title */
1693 item); /* items */
1694 }
1695 /* parallel ports */
1696 {
1697 item = QString::null;
1698 ulong count = mVBox.GetSystemProperties().GetParallelPortCount();
1699 int rows = 2; /* including section header and footer */
1700 for (ulong slot = 0; slot < count; slot ++)
1701 {
1702 CParallelPort port = m.GetParallelPort (slot);
1703 if (port.GetEnabled())
1704 {
1705 QString data =
1706 toLPTPortName (port.GetIRQ(), port.GetIOBase()) +
1707 QString (" (<nobr>%1</nobr>)")
1708 .arg (QDir::convertSeparators (port.GetPath()));
1709
1710 item += QString (sSectionItemTpl)
1711 .arg (tr ("Port %1", "details report (parallel ports)")
1712 .arg (port.GetSlot()))
1713 .arg (data);
1714 ++ rows;
1715 }
1716 }
1717 if (item.isNull())
1718 {
1719 item = QString (sSectionItemTpl)
1720 .arg (tr ("Disabled", "details report (parallel ports)"), "");
1721 ++ rows;
1722 }
1723
1724 /* Temporary disabled */
1725 QString dummy = sectionTpl /* detailsReport += sectionTpl */
1726 .arg (rows) /* rows */
1727 .arg (":/parallel_port_16px.png", /* icon */
1728 "#parallelPorts", /* link */
1729 tr ("Parallel Ports", "details report"), /* title */
1730 item); /* items */
1731 }
1732 /* USB */
1733 {
1734 CUSBController ctl = m.GetUSBController();
1735 if (!ctl.isNull())
1736 {
1737 /* the USB controller may be unavailable (i.e. in VirtualBox OSE) */
1738
1739 if (ctl.GetEnabled())
1740 {
1741 CUSBDeviceFilterCollection coll = ctl.GetDeviceFilters();
1742 CUSBDeviceFilterEnumerator en = coll.Enumerate();
1743 uint active = 0;
1744 while (en.HasMore())
1745 if (en.GetNext().GetActive())
1746 active ++;
1747
1748 item = QString (sSectionItemTpl)
1749 .arg (tr ("Device Filters", "details report (USB)"),
1750 tr ("%1 (%2 active)", "details report (USB)")
1751 .arg (coll.GetCount()).arg (active));
1752 }
1753 else
1754 item = QString (sSectionItemTpl)
1755 .arg (tr ("Disabled", "details report (USB)"), "");
1756
1757 detailsReport += sectionTpl
1758 .arg (2 + 1) /* rows */
1759 .arg (":/usb_16px.png", /* icon */
1760 "#usb", /* link */
1761 tr ("USB", "details report"), /* title */
1762 item); /* items */
1763 }
1764 }
1765 /* Shared folders */
1766 {
1767 ulong count = m.GetSharedFolders().GetCount();
1768 if (count > 0)
1769 {
1770 item = QString (sSectionItemTpl)
1771 .arg (tr ("Shared Folders", "details report (shared folders)"),
1772 tr ("%1", "details report (shadef folders)")
1773 .arg (count));
1774 }
1775 else
1776 item = QString (sSectionItemTpl)
1777 .arg (tr ("None", "details report (shared folders)"), "");
1778
1779 detailsReport += sectionTpl
1780 .arg (2 + 1) /* rows */
1781 .arg (":/shared_folder_16px.png", /* icon */
1782 "#sfolders", /* link */
1783 tr ("Shared Folders", "details report"), /* title */
1784 item); /* items */
1785 }
1786 /* VRDP */
1787 {
1788 CVRDPServer srv = m.GetVRDPServer();
1789 if (!srv.isNull())
1790 {
1791 /* the VRDP server may be unavailable (i.e. in VirtualBox OSE) */
1792
1793 if (srv.GetEnabled())
1794 item = QString (sSectionItemTpl)
1795 .arg (tr ("VRDP Server Port", "details report (VRDP)"),
1796 tr ("%1", "details report (VRDP)")
1797 .arg (srv.GetPort()));
1798 else
1799 item = QString (sSectionItemTpl)
1800 .arg (tr ("Disabled", "details report (VRDP)"), "");
1801
1802 detailsReport += sectionTpl
1803 .arg (2 + 1) /* rows */
1804 .arg (":/vrdp_16px.png", /* icon */
1805 "#vrdp", /* link */
1806 tr ("Remote Display", "details report"), /* title */
1807 item); /* items */
1808 }
1809 }
1810 }
1811
1812 return QString (sTableTpl). arg (detailsReport);
1813}
1814
1815#ifdef Q_WS_X11
1816bool VBoxGlobal::showVirtualBoxLicense()
1817{
1818 /* get the apps doc path */
1819 int size = 256;
1820 char *buffer = (char*) RTMemTmpAlloc (size);
1821 RTPathAppDocs (buffer, size);
1822 QString path (buffer);
1823 RTMemTmpFree (buffer);
1824 QDir docDir (path);
1825 docDir.setFilter (QDir::Files);
1826 docDir.setNameFilters (QStringList ("License-*.html"));
1827
1828 /* get the license files list and search for the latest license */
1829 QStringList filesList = docDir.entryList();
1830 double maxVersionNumber = 0;
1831 for (int index = 0; index < filesList.count(); ++ index)
1832 {
1833 QRegExp regExp ("License-([\\d\\.]+).html");
1834 regExp.indexIn (filesList [index]);
1835 QString version = regExp.cap (1);
1836 if (maxVersionNumber < version.toDouble())
1837 maxVersionNumber = version.toDouble();
1838 }
1839 if (!maxVersionNumber)
1840 {
1841 vboxProblem().cannotFindLicenseFiles (path);
1842 return false;
1843 }
1844
1845 /* compose the latest license file full path */
1846 QString latestVersion = QString::number (maxVersionNumber);
1847 QString latestFilePath = docDir.absoluteFilePath (
1848 QString ("License-%1.html").arg (latestVersion));
1849
1850 /* check for the agreed license version */
1851 QString licenseAgreed = virtualBox().GetExtraData (VBoxDefs::GUI_LicenseKey);
1852 if (licenseAgreed == latestVersion)
1853 return true;
1854
1855 VBoxLicenseViewer licenseDialog (latestFilePath);
1856 bool result = licenseDialog.exec() == QDialog::Accepted;
1857 if (result)
1858 virtualBox().SetExtraData (VBoxDefs::GUI_LicenseKey, latestVersion);
1859 return result;
1860}
1861#endif
1862
1863/**
1864 * Checks if any of the settings files were auto-converted and informs the user
1865 * if so.
1866 */
1867void VBoxGlobal::checkForAutoConvertedSettings()
1868{
1869 QString formatVersion = mVBox.GetSettingsFormatVersion();
1870
1871 bool isGlobalConverted = false;
1872 QList <CMachine> machines;
1873 QString fileList;
1874 QString version;
1875
1876 CMachineVector vec = mVBox.GetMachines2();
1877 for (CMachineVector::ConstIterator m = vec.begin();
1878 m != vec.end(); ++ m)
1879 {
1880 if (!m->GetAccessible())
1881 continue;
1882
1883 version = m->GetSettingsFileVersion();
1884 if (version != formatVersion)
1885 {
1886 machines.append (*m);
1887 fileList += QString ("<nobr>%1&nbsp;&nbsp;&nbsp;(<i>%2</i>)</nobr><br>")
1888 .arg (m->GetSettingsFilePath())
1889 .arg (version);
1890 }
1891 }
1892
1893 version = mVBox.GetSettingsFileVersion();
1894 if (version != formatVersion)
1895 {
1896 isGlobalConverted = true;
1897 fileList += QString ("<nobr>%1&nbsp;&nbsp;&nbsp;(<i>%2</i>)</nobr><br>")
1898 .arg (mVBox.GetSettingsFilePath())
1899 .arg (version);
1900 }
1901
1902
1903 if (!fileList.isNull())
1904 {
1905 int rc = vboxProblem()
1906 .warnAboutAutoConvertedSettings (formatVersion, fileList);
1907
1908 if (rc == QIMessageBox::No || rc == QIMessageBox::Yes)
1909 {
1910 /* backup (optionally) and save all settings files
1911 * (QIMessageBox::No = Backup, QIMessageBox::Yes = Save) */
1912
1913 foreach (CMachine m, machines)
1914 {
1915 CSession session = openSession (m.GetId());
1916 if (!session.isNull())
1917 {
1918 CMachine sm = session.GetMachine();
1919 if (rc == QIMessageBox::No)
1920 sm.SaveSettingsWithBackup();
1921 else
1922 sm.SaveSettings();
1923
1924 if (!sm.isOk())
1925 vboxProblem().cannotSaveMachineSettings (sm);
1926 session.Close();
1927 }
1928 }
1929
1930 if (isGlobalConverted)
1931 {
1932 if (rc == QIMessageBox::No)
1933 mVBox.SaveSettingsWithBackup();
1934 else
1935 mVBox.SaveSettings();
1936
1937 if (!mVBox.isOk())
1938 vboxProblem().cannotSaveGlobalSettings (mVBox);
1939 }
1940 }
1941 }
1942}
1943
1944/**
1945 * Opens a direct session for a machine with the given ID.
1946 * This method does user-friendly error handling (display error messages, etc.).
1947 * and returns a null CSession object in case of any error.
1948 * If this method succeeds, don't forget to close the returned session when
1949 * it is no more necessary.
1950 *
1951 * @param aId Machine ID.
1952 * @param aExisting @c true to open an existing session with the machine
1953 * which is already running, @c false to open a new direct
1954 * session.
1955 */
1956CSession VBoxGlobal::openSession (const QUuid &aId, bool aExisting /* = false */)
1957{
1958 CSession session;
1959 session.createInstance (CLSID_Session);
1960 if (session.isNull())
1961 {
1962 vboxProblem().cannotOpenSession (session);
1963 return session;
1964 }
1965
1966 aExisting ? mVBox.OpenExistingSession (session, aId) :
1967 mVBox.OpenSession (session, aId);
1968
1969 if (!mVBox.isOk())
1970 {
1971 CMachine machine = CVirtualBox (mVBox).GetMachine (aId);
1972 vboxProblem().cannotOpenSession (mVBox, machine);
1973 session.detach();
1974 }
1975
1976 return session;
1977}
1978
1979/**
1980 * Starts a machine with the given ID.
1981 */
1982bool VBoxGlobal::startMachine (const QUuid &id)
1983{
1984 AssertReturn (mValid, false);
1985
1986 CSession session = vboxGlobal().openSession (id);
1987 if (session.isNull())
1988 return false;
1989
1990 return consoleWnd().openView (session);
1991}
1992
1993/**
1994 * Appends the disk object and all its children to the media list.
1995 */
1996static
1997void addMediaToList (VBoxMediaList &aList,
1998 const CUnknown &aDisk,
1999 VBoxDefs::DiskType aType)
2000{
2001 VBoxMedia media (aDisk, aType, VBoxMedia::Unknown);
2002 aList += media;
2003 /* append all vdi children */
2004 if (aType == VBoxDefs::HD)
2005 {
2006 CHardDisk hd = aDisk;
2007 CHardDiskEnumerator enumerator = hd.GetChildren().Enumerate();
2008 while (enumerator.HasMore())
2009 {
2010 CHardDisk subHd = enumerator.GetNext();
2011 addMediaToList (aList, CUnknown (subHd), VBoxDefs::HD);
2012 }
2013 }
2014}
2015
2016/**
2017 * Starts a thread that asynchronously enumerates all currently registered
2018 * media, checks for its accessibility and posts VBoxEnumerateMediaEvent
2019 * events to the VBoxGlobal object until all media is enumerated.
2020 *
2021 * If the enumeration is already in progress, no new thread is started.
2022 *
2023 * @sa #currentMediaList()
2024 * @sa #isMediaEnumerationStarted()
2025 */
2026void VBoxGlobal::startEnumeratingMedia()
2027{
2028 Assert (mValid);
2029
2030 /* check if already started but not yet finished */
2031 if (media_enum_thread)
2032 return;
2033
2034 /* ignore the request during application termination */
2035 if (sVBoxGlobalInCleanup)
2036 return;
2037
2038 /* composes a list of all currently known media & their children */
2039 media_list.clear();
2040 {
2041 CHardDiskEnumerator enHD = mVBox.GetHardDisks().Enumerate();
2042 while (enHD.HasMore())
2043 addMediaToList (media_list, CUnknown (enHD.GetNext()), VBoxDefs::HD);
2044
2045 CDVDImageEnumerator enCD = mVBox.GetDVDImages().Enumerate();
2046 while (enCD.HasMore())
2047 addMediaToList (media_list, CUnknown (enCD.GetNext()), VBoxDefs::CD);
2048
2049 CFloppyImageEnumerator enFD = mVBox.GetFloppyImages().Enumerate();
2050 while (enFD.HasMore())
2051 addMediaToList (media_list, CUnknown (enFD.GetNext()), VBoxDefs::FD);
2052 }
2053
2054 /* enumeration thread class */
2055 class Thread : public QThread
2056 {
2057 public:
2058
2059 Thread (const VBoxMediaList &aList) : mList (aList) {}
2060
2061 virtual void run()
2062 {
2063 LogFlow (("MediaEnumThread started.\n"));
2064 COMBase::InitializeCOM();
2065
2066 CVirtualBox mVBox = vboxGlobal().virtualBox();
2067 QObject *target = &vboxGlobal();
2068
2069 /* enumerating list */
2070 int index = 0;
2071 VBoxMediaList::const_iterator it;
2072 for (it = mList.begin();
2073 it != mList.end() && !sVBoxGlobalInCleanup;
2074 ++ it, ++ index)
2075 {
2076 VBoxMedia media = *it;
2077 switch (media.type)
2078 {
2079 case VBoxDefs::HD:
2080 {
2081 CHardDisk hd = media.disk;
2082 media.status =
2083 hd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2084 hd.isOk() ? VBoxMedia::Inaccessible :
2085 VBoxMedia::Error;
2086 /* assign back to store error info if any */
2087 media.disk = hd;
2088 if (media.status == VBoxMedia::Inaccessible)
2089 {
2090 QUuid machineId = hd.GetMachineId();
2091 if (!machineId.isNull())
2092 {
2093 CMachine machine = mVBox.GetMachine (machineId);
2094 if (!machine.isNull() && (machine.GetState() >= KMachineState_Running))
2095 media.status = VBoxMedia::Ok;
2096 }
2097 }
2098 QApplication::postEvent (target,
2099 new VBoxEnumerateMediaEvent (media, index));
2100 break;
2101 }
2102 case VBoxDefs::CD:
2103 {
2104 CDVDImage cd = media.disk;
2105 media.status =
2106 cd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2107 cd.isOk() ? VBoxMedia::Inaccessible :
2108 VBoxMedia::Error;
2109 /* assign back to store error info if any */
2110 media.disk = cd;
2111 QApplication::postEvent (target,
2112 new VBoxEnumerateMediaEvent (media, index));
2113 break;
2114 }
2115 case VBoxDefs::FD:
2116 {
2117 CFloppyImage fd = media.disk;
2118 media.status =
2119 fd.GetAccessible() == TRUE ? VBoxMedia::Ok :
2120 fd.isOk() ? VBoxMedia::Inaccessible :
2121 VBoxMedia::Error;
2122 /* assign back to store error info if any */
2123 media.disk = fd;
2124 QApplication::postEvent (target,
2125 new VBoxEnumerateMediaEvent (media, index));
2126 break;
2127 }
2128 default:
2129 {
2130 AssertMsgFailed (("Invalid aMedia type\n"));
2131 break;
2132 }
2133 }
2134 }
2135
2136 /* post the last message to indicate the end of enumeration */
2137 if (!sVBoxGlobalInCleanup)
2138 QApplication::postEvent (target, new VBoxEnumerateMediaEvent());
2139
2140 COMBase::CleanupCOM();
2141 LogFlow (("MediaEnumThread finished.\n"));
2142 }
2143
2144 private:
2145
2146 const VBoxMediaList &mList;
2147 };
2148
2149 media_enum_thread = new Thread (media_list);
2150 AssertReturnVoid (media_enum_thread);
2151
2152 /* emit mediaEnumStarted() after we set media_enum_thread to != NULL
2153 * to cause isMediaEnumerationStarted() to return TRUE from slots */
2154 emit mediaEnumStarted();
2155
2156 media_enum_thread->start();
2157}
2158
2159/**
2160 * Adds a new media to the current media list.
2161 * @note Currently, this method does nothing but emits the mediaAdded() signal.
2162 * Later, it will be used to synchronize the current media list with
2163 * the actial media list on the server after a single media opetartion
2164 * performed from within one of our UIs.
2165 * @sa #currentMediaList()
2166 */
2167void VBoxGlobal::addMedia (const VBoxMedia &aMedia)
2168{
2169 emit mediaAdded (aMedia);
2170}
2171
2172/**
2173 * Updates the media in the current media list.
2174 * @note Currently, this method does nothing but emits the mediaUpdated() signal.
2175 * Later, it will be used to synchronize the current media list with
2176 * the actial media list on the server after a single media opetartion
2177 * performed from within one of our UIs.
2178 * @sa #currentMediaList()
2179 */
2180void VBoxGlobal::updateMedia (const VBoxMedia &aMedia)
2181{
2182 emit mediaUpdated (aMedia);
2183}
2184
2185/**
2186 * Removes the media from the current media list.
2187 * @note Currently, this method does nothing but emits the mediaRemoved() signal.
2188 * Later, it will be used to synchronize the current media list with
2189 * the actial media list on the server after a single media opetartion
2190 * performed from within one of our UIs.
2191 * @sa #currentMediaList()
2192 */
2193void VBoxGlobal::removeMedia (VBoxDefs::DiskType aType, const QUuid &aId)
2194{
2195 emit mediaRemoved (aType, aId);
2196}
2197
2198/**
2199 * Searches for a VBoxMedia object representing the given COM media object.
2200 *
2201 * @return true if found and false otherwise.
2202 */
2203bool VBoxGlobal::findMedia (const CUnknown &aObj, VBoxMedia &aMedia) const
2204{
2205 for (VBoxMediaList::ConstIterator it = media_list.begin();
2206 it != media_list.end(); ++ it)
2207 {
2208 if ((*it).disk == aObj)
2209 {
2210 aMedia = (*it);
2211 return true;
2212 }
2213 }
2214
2215 return false;
2216}
2217
2218/**
2219 * Native language name of the currently installed translation.
2220 * Returns "English" if no translation is installed
2221 * or if the translation file is invalid.
2222 */
2223QString VBoxGlobal::languageName() const
2224{
2225
2226 return qApp->translate ("@@@", "English",
2227 "Native language name");
2228}
2229
2230/**
2231 * Native language country name of the currently installed translation.
2232 * Returns "--" if no translation is installed or if the translation file is
2233 * invalid, or if the language is independent on the country.
2234 */
2235QString VBoxGlobal::languageCountry() const
2236{
2237 return qApp->translate ("@@@", "--",
2238 "Native language country name "
2239 "(empty if this language is for all countries)");
2240}
2241
2242/**
2243 * Language name of the currently installed translation, in English.
2244 * Returns "English" if no translation is installed
2245 * or if the translation file is invalid.
2246 */
2247QString VBoxGlobal::languageNameEnglish() const
2248{
2249
2250 return qApp->translate ("@@@", "English",
2251 "Language name, in English");
2252}
2253
2254/**
2255 * Language country name of the currently installed translation, in English.
2256 * Returns "--" if no translation is installed or if the translation file is
2257 * invalid, or if the language is independent on the country.
2258 */
2259QString VBoxGlobal::languageCountryEnglish() const
2260{
2261 return qApp->translate ("@@@", "--",
2262 "Language country name, in English "
2263 "(empty if native country name is empty)");
2264}
2265
2266/**
2267 * Comma-separated list of authors of the currently installed translation.
2268 * Returns "Sun Microsystems, Inc." if no translation is installed or if the
2269 * translation file is invalid, or if the translation is supplied by Sun
2270 * Microsystems, inc.
2271 */
2272QString VBoxGlobal::languageTranslators() const
2273{
2274 return qApp->translate ("@@@", "Sun Microsystems, Inc.",
2275 "Comma-separated list of translators");
2276}
2277
2278/**
2279 * Changes the language of all global string constants according to the
2280 * currently installed translations tables.
2281 */
2282void VBoxGlobal::languageChange()
2283{
2284 machineStates [KMachineState_PoweredOff] = tr ("Powered Off", "MachineState");
2285 machineStates [KMachineState_Saved] = tr ("Saved", "MachineState");
2286 machineStates [KMachineState_Aborted] = tr ("Aborted", "MachineState");
2287 machineStates [KMachineState_Running] = tr ("Running", "MachineState");
2288 machineStates [KMachineState_Paused] = tr ("Paused", "MachineState");
2289 machineStates [KMachineState_Stuck] = tr ("Stuck", "MachineState");
2290 machineStates [KMachineState_Starting] = tr ("Starting", "MachineState");
2291 machineStates [KMachineState_Stopping] = tr ("Stopping", "MachineState");
2292 machineStates [KMachineState_Saving] = tr ("Saving", "MachineState");
2293 machineStates [KMachineState_Restoring] = tr ("Restoring", "MachineState");
2294 machineStates [KMachineState_Discarding] = tr ("Discarding", "MachineState");
2295
2296 sessionStates [KSessionState_Closed] = tr ("Closed", "SessionState");
2297 sessionStates [KSessionState_Open] = tr ("Open", "SessionState");
2298 sessionStates [KSessionState_Spawning] = tr ("Spawning", "SessionState");
2299 sessionStates [KSessionState_Closing] = tr ("Closing", "SessionState");
2300
2301 deviceTypes [KDeviceType_Null] = tr ("None", "DeviceType");
2302 deviceTypes [KDeviceType_Floppy] = tr ("Floppy", "DeviceType");
2303 deviceTypes [KDeviceType_DVD] = tr ("CD/DVD-ROM", "DeviceType");
2304 deviceTypes [KDeviceType_HardDisk] = tr ("Hard Disk", "DeviceType");
2305 deviceTypes [KDeviceType_Network] = tr ("Network", "DeviceType");
2306 deviceTypes [KDeviceType_USB] = tr ("USB", "DeviceType");
2307 deviceTypes [KDeviceType_SharedFolder] = tr ("Shared Folder", "DeviceType");
2308
2309 storageBuses [KStorageBus_IDE] =
2310 tr ("IDE", "StorageBus");
2311 storageBuses [KStorageBus_SATA] =
2312 tr ("SATA", "StorageBus");
2313
2314 Assert (storageBusChannels.count() == 3);
2315 storageBusChannels [0] =
2316 tr ("Primary", "StorageBusChannel");
2317 storageBusChannels [1] =
2318 tr ("Secondary", "StorageBusChannel");
2319 storageBusChannels [2] =
2320 tr ("Channel&nbsp;%1", "StorageBusChannel");
2321
2322 Assert (storageBusDevices.count() == 3);
2323 storageBusDevices [0] = tr ("Master", "StorageBusDevice");
2324 storageBusDevices [1] = tr ("Slave", "StorageBusDevice");
2325 storageBusDevices [2] = tr ("Device&nbsp;%1", "StorageBusDevice");
2326
2327 diskTypes [KHardDiskType_Normal] =
2328 tr ("Normal", "DiskType");
2329 diskTypes [KHardDiskType_Immutable] =
2330 tr ("Immutable", "DiskType");
2331 diskTypes [KHardDiskType_Writethrough] =
2332 tr ("Writethrough", "DiskType");
2333
2334 diskStorageTypes [KHardDiskStorageType_VirtualDiskImage] =
2335 tr ("Virtual Disk Image", "DiskStorageType");
2336 diskStorageTypes [KHardDiskStorageType_ISCSIHardDisk] =
2337 tr ("iSCSI", "DiskStorageType");
2338 diskStorageTypes [KHardDiskStorageType_VMDKImage] =
2339 tr ("VMDK Image", "DiskStorageType");
2340 diskStorageTypes [KHardDiskStorageType_CustomHardDisk] =
2341 tr ("Custom Hard Disk", "DiskStorageType");
2342 diskStorageTypes [KHardDiskStorageType_VHDImage] =
2343 tr ("VHD Image", "DiskStorageType");
2344
2345 vrdpAuthTypes [KVRDPAuthType_Null] =
2346 tr ("Null", "VRDPAuthType");
2347 vrdpAuthTypes [KVRDPAuthType_External] =
2348 tr ("External", "VRDPAuthType");
2349 vrdpAuthTypes [KVRDPAuthType_Guest] =
2350 tr ("Guest", "VRDPAuthType");
2351
2352 portModeTypes [KPortMode_Disconnected] =
2353 tr ("Disconnected", "PortMode");
2354 portModeTypes [KPortMode_HostPipe] =
2355 tr ("Host Pipe", "PortMode");
2356 portModeTypes [KPortMode_HostDevice] =
2357 tr ("Host Device", "PortMode");
2358
2359 usbFilterActionTypes [KUSBDeviceFilterAction_Ignore] =
2360 tr ("Ignore", "USBFilterActionType");
2361 usbFilterActionTypes [KUSBDeviceFilterAction_Hold] =
2362 tr ("Hold", "USBFilterActionType");
2363
2364 audioDriverTypes [KAudioDriverType_Null] =
2365 tr ("Null Audio Driver", "AudioDriverType");
2366 audioDriverTypes [KAudioDriverType_WinMM] =
2367 tr ("Windows Multimedia", "AudioDriverType");
2368 audioDriverTypes [KAudioDriverType_SolAudio] =
2369 tr ("Solaris Audio", "AudioDriverType");
2370 audioDriverTypes [KAudioDriverType_OSS] =
2371 tr ("OSS Audio Driver", "AudioDriverType");
2372 audioDriverTypes [KAudioDriverType_ALSA] =
2373 tr ("ALSA Audio Driver", "AudioDriverType");
2374 audioDriverTypes [KAudioDriverType_DirectSound] =
2375 tr ("Windows DirectSound", "AudioDriverType");
2376 audioDriverTypes [KAudioDriverType_CoreAudio] =
2377 tr ("CoreAudio", "AudioDriverType");
2378 audioDriverTypes [KAudioDriverType_Pulse] =
2379 tr ("PulseAudio", "AudioDriverType");
2380
2381 audioControllerTypes [KAudioControllerType_AC97] =
2382 tr ("ICH AC97", "AudioControllerType");
2383 audioControllerTypes [KAudioControllerType_SB16] =
2384 tr ("SoundBlaster 16", "AudioControllerType");
2385
2386 networkAdapterTypes [KNetworkAdapterType_Am79C970A] =
2387 tr ("PCnet-PCI II (Am79C970A)", "NetworkAdapterType");
2388 networkAdapterTypes [KNetworkAdapterType_Am79C973] =
2389 tr ("PCnet-FAST III (Am79C973)", "NetworkAdapterType");
2390 networkAdapterTypes [KNetworkAdapterType_I82540EM] =
2391 tr ("Intel PRO/1000 MT Desktop (82540EM)", "NetworkAdapterType");
2392 networkAdapterTypes [KNetworkAdapterType_I82543GC] =
2393 tr ("Intel PRO/1000 T Server (82543GC)", "NetworkAdapterType");
2394
2395 networkAttachmentTypes [KNetworkAttachmentType_Null] =
2396 tr ("Not attached", "NetworkAttachmentType");
2397 networkAttachmentTypes [KNetworkAttachmentType_NAT] =
2398 tr ("NAT", "NetworkAttachmentType");
2399 networkAttachmentTypes [KNetworkAttachmentType_HostInterface] =
2400 tr ("Host Interface", "NetworkAttachmentType");
2401 networkAttachmentTypes [KNetworkAttachmentType_Internal] =
2402 tr ("Internal Network", "NetworkAttachmentType");
2403
2404 clipboardTypes [KClipboardMode_Disabled] =
2405 tr ("Disabled", "ClipboardType");
2406 clipboardTypes [KClipboardMode_HostToGuest] =
2407 tr ("Host To Guest", "ClipboardType");
2408 clipboardTypes [KClipboardMode_GuestToHost] =
2409 tr ("Guest To Host", "ClipboardType");
2410 clipboardTypes [KClipboardMode_Bidirectional] =
2411 tr ("Bidirectional", "ClipboardType");
2412
2413 ideControllerTypes [KIDEControllerType_PIIX3] =
2414 tr ("PIIX3", "IDEControllerType");
2415 ideControllerTypes [KIDEControllerType_PIIX4] =
2416 tr ("PIIX4", "IDEControllerType");
2417
2418 USBDeviceStates [KUSBDeviceState_NotSupported] =
2419 tr ("Not supported", "USBDeviceState");
2420 USBDeviceStates [KUSBDeviceState_Unavailable] =
2421 tr ("Unavailable", "USBDeviceState");
2422 USBDeviceStates [KUSBDeviceState_Busy] =
2423 tr ("Busy", "USBDeviceState");
2424 USBDeviceStates [KUSBDeviceState_Available] =
2425 tr ("Available", "USBDeviceState");
2426 USBDeviceStates [KUSBDeviceState_Held] =
2427 tr ("Held", "USBDeviceState");
2428 USBDeviceStates [KUSBDeviceState_Captured] =
2429 tr ("Captured", "USBDeviceState");
2430
2431 mUserDefinedPortName = tr ("User-defined", "serial port");
2432
2433 detailReportTemplatesReady = false;
2434
2435#if defined (Q_WS_PM) || defined (Q_WS_X11)
2436 /* As PM and X11 do not (to my knowledge) have functionality for providing
2437 * human readable key names, we keep a table of them, which must be
2438 * updated when the language is changed. */
2439#warning port me
2440 QIHotKeyEdit::languageChange_qt3();
2441#endif
2442}
2443
2444// public static stuff
2445////////////////////////////////////////////////////////////////////////////////
2446
2447/* static */
2448bool VBoxGlobal::isDOSType (const QString &aOSTypeId)
2449{
2450 if (aOSTypeId.left (3) == "dos" ||
2451 aOSTypeId.left (3) == "win" ||
2452 aOSTypeId.left (3) == "os2")
2453 return true;
2454
2455 return false;
2456}
2457
2458/**
2459 * Sets the QLabel background and frame colors according tho the pixmap
2460 * contents. The bottom right pixel of the label pixmap defines the
2461 * background color of the label, the top right pixel defines the color of
2462 * the one-pixel frame around it. This function also sets the alignment of
2463 * the pixmap to AlignVTop (to correspond to the color choosing logic).
2464 *
2465 * This method is useful to provide nice scaling of pixmal labels without
2466 * scaling pixmaps themselves. To see th eeffect, the size policy of the
2467 * label in the corresponding direction (vertical, for now) should be set to
2468 * something like MinimumExpanding.
2469 *
2470 * @todo Parametrize corners to select pixels from and set the alignment
2471 * accordingly.
2472 */
2473/* static */
2474void VBoxGlobal::adoptLabelPixmap (QLabel *aLabel)
2475{
2476 AssertReturnVoid (aLabel);
2477
2478 aLabel->setAlignment (Qt::AlignTop);
2479 aLabel->setFrameShape (QFrame::Box);
2480 aLabel->setFrameShadow (QFrame::Plain);
2481
2482 const QPixmap *pix = aLabel->pixmap();
2483 QImage img = pix->toImage();
2484 QRgb rgbBack = img.pixel (img.width() - 1, img.height() - 1);
2485 QRgb rgbFrame = img.pixel (img.width() - 1, 0);
2486
2487 QPalette pal = aLabel->palette();
2488 pal.setColor (QPalette::Window, rgbBack);
2489 pal.setColor (QPalette::WindowText, rgbFrame);
2490 aLabel->setPalette (pal);
2491}
2492
2493extern const char *gVBoxLangSubDir = "/nls4";
2494extern const char *gVBoxLangFileBase = "VirtualBox_";
2495extern const char *gVBoxLangFileExt = ".qm";
2496extern const char *gVBoxLangIDRegExp = "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
2497extern const char *gVBoxBuiltInLangName = "C";
2498
2499class VBoxTranslator : public QTranslator
2500{
2501public:
2502
2503 VBoxTranslator (QObject *aParent = 0)
2504 : QTranslator (aParent) {}
2505
2506 bool loadFile (const QString &aFileName)
2507 {
2508 QFile file (aFileName);
2509 if (!file.open (QIODevice::ReadOnly))
2510 return false;
2511 mData = file.readAll();
2512 return load ((uchar*) mData.data(), mData.size());
2513 }
2514
2515private:
2516
2517 QByteArray mData;
2518};
2519
2520static VBoxTranslator *sTranslator = 0;
2521static QString sLoadedLangId = gVBoxBuiltInLangName;
2522
2523/**
2524 * Returns the loaded (active) language ID.
2525 * Note that it may not match with VBoxGlobalSettings::languageId() if the
2526 * specified language cannot be loaded.
2527 * If the built-in language is active, this method returns "C".
2528 *
2529 * @note "C" is treated as the built-in language for simplicity -- the C
2530 * locale is used in unix environments as a fallback when the requested
2531 * locale is invalid. This way we don't need to process both the "built_in"
2532 * language and the "C" language (which is a valid environment setting)
2533 * separately.
2534 */
2535/* static */
2536QString VBoxGlobal::languageId()
2537{
2538 return sLoadedLangId;
2539}
2540
2541/**
2542 * Loads the language by language ID.
2543 *
2544 * @param aLangId Language ID in in form of xx_YY. QString::null means the
2545 * system default language.
2546 */
2547/* static */
2548void VBoxGlobal::loadLanguage (const QString &aLangId)
2549{
2550 QString langId = aLangId.isNull() ?
2551 VBoxGlobal::systemLanguageId() : aLangId;
2552 QString languageFileName;
2553 QString selectedLangId = gVBoxBuiltInLangName;
2554
2555 char szNlsPath[RTPATH_MAX];
2556 int rc;
2557
2558 rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
2559 Assert(RT_SUCCESS(rc));
2560
2561 QString nlsPath = QString(szNlsPath) + gVBoxLangSubDir;
2562 QDir nlsDir (nlsPath);
2563
2564 Assert (!langId.isEmpty());
2565 if (!langId.isEmpty() && langId != gVBoxBuiltInLangName)
2566 {
2567 QRegExp regExp (gVBoxLangIDRegExp);
2568 int pos = regExp.indexIn (langId);
2569 /* the language ID should match the regexp completely */
2570 AssertReturnVoid (pos == 0);
2571
2572 QString lang = regExp.cap (2);
2573
2574 if (nlsDir.exists (gVBoxLangFileBase + langId + gVBoxLangFileExt))
2575 {
2576 languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + langId +
2577 gVBoxLangFileExt);
2578 selectedLangId = langId;
2579 }
2580 else if (nlsDir.exists (gVBoxLangFileBase + lang + gVBoxLangFileExt))
2581 {
2582 languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + lang +
2583 gVBoxLangFileExt);
2584 selectedLangId = lang;
2585 }
2586 else
2587 {
2588 /* Never complain when the default language is requested. In any
2589 * case, if no explicit language file exists, we will simply
2590 * fall-back to English (built-in). */
2591 if (!aLangId.isNull())
2592 vboxProblem().cannotFindLanguage (langId, nlsPath);
2593 /* selectedLangId remains built-in here */
2594 AssertReturnVoid (selectedLangId == gVBoxBuiltInLangName);
2595 }
2596 }
2597
2598 /* delete the old translator if there is one */
2599 if (sTranslator)
2600 {
2601 /* QTranslator destructor will call qApp->removeTranslator() for
2602 * us. It will also delete all its child translations we attach to it
2603 * below, so we don't have to care about them specially. */
2604 delete sTranslator;
2605 }
2606
2607 /* load new language files */
2608 sTranslator = new VBoxTranslator (qApp);
2609 Assert (sTranslator);
2610 bool loadOk = true;
2611 if (sTranslator)
2612 {
2613 if (selectedLangId != gVBoxBuiltInLangName)
2614 {
2615 Assert (!languageFileName.isNull());
2616 loadOk = sTranslator->loadFile (languageFileName);
2617 }
2618 /* we install the translator in any case: on failure, this will
2619 * activate an empty translator that will give us English
2620 * (built-in) */
2621 qApp->installTranslator (sTranslator);
2622 }
2623 else
2624 loadOk = false;
2625
2626 if (loadOk)
2627 sLoadedLangId = selectedLangId;
2628 else
2629 {
2630 vboxProblem().cannotLoadLanguage (languageFileName);
2631 sLoadedLangId = gVBoxBuiltInLangName;
2632 }
2633
2634 /* Try to load the corresponding Qt translation */
2635 if (sLoadedLangId != gVBoxBuiltInLangName)
2636 {
2637#ifdef Q_OS_UNIX
2638 /* We use system installations of Qt on Linux systems, so first, try
2639 * to load the Qt translation from the system location. */
2640 languageFileName = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" +
2641 sLoadedLangId + gVBoxLangFileExt;
2642 QTranslator *qtSysTr = new QTranslator (sTranslator);
2643 Assert (qtSysTr);
2644 if (qtSysTr && qtSysTr->load (languageFileName))
2645 qApp->installTranslator (qtSysTr);
2646 /* Note that the Qt translation supplied by Sun is always loaded
2647 * afterwards to make sure it will take precedence over the system
2648 * translation (it may contain more decent variants of translation
2649 * that better correspond to VirtualBox UI). We need to load both
2650 * because a newer version of Qt may be installed on the user computer
2651 * and the Sun version may not fully support it. We don't do it on
2652 * Win32 because we supply a Qt library there and therefore the
2653 * Sun translation is always the best one. */
2654#endif
2655 languageFileName = nlsDir.absoluteFilePath (QString ("qt_") +
2656 sLoadedLangId +
2657 gVBoxLangFileExt);
2658 QTranslator *qtTr = new QTranslator (sTranslator);
2659 Assert (qtTr);
2660 if (qtTr && (loadOk = qtTr->load (languageFileName)))
2661 qApp->installTranslator (qtTr);
2662 /* The below message doesn't fit 100% (because it's an additonal
2663 * language and the main one won't be reset to built-in on failure)
2664 * but the load failure is so rare here that it's not worth a separate
2665 * message (but still, having something is better than having none) */
2666 if (!loadOk && !aLangId.isNull())
2667 vboxProblem().cannotLoadLanguage (languageFileName);
2668 }
2669}
2670
2671/* static */
2672QIcon VBoxGlobal::iconSet (const char *aNormal,
2673 const char *aDisabled /* = NULL */,
2674 const char *aActive /* = NULL */)
2675{
2676 QIcon iconSet;
2677
2678 iconSet.addFile (aNormal, QSize(),
2679 QIcon::Normal);
2680 if (aDisabled != NULL)
2681 iconSet.addFile (aDisabled, QSize(),
2682 QIcon::Disabled);
2683 if (aActive != NULL)
2684 iconSet.addFile (aActive, QSize(),
2685 QIcon::Active);
2686 return iconSet;
2687}
2688
2689/* static */
2690QIcon VBoxGlobal::
2691iconSetEx (const char *aNormal, const char *aSmallNormal,
2692 const char *aDisabled /* = NULL */,
2693 const char *aSmallDisabled /* = NULL */,
2694 const char *aActive /* = NULL */,
2695 const char *aSmallActive /* = NULL */)
2696{
2697 QIcon iconSet;
2698
2699 iconSet.addFile (aNormal, QSize(), QIcon::Normal);
2700 iconSet.addFile (aSmallNormal, QSize(), QIcon::Normal);
2701 if (aSmallDisabled != NULL)
2702 {
2703 iconSet.addFile (aDisabled, QSize(), QIcon::Disabled);
2704 iconSet.addFile (aSmallDisabled, QSize(), QIcon::Disabled);
2705 }
2706 if (aSmallActive != NULL)
2707 {
2708 iconSet.addFile (aActive, QSize(), QIcon::Active);
2709 iconSet.addFile (aSmallActive, QSize(), QIcon::Active);
2710 }
2711
2712 return iconSet;
2713}
2714
2715/**
2716 * Replacement for QToolButton::setTextLabel() that handles the shortcut
2717 * letter (if it is present in the argument string) as if it were a setText()
2718 * call: the shortcut letter is used to automatically assign an "Alt+<letter>"
2719 * accelerator key sequence to the given tool button.
2720 *
2721 * @note This method preserves the icon set if it was assigned before. Only
2722 * the text label and the accelerator are changed.
2723 *
2724 * @param aToolButton Tool button to set the text label on.
2725 * @param aTextLabel Text label to set.
2726 */
2727/* static */
2728void VBoxGlobal::setTextLabel (QToolButton *aToolButton,
2729 const QString &aTextLabel)
2730{
2731 AssertReturnVoid (aToolButton != NULL);
2732
2733 /* remember the icon set as setText() will kill it */
2734 QIcon iset = aToolButton->icon();
2735 /* re-use the setText() method to detect and set the accelerator */
2736 aToolButton->setText (aTextLabel);
2737 QKeySequence accel = aToolButton->shortcut();
2738 aToolButton->setText (aTextLabel);
2739 aToolButton->setIcon (iset);
2740 /* set the accel last as setIconSet() would kill it */
2741 aToolButton->setShortcut (accel);
2742}
2743
2744/**
2745 * Ensures that the given rectangle \a aRect is fully contained within the
2746 * rectangle \a aBoundRect by moving \a aRect if necessary. If \a aRect is
2747 * larger than \a aBoundRect, its top left corner is simply aligned with the
2748 * top left corner of \a aRect and, if \a aCanResize is true, \a aRect is
2749 * shrinked to become fully visible.
2750 */
2751/* static */
2752QRect VBoxGlobal::normalizeGeometry (const QRect &aRect, const QRect &aBoundRect,
2753 bool aCanResize /* = true */)
2754{
2755 QRect fr = aRect;
2756
2757 /* make the bottom right corner visible */
2758 int rd = aBoundRect.right() - fr.right();
2759 int bd = aBoundRect.bottom() - fr.bottom();
2760 fr.translate (rd < 0 ? rd : 0, bd < 0 ? bd : 0);
2761
2762 /* ensure the top left corner is visible */
2763 int ld = fr.left() - aBoundRect.left();
2764 int td = fr.top() - aBoundRect.top();
2765 fr.translate (ld < 0 ? -ld : 0, td < 0 ? -td : 0);
2766
2767 if (aCanResize)
2768 {
2769 /* adjust the size to make the rectangle fully contained */
2770 rd = aBoundRect.right() - fr.right();
2771 bd = aBoundRect.bottom() - fr.bottom();
2772 if (rd < 0)
2773 fr.setRight (fr.right() + rd);
2774 if (bd < 0)
2775 fr.setBottom (fr.bottom() + bd);
2776 }
2777
2778 return fr;
2779}
2780
2781/**
2782 * Aligns the center of \a aWidget with the center of \a aRelative.
2783 *
2784 * If necessary, \a aWidget's position is adjusted to make it fully visible
2785 * within the available desktop area. If \a aWidget is bigger then this area,
2786 * it will also be resized unless \a aCanResize is false or there is an
2787 * inappropriate minimum size limit (in which case the top left corner will be
2788 * simply aligned with the top left corner of the available desktop area).
2789 *
2790 * \a aWidget must be a top-level widget. \a aRelative may be any widget, but
2791 * if it's not top-level itself, its top-level widget will be used for
2792 * calculations. \a aRelative can also be NULL, in which case \a aWidget will
2793 * be centered relative to the available desktop area.
2794 */
2795/* static */
2796void VBoxGlobal::centerWidget (QWidget *aWidget, QWidget *aRelative,
2797 bool aCanResize /* = true */)
2798{
2799 AssertReturnVoid (aWidget);
2800 AssertReturnVoid (aWidget->isTopLevel());
2801
2802 QRect deskGeo, parentGeo;
2803 QWidget *w = aRelative;
2804 if (w)
2805 {
2806 w = w->topLevelWidget();
2807 deskGeo = QApplication::desktop()->availableGeometry (w);
2808 parentGeo = w->frameGeometry();
2809 /* On X11/Gnome, geo/frameGeo.x() and y() are always 0 for top level
2810 * widgets with parents, what a shame. Use mapToGlobal() to workaround. */
2811 QPoint d = w->mapToGlobal (QPoint (0, 0));
2812 d.rx() -= w->geometry().x() - w->x();
2813 d.ry() -= w->geometry().y() - w->y();
2814 parentGeo.moveTopLeft (d);
2815 }
2816 else
2817 {
2818 deskGeo = QApplication::desktop()->availableGeometry();
2819 parentGeo = deskGeo;
2820 }
2821
2822 /* On X11, there is no way to determine frame geometry (including WM
2823 * decorations) before the widget is shown for the first time. Stupidly
2824 * enumerate other top level widgets to find the thickest frame. The code
2825 * is based on the idea taken from QDialog::adjustPositionInternal(). */
2826
2827 int extraw = 0, extrah = 0;
2828
2829 QWidgetList list = QApplication::topLevelWidgets();
2830 QListIterator<QWidget*> it (list);
2831 while ((extraw == 0 || extrah == 0) && it.hasNext())
2832 {
2833 int framew, frameh;
2834 QWidget *current = it.next();
2835 if (!current->isVisible())
2836 continue;
2837
2838 framew = current->frameGeometry().width() - current->width();
2839 frameh = current->frameGeometry().height() - current->height();
2840
2841 extraw = QMAX (extraw, framew);
2842 extrah = QMAX (extrah, frameh);
2843 }
2844
2845 /// @todo (r=dmik) not sure if we really need this
2846#if 0
2847 /* sanity check for decoration frames. With embedding, we
2848 * might get extraordinary values */
2849 if (extraw == 0 || extrah == 0 || extraw > 20 || extrah > 50)
2850 {
2851 extrah = 50;
2852 extraw = 20;
2853 }
2854#endif
2855
2856 /* On non-X11 platforms, the following would be enough instead of the
2857 * above workaround: */
2858 // QRect geo = frameGeometry();
2859 QRect geo = QRect (0, 0, aWidget->width() + extraw,
2860 aWidget->height() + extrah);
2861
2862 geo.moveCenter (QPoint (parentGeo.x() + (parentGeo.width() - 1) / 2,
2863 parentGeo.y() + (parentGeo.height() - 1) / 2));
2864
2865 /* ensure the widget is within the available desktop area */
2866 QRect newGeo = normalizeGeometry (geo, deskGeo, aCanResize);
2867
2868 aWidget->move (newGeo.topLeft());
2869
2870 if (aCanResize &&
2871 (geo.width() != newGeo.width() || geo.height() != newGeo.height()))
2872 aWidget->resize (newGeo.width() - extraw, newGeo.height() - extrah);
2873}
2874
2875/**
2876 * Returns the decimal separator for the current locale.
2877 */
2878/* static */
2879QChar VBoxGlobal::decimalSep()
2880{
2881 return QLocale::system().decimalPoint();
2882}
2883
2884/**
2885 * Returns the regexp string that defines the format of the human-readable
2886 * size representation, <tt>####[.##] B|KB|MB|GB|TB|PB</tt>.
2887 *
2888 * This regexp will capture 5 groups of text:
2889 * - cap(1): integer number in case when no decimal point is present
2890 * (if empty, it means that decimal point is present)
2891 * - cap(2): size suffix in case when no decimal point is present (may be empty)
2892 * - cap(3): integer number in case when decimal point is present (may be empty)
2893 * - cap(4): fraction number (hundredth) in case when decimal point is present
2894 * - cap(5): size suffix in case when decimal point is present (note that
2895 * B cannot appear there)
2896 */
2897/* static */
2898QString VBoxGlobal::sizeRegexp()
2899{
2900 QString regexp =
2901 QString ("^(?:(?:(\\d+)(?:\\s?([KMGTP]?B))?)|(?:(\\d*)%1(\\d{1,2})(?:\\s?([KMGTP]B))))$")
2902 .arg (decimalSep());
2903 return regexp;
2904}
2905
2906/**
2907 * Parses the given size string that should be in form of
2908 * <tt>####[.##] B|KB|MB|GB|TB|PB</tt> and returns the size value
2909 * in bytes. Zero is returned on error.
2910 */
2911/* static */
2912Q_UINT64 VBoxGlobal::parseSize (const QString &aText)
2913{
2914 QRegExp regexp (sizeRegexp());
2915 int pos = regexp.indexIn (aText);
2916 if (pos != -1)
2917 {
2918 QString intgS = regexp.cap (1);
2919 QString hundS;
2920 QString suff = regexp.cap (2);
2921 if (intgS.isEmpty())
2922 {
2923 intgS = regexp.cap (3);
2924 hundS = regexp.cap (4);
2925 suff = regexp.cap (5);
2926 }
2927
2928 Q_UINT64 denom = 0;
2929 if (suff.isEmpty() || suff == "B")
2930 denom = 1;
2931 else if (suff == "KB")
2932 denom = _1K;
2933 else if (suff == "MB")
2934 denom = _1M;
2935 else if (suff == "GB")
2936 denom = _1G;
2937 else if (suff == "TB")
2938 denom = _1T;
2939 else if (suff == "PB")
2940 denom = _1P;
2941
2942 Q_UINT64 intg = intgS.toULongLong();
2943 if (denom == 1)
2944 return intg;
2945
2946 Q_UINT64 hund = hundS.leftJustified (2, '0').toULongLong();
2947 hund = hund * denom / 100;
2948 intg = intg * denom + hund;
2949 return intg;
2950 }
2951 else
2952 return 0;
2953}
2954
2955/**
2956 * Formats the given \a size value in bytes to a human readable string
2957 * in form of <tt>####[.##] B|KB|MB|GB|TB|PB</tt>.
2958 *
2959 * The \a mode parameter is used for resulting numbers that get a fractional
2960 * part after converting the \a size to KB, MB etc:
2961 * <ul>
2962 * <li>When \a mode is 0, the result is rounded to the closest number
2963 * containing two decimal digits.
2964 * </li>
2965 * <li>When \a mode is -1, the result is rounded to the largest two decimal
2966 * digit number that is not greater than the result. This guarantees that
2967 * converting the resulting string back to the integer value in bytes
2968 * will not produce a value greater that the initial \a size parameter.
2969 * </li>
2970 * <li>When \a mode is 1, the result is rounded to the smallest two decimal
2971 * digit number that is not less than the result. This guarantees that
2972 * converting the resulting string back to the integer value in bytes
2973 * will not produce a value less that the initial \a size parameter.
2974 * </li>
2975 * </ul>
2976 *
2977 * @param aSize size value in bytes
2978 * @param aMode convertion mode (-1, 0 or 1)
2979 * @return human-readable size string
2980 */
2981/* static */
2982QString VBoxGlobal::formatSize (Q_UINT64 aSize, int aMode /* = 0 */)
2983{
2984 static const char *Suffixes [] = { "B", "KB", "MB", "GB", "TB", "PB", NULL };
2985
2986 Q_UINT64 denom = 0;
2987 int suffix = 0;
2988
2989 if (aSize < _1K)
2990 {
2991 denom = 1;
2992 suffix = 0;
2993 }
2994 else if (aSize < _1M)
2995 {
2996 denom = _1K;
2997 suffix = 1;
2998 }
2999 else if (aSize < _1G)
3000 {
3001 denom = _1M;
3002 suffix = 2;
3003 }
3004 else if (aSize < _1T)
3005 {
3006 denom = _1G;
3007 suffix = 3;
3008 }
3009 else if (aSize < _1P)
3010 {
3011 denom = _1T;
3012 suffix = 4;
3013 }
3014 else
3015 {
3016 denom = _1P;
3017 suffix = 5;
3018 }
3019
3020 Q_UINT64 intg = aSize / denom;
3021 Q_UINT64 hund = aSize % denom;
3022
3023 QString number;
3024 if (denom > 1)
3025 {
3026 if (hund)
3027 {
3028 hund *= 100;
3029 /* not greater */
3030 if (aMode < 0) hund = hund / denom;
3031 /* not less */
3032 else if (aMode > 0) hund = (hund + denom - 1) / denom;
3033 /* nearest */
3034 else hund = (hund + denom / 2) / denom;
3035 }
3036 /* check for the fractional part overflow due to rounding */
3037 if (hund == 100)
3038 {
3039 hund = 0;
3040 ++ intg;
3041 /* check if we've got 1024 XB after rounding and scale down if so */
3042 if (intg == 1024 && Suffixes [suffix + 1] != NULL)
3043 {
3044 intg /= 1024;
3045 ++ suffix;
3046 }
3047 }
3048 number = QString ("%1%2%3").arg (intg).arg (decimalSep())
3049 .arg (QString::number (hund).rightJustified (2, '0'));
3050 }
3051 else
3052 {
3053 number = QString::number (intg);
3054 }
3055
3056 return QString ("%1 %2").arg (number).arg (Suffixes [suffix]);
3057}
3058
3059/**
3060 * Reformats the input string @a aStr so that:
3061 * - strings in single quotes will be put inside <nobr> and marked
3062 * with blue color;
3063 * - UUIDs be put inside <nobr> and marked
3064 * with green color;
3065 * - replaces new line chars with </p><p> constructs to form paragraphs
3066 * (note that <p> and </p> are not appended to the beginnign and to the
3067 * end of the string respectively, to allow the result be appended
3068 * or prepended to the existing paragraph).
3069 *
3070 * If @a aToolTip is true, colouring is not applied, only the <nobr> tag
3071 * is added. Also, new line chars are replaced with <br> instead of <p>.
3072 */
3073/* static */
3074QString VBoxGlobal::highlight (const QString &aStr, bool aToolTip /* = false */)
3075{
3076 QString strFont;
3077 QString uuidFont;
3078 QString endFont;
3079 if (!aToolTip)
3080 {
3081 strFont = "<font color=#0000CC>";
3082 uuidFont = "<font color=#008000>";
3083 endFont = "</font>";
3084 }
3085
3086 QString text = aStr;
3087
3088 /* replace special entities, '&' -- first! */
3089 text.replace ('&', "&amp;");
3090 text.replace ('<', "&lt;");
3091 text.replace ('>', "&gt;");
3092 text.replace ('\"', "&quot;");
3093
3094 /* mark strings in single quotes with color */
3095 QRegExp rx = QRegExp ("((?:^|\\s)[(]?)'([^']*)'(?=[:.-!);]?(?:\\s|$))");
3096 rx.setMinimal (true);
3097 text.replace (rx,
3098 QString ("\\1%1<nobr>'\\2'</nobr>%2")
3099 .arg (strFont). arg (endFont));
3100
3101 /* mark UUIDs with color */
3102 text.replace (QRegExp (
3103 "((?:^|\\s)[(]?)"
3104 "(\\{[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}\\})"
3105 "(?=[:.-!);]?(?:\\s|$))"),
3106 QString ("\\1%1<nobr>\\2</nobr>%2")
3107 .arg (uuidFont). arg (endFont));
3108
3109 /* split to paragraphs at \n chars */
3110 if (!aToolTip)
3111 text.replace ('\n', "</p><p>");
3112 else
3113 text.replace ('\n', "<br>");
3114
3115 return text;
3116}
3117
3118/**
3119 * This does exactly the same as QLocale::system().name() but corrects its
3120 * wrong behavior on Linux systems (LC_NUMERIC for some strange reason takes
3121 * precedence over any other locale setting in the QLocale::system()
3122 * implementation). This implementation first looks at LC_ALL (as defined by
3123 * SUS), then looks at LC_MESSAGES which is designed to define a language for
3124 * program messages in case if it differs from the language for other locale
3125 * categories. Then it looks for LANG and finally falls back to
3126 * QLocale::system().name().
3127 *
3128 * The order of precedence is well defined here:
3129 * http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
3130 *
3131 * @note This method will return "C" when the requested locale is invalid or
3132 * when the "C" locale is set explicitly.
3133 */
3134/* static */
3135QString VBoxGlobal::systemLanguageId()
3136{
3137#ifdef Q_OS_UNIX
3138 const char *s = RTEnvGet ("LC_ALL");
3139 if (s == 0)
3140 s = RTEnvGet ("LC_MESSAGES");
3141 if (s == 0)
3142 s = RTEnvGet ("LANG");
3143 if (s != 0)
3144 return QLocale (s).name();
3145#endif
3146 return QLocale::system().name();
3147}
3148
3149/**
3150 * Reimplementation of QFileDialog::getExistingDirectory() that removes some
3151 * oddities and limitations.
3152 *
3153 * On Win32, this function makes sure a native dialog is launched in
3154 * another thread to avoid dialog visualization errors occuring due to
3155 * multi-threaded COM apartment initialization on the main UI thread while
3156 * the appropriate native dialog function expects a single-threaded one.
3157 *
3158 * On all other platforms, this function is equivalent to
3159 * QFileDialog::getExistingDirectory().
3160 */
3161QString VBoxGlobal::getExistingDirectory (const QString &aDir,
3162 QWidget *aParent,
3163 const QString &aCaption,
3164 bool aDirOnly,
3165 bool aResolveSymlinks)
3166{
3167#if defined Q_WS_WIN
3168
3169 /**
3170 * QEvent class reimplementation to carry Win32 API native dialog's
3171 * result folder information
3172 */
3173 class GetExistDirectoryEvent : public OpenNativeDialogEvent
3174 {
3175 public:
3176
3177 enum { TypeId = QEvent::User + 300 };
3178
3179 GetExistDirectoryEvent (const QString &aResult)
3180 : OpenNativeDialogEvent (aResult, (QEvent::Type) TypeId) {}
3181 };
3182
3183 /**
3184 * QThread class reimplementation to open Win32 API native folder's dialog
3185 */
3186 class Thread : public QThread
3187 {
3188 public:
3189
3190 Thread (QWidget *aParent, QObject *aTarget,
3191 const QString &aDir, const QString &aCaption)
3192 : mParent (aParent), mTarget (aTarget), mDir (aDir), mCaption (aCaption) {}
3193
3194 virtual void run()
3195 {
3196 QString result;
3197
3198 QWidget *topParent = mParent ? mParent->topLevelWidget() : qApp->mainWidget();
3199 QString title = mCaption.isNull() ? tr ("Select a directory") : mCaption;
3200
3201 TCHAR path[MAX_PATH];
3202 path [0] = 0;
3203 TCHAR initPath [MAX_PATH];
3204 initPath [0] = 0;
3205
3206 BROWSEINFO bi;
3207 bi.hwndOwner = topParent ? topParent->winId() : 0;
3208 bi.pidlRoot = NULL;
3209 bi.lpszTitle = (TCHAR*)title.utf16();
3210 bi.pszDisplayName = initPath;
3211 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
3212 bi.lpfn = winGetExistDirCallbackProc;
3213 bi.lParam = Q_ULONG (&mDir);
3214
3215 /* Qt is uncapable to properly handle modal state if the modal
3216 * window is not a QWidget. For example, if we have the W1->W2->N
3217 * ownership where Wx are QWidgets (W2 is modal), and N is a
3218 * native modal window, cliking on the title bar of W1 will still
3219 * activate W2 and redirect keyboard/mouse to it. The dirty hack
3220 * to prevent it is to disable the entire widget... */
3221 if (mParent)
3222 mParent->setEnabled (false);
3223
3224 LPITEMIDLIST itemIdList = SHBrowseForFolder (&bi);
3225 if (itemIdList)
3226 {
3227 SHGetPathFromIDList (itemIdList, path);
3228 IMalloc *pMalloc;
3229 if (SHGetMalloc (&pMalloc) != NOERROR)
3230 result = QString::null;
3231 else
3232 {
3233 pMalloc->Free (itemIdList);
3234 pMalloc->Release();
3235 result = QString::fromUtf16 ((ushort*)path);
3236 }
3237 }
3238 else
3239 result = QString::null;
3240 QApplication::postEvent (mTarget, new GetExistDirectoryEvent (result));
3241
3242 /* Enable the parent widget again. */
3243 if (mParent)
3244 mParent->setEnabled (true);
3245 }
3246
3247 private:
3248
3249 QWidget *mParent;
3250 QObject *mTarget;
3251 QString mDir;
3252 QString mCaption;
3253 };
3254
3255 QString dir = QDir::convertSeparators (aDir);
3256 LoopObject loopObject ((QEvent::Type) GetExistDirectoryEvent::TypeId);
3257 Thread openDirThread (aParent, &loopObject, dir, aCaption);
3258 openDirThread.start();
3259 qApp->eventLoop()->enterLoop();
3260 openDirThread.wait();
3261 return loopObject.result();
3262
3263#else
3264
3265 QFileDialog::Options o;
3266 if (aDirOnly)
3267 o = QFileDialog::ShowDirsOnly;
3268 if (!aResolveSymlinks)
3269 o |= QFileDialog::DontResolveSymlinks;
3270 return QFileDialog::getExistingDirectory (aParent, aCaption, aDir, o);
3271
3272#endif
3273}
3274
3275/**
3276 * Reimplementation of QFileDialog::getOpenFileName() that removes some
3277 * oddities and limitations.
3278 *
3279 * On Win32, this function makes sure a file filter is applied automatically
3280 * right after it is selected from the drop-down list, to conform to common
3281 * experience in other applications. Note that currently, @a selectedFilter
3282 * is always set to null on return.
3283 *
3284 * On all other platforms, this function is equivalent to
3285 * QFileDialog::getOpenFileName().
3286 */
3287/* static */
3288QString VBoxGlobal::getOpenFileName (const QString &aStartWith,
3289 const QString &aFilters,
3290 QWidget *aParent,
3291 const QString &aCaption,
3292 QString *aSelectedFilter,
3293 bool aResolveSymlinks)
3294{
3295#if defined Q_WS_WIN
3296
3297 /**
3298 * QEvent class reimplementation to carry Win32 API native dialog's
3299 * result folder information
3300 */
3301 class GetOpenFileNameEvent : public OpenNativeDialogEvent
3302 {
3303 public:
3304
3305 enum { TypeId = QEvent::User + 301 };
3306
3307 GetOpenFileNameEvent (const QString &aResult)
3308 : OpenNativeDialogEvent (aResult, (QEvent::Type) TypeId) {}
3309 };
3310
3311 /**
3312 * QThread class reimplementation to open Win32 API native file dialog
3313 */
3314 class Thread : public QThread
3315 {
3316 public:
3317
3318 Thread (QWidget *aParent, QObject *aTarget,
3319 const QString &aStartWith, const QString &aFilters,
3320 const QString &aCaption) :
3321 mParent (aParent), mTarget (aTarget),
3322 mStartWith (aStartWith), mFilters (aFilters),
3323 mCaption (aCaption) {}
3324
3325 virtual void run()
3326 {
3327 QString result;
3328
3329 QString workDir;
3330 QString initSel;
3331 QFileInfo fi (mStartWith);
3332
3333 if (fi.isDir())
3334 workDir = mStartWith;
3335 else
3336 {
3337 workDir = fi.dirPath (true);
3338 initSel = fi.fileName();
3339 }
3340
3341 workDir = QDir::convertSeparators (workDir);
3342 if (!workDir.endsWith ("\\"))
3343 workDir += "\\";
3344
3345 QString title = mCaption.isNull() ? tr ("Select a file") : mCaption;
3346
3347 QWidget *topParent = mParent ? mParent->topLevelWidget() : qApp->mainWidget();
3348 QString winFilters = winFilter (mFilters);
3349 AssertCompile (sizeof (TCHAR) == sizeof (QChar));
3350 TCHAR buf [1024];
3351 if (initSel.length() > 0 && initSel.length() < sizeof (buf))
3352 memcpy (buf, initSel.utf16(), (initSel.length() + 1) * sizeof (TCHAR));
3353 else
3354 buf [0] = 0;
3355
3356 OPENFILENAME ofn;
3357 memset (&ofn, 0, sizeof (OPENFILENAME));
3358
3359 ofn.lStructSize = sizeof (OPENFILENAME);
3360 ofn.hwndOwner = topParent ? topParent->winId() : 0;
3361 ofn.lpstrFilter = (TCHAR *) winFilters.utf16();
3362 ofn.lpstrFile = buf;
3363 ofn.nMaxFile = sizeof (buf) - 1;
3364 ofn.lpstrInitialDir = (TCHAR *) workDir.utf16();
3365 ofn.lpstrTitle = (TCHAR *) title.utf16();
3366 ofn.Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY |
3367 OFN_EXPLORER | OFN_ENABLEHOOK |
3368 OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
3369 ofn.lpfnHook = OFNHookProc;
3370
3371 if (GetOpenFileName (&ofn))
3372 {
3373 result = QString::fromUtf16 ((ushort *) ofn.lpstrFile);
3374 }
3375
3376 // qt_win_eatMouseMove();
3377 MSG msg = {0, 0, 0, 0, 0, 0, 0};
3378 while (PeekMessage (&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
3379 if (msg.message == WM_MOUSEMOVE)
3380 PostMessage (msg.hwnd, msg.message, 0, msg.lParam);
3381
3382 result = result.isEmpty() ? result : QFileInfo (result).absFilePath();
3383
3384 QApplication::postEvent (mTarget, new GetOpenFileNameEvent (result));
3385 }
3386
3387 private:
3388
3389 QWidget *mParent;
3390 QObject *mTarget;
3391 QString mStartWith;
3392 QString mFilters;
3393 QString mCaption;
3394 };
3395
3396 if (aSelectedFilter)
3397 *aSelectedFilter = QString::null;
3398 QString startWith = QDir::convertSeparators (aStartWith);
3399 LoopObject loopObject ((QEvent::Type) GetOpenFileNameEvent::TypeId);
3400 if (aParent) qt_enter_modal (aParent);
3401 Thread openDirThread (aParent, &loopObject, startWith, aFilters, aCaption);
3402 openDirThread.start();
3403 qApp->eventLoop()->enterLoop();
3404 openDirThread.wait();
3405 if (aParent) qt_leave_modal (aParent);
3406 return loopObject.result();
3407
3408#else
3409
3410 QFileDialog::Options o;
3411 if (!aResolveSymlinks)
3412 o |= QFileDialog::DontResolveSymlinks;
3413 return QFileDialog::getOpenFileName (aParent, aCaption, aStartWith,
3414 aFilters, aSelectedFilter, o);
3415#endif
3416}
3417
3418/**
3419 * Search for the first directory that exists starting from the passed one
3420 * and going up through its parents. In case if none of the directories
3421 * exist (except the root one), the function returns QString::null.
3422 */
3423/* static */
3424QString VBoxGlobal::getFirstExistingDir (const QString &aStartDir)
3425{
3426 QString result = QString::null;
3427 QDir dir (aStartDir);
3428 while (!dir.exists() && !dir.isRoot())
3429 {
3430 QFileInfo dirInfo (dir.absolutePath());
3431 dir = dirInfo.absolutePath();
3432 }
3433 if (dir.exists() && !dir.isRoot())
3434 result = dir.absolutePath();
3435 return result;
3436}
3437
3438#if defined (Q_WS_X11)
3439
3440static char *XXGetProperty (Display *aDpy, Window aWnd,
3441 Atom aPropType, const char *aPropName)
3442{
3443 Atom propNameAtom = XInternAtom (aDpy, aPropName,
3444 True /* only_if_exists */);
3445 if (propNameAtom == None)
3446 return NULL;
3447
3448 Atom actTypeAtom = None;
3449 int actFmt = 0;
3450 unsigned long nItems = 0;
3451 unsigned long nBytesAfter = 0;
3452 unsigned char *propVal = NULL;
3453 int rc = XGetWindowProperty (aDpy, aWnd, propNameAtom,
3454 0, LONG_MAX, False /* delete */,
3455 aPropType, &actTypeAtom, &actFmt,
3456 &nItems, &nBytesAfter, &propVal);
3457 if (rc != Success)
3458 return NULL;
3459
3460 return reinterpret_cast <char *> (propVal);
3461}
3462
3463static Bool XXSendClientMessage (Display *aDpy, Window aWnd, const char *aMsg,
3464 unsigned long aData0 = 0, unsigned long aData1 = 0,
3465 unsigned long aData2 = 0, unsigned long aData3 = 0,
3466 unsigned long aData4 = 0)
3467{
3468 Atom msgAtom = XInternAtom (aDpy, aMsg, True /* only_if_exists */);
3469 if (msgAtom == None)
3470 return False;
3471
3472 XEvent ev;
3473
3474 ev.xclient.type = ClientMessage;
3475 ev.xclient.serial = 0;
3476 ev.xclient.send_event = True;
3477 ev.xclient.display = aDpy;
3478 ev.xclient.window = aWnd;
3479 ev.xclient.message_type = msgAtom;
3480
3481 /* always send as 32 bit for now */
3482 ev.xclient.format = 32;
3483 ev.xclient.data.l [0] = aData0;
3484 ev.xclient.data.l [1] = aData1;
3485 ev.xclient.data.l [2] = aData2;
3486 ev.xclient.data.l [3] = aData3;
3487 ev.xclient.data.l [4] = aData4;
3488
3489 return XSendEvent (aDpy, DefaultRootWindow (aDpy), False,
3490 SubstructureRedirectMask, &ev) != 0;
3491}
3492
3493#endif
3494
3495/**
3496 * Activates the specified window. If necessary, the window will be
3497 * de-iconified activation.
3498 *
3499 * @note On X11, it is implied that @a aWid represents a window of the same
3500 * display the application was started on.
3501 *
3502 * @param aWId Window ID to activate.
3503 * @param aSwitchDesktop @c true to switch to the window's desktop before
3504 * activation.
3505 *
3506 * @return @c true on success and @c false otherwise.
3507 */
3508/* static */
3509bool VBoxGlobal::activateWindow (WId aWId, bool aSwitchDesktop /* = true */)
3510{
3511 bool result = true;
3512
3513#if defined (Q_WS_WIN32)
3514
3515 if (IsIconic (aWId))
3516 result &= !!ShowWindow (aWId, SW_RESTORE);
3517 else if (!IsWindowVisible (aWId))
3518 result &= !!ShowWindow (aWId, SW_SHOW);
3519
3520 result &= !!SetForegroundWindow (aWId);
3521
3522#elif defined (Q_WS_X11)
3523
3524 Display *dpy = QX11Info::display();
3525
3526 if (aSwitchDesktop)
3527 {
3528 /* try to find the desktop ID using the NetWM property */
3529 CARD32 *desktop = (CARD32 *) XXGetProperty (dpy, aWId, XA_CARDINAL,
3530 "_NET_WM_DESKTOP");
3531 if (desktop == NULL)
3532 /* if the NetWM propery is not supported try to find the desktop
3533 * ID using the GNOME WM property */
3534 desktop = (CARD32 *) XXGetProperty (dpy, aWId, XA_CARDINAL,
3535 "_WIN_WORKSPACE");
3536
3537 if (desktop != NULL)
3538 {
3539 Bool ok = XXSendClientMessage (dpy, DefaultRootWindow (dpy),
3540 "_NET_CURRENT_DESKTOP",
3541 *desktop);
3542 if (!ok)
3543 {
3544 LogWarningFunc (("Couldn't switch to desktop=%08X\n",
3545 desktop));
3546 result = false;
3547 }
3548 XFree (desktop);
3549 }
3550 else
3551 {
3552 LogWarningFunc (("Couldn't find a desktop ID for aWId=%08X\n",
3553 aWId));
3554 result = false;
3555 }
3556 }
3557
3558 Bool ok = XXSendClientMessage (dpy, aWId, "_NET_ACTIVE_WINDOW");
3559 result &= !!ok;
3560
3561 XRaiseWindow (dpy, aWId);
3562
3563#else
3564
3565 AssertFailed();
3566 result = false;
3567
3568#endif
3569
3570 if (!result)
3571 LogWarningFunc (("Couldn't activate aWId=%08X\n", aWId));
3572
3573 return result;
3574}
3575
3576/**
3577 * Removes the acceletartor mark (the ampersand symbol) from the given string
3578 * and returns the result. The string is supposed to be a menu item's text
3579 * that may (or may not) contain the accelerator mark.
3580 *
3581 * In order to support accelerators used in non-alphabet languages
3582 * (e.g. Japanese) that has a form of "(&<L>)" (where <L> is a latin letter),
3583 * this method first searches for this pattern and, if found, removes it as a
3584 * whole. If such a pattern is not found, then the '&' character is simply
3585 * removed from the string.
3586 *
3587 * @note This function removes only the first occurense of the accelerator
3588 * mark.
3589 *
3590 * @param aText Menu item's text to remove the acceletaror mark from.
3591 *
3592 * @return The resulting string.
3593 */
3594/* static */
3595QString VBoxGlobal::removeAccelMark (const QString &aText)
3596{
3597 QString result = aText;
3598
3599 QRegExp accel ("\\(&[a-zA-Z]\\)");
3600 int pos = accel.indexIn (result);
3601 if (pos >= 0)
3602 result.remove (pos, accel.cap().length());
3603 else
3604 {
3605 pos = result.indexOf ('&');
3606 if (pos >= 0)
3607 result.remove (pos, 1);
3608 }
3609
3610 return result;
3611}
3612
3613/**
3614 * Searches for a widget that with @a aName (if it is not NULL) which inherits
3615 * @a aClassName (if it is not NULL) and among children of @a aParent. If @a
3616 * aParent is NULL, all top-level widgets are searched. If @a aRecursive is
3617 * true, child widgets are recursively searched as well.
3618 */
3619/* static */
3620QWidget *VBoxGlobal::findWidget (QWidget *aParent, const char *aName,
3621 const char *aClassName /* = NULL */,
3622 bool aRecursive /* = false */)
3623{
3624 if (aParent == NULL)
3625 {
3626 QWidgetList list = QApplication::topLevelWidgets();
3627 QWidget* w = NULL;
3628 foreach(w, list)
3629 {
3630 if ((!aName || strcmp (w->name(), aName) == 0) &&
3631 (!aClassName || strcmp (w->className(), aClassName) == 0))
3632 break;
3633 if (aRecursive)
3634 {
3635 w = findWidget (w, aName, aClassName, aRecursive);
3636 if (w)
3637 break;
3638 }
3639 }
3640 return w;
3641 }
3642
3643 QObjectList list = aParent->queryList (aName, aClassName, false, true);
3644 QObject *obj = NULL;
3645 foreach(obj, list)
3646 {
3647 if (obj->isWidgetType())
3648 break;
3649 }
3650 return (QWidget *) obj;
3651}
3652
3653// Public slots
3654////////////////////////////////////////////////////////////////////////////////
3655
3656/**
3657 * Opens the specified URL using OS/Desktop capabilities.
3658 *
3659 * @param aURL URL to open
3660 *
3661 * @return true on success and false otherwise
3662 */
3663bool VBoxGlobal::openURL (const QString &aURL)
3664{
3665#if defined (Q_WS_WIN)
3666 /* We cannot use ShellExecute() on the main UI thread because we've
3667 * initialized COM with CoInitializeEx(COINIT_MULTITHREADED). See
3668 * http://support.microsoft.com/default.aspx?scid=kb;en-us;287087
3669 * for more details. */
3670 class Thread : public QThread
3671 {
3672 public:
3673
3674 Thread (const QString &aURL, QObject *aObject)
3675 : mObject (aObject), mURL (aURL) {}
3676
3677 void run()
3678 {
3679 int rc = (int) ShellExecute (NULL, NULL, mURL.utf16(), NULL, NULL, SW_SHOW);
3680 bool ok = rc > 32;
3681 QApplication::postEvent
3682 (mObject,
3683 new VBoxShellExecuteEvent (this, mURL, ok));
3684 }
3685
3686 QString mURL;
3687 QObject *mObject;
3688 };
3689
3690 Thread *thread = new Thread (aURL, this);
3691 thread->start();
3692 /* thread will be deleted in the VBoxShellExecuteEvent handler */
3693
3694 return true;
3695
3696#elif defined (Q_WS_X11)
3697
3698 static const char * const commands[] =
3699 { "kfmclient:exec", "gnome-open", "x-www-browser", "firefox", "konqueror" };
3700
3701 for (size_t i = 0; i < ELEMENTS (commands); ++ i)
3702 {
3703 QStringList args = QString(commands [i]).split (':');
3704 args += aURL;
3705 QString command = args.takeFirst();
3706 if (QProcess::startDetached (command, args))
3707 return true;
3708 }
3709
3710#elif defined (Q_WS_MAC)
3711
3712 /* The code below is taken from Psi 0.10 sources
3713 * (http://www.psi-im.org) */
3714
3715 /* Use Internet Config to hand the URL to the appropriate application, as
3716 * set by the user in the Internet Preferences pane.
3717 * NOTE: ICStart could be called once at Psi startup, saving the
3718 * ICInstance in a global variable, as a minor optimization.
3719 * ICStop should then be called at Psi shutdown if ICStart
3720 * succeeded. */
3721 ICInstance icInstance;
3722 OSType psiSignature = 'psi ';
3723 OSStatus error = ::ICStart (&icInstance, psiSignature);
3724 if (error == noErr)
3725 {
3726 ConstStr255Param hint (0x0);
3727 QByteArray cs = aURL.toLocal8Bit();
3728 const char* data = cs.data();
3729 long length = cs.length();
3730 long start (0);
3731 long end (length);
3732 /* Don't bother testing return value (error); launched application
3733 * will report problems. */
3734 ::ICLaunchURL (icInstance, hint, data, length, &start, &end);
3735 ICStop (icInstance);
3736 return true;
3737 }
3738
3739#else
3740 vboxProblem().message
3741 (NULL, VBoxProblemReporter::Error,
3742 tr ("Opening URLs is not implemented yet."));
3743 return false;
3744#endif
3745
3746 /* if we go here it means we couldn't open the URL */
3747 vboxProblem().cannotOpenURL (aURL);
3748
3749 return false;
3750}
3751
3752void VBoxGlobal::showRegistrationDialog (bool aForce)
3753{
3754#ifdef VBOX_WITH_REGISTRATION
3755 if (!aForce && !VBoxRegistrationDlg::hasToBeShown())
3756 return;
3757
3758 if (mRegDlg)
3759 {
3760 /* Show the already opened registration dialog */
3761 mRegDlg->setWindowState (mRegDlg->windowState() & ~Qt::WindowMinimized);
3762 mRegDlg->raise();
3763 mRegDlg->activateWindow();
3764 }
3765 else
3766 {
3767 /* Store the ID of the main window to ensure that only one
3768 * registration dialog is shown at a time. Due to manipulations with
3769 * OnExtraDataCanChange() and OnExtraDataChange() signals, this extra
3770 * data item acts like an inter-process mutex, so the first process
3771 * that attempts to set it will win, the rest will get a failure from
3772 * the SetExtraData() call. */
3773 mVBox.SetExtraData (VBoxDefs::GUI_RegistrationDlgWinID,
3774 QString ("%1").arg ((long) qApp->mainWidget()->winId()));
3775
3776 if (mVBox.isOk())
3777 {
3778 /* We've got the "mutex", create a new registration dialog */
3779 VBoxRegistrationDlg *dlg =
3780 new VBoxRegistrationDlg (&mRegDlg, 0, Qt::WDestructiveClose);
3781 Assert (dlg == mRegDlg);
3782 mRegDlg->show();
3783 }
3784 }
3785#endif
3786}
3787
3788// Protected members
3789////////////////////////////////////////////////////////////////////////////////
3790
3791bool VBoxGlobal::event (QEvent *e)
3792{
3793 switch (e->type())
3794 {
3795#if defined (Q_WS_WIN)
3796 case VBoxDefs::ShellExecuteEventType:
3797 {
3798 VBoxShellExecuteEvent *ev = (VBoxShellExecuteEvent *) e;
3799 if (!ev->mOk)
3800 vboxProblem().cannotOpenURL (ev->mURL);
3801 /* wait for the thread and free resources */
3802 ev->mThread->wait();
3803 delete ev->mThread;
3804 return true;
3805 }
3806#endif
3807
3808 case VBoxDefs::AsyncEventType:
3809 {
3810 VBoxAsyncEvent *ev = (VBoxAsyncEvent *) e;
3811 ev->handle();
3812 return true;
3813 }
3814
3815 case VBoxDefs::EnumerateMediaEventType:
3816 {
3817 VBoxEnumerateMediaEvent *ev = (VBoxEnumerateMediaEvent *) e;
3818
3819 if (!ev->mLast)
3820 {
3821 if (ev->mMedia.status == VBoxMedia::Error)
3822 vboxProblem().cannotGetMediaAccessibility (ev->mMedia.disk);
3823 media_list [ev->mIndex] = ev->mMedia;
3824 emit mediaEnumerated (media_list [ev->mIndex], ev->mIndex);
3825 }
3826 else
3827 {
3828 /* the thread has posted the last message, wait for termination */
3829 media_enum_thread->wait();
3830 delete media_enum_thread;
3831 media_enum_thread = 0;
3832
3833 emit mediaEnumFinished (media_list);
3834 }
3835
3836 return true;
3837 }
3838
3839 /* VirtualBox callback events */
3840
3841 case VBoxDefs::MachineStateChangeEventType:
3842 {
3843 emit machineStateChanged (*(VBoxMachineStateChangeEvent *) e);
3844 return true;
3845 }
3846 case VBoxDefs::MachineDataChangeEventType:
3847 {
3848 emit machineDataChanged (*(VBoxMachineDataChangeEvent *) e);
3849 return true;
3850 }
3851 case VBoxDefs::MachineRegisteredEventType:
3852 {
3853 emit machineRegistered (*(VBoxMachineRegisteredEvent *) e);
3854 return true;
3855 }
3856 case VBoxDefs::SessionStateChangeEventType:
3857 {
3858 emit sessionStateChanged (*(VBoxSessionStateChangeEvent *) e);
3859 return true;
3860 }
3861 case VBoxDefs::SnapshotEventType:
3862 {
3863 emit snapshotChanged (*(VBoxSnapshotEvent *) e);
3864 return true;
3865 }
3866 case VBoxDefs::CanShowRegDlgEventType:
3867 {
3868 emit canShowRegDlg (((VBoxCanShowRegDlgEvent *) e)->mCanShow);
3869 return true;
3870 }
3871
3872 default:
3873 break;
3874 }
3875
3876 return QObject::event (e);
3877}
3878
3879bool VBoxGlobal::eventFilter (QObject *aObject, QEvent *aEvent)
3880{
3881 if (aEvent->type() == QEvent::LanguageChange &&
3882 aObject->isWidgetType() &&
3883 static_cast <QWidget *> (aObject)->isTopLevel())
3884 {
3885 /* Catch the language change event before any other widget gets it in
3886 * order to invalidate cached string resources (like the details view
3887 * templates) that may be used by other widgets. */
3888 QWidgetList list = QApplication::topLevelWidgets();
3889 if (list.first() == aObject)
3890 {
3891 /* call this only once per every language change (see
3892 * QApplication::installTranslator() for details) */
3893 languageChange();
3894 }
3895 }
3896
3897 return QObject::eventFilter (aObject, aEvent);
3898}
3899
3900// Private members
3901////////////////////////////////////////////////////////////////////////////////
3902
3903void VBoxGlobal::init()
3904{
3905#ifdef DEBUG
3906 verString += " [DEBUG]";
3907#endif
3908
3909#ifdef Q_WS_WIN
3910 /* COM for the main thread is initialized in main() */
3911#else
3912 HRESULT rc = COMBase::InitializeCOM();
3913 if (FAILED (rc))
3914 {
3915 vboxProblem().cannotInitCOM (rc);
3916 return;
3917 }
3918#endif
3919
3920 mVBox.createInstance (CLSID_VirtualBox);
3921 if (!mVBox.isOk())
3922 {
3923 vboxProblem().cannotCreateVirtualBox (mVBox);
3924 return;
3925 }
3926
3927 /* initialize guest OS type vector */
3928 CGuestOSTypeCollection coll = mVBox.GetGuestOSTypes();
3929 int osTypeCount = coll.GetCount();
3930 AssertMsg (osTypeCount > 0, ("Number of OS types must not be zero"));
3931 if (osTypeCount > 0)
3932 {
3933 vm_os_types.resize (osTypeCount);
3934 int i = 0;
3935 CGuestOSTypeEnumerator en = coll.Enumerate();
3936 while (en.HasMore())
3937 vm_os_types [i++] = en.GetNext();
3938 }
3939
3940 /* fill in OS type icon dictionary */
3941 static const char *kOSTypeIcons [][2] =
3942 {
3943 {"unknown", ":/os_unknown.png"},
3944 {"dos", ":/os_dos.png"},
3945 {"win31", ":/os_win31.png"},
3946 {"win95", ":/os_win95.png"},
3947 {"win98", ":/os_win98.png"},
3948 {"winme", ":/os_winme.png"},
3949 {"winnt4", ":/os_winnt4.png"},
3950 {"win2k", ":/os_win2k.png"},
3951 {"winxp", ":/os_winxp.png"},
3952 {"win2k3", ":/os_win2k3.png"},
3953 {"winvista", ":/os_winvista.png"},
3954 {"win2k8", ":/os_win2k8.png"},
3955 {"os2warp3", ":/os_os2warp3.png"},
3956 {"os2warp4", ":/os_os2warp4.png"},
3957 {"os2warp45", ":/os_os2warp45.png"},
3958 {"ecs", ":/os_ecs.png"},
3959 {"linux22", ":/os_linux22.png"},
3960 {"linux24", ":/os_linux24.png"},
3961 {"linux26", ":/os_linux26.png"},
3962 {"archlinux", ":/os_archlinux.png"},
3963 {"debian", ":/os_debian.png"},
3964 {"opensuse", ":/os_opensuse.png"},
3965 {"fedoracore",":/os_fedoracore.png"},
3966 {"gentoo", ":/os_gentoo.png"},
3967 {"mandriva", ":/os_mandriva.png"},
3968 {"redhat", ":/os_redhat.png"},
3969 {"ubuntu", ":/os_ubuntu.png"},
3970 {"xandros", ":/os_xandros.png"},
3971 {"freebsd", ":/os_freebsd.png"},
3972 {"openbsd", ":/os_openbsd.png"},
3973 {"netbsd", ":/os_netbsd.png"},
3974 {"netware", ":/os_netware.png"},
3975 {"solaris", ":/os_solaris.png"},
3976 {"l4", ":/os_l4.png"},
3977 };
3978 for (uint n = 0; n < SIZEOF_ARRAY (kOSTypeIcons); n ++)
3979 {
3980 vm_os_type_icons.insert (kOSTypeIcons [n][0],
3981 new QPixmap (kOSTypeIcons [n][1]));
3982 }
3983
3984 /* fill in VM state icon dictionary */
3985 static struct
3986 {
3987 KMachineState state;
3988 const char *name;
3989 }
3990 vmStateIcons[] =
3991 {
3992 {KMachineState_Null, NULL},
3993 {KMachineState_PoweredOff, ":/state_powered_off_16px.png"},
3994 {KMachineState_Saved, ":/state_saved_16px.png"},
3995 {KMachineState_Aborted, ":/state_aborted_16px.png"},
3996 {KMachineState_Running, ":/state_running_16px.png"},
3997 {KMachineState_Paused, ":/state_paused_16px.png"},
3998 {KMachineState_Stuck, ":/state_stuck_16px.png"},
3999 {KMachineState_Starting, ":/state_running_16px.png"}, /// @todo (dmik) separate icon?
4000 {KMachineState_Stopping, ":/state_running_16px.png"}, /// @todo (dmik) separate icon?
4001 {KMachineState_Saving, ":/state_saving_16px.png"},
4002 {KMachineState_Restoring, ":/state_restoring_16px.png"},
4003 {KMachineState_Discarding, ":/state_discarding_16px.png"},
4004 };
4005 for (uint n = 0; n < SIZEOF_ARRAY (vmStateIcons); n ++)
4006 {
4007 mStateIcons.insert (vmStateIcons [n].state,
4008 new QPixmap (vmStateIcons [n].name));
4009 }
4010
4011 /* online/offline snapshot icons */
4012 mOfflineSnapshotIcon = QPixmap (":/offline_snapshot_16px.png");
4013 mOnlineSnapshotIcon = QPixmap (":/online_snapshot_16px.png");
4014
4015 /* initialize state colors vector */
4016 vm_state_color.insert (KMachineState_Null, new QColor(Qt::red));
4017 vm_state_color.insert (KMachineState_PoweredOff, new QColor(Qt::gray));
4018 vm_state_color.insert (KMachineState_Saved, new QColor(Qt::yellow));
4019 vm_state_color.insert (KMachineState_Aborted, new QColor(Qt::darkRed));
4020 vm_state_color.insert (KMachineState_Running, new QColor(Qt::green));
4021 vm_state_color.insert (KMachineState_Paused, new QColor(Qt::darkGreen));
4022 vm_state_color.insert (KMachineState_Stuck, new QColor(Qt::darkMagenta));
4023 vm_state_color.insert (KMachineState_Starting, new QColor(Qt::green));
4024 vm_state_color.insert (KMachineState_Stopping, new QColor(Qt::green));
4025 vm_state_color.insert (KMachineState_Saving, new QColor(Qt::green));
4026 vm_state_color.insert (KMachineState_Restoring, new QColor(Qt::green));
4027 vm_state_color.insert (KMachineState_Discarding, new QColor(Qt::green));
4028
4029 /* Redefine default large and small icon sizes. In particular, it is
4030 * necessary to consider both 32px and 22px icon sizes as Large when we
4031 * explicitly define them as Large (seems to be a bug in
4032 * QToolButton::sizeHint()). */
4033#warning port me
4034// QIcon::setIconSize (QIcon::Small, QSize (16, 16));
4035// QIcon::setIconSize (QIcon::Large, QSize (22, 22));
4036
4037 qApp->installEventFilter (this);
4038
4039 /* create default non-null global settings */
4040 gset = VBoxGlobalSettings (false);
4041
4042 /* try to load global settings */
4043 gset.load (mVBox);
4044 if (!mVBox.isOk() || !gset)
4045 {
4046 vboxProblem().cannotLoadGlobalConfig (mVBox, gset.lastError());
4047 return;
4048 }
4049
4050 /* Load customized language if any */
4051 QString languageId = gset.languageId();
4052 if (!languageId.isNull())
4053 loadLanguage (languageId);
4054
4055 languageChange();
4056
4057 /* process command line */
4058
4059 vm_render_mode_str = 0;
4060#ifdef VBOX_WITH_DEBUGGER_GUI
4061#ifdef VBOX_WITH_DEBUGGER_GUI_MENU
4062 dbg_enabled = true;
4063#else
4064 dbg_enabled = false;
4065#endif
4066 dbg_visible_at_startup = false;
4067#endif
4068
4069 int argc = qApp->argc();
4070 int i = 1;
4071 while (i < argc)
4072 {
4073 const char *arg = qApp->argv() [i];
4074 if ( !::strcmp (arg, "-startvm"))
4075 {
4076 if (++i < argc)
4077 {
4078 QString param = QString (qApp->argv() [i]);
4079 QUuid uuid = QUuid (param);
4080 if (!uuid.isNull())
4081 {
4082 vmUuid = uuid;
4083 }
4084 else
4085 {
4086 CMachine m = mVBox.FindMachine (param);
4087 if (m.isNull())
4088 {
4089 vboxProblem().cannotFindMachineByName (mVBox, param);
4090 return;
4091 }
4092 vmUuid = m.GetId();
4093 }
4094 }
4095 }
4096 else if (!::strcmp (arg, "-comment"))
4097 {
4098 ++i;
4099 }
4100 else if (!::strcmp (arg, "-rmode"))
4101 {
4102 if (++i < argc)
4103 vm_render_mode_str = qApp->argv() [i];
4104 }
4105#ifdef VBOX_WITH_DEBUGGER_GUI
4106 else if (!::strcmp (arg, "-dbg"))
4107 {
4108 dbg_enabled = true;
4109 }
4110#ifdef DEBUG
4111 else if (!::strcmp (arg, "-nodebug"))
4112 {
4113 dbg_enabled = false;
4114 dbg_visible_at_startup = false;
4115 }
4116#else
4117 else if (!::strcmp( arg, "-debug"))
4118 {
4119 dbg_enabled = true;
4120 dbg_visible_at_startup = true;
4121 }
4122#endif
4123#endif
4124 i++;
4125 }
4126
4127 vm_render_mode = vboxGetRenderMode( vm_render_mode_str );
4128
4129 /* setup the callback */
4130 callback = CVirtualBoxCallback (new VBoxCallback (*this));
4131 mVBox.RegisterCallback (callback);
4132 AssertWrapperOk (mVBox);
4133 if (!mVBox.isOk())
4134 return;
4135
4136 mValid = true;
4137}
4138
4139/** @internal
4140 *
4141 * This method should be never called directly. It is called automatically
4142 * when the application terminates.
4143 */
4144void VBoxGlobal::cleanup()
4145{
4146 /* sanity check */
4147 if (!sVBoxGlobalInCleanup)
4148 {
4149 AssertMsgFailed (("Should never be called directly\n"));
4150 return;
4151 }
4152
4153 if (!callback.isNull())
4154 {
4155 mVBox.UnregisterCallback (callback);
4156 AssertWrapperOk (mVBox);
4157 callback.detach();
4158 }
4159
4160 if (media_enum_thread)
4161 {
4162 /* sVBoxGlobalInCleanup is true here, so just wait for the thread */
4163 media_enum_thread->wait();
4164 delete media_enum_thread;
4165 media_enum_thread = 0;
4166 }
4167
4168#ifdef VBOX_WITH_REGISTRATION
4169 if (mRegDlg)
4170 mRegDlg->close();
4171#endif
4172
4173 if (mConsoleWnd)
4174 delete mConsoleWnd;
4175 if (mSelectorWnd)
4176 delete mSelectorWnd;
4177
4178 /* ensure CGuestOSType objects are no longer used */
4179 vm_os_types.clear();
4180 /* media list contains a lot of CUUnknown, release them */
4181 media_list.clear();
4182 /* the last step to ensure we don't use COM any more */
4183 mVBox.detach();
4184
4185 /* There may be VBoxEnumerateMediaEvent instances still in the message
4186 * queue which reference COM objects. Remove them to release those objects
4187 * before uninitializing the COM subsystem. */
4188 QApplication::removePostedEvents (this);
4189
4190#ifdef Q_WS_WIN
4191 /* COM for the main thread is shutdown in main() */
4192#else
4193 COMBase::CleanupCOM();
4194#endif
4195
4196 mValid = false;
4197}
4198
4199/** @fn vboxGlobal
4200 *
4201 * Shortcut to the static VBoxGlobal::instance() method, for convenience.
4202 */
4203
4204
4205/**
4206 * USB Popup Menu class methods
4207 * This class provides the list of USB devices attached to the host.
4208 */
4209VBoxUSBMenu::VBoxUSBMenu (QWidget *aParent) : QMenu (aParent)
4210{
4211 connect (this, SIGNAL (aboutToShow()),
4212 this, SLOT (processAboutToShow()));
4213// connect (this, SIGNAL (hovered (QAction *)),
4214// this, SLOT (processHighlighted (QAction *)));
4215}
4216
4217const CUSBDevice& VBoxUSBMenu::getUSB (QAction *aAction)
4218{
4219 return mUSBDevicesMap [aAction];
4220}
4221
4222void VBoxUSBMenu::setConsole (const CConsole &aConsole)
4223{
4224 mConsole = aConsole;
4225}
4226
4227void VBoxUSBMenu::processAboutToShow()
4228{
4229 clear();
4230 mUSBDevicesMap.clear();
4231
4232 CHost host = vboxGlobal().virtualBox().GetHost();
4233
4234 bool isUSBEmpty = host.GetUSBDevices().GetCount() == 0;
4235 if (isUSBEmpty)
4236 {
4237 QAction *action = addAction (tr ("<no available devices>", "USB devices"));
4238 action->setEnabled (false);
4239 action->setToolTip (tr ("No supported devices connected to the host PC",
4240 "USB device tooltip"));
4241 }
4242 else
4243 {
4244 CHostUSBDeviceEnumerator en = host.GetUSBDevices().Enumerate();
4245 while (en.HasMore())
4246 {
4247 CHostUSBDevice iterator = en.GetNext();
4248 CUSBDevice usb = CUnknown (iterator);
4249 QAction *action = addAction (vboxGlobal().details (usb));
4250 action->setCheckable (true);
4251 mUSBDevicesMap [action] = usb;
4252 /* check if created item was alread attached to this session */
4253 if (!mConsole.isNull())
4254 {
4255 CUSBDevice attachedUSB =
4256 mConsole.GetUSBDevices().FindById (usb.GetId());
4257 action->setChecked (!attachedUSB.isNull());
4258 action->setEnabled (iterator.GetState() !=
4259 KUSBDeviceState_Unavailable);
4260 }
4261 }
4262 }
4263}
4264
4265bool VBoxUSBMenu::event(QEvent *aEvent)
4266{
4267 /* We provide dynamic tooltips for the usb devices */
4268 if (aEvent->type() == QEvent::ToolTip)
4269 {
4270 QHelpEvent *helpEvent = static_cast<QHelpEvent *> (aEvent);
4271 QAction *action = actionAt (helpEvent->pos());
4272 if (action)
4273 {
4274 CUSBDevice usb = mUSBDevicesMap [action];
4275 if (!usb.isNull())
4276 {
4277 QToolTip::showText (helpEvent->globalPos(), vboxGlobal().toolTip (usb));
4278 return true;
4279 }
4280 }
4281 }
4282 return QMenu::event (aEvent);
4283}
4284
4285/**
4286 * Enable/Disable Menu class.
4287 * This class provides enable/disable menu items.
4288 */
4289VBoxSwitchMenu::VBoxSwitchMenu (QWidget *aParent, QAction *aAction,
4290 bool aInverted)
4291 : QMenu (aParent), mAction (aAction), mInverted (aInverted)
4292{
4293 /* this menu works only with toggle action */
4294 Assert (aAction->isCheckable());
4295 addAction(aAction);
4296 connect (this, SIGNAL (aboutToShow()),
4297 this, SLOT (processAboutToShow()));
4298}
4299
4300void VBoxSwitchMenu::setToolTip (const QString &aTip)
4301{
4302 mAction->setToolTip (aTip);
4303}
4304
4305void VBoxSwitchMenu::processAboutToShow()
4306{
4307 QString text = mAction->isChecked() ^ mInverted ? tr ("Disable") : tr ("Enable");
4308 mAction->setText (text);
4309}
4310
4311#ifdef Q_WS_X11
4312#include "VBoxGlobal.moc"
4313#endif
Note: See TracBrowser for help on using the repository browser.

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