VirtualBox

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

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

FE/Qt4: 2883: Structure OS list. Feature request implemented in base designed variant.

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