VirtualBox

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

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

Fe/Qt4: Added Windows 7 icons.

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