VBoxMediaManagerDlg.cpp revision 71f125e3721909de05e3f550864ccf86952ab59b
/* $Id$ */
/** @file
*
* VBox frontends: Qt4 GUI ("VirtualBox"):
* VBoxMediaManagerDlg class implementation
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* 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.
*/
# include "precomp.h"
#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
/* Global includes */
#include <QCloseEvent>
#include <QDir>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QFileInfo>
#include <QHeaderView>
#include <QMenuBar>
#include <QProgressBar>
#include <QPushButton>
#include <QTimer>
#include <QUrl>
/* Local includes */
#include "VBoxGlobal.h"
#include "VBoxMediaManagerDlg.h"
#include "UINewHDWzd.h"
#include "VBoxProblemReporter.h"
#include "UIToolBar.h"
#include "QIFileDialog.h"
#include "QILabel.h"
#include "UIIconPool.h"
#include "UIVirtualBoxEventHandler.h"
#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
#ifdef Q_WS_MAC
# include "UIWindowMenuManager.h"
#endif /* Q_WS_MAC */
class AddVDMUrlsEvent: public QEvent
{
public:
{}
private:
};
class MediaItem : public QTreeWidgetItem
{
public:
{ refresh(); }
{ refresh(); }
void refreshAll()
{
refresh();
}
{
refresh();
}
QString toolTip() const { return mMedium.toolTip (!mManager->showDiffs(), mManager->inAttachMode()); }
bool operator< (const QTreeWidgetItem &aOther) const
{
}
private:
void refresh()
{
/* Fill in columns */
/* Set the text */
/* All columns get the same tooltip */
for (int i = 0; i < treeWidget()->columnCount(); ++ i)
setToolTip (i, tt);
}
const VBoxMediaManagerDlg *mManager;
};
class MediaItemIterator : public QTreeWidgetItemIterator
{
public:
: QTreeWidgetItemIterator (aTree) {}
MediaItem* operator*()
{
}
MediaItemIterator& operator++()
{
return static_cast <MediaItemIterator&> (QTreeWidgetItemIterator::operator++());
}
};
class VBoxProgressBar: public QWidget
{
public:
{
mProgressBar = new QProgressBar (this);
mProgressBar->setTextVisible (false);
}
private:
};
VBoxMediaManagerDlg::VBoxMediaManagerDlg (QWidget *aParent /* = 0 */, Qt::WindowFlags aFlags /* = Qt::Dialog */)
, mShowDiffs (true)
, mSetupMode (false)
{
/* Apply UI decorations */
/* Apply window icons */
":/diskimage_32px.png", ":/diskimage_16px.png"));
mFloppyImagesInaccessible = false;
/* Setup tab-widget icons */
/* Setup the tree-widgets */
mTwHD->setSortingEnabled (true);
mTwHD->installEventFilter (this);
this, SLOT (makeRequestForAdjustTable()));
this, SLOT (makeRequestForAdjustTable()));
mTwCD->setSortingEnabled (true);
mTwCD->installEventFilter (this);
this, SLOT (makeRequestForAdjustTable()));
this, SLOT (makeRequestForAdjustTable()));
mTwFD->setSortingEnabled (true);
mTwFD->installEventFilter (this);
this, SLOT (makeRequestForAdjustTable()));
this, SLOT (makeRequestForAdjustTable()));
/* Context menu composing */
mActionsContextMenu = new QMenu (this);
mNewAction = new QAction (this);
mAddAction = new QAction (this);
mRemoveAction = new QAction (this);
mReleaseAction = new QAction (this);
mRefreshAction = new QAction (this);
":/hd_new_22px.png", ":/hd_new_16px.png",
":/hd_new_disabled_22px.png", ":/hd_new_disabled_16px.png"));
":/hd_add_22px.png", ":/hd_add_16px.png",
":/hd_add_disabled_22px.png", ":/hd_add_disabled_16px.png"));
":/hd_remove_22px.png", ":/hd_remove_16px.png",
":/hd_remove_disabled_22px.png", ":/hd_remove_disabled_16px.png"));
":/hd_release_22px.png", ":/hd_release_16px.png",
":/hd_release_disabled_22px.png", ":/hd_release_disabled_16px.png"));
":/refresh_22px.png", ":/refresh_16px.png",
":/refresh_disabled_22px.png", ":/refresh_disabled_16px.png"));
/* Toolbar composing */
Assert (mainLayout);
/* Enable unified toolbars on Mac OS X. Available on Qt >= 4.3 */
#else /* MAC_LEOPARD_STYLE */
/* Add the toolbar */
#endif /* MAC_LEOPARD_STYLE */
// mToolBar->addAction (mNewAction);
// mToolBar->addAction (mAddAction);
// mToolBar->addSeparator();
// mToolBar->addSeparator();
/* Menu bar */
// mActionsMenu->addAction (mNewAction);
// mActionsMenu->addAction (mAddAction);
// mActionsMenu->addSeparator();
// mActionsMenu->addSeparator();
/* Setup information pane */
infoPane->setFullSizeSelection (true);
/* Enumeration progressbar creation */
mProgressBar = new VBoxProgressBar (this);
/* Add to the dialog button box */
/* Default is invisible */
mProgressBar->setVisible (false);
/* Set the default button */
/* Connects for the button box */
}
{
#ifdef Q_WS_MAC
if (!mDoSelect)
{
}
#endif /* Q_WS_MAC */
delete mToolBar;
}
/**
* Sets up the dialog.
*
* @param aType Media type to display (either one type or all).
* @param aDoSelect Whether to enable the select action (OK button).
* @param aRefresh Whether to do a media refresh.
* @param aSessionMachine Session machine object if this dialog is opened for
* a machine from its settings dialog.
* @param aSelectId Which medium to make selected? (ignored when @a
* aType is VBoxDefs::MediumType_All)
* @param aShowDiffs @c true to show differencing hard disks initially
* (ignored if @a aSessionMachine is null assuming
* @c true).
* @param aUsedMediaIds List containing IDs of mediums used in other
* attachments to restrict selection.
*/
bool aRefresh /* = true */,
bool aShowDiffs /* = true */,
{
mSetupMode = true;
switch (aType)
{
case VBoxDefs::MediumType_All: break;
default:
}
/* Listen to "media enumeration started" signals */
this, SLOT (mediumEnumStarted()));
/* Listen to "media enumeration" signals */
/* Listen to "media enumeration finished" signals */
/* Listen to "media add" signals */
/* Listen to "media update" signals */
/* Listen to "media remove" signals */
else
{
/* Insert already enumerated media */
int index = 0;
{
mediumAdded (*it);
}
/* Emulate the finished signal to reuse the code */
if (!vboxGlobal().isMediaEnumerationStarted())
}
/* For a newly opened dialog, select the first item */
/* Applying language settings */
#ifdef Q_WS_MAC
if (!mDoSelect)
{
}
#endif /* Q_WS_MAC */
mSetupMode = false;
}
/* static */
void VBoxMediaManagerDlg::showModeless (QWidget *aCenterWidget /* = 0 */, bool aRefresh /* = true */)
{
if (!mModelessDialog)
{
/* Setup 'closing' connection if main window is VBoxSelectorWnd: */
/* listen to events that may change the media status and refresh
* the contents of the modeless dialog */
/// @todo refreshAll() may be slow, so it may be better to analyze
// event details and update only what is changed */
}
mModelessDialog->show();
}
{
if (item)
return uuid;
}
{
if (item)
return loc;
}
void VBoxMediaManagerDlg::refreshAll()
{
/* Start enumerating media */
}
void VBoxMediaManagerDlg::retranslateUi()
{
/* Translate uic generated strings */
mReleaseAction->setStatusTip (tr ("Release the selected medium by detaching it from the machines"));
#ifdef Q_WS_MAC
/* Make sure that the widgets aren't jumping around while the progress bar get visible. */
int h = mProgressBar->height();
#endif
#ifdef QT_MAC_USE_COCOA
/* There is a bug in Qt Cocoa which result in showing a "more arrow" when
the necessary size of the toolbar is increased. Also for some languages
the with doesn't match if the text increase. So manually adjust the size
after changing the text. */
mToolBar->updateLayout();
#endif /* QT_MAC_USE_COCOA */
if (mDoSelect)
refreshAll();
}
{
mModelessDialog = 0;
}
{
/* Check for interesting objects */
{
{
{
/* Sometimes urls has an empty Url entry. Filter them out. */
if (checkDndUrls (urls))
{
}
}
return true;
break;
}
{
{
/* Sometimes urls has an empty Url entry. Filter them out. */
}
return true;
break;
}
case VBoxDefs::AddVDMUrlsEventType:
{
if (aObject == currentTreeWidget())
{
return true;
}
break;
}
default:
break;
}
}
{
/* Ignore non-interesting aMedium */
(aMedium.isHostDrive()))
return;
{
{
/* In !mShowDiffs mode, we ignore all diffs except ones that are
* directly attached to the related VM in the current state */
return;
/* Since the base hard disk of this diff has been already appended,
* we want to replace it with this diff to avoid duplicates in
* !mShowDiffs mode. */
/* Check if swapped diff disk is required one */
{
}
return;
}
}
{
case VBoxDefs::MediumType_HardDisk:
{
/* Damn Qt4 didn't notifies the table's QHeaderView on adding
* new tree-widget items, so initialize the header adjustment
* by calling resizeSections() slot... */
{
}
break;
}
case VBoxDefs::MediumType_DVD:
{
/* Damn Qt4 didn't notifies the table's QHeaderView on adding
* new tree-widget items, so initialize the header adjustment
* by calling resizeSections() slot... */
{
}
break;
}
case VBoxDefs::MediumType_Floppy:
{
/* Damn Qt4 didn't notifies the table's QHeaderView on adding
* new tree-widget items, so initialize the header adjustment
* by calling resizeSections() slot... */
{
}
break;
}
default:
AssertFailed();
}
/* If the media enumeration process is not started we have to select the
* newly added item as the current one for the case of new image was added
* or created. But we have to avoid this in case of we are adding already
* enumerated medias in setup() function when the media enumeration is not
* running. So the mSetupMode variable reflects the setup status for it. */
}
{
/* Ignore non-interesting aMedium */
(aMedium.isHostDrive()))
return;
{
case VBoxDefs::MediumType_HardDisk:
{
break;
}
case VBoxDefs::MediumType_DVD:
{
break;
}
case VBoxDefs::MediumType_Floppy:
{
break;
}
default:
AssertFailed();
}
/* There may be unwanted items due to !mShowDiffs */
if (!item)
return;
/* Note: current items on invisible tabs are not updated because
* it is always done in processCurrentChanged() when the user switches
* to an invisible tab */
}
{
/* Ignore non-interesting aMedium */
return;
/* There may be unwanted items due to !mShowDiffs */
if (!item)
return;
/* We need to silently delete item without selecting
* the new one because of complex selection mechanism
* which could provoke a segfault choosing the new
* one item during last item deletion routine. So blocking
* the tree-view for the time of item removing. */
tree->blockSignals (true);
delete item;
tree->blockSignals (false);
/* Note: current items on invisible tabs are not updated because
* it is always done in processCurrentChanged() when the user switches
* to an invisible tab */
}
void VBoxMediaManagerDlg::mediumEnumStarted()
{
/* Reset inaccessible flags */
mFloppyImagesInaccessible = false;
/* Load default tab icons */
/* Load current media list */
mediumAdded (*it);
/* Select the first item to be the current one if the previous saved item
* was not selected yet. */
if (!mTwHD->currentItem())
if (!mTwCD->currentItem())
if (!mTwFD->currentItem())
}
{
}
{
mProgressBar->setVisible (false);
mRefreshAction->setEnabled (true);
unsetCursor();
}
void VBoxMediaManagerDlg::doNewMedium()
{
UINewHDWzd dlg (this);
{
/* Select the newly created hard disk */
}
}
void VBoxMediaManagerDlg::doAddMedium()
{
switch (type)
{
case VBoxDefs::MediumType_DVD:
{
break;
}
case VBoxDefs::MediumType_HardDisk:
{
break;
}
case VBoxDefs::MediumType_Floppy:
{
break;
}
default:
AssertMsgFailed (("Selected tree should be equal to one item in VBoxDefs::MediumType.\n"));
break;
}
for (int i = 0; i < filterList.count(); ++i)
{
/* Create one backend filter string */
/* Save the suffix's for the "All" entry */
}
{
}
}
void VBoxMediaManagerDlg::doRemoveMedium()
{
return;
switch (type)
{
case VBoxDefs::MediumType_HardDisk:
{
bool deleteStorage = false;
/* We don't want to try to delete inaccessible storage as it will
* most likely fail. Note that
* VBoxProblemReporter::confirmRemoveMedium() is aware of that and
* will give a corresponding hint. Therefore, once the code is
* changed below, the hint should be re-checked for validity. */
{
int rc = vboxProblem().
return;
}
if (deleteStorage)
{
bool success = false;
{
vboxProblem().showModalProgressDialog (progress, windowTitle(), ":/progress_delete_90px.png", this, true);
success = true;
}
if (success)
else
/* We don't want to close the hard disk because it was
* implicitly closed and removed from the list of known media
* on storage deletion */
return;
}
break;
}
case VBoxDefs::MediumType_DVD:
{
break;
}
case VBoxDefs::MediumType_Floppy:
{
break;
}
default:
}
else
}
void VBoxMediaManagerDlg::doReleaseMedium()
{
/* Get the fresh attached VM list */
item->refreshAll();
{
continue;
usage += ", ";
}
if (machineIds.size() == 0)
{
/* It may happen that the new machine list is empty (medium was already
* released by a third party); update the details and silently return.*/
return;
}
return;
{
break;
}
/* Inform others about medium changes (use a copy since data owning is not
* clean there (to be fixed one day using shared_ptr)) */
}
{
/* Is this medium is attached to the VM we are setting up */
if (mSessionMachineId == aMachineId)
{
}
/* or to some other */
else
{
return false;
}
bool success = true;
{
case VBoxDefs::MediumType_HardDisk:
{
{
{
{
success = false;
break;
}
}
}
break;
}
case VBoxDefs::MediumType_DVD:
{
{
VBoxMedium medium = vboxGlobal().findMedium (attachment.GetMedium().isNull() ? QString() : attachment.GetMedium().GetId());
{
machine.MountMedium (attachment.GetController(), attachment.GetPort(), attachment.GetDevice(), CMedium(), false /* force */);
{
success = false;
break;
}
}
}
break;
}
case VBoxDefs::MediumType_Floppy:
{
{
VBoxMedium medium = vboxGlobal().findMedium (attachment.GetMedium().isNull() ? QString() : attachment.GetMedium().GetId());
{
machine.MountMedium (attachment.GetController(), attachment.GetPort(), attachment.GetDevice(), CMedium(), false /* force */);
{
success = false;
break;
}
}
}
break;
}
default:
AssertFailedBreakStmt (success = false);
}
if (success)
{
{
success = false;
}
}
/* If a new session was opened, we must close it */
return success;
}
{
QTreeWidget* tree = 0;
switch (aType)
{
case VBoxDefs::MediumType_HardDisk:
break;
case VBoxDefs::MediumType_DVD:
break;
case VBoxDefs::MediumType_Floppy:
break;
default:
break;
}
return tree;
}
{
switch (mTabWidget->currentIndex())
{
case HDTab:
break;
case CDTab:
break;
case FDTab:
break;
default:
break;
}
return type;
}
{
return treeWidget (currentTreeWidgetType());
}
{
/* Return the current selected item. The user can select one item at the
* time only, so return the first element always. */
}
{
/* Convert the QTreeWidgetItem to a MediaItem if it is valid. */
return item;
}
{
{
aItem->setSelected (true);
}
else processCurrentChanged();
}
{
}
{
{
/* We have to make sure that one item is selected always. If the new
* item is 0, set the old item again. */
}
if (item)
{
/* Set the file for the proxy icon */
/* Ensures current item visible every time we are switching page */
}
/* New and Add are now enabled even when enumerating since it should be safe */
bool addEnabled = true;
if (mDoSelect)
{
}
if (item)
{
{
}
{
}
{
}
}
else
}
{
accept();
}
{
if (curItem)
{
/* Make sure the item is selected and current */
}
}
{
switch (state)
{
case KMachineState_PoweredOff:
case KMachineState_Aborted:
case KMachineState_Saved:
case KMachineState_Teleported:
case KMachineState_Starting:
case KMachineState_Restoring:
{
refreshAll();
break;
}
default:
break;
}
}
{
/* We have to perform table adjustment only after all the [auto]resize
* events processed for every column of this table. */
}
{
/* Get all the tree widgets */
widgetList << mTwHD;
widgetList << mTwCD;
widgetList << mTwFD;
/* Calculate deduction for every header */
{
int deduction = 0;
}
/* Adjust the table's first column */
for (int i = 0; i < widgetList.size(); ++ i)
{
}
}
{
switch (aType)
{
case VBoxDefs::MediumType_HardDisk:
break;
case VBoxDefs::MediumType_DVD:
break;
case VBoxDefs::MediumType_Floppy:
break;
default:
}
else
}
MediaItem* VBoxMediaManagerDlg::createHardDiskItem (QTreeWidget *aTree, const VBoxMedium &aMedium) const
{
{
}
else
{
if (root)
else
}
return item;
}
{
int tab = -1;
bool *inaccessible = 0;
{
case VBoxDefs::MediumType_HardDisk:
icon = &mHardDiskIcon;
break;
case VBoxDefs::MediumType_DVD:
icon = &mDVDImageIcon;
break;
case VBoxDefs::MediumType_Floppy:
icon = &mFloppyImageIcon;
break;
default:
AssertFailed();
}
switch (aAction)
{
case ItemAction_Added:
{
/* Does it change the overall state? */
break; /* no */
*inaccessible = true;
break;
}
case ItemAction_Updated:
case ItemAction_Removed:
{
bool checkRest = false;
if (aAction == ItemAction_Updated)
{
/* Does it change the overall state? */
break; /* no */
/* Is the given item in charge? */
*inaccessible = true; /* yes */
else
checkRest = true; /* no */
}
else
checkRest = true;
if (checkRest)
{
*inaccessible = false;
/* Find the first inaccessible item to be in charge */
{
{
*inaccessible = true;
break;
}
}
}
if (*inaccessible)
else
break;
}
}
}
{
return 0;
while (*iterator)
{
return *iterator;
++ iterator;
}
return 0;
}
/**
* Checks if a specific action is valid for the given medium at its current
* state.
*
* @param aItem Media item.
* @param aAction Action to check.
*
* @return @c true if the action is possible and false otherwise.
*/
{
AssertReturn (aItem, false);
switch (aAction)
{
case Action_Select:
{
/* Restrict selecting mediums
* already used by other attachments */
}
case Action_Edit:
{
/* Edit means changing the description and alike; any media that is
* not being read to or written from can be altered in these
* terms */
{
case KMediumState_NotCreated:
case KMediumState_LockedRead:
case KMediumState_LockedWrite:
return false;
default:
break;
}
return true;
}
case Action_Remove:
{
/* Removable if not attached to anything */
}
case Action_Release:
{
/* Releasable if attached but not in snapshots */
}
}
AssertFailedReturn (false);
}
{
bool err = false;
/* Check that all file extensions fit to the current
* selected tree view and the files are valid. */
{
/* Check dropped media type */
/// @todo On OS/2 and windows (and mac?) extension checks should be case
/// insensitive, as OPPOSED to linux and the rest where case matters.
switch (currentTreeWidgetType())
{
case VBoxDefs::MediumType_HardDisk:
{
bool match = false;
for (int i = 0; i < filterList.count(); ++i)
{
{
match = true;
break;
}
}
break;
}
case VBoxDefs::MediumType_DVD:
break;
case VBoxDefs::MediumType_Floppy:
break;
default:
AssertMsgFailed (("Selected tree should be equal to one item in VBoxDefs::MediumType.\n"));
break;
}
}
return !err;
}
{
{
}
}
void VBoxMediaManagerDlg::clearInfoPanes()
{
}
{
/* Info panel clearing */
/* Prepare progressbar */
if (mProgressBar)
{
mProgressBar->setValue (0);
mProgressBar->setVisible (true);
}
mRefreshAction->setEnabled (false);
/* Store the current list selections */
if (mHDSelectedId.isNull())
if (mCDSelectedId.isNull())
if (mFDSelectedId.isNull())
/* Finally, clear all the lists...
* Qt4 has interesting bug here. It sends the currentChanged (cur, prev)
* signal during list clearing with 'cur' set to null and 'prev' pointing
* to already excluded element if this element is not topLevelItem
* (has parent). Cause the Hard Disk list has such elements there is
* seg-fault when trying to make 'prev' element the current due to 'cur'
* is null and at least one element have to be selected (by policy).
* So just blocking any signals outgoing from the list during clearing. */
mTwHD->blockSignals (true);
mTwHD->blockSignals (false);
}
/**
* Modifies the given text string for QILabel so that it optionally uses the
* <compact> syntax controlling visual text "compression" with elipsis.
*
* @param aText Original text string.
* @param aCompact @c true if "compression" should be activated.
* @param aElipsis Where to put the elipsis (see <compact> in QILabel).
*
* @return Modified text string.
*/
{
return info;
}
#include "VBoxMediaManagerDlg.moc"