The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
7.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. class GroupItem : public ProjectTreeItemBase
  20. {
  21. public:
  22. GroupItem (const Project::Item& projectItem, const String& filter = String())
  23. : ProjectTreeItemBase (projectItem),
  24. searchFilter (filter)
  25. {
  26. }
  27. bool isRoot() const override { return item.isMainGroup(); }
  28. bool acceptsFileDrop (const StringArray&) const override { return true; }
  29. void addNewGroup()
  30. {
  31. Project::Item newGroup (item.addNewSubGroup ("New Group", 0));
  32. triggerAsyncRename (newGroup);
  33. }
  34. bool acceptsDragItems (const OwnedArray<Project::Item>& selectedNodes) override
  35. {
  36. for (int i = selectedNodes.size(); --i >= 0;)
  37. if (item.canContain (*selectedNodes.getUnchecked(i)))
  38. return true;
  39. return false;
  40. }
  41. void addFilesAtIndex (const StringArray& files, int insertIndex) override
  42. {
  43. for (int i = 0; i < files.size(); ++i)
  44. {
  45. const File file (files[i]);
  46. if (item.addFileAtIndex (file, insertIndex, true))
  47. ++insertIndex;
  48. }
  49. }
  50. void addFilesRetainingSortOrder (const StringArray& files) override
  51. {
  52. for (int i = files.size(); --i >= 0;)
  53. item.addFileRetainingSortOrder (files[i], true);
  54. }
  55. void moveSelectedItemsTo (OwnedArray<Project::Item>& selectedNodes, int insertIndex) override
  56. {
  57. moveItems (selectedNodes, item, insertIndex);
  58. }
  59. void checkFileStatus() override
  60. {
  61. for (int i = 0; i < getNumSubItems(); ++i)
  62. if (ProjectTreeItemBase* p = dynamic_cast<ProjectTreeItemBase*> (getSubItem(i)))
  63. p->checkFileStatus();
  64. }
  65. bool isGroupEmpty (const Project::Item& group) // recursive
  66. {
  67. for (auto i = 0; i < group.getNumChildren(); ++i)
  68. {
  69. auto child = group.getChild (i);
  70. if ((child.isGroup() && ! isGroupEmpty (child))
  71. || (child.isFile() && child.getName().containsIgnoreCase (searchFilter)))
  72. return false;
  73. }
  74. return true;
  75. }
  76. ProjectTreeItemBase* createSubItem (const Project::Item& child) override
  77. {
  78. if (child.isGroup())
  79. {
  80. if (searchFilter.isNotEmpty() && isGroupEmpty (child))
  81. return nullptr;
  82. return new GroupItem (child, searchFilter);
  83. }
  84. if (child.isFile())
  85. {
  86. if (child.getName().containsIgnoreCase (searchFilter))
  87. return new SourceFileItem (child);
  88. return nullptr;
  89. }
  90. jassertfalse;
  91. return nullptr;
  92. }
  93. void showDocument() override
  94. {
  95. if (ProjectContentComponent* pcc = getProjectContentComponent())
  96. pcc->setEditorComponent (new GroupInformationComponent (item), nullptr);
  97. }
  98. static void openAllGroups (TreeViewItem* root)
  99. {
  100. for (auto i = 0; i < root->getNumSubItems(); ++i)
  101. if (auto* sub = root->getSubItem (i))
  102. openOrCloseAllSubGroups (*sub, true);
  103. }
  104. static void closeAllGroups (TreeViewItem* root)
  105. {
  106. for (auto i = 0; i < root->getNumSubItems(); ++i)
  107. if (auto* sub = root->getSubItem (i))
  108. openOrCloseAllSubGroups (*sub, false);
  109. }
  110. static void openOrCloseAllSubGroups (TreeViewItem& item, bool shouldOpen)
  111. {
  112. item.setOpen (shouldOpen);
  113. for (int i = item.getNumSubItems(); --i >= 0;)
  114. if (auto* sub = item.getSubItem (i))
  115. openOrCloseAllSubGroups (*sub, shouldOpen);
  116. }
  117. static void setFilesToCompile (Project::Item item, const bool shouldCompile)
  118. {
  119. if (item.isFile())
  120. item.getShouldCompileValue() = shouldCompile;
  121. for (int i = item.getNumChildren(); --i >= 0;)
  122. setFilesToCompile (item.getChild (i), shouldCompile);
  123. }
  124. void showPopupMenu() override
  125. {
  126. PopupMenu m;
  127. addCreateFileMenuItems (m);
  128. m.addSeparator();
  129. m.addItem (1, "Collapse all Groups");
  130. m.addItem (2, "Expand all Groups");
  131. if (! isRoot())
  132. {
  133. if (isOpen())
  134. m.addItem (3, "Collapse all Sub-groups");
  135. else
  136. m.addItem (4, "Expand all Sub-groups");
  137. }
  138. m.addSeparator();
  139. m.addItem (5, "Enable compiling of all enclosed files");
  140. m.addItem (6, "Disable compiling of all enclosed files");
  141. m.addSeparator();
  142. m.addItem (7, "Sort Items Alphabetically");
  143. m.addItem (8, "Sort Items Alphabetically (Groups first)");
  144. m.addSeparator();
  145. if (! isRoot())
  146. {
  147. m.addItem (9, "Rename...");
  148. m.addItem (10, "Delete");
  149. }
  150. launchPopupMenu (m);
  151. }
  152. void showPlusMenu() override
  153. {
  154. PopupMenu m;
  155. addCreateFileMenuItems (m);
  156. launchPopupMenu (m);
  157. }
  158. void handlePopupMenuResult (int resultCode) override
  159. {
  160. switch (resultCode)
  161. {
  162. case 1: closeAllGroups (getOwnerView()->getRootItem()); break;
  163. case 2: openAllGroups (getOwnerView()->getRootItem()); break;
  164. case 3: openOrCloseAllSubGroups (*this, false); break;
  165. case 4: openOrCloseAllSubGroups (*this, true); break;
  166. case 5: setFilesToCompile (item, true); break;
  167. case 6: setFilesToCompile (item, false); break;
  168. case 7: item.sortAlphabetically (false, false); break;
  169. case 8: item.sortAlphabetically (true, false); break;
  170. case 9: triggerAsyncRename (item); break;
  171. case 10: deleteAllSelectedItems(); break;
  172. default: processCreateFileMenuItem (resultCode); break;
  173. }
  174. }
  175. void addCreateFileMenuItems (PopupMenu& m)
  176. {
  177. m.addItem (1001, "Add New Group");
  178. m.addItem (1002, "Add Existing Files...");
  179. m.addSeparator();
  180. NewFileWizard().addWizardsToMenu (m);
  181. }
  182. void processCreateFileMenuItem (int menuID)
  183. {
  184. switch (menuID)
  185. {
  186. case 1001: addNewGroup(); break;
  187. case 1002: browseToAddExistingFiles(); break;
  188. default:
  189. jassert (getProject() != nullptr);
  190. NewFileWizard().runWizardFromMenu (menuID, *getProject(), item);
  191. break;
  192. }
  193. }
  194. Project* getProject()
  195. {
  196. if (TreeView* tv = getOwnerView())
  197. if (ProjectContentComponent* pcc = tv->findParentComponentOfClass<ProjectContentComponent>())
  198. return pcc->getProject();
  199. return nullptr;
  200. }
  201. void setSearchFilter (const String& filter)
  202. {
  203. searchFilter = filter;
  204. refreshSubItems();
  205. }
  206. String searchFilter;
  207. };