VirtualBox

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

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

Fe/Qt4: Systray: Do not spawn new selector windows when starting a VM.

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

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