VirtualBox

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

Last change on this file since 9822 was 9822, checked in by vboxsync, 16 years ago

Compile fix.

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