treeitem.cpp (7847B)
1 #include "treeitem.h" 2 3 #include "listmodel.h" 4 #include "patchmanager.h" 5 #include "savepatchdesc.h" 6 #include "tree.h" 7 8 #include "jucePluginLib/patchdb/patchdbtypes.h" 9 #include "juceUiLib/treeViewStyle.h" 10 11 namespace jucePluginEditorLib::patchManager 12 { 13 TreeItem::TreeItem(PatchManager& _patchManager, const std::string& _title, const uint32_t _count/* = g_invalidCount*/) : m_patchManager(_patchManager), m_count(_count) 14 { 15 setTitle(_title); 16 } 17 18 TreeItem::~TreeItem() 19 { 20 getPatchManager().removeSelectedItem(getTree(), this); 21 22 if(m_searchHandle != pluginLib::patchDB::g_invalidSearchHandle) 23 { 24 getPatchManager().cancelSearch(m_searchHandle); 25 m_searchHandle = pluginLib::patchDB::g_invalidSearchHandle; 26 } 27 } 28 29 void TreeItem::setTitle(const std::string& _title) 30 { 31 if (m_title == _title) 32 return; 33 m_title = _title; 34 updateText(); 35 } 36 37 void TreeItem::setCount(const uint32_t _count) 38 { 39 if (m_count == _count) 40 return; 41 m_count = _count; 42 updateText(); 43 } 44 45 void TreeItem::processDirty(const std::set<pluginLib::patchDB::SearchHandle>& _dirtySearches) 46 { 47 if (_dirtySearches.find(m_searchHandle) == _dirtySearches.end()) 48 return; 49 50 const auto search = getPatchManager().getSearch(m_searchHandle); 51 if (!search) 52 return; 53 54 processSearchUpdated(*search); 55 } 56 57 bool TreeItem::beginEdit(const std::string& _initialText, FinishedEditingCallback&& _callback) 58 { 59 auto pos = getItemPosition(true); 60 pos.setHeight(getItemHeight()); 61 62 return Editable::beginEdit(getOwnerView(), pos, _initialText, std::move(_callback)); 63 } 64 65 bool TreeItem::hasSearch() const 66 { 67 return m_searchHandle != pluginLib::patchDB::g_invalidSearchHandle; 68 } 69 70 Tree* TreeItem::getTree() const 71 { 72 return dynamic_cast<Tree*>(getOwnerView()); 73 } 74 75 void TreeItem::removeFromParent(const bool _destroy) const 76 { 77 auto* parent = getParentItem(); 78 if (!parent) 79 { 80 if (_destroy) 81 delete this; 82 return; 83 } 84 const auto idx = getIndexInParent(); 85 parent->removeSubItem(idx, _destroy); 86 } 87 88 void TreeItem::setParent(TreeViewItem* _parent, const bool _sorted/* = false*/) 89 { 90 const auto* parentExisting = getParentItem(); 91 92 if (_parent == parentExisting) 93 return; 94 95 removeFromParent(false); 96 97 if (_parent) 98 { 99 if(_sorted) 100 _parent->addSubItemSorted(*this, this); 101 else 102 _parent->addSubItem(this); 103 } 104 } 105 106 void TreeItem::itemSelectionChanged(const bool _isNowSelected) 107 { 108 if(_isNowSelected && m_forceDeselect) 109 { 110 m_forceDeselect = false; 111 112 juce::MessageManager::callAsync([this]() 113 { 114 setSelected(false, false); 115 }); 116 return; 117 } 118 119 m_selectedWasChanged = true; 120 121 TreeViewItem::itemSelectionChanged(_isNowSelected); 122 123 if(getTree()->isMultiSelectEnabled()) 124 { 125 if (_isNowSelected) 126 getPatchManager().addSelectedItem(getTree(), this); 127 else 128 getPatchManager().removeSelectedItem(getTree(), this); 129 } 130 else 131 { 132 if (_isNowSelected) 133 getPatchManager().setSelectedItem(getTree(), this); 134 } 135 } 136 137 void TreeItem::itemDropped(const juce::DragAndDropTarget::SourceDetails& dragSourceDetails, int insertIndex) 138 { 139 const auto patches = SavePatchDesc::getPatchesFromDragSource(dragSourceDetails); 140 141 if(!patches.empty()) 142 patchesDropped(patches); 143 } 144 145 bool TreeItem::isInterestedInDragSource(const juce::DragAndDropTarget::SourceDetails& _dragSourceDetails) 146 { 147 const auto* list = dynamic_cast<ListModel*>(_dragSourceDetails.sourceComponent.get()); 148 149 const auto& patches = SavePatchDesc::getPatchesFromDragSource(_dragSourceDetails); 150 151 return isInterestedInPatchList(list, patches); 152 } 153 154 void TreeItem::search(pluginLib::patchDB::SearchRequest&& _request) 155 { 156 cancelSearch(); 157 setCount(g_unknownCount); 158 m_searchRequest = _request; 159 m_searchHandle = getPatchManager().search(std::move(_request)); 160 } 161 162 void TreeItem::processSearchUpdated(const pluginLib::patchDB::Search& _search) 163 { 164 setCount(static_cast<uint32_t>(_search.getResultSize())); 165 } 166 167 const pluginLib::patchDB::SearchRequest& TreeItem::getParentSearchRequest() const 168 { 169 return m_parentSearchRequest; 170 } 171 172 void TreeItem::setText(const std::string& _text) 173 { 174 if (m_text == _text) 175 return; 176 m_text = _text; 177 repaintItem(); 178 } 179 180 void TreeItem::updateText() 181 { 182 if (m_count == g_invalidCount) 183 setText(m_title); 184 else if (m_count == g_unknownCount) 185 setText(m_title + " (?)"); 186 else 187 setText(m_title + " (" + std::to_string(m_count) + ')'); 188 } 189 190 void TreeItem::paintItem(juce::Graphics& _g, const int _width, const int _height) 191 { 192 getTree()->setColour(juce::TreeView::dragAndDropIndicatorColourId, juce::Colour(juce::ModifierKeys::currentModifiers.isShiftDown() ? 0xffff0000 : 0xff00ff00)); 193 194 const auto* style = dynamic_cast<const genericUI::TreeViewStyle*>(&getOwnerView()->getLookAndFeel()); 195 196 const auto color = getColor(); 197 198 _g.setColour(color != pluginLib::patchDB::g_invalidColor ? juce::Colour(color) : style ? style->getColor() : juce::Colour(0xffffffff)); 199 200 bool haveFont = false; 201 bool antialias = true; 202 203 if(style) 204 { 205 if (auto f = style->getFont()) 206 { 207 if (style->boldRootItems() && getParentItem() == getTree()->getRootItem()) 208 f = f->boldened(); 209 _g.setFont(*f); 210 haveFont = true; 211 } 212 antialias = style->getAntialiasing(); 213 } 214 215 if(!haveFont) 216 { 217 auto fnt = _g.getCurrentFont(); 218 if (getParentItem() == getTree()->getRootItem()) 219 fnt = fnt.boldened(); 220 _g.setFont(fnt); 221 } 222 223 _g.setImageResamplingQuality(antialias ? juce::Graphics::highResamplingQuality : juce::Graphics::lowResamplingQuality); 224 225 const juce::String t = juce::String::fromUTF8(m_text.c_str()); 226 _g.drawText(t, 0, 0, _width, _height, style ? style->getAlign() : juce::Justification(juce::Justification::centredLeft)); 227 TreeViewItem::paintItem(_g, _width, _height); 228 } 229 230 bool TreeItem::isInterestedInFileDrag(const juce::StringArray& _files) 231 { 232 return TreeViewItem::isInterestedInFileDrag(_files); 233 } 234 235 void TreeItem::filesDropped(const juce::StringArray& _files, const int _insertIndex) 236 { 237 const auto patches = m_patchManager.loadPatchesFromFiles(_files); 238 239 if(!patches.empty()) 240 patchesDropped(patches); 241 else 242 TreeViewItem::filesDropped(_files, _insertIndex); 243 } 244 245 int TreeItem::compareElements(const TreeViewItem* _a, const TreeViewItem* _b) 246 { 247 const auto* a = dynamic_cast<const TreeItem*>(_a); 248 const auto* b = dynamic_cast<const TreeItem*>(_b); 249 250 if(a && b) 251 return a->getText().compare(b->getText()); 252 253 if (_a < _b) 254 return -1; 255 if (_a > _b) 256 return 1; 257 return 0; 258 } 259 260 void TreeItem::setParentSearchRequest(const pluginLib::patchDB::SearchRequest& _parentSearch) 261 { 262 if(_parentSearch == m_parentSearchRequest) 263 return; 264 m_parentSearchRequest = _parentSearch; 265 onParentSearchChanged(m_parentSearchRequest); 266 } 267 268 void TreeItem::itemClicked(const juce::MouseEvent& _mouseEvent) 269 { 270 if(_mouseEvent.mods.isPopupMenu()) 271 { 272 TreeViewItem::itemClicked(_mouseEvent); 273 return; 274 } 275 276 if(!m_deselectOnSecondClick) 277 { 278 TreeViewItem::itemClicked(_mouseEvent); 279 return; 280 } 281 282 // we have the (for Juce) overly complex task to deselect a tree item on left click 283 // Juce does not let us though, this click is sent on mouse down and it reselects the item 284 // again on mouse up. 285 const auto selectedWasChanged = m_selectedWasChanged; 286 m_selectedWasChanged = false; 287 288 if(!selectedWasChanged && isSelected() && getOwnerView()->isMultiSelectEnabled()) 289 { 290 m_forceDeselect = true; 291 setSelected(false, false); 292 } 293 } 294 295 void TreeItem::cancelSearch() 296 { 297 if(m_searchHandle == pluginLib::patchDB::g_invalidSearchHandle) 298 return; 299 300 getPatchManager().cancelSearch(m_searchHandle); 301 m_searchHandle = pluginLib::patchDB::g_invalidSearchHandle; 302 } 303 304 void TreeItem::patchesDropped(const std::vector<pluginLib::patchDB::PatchPtr>& _patches, const SavePatchDesc* _savePatchDesc/* = nullptr*/) 305 { 306 for (const auto& patch : _patches) 307 patchDropped(patch); 308 } 309 }