VirtualBox

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

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

Older 82543GC chip support, works in linux only.

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