VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette