VirtualBox

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

Last change on this file since 103977 was 103580, checked in by vboxsync, 10 months ago

FE/Qt: Build fix for r161930.

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