VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/selector/UIVMPreviewWindow.cpp@ 35740

Last change on this file since 35740 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1/* $Id: UIVMPreviewWindow.cpp 33540 2010-10-28 09:27:05Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIPreviewWindow class implementation
6 */
7
8/*
9 * Copyright (C) 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/* Local includes */
21#include "UIVMPreviewWindow.h"
22#include "UIVirtualBoxEventHandler.h"
23#include "UIImageTools.h"
24#include "VBoxGlobal.h"
25
26/* Global includes */
27#include <QContextMenuEvent>
28#include <QMenu>
29#include <QPainter>
30#include <QTimer>
31
32UIVMPreviewWindow::UIVMPreviewWindow(QWidget *pParent)
33 : QIWithRetranslateUI<QWidget>(pParent)
34 , m_machineState(KMachineState_Null)
35 , m_pUpdateTimer(new QTimer(this))
36 , m_vMargin(10)
37 , m_pbgImage(0)
38 , m_pPreviewImg(0)
39 , m_pGlossyImg(0)
40{
41 m_session.createInstance(CLSID_Session);
42
43 setContentsMargins(0, 5, 0, 5);
44 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
45 /* Connect the update timer */
46 connect(m_pUpdateTimer, SIGNAL(timeout()),
47 this, SLOT(sltRecreatePreview()));
48 /* Connect the machine state event */
49 connect(gVBoxEvents, SIGNAL(sigMachineStateChange(QString, KMachineState)),
50 this, SLOT(sltMachineStateChange(QString, KMachineState)));
51 /* Create the context menu */
52 setContextMenuPolicy(Qt::DefaultContextMenu);
53 m_pUpdateTimerMenu = new QMenu(this);
54 QActionGroup *pUpdateTimeG = new QActionGroup(this);
55 pUpdateTimeG->setExclusive(true);
56 for(int i = 0; i < UpdateEnd; ++i)
57 {
58 QAction *pUpdateTime = new QAction(pUpdateTimeG);
59 pUpdateTime->setData(i);
60 pUpdateTime->setCheckable(true);
61 pUpdateTimeG->addAction(pUpdateTime);
62 m_pUpdateTimerMenu->addAction(pUpdateTime);
63 m_actions[static_cast<UpdateInterval>(i)] = pUpdateTime;
64 }
65 m_pUpdateTimerMenu->insertSeparator(m_actions[static_cast<UpdateInterval>(Update500ms)]);
66 /* Default value */
67 UpdateInterval interval = Update1000ms;
68 QString strInterval = vboxGlobal().virtualBox().GetExtraData(VBoxDefs::GUI_PreviewUpdate);
69 if (strInterval == "disabled")
70 interval = UpdateDisabled;
71 else if (strInterval == "500")
72 interval = Update500ms;
73 else if (strInterval == "1000")
74 interval = Update1000ms;
75 else if (strInterval == "2000")
76 interval = Update2000ms;
77 else if (strInterval == "5000")
78 interval = Update5000ms;
79 else if (strInterval == "10000")
80 interval = Update10000ms;
81 /* Initialize with the new update interval */
82 setUpdateInterval(interval, false);
83
84 /* Retranslate the UI */
85 retranslateUi();
86}
87
88UIVMPreviewWindow::~UIVMPreviewWindow()
89{
90 /* Close any open session */
91 if (m_session.GetState() == KSessionState_Locked)
92 m_session.UnlockMachine();
93 if (m_pbgImage)
94 delete m_pbgImage;
95 if (m_pGlossyImg)
96 delete m_pGlossyImg;
97}
98
99void UIVMPreviewWindow::setMachine(const CMachine& machine)
100{
101 m_pUpdateTimer->stop();
102 m_machine = machine;
103 restart();
104}
105
106CMachine UIVMPreviewWindow::machine() const
107{
108 return m_machine;
109}
110
111QSize UIVMPreviewWindow::sizeHint() const
112{
113 return QSize(220, 220 * 3.0/4.0);
114}
115
116void UIVMPreviewWindow::retranslateUi()
117{
118 m_actions.value(UpdateDisabled)->setText(tr("Update Disabled"));
119 m_actions.value(Update500ms)->setText(tr("Every 0.5 s"));
120 m_actions.value(Update1000ms)->setText(tr("Every 1 s"));
121 m_actions.value(Update2000ms)->setText(tr("Every 2 s"));
122 m_actions.value(Update5000ms)->setText(tr("Every 5 s"));
123 m_actions.value(Update10000ms)->setText(tr("Every 10 s"));
124}
125
126void UIVMPreviewWindow::resizeEvent(QResizeEvent *pEvent)
127{
128 repaintBGImages();
129 QWidget::resizeEvent(pEvent);
130}
131
132void UIVMPreviewWindow::showEvent(QShowEvent *pEvent)
133{
134 /* Make sure there is some valid preview image when shown. */
135 restart();
136 QWidget::showEvent(pEvent);
137}
138
139void UIVMPreviewWindow::hideEvent(QHideEvent *pEvent)
140{
141 /* Stop the update time when we aren't visible */
142 m_pUpdateTimer->stop();
143 QWidget::hideEvent(pEvent);
144}
145
146void UIVMPreviewWindow::paintEvent(QPaintEvent *pEvent)
147{
148 QPainter painter(this);
149 /* Enable clipping */
150 painter.setClipRect(pEvent->rect());
151 /* Where should the content go */
152 QRect cr = contentsRect();
153 /* Draw the background with the monitor and the shadow */
154 if (m_pbgImage)
155 painter.drawImage(cr.x(), cr.y(), *m_pbgImage);
156// painter.setPen(Qt::red);
157// painter.drawRect(cr.adjusted(0, 0, -1, -1));
158// return;
159 /* If there is a preview image available, use it. */
160 if (m_pPreviewImg)
161 painter.drawImage(0, 0, *m_pPreviewImg);
162 else
163 {
164 QString strName = tr("No Preview");
165 if (!m_machine.isNull())
166 strName = m_machine.GetName();
167
168 /* Paint the name in the center of the monitor */
169 painter.fillRect(m_vRect, Qt::black);
170 QFont font = painter.font();
171 font.setBold(true);
172 int fFlags = Qt::AlignCenter | Qt::TextWordWrap;
173 float h = m_vRect.size().height() * .2;
174 QRect r;
175 /* Make a little magic to find out if the given text fits into
176 * our rectangle. Decrease the font pixel size as long as it
177 * doesn't fit. */
178 int cMax = 30;
179 do
180 {
181 h = h * .8;
182 font.setPixelSize(h);
183 painter.setFont(font);
184 r = painter.boundingRect(m_vRect, fFlags, strName);
185 }while (( r.height() > m_vRect.height()
186 || r.width() > m_vRect.width())
187 && cMax-- != 0);
188 painter.setPen(Qt::white);
189 painter.drawText(m_vRect, fFlags, strName);
190 }
191 /* Draw the glossy overlay last */
192 if (m_pGlossyImg)
193 painter.drawImage(m_vRect.x(), m_vRect.y(), *m_pGlossyImg);
194}
195
196void UIVMPreviewWindow::contextMenuEvent(QContextMenuEvent *pEvent)
197{
198 QAction *pReturn = m_pUpdateTimerMenu->exec(pEvent->globalPos(), 0);
199 if (pReturn)
200 {
201 UpdateInterval interval = static_cast<UpdateInterval>(pReturn->data().toInt());
202 setUpdateInterval(interval, true);
203 restart();
204 }
205}
206
207void UIVMPreviewWindow::sltMachineStateChange(QString strId, KMachineState state)
208{
209 if ( !m_machine.isNull()
210 && m_machine.GetId() == strId)
211 {
212 /* Cache the machine state */
213 m_machineState = state;
214 restart();
215 }
216}
217
218void UIVMPreviewWindow::sltRecreatePreview()
219{
220 /* Only do this if we are visible */
221 if (!isVisible())
222 return;
223
224 if (m_pPreviewImg)
225 {
226 delete m_pPreviewImg;
227 m_pPreviewImg = 0;
228 }
229
230 if (!m_machine.isNull())
231 {
232 Assert(m_machineState != KMachineState_Null);
233 QImage image(size(), QImage::Format_ARGB32);
234 image.fill(Qt::transparent);
235 QPainter painter(&image);
236 bool fDone = false;
237
238 /* Preview enabled? */
239 if (m_pUpdateTimer->interval() > 0)
240 {
241 /* Use the image which may be included in the save state. */
242 if ( m_machineState == KMachineState_Saved
243 || m_machineState == KMachineState_Restoring)
244 {
245 ULONG width = 0, height = 0;
246 QVector<BYTE> screenData = m_machine.ReadSavedScreenshotPNGToArray(0, width, height);
247 if (screenData.size() != 0)
248 {
249 QImage shot = QImage::fromData(screenData.data(), screenData.size(), "PNG").scaled(m_vRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
250 dimImage(shot);
251 painter.drawImage(m_vRect.x(), m_vRect.y(), shot);
252 fDone = true;
253 }
254 }
255 /* Use the current VM output. */
256 else if ( m_machineState == KMachineState_Running
257// || m_machineState == KMachineState_Saving /* Not sure if this is valid */
258 || m_machineState == KMachineState_Paused)
259 {
260 if (m_session.GetState() == KSessionState_Locked)
261 {
262 CVirtualBox vbox = vboxGlobal().virtualBox();
263 if (vbox.isOk())
264 {
265 const CConsole& console = m_session.GetConsole();
266 if (!console.isNull())
267 {
268 CDisplay display = console.GetDisplay();
269 /* Todo: correct aspect radio */
270// ULONG w, h, bpp;
271// display.GetScreenResolution(0, w, h, bpp);
272// QImage shot = QImage(w, h, QImage::Format_RGB32);
273// shot.fill(Qt::black);
274// display.TakeScreenShot(0, shot.bits(), shot.width(), shot.height());
275 QVector<BYTE> screenData = display.TakeScreenShotToArray(0, m_vRect.width(), m_vRect.height());
276 if ( display.isOk()
277 && screenData.size() != 0)
278 {
279 /* Unfortunately we have to reorder the pixel
280 * data, cause the VBox API returns RGBA data,
281 * which is not a format QImage understand.
282 * Todo: check for 32bit alignment, for both
283 * the data and the scanlines. Maybe we need to
284 * copy the data in any case. */
285 uint32_t *d = (uint32_t*)screenData.data();
286 for (int i = 0; i < screenData.size() / 4; ++i)
287 {
288 uint32_t e = d[i];
289 d[i] = RT_MAKE_U32_FROM_U8(RT_BYTE3(e), RT_BYTE2(e), RT_BYTE1(e), RT_BYTE4(e));
290 }
291
292 QImage shot = QImage((uchar*)d, m_vRect.width(), m_vRect.height(), QImage::Format_RGB32);
293
294 if (m_machineState == KMachineState_Paused)
295 dimImage(shot);
296 painter.drawImage(m_vRect.x(), m_vRect.y(), shot);
297 fDone = true;
298 }
299 }
300 }
301 }
302 }
303 }
304 if (fDone)
305 m_pPreviewImg = new QImage(image);
306 }
307 update();
308}
309
310void UIVMPreviewWindow::setUpdateInterval(UpdateInterval interval, bool fSave)
311{
312 switch (interval)
313 {
314 case UpdateDisabled:
315 {
316 if (fSave)
317 vboxGlobal().virtualBox().SetExtraData(VBoxDefs::GUI_PreviewUpdate, "disabled");
318 m_pUpdateTimer->setInterval(0);
319 m_pUpdateTimer->stop();
320 m_actions[interval]->setChecked(true);
321 break;
322 }
323 case Update500ms:
324 {
325 if (fSave)
326 vboxGlobal().virtualBox().SetExtraData(VBoxDefs::GUI_PreviewUpdate, "500");
327 m_pUpdateTimer->setInterval(500);
328 m_actions[interval]->setChecked(true);
329 break;
330 }
331 case Update1000ms:
332 {
333 if (fSave)
334 vboxGlobal().virtualBox().SetExtraData(VBoxDefs::GUI_PreviewUpdate, "1000");
335 m_pUpdateTimer->setInterval(1000);
336 m_actions[interval]->setChecked(true);
337 break;
338 }
339 case Update2000ms:
340 {
341 if (fSave)
342 vboxGlobal().virtualBox().SetExtraData(VBoxDefs::GUI_PreviewUpdate, "2000");
343 m_pUpdateTimer->setInterval(2000);
344 m_actions[interval]->setChecked(true);
345 break;
346 }
347 case Update5000ms:
348 {
349 if (fSave)
350 vboxGlobal().virtualBox().SetExtraData(VBoxDefs::GUI_PreviewUpdate, "5000");
351 m_pUpdateTimer->setInterval(5000);
352 m_actions[interval]->setChecked(true);
353 break;
354 }
355 case Update10000ms:
356 {
357 if (fSave)
358 vboxGlobal().virtualBox().SetExtraData(VBoxDefs::GUI_PreviewUpdate, "10000");
359 m_pUpdateTimer->setInterval(10000);
360 m_actions[interval]->setChecked(true);
361 break;
362 }
363 case UpdateEnd: break;
364 }
365}
366
367void UIVMPreviewWindow::restart()
368{
369 /* Close any open session */
370 if (m_session.GetState() == KSessionState_Locked)
371 m_session.UnlockMachine();
372 if (!m_machine.isNull())
373 {
374 /* Fetch the latest machine state */
375 m_machineState = m_machine.GetState();
376 /* Lock the session for the current machine */
377 if ( m_machineState == KMachineState_Running
378// || m_machineState == KMachineState_Saving /* Not sure if this is valid */
379 || m_machineState == KMachineState_Paused)
380 m_machine.LockMachine(m_session, KLockType_Shared);
381 }
382
383 /* Recreate the preview image */
384 sltRecreatePreview();
385 /* Start the timer */
386 if (!m_machine.isNull())
387 {
388 if ( m_pUpdateTimer->interval() > 0
389 && m_machineState == KMachineState_Running)
390 m_pUpdateTimer->start();
391 }
392}
393
394void UIVMPreviewWindow::repaintBGImages()
395{
396 /* Delete the old images */
397 if (m_pbgImage)
398 delete m_pbgImage;
399 if (m_pGlossyImg)
400 delete m_pGlossyImg;
401
402 /* Check that there is enough room for our fancy stuff. If not we just
403 * draw nothing. */
404 QRect cr = contentsRect();
405 if ( cr.width() < 30
406 || cr.height() < 30)
407 return;
408
409 QPalette pal = palette();
410 m_wRect = cr.adjusted(10, 10, -10, -10);
411 m_vRect = m_wRect.adjusted(m_vMargin, m_vMargin, -m_vMargin, -m_vMargin).adjusted(-3, -3, 3, 3);
412
413 /* First draw the shadow. Its a rounded rectangle which get blurred. */
414 QImage imageW(cr.size(), QImage::Format_ARGB32);
415 QColor bg = pal.color(QPalette::Base);
416 bg.setAlpha(0); /* We want blur to transparent _and_ whatever the base color is. */
417 imageW.fill(bg.rgba());
418 QPainter pW(&imageW);
419 pW.setBrush(QColor(30, 30, 30)); /* Dark gray */
420 pW.setPen(Qt::NoPen);
421 pW.drawRoundedRect(QRect(QPoint(0, 0), cr.size()).adjusted(10, 10, -10, -10), m_vMargin, m_vMargin);
422 pW.end();
423 /* Blur the rectangle */
424 QImage imageO(cr.size(), QImage::Format_ARGB32);
425 blurImage(imageW, imageO, 10);
426 QPainter pO(&imageO);
427
428#if 1
429 /* Now paint the border with a gradient to get a look of a monitor. */
430 QRect rr = QRect(QPoint(0, 0), cr.size()).adjusted(10, 10, -10, -10);
431 QLinearGradient lg(0, rr.y(), 0, rr.height());
432 QColor base(200, 200, 200); /* light variant */
433 // QColor base(80, 80, 80); /* Dark variant */
434 lg.setColorAt(0, base);
435 lg.setColorAt(0.4, base.darker(300));
436 lg.setColorAt(0.5, base.darker(400));
437 lg.setColorAt(0.7, base.darker(300));
438 lg.setColorAt(1, base);
439 pO.setBrush(lg);
440 pO.setPen(QPen(base.darker(150), 1));
441 pO.drawRoundedRect(rr, m_vMargin, m_vMargin);
442 pO.end();
443#endif
444 /* Make a copy of the new bg image */
445 m_pbgImage = new QImage(imageO);
446
447 /* Now the glossy overlay has to be created. Start with defining a nice
448 * looking painter path. */
449 QRect gRect = QRect(QPoint(0, 0), m_vRect.size());
450 QPainterPath glossyPath(QPointF(gRect.x(), gRect.y()));
451 glossyPath.lineTo(gRect.x() + gRect.width(), gRect.y());
452 glossyPath.lineTo(gRect.x() + gRect.width(), gRect.y() + gRect.height() * 1.0/3.0);
453 glossyPath.cubicTo(gRect.x() + gRect.width() / 2.0, gRect.y() + gRect.height() * 1.0/3.0,
454 gRect.x() + gRect.width() / 2.0, gRect.y() + gRect.height() * 2.0/3.0,
455 gRect.x(), gRect.y() + gRect.height() * 2.0/3.0);
456 glossyPath.closeSubpath();
457
458 /* Paint the glossy path on a QImage */
459 QImage image(m_vRect.size(), QImage::Format_ARGB32);
460 QColor bg1(Qt::white); /* We want blur to transparent _and_ white. */
461 bg1.setAlpha(0);
462 image.fill(bg1.rgba());
463 QPainter painter(&image);
464 painter.fillPath(glossyPath, QColor(255, 255, 255, 80));
465 painter.end();
466 /* Blur the image to get a much more smooth feeling */
467 QImage image1(m_vRect.size(), QImage::Format_ARGB32);
468 blurImage(image, image1, 7);
469 m_pGlossyImg = new QImage(image1);
470
471 /* Repaint */
472 update();
473}
474
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use