VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/VBoxMediaManagerDlg.cpp@ 35740

Last change on this file since 35740 was 34983, checked in by vboxsync, 13 years ago

FE/Qt4: more progress images

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 62.7 KB
Line 
1/* $Id: VBoxMediaManagerDlg.cpp 34983 2010-12-13 10:14:08Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt4 GUI ("VirtualBox"):
5 * VBoxMediaManagerDlg class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#ifdef VBOX_WITH_PRECOMPILED_HEADERS
21# include "precomp.h"
22#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
23/* Global includes */
24#include <QCloseEvent>
25#include <QDir>
26#include <QDragEnterEvent>
27#include <QDropEvent>
28#include <QFileInfo>
29#include <QHeaderView>
30#include <QMenuBar>
31#include <QProgressBar>
32#include <QPushButton>
33#include <QTimer>
34#include <QUrl>
35
36/* Local includes */
37#include "VBoxGlobal.h"
38#include "VBoxMediaManagerDlg.h"
39#include "UINewHDWzd.h"
40#include "VBoxProblemReporter.h"
41#include "UIToolBar.h"
42#include "QIFileDialog.h"
43#include "QILabel.h"
44#include "UIIconPool.h"
45#include "UIVirtualBoxEventHandler.h"
46#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
47
48#ifdef Q_WS_MAC
49# include "UIWindowMenuManager.h"
50#endif /* Q_WS_MAC */
51
52class AddVDMUrlsEvent: public QEvent
53{
54public:
55
56 AddVDMUrlsEvent (const QList <QUrl> &aUrls)
57 : QEvent (static_cast <QEvent::Type> (VBoxDefs::AddVDMUrlsEventType))
58 , mUrls (aUrls)
59 {}
60
61 const QList <QUrl> &urls() const { return mUrls; }
62
63private:
64
65 const QList <QUrl> mUrls;
66};
67
68class MediaItem : public QTreeWidgetItem
69{
70public:
71
72 enum { MediaItemType = QTreeWidgetItem::UserType + 1 };
73
74 MediaItem (MediaItem *aParent, const VBoxMedium &aMedium, const VBoxMediaManagerDlg *aManager)
75 : QTreeWidgetItem (aParent, MediaItemType)
76 , mMedium (aMedium)
77 , mManager (aManager)
78 { refresh(); }
79
80 MediaItem (QTreeWidget *aParent, const VBoxMedium &aMedium, const VBoxMediaManagerDlg *aManager)
81 : QTreeWidgetItem (aParent, MediaItemType)
82 , mMedium (aMedium)
83 , mManager (aManager)
84 { refresh(); }
85
86 void refreshAll()
87 {
88 mMedium.refresh();
89 refresh();
90 }
91
92 void setMedium (const VBoxMedium &aMedium)
93 {
94 mMedium = aMedium;
95 refresh();
96 }
97
98 const VBoxMedium& medium() const { return mMedium; }
99
100 VBoxDefs::MediumType type() const { return mMedium.type(); }
101
102 KMediumState state() const { return mMedium.state (!mManager->showDiffs()); }
103
104 QString id() const { return mMedium.id(); }
105
106 QString location() const { return mMedium.location (!mManager->showDiffs()); }
107
108 QString hardDiskFormat() const { return mMedium.hardDiskFormat (!mManager->showDiffs()); }
109 QString hardDiskType() const { return mMedium.hardDiskType (!mManager->showDiffs()); }
110
111 QString usage() const { return mMedium.usage (!mManager->showDiffs()); }
112
113 QString toolTip() const { return mMedium.toolTip (!mManager->showDiffs(), mManager->inAttachMode()); }
114
115 bool isUsed() const { return mMedium.isUsed(); }
116 bool isUsedInSnapshots() const { return mMedium.isUsedInSnapshots(); }
117
118 bool operator< (const QTreeWidgetItem &aOther) const
119 {
120 int column = treeWidget()->sortColumn();
121 ULONG64 thisValue = vboxGlobal().parseSize ( text (column));
122 ULONG64 thatValue = vboxGlobal().parseSize (aOther.text (column));
123 return thisValue && thatValue ? thisValue < thatValue : QTreeWidgetItem::operator< (aOther);
124 }
125
126private:
127
128 void refresh()
129 {
130 /* Fill in columns */
131 setIcon (0, mMedium.icon (!mManager->showDiffs(), mManager->inAttachMode()));
132 /* Set the text */
133 setText (0, mMedium.name (!mManager->showDiffs()));
134 setText (1, mMedium.logicalSize (!mManager->showDiffs()));
135 setText (2, mMedium.size (!mManager->showDiffs()));
136 /* All columns get the same tooltip */
137 QString tt = mMedium.toolTip (!mManager->showDiffs());
138 for (int i = 0; i < treeWidget()->columnCount(); ++ i)
139 setToolTip (i, tt);
140 }
141
142 VBoxMedium mMedium;
143 const VBoxMediaManagerDlg *mManager;
144};
145
146class MediaItemIterator : public QTreeWidgetItemIterator
147{
148public:
149
150 MediaItemIterator (QTreeWidget* aTree)
151 : QTreeWidgetItemIterator (aTree) {}
152
153 MediaItem* operator*()
154 {
155 QTreeWidgetItem *item = QTreeWidgetItemIterator::operator*();
156 return item && item->type() == MediaItem::MediaItemType ?
157 static_cast <MediaItem*> (item) : 0;
158 }
159
160 MediaItemIterator& operator++()
161 {
162 return static_cast <MediaItemIterator&> (QTreeWidgetItemIterator::operator++());
163 }
164};
165
166class VBoxProgressBar: public QWidget
167{
168 Q_OBJECT;
169
170public:
171
172 VBoxProgressBar (QWidget *aParent)
173 : QWidget (aParent)
174 {
175 mText = new QLabel (this);
176 mProgressBar = new QProgressBar (this);
177 mProgressBar->setTextVisible (false);
178
179 QHBoxLayout *layout = new QHBoxLayout (this);
180 VBoxGlobal::setLayoutMargin (layout, 0);
181 layout->addWidget (mText);
182 layout->addWidget (mProgressBar);
183 }
184
185 void setText (const QString &aText) { mText->setText (aText); }
186 void setValue (int aValue) { mProgressBar->setValue (aValue); }
187 void setMaximum (int aValue) { mProgressBar->setMaximum (aValue); }
188
189 int value() const { return mProgressBar->value(); }
190
191private:
192
193 QLabel *mText;
194 QProgressBar *mProgressBar;
195};
196
197
198VBoxMediaManagerDlg* VBoxMediaManagerDlg::mModelessDialog = 0;
199
200VBoxMediaManagerDlg::VBoxMediaManagerDlg (QWidget *aParent /* = 0 */, Qt::WindowFlags aFlags /* = Qt::Dialog */)
201 : QIWithRetranslateUI2 <QIMainDialog> (aParent, aFlags)
202 , mType (VBoxDefs::MediumType_Invalid)
203 , mShowDiffs (true)
204 , mSetupMode (false)
205{
206 /* Apply UI decorations */
207 Ui::VBoxMediaManagerDlg::setupUi (this);
208
209 /* Apply window icons */
210 setWindowIcon(UIIconPool::iconSetFull(QSize (32, 32), QSize (16, 16),
211 ":/diskimage_32px.png", ":/diskimage_16px.png"));
212
213 mVBox = vboxGlobal().virtualBox();
214 Assert (!mVBox.isNull());
215
216 mHardDisksInaccessible =
217 mDVDImagesInaccessible =
218 mFloppyImagesInaccessible = false;
219
220 mHardDiskIcon = UIIconPool::iconSet(":/hd_16px.png", ":/hd_disabled_16px.png");
221 mDVDImageIcon = UIIconPool::iconSet(":/cd_16px.png", ":/cd_disabled_16px.png");
222 mFloppyImageIcon = UIIconPool::iconSet(":/fd_16px.png", ":/fd_disabled_16px.png");
223
224 /* Setup tab-widget icons */
225 mTabWidget->setTabIcon (HDTab, mHardDiskIcon);
226 mTabWidget->setTabIcon (CDTab, mDVDImageIcon);
227 mTabWidget->setTabIcon (FDTab, mFloppyImageIcon);
228
229 connect (mTabWidget, SIGNAL (currentChanged (int)), this, SLOT (processCurrentChanged (int)));
230
231 /* Setup the tree-widgets */
232 mTwHD->sortItems (0, Qt::AscendingOrder);
233 mTwHD->header()->setResizeMode (0, QHeaderView::Fixed);
234 mTwHD->header()->setResizeMode (1, QHeaderView::ResizeToContents);
235 mTwHD->header()->setResizeMode (2, QHeaderView::ResizeToContents);
236 mTwHD->header()->setStretchLastSection (false);
237 mTwHD->setSortingEnabled (true);
238 mTwHD->installEventFilter (this);
239 connect (mTwHD, SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
240 this, SLOT (processCurrentChanged (QTreeWidgetItem *, QTreeWidgetItem *)));
241 connect (mTwHD, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)),
242 this, SLOT (processDoubleClick (QTreeWidgetItem *, int)));
243 connect (mTwHD, SIGNAL (customContextMenuRequested (const QPoint &)),
244 this, SLOT (showContextMenu (const QPoint &)));
245 connect (mTwHD, SIGNAL (resized (const QSize &, const QSize &)),
246 this, SLOT (makeRequestForAdjustTable()));
247 connect (mTwHD->header(), SIGNAL (sectionResized (int, int, int)),
248 this, SLOT (makeRequestForAdjustTable()));
249
250 mTwCD->sortItems (0, Qt::AscendingOrder);
251 mTwCD->header()->setResizeMode (0, QHeaderView::Fixed);
252 mTwCD->header()->setResizeMode (1, QHeaderView::ResizeToContents);
253 mTwCD->header()->setStretchLastSection (false);
254 mTwCD->setSortingEnabled (true);
255 mTwCD->installEventFilter (this);
256 connect (mTwCD, SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
257 this, SLOT (processCurrentChanged (QTreeWidgetItem *, QTreeWidgetItem *)));
258 connect (mTwCD, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)),
259 this, SLOT (processDoubleClick (QTreeWidgetItem *, int)));
260 connect (mTwCD, SIGNAL (customContextMenuRequested (const QPoint &)),
261 this, SLOT (showContextMenu (const QPoint &)));
262 connect (mTwCD, SIGNAL (resized (const QSize&, const QSize&)),
263 this, SLOT (makeRequestForAdjustTable()));
264 connect (mTwCD->header(), SIGNAL (sectionResized (int, int, int)),
265 this, SLOT (makeRequestForAdjustTable()));
266
267 mTwFD->sortItems (0, Qt::AscendingOrder);
268 mTwFD->header()->setResizeMode (0, QHeaderView::Fixed);
269 mTwFD->header()->setResizeMode (1, QHeaderView::ResizeToContents);
270 mTwFD->header()->setStretchLastSection (false);
271 mTwFD->setSortingEnabled (true);
272 mTwFD->installEventFilter (this);
273 connect (mTwFD, SIGNAL (currentItemChanged (QTreeWidgetItem *, QTreeWidgetItem *)),
274 this, SLOT (processCurrentChanged (QTreeWidgetItem *, QTreeWidgetItem *)));
275 connect (mTwFD, SIGNAL (itemDoubleClicked (QTreeWidgetItem *, int)),
276 this, SLOT (processDoubleClick (QTreeWidgetItem *, int)));
277 connect (mTwFD, SIGNAL (customContextMenuRequested (const QPoint &)),
278 this, SLOT (showContextMenu (const QPoint &)));
279 connect (mTwFD, SIGNAL (resized (const QSize&, const QSize&)),
280 this, SLOT (makeRequestForAdjustTable()));
281 connect (mTwFD->header(), SIGNAL (sectionResized (int, int, int)),
282 this, SLOT (makeRequestForAdjustTable()));
283
284 /* Context menu composing */
285 mActionsContextMenu = new QMenu (this);
286
287 mNewAction = new QAction (this);
288 mAddAction = new QAction (this);
289 mRemoveAction = new QAction (this);
290 mReleaseAction = new QAction (this);
291 mRefreshAction = new QAction (this);
292
293 connect (mNewAction, SIGNAL (triggered()), this, SLOT (doNewMedium()));
294 connect (mAddAction, SIGNAL (triggered()), this, SLOT (doAddMedium()));
295 connect (mRemoveAction, SIGNAL (triggered()), this, SLOT (doRemoveMedium()));
296 connect (mReleaseAction, SIGNAL (triggered()), this, SLOT (doReleaseMedium()));
297 connect (mRefreshAction, SIGNAL (triggered()), this, SLOT (refreshAll()));
298
299 mNewAction->setIcon(UIIconPool::iconSetFull (
300 QSize (22, 22), QSize (16, 16),
301 ":/hd_new_22px.png", ":/hd_new_16px.png",
302 ":/hd_new_disabled_22px.png", ":/hd_new_disabled_16px.png"));
303 mAddAction->setIcon(UIIconPool::iconSetFull (
304 QSize (22, 22), QSize (16, 16),
305 ":/hd_add_22px.png", ":/hd_add_16px.png",
306 ":/hd_add_disabled_22px.png", ":/hd_add_disabled_16px.png"));
307 mRemoveAction->setIcon(UIIconPool::iconSetFull (
308 QSize (22, 22), QSize (16, 16),
309 ":/hd_remove_22px.png", ":/hd_remove_16px.png",
310 ":/hd_remove_disabled_22px.png", ":/hd_remove_disabled_16px.png"));
311 mReleaseAction->setIcon(UIIconPool::iconSetFull (
312 QSize (22, 22), QSize (16, 16),
313 ":/hd_release_22px.png", ":/hd_release_16px.png",
314 ":/hd_release_disabled_22px.png", ":/hd_release_disabled_16px.png"));
315 mRefreshAction->setIcon(UIIconPool::iconSetFull (
316 QSize (22, 22), QSize (16, 16),
317 ":/refresh_22px.png", ":/refresh_16px.png",
318 ":/refresh_disabled_22px.png", ":/refresh_disabled_16px.png"));
319
320 mActionsContextMenu->addAction (mRemoveAction);
321 mActionsContextMenu->addAction (mReleaseAction);
322
323 /* Toolbar composing */
324 mToolBar = new UIToolBar (this);
325 mToolBar->setIconSize (QSize (22, 22));
326 mToolBar->setToolButtonStyle (Qt::ToolButtonTextUnderIcon);
327 mToolBar->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred);
328
329 QVBoxLayout *mainLayout = qobject_cast <QVBoxLayout*> (centralWidget()->layout());
330 Assert (mainLayout);
331#if MAC_LEOPARD_STYLE
332 /* Enable unified toolbars on Mac OS X. Available on Qt >= 4.3 */
333 addToolBar (mToolBar);
334 mToolBar->setMacToolbar();
335 /* No spacing/margin on the mac */
336 VBoxGlobal::setLayoutMargin (mainLayout, 0);
337 mainLayout->insertSpacing (0, 10);
338#else /* MAC_LEOPARD_STYLE */
339 /* Add the toolbar */
340 mainLayout->insertWidget (0, mToolBar);
341 /* Set spacing/margin like in the selector window */
342 mainLayout->setSpacing (5);
343 VBoxGlobal::setLayoutMargin (mainLayout, 5);
344#endif /* MAC_LEOPARD_STYLE */
345
346// mToolBar->addAction (mNewAction);
347// mToolBar->addAction (mAddAction);
348// mToolBar->addSeparator();
349 mToolBar->addAction (mRemoveAction);
350 mToolBar->addAction (mReleaseAction);
351// mToolBar->addSeparator();
352 mToolBar->addAction (mRefreshAction);
353
354 /* Menu bar */
355 mActionsMenu = menuBar()->addMenu (QString::null);
356// mActionsMenu->addAction (mNewAction);
357// mActionsMenu->addAction (mAddAction);
358// mActionsMenu->addSeparator();
359 mActionsMenu->addAction (mRemoveAction);
360 mActionsMenu->addAction (mReleaseAction);
361// mActionsMenu->addSeparator();
362 mActionsMenu->addAction (mRefreshAction);
363
364 /* Setup information pane */
365 QList <QILabel*> paneList = findChildren <QILabel*>();
366 foreach (QILabel *infoPane, paneList)
367 infoPane->setFullSizeSelection (true);
368
369 /* Enumeration progressbar creation */
370 mProgressBar = new VBoxProgressBar (this);
371 /* Add to the dialog button box */
372 mButtonBox->addExtraWidget (mProgressBar);
373 /* Default is invisible */
374 mProgressBar->setVisible (false);
375
376 /* Set the default button */
377 mButtonBox->button (QDialogButtonBox::Ok)->setDefault (true);
378
379 /* Connects for the button box */
380 connect (mButtonBox, SIGNAL (accepted()), this, SLOT (accept()));
381 connect (mButtonBox, SIGNAL (rejected()), this, SLOT (reject()));
382 connect (mButtonBox, SIGNAL (helpRequested()), &vboxProblem(), SLOT (showHelpHelpDialog()));
383}
384
385VBoxMediaManagerDlg::~VBoxMediaManagerDlg()
386{
387#ifdef Q_WS_MAC
388 if (!mDoSelect)
389 {
390 UIWindowMenuManager::instance()->removeWindow(this);
391 UIWindowMenuManager::instance()->destroyMenu(this);
392 }
393#endif /* Q_WS_MAC */
394 delete mToolBar;
395}
396
397/**
398 * Sets up the dialog.
399 *
400 * @param aType Media type to display (either one type or all).
401 * @param aDoSelect Whether to enable the select action (OK button).
402 * @param aRefresh Whether to do a media refresh.
403 * @param aSessionMachine Session machine object if this dialog is opened for
404 * a machine from its settings dialog.
405 * @param aSelectId Which medium to make selected? (ignored when @a
406 * aType is VBoxDefs::MediumType_All)
407 * @param aShowDiffs @c true to show differencing hard disks initially
408 * (ignored if @a aSessionMachine is null assuming
409 * @c true).
410 * @param aUsedMediaIds List containing IDs of mediums used in other
411 * attachments to restrict selection.
412 */
413void VBoxMediaManagerDlg::setup (VBoxDefs::MediumType aType, bool aDoSelect,
414 bool aRefresh /* = true */,
415 const CMachine &aSessionMachine /* = CMachine() */,
416 const QString &aSelectId /* = QString() */,
417 bool aShowDiffs /* = true */,
418 const QStringList &aUsedMediaIds /* = QStringList() */)
419{
420 mSetupMode = true;
421
422 mType = aType;
423
424 mDoSelect = aDoSelect;
425
426 mSessionMachine = aSessionMachine;
427 mSessionMachineId = mSessionMachine.isNull() ? QString::null : mSessionMachine.GetId();
428 mShowDiffs = mSessionMachine.isNull() ? true : aShowDiffs;
429
430 switch (aType)
431 {
432 case VBoxDefs::MediumType_HardDisk: mHDSelectedId = aSelectId; break;
433 case VBoxDefs::MediumType_DVD: mCDSelectedId = aSelectId; break;
434 case VBoxDefs::MediumType_Floppy: mFDSelectedId = aSelectId; break;
435 case VBoxDefs::MediumType_All: break;
436 default:
437 AssertFailedReturnVoid();
438 }
439
440 mTabWidget->setTabEnabled (HDTab,
441 aType == VBoxDefs::MediumType_All ||
442 aType == VBoxDefs::MediumType_HardDisk);
443 mTabWidget->setTabEnabled (CDTab,
444 aType == VBoxDefs::MediumType_All ||
445 aType == VBoxDefs::MediumType_DVD);
446 mTabWidget->setTabEnabled (FDTab,
447 aType == VBoxDefs::MediumType_All ||
448 aType == VBoxDefs::MediumType_Floppy);
449
450 mDoSelect = aDoSelect;
451 mUsedMediaIds = aUsedMediaIds;
452
453 mButtonBox->button (QDialogButtonBox::Cancel)->setVisible (mDoSelect);
454
455 /* Listen to "media enumeration started" signals */
456 connect (&vboxGlobal(), SIGNAL (mediumEnumStarted()),
457 this, SLOT (mediumEnumStarted()));
458 /* Listen to "media enumeration" signals */
459 connect (&vboxGlobal(), SIGNAL (mediumEnumerated (const VBoxMedium &)),
460 this, SLOT (mediumEnumerated (const VBoxMedium &)));
461 /* Listen to "media enumeration finished" signals */
462 connect (&vboxGlobal(), SIGNAL (mediumEnumFinished (const VBoxMediaList &)),
463 this, SLOT (mediumEnumFinished (const VBoxMediaList &)));
464
465 /* Listen to "media add" signals */
466 connect (&vboxGlobal(), SIGNAL (mediumAdded (const VBoxMedium &)),
467 this, SLOT (mediumAdded (const VBoxMedium &)));
468 /* Listen to "media update" signals */
469 connect (&vboxGlobal(), SIGNAL (mediumUpdated (const VBoxMedium &)),
470 this, SLOT (mediumUpdated (const VBoxMedium &)));
471 /* Listen to "media remove" signals */
472 connect (&vboxGlobal(), SIGNAL (mediumRemoved (VBoxDefs::MediumType, const QString &)),
473 this, SLOT (mediumRemoved (VBoxDefs::MediumType, const QString &)));
474
475 if (aRefresh && !vboxGlobal().isMediaEnumerationStarted())
476 vboxGlobal().startEnumeratingMedia();
477 else
478 {
479 /* Insert already enumerated media */
480 const VBoxMediaList &list = vboxGlobal().currentMediaList();
481 prepareToRefresh (list.size());
482 VBoxMediaList::const_iterator it;
483 int index = 0;
484 for (it = list.begin(); it != list.end(); ++ it)
485 {
486 mediumAdded (*it);
487 if ((*it).state() != KMediumState_NotCreated)
488 mProgressBar->setValue (++ index);
489 }
490
491 /* Emulate the finished signal to reuse the code */
492 if (!vboxGlobal().isMediaEnumerationStarted())
493 mediumEnumFinished (list);
494 }
495
496 /* For a newly opened dialog, select the first item */
497 if (mTwHD->selectedItems().isEmpty())
498 if (QTreeWidgetItem *item = mTwHD->topLevelItem (0))
499 setCurrentItem (mTwHD, item);
500 if (mTwCD->selectedItems().isEmpty())
501 if (QTreeWidgetItem *item = mTwCD->topLevelItem (0))
502 setCurrentItem (mTwCD, item);
503 if (mTwFD->selectedItems().isEmpty())
504 if (QTreeWidgetItem *item = mTwFD->topLevelItem (0))
505 setCurrentItem (mTwFD, item);
506
507 /* Applying language settings */
508 retranslateUi();
509
510#ifdef Q_WS_MAC
511 if (!mDoSelect)
512 {
513 menuBar()->addMenu(UIWindowMenuManager::instance()->createMenu(this));
514 UIWindowMenuManager::instance()->addWindow(this);
515 }
516#endif /* Q_WS_MAC */
517
518 mSetupMode = false;
519}
520
521/* static */
522void VBoxMediaManagerDlg::showModeless (QWidget *aCenterWidget /* = 0 */, bool aRefresh /* = true */)
523{
524 if (!mModelessDialog)
525 {
526 mModelessDialog = new VBoxMediaManagerDlg (0, Qt::Window);
527 mModelessDialog->centerAccording (aCenterWidget);
528 mModelessDialog->setAttribute (Qt::WA_DeleteOnClose);
529 mModelessDialog->setup (VBoxDefs::MediumType_All, false /* aDoSelect */, aRefresh);
530
531 /* Setup 'closing' connection if main window is VBoxSelectorWnd: */
532 if (vboxGlobal().mainWindow() && vboxGlobal().mainWindow()->inherits("VBoxSelectorWnd"))
533 connect(vboxGlobal().mainWindow(), SIGNAL(closing()), mModelessDialog, SLOT(close()));
534
535 /* listen to events that may change the media status and refresh
536 * the contents of the modeless dialog */
537 /// @todo refreshAll() may be slow, so it may be better to analyze
538 // event details and update only what is changed */
539 connect (gVBoxEvents, SIGNAL(sigMachineDataChange(QString)),
540 mModelessDialog, SLOT(refreshAll()));
541 connect (gVBoxEvents, SIGNAL(sigMachineRegistered(QString, bool)),
542 mModelessDialog, SLOT(refreshAll()));
543 connect (gVBoxEvents, SIGNAL(sigSnapshotChange(QString, QString)),
544 mModelessDialog, SLOT(refreshAll()));
545 }
546
547 mModelessDialog->show();
548 mModelessDialog->setWindowState (mModelessDialog->windowState() & ~Qt::WindowMinimized);
549 mModelessDialog->activateWindow();
550}
551
552QString VBoxMediaManagerDlg::selectedId() const
553{
554 QTreeWidget *tree = currentTreeWidget();
555 QString uuid;
556
557 MediaItem *item = toMediaItem (selectedItem (tree));
558 if (item)
559 uuid = item->id();
560
561 return uuid;
562}
563
564QString VBoxMediaManagerDlg::selectedLocation() const
565{
566 QTreeWidget *tree = currentTreeWidget();
567 QString loc;
568
569 MediaItem *item = toMediaItem (selectedItem (tree));
570 if (item)
571 loc = item->location().trimmed();
572
573 return loc;
574}
575
576void VBoxMediaManagerDlg::refreshAll()
577{
578 /* Start enumerating media */
579 vboxGlobal().startEnumeratingMedia();
580}
581
582void VBoxMediaManagerDlg::retranslateUi()
583{
584 /* Translate uic generated strings */
585 Ui::VBoxMediaManagerDlg::retranslateUi (this);
586
587 mActionsMenu->setTitle (tr ("&Actions"));
588
589 mNewAction->setText (tr ("&New..."));
590 mAddAction->setText (tr ("&Add..."));
591 mRemoveAction->setText (tr ("R&emove"));
592 mReleaseAction->setText (tr ("Re&lease"));
593 mRefreshAction->setText (tr ("Re&fresh"));
594
595 mNewAction->setShortcut (QKeySequence (QKeySequence::New));
596 mAddAction->setShortcut (QKeySequence ("Ins"));
597 mRemoveAction->setShortcut (QKeySequence (QKeySequence::Delete));
598 mReleaseAction->setShortcut (QKeySequence ("Ctrl+L"));
599 mRefreshAction->setShortcut (QKeySequence (QKeySequence::Refresh));
600
601 mNewAction->setStatusTip (tr ("Create a new virtual hard disk"));
602 mAddAction->setStatusTip (tr ("Add an existing medium"));
603 mRemoveAction->setStatusTip (tr ("Remove the selected medium"));
604 mReleaseAction->setStatusTip (tr ("Release the selected medium by detaching it from the machines"));
605 mRefreshAction->setStatusTip (tr ("Refresh the media list"));
606
607 mNewAction->setToolTip (mNewAction->text().remove ('&') +
608 QString (" (%1)").arg (mNewAction->shortcut().toString()));
609 mAddAction->setToolTip (mAddAction->text().remove ('&') +
610 QString (" (%1)").arg (mAddAction->shortcut().toString()));
611 mRemoveAction->setToolTip (mRemoveAction->text().remove ('&') +
612 QString (" (%1)").arg (mRemoveAction->shortcut().toString()));
613 mReleaseAction->setToolTip (mReleaseAction->text().remove ('&') +
614 QString (" (%1)").arg (mReleaseAction->shortcut().toString()));
615 mRefreshAction->setToolTip (mRefreshAction->text().remove ('&') +
616 QString (" (%1)").arg (mRefreshAction->shortcut().toString()));
617
618 mLbHD1->setText (QString ("<nobr>%1:</nobr>").arg (tr ("Location")));
619 mLbHD2->setText (QString ("<nobr>%1:</nobr>").arg (tr ("Type (Format)")));
620 mLbHD3->setText (QString ("<nobr>%1:</nobr>").arg (tr ("Attached to", "VMM: Virtual Disk")));
621
622 mLbCD1->setText (QString ("<nobr>%1:</nobr>").arg (tr ("Location")));
623 mLbCD2->setText (QString ("<nobr>%1:</nobr>").arg (tr ("Attached to", "VMM: CD/DVD Image")));
624
625 mLbFD1->setText (QString ("<nobr>%1:</nobr>").arg (tr ("Location")));
626 mLbFD2->setText (QString ("<nobr>%1:</nobr>").arg (tr ("Attached to", "VMM: Floppy Image")));
627
628 mProgressBar->setText (tr ("Checking accessibility"));
629#ifdef Q_WS_MAC
630 /* Make sure that the widgets aren't jumping around while the progress bar get visible. */
631 mProgressBar->adjustSize();
632 int h = mProgressBar->height();
633 mButtonBox->setMinimumHeight (h + 12);
634#endif
635#ifdef QT_MAC_USE_COCOA
636 /* There is a bug in Qt Cocoa which result in showing a "more arrow" when
637 the necessary size of the toolbar is increased. Also for some languages
638 the with doesn't match if the text increase. So manually adjust the size
639 after changing the text. */
640 mToolBar->updateLayout();
641#endif /* QT_MAC_USE_COCOA */
642
643 if (mDoSelect)
644 mButtonBox->button (QDialogButtonBox::Ok)->setText (tr ("&Select"));
645
646 if (mTwHD->model()->rowCount() || mTwCD->model()->rowCount() || mTwFD->model()->rowCount())
647 refreshAll();
648}
649
650void VBoxMediaManagerDlg::closeEvent (QCloseEvent *aEvent)
651{
652 mModelessDialog = 0;
653 aEvent->accept();
654}
655
656bool VBoxMediaManagerDlg::eventFilter (QObject *aObject, QEvent *aEvent)
657{
658 /* Check for interesting objects */
659 if (!(aObject == mTwHD || aObject == mTwCD || aObject == mTwFD))
660 return QIMainDialog::eventFilter (aObject, aEvent);
661
662 switch (aEvent->type())
663 {
664 case QEvent::DragEnter:
665 {
666 QDragEnterEvent *deEvent = static_cast <QDragEnterEvent*> (aEvent);
667 if (deEvent->mimeData()->hasUrls())
668 {
669 QList <QUrl> urls = deEvent->mimeData()->urls();
670 /* Sometimes urls has an empty Url entry. Filter them out. */
671 urls.removeAll (QUrl());
672 if (checkDndUrls (urls))
673 {
674 deEvent->setDropAction (Qt::LinkAction);
675 deEvent->acceptProposedAction();
676 }
677 }
678 return true;
679 break;
680 }
681 case QEvent::Drop:
682 {
683 QDropEvent *dEvent = static_cast <QDropEvent*> (aEvent);
684 if (dEvent->mimeData()->hasUrls())
685 {
686 QList <QUrl> urls = dEvent->mimeData()->urls();
687 /* Sometimes urls has an empty Url entry. Filter them out. */
688 urls.removeAll (QUrl());
689 AddVDMUrlsEvent *event = new AddVDMUrlsEvent (urls);
690 QApplication::postEvent (currentTreeWidget(), event);
691 dEvent->acceptProposedAction();
692 }
693 return true;
694 break;
695 }
696 case VBoxDefs::AddVDMUrlsEventType:
697 {
698 if (aObject == currentTreeWidget())
699 {
700 AddVDMUrlsEvent *addEvent = static_cast <AddVDMUrlsEvent*> (aEvent);
701 addDndUrls (addEvent->urls());
702 return true;
703 }
704 break;
705 }
706 default:
707 break;
708 }
709 return QIMainDialog::eventFilter (aObject, aEvent);
710}
711
712void VBoxMediaManagerDlg::mediumAdded (const VBoxMedium &aMedium)
713{
714 /* Ignore non-interesting aMedium */
715 if ((aMedium.isNull()) ||
716 (mType != VBoxDefs::MediumType_All && mType != aMedium.type()) ||
717 (aMedium.isHostDrive()))
718 return;
719
720 if (!mShowDiffs && aMedium.type() == VBoxDefs::MediumType_HardDisk)
721 {
722 if (aMedium.parent() && !mSessionMachineId.isNull())
723 {
724 /* In !mShowDiffs mode, we ignore all diffs except ones that are
725 * directly attached to the related VM in the current state */
726 if (!aMedium.isAttachedInCurStateTo (mSessionMachineId))
727 return;
728
729 /* Since the base hard disk of this diff has been already appended,
730 * we want to replace it with this diff to avoid duplicates in
731 * !mShowDiffs mode. */
732 MediaItem *item = searchItem (mTwHD, aMedium.root().id());
733 AssertReturnVoid (item);
734
735 item->setMedium (aMedium);
736
737 /* Check if swapped diff disk is required one */
738 if (item->id() == mHDSelectedId)
739 {
740 setCurrentItem (mTwHD, item);
741 mHDSelectedId = QString::null;
742 }
743
744 updateTabIcons (item, ItemAction_Updated);
745 return;
746 }
747 }
748
749 MediaItem *item = 0;
750
751 switch (aMedium.type())
752 {
753 case VBoxDefs::MediumType_HardDisk:
754 {
755 item = createHardDiskItem (mTwHD, aMedium);
756 AssertReturnVoid (item);
757
758 /* Damn Qt4 didn't notifies the table's QHeaderView on adding
759 * new tree-widget items, so initialize the header adjustment
760 * by calling resizeSections() slot... */
761 QTimer::singleShot (0, mTwHD->header(), SLOT (resizeSections()));
762
763 if (item->id() == mHDSelectedId)
764 {
765 setCurrentItem (mTwHD, item);
766 mHDSelectedId = QString::null;
767 }
768 break;
769 }
770 case VBoxDefs::MediumType_DVD:
771 {
772 item = new MediaItem (mTwCD, aMedium, this);
773 AssertReturnVoid (item);
774
775 /* Damn Qt4 didn't notifies the table's QHeaderView on adding
776 * new tree-widget items, so initialize the header adjustment
777 * by calling resizeSections() slot... */
778 QTimer::singleShot (0, mTwCD->header(), SLOT (resizeSections()));
779
780 if (item->id() == mCDSelectedId)
781 {
782 setCurrentItem (mTwCD, item);
783 mCDSelectedId = QString::null;
784 }
785 break;
786 }
787 case VBoxDefs::MediumType_Floppy:
788 {
789 item = new MediaItem (mTwFD, aMedium, this);
790 AssertReturnVoid (item);
791
792 /* Damn Qt4 didn't notifies the table's QHeaderView on adding
793 * new tree-widget items, so initialize the header adjustment
794 * by calling resizeSections() slot... */
795 QTimer::singleShot (0, mTwFD->header(), SLOT (resizeSections()));
796
797 if (item->id() == mFDSelectedId)
798 {
799 setCurrentItem (mTwFD, item);
800 mFDSelectedId = QString::null;
801 }
802 break;
803 }
804 default:
805 AssertFailed();
806 }
807
808 AssertReturnVoid (item);
809
810 updateTabIcons (item, ItemAction_Added);
811
812 /* If the media enumeration process is not started we have to select the
813 * newly added item as the current one for the case of new image was added
814 * or created. But we have to avoid this in case of we are adding already
815 * enumerated medias in setup() function when the media enumeration is not
816 * running. So the mSetupMode variable reflects the setup status for it. */
817 if (!mSetupMode && !vboxGlobal().isMediaEnumerationStarted())
818 setCurrentItem (treeWidget (aMedium.type()), item);
819 if (item == currentTreeWidget()->currentItem())
820 processCurrentChanged (item);
821}
822
823void VBoxMediaManagerDlg::mediumUpdated (const VBoxMedium &aMedium)
824{
825 /* Ignore non-interesting aMedium */
826 if ((aMedium.isNull()) ||
827 (mType != VBoxDefs::MediumType_All && mType != aMedium.type()) ||
828 (aMedium.isHostDrive()))
829 return;
830
831 MediaItem *item = 0;
832
833 switch (aMedium.type())
834 {
835 case VBoxDefs::MediumType_HardDisk:
836 {
837 item = searchItem (mTwHD, aMedium.id());
838 break;
839 }
840 case VBoxDefs::MediumType_DVD:
841 {
842 item = searchItem (mTwCD, aMedium.id());
843 break;
844 }
845 case VBoxDefs::MediumType_Floppy:
846 {
847 item = searchItem (mTwFD, aMedium.id());
848 break;
849 }
850 default:
851 AssertFailed();
852 }
853
854 /* There may be unwanted items due to !mShowDiffs */
855 if (!item)
856 return;
857
858 item->setMedium (aMedium);
859
860 updateTabIcons (item, ItemAction_Updated);
861
862 /* Note: current items on invisible tabs are not updated because
863 * it is always done in processCurrentChanged() when the user switches
864 * to an invisible tab */
865 if (item == currentTreeWidget()->currentItem())
866 processCurrentChanged (item);
867}
868
869void VBoxMediaManagerDlg::mediumRemoved (VBoxDefs::MediumType aType, const QString &aId)
870{
871 /* Ignore non-interesting aMedium */
872 if (mType != VBoxDefs::MediumType_All && mType != aType)
873 return;
874
875 QTreeWidget *tree = currentTreeWidget();
876 MediaItem *item = toMediaItem (searchItem (tree, aId));
877
878 /* There may be unwanted items due to !mShowDiffs */
879 if (!item)
880 return;
881
882 updateTabIcons (item, ItemAction_Removed);
883
884 /* We need to silently delete item without selecting
885 * the new one because of complex selection mechanism
886 * which could provoke a segfault choosing the new
887 * one item during last item deletion routine. So blocking
888 * the tree-view for the time of item removing. */
889 tree->blockSignals (true);
890 delete item;
891 tree->blockSignals (false);
892
893 /* Note: current items on invisible tabs are not updated because
894 * it is always done in processCurrentChanged() when the user switches
895 * to an invisible tab */
896 setCurrentItem (tree, tree->currentItem());
897}
898
899void VBoxMediaManagerDlg::mediumEnumStarted()
900{
901 /* Reset inaccessible flags */
902 mHardDisksInaccessible =
903 mDVDImagesInaccessible =
904 mFloppyImagesInaccessible = false;
905
906 /* Load default tab icons */
907 mTabWidget->setTabIcon (HDTab, mHardDiskIcon);
908 mTabWidget->setTabIcon (CDTab, mDVDImageIcon);
909 mTabWidget->setTabIcon (FDTab, mFloppyImageIcon);
910
911 /* Load current media list */
912 const VBoxMediaList &list = vboxGlobal().currentMediaList();
913 prepareToRefresh (list.size());
914 VBoxMediaList::const_iterator it;
915 for (it = list.begin(); it != list.end(); ++ it)
916 mediumAdded (*it);
917
918 /* Select the first item to be the current one if the previous saved item
919 * was not selected yet. */
920 if (!mTwHD->currentItem())
921 if (QTreeWidgetItem *item = mTwHD->topLevelItem (0))
922 setCurrentItem (mTwHD, item);
923 if (!mTwCD->currentItem())
924 if (QTreeWidgetItem *item = mTwCD->topLevelItem (0))
925 setCurrentItem (mTwCD, item);
926 if (!mTwFD->currentItem())
927 if (QTreeWidgetItem *item = mTwFD->topLevelItem (0))
928 setCurrentItem (mTwFD, item);
929
930 processCurrentChanged();
931}
932
933void VBoxMediaManagerDlg::mediumEnumerated (const VBoxMedium &aMedium)
934{
935 mediumUpdated (aMedium);
936
937 mProgressBar->setValue (mProgressBar->value() + 1);
938}
939
940void VBoxMediaManagerDlg::mediumEnumFinished (const VBoxMediaList &/* aList */)
941{
942 mProgressBar->setVisible (false);
943
944 mRefreshAction->setEnabled (true);
945 unsetCursor();
946
947 processCurrentChanged();
948}
949
950void VBoxMediaManagerDlg::doNewMedium()
951{
952 AssertReturnVoid (currentTreeWidgetType() == VBoxDefs::MediumType_HardDisk);
953
954 UINewHDWzd dlg (this);
955
956 if (dlg.exec() == QDialog::Accepted)
957 {
958 CMedium hd = dlg.hardDisk();
959 /* Select the newly created hard disk */
960 MediaItem *item = searchItem (mTwHD, hd.GetId());
961 AssertReturnVoid (item);
962 mTwHD->setCurrentItem (item);
963 }
964}
965
966void VBoxMediaManagerDlg::doAddMedium()
967{
968 QTreeWidget *tree = currentTreeWidget();
969 MediaItem *item = toMediaItem (tree->currentItem());
970
971 QString title;
972 QString filter;
973 VBoxDefs::MediumType type = currentTreeWidgetType();
974
975 QString dir;
976 if (item && item->state() != KMediumState_Inaccessible
977 && item->state() != KMediumState_NotCreated)
978 dir = QFileInfo (item->location().trimmed()).absolutePath();
979
980 if (dir.isEmpty() || !QFileInfo (dir).exists())
981 dir = mVBox.GetHomeFolder();
982
983 QList < QPair <QString, QString> > filterList;
984 QStringList backends;
985 QStringList allPrefix;
986 QString allType;
987
988 switch (type)
989 {
990 case VBoxDefs::MediumType_DVD:
991 {
992 filterList = vboxGlobal().DVDBackends();
993 title = tr ("Select a CD/DVD-ROM disk image file");
994 allType = tr ("CD/DVD-ROM disk");
995 break;
996 }
997 case VBoxDefs::MediumType_HardDisk:
998 {
999 filterList = vboxGlobal().HDDBackends();
1000 title = tr ("Select a hard disk image file");
1001 allType = tr ("hard disk");
1002 break;
1003 }
1004 case VBoxDefs::MediumType_Floppy:
1005 {
1006 filterList = vboxGlobal().FloppyBackends();
1007 title = tr ("Select a floppy disk image file");
1008 allType = tr ("floppy disk");
1009 break;
1010 }
1011 default:
1012 AssertMsgFailed (("Selected tree should be equal to one item in VBoxDefs::MediumType.\n"));
1013 break;
1014 }
1015
1016 for (int i = 0; i < filterList.count(); ++i)
1017 {
1018 QPair <QString, QString> item = filterList.at (i);
1019 /* Create one backend filter string */
1020 backends << QString ("%1 (%2)").arg (item.first). arg (item.second);
1021 /* Save the suffix's for the "All" entry */
1022 allPrefix << item.second;
1023 }
1024 if (!allPrefix.isEmpty())
1025 backends.insert (0, tr ("All %1 images (%2)").arg (allType). arg (allPrefix.join (" ").trimmed()));
1026 backends << tr ("All files (*)");
1027 filter = backends.join (";;").trimmed();
1028
1029 QStringList files = QIFileDialog::getOpenFileNames (dir, filter, this, title);
1030 foreach (QString loc, files)
1031 {
1032 loc = QDir::convertSeparators (loc);
1033
1034 if (!loc.isEmpty())
1035 addMediumToList (loc, type);
1036 }
1037}
1038
1039void VBoxMediaManagerDlg::doRemoveMedium()
1040{
1041 QTreeWidget *tree = currentTreeWidget();
1042 MediaItem *item = toMediaItem (tree->currentItem());
1043 AssertMsgReturnVoid (item, ("Current item must not be null"));
1044
1045 /* Remember ID/type as they may get lost after the closure/deletion */
1046 QString id = item->id();
1047 AssertReturnVoid (!id.isNull());
1048 VBoxDefs::MediumType type = item->type();
1049
1050 if (!vboxProblem().confirmRemoveMedium (this, item->medium()))
1051 return;
1052
1053 COMResult result;
1054
1055 switch (type)
1056 {
1057 case VBoxDefs::MediumType_HardDisk:
1058 {
1059 bool deleteStorage = false;
1060
1061 /* We don't want to try to delete inaccessible storage as it will
1062 * most likely fail. Note that
1063 * VBoxProblemReporter::confirmRemoveMedium() is aware of that and
1064 * will give a corresponding hint. Therefore, once the code is
1065 * changed below, the hint should be re-checked for validity. */
1066 if (item->state() != KMediumState_Inaccessible &&
1067 item->medium().medium().GetMediumFormat().GetCapabilities() & MediumFormatCapabilities_File)
1068 {
1069 int rc = vboxProblem().
1070 confirmDeleteHardDiskStorage (this, item->location());
1071 if (rc == QIMessageBox::Cancel)
1072 return;
1073 deleteStorage = rc == QIMessageBox::Yes;
1074 }
1075
1076 CMedium hardDisk = item->medium().medium();
1077
1078 if (deleteStorage)
1079 {
1080 CProgress progress = hardDisk.DeleteStorage();
1081 if (hardDisk.isOk())
1082 {
1083 vboxProblem().showModalProgressDialog(progress, windowTitle(), ":/progress_media_delete_90px.png", this, true);
1084 if (!(progress.isOk() && progress.GetResultCode() == S_OK))
1085 {
1086 vboxProblem().cannotDeleteHardDiskStorage(this, hardDisk, progress);
1087 return;
1088 }
1089 }
1090 }
1091
1092 hardDisk.Close();
1093 result = hardDisk;
1094 break;
1095 }
1096 case VBoxDefs::MediumType_DVD:
1097 {
1098 CMedium image = item->medium().medium();
1099 image.Close();
1100 result = image;
1101 break;
1102 }
1103 case VBoxDefs::MediumType_Floppy:
1104 {
1105 CMedium image = item->medium().medium();
1106 image.Close();
1107 result = image;
1108 break;
1109 }
1110 default:
1111 AssertFailedReturnVoid();
1112 }
1113
1114 if (result.isOk())
1115 vboxGlobal().removeMedium (type, id);
1116 else
1117 vboxProblem().cannotCloseMedium (this, item->medium(), result);
1118}
1119
1120void VBoxMediaManagerDlg::doReleaseMedium()
1121{
1122 QTreeWidget *tree = currentTreeWidget();
1123 MediaItem *item = toMediaItem (tree->currentItem());
1124 AssertMsgReturnVoid (item, ("Current item must not be null"));
1125
1126 AssertReturnVoid (!item->id().isNull());
1127
1128 /* Get the fresh attached VM list */
1129 item->refreshAll();
1130
1131 QString usage;
1132 CMachineVector machines;
1133
1134 const QList <QString> &machineIds = item->medium().curStateMachineIds();
1135 for (QList <QString>::const_iterator it = machineIds.begin(); it != machineIds.end(); ++ it)
1136 {
1137 CMachine m = mVBox.FindMachine (*it);
1138 if (!mVBox.isOk())
1139 continue;
1140
1141 if (!usage.isEmpty())
1142 usage += ", ";
1143 usage += m.GetName();
1144
1145 machines.push_back (m);
1146 }
1147
1148 if (machineIds.size() == 0)
1149 {
1150 /* It may happen that the new machine list is empty (medium was already
1151 * released by a third party); update the details and silently return.*/
1152 processCurrentChanged (item);
1153 return;
1154 }
1155
1156 AssertReturnVoid (machines.size() > 0);
1157
1158 if (!vboxProblem().confirmReleaseMedium (this, item->medium(), usage))
1159 return;
1160
1161 for (QList <QString>::const_iterator it = machineIds.begin(); it != machineIds.end(); ++ it)
1162 {
1163 if (!releaseMediumFrom (item->medium(), *it))
1164 break;
1165 }
1166
1167 /* Inform others about medium changes (use a copy since data owning is not
1168 * clean there (to be fixed one day using shared_ptr)) */
1169 VBoxMedium newMedium = item->medium();
1170 newMedium.refresh();
1171 vboxGlobal().updateMedium (newMedium);
1172}
1173
1174bool VBoxMediaManagerDlg::releaseMediumFrom (const VBoxMedium &aMedium, const QString &aMachineId)
1175{
1176 CSession session;
1177 CMachine machine;
1178
1179 /* Is this medium is attached to the VM we are setting up */
1180 if (mSessionMachineId == aMachineId)
1181 {
1182 machine = mSessionMachine;
1183 }
1184 /* or to some other */
1185 else
1186 {
1187 session = vboxGlobal().openSession(aMachineId);
1188 if (session.isNull())
1189 return false;
1190
1191 machine = session.GetMachine();
1192 }
1193
1194 bool success = true;
1195
1196 switch (aMedium.type())
1197 {
1198 case VBoxDefs::MediumType_HardDisk:
1199 {
1200 CMediumAttachmentVector attachments = machine.GetMediumAttachments();
1201 foreach (const CMediumAttachment &attachment, attachments)
1202 {
1203 if (attachment.GetType() != KDeviceType_HardDisk) continue;
1204
1205 if (attachment.GetMedium().GetId() == aMedium.id())
1206 {
1207 machine.DetachDevice (attachment.GetController(), attachment.GetPort(), attachment.GetDevice());
1208 if (!machine.isOk())
1209 {
1210 CStorageController controller = machine.GetStorageControllerByName (attachment.GetController());
1211 vboxProblem().cannotDetachDevice (this, machine, VBoxDefs::MediumType_HardDisk, aMedium.location(),
1212 StorageSlot(controller.GetBus(), attachment.GetPort(), attachment.GetDevice()));
1213 success = false;
1214 break;
1215 }
1216 }
1217 }
1218 break;
1219 }
1220 case VBoxDefs::MediumType_DVD:
1221 {
1222 CMediumAttachmentVector attachments = machine.GetMediumAttachments();
1223 foreach (const CMediumAttachment &attachment, attachments)
1224 {
1225 if (attachment.GetType() != KDeviceType_DVD) continue;
1226
1227 VBoxMedium medium = vboxGlobal().findMedium (attachment.GetMedium().isNull() ? QString() : attachment.GetMedium().GetId());
1228 if (medium.id() == aMedium.id())
1229 {
1230 machine.MountMedium (attachment.GetController(), attachment.GetPort(), attachment.GetDevice(), CMedium(), false /* force */);
1231 if (!machine.isOk())
1232 {
1233 vboxProblem().cannotRemountMedium (this, machine, aMedium, false /* mount? */, false /* retry? */);
1234 success = false;
1235 break;
1236 }
1237 }
1238 }
1239 break;
1240 }
1241 case VBoxDefs::MediumType_Floppy:
1242 {
1243 CMediumAttachmentVector attachments = machine.GetMediumAttachments();
1244 foreach (const CMediumAttachment &attachment, attachments)
1245 {
1246 if (attachment.GetType() != KDeviceType_Floppy) continue;
1247
1248 VBoxMedium medium = vboxGlobal().findMedium (attachment.GetMedium().isNull() ? QString() : attachment.GetMedium().GetId());
1249 if (medium.id() == aMedium.id())
1250 {
1251 machine.MountMedium (attachment.GetController(), attachment.GetPort(), attachment.GetDevice(), CMedium(), false /* force */);
1252 if (!machine.isOk())
1253 {
1254 vboxProblem().cannotRemountMedium (this, machine, aMedium, false /* mount? */, false /* retry? */);
1255 success = false;
1256 break;
1257 }
1258 }
1259 }
1260 break;
1261 }
1262 default:
1263 AssertFailedBreakStmt (success = false);
1264 }
1265
1266 if (success)
1267 {
1268 machine.SaveSettings();
1269 if (!machine.isOk())
1270 {
1271 vboxProblem().cannotSaveMachineSettings (machine);
1272 success = false;
1273 }
1274 }
1275
1276 /* If a new session was opened, we must close it */
1277 if (!session.isNull())
1278 session.UnlockMachine();
1279
1280 return success;
1281}
1282
1283QTreeWidget* VBoxMediaManagerDlg::treeWidget (VBoxDefs::MediumType aType) const
1284{
1285 QTreeWidget* tree = 0;
1286 switch (aType)
1287 {
1288 case VBoxDefs::MediumType_HardDisk:
1289 tree = mTwHD;
1290 break;
1291 case VBoxDefs::MediumType_DVD:
1292 tree = mTwCD;
1293 break;
1294 case VBoxDefs::MediumType_Floppy:
1295 tree = mTwFD;
1296 break;
1297 default:
1298 AssertMsgFailed (("Disk type %d unknown!\n", aType));
1299 break;
1300 }
1301 return tree;
1302}
1303
1304VBoxDefs::MediumType VBoxMediaManagerDlg::currentTreeWidgetType() const
1305{
1306 VBoxDefs::MediumType type = VBoxDefs::MediumType_Invalid;
1307 switch (mTabWidget->currentIndex())
1308 {
1309 case HDTab:
1310 type = VBoxDefs::MediumType_HardDisk;
1311 break;
1312 case CDTab:
1313 type = VBoxDefs::MediumType_DVD;
1314 break;
1315 case FDTab:
1316 type = VBoxDefs::MediumType_Floppy;
1317 break;
1318 default:
1319 AssertMsgFailed (("Page type %d unknown!\n", mTabWidget->currentIndex()));
1320 break;
1321 }
1322 return type;
1323}
1324
1325QTreeWidget* VBoxMediaManagerDlg::currentTreeWidget() const
1326{
1327 return treeWidget (currentTreeWidgetType());
1328}
1329
1330QTreeWidgetItem *VBoxMediaManagerDlg::selectedItem (const QTreeWidget *aTree) const
1331{
1332 /* Return the current selected item. The user can select one item at the
1333 * time only, so return the first element always. */
1334 QList <QTreeWidgetItem*> selItems = aTree->selectedItems();
1335 return selItems.isEmpty() ? 0 : selItems.first();
1336}
1337
1338
1339MediaItem* VBoxMediaManagerDlg::toMediaItem (QTreeWidgetItem *aItem) const
1340{
1341 /* Convert the QTreeWidgetItem to a MediaItem if it is valid. */
1342 MediaItem *item = 0;
1343 if (aItem && aItem->type() == MediaItem::MediaItemType)
1344 item = static_cast <MediaItem*> (aItem);
1345 return item;
1346}
1347
1348void VBoxMediaManagerDlg::setCurrentItem (QTreeWidget *aTree, QTreeWidgetItem *aItem)
1349{
1350 if (aTree && aItem)
1351 {
1352 aItem->setSelected (true);
1353 aTree->setCurrentItem (aItem);
1354 aTree->scrollToItem (aItem, QAbstractItemView::EnsureVisible);
1355 processCurrentChanged (aItem);
1356 }
1357 else processCurrentChanged();
1358}
1359
1360void VBoxMediaManagerDlg::processCurrentChanged (int /* aIndex = -1 */)
1361{
1362 QTreeWidget *tree = currentTreeWidget();
1363 tree->setFocus();
1364 processCurrentChanged (tree->currentItem());
1365}
1366
1367void VBoxMediaManagerDlg::processCurrentChanged (QTreeWidgetItem *aItem,
1368 QTreeWidgetItem *aPrevItem /* = 0 */)
1369{
1370 MediaItem *item = toMediaItem (aItem);
1371
1372 if (!item && aPrevItem)
1373 {
1374 MediaItem *itemOld = toMediaItem (aPrevItem);
1375 /* We have to make sure that one item is selected always. If the new
1376 * item is 0, set the old item again. */
1377 setCurrentItem (currentTreeWidget(), itemOld);
1378 }
1379
1380 if (item)
1381 {
1382 /* Set the file for the proxy icon */
1383 setFileForProxyIcon (item->location());
1384 /* Ensures current item visible every time we are switching page */
1385 item->treeWidget()->scrollToItem (item, QAbstractItemView::EnsureVisible);
1386 }
1387
1388 bool notInEnum = !vboxGlobal().isMediaEnumerationStarted();
1389
1390 /* New and Add are now enabled even when enumerating since it should be safe */
1391 bool newEnabled = currentTreeWidgetType() == VBoxDefs::MediumType_HardDisk;
1392 bool addEnabled = true;
1393 bool removeEnabled = notInEnum && item && checkMediumFor (item, Action_Remove);
1394 bool releaseEnabled = item && checkMediumFor (item, Action_Release);
1395
1396 mNewAction->setEnabled (newEnabled);
1397 mAddAction->setEnabled (addEnabled);
1398 mRemoveAction->setEnabled (removeEnabled);
1399 mReleaseAction->setEnabled (releaseEnabled);
1400
1401 if (mDoSelect)
1402 {
1403 bool selectEnabled = item && checkMediumFor (item, Action_Select);
1404 mButtonBox->button (QDialogButtonBox::Ok)->setEnabled (selectEnabled);
1405 }
1406
1407 if (item)
1408 {
1409 QString usage = item->usage().isNull() ?
1410 formatPaneText (tr ("<i>Not&nbsp;Attached</i>"), false) :
1411 formatPaneText (item->usage());
1412
1413 if (item->treeWidget() == mTwHD)
1414 {
1415 mIpHD1->setText (formatPaneText (item->location(), true, "end"));
1416 mIpHD2->setText (formatPaneText (QString ("%1 (%2)").arg (item->hardDiskType())
1417 .arg (item->hardDiskFormat()), false));
1418 mIpHD3->setText (usage);
1419 }
1420 else if (item->treeWidget() == mTwCD)
1421 {
1422 mIpCD1->setText (formatPaneText (item->location(), true, "end"));
1423 mIpCD2->setText (usage);
1424 }
1425 else if (item->treeWidget() == mTwFD)
1426 {
1427 mIpFD1->setText (formatPaneText (item->location(), true, "end"));
1428 mIpFD2->setText (usage);
1429 }
1430 }
1431 else
1432 clearInfoPanes();
1433
1434 mHDContainer->setEnabled (item);
1435 mCDContainer->setEnabled (item);
1436 mFDContainer->setEnabled (item);
1437}
1438
1439void VBoxMediaManagerDlg::processDoubleClick (QTreeWidgetItem * /* aItem */, int /* aColumn */)
1440{
1441 QTreeWidget *tree = currentTreeWidget();
1442
1443 if (mDoSelect && selectedItem (tree) && mButtonBox->button (QDialogButtonBox::Ok)->isEnabled())
1444 accept();
1445}
1446
1447void VBoxMediaManagerDlg::showContextMenu (const QPoint &aPos)
1448{
1449 QTreeWidget *curWidget = currentTreeWidget();
1450 QTreeWidgetItem *curItem = curWidget->itemAt (aPos);
1451 if (curItem)
1452 {
1453 /* Make sure the item is selected and current */
1454 setCurrentItem (curWidget, curItem);
1455 mActionsContextMenu->exec (curWidget->viewport()->mapToGlobal (aPos));
1456 }
1457}
1458
1459void VBoxMediaManagerDlg::machineStateChanged(QString /* strId */, KMachineState state)
1460{
1461 switch (state)
1462 {
1463 case KMachineState_PoweredOff:
1464 case KMachineState_Aborted:
1465 case KMachineState_Saved:
1466 case KMachineState_Teleported:
1467 case KMachineState_Starting:
1468 case KMachineState_Restoring:
1469 case KMachineState_TeleportingIn:
1470 {
1471 refreshAll();
1472 break;
1473 }
1474 default:
1475 break;
1476 }
1477}
1478
1479void VBoxMediaManagerDlg::makeRequestForAdjustTable()
1480{
1481 /* We have to perform table adjustment only after all the [auto]resize
1482 * events processed for every column of this table. */
1483 QTimer::singleShot (0, this, SLOT (performTablesAdjustment()));
1484}
1485
1486void VBoxMediaManagerDlg::performTablesAdjustment()
1487{
1488 /* Get all the tree widgets */
1489 QList <QITreeWidget*> widgetList;
1490 widgetList << mTwHD;
1491 widgetList << mTwCD;
1492 widgetList << mTwFD;
1493
1494 /* Calculate deduction for every header */
1495 QList <int> deductionsList;
1496 foreach (QITreeWidget *widget, widgetList)
1497 {
1498 int deduction = 0;
1499 for (int i = 1; i < widget->header()->count(); ++ i)
1500 deduction += widget->header()->sectionSize (i);
1501 deductionsList << deduction;
1502 }
1503
1504 /* Adjust the table's first column */
1505 for (int i = 0; i < widgetList.size(); ++ i)
1506 {
1507 int size0 = widgetList [i]->viewport()->width() - deductionsList [i];
1508 if (widgetList [i]->header()->sectionSize (0) != size0)
1509 widgetList [i]->header()->resizeSection (0, size0);
1510 }
1511}
1512
1513void VBoxMediaManagerDlg::addMediumToList(const QString &aLocation, VBoxDefs::MediumType aType)
1514{
1515 AssertReturnVoid (!aLocation.isEmpty());
1516
1517 VBoxMedium medium;
1518 KDeviceType devType;
1519
1520 switch (aType)
1521 {
1522 case VBoxDefs::MediumType_HardDisk:
1523 devType = KDeviceType_HardDisk;
1524 break;
1525 case VBoxDefs::MediumType_DVD:
1526 devType = KDeviceType_DVD;
1527 break;
1528 case VBoxDefs::MediumType_Floppy:
1529 devType = KDeviceType_Floppy;
1530 break;
1531 default:
1532 AssertMsgFailedReturnVoid (("Invalid aType %d\n", aType));
1533 }
1534
1535 CMedium med = mVBox.OpenMedium(aLocation, devType, KAccessMode_ReadWrite);
1536 if (mVBox.isOk())
1537 medium = VBoxMedium(CMedium(med), aType, KMediumState_Created);
1538
1539 if (!mVBox.isOk())
1540 vboxProblem().cannotOpenMedium(this, mVBox, aType, aLocation);
1541 else
1542 vboxGlobal().addMedium(medium);
1543}
1544
1545MediaItem* VBoxMediaManagerDlg::createHardDiskItem (QTreeWidget *aTree, const VBoxMedium &aMedium) const
1546{
1547 AssertReturn (!aMedium.medium().isNull(), 0);
1548
1549 MediaItem *item = 0;
1550
1551 CMedium parent = aMedium.medium().GetParent();
1552 if (parent.isNull())
1553 {
1554 item = new MediaItem(aTree, aMedium, this);
1555 }
1556 else
1557 {
1558 MediaItem *root = searchItem (aTree, parent.GetId());
1559 if (root)
1560 item = new MediaItem(root, aMedium, this);
1561 else
1562 item = new MediaItem(aTree, aMedium, this);
1563 }
1564
1565 return item;
1566}
1567
1568void VBoxMediaManagerDlg::updateTabIcons (MediaItem *aItem, ItemAction aAction)
1569{
1570 AssertReturnVoid (aItem);
1571
1572 int tab = -1;
1573 QIcon *icon = 0;
1574 bool *inaccessible = 0;
1575
1576 switch (aItem->type())
1577 {
1578 case VBoxDefs::MediumType_HardDisk:
1579 tab = HDTab;
1580 icon = &mHardDiskIcon;
1581 inaccessible = &mHardDisksInaccessible;
1582 break;
1583 case VBoxDefs::MediumType_DVD:
1584 tab = CDTab;
1585 icon = &mDVDImageIcon;
1586 inaccessible = &mDVDImagesInaccessible;
1587 break;
1588 case VBoxDefs::MediumType_Floppy:
1589 tab = FDTab;
1590 icon = &mFloppyImageIcon;
1591 inaccessible = &mFloppyImagesInaccessible;
1592 break;
1593 default:
1594 AssertFailed();
1595 }
1596
1597 AssertReturnVoid (tab != -1 && icon && inaccessible);
1598
1599 switch (aAction)
1600 {
1601 case ItemAction_Added:
1602 {
1603 /* Does it change the overall state? */
1604 if (*inaccessible || aItem->state() != KMediumState_Inaccessible)
1605 break; /* no */
1606
1607 *inaccessible = true;
1608
1609 mTabWidget->setTabIcon (tab, vboxGlobal().warningIcon());
1610
1611 break;
1612 }
1613 case ItemAction_Updated:
1614 case ItemAction_Removed:
1615 {
1616 bool checkRest = false;
1617
1618 if (aAction == ItemAction_Updated)
1619 {
1620 /* Does it change the overall state? */
1621 if ((*inaccessible && aItem->state() == KMediumState_Inaccessible) ||
1622 (!*inaccessible && aItem->state() != KMediumState_Inaccessible))
1623 break; /* no */
1624
1625 /* Is the given item in charge? */
1626 if (!*inaccessible && aItem->state() == KMediumState_Inaccessible)
1627 *inaccessible = true; /* yes */
1628 else
1629 checkRest = true; /* no */
1630 }
1631 else
1632 checkRest = true;
1633
1634 if (checkRest)
1635 {
1636 *inaccessible = false;
1637
1638 QTreeWidget *tree = aItem->treeWidget();
1639
1640 /* Find the first inaccessible item to be in charge */
1641 MediaItemIterator it (tree);
1642 for (; *it; ++ it)
1643 {
1644 if (*it != aItem && (*it)->state() == KMediumState_Inaccessible)
1645 {
1646 *inaccessible = true;
1647 break;
1648 }
1649 }
1650 }
1651
1652 if (*inaccessible)
1653 mTabWidget->setTabIcon (tab, vboxGlobal().warningIcon());
1654 else
1655 mTabWidget->setTabIcon (tab, *icon);
1656
1657 break;
1658 }
1659 }
1660}
1661
1662MediaItem* VBoxMediaManagerDlg::searchItem (QTreeWidget *aTree, const QString &aId) const
1663{
1664 if (aId.isNull())
1665 return 0;
1666
1667 MediaItemIterator iterator (aTree);
1668 while (*iterator)
1669 {
1670 if ((*iterator)->id() == aId)
1671 return *iterator;
1672 ++ iterator;
1673 }
1674 return 0;
1675}
1676
1677/**
1678 * Checks if a specific action is valid for the given medium at its current
1679 * state.
1680 *
1681 * @param aItem Media item.
1682 * @param aAction Action to check.
1683 *
1684 * @return @c true if the action is possible and false otherwise.
1685 */
1686bool VBoxMediaManagerDlg::checkMediumFor (MediaItem *aItem, Action aAction)
1687{
1688 AssertReturn (aItem, false);
1689
1690 switch (aAction)
1691 {
1692 case Action_Select:
1693 {
1694 /* Restrict selecting mediums
1695 * already used by other attachments */
1696 return !mUsedMediaIds.contains (aItem->id());
1697 }
1698 case Action_Edit:
1699 {
1700 /* Edit means changing the description and alike; any media that is
1701 * not being read to or written from can be altered in these
1702 * terms */
1703 switch (aItem->state())
1704 {
1705 case KMediumState_NotCreated:
1706 case KMediumState_Inaccessible:
1707 case KMediumState_LockedRead:
1708 case KMediumState_LockedWrite:
1709 return false;
1710 default:
1711 break;
1712 }
1713 return true;
1714 }
1715 case Action_Remove:
1716 {
1717 /* Removable if not attached to anything */
1718 return !aItem->isUsed();
1719 }
1720 case Action_Release:
1721 {
1722 /* Releasable if attached but not in snapshots */
1723 return aItem->isUsed() && !aItem->isUsedInSnapshots();
1724 }
1725 }
1726
1727 AssertFailedReturn (false);
1728}
1729
1730bool VBoxMediaManagerDlg::checkDndUrls (const QList <QUrl> &aUrls) const
1731{
1732 bool err = false;
1733 /* Check that all file extensions fit to the current
1734 * selected tree view and the files are valid. */
1735 foreach (QUrl u, aUrls)
1736 {
1737 QFileInfo fi (u.toLocalFile());
1738 /* Check dropped media type */
1739 /// @todo On OS/2 and windows (and mac?) extension checks should be case
1740 /// insensitive, as OPPOSED to linux and the rest where case matters.
1741 QString suffix = fi.suffix().toLower();
1742 switch (currentTreeWidgetType())
1743 {
1744 case VBoxDefs::MediumType_HardDisk:
1745 {
1746 QList < QPair <QString, QString> > filterList = vboxGlobal().HDDBackends();
1747 bool match = false;
1748 for (int i = 0; i < filterList.count(); ++i)
1749 {
1750 QPair <QString, QString> item = filterList.at (i);
1751 if (QString ("*.%1").arg (suffix) == item.second)
1752 {
1753 match = true;
1754 break;
1755 }
1756 }
1757 err |= !match;
1758 break;
1759 }
1760 case VBoxDefs::MediumType_DVD:
1761 err |= (suffix != "iso");
1762 break;
1763 case VBoxDefs::MediumType_Floppy:
1764 err |= (suffix != "img");
1765 break;
1766 default:
1767 AssertMsgFailed (("Selected tree should be equal to one item in VBoxDefs::MediumType.\n"));
1768 break;
1769 }
1770 }
1771 return !err;
1772}
1773
1774void VBoxMediaManagerDlg::addDndUrls (const QList <QUrl> &aUrls)
1775{
1776 foreach (QUrl u, aUrls)
1777 {
1778 QString file = u.toLocalFile();
1779 VBoxDefs::MediumType type = currentTreeWidgetType();
1780 addMediumToList (file, type);
1781 }
1782}
1783
1784void VBoxMediaManagerDlg::clearInfoPanes()
1785{
1786 mIpHD1->clear(); mIpHD2->clear(); mIpHD3->clear();
1787 mIpCD1->clear(); mIpCD2->clear();
1788 mIpFD1->clear(); mIpFD2->clear();
1789}
1790
1791void VBoxMediaManagerDlg::prepareToRefresh (int aTotal)
1792{
1793 /* Info panel clearing */
1794 clearInfoPanes();
1795
1796 /* Prepare progressbar */
1797 if (mProgressBar)
1798 {
1799 mProgressBar->setMaximum (aTotal);
1800 mProgressBar->setValue (0);
1801 mProgressBar->setVisible (true);
1802 }
1803
1804 mRefreshAction->setEnabled (false);
1805 setCursor (QCursor (Qt::BusyCursor));
1806
1807 /* Store the current list selections */
1808 MediaItem *mi;
1809
1810 mi = toMediaItem (mTwHD->currentItem());
1811 if (mHDSelectedId.isNull())
1812 mHDSelectedId = mi ? mi->id() : QString::null;
1813
1814 mi = toMediaItem (mTwCD->currentItem());
1815 if (mCDSelectedId.isNull())
1816 mCDSelectedId = mi ? mi->id() : QString::null;
1817
1818 mi = toMediaItem (mTwFD->currentItem());
1819 if (mFDSelectedId.isNull())
1820 mFDSelectedId = mi ? mi->id() : QString::null;
1821
1822 /* Finally, clear all the lists...
1823 * Qt4 has interesting bug here. It sends the currentChanged (cur, prev)
1824 * signal during list clearing with 'cur' set to null and 'prev' pointing
1825 * to already excluded element if this element is not topLevelItem
1826 * (has parent). Cause the Hard Disk list has such elements there is
1827 * seg-fault when trying to make 'prev' element the current due to 'cur'
1828 * is null and at least one element have to be selected (by policy).
1829 * So just blocking any signals outgoing from the list during clearing. */
1830 mTwHD->blockSignals (true);
1831 mTwHD->clear();
1832 mTwHD->blockSignals (false);
1833 mTwCD->clear();
1834 mTwFD->clear();
1835}
1836
1837/**
1838 * Modifies the given text string for QILabel so that it optionally uses the
1839 * <compact> syntax controlling visual text "compression" with elipsis.
1840 *
1841 * @param aText Original text string.
1842 * @param aCompact @c true if "compression" should be activated.
1843 * @param aElipsis Where to put the elipsis (see <compact> in QILabel).
1844 *
1845 * @return Modified text string.
1846 */
1847QString VBoxMediaManagerDlg::formatPaneText (const QString &aText, bool aCompact /* = true */,
1848 const QString &aElipsis /* = "middle" */)
1849{
1850 QString compactString = QString ("<compact elipsis=\"%1\">").arg (aElipsis);
1851 QString info = QString ("<nobr>%1%2%3</nobr>")
1852 .arg (aCompact ? compactString : "")
1853 .arg (aText.isEmpty() ?
1854 QApplication::translate ("VBoxMediaManagerDlg", "--", "no info") :
1855 aText)
1856 .arg (aCompact ? "</compact>" : "");
1857 return info;
1858}
1859
1860#include "VBoxMediaManagerDlg.moc"
1861
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use