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.

410 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "jucer_ComponentDocument.h"
  19. //==============================================================================
  20. static const char* const componentDocumentTag = "COMPONENT";
  21. static const char* const componentGroupTag = "COMPONENTS";
  22. static const char* const idProperty = "id";
  23. static const char* const compBoundsProperty = "position";
  24. //==============================================================================
  25. static const String componentBoundsToString (const Rectangle<int>& bounds)
  26. {
  27. return bounds.toString();
  28. }
  29. static const Rectangle<int> stringToComponentBounds (const String& s)
  30. {
  31. return Rectangle<int>::fromString (s);
  32. }
  33. //==============================================================================
  34. class ComponentTypeHandler
  35. {
  36. public:
  37. //==============================================================================
  38. ComponentTypeHandler (const String& name_, const String& tag_)
  39. : name (name_), tag (tag_)
  40. {
  41. }
  42. virtual ~ComponentTypeHandler()
  43. {
  44. }
  45. const String& getName() const { return name; }
  46. const String& getTag() const { return tag; }
  47. virtual Component* createComponent() = 0;
  48. virtual const Rectangle<int> getDefaultSize() = 0;
  49. virtual void updateComponent (Component* comp, const ValueTree& state)
  50. {
  51. comp->setBounds (stringToComponentBounds (state [compBoundsProperty]));
  52. }
  53. virtual const ValueTree createNewItem()
  54. {
  55. ValueTree v (tag);
  56. v.setProperty (idProperty, createAlphaNumericUID(), 0);
  57. v.setProperty (compBoundsProperty,
  58. componentBoundsToString (getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100,
  59. Random::getSystemRandom().nextInt (100) + 100))), 0);
  60. return v;
  61. }
  62. //==============================================================================
  63. protected:
  64. const String name, tag;
  65. };
  66. //==============================================================================
  67. class TextButtonHandler : public ComponentTypeHandler
  68. {
  69. public:
  70. TextButtonHandler() : ComponentTypeHandler ("TextButton", "TEXTBUTTON") {}
  71. ~TextButtonHandler() {}
  72. Component* createComponent() { return new TextButton (String::empty); }
  73. const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 150, 24); }
  74. void updateComponent (Component* comp, const ValueTree& state)
  75. {
  76. ComponentTypeHandler::updateComponent (comp, state);
  77. }
  78. const ValueTree createNewItem()
  79. {
  80. ValueTree v (ComponentTypeHandler::createNewItem());
  81. return v;
  82. }
  83. };
  84. //==============================================================================
  85. class ComponentTypeManager : public DeletedAtShutdown
  86. {
  87. public:
  88. ComponentTypeManager()
  89. {
  90. handlers.add (new TextButtonHandler());
  91. }
  92. ~ComponentTypeManager()
  93. {
  94. }
  95. juce_DeclareSingleton_SingleThreaded_Minimal (ComponentTypeManager);
  96. Component* createFromStoredType (const ValueTree& value)
  97. {
  98. ComponentTypeHandler* handler = getHandlerFor (value.getType());
  99. if (handler == 0)
  100. return 0;
  101. Component* c = handler->createComponent();
  102. if (c != 0)
  103. handler->updateComponent (c, value);
  104. return c;
  105. }
  106. ComponentTypeHandler* getHandlerFor (const String& type)
  107. {
  108. for (int i = handlers.size(); --i >= 0;)
  109. if (handlers.getUnchecked(i)->getTag() == type)
  110. return handlers.getUnchecked(i);
  111. return 0;
  112. }
  113. const StringArray getTypeNames() const
  114. {
  115. StringArray s;
  116. for (int i = 0; i < handlers.size(); ++i)
  117. s.add (handlers.getUnchecked(i)->getName());
  118. return s;
  119. }
  120. int getNumHandlers() const { return handlers.size(); }
  121. ComponentTypeHandler* getHandler (const int index) const { return handlers[index]; }
  122. private:
  123. OwnedArray <ComponentTypeHandler> handlers;
  124. };
  125. juce_ImplementSingleton_SingleThreaded (ComponentTypeManager);
  126. //==============================================================================
  127. ComponentDocument::ComponentDocument (Project* project_, const File& cppFile_)
  128. : project (project_), cppFile (cppFile_), root (componentDocumentTag)
  129. {
  130. checkRootObject();
  131. }
  132. ComponentDocument::~ComponentDocument()
  133. {
  134. }
  135. bool ComponentDocument::isComponentFile (const File& file)
  136. {
  137. if (! file.hasFileExtension (".cpp"))
  138. return false;
  139. ScopedPointer <InputStream> in (file.createInputStream());
  140. if (in == 0)
  141. return false;
  142. const int amountToRead = 1024;
  143. HeapBlock <char> initialData;
  144. initialData.calloc (amountToRead + 4);
  145. in->read (initialData, amountToRead);
  146. return String::createStringFromData (initialData, amountToRead)
  147. .contains ("JUCER_" "COMPONENT"); // written like this to avoid thinking this file is a component!
  148. }
  149. bool ComponentDocument::save()
  150. {
  151. //XXX
  152. return false;
  153. }
  154. bool ComponentDocument::reload()
  155. {
  156. //XXX
  157. return true;
  158. }
  159. bool ComponentDocument::hasChangedSinceLastSave()
  160. {
  161. //XXX
  162. return false;
  163. }
  164. void ComponentDocument::checkRootObject()
  165. {
  166. jassert (root.hasType (componentDocumentTag));
  167. if (! getComponentGroup().isValid())
  168. root.addChild (ValueTree (componentGroupTag), -1, 0);
  169. }
  170. //==============================================================================
  171. const int menuItemOffset = 0x63451fa4;
  172. void ComponentDocument::addNewComponentMenuItems (PopupMenu& menu) const
  173. {
  174. const StringArray typeNames (ComponentTypeManager::getInstance()->getTypeNames());
  175. for (int i = 0; i < typeNames.size(); ++i)
  176. menu.addItem (i + menuItemOffset, "New " + typeNames[i]);
  177. }
  178. void ComponentDocument::performNewComponentMenuItem (int menuResultCode)
  179. {
  180. const StringArray typeNames (ComponentTypeManager::getInstance()->getTypeNames());
  181. if (menuResultCode >= menuItemOffset && menuResultCode < menuItemOffset + typeNames.size())
  182. {
  183. ComponentTypeHandler* handler = ComponentTypeManager::getInstance()->getHandler (menuResultCode - menuItemOffset);
  184. jassert (handler != 0);
  185. if (handler != 0)
  186. getComponentGroup().addChild (handler->createNewItem(), -1, getUndoManager());
  187. }
  188. }
  189. //==============================================================================
  190. ValueTree ComponentDocument::getComponentGroup() const
  191. {
  192. return root.getChildWithName (componentGroupTag);
  193. }
  194. int ComponentDocument::getNumComponents() const
  195. {
  196. return getComponentGroup().getNumChildren();
  197. }
  198. const ValueTree ComponentDocument::getComponent (int index) const
  199. {
  200. return getComponentGroup().getChild (index);
  201. }
  202. Component* ComponentDocument::createComponent (int index) const
  203. {
  204. const ValueTree v (getComponentGroup().getChild (index));
  205. if (v.isValid())
  206. {
  207. Component* c = ComponentTypeManager::getInstance()->createFromStoredType (v);
  208. c->getProperties().set (idProperty, v[idProperty]);
  209. jassert (c->getProperties()[idProperty].toString().isNotEmpty());
  210. return c;
  211. }
  212. return 0;
  213. }
  214. void ComponentDocument::updateComponent (Component* comp) const
  215. {
  216. const ValueTree v (getComponentState (comp));
  217. if (v.isValid())
  218. {
  219. ComponentTypeHandler* handler = ComponentTypeManager::getInstance()->getHandlerFor (v.getType());
  220. jassert (handler != 0);
  221. if (handler != 0)
  222. handler->updateComponent (comp, v);
  223. }
  224. }
  225. bool ComponentDocument::containsComponent (Component* comp) const
  226. {
  227. const ValueTree comps (getComponentGroup());
  228. for (int i = 0; i < comps.getNumChildren(); ++i)
  229. if (isStateForComponent (comps.getChild(i), comp))
  230. return true;
  231. return false;
  232. }
  233. const ValueTree ComponentDocument::getComponentState (Component* comp) const
  234. {
  235. jassert (comp != 0);
  236. const ValueTree comps (getComponentGroup());
  237. for (int i = 0; i < comps.getNumChildren(); ++i)
  238. if (isStateForComponent (comps.getChild(i), comp))
  239. return comps.getChild(i);
  240. jassertfalse;
  241. return ValueTree::invalid;
  242. }
  243. bool ComponentDocument::isStateForComponent (const ValueTree& storedState, Component* comp) const
  244. {
  245. jassert (comp != 0);
  246. jassert (! storedState [idProperty].isVoid());
  247. return storedState [idProperty] == comp->getProperties() [idProperty];
  248. }
  249. //==============================================================================
  250. class ComponentDocument::DragHandler
  251. {
  252. public:
  253. DragHandler (ComponentDocument& document_,
  254. const Array<Component*>& items,
  255. const MouseEvent& e,
  256. int zones_)
  257. : document (document_),
  258. zones (zones_)
  259. {
  260. for (int i = 0; i < items.size(); ++i)
  261. {
  262. jassert (items.getUnchecked(i) != 0);
  263. const ValueTree v (document.getComponentState (items.getUnchecked(i)));
  264. draggedComponents.add (v);
  265. originalPositions.add (stringToComponentBounds (v [compBoundsProperty]));
  266. }
  267. }
  268. ~DragHandler()
  269. {
  270. }
  271. void drag (const MouseEvent& e)
  272. {
  273. for (int i = 0; i < draggedComponents.size(); ++i)
  274. {
  275. if (zones == 0)
  276. move (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i));
  277. else
  278. resize (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i));
  279. }
  280. }
  281. void move (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos)
  282. {
  283. v.setProperty (compBoundsProperty, componentBoundsToString (originalPos + distance), document.getUndoManager());
  284. }
  285. void resize (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos)
  286. {
  287. Rectangle<int> r (originalPos);
  288. if ((zones & zoneL) != 0)
  289. r.setLeft (r.getX() + distance.getX());
  290. if ((zones & zoneT) != 0)
  291. r.setTop (r.getY() + distance.getY());
  292. if ((zones & zoneR) != 0)
  293. r.setWidth (r.getWidth() + distance.getX());
  294. if ((zones & zoneB) != 0)
  295. r.setHeight (r.getHeight() + distance.getY());
  296. v.setProperty (compBoundsProperty, componentBoundsToString (r), document.getUndoManager());
  297. }
  298. private:
  299. ComponentDocument& document;
  300. Array <ValueTree> draggedComponents;
  301. Array <Rectangle<int> > originalPositions;
  302. const int zones;
  303. };
  304. void ComponentDocument::beginDrag (const Array<Component*>& items, const MouseEvent& e, int zones)
  305. {
  306. dragger = new DragHandler (*this, items, e, zones);
  307. }
  308. void ComponentDocument::continueDrag (const MouseEvent& e)
  309. {
  310. if (dragger != 0)
  311. dragger->drag (e);
  312. }
  313. void ComponentDocument::endDrag (const MouseEvent& e)
  314. {
  315. if (dragger != 0)
  316. {
  317. dragger->drag (e);
  318. dragger = 0;
  319. }
  320. }