VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/selector/VBoxSnapshotsWgt.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: 29.4 KB
Line 
1/* $Id: VBoxSnapshotsWgt.cpp 34983 2010-12-13 10:14:08Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt4 GUI ("VirtualBox"):
5 * VBoxSnapshotsWgt 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/* Local includes */
24#include "UIIconPool.h"
25#include "VBoxProblemReporter.h"
26#include "VBoxSnapshotDetailsDlg.h"
27#include "VBoxSnapshotsWgt.h"
28#include "VBoxTakeSnapshotDlg.h"
29#include "UIToolBar.h"
30#include "UIVirtualBoxEventHandler.h"
31
32/* Global includes */
33#include <QDateTime>
34#include <QHeaderView>
35#include <QMenu>
36#include <QScrollBar>
37#include <QWindowsStyle>
38
39#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
40
41/**
42 * QTreeWidgetItem subclass for snapshots items
43 */
44class SnapshotWgtItem : public QTreeWidgetItem
45{
46public:
47
48 enum { ItemType = QTreeWidgetItem::UserType + 1 };
49
50 /* Normal snapshot item (child of tree-widget) */
51 SnapshotWgtItem (QTreeWidget *aTreeWidget, const CSnapshot &aSnapshot)
52 : QTreeWidgetItem (aTreeWidget, ItemType)
53 , mIsCurrentState (false)
54 , mSnapshot (aSnapshot)
55 {
56 }
57
58 /* Normal snapshot item (child of tree-widget-item) */
59 SnapshotWgtItem (QTreeWidgetItem *aRootItem, const CSnapshot &aSnapshot)
60 : QTreeWidgetItem (aRootItem, ItemType)
61 , mIsCurrentState (false)
62 , mSnapshot (aSnapshot)
63 {
64 }
65
66 /* Current state item (child of tree-widget) */
67 SnapshotWgtItem (QTreeWidget *aTreeWidget, const CMachine &aMachine)
68 : QTreeWidgetItem (aTreeWidget, ItemType)
69 , mIsCurrentState (true)
70 , mMachine (aMachine)
71 {
72 updateCurrentState (mMachine.GetState());
73 }
74
75 /* Current state item (child of tree-widget-item) */
76 SnapshotWgtItem (QTreeWidgetItem *aRootItem, const CMachine &aMachine)
77 : QTreeWidgetItem (aRootItem, ItemType)
78 , mIsCurrentState (true)
79 , mMachine (aMachine)
80 {
81 updateCurrentState (mMachine.GetState());
82 }
83
84 QVariant data (int aColumn, int aRole) const
85 {
86 switch (aRole)
87 {
88 case Qt::DisplayRole:
89 return mIsCurrentState ? QTreeWidgetItem::data (aColumn, aRole) : QVariant (QString ("%1%2")
90 .arg (QTreeWidgetItem::data (aColumn, Qt::DisplayRole).toString())
91 .arg (QTreeWidgetItem::data (aColumn, Qt::UserRole).toString()));
92 default:
93 break;
94 }
95 return QTreeWidgetItem::data (aColumn, aRole);
96 }
97
98 QString text (int aColumn) const
99 {
100 return QTreeWidgetItem::data (aColumn, Qt::DisplayRole).toString();
101 }
102
103 bool isCurrentStateItem() const
104 {
105 return mSnapshot.isNull();
106 }
107
108 int level()
109 {
110 QTreeWidgetItem *item = this;
111 int result = 0;
112 while (item->parent())
113 {
114 ++ result;
115 item = item->parent();
116 }
117 return result;
118 }
119
120 bool bold() const { return font (0).bold(); }
121 void setBold (bool aBold)
122 {
123 QFont myFont = font (0);
124 myFont.setBold (aBold);
125 setFont (0, myFont);
126 adjustText();
127 }
128
129 bool italic() const { return font (0).italic(); }
130 void setItalic (bool aItalic)
131 {
132 QFont myFont = font (0);
133 myFont.setItalic (aItalic);
134 setFont (0, myFont);
135 adjustText();
136 }
137
138 CSnapshot snapshot() const { return mSnapshot; }
139 QString snapshotId() const { return mId; }
140
141 void recache()
142 {
143 if (mIsCurrentState)
144 {
145 Assert (!mMachine.isNull());
146 mCurStateModified = mMachine.GetCurrentStateModified();
147 setText (0, mCurStateModified ?
148 VBoxSnapshotsWgt::tr ("Current State (changed)", "Current State (Modified)") :
149 VBoxSnapshotsWgt::tr ("Current State", "Current State (Unmodified)"));
150 mDesc = mCurStateModified ?
151 VBoxSnapshotsWgt::tr ("The current state differs from the state stored in the current snapshot") :
152 parent() != 0 ?
153 VBoxSnapshotsWgt::tr ("The current state is identical to the state stored in the current snapshot") :
154 QString::null;
155 }
156 else
157 {
158 Assert (!mSnapshot.isNull());
159 mId = mSnapshot.GetId();
160 setText (0, mSnapshot.GetName());
161 mOnline = mSnapshot.GetOnline();
162 setIcon (0, vboxGlobal().snapshotIcon (mOnline));
163 mDesc = mSnapshot.GetDescription();
164 mTimestamp.setTime_t (mSnapshot.GetTimeStamp() / 1000);
165 mCurStateModified = false;
166 }
167 adjustText();
168 recacheToolTip();
169 }
170
171 KMachineState getCurrentState()
172 {
173 if (mMachine.isNull())
174 return KMachineState_Null;
175
176 return mMachineState;
177 }
178
179 void updateCurrentState (KMachineState aState)
180 {
181 if (mMachine.isNull())
182 return;
183
184 setIcon (0, vboxGlobal().toIcon (aState));
185 mMachineState = aState;
186 mTimestamp.setTime_t (mMachine.GetLastStateChange() / 1000);
187 }
188
189 SnapshotAgeFormat updateAge()
190 {
191 QString age;
192
193 /* Age: [date time|%1d ago|%1h ago|%1min ago|%1sec ago] */
194 SnapshotAgeFormat ageFormat;
195 if (mTimestamp.daysTo (QDateTime::currentDateTime()) > 30)
196 {
197 age = VBoxSnapshotsWgt::tr (" (%1)").arg (mTimestamp.toString (Qt::LocalDate));
198 ageFormat = AgeMax;
199 }
200 else if (mTimestamp.secsTo (QDateTime::currentDateTime()) > 60 * 60 * 24)
201 {
202 age = VBoxSnapshotsWgt::tr (" (%1 ago)").arg(VBoxGlobal::daysToString(mTimestamp.secsTo (QDateTime::currentDateTime()) / 60 / 60 / 24));
203 ageFormat = AgeInDays;
204 }
205 else if (mTimestamp.secsTo (QDateTime::currentDateTime()) > 60 * 60)
206 {
207 age = VBoxSnapshotsWgt::tr (" (%1 ago)").arg(VBoxGlobal::hoursToString(mTimestamp.secsTo (QDateTime::currentDateTime()) / 60 / 60));
208 ageFormat = AgeInHours;
209 }
210 else if (mTimestamp.secsTo (QDateTime::currentDateTime()) > 60)
211 {
212 age = VBoxSnapshotsWgt::tr (" (%1 ago)").arg(VBoxGlobal::minutesToString(mTimestamp.secsTo (QDateTime::currentDateTime()) / 60));
213 ageFormat = AgeInMinutes;
214 }
215 else
216 {
217 age = VBoxSnapshotsWgt::tr (" (%1 ago)").arg(VBoxGlobal::secondsToString(mTimestamp.secsTo (QDateTime::currentDateTime())));
218 ageFormat = AgeInSeconds;
219 }
220
221 /* Update data */
222 setData (0, Qt::UserRole, age);
223
224 return ageFormat;
225 }
226
227private:
228
229 void adjustText()
230 {
231 if (!treeWidget()) return; /* only for initialised items */
232 QFontMetrics metrics (font (0));
233 int hei0 = (metrics.height() > 16 ?
234 metrics.height() /* text */ : 16 /* icon */) +
235 2 * 2 /* 2 pixel per margin */;
236 int wid0 = metrics.width (text (0)) /* text */ +
237 treeWidget()->indentation() /* indent */ +
238 16 /* icon */;
239 setSizeHint (0, QSize (wid0, hei0));
240 }
241
242 void recacheToolTip()
243 {
244 QString name = text (0);
245
246 bool dateTimeToday = mTimestamp.date() == QDate::currentDate();
247 QString dateTime = dateTimeToday ?
248 mTimestamp.time().toString (Qt::LocalDate) :
249 mTimestamp.toString (Qt::LocalDate);
250
251 QString details;
252
253 if (!mSnapshot.isNull())
254 {
255 /* The current snapshot is always bold */
256 if (bold())
257 details = VBoxSnapshotsWgt::tr (" (current, ", "Snapshot details");
258 else
259 details = " (";
260 details += mOnline ? VBoxSnapshotsWgt::tr ("online)", "Snapshot details")
261 : VBoxSnapshotsWgt::tr ("offline)", "Snapshot details");
262
263 if (dateTimeToday)
264 dateTime = VBoxSnapshotsWgt::tr ("Taken at %1", "Snapshot (time)").arg (dateTime);
265 else
266 dateTime = VBoxSnapshotsWgt::tr ("Taken on %1", "Snapshot (date + time)").arg (dateTime);
267 }
268 else
269 {
270 dateTime = VBoxSnapshotsWgt::tr ("%1 since %2", "Current State (time or date + time)")
271 .arg (vboxGlobal().toString (mMachineState)).arg (dateTime);
272 }
273
274 QString toolTip = QString ("<nobr><b>%1</b>%2</nobr><br><nobr>%3</nobr>")
275 .arg (name) .arg (details).arg (dateTime);
276
277 if (!mDesc.isEmpty())
278 toolTip += "<hr>" + mDesc;
279
280 setToolTip (0, toolTip);
281 }
282
283 bool mIsCurrentState;
284
285 CSnapshot mSnapshot;
286 CMachine mMachine;
287
288 QString mId;
289 bool mOnline;
290 QString mDesc;
291 QDateTime mTimestamp;
292
293 bool mCurStateModified;
294 KMachineState mMachineState;
295};
296
297/**
298 * Simple guard block to prevent cyclic call caused by:
299 * changing tree-widget item content (rename) leads to snapshot update &
300 * snapshot update leads to changing tree-widget item content.
301 */
302class SnapshotEditBlocker
303{
304public:
305
306 SnapshotEditBlocker (bool &aProtector)
307 : mProtector (aProtector)
308 {
309 mProtector = true;
310 }
311
312 ~SnapshotEditBlocker()
313 {
314 mProtector = false;
315 }
316
317private:
318
319 bool &mProtector;
320};
321
322VBoxSnapshotsWgt::VBoxSnapshotsWgt (QWidget *aParent)
323 : QIWithRetranslateUI <QWidget> (aParent)
324 , mCurSnapshotItem (0)
325 , mEditProtector (false)
326 , mSnapshotActionGroup (new QActionGroup (this))
327 , mCurStateActionGroup (new QActionGroup (this))
328 , mRestoreSnapshotAction (new QAction (mSnapshotActionGroup))
329 , mDeleteSnapshotAction (new QAction (mSnapshotActionGroup))
330 , mShowSnapshotDetailsAction (new QAction (mSnapshotActionGroup))
331 , mTakeSnapshotAction (new QAction (mCurStateActionGroup))
332{
333 /* Apply UI decorations */
334 Ui::VBoxSnapshotsWgt::setupUi (this);
335
336 mTreeWidget->header()->hide();
337
338 /* The snapshots widget is not very useful if there are a lot
339 * of snapshots in a tree and the current Qt style decides not
340 * to draw lines (branches) between the snapshot nodes; it is
341 * then often unclear which snapshot is a child of another.
342 * So on platforms whose styles do not normally draw branches,
343 * we use QWindowsStyle which is present on every platform and
344 * draws required thing like we want. */
345// #if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
346 QWindowsStyle *treeWidgetStyle = new QWindowsStyle;
347 mTreeWidget->setStyle (treeWidgetStyle);
348 connect (mTreeWidget, SIGNAL (destroyed (QObject *)), treeWidgetStyle, SLOT (deleteLater()));
349// #endif
350
351 /* ToolBar creation */
352 UIToolBar *toolBar = new UIToolBar (this);
353 toolBar->setUsesTextLabel (false);
354 toolBar->setIconSize (QSize (22, 22));
355 toolBar->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed);
356
357 toolBar->addAction (mTakeSnapshotAction);
358 toolBar->addSeparator();
359 toolBar->addAction (mRestoreSnapshotAction);
360 toolBar->addAction (mDeleteSnapshotAction);
361 toolBar->addSeparator();
362 toolBar->addAction (mShowSnapshotDetailsAction);
363
364 ((QVBoxLayout*)layout())->insertWidget (0, toolBar);
365
366 /* Setup actions */
367 mRestoreSnapshotAction->setIcon(UIIconPool::iconSetFull(
368 QSize (22, 22), QSize (16, 16),
369 ":/discard_cur_state_22px.png", ":/discard_cur_state_16px.png", // TODO: Update Icons!
370 ":/discard_cur_state_dis_22px.png", ":/discard_cur_state_dis_16px.png")); // TODO: Update Icons!
371 mDeleteSnapshotAction->setIcon(UIIconPool::iconSetFull(
372 QSize (22, 22), QSize (16, 16),
373 ":/delete_snapshot_22px.png", ":/delete_snapshot_16px.png",
374 ":/delete_snapshot_dis_22px.png", ":/delete_snapshot_dis_16px.png"));
375 mShowSnapshotDetailsAction->setIcon(UIIconPool::iconSetFull(
376 QSize (22, 22), QSize (16, 16),
377 ":/show_snapshot_details_22px.png", ":/show_snapshot_details_16px.png",
378 ":/show_snapshot_details_dis_22px.png", ":/show_snapshot_details_dis_16px.png"));
379 mTakeSnapshotAction->setIcon(UIIconPool::iconSetFull(
380 QSize (22, 22), QSize (16, 16),
381 ":/take_snapshot_22px.png", ":/take_snapshot_16px.png",
382 ":/take_snapshot_dis_22px.png", ":/take_snapshot_dis_16px.png"));
383
384 mRestoreSnapshotAction->setShortcut (QString ("Ctrl+Shift+R"));
385 mDeleteSnapshotAction->setShortcut (QString ("Ctrl+Shift+D"));
386 mShowSnapshotDetailsAction->setShortcut (QString ("Ctrl+Space"));
387 mTakeSnapshotAction->setShortcut (QString ("Ctrl+Shift+S"));
388
389 mAgeUpdateTimer.setSingleShot (true);
390
391 /* Setup connections */
392 connect (mTreeWidget, SIGNAL (currentItemChanged (QTreeWidgetItem*, QTreeWidgetItem*)),
393 this, SLOT (onCurrentChanged (QTreeWidgetItem*)));
394 connect (mTreeWidget, SIGNAL (customContextMenuRequested (const QPoint&)),
395 this, SLOT (onContextMenuRequested (const QPoint&)));
396 connect (mTreeWidget, SIGNAL (itemChanged (QTreeWidgetItem*, int)),
397 this, SLOT (onItemChanged (QTreeWidgetItem*)));
398
399 connect (mRestoreSnapshotAction, SIGNAL (triggered()), this, SLOT (restoreSnapshot()));
400 connect (mDeleteSnapshotAction, SIGNAL (triggered()), this, SLOT (deleteSnapshot()));
401 connect (mShowSnapshotDetailsAction, SIGNAL (triggered()), this, SLOT (showSnapshotDetails()));
402 connect (mTakeSnapshotAction, SIGNAL (triggered()), this, SLOT (takeSnapshot()));
403
404 connect (gVBoxEvents, SIGNAL(sigMachineDataChange(QString)),
405 this, SLOT(machineDataChanged(QString)));
406 connect (gVBoxEvents, SIGNAL(sigMachineStateChange(QString, KMachineState)),
407 this, SLOT(machineStateChanged(QString, KMachineState)));
408 connect (gVBoxEvents, SIGNAL(sigSessionStateChange(QString, KSessionState)),
409 this, SLOT(sessionStateChanged(QString, KSessionState)));
410
411 connect (&mAgeUpdateTimer, SIGNAL (timeout()), this, SLOT (updateSnapshotsAge()));
412
413 retranslateUi();
414}
415
416void VBoxSnapshotsWgt::setMachine (const CMachine &aMachine)
417{
418 mMachine = aMachine;
419
420 if (aMachine.isNull())
421 {
422 mMachineId = QString::null;
423 mSessionState = KSessionState_Null;
424 }
425 else
426 {
427 mMachineId = aMachine.GetId();
428 mSessionState = aMachine.GetSessionState();
429 }
430
431 refreshAll();
432}
433
434
435void VBoxSnapshotsWgt::onCurrentChanged (QTreeWidgetItem *aItem)
436{
437 /* Make the selected item visible */
438 SnapshotWgtItem *item = aItem ? static_cast <SnapshotWgtItem*> (aItem) : 0;
439 if (item)
440 {
441 mTreeWidget->horizontalScrollBar()->setValue (0);
442 mTreeWidget->scrollToItem (item);
443 mTreeWidget->horizontalScrollBar()->setValue (mTreeWidget->indentation() * item->level());
444 }
445
446 /* Whether another direct session is open or not */
447 bool busy = mSessionState != KSessionState_Unlocked;
448
449 /* Machine state of the current state item */
450 KMachineState s = KMachineState_Null;
451 if (curStateItem())
452 s = curStateItem()->getCurrentState();
453
454 /* Whether taking or deleting snapshots is possible right now */
455 bool canTakeDeleteSnapshot = !busy
456 || s == KMachineState_PoweredOff
457 || s == KMachineState_Saved
458 || s == KMachineState_Aborted
459 || s == KMachineState_Running
460 || s == KMachineState_Paused;
461
462 /* Enable/disable restoring snapshot */
463 mRestoreSnapshotAction->setEnabled (!busy && mCurSnapshotItem && item && !item->isCurrentStateItem());
464
465 /* Enable/disable deleting snapshot */
466 mDeleteSnapshotAction->setEnabled ( canTakeDeleteSnapshot
467 && mCurSnapshotItem && item && !item->isCurrentStateItem());
468
469 /* Enable/disable the details action regardless of the session state */
470 mShowSnapshotDetailsAction->setEnabled (mCurSnapshotItem && item && !item->isCurrentStateItem());
471
472 /* Enable/disable taking snapshots */
473 mTakeSnapshotAction->setEnabled ( ( canTakeDeleteSnapshot
474 && mCurSnapshotItem && item && item->isCurrentStateItem())
475 || (item && !mCurSnapshotItem));
476}
477
478void VBoxSnapshotsWgt::onContextMenuRequested (const QPoint &aPoint)
479{
480 QTreeWidgetItem *item = mTreeWidget->itemAt (aPoint);
481 SnapshotWgtItem *snapshotItem = item ? static_cast <SnapshotWgtItem*> (item) : 0;
482 if (!snapshotItem)
483 return;
484
485 QMenu menu;
486
487 if (mCurSnapshotItem && !snapshotItem->isCurrentStateItem())
488 {
489 menu.addAction (mRestoreSnapshotAction);
490 menu.addAction (mDeleteSnapshotAction);
491 menu.addSeparator();
492 menu.addAction (mShowSnapshotDetailsAction);
493 }
494 else
495 menu.addAction (mTakeSnapshotAction);
496
497 menu.exec (mTreeWidget->viewport()->mapToGlobal (aPoint));
498}
499
500void VBoxSnapshotsWgt::onItemChanged (QTreeWidgetItem *aItem)
501{
502 if (mEditProtector)
503 return;
504
505 SnapshotWgtItem *item = aItem ? static_cast <SnapshotWgtItem*> (aItem) : 0;
506
507 if (item)
508 {
509 CSnapshot snap = item->snapshotId().isNull() ? CSnapshot() : mMachine.FindSnapshot(item->snapshotId());
510 if (!snap.isNull() && snap.isOk() && snap.GetName() != item->text (0))
511 snap.SetName (item->text (0));
512 }
513}
514
515void VBoxSnapshotsWgt::restoreSnapshot()
516{
517 SnapshotWgtItem *item = !mTreeWidget->currentItem() ? 0 :
518 static_cast <SnapshotWgtItem*> (mTreeWidget->currentItem());
519 AssertReturn (item, (void) 0);
520
521 QString snapId = item->snapshotId();
522 AssertReturn (!snapId.isNull(), (void) 0);
523 CSnapshot snapshot = mMachine.FindSnapshot(snapId);
524
525 if (!vboxProblem().askAboutSnapshotRestoring (snapshot.GetName()))
526 return;
527
528 /* Open a direct session (this call will handle all errors) */
529 CSession session = vboxGlobal().openSession (mMachineId);
530 if (session.isNull())
531 return;
532
533 CConsole console = session.GetConsole();
534 CProgress progress = console.RestoreSnapshot (snapshot);
535 if (console.isOk())
536 {
537 /* Show the progress dialog */
538 vboxProblem().showModalProgressDialog (progress, mMachine.GetName(), ":/progress_snapshot_restore_90px.png",
539 vboxProblem().mainWindowShown(), true);
540
541 if (progress.GetResultCode() != 0)
542 vboxProblem().cannotRestoreSnapshot (progress, snapshot.GetName());
543 }
544 else
545 vboxProblem().cannotRestoreSnapshot (progress, snapshot.GetName());
546
547 session.UnlockMachine();
548}
549
550void VBoxSnapshotsWgt::deleteSnapshot()
551{
552 SnapshotWgtItem *item = !mTreeWidget->currentItem() ? 0 :
553 static_cast <SnapshotWgtItem*> (mTreeWidget->currentItem());
554 AssertReturn (item, (void) 0);
555
556 QString snapId = item->snapshotId();
557 AssertReturn (!snapId.isNull(), (void) 0);
558 CSnapshot snapshot = mMachine.FindSnapshot(snapId);
559
560 if (!vboxProblem().askAboutSnapshotDeleting (snapshot.GetName()))
561 return;
562
563 /** @todo check available space on the target filesystem etc etc. */
564#if 0
565 if (!vboxProblem().askAboutSnapshotDeletingFreeSpace (snapshot.GetName(),
566 "/home/juser/.VirtualBox/Machines/SampleVM/Snapshots/{01020304-0102-0102-0102-010203040506}.vdi",
567 "59 GiB",
568 "15 GiB"))
569 return;
570#endif
571
572
573 /* Open a direct session (this call will handle all errors) */
574 bool busy = mSessionState != KSessionState_Unlocked;
575 CSession session = vboxGlobal().openSession (mMachineId, busy /* aExisting */);
576 if (session.isNull())
577 return;
578
579 CConsole console = session.GetConsole();
580 CProgress progress = console.DeleteSnapshot (snapId);
581 if (console.isOk())
582 {
583 /* Show the progress dialog */
584 vboxProblem().showModalProgressDialog (progress, mMachine.GetName(), ":/progress_snapshot_discard_90px.png",
585 vboxProblem().mainWindowShown(), true);
586
587 if (progress.GetResultCode() != 0)
588 vboxProblem().cannotDeleteSnapshot (progress, snapshot.GetName());
589 }
590 else
591 vboxProblem().cannotDeleteSnapshot (console, snapshot.GetName());
592
593 session.UnlockMachine();
594}
595
596void VBoxSnapshotsWgt::showSnapshotDetails()
597{
598 SnapshotWgtItem *item = !mTreeWidget->currentItem() ? 0 :
599 static_cast <SnapshotWgtItem*> (mTreeWidget->currentItem());
600 AssertReturn (item, (void) 0);
601
602 CSnapshot snap = item->snapshot();
603 AssertReturn (!snap.isNull(), (void) 0);
604
605 CMachine snapMachine = snap.GetMachine();
606
607 VBoxSnapshotDetailsDlg dlg (this);
608 dlg.getFromSnapshot (snap);
609
610 if (dlg.exec() == QDialog::Accepted)
611 dlg.putBackToSnapshot();
612}
613
614void VBoxSnapshotsWgt::takeSnapshot()
615{
616 SnapshotWgtItem *item = !mTreeWidget->currentItem() ? 0 :
617 static_cast <SnapshotWgtItem*> (mTreeWidget->currentItem());
618 AssertReturn (item, (void) 0);
619
620 VBoxTakeSnapshotDlg dlg (this, mMachine);
621
622 QString typeId = mMachine.GetOSTypeId();
623 dlg.mLbIcon->setPixmap (vboxGlobal().vmGuestOSTypeIcon (typeId));
624
625 /* Search for the max available filter index */
626 int maxSnapShotIndex = 0;
627 QString snapShotName = tr ("Snapshot %1");
628 QRegExp regExp (QString ("^") + snapShotName.arg ("([0-9]+)") + QString ("$"));
629 QTreeWidgetItemIterator iterator (mTreeWidget);
630 while (*iterator)
631 {
632 QString snapShot = static_cast <SnapshotWgtItem*> (*iterator)->text (0);
633 int pos = regExp.indexIn (snapShot);
634 if (pos != -1)
635 maxSnapShotIndex = regExp.cap (1).toInt() > maxSnapShotIndex ?
636 regExp.cap (1).toInt() : maxSnapShotIndex;
637 ++ iterator;
638 }
639 dlg.mLeName->setText (snapShotName.arg (maxSnapShotIndex + 1));
640
641 if (dlg.exec() == QDialog::Accepted)
642 {
643 /* Open a direct session (this call will handle all errors) */
644 bool busy = mSessionState != KSessionState_Unlocked;
645 CSession session = vboxGlobal().openSession (mMachineId, busy /* aExisting */);
646 if (session.isNull())
647 return;
648
649 CConsole console = session.GetConsole();
650 CProgress progress = console.TakeSnapshot (dlg.mLeName->text().trimmed(),
651 dlg.mTeDescription->toPlainText());
652 if (console.isOk())
653 {
654 /* Show the progress dialog */
655 vboxProblem().showModalProgressDialog (progress, mMachine.GetName(), ":/progress_snapshot_create_90px.png",
656 vboxProblem().mainWindowShown(), true);
657
658 if (progress.GetResultCode() != 0)
659 vboxProblem().cannotTakeSnapshot (progress);
660 }
661 else
662 vboxProblem().cannotTakeSnapshot (console);
663
664 session.UnlockMachine();
665 }
666}
667
668
669void VBoxSnapshotsWgt::machineDataChanged(QString strId)
670{
671 SnapshotEditBlocker guardBlock (mEditProtector);
672
673 if (strId != mMachineId)
674 return;
675
676 curStateItem()->recache();
677}
678
679void VBoxSnapshotsWgt::machineStateChanged(QString strId, KMachineState state)
680{
681 SnapshotEditBlocker guardBlock (mEditProtector);
682
683 if (strId != mMachineId)
684 return;
685
686 curStateItem()->recache();
687 curStateItem()->updateCurrentState(state);
688}
689
690void VBoxSnapshotsWgt::sessionStateChanged(QString strId, KSessionState state)
691{
692 SnapshotEditBlocker guardBlock (mEditProtector);
693
694 if (strId != mMachineId)
695 return;
696
697 mSessionState = state;
698 onCurrentChanged (mTreeWidget->currentItem());
699}
700
701void VBoxSnapshotsWgt::updateSnapshotsAge()
702{
703 if (mAgeUpdateTimer.isActive())
704 mAgeUpdateTimer.stop();
705
706 SnapshotAgeFormat age = traverseSnapshotAge (mTreeWidget->invisibleRootItem());
707
708 switch (age)
709 {
710 case AgeInSeconds:
711 mAgeUpdateTimer.setInterval (5 * 1000);
712 break;
713 case AgeInMinutes:
714 mAgeUpdateTimer.setInterval (60 * 1000);
715 break;
716 case AgeInHours:
717 mAgeUpdateTimer.setInterval (60 * 60 * 1000);
718 break;
719 case AgeInDays:
720 mAgeUpdateTimer.setInterval (24 * 60 * 60 * 1000);
721 break;
722 default:
723 mAgeUpdateTimer.setInterval (0);
724 break;
725 }
726
727 if (mAgeUpdateTimer.interval() > 0)
728 mAgeUpdateTimer.start();
729}
730
731void VBoxSnapshotsWgt::retranslateUi()
732{
733 /* Translate uic generated strings */
734 Ui::VBoxSnapshotsWgt::retranslateUi (this);
735
736 mRestoreSnapshotAction->setText (tr ("&Restore Snapshot"));
737 mDeleteSnapshotAction->setText (tr ("&Delete Snapshot"));
738 mShowSnapshotDetailsAction->setText (tr ("S&how Details"));
739 mTakeSnapshotAction->setText (tr ("Take &Snapshot"));
740
741 mRestoreSnapshotAction->setStatusTip (tr ("Restore the selected snapshot of the virtual machine"));
742 mDeleteSnapshotAction->setStatusTip (tr ("Delete the selected snapshot of the virtual machine"));
743 mShowSnapshotDetailsAction->setStatusTip (tr ("Show the details of the selected snapshot"));
744 mTakeSnapshotAction->setStatusTip (tr ("Take a snapshot of the current virtual machine state"));
745
746 mRestoreSnapshotAction->setToolTip (mRestoreSnapshotAction->text().remove ('&').remove ('.') +
747 QString (" (%1)").arg (mRestoreSnapshotAction->shortcut().toString()));
748 mDeleteSnapshotAction->setToolTip (mDeleteSnapshotAction->text().remove ('&').remove ('.') +
749 QString (" (%1)").arg (mDeleteSnapshotAction->shortcut().toString()));
750 mShowSnapshotDetailsAction->setToolTip (mShowSnapshotDetailsAction->text().remove ('&').remove ('.') +
751 QString (" (%1)").arg (mShowSnapshotDetailsAction->shortcut().toString()));
752 mTakeSnapshotAction->setToolTip (mTakeSnapshotAction->text().remove ('&').remove ('.') +
753 QString (" (%1)").arg (mTakeSnapshotAction->shortcut().toString()));
754}
755
756void VBoxSnapshotsWgt::refreshAll()
757{
758 SnapshotEditBlocker guardBlock (mEditProtector);
759
760 if (mMachine.isNull())
761 {
762 onCurrentChanged();
763 return;
764 }
765
766 QString selectedItem, firstChildOfSelectedItem;
767 SnapshotWgtItem *cur = !mTreeWidget->currentItem() ? 0 :
768 static_cast <SnapshotWgtItem*> (mTreeWidget->currentItem());
769 if (cur)
770 {
771 selectedItem = cur->snapshotId();
772 if (cur->child (0))
773 firstChildOfSelectedItem = static_cast <SnapshotWgtItem*> (cur->child (0))->snapshotId();
774 }
775
776 mTreeWidget->clear();
777
778 /* Get the first snapshot */
779 if (mMachine.GetSnapshotCount() > 0)
780 {
781 CSnapshot snapshot = mMachine.FindSnapshot(QString::null);
782
783 populateSnapshots (snapshot, 0);
784 Assert (mCurSnapshotItem);
785
786 /* Add the "current state" item */
787 SnapshotWgtItem *csi = new SnapshotWgtItem (mCurSnapshotItem, mMachine);
788 csi->setBold (true);
789 csi->recache();
790
791 SnapshotWgtItem *cur = findItem (selectedItem);
792 if (cur == 0)
793 cur = findItem (firstChildOfSelectedItem);
794 if (cur == 0)
795 cur = curStateItem();
796
797 mTreeWidget->scrollToItem (cur);
798 mTreeWidget->setCurrentItem (cur);
799 onCurrentChanged (cur);
800 }
801 else
802 {
803 mCurSnapshotItem = 0;
804
805 /* Add the "current state" item */
806 SnapshotWgtItem *csi = new SnapshotWgtItem (mTreeWidget, mMachine);
807 csi->setBold (true);
808 csi->recache();
809
810 mTreeWidget->setCurrentItem (csi);
811 onCurrentChanged (csi);
812 }
813
814 /* Updating age */
815 updateSnapshotsAge();
816
817 mTreeWidget->resizeColumnToContents (0);
818}
819
820SnapshotWgtItem* VBoxSnapshotsWgt::findItem (const QString &aSnapshotId)
821{
822 QTreeWidgetItemIterator it (mTreeWidget);
823 while (*it)
824 {
825 SnapshotWgtItem *lvi = static_cast <SnapshotWgtItem*> (*it);
826 if (lvi->snapshotId() == aSnapshotId)
827 return lvi;
828 ++ it;
829 }
830
831 return 0;
832}
833
834SnapshotWgtItem *VBoxSnapshotsWgt::curStateItem()
835{
836 QTreeWidgetItem *csi = mCurSnapshotItem ?
837 mCurSnapshotItem->child (mCurSnapshotItem->childCount() - 1) :
838 mTreeWidget->invisibleRootItem()->child (0);
839 return static_cast <SnapshotWgtItem*> (csi);
840}
841
842void VBoxSnapshotsWgt::populateSnapshots (const CSnapshot &aSnapshot, QTreeWidgetItem *aItem)
843{
844 SnapshotWgtItem *item = aItem ? new SnapshotWgtItem (aItem, aSnapshot) :
845 new SnapshotWgtItem (mTreeWidget, aSnapshot);
846 item->recache();
847
848 if (mMachine.GetCurrentSnapshot().GetId() == aSnapshot.GetId())
849 {
850 item->setBold (true);
851 mCurSnapshotItem = item;
852 }
853
854 CSnapshotVector snapshots = aSnapshot.GetChildren();
855 foreach (const CSnapshot &snapshot, snapshots)
856 populateSnapshots (snapshot, item);
857
858 item->setExpanded (true);
859 item->setFlags (item->flags() | Qt::ItemIsEditable);
860}
861
862SnapshotAgeFormat VBoxSnapshotsWgt::traverseSnapshotAge (QTreeWidgetItem *aParentItem)
863{
864 SnapshotWgtItem *parentItem = aParentItem->type() == SnapshotWgtItem::ItemType ?
865 static_cast <SnapshotWgtItem*> (aParentItem) : 0;
866
867 SnapshotAgeFormat age = parentItem ? parentItem->updateAge() : AgeMax;
868 for (int i = 0; i < aParentItem->childCount(); ++ i)
869 {
870 SnapshotAgeFormat newAge = traverseSnapshotAge (aParentItem->child (i));
871 age = newAge < age ? newAge : age;
872 }
873
874 return age;
875}
876
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use