VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/manager/details/UIMachinePreview.cpp

Last change on this file was 104251, checked in by vboxsync, 8 weeks ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the manager UI classes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Frontends/VirtualBox/src/selector/UIVMPreviewWindow.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Frontends/VirtualBox/src/selector/UIVMPreviewWindow.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Frontends/VirtualBox/src/selector/UIVMPreviewWindow.cpp70873
    /branches/VBox-4.1/src/VBox/Frontends/VirtualBox/src/selector/UIVMPreviewWindow.cpp74233
    /branches/VBox-4.2/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGMachinePreview.cpp91503-91504,​91506-91508,​91510,​91514-91515,​91521
    /branches/VBox-4.3/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGMachinePreview.cpp91223
    /branches/VBox-4.3/trunk/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGMachinePreview.cpp91223
    /branches/dsen/gui/src/VBox/Frontends/VirtualBox/src/selector/UIVMPreviewWindow.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGMachinePreview.cpp79562-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Frontends/VirtualBox/src/selector/graphics/details/UIGMachinePreview.cpp79645-79692
    /trunk/src/VBox/Frontends/VirtualBox/src/selector/UIVMPreviewWindow.cpp79225,​79271
File size: 21.8 KB
Line 
1/* $Id: UIMachinePreview.cpp 104251 2024-04-09 12:36:47Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMachinePreview class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QActionGroup>
30#include <QApplication>
31#include <QGraphicsSceneContextMenuEvent>
32#include <QMenu>
33#include <QPainter>
34#include <QStyle>
35#include <QTimer>
36
37/* GUI includes: */
38#include "UIExtraDataManager.h"
39#include "UIConverter.h"
40#include "UIIconPool.h"
41#include "UIMachinePreview.h"
42#include "UIImageTools.h"
43#include "UITranslationEventListener.h"
44#include "UIVirtualBoxEventHandler.h"
45
46/* COM includes: */
47#include "CConsole.h"
48#include "CDisplay.h"
49
50/* VirtualBox interface declarations: */
51#include <VBox/com/VirtualBox.h>
52
53
54UIMachinePreview::UIMachinePreview(QIGraphicsWidget *pParent)
55 : QIGraphicsWidget(pParent)
56 , m_pUpdateTimer(new QTimer(this))
57 , m_pUpdateTimerMenu(0)
58 , m_dRatio((double)QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 16)
59 , m_iMargin(0)
60 , m_enmPreset(AspectRatioPreset_16x9)
61 , m_pPreviewImg(0)
62{
63 prepare();
64}
65
66UIMachinePreview::~UIMachinePreview()
67{
68 cleanup();
69}
70
71void UIMachinePreview::setMachine(const CMachine& comMachine)
72{
73 /* Pause: */
74 stop();
75
76 /* Assign new machine: */
77 m_comMachine = comMachine;
78
79 /* Fetch machine data: */
80 m_strPreviewName = tr("No preview");
81 if (!m_comMachine.isNull())
82 m_strPreviewName = m_comMachine.GetAccessible() ? m_comMachine.GetName() :
83 tr("Inaccessible");
84
85 /* Resume: */
86 restart();
87}
88
89CMachine UIMachinePreview::machine() const
90{
91 return m_comMachine;
92}
93
94void UIMachinePreview::sltRetranslateUI()
95{
96 /* Translate actions: */
97 m_actions.value(PreviewUpdateIntervalType_Disabled)->setText(tr("Update disabled"));
98 m_actions.value(PreviewUpdateIntervalType_500ms)->setText(tr("Every 0.5 s"));
99 m_actions.value(PreviewUpdateIntervalType_1000ms)->setText(tr("Every 1 s"));
100 m_actions.value(PreviewUpdateIntervalType_2000ms)->setText(tr("Every 2 s"));
101 m_actions.value(PreviewUpdateIntervalType_5000ms)->setText(tr("Every 5 s"));
102 m_actions.value(PreviewUpdateIntervalType_10000ms)->setText(tr("Every 10 s"));
103}
104
105void UIMachinePreview::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
106{
107 recalculatePreviewRectangle();
108 sltRecreatePreview();
109
110 /* Call to base-class: */
111 QIGraphicsWidget::resizeEvent(pEvent);
112}
113
114void UIMachinePreview::showEvent(QShowEvent *pEvent)
115{
116 restart();
117
118 /* Call to base-class: */
119 QIGraphicsWidget::showEvent(pEvent);
120}
121
122void UIMachinePreview::hideEvent(QHideEvent *pEvent)
123{
124 stop();
125
126 /* Call to base-class: */
127 QIGraphicsWidget::hideEvent(pEvent);
128}
129
130void UIMachinePreview::contextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent)
131{
132 QAction *pReturn = m_pUpdateTimerMenu->exec(pEvent->screenPos(), 0);
133 if (pReturn)
134 {
135 PreviewUpdateIntervalType enmInterval = static_cast<PreviewUpdateIntervalType>(pReturn->data().toInt());
136 setUpdateInterval(enmInterval, true);
137 restart();
138 }
139}
140
141void UIMachinePreview::paint(QPainter *pPainter, const QStyleOptionGraphicsItem*, QWidget*)
142{
143 /* Where should the content go: */
144 QRect cr = contentsRect().toRect();
145 if (!cr.isValid())
146 return;
147
148 /* If there is a preview image available: */
149 if (m_pPreviewImg)
150 {
151 /* Draw empty monitor frame: */
152 pPainter->drawPixmap(cr.x() + m_iMargin, cr.y() + m_iMargin, *m_emptyPixmaps.value(m_enmPreset));
153
154 /* Move image to viewport center: */
155 QRect imageRect(QPoint(0, 0), m_pPreviewImg->size());
156 imageRect.moveCenter(m_vRect.center());
157
158#ifdef VBOX_WS_MAC
159 /* Set composition-mode to opaque: */
160 pPainter->setCompositionMode(QPainter::CompositionMode_Source);
161 /* Replace translucent background with black one: */
162 pPainter->fillRect(imageRect, QColor(Qt::black));
163 /* Return default composition-mode back: */
164 pPainter->setCompositionMode(QPainter::CompositionMode_SourceAtop);
165#endif /* VBOX_WS_MAC */
166
167 /* Draw preview image: */
168 pPainter->drawImage(imageRect.topLeft(), *m_pPreviewImg);
169 }
170 else
171 {
172 /* Draw full monitor frame: */
173 pPainter->drawPixmap(cr.x() + m_iMargin, cr.y() + m_iMargin, *m_fullPixmaps.value(m_enmPreset));
174
175 /* Paint preview name: */
176 QFont font = pPainter->font();
177 font.setBold(true);
178 int fFlags = Qt::AlignCenter | Qt::TextWordWrap;
179 float h = m_vRect.size().height() * .2;
180 QRect r;
181 /* Make a little magic to find out if the given text fits into our rectangle.
182 * Decrease the font pixel size as long as it doesn't fit. */
183 int cMax = 30;
184 do
185 {
186 h = h * .8;
187 font.setPixelSize((int)h);
188 pPainter->setFont(font);
189 r = pPainter->boundingRect(m_vRect, fFlags, m_strPreviewName);
190 }
191 while ((r.height() > m_vRect.height() || r.width() > m_vRect.width()) && cMax-- != 0);
192 pPainter->setPen(Qt::white);
193 pPainter->drawText(m_vRect, fFlags, m_strPreviewName);
194 }
195}
196
197QSizeF UIMachinePreview::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
198{
199 if (enmWhich == Qt::MinimumSize)
200 {
201 AssertReturn(m_emptyPixmaps.contains(m_enmPreset),
202 QIGraphicsWidget::sizeHint(enmWhich, constraint));
203 QSize size = m_sizes.value(m_enmPreset);
204 if (m_iMargin != 0)
205 {
206 size.setWidth(size.width() - 2 * m_iMargin);
207 size.setHeight(size.height() - 2 * m_iMargin);
208 }
209 return size;
210 }
211
212 /* Call to base-class: */
213 return QIGraphicsWidget::sizeHint(enmWhich, constraint);
214}
215
216void UIMachinePreview::sltMachineStateChange(const QUuid &uId)
217{
218 /* Make sure its the event for our machine: */
219 if (m_comMachine.isNull() || m_comMachine.GetId() != uId)
220 return;
221
222 /* Restart the preview: */
223 restart();
224}
225
226void UIMachinePreview::sltRecreatePreview()
227{
228 /* Skip invisible preview: */
229 if (!isVisible())
230 return;
231
232 /* Cleanup previous image: */
233 if (m_pPreviewImg)
234 {
235 delete m_pPreviewImg;
236 m_pPreviewImg = 0;
237 }
238
239 /* Fetch actual machine-state: */
240 const KMachineState enmMachineState = m_comMachine.isNull() ? KMachineState_Null : m_comMachine.GetState();
241
242 /* We are creating preview only for assigned and accessible VMs: */
243 if (!m_comMachine.isNull() && enmMachineState != KMachineState_Null &&
244 m_vRect.width() > 0 && m_vRect.height() > 0)
245 {
246 /* Prepare image: */
247 QImage image;
248
249 /* Use 10x9 as the aspect-ratio preset by default: */
250 AspectRatioPreset enmPreset = AspectRatioPreset_16x9;
251
252 /* Preview update enabled? */
253 if (m_pUpdateTimer->interval() > 0)
254 {
255 /* Depending on machine state: */
256 switch (enmMachineState)
257 {
258 /* If machine is in SAVED/RESTORING state: */
259 case KMachineState_Saved:
260 case KMachineState_AbortedSaved:
261 case KMachineState_Restoring:
262 {
263 /* Use the screenshot from saved-state if possible: */
264 ULONG uGuestWidth = 0, uGuestHeight = 0;
265 QVector<BYTE> screenData = m_comMachine.ReadSavedScreenshotToArray(0, KBitmapFormat_PNG, uGuestWidth, uGuestHeight);
266
267 /* Make sure screen-data is OK: */
268 if (!m_comMachine.isOk() || screenData.isEmpty())
269 break;
270
271 if (uGuestWidth > 0 && uGuestHeight > 0)
272 {
273 /* Calculate aspect-ratio: */
274 const double dAspectRatio = (double)uGuestWidth / uGuestHeight;
275 /* Look for the best aspect-ratio preset: */
276 enmPreset = bestAspectRatioPreset(dAspectRatio, m_ratios);
277 }
278
279 /* Create image based on shallow copy or screenshot data,
280 * scale image down if necessary to the size possible to reflect: */
281 image = QImage::fromData(screenData.data(), screenData.size(), "PNG")
282 .scaled(imageAspectRatioSize(m_vRect.size(), QSize(uGuestWidth, uGuestHeight)),
283 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
284 /* And detach that copy to make it deep: */
285 image.detach();
286 /* Dim image to give it required look: */
287 dimImage(image);
288 break;
289 }
290 /* If machine is in RUNNING/PAUSED state: */
291 case KMachineState_Running:
292 case KMachineState_Paused:
293 {
294 /* Make sure session state is Locked: */
295 if (m_comSession.GetState() != KSessionState_Locked)
296 break;
297
298 /* Make sure console is OK: */
299 CConsole console = m_comSession.GetConsole();
300 if (!m_comSession.isOk() || console.isNull())
301 break;
302 /* Make sure display is OK: */
303 CDisplay display = console.GetDisplay();
304 if (!console.isOk() || display.isNull())
305 break;
306
307 /* Acquire guest-screen attributes: */
308 LONG iOriginX = 0, iOriginY = 0;
309 ULONG uGuestWidth = 0, uGuestHeight = 0, uBpp = 0;
310 KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
311 display.GetScreenResolution(0, uGuestWidth, uGuestHeight, uBpp, iOriginX, iOriginY, monitorStatus);
312 if (uGuestWidth > 0 && uGuestHeight > 0)
313 {
314 /* Calculate aspect-ratio: */
315 const double dAspectRatio = (double)uGuestWidth / uGuestHeight;
316 /* Look for the best aspect-ratio preset: */
317 enmPreset = bestAspectRatioPreset(dAspectRatio, m_ratios);
318 }
319
320 /* Calculate size corresponding to aspect-ratio: */
321 const QSize size = imageAspectRatioSize(m_vRect.size(), QSize(uGuestWidth, uGuestHeight));
322
323 /* Use direct VM content: */
324 QVector<BYTE> screenData = display.TakeScreenShotToArray(0, size.width(), size.height(), KBitmapFormat_BGR0);
325
326 /* Make sure screen-data is OK: */
327 if (!display.isOk() || screenData.isEmpty())
328 break;
329
330 /* Make sure screen-data size is valid: */
331 const int iExpectedSize = size.width() * size.height() * 4;
332 const int iActualSize = screenData.size();
333 if (iActualSize != iExpectedSize)
334 {
335 AssertMsgFailed(("Invalid screen-data size '%d', should be '%d'!\n", iActualSize, iExpectedSize));
336 break;
337 }
338
339 /* Create image based on shallow copy of acquired data: */
340 QImage tempImage(screenData.data(), size.width(), size.height(), QImage::Format_RGB32);
341 image = tempImage;
342 /* And detach that copy to make it deep: */
343 image.detach();
344 /* Dim image to give it required look for PAUSED state: */
345 if (enmMachineState == KMachineState_Paused)
346 dimImage(image);
347 break;
348 }
349 default:
350 break;
351 }
352 }
353
354 /* If image initialized: */
355 if (!image.isNull())
356 {
357 /* Shallow copy that image: */
358 m_pPreviewImg = new QImage(image);
359 /* And detach that copy to make it deep: */
360 m_pPreviewImg->detach();
361 }
362
363 /* If preset changed: */
364 if (m_enmPreset != enmPreset)
365 {
366 /* Save new preset: */
367 m_enmPreset = enmPreset;
368 /* And update geometry: */
369 updateGeometry();
370 emit sigSizeHintChanged();
371 }
372 }
373
374 /* Redraw preview in any case: */
375 update();
376}
377
378void UIMachinePreview::prepare()
379{
380 /* Create session instance: */
381 m_comSession.createInstance(CLSID_Session);
382
383 /* Cache aspect-ratio preset settings: */
384 const QIcon empty16x10 = UIIconPool::iconSet(":/preview_empty_16to10_242x167px.png");
385 const QIcon empty16x9 = UIIconPool::iconSet(":/preview_empty_16to9_242x155px.png");
386 const QIcon empty4x3 = UIIconPool::iconSet(":/preview_empty_4to3_242x192px.png");
387 const QIcon full16x10 = UIIconPool::iconSet(":/preview_full_16to10_242x167px.png");
388 const QIcon full16x9 = UIIconPool::iconSet(":/preview_full_16to9_242x155px.png");
389 const QIcon full4x3 = UIIconPool::iconSet(":/preview_full_4to3_242x192px.png");
390
391 // WORKAROUND:
392 // Since we don't have x3 and x4 HiDPI icons yet,
393 // and we hadn't enabled automatic up-scaling for now,
394 // we have to make sure m_dRatio is within possible bounds.
395 const QList<QSize> sizes = empty16x10.availableSizes();
396 if (sizes.size() >= 2)
397 m_dRatio = qMin(m_dRatio, (double)sizes.last().width() / sizes.first().width());
398
399 m_sizes.insert(AspectRatioPreset_16x10, QSize(242 * m_dRatio, 167 * m_dRatio));
400 m_sizes.insert(AspectRatioPreset_16x9, QSize(242 * m_dRatio, 155 * m_dRatio));
401 m_sizes.insert(AspectRatioPreset_4x3, QSize(242 * m_dRatio, 192 * m_dRatio));
402 m_ratios.insert(AspectRatioPreset_16x10, (double)16/10);
403 m_ratios.insert(AspectRatioPreset_16x9, (double)16/9);
404 m_ratios.insert(AspectRatioPreset_4x3, (double)4/3);
405 m_emptyPixmaps.insert(AspectRatioPreset_16x10, new QPixmap(empty16x10.pixmap(m_sizes.value(AspectRatioPreset_16x10))));
406 m_emptyPixmaps.insert(AspectRatioPreset_16x9, new QPixmap(empty16x9.pixmap(m_sizes.value(AspectRatioPreset_16x9))));
407 m_emptyPixmaps.insert(AspectRatioPreset_4x3, new QPixmap(empty4x3.pixmap(m_sizes.value(AspectRatioPreset_4x3))));
408 m_fullPixmaps.insert(AspectRatioPreset_16x10, new QPixmap(full16x10.pixmap(m_sizes.value(AspectRatioPreset_16x10))));
409 m_fullPixmaps.insert(AspectRatioPreset_16x9, new QPixmap(full16x9.pixmap(m_sizes.value(AspectRatioPreset_16x9))));
410 m_fullPixmaps.insert(AspectRatioPreset_4x3, new QPixmap(full4x3.pixmap(m_sizes.value(AspectRatioPreset_4x3))));
411
412 /* Setup contents (depends on presets above!): */
413 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
414
415 /* Create the context menu: */
416 m_pUpdateTimerMenu = new QMenu;
417 QActionGroup *pUpdateTimeG = new QActionGroup(this);
418 pUpdateTimeG->setExclusive(true);
419 for(int i = 0; i < PreviewUpdateIntervalType_Max; ++i)
420 {
421 QAction *pUpdateTime = new QAction(pUpdateTimeG);
422 pUpdateTime->setData(i);
423 pUpdateTime->setCheckable(true);
424 pUpdateTimeG->addAction(pUpdateTime);
425 m_pUpdateTimerMenu->addAction(pUpdateTime);
426 m_actions[static_cast<PreviewUpdateIntervalType>(i)] = pUpdateTime;
427 }
428 m_pUpdateTimerMenu->insertSeparator(m_actions[static_cast<PreviewUpdateIntervalType>(PreviewUpdateIntervalType_500ms)]);
429
430 /* Initialize with the new update interval: */
431 setUpdateInterval(gEDataManager->selectorWindowPreviewUpdateInterval(), false);
432
433 /* Setup connections: */
434 connect(m_pUpdateTimer, &QTimer::timeout, this, &UIMachinePreview::sltRecreatePreview);
435 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
436 this, &UIMachinePreview::sltMachineStateChange);
437
438 /* Retranslate the UI */
439 sltRetranslateUI();
440 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
441 this, &UIMachinePreview::sltRetranslateUI);
442}
443
444void UIMachinePreview::cleanup()
445{
446 /* Close any open session: */
447 if (m_comSession.GetState() == KSessionState_Locked)
448 m_comSession.UnlockMachine();
449
450 /* Destroy background images: */
451 foreach (const AspectRatioPreset &enmPreset, m_emptyPixmaps.keys())
452 {
453 delete m_emptyPixmaps.value(enmPreset);
454 m_emptyPixmaps.remove(enmPreset);
455 }
456 foreach (const AspectRatioPreset &enmPreset, m_fullPixmaps.keys())
457 {
458 delete m_fullPixmaps.value(enmPreset);
459 m_fullPixmaps.remove(enmPreset);
460 }
461
462 /* Destroy preview image: */
463 if (m_pPreviewImg)
464 delete m_pPreviewImg;
465
466 /* Destroy update timer: */
467 if (m_pUpdateTimerMenu)
468 delete m_pUpdateTimerMenu;
469}
470
471void UIMachinePreview::setUpdateInterval(PreviewUpdateIntervalType interval, bool fSave)
472{
473 switch (interval)
474 {
475 case PreviewUpdateIntervalType_Disabled:
476 {
477 /* Stop the timer: */
478 m_pUpdateTimer->stop();
479 /* And continue with other cases: */
480 }
481 RT_FALL_THRU();
482 case PreviewUpdateIntervalType_500ms:
483 case PreviewUpdateIntervalType_1000ms:
484 case PreviewUpdateIntervalType_2000ms:
485 case PreviewUpdateIntervalType_5000ms:
486 case PreviewUpdateIntervalType_10000ms:
487 {
488 /* Set the timer interval: */
489 m_pUpdateTimer->setInterval(gpConverter->toInternalInteger(interval));
490 /* Check corresponding action: */
491 m_actions[interval]->setChecked(true);
492 break;
493 }
494 case PreviewUpdateIntervalType_Max:
495 break;
496 }
497 if (fSave)
498 gEDataManager->setSelectorWindowPreviewUpdateInterval(interval);
499}
500
501void UIMachinePreview::recalculatePreviewRectangle()
502{
503 /* Contents rectangle: */
504 QRect cr = contentsRect().toRect();
505 m_vRect = cr.adjusted( 21 * m_dRatio + m_iMargin, 21 * m_dRatio + m_iMargin,
506 -21 * m_dRatio - m_iMargin, -21 * m_dRatio - m_iMargin);
507}
508
509void UIMachinePreview::restart()
510{
511 /* Fetch the latest machine-state: */
512 KMachineState enmMachineState = m_comMachine.isNull() ? KMachineState_Null : m_comMachine.GetState();
513
514 /* Reopen session if necessary: */
515 if (m_comSession.GetState() == KSessionState_Locked)
516 m_comSession.UnlockMachine();
517 if (!m_comMachine.isNull())
518 {
519 /* Lock the session for the current machine: */
520 if (enmMachineState == KMachineState_Running || enmMachineState == KMachineState_Paused)
521 m_comMachine.LockMachine(m_comSession, KLockType_Shared);
522 }
523
524 /* Recreate the preview image: */
525 sltRecreatePreview();
526
527 /* Start the timer if necessary: */
528 if (!m_comMachine.isNull())
529 {
530 if (m_pUpdateTimer->interval() > 0 && enmMachineState == KMachineState_Running)
531 m_pUpdateTimer->start();
532 }
533}
534
535void UIMachinePreview::stop()
536{
537 /* Stop the timer: */
538 m_pUpdateTimer->stop();
539}
540
541/* static */
542UIMachinePreview::AspectRatioPreset UIMachinePreview::bestAspectRatioPreset(const double dAspectRatio,
543 const QMap<AspectRatioPreset, double> &ratios)
544{
545 /* Use 16x9 preset as the 'best' by 'default': */
546 AspectRatioPreset bestPreset = AspectRatioPreset_16x9;
547 /* Calculate minimum diff based on 'default' preset: */
548 double dMinimumDiff = qAbs(dAspectRatio - ratios.value(bestPreset));
549 /* Now look for the 'best' aspect-ratio preset among existing: */
550 for (AspectRatioPreset currentPreset = AspectRatioPreset_16x10;
551 currentPreset <= AspectRatioPreset_4x3;
552 currentPreset = (AspectRatioPreset)(currentPreset + 1))
553 {
554 /* Calculate current diff based on 'current' preset: */
555 const double dDiff = qAbs(dAspectRatio - ratios.value(currentPreset));
556 /* If new 'best' preset found: */
557 if (dDiff < dMinimumDiff)
558 {
559 /* Remember new diff: */
560 dMinimumDiff = dDiff;
561 /* And new preset: */
562 bestPreset = currentPreset;
563 }
564 }
565 /* Return 'best' preset: */
566 return bestPreset;
567}
568
569/* static */
570QSize UIMachinePreview::imageAspectRatioSize(const QSize &hostSize, const QSize &guestSize)
571{
572 /* Make sure host-size and guest-size are valid: */
573 AssertReturn(!hostSize.isNull(), QSize());
574 if (guestSize.isNull())
575 return hostSize;
576
577 /* Calculate host/guest aspect-ratio: */
578 const double dHostAspectRatio = (double)hostSize.width() / hostSize.height();
579 const double dGuestAspectRatio = (double)guestSize.width() / guestSize.height();
580 int iWidth = 0, iHeight = 0;
581 /* Guest-screen more thin by vertical than host-screen: */
582 if (dGuestAspectRatio >= dHostAspectRatio)
583 {
584 /* Get host width: */
585 iWidth = hostSize.width();
586 /* And calculate height based on guest aspect ratio: */
587 iHeight = (int)((double)iWidth / dGuestAspectRatio);
588 /* But no more than host height: */
589 iHeight = qMin(iHeight, hostSize.height());
590 }
591 /* Host-screen more thin by vertical than guest-screen: */
592 else
593 {
594 /* Get host height: */
595 iHeight = hostSize.height();
596 /* And calculate width based on guest aspect ratio: */
597 iWidth = (int)((double)iHeight * dGuestAspectRatio);
598 /* But no more than host width: */
599 iWidth = qMin(iWidth, hostSize.width());
600 }
601 /* Return actual size: */
602 return QSize(iWidth, iHeight);
603}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use