VBoxSelectorWnd.cpp revision 8e0c2ca3abd721979958f95b9af73b60665478c8
/** @file
*
* VBox frontends: Qt GUI ("VirtualBox"):
* VBoxSelectorWnd class implementation
*/
/*
* Copyright (C) 2006-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;
* 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 "VBoxProblemReporter.h"
#include "VBoxSelectorWnd.h"
#include "VBoxVMListView.h"
#include "VBoxConsoleWnd.h"
#include "VBoxToolBar.h"
#include "VBoxSnapshotsWgt.h"
#include "VBoxNewVMWzd.h"
#include "VBoxMediaManagerDlg.h"
#include "VBoxSettingsDialogSpecific.h"
#include "VBoxVMLogViewer.h"
#include "VBoxGlobal.h"
#include "VBoxUtils.h"
#ifdef Q_WS_X11
#endif
/* Qt includes */
#include <QTextBrowser>
#include <QMenuBar>
#include <QMenu>
#include <QMenuItem>
#include <QStackedWidget>
#include <QDesktopWidget>
#include <QToolButton>
// VBoxVMDetailsView class
////////////////////////////////////////////////////////////////////////////////
/**
* Two-page widget stack to represent VM details: one page for normal details
* and another one for inaccessibility errors.
*/
{
public:
{
setCurrentIndex (0);
}
{
setCurrentIndex (1);
}
void setEmpty()
{
setCurrentIndex (0);
}
protected:
void retranslateUi();
private slots:
{
}
private:
void createErrPage();
};
, mRefreshButton (NULL)
{
/* create normal details page */
/* make "transparent" */
mDetailsText->setOpenLinks (false);
}
void VBoxVMDetailsView::createErrPage()
{
/* create inaccessible details page */
if (mErrBox)
return;
mErrLabel->setWordWrap (true);
if (mRefreshAction)
{
QSizePolicy::Minimum));
}
QSizePolicy::Expanding));
}
void VBoxVMDetailsView::retranslateUi()
{
if (mErrLabel)
"The selected virtual machine is <i>inaccessible</i>. Please "
"inspect the error message shown below and press the "
"<b>Refresh</b> button if you want to repeat the accessibility "
"check:"));
if (mRefreshAction && mRefreshButton)
{
}
}
// VBoxVMDescriptionPage class
////////////////////////////////////////////////////////////////////////////////
/**
* Comments page widget to represent VM comments.
*/
{
public:
~VBoxVMDescriptionPage() {}
void updateState();
protected:
void retranslateUi();
private slots:
void goToSettings();
private:
};
{
/* main layout */
/* mBrowser */
mBrowser = new QTextBrowser (this);
/* hidden by default */
mLabel->setWordWrap (true);
/* always disabled */
mLabel->setEnabled (false);
/* button layout */
QSizePolicy::Minimum));
/* button */
mBtnEdit = new QToolButton (this);
":/edit_description_disabled_16px.png"));
QSizePolicy::Minimum));
/* apply language settings */
updateState();
}
/**
* The machine list @a aItem is used to access cached machine data w/o making
* unnecessary RPC calls.
*/
{
{
mBrowser->setVisible (true);
}
else
{
mLabel->setVisible (true);
}
/* check initial machine and session states */
updateState();
}
void VBoxVMDescriptionPage::retranslateUi()
{
mBtnEdit->adjustSize();
}
/**
* Called by the parent from machineStateChanged() and sessionStateChanged()
* signal handlers. We cannot connect to these signals ourselves because we
* use the VBoxVMListBoxItem which needs to be properly updated by the parent
* first.
*/
void VBoxVMDescriptionPage::updateState()
{
/// @todo disabling the edit button for a saved VM will not be necessary
/// when we implement the selective VM Settings dialog, where only fields
/// that can be changed in the saved state, can be changed.
if (mItem)
{
}
else
mBtnEdit->setEnabled (false);
}
void VBoxVMDescriptionPage::goToSettings()
{
}
// VBoxSelectorWnd class
////////////////////////////////////////////////////////////////////////////////
/** \class VBoxSelectorWnd
*
* The VBoxSelectorWnd class is a VM selector window, one of two main VBox
* GUI windows.
*
* This window appears when the user starts the VirtualBox executable.
* It allows to view the list of configured VMs, their settings
* and the current state, create, reconfigure, delete and start VMs.
*/
/**
* Constructs the VM selector window.
*
* @param aSelf pointer to a variable where to store |this| right after
* this object's constructor is called (necessary to avoid
* recursion in VBoxGlobal::selectorWnd())
*/
, mDoneInaccessibleWarningOnce (false)
{
if (aSelf)
*aSelf = this;
statusBar();
qApp->installEventFilter (this);
#endif /* defined (Q_WS_MAC) && (QT_VERSION < 0x040402) */
/* The application icon. On Win32, it's built-in to the executable. On Mac
* OS X the icon referenced in info.plist is used. */
#endif
/* actions */
mFileMediaMgrAction = new QAction (this);
mFileSettingsAction = new QAction(this);
mFileExitAction = new QAction (this);
mVmNewAction = new QAction (this);
":/vm_new_32px.png", ":/new_16px.png"));
mVmConfigAction = new QAction (this);
":/vm_settings_32px.png", ":/settings_16px.png",
":/vm_settings_disabled_32px.png", ":/settings_dis_16px.png"));
mVmDeleteAction = new QAction (this);
":/vm_delete_32px.png", ":/delete_16px.png",
":/vm_delete_disabled_32px.png", ":/delete_dis_16px.png"));
mVmStartAction = new QAction (this);
":/vm_start_32px.png", ":/start_16px.png",
":/vm_start_disabled_32px.png", ":/start_dis_16px.png"));
mVmDiscardAction = new QAction (this);
":/vm_discard_32px.png", ":/discard_16px.png",
":/vm_discard_disabled_32px.png", ":/discard_dis_16px.png"));
mVmPauseAction = new QAction (this);
mVmPauseAction->setCheckable (true);
":/vm_pause_32px.png", ":/pause_16px.png",
":/vm_pause_disabled_32px.png", ":/pause_disabled_16px.png"));
mVmRefreshAction = new QAction (this);
":/refresh_32px.png", ":/refresh_16px.png",
":/refresh_disabled_32px.png", ":/refresh_disabled_16px.png"));
mVmShowLogsAction = new QAction (this);
":/vm_show_logs_32px.png", ":/show_logs_16px.png",
":/vm_show_logs_disabled_32px.png", ":/show_logs_disabled_16px.png"));
mHelpActions.setup (this);
/* Central widget @ horizontal layout */
setCentralWidget (new QWidget (this));
/* Left vertical box */
/* Right vertical box */
/* VM list toolbar */
/* Enable unified toolbars on Mac OS X. Available on Qt >= 4.3 */
vmTools->setMacToolbar();
leftVLayout->setSpacing (0);
rightVLayout->setSpacing (0);
#else /* MAC_LEOPARD_STYLE */
#endif /* MAC_LEOPARD_STYLE */
/* VM list view */
mVMListView = new VBoxVMListView();
/* VM tab widget containing details and snapshots tabs */
mVmTabWidget = new QTabWidget();
/* VM details view */
/* VM snapshots list */
":/take_snapshot_dis_16px.png"),
/* VM comments page */
mVmDescriptionPage = new VBoxVMDescriptionPage (this);
":/description_disabled_16px.png"),
/* add actions to the toolbar */
#if 0 /* delete action is really rare */
#endif
/* add actions to menubar */
mVMMenu->addSeparator();
mVMMenu->addSeparator();
mVMCtxtMenu = new QMenu (this);
#ifdef VBOX_GUI_WITH_SYSTRAY
#endif
/* Restore the position of the window */
{
int x = 0, y = 0, w = 0, h = 0;
if (ok)
if (ok)
if (ok)
if (ok)
if (ok /* previous parameters were read correctly */
{
mNormalGeo.moveTo (x, y);
if (max) /* maximize if needed */
}
else
{
}
}
/* Update the list */
/* Reset to the first item */
mVMListView->selectItemByRow (0);
/* restore the position of vm selector */
{
}
/* refresh the details et all (necessary for the case when the stored
* selection is still the first list item) */
/* signals and slots connections */
this, SLOT (vmListViewCurrentChanged()));
/* listen to media enumeration signals */
this, SLOT (mediumEnumStarted()));
/* connect VirtualBox callback events */
#ifdef VBOX_GUI_WITH_SYSTRAY
#endif
/* bring the VM list to the focus */
mVMListView->setFocus();
}
{
/* Save the position of the window */
{
if (isMaximized())
}
/* Save vm selector position */
{
}
#ifdef VBOX_GUI_WITH_SYSTRAY
/* Delete systray menu object */
delete mTrayIcon;
#endif
/* Delete the items from our model */
}
//
// Public slots
/////////////////////////////////////////////////////////////////////////////
void VBoxSelectorWnd::fileMediaMgr()
{
VBoxMediaManagerDlg::showModeless (this);
}
void VBoxSelectorWnd::fileSettings()
{
delete dlg;
}
void VBoxSelectorWnd::fileExit()
{
/* We have to check if there are any open windows beside this mainwindow
* (e.g. VDM) and if so close them. Note that the default behavior is
* different to Qt3 where a *mainWidget* exists & if this going to close
* all other windows are closed automatically. We do the same below. */
{
widget != this)
}
/* We close this widget last. */
close();
}
void VBoxSelectorWnd::vmNew()
{
VBoxNewVMWzd wzd (this);
{
/* wait until the list is updated by OnMachineRegistered() */
{
qApp->processEvents();
}
}
}
/**
* Opens the VM settings dialog.
*/
{
{
/* Assume it's a href from the Details HTML */
return;
}
// open a direct session to modify VM settings
return;
{
m.SaveSettings();
if (m.isOk())
{
}
else
/* To check use the result in future
* vboxProblem().cannotApplyMachineSettings (m, res); */
}
delete dlg;
mVMListView->setFocus();
}
{
{
bool ok = false;
if (item->accessible())
{
/* Open a direct session to modify VM settings */
return;
/* Detach all attached Hard Disks */
{
}
/* Commit changes */
else
ok = true;
}
else
ok = true;
if (ok)
{
{
/* delete machine settings */
/* remove the item shortly: cmachine it refers to is no longer valid! */
delete item;
}
}
}
}
{
/* Are we called from the mVMListView's activated() signal? */
{
/* We always get here when mVMListView emits the activated() signal,
* so we must explicitly check if the action is enabled or not. */
if (!mVmStartAction->isEnabled())
return;
}
#if defined (VBOX_GUI_SEPARATE_VM_PROCESS)
("Must NOT be a VM console process"));
/* just switch to the VM window if it already exists */
if (item->canSwitchTo())
{
return;
}
("Machine must be PoweredOff/Saved/Aborted"));
{
return;
}
#if defined (Q_OS_WIN32)
/* allow the started VM process to make itself the foreground window */
#endif
#if defined (Q_WS_X11)
/* make sure the VM process will start on the same display as the Selector */
{
if (display)
}
#endif
{
return;
}
/* show the "VM spawning" progress dialog */
if (progress.GetResultCode() != 0)
#else // !VBOX_GUI_SEPARATE_VM_PROCESS
return;
hide();
#endif
}
{
return;
/* open a session to modify VM settings */
{
return;
}
{
return;
}
}
{
return;
return;
if (aPause)
else
if (!ok)
{
if (aPause)
else
}
}
{
true /* aDetails */,
true /* aSnapshot */,
true /* aDescription */);
}
{
}
void VBoxSelectorWnd::refreshVMList()
{
#ifdef VBOX_GUI_WITH_SYSTRAY
if (vboxGlobal().isTrayMenu())
#endif
}
bool aSnapshots,
bool aDescription)
{
if (item)
{
}
}
{
/* Send a context menu request */
}
#ifdef VBOX_GUI_WITH_SYSTRAY
{
switch (aReason)
{
case QSystemTrayIcon::Context:
break;
case QSystemTrayIcon::Trigger:
break;
case QSystemTrayIcon::DoubleClick:
break;
case QSystemTrayIcon::MiddleClick:
break;
default:
break;
}
}
void VBoxSelectorWnd::showWindow()
{
showNormal();
raise();
}
#endif // VBOX_GUI_WITH_SYSTRAY
// Protected members
/////////////////////////////////////////////////////////////////////////////
{
switch (e->type())
{
/* By handling every Resize and Move we keep track of the normal
* (non-minimized and non-maximized) window geometry. Shame on Qt
* that it doesn't provide this geometry in its public APIs. */
{
Qt::WindowFullScreen)) == 0)
break;
}
{
Qt::WindowFullScreen)) == 0)
break;
}
default:
break;
}
return QMainWindow::event (e);
}
{
#ifdef VBOX_GUI_WITH_SYSTRAY
/* Needed for breaking out of the while() loop in main(). */
if (vboxGlobal().isTrayMenu())
vboxGlobal().setTrayMenu (false);
#endif
}
{
if (!isActiveWindow())
{
{
/* Bug in Qt below 4.4.2. The key events are send to the current
* window even if a menu is shown & has the focus. See
if (::darwinIsMenuOpen())
return true;
}
default:
break;
}
}
#endif /* defined (Q_WS_MAC) && (QT_VERSION < 0x040402) */
/**
* Sets the strings of the subwidgets using the current
* language.
*/
void VBoxSelectorWnd::retranslateUi()
{
#ifdef VBOX_OSE
#else
#endif
/* note: Snapshots and Details tabs are changed dynamically by
* vmListViewCurrentChanged() */
/* ensure the details and screenshot view are updated */
#ifdef Q_WS_MAC
/*
* Macification: Getting the right menu as application preference menu item.
*
* QMenuBar::isCommand() in qmenubar_mac.cpp doesn't recognize "Setting"(s)
* unless it's in the first position. So, we use the Mac term here to make
* sure we get picked instead of the VM settings.
*
* Now, since both QMenuBar and we translate these strings, it's going to
* be really interesting to see how this plays on non-english systems...
*/
#else
/*
* ...and on other platforms we use "Preferences" as well. The #ifdef is
* left because of the possible localization problems on Mac we first need
* to figure out.
*/
#endif
/* Note: mVmStartAction text is set up in vmListViewCurrentChanged() */
tr ("Discard the saved state of the selected virtual machine"));
tr ("Suspend the execution of the virtual machine"));
tr ("Refresh the accessibility state of the selected virtual machine"));
tr ("Show the log files of the selected virtual machine"));
#ifdef VBOX_GUI_WITH_SYSTRAY
if (vboxGlobal().isTrayMenu())
{
}
#endif
}
// Private members
/////////////////////////////////////////////////////////////////////////////
//
// Private slots
/////////////////////////////////////////////////////////////////////////////
bool aRefreshSnapshots,
bool aRefreshDescription)
{
{
if (aRefreshDetails)
{
modifyEnabled /* withLinks */));
}
if (aRefreshSnapshots)
{
/* update the snapshots tab name */
if (count)
/* refresh the snapshots widget */
mVmSnapshotsWgt->setMachine (m);
/* ensure the tab is enabled */
}
if (aRefreshDescription)
{
/* update the description tab name */
/* refresh the description widget */
/* ensure the tab is enabled */
}
/* change the Start button text accordingly */
if (state >= KMachineState_Running)
{
tr ("Switch to the window of the selected virtual machine"));
}
else
{
tr ("Start the selected virtual machine"));
}
if (state == KMachineState_Paused)
{
tr ("Resume the execution of the virtual machine"));
mVmPauseAction->blockSignals (true);
mVmPauseAction->setChecked (true);
mVmPauseAction->blockSignals (false);
}
else
{
tr ("Suspend the execution of the virtual machine"));
mVmPauseAction->blockSignals (true);
mVmPauseAction->setChecked (false);
mVmPauseAction->blockSignals (false);
}
/* disable Refresh for accessible machines */
mVmRefreshAction->setEnabled (false);
/* enable the show log item for the selected vm */
mVmShowLogsAction->setEnabled (true);
}
else
{
/* Note that the machine becomes inaccessible (or if the last VM gets
* deleted), we have to update all fields, ignoring input
* arguments. */
if (item)
{
/* the VM is inaccessible */
mVmRefreshAction->setEnabled (true);
}
else
{
/* default HTML support in Qt is terrible so just try to get
* something really simple */
(tr ("<h3>"
"Welcome to VirtualBox!</h3>"
"<p>The left part of this window is intended to display "
"a list of all virtual machines on your computer. "
"The list is empty now because you haven't created any virtual "
"machines yet."
"<img src=:/welcome.png align=right/></p>"
"<p>In order to create a new virtual machine, press the "
"<b>New</b> button in the main tool bar located "
"at the top of the window.</p>"
"<p>You can press the <b>F1</b> key to get instant help, "
"or visit "
"<a href=http://www.virtualbox.org>www.virtualbox.org</a> "
"for the latest information and news.</p>"));
mVmRefreshAction->setEnabled (false);
}
/* empty and disable other tabs */
/* disable modify actions */
mVmConfigAction->setEnabled (false);
mVmDiscardAction->setEnabled (false);
mVmPauseAction->setEnabled (false);
/* change the Start button text accordingly */
tr ("Start the selected virtual machine"));
mVmStartAction->setEnabled (false);
/* disable the show log item for the selected vm */
mVmShowLogsAction->setEnabled (false);
}
}
void VBoxSelectorWnd::mediumEnumStarted()
{
/* refresh the current details to pick up hard disk sizes */
vmListViewCurrentChanged (true /* aRefreshDetails */);
}
{
/* refresh the current details to pick up hard disk sizes */
vmListViewCurrentChanged (true /* aRefreshDetails */);
/* we warn about inaccessible media only once (after media emumeration
* started from main() at startup), to avoid annoying the user */
#ifdef VBOX_GUI_WITH_SYSTRAY
|| vboxGlobal().isTrayMenu()
#endif
)
return;
mDoneInaccessibleWarningOnce = true;
do
{
/* ignore the signal if a modal widget is currently active (we won't be
* able to properly show the modeless VDI manager window in this case) */
if (QApplication::activeModalWidget())
break;
/* ignore the signal if a VBoxMediaManagerDlg window is active */
if (qApp->activeWindow() &&
break;
/* look for at least one inaccessible media */
break;
{
/* Show the VDM dialog but don't refresh once more after a
* just-finished refresh */
}
}
while (0);
}
{
#ifdef VBOX_GUI_WITH_SYSTRAY
if (vboxGlobal().isTrayMenu())
{
/* Check if there are some machines alive - else quit, since
* we're not needed as a systray menu anymore. */
if (vboxGlobal().mainWindowCount() == 0)
{
fileExit();
return;
}
}
#endif
refreshVMItem (e.id,
false /* aDetails */,
false /* aSnapshots */,
false /* aDescription */);
/* simulate a state change signal */
}
{
refreshVMItem (e.id,
true /* aDetails */,
false /* aSnapshots */,
true /* aDescription */);
}
{
if (e.registered)
{
if (!m.isNull())
{
/* Make sure the description, ... pages are properly updated.
* Actualy we haven't call the next method, but unfortunately Qt
* seems buggy if the new item is on the same position as the
* previous one. So go on the safe side and call this by our self. */
}
/* m.isNull() is ok (theoretically, the machine could have been
* already deregistered by some other client at this point) */
}
else
{
if (item)
{
delete item;
}
/* item = 0 is ok (if we originated this event then the item
* has been already removed) */
}
}
{
refreshVMItem (e.id,
true /* aDetails */,
false /* aSnapshots */,
false /* aDescription */);
/* simulate a state change signal */
}
{
false /* aDetails */,
true /* aSnapshot */,
false /* aDescription */);
}
#ifdef VBOX_GUI_WITH_SYSTRAY
{
fileExit();
}
{
}
{
}
{
/* Not used yet. */
}
{
mShowSelectorAction = new QAction (this);
":/VirtualBox_16px.png"));
mHideSystrayMenuAction = new QAction (this);
":/exit_16px.png"));
/* reuse parent action data */
mVmConfigAction = new QAction (this);
mVmDeleteAction = new QAction (this);
mVmStartAction = new QAction (this);
mVmDiscardAction = new QAction (this);
mVmPauseAction = new QAction (this);
mVmPauseAction->setCheckable (true);
mVmRefreshAction = new QAction (this);
mVmShowLogsAction = new QAction (this);
}
VBoxTrayIcon::~VBoxTrayIcon ()
{
/* Erase dialog handle in config file. */
if (mActive)
{
hide();
}
}
void VBoxTrayIcon::retranslateUi ()
{
if (!mActive)
return;
"Show the selector window assigned to this menu"));
"Remove this icon from the system tray"));
/* reuse parent action data */
}
void VBoxTrayIcon::showSubMenu ()
{
if (!mActive)
return;
{
}
{
/* look at vmListViewCurrentChanged() */
/* Settings */
/* Delete */
/* Discard */
/* Change the Start button text accordingly */
if (s >= KMachineState_Running)
{
}
else
{
}
s == KMachineState_Paused);
if (s == KMachineState_Paused)
{
mVmPauseAction->blockSignals (true);
mVmPauseAction->setChecked (true);
mVmPauseAction->blockSignals (false);
}
else
{
mVmPauseAction->blockSignals (true);
mVmPauseAction->setChecked (false);
mVmPauseAction->blockSignals (false);
}
mVmShowLogsAction->setEnabled (true);
/* Disconnect old slot which maybe was connected from another selected sub menu. */
/* Connect new sub menu with slots. */
}
else /* Item is not accessible. */
{
mVmConfigAction->setEnabled (false);
mVmDiscardAction->setEnabled (false);
mVmPauseAction->setEnabled (false);
/* Set the Start button text accordingly. */
mVmStartAction->setEnabled (false);
/* Disable the show log item for the selected vm. */
mVmShowLogsAction->setEnabled (false);
}
/* Build sub menu entries (add rest of sub menu entries later here). */
}
void VBoxTrayIcon::hideSubMenu ()
{
if (!mActive)
return;
{
}
/* Nothing to do here yet. */
}
void VBoxTrayIcon::refresh ()
{
if (!mActive)
return;
mTrayIconMenu->clear();
int iCurItemCount = 0;
{
{
iCurItemCount = 0;
}
}
/* We're done constructing the menu, show it */
setVisible (true);
}
{
{
}
return pItem;
}
{
if (!vboxGlobal().isTrayMenu())
return;
if (mActive)
{
refresh();
}
if (!mActive)
}
void VBoxTrayIcon::vmSettings()
{
}
void VBoxTrayIcon::vmDelete()
{
}
void VBoxTrayIcon::vmStart()
{
}
void VBoxTrayIcon::vmDiscard()
{
}
{
}
void VBoxTrayIcon::vmRefresh()
{
}
void VBoxTrayIcon::vmShowLogs()
{
}
#endif // VBOX_GUI_WITH_SYSTRAY
#include "VBoxSelectorWnd.moc"