/** @file
*
* VBox frontends: Qt GUI ("VirtualBox"):
* VBoxGlobal class implementation
*/
/*
* Copyright (C) 2008 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#include "VBoxGlobal.h"
#include
Checking accessibility...",
"medium");
break;
}
case KMediaState_Inaccessible:
{
if (mResult.isOk())
{
/* not accessibile */
mToolTip += QString ("
%1").
arg (VBoxGlobal::highlight (mLastAccessError,
true /* aToolTip */));
}
else
{
/* accessibility check (eg GetState()) itself failed */
mToolTip = VBoxGlobal::tr (
"
Failed to check media accessibility.
%1.",
"medium").
arg (VBoxProblemReporter::formatErrorInfo (mResult));
}
break;
}
default:
break;
}
/* reset mNoDiffs */
mNoDiffs.isSet = false;
}
/**
* Returns a root medium of this medium. For non-hard disk media, this is always
* this medium itself.
*/
VBoxMedium &VBoxMedium::root() const
{
VBoxMedium *root = unconst (this);
while (root->mParent != NULL)
root = root->mParent;
return *root;
}
/**
* Returns a tooltip for this medium.
*
* In "don't show diffs" mode (where the attributes of the base hard disk are
* shown instead of the attributes of the differencing hard disk), extra
* information will be added to the tooltip to give the user a hint that the
* medium is actually a differencing hard disk.
*
* @param aNoDiffs @c true to enable user-friendly "don't show diffs" mode.
* @param aCheckRO @c true to perform the #readOnly() check and add a notice
* accordingly.
*/
QString VBoxMedium::toolTip (bool aNoDiffs /*= false*/,
bool aCheckRO /*= false*/) const
{
unconst (this)->checkNoDiffs (aNoDiffs);
QString tip = aNoDiffs ? mNoDiffs.toolTip : mToolTip;
if (aCheckRO && mIsReadOnly)
tip += VBoxGlobal::tr (
" Attaching this hard disk will "
"be performed indirectly using a newly created "
"differencing hard disk.",
"medium").
arg (":/new_16px.png");
return tip;
}
/**
* Returns an icon corresponding to the media state. Distinguishes between
* the Inaccessible state and the situation when querying the state itself
* failed.
*
* In "don't show diffs" mode (where the attributes of the base hard disk are
* shown instead of the attributes of the differencing hard disk), the most
* worst media state on the given hard disk chain will be used to select the
* media icon.
*
* @param aNoDiffs @c true to enable user-friendly "don't show diffs" mode.
* @param aCheckRO @c true to perform the #readOnly() check and change the icon
* accordingly.
*/
QPixmap VBoxMedium::icon (bool aNoDiffs /*= false*/,
bool aCheckRO /*= false*/) const
{
QPixmap icon;
if (state (aNoDiffs) == KMediaState_Inaccessible)
icon = result (aNoDiffs).isOk() ?
vboxGlobal().warningIcon() : vboxGlobal().errorIcon();
if (aCheckRO && mIsReadOnly)
icon = VBoxGlobal::
joinPixmaps (icon, QPixmap (":/new_16px.png"));
return icon;
}
/**
* Returns the details of this medium as a single-line string
*
* For hard disks, the details include the location, type and the logical size
* of the hard disk. Note that if @a aNoDiffs is @c true, these properties are
* queried on the root hard disk of the given hard disk because the primary
* purpose of the returned string is to be human readabile (so that seeing a
* complex diff hard disk name is usually not desirable).
*
* For other media types, the location and the actual size are returned.
* Arguments @a aPredictDiff and @a aNoRoot are ignored in this case.
*
* @param aNoDiffs @c true to enable user-friendly "don't show diffs" mode.
* @param aPredictDiff @c true to mark the hard disk as differencing if
* attaching it would create a differencing hard disk (not
* used when @a aNoRoot is true).
* @param aUseHTML @c true to allow for emphasizing using bold and italics.
*
* @note Use #detailsHTML() instead of passing @c true for @a aUseHTML.
*
* @note The media object may become uninitialized by a third party while this
* method is reading its properties. In this case, the method will return
* an empty string.
*/
QString VBoxMedium::details (bool aNoDiffs /*= false*/,
bool aPredictDiff /*= false*/,
bool aUseHTML /*= false */) const
{
// @todo *** the below check is rough; if mMedium becomes uninitialized, any
// of getters called afterwards will also fail. The same relates to the
// root hard disk object (that will be the hard disk itself in case of
// non-differencing disks). However, this check was added to fix a
// particular use case: when the hard disk is a differencing hard disk and
// it happens to be discarded (and uninitialized) after this method is
// called but before we read all its properties (yes, it's possible!), the
// root object will be null and calling methods on it will assert in the
// debug builds. This check seems to be enough as a quick solution (fresh
// hard disk attachments will be re-read by a machine state change signal
// after the discard operation is finished, so the user will eventually see
// correct data), but in order to solve the problem properly we need to use
// exceptions everywhere (or check the result after every method call). See
// also Defect #2149.
if (!mMedium.isOk())
return QString::null;
QString details, str;
VBoxMedium *root = unconst (this);
KMediaState state = mState;
if (mType == VBoxDefs::MediaType_HardDisk)
{
if (aNoDiffs)
{
root = &this->root();
bool isDiff =
(!aPredictDiff && mParent != NULL) ||
(aPredictDiff && mIsReadOnly);
details = isDiff && aUseHTML ?
QString ("%1, ").arg (root->mHardDiskType) :
QString ("%1, ").arg (root->mHardDiskType);
/* overall (worst) state */
state = this->state (true /* aNoDiffs */);
/* we cannot get the logical size if the root is not checked yet */
if (root->mState == KMediaState_NotCreated)
state = KMediaState_NotCreated;
}
else
{
details = QString ("%1, ").arg (root->mHardDiskType);
}
}
/// @todo prepend the details with the warning/error
// icon when not accessible
switch (state)
{
case KMediaState_NotCreated:
str = VBoxGlobal::tr ("Checking...", "medium");
details += aUseHTML ? QString ("%1").arg (str) : str;
break;
case KMediaState_Inaccessible:
str = VBoxGlobal::tr ("Inaccessible", "medium");
details += aUseHTML ? QString ("%1").arg (str) : str;
break;
default:
details += mType == VBoxDefs::MediaType_HardDisk ?
root->mLogicalSize : root->mSize;
break;
}
details = aUseHTML ?
QString ("%1 (
Some of the media in this hard disk chain are "
"inaccessible. Please use the Virtual Media Manager "
"in Show Differencing Hard Disks mode to inspect "
"these media.");
if (!cur->mResult.isOk())
{
mNoDiffs.result = cur->mResult;
break;
}
/* comtinue looking for another !cur->mResult.isOk() */
}
}
if (mParent != NULL && !mIsReadOnly)
{
mNoDiffs.toolTip = VBoxGlobal::tr (
"%1"
"
This base hard disk is indirectly attached using the "
"following differencing hard disk:
"
"%2%3").
arg (root().toolTip(), mToolTip, mNoDiffs.toolTip);
}
if (mNoDiffs.toolTip.isNull())
mNoDiffs.toolTip = mToolTip;
mNoDiffs.isSet = true;
}
// VBoxMediaEnumEvent
/////////////////////////////////////////////////////////////////////////////
class VBoxMediaEnumEvent : public QEvent
{
public:
/** Constructs a regular enum event */
VBoxMediaEnumEvent (const VBoxMedium &aMedium)
: QEvent ((QEvent::Type) VBoxDefs::MediaEnumEventType)
, mMedium (aMedium), mLast (false)
{}
/** Constructs the last enum event */
VBoxMediaEnumEvent()
: QEvent ((QEvent::Type) VBoxDefs::MediaEnumEventType)
, mLast (true)
{}
/** The last enumerated medium (not valid when #last is true) */
const VBoxMedium mMedium;
/** Whether this is the last event for the given enumeration or not */
const bool mLast;
};
#if defined (Q_WS_WIN)
class VBoxShellExecuteEvent : public QEvent
{
public:
/** Constructs a regular enum event */
VBoxShellExecuteEvent (QThread *aThread, const QString &aURL,
bool aOk)
: QEvent ((QEvent::Type) VBoxDefs::ShellExecuteEventType)
, mThread (aThread), mURL (aURL), mOk (aOk)
{}
QThread *mThread;
QString mURL;
bool mOk;
};
#endif
// VirtualBox callback class
/////////////////////////////////////////////////////////////////////////////
class VBoxCallback : public IVirtualBoxCallback
{
public:
VBoxCallback (VBoxGlobal &aGlobal)
: mGlobal (aGlobal)
, mIsRegDlgOwner (false), mIsUpdDlgOwner (false)
{
#if defined (Q_OS_WIN32)
refcnt = 0;
#endif
}
virtual ~VBoxCallback() {}
NS_DECL_ISUPPORTS
#if defined (Q_OS_WIN32)
STDMETHOD_(ULONG, AddRef)()
{
return ::InterlockedIncrement (&refcnt);
}
STDMETHOD_(ULONG, Release)()
{
long cnt = ::InterlockedDecrement (&refcnt);
if (cnt == 0)
delete this;
return cnt;
}
STDMETHOD(QueryInterface) (REFIID riid , void **ppObj)
{
if (riid == IID_IUnknown) {
*ppObj = this;
AddRef();
return S_OK;
}
if (riid == IID_IVirtualBoxCallback) {
*ppObj = this;
AddRef();
return S_OK;
}
*ppObj = NULL;
return E_NOINTERFACE;
}
#endif
// IVirtualBoxCallback methods
// Note: we need to post custom events to the GUI event queue
// instead of doing what we need directly from here because on Win32
// these callback methods are never called on the main GUI thread.
// Another reason to handle events asynchronously is that internally
// most callback interface methods are called from under the initiator
// object's lock, so accessing the initiator object (for example, reading
// some property) directly from the callback method will definitely cause
// a deadlock.
STDMETHOD(OnMachineStateChange) (IN_GUIDPARAM id, MachineState_T state)
{
postEvent (new VBoxMachineStateChangeEvent (COMBase::ToQUuid (id),
(KMachineState) state));
return S_OK;
}
STDMETHOD(OnMachineDataChange) (IN_GUIDPARAM id)
{
postEvent (new VBoxMachineDataChangeEvent (COMBase::ToQUuid (id)));
return S_OK;
}
STDMETHOD(OnExtraDataCanChange)(IN_GUIDPARAM id,
IN_BSTRPARAM key, IN_BSTRPARAM value,
BSTR *error, BOOL *allowChange)
{
if (!error || !allowChange)
return E_INVALIDARG;
if (COMBase::ToQUuid (id).isNull())
{
/* it's a global extra data key someone wants to change */
QString sKey = QString::fromUtf16 (key);
QString sVal = QString::fromUtf16 (value);
if (sKey.startsWith ("GUI/"))
{
if (sKey == VBoxDefs::GUI_RegistrationDlgWinID)
{
if (mIsRegDlgOwner)
{
if (sVal.isEmpty() ||
sVal == QString ("%1")
.arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
*allowChange = TRUE;
else
*allowChange = FALSE;
}
else
*allowChange = TRUE;
return S_OK;
}
if (sKey == VBoxDefs::GUI_UpdateDlgWinID)
{
if (mIsUpdDlgOwner)
{
if (sVal.isEmpty() ||
sVal == QString ("%1")
.arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
*allowChange = TRUE;
else
*allowChange = FALSE;
}
else
*allowChange = TRUE;
return S_OK;
}
/* try to set the global setting to check its syntax */
VBoxGlobalSettings gs (false /* non-null */);
if (gs.setPublicProperty (sKey, sVal))
{
/* this is a known GUI property key */
if (!gs)
{
/* disallow the change when there is an error*/
*error = SysAllocString ((const OLECHAR *)
(gs.lastError().isNull() ? 0 : gs.lastError().utf16()));
*allowChange = FALSE;
}
else
*allowChange = TRUE;
return S_OK;
}
}
}
/* not interested in this key -- never disagree */
*allowChange = TRUE;
return S_OK;
}
STDMETHOD(OnExtraDataChange) (IN_GUIDPARAM id,
IN_BSTRPARAM key, IN_BSTRPARAM value)
{
if (COMBase::ToQUuid (id).isNull())
{
QString sKey = QString::fromUtf16 (key);
QString sVal = QString::fromUtf16 (value);
if (sKey.startsWith ("GUI/"))
{
if (sKey == VBoxDefs::GUI_RegistrationDlgWinID)
{
if (sVal.isEmpty())
{
mIsRegDlgOwner = false;
QApplication::postEvent (&mGlobal, new VBoxCanShowRegDlgEvent (true));
}
else if (sVal == QString ("%1")
.arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
{
mIsRegDlgOwner = true;
QApplication::postEvent (&mGlobal, new VBoxCanShowRegDlgEvent (true));
}
else
QApplication::postEvent (&mGlobal, new VBoxCanShowRegDlgEvent (false));
}
if (sKey == VBoxDefs::GUI_UpdateDlgWinID)
{
if (sVal.isEmpty())
{
mIsUpdDlgOwner = false;
QApplication::postEvent (&mGlobal, new VBoxCanShowUpdDlgEvent (true));
}
else if (sVal == QString ("%1")
.arg ((qulonglong) vboxGlobal().mainWindow()->winId()))
{
mIsUpdDlgOwner = true;
QApplication::postEvent (&mGlobal, new VBoxCanShowUpdDlgEvent (true));
}
else
QApplication::postEvent (&mGlobal, new VBoxCanShowUpdDlgEvent (false));
}
if (sKey == "GUI/LanguageID")
QApplication::postEvent (&mGlobal, new VBoxChangeGUILanguageEvent (sVal));
mMutex.lock();
mGlobal.gset.setPublicProperty (sKey, sVal);
mMutex.unlock();
Assert (!!mGlobal.gset);
}
}
return S_OK;
}
STDMETHOD(OnMediaRegistered) (IN_GUIDPARAM id, DeviceType_T type,
BOOL registered)
{
/** @todo */
Q_UNUSED (id);
Q_UNUSED (type);
Q_UNUSED (registered);
return S_OK;
}
STDMETHOD(OnMachineRegistered) (IN_GUIDPARAM id, BOOL registered)
{
postEvent (new VBoxMachineRegisteredEvent (COMBase::ToQUuid (id),
registered));
return S_OK;
}
STDMETHOD(OnSessionStateChange) (IN_GUIDPARAM id, SessionState_T state)
{
postEvent (new VBoxSessionStateChangeEvent (COMBase::ToQUuid (id),
(KSessionState) state));
return S_OK;
}
STDMETHOD(OnSnapshotTaken) (IN_GUIDPARAM aMachineId, IN_GUIDPARAM aSnapshotId)
{
postEvent (new VBoxSnapshotEvent (COMBase::ToQUuid (aMachineId),
COMBase::ToQUuid (aSnapshotId),
VBoxSnapshotEvent::Taken));
return S_OK;
}
STDMETHOD(OnSnapshotDiscarded) (IN_GUIDPARAM aMachineId, IN_GUIDPARAM aSnapshotId)
{
postEvent (new VBoxSnapshotEvent (COMBase::ToQUuid (aMachineId),
COMBase::ToQUuid (aSnapshotId),
VBoxSnapshotEvent::Discarded));
return S_OK;
}
STDMETHOD(OnSnapshotChange) (IN_GUIDPARAM aMachineId, IN_GUIDPARAM aSnapshotId)
{
postEvent (new VBoxSnapshotEvent (COMBase::ToQUuid (aMachineId),
COMBase::ToQUuid (aSnapshotId),
VBoxSnapshotEvent::Changed));
return S_OK;
}
STDMETHOD(OnGuestPropertyChange) (IN_GUIDPARAM /* id */,
IN_BSTRPARAM /* key */,
IN_BSTRPARAM /* value */,
IN_BSTRPARAM /* flags */)
{
return S_OK;
}
private:
void postEvent (QEvent *e)
{
// currently, we don't post events if we are in the VM execution
// console mode, to save some CPU ticks (so far, there was no need
// to handle VirtualBox callback events in the execution console mode)
if (!mGlobal.isVMConsoleProcess())
QApplication::postEvent (&mGlobal, e);
}
VBoxGlobal &mGlobal;
/** protects #OnExtraDataChange() */
QMutex mMutex;
bool mIsRegDlgOwner;
bool mIsUpdDlgOwner;
#if defined (Q_OS_WIN32)
private:
long refcnt;
#endif
};
#if !defined (Q_OS_WIN32)
NS_DECL_CLASSINFO (VBoxCallback)
NS_IMPL_THREADSAFE_ISUPPORTS1_CI (VBoxCallback, IVirtualBoxCallback)
#endif
// Helpers for VBoxGlobal::getOpenFileName() & getExistingDirectory()
/////////////////////////////////////////////////////////////////////////////
#if defined Q_WS_WIN
extern void qt_enter_modal (QWidget*);
extern void qt_leave_modal (QWidget*);
static QString extractFilter (const QString &aRawFilter)
{
static const char qt_file_dialog_filter_reg_exp[] =
"([a-zA-Z0-9 ]*)\\(([a-zA-Z0-9_.*? +;#\\[\\]]*)\\)$";
QString result = aRawFilter;
QRegExp r (QString::fromLatin1 (qt_file_dialog_filter_reg_exp));
int index = r.indexIn (result);
if (index >= 0)
result = r.cap (2);
return result.replace (QChar (' '), QChar (';'));
}
/**
* Converts QFileDialog filter list to Win32 API filter list.
*/
static QString winFilter (const QString &aFilter)
{
QStringList filterLst;
if (!aFilter.isEmpty())
{
int i = aFilter.indexOf (";;", 0);
QString sep (";;");
if (i == -1)
{
if (aFilter.indexOf ("\n", 0) != -1)
{
sep = "\n";
i = aFilter.indexOf (sep, 0);
}
}
filterLst = aFilter.split (sep);
}
QStringList::Iterator it = filterLst.begin();
QString winfilters;
for (; it != filterLst.end(); ++it)
{
winfilters += *it;
winfilters += QChar::Null;
winfilters += extractFilter (*it);
winfilters += QChar::Null;
}
winfilters += QChar::Null;
return winfilters;
}
/*
* Callback function to control the native Win32 API file dialog
*/
UINT_PTR CALLBACK OFNHookProc (HWND aHdlg, UINT aUiMsg, WPARAM aWParam, LPARAM aLParam)
{
if (aUiMsg == WM_NOTIFY)
{
OFNOTIFY *notif = (OFNOTIFY*) aLParam;
if (notif->hdr.code == CDN_TYPECHANGE)
{
/* locate native dialog controls */
HWND parent = GetParent (aHdlg);
HWND button = GetDlgItem (parent, IDOK);
HWND textfield = ::GetDlgItem (parent, cmb13);
if (textfield == NULL)
textfield = ::GetDlgItem (parent, edt1);
if (textfield == NULL)
return FALSE;
HWND selector = ::GetDlgItem (parent, cmb1);
/* simulate filter change by pressing apply-key */
int size = 256;
TCHAR *buffer = (TCHAR*)malloc (size);
SendMessage (textfield, WM_GETTEXT, size, (LPARAM)buffer);
SendMessage (textfield, WM_SETTEXT, 0, (LPARAM)"\0");
SendMessage (button, BM_CLICK, 0, 0);
SendMessage (textfield, WM_SETTEXT, 0, (LPARAM)buffer);
free (buffer);
/* make request for focus moving to filter selector combo-box */
HWND curFocus = GetFocus();
PostMessage (curFocus, WM_KILLFOCUS, (WPARAM)selector, 0);
PostMessage (selector, WM_SETFOCUS, (WPARAM)curFocus, 0);
WPARAM wParam = MAKEWPARAM (WA_ACTIVE, 0);
PostMessage (selector, WM_ACTIVATE, wParam, (LPARAM)curFocus);
}
}
return FALSE;
}
/*
* Callback function to control the native Win32 API folders dialog
*/
static int __stdcall winGetExistDirCallbackProc (HWND hwnd, UINT uMsg,
LPARAM lParam, LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED && lpData != 0)
{
QString *initDir = (QString *)(lpData);
if (!initDir->isEmpty())
{
SendMessage (hwnd, BFFM_SETSELECTION, TRUE, Q_ULONG (
initDir->isNull() ? 0 : initDir->utf16()));
//SendMessage (hwnd, BFFM_SETEXPANDED, TRUE, Q_ULONG (initDir->utf16()));
}
}
else if (uMsg == BFFM_SELCHANGED)
{
TCHAR path [MAX_PATH];
SHGetPathFromIDList (LPITEMIDLIST (lParam), path);
QString tmpStr = QString::fromUtf16 ((ushort*)path);
if (!tmpStr.isEmpty())
SendMessage (hwnd, BFFM_ENABLEOK, 1, 1);
else
SendMessage (hwnd, BFFM_ENABLEOK, 0, 0);
SendMessage (hwnd, BFFM_SETSTATUSTEXT, 1, Q_ULONG (path));
}
return 0;
}
/**
* QEvent class to carry Win32 API native dialog's result information
*/
class OpenNativeDialogEvent : public QEvent
{
public:
OpenNativeDialogEvent (const QString &aResult, QEvent::Type aType)
: QEvent (aType), mResult (aResult) {}
const QString& result() { return mResult; }
private:
QString mResult;
};
/**
* QObject class reimplementation which is the target for OpenNativeDialogEvent
* event. It receives OpenNativeDialogEvent event from another thread,
* stores result information and exits the given local event loop.
*/
class LoopObject : public QObject
{
public:
LoopObject (QEvent::Type aType, QEventLoop &aLoop)
: mType (aType), mLoop (aLoop), mResult (QString::null) {}
const QString& result() { return mResult; }
private:
bool event (QEvent *aEvent)
{
if (aEvent->type() == mType)
{
OpenNativeDialogEvent *ev = (OpenNativeDialogEvent*) aEvent;
mResult = ev->result();
mLoop.quit();
return true;
}
return QObject::event (aEvent);
}
QEvent::Type mType;
QEventLoop &mLoop;
QString mResult;
};
#endif /* Q_WS_WIN */
// VBoxGlobal
////////////////////////////////////////////////////////////////////////////////
static bool sVBoxGlobalInited = false;
static bool sVBoxGlobalInCleanup = false;
/** @internal
*
* Special routine to do VBoxGlobal cleanup when the application is being
* terminated. It is called before some essential Qt functionality (for
* instance, QThread) becomes unavailable, allowing us to use it from
* VBoxGlobal::cleanup() if necessary.
*/
static void vboxGlobalCleanup()
{
Assert (!sVBoxGlobalInCleanup);
sVBoxGlobalInCleanup = true;
vboxGlobal().cleanup();
}
/** @internal
*
* Determines the rendering mode from the argument. Sets the appropriate
* default rendering mode if the argumen is NULL.
*/
static VBoxDefs::RenderMode vboxGetRenderMode (const char *aModeStr)
{
VBoxDefs::RenderMode mode = VBoxDefs::InvalidRenderMode;
#if defined (Q_WS_MAC) && defined (VBOX_GUI_USE_QUARTZ2D)
mode = VBoxDefs::Quartz2DMode;
#elif (defined (Q_WS_WIN32) || defined (Q_WS_PM)) && defined (VBOX_GUI_USE_QIMAGE)
mode = VBoxDefs::QImageMode;
#elif defined (Q_WS_X11) && defined (VBOX_GUI_USE_SDL)
mode = VBoxDefs::SDLMode;
#elif defined (VBOX_GUI_USE_QIMAGE)
mode = VBoxDefs::QImageMode;
#else
# error "Cannot determine the default render mode!"
#endif
if (aModeStr)
{
if (0) ;
#if defined (VBOX_GUI_USE_QIMAGE)
else if (::strcmp (aModeStr, "image") == 0)
mode = VBoxDefs::QImageMode;
#endif
#if defined (VBOX_GUI_USE_SDL)
else if (::strcmp (aModeStr, "sdl") == 0)
mode = VBoxDefs::SDLMode;
#endif
#if defined (VBOX_GUI_USE_DDRAW)
else if (::strcmp (aModeStr, "ddraw") == 0)
mode = VBoxDefs::DDRAWMode;
#endif
#if defined (VBOX_GUI_USE_QUARTZ2D)
else if (::strcmp (aModeStr, "quartz2d") == 0)
mode = VBoxDefs::Quartz2DMode;
#endif
}
return mode;
}
/** @class VBoxGlobal
*
* The VBoxGlobal class incapsulates the global VirtualBox data.
*
* There is only one instance of this class per VirtualBox application,
* the reference to it is returned by the static instance() method, or by
* the global vboxGlobal() function, that is just an inlined shortcut.
*/
VBoxGlobal::VBoxGlobal()
: mValid (false)
, mSelectorWnd (NULL), mConsoleWnd (NULL)
, mMainWindow (NULL)
#ifdef VBOX_WITH_REGISTRATION
, mRegDlg (NULL)
#endif
, mUpdDlg (NULL)
, mMediaEnumThread (NULL)
, verString ("1.0")
, vm_state_color (KMachineState_COUNT)
, machineStates (KMachineState_COUNT)
, sessionStates (KSessionState_COUNT)
, deviceTypes (KDeviceType_COUNT)
, storageBuses (KStorageBus_COUNT)
, storageBusDevices (2)
, storageBusChannels (3)
, diskTypes (KHardDiskType_COUNT)
, vrdpAuthTypes (KVRDPAuthType_COUNT)
, portModeTypes (KPortMode_COUNT)
, usbFilterActionTypes (KUSBDeviceFilterAction_COUNT)
, audioDriverTypes (KAudioDriverType_COUNT)
, audioControllerTypes (KAudioControllerType_COUNT)
, networkAdapterTypes (KNetworkAdapterType_COUNT)
, networkAttachmentTypes (KNetworkAttachmentType_COUNT)
, clipboardTypes (KClipboardMode_COUNT)
, ideControllerTypes (KIDEControllerType_COUNT)
, USBDeviceStates (KUSBDeviceState_COUNT)
, detailReportTemplatesReady (false)
{
}
//
// Public members
/////////////////////////////////////////////////////////////////////////////
/**
* Returns a reference to the global VirtualBox data, managed by this class.
*
* The main() function of the VBox GUI must call this function soon after
* creating a QApplication instance but before opening any of the main windows
* (to let the VBoxGlobal initialization procedure use various Qt facilities),
* and continue execution only when the isValid() method of the returned
* instancereturns true, i.e. do something like:
*
* @code
* if ( !VBoxGlobal::instance().isValid() )
* return 1;
* @endcode
* or
* @code
* if ( !vboxGlobal().isValid() )
* return 1;
* @endcode
*
* @note Some VBoxGlobal methods can be used on a partially constructed
* VBoxGlobal instance, i.e. from constructors and methods called
* from the VBoxGlobal::init() method, which obtain the instance
* using this instance() call or the ::vboxGlobal() function. Currently, such
* methods are:
* #vmStateText, #vmTypeIcon, #vmTypeText, #vmTypeTextList, #vmTypeFromText.
*
* @see ::vboxGlobal
*/
VBoxGlobal &VBoxGlobal::instance()
{
static VBoxGlobal vboxGlobal_instance;
if (!sVBoxGlobalInited)
{
/* check that a QApplication instance is created */
if (qApp)
{
sVBoxGlobalInited = true;
vboxGlobal_instance.init();
/* add our cleanup handler to the list of Qt post routines */
qAddPostRoutine (vboxGlobalCleanup);
}
else
AssertMsgFailed (("Must construct a QApplication first!"));
}
return vboxGlobal_instance;
}
VBoxGlobal::~VBoxGlobal()
{
qDeleteAll (vm_os_type_icons);
qDeleteAll (mStateIcons);
qDeleteAll (vm_state_color);
}
/**
* Sets the new global settings and saves them to the VirtualBox server.
*/
bool VBoxGlobal::setSettings (const VBoxGlobalSettings &gs)
{
gs.save (mVBox);
if (!mVBox.isOk())
{
vboxProblem().cannotSaveGlobalConfig (mVBox);
return false;
}
/* We don't assign gs to our gset member here, because VBoxCallback
* will update gset as necessary when new settings are successfullly
* sent to the VirtualBox server by gs.save(). */
return true;
}
/**
* Returns a reference to the main VBox VM Selector window.
* The reference is valid until application termination.
*
* There is only one such a window per VirtualBox application.
*/
VBoxSelectorWnd &VBoxGlobal::selectorWnd()
{
#if defined (VBOX_GUI_SEPARATE_VM_PROCESS)
AssertMsg (!vboxGlobal().isVMConsoleProcess(),
("Must NOT be a VM console process"));
#endif
Assert (mValid);
if (!mSelectorWnd)
{
/*
* We pass the address of mSelectorWnd to the constructor to let it be
* initialized right after the constructor is called. It is necessary
* to avoid recursion, since this method may be (and will be) called
* from the below constructor or from constructors/methods it calls.
*/
VBoxSelectorWnd *w = new VBoxSelectorWnd (&mSelectorWnd, 0);
Assert (w == mSelectorWnd);
NOREF(w);
}
return *mSelectorWnd;
}
/**
* Returns a reference to the main VBox VM Console window.
* The reference is valid until application termination.
*
* There is only one such a window per VirtualBox application.
*/
VBoxConsoleWnd &VBoxGlobal::consoleWnd()
{
#if defined (VBOX_GUI_SEPARATE_VM_PROCESS)
AssertMsg (vboxGlobal().isVMConsoleProcess(),
("Must be a VM console process"));
#endif
Assert (mValid);
if (!mConsoleWnd)
{
/*
* We pass the address of mConsoleWnd to the constructor to let it be
* initialized right after the constructor is called. It is necessary
* to avoid recursion, since this method may be (and will be) called
* from the below constructor or from constructors/methods it calls.
*/
VBoxConsoleWnd *w = new VBoxConsoleWnd (&mConsoleWnd, 0);
Assert (w == mConsoleWnd);
NOREF(w);
}
return *mConsoleWnd;
}
/**
* Returns the list of all guest OS type descriptions, queried from
* IVirtualBox.
*/
QStringList VBoxGlobal::vmGuestOSTypeDescriptions() const
{
static QStringList list;
if (list.empty()) {
for (int i = 0; i < vm_os_types.count(); i++) {
list += vm_os_types [i].GetDescription();
}
}
return list;
}
QList
"
"
"
"
" + tr ("
" + tr ("
" + tr ("
" + tr ("
" + tr ("
" + tr ("
" + tr ("%1
";
static const char *sSectionHrefTpl =
" "
"%5"
" "
" ";
static const char *sSectionBoldTpl =
" "
"%5"
" "
" ";
static const char *sSectionItemTpl =
" ";
static QString sGeneralBasicHrefTpl, sGeneralBasicBoldTpl;
static QString sGeneralFullHrefTpl, sGeneralFullBoldTpl;
/* generate templates after every language change */
if (!detailReportTemplatesReady)
{
detailReportTemplatesReady = true;
QString generalItems
= QString (sSectionItemTpl).arg (tr ("Name", "details report"), "%1")
+ QString (sSectionItemTpl).arg (tr ("OS Type", "details report"), "%2")
+ QString (sSectionItemTpl).arg (tr ("Base Memory", "details report"),
tr ("%2 ")
.arg (m->GetSettingsFilePath())
.arg (version);
}
}
version = mVBox.GetSettingsFileVersion();
if (version != formatVersion)
{
isGlobalConverted = true;
fileList += QString (" "
" ")
.arg (mVBox.GetSettingsFilePath())
.arg (version);
}
if (!fileList.isNull())
{
fileList = QString (" "
"%1
")
.arg (fileList);
int rc = vboxProblem()
.warnAboutAutoConvertedSettings (formatVersion, fileList);
if (rc == QIMessageBox::Cancel)
return false;
Assert (rc == QIMessageBox::No || rc == QIMessageBox::Yes);
/* backup (optionally) and save all settings files
* (QIMessageBox::No = Backup, QIMessageBox::Yes = Save) */
foreach (CMachine m, machines)
{
CSession session = openSession (m.GetId());
if (!session.isNull())
{
CMachine sm = session.GetMachine();
if (rc == QIMessageBox::No)
sm.SaveSettingsWithBackup();
else
sm.SaveSettings();
if (!sm.isOk())
vboxProblem().cannotSaveMachineSettings (sm);
session.Close();
}
}
if (isGlobalConverted)
{
if (rc == QIMessageBox::No)
mVBox.SaveSettingsWithBackup();
else
mVBox.SaveSettings();
if (!mVBox.isOk())
vboxProblem().cannotSaveGlobalSettings (mVBox);
}
}
return true;
}
/**
* Opens a direct session for a machine with the given ID.
* This method does user-friendly error handling (display error messages, etc.).
* and returns a null CSession object in case of any error.
* If this method succeeds, don't forget to close the returned session when
* it is no more necessary.
*
* @param aId Machine ID.
* @param aExisting @c true to open an existing session with the machine
* which is already running, @c false to open a new direct
* session.
*/
CSession VBoxGlobal::openSession (const QUuid &aId, bool aExisting /* = false */)
{
CSession session;
session.createInstance (CLSID_Session);
if (session.isNull())
{
vboxProblem().cannotOpenSession (session);
return session;
}
if (aExisting)
mVBox.OpenExistingSession (session, aId);
else
{
mVBox.OpenSession (session, aId);
CMachine machine = session.GetMachine ();
/* Make sure that the language is in two letter code.
* Note: if languageId() returns an empty string lang.name() will
* return "C" which is an valid language code. */
QLocale lang (VBoxGlobal::languageId());
machine.SetGuestPropertyValue ("/VirtualBox/HostInfo/GUI/LanguageID", lang.name());
}
if (!mVBox.isOk())
{
CMachine machine = CVirtualBox (mVBox).GetMachine (aId);
vboxProblem().cannotOpenSession (mVBox, machine);
session.detach();
}
return session;
}
/**
* Starts a machine with the given ID.
*/
bool VBoxGlobal::startMachine (const QUuid &id)
{
AssertReturn (mValid, false);
CSession session = vboxGlobal().openSession (id);
if (session.isNull())
return false;
return consoleWnd().openView (session);
}
/**
* Appends the given list of hard disks and all their children to the media
* list. To be called only from VBoxGlobal::startEnumeratingMedia().
*/
static
void AddHardDisksToList (const CHardDisk2Vector &aVector,
VBoxMediaList &aList,
VBoxMediaList::iterator aWhere,
VBoxMedium *aParent = 0)
{
VBoxMediaList::iterator first = aWhere;
/* First pass: Add siblings sorted */
for (CHardDisk2Vector::ConstIterator it = aVector.begin();
it != aVector.end(); ++ it)
{
CMedium cmedium (*it);
VBoxMedium medium (cmedium, VBoxDefs::MediaType_HardDisk, aParent);
/* Search for a proper alphabetic position */
VBoxMediaList::iterator jt = first;
for (; jt != aWhere; ++ jt)
if ((*jt).name().localeAwareCompare (medium.name()) > 0)
break;
aList.insert (jt, medium);
/* Adjust the first item if inserted before it */
if (jt == first)
-- first;
}
/* Second pass: Add children */
for (VBoxMediaList::iterator it = first; it != aWhere;)
{
CHardDisk2Vector children = (*it).hardDisk().GetChildren();
VBoxMedium *parent = &(*it);
++ it; /* go to the next sibling before inserting children */
AddHardDisksToList (children, aList, it, parent);
}
}
/**
* Starts a thread that asynchronously enumerates all currently registered
* media.
*
* Before the enumeration is started, the current media list (a list returned by
* #currentMediaList()) is populated with all registered media and the
* #mediumEnumStarted() signal is emitted. The enumeration thread then walks this
* list, checks for media acessiblity and emits #mediumEnumerated() signals of
* each checked medium. When all media are checked, the enumeration thread is
* stopped and the #mediumEnumFinished() signal is emitted.
*
* If the enumeration is already in progress, no new thread is started.
*
* The media list returned by #currentMediaList() is always sorted
* alphabetically by the location attribute and comes in the following order:
*
*
*
* Note that #mediumEnumerated() signals are emitted in the same order as
* described above.
*
* @sa #currentMediaList()
* @sa #isMediaEnumerationStarted()
*/
void VBoxGlobal::startEnumeratingMedia()
{
AssertReturnVoid (mValid);
/* check if already started but not yet finished */
if (mMediaEnumThread != NULL)
return;
/* ignore the request during application termination */
if (sVBoxGlobalInCleanup)
return;
/* composes a list of all currently known media & their children */
mMediaList.clear();
{
AddHardDisksToList (mVBox.GetHardDisks2(), mMediaList, mMediaList.end());
}
{
VBoxMediaList::iterator first = mMediaList.end();
CDVDImage2Vector vec = mVBox.GetDVDImages();
for (CDVDImage2Vector::ConstIterator it = vec.begin();
it != vec.end(); ++ it)
{
CMedium cmedium (*it);
VBoxMedium medium (cmedium, VBoxDefs::MediaType_DVD);
/* Search for a proper alphabetic position */
VBoxMediaList::iterator jt = first;
for (; jt != mMediaList.end(); ++ jt)
if ((*jt).name().localeAwareCompare (medium.name()) > 0)
break;
mMediaList.insert (jt, medium);
/* Adjust the first item if inserted before it */
if (jt == first)
-- first;
}
}
{
VBoxMediaList::iterator first = mMediaList.end();
CFloppyImage2Vector vec = mVBox.GetFloppyImages();
for (CFloppyImage2Vector::ConstIterator it = vec.begin();
it != vec.end(); ++ it)
{
CMedium cmedium (*it);
VBoxMedium medium (cmedium, VBoxDefs::MediaType_Floppy);
/* Search for a proper alphabetic position */
VBoxMediaList::iterator jt = first;
for (; jt != mMediaList.end(); ++ jt)
if ((*jt).name().localeAwareCompare (medium.name()) > 0)
break;
mMediaList.insert (jt, medium);
/* Adjust the first item if inserted before it */
if (jt == first)
-- first;
}
}
mCurrentMediumIterator = mMediaList.begin();
/* enumeration thread class */
class MediaEnumThread : public QThread
{
public:
MediaEnumThread (const VBoxMediaList &aList) : mList (aList) {}
virtual void run()
{
LogFlow (("MediaEnumThread started.\n"));
COMBase::InitializeCOM();
CVirtualBox mVBox = vboxGlobal().virtualBox();
QObject *self = &vboxGlobal();
/* Enumerate the list */
int index = 0;
VBoxMediaList::const_iterator it;
for (it = mList.begin();
it != mList.end() && !sVBoxGlobalInCleanup;
++ it, ++ index)
{
VBoxMedium medium = *it;
medium.blockAndQueryState();
QApplication::postEvent (self, new VBoxMediaEnumEvent (medium));
}
/* Post the end-of-enumeration event */
if (!sVBoxGlobalInCleanup)
QApplication::postEvent (self, new VBoxMediaEnumEvent());
COMBase::CleanupCOM();
LogFlow (("MediaEnumThread finished.\n"));
}
private:
const VBoxMediaList &mList;
};
mMediaEnumThread = new MediaEnumThread (mMediaList);
AssertReturnVoid (mMediaEnumThread);
/* emit mediumEnumStarted() after we set mMediaEnumThread to != NULL
* to cause isMediaEnumerationStarted() to return TRUE from slots */
emit mediumEnumStarted();
mMediaEnumThread->start();
}
/**
* Adds a new medium to the current media list and emits the #mediumAdded()
* signal.
*
* @sa #currentMediaList()
*/
void VBoxGlobal::addMedium (const VBoxMedium &aMedium)
{
/* Note that we maitain the same order here as #startEnumeratingMedia() */
VBoxMediaList::iterator it = mMediaList.begin();
if (aMedium.type() == VBoxDefs::MediaType_HardDisk)
{
VBoxMediaList::iterator parent = mMediaList.end();
for (; it != mMediaList.end(); ++ it)
{
if ((*it).type() != VBoxDefs::MediaType_HardDisk)
break;
if (aMedium.parent() != NULL && parent == mMediaList.end())
{
if (&*it == aMedium.parent())
parent = it;
}
else
{
/* break if met a parent's sibling (will insert before it) */
if (aMedium.parent() != NULL &&
(*it).parent() == (*parent).parent())
break;
/* compare to aMedium's siblings */
if ((*it).parent() == aMedium.parent() &&
(*it).name().localeAwareCompare (aMedium.name()) > 0)
break;
}
}
AssertReturnVoid (aMedium.parent() == NULL || parent != mMediaList.end());
}
else
{
for (; it != mMediaList.end(); ++ it)
{
/* skip HardDisks that come first */
if ((*it).type() == VBoxDefs::MediaType_HardDisk)
continue;
/* skip DVD when inserting Floppy */
if (aMedium.type() == VBoxDefs::MediaType_Floppy &&
(*it).type() == VBoxDefs::MediaType_DVD)
continue;
if ((*it).name().localeAwareCompare (aMedium.name()) > 0 ||
(aMedium.type() == VBoxDefs::MediaType_DVD &&
(*it).type() == VBoxDefs::MediaType_Floppy))
break;
}
}
it = mMediaList.insert (it, aMedium);
emit mediumAdded (*it);
}
/**
* Updates the medium in the current media list and emits the #mediumUpdated()
* signal.
*
* @sa #currentMediaList()
*/
void VBoxGlobal::updateMedium (const VBoxMedium &aMedium)
{
VBoxMediaList::Iterator it;
for (it = mMediaList.begin(); it != mMediaList.end(); ++ it)
if ((*it).id() == aMedium.id())
break;
AssertReturnVoid (it != mMediaList.end());
if (&*it != &aMedium)
*it = aMedium;
emit mediumUpdated (*it);
}
/**
* Removes the medium from the current media list and emits the #mediumRemoved()
* signal.
*
* @sa #currentMediaList()
*/
void VBoxGlobal::removeMedium (VBoxDefs::MediaType aType, const QUuid &aId)
{
VBoxMediaList::Iterator it;
for (it = mMediaList.begin(); it != mMediaList.end(); ++ it)
if ((*it).id() == aId)
break;
AssertReturnVoid (it != mMediaList.end());
#if DEBUG
/* sanity: must be no children */
{
VBoxMediaList::Iterator jt = it;
++ jt;
AssertReturnVoid (jt == mMediaList.end() || (*jt).parent() != &*it);
}
#endif
VBoxMedium *parent = (*it).parent();
/* remove the medium from the list to keep it in sync with the server "for
* free" when the medium is deleted from one of our UIs */
mMediaList.erase (it);
emit mediumRemoved (aType, aId);
/* also emit the parent update signal because some attributes like
* isReadOnly() may have been changed after child removal */
if (parent != NULL)
{
parent->refresh();
emit mediumUpdated (*parent);
}
}
/**
* Searches for a VBoxMedum object representing the given COM medium object.
*
* @return true if found and false otherwise.
*/
bool VBoxGlobal::findMedium (const CMedium &aObj, VBoxMedium &aMedium) const
{
for (VBoxMediaList::ConstIterator it = mMediaList.begin();
it != mMediaList.end(); ++ it)
{
if ((*it).medium() == aObj)
{
aMedium = (*it);
return true;
}
}
return false;
}
/**
* Native language name of the currently installed translation.
* Returns "English" if no translation is installed
* or if the translation file is invalid.
*/
QString VBoxGlobal::languageName() const
{
return qApp->translate ("@@@", "English",
"Native language name");
}
/**
* Native language country name of the currently installed translation.
* Returns "--" if no translation is installed or if the translation file is
* invalid, or if the language is independent on the country.
*/
QString VBoxGlobal::languageCountry() const
{
return qApp->translate ("@@@", "--",
"Native language country name "
"(empty if this language is for all countries)");
}
/**
* Language name of the currently installed translation, in English.
* Returns "English" if no translation is installed
* or if the translation file is invalid.
*/
QString VBoxGlobal::languageNameEnglish() const
{
return qApp->translate ("@@@", "English",
"Language name, in English");
}
/**
* Language country name of the currently installed translation, in English.
* Returns "--" if no translation is installed or if the translation file is
* invalid, or if the language is independent on the country.
*/
QString VBoxGlobal::languageCountryEnglish() const
{
return qApp->translate ("@@@", "--",
"Language country name, in English "
"(empty if native country name is empty)");
}
/**
* Comma-separated list of authors of the currently installed translation.
* Returns "Sun Microsystems, Inc." if no translation is installed or if the
* translation file is invalid, or if the translation is supplied by Sun
* Microsystems, inc.
*/
QString VBoxGlobal::languageTranslators() const
{
return qApp->translate ("@@@", "Sun Microsystems, Inc.",
"Comma-separated list of translators");
}
/**
* Changes the language of all global string constants according to the
* currently installed translations tables.
*/
void VBoxGlobal::retranslateUi()
{
machineStates [KMachineState_PoweredOff] = tr ("Powered Off", "MachineState");
machineStates [KMachineState_Saved] = tr ("Saved", "MachineState");
machineStates [KMachineState_Aborted] = tr ("Aborted", "MachineState");
machineStates [KMachineState_Running] = tr ("Running", "MachineState");
machineStates [KMachineState_Paused] = tr ("Paused", "MachineState");
machineStates [KMachineState_Stuck] = tr ("Stuck", "MachineState");
machineStates [KMachineState_Starting] = tr ("Starting", "MachineState");
machineStates [KMachineState_Stopping] = tr ("Stopping", "MachineState");
machineStates [KMachineState_Saving] = tr ("Saving", "MachineState");
machineStates [KMachineState_Restoring] = tr ("Restoring", "MachineState");
machineStates [KMachineState_Discarding] = tr ("Discarding", "MachineState");
machineStates [KMachineState_SettingUp] = tr ("Setting Up", "MachineState");
sessionStates [KSessionState_Closed] = tr ("Closed", "SessionState");
sessionStates [KSessionState_Open] = tr ("Open", "SessionState");
sessionStates [KSessionState_Spawning] = tr ("Spawning", "SessionState");
sessionStates [KSessionState_Closing] = tr ("Closing", "SessionState");
deviceTypes [KDeviceType_Null] = tr ("None", "DeviceType");
deviceTypes [KDeviceType_Floppy] = tr ("Floppy", "DeviceType");
deviceTypes [KDeviceType_DVD] = tr ("CD/DVD-ROM", "DeviceType");
deviceTypes [KDeviceType_HardDisk] = tr ("Hard Disk", "DeviceType");
deviceTypes [KDeviceType_Network] = tr ("Network", "DeviceType");
deviceTypes [KDeviceType_USB] = tr ("USB", "DeviceType");
deviceTypes [KDeviceType_SharedFolder] = tr ("Shared Folder", "DeviceType");
storageBuses [KStorageBus_IDE] =
tr ("IDE", "StorageBus");
storageBuses [KStorageBus_SATA] =
tr ("SATA", "StorageBus");
Assert (storageBusChannels.count() == 3);
storageBusChannels [0] =
tr ("Primary", "StorageBusChannel");
storageBusChannels [1] =
tr ("Secondary", "StorageBusChannel");
storageBusChannels [2] =
tr ("Port %1", "StorageBusChannel");
Assert (storageBusDevices.count() == 2);
storageBusDevices [0] = tr ("Master", "StorageBusDevice");
storageBusDevices [1] = tr ("Slave", "StorageBusDevice");
diskTypes [KHardDiskType_Normal] =
tr ("Normal", "DiskType");
diskTypes [KHardDiskType_Immutable] =
tr ("Immutable", "DiskType");
diskTypes [KHardDiskType_Writethrough] =
tr ("Writethrough", "DiskType");
diskTypes_Differencing =
tr ("Differencing", "DiskType");
vrdpAuthTypes [KVRDPAuthType_Null] =
tr ("Null", "VRDPAuthType");
vrdpAuthTypes [KVRDPAuthType_External] =
tr ("External", "VRDPAuthType");
vrdpAuthTypes [KVRDPAuthType_Guest] =
tr ("Guest", "VRDPAuthType");
portModeTypes [KPortMode_Disconnected] =
tr ("Disconnected", "PortMode");
portModeTypes [KPortMode_HostPipe] =
tr ("Host Pipe", "PortMode");
portModeTypes [KPortMode_HostDevice] =
tr ("Host Device", "PortMode");
usbFilterActionTypes [KUSBDeviceFilterAction_Ignore] =
tr ("Ignore", "USBFilterActionType");
usbFilterActionTypes [KUSBDeviceFilterAction_Hold] =
tr ("Hold", "USBFilterActionType");
audioDriverTypes [KAudioDriverType_Null] =
tr ("Null Audio Driver", "AudioDriverType");
audioDriverTypes [KAudioDriverType_WinMM] =
tr ("Windows Multimedia", "AudioDriverType");
audioDriverTypes [KAudioDriverType_SolAudio] =
tr ("Solaris Audio", "AudioDriverType");
audioDriverTypes [KAudioDriverType_OSS] =
tr ("OSS Audio Driver", "AudioDriverType");
audioDriverTypes [KAudioDriverType_ALSA] =
tr ("ALSA Audio Driver", "AudioDriverType");
audioDriverTypes [KAudioDriverType_DirectSound] =
tr ("Windows DirectSound", "AudioDriverType");
audioDriverTypes [KAudioDriverType_CoreAudio] =
tr ("CoreAudio", "AudioDriverType");
audioDriverTypes [KAudioDriverType_Pulse] =
tr ("PulseAudio", "AudioDriverType");
audioControllerTypes [KAudioControllerType_AC97] =
tr ("ICH AC97", "AudioControllerType");
audioControllerTypes [KAudioControllerType_SB16] =
tr ("SoundBlaster 16", "AudioControllerType");
networkAdapterTypes [KNetworkAdapterType_Am79C970A] =
tr ("PCnet-PCI II (Am79C970A)", "NetworkAdapterType");
networkAdapterTypes [KNetworkAdapterType_Am79C973] =
tr ("PCnet-FAST III (Am79C973)", "NetworkAdapterType");
networkAdapterTypes [KNetworkAdapterType_I82540EM] =
tr ("Intel PRO/1000 MT Desktop (82540EM)", "NetworkAdapterType");
networkAdapterTypes [KNetworkAdapterType_I82543GC] =
tr ("Intel PRO/1000 T Server (82543GC)", "NetworkAdapterType");
networkAttachmentTypes [KNetworkAttachmentType_Null] =
tr ("Not attached", "NetworkAttachmentType");
networkAttachmentTypes [KNetworkAttachmentType_NAT] =
tr ("NAT", "NetworkAttachmentType");
networkAttachmentTypes [KNetworkAttachmentType_HostInterface] =
tr ("Host Interface", "NetworkAttachmentType");
networkAttachmentTypes [KNetworkAttachmentType_Internal] =
tr ("Internal Network", "NetworkAttachmentType");
clipboardTypes [KClipboardMode_Disabled] =
tr ("Disabled", "ClipboardType");
clipboardTypes [KClipboardMode_HostToGuest] =
tr ("Host To Guest", "ClipboardType");
clipboardTypes [KClipboardMode_GuestToHost] =
tr ("Guest To Host", "ClipboardType");
clipboardTypes [KClipboardMode_Bidirectional] =
tr ("Bidirectional", "ClipboardType");
ideControllerTypes [KIDEControllerType_PIIX3] =
tr ("PIIX3", "IDEControllerType");
ideControllerTypes [KIDEControllerType_PIIX4] =
tr ("PIIX4", "IDEControllerType");
USBDeviceStates [KUSBDeviceState_NotSupported] =
tr ("Not supported", "USBDeviceState");
USBDeviceStates [KUSBDeviceState_Unavailable] =
tr ("Unavailable", "USBDeviceState");
USBDeviceStates [KUSBDeviceState_Busy] =
tr ("Busy", "USBDeviceState");
USBDeviceStates [KUSBDeviceState_Available] =
tr ("Available", "USBDeviceState");
USBDeviceStates [KUSBDeviceState_Held] =
tr ("Held", "USBDeviceState");
USBDeviceStates [KUSBDeviceState_Captured] =
tr ("Captured", "USBDeviceState");
mUserDefinedPortName = tr ("User-defined", "serial port");
mWarningIcon = standardIcon (QStyle::SP_MessageBoxWarning, 0).pixmap (16, 16);
Assert (!mWarningIcon.isNull());
mErrorIcon = standardIcon (QStyle::SP_MessageBoxCritical, 0).pixmap (16, 16);
Assert (!mErrorIcon.isNull());
detailReportTemplatesReady = false;
#if defined (Q_WS_PM) || defined (Q_WS_X11)
/* As PM and X11 do not (to my knowledge) have functionality for providing
* human readable key names, we keep a table of them, which must be
* updated when the language is changed. */
QIHotKeyEdit::retranslateUi();
#endif
}
// public static stuff
////////////////////////////////////////////////////////////////////////////////
/* static */
bool VBoxGlobal::isDOSType (const QString &aOSTypeId)
{
if (aOSTypeId.left (3) == "dos" ||
aOSTypeId.left (3) == "win" ||
aOSTypeId.left (3) == "os2")
return true;
return false;
}
/**
* Sets the QLabel background and frame colors according tho the pixmap
* contents. The bottom right pixel of the label pixmap defines the
* background color of the label, the top right pixel defines the color of
* the one-pixel frame around it. This function also sets the alignment of
* the pixmap to AlignVTop (to correspond to the color choosing logic).
*
* This method is useful to provide nice scaling of pixmal labels without
* scaling pixmaps themselves. To see th eeffect, the size policy of the
* label in the corresponding direction (vertical, for now) should be set to
* something like MinimumExpanding.
*
* @todo Parametrize corners to select pixels from and set the alignment
* accordingly.
*/
/* static */
void VBoxGlobal::adoptLabelPixmap (QLabel *aLabel)
{
AssertReturnVoid (aLabel);
aLabel->setAlignment (Qt::AlignTop);
aLabel->setFrameShape (QFrame::Box);
aLabel->setFrameShadow (QFrame::Plain);
const QPixmap *pix = aLabel->pixmap();
QImage img = pix->toImage();
QRgb rgbBack = img.pixel (img.width() - 1, img.height() - 1);
QRgb rgbFrame = img.pixel (img.width() - 1, 0);
QPalette pal = aLabel->palette();
pal.setColor (QPalette::Window, rgbBack);
pal.setColor (QPalette::WindowText, rgbFrame);
aLabel->setPalette (pal);
}
extern const char *gVBoxLangSubDir = "/nls";
extern const char *gVBoxLangFileBase = "VirtualBox_";
extern const char *gVBoxLangFileExt = ".qm";
extern const char *gVBoxLangIDRegExp = "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
extern const char *gVBoxBuiltInLangName = "C";
class VBoxTranslator : public QTranslator
{
public:
VBoxTranslator (QObject *aParent = 0)
: QTranslator (aParent) {}
bool loadFile (const QString &aFileName)
{
QFile file (aFileName);
if (!file.open (QIODevice::ReadOnly))
return false;
mData = file.readAll();
return load ((uchar*) mData.data(), mData.size());
}
private:
QByteArray mData;
};
static VBoxTranslator *sTranslator = 0;
static QString sLoadedLangId = gVBoxBuiltInLangName;
/**
* Returns the loaded (active) language ID.
* Note that it may not match with VBoxGlobalSettings::languageId() if the
* specified language cannot be loaded.
* If the built-in language is active, this method returns "C".
*
* @note "C" is treated as the built-in language for simplicity -- the C
* locale is used in unix environments as a fallback when the requested
* locale is invalid. This way we don't need to process both the "built_in"
* language and the "C" language (which is a valid environment setting)
* separately.
*/
/* static */
QString VBoxGlobal::languageId()
{
return sLoadedLangId;
}
/**
* Loads the language by language ID.
*
* @param aLangId Language ID in in form of xx_YY. QString::null means the
* system default language.
*/
/* static */
void VBoxGlobal::loadLanguage (const QString &aLangId)
{
QString langId = aLangId.isNull() ?
VBoxGlobal::systemLanguageId() : aLangId;
QString languageFileName;
QString selectedLangId = gVBoxBuiltInLangName;
char szNlsPath[RTPATH_MAX];
int rc;
rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
AssertRC (rc);
QString nlsPath = QString(szNlsPath) + gVBoxLangSubDir;
QDir nlsDir (nlsPath);
Assert (!langId.isEmpty());
if (!langId.isEmpty() && langId != gVBoxBuiltInLangName)
{
QRegExp regExp (gVBoxLangIDRegExp);
int pos = regExp.indexIn (langId);
/* the language ID should match the regexp completely */
AssertReturnVoid (pos == 0);
QString lang = regExp.cap (2);
if (nlsDir.exists (gVBoxLangFileBase + langId + gVBoxLangFileExt))
{
languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + langId +
gVBoxLangFileExt);
selectedLangId = langId;
}
else if (nlsDir.exists (gVBoxLangFileBase + lang + gVBoxLangFileExt))
{
languageFileName = nlsDir.absoluteFilePath (gVBoxLangFileBase + lang +
gVBoxLangFileExt);
selectedLangId = lang;
}
else
{
/* Never complain when the default language is requested. In any
* case, if no explicit language file exists, we will simply
* fall-back to English (built-in). */
if (!aLangId.isNull())
vboxProblem().cannotFindLanguage (langId, nlsPath);
/* selectedLangId remains built-in here */
AssertReturnVoid (selectedLangId == gVBoxBuiltInLangName);
}
}
/* delete the old translator if there is one */
if (sTranslator)
{
/* QTranslator destructor will call qApp->removeTranslator() for
* us. It will also delete all its child translations we attach to it
* below, so we don't have to care about them specially. */
delete sTranslator;
}
/* load new language files */
sTranslator = new VBoxTranslator (qApp);
Assert (sTranslator);
bool loadOk = true;
if (sTranslator)
{
if (selectedLangId != gVBoxBuiltInLangName)
{
Assert (!languageFileName.isNull());
loadOk = sTranslator->loadFile (languageFileName);
}
/* we install the translator in any case: on failure, this will
* activate an empty translator that will give us English
* (built-in) */
qApp->installTranslator (sTranslator);
}
else
loadOk = false;
if (loadOk)
sLoadedLangId = selectedLangId;
else
{
vboxProblem().cannotLoadLanguage (languageFileName);
sLoadedLangId = gVBoxBuiltInLangName;
}
/* Try to load the corresponding Qt translation */
if (sLoadedLangId != gVBoxBuiltInLangName)
{
#ifdef Q_OS_UNIX
/* We use system installations of Qt on Linux systems, so first, try
* to load the Qt translation from the system location. */
languageFileName = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + "/qt_" +
sLoadedLangId + gVBoxLangFileExt;
QTranslator *qtSysTr = new QTranslator (sTranslator);
Assert (qtSysTr);
if (qtSysTr && qtSysTr->load (languageFileName))
qApp->installTranslator (qtSysTr);
/* Note that the Qt translation supplied by Sun is always loaded
* afterwards to make sure it will take precedence over the system
* translation (it may contain more decent variants of translation
* that better correspond to VirtualBox UI). We need to load both
* because a newer version of Qt may be installed on the user computer
* and the Sun version may not fully support it. We don't do it on
* Win32 because we supply a Qt library there and therefore the
* Sun translation is always the best one. */
#endif
languageFileName = nlsDir.absoluteFilePath (QString ("qt_") +
sLoadedLangId +
gVBoxLangFileExt);
QTranslator *qtTr = new QTranslator (sTranslator);
Assert (qtTr);
if (qtTr && (loadOk = qtTr->load (languageFileName)))
qApp->installTranslator (qtTr);
/* The below message doesn't fit 100% (because it's an additonal
* language and the main one won't be reset to built-in on failure)
* but the load failure is so rare here that it's not worth a separate
* message (but still, having something is better than having none) */
if (!loadOk && !aLangId.isNull())
vboxProblem().cannotLoadLanguage (languageFileName);
}
}
QString VBoxGlobal::helpFile() const
{
#if defined (Q_WS_WIN32) || defined (Q_WS_X11)
const QString name = "VirtualBox";
const QString suffix = "chm";
#elif defined (Q_WS_MAC)
const QString name = "UserManual";
const QString suffix = "pdf";
#endif
/* Where are the docs located? */
char szDocsPath[RTPATH_MAX];
int rc = RTPathAppDocs (szDocsPath, sizeof (szDocsPath));
AssertRC (rc);
/* Make sure that the language is in two letter code.
* Note: if languageId() returns an empty string lang.name() will
* return "C" which is an valid language code. */
QLocale lang (VBoxGlobal::languageId());
/* Construct the path and the filename */
QString manual = QString ("%1/%2_%3.%4").arg (szDocsPath)
.arg (name)
.arg (lang.name())
.arg (suffix);
/* Check if a help file with that name exists */
QFileInfo fi (manual);
if (fi.exists())
return manual;
/* Fall back to the standard */
manual = QString ("%1/%2.%4").arg (szDocsPath)
.arg (name)
.arg (suffix);
return manual;
}
/* static */
QIcon VBoxGlobal::iconSet (const char *aNormal,
const char *aDisabled /* = NULL */,
const char *aActive /* = NULL */)
{
QIcon iconSet;
iconSet.addFile (aNormal, QSize(),
QIcon::Normal);
if (aDisabled != NULL)
iconSet.addFile (aDisabled, QSize(),
QIcon::Disabled);
if (aActive != NULL)
iconSet.addFile (aActive, QSize(),
QIcon::Active);
return iconSet;
}
/* static */
QIcon VBoxGlobal::iconSetFull (const QSize &aNormalSize, const QSize &aSmallSize,
const char *aNormal, const char *aSmallNormal,
const char *aDisabled /* = NULL */,
const char *aSmallDisabled /* = NULL */,
const char *aActive /* = NULL */,
const char *aSmallActive /* = NULL */)
{
QIcon iconSet;
iconSet.addFile (aNormal, aNormalSize, QIcon::Normal);
iconSet.addFile (aSmallNormal, aSmallSize, QIcon::Normal);
if (aSmallDisabled != NULL)
{
iconSet.addFile (aDisabled, aNormalSize, QIcon::Disabled);
iconSet.addFile (aSmallDisabled, aSmallSize, QIcon::Disabled);
}
if (aSmallActive != NULL)
{
iconSet.addFile (aActive, aNormalSize, QIcon::Active);
iconSet.addFile (aSmallActive, aSmallSize, QIcon::Active);
}
return iconSet;
}
QIcon VBoxGlobal::standardIcon (QStyle::StandardPixmap aStandard, QWidget *aWidget /* = NULL */)
{
QStyle *style = aWidget ? aWidget->style(): QApplication::style();
#ifdef Q_WS_MAC
/* At least in Qt 4.3.4/4.4 RC1 SP_MessageBoxWarning is the application
* icon. So change this to the critical icon. (Maybe this would be
* fixed in a later Qt version) */
if (aStandard == QStyle::SP_MessageBoxWarning)
return style->standardIcon (QStyle::SP_MessageBoxCritical, 0, aWidget);
#endif /* Q_WS_MAC */
return style->standardIcon (aStandard, 0, aWidget);
}
/**
* Replacement for QToolButton::setTextLabel() that handles the shortcut
* letter (if it is present in the argument string) as if it were a setText()
* call: the shortcut letter is used to automatically assign an "Alt+
*
*
* @param aSize size value in bytes
* @param aMode convertion mode (-1, 0 or 1)
* @return human-readable size string
*/
/* static */
QString VBoxGlobal::formatSize (quint64 aSize, int aMode /* = 0 */)
{
static const char *Suffixes [] = { "B", "KB", "MB", "GB", "TB", "PB", NULL };
quint64 denom = 0;
int suffix = 0;
if (aSize < _1K)
{
denom = 1;
suffix = 0;
}
else if (aSize < _1M)
{
denom = _1K;
suffix = 1;
}
else if (aSize < _1G)
{
denom = _1M;
suffix = 2;
}
else if (aSize < _1T)
{
denom = _1G;
suffix = 3;
}
else if (aSize < _1P)
{
denom = _1T;
suffix = 4;
}
else
{
denom = _1P;
suffix = 5;
}
quint64 intg = aSize / denom;
quint64 hund = aSize % denom;
QString number;
if (denom > 1)
{
if (hund)
{
hund *= 100;
/* not greater */
if (aMode < 0) hund = hund / denom;
/* not less */
else if (aMode > 0) hund = (hund + denom - 1) / denom;
/* nearest */
else hund = (hund + denom / 2) / denom;
}
/* check for the fractional part overflow due to rounding */
if (hund == 100)
{
hund = 0;
++ intg;
/* check if we've got 1024 XB after rounding and scale down if so */
if (intg == 1024 && Suffixes [suffix + 1] != NULL)
{
intg /= 1024;
++ suffix;
}
}
number = QString ("%1%2%3").arg (intg).arg (decimalSep())
.arg (QString::number (hund).rightJustified (2, '0'));
}
else
{
number = QString::number (intg);
}
return QString ("%1 %2").arg (number).arg (Suffixes [suffix]);
}
/**
* Puts soft hyphens after every path component in the given file name.
*
* @param aFileName File name (must be a full path name).
*/
/* static */
QString VBoxGlobal::locationForHTML (const QString &aFileName)
{
/// @todo (dmik) remove?
// QString result = QDir::toNativeSeparators (fn);
//#ifdef Q_OS_LINUX
// result.replace ('/', "/");
//#else
// result.replace ('\\', "\\");
//#endif
// return result;
QFileInfo fi (aFileName);
return fi.fileName();
}
/**
* Reformats the input string @a aStr so that:
* - strings in single quotes will be put inside
constructs to form paragraphs * (note that
and
are not appended to the beginnign and to the * end of the string respectively, to allow the result be appended * or prepended to the existing paragraph). * * If @a aToolTip is true, colouring is not applied, only the.
*/
/* static */
QString VBoxGlobal::highlight (const QString &aStr, bool aToolTip /* = false */)
{
QString strFont;
QString uuidFont;
QString endFont;
if (!aToolTip)
{
strFont = "";
uuidFont = "";
endFont = "";
}
QString text = aStr;
/* replace special entities, '&' -- first! */
text.replace ('&', "&");
text.replace ('<', "<");
text.replace ('>', ">");
text.replace ('\"', """);
/* mark strings in single quotes with color */
QRegExp rx = QRegExp ("((?:^|\\s)[(]?)'([^']*)'(?=[:.-!);]?(?:\\s|$))");
rx.setMinimal (true);
text.replace (rx,
QString ("\\1%1
");
else
text.replace ('\n', "
");
return text;
}
/**
* This does exactly the same as QLocale::system().name() but corrects its
* wrong behavior on Linux systems (LC_NUMERIC for some strange reason takes
* precedence over any other locale setting in the QLocale::system()
* implementation). This implementation first looks at LC_ALL (as defined by
* SUS), then looks at LC_MESSAGES which is designed to define a language for
* program messages in case if it differs from the language for other locale
* categories. Then it looks for LANG and finally falls back to
* QLocale::system().name().
*
* The order of precedence is well defined here:
* http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
*
* @note This method will return "C" when the requested locale is invalid or
* when the "C" locale is set explicitly.
*/
/* static */
QString VBoxGlobal::systemLanguageId()
{
#if defined (Q_WS_MAC)
/* QLocale return the right id only if the user select the format of the
* language also. So we use our own implementation */
return ::darwinSystemLanguage();
#elif defined (Q_OS_UNIX)
const char *s = RTEnvGet ("LC_ALL");
if (s == 0)
s = RTEnvGet ("LC_MESSAGES");
if (s == 0)
s = RTEnvGet ("LANG");
if (s != 0)
return QLocale (s).name();
#endif
return QLocale::system().name();
}
/**
* Reimplementation of QFileDialog::getExistingDirectory() that removes some
* oddities and limitations.
*
* On Win32, this function makes sure a native dialog is launched in
* another thread to avoid dialog visualization errors occuring due to
* multi-threaded COM apartment initialization on the main UI thread while
* the appropriate native dialog function expects a single-threaded one.
*
* On all other platforms, this function is equivalent to
* QFileDialog::getExistingDirectory().
*/
QString VBoxGlobal::getExistingDirectory (const QString &aDir,
QWidget *aParent,
const QString &aCaption,
bool aDirOnly,
bool aResolveSymlinks)
{
#if defined Q_WS_WIN
/**
* QEvent class reimplementation to carry Win32 API native dialog's
* result folder information
*/
class GetExistDirectoryEvent : public OpenNativeDialogEvent
{
public:
enum { TypeId = QEvent::User + 300 };
GetExistDirectoryEvent (const QString &aResult)
: OpenNativeDialogEvent (aResult, (QEvent::Type) TypeId) {}
};
/**
* QThread class reimplementation to open Win32 API native folder's dialog
*/
class Thread : public QThread
{
public:
Thread (QWidget *aParent, QObject *aTarget,
const QString &aDir, const QString &aCaption)
: mParent (aParent), mTarget (aTarget), mDir (aDir), mCaption (aCaption) {}
virtual void run()
{
QString result;
QWidget *topParent = mParent ? mParent->window() : vboxGlobal().mainWindow();
QString title = mCaption.isNull() ? tr ("Select a directory") : mCaption;
TCHAR path[MAX_PATH];
path [0] = 0;
TCHAR initPath [MAX_PATH];
initPath [0] = 0;
BROWSEINFO bi;
bi.hwndOwner = topParent ? topParent->winId() : 0;
bi.pidlRoot = NULL;
bi.lpszTitle = (TCHAR*)(title.isNull() ? 0 : title.utf16());
bi.pszDisplayName = initPath;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
bi.lpfn = winGetExistDirCallbackProc;
bi.lParam = Q_ULONG (&mDir);
/* Qt is uncapable to properly handle modal state if the modal
* window is not a QWidget. For example, if we have the W1->W2->N
* ownership where Wx are QWidgets (W2 is modal), and N is a
* native modal window, cliking on the title bar of W1 will still
* activate W2 and redirect keyboard/mouse to it. The dirty hack
* to prevent it is to disable the entire widget... */
if (mParent)
mParent->setEnabled (false);
LPITEMIDLIST itemIdList = SHBrowseForFolder (&bi);
if (itemIdList)
{
SHGetPathFromIDList (itemIdList, path);
IMalloc *pMalloc;
if (SHGetMalloc (&pMalloc) != NOERROR)
result = QString::null;
else
{
pMalloc->Free (itemIdList);
pMalloc->Release();
result = QString::fromUtf16 ((ushort*)path);
}
}
else
result = QString::null;
QApplication::postEvent (mTarget, new GetExistDirectoryEvent (result));
/* Enable the parent widget again. */
if (mParent)
mParent->setEnabled (true);
}
private:
QWidget *mParent;
QObject *mTarget;
QString mDir;
QString mCaption;
};
/* Local event loop to run while waiting for the result from another
* thread */
QEventLoop loop;
QString dir = QDir::toNativeSeparators (aDir);
LoopObject loopObject ((QEvent::Type) GetExistDirectoryEvent::TypeId, loop);
Thread openDirThread (aParent, &loopObject, dir, aCaption);
openDirThread.start();
loop.exec();
openDirThread.wait();
return loopObject.result();
#elif defined (Q_WS_X11) && (QT_VERSION < 0x040400)
/* Here is workaround for Qt4.3 bug with QFileDialog which crushes when
* gets initial path as hidden directory if no hidden files are shown.
* See http://trolltech.com/developer/task-tracker/index_html?method=entry&id=193483
* for details */
QFileDialog dlg (aParent);
dlg.setWindowTitle (aCaption);
dlg.setDirectory (aDir);
dlg.setResolveSymlinks (aResolveSymlinks);
dlg.setFileMode (aDirOnly ? QFileDialog::DirectoryOnly : QFileDialog::Directory);
QAction *hidden = dlg.findChild