VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserItemGroup.cpp@ 103538

Last change on this file since 103538 was 101559, checked in by vboxsync, 8 months ago

FE/Qt: bugref:10450: Get rid of Qt5 stuff; This one is about device-pixel-ratio stuff.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.1 KB
Line 
1/* $Id: UIChooserItemGroup.cpp 101559 2023-10-23 15:51:00Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIChooserItemGroup class implementation.
4 */
5
6/*
7 * Copyright (C) 2012-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 <QGraphicsLinearLayout>
30#include <QGraphicsScene>
31#include <QGraphicsSceneDragDropEvent>
32#include <QGraphicsView>
33#include <QHBoxLayout>
34#include <QLineEdit>
35#include <QPainter>
36#include <QRegularExpression>
37#include <QStyleOptionGraphicsItem>
38#include <QWindow>
39
40/* GUI includes: */
41#include "UIChooserItemGlobal.h"
42#include "UIChooserItemGroup.h"
43#include "UIChooserItemMachine.h"
44#include "UIChooserModel.h"
45#include "UIChooserNodeGroup.h"
46#include "UIChooserNodeMachine.h"
47#include "UIGraphicsRotatorButton.h"
48#include "UIGraphicsScrollArea.h"
49#include "UIIconPool.h"
50#include "UIImageTools.h"
51#include "UIVirtualBoxManager.h"
52#include "UIVirtualMachineItem.h"
53
54
55/*********************************************************************************************************************************
56* Class UIChooserItemGroup implementation. *
57*********************************************************************************************************************************/
58
59UIChooserItemGroup::UIChooserItemGroup(QGraphicsScene *pScene, UIChooserNodeGroup *pNode)
60 : UIChooserItem(0, pNode)
61 , m_pScene(pScene)
62 , m_iRootBackgroundDarknessStart(0)
63 , m_iRootBackgroundDarknessFinal(0)
64 , m_iItemBackgroundDarknessStart(0)
65 , m_iItemBackgroundDarknessFinal(0)
66 , m_iHighlightLightness(0)
67 , m_iAdditionalHeight(0)
68 , m_pToggleButton(0)
69 , m_pNameEditorWidget(0)
70 , m_pContainerFavorite(0)
71 , m_pLayoutFavorite(0)
72 , m_pScrollArea(0)
73 , m_pContainer(0)
74 , m_pLayout(0)
75 , m_pLayoutGlobal(0)
76 , m_pLayoutGroup(0)
77 , m_pLayoutMachine(0)
78 , m_iPreviousMinimumWidthHint(0)
79{
80 prepare();
81}
82
83UIChooserItemGroup::UIChooserItemGroup(UIChooserItem *pParent, UIChooserNodeGroup *pNode)
84 : UIChooserItem(pParent, pNode)
85 , m_pScene(0)
86 , m_iRootBackgroundDarknessStart(0)
87 , m_iRootBackgroundDarknessFinal(0)
88 , m_iItemBackgroundDarknessStart(0)
89 , m_iItemBackgroundDarknessFinal(0)
90 , m_iHighlightLightness(0)
91 , m_iAdditionalHeight(0)
92 , m_pToggleButton(0)
93 , m_pNameEditorWidget(0)
94 , m_pContainerFavorite(0)
95 , m_pLayoutFavorite(0)
96 , m_pScrollArea(0)
97 , m_pContainer(0)
98 , m_pLayout(0)
99 , m_pLayoutGlobal(0)
100 , m_pLayoutGroup(0)
101 , m_pLayoutMachine(0)
102 , m_iPreviousMinimumWidthHint(0)
103{
104 prepare();
105}
106
107UIChooserItemGroup::~UIChooserItemGroup()
108{
109 cleanup();
110}
111
112UIChooserNodeGroup *UIChooserItemGroup::nodeToGroupType() const
113{
114 return node() ? node()->toGroupNode() : 0;
115}
116
117QUuid UIChooserItemGroup::id() const
118{
119 return nodeToGroupType() ? nodeToGroupType()->id() : QUuid();
120}
121
122UIChooserNodeGroupType UIChooserItemGroup::groupType() const
123{
124 return nodeToGroupType() ? nodeToGroupType()->groupType() : UIChooserNodeGroupType_Invalid;
125}
126
127bool UIChooserItemGroup::isClosed() const
128{
129 return nodeToGroupType()->isClosed() && !isRoot();
130}
131
132void UIChooserItemGroup::close(bool fAnimated /* = true */)
133{
134 AssertMsg(!isRoot(), ("Can't close root-item!"));
135 m_pToggleButton->setToggled(false, fAnimated);
136}
137
138bool UIChooserItemGroup::isOpened() const
139{
140 return nodeToGroupType()->isOpened() || isRoot();
141}
142
143void UIChooserItemGroup::open(bool fAnimated /* = true */)
144{
145 AssertMsg(!isRoot(), ("Can't open root-item!"));
146 m_pToggleButton->setToggled(true, fAnimated);
147}
148
149void UIChooserItemGroup::updateFavorites()
150{
151 /* Global items only for now, move items to corresponding layout: */
152 foreach (UIChooserItem *pItem, items(UIChooserNodeType_Global))
153 if (pItem->isFavorite())
154 {
155 for (int iIndex = 0; iIndex < m_pLayoutGlobal->count(); ++iIndex)
156 if (m_pLayoutGlobal->itemAt(iIndex) == pItem)
157 m_pLayoutFavorite->addItem(pItem);
158 }
159 else
160 {
161 for (int iIndex = 0; iIndex < m_pLayoutFavorite->count(); ++iIndex)
162 if (m_pLayoutFavorite->itemAt(iIndex) == pItem)
163 m_pLayoutGlobal->addItem(pItem);
164 }
165
166 /* Update/activate children layout: */
167 m_pLayout->updateGeometry();
168 m_pLayout->activate();
169
170 /* Relayout model: */
171 model()->updateLayout();
172}
173
174int UIChooserItemGroup::scrollingValue() const
175{
176 return m_pScrollArea->scrollingValue();
177}
178
179void UIChooserItemGroup::setScrollingValue(int iValue)
180{
181 m_pScrollArea->setScrollingValue(iValue);
182}
183
184void UIChooserItemGroup::scrollBy(int iDelta)
185{
186 m_pScrollArea->scrollBy(iDelta);
187}
188
189void UIChooserItemGroup::makeSureItemIsVisible(UIChooserItem *pItem)
190{
191 /* Make sure item exists: */
192 AssertPtrReturnVoid(pItem);
193
194 /* Convert child rectangle to local coordinates for this group. This also
195 * works for a child at any sub-level, doesn't necessary of this group. */
196 const QPointF positionInScene = pItem->mapToScene(QPointF(0, 0));
197 const QPointF positionInGroup = mapFromScene(positionInScene);
198 const QRectF itemRectInGroup = QRectF(positionInGroup, pItem->size());
199 m_pScrollArea->makeSureRectIsVisible(itemRectInGroup);
200}
201
202/* static */
203QString UIChooserItemGroup::className()
204{
205 return "UIChooserItemGroup";
206}
207
208void UIChooserItemGroup::retranslateUi()
209{
210 updateToggleButtonToolTip();
211}
212
213void UIChooserItemGroup::showEvent(QShowEvent *pEvent)
214{
215 /* Call to base-class: */
216 UIChooserItem::showEvent(pEvent);
217
218 /* Update pixmaps: */
219 updatePixmaps();
220}
221
222void UIChooserItemGroup::resizeEvent(QGraphicsSceneResizeEvent *pEvent)
223{
224 /* Call to base-class: */
225 UIChooserItem::resizeEvent(pEvent);
226
227 /* What is the new geometry? */
228 const QRectF newGeometry = geometry();
229
230 /* Should we update visible name? */
231 if (previousGeometry().width() != newGeometry.width())
232 updateVisibleName();
233
234 /* Remember the new geometry: */
235 setPreviousGeometry(newGeometry);
236}
237
238void UIChooserItemGroup::hoverMoveEvent(QGraphicsSceneHoverEvent *pEvent)
239{
240 /* Skip if hovered: */
241 if (isHovered())
242 return;
243
244 /* Prepare variables: */
245 const QPoint pos = pEvent->pos().toPoint();
246 const int iMarginV = data(GroupItemData_MarginV).toInt();
247 const int iHeaderHeight = m_minimumHeaderSize.height();
248 const int iFullHeaderHeight = 2 * iMarginV + iHeaderHeight;
249 /* Skip if hovered part out of the header: */
250 if (pos.y() >= iFullHeaderHeight)
251 return;
252
253 /* Call to base-class: */
254 UIChooserItem::hoverMoveEvent(pEvent);
255
256 /* Update linked values: */
257 updateVisibleName();
258}
259
260void UIChooserItemGroup::hoverLeaveEvent(QGraphicsSceneHoverEvent *pEvent)
261{
262 /* Skip if not hovered: */
263 if (!isHovered())
264 return;
265
266 /* Call to base-class: */
267 UIChooserItem::hoverLeaveEvent(pEvent);
268
269 /* Update linked values: */
270 updateVisibleName();
271}
272
273void UIChooserItemGroup::paint(QPainter *pPainter, const QStyleOptionGraphicsItem *pOptions, QWidget* /* pWidget = 0 */)
274{
275 /* Acquire rectangle: */
276 const QRect rectangle = pOptions->rect;
277
278 /* Paint background: */
279 paintBackground(pPainter, rectangle);
280 /* Paint frame: */
281 paintFrame(pPainter, rectangle);
282 /* Paint header: */
283 paintHeader(pPainter, rectangle);
284}
285
286void UIChooserItemGroup::startEditing()
287{
288 /* Not for root: */
289 if (isRoot())
290 return;
291
292 /* Not while saving groups: */
293 if (model()->isGroupSavingInProgress())
294 return;
295
296 /* Make sure item visible: */
297 if (model()->root())
298 model()->root()->toGroupItem() ->makeSureItemIsVisible(this);
299
300 /* Assign name-editor text: */
301 m_pNameEditorWidget->setText(name());
302
303 /* Layout name-editor: */
304 const int iMarginV = data(GroupItemData_MarginV).toInt();
305 const int iHeaderHeight = 2 * iMarginV + m_minimumHeaderSize.height();
306 const QSize headerSize = QSize(geometry().width(), iHeaderHeight);
307 const QGraphicsView *pView = model()->scene()->views().first();
308 const QPointF viewPoint = pView->mapFromScene(mapToScene(QPointF(0, 0)));
309 const QPoint globalPoint = pView->parentWidget()->mapToGlobal(viewPoint.toPoint());
310 m_pNameEditorWidget->move(globalPoint);
311 m_pNameEditorWidget->resize(headerSize);
312
313 /* Show name-editor: */
314 m_pNameEditorWidget->show();
315 m_pNameEditorWidget->setFocus();
316}
317
318void UIChooserItemGroup::updateItem()
319{
320 /* Update this group-item: */
321 updateVisibleName();
322 updateMinimumHeaderSize();
323 updateToolTip();
324 update();
325
326 /* Update parent group-item: */
327 if (parentItem())
328 {
329 parentItem()->updateToolTip();
330 parentItem()->update();
331 }
332}
333
334void UIChooserItemGroup::updateToolTip()
335{
336 /* Not for root item: */
337 if (isRoot())
338 return;
339
340 /* Prepare variables: */
341 QStringList toolTipInfo;
342
343 /* Should we add name? */
344 if (!name().isEmpty())
345 {
346 /* Template: */
347 QString strTemplateForName = tr("<b>%1</b>", "Group item tool-tip / Group name");
348
349 /* Append value: */
350 toolTipInfo << strTemplateForName.arg(name());
351 }
352
353 /* Should we add group info? */
354 if (!items(UIChooserNodeType_Group).isEmpty())
355 {
356 /* Template: */
357 QString strGroupCount = tr("%n group(s)", "Group item tool-tip / Group info", items(UIChooserNodeType_Group).size());
358
359 /* Append value: */
360 QString strValue = tr("<nobr>%1</nobr>", "Group item tool-tip / Group info wrapper").arg(strGroupCount);
361 toolTipInfo << strValue;
362 }
363
364 /* Should we add machine info? */
365 if (!items(UIChooserNodeType_Machine).isEmpty())
366 {
367 /* Check if 'this' group contains started VMs: */
368 int iCountOfStartedMachineItems = 0;
369 foreach (UIChooserItem *pItem, items(UIChooserNodeType_Machine))
370 {
371 AssertPtrReturnVoid(pItem);
372 UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
373 AssertPtrReturnVoid(pMachineItem);
374 AssertPtrReturnVoid(pMachineItem->cache());
375 if (pMachineItem->cache()->isItemStarted())
376 ++iCountOfStartedMachineItems;
377 }
378 /* Template: */
379 QString strMachineCount = tr("%n machine(s)", "Group item tool-tip / Machine info", items(UIChooserNodeType_Machine).size());
380 QString strStartedMachineCount = tr("(%n running)", "Group item tool-tip / Running machine info", iCountOfStartedMachineItems);
381
382 /* Append value: */
383 QString strValue = !iCountOfStartedMachineItems ?
384 tr("<nobr>%1</nobr>", "Group item tool-tip / Machine info wrapper").arg(strMachineCount) :
385 tr("<nobr>%1 %2</nobr>", "Group item tool-tip / Machine info wrapper, including running").arg(strMachineCount).arg(strStartedMachineCount);
386 toolTipInfo << strValue;
387 }
388
389 /* Set tool-tip: */
390 setToolTip(toolTipInfo.join("<br>"));
391}
392
393void UIChooserItemGroup::installEventFilterHelper(QObject *pSource)
394{
395 /* The only object which need's that filter for now is scroll-area: */
396 pSource->installEventFilter(m_pScrollArea);
397}
398
399QList<UIChooserItem*> UIChooserItemGroup::items(UIChooserNodeType type /* = UIChooserNodeType_Any */) const
400{
401 switch (type)
402 {
403 case UIChooserNodeType_Any: return items(UIChooserNodeType_Global) + items(UIChooserNodeType_Group) + items(UIChooserNodeType_Machine);
404 case UIChooserNodeType_Global: return m_globalItems;
405 case UIChooserNodeType_Group: return m_groupItems;
406 case UIChooserNodeType_Machine: return m_machineItems;
407 default: break;
408 }
409 return QList<UIChooserItem*>();
410}
411
412void UIChooserItemGroup::addItem(UIChooserItem *pItem, bool fFavorite, int iPosition)
413{
414 /* Check item type: */
415 switch (pItem->type())
416 {
417 case UIChooserNodeType_Global:
418 {
419 AssertMsg(!m_globalItems.contains(pItem), ("Global-item already added!"));
420 if (iPosition < 0 || iPosition >= m_globalItems.size())
421 {
422 if (fFavorite)
423 m_pLayoutFavorite->addItem(pItem);
424 else
425 m_pLayoutGlobal->addItem(pItem);
426 m_globalItems.append(pItem);
427 }
428 else
429 {
430 if (fFavorite)
431 m_pLayoutFavorite->insertItem(iPosition, pItem);
432 else
433 m_pLayoutGlobal->insertItem(iPosition, pItem);
434 m_globalItems.insert(iPosition, pItem);
435 }
436 break;
437 }
438 case UIChooserNodeType_Group:
439 {
440 AssertMsg(!m_groupItems.contains(pItem), ("Group-item already added!"));
441 if (iPosition < 0 || iPosition >= m_groupItems.size())
442 {
443 m_pLayoutGroup->addItem(pItem);
444 m_groupItems.append(pItem);
445 }
446 else
447 {
448 m_pLayoutGroup->insertItem(iPosition, pItem);
449 m_groupItems.insert(iPosition, pItem);
450 }
451 break;
452 }
453 case UIChooserNodeType_Machine:
454 {
455 AssertMsg(!m_machineItems.contains(pItem), ("Machine-item already added!"));
456 if (iPosition < 0 || iPosition >= m_machineItems.size())
457 {
458 m_pLayoutMachine->addItem(pItem);
459 m_machineItems.append(pItem);
460 }
461 else
462 {
463 m_pLayoutMachine->insertItem(iPosition, pItem);
464 m_machineItems.insert(iPosition, pItem);
465 }
466 break;
467 }
468 default:
469 {
470 AssertMsgFailed(("Invalid item type!"));
471 break;
472 }
473 }
474
475 /* Update linked values: */
476 updateLayoutSpacings();
477 updateItemCountInfo();
478 updateToolTip();
479 updateGeometry();
480}
481
482void UIChooserItemGroup::removeItem(UIChooserItem *pItem)
483{
484 /* Check item type: */
485 switch (pItem->type())
486 {
487 case UIChooserNodeType_Global:
488 {
489 AssertMsg(m_globalItems.contains(pItem), ("Global-item was not found!"));
490 m_globalItems.removeAt(m_globalItems.indexOf(pItem));
491 if (pItem->isFavorite())
492 m_pLayoutFavorite->removeItem(pItem);
493 else
494 m_pLayoutGlobal->removeItem(pItem);
495 break;
496 }
497 case UIChooserNodeType_Group:
498 {
499 AssertMsg(m_groupItems.contains(pItem), ("Group-item was not found!"));
500 m_groupItems.removeAt(m_groupItems.indexOf(pItem));
501 if (pItem->isFavorite())
502 m_pLayoutFavorite->removeItem(pItem);
503 else
504 m_pLayoutGroup->removeItem(pItem);
505 break;
506 }
507 case UIChooserNodeType_Machine:
508 {
509 AssertMsg(m_machineItems.contains(pItem), ("Machine-item was not found!"));
510 m_machineItems.removeAt(m_machineItems.indexOf(pItem));
511 if (pItem->isFavorite())
512 m_pLayoutFavorite->removeItem(pItem);
513 else
514 m_pLayoutMachine->removeItem(pItem);
515 break;
516 }
517 default:
518 {
519 AssertMsgFailed(("Invalid item type!"));
520 break;
521 }
522 }
523
524 /* Update linked values: */
525 updateLayoutSpacings();
526 updateItemCountInfo();
527 updateToolTip();
528 updateGeometry();
529}
530
531UIChooserItem* UIChooserItemGroup::searchForItem(const QString &strSearchTag, int iSearchFlags)
532{
533 /* Are we searching among group-items? */
534 if ( ( iSearchFlags & UIChooserItemSearchFlag_LocalGroup
535 && groupType() == UIChooserNodeGroupType_Local)
536 || ( iSearchFlags & UIChooserItemSearchFlag_CloudProvider
537 && groupType() == UIChooserNodeGroupType_Provider)
538 || ( iSearchFlags & UIChooserItemSearchFlag_CloudProfile
539 && groupType() == UIChooserNodeGroupType_Profile))
540 {
541 /* Are we searching by the exact ID? */
542 if (iSearchFlags & UIChooserItemSearchFlag_ExactId)
543 {
544 /* Exact ID matches? */
545 if (id().toString() == strSearchTag)
546 return this;
547 }
548 /* Are we searching by the exact name? */
549 else if (iSearchFlags & UIChooserItemSearchFlag_ExactName)
550 {
551 /* Exact name matches? */
552 if (name() == strSearchTag)
553 return this;
554 }
555 /* Are we searching by the full name? */
556 else if (iSearchFlags & UIChooserItemSearchFlag_FullName)
557 {
558 /* Full name matches? */
559 if (fullName() == strSearchTag)
560 return this;
561 }
562 /* Are we searching by the few first symbols? */
563 else
564 {
565 /* Name starts with passed symbols? */
566 if (name().startsWith(strSearchTag, Qt::CaseInsensitive))
567 return this;
568 }
569 }
570
571 /* Search among all the children, but machines first: */
572 foreach (UIChooserItem *pItem, items(UIChooserNodeType_Machine))
573 if (UIChooserItem *pFoundItem = pItem->searchForItem(strSearchTag, iSearchFlags))
574 return pFoundItem;
575 foreach (UIChooserItem *pItem, items(UIChooserNodeType_Global))
576 if (UIChooserItem *pFoundItem = pItem->searchForItem(strSearchTag, iSearchFlags))
577 return pFoundItem;
578 foreach (UIChooserItem *pItem, items(UIChooserNodeType_Group))
579 if (UIChooserItem *pFoundItem = pItem->searchForItem(strSearchTag, iSearchFlags))
580 return pFoundItem;
581
582 /* Found nothing? */
583 return 0;
584}
585
586UIChooserItem *UIChooserItemGroup::firstMachineItem()
587{
588 /* If this group-item have at least one machine-item: */
589 if (node()->hasNodes(UIChooserNodeType_Machine))
590 /* Return the first machine-item: */
591 return items(UIChooserNodeType_Machine).first()->firstMachineItem();
592 /* If this group-item have at least one group-item: */
593 else if (node()->hasNodes(UIChooserNodeType_Group))
594 /* Return the first machine-item of the first group-item: */
595 return items(UIChooserNodeType_Group).first()->firstMachineItem();
596 /* Found nothing? */
597 return 0;
598}
599
600void UIChooserItemGroup::updateGeometry()
601{
602 /* Update/activate children layout: */
603 m_pLayout->updateGeometry();
604 m_pLayout->activate();
605
606 /* Call to base-class: */
607 UIChooserItem::updateGeometry();
608
609 /* Special handling for root-groups: */
610 if (isRoot())
611 {
612 /* Root-group should notify Chooser-view if minimum-width-hint was changed: */
613 const int iMinimumWidthHint = minimumWidthHint();
614 if (m_iPreviousMinimumWidthHint != iMinimumWidthHint)
615 {
616 /* Save new minimum-width-hint, notify listener: */
617 m_iPreviousMinimumWidthHint = iMinimumWidthHint;
618 emit sigMinimumWidthHintChanged(m_iPreviousMinimumWidthHint);
619 }
620 }
621}
622
623void UIChooserItemGroup::updateLayout()
624{
625 /* Prepare variables: */
626 const int iMarginHL = data(GroupItemData_MarginHL).toInt();
627 const int iMarginV = data(GroupItemData_MarginV).toInt();
628 const int iParentIndent = data(GroupItemData_ParentIndent).toInt();
629 const int iFullHeaderHeight = m_minimumHeaderSize.height();
630 int iPreviousVerticalIndent = 0;
631
632 /* Header (root-item): */
633 if (isRoot())
634 {
635 /* Acquire view: */
636 const QGraphicsView *pView = model()->scene()->views().first();
637
638 /* Adjust scroll-view geometry: */
639 QSize viewSize = pView->size();
640 viewSize.setHeight(viewSize.height() - iPreviousVerticalIndent);
641 /* Adjust favorite children container: */
642 m_pContainerFavorite->resize(viewSize.width(), m_pContainerFavorite->minimumSizeHint().height());
643 m_pContainerFavorite->setPos(0, iPreviousVerticalIndent);
644 iPreviousVerticalIndent += m_pContainerFavorite->minimumSizeHint().height();
645 /* Adjust other children scroll-area: */
646 m_pScrollArea->resize(viewSize.width(), viewSize.height() - m_pContainerFavorite->minimumSizeHint().height());
647 m_pScrollArea->setPos(0, iPreviousVerticalIndent);
648 }
649 /* Header (non-root-item): */
650 else
651 {
652 /* Toggle-button: */
653 if (m_pToggleButton)
654 {
655 /* Prepare variables: */
656 int iToggleButtonHeight = m_toggleButtonSize.height();
657 /* Layout toggle-button: */
658 int iToggleButtonX = iMarginHL;
659 int iToggleButtonY = iToggleButtonHeight == iFullHeaderHeight ? iMarginV :
660 iMarginV + (iFullHeaderHeight - iToggleButtonHeight) / 2;
661 m_pToggleButton->setPos(iToggleButtonX, iToggleButtonY);
662 /* Show toggle-button: */
663 m_pToggleButton->show();
664 }
665
666 /* Prepare body indent: */
667 iPreviousVerticalIndent = 2 * iMarginV + iFullHeaderHeight;
668
669 /* Adjust scroll-view geometry: */
670 QSize itemSize = size().toSize();
671 itemSize.setHeight(itemSize.height() - iPreviousVerticalIndent);
672 /* Adjust favorite children container: */
673 m_pContainerFavorite->resize(itemSize.width() - iParentIndent, m_pContainerFavorite->minimumSizeHint().height());
674 m_pContainerFavorite->setPos(iParentIndent, iPreviousVerticalIndent);
675 iPreviousVerticalIndent += m_pContainerFavorite->minimumSizeHint().height();
676 /* Adjust other children scroll-area: */
677 m_pScrollArea->resize(itemSize.width() - iParentIndent, itemSize.height() - m_pContainerFavorite->minimumSizeHint().height());
678 m_pScrollArea->setPos(iParentIndent, iPreviousVerticalIndent);
679 }
680
681 /* No body for closed group: */
682 if (isClosed())
683 {
684 m_pContainerFavorite->hide();
685 m_pScrollArea->hide();
686 }
687 /* Body for opened group: */
688 else
689 {
690 m_pContainerFavorite->show();
691 m_pScrollArea->show();
692 foreach (UIChooserItem *pItem, items())
693 pItem->updateLayout();
694 }
695}
696
697int UIChooserItemGroup::minimumWidthHint() const
698{
699 return minimumWidthHintForGroup(isOpened());
700}
701
702int UIChooserItemGroup::minimumHeightHint() const
703{
704 return minimumHeightHintForGroup(isOpened());
705}
706
707QSizeF UIChooserItemGroup::sizeHint(Qt::SizeHint enmWhich, const QSizeF &constraint /* = QSizeF() */) const
708{
709 /* If Qt::MinimumSize requested: */
710 if (enmWhich == Qt::MinimumSize)
711 return minimumSizeHintForGroup(isOpened());
712 /* Else call to base-class: */
713 return UIChooserItem::sizeHint(enmWhich, constraint);
714}
715
716QPixmap UIChooserItemGroup::toPixmap()
717{
718 /* Ask item to paint itself into pixmap: */
719 qreal dDpr = gpManager->windowHandle()->devicePixelRatio();
720 QSize actualSize = size().toSize();
721 QPixmap pixmap(actualSize * dDpr);
722 pixmap.setDevicePixelRatio(dDpr);
723 QPainter painter(&pixmap);
724 QStyleOptionGraphicsItem options;
725 options.rect = QRect(QPoint(0, 0), actualSize);
726 paint(&painter, &options);
727 return pixmap;
728}
729
730bool UIChooserItemGroup::isDropAllowed(QGraphicsSceneDragDropEvent *pEvent, UIChooserItemDragToken where) const
731{
732 /* No drops while saving groups: */
733 if (model()->isGroupSavingInProgress())
734 return false;
735 /* If drag token is shown, its up to parent to decide: */
736 if (where != UIChooserItemDragToken_Off)
737 return parentItem()->isDropAllowed(pEvent);
738
739 /* Else we should check mime format: */
740 const QMimeData *pMimeData = pEvent->mimeData();
741 if (pMimeData->hasFormat(UIChooserItemGroup::className()))
742 {
743 /* Get passed group-item: */
744 const UIChooserItemMimeData *pCastedMimeData = qobject_cast<const UIChooserItemMimeData*>(pMimeData);
745 AssertPtrReturn(pCastedMimeData, false);
746 UIChooserItem *pItem = pCastedMimeData->item();
747 AssertPtrReturn(pItem, false);
748 UIChooserItemGroup *pGroupItem = pItem->toGroupItem();
749 AssertPtrReturn(pGroupItem, false);
750
751 /* For local items: */
752 if ( groupType() == UIChooserNodeGroupType_Local
753 && pGroupItem->groupType() == UIChooserNodeGroupType_Local)
754 {
755 /* Make sure passed machine isn't immutable within own group: */
756 if ( pGroupItem->isContainsLockedMachine()
757 && !m_groupItems.contains(pItem))
758 return false;
759 /* Make sure passed group is not 'this': */
760 if (pItem == this)
761 return false;
762 /* Make sure passed group is not among our parents: */
763 const UIChooserItem *pTestedItem = this;
764 while (UIChooserItem *pParentOfTestedWidget = pTestedItem->parentItem())
765 {
766 if (pItem == pParentOfTestedWidget)
767 return false;
768 pTestedItem = pParentOfTestedWidget;
769 }
770
771 /* Allow finally: */
772 return true;
773 }
774 /* For profiles inside provider and providers inside root group: */
775 else
776 if ( ( groupType() == UIChooserNodeGroupType_Provider
777 && pGroupItem->groupType() == UIChooserNodeGroupType_Profile)
778 || ( groupType() == UIChooserNodeGroupType_Local
779 && pGroupItem->groupType() == UIChooserNodeGroupType_Provider))
780 {
781 /* Make sure passed item is ours: */
782 return m_groupItems.contains(pItem);
783 }
784 }
785 else if (pMimeData->hasFormat(UIChooserItemMachine::className()))
786 {
787 /* Get passed machine-item: */
788 const UIChooserItemMimeData *pCastedMimeData = qobject_cast<const UIChooserItemMimeData*>(pMimeData);
789 AssertPtrReturn(pCastedMimeData, false);
790 UIChooserItem *pItem = pCastedMimeData->item();
791 AssertPtrReturn(pItem, false);
792 UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
793 AssertPtrReturn(pMachineItem, false);
794
795 /* For local items: */
796 if ( groupType() == UIChooserNodeGroupType_Local
797 && pMachineItem->cacheType() == UIVirtualMachineItemType_Local)
798 {
799 /* Make sure passed machine isn't immutable within own group: */
800 if ( pMachineItem->isLockedMachine()
801 && !m_machineItems.contains(pItem))
802 return false;
803 switch (pEvent->proposedAction())
804 {
805 case Qt::MoveAction:
806 {
807 /* Make sure passed item is ours or there is no other item with such id: */
808 return m_machineItems.contains(pItem) || !isContainsMachine(pMachineItem->id());
809 }
810 case Qt::CopyAction:
811 {
812 /* Make sure there is no other item with such id: */
813 return !isContainsMachine(pMachineItem->id());
814 }
815 default:
816 break;
817 }
818 }
819 /* For cloud items: */
820 else
821 if ( groupType() == UIChooserNodeGroupType_Profile
822 && pMachineItem->cacheType() == UIVirtualMachineItemType_CloudReal)
823 {
824 /* Make sure passed item is ours: */
825 return m_machineItems.contains(pItem);
826 }
827 }
828 /* That was invalid mime: */
829 return false;
830}
831
832void UIChooserItemGroup::processDrop(QGraphicsSceneDragDropEvent *pEvent, UIChooserItem *pFromWho, UIChooserItemDragToken where)
833{
834 /* Get mime: */
835 const QMimeData *pMime = pEvent->mimeData();
836 /* Check mime format: */
837 if (pMime->hasFormat(UIChooserItemGroup::className()))
838 {
839 switch (pEvent->proposedAction())
840 {
841 case Qt::MoveAction:
842 case Qt::CopyAction:
843 {
844 /* Remember scene: */
845 UIChooserModel *pModel = model();
846
847 /* Get passed group-item: */
848 const UIChooserItemMimeData *pCastedMime = qobject_cast<const UIChooserItemMimeData*>(pMime);
849 AssertMsg(pCastedMime, ("Can't cast passed mime-data to UIChooserItemMimeData!"));
850 UIChooserNode *pNode = pCastedMime->item()->node();
851
852 /* Check if we have position information: */
853 int iPosition = m_groupItems.size();
854 if (pFromWho && where != UIChooserItemDragToken_Off)
855 {
856 /* Make sure sender item if our child: */
857 AssertMsg(m_groupItems.contains(pFromWho), ("Sender item is NOT our child!"));
858 if (m_groupItems.contains(pFromWho))
859 {
860 iPosition = m_groupItems.indexOf(pFromWho);
861 if (where == UIChooserItemDragToken_Down)
862 ++iPosition;
863 }
864 }
865
866 /* Copy passed group-item into this group: */
867 UIChooserNodeGroup *pNewGroupNode = new UIChooserNodeGroup(node(), iPosition, pNode->toGroupNode());
868 UIChooserItemGroup *pNewGroupItem = new UIChooserItemGroup(this, pNewGroupNode);
869 if (isClosed())
870 open(false);
871
872 /* If proposed action is 'move': */
873 if (pEvent->proposedAction() == Qt::MoveAction)
874 {
875 /* Delete passed item: */
876 delete pNode;
877 }
878
879 /* Update model: */
880 pModel->wipeOutEmptyGroups();
881 pModel->updateNavigationItemList();
882 pModel->updateLayout();
883 pModel->setSelectedItem(pNewGroupItem);
884 pModel->saveGroups();
885 break;
886 }
887 default:
888 break;
889 }
890 }
891 else if (pMime->hasFormat(UIChooserItemMachine::className()))
892 {
893 switch (pEvent->proposedAction())
894 {
895 case Qt::MoveAction:
896 case Qt::CopyAction:
897 {
898 /* Remember scene: */
899 UIChooserModel *pModel = model();
900
901 /* Get passed machine-item: */
902 const UIChooserItemMimeData *pCastedMime = qobject_cast<const UIChooserItemMimeData*>(pMime);
903 AssertMsg(pCastedMime, ("Can't cast passed mime-data to UIChooserItemMimeData!"));
904 UIChooserNode *pNode = pCastedMime->item()->node();
905
906 /* Check if we have position information: */
907 int iPosition = m_machineItems.size();
908 if (pFromWho && where != UIChooserItemDragToken_Off)
909 {
910 /* Make sure sender item if our child: */
911 AssertMsg(m_machineItems.contains(pFromWho), ("Sender item is NOT our child!"));
912 if (m_machineItems.contains(pFromWho))
913 {
914 iPosition = m_machineItems.indexOf(pFromWho);
915 if (where == UIChooserItemDragToken_Down)
916 ++iPosition;
917 }
918 }
919
920 /* Copy passed machine-item into this group: */
921 UIChooserNodeMachine *pNewMachineNode = new UIChooserNodeMachine(node(), iPosition, pNode->toMachineNode());
922 UIChooserItemMachine *pNewMachineItem = new UIChooserItemMachine(this, pNewMachineNode);
923 if (isClosed())
924 open(false);
925
926 /* If proposed action is 'move': */
927 if (pEvent->proposedAction() == Qt::MoveAction)
928 {
929 /* Delete passed item: */
930 delete pNode;
931 }
932
933 /* Update model: */
934 pModel->wipeOutEmptyGroups();
935 pModel->updateNavigationItemList();
936 pModel->updateLayout();
937 pModel->setSelectedItem(pNewMachineItem);
938 pModel->saveGroups();
939 break;
940 }
941 default:
942 break;
943 }
944 }
945}
946
947void UIChooserItemGroup::resetDragToken()
948{
949 /* Reset drag token for this item: */
950 if (dragTokenPlace() != UIChooserItemDragToken_Off)
951 {
952 setDragTokenPlace(UIChooserItemDragToken_Off);
953 update();
954 }
955 /* Reset drag tokens for all the items: */
956 foreach (UIChooserItem *pItem, items())
957 pItem->resetDragToken();
958}
959
960QMimeData* UIChooserItemGroup::createMimeData()
961{
962 return new UIChooserItemMimeData(this);
963}
964
965void UIChooserItemGroup::sltHandleWindowRemapped()
966{
967 /* Update pixmaps: */
968 updatePixmaps();
969}
970
971void UIChooserItemGroup::sltNameEditingFinished()
972{
973 /* Not for root: */
974 if (isRoot())
975 return;
976
977 /* Close name-editor: */
978 m_pNameEditorWidget->close();
979
980 /* Enumerate all the used machine and group names: */
981 QStringList usedNames;
982 foreach (UIChooserItem *pItem, parentItem()->items())
983 {
984 AssertPtrReturnVoid(pItem);
985 if ( pItem->type() == UIChooserNodeType_Machine
986 || ( pItem->type() == UIChooserNodeType_Group
987 && pItem->toGroupItem()->groupType() == UIChooserNodeGroupType_Local))
988 usedNames << pItem->name();
989 }
990 /* If proposed name is empty or not unique, reject it: */
991 QString strNewName = m_pNameEditorWidget->text().trimmed();
992 if (strNewName.isEmpty() || usedNames.contains(strNewName))
993 return;
994
995 /* We should replace forbidden symbols
996 * with ... well, probably underscores: */
997 strNewName.replace(QRegularExpression("[\\\\/:*?\"<>]"), "_");
998
999 /* Set new name, save settings: */
1000 nodeToGroupType()->setName(strNewName);
1001 model()->saveGroups();
1002}
1003
1004void UIChooserItemGroup::sltGroupToggleStart()
1005{
1006 /* Not for root: */
1007 if (isRoot())
1008 return;
1009
1010 /* Toggle started: */
1011 emit sigToggleStarted();
1012
1013 /* Setup animation: */
1014 updateAnimationParameters();
1015
1016 /* Group closed, we are opening it: */
1017 if (nodeToGroupType()->isClosed())
1018 {
1019 /* Toggle-state and navigation will be
1020 * updated on toggle-finish signal! */
1021 }
1022 /* Group opened, we are closing it: */
1023 else
1024 {
1025 /* Update toggle-state: */
1026 nodeToGroupType()->close();
1027 /* Update geometry: */
1028 updateGeometry();
1029 /* Update navigation: */
1030 model()->updateNavigationItemList();
1031 /* Relayout model: */
1032 model()->updateLayout();
1033 }
1034}
1035
1036void UIChooserItemGroup::sltGroupToggleFinish(bool fToggled)
1037{
1038 /* Not for root: */
1039 if (isRoot())
1040 return;
1041
1042 /* Update toggle-state: */
1043 fToggled ? nodeToGroupType()->open() : nodeToGroupType()->close();
1044 /* Update geometry: */
1045 updateGeometry();
1046 /* Update navigation: */
1047 model()->updateNavigationItemList();
1048 /* Relayout model: */
1049 model()->updateLayout();
1050 /* Update toggle-button tool-tip: */
1051 updateToggleButtonToolTip();
1052 /* Repaint finally: */
1053 update();
1054 /* Save changes: */
1055 model()->saveGroups();
1056
1057 /* Toggle finished: */
1058 emit sigToggleFinished();
1059}
1060
1061void UIChooserItemGroup::prepare()
1062{
1063 /* Color tones: */
1064 m_iRootBackgroundDarknessStart = 115;
1065 m_iRootBackgroundDarknessFinal = 150;
1066 m_iItemBackgroundDarknessStart = 100;
1067 m_iItemBackgroundDarknessFinal = 105;
1068#if defined(VBOX_WS_MAC)
1069 m_iHighlightLightness = 105;
1070#elif defined(VBOX_WS_WIN)
1071 m_iHighlightLightness = 190;
1072#else /* !VBOX_WS_MAC && !VBOX_WS_WIN */
1073 m_iHighlightLightness = 105;
1074#endif /* !VBOX_WS_MAC && !VBOX_WS_WIN */
1075
1076 /* Prepare self: */
1077 m_nameFont = font();
1078 m_nameFont.setWeight(QFont::Bold);
1079 m_infoFont = font();
1080 m_minimumHeaderSize = QSize(0, 0);
1081
1082 /* Prepare header widgets of non-root item: */
1083 if (!isRoot())
1084 {
1085 /* Setup toggle-button: */
1086 m_pToggleButton = new UIGraphicsRotatorButton(this, "additionalHeight", isOpened());
1087 if (m_pToggleButton)
1088 {
1089 m_pToggleButton->hide();
1090 connect(m_pToggleButton, &UIGraphicsRotatorButton::sigRotationStart,
1091 this, &UIChooserItemGroup::sltGroupToggleStart);
1092 connect(m_pToggleButton, &UIGraphicsRotatorButton::sigRotationFinish,
1093 this, &UIChooserItemGroup::sltGroupToggleFinish);
1094 }
1095 m_toggleButtonSize = m_pToggleButton ? m_pToggleButton->minimumSizeHint().toSize() : QSize(0, 0);
1096
1097 /* Setup name-editor: */
1098 m_pNameEditorWidget = new UIEditorGroupRename(name());
1099 if (m_pNameEditorWidget)
1100 {
1101 m_pNameEditorWidget->setFont(m_nameFont);
1102 connect(m_pNameEditorWidget, &UIEditorGroupRename::sigEditingFinished,
1103 this, &UIChooserItemGroup::sltNameEditingFinished);
1104 }
1105 }
1106
1107 /* Prepare favorite children container: */
1108 m_pContainerFavorite = new QIGraphicsWidget(this);
1109 if (m_pContainerFavorite)
1110 {
1111 /* Make it always above other children scroll-area: */
1112 m_pContainerFavorite->setZValue(1);
1113
1114 /* Prepare favorite children layout: */
1115 m_pLayoutFavorite = new QGraphicsLinearLayout(Qt::Vertical, m_pContainerFavorite);
1116 if (m_pLayoutFavorite)
1117 {
1118 m_pLayoutFavorite->setContentsMargins(0, 0, 0, 0);
1119 m_pLayoutFavorite->setSpacing(0);
1120 }
1121 }
1122
1123 /* Prepare scroll-area: */
1124 m_pScrollArea = new UIGraphicsScrollArea(Qt::Vertical, this);
1125 if (m_pScrollArea)
1126 {
1127 /* Prepare container: */
1128 m_pContainer = new QIGraphicsWidget;
1129 if (m_pContainer)
1130 {
1131 /* Prepare layout: */
1132 m_pLayout = new QGraphicsLinearLayout(Qt::Vertical, m_pContainer);
1133 if (m_pLayout)
1134 {
1135 m_pLayout->setContentsMargins(0, 0, 0, 0);
1136 m_pLayout->setSpacing(0);
1137
1138 /* Prepare global layout: */
1139 m_pLayoutGlobal = new QGraphicsLinearLayout(Qt::Vertical);
1140 if (m_pLayoutGlobal)
1141 {
1142 m_pLayoutGlobal->setContentsMargins(0, 0, 0, 0);
1143 m_pLayoutGlobal->setSpacing(1);
1144 m_pLayout->addItem(m_pLayoutGlobal);
1145 }
1146
1147 /* Prepare group layout: */
1148 m_pLayoutGroup = new QGraphicsLinearLayout(Qt::Vertical);
1149 if (m_pLayoutGroup)
1150 {
1151 m_pLayoutGroup->setContentsMargins(0, 0, 0, 0);
1152 m_pLayoutGroup->setSpacing(1);
1153 m_pLayout->addItem(m_pLayoutGroup);
1154 }
1155
1156 /* Prepare machine layout: */
1157 m_pLayoutMachine = new QGraphicsLinearLayout(Qt::Vertical);
1158 if (m_pLayoutMachine)
1159 {
1160 m_pLayoutMachine->setContentsMargins(0, 0, 0, 0);
1161 m_pLayoutMachine->setSpacing(1);
1162 m_pLayout->addItem(m_pLayoutMachine);
1163 }
1164 }
1165
1166 /* Assign to scroll-area: */
1167 m_pScrollArea->setViewport(m_pContainer);
1168 }
1169 }
1170
1171 /* Add item directly to the scene (if passed): */
1172 if (m_pScene)
1173 m_pScene->addItem(this);
1174 /* Add item to the parent instead (if passed),
1175 * it will be added to the scene indirectly: */
1176 else if (parentItem())
1177 parentItem()->addItem(this, isFavorite(), position());
1178 /* Otherwise sombody forgot to pass scene or parent. */
1179 else
1180 AssertFailedReturnVoid();
1181
1182 /* Copy contents: */
1183 copyContents(nodeToGroupType());
1184
1185 /* Apply language settings: */
1186 retranslateUi();
1187
1188 /* Initialize non-root items: */
1189 if (!isRoot())
1190 {
1191 updatePixmaps();
1192 updateItemCountInfo();
1193 updateVisibleName();
1194 updateToolTip();
1195 }
1196
1197 /* Configure connections: */
1198 connect(this, &UIChooserItemGroup::sigMinimumWidthHintChanged,
1199 model(), &UIChooserModel::sigRootItemMinimumWidthHintChanged);
1200 if (!isRoot())
1201 {
1202 /* Non-root items can be toggled: */
1203 connect(this, &UIChooserItemGroup::sigToggleStarted,
1204 model(), &UIChooserModel::sigToggleStarted);
1205 connect(this, &UIChooserItemGroup::sigToggleFinished,
1206 model(), &UIChooserModel::sigToggleFinished,
1207 Qt::QueuedConnection);
1208 /* Non-root items requires pixmap updates: */
1209 connect(gpManager, &UIVirtualBoxManager::sigWindowRemapped,
1210 this, &UIChooserItemGroup::sltHandleWindowRemapped);
1211 }
1212
1213 /* Invalidate minimum width hint
1214 * after we isntalled listener: */
1215 m_iPreviousMinimumWidthHint = 0;
1216 /* Update geometry finally: */
1217 updateGeometry();
1218}
1219
1220void UIChooserItemGroup::cleanup()
1221{
1222 /* Delete group name editor: */
1223 delete m_pNameEditorWidget;
1224 m_pNameEditorWidget = 0;
1225
1226 /* Delete all the items: */
1227 while (!m_groupItems.isEmpty()) { delete m_groupItems.last(); }
1228 while (!m_globalItems.isEmpty()) { delete m_globalItems.last(); }
1229 while (!m_machineItems.isEmpty()) { delete m_machineItems.last(); }
1230
1231 /* If that item is current: */
1232 if (model()->currentItem() == this)
1233 {
1234 /* Unset current-item: */
1235 model()->setCurrentItem(0);
1236 }
1237 /* If that item is in selection list: */
1238 if (model()->selectedItems().contains(this))
1239 {
1240 /* Remove item from the selection list: */
1241 model()->removeFromSelectedItems(this);
1242 }
1243 /* If that item is in navigation list: */
1244 if (model()->navigationItems().contains(this))
1245 {
1246 /* Remove item from the navigation list: */
1247 model()->removeFromNavigationItems(this);
1248 }
1249
1250 /* Remove item from the parent: */
1251 if (parentItem())
1252 parentItem()->removeItem(this);
1253}
1254
1255QVariant UIChooserItemGroup::data(int iKey) const
1256{
1257 /* Provide other members with required data: */
1258 switch (iKey)
1259 {
1260 /* Layout hints: */
1261 case GroupItemData_MarginHL: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
1262 case GroupItemData_MarginHR: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 4 * 5;
1263 case GroupItemData_MarginV: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
1264 case GroupItemData_HeaderSpacing: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
1265 case GroupItemData_ChildrenSpacing: return 1;
1266 case GroupItemData_ParentIndent: return QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize) / 2;
1267
1268 /* Default: */
1269 default: break;
1270 }
1271 return QVariant();
1272}
1273
1274int UIChooserItemGroup::additionalHeight() const
1275{
1276 return m_iAdditionalHeight;
1277}
1278
1279void UIChooserItemGroup::setAdditionalHeight(int iAdditionalHeight)
1280{
1281 m_iAdditionalHeight = iAdditionalHeight;
1282 updateGeometry();
1283 model()->updateLayout();
1284}
1285
1286void UIChooserItemGroup::updateAnimationParameters()
1287{
1288 /* Only for item with button: */
1289 if (!m_pToggleButton)
1290 return;
1291
1292 /* Recalculate animation parameters: */
1293 QSizeF openedSize = minimumSizeHintForGroup(true);
1294 QSizeF closedSize = minimumSizeHintForGroup(false);
1295 int iAdditionalHeight = (int)(openedSize.height() - closedSize.height());
1296 m_pToggleButton->setAnimationRange(0, iAdditionalHeight);
1297}
1298
1299void UIChooserItemGroup::updateToggleButtonToolTip()
1300{
1301 /* Only for item with button: */
1302 if (!m_pToggleButton)
1303 return;
1304
1305 /* Update toggle-button tool-tip: */
1306 m_pToggleButton->setToolTip(isOpened() ? tr("Collapse group") : tr("Expand group"));
1307}
1308
1309void UIChooserItemGroup::copyContents(UIChooserNodeGroup *pCopyFrom)
1310{
1311 foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Group))
1312 new UIChooserItemGroup(this, pNode->toGroupNode());
1313 foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Global))
1314 new UIChooserItemGlobal(this, pNode->toGlobalNode());
1315 foreach (UIChooserNode *pNode, pCopyFrom->nodes(UIChooserNodeType_Machine))
1316 new UIChooserItemMachine(this, pNode->toMachineNode());
1317}
1318
1319bool UIChooserItemGroup::isContainsMachine(const QUuid &uId) const
1320{
1321 /* Check each machine-item: */
1322 foreach (UIChooserItem *pItem, m_machineItems)
1323 {
1324 /* Make sure it's really machine node: */
1325 AssertPtrReturn(pItem, false);
1326 UIChooserItemMachine *pMachineItem = pItem->toMachineItem();
1327 AssertPtrReturn(pMachineItem, false);
1328 if (pMachineItem->id() == uId)
1329 return true;
1330 }
1331 /* Found nothing? */
1332 return false;
1333}
1334
1335bool UIChooserItemGroup::isContainsLockedMachine()
1336{
1337 /* Check each machine-item: */
1338 foreach (UIChooserItem *pItem, items(UIChooserNodeType_Machine))
1339 if (pItem->toMachineItem()->isLockedMachine())
1340 return true;
1341 /* Check each group-item: */
1342 foreach (UIChooserItem *pItem, items(UIChooserNodeType_Group))
1343 if (pItem->toGroupItem()->isContainsLockedMachine())
1344 return true;
1345 /* Found nothing? */
1346 return false;
1347}
1348
1349void UIChooserItemGroup::updateItemCountInfo()
1350{
1351 /* Not for root item: */
1352 if (isRoot())
1353 return;
1354
1355 /* Update item info attributes: */
1356 QPaintDevice *pPaintDevice = model()->paintDevice();
1357 QString strInfoGroups = m_groupItems.isEmpty() ? QString() : QString::number(m_groupItems.size());
1358 QString strInfoMachines = m_machineItems.isEmpty() ? QString() : QString::number(m_machineItems.size());
1359 QSize infoSizeGroups = textSize(m_infoFont, pPaintDevice, strInfoGroups);
1360 QSize infoSizeMachines = textSize(m_infoFont, pPaintDevice, strInfoMachines);
1361
1362 /* Update linked values: */
1363 bool fSomethingChanged = false;
1364 if (m_strInfoGroups != strInfoGroups)
1365 {
1366 m_strInfoGroups = strInfoGroups;
1367 fSomethingChanged = true;
1368 }
1369 if (m_strInfoMachines != strInfoMachines)
1370 {
1371 m_strInfoMachines = strInfoMachines;
1372 fSomethingChanged = true;
1373 }
1374 if (m_infoSizeGroups != infoSizeGroups)
1375 {
1376 m_infoSizeGroups = infoSizeGroups;
1377 fSomethingChanged = true;
1378 }
1379 if (m_infoSizeMachines != infoSizeMachines)
1380 {
1381 m_infoSizeMachines = infoSizeMachines;
1382 fSomethingChanged = true;
1383 }
1384 if (fSomethingChanged)
1385 {
1386 updateVisibleName();
1387 updateMinimumHeaderSize();
1388 }
1389}
1390
1391int UIChooserItemGroup::minimumWidthHintForGroup(bool fGroupOpened) const
1392{
1393 /* Calculating proposed width: */
1394 int iProposedWidth = 0;
1395
1396 /* For root item: */
1397 if (isRoot())
1398 {
1399 /* Main root-item always takes body into account: */
1400 if (node()->hasNodes())
1401 {
1402 /* We have to take maximum children width into account: */
1403 iProposedWidth = qMax(m_pContainerFavorite->minimumSizeHint().width(),
1404 m_pContainer->minimumSizeHint().width());
1405 }
1406 }
1407 /* For other items: */
1408 else
1409 {
1410 /* Prepare variables: */
1411 const int iMarginHL = data(GroupItemData_MarginHL).toInt();
1412 const int iMarginHR = data(GroupItemData_MarginHR).toInt();
1413
1414 /* Basically we have to take header width into account: */
1415 iProposedWidth += m_minimumHeaderSize.width();
1416
1417 /* But if group-item is opened: */
1418 if (fGroupOpened)
1419 {
1420 /* We have to take maximum children width into account: */
1421 iProposedWidth = qMax(m_pContainerFavorite->minimumSizeHint().width(),
1422 m_pContainer->minimumSizeHint().width());
1423 }
1424
1425 /* And 2 margins at last - left and right: */
1426 iProposedWidth += iMarginHL + iMarginHR;
1427 }
1428
1429 /* Return result: */
1430 return iProposedWidth;
1431}
1432
1433int UIChooserItemGroup::minimumHeightHintForGroup(bool fGroupOpened) const
1434{
1435 /* Calculating proposed height: */
1436 int iProposedHeight = 0;
1437
1438 /* For root item: */
1439 if (isRoot())
1440 {
1441 /* Main root-item always takes body into account: */
1442 if (node()->hasNodes())
1443 {
1444 /* Prepare variables: */
1445 const int iSpacingV = data(GroupItemData_ChildrenSpacing).toInt();
1446
1447 /* We have to take maximum children height into account: */
1448 iProposedHeight += m_pContainerFavorite->minimumSizeHint().height();
1449 iProposedHeight += m_pContainer->minimumSizeHint().height();
1450 iProposedHeight += iSpacingV;
1451 }
1452 }
1453 /* For other items: */
1454 else
1455 {
1456 /* Prepare variables: */
1457 const int iMarginV = data(GroupItemData_MarginV).toInt();
1458
1459 /* Group-item header have 2 margins - top and bottom: */
1460 iProposedHeight += 2 * iMarginV;
1461 /* And header content height to take into account: */
1462 iProposedHeight += m_minimumHeaderSize.height();
1463
1464 /* But if group-item is opened: */
1465 if (fGroupOpened)
1466 {
1467 /* We have to take maximum children height into account: */
1468 iProposedHeight += m_pContainerFavorite->minimumSizeHint().height();
1469 iProposedHeight += m_pContainer->minimumSizeHint().height();
1470 }
1471
1472 /* Finally, additional height during animation: */
1473 if (!fGroupOpened && m_pToggleButton && m_pToggleButton->isAnimationRunning())
1474 iProposedHeight += m_iAdditionalHeight;
1475 }
1476
1477 /* Return result: */
1478 return iProposedHeight;
1479}
1480
1481QSizeF UIChooserItemGroup::minimumSizeHintForGroup(bool fGroupOpened) const
1482{
1483 return QSizeF(minimumWidthHintForGroup(fGroupOpened), minimumHeightHintForGroup(fGroupOpened));
1484}
1485
1486void UIChooserItemGroup::updateVisibleName()
1487{
1488 /* Not for root item: */
1489 if (isRoot())
1490 return;
1491
1492 /* Prepare variables: */
1493 int iMarginHL = data(GroupItemData_MarginHL).toInt();
1494 int iMarginHR = data(GroupItemData_MarginHR).toInt();
1495 int iHeaderSpacing = data(GroupItemData_HeaderSpacing).toInt();
1496 int iToggleButtonWidth = m_toggleButtonSize.width();
1497 int iGroupPixmapWidth = m_pixmapSizeGroups.width();
1498 int iMachinePixmapWidth = m_pixmapSizeMachines.width();
1499 int iGroupCountTextWidth = m_infoSizeGroups.width();
1500 int iMachineCountTextWidth = m_infoSizeMachines.width();
1501 int iMaximumWidth = (int)geometry().width();
1502
1503 /* Left margin: */
1504 iMaximumWidth -= iMarginHL;
1505 /* Button width: */
1506 if (!isRoot())
1507 iMaximumWidth -= iToggleButtonWidth;
1508 /* Spacing between button and name: */
1509 iMaximumWidth -= iHeaderSpacing;
1510 if (isHovered())
1511 {
1512 /* Spacing between name and info: */
1513 iMaximumWidth -= iHeaderSpacing;
1514 /* Group info width: */
1515 if (!m_groupItems.isEmpty())
1516 iMaximumWidth -= (iGroupPixmapWidth + iGroupCountTextWidth);
1517 /* Machine info width: */
1518 if (!m_machineItems.isEmpty())
1519 iMaximumWidth -= (iMachinePixmapWidth + iMachineCountTextWidth);
1520 }
1521 /* Right margin: */
1522 iMaximumWidth -= iMarginHR;
1523 /* Calculate new visible name and name-size: */
1524 QPaintDevice *pPaintDevice = model()->paintDevice();
1525 QString strVisibleName = compressText(m_nameFont, pPaintDevice, name(), iMaximumWidth);
1526 QSize visibleNameSize = textSize(m_nameFont, pPaintDevice, strVisibleName);
1527
1528 /* Update linked values: */
1529 if (m_visibleNameSize != visibleNameSize)
1530 {
1531 m_visibleNameSize = visibleNameSize;
1532 updateGeometry();
1533 }
1534 if (m_strVisibleName != strVisibleName)
1535 {
1536 m_strVisibleName = strVisibleName;
1537 update();
1538 }
1539}
1540
1541void UIChooserItemGroup::updatePixmaps()
1542{
1543 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
1544 const qreal fDevicePixelRatio = gpManager->windowHandle() ? gpManager->windowHandle()->devicePixelRatio() : 1;
1545 m_groupsPixmap = UIIconPool::iconSet(":/group_abstract_16px.png").pixmap(QSize(iIconMetric, iIconMetric),
1546 fDevicePixelRatio);
1547 m_machinesPixmap = UIIconPool::iconSet(":/machine_abstract_16px.png").pixmap(QSize(iIconMetric, iIconMetric),
1548 fDevicePixelRatio);
1549 m_pixmapSizeGroups = m_groupsPixmap.size() / m_groupsPixmap.devicePixelRatio();
1550 m_pixmapSizeMachines = m_machinesPixmap.size() / m_machinesPixmap.devicePixelRatio();
1551}
1552
1553void UIChooserItemGroup::updateMinimumHeaderSize()
1554{
1555 /* Not for root item: */
1556 if (isRoot())
1557 return;
1558
1559 /* Prepare variables: */
1560 int iHeaderSpacing = data(GroupItemData_HeaderSpacing).toInt();
1561
1562 /* Calculate minimum visible name size: */
1563 QPaintDevice *pPaintDevice = model()->paintDevice();
1564 QFontMetrics fm(m_nameFont, pPaintDevice);
1565 int iMaximumNameWidth = textWidth(m_nameFont, pPaintDevice, 20);
1566 QString strCompressedName = compressText(m_nameFont, pPaintDevice, name(), iMaximumNameWidth);
1567#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
1568 int iMinimumNameWidth = fm.horizontalAdvance(strCompressedName);
1569#else
1570 int iMinimumNameWidth = fm.width(strCompressedName);
1571#endif
1572 int iMinimumNameHeight = fm.height();
1573
1574 /* Calculate minimum width: */
1575 int iHeaderWidth = 0;
1576 /* Button width: */
1577 if (!isRoot())
1578 iHeaderWidth += m_toggleButtonSize.width();
1579 iHeaderWidth += /* Spacing between button and name: */
1580 iHeaderSpacing +
1581 /* Minimum name width: */
1582 iMinimumNameWidth +
1583 /* Spacing between name and info: */
1584 iHeaderSpacing;
1585 /* Group info width: */
1586 if (!m_groupItems.isEmpty())
1587 iHeaderWidth += (m_pixmapSizeGroups.width() + m_infoSizeGroups.width());
1588 /* Machine info width: */
1589 if (!m_machineItems.isEmpty())
1590 iHeaderWidth += (m_pixmapSizeMachines.width() + m_infoSizeMachines.width());
1591
1592 /* Calculate maximum height: */
1593 QList<int> heights;
1594 /* Button height: */
1595 if (!isRoot())
1596 heights << m_toggleButtonSize.height();
1597 heights /* Minimum name height: */
1598 << iMinimumNameHeight
1599 /* Group info heights: */
1600 << m_pixmapSizeGroups.height() << m_infoSizeGroups.height()
1601 /* Machine info heights: */
1602 << m_pixmapSizeMachines.height() << m_infoSizeMachines.height();
1603 /* Button height: */
1604 int iHeaderHeight = 0;
1605 foreach (int iHeight, heights)
1606 iHeaderHeight = qMax(iHeaderHeight, iHeight);
1607
1608 /* Calculate new minimum header size: */
1609 QSize minimumHeaderSize = QSize(iHeaderWidth, iHeaderHeight);
1610
1611 /* Is there something changed? */
1612 if (m_minimumHeaderSize == minimumHeaderSize)
1613 return;
1614
1615 /* Update linked values: */
1616 m_minimumHeaderSize = minimumHeaderSize;
1617 updateGeometry();
1618}
1619
1620void UIChooserItemGroup::updateLayoutSpacings()
1621{
1622 m_pLayout->setItemSpacing(0, m_globalItems.isEmpty() ? 0 : 1);
1623 m_pLayout->setItemSpacing(1, m_groupItems.isEmpty() ? 0 : 1);
1624 m_pLayout->setItemSpacing(2, m_machineItems.isEmpty() ? 0 : 1);
1625}
1626
1627void UIChooserItemGroup::paintBackground(QPainter *pPainter, const QRect &rect)
1628{
1629 /* Save painter: */
1630 pPainter->save();
1631
1632 /* Root-item: */
1633 if (isRoot())
1634 {
1635 /* Acquire background color: */
1636 const QColor backgroundColor = QApplication::palette().color(QPalette::Active, QPalette::Window);
1637
1638 /* Paint default background: */
1639 QLinearGradient gradientDefault(rect.topRight(), rect.bottomLeft());
1640 gradientDefault.setColorAt(0, backgroundColor.darker(m_iRootBackgroundDarknessStart));
1641 gradientDefault.setColorAt(1, backgroundColor.darker(m_iRootBackgroundDarknessFinal));
1642 pPainter->fillRect(rect, gradientDefault);
1643 }
1644 else
1645 {
1646 /* Acquire background color: */
1647 const QColor backgroundColor = model()->selectedItems().contains(this)
1648 ? QApplication::palette().color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightness)
1649 : QApplication::palette().color(QPalette::Active, QPalette::Window);
1650
1651 /* Paint default background: */
1652 QLinearGradient gradientDefault(rect.topRight(), rect.bottomLeft());
1653 gradientDefault.setColorAt(0, backgroundColor.darker(m_iItemBackgroundDarknessStart));
1654 gradientDefault.setColorAt(1, backgroundColor.darker(m_iItemBackgroundDarknessFinal));
1655 pPainter->fillRect(rect, gradientDefault);
1656
1657 /* If element is hovered: */
1658 if (animatedValue())
1659 {
1660 /* Calculate header rectangle: */
1661 const int iMarginV = data(GroupItemData_MarginV).toInt();
1662 const int iFullHeaderHeight = 2 * iMarginV + m_minimumHeaderSize.height();
1663 QRect headRect = rect;
1664 headRect.setHeight(iFullHeaderHeight);
1665
1666 /* Acquire header color: */
1667 QColor headColor = backgroundColor.lighter(130);
1668
1669 /* Paint hovered background: */
1670 QColor hcTone1 = headColor;
1671 QColor hcTone2 = headColor;
1672 hcTone1.setAlpha(255 * animatedValue() / 100);
1673 hcTone2.setAlpha(0);
1674 QLinearGradient gradientHovered(headRect.topLeft(), headRect.bottomLeft());
1675 gradientHovered.setColorAt(0, hcTone1);
1676 gradientHovered.setColorAt(1, hcTone2);
1677 pPainter->fillRect(headRect, gradientHovered);
1678 }
1679
1680 /* Paint drag token UP? */
1681 if (dragTokenPlace() != UIChooserItemDragToken_Off)
1682 {
1683 QLinearGradient dragTokenGradient;
1684 QRect dragTokenRect = rect;
1685 if (dragTokenPlace() == UIChooserItemDragToken_Up)
1686 {
1687 dragTokenRect.setHeight(5);
1688 dragTokenGradient.setStart(dragTokenRect.bottomLeft());
1689 dragTokenGradient.setFinalStop(dragTokenRect.topLeft());
1690 }
1691 else if (dragTokenPlace() == UIChooserItemDragToken_Down)
1692 {
1693 dragTokenRect.setTopLeft(dragTokenRect.bottomLeft() - QPoint(0, 5));
1694 dragTokenGradient.setStart(dragTokenRect.topLeft());
1695 dragTokenGradient.setFinalStop(dragTokenRect.bottomLeft());
1696 }
1697 dragTokenGradient.setColorAt(0, backgroundColor.darker(dragTokenDarkness()));
1698 dragTokenGradient.setColorAt(1, backgroundColor.darker(dragTokenDarkness() + 40));
1699 pPainter->fillRect(dragTokenRect, dragTokenGradient);
1700 }
1701 }
1702
1703 /* Restore painter: */
1704 pPainter->restore();
1705}
1706
1707void UIChooserItemGroup::paintFrame(QPainter *pPainter, const QRect &rectangle)
1708{
1709 /* Not for roots: */
1710 if (isRoot())
1711 return;
1712
1713 /* Only selected item should have a frame: */
1714 if (!model()->selectedItems().contains(this))
1715 return;
1716
1717 /* Save painter: */
1718 pPainter->save();
1719
1720 /* Prepare variables: */
1721 const int iMarginV = data(GroupItemData_MarginV).toInt();
1722 const int iParentIndent = data(GroupItemData_ParentIndent).toInt();
1723 const int iFullHeaderHeight = 2 * iMarginV + m_minimumHeaderSize.height();
1724
1725 /* Prepare color: */
1726 const QColor frameColor = QApplication::palette().color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightness - 40);
1727
1728 /* Create/assign pen: */
1729 QPen pen(frameColor);
1730 pen.setWidth(0);
1731 pPainter->setPen(pen);
1732
1733 /* Calculate top rectangle: */
1734 QRect topRect = rectangle;
1735 if (nodeToGroupType()->isOpened())
1736 topRect.setBottom(topRect.top() + iFullHeaderHeight - 1);
1737
1738 /* Draw borders: */
1739 pPainter->drawLine(rectangle.topLeft(), rectangle.topRight());
1740 if (node()->hasNodes() && nodeToGroupType()->isOpened())
1741 pPainter->drawLine(topRect.bottomLeft() + QPoint(iParentIndent, 0), topRect.bottomRight() + QPoint(1, 0));
1742 else
1743 pPainter->drawLine(topRect.bottomLeft(), topRect.bottomRight() + QPoint(1, 0));
1744 pPainter->drawLine(rectangle.topLeft(), rectangle.bottomLeft());
1745
1746 /* Restore painter: */
1747 pPainter->restore();
1748}
1749
1750void UIChooserItemGroup::paintHeader(QPainter *pPainter, const QRect &rect)
1751{
1752 /* Not for root item: */
1753 if (isRoot())
1754 return;
1755
1756 /* Prepare variables: */
1757 const int iMarginHL = data(GroupItemData_MarginHL).toInt();
1758 const int iMarginHR = data(GroupItemData_MarginHR).toInt();
1759 const int iMarginV = data(GroupItemData_MarginV).toInt();
1760 const int iHeaderSpacing = data(GroupItemData_HeaderSpacing).toInt();
1761 const int iFullHeaderHeight = m_minimumHeaderSize.height();
1762
1763 /* Selected item foreground: */
1764 if (model()->selectedItems().contains(this))
1765 {
1766 /* Prepare palette: */
1767 const QPalette pal = QApplication::palette();
1768
1769 /* Get background color: */
1770 const QColor background = pal.color(QPalette::Active, QPalette::Highlight).lighter(m_iHighlightLightness);
1771
1772 /* Gather foreground color for background one: */
1773 const QColor foreground = suitableForegroundColor(pal, background);
1774 pPainter->setPen(foreground);
1775 }
1776
1777 /* Paint name: */
1778 int iNameX = iMarginHL;
1779 if (!isRoot())
1780 iNameX += m_toggleButtonSize.width();
1781 iNameX += iHeaderSpacing;
1782 int iNameY = m_visibleNameSize.height() == iFullHeaderHeight ? iMarginV :
1783 iMarginV + (iFullHeaderHeight - m_visibleNameSize.height()) / 2;
1784 paintText(/* Painter: */
1785 pPainter,
1786 /* Point to paint in: */
1787 QPoint(iNameX, iNameY),
1788 /* Font to paint text: */
1789 m_nameFont,
1790 /* Paint device: */
1791 model()->paintDevice(),
1792 /* Text to paint: */
1793 m_strVisibleName);
1794
1795 /* Should we add more info? */
1796 if (isHovered())
1797 {
1798 /* Indent: */
1799 int iHorizontalIndent = rect.right() - iMarginHR;
1800
1801 /* Should we draw machine count info? */
1802 if (!m_strInfoMachines.isEmpty())
1803 {
1804 iHorizontalIndent -= m_infoSizeMachines.width();
1805 int iMachineCountTextX = iHorizontalIndent;
1806 int iMachineCountTextY = m_infoSizeMachines.height() == iFullHeaderHeight ?
1807 iMarginV : iMarginV + (iFullHeaderHeight - m_infoSizeMachines.height()) / 2;
1808 paintText(/* Painter: */
1809 pPainter,
1810 /* Point to paint in: */
1811 QPoint(iMachineCountTextX, iMachineCountTextY),
1812 /* Font to paint text: */
1813 m_infoFont,
1814 /* Paint device: */
1815 model()->paintDevice(),
1816 /* Text to paint: */
1817 m_strInfoMachines);
1818
1819 iHorizontalIndent -= m_pixmapSizeMachines.width();
1820 int iMachinePixmapX = iHorizontalIndent;
1821 int iMachinePixmapY = m_pixmapSizeMachines.height() == iFullHeaderHeight ?
1822 iMarginV : iMarginV + (iFullHeaderHeight - m_pixmapSizeMachines.height()) / 2;
1823 paintPixmap(/* Painter: */
1824 pPainter,
1825 /* Point to paint in: */
1826 QPoint(iMachinePixmapX, iMachinePixmapY),
1827 /* Pixmap to paint: */
1828 m_machinesPixmap);
1829 }
1830
1831 /* Should we draw group count info? */
1832 if (!m_strInfoGroups.isEmpty())
1833 {
1834 iHorizontalIndent -= m_infoSizeGroups.width();
1835 int iGroupCountTextX = iHorizontalIndent;
1836 int iGroupCountTextY = m_infoSizeGroups.height() == iFullHeaderHeight ?
1837 iMarginV : iMarginV + (iFullHeaderHeight - m_infoSizeGroups.height()) / 2;
1838 paintText(/* Painter: */
1839 pPainter,
1840 /* Point to paint in: */
1841 QPoint(iGroupCountTextX, iGroupCountTextY),
1842 /* Font to paint text: */
1843 m_infoFont,
1844 /* Paint device: */
1845 model()->paintDevice(),
1846 /* Text to paint: */
1847 m_strInfoGroups);
1848
1849 iHorizontalIndent -= m_pixmapSizeGroups.width();
1850 int iGroupPixmapX = iHorizontalIndent;
1851 int iGroupPixmapY = m_pixmapSizeGroups.height() == iFullHeaderHeight ?
1852 iMarginV : iMarginV + (iFullHeaderHeight - m_pixmapSizeGroups.height()) / 2;
1853 paintPixmap(/* Painter: */
1854 pPainter,
1855 /* Point to paint in: */
1856 QPoint(iGroupPixmapX, iGroupPixmapY),
1857 /* Pixmap to paint: */
1858 m_groupsPixmap);
1859 }
1860 }
1861}
1862
1863
1864/*********************************************************************************************************************************
1865* Class UIEditorGroupRename implementation. *
1866*********************************************************************************************************************************/
1867
1868UIEditorGroupRename::UIEditorGroupRename(const QString &strName)
1869 : QWidget(0, Qt::Popup)
1870 , m_pLineEdit(0)
1871{
1872 /* Create layout: */
1873 QHBoxLayout *pLayout = new QHBoxLayout(this);
1874 if (pLayout)
1875 {
1876 /* Configure layout: */
1877 pLayout->setContentsMargins(0, 0, 0, 0);
1878
1879 /* Create line-edit: */
1880 m_pLineEdit = new QLineEdit(strName);
1881 if (m_pLineEdit)
1882 {
1883 setFocusProxy(m_pLineEdit);
1884 m_pLineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
1885 m_pLineEdit->setTextMargins(0, 0, 0, 0);
1886 connect(m_pLineEdit, &QLineEdit::returnPressed,
1887 this, &UIEditorGroupRename::sigEditingFinished);
1888 }
1889
1890 /* Add into layout: */
1891 pLayout->addWidget(m_pLineEdit);
1892 }
1893}
1894
1895QString UIEditorGroupRename::text() const
1896{
1897 return m_pLineEdit->text();
1898}
1899
1900void UIEditorGroupRename::setText(const QString &strText)
1901{
1902 m_pLineEdit->setText(strText);
1903}
1904
1905void UIEditorGroupRename::setFont(const QFont &font)
1906{
1907 QWidget::setFont(font);
1908 m_pLineEdit->setFont(font);
1909}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use