VirtualBox

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

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

Main: Added a describeProperty method for querying config properties of a
specific backend. Added it to the testcase, FE/Qt4 & FE/VBoxManage. Updated the
manual.

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