VirtualBox

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

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

FE/Qt4: Cumulative patch for defects (2928, 2883, 2405) : (1) Required video memory for VM Settings & New VM Wizard now feat integer megabytes number; (2) New VM Wizard updated with blocking 'Next' button when user assigns more than 50% of system memory to this vm; (3) New VM Wizard updated with silent calculation of video memory size required for fullscreen/seamless modes and selects the larger size between this and the recommended by os type; (4) OS Type Selector updated with 'recently selected os' storing mechanism used to switch to last chosen os type when switching os families; (5) 'Other' os family moved to the last position as the 'Other/Unknown' os type.

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