VirtualBox

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

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

Fe/Qt4: Systray: Added reference count for main windows.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette