VirtualBox

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

Last change on this file since 41774 was 41774, checked in by vboxsync, 13 years ago

bugref..

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