VirtualBox

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

Last change on this file was 103771, checked in by vboxsync, 6 weeks ago

FE/Qt: UICommon: Switching dependency from UICommon to UIGlobalSession whenever is possible.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.4 KB
Line 
1/* $Id: UIChooserAbstractModel.cpp 103771 2024-03-11 15:16:04Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIChooserAbstractModel 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 <QRegularExpression>
30#include <QThread>
31
32/* GUI includes: */
33#include "UICommon.h"
34#include "UIChooser.h"
35#include "UIChooserAbstractModel.h"
36#include "UIChooserNode.h"
37#include "UIChooserNodeGroup.h"
38#include "UIChooserNodeGlobal.h"
39#include "UIChooserNodeMachine.h"
40#include "UICloudNetworkingStuff.h"
41#include "UIExtraDataManager.h"
42#include "UIGlobalSession.h"
43#include "UILoggingDefs.h"
44#include "UIMessageCenter.h"
45#include "UINotificationCenter.h"
46#include "UIProgressTaskReadCloudMachineList.h"
47#include "UIVirtualBoxEventHandler.h"
48#include "UIVirtualMachineItemCloud.h"
49
50/* COM includes: */
51#include "CCloudMachine.h"
52#include "CCloudProfile.h"
53#include "CCloudProvider.h"
54#include "CMachine.h"
55
56/* Type defs: */
57typedef QSet<QString> UIStringSet;
58
59
60/** QThread subclass allowing to save group settings asynchronously. */
61class UIThreadGroupSettingsSave : public QThread
62{
63 Q_OBJECT;
64
65signals:
66
67 /** Notifies about machine with certain @a uMachineId to be reloaded. */
68 void sigReload(const QUuid &uMachineId);
69
70 /** Notifies about task is complete. */
71 void sigComplete();
72
73public:
74
75 /** Returns group settings saving thread instance. */
76 static UIThreadGroupSettingsSave *instance();
77 /** Prepares group settings saving thread instance. */
78 static void prepare();
79 /** Cleanups group settings saving thread instance. */
80 static void cleanup();
81
82 /** Configures @a group settings saving thread with corresponding @a pListener.
83 * @param oldLists Brings the old settings list to be compared.
84 * @param newLists Brings the new settings list to be saved. */
85 void configure(QObject *pParent,
86 const QMap<QString, QStringList> &oldLists,
87 const QMap<QString, QStringList> &newLists);
88
89protected:
90
91 /** Constructs group settings saving thread. */
92 UIThreadGroupSettingsSave();
93 /** Destructs group settings saving thread. */
94 virtual ~UIThreadGroupSettingsSave() RT_OVERRIDE;
95
96 /** Contains a thread task to be executed. */
97 virtual void run() RT_OVERRIDE;
98
99 /** Holds the singleton instance. */
100 static UIThreadGroupSettingsSave *s_pInstance;
101
102 /** Holds the map of group settings to be compared. */
103 QMap<QString, QStringList> m_oldLists;
104 /** Holds the map of group settings to be saved. */
105 QMap<QString, QStringList> m_newLists;
106};
107
108
109/** QThread subclass allowing to save group definitions asynchronously. */
110class UIThreadGroupDefinitionsSave : public QThread
111{
112 Q_OBJECT;
113
114signals:
115
116 /** Notifies about task is complete. */
117 void sigComplete();
118
119public:
120
121 /** Returns group definitions saving thread instance. */
122 static UIThreadGroupDefinitionsSave *instance();
123 /** Prepares group definitions saving thread instance. */
124 static void prepare();
125 /** Cleanups group definitions saving thread instance. */
126 static void cleanup();
127
128 /** Configures group definitions saving thread with corresponding @a pListener.
129 * @param lists Brings definitions lists to be saved. */
130 void configure(QObject *pListener,
131 const QMap<QString, QStringList> &lists);
132
133protected:
134
135 /** Constructs group definitions saving thread. */
136 UIThreadGroupDefinitionsSave();
137 /** Destructs group definitions saving thread. */
138 virtual ~UIThreadGroupDefinitionsSave() RT_OVERRIDE;
139
140 /** Contains a thread task to be executed. */
141 virtual void run() RT_OVERRIDE;
142
143 /** Holds the singleton instance. */
144 static UIThreadGroupDefinitionsSave *s_pInstance;
145
146 /** Holds the map of group definitions to be saved. */
147 QMap<QString, QStringList> m_lists;
148};
149
150
151/*********************************************************************************************************************************
152* Class UIThreadGroupSettingsSave implementation. *
153*********************************************************************************************************************************/
154
155/* static */
156UIThreadGroupSettingsSave *UIThreadGroupSettingsSave::s_pInstance = 0;
157
158/* static */
159UIThreadGroupSettingsSave *UIThreadGroupSettingsSave::instance()
160{
161 return s_pInstance;
162}
163
164/* static */
165void UIThreadGroupSettingsSave::prepare()
166{
167 /* Make sure instance is not prepared: */
168 if (s_pInstance)
169 return;
170
171 /* Crate instance: */
172 new UIThreadGroupSettingsSave;
173}
174
175/* static */
176void UIThreadGroupSettingsSave::cleanup()
177{
178 /* Make sure instance is prepared: */
179 if (!s_pInstance)
180 return;
181
182 /* Delete instance: */
183 delete s_pInstance;
184}
185
186void UIThreadGroupSettingsSave::configure(QObject *pParent,
187 const QMap<QString, QStringList> &oldLists,
188 const QMap<QString, QStringList> &newLists)
189{
190 m_oldLists = oldLists;
191 m_newLists = newLists;
192 UIChooserAbstractModel *pChooserAbstractModel = qobject_cast<UIChooserAbstractModel*>(pParent);
193 AssertPtrReturnVoid(pChooserAbstractModel);
194 {
195 connect(this, &UIThreadGroupSettingsSave::sigComplete,
196 pChooserAbstractModel, &UIChooserAbstractModel::sltGroupSettingsSaveComplete);
197 }
198}
199
200UIThreadGroupSettingsSave::UIThreadGroupSettingsSave()
201{
202 /* Assign instance: */
203 s_pInstance = this;
204}
205
206UIThreadGroupSettingsSave::~UIThreadGroupSettingsSave()
207{
208 /* Make sure thread work is complete: */
209 wait();
210
211 /* Erase instance: */
212 s_pInstance = 0;
213}
214
215void UIThreadGroupSettingsSave::run()
216{
217 /* COM prepare: */
218 COMBase::InitializeCOM(false);
219
220 /* For every particular machine ID: */
221 foreach (const QString &strId, m_newLists.keys())
222 {
223 /* Get new group list/set: */
224 const QStringList &newGroupList = m_newLists.value(strId);
225#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
226 const UIStringSet newGroupSet(newGroupList.begin(), newGroupList.end());
227#else
228 const UIStringSet &newGroupSet = UIStringSet::fromList(newGroupList);
229#endif
230 /* Get old group list/set: */
231 const QStringList &oldGroupList = m_oldLists.value(strId);
232#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
233 const UIStringSet oldGroupSet(oldGroupList.begin(), oldGroupList.end());
234#else
235 const UIStringSet &oldGroupSet = UIStringSet::fromList(oldGroupList);
236#endif
237 /* Make sure group set changed: */
238 if (newGroupSet == oldGroupSet)
239 continue;
240
241 /* The next steps are subsequent.
242 * Every of them is mandatory in order to continue
243 * with common cleanup in case of failure.
244 * We have to simulate a try-catch block. */
245 CSession comSession;
246 CMachine comMachine;
247 do
248 {
249 /* 1. Open session: */
250 comSession = uiCommon().openSession(QUuid(strId));
251 if (comSession.isNull())
252 break;
253
254 /* 2. Get session machine: */
255 comMachine = comSession.GetMachine();
256 if (comMachine.isNull())
257 break;
258
259 /* 3. Set new groups: */
260 comMachine.SetGroups(newGroupList.toVector());
261 if (!comMachine.isOk())
262 {
263 msgCenter().cannotSetGroups(comMachine);
264 break;
265 }
266
267 /* 4. Save settings: */
268 comMachine.SaveSettings();
269 if (!comMachine.isOk())
270 {
271 msgCenter().cannotSaveMachineSettings(comMachine);
272 break;
273 }
274 } while (0);
275
276 /* Cleanup if necessary: */
277 if (comMachine.isNull() || !comMachine.isOk())
278 emit sigReload(QUuid(strId));
279 if (!comSession.isNull())
280 comSession.UnlockMachine();
281 }
282
283 /* Notify listeners about completeness: */
284 emit sigComplete();
285
286 /* COM cleanup: */
287 COMBase::CleanupCOM();
288}
289
290
291/*********************************************************************************************************************************
292* Class UIThreadGroupDefinitionsSave implementation. *
293*********************************************************************************************************************************/
294
295/* static */
296UIThreadGroupDefinitionsSave *UIThreadGroupDefinitionsSave::s_pInstance = 0;
297
298/* static */
299UIThreadGroupDefinitionsSave *UIThreadGroupDefinitionsSave::instance()
300{
301 return s_pInstance;
302}
303
304/* static */
305void UIThreadGroupDefinitionsSave::prepare()
306{
307 /* Make sure instance is not prepared: */
308 if (s_pInstance)
309 return;
310
311 /* Crate instance: */
312 new UIThreadGroupDefinitionsSave;
313}
314
315/* static */
316void UIThreadGroupDefinitionsSave::cleanup()
317{
318 /* Make sure instance is prepared: */
319 if (!s_pInstance)
320 return;
321
322 /* Delete instance: */
323 delete s_pInstance;
324}
325
326void UIThreadGroupDefinitionsSave::configure(QObject *pParent,
327 const QMap<QString, QStringList> &groups)
328{
329 m_lists = groups;
330 UIChooserAbstractModel *pChooserAbstractModel = qobject_cast<UIChooserAbstractModel*>(pParent);
331 AssertPtrReturnVoid(pChooserAbstractModel);
332 {
333 connect(this, &UIThreadGroupDefinitionsSave::sigComplete,
334 pChooserAbstractModel, &UIChooserAbstractModel::sltGroupDefinitionsSaveComplete);
335 }
336}
337
338UIThreadGroupDefinitionsSave::UIThreadGroupDefinitionsSave()
339{
340 /* Assign instance: */
341 s_pInstance = this;
342}
343
344UIThreadGroupDefinitionsSave::~UIThreadGroupDefinitionsSave()
345{
346 /* Make sure thread work is complete: */
347 wait();
348
349 /* Erase instance: */
350 s_pInstance = 0;
351}
352
353void UIThreadGroupDefinitionsSave::run()
354{
355 /* COM prepare: */
356 COMBase::InitializeCOM(false);
357
358 /* Acquire a list of known group definition keys: */
359 QStringList knownKeys = gEDataManager->knownMachineGroupDefinitionKeys();
360 /* For every group definition to be saved: */
361 foreach (const QString &strId, m_lists.keys())
362 {
363 /* Save definition only if there is a change: */
364 if (gEDataManager->machineGroupDefinitions(strId) != m_lists.value(strId))
365 gEDataManager->setMachineGroupDefinitions(strId, m_lists.value(strId));
366 /* Remove it from known keys: */
367 knownKeys.removeAll(strId);
368 }
369 /* Wipe out rest of known group definitions: */
370 foreach (const QString strId, knownKeys)
371 gEDataManager->setMachineGroupDefinitions(strId, QStringList());
372
373 /* Notify listeners about completeness: */
374 emit sigComplete();
375
376 /* COM cleanup: */
377 COMBase::CleanupCOM();
378}
379
380
381/*********************************************************************************************************************************
382* Class UIChooserAbstractModel implementation. *
383*********************************************************************************************************************************/
384
385UIChooserAbstractModel::UIChooserAbstractModel(UIChooser *pParent)
386 : QObject(pParent)
387 , m_pParent(pParent)
388 , m_pInvisibleRootNode(0)
389 , m_fKeepCloudNodesUpdated(false)
390{
391 prepare();
392}
393
394UIChooserAbstractModel::~UIChooserAbstractModel()
395{
396 cleanup();
397}
398
399void UIChooserAbstractModel::init()
400{
401 /* Create invisible root group node: */
402 m_pInvisibleRootNode = new UIChooserNodeGroup(0 /* parent */,
403 0 /* position */,
404 QUuid() /* id */,
405 QString() /* name */,
406 UIChooserNodeGroupType_Local,
407 true /* opened */);
408 if (invisibleRoot())
409 {
410 /* Link root to this model: */
411 invisibleRoot()->setModel(this);
412
413 /* Create global node: */
414 new UIChooserNodeGlobal(invisibleRoot() /* parent */,
415 0 /* position */,
416 shouldGlobalNodeBeFavorite(invisibleRoot()),
417 QString() /* tip */);
418
419 /* Reload local tree: */
420 reloadLocalTree();
421 /* Reload cloud tree: */
422 reloadCloudTree();
423 }
424}
425
426void UIChooserAbstractModel::deinit()
427{
428 /* Make sure all saving steps complete: */
429 makeSureGroupSettingsSaveIsFinished();
430 makeSureGroupDefinitionsSaveIsFinished();
431}
432
433void UIChooserAbstractModel::wipeOutEmptyGroups()
434{
435 wipeOutEmptyGroupsStartingFrom(invisibleRoot());
436}
437
438QStringList UIChooserAbstractModel::possibleGroupNodeNamesForMachineNodeToMove(const QUuid &uId)
439{
440 /* Search for all the machine nodes with passed ID: */
441 QList<UIChooserNode*> machineNodes;
442 invisibleRoot()->searchForNodes(uId.toString(),
443 UIChooserItemSearchFlag_Machine | UIChooserItemSearchFlag_ExactId,
444 machineNodes);
445
446 /* Return group nodes starting from root one: */
447 return gatherPossibleGroupNodeNames(invisibleRoot(), machineNodes);
448}
449
450QStringList UIChooserAbstractModel::possibleGroupNodeNamesForGroupNodeToMove(const QString &strFullName)
451{
452 /* Search for all the group nodes with passed full-name: */
453 QList<UIChooserNode*> groupNodes;
454 invisibleRoot()->searchForNodes(strFullName,
455 UIChooserItemSearchFlag_LocalGroup | UIChooserItemSearchFlag_FullName,
456 groupNodes);
457
458 /* Return group nodes starting from root one: */
459 return gatherPossibleGroupNodeNames(invisibleRoot(), groupNodes);
460}
461
462/* static */
463QString UIChooserAbstractModel::uniqueGroupName(UIChooserNode *pRoot)
464{
465 /* Enumerate all the group names: */
466 QStringList groupNames;
467 foreach (UIChooserNode *pNode, pRoot->nodes(UIChooserNodeType_Group))
468 groupNames << pNode->name();
469
470 /* Prepare reg-exp: */
471 const QString strMinimumName = tr("New group");
472 const QString strShortTemplate = strMinimumName;
473 const QString strFullTemplate = strShortTemplate + QString(" (\\d+)");
474 const QRegularExpression shortRegExp(strShortTemplate);
475 const QRegularExpression fullRegExp(strFullTemplate);
476
477 /* Search for the maximum index: */
478 int iMinimumPossibleNumber = 0;
479 foreach (const QString &strName, groupNames)
480 {
481 const QRegularExpressionMatch mtShort = shortRegExp.match(strName);
482 const QRegularExpressionMatch mtFull = fullRegExp.match(strName);
483 if (mtShort.hasMatch())
484 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, 2);
485 else if (mtFull.hasMatch())
486 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, mtFull.captured(1).toInt() + 1);
487 }
488
489 /* Prepare/return result: */
490 QString strResult = strMinimumName;
491 if (iMinimumPossibleNumber)
492 strResult += " " + QString::number(iMinimumPossibleNumber);
493 return strResult;
494}
495
496void UIChooserAbstractModel::performSearch(const QString &strSearchTerm, int iSearchFlags)
497{
498 /* Make sure invisible root exists: */
499 AssertPtrReturnVoid(invisibleRoot());
500
501 /* Currently we perform the search only for machines, when this to be changed make
502 * sure the disabled flags of the other item types are also managed correctly. */
503
504 /* Reset the search first to erase the disabled flag,
505 * this also returns a full list of all machine nodes: */
506 const QList<UIChooserNode*> nodes = resetSearch();
507
508 /* Stop here if no search conditions specified: */
509 if (strSearchTerm.isEmpty())
510 return;
511
512 /* Search for all the nodes matching required condition: */
513 invisibleRoot()->searchForNodes(strSearchTerm, iSearchFlags, m_searchResults);
514
515 /* Assign/reset the disabled flag for required nodes: */
516 foreach (UIChooserNode *pNode, nodes)
517 {
518 AssertPtrReturnVoid(pNode);
519 pNode->setDisabled(!m_searchResults.contains(pNode));
520 }
521}
522
523QList<UIChooserNode*> UIChooserAbstractModel::resetSearch()
524{
525 /* Prepare resulting nodes: */
526 QList<UIChooserNode*> nodes;
527
528 /* Make sure invisible root exists: */
529 AssertPtrReturn(invisibleRoot(), nodes);
530
531 /* Calling UIChooserNode::searchForNodes with an empty search term
532 * returns a list all nodes (of the whole tree) of the required type: */
533 invisibleRoot()->searchForNodes(QString(), UIChooserItemSearchFlag_Machine, nodes);
534
535 /* Reset the disabled flag of the nodes first: */
536 foreach (UIChooserNode *pNode, nodes)
537 {
538 AssertPtrReturn(pNode, nodes);
539 pNode->setDisabled(false);
540 }
541
542 /* Reset the search result related data: */
543 m_searchResults.clear();
544
545 /* Return nodes: */
546 return nodes;
547}
548
549QList<UIChooserNode*> UIChooserAbstractModel::searchResult() const
550{
551 return m_searchResults;
552}
553
554void UIChooserAbstractModel::saveGroups()
555{
556 emit sigSaveSettings();
557}
558
559bool UIChooserAbstractModel::isGroupSavingInProgress() const
560{
561 return UIThreadGroupSettingsSave::instance()
562 || UIThreadGroupDefinitionsSave::instance();
563}
564
565/* static */
566QString UIChooserAbstractModel::toOldStyleUuid(const QUuid &uId)
567{
568 return uId.toString().remove(QRegularExpression("[{}]"));
569}
570
571/* static */
572QString UIChooserAbstractModel::prefixToString(UIChooserNodeDataPrefixType enmType)
573{
574 switch (enmType)
575 {
576 /* Global nodes: */
577 case UIChooserNodeDataPrefixType_Global: return "n";
578 /* Machine nodes: */
579 case UIChooserNodeDataPrefixType_Machine: return "m";
580 /* Group nodes: */
581 case UIChooserNodeDataPrefixType_Local: return "g";
582 case UIChooserNodeDataPrefixType_Provider: return "p";
583 case UIChooserNodeDataPrefixType_Profile: return "a";
584 }
585 return QString();
586}
587
588/* static */
589QString UIChooserAbstractModel::optionToString(UIChooserNodeDataOptionType enmType)
590{
591 switch (enmType)
592 {
593 /* Global nodes: */
594 case UIChooserNodeDataOptionType_GlobalFavorite: return "f";
595 /* Group nodes: */
596 case UIChooserNodeDataOptionType_GroupOpened: return "o";
597 }
598 return QString();
599}
600
601/* static */
602QString UIChooserAbstractModel::valueToString(UIChooserNodeDataValueType enmType)
603{
604 switch (enmType)
605 {
606 /* Global nodes: */
607 case UIChooserNodeDataValueType_GlobalDefault: return "GLOBAL";
608 }
609 return QString();
610}
611
612void UIChooserAbstractModel::setKeepCloudNodesUpdated(bool fUpdate)
613{
614 /* Make sure something changed: */
615 if (m_fKeepCloudNodesUpdated == fUpdate)
616 return;
617
618 /* Holds the value: */
619 m_fKeepCloudNodesUpdated = fUpdate;
620
621 /* Update all the real cloud machine items: */
622 foreach (UIChooserNode *pNode, enumerateCloudMachineNodes())
623 {
624 AssertReturnVoid(pNode && pNode->type() == UIChooserNodeType_Machine);
625 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
626 AssertPtrReturnVoid(pMachineNode);
627 if (pMachineNode->cacheType() != UIVirtualMachineItemType_CloudReal)
628 continue;
629 UIVirtualMachineItemCloud *pCloudMachineItem = pMachineNode->cache()->toCloud();
630 AssertPtrReturnVoid(pCloudMachineItem);
631 pCloudMachineItem->setUpdateRequiredByGlobalReason(m_fKeepCloudNodesUpdated);
632 if (m_fKeepCloudNodesUpdated)
633 pCloudMachineItem->updateInfoAsync(false /* delayed? */);
634 }
635}
636
637void UIChooserAbstractModel::insertCloudEntityKey(const UICloudEntityKey &key)
638{
639// printf("Cloud entity with key %s being updated..\n", key.toString().toUtf8().constData());
640 m_cloudEntityKeysBeingUpdated.insert(key);
641 emit sigCloudUpdateStateChanged();
642}
643
644void UIChooserAbstractModel::removeCloudEntityKey(const UICloudEntityKey &key)
645{
646// printf("Cloud entity with key %s is updated!\n", key.toString().toUtf8().constData());
647 m_cloudEntityKeysBeingUpdated.remove(key);
648 emit sigCloudUpdateStateChanged();
649}
650
651bool UIChooserAbstractModel::containsCloudEntityKey(const UICloudEntityKey &key) const
652{
653 return m_cloudEntityKeysBeingUpdated.contains(key);
654}
655
656bool UIChooserAbstractModel::isCloudProfileUpdateInProgress() const
657{
658 /* Compose RE for profile: */
659 const QRegularExpression re("^/[^/]+/[^/]+$");
660 /* Check whether keys match profile RE: */
661 foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
662 {
663 const QRegularExpressionMatch mt = re.match(key.toString());
664 if (mt.hasMatch())
665 return true;
666 }
667 /* False by default: */
668 return false;
669}
670
671QList<UIVirtualMachineItemCloud*> UIChooserAbstractModel::cloudMachineItems() const
672{
673 QList<UIVirtualMachineItemCloud*> items;
674 foreach (UIChooserNode *pNode, enumerateCloudMachineNodes())
675 {
676 AssertReturn(pNode && pNode->type() == UIChooserNodeType_Machine, items);
677 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
678 AssertPtrReturn(pMachineNode, items);
679 if (pMachineNode->cacheType() != UIVirtualMachineItemType_CloudReal)
680 continue;
681 UIVirtualMachineItemCloud *pCloudMachineItem = pMachineNode->cache()->toCloud();
682 AssertPtrReturn(pCloudMachineItem, items);
683 items << pCloudMachineItem;
684 }
685 return items;
686}
687
688void UIChooserAbstractModel::sltHandleCloudMachineRefreshStarted()
689{
690 /* Acquire sender: */
691 UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
692 AssertPtrReturnVoid(pCache);
693
694 /* Acquire sender's ID: */
695 const QUuid uId = pCache->id();
696
697 /* Search for a first machine node with passed ID: */
698 UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
699
700 /* Insert cloud machine key into a list of keys currently being updated: */
701 const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
702 pMachineNode->parentNode()->name(),
703 pMachineNode->toMachineNode()->id());
704 insertCloudEntityKey(guiCloudMachineKey);
705}
706
707void UIChooserAbstractModel::sltHandleCloudMachineRefreshFinished()
708{
709 /* Acquire sender: */
710 UIVirtualMachineItem *pCache = qobject_cast<UIVirtualMachineItem*>(sender());
711 AssertPtrReturnVoid(pCache);
712
713 /* Acquire sender's ID: */
714 const QUuid uId = pCache->id();
715
716 /* Search for a first machine node with passed ID: */
717 UIChooserNode *pMachineNode = searchMachineNode(invisibleRoot(), uId);
718
719 /* Remove cloud machine key from the list of keys currently being updated: */
720 const UICloudEntityKey guiCloudMachineKey = UICloudEntityKey(pMachineNode->parentNode()->parentNode()->name(),
721 pMachineNode->parentNode()->name(),
722 pMachineNode->toMachineNode()->id());
723 removeCloudEntityKey(guiCloudMachineKey);
724
725 /* Notify listeners: */
726 emit sigCloudMachineStateChange(uId);
727}
728
729void UIChooserAbstractModel::sltGroupSettingsSaveComplete()
730{
731 makeSureGroupSettingsSaveIsFinished();
732 emit sigGroupSavingStateChanged();
733}
734
735void UIChooserAbstractModel::sltGroupDefinitionsSaveComplete()
736{
737 makeSureGroupDefinitionsSaveIsFinished();
738 emit sigGroupSavingStateChanged();
739}
740
741void UIChooserAbstractModel::sltLocalMachineStateChanged(const QUuid &uMachineId, const KMachineState)
742{
743 /* Update machine-nodes with passed id: */
744 invisibleRoot()->updateAllNodes(uMachineId);
745}
746
747void UIChooserAbstractModel::sltLocalMachineDataChanged(const QUuid &uMachineId)
748{
749 /* Update machine-nodes with passed id: */
750 invisibleRoot()->updateAllNodes(uMachineId);
751}
752
753void UIChooserAbstractModel::sltLocalMachineRegistrationChanged(const QUuid &uMachineId, const bool fRegistered)
754{
755 /* Existing VM unregistered? */
756 if (!fRegistered)
757 {
758 /* Remove machine-items with passed id: */
759 invisibleRoot()->removeAllNodes(uMachineId);
760 /* Wipe out empty groups: */
761 wipeOutEmptyGroups();
762 }
763 /* New VM registered? */
764 else
765 {
766 /* Should we show this VM? */
767 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
768 {
769 /* Add new machine-item: */
770 const CMachine comMachine = gpGlobalSession->virtualBox().FindMachine(uMachineId.toString());
771 if (comMachine.isNotNull())
772 addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
773 }
774 }
775}
776
777void UIChooserAbstractModel::sltLocalMachineGroupsChanged(const QUuid &uMachineId)
778{
779 /* Skip VM if restricted: */
780 if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
781 return;
782
783 /* Search for cached group list: */
784 const QStringList oldGroupList = m_groups.value(toOldStyleUuid(uMachineId));
785 //printf("Old groups for VM with ID=%s: %s\n",
786 // uMachineId.toString().toUtf8().constData(),
787 // oldGroupList.join(", ").toUtf8().constData());
788
789 /* Search for existing registered machine: */
790 const CMachine comMachine = gpGlobalSession->virtualBox().FindMachine(uMachineId.toString());
791 if (comMachine.isNull())
792 return;
793 /* Look for a new group list: */
794 const QStringList newGroupList = comMachine.GetGroups().toList();
795 //printf("New groups for VM with ID=%s: %s\n",
796 // uMachineId.toString().toUtf8().constData(),
797 // newGroupList.join(", ").toUtf8().constData());
798
799 /* Re-register VM if required: */
800 QSet<QString> newGroupSet(newGroupList.begin(), newGroupList.end());
801 QSet<QString> oldGroupSet(oldGroupList.begin(), oldGroupList.end());
802 if (newGroupSet != oldGroupSet)
803 {
804 sltLocalMachineRegistrationChanged(uMachineId, false);
805 sltLocalMachineRegistrationChanged(uMachineId, true);
806 }
807}
808
809void UIChooserAbstractModel::sltSessionStateChanged(const QUuid &uMachineId, const KSessionState)
810{
811 /* Update machine-nodes with passed id: */
812 invisibleRoot()->updateAllNodes(uMachineId);
813}
814
815void UIChooserAbstractModel::sltSnapshotChanged(const QUuid &uMachineId, const QUuid &)
816{
817 /* Update machine-nodes with passed id: */
818 invisibleRoot()->updateAllNodes(uMachineId);
819}
820
821void UIChooserAbstractModel::sltHandleCloudProviderUninstall(const QUuid &uProviderId)
822{
823 /* First of all, stop all cloud updates: */
824 stopCloudUpdates();
825
826 /* Search and delete corresponding cloud provider node if present: */
827 delete searchProviderNode(uProviderId);
828}
829
830void UIChooserAbstractModel::sltReloadMachine(const QUuid &uMachineId)
831{
832 /* Remove machine-items with passed id: */
833 invisibleRoot()->removeAllNodes(uMachineId);
834 /* Wipe out empty groups: */
835 wipeOutEmptyGroups();
836
837 /* Should we show this VM? */
838 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineId))
839 {
840 /* Add new machine-item: */
841 const CMachine comMachine = gpGlobalSession->virtualBox().FindMachine(uMachineId.toString());
842 addLocalMachineIntoTheTree(comMachine, true /* make it visible */);
843 }
844}
845
846void UIChooserAbstractModel::sltCommitData()
847{
848 /* Finally, stop all cloud updates: */
849 stopCloudUpdates(true /* forced? */);
850}
851
852void UIChooserAbstractModel::sltDetachCOM()
853{
854 /* Delete tree: */
855 delete m_pInvisibleRootNode;
856 m_pInvisibleRootNode = 0;
857}
858
859void UIChooserAbstractModel::sltCloudMachineUnregistered(const QString &strProviderShortName,
860 const QString &strProfileName,
861 const QUuid &uId)
862{
863 /* Search for profile node: */
864 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
865 if (!pProfileNode)
866 return;
867
868 /* Remove machine-item with passed uId: */
869 pProfileNode->removeAllNodes(uId);
870
871 /* If there are no items left => add fake cloud VM node: */
872 if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
873 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
874}
875
876void UIChooserAbstractModel::sltCloudMachinesUnregistered(const QString &strProviderShortName,
877 const QString &strProfileName,
878 const QList<QUuid> &ids)
879{
880 /* Search for profile node: */
881 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
882 if (!pProfileNode)
883 return;
884
885 /* Remove machine-items with passed id: */
886 foreach (const QUuid &uId, ids)
887 pProfileNode->removeAllNodes(uId);
888
889 /* If there are no items left => add fake cloud VM node: */
890 if (pProfileNode->nodes(UIChooserNodeType_Machine).isEmpty())
891 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Done);
892}
893
894void UIChooserAbstractModel::sltCloudMachineRegistered(const QString &strProviderShortName,
895 const QString &strProfileName,
896 const CCloudMachine &comMachine)
897{
898 /* Search for profile node: */
899 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
900 if (!pProfileNode)
901 return;
902
903 /* Compose corresponding group path: */
904 const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
905 /* Make sure there is no VM with such ID already: */
906 QUuid uId;
907 if (!cloudMachineId(comMachine, uId))
908 return;
909 if (checkIfNodeContainChildWithId(pProfileNode, uId))
910 return;
911 /* Add new machine-item: */
912 addCloudMachineIntoTheTree(strGroup, comMachine, true /* make it visible? */);
913
914 /* Delete fake node if present: */
915 delete searchFakeNode(pProfileNode);
916}
917
918void UIChooserAbstractModel::sltCloudMachinesRegistered(const QString &strProviderShortName,
919 const QString &strProfileName,
920 const QVector<CCloudMachine> &machines)
921{
922 /* Search for profile node: */
923 UIChooserNode *pProfileNode = searchProfileNode(strProviderShortName, strProfileName);
924 if (!pProfileNode)
925 return;
926
927 /* Compose corresponding group path: */
928 const QString strGroup = QString("/%1/%2").arg(strProviderShortName, strProfileName);
929 foreach (const CCloudMachine &comMachine, machines)
930 {
931 /* Make sure there is no VM with such ID already: */
932 QUuid uId;
933 if (!cloudMachineId(comMachine, uId))
934 continue;
935 if (checkIfNodeContainChildWithId(pProfileNode, uId))
936 continue;
937 /* Add new machine-item: */
938 addCloudMachineIntoTheTree(strGroup, comMachine, false /* make it visible? */);
939 }
940
941 /* Delete fake node if present: */
942 delete searchFakeNode(pProfileNode);
943}
944
945void UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete()
946{
947 /* Parse task result: */
948 UIProgressTaskReadCloudMachineList *pSender = qobject_cast<UIProgressTaskReadCloudMachineList*>(sender());
949 AssertPtrReturnVoid(pSender);
950 const UICloudEntityKey guiCloudProfileKey = pSender->cloudProfileKey();
951 const QVector<CCloudMachine> machines = pSender->machines();
952 const QString strErrorMessage = pSender->errorMessage();
953
954 /* Delete task: */
955 delete pSender;
956
957 /* Check whether this task was expected: */
958 if (!containsCloudEntityKey(guiCloudProfileKey))
959 return;
960
961 /* Search for provider node separately, it can be removed already: */
962 UIChooserNode *pProviderNode = searchProviderNode(guiCloudProfileKey.m_strProviderShortName);
963 if (pProviderNode)
964 {
965 /* Search for profile node separately, it can be hidden at all: */
966 UIChooserNode *pProfileNode = searchProfileNode(pProviderNode, guiCloudProfileKey.m_strProfileName);
967 if (pProfileNode)
968 {
969 /* Compose old set of machine IDs: */
970 QSet<QUuid> oldIDs;
971 foreach (UIChooserNode *pNode, pProfileNode->nodes(UIChooserNodeType_Machine))
972 {
973 AssertPtrReturnVoid(pNode);
974 UIChooserNodeMachine *pNodeMachine = pNode->toMachineNode();
975 AssertPtrReturnVoid(pNodeMachine);
976 if (pNodeMachine->cacheType() != UIVirtualMachineItemType_CloudReal)
977 continue;
978 oldIDs << pNodeMachine->id();
979 }
980 /* Compose new set of machine IDs and map of machines: */
981 QSet<QUuid> newIDs;
982 QMap<QUuid, CCloudMachine> newMachines;
983 foreach (const CCloudMachine &comMachine, machines)
984 {
985 QUuid uId;
986 AssertReturnVoid(cloudMachineId(comMachine, uId));
987 newMachines[uId] = comMachine;
988 newIDs << uId;
989 }
990
991 /* Calculate set of unregistered/registered IDs: */
992 const QSet<QUuid> unregisteredIDs = oldIDs - newIDs;
993 const QSet<QUuid> registeredIDs = newIDs - oldIDs;
994 QVector<CCloudMachine> registeredMachines;
995 foreach (const QUuid &uId, registeredIDs)
996 registeredMachines << newMachines.value(uId);
997
998 /* Remove unregistered cloud VM nodes: */
999 if (!unregisteredIDs.isEmpty())
1000 {
1001#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
1002 QList<QUuid> listUnregisteredIDs(unregisteredIDs.begin(), unregisteredIDs.end());
1003#else
1004 QList<QUuid> listUnregisteredIDs = unregisteredIDs.toList();
1005#endif
1006 sltCloudMachinesUnregistered(guiCloudProfileKey.m_strProviderShortName,
1007 guiCloudProfileKey.m_strProfileName,
1008 listUnregisteredIDs);
1009 }
1010 /* Add registered cloud VM nodes: */
1011 if (!registeredMachines.isEmpty())
1012 sltCloudMachinesRegistered(guiCloudProfileKey.m_strProviderShortName,
1013 guiCloudProfileKey.m_strProfileName,
1014 registeredMachines);
1015 /* If we changed nothing and have nothing currently: */
1016 if (unregisteredIDs.isEmpty() && newIDs.isEmpty())
1017 {
1018 /* We should update at least fake cloud machine node: */
1019 UIChooserNode *pFakeNode = searchFakeNode(pProfileNode);
1020 AssertPtrReturnVoid(pFakeNode);
1021 UIVirtualMachineItemCloud *pFakeMachineItem = pFakeNode->toMachineNode()->cache()->toCloud();
1022 AssertPtrReturnVoid(pFakeMachineItem);
1023 pFakeMachineItem->setFakeCloudItemState(UIFakeCloudVirtualMachineItemState_Done);
1024 pFakeMachineItem->setFakeCloudItemErrorMessage(strErrorMessage);
1025 if (pFakeNode->item())
1026 pFakeNode->item()->updateItem();
1027 }
1028 }
1029 }
1030
1031 /* Remove cloud entity key from the list of keys currently being updated: */
1032 removeCloudEntityKey(guiCloudProfileKey);
1033
1034 /* Notify listeners: */
1035 emit sigCloudProfileStateChange(guiCloudProfileKey.m_strProviderShortName,
1036 guiCloudProfileKey.m_strProfileName);
1037}
1038
1039void UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange()
1040{
1041 /* Reload cloud tree: */
1042 reloadCloudTree();
1043}
1044
1045void UIChooserAbstractModel::createReadCloudMachineListTask(const UICloudEntityKey &guiCloudProfileKey, bool fWithRefresh)
1046{
1047 /* Do not create task if already registered: */
1048 if (containsCloudEntityKey(guiCloudProfileKey))
1049 return;
1050
1051 /* Create task: */
1052 UIProgressTaskReadCloudMachineList *pTask = new UIProgressTaskReadCloudMachineList(this,
1053 guiCloudProfileKey,
1054 fWithRefresh);
1055 if (pTask)
1056 {
1057 /* It's easy to find child by name later: */
1058 pTask->setObjectName(guiCloudProfileKey.toString());
1059
1060 /* Insert cloud profile key into a list of keys currently being updated: */
1061 insertCloudEntityKey(guiCloudProfileKey);
1062
1063 /* Connect and start it finally: */
1064 connect(pTask, &UIProgressTaskReadCloudMachineList::sigProgressFinished,
1065 this, &UIChooserAbstractModel::sltHandleReadCloudMachineListTaskComplete);
1066 pTask->start();
1067 }
1068}
1069
1070void UIChooserAbstractModel::sltSaveSettings()
1071{
1072 saveGroupSettings();
1073 saveGroupDefinitions();
1074}
1075
1076void UIChooserAbstractModel::prepare()
1077{
1078 prepareConnections();
1079}
1080
1081void UIChooserAbstractModel::prepareConnections()
1082{
1083 /* UICommon connections: */
1084 connect(&uiCommon(), &UICommon::sigAskToCommitData,
1085 this, &UIChooserAbstractModel::sltCommitData);
1086 connect(&uiCommon(), &UICommon::sigAskToDetachCOM,
1087 this, &UIChooserAbstractModel::sltDetachCOM);
1088 connect(&uiCommon(), &UICommon::sigCloudMachineUnregistered,
1089 this, &UIChooserAbstractModel::sltCloudMachineUnregistered);
1090 connect(&uiCommon(), &UICommon::sigCloudMachineRegistered,
1091 this, &UIChooserAbstractModel::sltCloudMachineRegistered);
1092
1093 /* Global connections: */
1094 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange,
1095 this, &UIChooserAbstractModel::sltLocalMachineStateChanged);
1096 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineDataChange,
1097 this, &UIChooserAbstractModel::sltLocalMachineDataChanged);
1098 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineRegistered,
1099 this, &UIChooserAbstractModel::sltLocalMachineRegistrationChanged);
1100 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineGroupsChange,
1101 this, &UIChooserAbstractModel::sltLocalMachineGroupsChanged);
1102 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSessionStateChange,
1103 this, &UIChooserAbstractModel::sltSessionStateChanged);
1104 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotTake,
1105 this, &UIChooserAbstractModel::sltSnapshotChanged);
1106 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotDelete,
1107 this, &UIChooserAbstractModel::sltSnapshotChanged);
1108 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotChange,
1109 this, &UIChooserAbstractModel::sltSnapshotChanged);
1110 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigSnapshotRestore,
1111 this, &UIChooserAbstractModel::sltSnapshotChanged);
1112 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderListChanged,
1113 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1114 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileRegistered,
1115 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1116 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProfileChanged,
1117 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1118 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigCloudProviderUninstall,
1119 this, &UIChooserAbstractModel::sltHandleCloudProviderUninstall);
1120
1121 /* Settings saving connections: */
1122 connect(this, &UIChooserAbstractModel::sigSaveSettings,
1123 this, &UIChooserAbstractModel::sltSaveSettings,
1124 Qt::QueuedConnection);
1125
1126 /* Extra-data connections: */
1127 connect(gEDataManager, &UIExtraDataManager::sigCloudProfileManagerRestrictionChange,
1128 this, &UIChooserAbstractModel::sltHandleCloudProfileManagerCumulativeChange);
1129}
1130
1131void UIChooserAbstractModel::cleanupConnections()
1132{
1133 /* Group saving connections: */
1134 disconnect(this, &UIChooserAbstractModel::sigSaveSettings,
1135 this, &UIChooserAbstractModel::sltSaveSettings);
1136}
1137
1138void UIChooserAbstractModel::cleanup()
1139{
1140 cleanupConnections();
1141}
1142
1143void UIChooserAbstractModel::reloadLocalTree()
1144{
1145 LogRelFlow(("UIChooserAbstractModel: Loading local VMs...\n"));
1146
1147 /* Acquire VBox: */
1148 const CVirtualBox comVBox = gpGlobalSession->virtualBox();
1149
1150 /* Acquire existing local machines: */
1151 const QVector<CMachine> machines = comVBox.GetMachines();
1152 /* Show error message if necessary: */
1153 if (!comVBox.isOk())
1154 UINotificationMessage::cannotAcquireVirtualBoxParameter(comVBox);
1155 else
1156 {
1157 /* Iterate through existing machines: */
1158 foreach (const CMachine &comMachine, machines)
1159 {
1160 /* Skip if we have nothing to populate (wtf happened?): */
1161 if (comMachine.isNull())
1162 continue;
1163
1164 /* Get machine ID: */
1165 const QUuid uMachineID = comMachine.GetId();
1166 /* Show error message if necessary: */
1167 if (!comMachine.isOk())
1168 {
1169 UINotificationMessage::cannotAcquireMachineParameter(comMachine);
1170 continue;
1171 }
1172
1173 /* Skip if we have nothing to show (wtf happened?): */
1174 if (uMachineID.isNull())
1175 continue;
1176
1177 /* Skip if machine is restricted from being shown: */
1178 if (!gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineID))
1179 continue;
1180
1181 /* Add machine into tree: */
1182 addLocalMachineIntoTheTree(comMachine);
1183 }
1184 }
1185
1186 LogRelFlow(("UIChooserAbstractModel: Local VMs loaded.\n"));
1187}
1188
1189void UIChooserAbstractModel::reloadCloudTree()
1190{
1191 LogRelFlow(("UIChooserAbstractModel: Loading cloud providers/profiles...\n"));
1192
1193 /* Wipe out existing cloud providers first.
1194 * This is quite rude, in future we need to reimplement it more wise.. */
1195 foreach (UIChooserNode *pNode, invisibleRoot()->nodes(UIChooserNodeType_Group))
1196 {
1197 AssertPtrReturnVoid(pNode);
1198 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1199 AssertPtrReturnVoid(pGroupNode);
1200 if (pGroupNode->groupType() == UIChooserNodeGroupType_Provider)
1201 delete pNode;
1202 }
1203
1204 /* Acquire Cloud Profile Manager restrictions: */
1205 const QStringList restrictions = gEDataManager->cloudProfileManagerRestrictions();
1206
1207 /* Iterate through existing providers: */
1208 foreach (CCloudProvider comCloudProvider, listCloudProviders())
1209 {
1210 /* Skip if we have nothing to populate: */
1211 if (comCloudProvider.isNull())
1212 continue;
1213
1214 /* Acquire provider id: */
1215 QUuid uProviderId;
1216 if (!cloudProviderId(comCloudProvider, uProviderId))
1217 continue;
1218
1219 /* Acquire provider short name: */
1220 QString strProviderShortName;
1221 if (!cloudProviderShortName(comCloudProvider, strProviderShortName))
1222 continue;
1223
1224 /* Make sure this provider isn't restricted: */
1225 const QString strProviderPath = QString("/%1").arg(strProviderShortName);
1226 if (restrictions.contains(strProviderPath))
1227 continue;
1228
1229 /* Acquire list of profiles: */
1230 const QVector<CCloudProfile> profiles = listCloudProfiles(comCloudProvider);
1231 if (profiles.isEmpty())
1232 continue;
1233
1234 /* Add provider group node: */
1235 UIChooserNodeGroup *pProviderNode =
1236 new UIChooserNodeGroup(invisibleRoot() /* parent */,
1237 getDesiredNodePosition(invisibleRoot(),
1238 UIChooserNodeDataPrefixType_Provider,
1239 strProviderShortName),
1240 uProviderId,
1241 strProviderShortName,
1242 UIChooserNodeGroupType_Provider,
1243 shouldGroupNodeBeOpened(invisibleRoot(),
1244 UIChooserNodeDataPrefixType_Provider,
1245 strProviderShortName));
1246
1247 /* Iterate through provider's profiles: */
1248 foreach (CCloudProfile comCloudProfile, profiles)
1249 {
1250 /* Skip if we have nothing to populate: */
1251 if (comCloudProfile.isNull())
1252 continue;
1253
1254 /* Acquire profile name: */
1255 QString strProfileName;
1256 if (!cloudProfileName(comCloudProfile, strProfileName))
1257 continue;
1258
1259 /* Make sure this profile isn't restricted: */
1260 const QString strProfilePath = QString("/%1/%2").arg(strProviderShortName, strProfileName);
1261 if (restrictions.contains(strProfilePath))
1262 continue;
1263
1264 /* Add profile sub-group node: */
1265 UIChooserNodeGroup *pProfileNode =
1266 new UIChooserNodeGroup(pProviderNode /* parent */,
1267 getDesiredNodePosition(pProviderNode,
1268 UIChooserNodeDataPrefixType_Profile,
1269 strProfileName),
1270 QUuid() /* id */,
1271 strProfileName,
1272 UIChooserNodeGroupType_Profile,
1273 shouldGroupNodeBeOpened(pProviderNode,
1274 UIChooserNodeDataPrefixType_Profile,
1275 strProfileName));
1276
1277 /* Add fake cloud VM item: */
1278 createCloudMachineNode(pProfileNode, UIFakeCloudVirtualMachineItemState_Loading);
1279
1280 /* Create read cloud machine list task: */
1281 const UICloudEntityKey guiCloudProfileKey = UICloudEntityKey(strProviderShortName, strProfileName);
1282 createReadCloudMachineListTask(guiCloudProfileKey, true /* with refresh? */);
1283 }
1284 }
1285
1286 LogRelFlow(("UIChooserAbstractModel: Cloud providers/profiles loaded.\n"));
1287}
1288
1289void UIChooserAbstractModel::addLocalMachineIntoTheTree(const CMachine &comMachine,
1290 bool fMakeItVisible /* = false */)
1291{
1292 /* Make sure passed VM is not NULL: */
1293 if (comMachine.isNull())
1294 LogRelFlow(("UIChooserModel: ERROR: Passed local VM is NULL!\n"));
1295 AssertReturnVoid(!comMachine.isNull());
1296
1297 /* Which VM we are loading: */
1298 const QUuid uId = comMachine.GetId();
1299 LogRelFlow(("UIChooserModel: Loading local VM with ID={%s}...\n",
1300 toOldStyleUuid(uId).toUtf8().constData()));
1301
1302 /* Is that machine accessible? */
1303 if (comMachine.GetAccessible())
1304 {
1305 /* Acquire VM name: */
1306 const QString strName = comMachine.GetName();
1307 LogRelFlow(("UIChooserModel: Local VM {%s} is accessible.\n", strName.toUtf8().constData()));
1308 /* Which groups passed machine attached to? */
1309 const QVector<QString> groups = comMachine.GetGroups();
1310 const QStringList groupList = groups.toList();
1311 const QString strGroups = groupList.join(", ");
1312 LogRelFlow(("UIChooserModel: Local VM {%s} has groups: {%s}.\n",
1313 strName.toUtf8().constData(), strGroups.toUtf8().constData()));
1314 foreach (QString strGroup, groups)
1315 {
1316 /* Remove last '/' if any: */
1317 if (strGroup.right(1) == "/")
1318 strGroup.truncate(strGroup.size() - 1);
1319 /* Create machine-item with found group-item as parent: */
1320 LogRelFlow(("UIChooserModel: Creating node for local VM {%s} in group {%s}.\n",
1321 strName.toUtf8().constData(), strGroup.toUtf8().constData()));
1322 createLocalMachineNode(getLocalGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
1323 }
1324 /* Update group settings: */
1325 m_groups[toOldStyleUuid(uId)] = groupList;
1326 }
1327 /* Inaccessible machine: */
1328 else
1329 {
1330 /* VM is accessible: */
1331 LogRelFlow(("UIChooserModel: Local VM {%s} is inaccessible.\n",
1332 toOldStyleUuid(uId).toUtf8().constData()));
1333 /* Create machine-item with main-root group-item as parent: */
1334 createLocalMachineNode(invisibleRoot(), comMachine);
1335 }
1336}
1337
1338void UIChooserAbstractModel::addCloudMachineIntoTheTree(const QString &strGroup,
1339 const CCloudMachine &comMachine,
1340 bool fMakeItVisible /* = false */)
1341{
1342 /* Make sure passed VM is not NULL: */
1343 if (comMachine.isNull())
1344 LogRelFlow(("UIChooserModel: ERROR: Passed cloud VM is NULL!\n"));
1345 AssertReturnVoid(!comMachine.isNull());
1346
1347 /* Which VM we are loading: */
1348 const QUuid uId = comMachine.GetId();
1349 LogRelFlow(("UIChooserModel: Loading cloud VM with ID={%s}...\n",
1350 toOldStyleUuid(uId).toUtf8().constData()));
1351
1352 /* Acquire VM name: */
1353 QString strName = comMachine.GetName();
1354 if (strName.isEmpty())
1355 strName = uId.toString();
1356 LogRelFlow(("UIChooserModel: Creating node for cloud VM {%s} in group {%s}.\n",
1357 strName.toUtf8().constData(), strGroup.toUtf8().constData()));
1358 /* Create machine-item with found group-item as parent: */
1359 createCloudMachineNode(getCloudGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);
1360 /* Update group settings: */
1361 const QStringList groupList(strGroup);
1362 m_groups[toOldStyleUuid(uId)] = groupList;
1363}
1364
1365UIChooserNode *UIChooserAbstractModel::getLocalGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
1366{
1367 /* Check passed stuff: */
1368 if (pParentNode->name() == strName)
1369 return pParentNode;
1370
1371 /* Prepare variables: */
1372 const QString strFirstSubName = strName.section('/', 0, 0);
1373 const QString strFirstSuffix = strName.section('/', 1, -1);
1374 const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
1375 const QString strSecondSuffix = strFirstSuffix.section('/', 1, -1);
1376
1377 /* Passed group name equal to first sub-name: */
1378 if (pParentNode->name() == strFirstSubName)
1379 {
1380 /* Make sure first-suffix is NOT empty: */
1381 AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
1382 /* Trying to get group node among our children: */
1383 foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
1384 {
1385 AssertPtrReturn(pNode, 0);
1386 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1387 AssertPtrReturn(pGroupNode, 0);
1388 if ( pGroupNode->groupType() == UIChooserNodeGroupType_Local
1389 && pNode->name() == strSecondSubName)
1390 {
1391 UIChooserNode *pFoundNode = getLocalGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
1392 if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
1393 if (fAllGroupsOpened && pFoundGroupNode->isClosed())
1394 pFoundGroupNode->open();
1395 return pFoundNode;
1396 }
1397 }
1398 }
1399
1400 /* Found nothing? Creating: */
1401 UIChooserNodeGroup *pNewGroupNode =
1402 new UIChooserNodeGroup(pParentNode,
1403 getDesiredNodePosition(pParentNode,
1404 UIChooserNodeDataPrefixType_Local,
1405 strSecondSubName),
1406 QUuid() /* id */,
1407 strSecondSubName,
1408 UIChooserNodeGroupType_Local,
1409 fAllGroupsOpened || shouldGroupNodeBeOpened(pParentNode,
1410 UIChooserNodeDataPrefixType_Local,
1411 strSecondSubName));
1412 return strSecondSuffix.isEmpty() ? pNewGroupNode : getLocalGroupNode(strFirstSuffix, pNewGroupNode, fAllGroupsOpened);
1413}
1414
1415UIChooserNode *UIChooserAbstractModel::getCloudGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)
1416{
1417 /* Check passed stuff: */
1418 if (pParentNode->name() == strName)
1419 return pParentNode;
1420
1421 /* Prepare variables: */
1422 const QString strFirstSubName = strName.section('/', 0, 0);
1423 const QString strFirstSuffix = strName.section('/', 1, -1);
1424 const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);
1425
1426 /* Passed group name equal to first sub-name: */
1427 if (pParentNode->name() == strFirstSubName)
1428 {
1429 /* Make sure first-suffix is NOT empty: */
1430 AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));
1431 /* Trying to get group node among our children: */
1432 foreach (UIChooserNode *pNode, pParentNode->nodes(UIChooserNodeType_Group))
1433 {
1434 AssertPtrReturn(pNode, 0);
1435 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1436 AssertPtrReturn(pGroupNode, 0);
1437 if ( ( pGroupNode->groupType() == UIChooserNodeGroupType_Provider
1438 || pGroupNode->groupType() == UIChooserNodeGroupType_Profile)
1439 && pNode->name() == strSecondSubName)
1440 {
1441 UIChooserNode *pFoundNode = getCloudGroupNode(strFirstSuffix, pNode, fAllGroupsOpened);
1442 if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())
1443 if (fAllGroupsOpened && pFoundGroupNode->isClosed())
1444 pFoundGroupNode->open();
1445 return pFoundNode;
1446 }
1447 }
1448 }
1449
1450 /* Found nothing? Returning parent: */
1451 AssertFailedReturn(pParentNode);
1452}
1453
1454bool UIChooserAbstractModel::shouldGroupNodeBeOpened(UIChooserNode *pParentNode,
1455 UIChooserNodeDataPrefixType enmDataType,
1456 const QString &strName) const
1457{
1458 /* Read group definitions: */
1459 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1460 /* Return 'false' if no definitions found: */
1461 if (definitions.isEmpty())
1462 return false;
1463
1464 /* Prepare required group definition reg-exp: */
1465 const QString strNodePrefix = prefixToString(enmDataType);
1466 const QString strNodeOptionOpened = optionToString(UIChooserNodeDataOptionType_GroupOpened);
1467 const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strName);
1468 const QRegularExpression re(strDefinitionTemplate);
1469 /* For each the group definition: */
1470 foreach (const QString &strDefinition, definitions)
1471 {
1472 /* Check if this is required definition: */
1473 const QRegularExpressionMatch mt = re.match(strDefinition);
1474 if (mt.capturedStart() == 0)
1475 {
1476 /* Get group descriptor: */
1477 const QString strDescriptor = mt.captured(1);
1478 if (strDescriptor.contains(strNodeOptionOpened))
1479 return true;
1480 }
1481 }
1482
1483 /* Return 'false' by default: */
1484 return false;
1485}
1486
1487bool UIChooserAbstractModel::shouldGlobalNodeBeFavorite(UIChooserNode *pParentNode) const
1488{
1489 /* Read group definitions: */
1490 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1491 /* Return 'false' if no definitions found: */
1492 if (definitions.isEmpty())
1493 return false;
1494
1495 /* Prepare required group definition reg-exp: */
1496 const QString strNodePrefix = prefixToString(UIChooserNodeDataPrefixType_Global);
1497 const QString strNodeOptionFavorite = optionToString(UIChooserNodeDataOptionType_GlobalFavorite);
1498 const QString strNodeValueDefault = valueToString(UIChooserNodeDataValueType_GlobalDefault);
1499 const QString strDefinitionTemplate = QString("%1(\\S)*=%2").arg(strNodePrefix, strNodeValueDefault);
1500 const QRegularExpression re(strDefinitionTemplate);
1501 /* For each the group definition: */
1502 foreach (const QString &strDefinition, definitions)
1503 {
1504 /* Check if this is required definition: */
1505 const QRegularExpressionMatch mt = re.match(strDefinition);
1506 if (mt.capturedStart() == 0)
1507 {
1508 /* Get group descriptor: */
1509 const QString strDescriptor = mt.captured(1);
1510 if (strDescriptor.contains(strNodeOptionFavorite))
1511 return true;
1512 }
1513 }
1514
1515 /* Return 'false' by default: */
1516 return false;
1517}
1518
1519void UIChooserAbstractModel::wipeOutEmptyGroupsStartingFrom(UIChooserNode *pParent)
1520{
1521 /* Cleanup all the group children recursively first: */
1522 foreach (UIChooserNode *pNode, pParent->nodes(UIChooserNodeType_Group))
1523 wipeOutEmptyGroupsStartingFrom(pNode);
1524 /* If parent isn't root and has no nodes: */
1525 if (!pParent->isRoot() && !pParent->hasNodes())
1526 {
1527 /* Delete parent node and item: */
1528 delete pParent;
1529 }
1530}
1531
1532int UIChooserAbstractModel::getDesiredNodePosition(UIChooserNode *pParentNode,
1533 UIChooserNodeDataPrefixType enmDataType,
1534 const QString &strName)
1535{
1536 /* End of list (by default)? */
1537 int iNewNodeDesiredPosition = -1;
1538 /* Which position should be new node placed by definitions: */
1539 const int iNewNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmDataType, strName);
1540
1541 /* If some position defined: */
1542 if (iNewNodeDefinitionPosition != -1)
1543 {
1544 /* Start of list if some definition present: */
1545 iNewNodeDesiredPosition = 0;
1546 /* We have to check all the existing node positions: */
1547 UIChooserNodeType enmType = UIChooserNodeType_Any;
1548 switch (enmDataType)
1549 {
1550 case UIChooserNodeDataPrefixType_Global: enmType = UIChooserNodeType_Global; break;
1551 case UIChooserNodeDataPrefixType_Machine: enmType = UIChooserNodeType_Machine; break;
1552 case UIChooserNodeDataPrefixType_Local:
1553 case UIChooserNodeDataPrefixType_Provider:
1554 case UIChooserNodeDataPrefixType_Profile: enmType = UIChooserNodeType_Group; break;
1555 }
1556 const QList<UIChooserNode*> nodes = pParentNode->nodes(enmType);
1557 for (int i = nodes.size() - 1; i >= 0; --i)
1558 {
1559 /* Get current node: */
1560 UIChooserNode *pNode = nodes.at(i);
1561 AssertPtrReturn(pNode, iNewNodeDesiredPosition);
1562 /* Which position should be current node placed by definitions? */
1563 UIChooserNodeDataPrefixType enmNodeDataType = UIChooserNodeDataPrefixType_Global;
1564 QString strDefinitionName;
1565 switch (pNode->type())
1566 {
1567 case UIChooserNodeType_Machine:
1568 {
1569 enmNodeDataType = UIChooserNodeDataPrefixType_Machine;
1570 strDefinitionName = toOldStyleUuid(pNode->toMachineNode()->id());
1571 break;
1572 }
1573 case UIChooserNodeType_Group:
1574 {
1575 /* Cast to group node: */
1576 UIChooserNodeGroup *pGroupNode = pNode->toGroupNode();
1577 AssertPtrReturn(pGroupNode, iNewNodeDesiredPosition);
1578 switch (pGroupNode->groupType())
1579 {
1580 case UIChooserNodeGroupType_Local: enmNodeDataType = UIChooserNodeDataPrefixType_Local; break;
1581 case UIChooserNodeGroupType_Provider: enmNodeDataType = UIChooserNodeDataPrefixType_Provider; break;
1582 case UIChooserNodeGroupType_Profile: enmNodeDataType = UIChooserNodeDataPrefixType_Profile; break;
1583 default: break;
1584 }
1585 strDefinitionName = pNode->name();
1586 break;
1587 }
1588 default:
1589 break;
1590 }
1591 /* If some position defined: */
1592 const int iNodeDefinitionPosition = getDefinedNodePosition(pParentNode, enmNodeDataType, strDefinitionName);
1593 if (iNodeDefinitionPosition != -1)
1594 {
1595 AssertReturn(iNodeDefinitionPosition != iNewNodeDefinitionPosition, iNewNodeDesiredPosition);
1596 if (iNodeDefinitionPosition < iNewNodeDefinitionPosition)
1597 {
1598 iNewNodeDesiredPosition = i + 1;
1599 break;
1600 }
1601 }
1602 }
1603 }
1604
1605 /* Return desired node position: */
1606 return iNewNodeDesiredPosition;
1607}
1608
1609int UIChooserAbstractModel::getDefinedNodePosition(UIChooserNode *pParentNode, UIChooserNodeDataPrefixType enmDataType, const QString &strName)
1610{
1611 /* Read group definitions: */
1612 const QStringList definitions = gEDataManager->machineGroupDefinitions(pParentNode->fullName());
1613 /* Return 'false' if no definitions found: */
1614 if (definitions.isEmpty())
1615 return -1;
1616
1617 /* Prepare definition reg-exp: */
1618 QString strDefinitionTemplateShort;
1619 QString strDefinitionTemplateFull;
1620 const QString strNodePrefixLocal = prefixToString(UIChooserNodeDataPrefixType_Local);
1621 const QString strNodePrefixProvider = prefixToString(UIChooserNodeDataPrefixType_Provider);
1622 const QString strNodePrefixProfile = prefixToString(UIChooserNodeDataPrefixType_Profile);
1623 const QString strNodePrefixMachine = prefixToString(UIChooserNodeDataPrefixType_Machine);
1624 switch (enmDataType)
1625 {
1626 case UIChooserNodeDataPrefixType_Local:
1627 {
1628 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1629 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixLocal, strName);
1630 break;
1631 }
1632 case UIChooserNodeDataPrefixType_Provider:
1633 {
1634 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1635 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProvider, strName);
1636 break;
1637 }
1638 case UIChooserNodeDataPrefixType_Profile:
1639 {
1640 strDefinitionTemplateShort = QString("^[%1%2%3](\\S)*=").arg(strNodePrefixLocal, strNodePrefixProvider, strNodePrefixProfile);
1641 strDefinitionTemplateFull = QString("^%1(\\S)*=%2$").arg(strNodePrefixProfile, strName);
1642 break;
1643 }
1644 case UIChooserNodeDataPrefixType_Machine:
1645 {
1646 strDefinitionTemplateShort = QString("^%1=").arg(strNodePrefixMachine);
1647 strDefinitionTemplateFull = QString("^%1=%2$").arg(strNodePrefixMachine, strName);
1648 break;
1649 }
1650 default:
1651 return -1;
1652 }
1653 const QRegularExpression definitionRegExpShort(strDefinitionTemplateShort);
1654 const QRegularExpression definitionRegExpFull(strDefinitionTemplateFull);
1655
1656 /* For each the definition: */
1657 int iDefinitionIndex = -1;
1658 foreach (const QString &strDefinition, definitions)
1659 {
1660 /* Check if this definition is of required type: */
1661 const QRegularExpressionMatch mtShort = definitionRegExpShort.match(strDefinition);
1662 if (mtShort.capturedStart() == 0)
1663 {
1664 ++iDefinitionIndex;
1665 /* Check if this definition is exactly what we need: */
1666 const QRegularExpressionMatch mtFull = definitionRegExpFull.match(strDefinition);
1667 if (mtFull.capturedStart() == 0)
1668 return iDefinitionIndex;
1669 }
1670 }
1671
1672 /* Return result: */
1673 return -1;
1674}
1675
1676void UIChooserAbstractModel::createLocalMachineNode(UIChooserNode *pParentNode, const CMachine &comMachine)
1677{
1678 new UIChooserNodeMachine(pParentNode,
1679 getDesiredNodePosition(pParentNode,
1680 UIChooserNodeDataPrefixType_Machine,
1681 toOldStyleUuid(comMachine.GetId())),
1682 comMachine);
1683}
1684
1685void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, UIFakeCloudVirtualMachineItemState enmState)
1686{
1687 new UIChooserNodeMachine(pParentNode,
1688 0 /* position */,
1689 enmState);
1690}
1691
1692void UIChooserAbstractModel::createCloudMachineNode(UIChooserNode *pParentNode, const CCloudMachine &comMachine)
1693{
1694 UIChooserNodeMachine *pNode = new UIChooserNodeMachine(pParentNode,
1695 getDesiredNodePosition(pParentNode,
1696 UIChooserNodeDataPrefixType_Machine,
1697 toOldStyleUuid(comMachine.GetId())),
1698 comMachine);
1699 /* Request for async node update if necessary: */
1700 if ( m_fKeepCloudNodesUpdated
1701 || !comMachine.GetAccessible())
1702 {
1703 AssertReturnVoid(pNode && pNode->cacheType() == UIVirtualMachineItemType_CloudReal);
1704 UIVirtualMachineItemCloud *pCloudMachineItem = pNode->cache()->toCloud();
1705 pCloudMachineItem->setUpdateRequiredByGlobalReason(m_fKeepCloudNodesUpdated);
1706 pCloudMachineItem->updateInfoAsync(false /* delayed? */);
1707 }
1708}
1709
1710QStringList UIChooserAbstractModel::gatherPossibleGroupNodeNames(UIChooserNode *pCurrentNode, QList<UIChooserNode*> exceptions) const
1711{
1712 /* Prepare result: */
1713 QStringList result;
1714
1715 /* Walk through all the children and make sure there are no exceptions: */
1716 bool fAddCurrent = true;
1717 foreach (UIChooserNode *pChild, pCurrentNode->nodes(UIChooserNodeType_Any))
1718 {
1719 AssertPtrReturn(pChild, result);
1720 if (exceptions.contains(pChild))
1721 fAddCurrent = false;
1722 else
1723 {
1724 if (pChild->type() == UIChooserNodeType_Group)
1725 {
1726 UIChooserNodeGroup *pChildGroup = pChild->toGroupNode();
1727 AssertPtrReturn(pChildGroup, result);
1728 if (pChildGroup->groupType() == UIChooserNodeGroupType_Local)
1729 result << gatherPossibleGroupNodeNames(pChild, exceptions);
1730 }
1731 }
1732 }
1733
1734 /* Add current item if not overridden: */
1735 if (fAddCurrent)
1736 result.prepend(pCurrentNode->fullName());
1737
1738 /* Return result: */
1739 return result;
1740}
1741
1742bool UIChooserAbstractModel::checkIfNodeContainChildWithId(UIChooserNode *pParentNode, const QUuid &uId) const
1743{
1744 /* Check parent-node type: */
1745 AssertPtrReturn(pParentNode, false);
1746 switch (pParentNode->type())
1747 {
1748 case UIChooserNodeType_Machine:
1749 {
1750 /* Check if pParentNode has the passed uId itself: */
1751 UIChooserNodeMachine *pMachineNode = pParentNode->toMachineNode();
1752 AssertPtrReturn(pMachineNode, false);
1753 if (pMachineNode->id() == uId)
1754 return true;
1755 break;
1756 }
1757 case UIChooserNodeType_Group:
1758 {
1759 /* Recursively iterate through children: */
1760 foreach (UIChooserNode *pChildNode, pParentNode->nodes())
1761 if (checkIfNodeContainChildWithId(pChildNode, uId))
1762 return true;
1763 break;
1764 }
1765 default:
1766 break;
1767 }
1768
1769 /* False by default: */
1770 return false;
1771}
1772
1773void UIChooserAbstractModel::saveGroupSettings()
1774{
1775 /* Make sure there is no group settings saving activity: */
1776 if (UIThreadGroupSettingsSave::instance())
1777 return;
1778
1779 /* Prepare full group map: */
1780 QMap<QString, QStringList> groups;
1781 gatherGroupSettings(groups, invisibleRoot());
1782
1783 /* Save information in other thread: */
1784 UIThreadGroupSettingsSave::prepare();
1785 emit sigGroupSavingStateChanged();
1786 connect(UIThreadGroupSettingsSave::instance(), &UIThreadGroupSettingsSave::sigReload,
1787 this, &UIChooserAbstractModel::sltReloadMachine);
1788 UIThreadGroupSettingsSave::instance()->configure(this, m_groups, groups);
1789 UIThreadGroupSettingsSave::instance()->start();
1790 m_groups = groups;
1791}
1792
1793void UIChooserAbstractModel::saveGroupDefinitions()
1794{
1795 /* Make sure there is no group definitions save activity: */
1796 if (UIThreadGroupDefinitionsSave::instance())
1797 return;
1798
1799 /* Prepare full group map: */
1800 QMap<QString, QStringList> groups;
1801 gatherGroupDefinitions(groups, invisibleRoot());
1802
1803 /* Save information in other thread: */
1804 UIThreadGroupDefinitionsSave::prepare();
1805 emit sigGroupSavingStateChanged();
1806 UIThreadGroupDefinitionsSave::instance()->configure(this, groups);
1807 UIThreadGroupDefinitionsSave::instance()->start();
1808}
1809
1810void UIChooserAbstractModel::gatherGroupSettings(QMap<QString, QStringList> &settings,
1811 UIChooserNode *pParentGroup)
1812{
1813 /* Iterate over all the machine-nodes: */
1814 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
1815 {
1816 /* Make sure it's really machine node: */
1817 AssertPtrReturnVoid(pNode);
1818 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
1819 AssertPtrReturnVoid(pMachineNode);
1820 /* Make sure it's local machine node exactly and it's accessible: */
1821 if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
1822 && pMachineNode->accessible())
1823 settings[toOldStyleUuid(pMachineNode->id())] << pParentGroup->fullName();
1824 }
1825 /* Iterate over all the group-nodes: */
1826 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
1827 gatherGroupSettings(settings, pNode);
1828}
1829
1830void UIChooserAbstractModel::gatherGroupDefinitions(QMap<QString, QStringList> &definitions,
1831 UIChooserNode *pParentGroup)
1832{
1833 /* Prepare extra-data key for current group: */
1834 const QString strExtraDataKey = pParentGroup->fullName();
1835 /* Iterate over all the global-nodes: */
1836 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Global))
1837 {
1838 /* Append node definition: */
1839 AssertPtrReturnVoid(pNode);
1840 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1841 }
1842 /* Iterate over all the group-nodes: */
1843 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Group))
1844 {
1845 /* Append node definition: */
1846 AssertPtrReturnVoid(pNode);
1847 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1848 /* Go recursively through children: */
1849 gatherGroupDefinitions(definitions, pNode);
1850 }
1851 /* Iterate over all the machine-nodes: */
1852 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserNodeType_Machine))
1853 {
1854 /* Make sure it's really machine node: */
1855 AssertPtrReturnVoid(pNode);
1856 UIChooserNodeMachine *pMachineNode = pNode->toMachineNode();
1857 AssertPtrReturnVoid(pMachineNode);
1858 /* Append node definition, make sure it's local or real cloud machine node only: */
1859 if ( pMachineNode->cacheType() == UIVirtualMachineItemType_Local
1860 || pMachineNode->cacheType() == UIVirtualMachineItemType_CloudReal)
1861 definitions[strExtraDataKey] << pNode->definition(true /* full */);
1862 }
1863}
1864
1865void UIChooserAbstractModel::makeSureGroupSettingsSaveIsFinished()
1866{
1867 /* Cleanup if necessary: */
1868 if (UIThreadGroupSettingsSave::instance())
1869 UIThreadGroupSettingsSave::cleanup();
1870}
1871
1872void UIChooserAbstractModel::makeSureGroupDefinitionsSaveIsFinished()
1873{
1874 /* Cleanup if necessary: */
1875 if (UIThreadGroupDefinitionsSave::instance())
1876 UIThreadGroupDefinitionsSave::cleanup();
1877}
1878
1879UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QUuid &uProviderId)
1880{
1881 /* Search for a list of nodes matching passed name: */
1882 QList<UIChooserNode*> providerNodes;
1883 invisibleRoot()->searchForNodes(uProviderId.toString(),
1884 UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactId,
1885 providerNodes);
1886
1887 /* Return 1st node if any: */
1888 return providerNodes.value(0);
1889}
1890
1891UIChooserNode *UIChooserAbstractModel::searchProviderNode(const QString &strProviderShortName)
1892{
1893 /* Search for a list of nodes matching passed name: */
1894 QList<UIChooserNode*> providerNodes;
1895 invisibleRoot()->searchForNodes(strProviderShortName,
1896 UIChooserItemSearchFlag_CloudProvider | UIChooserItemSearchFlag_ExactName,
1897 providerNodes);
1898
1899 /* Return 1st node if any: */
1900 return providerNodes.value(0);
1901}
1902
1903UIChooserNode *UIChooserAbstractModel::searchProfileNode(UIChooserNode *pProviderNode, const QString &strProfileName)
1904{
1905 AssertPtrReturn(pProviderNode, 0);
1906
1907 /* Search for a list of nodes matching passed name: */
1908 QList<UIChooserNode*> profileNodes;
1909 pProviderNode->searchForNodes(strProfileName,
1910 UIChooserItemSearchFlag_CloudProfile | UIChooserItemSearchFlag_ExactName,
1911 profileNodes);
1912
1913 /* Return 1st node if any: */
1914 return profileNodes.value(0);
1915}
1916
1917UIChooserNode *UIChooserAbstractModel::searchProfileNode(const QString &strProviderShortName, const QString &strProfileName)
1918{
1919 /* Wrap method above: */
1920 return searchProfileNode(searchProviderNode(strProviderShortName), strProfileName);
1921}
1922
1923UIChooserNode *UIChooserAbstractModel::searchMachineNode(UIChooserNode *pProfileNode, const QUuid &uMachineId)
1924{
1925 AssertPtrReturn(pProfileNode, 0);
1926
1927 /* Search for a list of nodes matching passed ID: */
1928 QList<UIChooserNode*> machineNodes;
1929 pProfileNode->searchForNodes(uMachineId.toString(),
1930 UIChooserItemSearchFlag_Machine | UIChooserItemSearchFlag_ExactId,
1931 machineNodes);
1932
1933 /* Return 1st node if any: */
1934 return machineNodes.value(0);
1935}
1936
1937UIChooserNode *UIChooserAbstractModel::searchMachineNode(const QString &strProviderShortName, const QString &strProfileName, const QUuid &uMachineId)
1938{
1939 /* Wrap method above: */
1940 return searchMachineNode(searchProfileNode(strProviderShortName, strProfileName), uMachineId);
1941}
1942
1943UIChooserNode *UIChooserAbstractModel::searchFakeNode(UIChooserNode *pProfileNode)
1944{
1945 /* Wrap method above: */
1946 return searchMachineNode(pProfileNode, QUuid());
1947}
1948
1949UIChooserNode *UIChooserAbstractModel::searchFakeNode(const QString &strProviderShortName, const QString &strProfileName)
1950{
1951 /* Wrap method above: */
1952 return searchMachineNode(strProviderShortName, strProfileName, QUuid());
1953}
1954
1955QList<UIChooserNode*> UIChooserAbstractModel::enumerateCloudMachineNodes() const
1956{
1957 /* Search for a list of provider nodes: */
1958 QList<UIChooserNode*> providerNodes;
1959 invisibleRoot()->searchForNodes(QString(),
1960 UIChooserItemSearchFlag_CloudProvider,
1961 providerNodes);
1962
1963 /* Search for a list of profile nodes: */
1964 QList<UIChooserNode*> profileNodes;
1965 foreach (UIChooserNode *pProviderNode, providerNodes)
1966 pProviderNode->searchForNodes(QString(),
1967 UIChooserItemSearchFlag_CloudProfile,
1968 profileNodes);
1969
1970 /* Search for a list of machine nodes: */
1971 QList<UIChooserNode*> machineNodes;
1972 foreach (UIChooserNode *pProfileNode, profileNodes)
1973 pProfileNode->searchForNodes(QString(),
1974 UIChooserItemSearchFlag_Machine,
1975 machineNodes);
1976
1977 return machineNodes;
1978}
1979
1980void UIChooserAbstractModel::stopCloudUpdates(bool fForced /* = false */)
1981{
1982 /* Stop all cloud entity updates currently being performed: */
1983 foreach (const UICloudEntityKey &key, m_cloudEntityKeysBeingUpdated)
1984 {
1985 /* For profiles: */
1986 if (key.m_uMachineId.isNull())
1987 {
1988 /* Search task child by key: */
1989 UIProgressTaskReadCloudMachineList *pTask = findChild<UIProgressTaskReadCloudMachineList*>(key.toString());
1990 AssertPtrReturnVoid(pTask);
1991
1992 /* Wait for cloud profile refresh task to complete,
1993 * then delete the task itself manually: */
1994 if (!fForced)
1995 pTask->cancel();
1996 delete pTask;
1997 }
1998 /* For machines: */
1999 else
2000 {
2001 /* Search machine node: */
2002 UIChooserNode *pNode = searchMachineNode(key.m_strProviderShortName, key.m_strProfileName, key.m_uMachineId);
2003 AssertPtrReturnVoid(pNode);
2004 /* Acquire cloud machine item: */
2005 UIVirtualMachineItemCloud *pCloudMachineItem = pNode->toMachineNode()->cache()->toCloud();
2006 AssertPtrReturnVoid(pCloudMachineItem);
2007
2008 /* Wait for cloud machine refresh task to complete,
2009 * task itself will be deleted with the machine-node: */
2010 pCloudMachineItem->waitForAsyncInfoUpdateFinished();
2011 }
2012 }
2013
2014 /* We haven't let tasks to unregister themselves
2015 * so we have to cleanup task set ourselves: */
2016 m_cloudEntityKeysBeingUpdated.clear();
2017}
2018
2019
2020#include "UIChooserAbstractModel.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use