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.

347 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. class ErrorListComp : public TreePanelBase,
  18. private ChangeListener
  19. {
  20. public:
  21. ErrorListComp (ErrorList& el)
  22. : TreePanelBase (nullptr, String()),
  23. errorList (el)
  24. {
  25. setName ("Errors and Warnings");
  26. setEmptyTreeMessage ("(No Messages)");
  27. tree.setMultiSelectEnabled (false);
  28. tree.setRootItemVisible (false);
  29. setRoot (new ErrorRootTreeItem (errorList));
  30. errorList.addChangeListener (this);
  31. errorListChanged();
  32. }
  33. ~ErrorListComp()
  34. {
  35. errorList.removeChangeListener (this);
  36. }
  37. void errorListChanged()
  38. {
  39. static_cast<ErrorRootTreeItem*> (rootItem.get())->refreshSubItems();
  40. }
  41. void moveBy (const int delta)
  42. {
  43. if (delta < 0)
  44. if (TreeViewItem* selected = tree.getSelectedItem (0))
  45. if (selected->getRowNumberInTree() <= 1)
  46. return;
  47. tree.moveSelectedRow (delta);
  48. if (dynamic_cast<ErrorMessageTreeItem*> (tree.getSelectedItem (0)) == nullptr)
  49. tree.moveSelectedRow (delta);
  50. }
  51. void showNext() { moveBy (1); }
  52. void showPrevious() { moveBy (-1); }
  53. private:
  54. TreeView list;
  55. ErrorList& errorList;
  56. struct ErrorMessageTreeItem;
  57. void changeListenerCallback (ChangeBroadcaster*) override
  58. {
  59. errorListChanged();
  60. }
  61. static void limitNumberOfSubItems (TreeViewItem& item, const int maxSubItems)
  62. {
  63. while (item.getNumSubItems() > maxSubItems)
  64. item.removeSubItem (item.getNumSubItems() - 1);
  65. }
  66. //==============================================================================
  67. class ErrorRootTreeItem : public JucerTreeViewBase
  68. {
  69. public:
  70. ErrorRootTreeItem (ErrorList& el) : errorList (el) {}
  71. String getRenamingName() const override { return getDisplayName(); }
  72. String getDisplayName() const override { return "Errors and Warnings"; }
  73. void setName (const String&) override {}
  74. bool isMissing() const override { return false; }
  75. Icon getIcon() const override { return Icon (getIcons().bug, getContentColour (true)); }
  76. bool canBeSelected() const override { return true; }
  77. bool mightContainSubItems() override { return true; }
  78. String getUniqueName() const override { return "errors"; }
  79. void refreshSubItems()
  80. {
  81. Array<DiagnosticMessage> errors;
  82. errorList.takeCopy (errors);
  83. StringArray files;
  84. for (const auto& m : errors)
  85. {
  86. files.addIfNotAlreadyThere (m.mainFile);
  87. if (m.associatedDiagnostic != nullptr)
  88. files.addIfNotAlreadyThere (m.associatedDiagnostic->mainFile);
  89. }
  90. limitNumberOfSubItems (*this, files.size());
  91. int i = 0;
  92. for (const auto& f : files)
  93. {
  94. if (i >= getNumSubItems() || static_cast<CompileUnitTreeItem*> (getSubItem (i))->compileUnit != f)
  95. {
  96. limitNumberOfSubItems (*this, i);
  97. addSubItem (new CompileUnitTreeItem (f));
  98. }
  99. static_cast<CompileUnitTreeItem*> (getSubItem (i))->refresh (errors);
  100. ++i;
  101. }
  102. }
  103. private:
  104. ErrorList& errorList;
  105. };
  106. //==============================================================================
  107. struct CompileUnitTreeItem : public JucerTreeViewBase
  108. {
  109. CompileUnitTreeItem (const String& filename) : compileUnit (filename) {}
  110. void setName (const String&) override {}
  111. void addSubItems() override {}
  112. bool isMissing() const override { return false; }
  113. Icon getIcon() const override { return Icon (getIcons().bug, getContentColour (true)); }
  114. bool canBeSelected() const override { return true; }
  115. bool mightContainSubItems() override { return true; }
  116. String getUniqueName() const override { return String::toHexString (compileUnit.hashCode64()); }
  117. String getRenamingName() const override { return getDisplayName(); }
  118. String getDisplayName() const override
  119. {
  120. if (File::isAbsolutePath (compileUnit))
  121. {
  122. File f (compileUnit);
  123. return f.exists() ? f.getFileName() : compileUnit;
  124. }
  125. if (! compileUnit.isEmpty())
  126. return compileUnit;
  127. return String ("Global");
  128. }
  129. void showOverlays()
  130. {
  131. for (int i = 0; i < getNumSubItems(); ++i)
  132. if (auto* e = dynamic_cast<ErrorMessageTreeItem*> (getSubItem (i)))
  133. e->showOverlays();
  134. }
  135. ErrorMessageTreeItem* getItemForError (const DiagnosticMessage& m) const
  136. {
  137. for (int i = 0; i < getNumSubItems(); ++i)
  138. if (auto* item = dynamic_cast<ErrorMessageTreeItem*> (getSubItem(i)))
  139. if (item->message == m)
  140. return item;
  141. return nullptr;
  142. }
  143. void refresh (const Array<DiagnosticMessage>& allErrors)
  144. {
  145. clearSubItems();
  146. for (const auto& error : allErrors)
  147. if (error.mainFile == compileUnit && error.associatedDiagnostic == nullptr)
  148. addSubItem (new ErrorMessageTreeItem (error));
  149. for (const auto& error : allErrors)
  150. if (error.mainFile == compileUnit && error.associatedDiagnostic != nullptr)
  151. if (ErrorMessageTreeItem* parent = getItemForError (*error.associatedDiagnostic))
  152. parent->addSubItem (new ErrorMessageTreeItem (error));
  153. }
  154. void showDocument() override
  155. {
  156. if (ProjectContentComponent* pcc = getProjectContentComponent())
  157. if (File::isAbsolutePath (compileUnit) && File (compileUnit).exists())
  158. pcc->showEditorForFile (File (compileUnit), true);
  159. }
  160. String compileUnit;
  161. };
  162. //==============================================================================
  163. struct ErrorMessageTreeItem : public JucerTreeViewBase
  164. {
  165. ErrorMessageTreeItem (const DiagnosticMessage& m)
  166. : message (m), itemHeight (25)
  167. {
  168. setOpenness (Openness::opennessClosed);
  169. uniqueID << message.message << ':' << message.range.toString();
  170. }
  171. ~ErrorMessageTreeItem()
  172. {
  173. overlay.deleteAndZero();
  174. }
  175. String getRenamingName() const override { return getDisplayName(); }
  176. String getDisplayName() const override { return message.message; }
  177. void setName (const String&) override {}
  178. bool isMissing() const override { return false; }
  179. Icon getIcon() const override { return Icon (message.isNote() ? getIcons().info
  180. : getIcons().warning, getContentColour (true)); }
  181. bool canBeSelected() const override { return true; }
  182. bool mightContainSubItems() override { return getNumSubItems() != 0; }
  183. String getUniqueName() const override { return uniqueID; }
  184. void paintContent (Graphics& g, const Rectangle<int>& area) override
  185. {
  186. jassert (area.getWidth() >= 0);
  187. AttributedString s (message.message);
  188. s.setFont (Font (12.0f));
  189. s.setColour (getContentColour (false));
  190. s.setJustification (Justification::centredLeft);
  191. text.createLayout (s, (float) area.getWidth());
  192. const auto newHeight = 2 + jmax (25, (int) text.getHeight());
  193. if (itemHeight != newHeight)
  194. {
  195. itemHeight = newHeight;
  196. treeHasChanged();
  197. }
  198. text.draw (g, area.toFloat());
  199. }
  200. Colour getContentColour (bool isIcon) const override
  201. {
  202. return message.isError() ? Colours::red
  203. : message.isWarning() ? Colours::yellow
  204. : getOwnerView()->findColour (isIcon ? treeIconColourId
  205. : defaultTextColourId);
  206. }
  207. void showPopupMenu() override
  208. {
  209. PopupMenu menu;
  210. menu.addItem (1, "Copy");
  211. launchPopupMenu (menu);
  212. }
  213. void handlePopupMenuResult (int resultCode) override
  214. {
  215. if (resultCode == 1)
  216. SystemClipboard::copyTextToClipboard (message.toString());
  217. }
  218. int getItemHeight() const override
  219. {
  220. return itemHeight;
  221. }
  222. SourceCodeEditor* getEditor()
  223. {
  224. if (ProjectContentComponent* pcc = getProjectContentComponent())
  225. {
  226. const File file (File::createFileWithoutCheckingPath (message.range.file));
  227. if (message.range.isValid() && file.exists() && pcc->showEditorForFile (file, false))
  228. {
  229. if (SourceCodeEditor* ed = dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent()))
  230. {
  231. return ed;
  232. }
  233. }
  234. }
  235. return nullptr;
  236. }
  237. void showDocument() override
  238. {
  239. if (SourceCodeEditor* ed = getEditor())
  240. {
  241. ed->grabKeyboardFocus();
  242. ed->highlight (message.range.range, false);
  243. if (auto cu = findCompileUnitParent())
  244. cu->showOverlays();
  245. }
  246. }
  247. CompileUnitTreeItem* findCompileUnitParent()
  248. {
  249. for (TreeViewItem* p = getParentItem(); p != nullptr; p = p->getParentItem())
  250. if (auto cu = dynamic_cast<CompileUnitTreeItem*> (p))
  251. return cu;
  252. return nullptr;
  253. }
  254. void showOverlays()
  255. {
  256. overlay.deleteAndZero();
  257. if (ProjectContentComponent* pcc = getProjectContentComponent())
  258. {
  259. if (SourceCodeEditor* ed = dynamic_cast<SourceCodeEditor*> (pcc->getEditorComponent()))
  260. {
  261. auto start = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getStart());
  262. auto end = CodeDocument::Position (ed->editor->getDocument(), message.range.range.getEnd());
  263. if (auto* ce = dynamic_cast<LiveBuildCodeEditor*> (ed->editor.get()))
  264. overlay = ce->addDiagnosticOverlay (start, end, message.type);
  265. }
  266. }
  267. for (int i = 0; i < getNumSubItems(); ++i)
  268. if (auto* e = dynamic_cast<ErrorMessageTreeItem*> (getSubItem (i)))
  269. e->showOverlays();
  270. }
  271. DiagnosticMessage message;
  272. private:
  273. String uniqueID;
  274. TextLayout text;
  275. int itemHeight;
  276. Component::SafePointer<Component> overlay;
  277. };
  278. };