VirtualBox

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

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

FE/Qt: Disable too optimistic assertion in VBoxGlobal::details (CHardDisk2). Should fix GUI crashes during high hard disk management activitiy (creating/deleting) caused by third parties (discarding snapshots and so on).

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