VirtualBox

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

Last change on this file since 48089 was 48089, checked in by vboxsync, 11 years ago

FE/Qt: 6909: Medium-enumeration cleanup (part 4).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/* $Id: UIMedium.cpp 48089 2013-08-27 16:00:23Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIMedium class implementation
6 */
7
8/*
9 * Copyright (C) 2009-2013 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
24/* Qt includes: */
25#include <QDir>
26
27/* GUI includes: */
28#include "UIMedium.h"
29#include "VBoxGlobal.h"
30#include "UIMessageCenter.h"
31#include "UIConverter.h"
32
33/* COM includes: */
34#include "CMachine.h"
35#include "CSnapshot.h"
36
37#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
38
39QString UIMedium::m_sstrTable = QString("<table>%1</table>");
40QString UIMedium::m_sstrRow = QString("<tr><td>%1</td></tr>");
41
42UIMedium& UIMedium::operator=(const UIMedium &other)
43{
44 m_medium = other.medium();
45 m_type = other.type();
46 m_state = other.state();
47 m_strLastAccessError = other.lastAccessError();
48 m_result = other.result();
49
50 m_strId = other.id();
51 m_strName = other.name();
52 m_strLocation = other.location();
53
54 m_strSize = other.size();
55 m_strLogicalSize = other.logicalSize();
56
57 m_strHardDiskFormat = other.hardDiskFormat();
58 m_strHardDiskType = other.hardDiskType();
59
60 m_strStorageDetails = other.storageDetails();
61
62 m_strUsage = other.usage();
63 m_strToolTip = other.tip();
64
65 m_fHidden = other.m_fHidden;
66 m_fAttachedToHiddenMachinesOnly = other.m_fAttachedToHiddenMachinesOnly;
67 m_fReadOnly = other.isReadOnly();
68 m_fUsedInSnapshots = other.isUsedInSnapshots();
69 m_fHostDrive = other.isHostDrive();
70
71 m_curStateMachineIds = other.curStateMachineIds();
72
73 m_pParent = other.parent();
74
75 m_noDiffs = other.cache();
76
77 return *this;
78}
79
80/**
81 * Queries the medium state. Call this and then read the state field instead
82 * of calling GetState() on medium directly as it will properly handle the
83 * situation when GetState() itself fails by setting state to Inaccessible
84 * and memorizing the error info describing why GetState() failed.
85 *
86 * As the last step, this method calls #refresh() to refresh all precomposed
87 * strings.
88 *
89 * @note This method blocks for the duration of the state check. Since this
90 * check may take quite a while (e.g. for a medium located on a
91 * network share), the calling thread must not be the UI thread. You
92 * have been warned.
93 */
94void UIMedium::blockAndQueryState()
95{
96 if (m_medium.isNull())
97 return;
98
99 m_state = m_medium.RefreshState();
100
101 /* Save the result to distinguish between
102 * inaccessible and e.g. uninitialized objects: */
103 m_result = COMResult(m_medium);
104 if (!m_result.isOk())
105 {
106 m_state = KMediumState_Inaccessible;
107 m_strLastAccessError = QString();
108 }
109 else
110 m_strLastAccessError = m_medium.GetLastAccessError();
111
112 refresh();
113}
114
115/**
116 * Refreshes the precomposed strings containing such media parameters as
117 * location, size by querying the respective data from the associated
118 * media object.
119 *
120 * Note that some string such as #size() are meaningless if the media state is
121 * KMediumState_NotCreated (i.e. the medium has not yet been checked for
122 * accessibility).
123 */
124void UIMedium::refresh()
125{
126 /* Flags are 'false' by default: */
127 m_fHidden = false;
128 m_fAttachedToHiddenMachinesOnly = false;
129 m_fReadOnly = false;
130 m_fUsedInSnapshots = false;
131 m_fHostDrive = false;
132
133 /* Detect basic parameters */
134 m_strId = m_medium.isNull() ? QUuid().toString().remove ('{').remove ('}') : m_medium.GetId();
135
136 m_fHostDrive = m_medium.isNull() ? false : m_medium.GetHostDrive();
137
138 if (m_medium.isNull())
139 m_strName = VBoxGlobal::tr("Empty", "medium");
140 else if (!m_fHostDrive)
141 m_strName = m_medium.GetName();
142 else if (m_medium.GetDescription().isEmpty())
143 m_strName = VBoxGlobal::tr("Host Drive '%1'", "medium").arg(QDir::toNativeSeparators(m_medium.GetLocation()));
144 else
145 m_strName = VBoxGlobal::tr("Host Drive %1 (%2)", "medium").arg(m_medium.GetDescription(), m_medium.GetName());
146
147 m_strLocation = m_medium.isNull() || m_fHostDrive ? QString("--") :
148 QDir::toNativeSeparators(m_medium.GetLocation());
149
150 QString tmp;
151 if (!m_medium.isNull())
152 tmp = m_medium.GetProperty("Special/GUI/Hints");
153 if (!tmp.isEmpty())
154 {
155 QStringList tmpList(tmp.split(','));
156 if (tmpList.contains("Hide", Qt::CaseInsensitive))
157 m_fHidden = true;
158 }
159
160 if (m_type == UIMediumType_HardDisk)
161 {
162 m_strHardDiskFormat = m_medium.GetFormat();
163 m_strHardDiskType = vboxGlobal().mediumTypeString(m_medium);
164
165 QVector<KMediumVariant> mediumVariants = m_medium.GetVariant();
166 qlonglong mediumVariant = 0;
167 for (int i = 0; i < mediumVariants.size(); ++i)
168 mediumVariant |= mediumVariants[i];
169
170 m_strStorageDetails = gpConverter->toString((KMediumVariant)mediumVariant);
171 m_fReadOnly = m_medium.GetReadOnly();
172
173 /* Adjust the parent if its possible: */
174 CMedium parentMedium = m_medium.GetParent();
175 Assert(!parentMedium.isNull() || m_pParent == NULL);
176
177 if (!parentMedium.isNull() && (m_pParent == NULL || m_pParent->m_medium != parentMedium))
178 {
179 /* Search for the parent (might be there): */
180 const VBoxMediaList &list = vboxGlobal().currentMediaList();
181 for (VBoxMediaList::const_iterator it = list.begin(); it != list.end(); ++it)
182 {
183 if ((*it).m_type != UIMediumType_HardDisk)
184 break;
185
186 if ((*it).m_medium == parentMedium)
187 {
188 m_pParent = unconst(&*it);
189 break;
190 }
191 }
192 }
193 }
194 else
195 {
196 m_strHardDiskFormat = QString();
197 m_strHardDiskType = QString();
198 m_fReadOnly = false;
199 }
200
201 /* Detect sizes */
202 if (m_state != KMediumState_Inaccessible && m_state != KMediumState_NotCreated && !m_fHostDrive)
203 {
204 m_strSize = vboxGlobal().formatSize(m_medium.GetSize());
205 if (m_type == UIMediumType_HardDisk)
206 m_strLogicalSize = vboxGlobal().formatSize(m_medium.GetLogicalSize());
207 else
208 m_strLogicalSize = m_strSize;
209 }
210 else
211 {
212 m_strSize = m_strLogicalSize = QString("--");
213 }
214
215 /* Detect usage */
216 m_strUsage = QString();
217 if (!m_medium.isNull())
218 {
219 m_curStateMachineIds.clear();
220 QVector <QString> machineIds = m_medium.GetMachineIds();
221 if (machineIds.size() > 0)
222 {
223 /* We assume this flag is 'true' if at least one machine present: */
224 m_fAttachedToHiddenMachinesOnly = true;
225
226 QString strUsage;
227
228 CVirtualBox vbox = vboxGlobal().virtualBox();
229
230 for (QVector <QString>::ConstIterator it = machineIds.begin(); it != machineIds.end(); ++it)
231 {
232 CMachine machine = vbox.FindMachine(*it);
233
234 /* UIMedium object can wrap newly created CMedium object which belongs to
235 * not yet registered machine, like while creating VM clone.
236 * We can skip such a machines in usage string.
237 * CVirtualBox::FindMachine() will return null machine for such case. */
238 if (machine.isNull())
239 {
240 /* We can't decide for that medium yet,
241 * assume this flag is 'false' for now: */
242 m_fAttachedToHiddenMachinesOnly = false;
243 continue;
244 }
245
246 /* Finally, we are checking if current machine overrides this flag: */
247 if (m_fAttachedToHiddenMachinesOnly && vboxGlobal().shouldWeShowMachine(machine))
248 m_fAttachedToHiddenMachinesOnly = false;
249
250 QString strName = machine.GetName();
251 QString strSnapshots;
252
253 QVector <QString> snapIds = m_medium.GetSnapshotIds(*it);
254 for (QVector <QString>::ConstIterator jt = snapIds.begin(); jt != snapIds.end(); ++jt)
255 {
256 if (*jt == *it)
257 {
258 /* The medium is attached to the machine in the current
259 * state, we don't distinguish this for now by always
260 * giving the VM name in front of snapshot names. */
261 m_curStateMachineIds.push_back(*jt);
262 continue;
263 }
264
265 CSnapshot snapshot = machine.FindSnapshot(*jt);
266 if (!snapshot.isNull()) // can be NULL while takeSnaphot is in progress
267 {
268 if (!strSnapshots.isNull())
269 strSnapshots += ", ";
270 strSnapshots += snapshot.GetName();
271 }
272 }
273
274 if (!strUsage.isNull())
275 strUsage += ", ";
276
277 strUsage += strName;
278
279 if (!strSnapshots.isNull())
280 {
281 strUsage += QString(" (%2)").arg(strSnapshots);
282 m_fUsedInSnapshots = true;
283 }
284 else
285 m_fUsedInSnapshots = false;
286 }
287
288 if (!strUsage.isEmpty())
289 m_strUsage = strUsage;
290 }
291 }
292
293 /* Compose the tooltip */
294 if (!m_medium.isNull())
295 {
296 m_strToolTip = m_sstrRow.arg(QString("<p style=white-space:pre><b>%1</b></p>").arg(m_fHostDrive ? m_strName : m_strLocation));
297
298 if (m_type == UIMediumType_HardDisk)
299 {
300 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p style=white-space:pre>Type (Format): %1 (%2)</p>", "medium")
301 .arg(m_strHardDiskType).arg(m_strHardDiskFormat));
302 }
303
304 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<p>Attached to: %1</p>", "image")
305 .arg(m_strUsage.isNull() ? VBoxGlobal::tr("<i>Not Attached</i>", "image") : m_strUsage));
306
307 switch (m_state)
308 {
309 case KMediumState_NotCreated:
310 {
311 m_strToolTip += m_sstrRow.arg(VBoxGlobal::tr("<i>Checking accessibility...</i>", "medium"));
312 break;
313 }
314 case KMediumState_Inaccessible:
315 {
316 if (m_result.isOk())
317 {
318 /* Not Accessible */
319 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::highlight(m_strLastAccessError, true /* aToolTip */));
320 }
321 else
322 {
323 /* Accessibility check (eg GetState()) itself failed: */
324 m_strToolTip += m_sstrRow.arg("<hr>") + m_sstrRow.arg(VBoxGlobal::tr("Failed to check media accessibility.", "medium")) +
325 m_sstrRow.arg(UIMessageCenter::formatErrorInfo(m_result) + ".");
326 }
327 break;
328 }
329 default:
330 break;
331 }
332 }
333
334 /* Reset m_noDiffs */
335 m_noDiffs.isSet = false;
336}
337
338/**
339 * Returns a root medium of this medium. For non-hard disk media, this is always
340 * this medium itself.
341 */
342UIMedium &UIMedium::root() const
343{
344 UIMedium *pRoot = unconst(this);
345 while (pRoot->m_pParent != NULL)
346 pRoot = pRoot->m_pParent;
347
348 return *pRoot;
349}
350
351/**
352 * Returns generated tooltip for this medium.
353 *
354 * In "don't show diffs" mode (where the attributes of the base hard disk are
355 * shown instead of the attributes of the differencing hard disk), extra
356 * information will be added to the tooltip to give the user a hint that the
357 * medium is actually a differencing hard disk.
358 *
359 * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
360 * @param fCheckRO @c true to perform the #readOnly() check and add a notice
361 * accordingly.
362 */
363QString UIMedium::toolTip (bool fNoDiffs /* = false */, bool fCheckRO /* = false */, bool fNullAllowed /* = false */) const
364{
365 QString strTip;
366
367 if (m_medium.isNull())
368 {
369 strTip = fNullAllowed ? m_sstrRow.arg(VBoxGlobal::tr("<b>No medium selected</b>", "medium")) +
370 m_sstrRow.arg(VBoxGlobal::tr("You can also change this while the machine is running.")) :
371 m_sstrRow.arg(VBoxGlobal::tr("<b>No media available</b>", "medium")) +
372 m_sstrRow.arg(VBoxGlobal::tr("You can create media images using the virtual media manager."));
373 }
374 else
375 {
376 unconst(this)->checkNoDiffs(fNoDiffs);
377
378 strTip = fNoDiffs ? m_noDiffs.toolTip : m_strToolTip;
379
380 if (fCheckRO && m_fReadOnly)
381 strTip += m_sstrRow.arg("<hr>") +
382 m_sstrRow.arg(VBoxGlobal::tr("Attaching this hard disk will be performed indirectly using "
383 "a newly created differencing hard disk.", "medium"));
384 }
385
386 return m_sstrTable.arg(strTip);
387}
388
389/**
390 * Returns an icon corresponding to the media state. Distinguishes between
391 * the Inaccessible state and the situation when querying the state itself
392 * failed.
393 *
394 * In "don't show diffs" mode (where the attributes of the base hard disk are
395 * shown instead of the attributes of the differencing hard disk), the most
396 * worst media state on the given hard disk chain will be used to select the
397 * media icon.
398 *
399 * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
400 * @param fCheckRO @c true to perform the #readOnly() check and change the icon
401 * accordingly.
402 */
403QPixmap UIMedium::icon(bool fNoDiffs /* = false */, bool fCheckRO /* = false */) const
404{
405 QPixmap pixmap;
406
407 if (state(fNoDiffs) == KMediumState_Inaccessible)
408 pixmap = result(fNoDiffs).isOk() ? vboxGlobal().warningIcon() : vboxGlobal().errorIcon();
409
410 if (fCheckRO && m_fReadOnly)
411 pixmap = VBoxGlobal::joinPixmaps(pixmap, QPixmap(":/hd_new_16px.png"));
412
413 return pixmap;
414}
415
416/**
417 * Returns the details of this medium as a single-line string
418 *
419 * For hard disks, the details include the location, type and the logical size
420 * of the hard disk. Note that if @a fNoDiffs is @c true, these properties are
421 * queried on the root hard disk of the given hard disk because the primary
422 * purpose of the returned string is to be human readable (so that seeing a
423 * complex diff hard disk name is usually not desirable).
424 *
425 * For other media types, the location and the actual size are returned.
426 * Arguments @a fPredictDiff and @a aNoRoot are ignored in this case.
427 *
428 * @param fNoDiffs @c true to enable user-friendly "don't show diffs" mode.
429 * @param fPredictDiff @c true to mark the hard disk as differencing if
430 * attaching it would create a differencing hard disk (not
431 * used when @a aNoRoot is true).
432 * @param fUseHTML @c true to allow for emphasizing using bold and italics.
433 *
434 * @note Use #detailsHTML() instead of passing @c true for @a fUseHTML.
435 *
436 * @note The media object may become uninitialized by a third party while this
437 * method is reading its properties. In this case, the method will return
438 * an empty string.
439 */
440QString UIMedium::details(bool fNoDiffs /* = false */,
441 bool fPredictDiff /* = false */,
442 bool fUseHTML /* = false */) const
443{
444 // @todo the below check is rough; if m_medium becomes uninitialized, any
445 // of getters called afterwards will also fail. The same relates to the
446 // root hard disk object (that will be the hard disk itself in case of
447 // non-differencing disks). However, this check was added to fix a
448 // particular use case: when the hard disk is a differencing hard disk and
449 // it happens to be discarded (and uninitialized) after this method is
450 // called but before we read all its properties (yes, it's possible!), the
451 // root object will be null and calling methods on it will assert in the
452 // debug builds. This check seems to be enough as a quick solution (fresh
453 // hard disk attachments will be re-read by a machine state change signal
454 // after the discard operation is finished, so the user will eventually see
455 // correct data), but in order to solve the problem properly we need to use
456 // exceptions everywhere (or check the result after every method call). See
457 // @bugref{2149}.
458
459 if (m_medium.isNull() || m_fHostDrive)
460 return m_strName;
461
462 if (!m_medium.isOk())
463 return QString();
464
465 QString strDetails, strText;
466
467 UIMedium *pRoot = unconst(this);
468 KMediumState eState = m_state;
469
470 if (m_type == UIMediumType_HardDisk)
471 {
472 if (fNoDiffs)
473 {
474 pRoot = &this->root();
475
476 bool isDiff = (!fPredictDiff && m_pParent != NULL) || (fPredictDiff && m_fReadOnly);
477
478 strDetails = isDiff && fUseHTML ?
479 QString("<i>%1</i>, ").arg(pRoot->m_strHardDiskType) :
480 QString("%1, ").arg(pRoot->m_strHardDiskType);
481
482 eState = this->state(true /* fNoDiffs */);
483
484 if (pRoot->m_state == KMediumState_NotCreated)
485 eState = KMediumState_NotCreated;
486 }
487 else
488 {
489 strDetails = QString("%1, ").arg(pRoot->m_strHardDiskType);
490 }
491 }
492
493 // @todo prepend the details with the warning/error icon when not accessible
494
495 switch (eState)
496 {
497 case KMediumState_NotCreated:
498 strText = VBoxGlobal::tr("Checking...", "medium");
499 strDetails += fUseHTML ? QString("<i>%1</i>").arg(strText) : strText;
500 break;
501 case KMediumState_Inaccessible:
502 strText = VBoxGlobal::tr("Inaccessible", "medium");
503 strDetails += fUseHTML ? QString("<b>%1</b>").arg(strText) : strText;
504 break;
505 default:
506 strDetails += m_type == UIMediumType_HardDisk ? pRoot->m_strLogicalSize : pRoot->m_strSize;
507 break;
508 }
509
510 strDetails = fUseHTML ?
511 QString("%1 (<nobr>%2</nobr>)").arg(VBoxGlobal::locationForHTML(pRoot->m_strName), strDetails) :
512 QString("%1 (%2)").arg(VBoxGlobal::locationForHTML(pRoot->m_strName), strDetails);
513
514 return strDetails;
515}
516
517/**
518 * Checks if m_noDiffs is filled in and does it if not.
519 *
520 * @param fNoDiffs @if false, this method immediately returns.
521 */
522void UIMedium::checkNoDiffs(bool fNoDiffs)
523{
524 if (!fNoDiffs || m_noDiffs.isSet)
525 return;
526
527 m_noDiffs.toolTip = QString();
528
529 m_noDiffs.state = m_state;
530 for (UIMedium *cur = m_pParent; cur != NULL; cur = cur->m_pParent)
531 {
532 if (cur->m_state == KMediumState_Inaccessible)
533 {
534 m_noDiffs.state = cur->m_state;
535
536 if (m_noDiffs.toolTip.isNull())
537 m_noDiffs.toolTip = m_sstrRow.arg(VBoxGlobal::tr("Some of the media in this hard disk chain "
538 "are inaccessible. Please use the Virtual Media "
539 "Manager in <b>Show Differencing Hard Disks</b> "
540 "mode to inspect these media.", "medium"));
541
542 if (!cur->m_result.isOk())
543 {
544 m_noDiffs.result = cur->m_result;
545 break;
546 }
547 }
548 }
549
550 if (m_pParent != NULL && !m_fReadOnly)
551 {
552 m_noDiffs.toolTip = root().tip() +
553 m_sstrRow.arg("<hr>") +
554 m_sstrRow.arg(VBoxGlobal::tr("This base hard disk is indirectly attached using "
555 "the following differencing hard disk:", "medium")) +
556 m_strToolTip + m_noDiffs.toolTip;
557 }
558
559 if (m_noDiffs.toolTip.isNull())
560 m_noDiffs.toolTip = m_strToolTip;
561
562 m_noDiffs.isSet = true;
563}
564
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