VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/settings/UIAdvancedSettingsDialog.cpp

Last change on this file was 104358, checked in by vboxsync, 5 weeks ago

FE/Qt. bugref:10622. More refactoring around the retranslation functionality.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.5 KB
Line 
1/* $Id: UIAdvancedSettingsDialog.cpp 104358 2024-04-18 05:33:40Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIAdvancedSettingsDialog class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-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 <QAbstractButton>
30#include <QAbstractScrollArea>
31#include <QAbstractSpinBox>
32#include <QApplication>
33#include <QCheckBox>
34#include <QCloseEvent>
35#include <QComboBox>
36#include <QCoreApplication>
37#include <QGridLayout>
38#include <QPainter>
39#include <QPainterPath>
40#include <QProgressBar>
41#include <QPropertyAnimation>
42#include <QPushButton>
43#include <QScrollArea>
44#include <QScrollBar>
45#include <QSlider>
46#include <QStackedWidget>
47#include <QTimer>
48#include <QToolButton>
49#include <QVariant>
50#include <QVBoxLayout>
51
52/* GUI includes: */
53#include "QIDialogButtonBox.h"
54#include "QILineEdit.h"
55#include "UIAdvancedSettingsDialog.h"
56#include "UIAnimationFramework.h"
57#include "UICommon.h"
58#include "UIDesktopWidgetWatchdog.h"
59#include "UIExtraDataManager.h"
60#include "UIIconPool.h"
61#include "UIImageTools.h"
62#include "UILoggingDefs.h"
63#include "UIMessageCenter.h"
64#include "UIModalWindowManager.h"
65#include "UIPopupCenter.h"
66#include "UISettingsPage.h"
67#include "UISettingsPageValidator.h"
68#include "UISettingsSelector.h"
69#include "UISettingsSerializer.h"
70#include "UISettingsWarningPane.h"
71#include "UIShortcutPool.h"
72#include "UITranslationEventListener.h"
73#ifdef VBOX_WS_MAC
74# include "VBoxUtils.h"
75#endif
76
77
78/** QCheckBox subclass used as mode checkbox. */
79class UIModeCheckBox : public QCheckBox
80{
81 Q_OBJECT;
82
83public:
84
85 /** Constructs checkbox passing @a pParent to the base-class. */
86 UIModeCheckBox(QWidget *pParent);
87
88 /** Returns text 1. */
89 QString text1() const { return m_strText1; }
90 /** Defines @a strText1. */
91 void setText1(const QString &strText1) { m_strText1 = strText1; }
92 /** Returns text 2. */
93 QString text2() const { return m_strText2; }
94 /** Defines @a strText2. */
95 void setText2(const QString &strText2) { m_strText2 = strText2; }
96
97protected:
98
99 /** Handles any @a pEvent. */
100 virtual bool event(QEvent *pEvent) RT_OVERRIDE;
101
102 /** Handles paint @a pEvent. */
103 virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
104
105private:
106
107 /** Returns text 1. */
108 QString m_strText1;
109 /** Returns text 2. */
110 QString m_strText2;
111};
112
113
114/** QWidget reimplementation
115 * wrapping QILineEdit and
116 * representing filter editor for advanced settings dialog. */
117class UIFilterEditor : public QWidget
118{
119 Q_OBJECT;
120 Q_PROPERTY(int editorWidth READ editorWidth WRITE setEditorWidth);
121 Q_PROPERTY(int unfocusedEditorWidth READ unfocusedEditorWidth);
122 Q_PROPERTY(int focusedEditorWidth READ focusedEditorWidth);
123
124signals:
125
126 /** Notifies listeners about @a strText changed. */
127 void sigTextChanged(const QString &strText);
128
129 /** Notifies listeners about editor focused. */
130 void sigFocused();
131 /** Notifies listeners about editor unfocused. */
132 void sigUnfocused();
133
134public:
135
136 /** Constructs filter editor passing @a pParent to the base-class. */
137 UIFilterEditor(QWidget *pParent);
138 /** Destructs filter editor. */
139 virtual ~UIFilterEditor() RT_OVERRIDE;
140
141 /** Defines placeholder @a strText. */
142 void setPlaceholderText(const QString &strText);
143
144 /** Returns filter editor text. */
145 QString text() const;
146
147protected:
148
149 /** Returns the minimum widget size. */
150 virtual QSize minimumSizeHint() const RT_OVERRIDE;
151
152 /** Preprocesses Qt @a pEvent for passed @a pObject. */
153 virtual bool eventFilter(QObject *pObject, QEvent *pEvent) RT_OVERRIDE;
154
155 /** Handles resize @a pEvent. */
156 virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
157
158 /** Handles paint @a pEvent. */
159 virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
160
161private slots:
162
163 /** Handles editor @a strText change. */
164 void sltHandleEditorTextChanged(const QString &strText);
165 /** Handles button click. */
166 void sltHandleButtonClicked();
167
168private:
169
170 /** Prepares all. */
171 void prepare();
172 /** Cleanups all. */
173 void cleanup();
174
175 /** Returns painter path for the passed @a pathRect. */
176 static QPainterPath cookPainterPath(const QRect &pathRect, int iRadius);
177
178 /** Adjusts editor geometry. */
179 void adjustEditorGeometry();
180 /** Adjusts editor button icon. */
181 void adjustEditorButtonIcon();
182
183 /** Defines internal widget @a iWidth. */
184 void setEditorWidth(int iWidth);
185 /** Returns internal widget width. */
186 int editorWidth() const;
187 /** Returns internal widget width when it's unfocused. */
188 int unfocusedEditorWidth() const { return m_iUnfocusedEditorWidth; }
189 /** Returns internal widget width when it's focused. */
190 int focusedEditorWidth() const { return m_iFocusedEditorWidth; }
191
192 /** Holds the decoration radius. */
193 int m_iRadius;
194
195 /** Holds the filter editor instance. */
196 QILineEdit *m_pLineEdit;
197 /** Holds the filter reset button instance. */
198 QToolButton *m_pToolButton;
199
200 /** Holds whether filter editor focused. */
201 bool m_fFocused;
202 /** Holds unfocused filter editor width. */
203 int m_iUnfocusedEditorWidth;
204 /** Holds focused filter editor width. */
205 int m_iFocusedEditorWidth;
206 /** Holds the animation framework object. */
207 UIAnimation *m_pAnimation;
208};
209
210
211/** QScrollArea extension to be used for
212 * advanced settings dialog. The idea is to make
213 * vertical scroll-bar always visible, keeping
214 * horizontal scroll-bar always hidden. */
215class UIVerticalScrollArea : public QScrollArea
216{
217 Q_OBJECT;
218 Q_PROPERTY(int verticalScrollBarPosition READ verticalScrollBarPosition WRITE setVerticalScrollBarPosition);
219
220signals:
221
222 /** Notifies listeners about wheel-event. */
223 void sigWheelEvent();
224
225public:
226
227 /** Constructs vertical scroll-area passing @a pParent to the base-class. */
228 UIVerticalScrollArea(QWidget *pParent);
229
230 /** Returns vertical scrollbar position. */
231 int verticalScrollBarPosition() const;
232 /** Defines vertical scrollbar @a iPosition. */
233 void setVerticalScrollBarPosition(int iPosition) const;
234
235 /** Requests vertical scrollbar @a iPosition. */
236 void requestVerticalScrollBarPosition(int iPosition);
237
238protected:
239
240 /** Returns the minimum widget size. */
241 virtual QSize minimumSizeHint() const RT_OVERRIDE;
242
243 /** Handles wheel @a pEvent. */
244 virtual void wheelEvent(QWheelEvent *pEvent) RT_OVERRIDE;
245
246private:
247
248 /** Prepares all. */
249 void prepare();
250
251 /** Holds the vertical scrollbar animation instance. */
252 QPropertyAnimation *m_pAnimation;
253};
254
255
256/*********************************************************************************************************************************
257* Class UIModeCheckBox implementation. *
258*********************************************************************************************************************************/
259
260UIModeCheckBox::UIModeCheckBox(QWidget *pParent)
261 : QCheckBox(pParent)
262{
263 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
264}
265
266bool UIModeCheckBox::event(QEvent *pEvent)
267{
268 /* Handle desired events: */
269 switch (pEvent->type())
270 {
271 /* Handles mouse button press/release: */
272 case QEvent::MouseButtonPress:
273 case QEvent::MouseButtonRelease:
274 {
275 /* Handle release, ignore press: */
276 if (pEvent->type() == QEvent::MouseButtonRelease)
277 {
278 QMouseEvent *pMouseEvent = static_cast<QMouseEvent*>(pEvent);
279 setCheckState(pMouseEvent->pos().x() < width() / 2 ? Qt::Unchecked : Qt::Checked);
280 }
281 /* Prevent from handling somewhere else: */
282 pEvent->accept();
283 return true;
284 }
285
286 default:
287 break;
288 }
289
290 return QCheckBox::event(pEvent);
291}
292
293void UIModeCheckBox::paintEvent(QPaintEvent *pEvent)
294{
295 /* Prepare painter: */
296 QPainter painter(this);
297 painter.setRenderHint(QPainter::Antialiasing);
298 painter.setRenderHint(QPainter::TextAntialiasing);
299 /* Avoid painting more than necessary: */
300 painter.setClipRect(pEvent->rect());
301
302 /* Acquire useful properties: */
303 const QPalette pal = qApp->palette();
304 QRect contentRect = rect();
305#ifdef VBOX_WS_MAC
306 contentRect.setLeft(contentRect.left() + 2); /// @todo justify!
307 contentRect.setWidth(contentRect.width() - 10); /// @todo justify!
308#endif
309
310 /* Prepare left painter paths: */
311 QPainterPath painterPath1;
312 painterPath1.moveTo(contentRect.x(), contentRect.y());
313 painterPath1.lineTo(contentRect.width() / 2, contentRect.y());
314 painterPath1.lineTo(contentRect.width() / 2 - contentRect.height(), contentRect.height());
315 painterPath1.lineTo(contentRect.x(), contentRect.height());
316 painterPath1.closeSubpath();
317
318 /* Prepare right painter paths: */
319 QPainterPath painterPath2;
320 painterPath2.moveTo(contentRect.width() / 2, contentRect.y());
321 painterPath2.lineTo(contentRect.width(), contentRect.y());
322 painterPath2.lineTo(contentRect.width() - contentRect.height(), contentRect.height());
323 painterPath2.lineTo(contentRect.width() / 2 - contentRect.height(), contentRect.height());
324 painterPath2.closeSubpath();
325
326 /* Prepare left painting gradient: */
327 const QColor backColor1 = pal.color(QPalette::Active, isChecked() ? QPalette::Window : QPalette::Highlight);
328 const QColor bcTone11 = backColor1.lighter(isChecked() ? 120 : 100);
329 const QColor bcTone12 = backColor1.lighter(isChecked() ? 140 : 120);
330 QLinearGradient grad1(painterPath1.boundingRect().topLeft(), painterPath1.boundingRect().bottomRight());
331 grad1.setColorAt(0, bcTone11);
332 grad1.setColorAt(1, bcTone12);
333
334 /* Prepare right painting gradient: */
335 const QColor backColor2 = pal.color(QPalette::Active, isChecked() ? QPalette::Highlight : QPalette::Window);
336 const QColor bcTone21 = backColor2.lighter(isChecked() ? 100 : 120);
337 const QColor bcTone22 = backColor2.lighter(isChecked() ? 120 : 140);
338 QLinearGradient grad2(painterPath2.boundingRect().topLeft(), painterPath2.boundingRect().bottomRight());
339 grad2.setColorAt(0, bcTone21);
340 grad2.setColorAt(1, bcTone22);
341
342 /* Paint fancy shape: */
343 painter.save();
344 painter.fillPath(painterPath1, grad1);
345 painter.strokePath(painterPath1, uiCommon().isInDarkMode() ? backColor1.lighter(120) : backColor1.darker(110));
346 painter.fillPath(painterPath2, grad2);
347 painter.strokePath(painterPath2, uiCommon().isInDarkMode() ? backColor2.lighter(120) : backColor2.darker(110));
348 painter.restore();
349
350 /* Prepare text1/text2: */
351 const QFont fnt = font();
352 const QFontMetrics fm(fnt);
353 const QColor foreground1 = suitableForegroundColor(pal, backColor1);
354 const QString strName1 = text1();
355 const QPoint point1 = QPoint(contentRect.left() + 5 /** @todo justify! */,
356 contentRect.height() / 2 + fm.ascent() / 2 - 1 /* base line */);
357 const QColor foreground2 = suitableForegroundColor(pal, backColor2);
358 const QString strName2 = text2();
359 const QPoint point2 = QPoint(contentRect.width() / 2 + 1 + 5 /** @todo justify! */,
360 contentRect.height() / 2 + fm.ascent() / 2 - 1 /* base line */);
361
362 /* Paint text: */
363 painter.save();
364 painter.setFont(fnt);
365 painter.setPen(foreground1);
366 painter.drawText(point1, strName1);
367 painter.setPen(foreground2);
368 painter.drawText(point2, strName2);
369 painter.restore();
370}
371
372
373/*********************************************************************************************************************************
374* Class UIFilterEditor implementation. *
375*********************************************************************************************************************************/
376
377UIFilterEditor::UIFilterEditor(QWidget *pParent)
378 : QWidget(pParent)
379 , m_iRadius(0)
380 , m_pLineEdit(0)
381 , m_pToolButton(0)
382 , m_fFocused(false)
383 , m_iUnfocusedEditorWidth(0)
384 , m_iFocusedEditorWidth(0)
385 , m_pAnimation(0)
386{
387 prepare();
388}
389
390UIFilterEditor::~UIFilterEditor()
391{
392 cleanup();
393}
394
395void UIFilterEditor::setPlaceholderText(const QString &strText)
396{
397 if (m_pLineEdit)
398 {
399 m_pLineEdit->setPlaceholderText(strText);
400 adjustEditorGeometry();
401 }
402}
403
404QString UIFilterEditor::text() const
405{
406 return m_pLineEdit ? m_pLineEdit->text() : QString();
407}
408
409QSize UIFilterEditor::minimumSizeHint() const
410{
411 return m_pLineEdit ? m_pLineEdit->minimumSizeHint() : QWidget::minimumSizeHint();
412}
413
414bool UIFilterEditor::eventFilter(QObject *pObject, QEvent *pEvent)
415{
416 /* Preprocess events for m_pLineEdit only: */
417 if (pObject != m_pLineEdit)
418 return QWidget::eventFilter(pObject, pEvent);
419
420 /* Handles various event types: */
421 switch (pEvent->type())
422 {
423 /* Foreard animation on focus-in: */
424 case QEvent::FocusIn:
425 m_fFocused = true;
426 emit sigFocused();
427 update();
428 break;
429 /* Backward animation on focus-out: */
430 case QEvent::FocusOut:
431 m_fFocused = false;
432 emit sigUnfocused();
433 update();
434 break;
435 default:
436 break;
437 }
438
439 /* Call to base-class: */
440 return QWidget::eventFilter(pObject, pEvent);
441}
442
443void UIFilterEditor::resizeEvent(QResizeEvent *pEvent)
444{
445 /* Call to base-class: */
446 QWidget::resizeEvent(pEvent);
447
448 /* Adjust filter editor geometry on each parent resize: */
449 adjustEditorGeometry();
450}
451
452void UIFilterEditor::paintEvent(QPaintEvent *pEvent)
453{
454 /* Prepare painter: */
455 QPainter painter(this);
456 painter.setRenderHint(QPainter::Antialiasing);
457 painter.setRenderHint(QPainter::TextAntialiasing);
458 /* Avoid painting more than necessary: */
459 painter.setClipRect(pEvent->rect());
460
461 /* Prepare colors: */
462 const bool fActive = window() && window()->isActiveWindow();
463 const QPalette::ColorGroup enmColorGroup = fActive ? QPalette::Active : QPalette::Inactive;
464#ifdef VBOX_WS_MAC
465 const QColor colorHighlight = uiCommon().isInDarkMode()
466 ? qApp->palette().color(enmColorGroup, QPalette::Highlight).lighter(110)
467 : qApp->palette().color(enmColorGroup, QPalette::Highlight).darker(110);
468#endif
469 const QColor colorBase = qApp->palette().color(enmColorGroup, QPalette::Base);
470 const QColor colorFrame = uiCommon().isInDarkMode()
471 ? qApp->palette().color(enmColorGroup, QPalette::Window).lighter(120)
472 : qApp->palette().color(enmColorGroup, QPalette::Window).darker(120);
473
474 /* Prepare base/frame painter path: */
475 const QRegion totalRegion = QRegion(m_pLineEdit->geometry()) + QRegion(m_pToolButton->geometry());
476 QRect widgetRect = totalRegion.boundingRect();
477#ifdef VBOX_WS_MAC
478 const QRect focusRect = widgetRect;
479 widgetRect.adjust(3, 3, -3, -3);
480 const QPainterPath focusPath = cookPainterPath(focusRect, m_iRadius + 2);
481#endif
482 const QPainterPath widgetPath = cookPainterPath(widgetRect, m_iRadius);
483
484 /* Draw base/frame: */
485#ifdef VBOX_WS_MAC
486 if (m_pLineEdit->hasFocus())
487 painter.fillPath(focusPath, colorHighlight);
488#endif
489 painter.fillPath(widgetPath, colorBase);
490 painter.strokePath(widgetPath, colorFrame);
491}
492
493void UIFilterEditor::sltHandleEditorTextChanged(const QString &strText)
494{
495 adjustEditorButtonIcon();
496 emit sigTextChanged(strText);
497}
498
499void UIFilterEditor::sltHandleButtonClicked()
500{
501 m_pLineEdit->clear();
502}
503
504void UIFilterEditor::prepare()
505{
506 /* Init the decoration radius: */
507 m_iRadius = 10;
508
509 /* Prepare filter editor: */
510 m_pLineEdit = new QILineEdit(this);
511 if (m_pLineEdit)
512 {
513#ifdef VBOX_WS_MAC
514 /* A bit of magic to be able to replace the frame.
515 * Disable border, adjust margins and make background transparent.
516 * Left and right margins also take focus ring into account. */
517 m_pLineEdit->setStyleSheet("QLineEdit {\
518 background-color: rgba(255, 255, 255, 0%);\
519 border: 0px none black;\
520 margin: 6px 0px 6px 10px;\
521 }");
522#else
523 /* A bit of magic to be able to replace the frame.
524 * Disable border, adjust margins and make background transparent. */
525 m_pLineEdit->setStyleSheet("QLineEdit {\
526 background-color: rgba(255, 255, 255, 0%);\
527 border: 0px none black;\
528 margin: 3px 0px 3px 10px;\
529 }");
530#endif
531 m_pLineEdit->installEventFilter(this);
532 connect(m_pLineEdit, &QILineEdit::textChanged,
533 this, &UIFilterEditor::sltHandleEditorTextChanged);
534 }
535
536 /* Prepare filter reset button: */
537 m_pToolButton = new QToolButton(this);
538 if (m_pToolButton)
539 {
540 m_pToolButton->setStyleSheet("QToolButton {\
541 border: 0px none black;\
542 margin: 0px 5px 0px 5px;\
543 }\
544 QToolButton::menu-indicator {\
545 image: none;\
546 }");
547 m_pToolButton->setIconSize(QSize(10, 10));
548 connect(m_pToolButton, &QToolButton::clicked,
549 this, &UIFilterEditor::sltHandleButtonClicked);
550 }
551
552 /* Install 'unfocus/focus' animation to 'editorWidth' property: */
553 m_pAnimation = UIAnimation::installPropertyAnimation(this,
554 "editorWidth",
555 "unfocusedEditorWidth", "focusedEditorWidth",
556 SIGNAL(sigFocused()), SIGNAL(sigUnfocused()));
557
558 /* Adjust stuff initially: */
559 adjustEditorGeometry();
560 adjustEditorButtonIcon();
561}
562
563void UIFilterEditor::cleanup()
564{
565 /* Cleanup 'unfocus/focus' animation: */
566 delete m_pAnimation;
567 m_pAnimation = 0;
568}
569
570/* static */
571QPainterPath UIFilterEditor::cookPainterPath(const QRect &pathRect, int iRadius)
572{
573 QPainterPath path;
574 const QSizeF arcSize(2 * iRadius, 2 * iRadius);
575 path.moveTo(pathRect.x() + iRadius, pathRect.y());
576 path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-iRadius, 0), 90, 90);
577 path.lineTo(path.currentPosition().x(), path.currentPosition().y() + pathRect.height() - 2 * iRadius);
578 path.arcTo(QRectF(path.currentPosition(), arcSize).translated(0, -iRadius), 180, 90);
579 path.lineTo(path.currentPosition().x() + pathRect.width() - 2 * iRadius, path.currentPosition().y());
580 path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-iRadius, -2 * iRadius), 270, 90);
581 path.lineTo(path.currentPosition().x(), path.currentPosition().y() - pathRect.height() + 2 * iRadius);
582 path.arcTo(QRectF(path.currentPosition(), arcSize).translated(-2 * iRadius, -iRadius), 0, 90);
583 path.closeSubpath();
584 return path;
585}
586
587void UIFilterEditor::adjustEditorGeometry()
588{
589 /* Acquire maximum widget width: */
590 const int iWidth = width();
591
592 /* Acquire filter editor placeholder width: */
593 QFontMetrics fm(m_pLineEdit->font());
594 const int iPlaceholderWidth = fm.horizontalAdvance(m_pLineEdit->placeholderText())
595 + 2 * 5 /* left/right panel/frame width, no pixelMetric */
596 + 10 /* left margin, assigned via setStyleSheet */;
597 /* Acquire filter editor size-hint: */
598 const QSize esh = m_pLineEdit->minimumSizeHint();
599 const int iMinimumEditorWidth = qMax(esh.width(), iPlaceholderWidth);
600 const int iMinimumEditorHeight = esh.height();
601 /* Acquire filter button size-hint: */
602 const QSize bsh = m_pToolButton->minimumSizeHint();
603 const int iMinimumButtonWidth = bsh.width();
604 const int iMinimumButtonHeight = bsh.height();
605
606 /* Update filter button geo: */
607 const int iButtonX = iWidth - iMinimumButtonWidth;
608 const int iButtonY = iMinimumEditorHeight > iMinimumButtonHeight
609 ? (iMinimumEditorHeight - iMinimumButtonHeight) / 2 + 1
610 : 0;
611 m_pToolButton->setGeometry(iButtonX, iButtonY, iMinimumButtonWidth, iMinimumButtonHeight);
612
613 /* Update minimum/maximum filter editor width: */
614 m_iUnfocusedEditorWidth = qMin(iWidth / 2 - iMinimumButtonWidth, iMinimumEditorWidth);
615 m_iFocusedEditorWidth = qMax(iWidth - iMinimumButtonWidth, iMinimumEditorWidth);
616 m_pAnimation->update();
617 setEditorWidth(m_fFocused ? m_iFocusedEditorWidth : m_iUnfocusedEditorWidth);
618}
619
620void UIFilterEditor::adjustEditorButtonIcon()
621{
622 AssertPtrReturnVoid(m_pLineEdit);
623 AssertPtrReturnVoid(m_pToolButton);
624 m_pToolButton->setIcon( m_pLineEdit->text().isEmpty()
625 ? UIIconPool::iconSet(":/search_16px.png")
626 : UIIconPool::iconSet(":/close_16px.png"));
627}
628
629void UIFilterEditor::setEditorWidth(int iEditorWidth)
630{
631 /* Align filter editor right: */
632 const int iX = m_pToolButton->x() - iEditorWidth;
633 const int iY = 0;
634 const int iEditorHeight = m_pLineEdit->minimumSizeHint().height();
635 const QRect oldGeo = m_pLineEdit->geometry();
636 m_pLineEdit->setGeometry(iX, iY, iEditorWidth, iEditorHeight);
637 const QRect newGeo = m_pLineEdit->geometry();
638
639 /* Update rasterizer: */
640 const QRect rasterizer = oldGeo | newGeo;
641 update(rasterizer.adjusted(-1, -1, 1, 1));
642}
643
644int UIFilterEditor::editorWidth() const
645{
646 return m_pLineEdit->width();
647}
648
649
650/*********************************************************************************************************************************
651* Class UIVerticalScrollArea implementation. *
652*********************************************************************************************************************************/
653
654UIVerticalScrollArea::UIVerticalScrollArea(QWidget *pParent)
655 : QScrollArea(pParent)
656 , m_pAnimation(0)
657{
658 prepare();
659}
660
661int UIVerticalScrollArea::verticalScrollBarPosition() const
662{
663 return verticalScrollBar()->value();
664}
665
666void UIVerticalScrollArea::setVerticalScrollBarPosition(int iPosition) const
667{
668 verticalScrollBar()->setValue(iPosition);
669}
670
671void UIVerticalScrollArea::requestVerticalScrollBarPosition(int iPosition)
672{
673 /* Acquire scroll-bar minumum, maximum and length: */
674 const int iScrollBarMinimum = verticalScrollBar()->minimum();
675 const int iScrollBarMaximum = verticalScrollBar()->maximum();
676 const int iScrollBarLength = qAbs(iScrollBarMaximum - iScrollBarMinimum);
677
678 /* Acquire start, final position and total shift:: */
679 const int iStartPosition = verticalScrollBarPosition();
680 const int iFinalPosition = iPosition;
681 int iShift = qAbs(iFinalPosition - iStartPosition);
682 /* Make sure iShift is no more than iScrollBarLength: */
683 iShift = qMin(iShift, iScrollBarLength);
684
685 /* Calculate walking ratio: */
686 const float dRatio = iScrollBarLength > 0 ? (double)iShift / iScrollBarLength : 0;
687 m_pAnimation->setDuration(dRatio * 500 /* 500ms is the max */);
688 m_pAnimation->setStartValue(iStartPosition);
689 m_pAnimation->setEndValue(iFinalPosition);
690 m_pAnimation->start();
691}
692
693QSize UIVerticalScrollArea::minimumSizeHint() const
694{
695 /* To make horizontal scroll-bar always hidden we'll
696 * have to make sure minimum size-hint updated accordingly. */
697 const int iMinWidth = viewportSizeHint().width()
698 + verticalScrollBar()->sizeHint().width()
699 + frameWidth() * 2;
700 const int iMinHeight = qMax(QScrollArea::minimumSizeHint().height(),
701 (int)(iMinWidth / 1.6));
702 return QSize(iMinWidth, iMinHeight);
703}
704
705void UIVerticalScrollArea::wheelEvent(QWheelEvent *pEvent)
706{
707 /* Call to base-class: */
708 QScrollArea::wheelEvent(pEvent);
709
710 /* Notify listeners: */
711 emit sigWheelEvent();
712}
713
714void UIVerticalScrollArea::prepare()
715{
716 /* Make vertical scroll-bar always hidden: */
717 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
718
719 /* Prepare vertical scrollbar animation: */
720 m_pAnimation = new QPropertyAnimation(this, "verticalScrollBarPosition", this);
721}
722
723
724/*********************************************************************************************************************************
725* Class UIAdvancedSettingsDialog implementation. *
726*********************************************************************************************************************************/
727
728UIAdvancedSettingsDialog::UIAdvancedSettingsDialog(QWidget *pParent,
729 const QString &strCategory,
730 const QString &strControl)
731 : QMainWindow(pParent)
732 , m_strCategory(strCategory)
733 , m_strControl(strControl)
734 , m_pSelector(0)
735 , m_enmConfigurationAccessLevel(ConfigurationAccessLevel_Null)
736 , m_pSerializeProcess(0)
737 , m_fPolished(false)
738 , m_fSerializationIsInProgress(false)
739 , m_fSerializationClean(false)
740 , m_fClosed(false)
741 , m_iPageId(MachineSettingsPageType_Invalid)
742 , m_pStatusBar(0)
743 , m_pProcessBar(0)
744 , m_pWarningPane(0)
745 , m_fValid(true)
746 , m_fSilent(true)
747 , m_pScrollingTimer(0)
748 , m_pLayoutMain(0)
749 , m_pCheckBoxMode(0)
750 , m_pEditorFilter(0)
751 , m_pScrollArea(0)
752 , m_pScrollViewport(0)
753 , m_pButtonBox(0)
754{
755 prepare();
756}
757
758UIAdvancedSettingsDialog::~UIAdvancedSettingsDialog()
759{
760 cleanup();
761}
762
763void UIAdvancedSettingsDialog::accept()
764{
765 /* Save data: */
766 save();
767
768 /* Close if there is no ongoing serialization: */
769 if (!isSerializationInProgress())
770 sltClose();
771}
772
773void UIAdvancedSettingsDialog::reject()
774{
775 /* Close if there is no ongoing serialization: */
776 if (!isSerializationInProgress())
777 sltClose();
778}
779
780void UIAdvancedSettingsDialog::sltCategoryChanged(int cId)
781{
782 /* Cache current page ID for reusing: */
783 m_iPageId = cId;
784
785 /* Let's calculate required scroll-bar position: */
786 int iPosition = 0;
787 /* We'll have to take upper content's margin into account: */
788 int iL, iT, iR, iB;
789 m_pScrollViewport->layout()->getContentsMargins(&iL, &iT, &iR, &iB);
790 iPosition -= iT;
791 /* And actual page position according to parent: */
792 UISettingsPageFrame *pFrame = m_frames.value(m_iPageId, 0);
793 AssertPtr(pFrame);
794 if (pFrame)
795 {
796 const QPoint pnt = pFrame->pos();
797 iPosition += pnt.y();
798 }
799 /* Make sure corresponding page is visible: */
800 m_pScrollArea->requestVerticalScrollBarPosition(iPosition);
801
802#ifndef VBOX_WS_MAC
803 uiCommon().setHelpKeyword(m_pButtonBox->button(QDialogButtonBox::Help), m_pageHelpKeywords.value(cId));
804#endif
805}
806
807void UIAdvancedSettingsDialog::sltHandleSerializationStarted()
808{
809 m_pProcessBar->setValue(0);
810 m_pStatusBar->setCurrentWidget(m_pProcessBar);
811}
812
813void UIAdvancedSettingsDialog::sltHandleSerializationProgressChange(int iValue)
814{
815 m_pProcessBar->setValue(iValue);
816 if (m_pProcessBar->value() == m_pProcessBar->maximum())
817 {
818 if (!m_fValid || !m_fSilent)
819 m_pStatusBar->setCurrentWidget(m_pWarningPane);
820 else
821 m_pStatusBar->setCurrentIndex(0);
822 }
823}
824
825void UIAdvancedSettingsDialog::sltHandleSerializationFinished()
826{
827 /* Delete serializer if exists: */
828 delete m_pSerializeProcess;
829 m_pSerializeProcess = 0;
830
831 /* Mark serialization finished: */
832 m_fSerializationIsInProgress = false;
833}
834
835bool UIAdvancedSettingsDialog::eventFilter(QObject *pObject, QEvent *pEvent)
836{
837 /* Ignore other than wheel events in this handler: */
838 if (pEvent->type() != QEvent::Wheel)
839 return QMainWindow::eventFilter(pObject, pEvent);
840
841 /* Do not touch wheel events for m_pScrollArea or it's children: */
842 if ( pObject == m_pScrollArea
843 || pObject->parent() == m_pScrollArea)
844 {
845 /* Moreover restart 'sticky scrolling timer' during which
846 * all the scrolling will be redirected to m_pScrollViewport: */
847 m_pScrollingTimer->start();
848 return QMainWindow::eventFilter(pObject, pEvent);
849 }
850
851 /* Unconditionally and for good
852 * redirect wheel event for widgets of following types to m_pScrollViewport: */
853 if ( qobject_cast<QAbstractButton*>(pObject)
854 || qobject_cast<QAbstractSpinBox*>(pObject)
855 || qobject_cast<QAbstractSpinBox*>(pObject->parent())
856 || qobject_cast<QComboBox*>(pObject)
857 || qobject_cast<QSlider*>(pObject)
858 || qobject_cast<QTabWidget*>(pObject)
859 || qobject_cast<QTabWidget*>(pObject->parent()))
860 {
861 /* Check if redirected event was really handled, otherwise give it back: */
862 if (QCoreApplication::sendEvent(m_pScrollViewport, pEvent))
863 return true;
864 }
865
866 /* While 'sticky scrolling timer' is active
867 * redirect wheel event for widgets of following types to m_pScrollViewport: */
868 if ( m_pScrollingTimer->isActive()
869 && ( qobject_cast<QAbstractScrollArea*>(pObject)
870 || qobject_cast<QAbstractScrollArea*>(pObject->parent())))
871 {
872 /* Check if redirected event was really handled, otherwise give it back: */
873 if (QCoreApplication::sendEvent(m_pScrollViewport, pEvent))
874 return true;
875 }
876
877 /* Call to base-class: */
878 return QMainWindow::eventFilter(pObject, pEvent);
879}
880
881void UIAdvancedSettingsDialog::sltRetranslateUI()
882{
883 /* Translate mode checkbox: */
884 m_pCheckBoxMode->setText1(tr("Basic"));
885 m_pCheckBoxMode->setText2(tr("Expert"));
886
887 /* Translate filter editor placeholder: */
888 if (m_pEditorFilter)
889 m_pEditorFilter->setPlaceholderText(tr("Search settings"));
890
891 /* Translate warning-pane stuff: */
892 m_pWarningPane->setWarningLabelText(tr("Invalid settings detected"));
893
894 /* Translate page-frames: */
895 foreach (int cId, m_frames.keys())
896 m_frames.value(cId)->setName(m_pSelector->itemText(cId));
897
898 /* Retranslate all validators: */
899 foreach (UISettingsPageValidator *pValidator, findChildren<UISettingsPageValidator*>())
900 pValidator->setTitlePrefix(m_pSelector->itemTextByPage(pValidator->page()));
901 revalidate();
902}
903
904void UIAdvancedSettingsDialog::showEvent(QShowEvent *pEvent)
905{
906 /* Polish stuff: */
907 if (!m_fPolished)
908 {
909 m_fPolished = true;
910 polishEvent();
911 }
912
913 /* Call to base-class: */
914 QMainWindow::showEvent(pEvent);
915}
916
917void UIAdvancedSettingsDialog::polishEvent()
918{
919 /* Resize to minimum size: */
920 resize(minimumSizeHint());
921
922 /* Choose page/tab finally: */
923 choosePageAndTab();
924
925 /* Apply actual experience mode: */
926 sltHandleExperienceModeChanged();
927
928 /* Explicit centering according to our parent: */
929 gpDesktop->centerWidget(this, parentWidget(), false);
930}
931
932void UIAdvancedSettingsDialog::closeEvent(QCloseEvent *pEvent)
933{
934 /* Ignore event initially: */
935 pEvent->ignore();
936
937 /* Use pure QWidget close functionality,
938 * QWindow stuff is kind of overkill here.. */
939 sltClose();
940}
941
942void UIAdvancedSettingsDialog::choosePageAndTab(bool fKeepPreviousByDefault /* = false */)
943{
944 /* Setup settings window: */
945 if (!m_strCategory.isNull())
946 {
947 m_pSelector->selectByLink(m_strCategory);
948 /* Search for a widget with the given name: */
949 if (!m_strControl.isNull())
950 {
951 if (QWidget *pWidget = m_pScrollViewport->findChild<QWidget*>(m_strControl))
952 {
953 QList<QWidget*> parents;
954 QWidget *pParentWidget = pWidget;
955 while ((pParentWidget = pParentWidget->parentWidget()) != 0)
956 {
957 if (QTabWidget *pTabWidget = qobject_cast<QTabWidget*>(pParentWidget))
958 {
959 // WORKAROUND:
960 // The tab contents widget is two steps down
961 // (QTabWidget -> QStackedWidget -> QWidget).
962 QWidget *pTabPage = parents[parents.count() - 1];
963 if (pTabPage)
964 pTabPage = parents[parents.count() - 2];
965 if (pTabPage)
966 pTabWidget->setCurrentWidget(pTabPage);
967 }
968 parents.append(pParentWidget);
969 }
970 pWidget->setFocus();
971 }
972 }
973 }
974 /* First item as default (if previous is not guarded): */
975 else if (!fKeepPreviousByDefault)
976 m_pSelector->selectById(1);
977}
978
979void UIAdvancedSettingsDialog::loadData(QVariant &data)
980{
981 /* Mark serialization started: */
982 m_fSerializationIsInProgress = true;
983
984 /* Create settings loader: */
985 m_pSerializeProcess = new UISettingsSerializer(this, UISettingsSerializer::Load,
986 data, m_pSelector->settingPages());
987 if (m_pSerializeProcess)
988 {
989 /* Configure settings loader: */
990 connect(m_pSerializeProcess, &UISettingsSerializer::sigNotifyAboutProcessStarted,
991 this, &UIAdvancedSettingsDialog::sltHandleSerializationStarted);
992 connect(m_pSerializeProcess, &UISettingsSerializer::sigNotifyAboutProcessProgressChanged,
993 this, &UIAdvancedSettingsDialog::sltHandleSerializationProgressChange);
994 connect(m_pSerializeProcess, &UISettingsSerializer::sigNotifyAboutProcessFinished,
995 this, &UIAdvancedSettingsDialog::sltHandleSerializationFinished);
996
997 /* Raise current page priority: */
998 m_pSerializeProcess->raisePriorityOfPage(m_pSelector->currentId());
999
1000 /* Start settings loader: */
1001 m_pSerializeProcess->start();
1002
1003 /* Upload data finally: */
1004 data = m_pSerializeProcess->data();
1005 }
1006}
1007
1008void UIAdvancedSettingsDialog::saveData(QVariant &data)
1009{
1010 /* Mark serialization started: */
1011 m_fSerializationIsInProgress = true;
1012
1013 /* Create the 'settings saver': */
1014 QPointer<UISettingsSerializerProgress> pDlgSerializeProgress =
1015 new UISettingsSerializerProgress(this, UISettingsSerializer::Save,
1016 data, m_pSelector->settingPages());
1017 if (pDlgSerializeProgress)
1018 {
1019 /* Make the 'settings saver' temporary parent for all sub-dialogs: */
1020 windowManager().registerNewParent(pDlgSerializeProgress, windowManager().realParentWindow(this));
1021
1022 /* Execute the 'settings saver': */
1023 pDlgSerializeProgress->exec();
1024
1025 /* Any modal dialog can be destroyed in own event-loop
1026 * as a part of application termination procedure..
1027 * We have to check if the dialog still valid. */
1028 if (pDlgSerializeProgress)
1029 {
1030 /* Remember whether the serialization was clean: */
1031 m_fSerializationClean = pDlgSerializeProgress->isClean();
1032
1033 /* Upload 'settings saver' data: */
1034 data = pDlgSerializeProgress->data();
1035
1036 /* Delete the 'settings saver': */
1037 delete pDlgSerializeProgress;
1038 }
1039 }
1040}
1041
1042void UIAdvancedSettingsDialog::setConfigurationAccessLevel(ConfigurationAccessLevel enmConfigurationAccessLevel)
1043{
1044 /* Make sure something changed: */
1045 if (m_enmConfigurationAccessLevel == enmConfigurationAccessLevel)
1046 return;
1047
1048 /* Apply new configuration access level: */
1049 m_enmConfigurationAccessLevel = enmConfigurationAccessLevel;
1050
1051 /* And propagate it to settings-page(s): */
1052 foreach (UISettingsPage *pPage, m_pSelector->settingPages())
1053 pPage->setConfigurationAccessLevel(configurationAccessLevel());
1054}
1055
1056void UIAdvancedSettingsDialog::setOptionalFlags(const QMap<QString, QVariant> &flags)
1057{
1058 /* Avoid excessive calls: */
1059 if (m_flags == flags)
1060 return;
1061
1062 /* Save new flags: */
1063 m_flags = flags;
1064
1065 /* Reapply optional flags: */
1066 sltApplyFilteringRules();
1067}
1068
1069void UIAdvancedSettingsDialog::addItem(const QString &strBigIcon,
1070 const QString &strMediumIcon,
1071 const QString &strSmallIcon,
1072 int cId,
1073 const QString &strLink,
1074 UISettingsPage *pSettingsPage /* = 0 */,
1075 int iParentId /* = -1 */)
1076{
1077 /* Init m_iPageId if we haven't yet: */
1078 if (m_iPageId == MachineSettingsPageType_Invalid)
1079 m_iPageId = cId;
1080
1081 /* Add new selector item: */
1082 if (m_pSelector->addItem(strBigIcon, strMediumIcon, strSmallIcon,
1083 cId, strLink, pSettingsPage, iParentId))
1084 {
1085 /* Create frame with page inside: */
1086 UISettingsPageFrame *pFrame = new UISettingsPageFrame(pSettingsPage, m_pScrollViewport);
1087 if (pFrame)
1088 {
1089 /* Add frame to scroll-viewport: */
1090 m_pScrollViewport->layout()->addWidget(pFrame);
1091
1092 /* Remember page-frame for referencing: */
1093 m_frames[cId] = pFrame;
1094
1095 /* Notify about frame visibility changes: */
1096 connect(pFrame, &UISettingsPageFrame::sigVisibilityChange,
1097 this, &UIAdvancedSettingsDialog::sltHandleFrameVisibilityChange);
1098 }
1099 }
1100
1101 /* Assign validator if necessary: */
1102 if (pSettingsPage)
1103 {
1104 pSettingsPage->setId(cId);
1105
1106 /* Create validator: */
1107 UISettingsPageValidator *pValidator = new UISettingsPageValidator(this, pSettingsPage);
1108 connect(pValidator, &UISettingsPageValidator::sigValidityChanged,
1109 this, &UIAdvancedSettingsDialog::sltHandleValidityChange);
1110 pSettingsPage->setValidator(pValidator);
1111 m_pWarningPane->registerValidator(pValidator);
1112
1113 /* Update navigation (tab-order): */
1114 pSettingsPage->setOrderAfter(m_pSelector->widget());
1115 }
1116}
1117
1118void UIAdvancedSettingsDialog::addPageHelpKeyword(int iPageType, const QString &strHelpKeyword)
1119{
1120 m_pageHelpKeywords[iPageType] = strHelpKeyword;
1121}
1122
1123void UIAdvancedSettingsDialog::revalidate()
1124{
1125 /* Perform dialog revalidation: */
1126 m_fValid = true;
1127 m_fSilent = true;
1128
1129 /* Enumerating all the validators we have: */
1130 foreach (UISettingsPageValidator *pValidator, findChildren<UISettingsPageValidator*>())
1131 {
1132 /* Is current validator have something to say? */
1133 if (!pValidator->lastMessage().isEmpty())
1134 {
1135 /* What page is it related to? */
1136 UISettingsPage *pFailedSettingsPage = pValidator->page();
1137 LogRelFlow(("Settings Dialog: Dialog validation FAILED: Page *%s*\n",
1138 pFailedSettingsPage->internalName().toUtf8().constData()));
1139
1140 /* Show error first: */
1141 if (!pValidator->isValid())
1142 m_fValid = false;
1143 /* Show warning if message is not an error: */
1144 else
1145 m_fSilent = false;
1146
1147 /* Stop dialog revalidation on first error/warning: */
1148 break;
1149 }
1150 }
1151
1152 /* Update warning-pane visibility: */
1153 m_pWarningPane->setWarningLabelVisible(!m_fValid || !m_fSilent);
1154
1155 /* Make sure warning-pane visible if necessary: */
1156 if ((!m_fValid || !m_fSilent) && m_pStatusBar->currentIndex() == 0)
1157 m_pStatusBar->setCurrentWidget(m_pWarningPane);
1158 /* Make sure empty-pane visible otherwise: */
1159 else if (m_fValid && m_fSilent && m_pStatusBar->currentWidget() == m_pWarningPane)
1160 m_pStatusBar->setCurrentIndex(0);
1161
1162 /* Lock/unlock settings-page OK button according global validity status: */
1163 m_pButtonBox->button(QDialogButtonBox::Ok)->setEnabled(m_fValid);
1164}
1165
1166bool UIAdvancedSettingsDialog::isSettingsChanged()
1167{
1168 bool fIsSettingsChanged = false;
1169 foreach (UISettingsPage *pPage, m_pSelector->settingPages())
1170 {
1171 pPage->putToCache();
1172 if (!fIsSettingsChanged && pPage->changed())
1173 fIsSettingsChanged = true;
1174 }
1175 return fIsSettingsChanged;
1176}
1177
1178void UIAdvancedSettingsDialog::sltClose()
1179{
1180 /* Check whether serialization was clean (save)
1181 * or there are no unsaved settings to be lost (cancel): */
1182 if ( m_fSerializationClean
1183 || !isSettingsChanged()
1184 || msgCenter().confirmSettingsDiscarding(this))
1185 {
1186 /* Tell the listener to close us (once): */
1187 if (!m_fClosed)
1188 {
1189 m_fClosed = true;
1190 emit sigClose();
1191 return;
1192 }
1193 }
1194}
1195
1196void UIAdvancedSettingsDialog::sltHandleValidityChange(UISettingsPageValidator *pValidator)
1197{
1198 /* Determine which settings-page had called for revalidation: */
1199 if (UISettingsPage *pSettingsPage = pValidator->page())
1200 {
1201 /* Determine settings-page name: */
1202 const QString strPageName(pSettingsPage->internalName());
1203
1204 LogRelFlow(("Settings Dialog: %s Page: Revalidation in progress..\n",
1205 strPageName.toUtf8().constData()));
1206
1207 /* Perform page revalidation: */
1208 pValidator->revalidate();
1209 /* Perform inter-page recorrelation: */
1210 recorrelate(pSettingsPage);
1211 /* Perform dialog revalidation: */
1212 revalidate();
1213
1214 LogRelFlow(("Settings Dialog: %s Page: Revalidation complete.\n",
1215 strPageName.toUtf8().constData()));
1216 }
1217}
1218
1219void UIAdvancedSettingsDialog::sltHandleWarningPaneHovered(UISettingsPageValidator *pValidator)
1220{
1221 LogRelFlow(("Settings Dialog: Warning-icon hovered: %s.\n", pValidator->internalName().toUtf8().constData()));
1222
1223 /* Show corresponding popup: */
1224 if (!m_fValid || !m_fSilent)
1225 popupCenter().popup(m_pScrollArea, "SettingsDialogWarning",
1226 pValidator->lastMessage());
1227}
1228
1229void UIAdvancedSettingsDialog::sltHandleWarningPaneUnhovered(UISettingsPageValidator *pValidator)
1230{
1231 LogRelFlow(("Settings Dialog: Warning-icon unhovered: %s.\n", pValidator->internalName().toUtf8().constData()));
1232
1233 /* Recall corresponding popup: */
1234 popupCenter().recall(m_pScrollArea, "SettingsDialogWarning");
1235}
1236
1237void UIAdvancedSettingsDialog::sltHandleExperienceModeCheckBoxChanged()
1238{
1239 /* Save new value: */
1240 gEDataManager->setSettingsInExpertMode(m_pCheckBoxMode->isChecked());
1241}
1242
1243void UIAdvancedSettingsDialog::sltHandleExperienceModeChanged()
1244{
1245 /* Acquire actual value: */
1246 const bool fExpertMode = gEDataManager->isSettingsInExpertMode();
1247
1248 /* Update check-box state: */
1249 m_pCheckBoxMode->blockSignals(true);
1250 m_pCheckBoxMode->setChecked(fExpertMode);
1251 m_pCheckBoxMode->blockSignals(false);
1252
1253 /* Reapply mode: */
1254 sltApplyFilteringRules();
1255}
1256
1257void UIAdvancedSettingsDialog::sltApplyFilteringRules()
1258{
1259 /* Filter-out page contents: */
1260 foreach (UISettingsPageFrame *pFrame, m_frames.values())
1261 pFrame->filterOut(m_pCheckBoxMode->isChecked(),
1262 m_pEditorFilter->text(),
1263 m_flags);
1264
1265 /* Make sure current page chosen again: */
1266 /// @todo fix this WORKAROUND properly!
1267 // Why the heck simple call to
1268 // QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
1269 // isn't enough and we still need some time to let system
1270 // process the layouts and vertical scroll-bar position?
1271 QTimer::singleShot(50, this, SLOT(sltCategoryChangedRepeat()));
1272}
1273
1274void UIAdvancedSettingsDialog::sltHandleFrameVisibilityChange(bool fVisible)
1275{
1276 /* Acquire frame: */
1277 UISettingsPageFrame *pFrame = qobject_cast<UISettingsPageFrame*>(sender());
1278 AssertPtrReturnVoid(pFrame);
1279
1280 /* Update selector item visibility: */
1281 const int iId = m_frames.key(pFrame);
1282 m_pSelector->setItemVisible(iId, fVisible);
1283}
1284
1285void UIAdvancedSettingsDialog::sltHandleVerticalScrollAreaWheelEvent()
1286{
1287 /* Acquire layout info: */
1288 int iL = 0, iT = 0, iR = 0, iB = 0;
1289 if ( m_pScrollViewport
1290 && m_pScrollViewport->layout())
1291 m_pScrollViewport->layout()->getContentsMargins(&iL, &iT, &iR, &iB);
1292
1293 /* Search through all the frame keys we have: */
1294 int iActualKey = -1;
1295 foreach (int iKey, m_frames.keys())
1296 {
1297 /* Let's calculate scroll-bar position for enumerated frame: */
1298 int iPosition = 0;
1299 /* We'll have to take upper content's margin into account: */
1300 iPosition -= iT;
1301 /* And actual page position according to parent: */
1302 const QPoint pnt = m_frames.value(iKey)->pos();
1303 iPosition += pnt.y();
1304
1305 /* Check if scroll-bar haven't passed this position yet: */
1306 if (m_pScrollArea->verticalScrollBarPosition() < iPosition)
1307 break;
1308
1309 /* Remember last suitable frame key: */
1310 iActualKey = iKey;
1311 }
1312
1313 /* Silently update the selector with frame number we found: */
1314 if (iActualKey != -1)
1315 m_pSelector->selectById(iActualKey, true /* silently */);
1316}
1317
1318void UIAdvancedSettingsDialog::prepare()
1319{
1320 /* Prepare 'sticky scrolling timer': */
1321 m_pScrollingTimer = new QTimer(this);
1322 if (m_pScrollingTimer)
1323 {
1324 m_pScrollingTimer->setInterval(500);
1325 m_pScrollingTimer->setSingleShot(true);
1326 }
1327
1328 /* Prepare central-widget: */
1329 setCentralWidget(new QWidget);
1330 if (centralWidget())
1331 {
1332 /* Prepare main layout: */
1333 m_pLayoutMain = new QGridLayout(centralWidget());
1334 if (m_pLayoutMain)
1335 {
1336 /* Prepare widgets: */
1337 prepareSelector();
1338 prepareScrollArea();
1339 prepareButtonBox();
1340 }
1341 }
1342
1343 /* Apply language settings: */
1344 sltRetranslateUI();
1345 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
1346 this, &UIAdvancedSettingsDialog::sltRetranslateUI);
1347}
1348
1349void UIAdvancedSettingsDialog::prepareSelector()
1350{
1351 /* Make sure there is a serious spacing between selector and pages: */
1352 m_pLayoutMain->setColumnMinimumWidth(1, 20);
1353 m_pLayoutMain->setRowStretch(1, 1);
1354 m_pLayoutMain->setColumnStretch(2, 1);
1355
1356 /* Prepare mode checkbox: */
1357 m_pCheckBoxMode = new UIModeCheckBox(centralWidget());
1358 if (m_pCheckBoxMode)
1359 {
1360 connect(m_pCheckBoxMode, &UIModeCheckBox::stateChanged,
1361 this, &UIAdvancedSettingsDialog::sltHandleExperienceModeCheckBoxChanged);
1362 connect(gEDataManager, &UIExtraDataManager::sigSettingsExpertModeChange,
1363 this, &UIAdvancedSettingsDialog::sltHandleExperienceModeChanged);
1364 m_pLayoutMain->addWidget(m_pCheckBoxMode, 0, 0);
1365 }
1366
1367 /* Prepare classical tree-view selector: */
1368 m_pSelector = new UISettingsSelectorTreeView(centralWidget());
1369 if (m_pSelector)
1370 {
1371 m_pLayoutMain->addWidget(m_pSelector->widget(), 1, 0);
1372 m_pSelector->widget()->setFocus();
1373 }
1374
1375 /* Prepare filter editor: */
1376 m_pEditorFilter = new UIFilterEditor(centralWidget());
1377 if (m_pEditorFilter)
1378 {
1379 connect(m_pEditorFilter, &UIFilterEditor::sigTextChanged,
1380 this, &UIAdvancedSettingsDialog::sltApplyFilteringRules);
1381 m_pLayoutMain->addWidget(m_pEditorFilter, 0, 2);
1382 }
1383
1384 /* Configure selector created above: */
1385 if (m_pSelector)
1386 connect(m_pSelector, &UISettingsSelectorTreeView::sigCategoryChanged,
1387 this, &UIAdvancedSettingsDialog::sltCategoryChanged);
1388}
1389
1390void UIAdvancedSettingsDialog::prepareScrollArea()
1391{
1392 /* Prepare scroll-area: */
1393 m_pScrollArea = new UIVerticalScrollArea(centralWidget());
1394 if (m_pScrollArea)
1395 {
1396 /* Configure popup-stack: */
1397 popupCenter().setPopupStackOrientation(m_pScrollArea, UIPopupStackOrientation_Bottom);
1398
1399 m_pScrollArea->setWidgetResizable(true);
1400 m_pScrollArea->setFrameShape(QFrame::NoFrame);
1401 connect(m_pScrollArea, &UIVerticalScrollArea::sigWheelEvent,
1402 this, &UIAdvancedSettingsDialog::sltHandleVerticalScrollAreaWheelEvent);
1403
1404 /* Prepare scroll-viewport: */
1405 m_pScrollViewport = new QWidget(m_pScrollArea);
1406 if (m_pScrollViewport)
1407 {
1408 QVBoxLayout *pLayout = new QVBoxLayout(m_pScrollViewport);
1409 if (pLayout)
1410 {
1411 pLayout->setAlignment(Qt::AlignTop);
1412 pLayout->setContentsMargins(0, 0, 0, 0);
1413 int iSpacing = pLayout->spacing();
1414 pLayout->setSpacing(2 * iSpacing);
1415 }
1416 m_pScrollArea->setWidget(m_pScrollViewport);
1417 }
1418
1419 /* Add scroll-area into main layout: */
1420 m_pLayoutMain->addWidget(m_pScrollArea, 1, 2);
1421 }
1422}
1423
1424void UIAdvancedSettingsDialog::prepareButtonBox()
1425{
1426 /* Prepare button-box: */
1427 m_pButtonBox = new QIDialogButtonBox(centralWidget());
1428 if (m_pButtonBox)
1429 {
1430#ifndef VBOX_WS_MAC
1431 m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel |
1432 QDialogButtonBox::NoButton | QDialogButtonBox::Help);
1433 m_pButtonBox->button(QDialogButtonBox::Help)->setShortcut(UIShortcutPool::standardSequence(QKeySequence::HelpContents));
1434#else
1435 // WORKAROUND:
1436 // No Help button on macOS for now, conflict with old Qt.
1437 m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel |
1438 QDialogButtonBox::NoButton);
1439#endif
1440 m_pButtonBox->button(QDialogButtonBox::Ok)->setShortcut(Qt::Key_Return);
1441 m_pButtonBox->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
1442 connect(m_pButtonBox, &QIDialogButtonBox::rejected, this, &UIAdvancedSettingsDialog::sltClose);
1443 connect(m_pButtonBox, &QIDialogButtonBox::accepted, this, &UIAdvancedSettingsDialog::accept);
1444#ifndef VBOX_WS_MAC
1445 connect(m_pButtonBox->button(QDialogButtonBox::Help), &QAbstractButton::pressed,
1446 m_pButtonBox, &QIDialogButtonBox::sltHandleHelpRequest);
1447#endif
1448
1449 /* Prepare status-bar: */
1450 m_pStatusBar = new QStackedWidget(m_pButtonBox);
1451 if (m_pStatusBar)
1452 {
1453 /* Add empty widget: */
1454 m_pStatusBar->addWidget(new QWidget);
1455
1456 /* Prepare process-bar: */
1457 m_pProcessBar = new QProgressBar(m_pStatusBar);
1458 if (m_pProcessBar)
1459 {
1460 m_pProcessBar->setMinimum(0);
1461 m_pProcessBar->setMaximum(100);
1462 m_pStatusBar->addWidget(m_pProcessBar);
1463 }
1464
1465 /* Prepare warning-pane: */
1466 m_pWarningPane = new UISettingsWarningPane(m_pStatusBar);
1467 if (m_pWarningPane)
1468 {
1469 connect(m_pWarningPane, &UISettingsWarningPane::sigHoverEnter,
1470 this, &UIAdvancedSettingsDialog::sltHandleWarningPaneHovered);
1471 connect(m_pWarningPane, &UISettingsWarningPane::sigHoverLeave,
1472 this, &UIAdvancedSettingsDialog::sltHandleWarningPaneUnhovered);
1473 m_pStatusBar->addWidget(m_pWarningPane);
1474 }
1475
1476 /* Add status-bar to button-box: */
1477 m_pButtonBox->addExtraWidget(m_pStatusBar);
1478 }
1479
1480 /* Add button-box into main layout: */
1481 m_pLayoutMain->addWidget(m_pButtonBox, 2, 0, 1, 3);
1482 }
1483}
1484
1485void UIAdvancedSettingsDialog::cleanup()
1486{
1487 /* Delete serializer if exists: */
1488 delete m_pSerializeProcess;
1489 m_pSerializeProcess = 0;
1490
1491 /* Recall popup-pane if any: */
1492 popupCenter().recall(m_pScrollArea, "SettingsDialogWarning");
1493
1494 /* Delete selector early! */
1495 delete m_pSelector;
1496}
1497
1498#include "UIAdvancedSettingsDialog.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use