VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/medium/UIMedium.cpp@ 67946

Last change on this file since 67946 was 67946, checked in by vboxsync, 8 years ago

FE/Qt: bugref:8400: Caching medium variant in UIMedium.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.2 KB
Line 
1/* $Id: UIMedium.cpp 67946 2017-07-13 09:50:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMedium class implementation.
4 */
5
6/*
7 * Copyright (C) 2009-2016 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#ifdef VBOX_WITH_PRECOMPILED_HEADERS
19# include <precomp.h>
20#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
21/* Qt includes: */
22# include <QDir>
23/* GUI includes: */
24# include "UIMedium.h"
25# include "VBoxGlobal.h"
26# include "UIConverter.h"
27# include "UIMessageCenter.h"
28# include "UIExtraDataManager.h"
29# include "UIIconPool.h"
30/* COM includes: */
31# include "CMachine.h"
32# include "CSnapshot.h"
33#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
34
35QString UIMedium::m_sstrNullID = QUuid().toString().remove('{').remove('}');
36QString UIMedium::m_sstrTable = QString("<table>%1</table>");
37QString UIMedium::m_sstrRow = QString("<tr><td>%1</td></tr>");
38
39UIMedium::UIMedium()
40 : m_type(UIMediumType_Invalid)
41 , m_medium(CMedium())
42 , m_state(KMediumState_NotCreated)
43 , m_enmMediumType(KMediumType_Max)
44 , m_enmMediumVariant(KMediumVariant_Max)
45{
46 refresh();
47}
48
49UIMedium::UIMedium(const CMedium &medium, UIMediumType type)
50 : m_type(type)
51 , m_medium(medium)
52 , m_state(KMediumState_NotCreated)
53 , m_enmMediumType(KMediumType_Max)
54 , m_enmMediumVariant(KMediumVariant_Max)
55{
56 refresh();
57}
58
59UIMedium::UIMedium(const CMedium &medium, UIMediumType type, KMediumState state)
60 : m_type(type)
61 , m_medium(medium)
62 , m_state(state)
63 , m_enmMediumType(KMediumType_Max)
64 , m_enmMediumVariant(KMediumVariant_Max)
65{
66 refresh();
67}
68
69UIMedium::UIMedium(const UIMedium &other)
70{
71 *this = other;
72}
73
74UIMedium& UIMedium::operator=(const UIMedium &other)
75{
76 m_type = other.type();
77
78 m_medium = other.medium();
79
80 m_state = other.state();
81 m_result = other.result();
82 m_strLastAccessError = other.lastAccessError();
83
84 m_strId = other.id();
85 m_strRootId = other.rootID();
86 m_strParentId = other.parentID();
87
88 m_strKey = other.key();
89
90 m_strName = other.name();
91 m_strLocation = other.location();
92
93 m_uSize = other.sizeInBytes();
94 m_uLogicalSize = other.logicalSizeInBytes();
95 m_strSize = other.size();
96 m_strLogicalSize = other.logicalSize();
97
98 m_enmMediumType = other.mediumType();
99 m_enmMediumVariant = other.mediumVariant();
100
101 m_strHardDiskType = other.hardDiskType();
102 m_strHardDiskFormat = other.hardDiskFormat();
103 m_strStorageDetails = other.storageDetails();
104 m_strEncryptionPasswordID = other.encryptionPasswordID();
105
106 m_strUsage = other.usage();
107 m_strToolTip = other.tip();
108 m_machineIds = other.machineIds();
109 m_curStateMachineIds = other.curStateMachineIds();
110
111 m_noDiffs = other.cache();
112
113 m_fHidden = other.m_fHidden;
114 m_fUsedByHiddenMachinesOnly = other.m_fUsedByHiddenMachinesOnly;
115 m_fReadOnly = other.isReadOnly();
116 m_fUsedInSnapshots = other.isUsedInSnapshots();
117 m_fHostDrive = other.isHostDrive();
118 m_fEncrypted = other.isEncrypted();
119
120 return *this;
121}
122
123void UIMedium::blockAndQueryState()
124{
125 /* Ignore for NULL medium: */
126 if (m_medium.isNull())
127 return;
128
129 /* Acquire actual medium state: */
130 m_state = m_medium.RefreshState();
131
132 /* Save the result to distinguish between
133 * inaccessible and e.g. uninitialized objects: */
134 m_result = COMResult(m_medium);
135 if (!m_result.isOk())
136 {
137 m_state = KMediumState_Inaccessible;
138 m_strLastAccessError = QString();
139 }
140 else
141 m_strLastAccessError = m_medium.GetLastAccessError();
142
143 /* Refresh finally: */
144 refresh();
145}
146
147void UIMedium::refresh()
148{
149 /* Reset ID parameters: */
150 m_strId = nullID();
151 m_strRootId = nullID();
152 m_strParentId = nullID();
153
154 /* Reset cache parameters: */
155 //m_strKey = nullID();
156
157 /* Reset name/location/size parameters: */
158 m_strName = VBoxGlobal::tr("Empty", "medium");
159 m_strLocation = m_strSize = m_strLogicalSize = QString("--");
160 m_uSize = m_uLogicalSize = 0;
161
162 /* Reset medium type & variant parameter: */
163 m_enmMediumType = KMediumType_Max;
164 m_enmMediumVariant = KMediumVariant_Max;
165
166 /* Reset hard drive related parameters: */
167 m_strHardDiskType = QString();
168 m_strHardDiskFormat = QString();
169 m_strStorageDetails = QString();
170 m_strEncryptionPasswordID = QString();
171
172 /* Reset data parameters: */
173 m_strUsage = QString();
174 m_strToolTip = QString();
175 m_machineIds.clear();
176 m_curStateMachineIds.clear();
177
178 /* Reset m_noDiffs: */
179 m_noDiffs.isSet = false;
180
181 /* Reset flags: */
182 m_fHidden = false;
183 m_fUsedByHiddenMachinesOnly = false;
184 m_fReadOnly = false;
185 m_fUsedInSnapshots = false;
186 m_fHostDrive = false;
187 m_fEncrypted = false;
188
189 /* For non NULL medium: */
190 if (!m_medium.isNull())
191 {
192 /* Refresh medium ID: */
193 m_strId = normalizedID(m_medium.GetId());
194 /* Refresh root medium ID: */
195 m_strRootId = m_strId;
196
197 /* Init medium key if necessary: */
198 if (m_strKey.isNull())
199 m_strKey = m_strId;
200
201 /* Check whether this is host-drive medium: */
202 m_fHostDrive = m_medium.GetHostDrive();
203
204 /* Refresh medium name: */
205 if (!m_fHostDrive)
206 m_strName = m_medium.GetName();
207 else if (m_medium.GetDescription().isEmpty())
208 m_strName = VBoxGlobal::tr("Host Drive '%1'", "medium").arg(QDir::toNativeSeparators(m_medium.GetLocation()));
209 else
210 m_strName = VBoxGlobal::tr("Host Drive %1 (%2)", "medium").arg(m_medium.GetDescription(), m_medium.GetName());
211 /* Refresh medium location: */
212 if (!m_fHostDrive)
213 m_strLocation = QDir::toNativeSeparators(m_medium.GetLocation());
214
215 /* Refresh medium size and logical size: */
216 if (!m_fHostDrive)
217 {
218 /* Only for created and accessible mediums: */
219 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
220 {
221 m_uSize = m_medium.GetSize();
222 m_strSize = vboxGlobal().formatSize(m_uSize);
223 if (m_type == UIMediumType_HardDisk)
224 {
225 m_uLogicalSize = m_medium.GetLogicalSize();
226 m_strLogicalSize = vboxGlobal().formatSize(m_uLogicalSize);
227 }
228 else
229 {
230 m_uLogicalSize = m_uSize;
231 m_strLogicalSize = m_strSize;
232 }
233 }
234 }
235
236 /* Refresh medium type & variant: */
237 m_enmMediumType = m_medium.GetType();
238 qlonglong iMediumVariant = 0;
239 foreach (const KMediumVariant &enmVariant, m_medium.GetVariant())
240 iMediumVariant |= enmVariant;
241 m_enmMediumVariant = (KMediumVariant)iMediumVariant;
242
243 /* For hard drive medium: */
244 if (m_type == UIMediumType_HardDisk)
245 {
246 /* Refresh hard drive disk type: */
247 m_strHardDiskType = mediumTypeToString(m_medium);
248 /* Refresh hard drive format: */
249 m_strHardDiskFormat = m_medium.GetFormat();
250
251 /* Refresh hard drive storage details: */
252 m_strStorageDetails = gpConverter->toString(m_enmMediumVariant);
253
254 /* Check whether this is read-only hard drive: */
255 m_fReadOnly = m_medium.GetReadOnly();
256
257 /* Refresh parent hard drive ID: */
258 CMedium parentMedium = m_medium.GetParent();
259 if (!parentMedium.isNull())
260 m_strParentId = normalizedID(parentMedium.GetId());
261
262 /* Only for created and accessible mediums: */
263 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated)
264 {
265 /* Refresh root hard drive ID: */
266 while (!parentMedium.isNull())
267 {
268 m_strRootId = normalizedID(parentMedium.GetId());
269 parentMedium = parentMedium.GetParent();
270 }
271
272 /* Refresh encryption attributes: */
273 if (m_strRootId != m_strId)
274 {
275 m_strEncryptionPasswordID = root().encryptionPasswordID();
276 m_fEncrypted = root().isEncrypted();
277 }
278 else
279 {
280 QString strCipher;
281 CMedium medium(m_medium);
282 const QString strEncryptionPasswordID = medium.GetEncryptionSettings(strCipher);
283 if (medium.isOk())
284 {
285 m_strEncryptionPasswordID = strEncryptionPasswordID;
286 m_fEncrypted = true;
287 }
288 }
289 }
290 }
291
292 /* Check whether this is hidden medium: */
293 QString strHints = m_medium.GetProperty("Special/GUI/Hints");
294 if (!strHints.isEmpty())
295 {
296 QStringList hints(strHints.split(','));
297 if (hints.contains("Hide", Qt::CaseInsensitive))
298 m_fHidden = true;
299 }
300
301 /* Refresh usage data: */
302 m_curStateMachineIds.clear();
303 m_machineIds = m_medium.GetMachineIds().toList();
304 if (m_machineIds.size() > 0)
305 {
306 /* Get CVirtualBox object: */
307 CVirtualBox vbox = vboxGlobal().virtualBox();
308
309 /* By default we assuming that this medium is attached
310 * to 'hidden' machines only, if at least one machine present: */
311 m_fUsedByHiddenMachinesOnly = true;
312
313 /* Prepare machine usage: */
314 QString strMachineUsage;
315 /* Walk through all the machines this medium attached to: */
316 foreach (const QString &strMachineID, m_machineIds)
317 {
318 /* Look for the corresponding machine: */
319 CMachine machine = vbox.FindMachine(strMachineID);
320
321 /* UIMedium object can wrap newly created CMedium object
322 * which belongs to not yet registered machine, like while creating VM clone.
323 * We can skip such a machines in usage string. */
324 if (machine.isNull())
325 {
326 /* Since we can't precisely check 'hidden' status for that machine in such case,
327 * we have to assume that medium attached not only to 'hidden' machines: */
328 m_fUsedByHiddenMachinesOnly = false;
329 continue;
330 }
331
332 /* Finally we can precisely check if current machine is 'hidden': */
333 if (gEDataManager->showMachineInSelectorChooser(strMachineID))
334 m_fUsedByHiddenMachinesOnly = false;
335
336 /* Prepare snapshot usage: */
337 QString strSnapshotUsage;
338 /* Walk through all the snapshots this medium attached to: */
339 foreach (const QString &strSnapshotID, m_medium.GetSnapshotIds(strMachineID))
340 {
341 if (strSnapshotID == strMachineID)
342 {
343 /* The medium is attached to the machine in the current
344 * state, we don't distinguish this for now by always
345 * giving the VM name in front of snapshot names. */
346 m_curStateMachineIds.push_back(strSnapshotID);
347 continue;
348 }
349
350 /* Look for the corresponding snapshot: */
351 CSnapshot snapshot = machine.FindSnapshot(strSnapshotID);
352
353 /* Snapshot can be NULL while takeSnaphot is in progress: */
354 if (snapshot.isNull())
355 continue;
356
357 /* Refresh snapshot usage flag: */
358 m_fUsedInSnapshots = true;
359
360 /* Append snapshot usage: */
361 if (!strSnapshotUsage.isNull())
362 strSnapshotUsage += ", ";
363 strSnapshotUsage += snapshot.GetName();
364 }
365
366 /* Append machine usage: */
367 if (!strMachineUsage.isNull())
368 strMachineUsage += ", ";
369 strMachineUsage += machine.GetName();
370
371 /* Append snapshot usage: */
372 if (!strSnapshotUsage.isNull())
373 strMachineUsage += QString(" (%2)").arg(strSnapshotUsage);
374 }
375
376 /* Append machine usage: */
377 if (!strMachineUsage.isEmpty())
378 m_strUsage += strMachineUsage;
379 }
380
381 /* Refresh tool-tip: */
382 m_strToolTip = m_sstrRow.arg(QString("<p style=white-space:pre><b>%1</b></p>").arg(m_fHostDrive ? m_strName : m_strLocation));
383 if (m_type == UIMediumType_HardDisk)
384 {
385 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p style=white-space:pre>Type (Format): %1 (%2)</p>", "medium")
386 .arg(m_strHardDiskType).arg(m_strHardDiskFormat));
387 }
388 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p>Attached to: %1</p>", "image")
389 .arg(m_strUsage.isNull() ? VBoxGlobal::tr("<i>Not Attached</i>", "image") : m_strUsage));
390 switch (m_state)
391 {
392 case KMediumState_NotCreated:
393 {
394 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<i>Checking accessibility...</i>", "medium"));
395 break;
396 }
397 case KMediumState_Inaccessible:
398 {
399 if (m_result.isOk())
400 {
401 /* Not Accessible: */
402 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::highlight(m_strLastAccessError, true /* aToolTip */));
403 }
404 else
405 {
406 /* Accessibility check (eg GetState()) itself failed: */
407 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::tr("Failed to check accessibility of disk image files.", "medium")) +
408 m_sstrRow.arg(UIMessageCenter::formatErrorInfo(m_result) + ".");
409 }
410 break;
411 }
412 default:
413 break;
414 }
415 }
416}
417
418void UIMedium::updateParentID()
419{
420 m_strParentId = nullID();
421 if (m_type == UIMediumType_HardDisk)
422 {
423 CMedium parentMedium = m_medium.GetParent();
424 if (!parentMedium.isNull())
425 m_strParentId = normalizedID(parentMedium.GetId());
426 }
427}
428
429QString UIMedium::toolTip(bool fNoDiffs /* = false */, bool fCheckRO /* = false */, bool fNullAllowed /* = false */) const
430{
431 QString strTip;
432
433 if (m_medium.isNull())
434 {
435 strTip = fNullAllowed ? m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image file selected</b>", "medium")) +
436 m_sstrRow.arg(VBoxGlobal::tr("You can also change this while the machine is running.")) :
437 m_sstrRow.arg(VBoxGlobal::tr("<b>No disk image files available</b>", "medium")) +
438 m_sstrRow.arg(VBoxGlobal::tr("You can create or add disk image files in the virtual machine settings."));
439 }
440 else
441 {
442 unconst(this)->checkNoDiffs(fNoDiffs);
443
444 strTip = fNoDiffs ? m_noDiffs.toolTip : m_strToolTip;
445
446 if (fCheckRO && m_fReadOnly)
447 strTip += m_sstrRow.arg("<hr>") +
448 m_sstrRow.arg(VBoxGlobal::tr("Attaching this hard disk will be performed indirectly using "
449 "a newly created differencing hard disk.", "medium"));
450 }
451
452 return m_sstrTable.arg(strTip);
453}
454
455QPixmap UIMedium::icon(bool fNoDiffs /* = false */, bool fCheckRO /* = false */) const
456{
457 QPixmap pixmap;
458
459 if (state(fNoDiffs) == KMediumState_Inaccessible)
460 pixmap = result(fNoDiffs).isOk() ? vboxGlobal().warningIcon() : vboxGlobal().errorIcon();
461
462 if (fCheckRO && m_fReadOnly)
463 {
464 QIcon icon = UIIconPool::iconSet(":/hd_new_16px.png");
465 pixmap = VBoxGlobal::joinPixmaps(pixmap, icon.pixmap(icon.availableSizes().first()));
466 }
467
468 return pixmap;
469}
470
471QString UIMedium::details(bool fNoDiffs /* = false */,
472 bool fPredictDiff /* = false */,
473 bool fUseHTML /* = false */) const
474{
475 /// @todo the below check is rough; if m_medium becomes uninitialized, any
476 // of getters called afterwards will also fail. The same relates to the
477 // root hard drive object (that will be the hard drive itself in case of
478 // non-differencing disks). However, this check was added to fix a
479 // particular use case: when the hard drive is a differencing hard drive and
480 // it happens to be discarded (and uninitialized) after this method is
481 // called but before we read all its properties (yes, it's possible!), the
482 // root object will be null and calling methods on it will assert in the
483 // debug builds. This check seems to be enough as a quick solution (fresh
484 // hard drive attachments will be re-read by a machine state change signal
485 // after the discard operation is finished, so the user will eventually see
486 // correct data), but in order to solve the problem properly we need to use
487 // exceptions everywhere (or check the result after every method call). See
488 // @bugref{2149}.
489
490 if (m_medium.isNull() || m_fHostDrive)
491 return m_strName;
492
493 if (!m_medium.isOk())
494 return QString();
495
496 QString strDetails, strText;
497
498 /* Note: root accessible only if medium enumerated: */
499 UIMedium rootMedium = root();
500 KMediumState eState = m_state;
501
502 if (m_type == UIMediumType_HardDisk)
503 {
504 if (fNoDiffs)
505 {
506 bool isDiff = (!fPredictDiff && parentID() != nullID()) || (fPredictDiff && m_fReadOnly);
507
508 strDetails = isDiff && fUseHTML ?
509 QString("<i>%1</i>, ").arg(rootMedium.m_strHardDiskType) :
510 QString("%1, ").arg(rootMedium.m_strHardDiskType);
511
512 eState = this->state(true /* fNoDiffs */);
513
514 if (rootMedium.m_state == KMediumState_NotCreated)
515 eState = KMediumState_NotCreated;
516 }
517 else
518 {
519 strDetails = QString("%1, ").arg(rootMedium.m_strHardDiskType);
520 }
521
522 /* Add encryption status: */
523 if (m_fEncrypted)
524 strDetails += QString("%1, ").arg(VBoxGlobal::tr("Encrypted", "medium"));
525 }
526
527 /// @todo prepend the details with the warning/error icon when not accessible
528
529 switch (eState)
530 {
531 case KMediumState_NotCreated:
532 strText = VBoxGlobal::tr("Checking...", "medium");
533 strDetails += fUseHTML ? QString("<i>%1</i>").arg(strText) : strText;
534 break;
535 case KMediumState_Inaccessible:
536 strText = VBoxGlobal::tr("Inaccessible", "medium");
537 strDetails += fUseHTML ? QString("<b>%1</b>").arg(strText) : strText;
538 break;
539 default:
540 strDetails += m_type == UIMediumType_HardDisk ? rootMedium.m_strLogicalSize : rootMedium.m_strSize;
541 break;
542 }
543
544 strDetails = fUseHTML ?
545 QString("%1 (<nobr>%2</nobr>)").arg(VBoxGlobal::locationForHTML(rootMedium.m_strName), strDetails) :
546 QString("%1 (%2)").arg(VBoxGlobal::locationForHTML(rootMedium.m_strName), strDetails);
547
548 return strDetails;
549}
550
551/* static */
552QString UIMedium::nullID()
553{
554 return m_sstrNullID;
555}
556
557/* static */
558QString UIMedium::normalizedID(const QString &strID)
559{
560 /* Handle wrong UUID (null/empty or invalid format): */
561 if (QUuid(strID).toString().remove('{').remove('}') != strID)
562 return nullID();
563 return strID;
564}
565
566/* static */
567bool UIMedium::isMediumAttachedToHiddenMachinesOnly(const UIMedium &medium)
568{
569 /* Iterate till the root: */
570 UIMedium mediumIterator = medium;
571 do
572 {
573 /* Ignore medium if its hidden
574 * or attached to hidden machines only: */
575 if (mediumIterator.isHidden())
576 return true;
577 /* Move iterator to parent: */
578 mediumIterator = mediumIterator.parent();
579 }
580 while (!mediumIterator.isNull());
581 /* False by default: */
582 return false;
583}
584
585UIMedium UIMedium::root() const
586{
587 /* Redirect call to VBoxGlobal: */
588 return vboxGlobal().medium(m_strRootId);
589}
590
591UIMedium UIMedium::parent() const
592{
593 /* Redirect call to VBoxGlobal: */
594 return vboxGlobal().medium(m_strParentId);
595}
596
597void UIMedium::checkNoDiffs(bool fNoDiffs)
598{
599 if (!fNoDiffs || m_noDiffs.isSet)
600 return;
601
602 m_noDiffs.toolTip = QString();
603
604 m_noDiffs.state = m_state;
605 for (UIMedium parentMedium = parent(); !parentMedium.isNull(); parentMedium = parentMedium.parent())
606 {
607 if (parentMedium.m_state == KMediumState_Inaccessible)
608 {
609 m_noDiffs.state = parentMedium.m_state;
610
611 if (m_noDiffs.toolTip.isNull())
612 m_noDiffs.toolTip = m_sstrRow.arg(VBoxGlobal::tr("Some of the files in this hard disk chain "
613 "are inaccessible. Please use the Virtual Medium "
614 "Manager to inspect these files.", "medium"));
615
616 if (!parentMedium.m_result.isOk())
617 {
618 m_noDiffs.result = parentMedium.m_result;
619 break;
620 }
621 }
622 }
623
624 if (parentID() != nullID() && !m_fReadOnly)
625 {
626 m_noDiffs.toolTip = root().tip() +
627 m_sstrRow.arg("<hr>") +
628 m_sstrRow.arg(VBoxGlobal::tr("This base hard disk is indirectly attached using "
629 "the following differencing hard disk:", "medium")) +
630 m_strToolTip + m_noDiffs.toolTip;
631 }
632
633 if (m_noDiffs.toolTip.isNull())
634 m_noDiffs.toolTip = m_strToolTip;
635
636 m_noDiffs.isSet = true;
637}
638
639/* static */
640QString UIMedium::mediumTypeToString(const CMedium &comMedium)
641{
642 if (!comMedium.GetParent().isNull())
643 {
644 Assert(comMedium.GetType() == KMediumType_Normal);
645 return QApplication::translate("VBoxGlobal", "Differencing", "MediumType");
646 }
647 return gpConverter->toString(comMedium.GetType());
648}
649
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