Changeset 77891 in vbox for trunk/src/VBox/Frontends/VirtualBox
- Timestamp:
- Mar 26, 2019 5:33:58 PM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 129596
- Location:
- trunk/src/VBox/Frontends/VirtualBox
- Files:
-
- 3 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk
r77702 r77891 613 613 src/manager/UIWelcomePane.h \ 614 614 src/manager/chooser/UIChooser.h \ 615 src/manager/chooser/UIChooserAbstractModel.h \ 615 616 src/manager/chooser/UIChooserModel.h \ 616 617 src/manager/chooser/UIChooserView.h \ … … 1039 1040 src/manager/UIWelcomePane.cpp \ 1040 1041 src/manager/chooser/UIChooser.cpp \ 1042 src/manager/chooser/UIChooserAbstractModel.cpp \ 1041 1043 src/manager/chooser/UIChooserModel.cpp \ 1042 1044 src/manager/chooser/UIChooserView.cpp \ -
trunk/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.cpp
r77890 r77891 16 16 */ 17 17 18 /* Qt includes: */19 #include <QDrag>20 #include <QGraphicsScene>21 #include <QGraphicsSceneContextMenuEvent>22 #include <QGraphicsView>23 #include <QScrollBar>24 #include <QTimer>25 26 18 /* GUI includes: */ 27 #include "QIMessageBox.h"28 19 #include "VBoxGlobal.h" 29 #include "UIActionPoolManager.h"30 20 #include "UIChooser.h" 31 #include "UIChooserHandlerMouse.h" 32 #include "UIChooserHandlerKeyboard.h" 33 #include "UIChooserItemGroup.h" 34 #include "UIChooserItemGlobal.h" 35 #include "UIChooserItemMachine.h" 36 #include "UIChooserModel.h" 21 #include "UIChooserAbstractModel.h" 37 22 #include "UIChooserNode.h" 38 23 #include "UIChooserNodeGroup.h" 39 24 #include "UIChooserNodeGlobal.h" 40 25 #include "UIChooserNodeMachine.h" 41 #include "UIChooserView.h"42 26 #include "UIExtraDataManager.h" 43 27 #include "UIMessageCenter.h" 44 #include "UIModalWindowManager.h"45 #include "UIVirtualBoxManagerWidget.h"46 28 #include "UIVirtualBoxEventHandler.h" 47 #include "UIWizardNewVM.h"48 49 /* COM includes: */50 #include "CExtPack.h"51 #include "CExtPackManager.h"52 29 53 30 /* Type defs: */ … … 631 608 632 609 /********************************************************************************************************************************* 633 * Class UIChooserModel implementation. *634 *********************************************************************************************************************************/635 636 UIChooserModel:: UIChooserModel(UIChooser *pParent)637 : UIChooserAbstractModel(pParent)638 , m_pChooser(pParent)639 , m_pScene(0)640 , m_pMouseHandler(0)641 , m_pKeyboardHandler(0)642 , m_pContextMenuGlobal(0)643 , m_pContextMenuGroup(0)644 , m_pContextMenuMachine(0)645 , m_iCurrentScrolledIndex(-1)646 , m_iScrollingTokenSize(30)647 , m_fIsScrollingInProgress(false)648 , m_pLookupTimer(0)649 {650 prepare();651 }652 653 UIChooserModel::~UIChooserModel()654 {655 cleanup();656 }657 658 void UIChooserModel::init()659 {660 /* Call to base-class: */661 UIChooserAbstractModel::init();662 663 /* Build tree for main root: */664 buildTreeForMainRoot();665 updateNavigation();666 updateLayout();667 668 /* Load last selected item: */669 loadLastSelectedItem();670 }671 672 void UIChooserModel::deinit()673 {674 /* Save last selected item: */675 saveLastSelectedItem();676 677 /* Unset current items: */678 unsetCurrentItems();679 680 /* Call to base-class: */681 UIChooserAbstractModel::deinit();682 }683 684 UIChooser *UIChooserModel::chooser() const685 {686 return m_pChooser;687 }688 689 UIActionPool *UIChooserModel::actionPool() const690 {691 return chooser()->actionPool();692 }693 694 QGraphicsScene *UIChooserModel::scene() const695 {696 return m_pScene;697 }698 699 UIChooserView *UIChooserModel::view()700 {701 if (!scene())702 return 0;703 UIChooserView *pChooserView = qobject_cast<UIChooserView*>(scene()->views()[0]);704 return pChooserView;705 }706 707 QPaintDevice *UIChooserModel::paintDevice() const708 {709 if (scene() && !scene()->views().isEmpty())710 return scene()->views().first();711 return 0;712 }713 714 QGraphicsItem *UIChooserModel::itemAt(const QPointF &position, const QTransform &deviceTransform /* = QTransform() */) const715 {716 return scene()->itemAt(position, deviceTransform);717 }718 719 void UIChooserModel::handleToolButtonClick(UIChooserItem *pItem)720 {721 switch (pItem->type())722 {723 case UIChooserItemType_Global:724 emit sigToolMenuRequested(UIToolClass_Global, pItem->mapToScene(QPointF(pItem->size().width(), 0)).toPoint());725 break;726 case UIChooserItemType_Machine:727 emit sigToolMenuRequested(UIToolClass_Machine, pItem->mapToScene(QPointF(pItem->size().width(), 0)).toPoint());728 break;729 default:730 break;731 }732 }733 734 void UIChooserModel::handlePinButtonClick(UIChooserItem *pItem)735 {736 switch (pItem->type())737 {738 case UIChooserItemType_Global:739 pItem->setFavorite(!pItem->isFavorite());740 break;741 default:742 break;743 }744 }745 746 void UIChooserModel::setCurrentItems(const QList<UIChooserItem*> &items)747 {748 /* Is there something changed? */749 if (m_currentItems == items)750 return;751 752 /* Remember old current-item list: */753 const QList<UIChooserItem*> oldCurrentItems = m_currentItems;754 755 /* Clear current current-item list: */756 m_currentItems.clear();757 758 /* Iterate over all the passed items: */759 foreach (UIChooserItem *pItem, items)760 {761 /* Add item to current list if navigation list contains it: */762 if (pItem && navigationList().contains(pItem))763 m_currentItems << pItem;764 else765 AssertMsgFailed(("Passed item is not in navigation list!"));766 }767 768 /* Is there something really changed? */769 if (oldCurrentItems == m_currentItems)770 return;771 772 /* Update all the old items (they are no longer selected): */773 foreach (UIChooserItem *pItem, oldCurrentItems)774 pItem->update();775 /* Update all the new items (they are selected now): */776 foreach (UIChooserItem *pItem, m_currentItems)777 pItem->update();778 779 /* Notify about selection changes: */780 emit sigSelectionChanged();781 }782 783 void UIChooserModel::setCurrentItem(UIChooserItem *pItem)784 {785 /* Call for wrapper above: */786 QList<UIChooserItem*> items;787 if (pItem)788 items << pItem;789 setCurrentItems(items);790 791 /* Move focus to current-item: */792 setFocusItem(currentItem());793 }794 795 void UIChooserModel::setCurrentItem(const QString &strDefinition)796 {797 /* Ignore if empty definition passed: */798 if (strDefinition.isEmpty())799 return;800 801 /* Parse definition: */802 UIChooserItem *pItem = 0;803 const QString strItemType = strDefinition.section('=', 0, 0);804 const QString strItemDescriptor = strDefinition.section('=', 1, -1);805 /* Its a group-item definition? */806 if (strItemType == "g")807 {808 /* Search for group-item with passed descriptor (name): */809 pItem = root()->searchForItem(strItemDescriptor,810 UIChooserItemSearchFlag_Group |811 UIChooserItemSearchFlag_ExactName);812 }813 /* Its a machine-item definition? */814 else if (strItemType == "m")815 {816 /* Check if machine-item with passed descriptor (name or id) registered: */817 CMachine comMachine = vboxGlobal().virtualBox().FindMachine(strItemDescriptor);818 if (!comMachine.isNull())819 {820 /* Search for machine-item with required name: */821 pItem = root()->searchForItem(comMachine.GetName(),822 UIChooserItemSearchFlag_Machine |823 UIChooserItemSearchFlag_ExactName);824 }825 }826 827 /* Make sure found item is in navigation list: */828 if (!pItem || !navigationList().contains(pItem))829 return;830 831 /* Call for wrapper above: */832 setCurrentItem(pItem);833 }834 835 void UIChooserModel::unsetCurrentItems()836 {837 /* Call for wrapper above: */838 setCurrentItem(0);839 }840 841 void UIChooserModel::addToCurrentItems(UIChooserItem *pItem)842 {843 /* Call for wrapper above: */844 setCurrentItems(QList<UIChooserItem*>(m_currentItems) << pItem);845 }846 847 void UIChooserModel::removeFromCurrentItems(UIChooserItem *pItem)848 {849 /* Prepare filtered list: */850 QList<UIChooserItem*> list(m_currentItems);851 list.removeAll(pItem);852 /* Call for wrapper above: */853 setCurrentItems(list);854 }855 856 UIChooserItem *UIChooserModel::currentItem() const857 {858 /* Return first of current items, if any: */859 return currentItems().isEmpty() ? 0 : currentItems().first();860 }861 862 const QList<UIChooserItem*> &UIChooserModel::currentItems() const863 {864 return m_currentItems;865 }866 867 UIVirtualMachineItem *UIChooserModel::currentMachineItem() const868 {869 /* Return first machine-item of the current-item: */870 return currentItem() && currentItem()->firstMachineItem() && currentItem()->firstMachineItem()->node()871 ? currentItem()->firstMachineItem()->node()->toMachineNode()872 : 0;873 }874 875 QList<UIVirtualMachineItem*> UIChooserModel::currentMachineItems() const876 {877 /* Gather list of current unique machine-items: */878 QList<UIChooserItemMachine*> currentMachineItemList;879 UIChooserItemMachine::enumerateMachineItems(currentItems(), currentMachineItemList,880 UIChooserItemMachineEnumerationFlag_Unique);881 882 /* Reintegrate machine-items into valid format: */883 QList<UIVirtualMachineItem*> currentMachineList;884 foreach (UIChooserItemMachine *pItem, currentMachineItemList)885 currentMachineList << pItem->node()->toMachineNode();886 return currentMachineList;887 }888 889 bool UIChooserModel::isGroupItemSelected() const890 {891 return currentItem() && currentItem()->type() == UIChooserItemType_Group;892 }893 894 bool UIChooserModel::isGlobalItemSelected() const895 {896 return currentItem() && currentItem()->type() == UIChooserItemType_Global;897 }898 899 bool UIChooserModel::isMachineItemSelected() const900 {901 return currentItem() && currentItem()->type() == UIChooserItemType_Machine;902 }903 904 bool UIChooserModel::isSingleGroupSelected() const905 {906 return currentItems().size() == 1907 && currentItem()->type() == UIChooserItemType_Group;908 }909 910 bool UIChooserModel::isAllItemsOfOneGroupSelected() const911 {912 /* Make sure at least one item selected: */913 if (currentItems().isEmpty())914 return false;915 916 /* Determine the parent group of the first item: */917 UIChooserItem *pFirstParent = currentItem()->parentItem();918 919 /* Make sure this parent is not main root-item: */920 if (pFirstParent == root())921 return false;922 923 /* Enumerate current-item set: */924 QSet<UIChooserItem*> currentItemSet;925 foreach (UIChooserItem *pCurrentItem, currentItems())926 currentItemSet << pCurrentItem;927 928 /* Enumerate first parent children set: */929 QSet<UIChooserItem*> firstParentItemSet;930 foreach (UIChooserItem *pFirstParentItem, pFirstParent->items())931 firstParentItemSet << pFirstParentItem;932 933 /* Check if both sets contains the same: */934 return currentItemSet == firstParentItemSet;935 }936 937 UIChooserItem *UIChooserModel::findClosestUnselectedItem() const938 {939 /* Take the focus item (if any) as a starting point940 * and find the closest non-selected item. */941 UIChooserItem *pItem = focusItem();942 if (!pItem)943 pItem = currentItem();944 if (pItem)945 {946 int idxBefore = navigationList().indexOf(pItem) - 1;947 int idxAfter = idxBefore + 2;948 while (idxBefore >= 0 || idxAfter < navigationList().size())949 {950 if (idxBefore >= 0)951 {952 pItem = navigationList().at(idxBefore);953 if (!currentItems().contains(pItem) && pItem->type() == UIChooserItemType_Machine)954 return pItem;955 --idxBefore;956 }957 if (idxAfter < navigationList().size())958 {959 pItem = navigationList().at(idxAfter);960 if (!currentItems().contains(pItem) && pItem->type() == UIChooserItemType_Machine)961 return pItem;962 ++idxAfter;963 }964 }965 }966 return 0;967 }968 969 void UIChooserModel::makeSureSomeItemIsSelected()970 {971 /* Make sure selection item list is never empty972 * if at least one item (for example 'focus') present: */973 if (!currentItem() && focusItem())974 setCurrentItem(focusItem());975 }976 977 void UIChooserModel::setFocusItem(UIChooserItem *pItem)978 {979 /* Make sure real focus unset: */980 clearRealFocus();981 982 /* Is there something changed? */983 if (m_pFocusItem == pItem)984 return;985 986 /* Remember old focus-item: */987 UIChooserItem *pOldFocusItem = m_pFocusItem;988 989 /* Set new focus-item: */990 m_pFocusItem = pItem;991 992 /* Disconnect old focus-item (if any): */993 if (pOldFocusItem)994 disconnect(pOldFocusItem, SIGNAL(destroyed(QObject*)), this, SLOT(sltFocusItemDestroyed()));995 /* Connect new focus-item (if any): */996 if (m_pFocusItem)997 connect(m_pFocusItem, SIGNAL(destroyed(QObject*)), this, SLOT(sltFocusItemDestroyed()));998 999 /* If dialog is visible and item exists => make it visible as well: */1000 if (view() && view()->window() && root())1001 if (view()->window()->isVisible() && pItem)1002 root()->makeSureItemIsVisible(pItem);1003 }1004 1005 UIChooserItem *UIChooserModel::focusItem() const1006 {1007 return m_pFocusItem;1008 }1009 1010 const QList<UIChooserItem*> &UIChooserModel::navigationList() const1011 {1012 return m_navigationList;1013 }1014 1015 void UIChooserModel::removeFromNavigationList(UIChooserItem *pItem)1016 {1017 AssertMsg(pItem, ("Passed item is invalid!"));1018 m_navigationList.removeAll(pItem);1019 }1020 1021 void UIChooserModel::updateNavigation()1022 {1023 m_navigationList.clear();1024 m_navigationList = createNavigationList(root());1025 }1026 1027 void UIChooserModel::performSearch(const QString &strSearchTerm, int iItemSearchFlags)1028 {1029 if (!invisibleRoot())1030 return;1031 1032 /* Currently we perform the search only for machines. when this to be changed make sure the disabled flags1033 of the other item types are also managed correctly: */1034 1035 QList<UIChooserNode*> allNodes = resetSearch();1036 if (strSearchTerm.isEmpty())1037 return;1038 1039 invisibleRoot()->searchForNodes(strSearchTerm, iItemSearchFlags, m_searchResults);1040 1041 foreach (UIChooserNode* pNode, allNodes)1042 {1043 if (!pNode)1044 continue;1045 pNode->setDisabled(!m_searchResults.contains(pNode));1046 }1047 1048 scrollToSearchResult(true);1049 }1050 1051 QList<UIChooserNode*> UIChooserModel::resetSearch()1052 {1053 QList<UIChooserNode*> allNodes;1054 /* Calling UIChooserNode::searchForNodes with an empty search string returns a list all nodes (of the whole treee) of the required type: */1055 invisibleRoot()->searchForNodes(QString(), UIChooserItemSearchFlag_Machine, allNodes);1056 1057 /* Reset the disabled flag of the node items first. */1058 foreach (UIChooserNode* pNode, allNodes)1059 {1060 if (!pNode)1061 continue;1062 pNode->setDisabled(false);1063 }1064 /* Reset the search result relate data: */1065 m_searchResults.clear();1066 m_iCurrentScrolledIndex = -1;1067 return allNodes;1068 }1069 1070 void UIChooserModel::scrollToSearchResult(bool fIsNext)1071 {1072 if (m_searchResults.isEmpty())1073 {1074 m_iCurrentScrolledIndex = -1;1075 if (view())1076 view()->setSearchResultsCount(m_searchResults.size(), m_iCurrentScrolledIndex);1077 return;1078 }1079 1080 if (fIsNext)1081 {1082 if (++m_iCurrentScrolledIndex >= m_searchResults.size())1083 m_iCurrentScrolledIndex = 0;1084 }1085 else1086 {1087 if (--m_iCurrentScrolledIndex < 0)1088 m_iCurrentScrolledIndex = m_searchResults.size() - 1;1089 }1090 1091 if (m_searchResults.at(m_iCurrentScrolledIndex))1092 {1093 UIChooserItem *pItem = m_searchResults.at(m_iCurrentScrolledIndex)->item();1094 if (pItem)1095 {1096 pItem->makeSureItsVisible();1097 setCurrentItem(pItem);1098 }1099 }1100 1101 /* Update the search widget's match count(s): */1102 if (view())1103 view()->setSearchResultsCount(m_searchResults.size(), m_iCurrentScrolledIndex);1104 }1105 1106 void UIChooserModel::setSearchWidgetVisible(bool fVisible)1107 {1108 if (view())1109 view()->setSearchWidgetVisible(fVisible);1110 }1111 1112 UIChooserItem *UIChooserModel::root() const1113 {1114 return m_pRoot.data();1115 }1116 1117 void UIChooserModel::startEditingGroupItemName()1118 {1119 sltEditGroupName();1120 }1121 1122 void UIChooserModel::activateMachineItem()1123 {1124 actionPool()->action(UIActionIndexST_M_Machine_M_StartOrShow)->activate(QAction::Trigger);1125 }1126 1127 void UIChooserModel::setCurrentDragObject(QDrag *pDragObject)1128 {1129 /* Make sure real focus unset: */1130 clearRealFocus();1131 1132 /* Remember new drag-object: */1133 m_pCurrentDragObject = pDragObject;1134 connect(m_pCurrentDragObject, SIGNAL(destroyed(QObject*)), this, SLOT(sltCurrentDragObjectDestroyed()));1135 }1136 1137 void UIChooserModel::lookFor(const QString &strLookupSymbol)1138 {1139 if (view())1140 {1141 view()->setSearchWidgetVisible(true);1142 view()->appendToSearchString(strLookupSymbol);1143 }1144 }1145 1146 bool UIChooserModel::isLookupInProgress() const1147 {1148 return m_pLookupTimer->isActive();1149 }1150 1151 void UIChooserModel::updateLayout()1152 {1153 if (!view() || !root())1154 return;1155 1156 /* Initialize variables: */1157 const QSize viewportSize = view()->size();1158 const int iViewportWidth = viewportSize.width();1159 const int iViewportHeight = viewportSize.height();1160 1161 /* Set root-item position: */1162 root()->setPos(0, 0);1163 /* Set root-item size: */1164 root()->resize(iViewportWidth, iViewportHeight);1165 /* Relayout root-item: */1166 root()->updateLayout();1167 /* Make sure root-item is shown: */1168 root()->show();1169 }1170 1171 void UIChooserModel::setGlobalItemHeightHint(int iHint)1172 {1173 /* Walk thrugh all the items of navigation list: */1174 foreach (UIChooserItem *pItem, navigationList())1175 {1176 /* And for each global item: */1177 if (pItem->type() == UIChooserItemType_Global)1178 {1179 /* Apply the height hint we have: */1180 UIChooserItemGlobal *pGlobalItem = pItem->toGlobalItem();1181 if (pGlobalItem)1182 pGlobalItem->setHeightHint(iHint);1183 }1184 }1185 }1186 1187 void UIChooserModel::sltHandleViewResized()1188 {1189 /* Relayout: */1190 updateLayout();1191 }1192 1193 bool UIChooserModel::eventFilter(QObject *pWatched, QEvent *pEvent)1194 {1195 /* Process only scene events: */1196 if (pWatched != scene())1197 return QObject::eventFilter(pWatched, pEvent);1198 1199 /* Process only item focused by model: */1200 if (scene()->focusItem())1201 return QObject::eventFilter(pWatched, pEvent);1202 1203 /* Checking event-type: */1204 switch (pEvent->type())1205 {1206 /* Keyboard handler: */1207 case QEvent::KeyPress:1208 return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Press);1209 case QEvent::KeyRelease:1210 return m_pKeyboardHandler->handle(static_cast<QKeyEvent*>(pEvent), UIKeyboardEventType_Release);1211 /* Mouse handler: */1212 case QEvent::GraphicsSceneMousePress:1213 return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Press);1214 case QEvent::GraphicsSceneMouseRelease:1215 return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_Release);1216 case QEvent::GraphicsSceneMouseDoubleClick:1217 return m_pMouseHandler->handle(static_cast<QGraphicsSceneMouseEvent*>(pEvent), UIMouseEventType_DoubleClick);1218 /* Context-menu handler: */1219 case QEvent::GraphicsSceneContextMenu:1220 return processContextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent*>(pEvent));1221 /* Drag&drop scroll-event (drag-move) handler: */1222 case QEvent::GraphicsSceneDragMove:1223 return processDragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(pEvent));1224 /* Drag&drop scroll-event (drag-leave) handler: */1225 case QEvent::GraphicsSceneDragLeave:1226 return processDragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(pEvent));1227 default: break; /* Shut up MSC */1228 }1229 1230 /* Call to base-class: */1231 return QObject::eventFilter(pWatched, pEvent);1232 }1233 1234 void UIChooserModel::sltMachineRegistered(const QUuid &uId, const bool fRegistered)1235 {1236 /* Call to base-class: */1237 UIChooserAbstractModel::sltMachineRegistered(uId, fRegistered);1238 1239 /* Existing VM unregistered? */1240 if (!fRegistered)1241 {1242 /* Rebuild tree for main root: */1243 buildTreeForMainRoot();1244 updateNavigation();1245 updateLayout();1246 1247 /* Make sure current-item present, if possible: */1248 if (!currentItem() && !navigationList().isEmpty())1249 setCurrentItem(navigationList().first());1250 /* Make sure focus-item present, if possible: */1251 else if (!focusItem() && currentItem())1252 setFocusItem(currentItem());1253 /* Notify about current-item change: */1254 emit sigSelectionChanged();1255 }1256 /* New VM registered? */1257 else1258 {1259 /* Should we show this VM? */1260 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uId))1261 {1262 /* Rebuild tree for main root: */1263 buildTreeForMainRoot();1264 updateNavigation();1265 updateLayout();1266 1267 /* Choose newly added item: */1268 CMachine comMachine = vboxGlobal().virtualBox().FindMachine(uId.toString());1269 setCurrentItem(root()->searchForItem(comMachine.GetName(),1270 UIChooserItemSearchFlag_Machine |1271 UIChooserItemSearchFlag_ExactName));1272 }1273 }1274 }1275 1276 void UIChooserModel::sltReloadMachine(const QUuid &uId)1277 {1278 /* Call to base-class: */1279 UIChooserAbstractModel::sltReloadMachine(uId);1280 1281 /* Show machine if we should: */1282 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uId))1283 {1284 /* Rebuild tree for main root: */1285 buildTreeForMainRoot();1286 updateNavigation();1287 updateLayout();1288 1289 /* Choose newly added item: */1290 CMachine comMachine = vboxGlobal().virtualBox().FindMachine(uId.toString());1291 setCurrentItem(root()->searchForItem(comMachine.GetName(),1292 UIChooserItemSearchFlag_Machine |1293 UIChooserItemSearchFlag_ExactName));1294 }1295 else1296 {1297 /* Make sure at least one item selected after that: */1298 if (!currentItem() && !navigationList().isEmpty())1299 setCurrentItem(navigationList().first());1300 }1301 1302 /* Notify listeners about selection change: */1303 emit sigSelectionChanged();1304 }1305 1306 void UIChooserModel::sltFocusItemDestroyed()1307 {1308 AssertMsgFailed(("Focus item destroyed!"));1309 }1310 1311 void UIChooserModel::sltEditGroupName()1312 {1313 /* Check if action is enabled: */1314 if (!actionPool()->action(UIActionIndexST_M_Group_S_Rename)->isEnabled())1315 return;1316 1317 /* Only for single selected group: */1318 if (!isSingleGroupSelected())1319 return;1320 1321 /* Start editing group name: */1322 currentItem()->startEditing();1323 }1324 1325 void UIChooserModel::sltSortGroup()1326 {1327 /* Check if action is enabled: */1328 if (!actionPool()->action(UIActionIndexST_M_Group_S_Sort)->isEnabled())1329 return;1330 1331 /* Only for single selected group: */1332 if (!isSingleGroupSelected())1333 return;1334 1335 /* Sort group-node: */1336 sortNodes(currentItem()->node());1337 }1338 1339 void UIChooserModel::sltShowHideSearchWidget()1340 {1341 if (view())1342 setSearchWidgetVisible(!view()->isSearchWidgetVisible());1343 }1344 1345 void UIChooserModel::sltUngroupSelectedGroup()1346 {1347 /* Check if action is enabled: */1348 if (!actionPool()->action(UIActionIndexST_M_Group_S_Remove)->isEnabled())1349 return;1350 1351 /* Make sure focus item is of group type! */1352 AssertMsg(focusItem()->type() == UIChooserItemType_Group, ("This is not group-item!"));1353 1354 /* Check if we have collisions with our siblings: */1355 UIChooserItem *pFocusItem = focusItem();1356 UIChooserNode *pFocusNode = pFocusItem->node();1357 UIChooserItem *pParentItem = pFocusItem->parentItem();1358 UIChooserNode *pParentNode = pParentItem->node();1359 QList<UIChooserNode*> siblings = pParentNode->nodes();1360 QList<UIChooserNode*> toBeRenamed;1361 QList<UIChooserNode*> toBeRemoved;1362 foreach (UIChooserNode *pNode, pFocusNode->nodes())1363 {1364 QString strItemName = pNode->name();1365 UIChooserNode *pCollisionSibling = 0;1366 foreach (UIChooserNode *pSibling, siblings)1367 if (pSibling != pFocusNode && pSibling->name() == strItemName)1368 pCollisionSibling = pSibling;1369 if (pCollisionSibling)1370 {1371 if (pNode->type() == UIChooserItemType_Machine)1372 {1373 if (pCollisionSibling->type() == UIChooserItemType_Machine)1374 toBeRemoved << pNode;1375 else if (pCollisionSibling->type() == UIChooserItemType_Group)1376 {1377 msgCenter().cannotResolveCollisionAutomatically(strItemName, pParentNode->name());1378 return;1379 }1380 }1381 else if (pNode->type() == UIChooserItemType_Group)1382 {1383 if (msgCenter().confirmAutomaticCollisionResolve(strItemName, pParentNode->name()))1384 toBeRenamed << pNode;1385 else1386 return;1387 }1388 }1389 }1390 1391 /* Copy all the children into our parent: */1392 QList<UIChooserItem*> copiedItems;1393 foreach (UIChooserNode *pNode, pFocusNode->nodes())1394 {1395 if (toBeRemoved.contains(pNode))1396 continue;1397 switch (pNode->type())1398 {1399 case UIChooserItemType_Group:1400 {1401 UIChooserNodeGroup *pGroupNode = new UIChooserNodeGroup(pParentNode,1402 pNode->toGroupNode(),1403 pParentNode->nodes().size());1404 UIChooserItemGroup *pGroupItem = new UIChooserItemGroup(pParentItem, pGroupNode);1405 if (toBeRenamed.contains(pNode))1406 pGroupNode->setName(uniqueGroupName(pParentNode));1407 copiedItems << pGroupItem;1408 break;1409 }1410 case UIChooserItemType_Machine:1411 {1412 UIChooserNodeMachine *pMachineNode = new UIChooserNodeMachine(pParentNode,1413 pNode->toMachineNode(),1414 pParentNode->nodes().size());1415 UIChooserItemMachine *pMachineItem = new UIChooserItemMachine(pParentItem, pMachineNode);1416 copiedItems << pMachineItem;1417 break;1418 }1419 default:1420 break;1421 }1422 }1423 1424 /* Delete focus group: */1425 delete pFocusNode;1426 1427 /* Notify about selection invalidated: */1428 emit sigSelectionInvalidated();1429 1430 /* And update model: */1431 updateNavigation();1432 updateLayout();1433 if (!copiedItems.isEmpty())1434 {1435 setCurrentItems(copiedItems);1436 setFocusItem(currentItem());1437 }1438 else1439 setCurrentItem(navigationList().first());1440 saveGroupSettings();1441 }1442 1443 void UIChooserModel::sltCreateNewMachine()1444 {1445 /* Check if action is enabled: */1446 if (!actionPool()->action(UIActionIndexST_M_Machine_S_New)->isEnabled())1447 return;1448 1449 /* Choose the parent: */1450 UIChooserItem *pGroup = 0;1451 if (isSingleGroupSelected())1452 pGroup = currentItem();1453 else if (!currentItems().isEmpty())1454 pGroup = currentItem()->parentItem();1455 QString strGroupName;1456 if (pGroup)1457 strGroupName = pGroup->fullName();1458 1459 /* Lock the action preventing cascade calls: */1460 actionPool()->action(UIActionIndexST_M_Welcome_S_New)->setEnabled(false);1461 actionPool()->action(UIActionIndexST_M_Machine_S_New)->setEnabled(false);1462 actionPool()->action(UIActionIndexST_M_Group_S_New)->setEnabled(false);1463 1464 /* Use the "safe way" to open stack of Mac OS X Sheets: */1465 QWidget *pWizardParent = windowManager().realParentWindow(chooser()->managerWidget());1466 UISafePointerWizardNewVM pWizard = new UIWizardNewVM(pWizardParent, strGroupName);1467 windowManager().registerNewParent(pWizard, pWizardParent);1468 pWizard->prepare();1469 1470 /* Execute wizard: */1471 pWizard->exec();1472 if (pWizard)1473 delete pWizard;1474 1475 /* Unlock the action allowing further calls: */1476 actionPool()->action(UIActionIndexST_M_Welcome_S_New)->setEnabled(true);1477 actionPool()->action(UIActionIndexST_M_Machine_S_New)->setEnabled(true);1478 actionPool()->action(UIActionIndexST_M_Group_S_New)->setEnabled(true);1479 }1480 1481 void UIChooserModel::sltGroupSelectedMachines()1482 {1483 /* Check if action is enabled: */1484 if (!actionPool()->action(UIActionIndexST_M_Machine_S_AddGroup)->isEnabled())1485 return;1486 1487 /* Create new group node in the current root: */1488 UIChooserNodeGroup *pNewGroupNode = new UIChooserNodeGroup(invisibleRoot(),1489 false /* favorite */,1490 invisibleRoot()->nodes().size() /* position */,1491 uniqueGroupName(invisibleRoot()),1492 true /* opened */);1493 UIChooserItemGroup *pNewGroupItem = new UIChooserItemGroup(root(), pNewGroupNode);1494 1495 /* Enumerate all the currently chosen items: */1496 QStringList busyGroupNames;1497 QStringList busyMachineNames;1498 QList<UIChooserItem*> selectedItems = currentItems();1499 foreach (UIChooserItem *pItem, selectedItems)1500 {1501 /* For each of known types: */1502 switch (pItem->type())1503 {1504 case UIChooserItemType_Group:1505 {1506 /* Avoid name collisions: */1507 if (busyGroupNames.contains(pItem->name()))1508 break;1509 /* Add name to busy: */1510 busyGroupNames << pItem->name();1511 /* Copy or move group-item: */1512 UIChooserNodeGroup *pNewGroupSubNode = new UIChooserNodeGroup(pNewGroupNode,1513 pItem->node()->toGroupNode(),1514 pNewGroupNode->nodes().size());1515 new UIChooserItemGroup(pNewGroupItem, pNewGroupSubNode);1516 delete pItem->node();1517 break;1518 }1519 case UIChooserItemType_Machine:1520 {1521 /* Avoid name collisions: */1522 if (busyMachineNames.contains(pItem->name()))1523 break;1524 /* Add name to busy: */1525 busyMachineNames << pItem->name();1526 /* Copy or move machine-item: */1527 UIChooserNodeMachine *pNewMachineSubNode = new UIChooserNodeMachine(pNewGroupNode,1528 pItem->node()->toMachineNode(),1529 pNewGroupNode->nodes().size());1530 new UIChooserItemMachine(pNewGroupItem, pNewMachineSubNode);1531 delete pItem->node();1532 break;1533 }1534 }1535 }1536 1537 /* Update model: */1538 wipeOutEmptyGroups();1539 updateNavigation();1540 updateLayout();1541 setCurrentItem(pNewGroupItem);1542 saveGroupSettings();1543 }1544 1545 void UIChooserModel::sltSortParentGroup()1546 {1547 /* Check if action is enabled: */1548 if (!actionPool()->action(UIActionIndexST_M_Machine_S_SortParent)->isEnabled())1549 return;1550 1551 /* Only if some item selected: */1552 if (!currentItem())1553 return;1554 1555 /* Sort parent group-node: */1556 sortNodes(currentItem()->parentItem()->node());1557 }1558 1559 void UIChooserModel::sltPerformRefreshAction()1560 {1561 /* Check if action is enabled: */1562 if (!actionPool()->action(UIActionIndexST_M_Group_S_Refresh)->isEnabled())1563 return;1564 1565 /* Gather list of current unique inaccessible machine-items: */1566 QList<UIChooserItemMachine*> inaccessibleMachineItemList;1567 UIChooserItemMachine::enumerateMachineItems(currentItems(), inaccessibleMachineItemList,1568 UIChooserItemMachineEnumerationFlag_Unique |1569 UIChooserItemMachineEnumerationFlag_Inaccessible);1570 1571 /* For each machine-item: */1572 UIChooserItem *pSelectedItem = 0;1573 foreach (UIChooserItemMachine *pItem, inaccessibleMachineItemList)1574 {1575 /* Recache: */1576 pItem->recache();1577 /* Become accessible? */1578 if (pItem->accessible())1579 {1580 /* Machine name: */1581 const QString strMachineName = ((UIChooserItem*)pItem)->name();1582 /* We should reload this machine: */1583 sltReloadMachine(pItem->id());1584 /* Select first of reloaded items: */1585 if (!pSelectedItem)1586 pSelectedItem = root()->searchForItem(strMachineName,1587 UIChooserItemSearchFlag_Machine |1588 UIChooserItemSearchFlag_ExactName);1589 }1590 }1591 1592 /* Some item to be selected? */1593 if (pSelectedItem)1594 {1595 pSelectedItem->makeSureItsVisible();1596 setCurrentItem(pSelectedItem);1597 }1598 }1599 1600 void UIChooserModel::sltRemoveSelectedMachine()1601 {1602 /* Check if action is enabled: */1603 if (!actionPool()->action(UIActionIndexST_M_Machine_S_Remove)->isEnabled())1604 return;1605 1606 /* Enumerate all the selected machine-items: */1607 QList<UIChooserItemMachine*> selectedMachineItemList;1608 UIChooserItemMachine::enumerateMachineItems(currentItems(), selectedMachineItemList);1609 /* Enumerate all the existing machine-items: */1610 QList<UIChooserItemMachine*> existingMachineItemList;1611 UIChooserItemMachine::enumerateMachineItems(root()->items(), existingMachineItemList);1612 1613 /* Prepare arrays: */1614 QMap<QUuid, bool> verdicts;1615 QList<UIChooserItem*> itemsToRemove;1616 QList<QUuid> machinesToUnregister;1617 1618 /* For each selected machine-item: */1619 foreach (UIChooserItem *pItem, selectedMachineItemList)1620 {1621 /* Get machine-item id: */1622 QUuid uId = pItem->toMachineItem()->id();1623 1624 /* We already decided for that machine? */1625 if (verdicts.contains(uId))1626 {1627 /* To remove similar machine items? */1628 if (!verdicts[uId])1629 itemsToRemove << pItem;1630 continue;1631 }1632 1633 /* Selected copy count: */1634 int iSelectedCopyCount = 0;1635 foreach (UIChooserItem *pSelectedItem, selectedMachineItemList)1636 if (pSelectedItem->toMachineItem()->id() == uId)1637 ++iSelectedCopyCount;1638 /* Existing copy count: */1639 int iExistingCopyCount = 0;1640 foreach (UIChooserItem *pExistingItem, existingMachineItemList)1641 if (pExistingItem->toMachineItem()->id() == uId)1642 ++iExistingCopyCount;1643 /* If selected copy count equal to existing copy count,1644 * we will propose ro unregister machine fully else1645 * we will just propose to remove selected items: */1646 bool fVerdict = iSelectedCopyCount == iExistingCopyCount;1647 verdicts.insert(uId, fVerdict);1648 if (fVerdict)1649 machinesToUnregister.append(uId);1650 else1651 itemsToRemove << pItem;1652 }1653 1654 /* If we have something to remove: */1655 if (!itemsToRemove.isEmpty())1656 removeItems(itemsToRemove);1657 /* If we have something to unregister: */1658 if (!machinesToUnregister.isEmpty())1659 unregisterMachines(machinesToUnregister);1660 }1661 1662 void UIChooserModel::sltStartScrolling()1663 {1664 /* Should we scroll? */1665 if (!m_fIsScrollingInProgress)1666 return;1667 1668 /* Reset scrolling progress: */1669 m_fIsScrollingInProgress = false;1670 1671 /* Get view/scrollbar: */1672 QGraphicsView *pView = view();1673 QScrollBar *pVerticalScrollBar = pView->verticalScrollBar();1674 1675 /* Convert mouse position to view co-ordinates: */1676 const QPoint mousePos = pView->mapFromGlobal(QCursor::pos());1677 /* Mouse position is at the top of view? */1678 if (mousePos.y() < m_iScrollingTokenSize && mousePos.y() > 0)1679 {1680 int iValue = mousePos.y();1681 if (!iValue) iValue = 1;1682 int iDelta = m_iScrollingTokenSize / iValue;1683 if (pVerticalScrollBar->value() > pVerticalScrollBar->minimum())1684 {1685 /* Backward scrolling: */1686 pVerticalScrollBar->setValue(pVerticalScrollBar->value() - 2 * iDelta);1687 m_fIsScrollingInProgress = true;1688 QTimer::singleShot(10, this, SLOT(sltStartScrolling()));1689 }1690 }1691 /* Mouse position is at the bottom of view? */1692 else if (mousePos.y() > pView->height() - m_iScrollingTokenSize && mousePos.y() < pView->height())1693 {1694 int iValue = pView->height() - mousePos.y();1695 if (!iValue) iValue = 1;1696 int iDelta = m_iScrollingTokenSize / iValue;1697 if (pVerticalScrollBar->value() < pVerticalScrollBar->maximum())1698 {1699 /* Forward scrolling: */1700 pVerticalScrollBar->setValue(pVerticalScrollBar->value() + 2 * iDelta);1701 m_fIsScrollingInProgress = true;1702 QTimer::singleShot(10, this, SLOT(sltStartScrolling()));1703 }1704 }1705 }1706 1707 void UIChooserModel::sltCurrentDragObjectDestroyed()1708 {1709 root()->resetDragToken();1710 }1711 1712 void UIChooserModel::sltEraseLookupTimer()1713 {1714 m_pLookupTimer->stop();1715 m_strLookupString = QString();1716 }1717 1718 void UIChooserModel::prepare()1719 {1720 prepareScene();1721 prepareLookup();1722 prepareContextMenu();1723 prepareHandlers();1724 prepareConnections();1725 }1726 1727 void UIChooserModel::prepareScene()1728 {1729 m_pScene = new QGraphicsScene(this);1730 if (m_pScene)1731 m_pScene->installEventFilter(this);1732 }1733 1734 void UIChooserModel::prepareLookup()1735 {1736 m_pLookupTimer = new QTimer(this);1737 if (m_pLookupTimer)1738 {1739 m_pLookupTimer->setInterval(1000);1740 m_pLookupTimer->setSingleShot(true);1741 connect(m_pLookupTimer, SIGNAL(timeout()), this, SLOT(sltEraseLookupTimer()));1742 }1743 }1744 1745 void UIChooserModel::prepareContextMenu()1746 {1747 /* Context menu for global(s): */1748 m_pContextMenuGlobal = new QMenu;1749 if (m_pContextMenuGlobal)1750 {1751 /* Check if Ext Pack is ready, some of actions my depend on it: */1752 CExtPack extPack = vboxGlobal().virtualBox().GetExtensionPackManager().Find(GUI_ExtPackName);1753 const bool fExtPackAccessible = !extPack.isNull() && extPack.GetUsable();1754 1755 #ifdef VBOX_WS_MAC1756 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_About));1757 m_pContextMenuGlobal->addSeparator();1758 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_Preferences));1759 m_pContextMenuGlobal->addSeparator();1760 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ImportAppliance));1761 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ExportAppliance));1762 # ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI1763 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowExtraDataManager));1764 # endif1765 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowVirtualMediumManager));1766 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowHostNetworkManager));1767 if (fExtPackAccessible)1768 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowCloudProfileManager));1769 1770 #else /* !VBOX_WS_MAC */1771 1772 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_Preferences));1773 m_pContextMenuGlobal->addSeparator();1774 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ImportAppliance));1775 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ExportAppliance));1776 m_pContextMenuGlobal->addSeparator();1777 # ifdef VBOX_GUI_WITH_EXTRADATA_MANAGER_UI1778 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowExtraDataManager));1779 # endif1780 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowVirtualMediumManager));1781 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowHostNetworkManager));1782 if (fExtPackAccessible)1783 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndexST_M_File_S_ShowCloudProfileManager));1784 # ifdef VBOX_GUI_WITH_NETWORK_MANAGER1785 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_NetworkAccessManager));1786 if (gEDataManager->applicationUpdateEnabled())1787 m_pContextMenuGlobal->addAction(actionPool()->action(UIActionIndex_M_Application_S_CheckForUpdates));1788 # endif1789 #endif /* !VBOX_WS_MAC */1790 }1791 1792 /* Context menu for group(s): */1793 m_pContextMenuGroup = new QMenu;1794 if (m_pContextMenuGroup)1795 {1796 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_New));1797 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_Add));1798 m_pContextMenuGroup->addSeparator();1799 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_Rename));1800 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_Remove));1801 m_pContextMenuGroup->addSeparator();1802 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_M_StartOrShow));1803 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_T_Pause));1804 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_Reset));1805 m_pContextMenuGroup->addMenu(actionPool()->action(UIActionIndexST_M_Group_M_Close)->menu());1806 m_pContextMenuGroup->addSeparator();1807 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_Discard));1808 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_ShowLogDialog));1809 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_Refresh));1810 m_pContextMenuGroup->addSeparator();1811 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_ShowInFileManager));1812 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_CreateShortcut));1813 m_pContextMenuGroup->addSeparator();1814 m_pContextMenuGroup->addAction(actionPool()->action(UIActionIndexST_M_Group_S_Sort));1815 }1816 1817 /* Context menu for machine(s): */1818 m_pContextMenuMachine = new QMenu;1819 if (m_pContextMenuMachine)1820 {1821 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Settings));1822 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Clone));1823 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Move));1824 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_ExportToOCI));1825 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Remove));1826 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_AddGroup));1827 m_pContextMenuMachine->addSeparator();1828 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_M_StartOrShow));1829 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_T_Pause));1830 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Reset));1831 m_pContextMenuMachine->addMenu(actionPool()->action(UIActionIndexST_M_Machine_M_Close)->menu());1832 m_pContextMenuMachine->addSeparator();1833 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Discard));1834 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_ShowLogDialog));1835 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Refresh));1836 m_pContextMenuMachine->addSeparator();1837 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_ShowInFileManager));1838 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_CreateShortcut));1839 m_pContextMenuMachine->addSeparator();1840 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_SortParent));1841 m_pContextMenuMachine->addAction(actionPool()->action(UIActionIndexST_M_Machine_S_Search));1842 }1843 }1844 1845 void UIChooserModel::prepareHandlers()1846 {1847 m_pMouseHandler = new UIChooserHandlerMouse(this);1848 m_pKeyboardHandler = new UIChooserHandlerKeyboard(this);1849 }1850 1851 void UIChooserModel::prepareConnections()1852 {1853 /* Setup parent connections: */1854 connect(this, SIGNAL(sigSelectionChanged()),1855 parent(), SIGNAL(sigSelectionChanged()));1856 connect(this, SIGNAL(sigSelectionInvalidated()),1857 parent(), SIGNAL(sigSelectionInvalidated()));1858 connect(this, SIGNAL(sigToggleStarted()),1859 parent(), SIGNAL(sigToggleStarted()));1860 connect(this, SIGNAL(sigToggleFinished()),1861 parent(), SIGNAL(sigToggleFinished()));1862 1863 /* Setup action connections: */1864 connect(actionPool()->action(UIActionIndexST_M_Welcome_S_New), SIGNAL(triggered()),1865 this, SLOT(sltCreateNewMachine()));1866 connect(actionPool()->action(UIActionIndexST_M_Group_S_New), SIGNAL(triggered()),1867 this, SLOT(sltCreateNewMachine()));1868 connect(actionPool()->action(UIActionIndexST_M_Machine_S_New), SIGNAL(triggered()),1869 this, SLOT(sltCreateNewMachine()));1870 connect(actionPool()->action(UIActionIndexST_M_Group_S_Rename), SIGNAL(triggered()),1871 this, SLOT(sltEditGroupName()));1872 connect(actionPool()->action(UIActionIndexST_M_Group_S_Remove), SIGNAL(triggered()),1873 this, SLOT(sltUngroupSelectedGroup()));1874 connect(actionPool()->action(UIActionIndexST_M_Machine_S_Remove), SIGNAL(triggered()),1875 this, SLOT(sltRemoveSelectedMachine()));1876 connect(actionPool()->action(UIActionIndexST_M_Machine_S_AddGroup), SIGNAL(triggered()),1877 this, SLOT(sltGroupSelectedMachines()));1878 connect(actionPool()->action(UIActionIndexST_M_Group_S_Refresh), SIGNAL(triggered()),1879 this, SLOT(sltPerformRefreshAction()));1880 connect(actionPool()->action(UIActionIndexST_M_Machine_S_Refresh), SIGNAL(triggered()),1881 this, SLOT(sltPerformRefreshAction()));1882 connect(actionPool()->action(UIActionIndexST_M_Machine_S_SortParent), SIGNAL(triggered()),1883 this, SLOT(sltSortParentGroup()));1884 connect(actionPool()->action(UIActionIndexST_M_Group_S_Sort), SIGNAL(triggered()),1885 this, SLOT(sltSortGroup()));1886 connect(actionPool()->action(UIActionIndexST_M_Machine_S_Search), SIGNAL(triggered()),1887 this, SLOT(sltShowHideSearchWidget()));1888 }1889 1890 void UIChooserModel::loadLastSelectedItem()1891 {1892 /* Load last selected item (choose first if unable to load): */1893 setCurrentItem(gEDataManager->selectorWindowLastItemChosen());1894 if (!currentItem() && !navigationList().isEmpty())1895 setCurrentItem(navigationList().first());1896 }1897 1898 void UIChooserModel::saveLastSelectedItem()1899 {1900 /* Save last selected item: */1901 gEDataManager->setSelectorWindowLastItemChosen(currentItem() ? currentItem()->definition() : QString());1902 }1903 1904 void UIChooserModel::cleanupHandlers()1905 {1906 delete m_pKeyboardHandler;1907 m_pKeyboardHandler = 0;1908 delete m_pMouseHandler;1909 m_pMouseHandler = 0;1910 }1911 1912 void UIChooserModel::cleanupContextMenu()1913 {1914 delete m_pContextMenuGlobal;1915 m_pContextMenuGlobal = 0;1916 delete m_pContextMenuGroup;1917 m_pContextMenuGroup = 0;1918 delete m_pContextMenuMachine;1919 m_pContextMenuMachine = 0;1920 }1921 1922 void UIChooserModel::cleanupLookup()1923 {1924 delete m_pLookupTimer;1925 m_pLookupTimer = 0;1926 }1927 1928 void UIChooserModel::cleanupScene()1929 {1930 delete m_pScene;1931 m_pScene = 0;1932 }1933 1934 void UIChooserModel::cleanup()1935 {1936 cleanupHandlers();1937 cleanupContextMenu();1938 cleanupLookup();1939 cleanupScene();1940 }1941 1942 bool UIChooserModel::processContextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent)1943 {1944 /* Whats the reason? */1945 switch (pEvent->reason())1946 {1947 case QGraphicsSceneContextMenuEvent::Mouse:1948 {1949 /* First of all we should look for an item under cursor: */1950 if (QGraphicsItem *pItem = itemAt(pEvent->scenePos()))1951 {1952 /* If this item of known type? */1953 switch (pItem->type())1954 {1955 case UIChooserItemType_Global:1956 {1957 /* Global context menu for global item cases: */1958 popupContextMenu(UIGraphicsSelectorContextMenuType_Global, pEvent->screenPos());1959 return true;1960 }1961 case UIChooserItemType_Group:1962 {1963 /* Get group-item: */1964 UIChooserItem *pGroupItem = qgraphicsitem_cast<UIChooserItemGroup*>(pItem);1965 /* Make sure thats not root: */1966 if (pGroupItem->isRoot())1967 return false;1968 /* Is this group-item only the one selected? */1969 if (currentItems().contains(pGroupItem) && currentItems().size() == 1)1970 {1971 /* Group context menu in that case: */1972 popupContextMenu(UIGraphicsSelectorContextMenuType_Group, pEvent->screenPos());1973 return true;1974 }1975 }1976 RT_FALL_THRU();1977 case UIChooserItemType_Machine:1978 {1979 /* Machine context menu for other Group/Machine cases: */1980 popupContextMenu(UIGraphicsSelectorContextMenuType_Machine, pEvent->screenPos());1981 return true;1982 }1983 default:1984 break;1985 }1986 }1987 return true;1988 }1989 case QGraphicsSceneContextMenuEvent::Keyboard:1990 {1991 /* Get first selected item: */1992 if (UIChooserItem *pItem = currentItem())1993 {1994 /* If this item of known type? */1995 switch (pItem->type())1996 {1997 case UIChooserItemType_Global:1998 {1999 /* Global context menu for global item cases: */2000 popupContextMenu(UIGraphicsSelectorContextMenuType_Machine, pEvent->screenPos());2001 return true;2002 }2003 case UIChooserItemType_Group:2004 {2005 /* Is this group-item only the one selected? */2006 if (currentItems().size() == 1)2007 {2008 /* Group context menu in that case: */2009 popupContextMenu(UIGraphicsSelectorContextMenuType_Group, pEvent->screenPos());2010 return true;2011 }2012 }2013 RT_FALL_THRU();2014 case UIChooserItemType_Machine:2015 {2016 /* Machine context menu for other Group/Machine cases: */2017 popupContextMenu(UIGraphicsSelectorContextMenuType_Machine, pEvent->screenPos());2018 return true;2019 }2020 default:2021 break;2022 }2023 }2024 return true;2025 }2026 default:2027 break;2028 }2029 /* Pass others context menu events: */2030 return false;2031 }2032 2033 void UIChooserModel::popupContextMenu(UIGraphicsSelectorContextMenuType enmType, QPoint point)2034 {2035 /* Which type of context-menu requested? */2036 switch (enmType)2037 {2038 /* For global item? */2039 case UIGraphicsSelectorContextMenuType_Global:2040 {2041 m_pContextMenuGlobal->exec(point);2042 break;2043 }2044 /* For group? */2045 case UIGraphicsSelectorContextMenuType_Group:2046 {2047 m_pContextMenuGroup->exec(point);2048 break;2049 }2050 /* For machine(s)? */2051 case UIGraphicsSelectorContextMenuType_Machine:2052 {2053 m_pContextMenuMachine->exec(point);2054 break;2055 }2056 }2057 }2058 2059 void UIChooserModel::clearRealFocus()2060 {2061 /* Set the real focus to null: */2062 scene()->setFocusItem(0);2063 }2064 2065 QList<UIChooserItem*> UIChooserModel::createNavigationList(UIChooserItem *pItem)2066 {2067 /* Prepare navigation list: */2068 QList<UIChooserItem*> navigationItems;2069 2070 /* Iterate over all the global-items: */2071 foreach (UIChooserItem *pGlobalItem, pItem->items(UIChooserItemType_Global))2072 navigationItems << pGlobalItem;2073 /* Iterate over all the group-items: */2074 foreach (UIChooserItem *pGroupItem, pItem->items(UIChooserItemType_Group))2075 {2076 navigationItems << pGroupItem;2077 if (pGroupItem->toGroupItem()->isOpened())2078 navigationItems << createNavigationList(pGroupItem);2079 }2080 /* Iterate over all the machine-items: */2081 foreach (UIChooserItem *pMachineItem, pItem->items(UIChooserItemType_Machine))2082 navigationItems << pMachineItem;2083 2084 /* Return navigation list: */2085 return navigationItems;2086 }2087 2088 void UIChooserModel::buildTreeForMainRoot()2089 {2090 /* Cleanup previous tree if exists: */2091 delete m_pRoot;2092 m_pRoot = 0;2093 2094 /* Build whole tree for invisible root item: */2095 m_pRoot = new UIChooserItemGroup(scene(), invisibleRoot()->toGroupNode());2096 2097 /* Install root as event-filter for scene view,2098 * we need QEvent::Scroll events from it: */2099 root()->installEventFilterHelper(view());2100 }2101 2102 void UIChooserModel::removeItems(const QList<UIChooserItem*> &itemsToRemove)2103 {2104 /* Confirm machine-items removal: */2105 QStringList names;2106 foreach (UIChooserItem *pItem, itemsToRemove)2107 names << pItem->name();2108 if (!msgCenter().confirmMachineItemRemoval(names))2109 return;2110 2111 /* Remove all the passed nodes: */2112 foreach (UIChooserItem *pItem, itemsToRemove)2113 delete pItem->node();2114 2115 /* And update model: */2116 wipeOutEmptyGroups();2117 updateNavigation();2118 updateLayout();2119 if (!navigationList().isEmpty())2120 setCurrentItem(navigationList().first());2121 else2122 unsetCurrentItems();2123 saveGroupSettings();2124 }2125 2126 void UIChooserModel::unregisterMachines(const QList<QUuid> &ids)2127 {2128 /* Populate machine list: */2129 QList<CMachine> machines;2130 CVirtualBox vbox = vboxGlobal().virtualBox();2131 foreach (const QUuid &uId, ids)2132 {2133 CMachine machine = vbox.FindMachine(uId.toString());2134 if (!machine.isNull())2135 machines << machine;2136 }2137 2138 /* Confirm machine removal: */2139 int iResultCode = msgCenter().confirmMachineRemoval(machines);2140 if (iResultCode == AlertButton_Cancel)2141 return;2142 2143 /* Change selection to some close by item: */2144 setCurrentItem(findClosestUnselectedItem());2145 2146 /* For every selected item: */2147 for (int iMachineIndex = 0; iMachineIndex < machines.size(); ++iMachineIndex)2148 {2149 /* Get iterated machine: */2150 CMachine &machine = machines[iMachineIndex];2151 if (iResultCode == AlertButton_Choice1)2152 {2153 /* Unregister machine first: */2154 CMediumVector media = machine.Unregister(KCleanupMode_DetachAllReturnHardDisksOnly);2155 if (!machine.isOk())2156 {2157 msgCenter().cannotRemoveMachine(machine);2158 continue;2159 }2160 /* Prepare cleanup progress: */2161 CProgress progress = machine.DeleteConfig(media);2162 if (!machine.isOk())2163 {2164 msgCenter().cannotRemoveMachine(machine);2165 continue;2166 }2167 /* And show cleanup progress finally: */2168 msgCenter().showModalProgressDialog(progress, machine.GetName(), ":/progress_delete_90px.png");2169 if (!progress.isOk() || progress.GetResultCode() != 0)2170 {2171 msgCenter().cannotRemoveMachine(machine, progress);2172 continue;2173 }2174 }2175 else if (iResultCode == AlertButton_Choice2 || iResultCode == AlertButton_Ok)2176 {2177 /* Unregister machine first: */2178 CMediumVector media = machine.Unregister(KCleanupMode_DetachAllReturnHardDisksOnly);2179 if (!machine.isOk())2180 {2181 msgCenter().cannotRemoveMachine(machine);2182 continue;2183 }2184 /* Finally close all media, deliberately ignoring errors: */2185 foreach (CMedium medium, media)2186 {2187 if (!medium.isNull())2188 medium.Close();2189 }2190 }2191 }2192 }2193 2194 bool UIChooserModel::processDragMoveEvent(QGraphicsSceneDragDropEvent *pEvent)2195 {2196 /* Do we scrolling already? */2197 if (m_fIsScrollingInProgress)2198 return false;2199 2200 /* Get view: */2201 QGraphicsView *pView = view();2202 2203 /* Check scroll-area: */2204 const QPoint eventPoint = pView->mapFromGlobal(pEvent->screenPos());2205 if ((eventPoint.y() < m_iScrollingTokenSize) ||2206 (eventPoint.y() > pView->height() - m_iScrollingTokenSize))2207 {2208 /* Set scrolling in progress: */2209 m_fIsScrollingInProgress = true;2210 /* Start scrolling: */2211 QTimer::singleShot(200, this, SLOT(sltStartScrolling()));2212 }2213 2214 /* Pass event: */2215 return false;2216 }2217 2218 bool UIChooserModel::processDragLeaveEvent(QGraphicsSceneDragDropEvent *pEvent)2219 {2220 /* Event object is not required here: */2221 Q_UNUSED(pEvent);2222 2223 /* Make sure to stop scrolling as drag-leave event happened: */2224 if (m_fIsScrollingInProgress)2225 m_fIsScrollingInProgress = false;2226 2227 /* Pass event: */2228 return false;2229 }2230 2231 void UIChooserModel::sortNodes(UIChooserNode *pNode)2232 {2233 /* Sort nodes: */2234 pNode->sortNodes();2235 2236 /* Rebuild tree for main root: */2237 buildTreeForMainRoot();2238 updateNavigation();2239 updateLayout();2240 }2241 2242 2243 /*********************************************************************************************************************************2244 610 * Class UIThreadGroupDefinitionSave implementation. * 2245 611 *********************************************************************************************************************************/ -
trunk/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserAbstractModel.h
r77890 r77891 1 1 /* $Id$ */ 2 2 /** @file 3 * VBox Qt GUI - UIChooser Model class declaration.3 * VBox Qt GUI - UIChooserAbstractModel class declaration. 4 4 */ 5 5 … … 16 16 */ 17 17 18 #ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooser Model_h19 #define FEQT_INCLUDED_SRC_manager_chooser_UIChooser Model_h18 #ifndef FEQT_INCLUDED_SRC_manager_chooser_UIChooserAbstractModel_h 19 #define FEQT_INCLUDED_SRC_manager_chooser_UIChooserAbstractModel_h 20 20 #ifndef RT_WITHOUT_PRAGMA_ONCE 21 21 # pragma once … … 23 23 24 24 /* Qt includes: */ 25 #include <QPointer>26 25 #include <QThread> 27 #include <QUuid>28 26 29 27 /* GUI includes: */ 30 28 #include "UIChooserDefs.h" 31 #include "UIExtraDataDefs.h"32 29 33 30 /* COM includes: */ … … 35 32 36 33 /* Forward declaration: */ 37 class QDrag; 38 class UIActionPool; 34 class QUuid; 39 35 class UIChooser; 40 class UIChooserHandlerMouse;41 class UIChooserHandlerKeyboard;42 class UIChooserItem;43 36 class UIChooserNode; 44 class UIChooserView;45 class UIVirtualMachineItem;46 37 class CMachine; 47 48 49 /** Context-menu types. */50 enum UIGraphicsSelectorContextMenuType51 {52 UIGraphicsSelectorContextMenuType_Global,53 UIGraphicsSelectorContextMenuType_Group,54 UIGraphicsSelectorContextMenuType_Machine55 };56 38 57 39 … … 215 197 216 198 217 /** UIChooserAbstractModel extension used as VM Chooser-pane model.218 * This class is used to operate on tree of visible tree items219 * representing VMs and their groups. */220 class UIChooserModel : public UIChooserAbstractModel221 {222 Q_OBJECT;223 224 signals:225 226 /** @name General stuff.227 * @{ */228 /** Notify listeners about tool menu popup request for certain @a enmClass and @a position. */229 void sigToolMenuRequested(UIToolClass enmClass, const QPoint &position);230 /** @} */231 232 /** @name Selection stuff.233 * @{ */234 /** Notifies about selection changed. */235 void sigSelectionChanged();236 /** Notifies about selection invalidated. */237 void sigSelectionInvalidated();238 239 /** Notifies about group toggling started. */240 void sigToggleStarted();241 /** Notifies about group toggling finished. */242 void sigToggleFinished();243 /** @} */244 245 /** @name Layout stuff.246 * @{ */247 /** Notifies about root item minimum width @a iHint changed. */248 void sigRootItemMinimumWidthHintChanged(int iHint);249 /** @} */250 251 public:252 253 /** Constructs Chooser-model passing @a pParent to the base-class. */254 UIChooserModel(UIChooser *pParent);255 /** Destructs Chooser-model. */256 virtual ~UIChooserModel() /* override */;257 258 /** @name General stuff.259 * @{ */260 /** Inits model. */261 void init();262 /** Deinits model. */263 void deinit();264 265 /** Returns the Chooser reference. */266 UIChooser *chooser() const;267 /** Returns the action-pool reference. */268 UIActionPool *actionPool() const;269 /** Returns the scene reference. */270 QGraphicsScene *scene() const;271 /** Returns the paint device reference. */272 QPaintDevice *paintDevice() const;273 274 /** Returns item at @a position, taking into account possible @a deviceTransform. */275 QGraphicsItem *itemAt(const QPointF &position, const QTransform &deviceTransform = QTransform()) const;276 277 /** Handles tool button click for certain @a pItem. */278 void handleToolButtonClick(UIChooserItem *pItem);279 /** Handles pin button click for certain @a pItem. */280 void handlePinButtonClick(UIChooserItem *pItem);281 /** @} */282 283 /** @name Selection stuff.284 * @{ */285 /** Sets a list of current @a items. */286 void setCurrentItems(const QList<UIChooserItem*> &items);287 /** Defines current @a pItem. */288 void setCurrentItem(UIChooserItem *pItem);289 /** Defines current item by @a definition. */290 void setCurrentItem(const QString &strDefinition);291 /** Unsets all current items. */292 void unsetCurrentItems();293 294 /** Adds @a pItem to list of current. */295 void addToCurrentItems(UIChooserItem *pItem);296 /** Removes @a pItem from list of current. */297 void removeFromCurrentItems(UIChooserItem *pItem);298 299 /** Returns current item. */300 UIChooserItem *currentItem() const;301 /** Returns a list of current items. */302 const QList<UIChooserItem*> ¤tItems() const;303 304 /** Returns current machine item. */305 UIVirtualMachineItem *currentMachineItem() const;306 /** Returns a list of current machine items. */307 QList<UIVirtualMachineItem*> currentMachineItems() const;308 309 /** Returns whether group item is selected. */310 bool isGroupItemSelected() const;311 /** Returns whether global item is selected. */312 bool isGlobalItemSelected() const;313 /** Returns whether machine item is selected. */314 bool isMachineItemSelected() const;315 316 /** Returns whether single group is selected. */317 bool isSingleGroupSelected() const;318 /** Returns whether all machine items of one group is selected. */319 bool isAllItemsOfOneGroupSelected() const;320 321 /** Finds closest non-selected item. */322 UIChooserItem *findClosestUnselectedItem() const;323 324 /** Makes sure some item is selected. */325 void makeSureSomeItemIsSelected();326 327 /** Defines focus @a pItem. */328 void setFocusItem(UIChooserItem *pItem);329 /** Returns focus item. */330 UIChooserItem *focusItem() const;331 /** @} */332 333 /** @name Navigation stuff.334 * @{ */335 /** Returns navigation item list. */336 const QList<UIChooserItem*> &navigationList() const;337 /** Removes @a pItem from navigation list. */338 void removeFromNavigationList(UIChooserItem *pItem);339 /** Updates navigation list. */340 void updateNavigation();341 /** @} */342 343 /** @name Virtual Machine/Group search stuff.344 * @{ */345 /** Performs a search starting from the m_pInvisibleRootNode. */346 void performSearch(const QString &strSearchTerm, int iItemSearchFlags);347 /** Clean the search result data members and disables item's visual effects. Also returns a list of348 * all nodes which may be utilized by the calling code. */349 QList<UIChooserNode*> resetSearch();350 /** Scrolls to next/prev (wrt. @a fIsNext) search result. */351 void scrollToSearchResult(bool fIsNext);352 /** Shows/hides machine search widget. */353 void setSearchWidgetVisible(bool fVisible);354 /** @} */355 356 /** @name Children stuff.357 * @{ */358 /** Returns the root instance. */359 UIChooserItem *root() const;360 361 /** Starts editing group name. */362 void startEditingGroupItemName();363 364 /** Activates machine item. */365 void activateMachineItem();366 367 /** Defines current @a pDragObject. */368 void setCurrentDragObject(QDrag *pDragObject);369 370 /** Looks for item with certain @a strLookupSymbol. */371 void lookFor(const QString &strLookupSymbol);372 /** Returns whether looking is in progress. */373 bool isLookupInProgress() const;374 /** @} */375 376 /** @name Layout stuff.377 * @{ */378 /** Updates layout. */379 void updateLayout();380 381 /** Defines global item height @a iHint. */382 void setGlobalItemHeightHint(int iHint);383 /** @} */384 385 public slots:386 387 /** @name General stuff.388 * @{ */389 /** Handles Chooser-view resize. */390 void sltHandleViewResized();391 /** @} */392 393 protected:394 395 /** @name Event handling stuff.396 * @{ */397 /** Preprocesses Qt @a pEvent for passed @a pObject. */398 virtual bool eventFilter(QObject *pObject, QEvent *pEvent) /* override */;399 /** @} */400 401 protected slots:402 403 /** @name Main event handling stuff.404 * @{ */405 /** Handles machine registering/unregistering for machine with certain @a uId. */406 virtual void sltMachineRegistered(const QUuid &uId, const bool fRegistered) /* override */;407 /** @} */408 409 /** @name Children stuff.410 * @{ */411 /** Handles reload machine with certain @a uId request. */412 virtual void sltReloadMachine(const QUuid &uId) /* override */;413 /** @} */414 415 private slots:416 417 /** @name Selection stuff.418 * @{ */419 /** Handles focus item destruction. */420 void sltFocusItemDestroyed();421 /** @} */422 423 /** @name Children stuff.424 * @{ */425 /** Handles group rename request. */426 void sltEditGroupName();427 /** Handles group sort request. */428 void sltSortGroup();429 /** Handles machine search widget show/hide request. */430 void sltShowHideSearchWidget();431 /** Handles group destroy request. */432 void sltUngroupSelectedGroup();433 434 /** Handles create new machine request. */435 void sltCreateNewMachine();436 /** Handles group selected machines request. */437 void sltGroupSelectedMachines();438 /** Handles sort parent group request. */439 void sltSortParentGroup();440 /** Handles refresh request. */441 void sltPerformRefreshAction();442 /** Handles remove selected machine request. */443 void sltRemoveSelectedMachine();444 445 /** Handles D&D scrolling. */446 void sltStartScrolling();447 /** Handles D&D object destruction. */448 void sltCurrentDragObjectDestroyed();449 450 /** Handles request to erase lookup timer. */451 void sltEraseLookupTimer();452 /** @} */453 454 private:455 456 /** @name Prepare/Cleanup cascade.457 * @{ */458 /** Prepares all. */459 void prepare();460 /** Prepares scene. */461 void prepareScene();462 /** Prepares lookup. */463 void prepareLookup();464 /** Prepares context-menu. */465 void prepareContextMenu();466 /** Prepares handlers. */467 void prepareHandlers();468 /** Prepares connections. */469 void prepareConnections();470 /** Loads last selected items. */471 void loadLastSelectedItem();472 473 /** Saves last selected items. */474 void saveLastSelectedItem();475 /** Cleanups connections. */476 void cleanupHandlers();477 /** Cleanups context-menu. */478 void cleanupContextMenu();479 /** Cleanups lookup. */480 void cleanupLookup();481 /** Cleanups scene. */482 void cleanupScene();483 /** Cleanups all. */484 void cleanup();485 /** @} */486 487 /** @name General stuff.488 * @{ */489 /** Handles context-menu @a pEvent. */490 bool processContextMenuEvent(QGraphicsSceneContextMenuEvent *pEvent);491 /** Popups context-menu of certain @a enmType in specified @a point. */492 void popupContextMenu(UIGraphicsSelectorContextMenuType enmType, QPoint point);493 /** Returns the reference of the first view of the scene(). */494 UIChooserView *view();495 /** @} */496 497 /** @name Selection stuff.498 * @{ */499 /** Clears real focus. */500 void clearRealFocus();501 /** @} */502 503 /** @name Navigation stuff.504 * @{ */505 /** Creates navigation list for passed root @a pItem. */506 QList<UIChooserItem*> createNavigationList(UIChooserItem *pItem);507 /** @} */508 509 /** @name Children stuff.510 * @{ */511 /** Build tree for main root. */512 void buildTreeForMainRoot();513 514 /** Removes machine @a items. */515 void removeItems(const QList<UIChooserItem*> &items);516 /** Unregisters virtual machines using list of @a ids. */517 void unregisterMachines(const QList<QUuid> &ids);518 519 /** Processes drag move @a pEvent. */520 bool processDragMoveEvent(QGraphicsSceneDragDropEvent *pEvent);521 /** Processes drag leave @a pEvent. */522 bool processDragLeaveEvent(QGraphicsSceneDragDropEvent *pEvent);523 524 /** Performs sorting for @a pNode. */525 void sortNodes(UIChooserNode *pNode);526 /** @} */527 528 /** @name General stuff.529 * @{ */530 /** Holds the Chooser reference. */531 UIChooser *m_pChooser;532 533 /** Holds the scene reference. */534 QGraphicsScene *m_pScene;535 536 /** Holds the mouse handler instance. */537 UIChooserHandlerMouse *m_pMouseHandler;538 /** Holds the keyboard handler instance. */539 UIChooserHandlerKeyboard *m_pKeyboardHandler;540 541 /** Holds the global item context menu instance. */542 QMenu *m_pContextMenuGlobal;543 /** Holds the group item context menu instance. */544 QMenu *m_pContextMenuGroup;545 /** Holds the machine item context menu instance. */546 QMenu *m_pContextMenuMachine;547 /** @} */548 549 /** @name Selection stuff.550 * @{ */551 /** Holds the focus item reference. */552 QPointer<UIChooserItem> m_pFocusItem;553 /** @} */554 555 /** @name Virtual Machine/Group search stuff.556 * @{ */557 /** Stores the results of the current search. */558 QList<UIChooserNode*> m_searchResults;559 /** Stores the index (within the m_searchResults) of the currently scrolled item. */560 int m_iCurrentScrolledIndex;561 /** @} */562 563 /** @name Children stuff.564 * @{ */565 /** Holds the root instance. */566 QPointer<UIChooserItem> m_pRoot;567 568 /** Holds the navigation list. */569 QList<UIChooserItem*> m_navigationList;570 QList<UIChooserItem*> m_currentItems;571 572 /** Holds the current drag object instance. */573 QPointer<QDrag> m_pCurrentDragObject;574 /** Holds the drag scrolling token size. */575 int m_iScrollingTokenSize;576 /** Holds whether drag scrolling is in progress. */577 bool m_fIsScrollingInProgress;578 579 /** Holds the item lookup timer instance. */580 QTimer *m_pLookupTimer;581 /** Holds the item lookup string. */582 QString m_strLookupString;583 /** @} */584 };585 586 587 199 /** QThread subclass allowing to save group definitions asynchronously. */ 588 200 class UIThreadGroupDefinitionSave : public QThread … … 676 288 677 289 678 #endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooser Model_h */290 #endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserAbstractModel_h */ -
trunk/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.cpp
r77890 r77891 44 44 #include "UIModalWindowManager.h" 45 45 #include "UIVirtualBoxManagerWidget.h" 46 #include "UIVirtualBoxEventHandler.h"47 46 #include "UIWizardNewVM.h" 48 47 … … 54 53 typedef QSet<QString> UIStringSet; 55 54 56 57 /*********************************************************************************************************************************58 * Class UIChooserAbstractModel implementation. *59 *********************************************************************************************************************************/60 61 UIChooserAbstractModel:: UIChooserAbstractModel(UIChooser *pParent)62 : QObject(pParent)63 , m_pInvisibleRootNode(0)64 {65 prepare();66 }67 68 void UIChooserAbstractModel::init()69 {70 /* Load tree: */71 loadTree();72 }73 74 void UIChooserAbstractModel::deinit()75 {76 /* Currently we are not saving group descriptors77 * (which reflecting group toggle-state) on-the-fly,78 * so, for now we are additionally save group orders79 * when exiting application: */80 saveGroupOrders();81 82 /* Make sure all saving steps complete: */83 makeSureGroupDefinitionsSaveIsFinished();84 makeSureGroupOrdersSaveIsFinished();85 86 /* Delete tree: */87 delete m_pInvisibleRootNode;88 m_pInvisibleRootNode = 0;89 }90 91 UIChooserNode *UIChooserAbstractModel::invisibleRoot() const92 {93 return m_pInvisibleRootNode;94 }95 96 void UIChooserAbstractModel::wipeOutEmptyGroups()97 {98 wipeOutEmptyGroups(invisibleRoot());99 }100 101 /* static */102 QString UIChooserAbstractModel::uniqueGroupName(UIChooserNode *pRoot)103 {104 /* Enumerate all the group names: */105 QStringList groupNames;106 foreach (UIChooserNode *pNode, pRoot->nodes(UIChooserItemType_Group))107 groupNames << pNode->name();108 109 /* Prepare reg-exp: */110 const QString strMinimumName = tr("New group");111 const QString strShortTemplate = strMinimumName;112 const QString strFullTemplate = strShortTemplate + QString(" (\\d+)");113 const QRegExp shortRegExp(strShortTemplate);114 const QRegExp fullRegExp(strFullTemplate);115 116 /* Search for the maximum index: */117 int iMinimumPossibleNumber = 0;118 foreach (const QString &strName, groupNames)119 {120 if (shortRegExp.exactMatch(strName))121 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, 2);122 else if (fullRegExp.exactMatch(strName))123 iMinimumPossibleNumber = qMax(iMinimumPossibleNumber, fullRegExp.cap(1).toInt() + 1);124 }125 126 /* Prepare result: */127 QString strResult = strMinimumName;128 if (iMinimumPossibleNumber)129 strResult += " " + QString::number(iMinimumPossibleNumber);130 return strResult;131 }132 133 void UIChooserAbstractModel::saveGroupSettings()134 {135 emit sigGroupSavingStarted();136 }137 138 bool UIChooserAbstractModel::isGroupSavingInProgress() const139 {140 return UIThreadGroupDefinitionSave::instance()141 || UIThreadGroupOrderSave::instance();142 }143 144 void UIChooserAbstractModel::sltMachineStateChanged(const QUuid &uId, const KMachineState)145 {146 /* Update machine-nodes with passed id: */147 invisibleRoot()->updateAllNodes(uId);148 }149 150 void UIChooserAbstractModel::sltMachineDataChanged(const QUuid &uId)151 {152 /* Update machine-nodes with passed id: */153 invisibleRoot()->updateAllNodes(uId);154 }155 156 void UIChooserAbstractModel::sltMachineRegistered(const QUuid &uId, const bool fRegistered)157 {158 /* Existing VM unregistered? */159 if (!fRegistered)160 {161 /* Remove machine-items with passed id: */162 invisibleRoot()->removeAllNodes(uId);163 /* Wipe out empty groups: */164 wipeOutEmptyGroups();165 }166 /* New VM registered? */167 else168 {169 /* Should we show this VM? */170 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uId))171 {172 /* Add new machine-item: */173 CMachine comMachine = vboxGlobal().virtualBox().FindMachine(uId.toString());174 addMachineIntoTheTree(comMachine, true /* make it visible */);175 }176 }177 }178 179 void UIChooserAbstractModel::sltSessionStateChanged(const QUuid &uId, const KSessionState)180 {181 /* Update machine-nodes with passed id: */182 invisibleRoot()->updateAllNodes(uId);183 }184 185 void UIChooserAbstractModel::sltSnapshotChanged(const QUuid &uId, const QUuid &)186 {187 /* Update machine-nodes with passed id: */188 invisibleRoot()->updateAllNodes(uId);189 }190 191 void UIChooserAbstractModel::sltReloadMachine(const QUuid &uId)192 {193 /* Remove machine-items with passed id: */194 invisibleRoot()->removeAllNodes(uId);195 /* Wipe out empty groups: */196 wipeOutEmptyGroups();197 198 /* Should we show this VM? */199 if (gEDataManager->showMachineInVirtualBoxManagerChooser(uId))200 {201 /* Add new machine-item: */202 CMachine comMachine = vboxGlobal().virtualBox().FindMachine(uId.toString());203 addMachineIntoTheTree(comMachine, true /* make it visible */);204 }205 }206 207 void UIChooserAbstractModel::sltGroupSavingStart()208 {209 saveGroupDefinitions();210 saveGroupOrders();211 }212 213 void UIChooserAbstractModel::sltGroupDefinitionsSaveComplete()214 {215 makeSureGroupDefinitionsSaveIsFinished();216 emit sigGroupSavingStateChanged();217 }218 219 void UIChooserAbstractModel::sltGroupOrdersSaveComplete()220 {221 makeSureGroupOrdersSaveIsFinished();222 emit sigGroupSavingStateChanged();223 }224 225 void UIChooserAbstractModel::prepare()226 {227 prepareConnections();228 }229 230 void UIChooserAbstractModel::prepareConnections()231 {232 /* Setup parent connections: */233 connect(this, SIGNAL(sigGroupSavingStateChanged()),234 parent(), SIGNAL(sigGroupSavingStateChanged()));235 236 /* Setup global connections: */237 connect(gVBoxEvents, SIGNAL(sigMachineStateChange(QUuid, KMachineState)),238 this, SLOT(sltMachineStateChanged(QUuid, KMachineState)));239 connect(gVBoxEvents, SIGNAL(sigMachineDataChange(QUuid)),240 this, SLOT(sltMachineDataChanged(QUuid)));241 connect(gVBoxEvents, SIGNAL(sigMachineRegistered(QUuid, bool)),242 this, SLOT(sltMachineRegistered(QUuid, bool)));243 connect(gVBoxEvents, SIGNAL(sigSessionStateChange(QUuid, KSessionState)),244 this, SLOT(sltSessionStateChanged(QUuid, KSessionState)));245 connect(gVBoxEvents, SIGNAL(sigSnapshotTake(QUuid, QUuid)),246 this, SLOT(sltSnapshotChanged(QUuid, QUuid)));247 connect(gVBoxEvents, SIGNAL(sigSnapshotDelete(QUuid, QUuid)),248 this, SLOT(sltSnapshotChanged(QUuid, QUuid)));249 connect(gVBoxEvents, SIGNAL(sigSnapshotChange(QUuid, QUuid)),250 this, SLOT(sltSnapshotChanged(QUuid, QUuid)));251 connect(gVBoxEvents, SIGNAL(sigSnapshotRestore(QUuid, QUuid)),252 this, SLOT(sltSnapshotChanged(QUuid, QUuid)));253 254 /* Setup group saving connections: */255 connect(this, &UIChooserAbstractModel::sigGroupSavingStarted,256 this, &UIChooserAbstractModel::sltGroupSavingStart,257 Qt::QueuedConnection);258 }259 260 void UIChooserAbstractModel::loadTree()261 {262 /* Create invisible root group node: */263 m_pInvisibleRootNode = new UIChooserNodeGroup(0 /* parent */,264 false /* favorite */,265 0 /* position */,266 QString() /* name */,267 true /* opened */);268 if (invisibleRoot())269 {270 /* Create global node: */271 new UIChooserNodeGlobal(m_pInvisibleRootNode,272 isGlobalNodeFavorite(m_pInvisibleRootNode),273 0 /* position */,274 QString() /* tip */);275 276 /* Add all the approved machine nodes into the tree: */277 LogRelFlow(("UIChooserModel: Loading VMs...\n"));278 foreach (const CMachine &comMachine, vboxGlobal().virtualBox().GetMachines())279 {280 const QUuid uMachineID = comMachine.GetId();281 if (!uMachineID.isNull() && gEDataManager->showMachineInVirtualBoxManagerChooser(uMachineID))282 addMachineIntoTheTree(comMachine);283 }284 LogRelFlow(("UIChooserModel: VMs loaded.\n"));285 }286 }287 288 void UIChooserAbstractModel::addMachineIntoTheTree(const CMachine &comMachine, bool fMakeItVisible /* = false */)289 {290 /* Make sure passed VM is not NULL: */291 if (comMachine.isNull())292 LogRelFlow(("UIChooserModel: ERROR: Passed VM is NULL!\n"));293 AssertReturnVoid(!comMachine.isNull());294 295 /* Which VM we are loading: */296 LogRelFlow(("UIChooserModel: Loading VM with ID={%s}...\n", toOldStyleUuid(comMachine.GetId()).toUtf8().constData()));297 /* Is that machine accessible? */298 if (comMachine.GetAccessible())299 {300 /* VM is accessible: */301 const QString strName = comMachine.GetName();302 LogRelFlow(("UIChooserModel: VM {%s} is accessible.\n", strName.toUtf8().constData()));303 /* Which groups passed machine attached to? */304 const QVector<QString> groups = comMachine.GetGroups();305 const QStringList groupList = groups.toList();306 const QString strGroups = groupList.join(", ");307 LogRelFlow(("UIChooserModel: VM {%s} has groups: {%s}.\n", strName.toUtf8().constData(),308 strGroups.toUtf8().constData()));309 foreach (QString strGroup, groups)310 {311 /* Remove last '/' if any: */312 if (strGroup.right(1) == "/")313 strGroup.truncate(strGroup.size() - 1);314 /* Create machine-item with found group-item as parent: */315 LogRelFlow(("UIChooserModel: Creating item for VM {%s} in group {%s}.\n", strName.toUtf8().constData(),316 strGroup.toUtf8().constData()));317 createMachineNode(getGroupNode(strGroup, invisibleRoot(), fMakeItVisible), comMachine);318 }319 /* Update group definitions: */320 m_groups[toOldStyleUuid(comMachine.GetId())] = groupList;321 }322 /* Inaccessible machine: */323 else324 {325 /* VM is accessible: */326 LogRelFlow(("UIChooserModel: VM {%s} is inaccessible.\n", toOldStyleUuid(comMachine.GetId()).toUtf8().constData()));327 /* Create machine-item with main-root group-item as parent: */328 createMachineNode(invisibleRoot(), comMachine);329 }330 }331 332 UIChooserNode *UIChooserAbstractModel::getGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened)333 {334 /* Check passed stuff: */335 if (pParentNode->name() == strName)336 return pParentNode;337 338 /* Prepare variables: */339 const QString strFirstSubName = strName.section('/', 0, 0);340 const QString strFirstSuffix = strName.section('/', 1, -1);341 const QString strSecondSubName = strFirstSuffix.section('/', 0, 0);342 const QString strSecondSuffix = strFirstSuffix.section('/', 1, -1);343 344 /* Passed group name equal to first sub-name: */345 if (pParentNode->name() == strFirstSubName)346 {347 /* Make sure first-suffix is NOT empty: */348 AssertMsg(!strFirstSuffix.isEmpty(), ("Invalid group name!"));349 /* Trying to get group node among our children: */350 foreach (UIChooserNode *pGroupNode, pParentNode->nodes(UIChooserItemType_Group))351 {352 if (pGroupNode->name() == strSecondSubName)353 {354 UIChooserNode *pFoundNode = getGroupNode(strFirstSuffix, pGroupNode, fAllGroupsOpened);355 if (UIChooserNodeGroup *pFoundGroupNode = pFoundNode->toGroupNode())356 if (fAllGroupsOpened && pFoundGroupNode->isClosed())357 pFoundGroupNode->open();358 return pFoundNode;359 }360 }361 }362 363 /* Found nothing? Creating: */364 UIChooserNodeGroup *pNewGroupNode =365 new UIChooserNodeGroup(pParentNode,366 false /* favorite */,367 getDesiredPosition(pParentNode, UIChooserItemType_Group, strSecondSubName),368 strSecondSubName,369 fAllGroupsOpened || shouldBeGroupOpened(pParentNode, strSecondSubName));370 return strSecondSuffix.isEmpty() ? pNewGroupNode : getGroupNode(strFirstSuffix, pNewGroupNode, fAllGroupsOpened);371 }372 373 bool UIChooserAbstractModel::shouldBeGroupOpened(UIChooserNode *pParentNode, const QString &strName)374 {375 /* Read group definitions: */376 const QStringList definitions = gEDataManager->selectorWindowGroupsDefinitions(pParentNode->fullName());377 /* Return 'false' if no definitions found: */378 if (definitions.isEmpty())379 return false;380 381 /* Prepare required group definition reg-exp: */382 const QString strDefinitionTemplate = QString("g(\\S)*=%1").arg(strName);383 const QRegExp definitionRegExp(strDefinitionTemplate);384 /* For each the group definition: */385 foreach (const QString &strDefinition, definitions)386 {387 /* Check if this is required definition: */388 if (definitionRegExp.indexIn(strDefinition) == 0)389 {390 /* Get group descriptor: */391 const QString strDescriptor(definitionRegExp.cap(1));392 if (strDescriptor.contains('o'))393 return true;394 }395 }396 397 /* Return 'false' by default: */398 return false;399 }400 401 void UIChooserAbstractModel::wipeOutEmptyGroups(UIChooserNode *pParent)402 {403 /* Cleanup all the group-items recursively first: */404 foreach (UIChooserNode *pNode, pParent->nodes(UIChooserItemType_Group))405 wipeOutEmptyGroups(pNode);406 /* If parent has no nodes: */407 if (!pParent->hasNodes())408 {409 /* If that is non-root item: */410 if (!pParent->isRoot())411 {412 /* Delete parent node and item: */413 delete pParent;414 }415 }416 }417 418 bool UIChooserAbstractModel::isGlobalNodeFavorite(UIChooserNode *pParentNode) const419 {420 /* Read group definitions: */421 const QStringList definitions = gEDataManager->selectorWindowGroupsDefinitions(pParentNode->fullName());422 /* Return 'false' if no definitions found: */423 if (definitions.isEmpty())424 return false;425 426 /* Prepare required group definition reg-exp: */427 const QString strDefinitionTemplate = QString("n(\\S)*=GLOBAL");428 const QRegExp definitionRegExp = QRegExp(strDefinitionTemplate);429 /* For each the group definition: */430 foreach (const QString &strDefinition, definitions)431 {432 /* Check if this is required definition: */433 if (definitionRegExp.indexIn(strDefinition) == 0)434 {435 /* Get group descriptor: */436 const QString strDescriptor(definitionRegExp.cap(1));437 if (strDescriptor.contains('f'))438 return true;439 }440 }441 442 /* Return 'false' by default: */443 return false;444 }445 446 int UIChooserAbstractModel::getDesiredPosition(UIChooserNode *pParentNode, UIChooserItemType enmType, const QString &strName)447 {448 /* End of list (by default)? */449 int iNewNodeDesiredPosition = -1;450 /* Which position should be new node placed by definitions: */451 int iNewNodeDefinitionPosition = positionFromDefinitions(pParentNode, enmType, strName);452 /* If some position wanted: */453 if (iNewNodeDefinitionPosition != -1)454 {455 /* Start of list if some definition present: */456 iNewNodeDesiredPosition = 0;457 /* We have to check all the existing node positions: */458 QList<UIChooserNode*> nodes = pParentNode->nodes(enmType);459 for (int i = nodes.size() - 1; i >= 0; --i)460 {461 /* Get current node: */462 UIChooserNode *pNode = nodes[i];463 /* Which position should be current node placed by definitions? */464 QString strDefinitionName = pNode->type() == UIChooserItemType_Group ? pNode->name() :465 pNode->type() == UIChooserItemType_Machine ? toOldStyleUuid(pNode->toMachineNode()->id()) :466 QString();467 AssertMsg(!strDefinitionName.isEmpty(), ("Wrong definition name!"));468 int iNodeDefinitionPosition = positionFromDefinitions(pParentNode, enmType, strDefinitionName);469 /* If some position wanted: */470 if (iNodeDefinitionPosition != -1)471 {472 AssertMsg(iNodeDefinitionPosition != iNewNodeDefinitionPosition, ("Incorrect definitions!"));473 if (iNodeDefinitionPosition < iNewNodeDefinitionPosition)474 {475 iNewNodeDesiredPosition = i + 1;476 break;477 }478 }479 }480 }481 /* Return desired node position: */482 return iNewNodeDesiredPosition;483 }484 485 int UIChooserAbstractModel::positionFromDefinitions(UIChooserNode *pParentNode, UIChooserItemType enmType, const QString &strName)486 {487 /* Read group definitions: */488 const QStringList definitions = gEDataManager->selectorWindowGroupsDefinitions(pParentNode->fullName());489 /* Return 'false' if no definitions found: */490 if (definitions.isEmpty())491 return -1;492 493 /* Prepare definition reg-exp: */494 QString strDefinitionTemplateShort;495 QString strDefinitionTemplateFull;496 switch (enmType)497 {498 case UIChooserItemType_Group:499 strDefinitionTemplateShort = QString("^g(\\S)*=");500 strDefinitionTemplateFull = QString("^g(\\S)*=%1$").arg(strName);501 break;502 case UIChooserItemType_Machine:503 strDefinitionTemplateShort = QString("^m=");504 strDefinitionTemplateFull = QString("^m=%1$").arg(strName);505 break;506 default: return -1;507 }508 QRegExp definitionRegExpShort(strDefinitionTemplateShort);509 QRegExp definitionRegExpFull(strDefinitionTemplateFull);510 511 /* For each the definition: */512 int iDefinitionIndex = -1;513 foreach (const QString &strDefinition, definitions)514 {515 /* Check if this definition is of required type: */516 if (definitionRegExpShort.indexIn(strDefinition) == 0)517 {518 ++iDefinitionIndex;519 /* Check if this definition is exactly what we need: */520 if (definitionRegExpFull.indexIn(strDefinition) == 0)521 return iDefinitionIndex;522 }523 }524 525 /* Return result: */526 return -1;527 }528 529 void UIChooserAbstractModel::createMachineNode(UIChooserNode *pParentNode, const CMachine &comMachine)530 {531 /* Create machine node: */532 new UIChooserNodeMachine(pParentNode,533 false /* favorite */,534 getDesiredPosition(pParentNode, UIChooserItemType_Machine, toOldStyleUuid(comMachine.GetId())),535 comMachine);536 }537 538 void UIChooserAbstractModel::saveGroupDefinitions()539 {540 /* Make sure there is no group save activity: */541 if (UIThreadGroupDefinitionSave::instance())542 return;543 544 /* Prepare full group map: */545 QMap<QString, QStringList> groups;546 gatherGroupDefinitions(groups, invisibleRoot());547 548 /* Save information in other thread: */549 UIThreadGroupDefinitionSave::prepare();550 emit sigGroupSavingStateChanged();551 connect(UIThreadGroupDefinitionSave::instance(), SIGNAL(sigReload(QUuid)),552 this, SLOT(sltReloadMachine(QUuid)));553 UIThreadGroupDefinitionSave::instance()->configure(this, m_groups, groups);554 UIThreadGroupDefinitionSave::instance()->start();555 m_groups = groups;556 }557 558 void UIChooserAbstractModel::saveGroupOrders()559 {560 /* Make sure there is no group save activity: */561 if (UIThreadGroupOrderSave::instance())562 return;563 564 /* Prepare full group map: */565 QMap<QString, QStringList> groups;566 gatherGroupOrders(groups, invisibleRoot());567 568 /* Save information in other thread: */569 UIThreadGroupOrderSave::prepare();570 emit sigGroupSavingStateChanged();571 UIThreadGroupOrderSave::instance()->configure(this, groups);572 UIThreadGroupOrderSave::instance()->start();573 }574 575 void UIChooserAbstractModel::gatherGroupDefinitions(QMap<QString, QStringList> &definitions,576 UIChooserNode *pParentGroup)577 {578 /* Iterate over all the machine-nodes: */579 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserItemType_Machine))580 if (UIChooserNodeMachine *pMachineNode = pNode->toMachineNode())581 if (pMachineNode->accessible())582 definitions[toOldStyleUuid(pMachineNode->id())] << pParentGroup->fullName();583 /* Iterate over all the group-nodes: */584 foreach (UIChooserNode *pNode, pParentGroup->nodes(UIChooserItemType_Group))585 gatherGroupDefinitions(definitions, pNode);586 }587 588 void UIChooserAbstractModel::gatherGroupOrders(QMap<QString, QStringList> &orders,589 UIChooserNode *pParentItem)590 {591 /* Prepare extra-data key for current group: */592 const QString strExtraDataKey = pParentItem->fullName();593 /* Iterate over all the global-nodes: */594 foreach (UIChooserNode *pNode, pParentItem->nodes(UIChooserItemType_Global))595 {596 const QString strGlobalDescriptor(pNode->isFavorite() ? "nf" : "n");597 orders[strExtraDataKey] << QString("%1=GLOBAL").arg(strGlobalDescriptor);598 }599 /* Iterate over all the group-nodes: */600 foreach (UIChooserNode *pNode, pParentItem->nodes(UIChooserItemType_Group))601 {602 const QString strGroupDescriptor(pNode->toGroupNode()->isOpened() ? "go" : "gc");603 orders[strExtraDataKey] << QString("%1=%2").arg(strGroupDescriptor, pNode->name());604 gatherGroupOrders(orders, pNode);605 }606 /* Iterate over all the machine-nodes: */607 foreach (UIChooserNode *pNode, pParentItem->nodes(UIChooserItemType_Machine))608 orders[strExtraDataKey] << QString("m=%1").arg(toOldStyleUuid(pNode->toMachineNode()->id()));609 }610 611 void UIChooserAbstractModel::makeSureGroupDefinitionsSaveIsFinished()612 {613 /* Cleanup if necessary: */614 if (UIThreadGroupDefinitionSave::instance())615 UIThreadGroupDefinitionSave::cleanup();616 }617 618 void UIChooserAbstractModel::makeSureGroupOrdersSaveIsFinished()619 {620 /* Cleanup if necessary: */621 if (UIThreadGroupOrderSave::instance())622 UIThreadGroupOrderSave::cleanup();623 }624 625 /* static */626 QString UIChooserAbstractModel::toOldStyleUuid(const QUuid &uId)627 {628 return uId.toString().remove(QRegExp("[{}]"));629 }630 631 632 /*********************************************************************************************************************************633 * Class UIChooserModel implementation. *634 *********************************************************************************************************************************/635 55 636 56 UIChooserModel:: UIChooserModel(UIChooser *pParent) … … 2239 1659 updateLayout(); 2240 1660 } 2241 2242 2243 /*********************************************************************************************************************************2244 * Class UIThreadGroupDefinitionSave implementation. *2245 *********************************************************************************************************************************/2246 2247 /* static */2248 UIThreadGroupDefinitionSave *UIThreadGroupDefinitionSave::s_pInstance = 0;2249 2250 /* static */2251 UIThreadGroupDefinitionSave *UIThreadGroupDefinitionSave::instance()2252 {2253 return s_pInstance;2254 }2255 2256 /* static */2257 void UIThreadGroupDefinitionSave::prepare()2258 {2259 /* Make sure instance not prepared: */2260 if (s_pInstance)2261 return;2262 2263 /* Crate instance: */2264 new UIThreadGroupDefinitionSave;2265 }2266 2267 /* static */2268 void UIThreadGroupDefinitionSave::cleanup()2269 {2270 /* Make sure instance prepared: */2271 if (!s_pInstance)2272 return;2273 2274 /* Crate instance: */2275 delete s_pInstance;2276 }2277 2278 void UIThreadGroupDefinitionSave::configure(QObject *pParent,2279 const QMap<QString, QStringList> &oldLists,2280 const QMap<QString, QStringList> &newLists)2281 {2282 m_oldLists = oldLists;2283 m_newLists = newLists;2284 connect(this, SIGNAL(sigComplete()), pParent, SLOT(sltGroupDefinitionsSaveComplete()));2285 }2286 2287 UIThreadGroupDefinitionSave::UIThreadGroupDefinitionSave()2288 {2289 /* Assign instance: */2290 s_pInstance = this;2291 }2292 2293 UIThreadGroupDefinitionSave::~UIThreadGroupDefinitionSave()2294 {2295 /* Wait: */2296 wait();2297 2298 /* Erase instance: */2299 s_pInstance = 0;2300 }2301 2302 void UIThreadGroupDefinitionSave::run()2303 {2304 /* COM prepare: */2305 COMBase::InitializeCOM(false);2306 2307 /* For every particular machine ID: */2308 foreach (const QString &strId, m_newLists.keys())2309 {2310 /* Get new group list/set: */2311 const QStringList &newGroupList = m_newLists.value(strId);2312 const UIStringSet &newGroupSet = UIStringSet::fromList(newGroupList);2313 /* Get old group list/set: */2314 const QStringList &oldGroupList = m_oldLists.value(strId);2315 const UIStringSet &oldGroupSet = UIStringSet::fromList(oldGroupList);2316 /* Make sure group set changed: */2317 if (newGroupSet == oldGroupSet)2318 continue;2319 2320 /* The next steps are subsequent.2321 * Every of them is mandatory in order to continue2322 * with common cleanup in case of failure.2323 * We have to simulate a try-catch block. */2324 CSession session;2325 CMachine machine;2326 do2327 {2328 /* 1. Open session: */2329 session = vboxGlobal().openSession(QUuid(strId));2330 if (session.isNull())2331 break;2332 2333 /* 2. Get session machine: */2334 machine = session.GetMachine();2335 if (machine.isNull())2336 break;2337 2338 /* 3. Set new groups: */2339 machine.SetGroups(newGroupList.toVector());2340 if (!machine.isOk())2341 {2342 msgCenter().cannotSetGroups(machine);2343 break;2344 }2345 2346 /* 4. Save settings: */2347 machine.SaveSettings();2348 if (!machine.isOk())2349 {2350 msgCenter().cannotSaveMachineSettings(machine);2351 break;2352 }2353 } while (0);2354 2355 /* Cleanup if necessary: */2356 if (machine.isNull() || !machine.isOk())2357 emit sigReload(QUuid(strId));2358 if (!session.isNull())2359 session.UnlockMachine();2360 }2361 2362 /* Notify listeners about completeness: */2363 emit sigComplete();2364 2365 /* COM cleanup: */2366 COMBase::CleanupCOM();2367 }2368 2369 2370 /*********************************************************************************************************************************2371 * Class UIThreadGroupOrderSave implementation. *2372 *********************************************************************************************************************************/2373 2374 /* static */2375 UIThreadGroupOrderSave *UIThreadGroupOrderSave::s_pInstance = 0;2376 2377 /* static */2378 UIThreadGroupOrderSave *UIThreadGroupOrderSave::instance()2379 {2380 return s_pInstance;2381 }2382 2383 /* static */2384 void UIThreadGroupOrderSave::prepare()2385 {2386 /* Make sure instance not prepared: */2387 if (s_pInstance)2388 return;2389 2390 /* Crate instance: */2391 new UIThreadGroupOrderSave;2392 }2393 2394 /* static */2395 void UIThreadGroupOrderSave::cleanup()2396 {2397 /* Make sure instance prepared: */2398 if (!s_pInstance)2399 return;2400 2401 /* Crate instance: */2402 delete s_pInstance;2403 }2404 2405 void UIThreadGroupOrderSave::configure(QObject *pParent,2406 const QMap<QString, QStringList> &groups)2407 {2408 m_groups = groups;2409 connect(this, SIGNAL(sigComplete()), pParent, SLOT(sltGroupOrdersSaveComplete()));2410 }2411 2412 UIThreadGroupOrderSave::UIThreadGroupOrderSave()2413 {2414 /* Assign instance: */2415 s_pInstance = this;2416 }2417 2418 UIThreadGroupOrderSave::~UIThreadGroupOrderSave()2419 {2420 /* Wait: */2421 wait();2422 2423 /* Erase instance: */2424 s_pInstance = 0;2425 }2426 2427 void UIThreadGroupOrderSave::run()2428 {2429 /* COM prepare: */2430 COMBase::InitializeCOM(false);2431 2432 /* Clear all the extra-data records related to group definitions: */2433 gEDataManager->clearSelectorWindowGroupsDefinitions();2434 /* For every particular group definition: */2435 foreach (const QString &strId, m_groups.keys())2436 gEDataManager->setSelectorWindowGroupsDefinitions(strId, m_groups[strId]);2437 2438 /* Notify listeners about completeness: */2439 emit sigComplete();2440 2441 /* COM cleanup: */2442 COMBase::CleanupCOM();2443 } -
trunk/src/VBox/Frontends/VirtualBox/src/manager/chooser/UIChooserModel.h
r77890 r77891 24 24 /* Qt includes: */ 25 25 #include <QPointer> 26 #include <QThread>27 #include <QUuid>28 26 29 27 /* GUI includes: */ 30 #include "UIChooser Defs.h"28 #include "UIChooserAbstractModel.h" 31 29 #include "UIExtraDataDefs.h" 32 33 /* COM includes: */34 #include "COMEnums.h"35 30 36 31 /* Forward declaration: */ … … 44 39 class UIChooserView; 45 40 class UIVirtualMachineItem; 46 class CMachine;47 41 48 42 … … 53 47 UIGraphicsSelectorContextMenuType_Group, 54 48 UIGraphicsSelectorContextMenuType_Machine 55 };56 57 58 /** QObject extension used as VM Chooser-pane abstract model.59 * This class is used to load/save a tree of abstract invisible60 * nodes representing VMs and their groups from/to extra-data. */61 class UIChooserAbstractModel : public QObject62 {63 Q_OBJECT;64 65 signals:66 67 /** @name Group saving stuff.68 * @{ */69 /** Notifies about group saving started. */70 void sigGroupSavingStarted();71 /** Notifies about group saving state changed. */72 void sigGroupSavingStateChanged();73 /** @} */74 75 public:76 77 /** Constructs abstract Chooser-model passing @a pParent to the base-class. */78 UIChooserAbstractModel(UIChooser *pParent);79 80 /** @name General stuff.81 * @{ */82 /** Inits model. */83 void init();84 /** Deinits model. */85 void deinit();86 /** @} */87 88 /** @name Children stuff.89 * @{ */90 /** Returns invisible root node instance. */91 UIChooserNode *invisibleRoot() const;92 93 /** Wipes out empty groups. */94 void wipeOutEmptyGroups();95 96 /** Generates unique group name traversing recursively starting from @a pRoot. */97 static QString uniqueGroupName(UIChooserNode *pRoot);98 /** @} */99 100 /** @name Group saving stuff.101 * @{ */102 /** Commands to save group settings. */103 void saveGroupSettings();104 /** Returns whether group saving is in progress. */105 bool isGroupSavingInProgress() const;106 /** @} */107 108 protected slots:109 110 /** @name Main event handling stuff.111 * @{ */112 /** Handles machine @a enmState change for machine with certain @a uId. */113 virtual void sltMachineStateChanged(const QUuid &uId, const KMachineState enmState);114 /** Handles machine data change for machine with certain @a uId. */115 virtual void sltMachineDataChanged(const QUuid &uId);116 /** Handles machine registering/unregistering for machine with certain @a uId. */117 virtual void sltMachineRegistered(const QUuid &uId, const bool fRegistered);118 /** Handles session @a enmState change for machine with certain @a uId. */119 virtual void sltSessionStateChanged(const QUuid &uId, const KSessionState enmState);120 /** Handles snapshot change for machine/snapshot with certain @a uId / @a uSnapshotId. */121 virtual void sltSnapshotChanged(const QUuid &uId, const QUuid &uSnapshotId);122 /** @} */123 124 /** @name Children stuff.125 * @{ */126 /** Handles reload machine with certain @a uId request. */127 virtual void sltReloadMachine(const QUuid &uId);128 /** @} */129 130 private slots:131 132 /** @name Group saving stuff.133 * @{ */134 /** Handles request to start group saving. */135 void sltGroupSavingStart();136 /** Handles group definition saving complete. */137 void sltGroupDefinitionsSaveComplete();138 /** Handles group order saving complete. */139 void sltGroupOrdersSaveComplete();140 /** @} */141 142 private:143 144 /** @name Prepare/Cleanup cascade.145 * @{ */146 /** Prepares all. */147 void prepare();148 /** Prepares connections. */149 void prepareConnections();150 /** @} */151 152 /** @name Children stuff.153 * @{ */154 /** Loads tree. */155 void loadTree();156 /** Adds machine item based on certain @a comMachine and optionally @a fMakeItVisible. */157 void addMachineIntoTheTree(const CMachine &comMachine, bool fMakeItVisible = false);158 /** Acquires group node, creates one if necessary.159 * @param strName Brings the name of group we looking for.160 * @param pParentNode Brings the parent we starting to look for a group from.161 * @param fAllGroupsOpened Brings whether we should open all the groups till the required one. */162 UIChooserNode *getGroupNode(const QString &strName, UIChooserNode *pParentNode, bool fAllGroupsOpened);163 /** Returns whether group with certain @a strName should be opened, searching starting from the passed @a pParentItem. */164 bool shouldBeGroupOpened(UIChooserNode *pParentNode, const QString &strName);165 166 /** Wipes out empty groups starting from @a pParentItem. */167 void wipeOutEmptyGroups(UIChooserNode *pParentNode);168 169 /** Returns whether global node within the @a pParentNode is favorite. */170 bool isGlobalNodeFavorite(UIChooserNode *pParentNode) const;171 172 /** Acquires desired position for a child of @a pParentNode with specified @a enmType and @a strName. */173 int getDesiredPosition(UIChooserNode *pParentNode, UIChooserItemType enmType, const QString &strName);174 /** Acquires saved position for a child of @a pParentNode with specified @a enmType and @a strName. */175 int positionFromDefinitions(UIChooserNode *pParentNode, UIChooserItemType enmType, const QString &strName);176 177 /** Creates machine node based on certain @a comMachine as a child of specified @a pParentNode. */178 void createMachineNode(UIChooserNode *pParentNode, const CMachine &comMachine);179 /** @} */180 181 /** @name Group saving stuff.182 * @{ */183 /** Saves group definitions. */184 void saveGroupDefinitions();185 /** Saves group orders. */186 void saveGroupOrders();187 188 /** Gathers group @a definitions of @a pParentGroup. */189 void gatherGroupDefinitions(QMap<QString, QStringList> &definitions, UIChooserNode *pParentGroup);190 /** Gathers group @a orders of @a pParentGroup. */191 void gatherGroupOrders(QMap<QString, QStringList> &orders, UIChooserNode *pParentItem);192 193 /** Makes sure group definitions saving is finished. */194 void makeSureGroupDefinitionsSaveIsFinished();195 /** Makes sure group orders saving is finished. */196 void makeSureGroupOrdersSaveIsFinished();197 198 /** Returns QString representation for passed @a uId, wiping out {} symbols.199 * @note Required for backward compatibility after QString=>QUuid change. */200 static QString toOldStyleUuid(const QUuid &uId);201 /** @} */202 203 /** @name Children stuff.204 * @{ */205 /** Holds the invisible root node instance. */206 UIChooserNode *m_pInvisibleRootNode;207 /** @} */208 209 /** @name Group saving stuff.210 * @{ */211 /** Holds the consolidated map of group definitions/orders. */212 QMap<QString, QStringList> m_groups;213 /** @} */214 49 }; 215 50 … … 585 420 586 421 587 /** QThread subclass allowing to save group definitions asynchronously. */588 class UIThreadGroupDefinitionSave : public QThread589 {590 Q_OBJECT;591 592 signals:593 594 /** Notifies about machine with certain @a uId to be reloaded. */595 void sigReload(const QUuid &uId);596 597 /** Notifies about task is complete. */598 void sigComplete();599 600 public:601 602 /** Returns group saving thread instance. */603 static UIThreadGroupDefinitionSave* instance();604 /** Prepares group saving thread instance. */605 static void prepare();606 /** Cleanups group saving thread instance. */607 static void cleanup();608 609 /** Configures @a groups saving thread with corresponding @a pListener.610 * @param oldLists Brings the old definition list to be compared.611 * @param newLists Brings the new definition list to be saved. */612 void configure(QObject *pParent,613 const QMap<QString, QStringList> &oldLists,614 const QMap<QString, QStringList> &newLists);615 616 protected:617 618 /** Constructs group saving thread. */619 UIThreadGroupDefinitionSave();620 /** Destructs group saving thread. */621 virtual ~UIThreadGroupDefinitionSave() /* override */;622 623 /** Contains a thread task to be executed. */624 void run();625 626 /** Holds the singleton instance. */627 static UIThreadGroupDefinitionSave *s_pInstance;628 629 /** Holds the map of group definitions to be compared. */630 QMap<QString, QStringList> m_oldLists;631 /** Holds the map of group definitions to be saved. */632 QMap<QString, QStringList> m_newLists;633 };634 635 636 /** QThread subclass allowing to save group order asynchronously. */637 class UIThreadGroupOrderSave : public QThread638 {639 Q_OBJECT;640 641 signals:642 643 /** Notifies about task is complete. */644 void sigComplete();645 646 public:647 648 /** Returns group saving thread instance. */649 static UIThreadGroupOrderSave *instance();650 /** Prepares group saving thread instance. */651 static void prepare();652 /** Cleanups group saving thread instance. */653 static void cleanup();654 655 /** Configures group saving thread with corresponding @a pListener.656 * @param groups Brings the groups to be saved. */657 void configure(QObject *pListener,658 const QMap<QString, QStringList> &groups);659 660 protected:661 662 /** Constructs group saving thread. */663 UIThreadGroupOrderSave();664 /** Destructs group saving thread. */665 virtual ~UIThreadGroupOrderSave() /* override */;666 667 /** Contains a thread task to be executed. */668 virtual void run() /* override */;669 670 /** Holds the singleton instance. */671 static UIThreadGroupOrderSave *s_pInstance;672 673 /** Holds the map of groups to be saved. */674 QMap<QString, QStringList> m_groups;675 };676 677 678 422 #endif /* !FEQT_INCLUDED_SRC_manager_chooser_UIChooserModel_h */
Note:
See TracChangeset
for help on using the changeset viewer.