GuiScrollBar.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /* GuiScrollBar.cpp
  2. *
  3. * Copyright (C) 1993-2011,2012,2013,2014,2015,2016,2017 Paul Boersma, 2013 Tom Naughton
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "GuiP.h"
  19. Thing_implement (GuiScrollBar, GuiControl, 0);
  20. #if motif
  21. #define iam_scrollbar \
  22. Melder_assert (widget -> widgetClass == xmScrollBarWidgetClass); \
  23. GuiScrollBar me = (GuiScrollBar) widget -> userData
  24. #else
  25. #define iam_scrollbar \
  26. GuiScrollBar me = (GuiScrollBar) _GuiObject_getUserData (widget)
  27. #endif
  28. #if gtk
  29. static void _GuiGtkScrollBar_destroyCallback (GuiObject /* widget */, gpointer void_me) {
  30. iam (GuiScrollBar);
  31. forget (me);
  32. }
  33. static void _GuiGtkScrollBar_valueChangedCallback (GuiObject widget, gpointer void_me) {
  34. iam (GuiScrollBar);
  35. trace (U"enter: blocked ", my d_blockValueChangedCallbacks);
  36. if (my d_blockValueChangedCallbacks) {
  37. my d_blockValueChangedCallbacks = false;
  38. } else if (my d_valueChangedCallback) {
  39. struct structGuiScrollBarEvent event { me };
  40. try {
  41. my d_valueChangedCallback (my d_valueChangedBoss, & event);
  42. } catch (MelderError) {
  43. Melder_flushError (U"Your action in a scroll bar was not completely handled.");
  44. }
  45. }
  46. }
  47. #elif motif
  48. void _GuiWinScrollBar_destroy (GuiObject widget) {
  49. _GuiNativeControl_destroy (widget);
  50. iam_scrollbar;
  51. forget (me); // NOTE: my widget is not destroyed here
  52. }
  53. #elif cocoa
  54. @interface GuiCocoaScrollBar ()
  55. @property (nonatomic, assign) double m_minimum;
  56. @property (nonatomic, assign) double m_maximum;
  57. @property (nonatomic, assign) double m_value;
  58. @property (nonatomic, assign) double m_sliderSize;
  59. @property (nonatomic, assign) double m_increment;
  60. @property (nonatomic, assign) double m_pageIncrement;
  61. @end
  62. // http://www.lucernesys.com/blog/2010/02/11/nsscroller/
  63. @implementation GuiCocoaScrollBar {
  64. GuiScrollBar d_userData;
  65. }
  66. - (void) dealloc { // override
  67. GuiScrollBar me = d_userData;
  68. forget (me);
  69. trace (U"deleting a scroll bar");
  70. [super dealloc];
  71. }
  72. - (GuiThing) getUserData {
  73. return d_userData;
  74. }
  75. - (void) setUserData: (GuiThing) userData {
  76. Melder_assert (userData == nullptr || Thing_isa (userData, classGuiScrollBar));
  77. d_userData = static_cast <GuiScrollBar> (userData);
  78. }
  79. - (void) setMinimum: (double)minimum maximum:(double)maximum value:(double)value sliderSize:(double)sliderSize increment:(double)increment pageIncrement:(double)pageIncrement {
  80. Melder_assert (isdefined (minimum));
  81. _m_minimum = minimum;
  82. _m_maximum = maximum;
  83. _m_value = value;
  84. _m_sliderSize = sliderSize;
  85. _m_increment = increment;
  86. _m_pageIncrement = pageIncrement;
  87. double spaceLeft = (maximum - minimum) - sliderSize;
  88. if (spaceLeft <= 0.0) {
  89. [self setKnobProportion: 1.0];
  90. [self setDoubleValue: 0.5];
  91. } else {
  92. [self setKnobProportion: sliderSize / (maximum - minimum)];
  93. [self setDoubleValue: (value - minimum) / spaceLeft];
  94. }
  95. }
  96. - (void) _update {
  97. GuiScrollBar me = (GuiScrollBar) self -> d_userData;
  98. [self setMinimum: _m_minimum maximum: _m_maximum value: _m_value sliderSize: _m_sliderSize increment: _m_increment pageIncrement: _m_pageIncrement];
  99. if (my d_valueChangedCallback) {
  100. struct structGuiScrollBarEvent event { me };
  101. try {
  102. my d_valueChangedCallback (my d_valueChangedBoss, & event);
  103. } catch (MelderError) {
  104. Melder_flushError (U"Scroll not completely handled.");
  105. }
  106. }
  107. }
  108. - (void) scrollBy: (double) step {
  109. trace (U"step ", step);
  110. if (step == 0) return;
  111. _m_value -= 0.3 * step * _m_increment;
  112. if (_m_value < _m_minimum)
  113. _m_value = _m_minimum;
  114. if (_m_value > _m_maximum - _m_sliderSize)
  115. _m_value = _m_maximum - _m_sliderSize;
  116. [self _update];
  117. }
  118. - (void) magnifyBy: (double) step {
  119. trace (U"step ", step);
  120. double increase = _m_sliderSize * (exp (- step) - 1.0);
  121. _m_sliderSize += increase;
  122. if (_m_sliderSize > _m_maximum - _m_minimum)
  123. _m_sliderSize = _m_maximum - _m_minimum;
  124. _m_value -= 0.5 * increase;
  125. if (_m_value < _m_minimum)
  126. _m_value = _m_minimum;
  127. if (_m_value > _m_maximum - _m_sliderSize)
  128. _m_value = _m_maximum - _m_sliderSize;
  129. [self _update];
  130. }
  131. - (void) valueChanged {
  132. GuiScrollBar me = (GuiScrollBar) d_userData;
  133. switch ([self hitPart]) {
  134. case NSScrollerIncrementLine: {
  135. // Include code here for the case where the down arrow is pressed
  136. _m_value += _m_increment;
  137. if (_m_value > _m_maximum - _m_sliderSize)
  138. _m_value = _m_maximum - _m_sliderSize;
  139. [self setMinimum: _m_minimum maximum: _m_maximum value: _m_value sliderSize: _m_sliderSize increment: _m_increment pageIncrement: _m_pageIncrement];
  140. } break;
  141. case NSScrollerIncrementPage: {
  142. // Include code here for the case where CTRL + down arrow is pressed, or the space the scroll knob moves in is pressed
  143. _m_value += _m_pageIncrement;
  144. if (_m_value > _m_maximum - _m_sliderSize)
  145. _m_value = _m_maximum - _m_sliderSize;
  146. [self setMinimum: _m_minimum maximum: _m_maximum value: _m_value sliderSize: _m_sliderSize increment: _m_increment pageIncrement: _m_pageIncrement];
  147. } break;
  148. case NSScrollerDecrementLine: {
  149. // Include code here for the case where the up arrow is pressed
  150. _m_value -= _m_increment;
  151. if (_m_value < _m_minimum)
  152. _m_value = _m_minimum;
  153. [self setMinimum: _m_minimum maximum: _m_maximum value: _m_value sliderSize: _m_sliderSize increment: _m_increment pageIncrement: _m_pageIncrement];
  154. } break;
  155. case NSScrollerDecrementPage: {
  156. // Include code here for the case where CTRL + up arrow is pressed, or the space the scroll knob moves in is pressed
  157. _m_value -= _m_pageIncrement;
  158. if (_m_value < _m_minimum)
  159. _m_value = _m_minimum;
  160. [self setMinimum: _m_minimum maximum: _m_maximum value: _m_value sliderSize: _m_sliderSize increment: _m_increment pageIncrement: _m_pageIncrement];
  161. } break;
  162. case NSScrollerKnob: {
  163. // This case is when the knob itself is pressed
  164. double spaceLeft = (_m_maximum - _m_minimum) - _m_sliderSize;
  165. _m_value = _m_minimum + [self doubleValue] * (spaceLeft <= 0.0 ? 0.0 : spaceLeft);
  166. } break;
  167. default: {
  168. } break;
  169. }
  170. if (my d_valueChangedCallback) {
  171. struct structGuiScrollBarEvent event { me };
  172. try {
  173. my d_valueChangedCallback (my d_valueChangedBoss, & event);
  174. } catch (MelderError) {
  175. Melder_flushError (U"Scroll not completely handled.");
  176. }
  177. }
  178. }
  179. @end
  180. #endif
  181. #if motif
  182. static void _GuiMotifScrollBar_valueChangedCallback (GuiObject widget, XtPointer void_me, XtPointer call) {
  183. iam (GuiScrollBar);
  184. if (my d_valueChangedCallback) {
  185. struct structGuiScrollBarEvent event { me };
  186. try {
  187. my d_valueChangedCallback (my d_valueChangedBoss, & event);
  188. } catch (MelderError) {
  189. Melder_flushError (U"Scroll not completely handled.");
  190. }
  191. }
  192. }
  193. #endif
  194. GuiScrollBar GuiScrollBar_create (GuiForm parent, int left, int right, int top, int bottom,
  195. double minimum, double maximum, double value, double sliderSize, double increment, double pageIncrement,
  196. GuiScrollBarCallback valueChangedCallback, Thing valueChangedBoss, uint32 flags)
  197. {
  198. autoGuiScrollBar me = Thing_new (GuiScrollBar);
  199. my d_shell = parent -> d_shell;
  200. my d_parent = parent;
  201. my d_valueChangedCallback = valueChangedCallback;
  202. my d_valueChangedBoss = valueChangedBoss;
  203. #if gtk
  204. GtkObject *adj = gtk_adjustment_new (value, minimum, maximum, increment, pageIncrement, sliderSize);
  205. my d_widget = flags & GuiScrollBar_HORIZONTAL ? gtk_hscrollbar_new (GTK_ADJUSTMENT (adj)) : gtk_vscrollbar_new (GTK_ADJUSTMENT (adj));
  206. _GuiObject_setUserData (my d_widget, me.get());
  207. my v_positionInForm (my d_widget, left, right, top, bottom, parent);
  208. g_signal_connect (G_OBJECT (my d_widget), "value-changed", G_CALLBACK (_GuiGtkScrollBar_valueChangedCallback), me.get());
  209. #elif motif
  210. my d_widget = XtVaCreateWidget (flags & GuiScrollBar_HORIZONTAL ? "horizontalScrollBar" : "verticalScrollBar", // the name is checked for deciding the orientation...
  211. xmScrollBarWidgetClass, parent -> d_widget,
  212. XmNorientation, flags & GuiScrollBar_HORIZONTAL ? XmHORIZONTAL : XmVERTICAL,
  213. XmNminimum, (int) minimum,
  214. XmNmaximum, (int) maximum,
  215. XmNvalue, (int) value,
  216. XmNsliderSize, (int) sliderSize,
  217. XmNincrement, (int) increment,
  218. XmNpageIncrement, (int) pageIncrement,
  219. nullptr);
  220. _GuiObject_setUserData (my d_widget, me.get());
  221. my v_positionInForm (my d_widget, left, right, top, bottom, parent);
  222. XtAddCallback (my d_widget, XmNvalueChangedCallback, _GuiMotifScrollBar_valueChangedCallback, (XtPointer) me.get());
  223. XtAddCallback (my d_widget, XmNdragCallback, _GuiMotifScrollBar_valueChangedCallback, (XtPointer) me.get());
  224. #elif cocoa
  225. NSRect dummyFrame = flags & GuiScrollBar_HORIZONTAL ? NSMakeRect (20, 20, 100, [NSScroller scrollerWidth]) : NSMakeRect (20, 20, [NSScroller scrollerWidth], 100);
  226. GuiCocoaScrollBar *scroller = [[GuiCocoaScrollBar alloc] initWithFrame: dummyFrame];
  227. my d_widget = scroller;
  228. my v_positionInForm (my d_widget, left, right, top, bottom, parent);
  229. [scroller setUserData: me.get()];
  230. [scroller setEnabled: YES];
  231. [scroller setMinimum: minimum maximum: maximum value: value sliderSize: sliderSize increment: increment pageIncrement: pageIncrement];
  232. //[scroller setScrollerStyle: NSScrollerStyleOverlay];
  233. [scroller setTarget: scroller];
  234. [scroller setAction: @selector (valueChanged)];
  235. #endif
  236. return me.releaseToAmbiguousOwner();
  237. }
  238. GuiScrollBar GuiScrollBar_createShown (GuiForm parent, int left, int right, int top, int bottom,
  239. double minimum, double maximum, double value, double sliderSize, double increment, double pageIncrement,
  240. GuiScrollBarCallback valueChangedCallback, Thing valueChangedBoss, uint32 flags)
  241. {
  242. GuiScrollBar me = GuiScrollBar_create (parent, left, right, top, bottom,
  243. minimum, maximum, value, sliderSize, increment, pageIncrement,
  244. valueChangedCallback, valueChangedBoss, flags);
  245. GuiThing_show (me);
  246. return me;
  247. }
  248. void GuiScrollBar_set (GuiScrollBar me, double minimum, double maximum, double value, double sliderSize, double increment, double pageIncrement) {
  249. /*
  250. * This function calls the native scroll bar modification function.
  251. *
  252. * Note:
  253. * On almost all platforms, using the native scroll bar modification function sends a value-changed notification to the scroll bar.
  254. * This will call our own d_valueChangedCallback if we don't prevent it.
  255. * We have to prevent that, because our d_valueChangedCallback is only for user-initiated modifications.
  256. */
  257. trace (U"enter ", minimum, U" ", maximum, U" ", value, U" ", sliderSize, U" ", increment, U" ", pageIncrement);
  258. #if gtk
  259. /*
  260. * We're going to modify the scroll bar with gtk_adjustment_configure ().
  261. * This function sends a *slow* value-changed notification to the scroll bar.
  262. * We have to make sure that our own d_valueChangedCallback is not called.
  263. */
  264. my d_blockValueChangedCallbacks = true;
  265. GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (my d_widget));
  266. gtk_adjustment_configure (GTK_ADJUSTMENT (adj),
  267. isdefined (value) ? value : gtk_adjustment_get_value (GTK_ADJUSTMENT (adj)),
  268. isdefined (minimum) ? minimum : gtk_adjustment_get_lower (GTK_ADJUSTMENT (adj)),
  269. isdefined (maximum) ? maximum : gtk_adjustment_get_upper (GTK_ADJUSTMENT (adj)),
  270. isdefined (increment) ? increment : gtk_adjustment_get_step_increment (GTK_ADJUSTMENT (adj)),
  271. isdefined (pageIncrement) ? pageIncrement : gtk_adjustment_get_page_increment (GTK_ADJUSTMENT (adj)),
  272. isdefined (sliderSize) ? sliderSize : gtk_adjustment_get_page_size (GTK_ADJUSTMENT (adj)));
  273. /*
  274. * We don't set d_blockValueChangedCallbacks back to false yet, because GTK calls the valueChangedCallback with a delay.
  275. */
  276. #elif motif
  277. if (isdefined (minimum))
  278. XtVaSetValues (my d_widget, XmNminimum, (int) minimum, nullptr);
  279. if (isdefined (maximum))
  280. XtVaSetValues (my d_widget, XmNmaximum, (int) maximum, nullptr);
  281. int oldValue, oldSliderSize, oldIncrement, oldPageIncrement;
  282. XmScrollBarGetValues (my d_widget, & oldValue, & oldSliderSize, & oldIncrement, & oldPageIncrement);
  283. XmScrollBarSetValues (my d_widget,
  284. isdefined (value) ? value : oldValue,
  285. isdefined (sliderSize) ? sliderSize : oldSliderSize,
  286. isdefined (increment) ? increment : oldIncrement,
  287. isdefined (pageIncrement) ? pageIncrement : oldPageIncrement,
  288. False);
  289. #elif cocoa
  290. /*
  291. * We're going to modify the scroll bar with setMinimum:maximum:...
  292. * This function sends a *synchronous* value-changed notification to the scroll bar.
  293. * We have to make sure that our own d_valueChangedCallback is not called.
  294. */
  295. GuiControlBlockValueChangedCallbacks block (me);
  296. GuiCocoaScrollBar *scroller = (GuiCocoaScrollBar *) my d_widget;
  297. [scroller
  298. setMinimum: isdefined (minimum) ? minimum : [scroller m_minimum]
  299. maximum: isdefined (maximum) ? maximum : [scroller m_maximum]
  300. value: isdefined (value) ? value : [scroller m_value]
  301. sliderSize: isdefined (sliderSize) ? sliderSize : [scroller m_sliderSize]
  302. increment: isdefined (increment) ? increment : [scroller m_increment]
  303. pageIncrement: isdefined (pageIncrement) ? pageIncrement : [scroller m_pageIncrement]];
  304. #endif
  305. trace (U"exit");
  306. }
  307. double GuiScrollBar_getValue (GuiScrollBar me) {
  308. #if gtk
  309. return gtk_range_get_value (GTK_RANGE (my d_widget));
  310. #elif motif
  311. int value, slider, incr, pincr;
  312. XmScrollBarGetValues (my d_widget, & value, & slider, & incr, & pincr);
  313. return value;
  314. #elif cocoa
  315. GuiCocoaScrollBar *scroller = (GuiCocoaScrollBar *) my d_widget;
  316. return [scroller m_value];
  317. #else
  318. return 0;
  319. #endif
  320. }
  321. double GuiScrollBar_getSliderSize (GuiScrollBar me) {
  322. #if gtk
  323. return 1; // NYI
  324. #elif motif
  325. int value, slider, incr, pincr;
  326. XmScrollBarGetValues (my d_widget, & value, & slider, & incr, & pincr);
  327. return slider;
  328. #elif cocoa
  329. GuiCocoaScrollBar *scroller = (GuiCocoaScrollBar *) my d_widget;
  330. return [scroller m_sliderSize];
  331. #else
  332. return 0;
  333. #endif
  334. }
  335. /* End of file GuiScrollBar.cpp */