VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox4/src/VBoxVMSettingsHD.cpp@ 13580

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

Ported s2 branch (r37120:38456).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 34.2 KB
Line 
1/** @file
2 *
3 * VBox frontends: Qt4 GUI ("VirtualBox"):
4 * VBoxVMSettingsHD class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include "VBoxVMSettingsHD.h"
24#include "VBoxGlobal.h"
25#include "VBoxProblemReporter.h"
26#include "QIWidgetValidator.h"
27#include "VBoxToolBar.h"
28#include "VBoxMediaManagerDlg.h"
29#include "VBoxNewHDWzd.h"
30
31/* Qt includes */
32#include <QHeaderView>
33#include <QItemEditorFactory>
34#include <QMetaProperty>
35#include <QScrollBar>
36#include <QStylePainter>
37
38/** SATA Ports count */
39static const ULONG SATAPortsCount = 30;
40
41/**
42 * Clear the focus from the current focus owner on guard creation.
43 * And put it into the desired object on guard deletion.
44 *
45 * Here this is used to temporary remove the focus from the attachments
46 * table to close the temporary editor of this table to prevent
47 * any side-process (enumeration) influencing model's data.
48 */
49class FocusGuardBlock
50{
51public:
52 FocusGuardBlock (QWidget *aReturnTo) : mReturnTo (aReturnTo)
53 {
54 if (QApplication::focusWidget())
55 {
56 QApplication::focusWidget()->clearFocus();
57 qApp->processEvents();
58 }
59 }
60 ~FocusGuardBlock()
61 {
62 mReturnTo->setFocus();
63 qApp->processEvents();
64 }
65
66private:
67 QWidget *mReturnTo;
68};
69
70/** Type to store disk data */
71DiskValue::DiskValue (const QUuid &aId)
72 : id (aId)
73 , name (QString::null), tip (QString::null), pix (QPixmap())
74{
75 if (aId.isNull())
76 return;
77
78 VBoxMedium medium = vboxGlobal().getMedium (
79 CMedium (vboxGlobal().virtualBox().GetHardDisk2 (aId)));
80 medium.refresh();
81 bool noDiffs = !HDSettings::instance()->showDiffs();
82 name = medium.details (noDiffs);
83 tip = medium.toolTipCheckRO (noDiffs);
84 pix = medium.iconCheckRO (noDiffs);
85}
86
87/**
88 * QAbstractTableModel class reimplementation.
89 * Used to feat slot/disk selection mechanism.
90 */
91Qt::ItemFlags AttachmentsModel::flags (const QModelIndex &aIndex) const
92{
93 return aIndex.row() == rowCount() - 1 ?
94 QAbstractItemModel::flags (aIndex) ^ Qt::ItemIsSelectable :
95 QAbstractItemModel::flags (aIndex) | Qt::ItemIsEditable;
96}
97
98QVariant AttachmentsModel::data (const QModelIndex &aIndex, int aRole) const
99{
100 if (!aIndex.isValid())
101 return QVariant();
102
103 if (aIndex.row() < 0 || aIndex.row() >= rowCount())
104 return QVariant();
105
106 switch (aRole)
107 {
108 case Qt::DisplayRole:
109 {
110 if (aIndex.row() == rowCount() - 1)
111 return QVariant();
112 else if (aIndex.column() == 0)
113 return QVariant (mUsedSlotsList [aIndex.row()].name);
114 else if (aIndex.column() == 1)
115 return QVariant (mUsedDisksList [aIndex.row()].name);
116
117 Assert (0);
118 return QVariant();
119 }
120 case Qt::DecorationRole:
121 {
122 return aIndex.row() != rowCount() - 1 &&
123 aIndex.column() == 1 &&
124 (aIndex != mParent->currentIndex() ||
125 !DiskEditor::activeEditor())
126 ? QVariant (mUsedDisksList [aIndex.row()].pix) : QVariant();
127 }
128 case Qt::EditRole:
129 {
130 if (aIndex.column() == 0)
131 return QVariant (mSlotId, &mUsedSlotsList [aIndex.row()]);
132 else if (aIndex.column() == 1)
133 return QVariant (mDiskId, &mUsedDisksList [aIndex.row()]);
134
135 Assert (0);
136 return QVariant();
137 }
138 case Qt::ToolTipRole:
139 {
140 if (aIndex.row() == rowCount() - 1)
141 return QVariant (tr ("Double-click to add a new attachment"));
142
143 return QVariant (mUsedDisksList [aIndex.row()].tip);
144 }
145 default:
146 {
147 return QVariant();
148 }
149 }
150}
151
152bool AttachmentsModel::setData (const QModelIndex &aIndex,
153 const QVariant &aValue,
154 int /* aRole = Qt::EditRole */)
155{
156 if (!aIndex.isValid())
157 return false;
158
159 if (aIndex.row() < 0 || aIndex.row() >= rowCount())
160 return false;
161
162 if (aIndex.column() == 0)
163 {
164 SlotValue newSlot = aValue.isValid() ?
165 aValue.value <SlotValue>() : SlotValue();
166 if (mUsedSlotsList [aIndex.row()] != newSlot)
167 {
168 mUsedSlotsList [aIndex.row()] = newSlot;
169 emit dataChanged (aIndex, aIndex);
170 return true;
171 }
172 return false;
173 } else
174 if (aIndex.column() == 1)
175 {
176 DiskValue newDisk = aValue.isValid() ?
177 aValue.value <DiskValue>() : DiskValue();
178 if (mUsedDisksList [aIndex.row()] != newDisk)
179 {
180 mUsedDisksList [aIndex.row()] = newDisk;
181 emit dataChanged (aIndex, aIndex);
182 return true;
183 }
184 return false;
185 }
186 Assert (0);
187 return false;
188}
189
190QVariant AttachmentsModel::headerData (int aSection,
191 Qt::Orientation aOrientation,
192 int aRole) const
193{
194 if (aRole != Qt::DisplayRole)
195 return QVariant();
196
197 if (aOrientation == Qt::Horizontal)
198 return aSection ? tr ("Hard Disk") : tr ("Slot");
199 else
200 return QVariant();
201}
202
203void AttachmentsModel::addItem (const SlotValue &aSlot, const DiskValue &aDisk)
204{
205 beginInsertRows (QModelIndex(), rowCount() - 1, rowCount() - 1);
206 mUsedSlotsList.append (aSlot);
207 mUsedDisksList.append (aDisk);
208 endInsertRows();
209}
210
211void AttachmentsModel::delItem (int aIndex)
212{
213 beginRemoveRows (QModelIndex(), aIndex, aIndex);
214 mUsedSlotsList.removeAt (aIndex);
215 mUsedDisksList.removeAt (aIndex);
216 endRemoveRows();
217}
218
219QList <Attachment> AttachmentsModel::fullUsedList()
220{
221 QList <Attachment> list;
222 QList <SlotValue> slts = usedSlotsList();
223 QList <DiskValue> dsks = usedDisksList();
224 for (int i = 0; i < slts.size(); ++ i)
225 list << Attachment (slts [i], dsks [i]);
226 qSort (list.begin(), list.end());
227 return list;
228}
229
230void AttachmentsModel::removeSata()
231{
232 QList <SlotValue>::iterator slotIt = mUsedSlotsList.begin();
233 QList <DiskValue>::iterator diskIt = mUsedDisksList.begin();
234 while (slotIt != mUsedSlotsList.end())
235 {
236 if ((*slotIt).bus == KStorageBus_SATA)
237 {
238 slotIt = mUsedSlotsList.erase (slotIt);
239 diskIt = mUsedDisksList.erase (diskIt);
240 }
241 else
242 {
243 ++ slotIt;
244 ++ diskIt;
245 }
246 }
247}
248
249void AttachmentsModel::updateDisks()
250{
251 QList <DiskValue> newDisks (HDSettings::instance()->disksList());
252 for (int i = 0; i < mUsedDisksList.size(); ++ i)
253 {
254 if (newDisks.isEmpty())
255 mUsedDisksList [i] = DiskValue();
256 else if (newDisks.contains (mUsedDisksList [i]))
257 mUsedDisksList [i] = DiskValue (mUsedDisksList [i].id);
258 else
259 mUsedDisksList [i] = DiskValue (newDisks [0].id);
260 }
261 emit dataChanged (index (0, 1), index (rowCount() - 1, 1));
262}
263
264/**
265 * QComboBox class reimplementation.
266 * Used as editor for HD Attachment SLOT field.
267 */
268SlotEditor::SlotEditor (QWidget *aParent)
269 : QComboBox (aParent)
270{
271 connect (this, SIGNAL (currentIndexChanged (int)), this, SLOT (onActivate()));
272 connect (this, SIGNAL (readyToCommit (QWidget*)),
273 parent()->parent(), SLOT (commitData (QWidget*)));
274}
275
276QVariant SlotEditor::slot() const
277{
278 int current = currentIndex();
279 QVariant result;
280 if (current >= 0 && current < mList.size())
281 result.setValue (mList [current]);
282 return result;
283}
284
285void SlotEditor::setSlot (QVariant aSlot)
286{
287 SlotValue val (aSlot.value <SlotValue>());
288 populate (val);
289 int current = findText (val.name);
290 setCurrentIndex (current == -1 ? 0 : current);
291}
292
293void SlotEditor::onActivate()
294{
295 emit readyToCommit (this);
296}
297
298#if 0 /* F2 key binding left for future releases... */
299void SlotEditor::keyPressEvent (QKeyEvent *aEvent)
300{
301 /* Make F2 key to show the popup. */
302 if (aEvent->key() == Qt::Key_F2)
303 {
304 aEvent->accept();
305 showPopup();
306 }
307 else
308 aEvent->ignore();
309 QComboBox::keyPressEvent (aEvent);
310}
311#endif
312
313void SlotEditor::populate (const SlotValue &aIncluding)
314{
315 clear(), mList.clear();
316 QList <SlotValue> list (HDSettings::instance()->slotsList (aIncluding, true));
317 for (int i = 0; i < list.size() ; ++ i)
318 {
319 insertItem (i, list [i].name);
320 mList << list [i];
321 }
322}
323
324/**
325 * VBoxMediaComboBox class reimplementation.
326 * Used as editor for HD Attachment DISK field.
327 */
328DiskEditor* DiskEditor::mInstance = 0;
329DiskEditor* DiskEditor::activeEditor()
330{
331 return mInstance;
332}
333
334DiskEditor::DiskEditor (QWidget *aParent)
335 : VBoxMediaComboBox (aParent)
336{
337 mInstance = this;
338 setIconSize (QSize (iconSize().width() * 2 + 2, iconSize().height()));
339 Assert (!HDSettings::instance()->machine().isNull());
340 setType (VBoxDefs::MediaType_HardDisk);
341 setMachineId (HDSettings::instance()->machine().GetId());
342 setShowDiffs (HDSettings::instance()->showDiffs());
343 connect (this, SIGNAL (currentIndexChanged (int)), this, SLOT (onActivate()));
344 connect (this, SIGNAL (readyToCommit (QWidget *)),
345 parent()->parent(), SLOT (commitData (QWidget *)));
346 refresh();
347}
348DiskEditor::~DiskEditor()
349{
350 if (mInstance == this)
351 mInstance = 0;
352}
353
354QVariant DiskEditor::disk() const
355{
356 int current = currentIndex();
357 QVariant result;
358 if (current >= 0 && current < count())
359 result.setValue (DiskValue (id (current)));
360 return result;
361}
362
363void DiskEditor::setDisk (QVariant aDisk)
364{
365 setCurrentItem (DiskValue (aDisk.value <DiskValue>()).id);
366}
367
368void DiskEditor::paintEvent (QPaintEvent*)
369{
370 /* Create the style painter to paint the elements. */
371 QStylePainter painter (this);
372 painter.setPen (palette().color (QPalette::Text));
373 /* Initialize combo-box options and draw the elements. */
374 QStyleOptionComboBox options;
375 initStyleOption (&options);
376 painter.drawComplexControl (QStyle::CC_ComboBox, options);
377 painter.drawControl (QStyle::CE_ComboBoxLabel, options);
378}
379
380void DiskEditor::initStyleOption (QStyleOptionComboBox *aOption) const
381{
382 /* The base version of Qt4::QComboBox ignores the fact what each
383 * combo-box item can have the icon of different size and uses the
384 * maximum possible icon-size to draw the icon then performing
385 * paintEvent(). As a result, stand-alone icons are painted using
386 * the same huge region as the merged paired icons, so we have to
387 * perform the size calculation ourself... */
388
389 /* Init all style option by default... */
390 VBoxMediaComboBox::initStyleOption (aOption);
391 /* But calculate the icon size ourself. */
392 QIcon currentItemIcon (itemIcon (currentIndex()));
393 QPixmap realPixmap (currentItemIcon.pixmap (iconSize()));
394 aOption->iconSize = realPixmap.size();
395}
396
397void DiskEditor::onActivate()
398{
399 emit readyToCommit (this);
400}
401
402#if 0 /* F2 key binding left for future releases... */
403void DiskEditor::keyPressEvent (QKeyEvent *aEvent)
404{
405 /* Make F2 key to show the popup. */
406 if (aEvent->key() == Qt::Key_F2)
407 {
408 aEvent->accept();
409 showPopup();
410 }
411 else
412 aEvent->ignore();
413 VBoxMediaComboBox::keyPressEvent (aEvent);
414}
415#endif
416
417/**
418 * Singleton QObject class reimplementation.
419 * Used to make selected HD Attachments slots unique &
420 * stores some local data used for HD Settings.
421 */
422HDSettings* HDSettings::mInstance = 0;
423HDSettings* HDSettings::instance (QWidget *aParent,
424 AttachmentsModel *aWatched)
425{
426 if (!mInstance)
427 {
428 Assert (aParent && aWatched);
429 mInstance = new HDSettings (aParent, aWatched);
430 }
431 return mInstance;
432}
433
434HDSettings::HDSettings (QWidget *aParent, AttachmentsModel *aWatched)
435 : QObject (aParent)
436 , mModel (aWatched)
437 , mSataCount (SATAPortsCount)
438 , mShowDiffs (false)
439{
440 makeIDEList();
441 makeSATAList();
442}
443
444HDSettings::~HDSettings()
445{
446 mInstance = 0;
447}
448
449QList <SlotValue> HDSettings::slotsList (const SlotValue &aIncluding,
450 bool aFilter /* = false */) const
451{
452 /* Compose the full slots list */
453 QList <SlotValue> list (mIDEList + mSATAList);
454 if (!aFilter)
455 return list;
456
457 /* Current used list */
458 QList <SlotValue> usedList (mModel->usedSlotsList());
459
460 /* Filter the list */
461 foreach (SlotValue value, usedList)
462 if (value != aIncluding)
463 list.removeAll (value);
464
465 return list;
466}
467
468QList <DiskValue> HDSettings::disksList() const
469{
470 return mDisksList;
471}
472
473bool HDSettings::tryToChooseUniqueDisk (DiskValue &aResult) const
474{
475 bool status = false;
476
477 /* Current used list */
478 QList <DiskValue> usedList (mModel->usedDisksList());
479
480 /* Select the first available disk initially */
481 aResult = mDisksList.isEmpty() ? DiskValue() : mDisksList [0];
482
483 /* Search for first not busy disk */
484 for (int i = 0; i < mDisksList.size(); ++ i)
485 if (!usedList.contains (mDisksList [i]))
486 {
487 aResult = mDisksList [i];
488 status = true;
489 break;
490 }
491
492 return status;
493}
494
495void HDSettings::makeIDEList()
496{
497 mIDEList.clear();
498
499 /* IDE Primary Master */
500 mIDEList << SlotValue (KStorageBus_IDE, 0, 0);
501 /* IDE Primary Slave */
502 mIDEList << SlotValue (KStorageBus_IDE, 0, 1);
503 /* IDE Secondary Slave */
504 mIDEList << SlotValue (KStorageBus_IDE, 1, 1);
505}
506
507void HDSettings::makeSATAList()
508{
509 mSATAList.clear();
510
511 for (int i = 0; i < mSataCount; ++ i)
512 mSATAList << SlotValue (KStorageBus_SATA, i, 0);
513}
514
515void HDSettings::makeMediumList()
516{
517 mDisksList.clear();
518 VBoxMediaList list (vboxGlobal().currentMediaList());
519 foreach (VBoxMedium medium, list)
520 {
521 /* Filter out unnecessary mediums */
522 if (medium.type() != VBoxDefs::MediaType_HardDisk)
523 continue;
524
525 /* If !mShowDiffs we ignore all diffs except ones that are
526 * directly attached to the related VM in the current state */
527 if (!mShowDiffs && medium.parent() &&
528 !medium.isAttachedInCurStateTo (mMachine.GetId()))
529 continue;
530
531 /* If !mShowDiffs we have to replace the root medium with his
532 * differencing child which is directly used if the parent is found. */
533 if (!mShowDiffs && medium.parent())
534 {
535 int index = mDisksList.indexOf (DiskValue (medium.root().id()));
536 if (index != -1)
537 {
538 mDisksList.replace (index, DiskValue (medium.id()));
539 continue;
540 }
541 }
542
543 mDisksList.append (DiskValue (medium.id()));
544 }
545}
546
547/**
548 * QWidget class reimplementation.
549 * Used as HD Settings widget.
550 */
551VBoxVMSettingsHD::VBoxVMSettingsHD()
552 : mValidator (0)
553 , mWasTableSelected (false)
554 , mPolished (false)
555{
556 /* Apply UI decorations */
557 Ui::VBoxVMSettingsHD::setupUi (this);
558
559 /* Setup model/view factory */
560 int idHDSlot = qRegisterMetaType <SlotValue>();
561 int idHDDisk = qRegisterMetaType <DiskValue>();
562 QItemEditorFactory *factory = new QItemEditorFactory;
563 QItemEditorCreatorBase *slotCreator =
564 new QStandardItemEditorCreator <SlotEditor>();
565 QItemEditorCreatorBase *diskCreator =
566 new QStandardItemEditorCreator <DiskEditor>();
567 factory->registerEditor ((QVariant::Type)idHDSlot, slotCreator);
568 factory->registerEditor ((QVariant::Type)idHDDisk, diskCreator);
569 QItemEditorFactory::setDefaultFactory (factory);
570
571 /* Setup view-model */
572 mModel = new AttachmentsModel (mTwAts, idHDSlot, idHDDisk);
573 connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
574 this, SIGNAL (hdChanged()));
575
576 /* Initialize HD Settings */
577 HDSettings::instance (mTwAts, mModel);
578
579 /* Setup table-view */
580 mTwAts->verticalHeader()->setDefaultSectionSize (
581 (int) (mTwAts->fontMetrics().height() * 1.30 /* 130% of font height */));
582 mTwAts->verticalHeader()->hide();
583 mTwAts->horizontalHeader()->setStretchLastSection (true);
584 mTwAts->setModel (mModel);
585 mTwAts->setToolTip (mModel->data (mModel->index (mModel->rowCount() - 1, 0),
586 Qt::ToolTipRole).toString());
587
588 /* Prepare actions */
589 mNewAction = new QAction (mTwAts);
590 mDelAction = new QAction (mTwAts);
591 mVdmAction = new QAction (mTwAts);
592
593 mTwAts->addAction (mNewAction);
594 mTwAts->addAction (mDelAction);
595 mTwAts->addAction (mVdmAction);
596
597 mNewAction->setShortcut (QKeySequence ("Ins"));
598 mDelAction->setShortcut (QKeySequence ("Del"));
599 mVdmAction->setShortcut (QKeySequence ("Ctrl+Space"));
600
601 mNewAction->setIcon (VBoxGlobal::iconSet (":/vdm_add_16px.png",
602 ":/vdm_add_disabled_16px.png"));
603 mDelAction->setIcon (VBoxGlobal::iconSet (":/vdm_remove_16px.png",
604 ":/vdm_remove_disabled_16px.png"));
605 mVdmAction->setIcon (VBoxGlobal::iconSet (":/select_file_16px.png",
606 ":/select_file_dis_16px.png"));
607
608 /* Prepare toolbar */
609 VBoxToolBar *toolBar = new VBoxToolBar (mGbAts);
610 toolBar->setUsesTextLabel (false);
611 toolBar->setIconSize (QSize (16, 16));
612 toolBar->setOrientation (Qt::Vertical);
613 toolBar->addAction (mNewAction);
614 toolBar->addAction (mDelAction);
615 toolBar->addAction (mVdmAction);
616 mGbAts->layout()->addWidget (toolBar);
617
618 /* Setup connections */
619 connect (mNewAction, SIGNAL (triggered (bool)),
620 this, SLOT (addAttachment()));
621 connect (mDelAction, SIGNAL (triggered (bool)),
622 this, SLOT (delAttachment()));
623 connect (mVdmAction, SIGNAL (triggered (bool)),
624 this, SLOT (showMediaManager()));
625
626 connect (mSATACheck, SIGNAL (stateChanged (int)),
627 this, SLOT (onSATACheckToggled (int)));
628 connect (mShowDiffsCheck, SIGNAL (stateChanged (int)),
629 this, SLOT (onShowDiffsCheckToggled (int)));
630
631 connect (mTwAts, SIGNAL (currentChanged (const QModelIndex &)),
632 this, SLOT (updateActions (const QModelIndex &)));
633
634 connect (&vboxGlobal(), SIGNAL (mediumAdded (const VBoxMedium &)),
635 HDSettings::instance(), SLOT (update()));
636 connect (&vboxGlobal(), SIGNAL (mediumUpdated (const VBoxMedium &)),
637 HDSettings::instance(), SLOT (update()));
638 connect (&vboxGlobal(), SIGNAL (mediumRemoved (VBoxDefs::MediaType, const QUuid &)),
639 HDSettings::instance(), SLOT (update()));
640
641 /* Install global event filter */
642 qApp->installEventFilter (this);
643
644 /* Applying language settings */
645 retranslateUi();
646}
647
648void VBoxVMSettingsHD::getFrom (const CMachine &aMachine)
649{
650 mMachine = aMachine;
651 HDSettings::instance()->setMachine (mMachine);
652
653 CSATAController ctl = mMachine.GetSATAController();
654 /* Hide the SATA check box if the SATA controller is not available
655 * (i.e. in VirtualBox OSE) */
656 if (ctl.isNull())
657 mSATACheck->setHidden (true);
658 else
659 mSATACheck->setChecked (ctl.GetEnabled());
660 onSATACheckToggled (mSATACheck->checkState());
661 onShowDiffsCheckToggled (mShowDiffsCheck->checkState());
662
663 /* Load attachments list */
664 CHardDisk2AttachmentVector vec = mMachine.GetHardDisk2Attachments();
665 for (int i = 0; i < vec.size(); ++ i)
666 {
667 CHardDisk2Attachment hda = vec [i];
668 SlotValue slot (hda.GetBus(), hda.GetChannel(), hda.GetDevice());
669 DiskValue disk (hda.GetHardDisk().GetId());
670 mModel->addItem (slot, disk);
671 }
672
673 /* Initially select the first table item & update the actions */
674 mTwAts->setCurrentIndex (mModel->index (0, 1));
675 updateActions (mTwAts->currentIndex());
676
677 /* Validate if possible */
678 if (mValidator)
679 mValidator->revalidate();
680}
681
682void VBoxVMSettingsHD::putBackTo()
683{
684 CSATAController ctl = mMachine.GetSATAController();
685 if (!ctl.isNull())
686 ctl.SetEnabled (mSATACheck->isChecked());
687
688 /* Detach all attached Hard Disks */
689 CHardDisk2AttachmentVector vec = mMachine.GetHardDisk2Attachments();
690 for (int i = 0; i < vec.size(); ++ i)
691 {
692 CHardDisk2Attachment hda = vec [i];
693 mMachine.DetachHardDisk2 (hda.GetBus(), hda.GetChannel(), hda.GetDevice());
694
695 /* [dsen] check this */
696 if (!mMachine.isOk())
697 vboxProblem().cannotDetachHardDisk (this, mMachine,
698 vboxGlobal().getMedium (CMedium (hda.GetHardDisk())).location(),
699 hda.GetBus(), hda.GetChannel(), hda.GetDevice());
700 }
701
702 /* Attach all listed Hard Disks */
703 LONG maxSATAPort = 1;
704 QList <Attachment> list (mModel->fullUsedList());
705 for (int i = 0; i < list.size(); ++ i)
706 {
707 if (list [i].slot.bus == KStorageBus_SATA)
708 maxSATAPort = maxSATAPort < (list [i].slot.channel + 1) ?
709 (list [i].slot.channel + 1) : maxSATAPort;
710 mMachine.AttachHardDisk2 (list [i].disk.id,
711 list [i].slot.bus, list [i].slot.channel, list [i].slot.device);
712
713 /* [dsen] check this */
714 if (!mMachine.isOk())
715 vboxProblem().cannotAttachHardDisk (this, mMachine,
716 vboxGlobal().getMedium (CMedium (vboxGlobal().virtualBox()
717 .GetHardDisk2 (list [i].disk.id))).location(),
718 list [i].slot.bus, list [i].slot.channel, list [i].slot.device);
719 }
720
721 if (!ctl.isNull())
722 ctl.SetPortCount (maxSATAPort);
723}
724
725void VBoxVMSettingsHD::setValidator (QIWidgetValidator *aVal)
726{
727 mValidator = aVal;
728 connect (mModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
729 mValidator, SLOT (revalidate()));
730}
731
732bool VBoxVMSettingsHD::revalidate (QString &aWarning, QString &)
733{
734 QList <SlotValue> slotList (mModel->usedSlotsList());
735 QList <DiskValue> diskList (mModel->usedDisksList());
736 for (int i = 0; i < diskList.size(); ++ i)
737 {
738 /* Check for emptiness */
739 if (diskList [i].id.isNull())
740 {
741 aWarning = tr ("No hard disk is selected for <i>%1</i>")
742 .arg (slotList [i].name);
743 break;
744 }
745
746 /* Check for coincidence */
747 if (diskList.count (diskList [i]) > 1)
748 {
749 int first = diskList.indexOf (diskList [i]);
750 int second = diskList.indexOf (diskList [i], first + 1);
751 Assert (first != -1 && second != -1);
752 aWarning = tr ("<i>%1</i> uses the hard disk that is "
753 "already attached to <i>%2</i>")
754 .arg (slotList [second].name,
755 slotList [first].name);
756 break;
757 }
758 }
759
760 return aWarning.isNull();
761}
762
763void VBoxVMSettingsHD::setOrderAfter (QWidget *aWidget)
764{
765 setTabOrder (aWidget, mSATACheck);
766 setTabOrder (mSATACheck, mTwAts);
767 setTabOrder (mTwAts, mShowDiffsCheck);
768}
769
770void VBoxVMSettingsHD::retranslateUi()
771{
772 /* Translate uic generated strings */
773 Ui::VBoxVMSettingsHD::retranslateUi (this);
774
775 mNewAction->setText (tr ("&Add Attachment"));
776 mDelAction->setText (tr ("&Remove Attachment"));
777 mVdmAction->setText (tr ("&Select Hard Disk"));
778
779 mNewAction->setToolTip (mNewAction->text().remove ('&') +
780 QString (" (%1)").arg (mNewAction->shortcut().toString()));
781 mDelAction->setToolTip (mDelAction->text().remove ('&') +
782 QString (" (%1)").arg (mDelAction->shortcut().toString()));
783 mVdmAction->setToolTip (mVdmAction->text().remove ('&') +
784 QString (" (%1)").arg (mVdmAction->shortcut().toString()));
785
786 mNewAction->setWhatsThis (tr ("Adds a new hard disk attachment."));
787 mDelAction->setWhatsThis (tr ("Removes the highlighted hard disk attachment."));
788 mVdmAction->setWhatsThis (tr ("Invokes the Virtual Media Manager to select "
789 "a hard disk to attach to the currently "
790 "highlighted slot."));
791}
792
793void VBoxVMSettingsHD::addAttachment()
794{
795 /* Temporary disable corresponding action now to prevent calling it again
796 * before it will be disabled by current-changed processing. This can
797 * happens if the user just pressed & hold the shortcut combination. */
798 mNewAction->setEnabled (false);
799
800 QUuid newId;
801
802 { /* Clear the focus */
803 FocusGuardBlock guard (mTwAts);
804
805 bool uniqueDiskSelected = false;
806 HDSettings *hds = HDSettings::instance();
807
808 { /* Add new item with default values */
809 SlotValue slot (hds->slotsList (SlotValue(), true) [0]);
810 DiskValue disk;
811 uniqueDiskSelected = hds->tryToChooseUniqueDisk (disk);
812 mModel->addItem (slot, disk);
813 } /* Add new item with default values */
814
815 /* If there are not enough unique disks */
816 if (!uniqueDiskSelected)
817 {
818 /* Ask the user for method to add new disk */
819 int confirm = vboxProblem().confirmRunNewHDWzdOrVDM (this);
820 newId = confirm == QIMessageBox::Yes ? getWithNewHDWizard() :
821 confirm == QIMessageBox::No ? getWithMediaManager() : QUuid();
822 }
823 } /* Clear the focus */
824
825 /* Set the right column of new index to be the current */
826 mTwAts->setCurrentIndex (mModel->index (mModel->rowCount() - 2, 1));
827
828 if (!newId.isNull())
829 {
830 /* Compose & apply resulting disk */
831 QVariant newValue;
832 newValue.setValue (DiskValue (newId));
833 mModel->setData (mTwAts->currentIndex(), newValue);
834 }
835
836 /* Validate if possible */
837 if (mValidator)
838 mValidator->revalidate();
839 emit hdChanged();
840}
841
842void VBoxVMSettingsHD::delAttachment()
843{
844 Assert (mTwAts->currentIndex().isValid());
845
846 /* Temporary disable corresponding action now to prevent calling it again
847 * before it will be disabled by current-changed processing. This can
848 * happens if the user just pressed & hold the shortcut combination. */
849 mDelAction->setEnabled (false);
850
851 /* Clear the focus */
852 FocusGuardBlock guard (mTwAts);
853
854 /* Storing current attributes */
855 int row = mTwAts->currentIndex().row();
856 int col = mTwAts->currentIndex().column();
857
858 /* Erase current index */
859 mTwAts->setCurrentIndex (QModelIndex());
860
861 /* Calculate new current index */
862 int newRow = row < mModel->rowCount() - 2 ? row :
863 row > 0 ? row - 1 : -1;
864 QModelIndex next = newRow == -1 ? mModel->index (0, col) :
865 mModel->index (newRow, col);
866
867 /* Delete current index */
868 mModel->delItem (row);
869
870 /* Set the new index to be the current */
871 mTwAts->setCurrentIndex (next);
872 updateActions (next);
873
874 if (mValidator)
875 mValidator->revalidate();
876 emit hdChanged();
877}
878
879void VBoxVMSettingsHD::showMediaManager()
880{
881 Assert (mTwAts->currentIndex().isValid());
882
883 /* Clear the focus */
884 FocusGuardBlock guard (mTwAts);
885
886 DiskValue current (mModel->data (mTwAts->currentIndex(), Qt::EditRole)
887 .value <DiskValue>());
888
889 QUuid id = getWithMediaManager (current.id);
890
891 if (!id.isNull())
892 {
893 /* Compose & apply resulting disk */
894 QVariant newValue;
895 newValue.setValue (DiskValue (id));
896 mModel->setData (mTwAts->currentIndex(), newValue);
897 }
898}
899
900void VBoxVMSettingsHD::updateActions (const QModelIndex& /* aIndex */)
901{
902 mNewAction->setEnabled (mModel->rowCount() - 1 <
903 HDSettings::instance()->slotsList().count());
904 mDelAction->setEnabled (mTwAts->currentIndex().row() != mModel->rowCount() - 1);
905 mVdmAction->setEnabled (mTwAts->currentIndex().row() != mModel->rowCount() - 1 &&
906 mTwAts->currentIndex().column() == 1);
907}
908
909void VBoxVMSettingsHD::onSATACheckToggled (int aState)
910{
911 if (aState == Qt::Unchecked)
912 {
913 /* Search the list for at least one SATA port in */
914 QList <SlotValue> list (mModel->usedSlotsList());
915 int firstSataPort = 0;
916 for (; firstSataPort < list.size(); ++ firstSataPort)
917 if (list [firstSataPort].bus == KStorageBus_SATA)
918 break;
919
920 /* If list contains at least one SATA port */
921 if (firstSataPort < list.size())
922 {
923 if (vboxProblem().confirmDetachSATASlots (this) != QIMessageBox::Ok)
924 {
925 /* Switch check-box back to "Qt::Checked" */
926 mSATACheck->blockSignals (true);
927 mSATACheck->setCheckState (Qt::Checked);
928 mSATACheck->blockSignals (false);
929 return;
930 }
931 else
932 {
933 /* Delete SATA items */
934 mModel->removeSata();
935
936 /* Set column #1 of first index to be the current */
937 mTwAts->setCurrentIndex (mModel->index (0, 1));
938
939 if (mValidator)
940 mValidator->revalidate();
941 }
942 }
943 }
944
945 HDSettings::instance()->setSataCount (aState == Qt::Checked ?
946 SATAPortsCount : 0);
947 updateActions (mTwAts->currentIndex());
948}
949
950void VBoxVMSettingsHD::onShowDiffsCheckToggled (int aState)
951{
952 HDSettings::instance()->setShowDiffs (aState == Qt::Checked);
953}
954
955bool VBoxVMSettingsHD::eventFilter (QObject *aObject, QEvent *aEvent)
956{
957 if (!aObject->isWidgetType())
958 return QWidget::eventFilter (aObject, aEvent);
959
960 QWidget *widget = static_cast <QWidget*> (aObject);
961 if (widget->inherits ("SlotEditor") ||
962 widget->inherits ("DiskEditor"))
963 {
964 if (aEvent->type() == QEvent::KeyPress)
965 {
966 QKeyEvent *e = static_cast <QKeyEvent*> (aEvent);
967 QModelIndex cur = mTwAts->currentIndex();
968 switch (e->key())
969 {
970 case Qt::Key_Up:
971 {
972 if (cur.row() > 0)
973 mTwAts->setCurrentIndex (mModel->index (cur.row() - 1,
974 cur.column()));
975 return true;
976 }
977 case Qt::Key_Down:
978 {
979 if (cur.row() < mModel->rowCount() - 1)
980 mTwAts->setCurrentIndex (mModel->index (cur.row() + 1,
981 cur.column()));
982 return true;
983 }
984 case Qt::Key_Right:
985 {
986 if (cur.column() == 0)
987 mTwAts->setCurrentIndex (mModel->index (cur.row(), 1));
988 return true;
989 }
990 case Qt::Key_Left:
991 {
992 if (cur.column() == 1)
993 mTwAts->setCurrentIndex (mModel->index (cur.row(), 0));
994 return true;
995 }
996 case Qt::Key_Tab:
997 {
998 focusNextPrevChild (true);
999 return true;
1000 }
1001 case Qt::Key_Backtab:
1002 {
1003 /* Due to table on getting focus back from the child
1004 * put it instantly to this child again, make a hack
1005 * to put focus to the real previous owner. */
1006 mSATACheck->setFocus();
1007 return true;
1008 }
1009 default:
1010 break;
1011 }
1012 } else
1013 if (aEvent->type() == QEvent::WindowDeactivate)
1014 {
1015 /* Store focus state if it is on temporary editor. */
1016 if (widget->hasFocus())
1017 mWasTableSelected = true;
1018 }
1019 } else
1020 if (widget == mTwAts->viewport() &&
1021 aEvent->type() == QEvent::MouseButtonDblClick)
1022 {
1023 QMouseEvent *e = static_cast <QMouseEvent*> (aEvent);
1024 QModelIndex index = mTwAts->indexAt (e->pos());
1025 if (mNewAction->isEnabled() &&
1026 (index.row() == mModel->rowCount() - 1 || !index.isValid()))
1027 addAttachment();
1028 } else
1029 if (aEvent->type() == QEvent::WindowActivate)
1030 {
1031 if (mWasTableSelected)
1032 {
1033 /* Restore focus state if it was on temporary editor. */
1034 mWasTableSelected = false;
1035 mTwAts->setFocus();
1036 }
1037 }
1038
1039 return QWidget::eventFilter (aObject, aEvent);
1040}
1041
1042void VBoxVMSettingsHD::showEvent (QShowEvent *aEvent)
1043{
1044 QWidget::showEvent (aEvent);
1045
1046 if (mPolished)
1047 return;
1048 mPolished = true;
1049
1050 /* Some delayed polishing */
1051 mTwAts->horizontalHeader()->resizeSection (0,
1052 style()->pixelMetric (QStyle::PM_ScrollBarExtent, 0, this) +
1053 maxNameLength() + 9 * 2 /* 2 margins */);
1054
1055 /* Activate edit triggers only now to avoid influencing
1056 * HD Attachments table during data loading. */
1057 mTwAts->setEditTriggers (QAbstractItemView::CurrentChanged |
1058 QAbstractItemView::SelectedClicked |
1059 QAbstractItemView::EditKeyPressed);
1060
1061 /* That little hack allows avoid one of qt4 children focusing bug */
1062 QWidget *current = QApplication::focusWidget();
1063 mTwAts->setFocus (Qt::TabFocusReason);
1064 if (current)
1065 current->setFocus (Qt::TabFocusReason);
1066}
1067
1068QUuid VBoxVMSettingsHD::getWithMediaManager (const QUuid &aInitialId)
1069{
1070 /* Run Media Manager */
1071 VBoxMediaManagerDlg dlg (this);
1072 dlg.setup (VBoxDefs::MediaType_HardDisk,
1073 true /* do select? */,
1074 false /* do refresh? */,
1075 mMachine,
1076 aInitialId,
1077 HDSettings::instance()->showDiffs());
1078
1079 return dlg.exec() == QDialog::Accepted ? dlg.selectedId() : QUuid();
1080}
1081
1082QUuid VBoxVMSettingsHD::getWithNewHDWizard()
1083{
1084 /* Run New HD Wizard */
1085 VBoxNewHDWzd dlg (this);
1086
1087 return dlg.exec() == QDialog::Accepted ? dlg.hardDisk().GetId() : QUuid();
1088}
1089
1090int VBoxVMSettingsHD::maxNameLength() const
1091{
1092 QList <SlotValue> slts (HDSettings::instance()->slotsList());
1093 int nameLength = 0;
1094 for (int i = 0; i < slts.size(); ++ i)
1095 {
1096 int length = mTwAts->fontMetrics().width (slts [i].name);
1097 nameLength = length > nameLength ? length : nameLength;
1098 }
1099 return nameLength;
1100}
1101
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette