1 /*
2 * spinner2.h
3 * DIN Is Noise is copyright (c) 2006-2021 Jagannathan Sampath
4 * For more information, please visit http://dinisnoise.org/
5 */
6 
7 
8 #ifndef __spinner22
9 #define __spinner22
10 
11 #include "input.h"
12 #include "font.h"
13 #include "widget.h"
14 #include "label.h"
15 #include "arrow_button.h"
16 #include "checkbutton.h"
17 #include "field.h"
18 #include "utils.h"
19 #include "mouse_slider.h"
20 #include "tokenizer.h"
21 
22 #include <string>
23 #include <typeinfo>
24 
25 extern void abort_selectors ();
26 extern int SPACING;
27 extern const char spc;
28 extern char BUFFER[];
29 
30 extern button& detach_from_menu (widget** wa, int n, int posx, int posy);
31 extern void attach_to_menu (widget** wa, int n);
32 
33 template <typename T> struct spinner2 : widget, state_listener, change_listener<field>, typing_listener, mouse_slider_listener, nullt {
34 
35 	template <typename Q> struct re_pre_lis : click_listener {
36 		spinner2<Q>& sp;
37 		click_listener* pre;
re_pre_lisspinner2::re_pre_lis38 		re_pre_lis (spinner2<Q>& _sp, click_listener* _pre) : sp (_sp), pre (_pre) {}
clickedspinner2::re_pre_lis39 		void clicked (button& b) {
40       widget* w[] = {&sp};
41 			attach_to_menu (w, 1);
42 			LISTEN (sp.dec, pre)
43 			LISTEN (sp.inc, pre)
44       if (sp.null) sp.set_value (0);
45 		}
46 	};
47 
48 	template <typename Q> struct pre_lis : click_listener {
49 		spinner2<Q>& sp;
50 		click_listener *decl, *incl;
pre_lisspinner2::pre_lis51 		pre_lis (spinner2<Q>& _sp, click_listener* _decl, click_listener* _incl) : sp (_sp), decl (_decl), incl (_incl) {}
clickedspinner2::pre_lis52 		void clicked (button& b) {
53 			sp.dec.set_listener (decl);
54 			sp.inc.set_listener (incl);
55 			widget* wa [] = {&sp};
56 			button& b_close = detach_from_menu (wa, 1, sp.dec.posx, sp.lbl.posy);
57 			LISTEN (b_close, &sp.reprel);
58       b.call_listener ();
59 		}
60 	};
61 
62 	template <typename Q> struct dec_lis : click_listener {
63 		spinner2<Q>& sp;
dec_lisspinner2::dec_lis64 		dec_lis (spinner2<Q>& _sp) : sp (_sp) {}
clickedspinner2::dec_lis65 		void clicked (button& b) {sp.decrease ();}
66 	};
67 
68 	template <typename Q> struct inc_lis : click_listener {
69 		spinner2<Q>& sp;
inc_lisspinner2::inc_lis70 		inc_lis (spinner2<Q>& _sp) : sp (_sp) {}
clickedspinner2::inc_lis71 		void clicked (button& b) {sp.increase ();}
72 	};
73 
74 	template <typename Q> struct more_lis : click_listener {
75 		spinner2<Q>& sp;
more_lisspinner2::more_lis76 		more_lis (spinner2<Q>& _sp) : sp (_sp) {}
clickedspinner2::more_lis77 		void clicked (button& b) {sp.toggle_delta();}
78 	};
79 
80 	template <typename Q> struct delta_lis : change_listener<field> {
81 		spinner2<Q>* sp;
spspinner2::delta_lis82 		delta_lis (spinner2<Q>* _sp = 0) : sp (_sp) {}
changedspinner2::delta_lis83 		void changed (field& f) {
84 			sp->delta0 = sp->delta = f;
85 			sp->set_pos (sp->posx, sp->posy);
86 			sp->call_listener (1, f);
87 		}
88 	};
89 
90 	template <typename Q> struct variance_lis : change_listener<field> {
91 		spinner2<Q>* sp;
spspinner2::variance_lis92 		variance_lis (spinner2<Q>* _sp = 0) : sp (_sp) {}
changedspinner2::variance_lis93 		void changed (field& f) {
94 			tokenizer tz (f.text);
95 			float mn, mx; tz >> mn >> mx;
96 			sp->variance.setrd (mn, mx);
97 			sp->call_listener (2, f);
98 		}
99 	};
100 
101   checkbutton lbl; // main label
102 
103 	label backlbl; // back label, after field
104 	int draw_backlbl;
105 
106   // increase/decrease buttons
107   arrow_button dec, inc;
updowndecincspinner2108   void updowndecinc () {
109     dec.dir = arrow_button::down;
110     inc.dir = arrow_button::up;
111   }
112 
113 	dec_lis<T> decl;
114 	inc_lis<T> incl;
115   int dir; // < 0 = decrease, > 0 = increase
116 
117   // pre listeners so can detach from menu
118 	int pre;
119 	pre_lis<T> prel;
120 	re_pre_lis<T> reprel;
121 
122   // value
123   field f_value;
124   T value;
125 	T lastv;
126 
127 	int draw_more;
128   arrow_button more;
129 	more_lis<T> mol;
130 
131   label l_delta;
132   field f_delta;
133 	delta_lis<T> dell;
134 
135 	struct variancet {
136 		checkbutton cb;
137 		label lbl;
138 		field fld;
139 		variance_lis<T> lis;
140 		rnd<float> rd;
141 		int ui;
lisspinner2::variancet142 		variancet (spinner2<T>* sp = 0) : lis (sp) {
143 			lbl.set_text (" ~% ");
144 			fld.change_lsnr = &lis;
145 			fld.expr = 0;
146       cb.set_text ("~");
147 			ui = 1;
148 		}
setrdspinner2::variancet149 		void setrd (float s, float t) {
150 			// assume s,t in %
151 			s /= 100.0f;
152 			t /= 100.0f;
153 			rd.set (s, t);
154 		}
setfldspinner2::variancet155     void setfld () {
156       int i = 100 * rd.min, j = 100 * rd.max;
157       sprintf (BUFFER, "%d %d", i, j);
158       fld.set_text (BUFFER);
159     }
160 	} variance;
161 
162   int limits;
163   T lo, hi;
164 
set_limitsspinner2165   void set_limits (T _lo, T _hi) {
166 		limits = 1;
167     lo = _lo;
168     hi = _hi;
169   }
170 
171   change_listener<field> *lis[3]; // 0 - f_value, 1 - f_delta, 2 - variance.fld = null
172 
173   spinner2 (const std::string& _name = "unknown") :
174 	decl(*this), incl(*this),
175 	pre(1),
176 	prel (*this, &decl, &incl),
177 	reprel (*this, &prel),
178 	mol(*this),
179 	l_delta ("+-"),  dell(this), variance(this) {
180 
181 #ifndef __WIDGET_MOVE__
182 		lbl.set_listener (this);
183 #endif
184 
185     widget* chld [] = {
186 			this,
187 			&dec,
188 			&inc,
189 			&f_value,
190 			&more,
191 			&l_delta,
192 			&f_delta,
193 			&variance.cb,
194 			&variance.lbl,
195 			&variance.fld
196 		};
197     for (int i = 0; i < 10; ++i) lbl.add_child (chld[i]);
198 
199     dec.set_dir (arrow_button::left);
200     inc.set_dir (arrow_button::right);
201 		inc.click_repeat = 1;
202 		dec.click_repeat = 1;
203 
204 		dir = 1;
205 		draw_more = 1;
206     more.set_dir (arrow_button::right);
207 
208 		set_pre (pre);
209 
210 		LISTEN (more,&mol)
211 
212     l_delta.hide ();
213     f_delta.hide ();
214 
215 		lastv = value = 0;
216 
217     f_value.change_lsnr = this;
218 		f_delta.change_lsnr = &dell;
219 
220 
221 		f_value.typing_lsnr = f_delta.typing_lsnr = this;
222 
223 		lis [0]=lis[1]=lis[2]=0;
224 
225     limits = 0;
226     lo = hi = 0;
227 
228 		// see field::call_listener ()
229 		const std::type_info& ti = typeid (T);
230 		std::string tn (ti.name());
231 		if (tn == "i")
232 			f_value.type = f_delta.type = "int";
233 		else if (tn == "f")
234 			f_value.type = f_delta.type = "double";
235 
236 		draw_backlbl = 0;
237 
238 
239   }
240 
set_posspinner2241   void set_pos (int x, int y) {
242     widget::set_pos (x, y);
243 		int i = 0;
244 		if (draw_more == 0 || variance.ui == 0) i = 1;
245     widget* w [] = {&variance.cb, &lbl, &dec, &inc, &f_value, &more, &variance.lbl, &variance.fld, &l_delta, &f_delta, };
246     int xshift [] = {0, 0, 0, -1, 1, -1, 0, 0, 5, 1};
247     int lft [] = {0, 0, fnt.lift, fnt.lift, 0, fnt.lift, 0, 0, 0, 0};
248     for (; i < 10; ++i) {
249 			x += xshift [i];
250 			widget* wi = w[i];
251 			wi->set_pos (x, y + lft[i]);
252 			advance_right (x, *wi, SPACING);
253     }
254 		set_pos_backlbl ();
255   }
256 
set_pos_backlblspinner2257 	void set_pos_backlbl () {
258 		if (draw_backlbl) {
259 			widget* w = 0;
260 			if (f_delta.visible) w = &f_delta;
261 			else if (draw_more) w = &more;
262 			else w = &f_value;
263 			backlbl.set_pos (w->extents.right + SPACING, w->extents.bottom - fnt.lift);
264 		}
265 	}
266 
updatespinner2267   void update () {
268     lbl.update ();
269 		backlbl.update ();
270     l_delta.update ();
271     f_value.update ();
272     f_delta.update ();
273 		variance.cb.update ();
274 		variance.lbl.update ();
275 		variance.fld.update ();
276     set_pos (posx, posy);
277 		inc.update ();
278 		dec.update ();
279 		more.update ();
280   }
281 
toggle_deltaspinner2282   void toggle_delta () {
283     if (more.dir == arrow_button::right) {
284       l_delta.show ();
285       f_delta.show ();
286 			if (variance.ui) {
287 				variance.lbl.show ();
288 				variance.fld.show ();
289 			}
290       more.set_dir (arrow_button::left);
291     } else {
292       l_delta.hide ();
293       f_delta.hide ();
294 			if (variance.ui) {
295 				variance.lbl.hide ();
296 				variance.fld.hide ();
297 			}
298       more.set_dir (arrow_button::right);
299     }
300 		set_pos_backlbl ();
301 		abort_selectors ();
302   }
303 
304   void change_value (int _dir, double scl = 1.0) {
305 		dir = _dir;
306 		delta = scl * delta0;
307 		value += dir_delta ();
308 		f_value = value;
309 		changed (f_value);
310 	}
311 
decreasespinner2312 	void decrease () {
313 		change_value (-1);
314 	}
315 
increasespinner2316 	void increase () {
317 		change_value (+1);
318 	}
319 
320 	spinner2<T>& operator++ () {
321 		change_value (+1);
322 		return *this;
323 	}
324 
325 	spinner2<T>& operator-- () {
326 		change_value (-1);
327 		return *this;
328 	}
329 
changedspinner2330 	void changed (checkbutton& cb) {
331 		if (orient == NONE) {
332 			cb.turn_off (0);
333 			if (mouse_slider0.active) cant_mouse_slide ();
334 		} else {
335 			if (cb.state) mouse_slider0.add (this); else mouse_slider0.remove (this);
336 			if (SHIFT == 0) activate_mouse_slider ();
337 		}
338 	}
339 
changedspinner2340   void changed (field& f) {
341 		if (limits) {
342 			T v = f;
343 			if (clamp<T>(lo, v, hi)) f = v;
344 		}
345 		value = f;
346 		if (f.edited) {
347 			delta = value - lastv;
348       if (delta > 0) {
349         dir = 1;
350       } else {
351         dir = -1;
352         delta = -delta;
353       }
354 		}
355 		lastv = value;
356 		set_pos (posx, posy);
357 		call_listener (0, f);
358   }
359 
call_listenerspinner2360 	void call_listener (int i, field& f) {
361 		change_listener<field>* lisi = lis[i];
362 		if (lisi) lisi->changed (f);
363 	}
364 
typingspinner2365   void typing (field& f) {
366     f.update ();
367     set_pos (posx, posy);
368   }
369 
mousedspinner2370 	void moused (int _dir, double scl) {
371 		change_value (_dir, scl);
372 	}
373 
after_slidespinner2374 	void after_slide () {
375 		lbl.turn_off (DONT_CALL_LISTENER);
376     set_delta (delta0 * mouse_slider0.scale.value);
377     if (null) set_value (0);
378 	}
379 
drawspinner2380   void draw () {
381     glColor3f (clr.r, clr.g, clr.b);
382 		widget* w [] = {&lbl, &dec, &inc, &f_value};
383 		for (int i = 0, j = 4; i < j; ++i) w[i]->draw ();
384 		if (variance.ui) variance.cb.draw ();
385 		if (draw_more) {
386 			more.draw ();
387 			if (more.dir == arrow_button::left) {
388 				l_delta.draw ();
389 				f_delta.draw ();
390 				if (variance.ui) {
391 					variance.lbl.draw ();
392 					variance.fld.draw ();
393 				}
394 			}
395 		}
396 		if (draw_backlbl) backlbl.draw ();
397   }
398 
set_valuespinner2399   void set_value (T t) {
400 		value = t;
401 		lastv = value;
402     f_value = value;
403 		set_pos (posx, posy);
404   }
405 
set_deltaspinner2406   void set_delta (T t) {
407 		delta0 = delta = t;
408     f_delta = (T) delta0;
409 		set_pos (posx, posy);
410   }
411 
412   void set_listener (change_listener<field>* _lis, int id = 0) {
413     lis [id] = _lis;
414   }
415 
416   void set_text (const std::string& l, const std::string& bl = "") {
417     lbl.set_text (l);
418     set_name (l);
419 		mouse_slider_listener::name = l;
420 		if (bl != "") {
421 			backlbl.set_text (bl);
422 			draw_backlbl = 1;
423 		}
424   }
425 
426 	void set_moveable (int m, int mc = 0, int* pmb = &lmb) {lbl.set_moveable (m, mc, pmb);}
427 
handle_inputspinner2428 	int handle_input () {
429 
430 		int r = lbl.handle_input ();
431 		if (r) return r;
432 
433 		if (variance.ui && variance.cb.handle_input()) return 1;
434 
435 		int d1 = 0, i1 = 0, m1 = 0;
436 		d1 = dec.handle_input ();
437 		if (d1 == 0) {
438 			i1 = inc.handle_input ();
439 			if (i1 == 0)
440 				m1 = more.handle_input ();
441 		}
442 
443 		int c = d1 | i1 | m1;
444 		if (c) return c;
445 
446 		int s = f_value.handle_input ();
447 		if (s) return s;
448 
449   	if (more.dir == arrow_button::left) {
450 			int fd = f_delta.handle_input ();
451 			if (fd) return fd;
452 			if (variance.ui) {
453 				if (variance.fld.handle_input()) return 1;
454 			}
455 		}
456 
457 		return 0;
458 
459 	}
460 
dir_deltaspinner2461 	inline T dir_delta () {
462 		return dir * delta;
463 	}
464 
operatorspinner2465 	inline T operator() () {
466     if (variance.cb.state) return ( variance.rd () * value ); else return value;
467 	}
468 
469 	void set (const std::string& t, T d, T lmin, T lmax, change_listener<field>* l = 0, int _pre = 1) {
470 		set_text (t);
471 		set_delta (d);
472 		set_limits (lmin, lmax);
473 		set_listener (l);
474 		set_pre (_pre);
475 	}
476 
setspinner2477 	void set (const std::string& t, T d, change_listener<field>* l) {
478 		set_text (t);
479 		set_delta (d);
480     set_listener (l);
481 	}
482 
setspinner2483   void set (T d, T lmin, T lmax, change_listener<field>* l) {
484     set_delta (d);
485     set_limits (lmin, lmax);
486     set_listener (l);
487   }
488 
set_prespinner2489 	void set_pre (int _pre) {
490 		pre = _pre;
491 		if (pre) {
492 			LISTEN (dec,&prel)
493 			LISTEN (inc,&prel)
494 		} else {
495 			LISTEN (dec,&decl)
496 			LISTEN (inc,&incl)
497 		}
498 	}
499 
500 };
501 
502 template <typename T> ifstream& operator>> (ifstream& f, spinner2<T>& spn) {
503 
504   int state;
505   float minn, maxx;
506 
507   f >> state >> minn >> maxx;
508 
509   spn.variance.cb.set_state (state, DONT_CALL_LISTENER);
510   spn.variance.rd.set (minn, maxx);
511   spn.variance.setfld ();
512 
513   return f;
514 
515 }
516 
517 template <typename T> ofstream& operator<< (ofstream& f, spinner2<T>& spn) {
518   f << spn.variance.cb.state << spc << spn.variance.rd.min << spc << spn.variance.rd.max << spc;
519   return f;
520 }
521 
522 #endif
523