VirtualBox

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

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

FE/Qt4: FormatSize function reworked, warning about too low memory for seamless updated to use integer format of size instead of decimal.

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