Jamoma API  0.6.0.a19
PureData/source/j.ui/j.ui.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup implementationPdExternals
4  *
5  * @brief j.ui : Provide standard user interface component for modules
6  *
7  * @details
8  *
9  * @authors Tim Place, Trond Lossius, Antoine Villeret
10  *
11  * @copyright © 2007 by Tim Place @n
12  * Copyright © 2015, Antoine Villeret@n
13  * This code is licensed under the terms of the "New BSD License" @n
14  * http://creativecommons.org/licenses/BSD/
15  */
16 
17 
18 #include "j.ui.h"
19 #include "TTUiInfo.h"
20 
21 // class variables
22 static t_class *s_ui_class = NULL;
23 
24 static t_jrgba s_color_background_button = {0.2, 0.2, 0.2, 1.0};
25 static t_jrgba s_color_border_button = {0.4, 0.4, 0.4, 1.0};
26 static t_jrgba s_color_text_button_on = {0.7, 0.7, 0.7, 1.0};
27 static t_jrgba s_color_text_button_off = {0.6, 0.6, 0.6, 1.0};
28 
29 static t_jrgba s_color_blue_button = {0.2, 0.2, 0.7, 1.0};
30 static t_jrgba s_color_blue_ring = {0.45, 0.45, 1.0, 1.0};
31 static t_jrgba s_color_darkblue = {0.1, 0.1, 0.5, 1.0};
32 
33 static t_jrgba s_color_orange_button = {0.8, 0.6, 0.2, 1.0};
34 static t_jrgba s_color_red_button = {0.6, 0.2, 0.2, 1.0};
35 
36 static t_jrgba s_color_green_button = {0.2, 0.7, 0.2, 1.0};
37 static t_jrgba s_color_green_ring = {0.25, 0.75, 0.25, 1.0};
38 static t_jrgba s_color_darkgreen = {0.05, 0.4, 0.05, 1.0};
39 
40 static t_jrgba s_color_selected = {0.62, 0., 0.36, 0.70};
41 
42 
43 #pragma mark -
44 #pragma mark life cycle
45 
46 extern "C" void JAMOMA_EXPORT_MAXOBJ setup_j0x2eui(void){
47  long flags;
48  t_eclass *c;
49 
50  jamoma_init();
51  common_symbols_init();
52 
53  TTUiInfo::registerClass();
54 
55  c = eclass_new("j.ui",
56  (method)ui_new,
57  (method)ui_free,
58  sizeof(t_ui),
59  0,
60  A_GIMME,
61  0);
62 
63  //flags = JBOX_TEXTFIELD | JBOX_COLOR;
64  flags = JBOX_TEXTFIELD;
65  jbox_initclass(c, flags);
66  c->c_flags |= CLASS_FLAG_NEWDICTIONARY; // to specify dictionary constructor
67 
68  eclass_addmethod(c, (method)ui_notify, "notify", A_CANT, 0);
69  eclass_addmethod(c, (method)ui_paint, "paint", A_CANT, 0);
70  eclass_addmethod(c, (method)ui_mousedown, "mousedown", A_CANT, 0);
71  eclass_addmethod(c, (method)ui_mousedragdelta, "mousedragdelta", A_CANT, 0);
72  eclass_addmethod(c, (method)ui_mouseup, "mouseup", A_CANT, 0);
73  eclass_addmethod(c, (method)ui_mousemove, "mousemove", A_CANT, 0);
74  eclass_addmethod(c, (method)ui_mouseleave, "mouseleave", A_CANT, 0);
75  eclass_addmethod(c, (method)ui_oksize, "oksize", A_CANT, 0);
76 
77  eclass_addmethod(c, (method)ui_edit_state, "dblclick", A_CANT, 0);
78  eclass_addmethod(c, (method)ui_edclose, "edclose", A_CANT, 0);
79 
80  eclass_addmethod(c, (method)ui_modelParamExplorer_callback, "return_modelParamExploration", A_CANT, 0);
81  eclass_addmethod(c, (method)ui_modelMessExplorer_callback, "return_modelMessExploration", A_CANT, 0);
82  eclass_addmethod(c, (method)ui_modelRetExplorer_callback, "return_modelRetExploration", A_CANT, 0);
83 
84  eclass_addmethod(c, (method)ui_return_model_address, "return_model_address", A_CANT, 0);
85 
86  eclass_addmethod(c, (method)ui_return_model_init, "return_model_init", A_CANT, 0);
87 
88  eclass_addmethod(c, (method)ui_return_model_content, "return_model_content", A_CANT, 0);
89 
90  eclass_addmethod(c, (method)ui_return_mute, "return_mute", A_CANT, 0);
91  eclass_addmethod(c, (method)ui_return_bypass, "return_bypass", A_CANT, 0);
92  eclass_addmethod(c, (method)ui_return_mix, "return_mix", A_CANT, 0);
93  eclass_addmethod(c, (method)ui_return_gain, "return_gain", A_CANT, 0);
94  eclass_addmethod(c, (method)ui_return_freeze, "return_freeze", A_CANT, 0);
95  eclass_addmethod(c, (method)ui_return_active, "return_active", A_CANT, 0);
96  eclass_addmethod(c, (method)ui_return_preset_names, "return_preset_names", A_CANT, 0);
97 
98  CLASS_ATTR_DEFAULT(c, "patching_rect", 0, "0. 0. 300. 70.");
99  CLASS_ATTR_DEFAULT(c, "fontname", 0, JAMOMA_DEFAULT_FONT);
100  CLASS_ATTR_DEFAULT(c, "fontsize", 0, "11");
101 
102  CLASS_STICKY_ATTR(c, "category", 0, "Color");
103 
104  CLASS_ATTR_RGBA(c, "bgcolor", 0, t_ui, bgcolor);
105  CLASS_ATTR_DEFAULTNAME_SAVE_PAINT(c, "bgcolor", 0, "0.93 0.93 0.93 1.0");
106  CLASS_ATTR_STYLE(c, "bgcolor", 0, "rgba");
107 
108  CLASS_ATTR_RGBA(c, "bordercolor", 0, t_ui, bordercolor);
109  CLASS_ATTR_DEFAULTNAME_SAVE_PAINT(c, "bordercolor", 0, "0.6 0.6 0.6 1.0");
110  CLASS_ATTR_STYLE(c, "bordercolor", 0, "rgba");
111 
112  CLASS_ATTR_RGBA(c, "headercolor", 0, t_ui, headercolor);
113  CLASS_ATTR_DEFAULTNAME_SAVE_PAINT(c, "headercolor", 0, "0.82 0.82 0.82 1.0");
114  CLASS_ATTR_STYLE(c, "headercolor", 0, "rgba");
115 
116  CLASS_ATTR_RGBA(c, "textcolor", 0, t_ui, textcolor);
117  CLASS_ATTR_DEFAULTNAME_SAVE_PAINT(c, "textcolor", 0, "0. 0. 0. 1.0");
118  CLASS_ATTR_STYLE(c, "textcolor", 0, "rgba");
119 
120  CLASS_ATTR_RGBA(c, "highlightcolor", 0, t_ui, highlightcolor);
121  CLASS_ATTR_DEFAULTNAME_SAVE_PAINT(c, "highlightcolor", 0, "0. 0. 0. 1.0");
122  CLASS_ATTR_STYLE(c, "highlightcolor", 0, "rgba");
123 
124  CLASS_STICKY_ATTR_CLEAR(c, "category");
125  CLASS_STICKY_ATTR(c, "category", 0, "Jamoma");
126 
127  CLASS_STICKY_ATTR_CLEAR(c, "category");
128 
129  class_register(CLASS_BOX, c);
130  s_ui_class = c;
131  return 0;
132 }
133 
134 t_ui* ui_new(t_symbol *s, long argc, t_atom *argv)
135 {
136  t_ui *x = NULL;
137  t_dictionary *d = NULL;
138  long flags;
139  TTValue f, out, filters;
140 
141  if (!(d=object_dictionaryarg(argc, argv)))
142  return NULL;
143 
144  x = (t_ui*)object_alloc(s_ui_class);
145  if (x) {
146  flags = 0
147  | JBOX_DRAWFIRSTIN
148  | JBOX_NODRAWBOX
149  | JBOX_TRANSPARENT
150  | JBOX_GROWBOTH
151  | JBOX_HILITE
152  | JBOX_BACKGROUND
153  | JBOX_MOUSEDRAGDELTA
154  | JBOX_TEXTFIELD
155  ;
156 
157  jbox_new(&x->box, flags, argc, argv);
158  x->box.b_firstin = (t_object *)x;
159 
160  // Make two outlets
161  x->outlets = (TTHandle)sysmem_newptr(sizeof(TTPtr) * 1);
162  x->outlets[panel_out] = outlet_new(x, NULL); // outlet to open panel
163 
164  x->uiInfo = NULL;
165 
166  x->menu_items = NULL;
167  x->refmenu_items = NULL;
168  x->hash_datas = new TTHash();
169  x->hash_viewers = new TTHash();
170  x->hash_receivers = new TTHash();
171  x->preset_names = NULL;
172  x->preset_num = 0;
173 
174  jbox_ready(&x->box);
175 
176  x->menu_items = (t_linklist *)linklist_new();
177  x->menu_qelem = qelem_new(x, (method)ui_menu_qfn);
178 
179  x->refmenu_items = (t_linklist *)linklist_new();
180  x->refmenu_qelem = qelem_new(x, (method)ui_refmenu_qfn);
181 
182  x->viewAddress = kTTAdrsEmpty;
183  x->modelAddress = kTTAdrsEmpty;
184  x->patcherPtr = NULL;
185  x->patcherContext = kTTSymEmpty;
186  x->patcherClass = kTTSymEmpty;
187  x->patcherName = kTTSymEmpty;
188 
189  x->highlight = false;
190  x->hover = false;
191 
192  x->has_preset = false;
193  x->has_model = false;
194  x->has_panel = false;
195  x->has_mute = false;
196  x->has_gain = false;
197  x->has_bypass = false;
198  x->has_mix = false;
199  x->has_active = false;
200  x->has_freeze = false;
201 
202  x->highlight_mute = false;
203  x->highlight_gain = false;
204  x->highlight_bypass = false;
205  x->highlight_mix = false;
206  x->highlight_active = false;
207  x->highlight_freeze = false;
208 
209  x->text = NULL;
210  x->textEditor = NULL;
211  x->textHandler = NULL;
212  x->state = NULL;
213 
214  x->patcher_panel = NULL;
215 
216  attr_dictionary_process(x, d); // handle attribute args
217 
218  // The following must be deferred because we have to interrogate our box,
219  // and our box is not yet valid until we have finished instantiating the object.
220  // Trying to use a loadbang method instead is also not fully successful (as of Max 5.0.6)
221  defer_low((t_object*)x, (method)ui_register_info, NULL, 0, 0);
222 
223  // The following must be deferred because we have to interrogate our box,
224  // and our box is not yet valid until we have finished instantiating the object.
225  // Trying to use a loadbang method instead is also not fully successful (as of Max 5.0.6)
226  defer_low((t_object*)x, (method)ui_build, NULL, 0, 0);
227 
228  // The following must be deferred because we have to interrogate our box,
229  // and our box is not yet valid until we have finished instantiating the object.
230  // Trying to use a loadbang method instead is also not fully successful (as of Max 5.0.6)
231  defer_low((t_object*)x, (method)ui_view_panel_attach, NULL, 0, 0);
232  }
233  return x;
234 }
235 
236 void ui_free(t_ui *x)
237 {
238  jbox_free(&x->box);
239 
240  qelem_free(x->menu_qelem);
241  x->menu_qelem = NULL;
242  object_free(x->menu_items);
243 
244  qelem_free(x->refmenu_qelem);
245  x->refmenu_qelem = NULL;
246  object_free(x->refmenu_items);
247 
248  if (x->preset_names)
249  sysmem_freeptr(x->preset_names);
250 
251  ui_receiver_destroy_all(x);
252 
253  ui_unregister_info(x);
254  ui_viewer_destroy_all(x);
255 }
256 
257 
258 #pragma mark -
259 #pragma mark methods
260 
261 t_max_err ui_notify(t_ui *x, t_symbol *s, t_symbol *msg, void *sender, void *data)
262 {
263  t_object *textfield;
264  t_symbol *attrname;
265 
266  if ((msg == _sym_attr_modified) && (sender == x)) {
267  attrname = (t_symbol *)object_method((t_object *)data, gensym("getname"));
268  textfield = jbox_get_textfield((t_object*) x);
269  if (textfield)
270  textfield_set_textcolor(textfield, &x->textcolor);
271 
272  if (attrname == gensym("address"))
273  object_method(textfield, gensym("settext"), x->modelAddress.c_str());
274 
275  jbox_redraw(&x->box);
276  }
277  return jbox_notify((t_jbox*)x, s, msg, sender, data);
278 }
279 
280 void ui_subscribe(t_ui *x, t_symbol *address)
281 {
282  TTAddress adrs = TTAddress(address->s_name);
283  TTValue v, args;
284  TTAttributePtr anAttribute;
285  TTObject aReceiver;
286  TTErr err;
287 
288  if ((x->modelAddress == kTTAdrsEmpty && adrs != kTTAdrsEmpty) || adrs != x->modelAddress) {
289 
290  x->modelAddress = adrs;
291 
292  // Clear all viewers
293  ui_viewer_destroy_all(x);
294  x->hash_viewers = new TTHash();
295 
296  x->has_preset = false;
297  x->has_model = false;
298  x->has_mute = false;
299  x->has_gain = false;
300  x->has_bypass = false;
301  x->has_mix = false;
302  x->has_active = false;
303  x->has_freeze = false;
304 
305  if (x->preset_names) {
306  sysmem_freeptr(x->preset_names);
307  x->preset_names = NULL;
308  }
309 
310  if (x->hash_receivers->lookup(kTTSym_initialized, v))
311 
312  // observe model initialisation to explore (the method also get the value)
313  ui_receiver_create(x, aReceiver, gensym("return_model_init"), kTTSym_initialized, x->modelAddress, NO, YES);
314 
315  else {
316 
317  // update the model address and get the initialized state
318  aReceiver = v[0];
319  aReceiver.set(kTTSym_address, x->modelAddress.appendAttribute(kTTSym_initialized));
320  }
321 
322  // create internal TTPreset to handle model's state
323  if (!x->state.valid()) {
324  x->state = TTObject(kTTSym_Preset);
325  x->state.set(kTTSym_Flatten, YES);
326  }
327 
328  // create internal TTTextHandler to edit model's state via the Max text editor
329  if (!x->textHandler.valid()) {
330  x->textHandler = TTObject(kTTSym_TextHandler);
331  x->textHandler.set(kTTSym_object, x->state);
332  }
333 
334  // set the model address
335  x->state.set(kTTSym_address, x->modelAddress);
336  }
337 
338  // The following must be deferred because
339  // we have to wait each component of the model are registered
340  defer_low((t_object*)x, (method)ui_build, NULL, 0, 0);
341 }
342 
343 void ui_build(t_ui *x)
344 {
345  TTValue v, n, p, args;
346  t_symbol *hierarchy, *moduleHierarchy;
347  t_object *box, *textfield;
348  t_object *modulePatcher, *moduleBox;
349  t_rect boxRect;
350  t_rect uiRect;
351 
352  // Examine the context to resize the view, set textfield, ...
353 
354  x->patcherPtr = jamoma_patcher_get((t_object*)x);
355  hierarchy = jamoma_patcher_get_hierarchy(x->patcherPtr);
356 
357  box = object_attr_getobj(x->patcherPtr, gensym("box"));
358  object_attr_get_rect((t_object*)x, _sym_presentation_rect, &uiRect);
359 
360  if (hierarchy == _sym_bpatcher) {
361 
362  // resize the bpatcher
363  object_attr_get_rect(box, _sym_patching_rect, &boxRect);
364  boxRect.width = uiRect.width;
365  boxRect.height = uiRect.height;
366  object_attr_set_rect(box, _sym_patching_rect, &boxRect);
367  object_attr_get_rect(box, _sym_presentation_rect, &boxRect);
368  boxRect.width = uiRect.width;
369  boxRect.height = uiRect.height;
370  object_attr_set_rect(box, _sym_presentation_rect, &boxRect);
371 
372  // Module CASE : is this bpatcher inside another bpatcher ?
373  modulePatcher = jamoma_patcher_get(x->patcherPtr);
374  moduleHierarchy = jamoma_patcher_get_hierarchy(modulePatcher);
375 
376  moduleBox = object_attr_getobj(modulePatcher, gensym("box"));
377 
378  if (moduleHierarchy == _sym_bpatcher) {
379 
380  // is there no j.ui object inside the module bpatcher
381  if (!jamoma_patcher_get_ui(modulePatcher)) {
382 
383  // resize the module bpatcher
384  object_attr_get_rect(moduleBox, _sym_patching_rect, &boxRect);
385  boxRect.width = uiRect.width;
386  boxRect.height = uiRect.height;
387  object_attr_set_rect(moduleBox, _sym_patching_rect, &boxRect);
388  object_attr_get_rect(moduleBox, _sym_presentation_rect, &boxRect);
389  boxRect.width = uiRect.width;
390  boxRect.height = uiRect.height;
391  object_attr_set_rect(moduleBox, _sym_presentation_rect, &boxRect);
392  }
393  }
394  }
395  else if (hierarchy == _sym_subpatcher) {
396  object_attr_get_rect(x->patcherPtr, _sym_defrect, &boxRect);
397  boxRect.width = uiRect.width;
398  boxRect.height = uiRect.height;
399  object_attr_set_rect(x->patcherPtr, _sym_defrect, &boxRect);
400  object_attr_setchar(x->patcherPtr, _sym_toolbarvisible, 0);
401  object_method_parse(x->patcherPtr, _sym_window, "flags nogrow", NULL); // get rid of the grow thingies
402  object_method_parse(x->patcherPtr, _sym_window, "flags nozoom", NULL); // disable maximize button
403  object_method_parse(x->patcherPtr, _sym_window, "exec", NULL);
404 
405  // set the window title to the module class, j.ui shows osc_name already
406  if (x->patcherClass != kTTSymEmpty)
407  object_attr_setsym(x->patcherPtr, _sym_title, gensym((char*)x->patcherClass.c_str()));
408  else
409  object_attr_setsym(x->patcherPtr, _sym_title, gensym("No class"));
410 
411  object_attr_setchar(x->patcherPtr, _sym_enablehscroll, 0); // turn off scroll bars
412  object_attr_setchar(x->patcherPtr, _sym_enablevscroll, 0);
413  }
414 
415  // Set the textfield to display the address
416  textfield = jbox_get_textfield((t_object*) x);
417  if (textfield)
418  ui_paint_address(x, textfield);
419 
420  // Redraw
421  jbox_redraw(&x->box);
422 }
423 
424 t_object *ui_get_model_object(t_ui *x)
425 {
426  TTNodePtr patcherNode;
427  t_object *obj;
428  t_object *modelObject = NULL;
429  t_symbol *_sym_jclass, *_sym_jmodel = gensym("j.model");
430 
431  // get model patcher
432  if (!accessApplicationLocalDirectory->getTTNode(x->modelAddress, &patcherNode)) {
433 
434  if (patcherNode->getContext()) {
435 
436  // find the j.model object inside the model patcher
437  obj = object_attr_getobj((t_object*)patcherNode->getContext(), _sym_firstobject);
438 
439  while (obj) {
440 
441  _sym_jclass = object_attr_getsym(obj, _sym_maxclass);
442  if (_sym_jclass == _sym_jmodel) {
443 
444  modelObject = object_attr_getobj(obj, _sym_object);
445  break;
446  }
447  obj = object_attr_getobj(obj, _sym_nextobject);
448  }
449  }
450  }
451 
452  return modelObject;
453 }
454 
455 #pragma mark -
456 #pragma mark drawing and appearance
457 
458 void ui_paint(t_ui *x, t_object *view)
459 {
460  TTSymbol highlight = kTTSym_none;
461  TTValue v;
462  t_rect rect;
463  t_jgraphics *g;
464  double border_thickness = 0.5;
465  double cornersize = 12.0;
466  double middle;
467  t_jrgba headercolor;
468  t_jrgba bgcolor;
469  t_jrgba bordercolor;
470 
471  g = (t_jgraphics*) patcherview_get_jgraphics(view);
472  jbox_get_rect_for_view((t_object*) &x->box, view, &rect);
473 
474  // process highlight
475  if (x->uiInfo.valid()) {
476  x->uiInfo.get("highlight", v);
477  highlight = v[0];
478  }
479 
480  if (highlight == kTTSym_none) {
481  headercolor = x->headercolor;
482  bgcolor = x->bgcolor;
483  bordercolor = x->bordercolor;
484  }
485  else {
486  headercolor.red = 0.8*x->headercolor.red + 0.2*x->highlightcolor.red;
487  headercolor.green = 0.8*x->headercolor.green + 0.2*x->highlightcolor.green;
488  headercolor.blue = 0.8*x->headercolor.blue + 0.2*x->highlightcolor.blue;
489  headercolor.alpha = x->headercolor.alpha;
490 
491  bgcolor.red = 0.8*x->bgcolor.red + 0.2*x->highlightcolor.red;
492  bgcolor.green = 0.8*x->bgcolor.green + 0.2*x->highlightcolor.green;
493  bgcolor.blue = 0.8*x->bgcolor.blue + 0.2*x->highlightcolor.blue;
494  bgcolor.alpha = x->bgcolor.alpha;
495 
496  bordercolor.red = 0.8*x->bordercolor.red + 0.2*x->highlightcolor.red;
497  bordercolor.green = 0.8*x->bordercolor.green + 0.2*x->highlightcolor.green;
498  bordercolor.blue = 0.8*x->bordercolor.blue + 0.2*x->highlightcolor.blue;
499  bordercolor.alpha = x->bordercolor.alpha;
500  }
501 
502  // clear the background
503  jgraphics_rectangle_rounded(g, border_thickness,
504  border_thickness,
505  rect.width - ((border_thickness) * 2.0),
506  rect.height - ((border_thickness) * 2.0),
507  cornersize, cornersize);
508  jgraphics_set_source_jrgba(g, &bgcolor);
509  jgraphics_fill(g);
510 
511  // draw the titlebar
512  jgraphics_rectangle_rounded(g, border_thickness,
513  border_thickness,
514  rect.width - (border_thickness * 2.0 + 1.0),
515  18.0,
516  cornersize, cornersize);
517  jgraphics_set_source_jrgba(g, &headercolor);
518  jgraphics_fill(g);
519 
520  jgraphics_rectangle_fill_fast(g, border_thickness,
521  9.0,
522  rect.width - (border_thickness * 2.0 + 1.0),
523  10.0);
524 
525  // draw borders
526  jgraphics_rectangle_rounded(g, border_thickness,
527  border_thickness,
528  rect.width - (border_thickness * 2.0),
529  rect.height - (border_thickness * 2.0),
530  cornersize, cornersize);
531  jgraphics_set_source_jrgba(g, &bordercolor);
532  jgraphics_set_line_width(g, 1.0);
533  jgraphics_stroke(g);
534 
535  jgraphics_set_line_width(g, 1.0);
536  jgraphics_move_to(g, border_thickness, 19.5);
537  jgraphics_line_to(g, rect.width - (border_thickness * 1.0), 19.5);
538  jgraphics_stroke(g);
539 
540  // draw the menu icon
541  jgraphics_set_line_width(g, 1.5);
542  jgraphics_arc(g, 9.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
543  jgraphics_fill(g);
544 
545  jgraphics_set_source_jrgba(g, &s_color_border_button);
546  jgraphics_arc(g, 9.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
547  jgraphics_stroke(g);
548 
549  middle = 9.0;
550  jgraphics_move_to(g, 9.5, middle + 4.0);
551  jgraphics_line_to(g, 13.5, middle);
552  jgraphics_line_to(g, 5.5, middle);
553  jgraphics_close_path(g);
554  jgraphics_fill(g);
555 
556  // draw the gain knob
557  if (x->has_gain) {
558  long right_side = rect.width - 16.0;
559  float gain = x->gain;
560  TTLimit(gain, 0.0f, 127.0f);
561 
562  if (x->has_mix)
563  right_side -= 16.0;
564  if (x->has_mute)
565  right_side -= 16.0;
566  if (x->has_bypass)
567  right_side -= 16.0;
568  if (x->has_freeze)
569  right_side -= 16.0;
570  if (x->has_active)
571  right_side -= 16.0;
572  if (x->has_panel)
573  right_side -= 16.0;
574 
575  jgraphics_set_source_jrgba(g, &s_color_background_button);
576 
577  x->rect_gain.x = right_side;
578  x->rect_gain.width = 13.0;
579 
580  jgraphics_set_line_width(g, 1.5);
581  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
582  jgraphics_fill(g);
583 
584  if (x->highlight && x->highlight_gain)
585  jgraphics_set_source_jrgba(g, &s_color_selected);
586  else
587  jgraphics_set_source_jrgba(g, &s_color_border_button);
588  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
589  jgraphics_stroke(g);
590 
591  jgraphics_set_source_jrgba(g, &s_color_darkgreen);
592 
593  jgraphics_arc(g, right_side+6.5, 3.0+6.5, 6.5, PI / 2, ((gain / 127.0) * TWOPI) + (PI/2)); // angles are in radians
594  jgraphics_line_to(g, right_side+6.5, 3.0+6.5);
595  jgraphics_close_path(g);
596  jgraphics_fill(g);
597 
598  jgraphics_set_source_jrgba(g, &s_color_green_ring);
599  jgraphics_arc(g, right_side+6.5, 3.0+6.5, 6.5, PI / 2, ((gain / 127.0) * TWOPI) + (PI/2));
600  jgraphics_line_to(g, right_side+6.5, 3.0+6.5);
601  jgraphics_stroke(g);
602 
603  jgraphics_arc(g, right_side+6.5, 3.0+6.5, 1.5, 0, TWOPI); // angles are in radians
604  jgraphics_fill(g);
605  }
606 
607  // draw the mix knob
608  if (x->has_mix) {
609  long right_side = rect.width - 16.0;
610  float mix = x->mix;
611  TTLimit(mix, 0.0f, 100.0f);
612 
613  if (x->has_mute)
614  right_side -= 16.0;
615  if (x->has_bypass)
616  right_side -= 16.0;
617  if (x->has_freeze)
618  right_side -= 16.0;
619  if (x->has_active)
620  right_side -= 16.0;
621  if (x->has_panel)
622  right_side -= 16.0;
623 
624  jgraphics_set_source_jrgba(g, &s_color_background_button);
625 
626  x->rect_mix.x = right_side;
627  x->rect_mix.width = 13.0;
628 
629  jgraphics_set_line_width(g, 1.5);
630  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
631  jgraphics_fill(g);
632 
633  if (x->highlight && x->highlight_mix)
634  jgraphics_set_source_jrgba(g, &s_color_selected);
635  else
636  jgraphics_set_source_jrgba(g, &s_color_border_button);
637  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
638  jgraphics_stroke(g);
639 
640  jgraphics_set_source_jrgba(g, &s_color_darkblue);
641 
642  jgraphics_arc(g, right_side+6.5, 3.0+6.5, 6.5, PI / 2, ((mix / 100.0) * TWOPI) + (PI/2)); // angles are in radians
643  jgraphics_line_to(g, right_side+6.5, 3.0+6.5);
644  jgraphics_close_path(g);
645  jgraphics_fill(g);
646 
647  jgraphics_set_source_jrgba(g, &s_color_blue_ring);
648  jgraphics_arc(g, right_side+6.5, 3.0+6.5, 6.5, PI / 2, ((mix / 100.0) * TWOPI) + (PI/2));
649  jgraphics_line_to(g, right_side+6.5, 3.0+6.5);
650  jgraphics_stroke(g);
651 
652  jgraphics_arc(g, right_side+6.5, 3.0+6.5, 1.5, 0, TWOPI); // angles are in radians
653  jgraphics_fill(g);
654  }
655 
656  // draw the mute button
657  if (x->has_mute) {
658  long right_side = rect.width - 16.0;
659 
660  if (x->has_bypass)
661  right_side -= 16.0;
662  if (x->has_freeze)
663  right_side -= 16.0;
664  if (x->has_active)
665  right_side -= 16.0;
666  if (x->has_panel)
667  right_side -= 16.0;
668 
669  if (x->is_muted)
670  jgraphics_set_source_jrgba(g, &s_color_red_button);
671  else
672  jgraphics_set_source_jrgba(g, &s_color_background_button);
673 
674  x->rect_mute.x = right_side;
675  x->rect_mute.width = 13.0;
676 
677  jgraphics_set_line_width(g, 1.5);
678  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
679  jgraphics_fill(g);
680 
681  if (x->highlight && x->highlight_mute)
682  jgraphics_set_source_jrgba(g, &s_color_selected);
683  else
684  jgraphics_set_source_jrgba(g, &s_color_border_button);
685  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
686  jgraphics_stroke(g);
687 
688  // m
689  if (x->is_muted)
690  jgraphics_set_source_jrgba(g, &s_color_text_button_on);
691  else
692  jgraphics_set_source_jrgba(g, &s_color_text_button_off);
693 
694  jgraphics_set_line_width(g, 2.0);
695  jgraphics_move_to(g, right_side + 2.5, 13.0);
696  jgraphics_select_font_face(g, JAMOMA_BUTTON_FONT, JGRAPHICS_FONT_SLANT_NORMAL, JGRAPHICS_FONT_WEIGHT_BOLD);
697  jgraphics_set_font_size(g, 7.0);
698  jgraphics_show_text(g, "m");
699  }
700 
701  // draw the bypass button
702  if (x->has_bypass) {
703  long right_side = rect.width - 16.0;
704 
705  if (x->has_freeze)
706  right_side -= 16.0;
707  if (x->has_active)
708  right_side -= 16.0;
709  if (x->has_panel)
710  right_side -= 16.0;
711 
712  if (x->is_bypassed)
713  jgraphics_set_source_jrgba(g, &s_color_blue_button);
714  else
715  jgraphics_set_source_jrgba(g, &s_color_background_button);
716 
717  x->rect_bypass.x = right_side;
718  x->rect_bypass.width = 13.0;
719 
720  jgraphics_set_line_width(g, 1.5);
721  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
722  jgraphics_fill(g);
723 
724  if (x->highlight && x->highlight_bypass)
725  jgraphics_set_source_jrgba(g, &s_color_selected);
726  else
727  jgraphics_set_source_jrgba(g, &s_color_border_button);
728  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
729  jgraphics_stroke(g);
730 
731  // b
732  if (x->is_bypassed)
733  jgraphics_set_source_jrgba(g, &s_color_text_button_on);
734  else
735  jgraphics_set_source_jrgba(g, &s_color_text_button_off);
736  jgraphics_set_line_width(g, 2.0);
737  jgraphics_move_to(g, right_side + 4.0, 13.0);
738  jgraphics_select_font_face(g, JAMOMA_BUTTON_FONT, JGRAPHICS_FONT_SLANT_NORMAL, JGRAPHICS_FONT_WEIGHT_BOLD);
739  jgraphics_set_font_size(g, 7.0);
740  jgraphics_show_text(g, "b");
741  }
742 
743  // draw the freeze button
744  if (x->has_freeze) {
745  long right_side = rect.width - 16.0;
746 
747  if (x->has_active)
748  right_side -= 16.0;
749  if (x->has_panel)
750  right_side -= 16.0;
751 
752  if (x->is_frozen)
753  jgraphics_set_source_jrgba(g, &s_color_orange_button);
754  else
755  jgraphics_set_source_jrgba(g, &s_color_background_button);
756 
757  x->rect_freeze.x = right_side;
758  x->rect_freeze.width = 13.0;
759 
760  jgraphics_set_line_width(g, 1.5);
761  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
762  jgraphics_fill(g);
763 
764  if (x->highlight && x->highlight_freeze)
765  jgraphics_set_source_jrgba(g, &s_color_selected);
766  else
767  jgraphics_set_source_jrgba(g, &s_color_border_button);
768  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
769  jgraphics_stroke(g);
770 
771  // f
772  if (x->is_frozen)
773  jgraphics_set_source_jrgba(g, &s_color_text_button_on);
774  else
775  jgraphics_set_source_jrgba(g, &s_color_text_button_off);
776  jgraphics_set_line_width(g, 2.0);
777  jgraphics_move_to(g, right_side + 5.0, 13.0);
778  jgraphics_select_font_face(g, JAMOMA_BUTTON_FONT, JGRAPHICS_FONT_SLANT_NORMAL, JGRAPHICS_FONT_WEIGHT_BOLD);
779  jgraphics_set_font_size(g, 7.0);
780  jgraphics_show_text(g, "f");
781  }
782 
783  // draw the active button
784  if (x->has_active) {
785  long right_side = rect.width - 16.0;
786 
787  if (x->has_panel)
788  right_side -= 16.0;
789 
790  if (x->is_active)
791  jgraphics_set_source_jrgba(g, &s_color_green_button);
792  else
793  jgraphics_set_source_jrgba(g, &s_color_background_button);
794 
795  x->rect_active.x = right_side;
796  x->rect_active.width = 13.0;
797 
798  jgraphics_set_line_width(g, 1.5);
799  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
800  jgraphics_fill(g);
801 
802  if (x->highlight && x->highlight_active)
803  jgraphics_set_source_jrgba(g, &s_color_selected);
804  else
805  jgraphics_set_source_jrgba(g, &s_color_border_button);
806  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
807  jgraphics_stroke(g);
808 
809  // p
810  if (x->is_active)
811  jgraphics_set_source_jrgba(g, &s_color_text_button_on);
812  else
813  jgraphics_set_source_jrgba(g, &s_color_text_button_off);
814  jgraphics_set_line_width(g, 2.0);
815  jgraphics_move_to(g, right_side + 4.0, 12.0);
816  jgraphics_select_font_face(g, JAMOMA_BUTTON_FONT, JGRAPHICS_FONT_SLANT_NORMAL, JGRAPHICS_FONT_WEIGHT_BOLD);
817  jgraphics_set_font_size(g, 7.0);
818  jgraphics_show_text(g, "a");
819  }
820 
821  // draw the panel button
822  if (x->has_panel) {
823  long right_side = rect.width - 16.0;
824 
825  x->rect_panel.x = right_side;
826  x->rect_panel.width = 13.0;
827 
828  jgraphics_set_source_jrgba(g, &s_color_background_button);
829  jgraphics_set_line_width(g, 1.5);
830  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
831  jgraphics_fill(g);
832 
833  jgraphics_set_source_jrgba(g, &s_color_border_button);
834  jgraphics_arc(g, right_side+6.5, 9.5, 6.5, 0., JGRAPHICS_2PI);
835  jgraphics_stroke(g);
836 
837  // i
838  jgraphics_set_source_jrgba(g, &s_color_text_button_off);
839  jgraphics_set_line_width(g, 2.0);
840  jgraphics_move_to(g, right_side + 2.0, 13.0);
841  jgraphics_select_font_face(g, JAMOMA_BUTTON_FONT, JGRAPHICS_FONT_SLANT_NORMAL, JGRAPHICS_FONT_WEIGHT_BOLD);
842  jgraphics_set_font_size(g, 10.0);
843  jgraphics_show_text(g, "+");
844  }
845 }
846 
847 void ui_paint_address(t_ui *x, t_object *textfield)
848 {
849  // if there is still no address
850  if (x->modelAddress == kTTAdrsEmpty) {
851 
852  object_method(textfield, gensym("settext"), NO_MODEL_STRING);
853  }
854  else {
855 
856  double r,t,l,b;
857  t_rect uiRect;
858 
859  object_attr_get_rect((t_object*)x, _sym_presentation_rect, &uiRect);
860  textfield_get_textmargins(textfield, &r, &t, &l, &b);
861 
862  TTUInt32 maxLetter = (uiRect.width - l - r) / 6; // assuming a letter is 6 pixels max
863 
864  if (strlen(x->modelAddress.c_str()) >= maxLetter) {
865 
866  TTString croppedAddress;
867  TTString modelAddress = x->modelAddress.string();
868  TTStringIter begin = modelAddress.begin();
869  TTStringIter end = modelAddress.end();
870 
871  croppedAddress = TTString(begin, begin+(maxLetter/2)-1);
872  croppedAddress += "..";
873  croppedAddress += TTString(end-(maxLetter/2)+1, end);
874  object_method(textfield, gensym("settext"), croppedAddress.c_str());
875  }
876  else
877  object_method(textfield, gensym("settext"), x->modelAddress.c_str());
878 
879  }
880 }
881 
882 void ui_mousedown(t_ui *x, t_object *patcherview, t_pt px, long modifiers)
883 {
884  t_object *obj;
885  t_symbol *objclass;
886  t_rect rect;
887  TTValue none;
888 
889  // usually we don't want mousedragdelta -- we turn it on below as necessary
890  jbox_set_mousedragdelta((t_object *)x, 0);
891 
892  jbox_get_rect_for_view((t_object *)x, patcherview, &rect);
893 
894  // a click on j.ui panel will select/unselect all j.remote
895  if (px.y > 20.0) {
896 
897  // if the control key is pressed
898  if (modifiers & eShiftKey) {
899 
900  obj = object_attr_getobj(jamoma_patcher_get((t_object*)x), _sym_firstobject);
901  while (obj) {
902  objclass = object_attr_getsym(obj, _sym_maxclass);
903  if (objclass == gensym("j.remote")) {
904 
905  // we should not pass it a t_pt any longer.
906  // instead we should pass two doubles (one for the x coordinate and one for the y coordinate)
907  // so in j.remote : that function should change its prototype,
908  // and then the arguments to object_method should change too object_method() is much more picky now
909  object_method(object_attr_getobj(obj, _sym_object), gensym("mousedown"), patcherview, px.x, px.y, modifiers);
910 
911  }
912  obj = object_attr_getobj(obj, _sym_nextobject);
913  }
914 
915  // update the mouse position to display
916  ui_mousemove(x, patcherview, px, modifiers);
917  }
918 
919  return;
920  }
921 
922  if (px.x > 18 && px.y < 20.0) {//(rect.width - 112)) {
923  // we check the gain and mix knobs first because they are continuous datas and should run as fast as possible
924  if (x->has_gain && px.x >= x->rect_gain.x && px.x <= (x->rect_gain.x + x->rect_gain.width)) {
925  if (x->highlight) {
926  x->highlight_gain = !x->highlight_gain;
927  ui_viewer_highlight(x, TTSymbol("audio/gain"), x->highlight_gain);
928  }
929  else {
930  x->gainDragging = true;
931  x->anchor.x = x->anchor.y = 0.0;
932  x->anchorValue = x->gain;
933  jbox_set_mousedragdelta((t_object *)x, 1);
934  }
935  }
936  else if (x->has_mix && px.x >= x->rect_mix.x && px.x <= (x->rect_mix.x + x->rect_mix.width)) {
937  if (x->highlight) {
938  x->highlight_mix = !x->highlight_mix;
939  ui_viewer_highlight(x, TTSymbol("audio/mix"), x->highlight_mix);
940  }
941  else {
942  x->mixDragging = true;
943  x->anchor.x = x->anchor.y = 0.0;
944  x->anchorValue = x->mix;
945  jbox_set_mousedragdelta((t_object *)x, 1);
946  }
947  }
948  else if (x->has_panel && px.x >= x->rect_panel.x && px.x <= (x->rect_panel.x + x->rect_panel.width))
949  x->uiInfo.send("Panel");
950 
951  else if (x->has_active && px.x >= x->rect_active.x && px.x <= (x->rect_active.x + x->rect_active.width)) {
952  if (x->highlight) {
953  x->highlight_active = !x->highlight_active;
954  ui_viewer_highlight(x, TTSymbol("data/active"), x->highlight_active);
955  }
956  else
957  ui_viewer_send(x, TTSymbol("data/active"), TTValue(!x->is_active));
958  }
959  else if (x->has_freeze && px.x >= x->rect_freeze.x && px.x <= (x->rect_freeze.x + x->rect_freeze.width)) {
960  if (x->highlight) {
961  x->highlight_freeze = !x->highlight_freeze;
962  ui_viewer_highlight(x, TTSymbol("data/freeze"), x->highlight_freeze);
963  }
964  else
965  ui_viewer_send(x, TTSymbol("data/freeze"), TTValue(!x->is_frozen));
966  }
967  else if (x->has_bypass && px.x >= x->rect_bypass.x && px.x <= (x->rect_bypass.x + x->rect_bypass.width)) {
968  if (x->highlight) {
969  x->highlight_bypass = !x->highlight_bypass;
970  ui_viewer_highlight(x, TTSymbol("*.*/bypass"), x->highlight_bypass);
971  }
972  else
973  ui_viewer_send(x, TTSymbol("*.*/bypass"), TTValue(!x->is_bypassed));
974  }
975  else if (x->has_mute && px.x >= x->rect_mute.x && px.x <= (x->rect_mute.x + x->rect_mute.width)) {
976  if (x->highlight) {
977  x->highlight_mute = !x->highlight_mute;
978  ui_viewer_highlight(x, TTSymbol("*.*/mute"), x->highlight_mute);
979  }
980  else
981  ui_viewer_send(x, TTSymbol("*.*/mute"), TTValue(!x->is_muted));
982  }
983 
984  else if (px.x < 100)
985  ui_refmenu_do(x, patcherview, px, modifiers);
986  }
987  else //if (px.x < 18)
988  ui_menu_do(x, patcherview, px, modifiers);
989 }
990 
991 // mousedragdelta sends the amount the mouse moved in t_pt
992 void ui_mousedragdelta(t_ui *x, t_object *patcherview, t_pt pt, long modifiers)
993 {
994  t_object* textfield = jbox_get_textfield((t_object*) x);
995  t_rect rect;
996  char str[5];
997  double factor = 1.0; // factor determines how much precision (vs. immediacy) you have when dragging the knob
998 
999  jbox_get_rect_for_view((t_object *)x, patcherview, &rect);
1000 
1001  if (modifiers & eShiftKey)
1002  factor = 0.02;
1003 
1004  if (textfield)
1005  textfield_set_textcolor(textfield, &x->textcolor);
1006 
1007  if (x->mixDragging) {
1008  x->anchorValue = x->anchorValue - (pt.y * factor);
1009  TTLimit(x->anchorValue, 0.0f, 100.0f);
1010  ui_viewer_send(x, TTSymbol("audio/mix"), TTValue(x->anchorValue));
1011 
1012  snprintf(str, sizeof(str), "%f", x->mix);
1013  object_method(textfield, gensym("settext"), str);
1014  }
1015  else if (x->gainDragging) {
1016  x->anchorValue = x->anchorValue - (pt.y * factor);
1017  TTLimit(x->anchorValue, 0.0f, 127.0f);
1018  ui_viewer_send(x, TTSymbol("audio/gain"), TTValue(x->anchorValue));
1019 
1020  snprintf(str, sizeof(str), "%f", x->gain);
1021  object_method(textfield, gensym("settext"), str);
1022  }
1023 }
1024 
1025 void ui_mouseup(t_ui *x, t_object *patcherview)
1026 {
1027  x->mixDragging = false;
1028  x->gainDragging = false;
1029 
1030  // Set the textfield to display the address after dragging widget
1031  t_object *textfield = jbox_get_textfield((t_object*) x);
1032  if (textfield)
1033  ui_paint_address(x, textfield);
1034 
1035  jbox_redraw(&x->box);
1036 }
1037 
1038 void ui_mousemove(t_ui *x, t_object *patcherview, t_pt pt, long modifiers)
1039 {
1040  t_symbol *objclass;
1041  t_object *obj = object_attr_getobj(jamoma_patcher_get((t_object*)x), _sym_firstobject);
1042 
1043  // théo - we decide to mute this feature in june 2014 Albi workshop
1044  return;
1045 
1046  // if the control key is pressed
1047  if (modifiers & eShiftKey) {
1048 
1049  x->highlight = true;
1050 
1051  // Is the mouse wasn't hover the j.ui panel
1052  if (!x->hover) {
1053  x->hover = true;
1054  x->uiInfo.set("highlight", TTSymbol("jamoma"));
1055  }
1056  }
1057  else {
1058 
1059  x->highlight = false;
1060 
1061  if (x->hover) {
1062  x->hover = false;
1063  x->uiInfo.set("highlight", kTTSym_none);
1064  }
1065  }
1066 
1067  while (obj) {
1068  objclass = object_attr_getsym(obj, _sym_maxclass);
1069  if (objclass == gensym("j.remote")) {
1070 
1071  // we should not pass it a t_pt any longer.
1072  // instead we should pass two doubles (one for the x coordinate and one for the y coordinate)
1073  // so in j.remote : that function should change its prototype,
1074  // and then the arguments to object_method should change too object_method() is much more picky now
1075  object_method(object_attr_getobj(obj, _sym_object), gensym("mousemove"), patcherview, pt.x, pt.y, modifiers);
1076 
1077  }
1078  obj = object_attr_getobj(obj, _sym_nextobject);
1079  }
1080 }
1081 
1082 void ui_mouseleave(t_ui *x, t_object *patcherview, t_pt pt, long modifiers)
1083 {
1084  t_symbol *objclass;
1085  t_object *obj = object_attr_getobj(jamoma_patcher_get((t_object*)x), _sym_firstobject);
1086 
1087  // théo - we decide to mute this feature in june 2014 Albi workshop
1088  return;
1089 
1090  // Is the mouse leave outside the j.ui (not hover an ui object)
1091  if ( pt.x <= x->box.b_presentation_rect.x || pt.x >= (x->box.b_presentation_rect.x + x->box.b_presentation_rect.width)
1092  || pt.y <= x->box.b_presentation_rect.y || pt.y >= (x->box.b_presentation_rect.y + x->box.b_presentation_rect.height)) {
1093 
1094  x->highlight = false;
1095  x->hover = false;
1096 
1097  if (x->uiInfo.valid())
1098  x->uiInfo.set("highlight", kTTSym_none);
1099  }
1100 
1101  while (obj) {
1102  objclass = object_attr_getsym(obj, _sym_maxclass);
1103  if (objclass == gensym("j.remote")) {
1104 
1105  // we should not pass it a t_pt any longer.
1106  // instead we should pass two doubles (one for the x coordinate and one for the y coordinate)
1107  // so in j.remote : that function should change its prototype,
1108  // and then the arguments to object_method should change too object_method() is much more picky now
1109  object_method(object_attr_getobj(obj, _sym_object), gensym("mouseleave"), patcherview, pt.x, pt.y, modifiers*x->hover); // * hover allows to return to a normal display
1110 
1111  }
1112  obj = object_attr_getobj(obj, _sym_nextobject);
1113  }
1114 }
1115 
1116 #pragma mark -
1117 #pragma mark Menus
1118 
1119 void ui_menu_do(t_ui *x, t_object *patcherview, t_pt px, long modifiers)
1120 {
1121  t_jpopupmenu *p;
1122  t_symobject *item;
1123  long size, i;
1124  int selectedId;
1125  t_jfont *font;
1126  int coord_x=0, coord_y=0;
1127  t_pt pt;
1128 
1129  ui_menu_build(x); // would be better to not rebuild the menu every single time? or not? this uses less memory...
1130 
1131  jbox_set_mousedragdelta((t_object *)x, 0);
1132  p = jpopupmenu_create();
1133 
1134  font = jfont_create(JAMOMA_MENU_FONT,
1135  JGRAPHICS_FONT_SLANT_NORMAL,
1136  JGRAPHICS_FONT_WEIGHT_NORMAL,
1137  JAMOMA_MENU_FONTSIZE);
1138  jpopupmenu_setfont(p, font);
1139  jfont_destroy(font);
1140  size = linklist_getsize(x->menu_items);
1141  for (i=0; i<size; i++) {
1142  item = (t_symobject *)linklist_getindex(x->menu_items, i);
1143  if (!item->sym || (item->sym->s_name[0] == '\0') || item->sym->s_name[0] == '-')
1144  jpopupmenu_addseperator(p);
1145  else {
1146  if (item->sym == gensym("Defeat Signal Meters")) {
1147  if (x->is_metersdefeated)
1148  jpopupmenu_additem(p, i+1, item->sym->s_name, NULL, 1, 0, NULL);
1149  else
1150  jpopupmenu_additem(p, i+1, item->sym->s_name, NULL, 0, item->flags, NULL);
1151  }
1152  else if (item->sym == gensym("Disable UI Updates")) {
1153  if (x->ui_freeze)
1154  jpopupmenu_additem(p, i+1, item->sym->s_name, NULL, 1, 0, NULL);
1155  else
1156  jpopupmenu_additem(p, i+1, item->sym->s_name, NULL, 0, item->flags, NULL);
1157  }
1158  else
1159  jpopupmenu_additem(p, i+1, item->sym->s_name, NULL, 0, item->flags, NULL);
1160  }
1161  }
1162 
1163  object_method(patcherview, gensym("canvastoscreen"), 0.0, 0.0, &coord_x, &coord_y);
1164  coord_x += x->box.b_presentation_rect.x;
1165  coord_y += x->box.b_presentation_rect.y;
1166  pt.x = coord_x;
1167  pt.y = coord_y;
1168  selectedId = jpopupmenu_popup(p, pt, x->menu_selection+1);
1169  if (selectedId) {
1170  x->menu_selection = selectedId -1;
1171  qelem_set(x->menu_qelem);
1172  }
1173  jpopupmenu_destroy(p);
1174 }
1175 
1176 void ui_menu_qfn(t_ui *x)
1177 {
1178  t_symobject *item = (t_symobject *)linklist_getindex(x->menu_items, x->menu_selection);
1179  t_atom a;
1180 
1181  // get model object
1182  t_object *modelObject = ui_get_model_object(x);
1183  if (!modelObject)
1184  return;
1185 
1186  if (item->sym == gensym("Open Model Reference Page"))
1187  object_method(modelObject, _sym_anything, gensym("model:reference/open"), 0, NULL);
1188 
1189  else if (item->sym == gensym("Open Model Help Patch"))
1190  object_method(modelObject, _sym_anything, gensym("model:help/open"), 0, NULL);
1191 
1192  else if (item->sym == gensym("Open Model Internal"))
1193  object_method(modelObject, _sym_anything, gensym("model:internal/open"), 0, NULL);
1194 
1195  else if (item->sym == gensym("Edit Current State"))
1196  ui_edit_state(x);
1197 
1198  else if (item->sym == gensym("Load Presets File"))
1199  object_method(modelObject, gensym("preset:read"), 0, NULL);
1200 
1201  else if (item->sym == gensym("Save Presets File"))
1202  object_method(modelObject, gensym("preset:write/again"), 0, NULL);
1203 
1204  else if (item->sym == gensym("Save Presets File As"))
1205  object_method(modelObject, gensym("preset:write"), 0, NULL);
1206 
1207  else if (item->sym == gensym("Edit Presets File"))
1208  object_method(modelObject, gensym("preset:edit"), 0, NULL);
1209 
1210  else if (item->sym == gensym("Create New Preset")) {
1211 
1212  long result;
1213  char *text;
1214  char buf[512];
1215 
1216  strcpy(buf, "Château de Preset");
1217 
1218  result = jdialog_showtext("Provide a name for this Preset", buf, 0, &text);
1219  if (result != 1)
1220  return;
1221 
1222  atom_setsym(&a, gensym(text));
1223  object_method(modelObject, _sym_anything, gensym("preset:new"), 1, &a);
1224  }
1225 
1226  else if (item->sym == gensym("Update Current Preset"))
1227  object_method(modelObject, _sym_anything, gensym("preset:update"), 0, NULL);
1228 
1229  else if (item->sym == gensym("Delete Current Preset"))
1230  object_method(modelObject, _sym_anything, gensym("preset:delete"), 0, NULL);
1231 
1232  else if (item->sym == gensym("Open Preset Interface"))
1233  ui_preset_interface(x);
1234 
1235  else { // assume the menu item is a preset name
1236 
1237  atom_setsym(&a, item->sym);
1238  object_method(modelObject, _sym_anything, gensym("preset:recall"), 1, &a);
1239  }
1240 }
1241 
1242 void ui_menu_build(t_ui *x)
1243 {
1244  t_symobject* item = NULL;
1245  int i;
1246 
1247  if (!x->menu_items)
1248  return;
1249 
1250  linklist_clear(x->menu_items);
1251 
1252  // append model operations
1253  if (x->has_model) {
1254 
1255  item = (t_symobject *)symobject_new(gensym("Open Model Reference Page"));
1256  linklist_append(x->menu_items, item);
1257  item = (t_symobject *)symobject_new(gensym("Open Model Help Patch"));
1258  linklist_append(x->menu_items, item);
1259  item = (t_symobject *)symobject_new(gensym("Open Model Internal"));
1260  linklist_append(x->menu_items, item);
1261  item = (t_symobject *)symobject_new(gensym("Edit Current State"));
1262  linklist_append(x->menu_items, item);
1263  }
1264 
1265  // append preset operations
1266  if (x->has_preset) {
1267 
1268  item = (t_symobject *)symobject_new(gensym("-"));
1269  linklist_append(x->menu_items, item);
1270 
1271  item = (t_symobject *)symobject_new(gensym("Load Presets File"));
1272  linklist_append(x->menu_items, item);
1273  item = (t_symobject *)symobject_new(gensym("Save Presets File"));
1274  linklist_append(x->menu_items, item);
1275  item = (t_symobject *)symobject_new(gensym("Save Presets File As"));
1276  linklist_append(x->menu_items, item);
1277  item = (t_symobject *)symobject_new(gensym("Edit Presets File"));
1278  linklist_append(x->menu_items, item);
1279  item = (t_symobject *)symobject_new(gensym("Create New Preset"));
1280  linklist_append(x->menu_items, item);
1281  item = (t_symobject *)symobject_new(gensym("Update Current Preset"));
1282  linklist_append(x->menu_items, item);
1283  item = (t_symobject *)symobject_new(gensym("Delete Current Preset"));
1284  linklist_append(x->menu_items, item);
1285  item = (t_symobject *)symobject_new(gensym("Open Preset Interface"));
1286  }
1287 
1288  // append preset name list
1289  if (x->preset_names) {
1290 
1291  item = (t_symobject *)symobject_new(gensym("-"));
1292  linklist_append(x->menu_items, item);
1293 
1294  for (i=0; i<x->preset_num; i++) {
1295  item = (t_symobject *)symobject_new(atom_getsym(&x->preset_names[i]));
1296  linklist_append(x->menu_items, item);
1297  }
1298  }
1299 }
1300 
1301 void ui_refmenu_do(t_ui *x, t_object *patcherview, t_pt px, long modifiers)
1302 {
1303  t_jpopupmenu *p;
1304  t_symobject *item;
1305  long size, i;
1306  int selectedId;
1307  t_jfont *font;
1308  int coord_x=0, coord_y=0;
1309  t_pt pt;
1310 
1311  ui_refmenu_build(x); // TODO: would be better to not rebuild the menu every single time? or not? this uses less memory...
1312 
1313  jbox_set_mousedragdelta((t_object *)x, 0);
1314  p = jpopupmenu_create();
1315 
1316  font = jfont_create(JAMOMA_MENU_FONT,
1317  JGRAPHICS_FONT_SLANT_NORMAL,
1318  JGRAPHICS_FONT_WEIGHT_NORMAL,
1319  JAMOMA_MENU_FONTSIZE);
1320  jpopupmenu_setfont(p, font);
1321  jfont_destroy(font);
1322  size = linklist_getsize(x->refmenu_items);
1323  for (i=0; i<size; i++) {
1324  item = (t_symobject *)linklist_getindex(x->refmenu_items, i);
1325  if (!item->sym || (item->sym->s_name[0] == '\0') || item->sym->s_name[0] == '-')//{
1326  jpopupmenu_addseperator(p);
1327  else {
1328  //TODO: Instead of passing NULL for the 4th data, we can pass a custom color for "header" items
1329  jpopupmenu_additem(p, i+1, item->sym->s_name, NULL, 0, item->flags, NULL);
1330  // TODO: use jpopupmenu_addheader instead -- requires that Max export this function though (which it currently doesn't)
1331  // if (item->flags)
1332  // jpopupmenu_addheader(p, item->sym->s_name);
1333  // else
1334  // jpopupmenu_additem(p, i+1, item->sym->s_name, NULL, 0, item->flags, NULL);
1335  }
1336  }
1337 
1338  object_method(patcherview, gensym("canvastoscreen"), 0.0, 0.0, &coord_x, &coord_y);
1339  coord_x += x->box.b_presentation_rect.x;
1340  coord_y += x->box.b_presentation_rect.y;
1341  pt.x = coord_x + 20.0;
1342  pt.y = coord_y;
1343 
1344  selectedId = jpopupmenu_popup(p, pt, x->refmenu_selection+1);
1345  if (selectedId) {
1346  x->refmenu_selection = selectedId -1;
1347  qelem_set(x->refmenu_qelem);
1348  }
1349  jpopupmenu_destroy(p);
1350 }
1351 
1352 void ui_refmenu_qfn(t_ui *x)
1353 {
1354  t_symobject *item = (t_symobject *)linklist_getindex(x->refmenu_items, x->refmenu_selection);
1355 
1356  ui_data_interface(x, TTSymbol(item->sym->s_name));
1357 }
1358 
1359 void ui_refmenu_build(t_ui *x)
1360 {
1361  t_symobject *item = NULL;
1362  char tempStr[512];
1363  TTValue filters;
1364 
1365  if (!x->refmenu_items)
1366  return;
1367 
1368  // Prepare a new refmenu
1369  linklist_clear(x->refmenu_items);
1370 
1371  // Edit refmenu title
1372  if (x->modelAddress != kTTSymEmpty)
1373  snprintf(tempStr, 512, "Model: %s", x->modelAddress.c_str());
1374  else
1375  strncpy_zero(tempStr, "Model: ?", 512);
1376 
1377  item = (t_symobject *)symobject_new(gensym(tempStr));
1378  linklist_append(x->refmenu_items, item);
1379  item->flags = 1; // mark to disable this item (we use it as a label)
1380 
1381  // Look for User-Defined Parameters into the model
1382  item = (t_symobject *)symobject_new(gensym("-"));
1383  linklist_append(x->refmenu_items, item);
1384  item = (t_symobject *)symobject_new(gensym("User-Defined Parameters"));
1385  linklist_append(x->refmenu_items, item);
1386  item->flags = 1; // mark to disable this item (we use it as a label)
1387 
1388  ui_explorer_create((t_object*)x, x->modelParamExplorer, gensym("return_modelParamExploration"));
1389 
1390  filters = TTValue(kTTSym_parameter);
1391  filters.append(TTSymbol("noGenericTag"));
1392  x->modelParamExplorer.set("filterList", filters);
1393 
1394  x->modelParamExplorer.set(kTTSym_address, x->modelAddress);
1395  x->modelParamExplorer.send("Explore");
1396 
1397  // Look for User-Defined Messages into the model
1398  item = (t_symobject *)symobject_new(gensym("-"));
1399  linklist_append(x->refmenu_items, item);
1400  item = (t_symobject *)symobject_new(gensym("User-Defined Messages"));
1401  linklist_append(x->refmenu_items, item);
1402  item->flags = 1; // mark to disable this item (we use it as a label)
1403 
1404  ui_explorer_create((t_object*)x, x->modelMessExplorer, gensym("return_modelMessExploration"));
1405 
1406  filters = TTValue(kTTSym_message);
1407  filters.append(TTSymbol("noGenericTag"));
1408  x->modelMessExplorer.set("filterList", filters);
1409 
1410  x->modelMessExplorer.set(kTTSym_address, x->modelAddress);
1411  x->modelMessExplorer.send("Explore");
1412 
1413  // Look for User-Defined Returns into the model
1414  item = (t_symobject *)symobject_new(gensym("-"));
1415  linklist_append(x->refmenu_items, item);
1416  item = (t_symobject *)symobject_new(gensym("User-Defined Returns"));
1417  linklist_append(x->refmenu_items, item);
1418  item->flags = 1; // mark to disable this item (we use it as a label)
1419 
1420  ui_explorer_create((t_object*)x, x->modelRetExplorer, gensym("return_modelRetExploration"));
1421 
1422  filters = TTValue(kTTSym_return);
1423  filters.append(TTSymbol("noGenericTag"));
1424  x->modelRetExplorer.set("filterList", filters);
1425 
1426  x->modelRetExplorer.set(kTTSym_address, x->modelAddress);
1427  x->modelRetExplorer.send("Explore");
1428 
1429  // Look for Generic Parameters into the model
1430  item = (t_symobject *)symobject_new(gensym("-"));
1431  linklist_append(x->refmenu_items, item);
1432  item = (t_symobject *)symobject_new(gensym("Generic Parameters"));
1433  linklist_append(x->refmenu_items, item);
1434  item->flags = 1; // mark to disable this item (we use it as a label)
1435 
1436  filters = TTValue(kTTSym_parameter);
1437  filters.append(TTSymbol("genericTag"));
1438  x->modelParamExplorer.set("filterList", filters);
1439 
1440  x->modelParamExplorer.set(kTTSym_address, x->modelAddress);
1441  x->modelParamExplorer.send("Explore");
1442 
1443  // Look for Generic Messages into the model
1444  item = (t_symobject *)symobject_new(gensym("-"));
1445  linklist_append(x->refmenu_items, item);
1446  item = (t_symobject *)symobject_new(gensym("Generic Messages"));
1447  linklist_append(x->refmenu_items, item);
1448  item->flags = 1; // mark to disable this item (we use it as a label)
1449 
1450  filters = TTValue(kTTSym_message);
1451  filters.append(TTSymbol("genericTag"));
1452  x->modelMessExplorer.set("filterList", filters);
1453 
1454  x->modelMessExplorer.set(kTTSym_address, x->modelAddress);
1455  x->modelMessExplorer.send("Explore");
1456 
1457  // Look for Generic Returns into the model
1458  item = (t_symobject *)symobject_new(gensym("-"));
1459  linklist_append(x->refmenu_items, item);
1460  item = (t_symobject *)symobject_new(gensym("Generic Returns"));
1461  linklist_append(x->refmenu_items, item);
1462  item->flags = 1; // mark to disable this item (we use it as a label)
1463 
1464  filters = TTValue(kTTSym_return);
1465  filters.append(TTSymbol("genericTag"));
1466  x->modelRetExplorer.set("filterList", filters);
1467 
1468  x->modelRetExplorer.set(kTTSym_address, x->modelAddress);
1469  x->modelRetExplorer.send("Explore");
1470 
1471  x->modelParamExplorer = TTObject();
1472  x->modelMessExplorer = TTObject();
1473  x->modelRetExplorer = TTObject();
1474 }
1475 
1476 void* ui_oksize(t_ui *x, t_rect *rect)
1477 {
1478  long unitHeight = 0;
1479  long unitWidth = 0;
1480  double unitFrac= 0.0;
1481  t_object *textfield = NULL;
1482 
1483  unitHeight = rect->height / JAMOMA_UNIT_HEIGHT;
1484  unitFrac = rect->height - (unitHeight * JAMOMA_UNIT_HEIGHT);
1485  if (unitFrac > (JAMOMA_UNIT_HEIGHT/2))
1486  unitHeight += 1;
1487  if (unitHeight < 1)
1488  unitHeight = 1;
1489  rect->height = unitHeight * JAMOMA_UNIT_HEIGHT;
1490 
1491  unitWidth = rect->width / JAMOMA_UNIT_WIDTH;
1492  unitFrac = rect->width - (unitWidth * JAMOMA_UNIT_WIDTH);
1493  if (unitFrac > (JAMOMA_UNIT_WIDTH/2))
1494  unitWidth += 1;
1495  if (unitWidth < 1)
1496  unitWidth = 1;
1497  rect->width = unitWidth * JAMOMA_UNIT_WIDTH;
1498 
1499  // We do textfield configuration here because the margins are dependent upon the dimensions
1500  textfield = jbox_get_textfield((t_object*) x);
1501  textfield_set_noactivate(textfield, 1);
1502  textfield_set_readonly(textfield, 1);
1503  textfield_set_editonclick(textfield, 0);
1504  textfield_set_wordwrap(textfield, 0);
1505  textfield_set_useellipsis(textfield, 0);
1506  textfield_set_textcolor(textfield, &x->textcolor);
1507  textfield_set_textmargins(textfield, 20.0, 2.0, 60.0, rect->height - 19.0);
1508 
1509  return (void *)1;
1510 }
1511 
1512 void ui_edit_state(t_ui *x)
1513 {
1514  TTString *buffer;
1515  char title[MAX_FILENAME_CHARS];
1516  TTValue none;
1517  TTSymbol name;
1518 
1519  // only one editor can be open in the same time
1520  if (!x->textEditor) {
1521 
1522  x->textEditor = (t_object*)object_new(_sym_nobox, _sym_jed, x, 0);
1523  buffer = new TTString();
1524 
1525  // Store the preset
1526  x->state.send("Store");
1527 
1528  critical_enter(0);
1529  x->textHandler.send(kTTSym_Write, (TTPtr)buffer, none);
1530  critical_exit(0);
1531 
1532  // pass the buffer to the editor
1533  object_method(x->textEditor, _sym_settext, buffer->c_str(), _sym_utf_8);
1534  object_attr_setchar(x->textEditor, gensym("scratch"), 1);
1535 
1536  snprintf(title, MAX_FILENAME_CHARS, "%s state editor", x->patcherClass.c_str());
1537  object_attr_setsym(x->textEditor, _sym_title, gensym(title));
1538 
1539  buffer->clear();
1540  delete buffer;
1541  buffer = NULL;
1542  }
1543 }
1544 
1545 void ui_edclose(t_ui *x, char **text, long size)
1546 {
1547  x->text = new TTString(*text);
1548  x->textEditor = NULL;
1549 
1550  defer_low((t_object*)x, (method)ui_doedit, NULL, 0, NULL);
1551 }
1552 
1553 void ui_doedit(t_ui *x)
1554 {
1555  TTValue none;
1556 
1557  critical_enter(0);
1558  x->textHandler.send(kTTSym_Read, (TTPtr)x->text, none);
1559  critical_exit(0);
1560 
1561  // recall the preset
1562  x->state.send(kTTSym_Recall);
1563 
1564  delete x->text;
1565  x->text = NULL;
1566  x->textEditor = NULL;
1567 }
A base class for j.ui info.
We build a directory of TTNodes, and you can request a pointer for any TTNode, or add an observer to ...
Definition: TTNode.h:59
TTPtr getContext()
Get a pointer to the context of this node.
Definition: TTNode.cpp:473
const char * c_str() const
Return a pointer to the internal C-string.
Definition: TTString.h:83
The TTAddress class is used to represent a string and efficiently pass and compare that string...
Definition: TTAddress.h:29
Create and use Jamoma object instances.
Definition: TTObject.h:29
t_object JAMOMA_EXPORT * jamoma_patcher_get(t_object *obj)
Convenient method to get the patcher easily.
This class represents a single attribute, as used by the TTObjectBase class.
Definition: TTAttribute.h:79
Maintain a collection of TTValue objects indexed by TTSymbol pointers.
Definition: TTHash.h:36
j.ui : Provide standard user interface component for modules
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
TTErr set(const TTSymbol aName, T aValue)
Set an attribute value for an object.
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
t_symbol JAMOMA_EXPORT * jamoma_patcher_get_hierarchy(t_object *patcher)
Get the hierarchy of the patcher : bpatcher, subpatcher or toplevel.
The TTString class is used to represent a string.
Definition: TTString.h:34
#define accessApplicationLocalDirectory
Access to the local application directory.
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
TTBoolean JAMOMA_EXPORT jamoma_patcher_get_ui(t_object *patcher)
Look into the given patcher to know if there is a j.ui.