VirtualBox

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

Last change on this file since 103362 was 103362, checked in by vboxsync, 4 months ago

FE/Qt: bugref:10450: Get rid of QRegExp which is a part of Qt5 legacy API; This is necessary to get rid of Qt5 support lib.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use