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.

361 lines
13KB

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