VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/snapshots/UISnapshotPane.cpp@ 82781

Last change on this file since 82781 was 79365, checked in by vboxsync, 5 years ago

Renaming VBoxGlobal to UICommon for bugref:9049 as planned.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.1 KB
Line 
1/* $Id: UISnapshotPane.cpp 79365 2019-06-26 15:57:32Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UISnapshotPane class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QAccessibleWidget>
20#include <QApplication>
21#include <QDateTime>
22#include <QHeaderView>
23#include <QIcon>
24#include <QMenu>
25#include <QPointer>
26#include <QReadWriteLock>
27#include <QScrollBar>
28#include <QTimer>
29#include <QVBoxLayout>
30#include <QWriteLocker>
31
32/* GUI includes: */
33#include "QIMessageBox.h"
34#include "QITreeWidget.h"
35#include "UIActionPoolManager.h"
36#include "UIConverter.h"
37#include "UIExtraDataManager.h"
38#include "UIIconPool.h"
39#include "UIMessageCenter.h"
40#include "UIModalWindowManager.h"
41#include "UISnapshotDetailsWidget.h"
42#include "UISnapshotPane.h"
43#include "UITakeSnapshotDialog.h"
44#include "UIToolBar.h"
45#include "UIVirtualBoxEventHandler.h"
46#include "UIWizardCloneVM.h"
47
48/* COM includes: */
49#include "CConsole.h"
50
51
52/** Snapshot tree column tags. */
53enum
54{
55 Column_Name,
56 Column_Taken,
57 Column_Max,
58};
59
60
61/** QITreeWidgetItem subclass for snapshots items. */
62class UISnapshotItem : public QITreeWidgetItem, public UIDataSnapshot
63{
64 Q_OBJECT;
65
66public:
67
68 /** Casts QTreeWidgetItem* to UISnapshotItem* if possible. */
69 static UISnapshotItem *toSnapshotItem(QTreeWidgetItem *pItem);
70 /** Casts const QTreeWidgetItem* to const UISnapshotItem* if possible. */
71 static const UISnapshotItem *toSnapshotItem(const QTreeWidgetItem *pItem);
72
73 /** Constructs normal snapshot item (child of tree-widget). */
74 UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidget *pTreeWidget, const CSnapshot &comSnapshot);
75 /** Constructs normal snapshot item (child of tree-widget-item). */
76 UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidgetItem *pRootItem, const CSnapshot &comSnapshot);
77
78 /** Constructs "current state" item (child of tree-widget). */
79 UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidget *pTreeWidget, const CMachine &comMachine);
80 /** Constructs "current state" item (child of tree-widget-item). */
81 UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidgetItem *pRootItem, const CMachine &comMachine);
82
83 /** Returns item machine. */
84 CMachine machine() const { return m_comMachine; }
85 /** Returns item snapshot. */
86 CSnapshot snapshot() const { return m_comSnapshot; }
87 /** Returns item snapshot ID. */
88 QUuid snapshotID() const { return m_uSnapshotID; }
89
90 /** Returns whether this is the "current state" item. */
91 bool isCurrentStateItem() const { return m_fCurrentStateItem; }
92
93 /** Returns whether this is the "current snapshot" item. */
94 bool isCurrentSnapshotItem() const { return m_fCurrentSnapshotItem; }
95 /** Defines whether this is the @a fCurrent snapshot item. */
96 void setCurrentSnapshotItem(bool fCurrent);
97
98 /** Calculates and returns the current item level. */
99 int level() const;
100
101 /** Recaches the item's contents. */
102 void recache();
103
104 /** Returns current machine state. */
105 KMachineState machineState() const;
106 /** Defines current machine @a enmState. */
107 void setMachineState(KMachineState enmState);
108
109 /** Updates item age. */
110 SnapshotAgeFormat updateAge();
111
112protected:
113
114 /** Recaches item tool-tip. */
115 void recacheToolTip();
116
117private:
118
119 /** Holds the pointer to the snapshot-widget this item belongs to. */
120 QPointer<UISnapshotPane> m_pSnapshotWidget;
121
122 /** Holds whether this is a "current state" item. */
123 bool m_fCurrentStateItem;
124 /** Holds whether this is a "current snapshot" item. */
125 bool m_fCurrentSnapshotItem;
126
127 /** Holds the snapshot COM wrapper. */
128 CSnapshot m_comSnapshot;
129 /** Holds the machine COM wrapper. */
130 CMachine m_comMachine;
131
132 /** Holds the "current snapshot" ID. */
133 QUuid m_uSnapshotID;
134 /** Holds whether the "current snapshot" is online one. */
135 bool m_fOnline;
136
137 /** Holds the item timestamp. */
138 QDateTime m_timestamp;
139
140 /** Holds whether the current state is modified. */
141 bool m_fCurrentStateModified;
142 /** Holds the cached machine state. */
143 KMachineState m_enmMachineState;
144};
145
146
147/** QITreeWidget subclass for snapshots items. */
148class UISnapshotTree : public QITreeWidget
149{
150 Q_OBJECT;
151
152public:
153
154 /** Constructs snapshot tree passing @a pParent to the base-class. */
155 UISnapshotTree(QWidget *pParent);
156};
157
158
159/*********************************************************************************************************************************
160* Class UISnapshotItem implementation. *
161*********************************************************************************************************************************/
162
163/* static */
164UISnapshotItem *UISnapshotItem::toSnapshotItem(QTreeWidgetItem *pItem)
165{
166 /* Get QITreeWidgetItem item first: */
167 QITreeWidgetItem *pIItem = QITreeWidgetItem::toItem(pItem);
168 if (!pIItem)
169 return 0;
170
171 /* Return casted UISnapshotItem then: */
172 return qobject_cast<UISnapshotItem*>(pIItem);
173}
174
175/* static */
176const UISnapshotItem *UISnapshotItem::toSnapshotItem(const QTreeWidgetItem *pItem)
177{
178 /* Get QITreeWidgetItem item first: */
179 const QITreeWidgetItem *pIItem = QITreeWidgetItem::toItem(pItem);
180 if (!pIItem)
181 return 0;
182
183 /* Return casted UISnapshotItem then: */
184 return qobject_cast<const UISnapshotItem*>(pIItem);
185}
186
187UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidget *pTreeWidget, const CSnapshot &comSnapshot)
188 : QITreeWidgetItem(pTreeWidget)
189 , m_pSnapshotWidget(pSnapshotWidget)
190 , m_fCurrentStateItem(false)
191 , m_fCurrentSnapshotItem(false)
192 , m_comSnapshot(comSnapshot)
193{
194}
195
196UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidgetItem *pRootItem, const CSnapshot &comSnapshot)
197 : QITreeWidgetItem(pRootItem)
198 , m_pSnapshotWidget(pSnapshotWidget)
199 , m_fCurrentStateItem(false)
200 , m_fCurrentSnapshotItem(false)
201 , m_comSnapshot(comSnapshot)
202{
203}
204
205UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidget *pTreeWidget, const CMachine &comMachine)
206 : QITreeWidgetItem(pTreeWidget)
207 , m_pSnapshotWidget(pSnapshotWidget)
208 , m_fCurrentStateItem(true)
209 , m_fCurrentSnapshotItem(false)
210 , m_comMachine(comMachine)
211{
212 /* Set the bold font state
213 * for "current state" item: */
214 QFont myFont = font(Column_Name);
215 myFont.setBold(true);
216 setFont(Column_Name, myFont);
217
218 /* Fetch current machine state: */
219 setMachineState(m_comMachine.GetState());
220}
221
222UISnapshotItem::UISnapshotItem(UISnapshotPane *pSnapshotWidget, QITreeWidgetItem *pRootItem, const CMachine &comMachine)
223 : QITreeWidgetItem(pRootItem)
224 , m_pSnapshotWidget(pSnapshotWidget)
225 , m_fCurrentStateItem(true)
226 , m_fCurrentSnapshotItem(false)
227 , m_comMachine(comMachine)
228{
229 /* Set the bold font state
230 * for "current state" item: */
231 QFont myFont = font(Column_Name);
232 myFont.setBold(true);
233 setFont(Column_Name, myFont);
234
235 /* Fetch current machine state: */
236 setMachineState(m_comMachine.GetState());
237}
238
239int UISnapshotItem::level() const
240{
241 const QTreeWidgetItem *pItem = this;
242 int iResult = 0;
243 while (pItem->parent())
244 {
245 ++iResult;
246 pItem = pItem->parent();
247 }
248 return iResult;
249}
250
251void UISnapshotItem::setCurrentSnapshotItem(bool fCurrent)
252{
253 /* Remember the state: */
254 m_fCurrentSnapshotItem = fCurrent;
255
256 /* Set/clear the bold font state
257 * for "current snapshot" item: */
258 QFont myFont = font(Column_Name);
259 myFont.setBold(fCurrent);
260 setFont(Column_Name, myFont);
261
262 /* Update tool-tip: */
263 recacheToolTip();
264}
265
266void UISnapshotItem::recache()
267{
268 /* For "current state" item: */
269 if (m_fCurrentStateItem)
270 {
271 /* Fetch machine information: */
272 AssertReturnVoid(m_comMachine.isNotNull());
273 m_fCurrentStateModified = m_comMachine.GetCurrentStateModified();
274 m_strName = m_fCurrentStateModified
275 ? tr("Current State (changed)", "Current State (Modified)")
276 : tr("Current State", "Current State (Unmodified)");
277 setText(Column_Name, m_strName);
278 m_strDescription = m_fCurrentStateModified
279 ? tr("The current state differs from the state stored in the current snapshot")
280 : QTreeWidgetItem::parent() != 0
281 ? tr("The current state is identical to the state stored in the current snapshot")
282 : QString();
283 }
284 /* For snapshot item: */
285 else
286 {
287 /* Fetch snapshot information: */
288 AssertReturnVoid(m_comSnapshot.isNotNull());
289 m_uSnapshotID = m_comSnapshot.GetId();
290 m_strName = m_comSnapshot.GetName();
291 setText(Column_Name, m_strName);
292 m_fOnline = m_comSnapshot.GetOnline();
293 setIcon(Column_Name, *m_pSnapshotWidget->snapshotItemIcon(m_fOnline));
294 m_strDescription = m_comSnapshot.GetDescription();
295 m_timestamp.setTime_t(m_comSnapshot.GetTimeStamp() / 1000);
296 m_fCurrentStateModified = false;
297 }
298
299 /* Update tool-tip: */
300 recacheToolTip();
301}
302
303KMachineState UISnapshotItem::machineState() const
304{
305 /* Make sure machine is valid: */
306 if (m_comMachine.isNull())
307 return KMachineState_Null;
308
309 /* Return cached state: */
310 return m_enmMachineState;
311}
312
313void UISnapshotItem::setMachineState(KMachineState enmState)
314{
315 /* Make sure machine is valid: */
316 if (m_comMachine.isNull())
317 return;
318
319 /* Cache new state: */
320 m_enmMachineState = enmState;
321 /* Set corresponding icon: */
322 setIcon(Column_Name, gpConverter->toIcon(m_enmMachineState));
323 /* Update timestamp: */
324 m_timestamp.setTime_t(m_comMachine.GetLastStateChange() / 1000);
325}
326
327SnapshotAgeFormat UISnapshotItem::updateAge()
328{
329 /* Prepare age: */
330 QString strAge;
331
332 /* Age: [date time|%1d ago|%1h ago|%1min ago|%1sec ago] */
333 SnapshotAgeFormat enmAgeFormat;
334 const QDateTime now = QDateTime::currentDateTime();
335 QDateTime then = m_timestamp;
336 if (then > now)
337 then = now; /* can happen if the host time is wrong */
338 if (then.daysTo(now) > 30)
339 {
340 strAge = then.toString(Qt::LocalDate);
341 enmAgeFormat = SnapshotAgeFormat_Max;
342 }
343 else if (then.secsTo(now) > 60 * 60 * 24)
344 {
345 strAge = tr("%1 (%2 ago)", "date time (how long ago)")
346 .arg(then.toString(Qt::LocalDate), UICommon::daysToString(then.secsTo(now) / 60 / 60 / 24));
347 enmAgeFormat = SnapshotAgeFormat_InDays;
348 }
349 else if (then.secsTo(now) > 60 * 60)
350 {
351 strAge = tr("%1 (%2 ago)", "date time (how long ago)")
352 .arg(then.toString(Qt::LocalDate), UICommon::hoursToString(then.secsTo(now) / 60 / 60));
353 enmAgeFormat = SnapshotAgeFormat_InHours;
354 }
355 else if (then.secsTo(now) > 60)
356 {
357 strAge = tr("%1 (%2 ago)", "date time (how long ago)")
358 .arg(then.toString(Qt::LocalDate), UICommon::minutesToString(then.secsTo(now) / 60));
359 enmAgeFormat = SnapshotAgeFormat_InMinutes;
360 }
361 else
362 {
363 strAge = tr("%1 (%2 ago)", "date time (how long ago)")
364 .arg(then.toString(Qt::LocalDate), UICommon::secondsToString(then.secsTo(now)));
365 enmAgeFormat = SnapshotAgeFormat_InSeconds;
366 }
367
368 /* Update data: */
369 if (!m_fCurrentStateItem)
370 setText(Column_Taken, strAge);
371
372 /* Return age: */
373 return enmAgeFormat;
374}
375
376void UISnapshotItem::recacheToolTip()
377{
378 /* Is the saved date today? */
379 const bool fDateTimeToday = m_timestamp.date() == QDate::currentDate();
380
381 /* Compose date time: */
382 QString strDateTime = fDateTimeToday
383 ? m_timestamp.time().toString(Qt::LocalDate)
384 : m_timestamp.toString(Qt::LocalDate);
385
386 /* Prepare details: */
387 QString strDetails;
388
389 /* For "current state" item: */
390 if (m_fCurrentStateItem)
391 {
392 strDateTime = tr("%1 since %2", "Current State (time or date + time)")
393 .arg(gpConverter->toString(m_enmMachineState)).arg(strDateTime);
394 }
395 /* For snapshot item: */
396 else
397 {
398 /* Gather details: */
399 QStringList details;
400 if (isCurrentSnapshotItem())
401 details << tr("current", "snapshot");
402 details << (m_fOnline ? tr("online", "snapshot")
403 : tr("offline", "snapshot"));
404 strDetails = QString(" (%1)").arg(details.join(", "));
405
406 /* Add date/time information: */
407 if (fDateTimeToday)
408 strDateTime = tr("Taken at %1", "Snapshot (time)").arg(strDateTime);
409 else
410 strDateTime = tr("Taken on %1", "Snapshot (date + time)").arg(strDateTime);
411 }
412
413 /* Prepare tool-tip: */
414 QString strToolTip = QString("<nobr><b>%1</b>%2</nobr><br><nobr>%3</nobr>")
415 .arg(text(Column_Name)).arg(strDetails).arg(strDateTime);
416
417 /* Append description if any: */
418 if (!m_strDescription.isEmpty())
419 strToolTip += "<hr>" + m_strDescription;
420
421 /* Assign tool-tip finally: */
422 setToolTip(Column_Name, strToolTip);
423}
424
425
426/*********************************************************************************************************************************
427* Class UISnapshotTree implementation. *
428*********************************************************************************************************************************/
429
430UISnapshotTree::UISnapshotTree(QWidget *pParent)
431 : QITreeWidget(pParent)
432{
433 /* Configure snapshot tree: */
434 setColumnCount(Column_Max);
435 setAllColumnsShowFocus(true);
436 setAlternatingRowColors(true);
437 setExpandsOnDoubleClick(false);
438 setContextMenuPolicy(Qt::CustomContextMenu);
439 setEditTriggers( QAbstractItemView::SelectedClicked
440 | QAbstractItemView::EditKeyPressed);
441}
442
443
444/*********************************************************************************************************************************
445* Class UISnapshotPane implementation. *
446*********************************************************************************************************************************/
447
448UISnapshotPane::UISnapshotPane(UIActionPool *pActionPool, bool fShowToolbar /* = true */, QWidget *pParent /* = 0 */)
449 : QIWithRetranslateUI<QWidget>(pParent)
450 , m_pActionPool(pActionPool)
451 , m_fShowToolbar(fShowToolbar)
452 , m_enmSessionState(KSessionState_Null)
453 , m_fShapshotOperationsAllowed(false)
454 , m_pLockReadWrite(0)
455 , m_pIconSnapshotOffline(0)
456 , m_pIconSnapshotOnline(0)
457 , m_pTimerUpdateAge(0)
458 , m_pToolBar(0)
459 , m_pSnapshotTree(0)
460 , m_pCurrentSnapshotItem(0)
461 , m_pCurrentStateItem(0)
462 , m_pDetailsWidget(0)
463{
464 /* Prepare: */
465 prepare();
466}
467
468UISnapshotPane::~UISnapshotPane()
469{
470 /* Cleanup: */
471 cleanup();
472}
473
474void UISnapshotPane::setMachine(const CMachine &comMachine)
475{
476 /* Cache passed machine: */
477 m_comMachine = comMachine;
478
479 /* Cache machine details: */
480 if (m_comMachine.isNull())
481 {
482 m_uMachineId = QUuid();
483 m_enmSessionState = KSessionState_Null;
484 m_fShapshotOperationsAllowed = false;
485 }
486 else
487 {
488 m_uMachineId = comMachine.GetId();
489 m_enmSessionState = comMachine.GetSessionState();
490 m_fShapshotOperationsAllowed = gEDataManager->machineSnapshotOperationsEnabled(m_uMachineId);
491 }
492
493 /* Refresh everything: */
494 refreshAll();
495}
496
497const QIcon *UISnapshotPane::snapshotItemIcon(bool fOnline) const
498{
499 return !fOnline ? m_pIconSnapshotOffline : m_pIconSnapshotOnline;
500}
501
502bool UISnapshotPane::isCurrentStateItemSelected() const
503{
504 return m_pCurrentStateItem && m_pSnapshotTree->currentItem() == m_pCurrentStateItem;
505}
506
507void UISnapshotPane::retranslateUi()
508{
509 /* Translate snapshot tree: */
510 m_pSnapshotTree->setWhatsThis(tr("Contains the snapshot tree of the current virtual machine"));
511
512 /* Translate toolbar: */
513#ifdef VBOX_WS_MAC
514 // WORKAROUND:
515 // There is a bug in Qt Cocoa which result in showing a "more arrow" when
516 // the necessary size of the toolbar is increased. Also for some languages
517 // the with doesn't match if the text increase. So manually adjust the size
518 // after changing the text. */
519 if (m_pToolBar)
520 m_pToolBar->updateLayout();
521#endif
522
523 /* Translate snapshot tree: */
524 const QStringList fields = QStringList()
525 << tr("Name", "snapshot")
526 << tr("Taken", "snapshot");
527 m_pSnapshotTree->setHeaderLabels(fields);
528
529 /* Refresh whole the tree: */
530 refreshAll();
531}
532
533void UISnapshotPane::resizeEvent(QResizeEvent *pEvent)
534{
535 /* Call to base-class: */
536 QIWithRetranslateUI<QWidget>::resizeEvent(pEvent);
537
538 /* Adjust snapshot tree: */
539 adjustTreeWidget();
540}
541
542void UISnapshotPane::showEvent(QShowEvent *pEvent)
543{
544 /* Call to base-class: */
545 QIWithRetranslateUI<QWidget>::showEvent(pEvent);
546
547 /* Adjust snapshot tree: */
548 adjustTreeWidget();
549}
550
551void UISnapshotPane::sltHandleMachineDataChange(const QUuid &uMachineId)
552{
553 /* Make sure it's our VM: */
554 if (uMachineId != m_uMachineId)
555 return;
556
557 /* Prevent snapshot editing in the meantime: */
558 QWriteLocker locker(m_pLockReadWrite);
559
560 /* Recache "current state" item data: */
561 m_pCurrentStateItem->recache();
562
563 /* Choose current item again (to update details-widget): */
564 sltHandleCurrentItemChange();
565}
566
567void UISnapshotPane::sltHandleMachineStateChange(const QUuid &uMachineId, const KMachineState enmState)
568{
569 /* Make sure it's our VM: */
570 if (uMachineId != m_uMachineId)
571 return;
572
573 /* Prevent snapshot editing in the meantime: */
574 QWriteLocker locker(m_pLockReadWrite);
575
576 /* Recache "current state" item data and machine-state: */
577 m_pCurrentStateItem->recache();
578 m_pCurrentStateItem->setMachineState(enmState);
579}
580
581void UISnapshotPane::sltHandleSessionStateChange(const QUuid &uMachineId, const KSessionState enmState)
582{
583 /* Make sure it's our VM: */
584 if (uMachineId != m_uMachineId)
585 return;
586
587 /* Prevent snapshot editing in the meantime: */
588 QWriteLocker locker(m_pLockReadWrite);
589
590 /* Recache current session-state: */
591 m_enmSessionState = enmState;
592
593 /* Update action states: */
594 updateActionStates();
595}
596
597void UISnapshotPane::sltHandleSnapshotTake(const QUuid &uMachineId, const QUuid &uSnapshotId)
598{
599 /* Make sure it's our VM: */
600 if (uMachineId != m_uMachineId)
601 return;
602
603 LogRel(("GUI: Updating snapshot tree after TAKING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
604 uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
605
606 /* Prepare result: */
607 bool fSuccess = true;
608 {
609 /* Prevent snapshot editing in the meantime: */
610 QWriteLocker locker(m_pLockReadWrite);
611
612 /* Search for corresponding snapshot: */
613 CSnapshot comSnapshot = m_comMachine.FindSnapshot(uSnapshotId.toString());
614 fSuccess = m_comMachine.isOk() && !comSnapshot.isNull();
615
616 /* Show error message if necessary: */
617 if (!fSuccess)
618 msgCenter().cannotFindSnapshotById(m_comMachine, uSnapshotId, this);
619 else
620 {
621 /* Where will be the newly created item located? */
622 UISnapshotItem *pParentItem = 0;
623
624 /* Acquire snapshot parent: */
625 CSnapshot comParentSnapshot = comSnapshot.GetParent();
626 if (comParentSnapshot.isNotNull())
627 {
628 /* Acquire parent snapshot id: */
629 const QUuid uParentSnapshotId = comParentSnapshot.GetId();
630 fSuccess = comParentSnapshot.isOk();
631
632 /* Show error message if necessary: */
633 if (!fSuccess)
634 msgCenter().cannotAcquireSnapshotAttributes(comSnapshot, this);
635 else
636 {
637 /* Search for an existing parent-item with such id: */
638 pParentItem = findItem(uParentSnapshotId);
639 fSuccess = pParentItem;
640 }
641 }
642
643 /* Make sure this parent-item is a parent of "current state" item as well: */
644 if (fSuccess)
645 fSuccess = qobject_cast<UISnapshotItem*>(m_pCurrentStateItem->parentItem()) == pParentItem;
646 /* Make sure this parent-item is a "current snapshot" item as well: */
647 if (fSuccess)
648 fSuccess = m_pCurrentSnapshotItem == pParentItem;
649
650 /* Create new item: */
651 if (fSuccess)
652 {
653 /* Delete "current state" item first of all: */
654 UISnapshotItem *pCurrentStateItem = m_pCurrentStateItem;
655 m_pCurrentStateItem = 0;
656 delete pCurrentStateItem;
657 pCurrentStateItem = 0;
658
659 /* Create "current snapshot" item for a newly taken snapshot: */
660 if (m_pCurrentSnapshotItem)
661 m_pCurrentSnapshotItem->setCurrentSnapshotItem(false);
662 m_pCurrentSnapshotItem = pParentItem
663 ? new UISnapshotItem(this, pParentItem, comSnapshot)
664 : new UISnapshotItem(this, m_pSnapshotTree, comSnapshot);
665 /* Mark it as editable: */
666 m_pCurrentSnapshotItem->setFlags(m_pCurrentSnapshotItem->flags() | Qt::ItemIsEditable);
667 /* Mark it as current: */
668 m_pCurrentSnapshotItem->setCurrentSnapshotItem(true);
669 /* And recache it's content: */
670 m_pCurrentSnapshotItem->recache();
671
672 /* Create "current state" item as a child of "current snapshot" item: */
673 m_pCurrentStateItem = new UISnapshotItem(this, m_pCurrentSnapshotItem, m_comMachine);
674 /* Recache it's content: */
675 m_pCurrentStateItem->recache();
676 /* And choose is as current one: */
677 m_pSnapshotTree->setCurrentItem(m_pCurrentStateItem);
678 sltHandleCurrentItemChange();
679
680 LogRel(("GUI: Snapshot tree update successful!\n"));
681 }
682 }
683 }
684
685 /* Just refresh everything as fallback: */
686 if (!fSuccess)
687 {
688 LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
689 refreshAll();
690 }
691}
692
693void UISnapshotPane::sltHandleSnapshotDelete(const QUuid &uMachineId, const QUuid &uSnapshotId)
694{
695 /* Make sure it's our VM: */
696 if (uMachineId != m_uMachineId)
697 return;
698
699 LogRel(("GUI: Updating snapshot tree after DELETING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
700 uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
701
702 /* Prepare result: */
703 bool fSuccess = false;
704 {
705 /* Prevent snapshot editing in the meantime: */
706 QWriteLocker locker(m_pLockReadWrite);
707
708 /* Search for an existing item with such id: */
709 UISnapshotItem *pItem = findItem(uSnapshotId);
710 fSuccess = pItem;
711
712 /* Make sure item has no more than one child: */
713 if (fSuccess)
714 fSuccess = pItem->childCount() <= 1;
715
716 /* Detach child before deleting item: */
717 QTreeWidgetItem *pChild = 0;
718 if (fSuccess && pItem->childCount() == 1)
719 pChild = pItem->takeChild(0);
720
721 /* Check whether item has parent: */
722 QTreeWidgetItem *pParent = 0;
723 if (fSuccess)
724 pParent = pItem->QTreeWidgetItem::parent();
725
726 /* If item has child: */
727 if (pChild)
728 {
729 /* Determine where the item located: */
730 int iIndexOfChild = -1;
731 if (fSuccess)
732 {
733 if (pParent)
734 iIndexOfChild = pParent->indexOfChild(pItem);
735 else
736 iIndexOfChild = m_pSnapshotTree->indexOfTopLevelItem(pItem);
737 fSuccess = iIndexOfChild != -1;
738 }
739
740 /* And move child into this place: */
741 if (fSuccess)
742 {
743 if (pParent)
744 pParent->insertChild(iIndexOfChild, pChild);
745 else
746 m_pSnapshotTree->insertTopLevelItem(iIndexOfChild, pChild);
747 expandItemChildren(pChild);
748 }
749 }
750
751 /* Delete item finally: */
752 if (fSuccess)
753 {
754 if (pItem == m_pCurrentSnapshotItem)
755 {
756 m_pCurrentSnapshotItem = UISnapshotItem::toSnapshotItem(pParent);
757 if (m_pCurrentSnapshotItem)
758 m_pCurrentSnapshotItem->setCurrentSnapshotItem(true);
759 }
760 delete pItem;
761 pItem = 0;
762
763 LogRel(("GUI: Snapshot tree update successful!\n"));
764 }
765 }
766
767 /* Just refresh everything as fallback: */
768 if (!fSuccess)
769 {
770 LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
771 refreshAll();
772 }
773}
774
775void UISnapshotPane::sltHandleSnapshotChange(const QUuid &uMachineId, const QUuid &uSnapshotId)
776{
777 /* Make sure it's our VM: */
778 if (uMachineId != m_uMachineId)
779 return;
780
781 LogRel(("GUI: Updating snapshot tree after CHANGING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
782 uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
783
784 /* Prepare result: */
785 bool fSuccess = true;
786 {
787 /* Prevent snapshot editing in the meantime: */
788 QWriteLocker locker(m_pLockReadWrite);
789
790 /* Search for an existing item with such id: */
791 UISnapshotItem *pItem = findItem(uSnapshotId);
792 fSuccess = pItem;
793
794 /* Update the item: */
795 if (fSuccess)
796 {
797 /* Recache it: */
798 pItem->recache();
799 /* And choose it again if it's current one (to update details-widget): */
800 if (UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem()) == pItem)
801 sltHandleCurrentItemChange();
802
803 LogRel(("GUI: Snapshot tree update successful!\n"));
804 }
805 }
806
807 /* Just refresh everything as fallback: */
808 if (!fSuccess)
809 {
810 LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
811 refreshAll();
812 }
813}
814
815void UISnapshotPane::sltHandleSnapshotRestore(const QUuid &uMachineId, const QUuid &uSnapshotId)
816{
817 /* Make sure it's our VM: */
818 if (uMachineId != m_uMachineId)
819 return;
820
821 LogRel(("GUI: Updating snapshot tree after RESTORING snapshot with MachineID={%s}, SnapshotID={%s}...\n",
822 uMachineId.toString().toUtf8().constData(), uSnapshotId.toString().toUtf8().constData()));
823
824 /* Prepare result: */
825 bool fSuccess = true;
826 {
827 /* Prevent snapshot editing in the meantime: */
828 QWriteLocker locker(m_pLockReadWrite);
829
830 /* Search for an existing item with such id: */
831 UISnapshotItem *pItem = findItem(uSnapshotId);
832 fSuccess = pItem;
833
834 /* Choose this item as new "current snapshot": */
835 if (fSuccess)
836 {
837 /* Delete "current state" item first of all: */
838 UISnapshotItem *pCurrentStateItem = m_pCurrentStateItem;
839 m_pCurrentStateItem = 0;
840 delete pCurrentStateItem;
841 pCurrentStateItem = 0;
842
843 /* Move the "current snapshot" token from one to another: */
844 AssertPtrReturnVoid(m_pCurrentSnapshotItem);
845 m_pCurrentSnapshotItem->setCurrentSnapshotItem(false);
846 m_pCurrentSnapshotItem = pItem;
847 m_pCurrentSnapshotItem->setCurrentSnapshotItem(true);
848
849 /* Create "current state" item as a child of "current snapshot" item: */
850 m_pCurrentStateItem = new UISnapshotItem(this, m_pCurrentSnapshotItem, m_comMachine);
851 m_pCurrentStateItem->recache();
852 /* And choose is as current one: */
853 m_pSnapshotTree->setCurrentItem(m_pCurrentStateItem);
854 sltHandleCurrentItemChange();
855
856 LogRel(("GUI: Snapshot tree update successful!\n"));
857 }
858 }
859
860 /* Just refresh everything as fallback: */
861 if (!fSuccess)
862 {
863 LogRel(("GUI: Snapshot tree update failed! Rebuilding from scratch...\n"));
864 refreshAll();
865 }
866}
867
868void UISnapshotPane::sltUpdateSnapshotsAge()
869{
870 /* Stop timer if active: */
871 if (m_pTimerUpdateAge->isActive())
872 m_pTimerUpdateAge->stop();
873
874 /* Search for smallest snapshot age to optimize timer timeout: */
875 const SnapshotAgeFormat enmAge = traverseSnapshotAge(m_pSnapshotTree->invisibleRootItem());
876 switch (enmAge)
877 {
878 case SnapshotAgeFormat_InSeconds: m_pTimerUpdateAge->setInterval(5 * 1000); break;
879 case SnapshotAgeFormat_InMinutes: m_pTimerUpdateAge->setInterval(60 * 1000); break;
880 case SnapshotAgeFormat_InHours: m_pTimerUpdateAge->setInterval(60 * 60 * 1000); break;
881 case SnapshotAgeFormat_InDays: m_pTimerUpdateAge->setInterval(24 * 60 * 60 * 1000); break;
882 default: m_pTimerUpdateAge->setInterval(0); break;
883 }
884
885 /* Restart timer if necessary: */
886 if (m_pTimerUpdateAge->interval() > 0)
887 m_pTimerUpdateAge->start();
888}
889
890void UISnapshotPane::sltToggleSnapshotDetailsVisibility(bool fVisible)
891{
892 /* Save the setting: */
893 gEDataManager->setSnapshotManagerDetailsExpanded(fVisible);
894 /* Show/hide details-widget: */
895 m_pDetailsWidget->setVisible(fVisible);
896 /* If details-widget is visible: */
897 if (m_pDetailsWidget->isVisible())
898 {
899 /* Acquire selected item: */
900 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
901 AssertPtrReturnVoid(pSnapshotItem);
902 /* Update details-widget: */
903 if (pSnapshotItem->isCurrentStateItem())
904 m_pDetailsWidget->setData(m_comMachine);
905 else
906 m_pDetailsWidget->setData(*pSnapshotItem, pSnapshotItem->snapshot());
907 }
908}
909
910void UISnapshotPane::sltApplySnapshotDetailsChanges()
911{
912 /* Acquire selected item: */
913 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
914 AssertPtrReturnVoid(pSnapshotItem);
915
916 /* For current state item: */
917 if (pSnapshotItem->isCurrentStateItem())
918 {
919 /* Get item data: */
920 UIDataSnapshot newData = m_pDetailsWidget->data();
921
922 /* Open a session (this call will handle all errors): */
923 CSession comSession;
924 if (m_enmSessionState != KSessionState_Unlocked)
925 comSession = uiCommon().openExistingSession(m_uMachineId);
926 else
927 comSession = uiCommon().openSession(m_uMachineId);
928 if (comSession.isNotNull())
929 {
930 /* Get corresponding machine object: */
931 CMachine comMachine = comSession.GetMachine();
932
933 /* Perform separate linked steps: */
934 do
935 {
936 /* Take snapshot: */
937 QUuid uSnapshotId;
938 CProgress comProgress = comMachine.TakeSnapshot(newData.m_strName,
939 newData.m_strDescription,
940 true, uSnapshotId);
941 if (!comMachine.isOk())
942 {
943 msgCenter().cannotTakeSnapshot(comMachine, comMachine.GetName());
944 break;
945 }
946
947 /* Show snapshot taking progress: */
948 msgCenter().showModalProgressDialog(comProgress, comMachine.GetName(),
949 ":/progress_snapshot_create_90px.png");
950 if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
951 {
952 msgCenter().cannotTakeSnapshot(comProgress, comMachine.GetName());
953 break;
954 }
955 }
956 while (0);
957
958 /* Cleanup session: */
959 comSession.UnlockMachine();
960 }
961 }
962 /* For snapshot items: */
963 else
964 {
965 /* Make sure nothing being edited in the meantime: */
966 if (!m_pLockReadWrite->tryLockForWrite())
967 return;
968
969 /* Make sure that's a snapshot item indeed: */
970 CSnapshot comSnapshot = pSnapshotItem->snapshot();
971 AssertReturnVoid(comSnapshot.isNotNull());
972
973 /* Get item data: */
974 UIDataSnapshot oldData = *pSnapshotItem;
975 UIDataSnapshot newData = m_pDetailsWidget->data();
976 AssertReturnVoid(newData != oldData);
977
978 /* Open a session (this call will handle all errors): */
979 CSession comSession;
980 if (m_enmSessionState != KSessionState_Unlocked)
981 comSession = uiCommon().openExistingSession(m_uMachineId);
982 else
983 comSession = uiCommon().openSession(m_uMachineId);
984 if (comSession.isNotNull())
985 {
986 /* Get corresponding machine object: */
987 CMachine comMachine = comSession.GetMachine();
988
989 /* Perform separate independent steps: */
990 do
991 {
992 /* Save snapshot name: */
993 if (newData.m_strName != oldData.m_strName)
994 {
995 comSnapshot.SetName(newData.m_strName);
996 if (!comSnapshot.isOk())
997 {
998 msgCenter().cannotChangeSnapshot(comSnapshot, oldData.m_strName, comMachine.GetName());
999 break;
1000 }
1001 }
1002
1003 /* Save snapshot description: */
1004 if (newData.m_strDescription != oldData.m_strDescription)
1005 {
1006 comSnapshot.SetDescription(newData.m_strDescription);
1007 if (!comSnapshot.isOk())
1008 {
1009 msgCenter().cannotChangeSnapshot(comSnapshot, oldData.m_strName, comMachine.GetName());
1010 break;
1011 }
1012 }
1013 }
1014 while (0);
1015
1016 /* Cleanup session: */
1017 comSession.UnlockMachine();
1018 }
1019
1020 /* Allows editing again: */
1021 m_pLockReadWrite->unlock();
1022 }
1023
1024 /* Adjust snapshot tree: */
1025 adjustTreeWidget();
1026}
1027
1028void UISnapshotPane::sltHandleCurrentItemChange()
1029{
1030 /* Acquire "current snapshot" item: */
1031 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
1032
1033 /* Make the current item visible: */
1034 if (pSnapshotItem)
1035 {
1036 m_pSnapshotTree->horizontalScrollBar()->setValue(0);
1037 m_pSnapshotTree->scrollToItem(pSnapshotItem);
1038 m_pSnapshotTree->horizontalScrollBar()->setValue(m_pSnapshotTree->indentation() * pSnapshotItem->level());
1039 }
1040
1041 /* Update action states: */
1042 updateActionStates();
1043
1044 /* Update details-widget if it's visible: */
1045 if (pSnapshotItem && !m_pDetailsWidget->isHidden())
1046 {
1047 if (pSnapshotItem->isCurrentStateItem())
1048 m_pDetailsWidget->setData(m_comMachine);
1049 else
1050 m_pDetailsWidget->setData(*pSnapshotItem, pSnapshotItem->snapshot());
1051 }
1052
1053 /* Notify listeners: */
1054 emit sigCurrentItemChange();
1055}
1056
1057void UISnapshotPane::sltHandleContextMenuRequest(const QPoint &position)
1058{
1059 /* Search for corresponding item: */
1060 const QTreeWidgetItem *pItem = m_pSnapshotTree->itemAt(position);
1061 if (!pItem)
1062 return;
1063
1064 /* Acquire corresponding snapshot item: */
1065 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
1066 AssertReturnVoid(pSnapshotItem);
1067
1068 /* Prepare menu: */
1069 QMenu menu;
1070 /* For snapshot item: */
1071 if (m_pCurrentSnapshotItem && !pSnapshotItem->isCurrentStateItem())
1072 {
1073 menu.addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Delete));
1074 menu.addSeparator();
1075 menu.addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Restore));
1076 menu.addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_T_Properties));
1077 menu.addSeparator();
1078 menu.addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Clone));
1079 }
1080 /* For "current state" item: */
1081 else
1082 {
1083 menu.addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Take));
1084 menu.addSeparator();
1085 menu.addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Clone));
1086 }
1087
1088 /* Show menu: */
1089 menu.exec(m_pSnapshotTree->viewport()->mapToGlobal(position));
1090}
1091
1092void UISnapshotPane::sltHandleItemChange(QTreeWidgetItem *pItem)
1093{
1094 /* Make sure nothing being edited in the meantime: */
1095 if (!m_pLockReadWrite->tryLockForWrite())
1096 return;
1097
1098 /* Acquire corresponding snapshot item: */
1099 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
1100 AssertPtr(pSnapshotItem);
1101 if (pSnapshotItem)
1102 {
1103 /* Make sure that's a snapshot item indeed: */
1104 CSnapshot comSnapshot = pSnapshotItem->snapshot();
1105 if (comSnapshot.isNotNull())
1106 {
1107 /* Rename corresponding snapshot if necessary: */
1108 if (comSnapshot.GetName() != pSnapshotItem->text(Column_Name))
1109 {
1110 /* We need to open a session when we manipulate the snapshot data of a machine: */
1111 CSession comSession = uiCommon().openExistingSession(comSnapshot.GetMachine().GetId());
1112 if (!comSession.isNull())
1113 {
1114 /// @todo Add settings save validation.
1115
1116 /* Save snapshot name: */
1117 comSnapshot.SetName(pSnapshotItem->text(Column_Name));
1118
1119 /* Close the session again: */
1120 comSession.UnlockMachine();
1121 }
1122 }
1123 }
1124 }
1125
1126 /* Allows editing again: */
1127 m_pLockReadWrite->unlock();
1128
1129 /* Adjust snapshot tree: */
1130 adjustTreeWidget();
1131}
1132
1133void UISnapshotPane::sltHandleItemDoubleClick(QTreeWidgetItem *pItem)
1134{
1135 /* Acquire corresponding snapshot item: */
1136 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
1137 AssertReturnVoid(pSnapshotItem);
1138
1139 /* If this is a snapshot item: */
1140 if (pSnapshotItem)
1141 {
1142 /* Handle Ctrl+DoubleClick: */
1143 if (QApplication::keyboardModifiers() == Qt::ControlModifier)
1144 {
1145 /* As snapshot-restore procedure: */
1146 if (pSnapshotItem->isCurrentStateItem())
1147 takeSnapshot(true /* automatically */);
1148 else
1149 restoreSnapshot(true /* suppress non-critical warnings */);
1150 }
1151 /* Handle Ctrl+Shift+DoubleClick: */
1152 else if (QApplication::keyboardModifiers() == (Qt::KeyboardModifiers)(Qt::ControlModifier | Qt::ShiftModifier))
1153 {
1154 /* As snapshot-delete procedure: */
1155 if (!pSnapshotItem->isCurrentStateItem())
1156 deleteSnapshot(true /* automatically */);
1157 }
1158 /* Handle other kinds of DoubleClick: */
1159 else
1160 {
1161 /* As show details-widget procedure: */
1162 m_pActionPool->action(UIActionIndexST_M_Snapshot_T_Properties)->setChecked(true);
1163 }
1164 }
1165}
1166
1167void UISnapshotPane::prepare()
1168{
1169 /* Create read-write locker: */
1170 m_pLockReadWrite = new QReadWriteLock;
1171
1172 /* Create pixmaps: */
1173 m_pIconSnapshotOffline = new QIcon(UIIconPool::iconSet(":/snapshot_offline_16px.png"));
1174 m_pIconSnapshotOnline = new QIcon(UIIconPool::iconSet(":/snapshot_online_16px.png"));
1175
1176 /* Create timer: */
1177 m_pTimerUpdateAge = new QTimer;
1178 AssertPtrReturnVoid(m_pTimerUpdateAge);
1179 {
1180 /* Configure timer: */
1181 m_pTimerUpdateAge->setSingleShot(true);
1182 connect(m_pTimerUpdateAge, &QTimer::timeout, this, &UISnapshotPane::sltUpdateSnapshotsAge);
1183 }
1184
1185 /* Prepare connections: */
1186 prepareConnections();
1187 /* Prepare actions: */
1188 prepareActions();
1189 /* Prepare widgets: */
1190 prepareWidgets();
1191
1192 /* Load settings: */
1193 loadSettings();
1194
1195 /* Apply language settings: */
1196 retranslateUi();
1197}
1198
1199void UISnapshotPane::prepareConnections()
1200{
1201 /* Configure Main event connections: */
1202 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
1203 this, &UISnapshotPane::sltHandleMachineDataChange);
1204 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
1205 this, &UISnapshotPane::sltHandleMachineStateChange);
1206 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
1207 this, &UISnapshotPane::sltHandleSessionStateChange);
1208 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotTake,
1209 this, &UISnapshotPane::sltHandleSnapshotTake);
1210 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotDelete,
1211 this, &UISnapshotPane::sltHandleSnapshotDelete);
1212 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotChange,
1213 this, &UISnapshotPane::sltHandleSnapshotChange);
1214 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotRestore,
1215 this, &UISnapshotPane::sltHandleSnapshotRestore);
1216}
1217
1218void UISnapshotPane::prepareActions()
1219{
1220 /* First of all, add actions which has smaller shortcut scope: */
1221 addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Take));
1222 addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Delete));
1223 addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Restore));
1224 addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_T_Properties));
1225 addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Clone));
1226
1227 /* Connect actions: */
1228 connect(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Take), &UIAction::triggered,
1229 this, &UISnapshotPane::sltTakeSnapshot);
1230 connect(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Delete), &UIAction::triggered,
1231 this, &UISnapshotPane::sltDeleteSnapshot);
1232 connect(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Restore), &UIAction::triggered,
1233 this, &UISnapshotPane::sltRestoreSnapshot);
1234 connect(m_pActionPool->action(UIActionIndexST_M_Snapshot_T_Properties), &UIAction::toggled,
1235 this, &UISnapshotPane::sltToggleSnapshotDetailsVisibility);
1236 connect(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Clone), &UIAction::triggered,
1237 this, &UISnapshotPane::sltCloneSnapshot);
1238}
1239
1240void UISnapshotPane::prepareWidgets()
1241{
1242 /* Create layout: */
1243 new QVBoxLayout(this);
1244 AssertPtrReturnVoid(layout());
1245 {
1246 /* Configure layout: */
1247 layout()->setContentsMargins(0, 0, 0, 0);
1248#ifdef VBOX_WS_MAC
1249 layout()->setSpacing(10);
1250#else
1251 layout()->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
1252#endif
1253
1254 /* Prepare toolbar, if requested: */
1255 if (m_fShowToolbar)
1256 prepareToolbar();
1257 /* Prepare snapshot tree: */
1258 prepareTreeWidget();
1259 /* Prepare details-widget: */
1260 prepareDetailsWidget();
1261 }
1262}
1263
1264void UISnapshotPane::prepareToolbar()
1265{
1266 /* Create snapshot toolbar: */
1267 m_pToolBar = new UIToolBar(this);
1268 if (m_pToolBar)
1269 {
1270 /* Configure toolbar: */
1271 const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
1272 m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
1273 m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
1274
1275 /* Add toolbar actions: */
1276 m_pToolBar->addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Take));
1277 m_pToolBar->addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Delete));
1278 m_pToolBar->addSeparator();
1279 m_pToolBar->addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Restore));
1280 m_pToolBar->addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_T_Properties));
1281 m_pToolBar->addSeparator();
1282 m_pToolBar->addAction(m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Clone));
1283
1284 /* Add into layout: */
1285 layout()->addWidget(m_pToolBar);
1286 }
1287}
1288
1289void UISnapshotPane::prepareTreeWidget()
1290{
1291 /* Create snapshot tree: */
1292 m_pSnapshotTree = new UISnapshotTree(this);
1293 AssertPtrReturnVoid(m_pSnapshotTree);
1294 {
1295 /* Configure tree: */
1296 connect(m_pSnapshotTree, &UISnapshotTree::currentItemChanged,
1297 this, &UISnapshotPane::sltHandleCurrentItemChange);
1298 connect(m_pSnapshotTree, &UISnapshotTree::customContextMenuRequested,
1299 this, &UISnapshotPane::sltHandleContextMenuRequest);
1300 connect(m_pSnapshotTree, &UISnapshotTree::itemChanged,
1301 this, &UISnapshotPane::sltHandleItemChange);
1302 connect(m_pSnapshotTree, &UISnapshotTree::itemDoubleClicked,
1303 this, &UISnapshotPane::sltHandleItemDoubleClick);
1304
1305 /* Add into layout: */
1306 layout()->addWidget(m_pSnapshotTree);
1307 }
1308}
1309
1310void UISnapshotPane::prepareDetailsWidget()
1311{
1312 /* Create details-widget: */
1313 m_pDetailsWidget = new UISnapshotDetailsWidget;
1314 AssertPtrReturnVoid(m_pDetailsWidget);
1315 {
1316 /* Configure details-widget: */
1317 m_pDetailsWidget->setVisible(false);
1318 connect(m_pDetailsWidget, &UISnapshotDetailsWidget::sigDataChangeAccepted,
1319 this, &UISnapshotPane::sltApplySnapshotDetailsChanges);
1320
1321 /* Add into layout: */
1322 layout()->addWidget(m_pDetailsWidget);
1323 }
1324}
1325
1326void UISnapshotPane::loadSettings()
1327{
1328 /* Details action/widget: */
1329 m_pActionPool->action(UIActionIndexST_M_Snapshot_T_Properties)->
1330 setChecked(gEDataManager->snapshotManagerDetailsExpanded());
1331}
1332
1333void UISnapshotPane::refreshAll()
1334{
1335 /* Prevent snapshot editing in the meantime: */
1336 QWriteLocker locker(m_pLockReadWrite);
1337
1338 /* If VM is null, just updated the current item: */
1339 if (m_comMachine.isNull())
1340 {
1341 sltHandleCurrentItemChange();
1342 return;
1343 }
1344
1345 /* Remember the selected item and it's first child: */
1346 QUuid uSelectedItem, uFirstChildOfSelectedItem;
1347 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
1348 if (pSnapshotItem)
1349 {
1350 uSelectedItem = pSnapshotItem->snapshotID();
1351 if (pSnapshotItem->child(0))
1352 uFirstChildOfSelectedItem = UISnapshotItem::toSnapshotItem(pSnapshotItem->child(0))->snapshotID();
1353 }
1354
1355 /* Clear the tree: */
1356 m_pSnapshotTree->clear();
1357
1358 /* If machine has snapshots: */
1359 if (m_comMachine.GetSnapshotCount() > 0)
1360 {
1361 /* Get the first snapshot: */
1362 const CSnapshot comSnapshot = m_comMachine.FindSnapshot(QString());
1363
1364 /* Populate snapshot tree: */
1365 populateSnapshots(comSnapshot, 0);
1366 /* And make sure it has "current snapshot" item: */
1367 Assert(m_pCurrentSnapshotItem);
1368
1369 /* Add the "current state" item as a child to "current snapshot" item: */
1370 m_pCurrentStateItem = new UISnapshotItem(this, m_pCurrentSnapshotItem, m_comMachine);
1371 m_pCurrentStateItem->recache();
1372
1373 /* Search for a previously selected item: */
1374 UISnapshotItem *pCurrentItem = findItem(uSelectedItem);
1375 if (pCurrentItem == 0)
1376 pCurrentItem = findItem(uFirstChildOfSelectedItem);
1377 if (pCurrentItem == 0)
1378 pCurrentItem = m_pCurrentStateItem;
1379
1380 /* Choose current item: */
1381 m_pSnapshotTree->scrollToItem(pCurrentItem);
1382 m_pSnapshotTree->setCurrentItem(pCurrentItem);
1383 sltHandleCurrentItemChange();
1384 }
1385 /* If machine has no snapshots: */
1386 else
1387 {
1388 /* There is no "current snapshot" item: */
1389 m_pCurrentSnapshotItem = 0;
1390
1391 /* Add the "current state" item as a child of snapshot tree: */
1392 m_pCurrentStateItem = new UISnapshotItem(this, m_pSnapshotTree, m_comMachine);
1393 m_pCurrentStateItem->recache();
1394
1395 /* Choose current item: */
1396 m_pSnapshotTree->setCurrentItem(m_pCurrentStateItem);
1397 sltHandleCurrentItemChange();
1398 }
1399
1400 /* Update age: */
1401 sltUpdateSnapshotsAge();
1402
1403 /* Adjust snapshot tree: */
1404 adjustTreeWidget();
1405}
1406
1407void UISnapshotPane::populateSnapshots(const CSnapshot &comSnapshot, QITreeWidgetItem *pItem)
1408{
1409 /* Create a child of passed item: */
1410 UISnapshotItem *pSnapshotItem = pItem ? new UISnapshotItem(this, pItem, comSnapshot) :
1411 new UISnapshotItem(this, m_pSnapshotTree, comSnapshot);
1412 /* And recache it's content: */
1413 pSnapshotItem->recache();
1414
1415 /* Mark snapshot item as "current" and remember it: */
1416 CSnapshot comCurrentSnapshot = m_comMachine.GetCurrentSnapshot();
1417 if (!comCurrentSnapshot.isNull() && comCurrentSnapshot.GetId() == comSnapshot.GetId())
1418 {
1419 pSnapshotItem->setCurrentSnapshotItem(true);
1420 m_pCurrentSnapshotItem = pSnapshotItem;
1421 }
1422
1423 /* Walk through the children recursively: */
1424 foreach (const CSnapshot &comIteratedSnapshot, comSnapshot.GetChildren())
1425 populateSnapshots(comIteratedSnapshot, pSnapshotItem);
1426
1427 /* Expand the newly created item: */
1428 pSnapshotItem->setExpanded(true);
1429 /* And mark it as editable: */
1430 pSnapshotItem->setFlags(pSnapshotItem->flags() | Qt::ItemIsEditable);
1431}
1432
1433void UISnapshotPane::cleanup()
1434{
1435 /* Stop timer if active: */
1436 if (m_pTimerUpdateAge->isActive())
1437 m_pTimerUpdateAge->stop();
1438 /* Destroy timer: */
1439 delete m_pTimerUpdateAge;
1440 m_pTimerUpdateAge = 0;
1441
1442 /* Destroy icons: */
1443 delete m_pIconSnapshotOffline;
1444 delete m_pIconSnapshotOnline;
1445 m_pIconSnapshotOffline = 0;
1446 m_pIconSnapshotOnline = 0;
1447
1448 /* Destroy read-write locker: */
1449 delete m_pLockReadWrite;
1450 m_pLockReadWrite = 0;
1451}
1452
1453void UISnapshotPane::updateActionStates()
1454{
1455 /* Acquire "current snapshot" item: */
1456 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
1457
1458 /* Check whether another direct session is opened: */
1459 const bool fBusy = m_enmSessionState != KSessionState_Unlocked;
1460
1461 /* Acquire machine-state of the "current state" item: */
1462 KMachineState enmState = KMachineState_Null;
1463 if (m_pCurrentStateItem)
1464 enmState = m_pCurrentStateItem->machineState();
1465
1466 /* Determine whether taking or deleting snapshots is possible: */
1467 const bool fCanTakeDeleteSnapshot = !fBusy
1468 || enmState == KMachineState_PoweredOff
1469 || enmState == KMachineState_Saved
1470 || enmState == KMachineState_Aborted
1471 || enmState == KMachineState_Running
1472 || enmState == KMachineState_Paused;
1473
1474 /* Update 'Take' action: */
1475 m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Take)->setEnabled(
1476 m_fShapshotOperationsAllowed
1477 && ( ( fCanTakeDeleteSnapshot
1478 && m_pCurrentSnapshotItem
1479 && pSnapshotItem
1480 && pSnapshotItem->isCurrentStateItem())
1481 || ( pSnapshotItem
1482 && !m_pCurrentSnapshotItem))
1483 );
1484
1485 /* Update 'Delete' action: */
1486 m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Delete)->setEnabled(
1487 m_fShapshotOperationsAllowed
1488 && fCanTakeDeleteSnapshot
1489 && m_pCurrentSnapshotItem
1490 && pSnapshotItem
1491 && !pSnapshotItem->isCurrentStateItem()
1492 );
1493
1494 /* Update 'Restore' action: */
1495 m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Restore)->setEnabled(
1496 !fBusy
1497 && m_pCurrentSnapshotItem
1498 && pSnapshotItem
1499 && !pSnapshotItem->isCurrentStateItem()
1500 );
1501
1502 /* Update 'Show Details' action: */
1503 m_pActionPool->action(UIActionIndexST_M_Snapshot_T_Properties)->setEnabled(
1504 pSnapshotItem
1505 );
1506
1507 /* Update 'Clone' action: */
1508 m_pActionPool->action(UIActionIndexST_M_Snapshot_S_Clone)->setEnabled(
1509 pSnapshotItem
1510 && ( !pSnapshotItem->isCurrentStateItem()
1511 || !fBusy)
1512 );
1513}
1514
1515bool UISnapshotPane::takeSnapshot(bool fAutomatically /* = false */)
1516{
1517 /* Simulate try-catch block: */
1518 bool fSuccess = false;
1519 do
1520 {
1521 /* Open a session (this call will handle all errors): */
1522 CSession comSession;
1523 if (m_enmSessionState != KSessionState_Unlocked)
1524 comSession = uiCommon().openExistingSession(m_uMachineId);
1525 else
1526 comSession = uiCommon().openSession(m_uMachineId);
1527 if (comSession.isNull())
1528 break;
1529
1530 /* Simulate try-catch block: */
1531 do
1532 {
1533 /* Get corresponding machine object: */
1534 CMachine comMachine = comSession.GetMachine();
1535
1536 /* Search for a maximum available snapshot index: */
1537 int iMaximumIndex = 0;
1538 const QString strNameTemplate = tr("Snapshot %1");
1539 const QRegExp reName(QString("^") + strNameTemplate.arg("([0-9]+)") + QString("$"));
1540 QTreeWidgetItemIterator iterator(m_pSnapshotTree);
1541 while (*iterator)
1542 {
1543 const QString strName = static_cast<UISnapshotItem*>(*iterator)->text(Column_Name);
1544 const int iPosition = reName.indexIn(strName);
1545 if (iPosition != -1)
1546 iMaximumIndex = reName.cap(1).toInt() > iMaximumIndex
1547 ? reName.cap(1).toInt()
1548 : iMaximumIndex;
1549 ++iterator;
1550 }
1551
1552 /* Prepare final snapshot name/description: */
1553 QString strFinalName = strNameTemplate.arg(iMaximumIndex + 1);
1554 QString strFinalDescription;
1555
1556 /* In manual mode we should show take snapshot dialog: */
1557 if (!fAutomatically)
1558 {
1559 /* Create take-snapshot dialog: */
1560 QWidget *pDlgParent = windowManager().realParentWindow(this);
1561 QPointer<UITakeSnapshotDialog> pDlg = new UITakeSnapshotDialog(pDlgParent, comMachine);
1562 windowManager().registerNewParent(pDlg, pDlgParent);
1563
1564 /* Assign corresponding icon: */
1565 QIcon icon = uiCommon().vmUserIcon(comMachine);
1566 if (icon.isNull())
1567 icon = uiCommon().vmGuestOSTypeIcon(comMachine.GetOSTypeId());
1568 pDlg->setIcon(icon);
1569
1570 /* Assign corresponding snapshot name: */
1571 pDlg->setName(strFinalName);
1572
1573 /* Show Take Snapshot dialog: */
1574 if (pDlg->exec() != QDialog::Accepted)
1575 {
1576 /* Cleanup dialog if it wasn't destroyed in own loop: */
1577 if (pDlg)
1578 delete pDlg;
1579 break;
1580 }
1581
1582 /* Acquire final snapshot name/description: */
1583 strFinalName = pDlg->name().trimmed();
1584 strFinalDescription = pDlg->description();
1585
1586 /* Cleanup dialog: */
1587 delete pDlg;
1588 }
1589
1590 /* Take snapshot: */
1591 QUuid uSnapshotId;
1592 CProgress comProgress = comMachine.TakeSnapshot(strFinalName, strFinalDescription, true, uSnapshotId);
1593 if (!comMachine.isOk())
1594 {
1595 msgCenter().cannotTakeSnapshot(comMachine, comMachine.GetName());
1596 break;
1597 }
1598
1599 /* Show snapshot taking progress: */
1600 msgCenter().showModalProgressDialog(comProgress, comMachine.GetName(), ":/progress_snapshot_create_90px.png");
1601 if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
1602 {
1603 msgCenter().cannotTakeSnapshot(comProgress, comMachine.GetName());
1604 break;
1605 }
1606
1607 /* Mark snapshot restoring successful: */
1608 fSuccess = true;
1609 }
1610 while (0);
1611
1612 /* Cleanup try-catch block: */
1613 comSession.UnlockMachine();
1614 }
1615 while (0);
1616
1617 /* Adjust snapshot tree: */
1618 adjustTreeWidget();
1619
1620 /* Return result: */
1621 return fSuccess;
1622}
1623
1624bool UISnapshotPane::deleteSnapshot(bool fAutomatically /* = false */)
1625{
1626 /* Simulate try-catch block: */
1627 bool fSuccess = false;
1628 do
1629 {
1630 /* Acquire "current snapshot" item: */
1631 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
1632 AssertPtr(pSnapshotItem);
1633 if (!pSnapshotItem)
1634 break;
1635
1636 /* Get corresponding snapshot: */
1637 const CSnapshot comSnapshot = pSnapshotItem->snapshot();
1638 Assert(!comSnapshot.isNull());
1639 if (comSnapshot.isNull())
1640 break;
1641
1642 /* In manual mode we should ask if user really wants to remove the selected snapshot: */
1643 if (!fAutomatically && !msgCenter().confirmSnapshotRemoval(comSnapshot.GetName()))
1644 break;
1645
1646 /** @todo check available space on the target filesystem etc etc. */
1647#if 0
1648 if (!msgCenter().warnAboutSnapshotRemovalFreeSpace(comSnapshot.GetName(),
1649 "/home/juser/.VirtualBox/Machines/SampleVM/Snapshots/{01020304-0102-0102-0102-010203040506}.vdi",
1650 "59 GiB",
1651 "15 GiB"))
1652 break;
1653#endif
1654
1655 /* Open a session (this call will handle all errors): */
1656 CSession comSession;
1657 if (m_enmSessionState != KSessionState_Unlocked)
1658 comSession = uiCommon().openExistingSession(m_uMachineId);
1659 else
1660 comSession = uiCommon().openSession(m_uMachineId);
1661 if (comSession.isNull())
1662 break;
1663
1664 /* Simulate try-catch block: */
1665 do
1666 {
1667 /* Remove chosen snapshot: */
1668 CMachine comMachine = comSession.GetMachine();
1669 CProgress comProgress = comMachine.DeleteSnapshot(pSnapshotItem->snapshotID());
1670 if (!comMachine.isOk())
1671 {
1672 msgCenter().cannotRemoveSnapshot(comMachine, comSnapshot.GetName(), m_comMachine.GetName());
1673 break;
1674 }
1675
1676 /* Show snapshot removing progress: */
1677 msgCenter().showModalProgressDialog(comProgress, m_comMachine.GetName(), ":/progress_snapshot_discard_90px.png");
1678 if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
1679 {
1680 msgCenter().cannotRemoveSnapshot(comProgress, comSnapshot.GetName(), m_comMachine.GetName());
1681 break;
1682 }
1683
1684 /* Mark snapshot removing successful: */
1685 fSuccess = true;
1686 }
1687 while (0);
1688
1689 /* Cleanup try-catch block: */
1690 comSession.UnlockMachine();
1691 }
1692 while (0);
1693
1694 /* Adjust snapshot tree: */
1695 adjustTreeWidget();
1696
1697 /* Return result: */
1698 return fSuccess;
1699}
1700
1701bool UISnapshotPane::restoreSnapshot(bool fAutomatically /* = false */)
1702{
1703 /* Simulate try-catch block: */
1704 bool fSuccess = false;
1705 do
1706 {
1707 /* Acquire "current snapshot" item: */
1708 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
1709 AssertPtr(pSnapshotItem);
1710 if (!pSnapshotItem)
1711 break;
1712
1713 /* Get corresponding snapshot: */
1714 const CSnapshot comSnapshot = pSnapshotItem->snapshot();
1715 Assert(!comSnapshot.isNull());
1716 if (comSnapshot.isNull())
1717 break;
1718
1719 /* In manual mode we should check whether current state is changed: */
1720 if (!fAutomatically && m_comMachine.GetCurrentStateModified())
1721 {
1722 /* Ask if user really wants to restore the selected snapshot: */
1723 int iResultCode = msgCenter().confirmSnapshotRestoring(comSnapshot.GetName(), m_comMachine.GetCurrentStateModified());
1724 if (iResultCode & AlertButton_Cancel)
1725 break;
1726
1727 /* Ask if user also wants to create new snapshot of current state which is changed: */
1728 if (iResultCode & AlertOption_CheckBox)
1729 {
1730 /* Take snapshot of changed current state: */
1731 m_pSnapshotTree->setCurrentItem(m_pCurrentStateItem);
1732 if (!takeSnapshot())
1733 break;
1734 }
1735 }
1736
1737 /* Open a direct session (this call will handle all errors): */
1738 CSession comSession = uiCommon().openSession(m_uMachineId);
1739 if (comSession.isNull())
1740 break;
1741
1742 /* Simulate try-catch block: */
1743 do
1744 {
1745 /* Restore chosen snapshot: */
1746 CMachine comMachine = comSession.GetMachine();
1747 CProgress comProgress = comMachine.RestoreSnapshot(comSnapshot);
1748 if (!comMachine.isOk())
1749 {
1750 msgCenter().cannotRestoreSnapshot(comMachine, comSnapshot.GetName(), m_comMachine.GetName());
1751 break;
1752 }
1753
1754 /* Show snapshot restoring progress: */
1755 msgCenter().showModalProgressDialog(comProgress, m_comMachine.GetName(), ":/progress_snapshot_restore_90px.png");
1756 if (!comProgress.isOk() || comProgress.GetResultCode() != 0)
1757 {
1758 msgCenter().cannotRestoreSnapshot(comProgress, comSnapshot.GetName(), m_comMachine.GetName());
1759 break;
1760 }
1761
1762 /* Mark snapshot restoring successful: */
1763 fSuccess = true;
1764 }
1765 while (0);
1766
1767 /* Cleanup try-catch block: */
1768 comSession.UnlockMachine();
1769 }
1770 while (0);
1771
1772 /* Adjust snapshot tree: */
1773 adjustTreeWidget();
1774
1775 /* Return result: */
1776 return fSuccess;
1777}
1778
1779void UISnapshotPane::cloneSnapshot()
1780{
1781 /* Acquire "current snapshot" item: */
1782 const UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(m_pSnapshotTree->currentItem());
1783 AssertReturnVoid(pSnapshotItem);
1784
1785 /* Get desired machine/snapshot: */
1786 CMachine comMachine;
1787 CSnapshot comSnapshot;
1788 if (pSnapshotItem->isCurrentStateItem())
1789 comMachine = pSnapshotItem->machine();
1790 else
1791 {
1792 comSnapshot = pSnapshotItem->snapshot();
1793 AssertReturnVoid(!comSnapshot.isNull());
1794 comMachine = comSnapshot.GetMachine();
1795 }
1796 AssertReturnVoid(!comMachine.isNull());
1797
1798 /* Show Clone VM wizard: */
1799 UISafePointerWizard pWizard = new UIWizardCloneVM(this, comMachine, QString(), comSnapshot);
1800 pWizard->prepare();
1801 pWizard->exec();
1802 if (pWizard)
1803 delete pWizard;
1804}
1805
1806void UISnapshotPane::adjustTreeWidget()
1807{
1808 /* Get the snapshot tree abstract interface: */
1809 QAbstractItemView *pItemView = m_pSnapshotTree;
1810 /* Get the snapshot tree header-view: */
1811 QHeaderView *pItemHeader = m_pSnapshotTree->header();
1812
1813 /* Calculate the total snapshot tree width: */
1814 const int iTotal = m_pSnapshotTree->viewport()->width();
1815 /* Look for a minimum width hints for non-important columns: */
1816 const int iMinWidth1 = qMax(pItemView->sizeHintForColumn(Column_Taken), pItemHeader->sectionSizeHint(Column_Taken));
1817 /* Propose suitable width hints for non-important columns: */
1818 const int iWidth1 = iMinWidth1 < iTotal / Column_Max ? iMinWidth1 : iTotal / Column_Max;
1819 /* Apply the proposal: */
1820 m_pSnapshotTree->setColumnWidth(Column_Taken, iWidth1);
1821 m_pSnapshotTree->setColumnWidth(Column_Name, iTotal - iWidth1);
1822}
1823
1824UISnapshotItem *UISnapshotPane::findItem(const QUuid &uSnapshotID) const
1825{
1826 /* Search for the first item with required ID: */
1827 QTreeWidgetItemIterator it(m_pSnapshotTree);
1828 while (*it)
1829 {
1830 UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(*it);
1831 if (pSnapshotItem->snapshotID() == uSnapshotID)
1832 return pSnapshotItem;
1833 ++it;
1834 }
1835
1836 /* Null by default: */
1837 return 0;
1838}
1839
1840SnapshotAgeFormat UISnapshotPane::traverseSnapshotAge(QTreeWidgetItem *pItem) const
1841{
1842 /* Acquire corresponding snapshot item: */
1843 UISnapshotItem *pSnapshotItem = UISnapshotItem::toSnapshotItem(pItem);
1844
1845 /* Fetch the snapshot age of the root if it's valid: */
1846 SnapshotAgeFormat age = pSnapshotItem ? pSnapshotItem->updateAge() : SnapshotAgeFormat_Max;
1847
1848 /* Walk through the children recursively: */
1849 for (int i = 0; i < pItem->childCount(); ++i)
1850 {
1851 /* Fetch the smallest snapshot age of the children: */
1852 const SnapshotAgeFormat newAge = traverseSnapshotAge(pItem->child(i));
1853 /* Remember the smallest snapshot age among existing: */
1854 age = newAge < age ? newAge : age;
1855 }
1856
1857 /* Return result: */
1858 return age;
1859}
1860
1861void UISnapshotPane::expandItemChildren(QTreeWidgetItem *pItem)
1862{
1863 pItem ->setExpanded(true);
1864 for (int i = 0; i < pItem->childCount(); ++i)
1865 expandItemChildren(pItem->child(i));
1866}
1867
1868#include "UISnapshotPane.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use