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.

514 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCETICE project - Copyright 2009 by Lucio Asnaghi.
  4. JUCETICE is based around the JUCE library - "Jules' Utility Class Extensions"
  5. Copyright 2007 by Julian Storer.
  6. ------------------------------------------------------------------------------
  7. JUCE and JUCETICE can be redistributed and/or modified under the terms of
  8. the GNU General Public License, as published by the Free Software Foundation;
  9. either version 2 of the License, or (at your option) any later version.
  10. JUCE and JUCETICE are distributed in the hope that they will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with JUCE and JUCETICE; if not, visit www.gnu.org/licenses or write to
  16. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  17. Boston, MA 02111-1307 USA
  18. ==============================================================================
  19. */
  20. BEGIN_JUCE_NAMESPACE
  21. //==============================================================================
  22. Joystick::Joystick()
  23. : Component ("Joystick"),
  24. current_x (0),
  25. current_y (0),
  26. x_min (0),
  27. x_max (1),
  28. y_min (0),
  29. y_max (1),
  30. backgroundColour (Colours::black),
  31. sendChangeOnlyOnRelease (false),
  32. holdOnMouseRelease (false),
  33. isVelocityBased (false),
  34. menuEnabled (true),
  35. menuShown (false),
  36. mouseWasHidden (false),
  37. numDecimalPlaces (4)
  38. {
  39. setOpaque (true);
  40. setWantsKeyboardFocus (true);
  41. calculateRatio();
  42. calculateSnapBack();
  43. }
  44. Joystick::~Joystick()
  45. {
  46. deleteAllChildren();
  47. }
  48. //==============================================================================
  49. void Joystick::resized()
  50. {
  51. x_ratio = getWidth() / (float) (x_max - x_min);
  52. y_ratio = getHeight() / (float) (y_max - y_min);
  53. calculateDrawingSpot();
  54. }
  55. //==============================================================================
  56. const String Joystick::getCurrentValueAsString()
  57. {
  58. String text;
  59. text << getTextFromValue (current_x+x_min) << ", " << getTextFromValue (((y_max-y_min)-current_y)+y_min) ;
  60. return text;
  61. }
  62. //==============================================================================
  63. void Joystick::setVelocityBasedMode (const bool velBased)
  64. {
  65. isVelocityBased = velBased;
  66. }
  67. void Joystick::setPopupMenuEnabled (const bool menuEnabled_)
  68. {
  69. menuEnabled = menuEnabled_;
  70. }
  71. //==============================================================================
  72. void Joystick::setRanges (const double newHorizontalMin,
  73. const double newHorizontalMax,
  74. const double newVerticalMin,
  75. const double newVerticalMax,
  76. const double newInt)
  77. {
  78. bool horizontalChanged = false;
  79. bool verticalChanged = false;
  80. if (x_min != newHorizontalMin
  81. || x_max != newVerticalMax)
  82. {
  83. x_min = newHorizontalMin;
  84. x_max = newHorizontalMax;
  85. horizontalChanged = true;
  86. }
  87. if (y_min != newVerticalMin
  88. || y_max != newVerticalMax)
  89. {
  90. y_min = newVerticalMin;
  91. y_max = newVerticalMax;
  92. verticalChanged = true;
  93. }
  94. // figure out the number of DPs needed to display all values at this
  95. // interval setting.
  96. interval = newInt;
  97. numDecimalPlaces = 7;
  98. if (newInt != 0)
  99. {
  100. int v = abs ((int) (newInt * 10000000));
  101. while ((v % 10) == 0)
  102. {
  103. --numDecimalPlaces;
  104. v /= 10;
  105. }
  106. }
  107. if (horizontalChanged || verticalChanged)
  108. setValues (current_x, current_y, false);
  109. calculateRatio();
  110. calculateSnapBack();
  111. calculateDrawingSpot();
  112. }
  113. void Joystick::setValues (double newHorizontalValue, double newVerticalValue,
  114. const bool sendUpdateMessage,
  115. const bool sendMessageSynchronously)
  116. {
  117. if (newHorizontalValue <= x_min || x_max <= x_min)
  118. {
  119. newHorizontalValue = x_min;
  120. }
  121. else if (newHorizontalValue >= x_max)
  122. {
  123. newHorizontalValue = x_max;
  124. }
  125. else if (interval > 0)
  126. {
  127. newHorizontalValue = x_min + interval * floor ((newHorizontalValue - x_min) / interval + 0.5);
  128. }
  129. if (newVerticalValue <= y_min || y_max <= y_min)
  130. {
  131. newVerticalValue = y_min;
  132. }
  133. else if (newVerticalValue >= y_max)
  134. {
  135. newVerticalValue = y_max;
  136. }
  137. else if (interval > 0)
  138. {
  139. newVerticalValue = y_min + interval * floor ((newVerticalValue - y_min) / interval + 0.5);
  140. }
  141. if (current_x != newHorizontalValue)
  142. current_x = newHorizontalValue;
  143. if (current_y != newVerticalValue)
  144. current_y = newVerticalValue;
  145. if (current_x != newHorizontalValue || current_y != newVerticalValue)
  146. {
  147. if (sendUpdateMessage)
  148. {
  149. if (sendMessageSynchronously)
  150. sendChanges();
  151. else
  152. jassertfalse; // asyncronous message not implemented !
  153. valueChanged (current_x, current_y);
  154. }
  155. calculateDrawingSpot();
  156. }
  157. }
  158. //==============================================================================
  159. void Joystick::paint (Graphics& g)
  160. {
  161. // Draw main section of box ------------------------
  162. g.setColour (backgroundColour);
  163. g.fillAll ();
  164. // Determine the location of the snapback square ---
  165. int snap_x = roundDoubleToInt (floor (x_snap * x_ratio));
  166. int snap_y = roundDoubleToInt (floor (y_snap * y_ratio));
  167. // if at max value, don't go over edge
  168. if (x_snap == x_min) snap_x++;
  169. if (y_snap == x_min) snap_y++;
  170. if (x_snap == (x_max - x_min)) snap_x--;
  171. if (y_snap == (x_max - x_min)) snap_y--;
  172. // Draw the snapback square ------------------------
  173. g.setColour (Colours::lightyellow);
  174. // g.fillEllipse(snap_x-2,snap_y-2,5,5);
  175. g.drawEllipse (snap_x - 4, snap_y - 4, 9, 9, 1.0);
  176. // Draw the main dot -------------------------------
  177. if (holdOnMouseRelease)
  178. g.setColour (Colours::lightblue);
  179. else
  180. g.setColour (Colours::orange);
  181. g.fillEllipse (draw_x - 2, draw_y - 2, 5, 5);
  182. g.drawEllipse (draw_x - 4, draw_y - 4, 9, 9, 1.0);
  183. // Draw inset bevel --------------------------------
  184. LookAndFeel_V2::drawBevel (g,
  185. 0, 0,
  186. getWidth(), getHeight(),
  187. 2,
  188. insetDarkColour,
  189. insetLightColour,
  190. true);
  191. }
  192. //==============================================================================
  193. void Joystick::mouseDown(const MouseEvent& e)
  194. {
  195. mouseWasHidden = false;
  196. if (isEnabled())
  197. {
  198. if (e.mods.isPopupMenu() && menuEnabled)
  199. {
  200. menuShown = true;
  201. PopupMenu m;
  202. m.addItem (1, TRANS ("velocity-sensitive mode"), true, isVelocityBased);
  203. m.addSeparator();
  204. const int r = m.show();
  205. if (r == 1)
  206. {
  207. setVelocityBasedMode (! isVelocityBased);
  208. }
  209. else if (r == 2)
  210. {
  211. // setSliderStyle (RotaryHorizontalDrag);
  212. }
  213. else if (r == 3)
  214. {
  215. // setSliderStyle (RotaryVerticalDrag);
  216. }
  217. }
  218. else if (e.mods.isLeftButtonDown()
  219. || e.mods.isMiddleButtonDown())
  220. {
  221. menuShown = false;
  222. // unbound mouse movements
  223. if (isVelocityBased)
  224. {
  225. e.source.enableUnboundedMouseMovement (true, false);
  226. mouseWasHidden = true;
  227. }
  228. startPressX = e.x;
  229. startPressY = e.y;
  230. // In case it was a click without a drag, do the drag anyway
  231. mouseDrag (e);
  232. }
  233. // So arrow keys works
  234. grabKeyboardFocus();
  235. }
  236. }
  237. void Joystick::mouseDrag (const MouseEvent& e) //Called continuously while moving mouse
  238. {
  239. if (isEnabled() && ! menuShown)
  240. {
  241. // If shift is down, don't allow changes to the X value
  242. if (! e.mods.isShiftDown())
  243. {
  244. //Determine current X value based on mouse location
  245. int x = startPressX + e.getDistanceFromDragStartX ();
  246. if (x < 0)
  247. current_x = 0;
  248. else if (x >= getWidth())
  249. current_x = x_max - x_min;
  250. else
  251. current_x = x / x_ratio;
  252. }
  253. // If ctrl is down, don't allow changes to Y value
  254. if (! e.mods.isCtrlDown())
  255. {
  256. //Determine current Y value based on mouse location
  257. int y = startPressY + e.getDistanceFromDragStartY ();
  258. if (y < 0)
  259. current_y = 0;
  260. else if (y > getHeight())
  261. current_y = y_max - y_min;
  262. else
  263. current_y = y / y_ratio;
  264. }
  265. // If holding down control key, then set snapback values
  266. if (e.mods.isLeftButtonDown())
  267. {
  268. x_snap = current_x;
  269. y_snap = current_y;
  270. }
  271. // If right left is down, then hold
  272. if (e.mods.isLeftButtonDown())
  273. holdOnMouseRelease = true;
  274. else
  275. holdOnMouseRelease = false;
  276. // Send changes and repaint screen
  277. if (! sendChangeOnlyOnRelease)
  278. {
  279. valueChanged (current_x, current_y);
  280. sendChanges ();
  281. }
  282. // update drawing position
  283. calculateDrawingSpot();
  284. repaint ();
  285. }
  286. }
  287. void Joystick::mouseUp (const MouseEvent& e) //Called when the mouse button is released
  288. {
  289. if (isEnabled() && ! menuShown)
  290. {
  291. // If not holding, then jump to snapback values
  292. if (! holdOnMouseRelease)
  293. {
  294. current_x = x_snap;
  295. current_y = y_snap;
  296. }
  297. // Call user defined callback
  298. valueChanged (current_x, current_y);
  299. // Send controllers changes values
  300. sendChanges ();
  301. // update drawing position
  302. calculateDrawingSpot();
  303. // restore mouse
  304. restoreMouseIfHidden();
  305. repaint ();
  306. }
  307. }
  308. void Joystick::mouseMove (const MouseEvent& e)
  309. {
  310. }
  311. //==============================================================================
  312. bool Joystick::keyPressed (const KeyPress& key)
  313. {
  314. if (key.isKeyCode (KeyPress::upKey))
  315. {
  316. setValues(current_x,current_y-interval);
  317. }
  318. else if (key.isKeyCode (KeyPress::downKey))
  319. {
  320. setValues(current_x,current_y+interval);
  321. }
  322. else if (key.isKeyCode (KeyPress::leftKey))
  323. {
  324. setValues(current_x-interval,current_y);
  325. }
  326. else if (key.isKeyCode (KeyPress::rightKey))
  327. {
  328. setValues(current_x+interval,current_y);
  329. }
  330. else
  331. {
  332. return Component::keyPressed (key);
  333. }
  334. return true;
  335. }
  336. bool Joystick::keyStateChanged(const bool keyDown)
  337. {
  338. // only forward key events that aren't used by this component
  339. if (keyDown &&
  340. ! (KeyPress::isKeyCurrentlyDown (KeyPress::upKey)
  341. || KeyPress::isKeyCurrentlyDown (KeyPress::leftKey)
  342. || KeyPress::isKeyCurrentlyDown (KeyPress::downKey)
  343. || KeyPress::isKeyCurrentlyDown (KeyPress::rightKey)))
  344. {
  345. return Component::keyStateChanged(keyDown);
  346. }
  347. return false;
  348. }
  349. //==============================================================================
  350. void Joystick::addListener (JoystickListener* const listener)
  351. {
  352. jassert (listener != 0);
  353. if (listener != 0)
  354. listeners.add (listener);
  355. }
  356. void Joystick::removeListener (JoystickListener* const listener)
  357. {
  358. int index = listeners.indexOf (listener);
  359. if (index >= 0)
  360. listeners.remove (index);
  361. }
  362. void Joystick::sendChanges()
  363. {
  364. for (int i = listeners.size (); --i >= 0;)
  365. ((JoystickListener*) listeners.getUnchecked (i))->joystickValueChanged (this);
  366. }
  367. //==============================================================================
  368. void Joystick::calculateRatio()
  369. {
  370. // Calculate the x and y ratio
  371. x_ratio = getWidth() / (float) (x_max - x_min);
  372. y_ratio = getHeight() / (float) (y_max - y_min);
  373. }
  374. void Joystick::calculateSnapBack()
  375. {
  376. // Calculate the new snapback values based on the min and max
  377. // x_snap = roundDoubleToInt(floor((x_max-x_min)/2.0));
  378. // y_snap = roundDoubleToInt(floor((y_max-y_min)/2.0));
  379. x_snap = (x_max - x_min) / 2.0;
  380. y_snap = (y_max - y_min) / 2.0;
  381. // If the range is odd, then add 1 to X to calculate center correctly (HACK!)
  382. // if (roundDoubleToInt(x_max-x_min) % 2 != 0)
  383. // x_snap++;
  384. // Set the current location to the snapback
  385. current_x = x_snap;
  386. current_y = y_snap;
  387. }
  388. void Joystick::calculateDrawingSpot()
  389. {
  390. // Determine the current drawing location
  391. draw_x = roundDoubleToInt (floor (current_x * x_ratio));
  392. draw_y = roundDoubleToInt (floor (current_y * y_ratio));
  393. // If at max value, don't go over edge
  394. if (current_x == x_min) draw_x++;
  395. if (current_y == y_min) draw_y++;
  396. if (current_x == (x_max - x_min)) draw_x--;
  397. if (current_y == (y_max - y_min)) draw_y--;
  398. }
  399. const String Joystick::getTextFromValue (double v)
  400. {
  401. if (numDecimalPlaces > 0)
  402. return String (v, numDecimalPlaces);
  403. else
  404. return String (roundDoubleToInt (v));
  405. }
  406. //==============================================================================
  407. void Joystick::restoreMouseIfHidden()
  408. {
  409. if (mouseWasHidden)
  410. {
  411. mouseWasHidden = false;
  412. /*Component* c = Component::getComponentUnderMouse();
  413. if (c == 0)
  414. c = this;
  415. c->enableUnboundedMouseMovement (false);*/
  416. Point<int> p (getScreenX() + draw_x,
  417. getScreenY() + draw_y);
  418. Desktop::setMousePosition (p);
  419. }
  420. }
  421. //==============================================================================
  422. void Joystick::startedDragging()
  423. {
  424. }
  425. void Joystick::stoppedDragging()
  426. {
  427. }
  428. void Joystick::valueChanged (double x, double y)
  429. {
  430. }
  431. END_JUCE_NAMESPACE