1 /* GSequencer - Advanced GTK Sequencer
2  * Copyright (C) 2005-2021 Joël Krähemann
3  *
4  * This file is part of GSequencer.
5  *
6  * GSequencer is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSequencer is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSequencer.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <ags/X/ags_line_member.h>
21 #include <ags/X/ags_line_member_callbacks.h>
22 
23 #include <ags/X/ags_ui_provider.h>
24 #include <ags/X/ags_window.h>
25 #include <ags/X/ags_machine.h>
26 #include <ags/X/ags_pad.h>
27 #include <ags/X/ags_line.h>
28 #include <ags/X/ags_effect_pad.h>
29 #include <ags/X/ags_effect_line.h>
30 
31 #include <ags/i18n.h>
32 
33 void ags_line_member_class_init(AgsLineMemberClass *line_member);
34 void ags_line_member_connectable_interface_init(AgsConnectableInterface *connectable);
35 void ags_line_member_init(AgsLineMember *line_member);
36 void ags_line_member_set_property(GObject *gobject,
37 				  guint prop_id,
38 				  const GValue *value,
39 				  GParamSpec *param_spec);
40 void ags_line_member_get_property(GObject *gobject,
41 				  guint prop_id,
42 				  GValue *value,
43 				  GParamSpec *param_spec);
44 void ags_line_member_finalize(GObject *gobject);
45 
46 void ags_line_member_connect(AgsConnectable *connectable);
47 void ags_line_member_disconnect(AgsConnectable *connectable);
48 
49 void ags_line_member_real_change_port(AgsLineMember *line_member,
50 				      gpointer port_data);
51 
52 AgsPort* ags_line_member_find_specifier(GList *recall,
53 					gchar *specifier);
54 
55 GList* ags_line_member_real_find_port(AgsLineMember *line_member);
56 
57 
58 /**
59  * SECTION:ags_line_member
60  * @short_description: Modify assigned recall's port
61  * @title: AgsLineMember
62  * @section_id:
63  * @include: ags/X/ags_line_member.h
64  *
65  * #AgsLineMember is a composite widget to modify ports of recalls. A line member
66  * controls only one specific port of a recall but distinguishes between simple/complex
67  * recall. It is generally packed into a #AgsLine.
68  */
69 
70 enum{
71   CHANGE_PORT,
72   FIND_PORT,
73   LAST_SIGNAL,
74 };
75 
76 enum{
77   PROP_0,
78   PROP_WIDGET_TYPE,
79   PROP_WIDGET_ORIENTATION,
80   PROP_WIDGET_LABEL,
81   PROP_PLAY_CONTAINER,
82   PROP_RECALL_CONTAINER,
83   PROP_PLUGIN_NAME,
84   PROP_FILENAME,
85   PROP_EFFECT,
86   PROP_SPECIFIER,
87   PROP_CONTROL_PORT,
88   PROP_SCALE_PRECISION,
89   PROP_STEP_COUNT,
90   PROP_CONVERSION,
91   PROP_PORT,
92   PROP_PORT_DATA,
93   PROP_RECALL_PORT,
94   PROP_RECALL_PORT_DATA,
95   PROP_TASK_TYPE,
96 };
97 
98 static gpointer ags_line_member_parent_class = NULL;
99 static guint line_member_signals[LAST_SIGNAL];
100 
101 GType
ags_line_member_get_type(void)102 ags_line_member_get_type(void)
103 {
104   static volatile gsize g_define_type_id__volatile = 0;
105 
106   if(g_once_init_enter (&g_define_type_id__volatile)){
107     GType ags_type_line_member = 0;
108 
109     static const GTypeInfo ags_line_member_info = {
110       sizeof(AgsLineMemberClass),
111       NULL, /* base_init */
112       NULL, /* base_finalize */
113       (GClassInitFunc) ags_line_member_class_init,
114       NULL, /* class_finalize */
115       NULL, /* class_data */
116       sizeof(AgsLineMember),
117       0,    /* n_preallocs */
118       (GInstanceInitFunc) ags_line_member_init,
119     };
120 
121     static const GInterfaceInfo ags_connectable_interface_info = {
122       (GInterfaceInitFunc) ags_line_member_connectable_interface_init,
123       NULL, /* interface_finalize */
124       NULL, /* interface_data */
125     };
126 
127     ags_type_line_member = g_type_register_static(GTK_TYPE_FRAME,
128 						  "AgsLineMember", &ags_line_member_info,
129 						  0);
130 
131     g_type_add_interface_static(ags_type_line_member,
132 				AGS_TYPE_CONNECTABLE,
133 				&ags_connectable_interface_info);
134 
135     g_once_init_leave(&g_define_type_id__volatile, ags_type_line_member);
136   }
137 
138   return g_define_type_id__volatile;
139 }
140 
141 void
ags_line_member_class_init(AgsLineMemberClass * line_member)142 ags_line_member_class_init(AgsLineMemberClass *line_member)
143 {
144   GObjectClass *gobject;
145   GParamSpec *param_spec;
146 
147   ags_line_member_parent_class = g_type_class_peek_parent(line_member);
148 
149   /* GObjectClass */
150   gobject = G_OBJECT_CLASS(line_member);
151 
152   gobject->set_property = ags_line_member_set_property;
153   gobject->get_property = ags_line_member_get_property;
154 
155   gobject->finalize = ags_line_member_finalize;
156 
157   /* properties */
158   /**
159    * AgsLineMember:widget-type:
160    *
161    * The widget type to instantiate and use as control.
162    *
163    * Since: 3.0.0
164    */
165   param_spec = g_param_spec_ulong("widget-type",
166 				  i18n_pspec("widget type of line member"),
167 				  i18n_pspec("The widget type this line member packs"),
168 				  0, G_MAXULONG,
169 				  G_TYPE_NONE,
170 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
171   g_object_class_install_property(gobject,
172 				  PROP_WIDGET_TYPE,
173 				  param_spec);
174 
175   /**
176    * AgsLineMember:widget-orientation:
177    *
178    * The widget orientation.
179    *
180    * Since: 3.8.0
181    */
182   param_spec = g_param_spec_uint("widget-orientation",
183 				 i18n_pspec("widget orientation"),
184 				 i18n_pspec("widget orientation"),
185 				 0, G_MAXUINT,
186 				 GTK_ORIENTATION_VERTICAL,
187 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
188   g_object_class_install_property(gobject,
189 				  PROP_WIDGET_ORIENTATION,
190 				  param_spec);
191 
192   /**
193    * AgsLineMember:widget-label:
194    *
195    * The widget's label to use.
196    *
197    * Since: 3.0.0
198    */
199   param_spec = g_param_spec_string("widget-label",
200 				   i18n_pspec("label to display"),
201 				   i18n_pspec("The label to display"),
202 				   NULL,
203 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
204   g_object_class_install_property(gobject,
205 				  PROP_WIDGET_LABEL,
206 				  param_spec);
207 
208   /**
209    * AgsLineMember:play-container:
210    *
211    * The play context recall container.
212    *
213    * Since: 3.3.0
214    */
215   param_spec = g_param_spec_object("play-container",
216 				   i18n_pspec("play container"),
217 				   i18n_pspec("The play context recall container"),
218 				   AGS_TYPE_RECALL_CONTAINER,
219 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
220   g_object_class_install_property(gobject,
221 				  PROP_PLAY_CONTAINER,
222 				  param_spec);
223 
224   /**
225    * AgsLineMember:recall-container:
226    *
227    * The recall context recall container.
228    *
229    * Since: 3.3.0
230    */
231   param_spec = g_param_spec_object("recall-container",
232 				   i18n_pspec("recall container"),
233 				   i18n_pspec("The recall context recall container"),
234 				   AGS_TYPE_RECALL_CONTAINER,
235 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
236   g_object_class_install_property(gobject,
237 				  PROP_RECALL_CONTAINER,
238 				  param_spec);
239 
240   /**
241    * AgsLineMember:plugin-name:
242    *
243    * The plugin name of the recall to use.
244    *
245    * Since: 3.0.0
246    */
247   param_spec = g_param_spec_string("plugin-name",
248 				   i18n_pspec("plugin name to control"),
249 				   i18n_pspec("The plugin's name to control"),
250 				   NULL,
251 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
252   g_object_class_install_property(gobject,
253 				  PROP_PLUGIN_NAME,
254 				  param_spec);
255 
256   /**
257    * AgsLineMember:filename:
258    *
259    * The plugin filename of the recall to apply.
260    *
261    * Since: 3.0.0
262    */
263   param_spec = g_param_spec_string("filename",
264 				   i18n_pspec("the filename"),
265 				   i18n_pspec("The filename of the plugin"),
266 				   NULL,
267 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
268   g_object_class_install_property(gobject,
269 				  PROP_FILENAME,
270 				  param_spec);
271 
272     /**
273    * AgsLineMember:effect:
274    *
275    * The plugin effect of the recall to apply.
276    *
277    * Since: 3.0.0
278    */
279   param_spec = g_param_spec_string("effect",
280 				   i18n_pspec("the effect"),
281 				   i18n_pspec("The effect of the plugin"),
282 				   NULL,
283 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
284   g_object_class_install_property(gobject,
285 				  PROP_EFFECT,
286 				  param_spec);
287 
288   /**
289    * AgsLineMember:specifier:
290    *
291    * The plugin specifier of the recall to apply.
292    *
293    * Since: 3.0.0
294    */
295   param_spec = g_param_spec_string("specifier",
296 				   i18n_pspec("port specifier"),
297 				   i18n_pspec("The specifier of the port"),
298 				   NULL,
299 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
300   g_object_class_install_property(gobject,
301 				  PROP_SPECIFIER,
302 				  param_spec);
303 
304   /**
305    * AgsLineMember:control-port:
306    *
307    * The control port of the recall.
308    *
309    * Since: 3.0.0
310    */
311   param_spec = g_param_spec_string("control-port",
312 				   i18n_pspec("control port index"),
313 				   i18n_pspec("The index of the port to control"),
314 				   NULL,
315 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
316   g_object_class_install_property(gobject,
317 				  PROP_CONTROL_PORT,
318 				  param_spec);
319 
320   /**
321    * AgsLineMember:scale-precision:
322    *
323    * If line member has integer ports, this is the number of steps.
324    *
325    * Since: 3.0.0
326    */
327   param_spec = g_param_spec_uint("scale-precision",
328 				 i18n_pspec("scale precision of line members port"),
329 				 i18n_pspec("The scale precision this line members port has"),
330 				 0,
331 				 G_MAXUINT,
332 				 (guint) AGS_DIAL_DEFAULT_PRECISION,
333 				 G_PARAM_READABLE | G_PARAM_WRITABLE);
334   g_object_class_install_property(gobject,
335 				  PROP_SCALE_PRECISION,
336 				  param_spec);
337 
338   /**
339    * AgsLineMember:step-count:
340    *
341    * If line member has logarithmic ports, this is the number of step count.
342    *
343    * Since: 3.0.0
344    */
345   param_spec = g_param_spec_double("step-count",
346 				   i18n_pspec("step count of line members port"),
347 				   i18n_pspec("The step count this line members port has"),
348 				   0.0,
349 				   G_MAXDOUBLE,
350 				   AGS_LADSPA_CONVERSION_DEFAULT_STEP_COUNT,
351 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
352   g_object_class_install_property(gobject,
353 				  PROP_STEP_COUNT,
354 				  param_spec);
355 
356   /**
357    * AgsLineMember:conversion:
358    *
359    * The conversion of plugin.
360    *
361    * Since: 3.0.0
362    */
363   param_spec = g_param_spec_object("conversion",
364 				   i18n_pspec("conversion to apply"),
365 				   i18n_pspec("The conversion to apply"),
366 				   AGS_TYPE_CONVERSION,
367 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
368   g_object_class_install_property(gobject,
369 				  PROP_CONVERSION,
370 				  param_spec);
371 
372   /**
373    * AgsLineMember:port:
374    *
375    * The matching simple port of plugin name and specifier.
376    *
377    * Since: 3.0.0
378    */
379   param_spec = g_param_spec_object("port",
380 				   i18n_pspec("port to apply"),
381 				   i18n_pspec("The port to apply"),
382 				   AGS_TYPE_PORT,
383 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
384   g_object_class_install_property(gobject,
385 				  PROP_PORT,
386 				  param_spec);
387 
388   /**
389    * AgsLineMember:port-data:
390    *
391    * The port data to apply.
392    *
393    * Since: 3.0.0
394    */
395   param_spec = g_param_spec_pointer("port-data",
396 				    i18n_pspec("port data"),
397 				    i18n_pspec("The port data"),
398 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
399   g_object_class_install_property(gobject,
400 				  PROP_PORT_DATA,
401 				  param_spec);
402 
403   /**
404    * AgsLineMember:recall-port:
405    *
406    * The matching complex port of plugin name and specifier.
407    *
408    * Since: 3.0.0
409    */
410   param_spec = g_param_spec_object("recall-port",
411 				   i18n_pspec("recall port to apply"),
412 				   i18n_pspec("The recall port to apply"),
413 				   AGS_TYPE_PORT,
414 				   G_PARAM_READABLE | G_PARAM_WRITABLE);
415   g_object_class_install_property(gobject,
416 				  PROP_RECALL_PORT,
417 				  param_spec);
418 
419   /**
420    * AgsLineMember:recall-port-data:
421    *
422    * The complex port data to apply.
423    *
424    * Since: 3.0.0
425    */
426   param_spec = g_param_spec_pointer("recall-port-data",
427 				    i18n_pspec("recall port data"),
428 				    i18n_pspec("The recall port data"),
429 				    G_PARAM_READABLE | G_PARAM_WRITABLE);
430   g_object_class_install_property(gobject,
431 				  PROP_RECALL_PORT_DATA,
432 				  param_spec);
433 
434   /**
435    * AgsLineMember:task-type:
436    *
437    * The task type to apply the port.
438    *
439    * Since: 3.0.0
440    */
441   param_spec = g_param_spec_ulong("task-type",
442 				  i18n_pspec("task type to apply"),
443 				  i18n_pspec("The task type to apply the port"),
444 				  0, G_MAXULONG,
445 				  G_TYPE_NONE,
446 				  G_PARAM_READABLE | G_PARAM_WRITABLE);
447   g_object_class_install_property(gobject,
448 				  PROP_TASK_TYPE,
449 				  param_spec);
450 
451   /* AgsLineMember */
452   line_member->change_port = ags_line_member_real_change_port;
453   line_member->find_port = ags_line_member_real_find_port;
454 
455   /* signals */
456   /**
457    * AgsLineMember::change-port:
458    * @line_member: the #AgsLineMember
459    * @port_data: the port's data
460    *
461    * The ::change-port signal notifies modified port.
462    *
463    * Since: 3.0.0
464    */
465   line_member_signals[CHANGE_PORT] =
466     g_signal_new("change-port",
467 		 G_TYPE_FROM_CLASS(line_member),
468 		 G_SIGNAL_RUN_LAST,
469 		 G_STRUCT_OFFSET(AgsLineMemberClass, change_port),
470 		 NULL, NULL,
471 		 g_cclosure_marshal_VOID__POINTER,
472 		 G_TYPE_NONE, 1,
473 		 G_TYPE_POINTER);
474 
475   /**
476    * AgsLine_Member::find-port:
477    * @line_member: the #AgsLineMember to resize
478    *
479    * The ::find-port retrieves all associated ports
480    *
481    * Returns: a #GList-struct with associated ports
482    *
483    * Since: 3.0.0
484    */
485   line_member_signals[FIND_PORT] =
486     g_signal_new("find-port",
487 		 G_TYPE_FROM_CLASS(line_member),
488 		 G_SIGNAL_RUN_LAST,
489 		 G_STRUCT_OFFSET(AgsLineMemberClass, find_port),
490 		 NULL, NULL,
491 		 ags_cclosure_marshal_POINTER__VOID,
492 		 G_TYPE_POINTER, 0);
493 }
494 
495 void
ags_line_member_connectable_interface_init(AgsConnectableInterface * connectable)496 ags_line_member_connectable_interface_init(AgsConnectableInterface *connectable)
497 {
498   connectable->connect = ags_line_member_connect;
499   connectable->disconnect = ags_line_member_disconnect;
500 }
501 
502 void
ags_line_member_init(AgsLineMember * line_member)503 ags_line_member_init(AgsLineMember *line_member)
504 {
505   AgsDial *dial;
506 
507   AgsApplicationContext *application_context;
508 
509   gdouble gui_scale_factor;
510 
511   application_context = ags_application_context_get_instance();
512 
513   g_signal_connect_after((GObject *) line_member, "parent_set",
514 			 G_CALLBACK(ags_line_member_parent_set_callback), (gpointer) line_member);
515 
516   line_member->flags = (AGS_LINE_MEMBER_RESET_BY_ATOMIC |
517 			AGS_LINE_MEMBER_APPLY_RECALL);
518   line_member->port_flags = 0;
519 
520   line_member->widget_type = AGS_TYPE_DIAL;
521   line_member->widget_orientation = GTK_ORIENTATION_VERTICAL;
522   dial = (AgsDial *) g_object_new(AGS_TYPE_DIAL,
523 				  "adjustment", gtk_adjustment_new(0.0, 0.0, 1.0, 0.1, 0.1, 0.0),
524 				  NULL);
525 
526   /* scale factor */
527   gui_scale_factor = ags_ui_provider_get_gui_scale_factor(AGS_UI_PROVIDER(application_context));
528 
529   g_object_set(dial,
530 	       "radius", (guint) (gui_scale_factor * AGS_DIAL_DEFAULT_RADIUS),
531 	       "font-size", (guint) (gui_scale_factor * AGS_DIAL_DEFAULT_FONT_SIZE),
532 	       "button-width", (gint) (gui_scale_factor * AGS_DIAL_DEFAULT_BUTTON_WIDTH),
533 	       "button-height", (gint) (gui_scale_factor * AGS_DIAL_DEFAULT_BUTTON_HEIGHT),
534 	       NULL);
535 
536   gtk_container_add(GTK_CONTAINER(line_member),
537 		    (GtkWidget *) dial);
538 
539   line_member->widget_label = NULL;
540 
541   line_member->play_container = NULL;
542   line_member->recall_container = NULL;
543 
544   line_member->plugin_name = NULL;
545 
546   line_member->filename = NULL;
547   line_member->effect = NULL;
548 
549   line_member->specifier = NULL;
550 
551   line_member->control_port = NULL;
552 
553   line_member->scale_precision = AGS_DIAL_DEFAULT_PRECISION;
554   line_member->step_count = AGS_LADSPA_CONVERSION_DEFAULT_STEP_COUNT;
555 
556   line_member->conversion = NULL;
557 
558   line_member->port = NULL;
559   line_member->port_data = NULL;
560   line_member->active = FALSE;
561 
562   line_member->recall_port = NULL;
563   line_member->recall_port_data = NULL;
564   line_member->recall_active = FALSE;
565 
566   line_member->task_type = G_TYPE_NONE;
567 }
568 
569 void
ags_line_member_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * param_spec)570 ags_line_member_set_property(GObject *gobject,
571 			     guint prop_id,
572 			     const GValue *value,
573 			     GParamSpec *param_spec)
574 {
575   AgsLineMember *line_member;
576 
577   line_member = AGS_LINE_MEMBER(gobject);
578 
579   switch(prop_id){
580   case PROP_WIDGET_TYPE:
581     {
582       GtkWidget *child, *new_child;
583 
584       GtkAdjustment *adjustment;
585 
586       AgsApplicationContext *application_context;
587 
588       GType widget_type;
589 
590       gdouble gui_scale_factor;
591       gdouble lower_value, upper_value;
592       gdouble current_value;
593       gboolean active;
594 
595       //TODO:JK: verify me
596       widget_type = g_value_get_ulong(value);
597 
598       if(widget_type == line_member->widget_type){
599 	return;
600       }
601 
602       application_context = ags_application_context_get_instance();
603 
604       child = gtk_bin_get_child(GTK_BIN(line_member));
605 
606       /* preserver previous range */
607       adjustment = NULL;
608       active = FALSE;
609 
610       if(GTK_IS_RANGE(child)){
611 	adjustment = gtk_range_get_adjustment(GTK_RANGE(child));
612       }else if(GTK_IS_SPIN_BUTTON(child)){
613 	adjustment = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(child));
614       }else if(AGS_IS_DIAL(child)){
615 	adjustment = AGS_DIAL(child)->adjustment;
616       }else if(GTK_IS_TOGGLE_BUTTON(child)){
617 	active = gtk_toggle_button_get_active((GtkToggleButton *) child);
618       }
619 
620       lower_value = 0.0;
621       upper_value = 0.0;
622 
623       current_value = 0.0;
624 
625       if(adjustment != NULL){
626 	lower_value = gtk_adjustment_get_lower(adjustment);
627 	upper_value = gtk_adjustment_get_upper(adjustment);
628 
629 	current_value = gtk_adjustment_get_value(adjustment);
630       }
631 
632       /* destroy old */
633       if(child != NULL){
634 	gtk_widget_destroy(child);
635       }
636 
637       line_member->widget_type = widget_type;
638       new_child = (GtkWidget *) g_object_new(widget_type,
639 					     NULL);
640 
641       /* scale factor */
642       gui_scale_factor = ags_ui_provider_get_gui_scale_factor(AGS_UI_PROVIDER(application_context));
643 
644       if(AGS_IS_DIAL(new_child)){
645 	g_object_set(new_child,
646 		     "radius", (guint) (gui_scale_factor * AGS_DIAL_DEFAULT_RADIUS),
647 		     "font-size", (guint) (gui_scale_factor * AGS_DIAL_DEFAULT_FONT_SIZE),
648 		     "button-width", (gint) (gui_scale_factor * AGS_DIAL_DEFAULT_BUTTON_WIDTH),
649 		     "button-height", (gint) (gui_scale_factor * AGS_DIAL_DEFAULT_BUTTON_HEIGHT),
650 		     NULL);
651       }else if(GTK_IS_SCALE(new_child)){
652 	guint widget_orientation;
653 
654 	widget_orientation = GTK_ORIENTATION_VERTICAL;
655 
656 	g_object_get(line_member,
657 		     "widget-orientation", &widget_orientation,
658 		     NULL);
659 
660 	gtk_orientable_set_orientation(GTK_ORIENTABLE(new_child),
661 				       widget_orientation);
662 
663 	if(widget_orientation == GTK_ORIENTATION_VERTICAL){
664 	  gtk_widget_set_size_request(new_child,
665 				      gui_scale_factor * 16, gui_scale_factor * 100);
666 	}else{
667 	  gtk_widget_set_size_request(new_child,
668 				      gui_scale_factor * 100, gui_scale_factor * 16);
669 	}
670       }else if(AGS_IS_VINDICATOR(new_child)){
671 	g_object_set(new_child,
672 		     "segment-width", (guint) (gui_scale_factor * AGS_VINDICATOR_DEFAULT_SEGMENT_WIDTH),
673 		     "segment-height", (guint) (gui_scale_factor * AGS_VINDICATOR_DEFAULT_SEGMENT_HEIGHT),
674 		     "segment-padding", (guint) (gui_scale_factor * AGS_INDICATOR_DEFAULT_SEGMENT_PADDING),
675 		     NULL);
676 
677 	//FIXME:JK: make indicator orientable
678       }else if(AGS_IS_HINDICATOR(new_child)){
679 	g_object_set(new_child,
680 		     "segment-width", (guint) (gui_scale_factor * AGS_HINDICATOR_DEFAULT_SEGMENT_WIDTH),
681 		     "segment-height", (guint) (gui_scale_factor * AGS_HINDICATOR_DEFAULT_SEGMENT_HEIGHT),
682 		     "segment-padding", (guint) (gui_scale_factor * AGS_INDICATOR_DEFAULT_SEGMENT_PADDING),
683 		     NULL);
684 
685 	//FIXME:JK: make indicator orientable
686       }
687 
688       gtk_widget_queue_resize_no_redraw(new_child);
689       gtk_widget_queue_draw(new_child);
690 
691       /* set range */
692       if(GTK_IS_RANGE(new_child)){
693 	GtkAdjustment *new_adjustment;
694 
695 	new_adjustment = gtk_range_get_adjustment(GTK_RANGE(new_child));
696 
697 	gtk_adjustment_set_lower(new_adjustment,
698 				 lower_value);
699 	gtk_adjustment_set_upper(new_adjustment,
700 				 upper_value);
701 
702 	gtk_adjustment_set_value(new_adjustment,
703 				 current_value);
704       }else if(GTK_IS_SPIN_BUTTON(new_child)){
705 	GtkAdjustment *new_adjustment;
706 
707 	new_adjustment = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(new_child));
708 
709 	gtk_adjustment_set_lower(new_adjustment,
710 				 lower_value);
711 	gtk_adjustment_set_upper(new_adjustment,
712 				 upper_value);
713 
714 	gtk_adjustment_set_value(new_adjustment,
715 				 current_value);
716       }else if(AGS_IS_DIAL(new_child)){
717 	GtkAdjustment *new_adjustment;
718 
719 	new_adjustment = AGS_DIAL(new_child)->adjustment;
720 
721 	gtk_adjustment_set_lower(new_adjustment,
722 				 lower_value);
723 	gtk_adjustment_set_upper(new_adjustment,
724 				 upper_value);
725 
726 	gtk_adjustment_set_value(new_adjustment,
727 				 current_value);
728 
729 	gtk_widget_queue_draw((GtkWidget *) new_child);
730       }else if(GTK_IS_TOGGLE_BUTTON(new_child)){
731 	gtk_toggle_button_set_active((GtkToggleButton *) new_child,
732 				     active);
733       }else{
734 	if(!(AGS_IS_INDICATOR(new_child) ||
735 	     AGS_IS_LED(new_child))){
736 	  g_warning("ags_line_member_set_property() - unknown child type %s", g_type_name(widget_type));
737 	}
738       }
739 
740       /* add */
741       gtk_container_add(GTK_CONTAINER(line_member),
742 			new_child);
743     }
744     break;
745   case PROP_WIDGET_ORIENTATION:
746     {
747       GtkWidget *child;
748 
749       AgsApplicationContext *application_context;
750 
751       gdouble gui_scale_factor;
752       guint widget_orientation;
753 
754       widget_orientation = g_value_get_uint(value);
755 
756       line_member->widget_orientation = widget_orientation;
757 
758       application_context = ags_application_context_get_instance();
759 
760       /* scale factor */
761       gui_scale_factor = ags_ui_provider_get_gui_scale_factor(AGS_UI_PROVIDER(application_context));
762 
763       child = gtk_bin_get_child(GTK_BIN(line_member));
764 
765       if(GTK_IS_SCALE(child)){
766 	gtk_orientable_set_orientation(GTK_ORIENTABLE(child),
767 				       widget_orientation);
768 
769 	if(widget_orientation == GTK_ORIENTATION_VERTICAL){
770 	  gtk_widget_set_size_request(child,
771 				      gui_scale_factor * 16, gui_scale_factor * 100);
772 	}else{
773 	  gtk_widget_set_size_request(child,
774 				      gui_scale_factor * 100, gui_scale_factor * 16);
775 	}
776       }else if(AGS_IS_INDICATOR(child)){
777 	gtk_orientable_set_orientation(GTK_ORIENTABLE(child),
778 				       widget_orientation);
779 
780 	//FIXME:JK: make indicator orientable
781       }
782     }
783     break;
784   case PROP_WIDGET_LABEL:
785     {
786       gchar *label;
787 
788       label = g_value_get_string(value);
789 
790       if(label == line_member->widget_label){
791 	return;
792       }
793 
794       if(line_member->widget_label != NULL){
795 	g_free(line_member->widget_label);
796       }
797 
798       line_member->widget_label = g_strdup(label);
799       ags_line_member_set_label(line_member, line_member->widget_label);
800     }
801     break;
802   case PROP_PLAY_CONTAINER:
803     {
804       AgsRecallContainer *play_container;
805 
806       play_container = (AgsRecallContainer *) g_value_get_object(value);
807 
808       if(play_container == line_member->play_container){
809 	return;
810       }
811 
812       if(line_member->play_container != NULL){
813 	g_object_unref(line_member->play_container);
814       }
815 
816       if(play_container != NULL){
817 	g_object_ref(play_container);
818       }
819 
820       line_member->play_container = play_container;
821     }
822     break;
823   case PROP_RECALL_CONTAINER:
824     {
825       AgsRecallContainer *recall_container;
826 
827       recall_container = (AgsRecallContainer *) g_value_get_object(value);
828 
829       if(recall_container == line_member->recall_container){
830 	return;
831       }
832 
833       if(line_member->recall_container != NULL){
834 	g_object_unref(line_member->recall_container);
835       }
836 
837       if(recall_container != NULL){
838 	g_object_ref(recall_container);
839       }
840 
841       line_member->recall_container = recall_container;
842     }
843     break;
844   case PROP_PLUGIN_NAME:
845     {
846       gchar *plugin_name;
847 
848       plugin_name = g_value_get_string(value);
849 
850       if(plugin_name == line_member->plugin_name){
851 	return;
852       }
853 
854       line_member->plugin_name = g_strdup(plugin_name);
855     }
856     break;
857   case PROP_FILENAME:
858     {
859       gchar *filename;
860 
861       filename = g_value_get_string(value);
862 
863       if(filename == line_member->filename){
864 	return;
865       }
866 
867       if(line_member->filename != NULL){
868 	g_free(line_member->filename);
869       }
870 
871       if(filename != NULL){
872 	if(!g_file_test(filename,
873 			G_FILE_TEST_EXISTS)){
874 	  AgsWindow *window;
875 
876 	  window = (AgsWindow *) gtk_widget_get_toplevel((GtkWidget *) line_member);
877 
878 	  ags_window_show_error(window,
879 				g_strdup_printf("Plugin file not present %s",
880 						filename));
881 	}
882       }
883 
884       line_member->filename = g_strdup(filename);
885     }
886     break;
887   case PROP_EFFECT:
888     {
889       gchar *effect;
890 
891       effect = g_value_get_string(value);
892 
893       if(effect == line_member->effect){
894 	return;
895       }
896 
897       line_member->effect = g_strdup(effect);
898     }
899     break;
900   case PROP_SPECIFIER:
901     {
902       gchar *specifier;
903 
904       specifier = g_value_get_string(value);
905 
906       if(specifier == line_member->specifier){
907 	return;
908       }
909 
910       line_member->specifier = g_strdup(specifier);
911     }
912     break;
913   case PROP_CONTROL_PORT:
914     {
915       gchar *control_port;
916 
917       control_port = g_value_get_string(value);
918 
919       if(control_port == line_member->control_port){
920 	return;
921       }
922 
923       line_member->control_port = g_strdup(control_port);
924     }
925     break;
926   case PROP_SCALE_PRECISION:
927     {
928       GtkWidget *child;
929 
930       guint scale_precision;
931 
932       scale_precision = g_value_get_uint(value);
933 
934       line_member->scale_precision = scale_precision;
935       child = gtk_bin_get_child(GTK_BIN(line_member));
936 
937       if(AGS_IS_DIAL(child)){
938 	g_object_set(child,
939 		     "scale-precision", scale_precision,
940 		     NULL);
941       }
942     }
943     break;
944   case PROP_STEP_COUNT:
945     {
946       gdouble step_count;
947 
948       step_count = g_value_get_double(value);
949 
950       line_member->step_count = step_count;
951     }
952     break;
953   case PROP_CONVERSION:
954     {
955       AgsConversion *conversion;
956 
957       conversion = g_value_get_object(value);
958 
959       if(conversion == line_member->conversion){
960 	return;
961       }
962 
963       if(line_member->conversion != NULL){
964 	g_object_unref(line_member->conversion);
965       }
966 
967       if(conversion != NULL){
968 	g_object_ref(conversion);
969       }
970 
971       line_member->conversion = conversion;
972     }
973     break;
974     case PROP_PORT:
975     {
976       AgsPort *port;
977 
978       port = g_value_get_object(value);
979 
980       if(port == line_member->port){
981 	return;
982       }
983 
984       if(line_member->port != NULL){
985 	g_object_unref(line_member->port);
986       }
987 
988       if(port != NULL){
989 	g_object_ref(port);
990 
991 	if((AGS_PORT_INFINITE_RANGE & (port->flags)) != 0){
992 	  GtkWidget *child;
993 
994 	  child = gtk_bin_get_child(GTK_BIN(line_member));
995 
996 	  //TODO:JK: add more orientations
997 
998 	  if(AGS_IS_DIAL(child)){
999 	    AGS_DIAL(child)->flags |= AGS_DIAL_SEEMLESS_MODE;
1000 	  }
1001 	}
1002       }
1003 
1004       line_member->port = port;
1005     }
1006     break;
1007   case PROP_PORT_DATA:
1008     {
1009       gpointer port_data;
1010 
1011       port_data = g_value_get_pointer(value);
1012 
1013       if(port_data == line_member->port_data){
1014 	return;
1015       }
1016 
1017       line_member->port_data = port_data;
1018     }
1019     break;
1020   case PROP_RECALL_PORT:
1021     {
1022       AgsPort *port;
1023 
1024       port = g_value_get_object(value);
1025 
1026       if(port == line_member->recall_port){
1027 	return;
1028       }
1029 
1030       if(line_member->recall_port != NULL){
1031 	g_object_unref(line_member->recall_port);
1032       }
1033 
1034       if(port != NULL){
1035 	g_object_ref(port);
1036       }
1037 
1038       line_member->recall_port = port;
1039     }
1040     break;
1041   case PROP_RECALL_PORT_DATA:
1042     {
1043       gpointer port_data;
1044 
1045       port_data = g_value_get_pointer(value);
1046 
1047       if(port_data == line_member->recall_port_data){
1048 	return;
1049       }
1050 
1051       line_member->recall_port_data = port_data;
1052     }
1053     break;
1054   case PROP_TASK_TYPE:
1055     {
1056       GType type;
1057 
1058       type = g_value_get_ulong(value);
1059 
1060       if(line_member->task_type == type){
1061 	return;
1062       }
1063 
1064       line_member->task_type = type;
1065     }
1066     break;
1067   default:
1068     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
1069     break;
1070   }
1071 }
1072 
1073 void
ags_line_member_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * param_spec)1074 ags_line_member_get_property(GObject *gobject,
1075 			     guint prop_id,
1076 			     GValue *value,
1077 			     GParamSpec *param_spec)
1078 {
1079   AgsLineMember *line_member;
1080 
1081   line_member = AGS_LINE_MEMBER(gobject);
1082 
1083   switch(prop_id){
1084   case PROP_WIDGET_TYPE:
1085     {
1086       g_value_set_ulong(value, line_member->widget_type);
1087     }
1088     break;
1089   case PROP_WIDGET_ORIENTATION:
1090     {
1091       g_value_set_uint(value, line_member->widget_orientation);
1092     }
1093     break;
1094   case PROP_WIDGET_LABEL:
1095     {
1096       g_value_set_string(value, line_member->widget_label);
1097     }
1098     break;
1099   case PROP_PLAY_CONTAINER:
1100     {
1101       g_value_set_object(value, line_member->play_container);
1102     }
1103     break;
1104   case PROP_RECALL_CONTAINER:
1105     {
1106       g_value_set_object(value, line_member->recall_container);
1107     }
1108     break;
1109   case PROP_PLUGIN_NAME:
1110     {
1111       g_value_set_string(value, line_member->plugin_name);
1112     }
1113     break;
1114   case PROP_FILENAME:
1115     {
1116       g_value_set_string(value, line_member->filename);
1117     }
1118     break;
1119   case PROP_EFFECT:
1120     {
1121       g_value_set_string(value, line_member->effect);
1122     }
1123     break;
1124   case PROP_SPECIFIER:
1125     {
1126       g_value_set_string(value, line_member->specifier);
1127     }
1128     break;
1129   case PROP_CONTROL_PORT:
1130     {
1131       g_value_set_string(value, line_member->control_port);
1132     }
1133     break;
1134   case PROP_SCALE_PRECISION:
1135     {
1136       g_value_set_uint(value, line_member->scale_precision);
1137     }
1138     break;
1139   case PROP_STEP_COUNT:
1140     {
1141       g_value_set_double(value, line_member->step_count);
1142     }
1143     break;
1144   case PROP_CONVERSION:
1145     {
1146       g_value_set_object(value, line_member->conversion);
1147     }
1148     break;
1149   case PROP_PORT:
1150     {
1151       g_value_set_object(value, line_member->port);
1152     }
1153     break;
1154   case PROP_PORT_DATA:
1155     {
1156       g_value_set_pointer(value, line_member->port_data);
1157     }
1158     break;
1159   case PROP_RECALL_PORT:
1160     {
1161       g_value_set_object(value, line_member->port);
1162     }
1163     break;
1164   case PROP_RECALL_PORT_DATA:
1165     {
1166       g_value_set_pointer(value, line_member->port_data);
1167     }
1168     break;
1169   case PROP_TASK_TYPE:
1170     {
1171       g_value_set_ulong(value, line_member->task_type);
1172     }
1173     break;
1174   default:
1175     G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
1176     break;
1177   }
1178 }
1179 
1180 void
ags_line_member_finalize(GObject * gobject)1181 ags_line_member_finalize(GObject *gobject)
1182 {
1183   AgsLineMember *line_member;
1184 
1185   line_member = (AgsLineMember *) gobject;
1186 
1187   g_free(line_member->widget_label);
1188 
1189   if(line_member->play_container != NULL){
1190     g_object_unref(line_member->play_container);
1191   }
1192 
1193   if(line_member->recall_container != NULL){
1194     g_object_unref(line_member->recall_container);
1195   }
1196 
1197   g_free(line_member->plugin_name);
1198 
1199   g_free(line_member->filename);
1200   g_free(line_member->effect);
1201 
1202   g_free(line_member->specifier);
1203 
1204   g_free(line_member->control_port);
1205 
1206   if(line_member->conversion != NULL){
1207     g_object_unref(line_member->conversion);
1208   }
1209 
1210   if(line_member->port != NULL){
1211     g_object_unref(line_member->port);
1212   }
1213 
1214   if(line_member->recall_port != NULL){
1215     g_object_unref(line_member->recall_port);
1216   }
1217 
1218   /* call parent */
1219   G_OBJECT_CLASS(ags_line_member_parent_class)->finalize(gobject);
1220 }
1221 
1222 void
ags_line_member_connect(AgsConnectable * connectable)1223 ags_line_member_connect(AgsConnectable *connectable)
1224 {
1225   AgsLineMember *line_member;
1226   GtkWidget *control;
1227 
1228   line_member = AGS_LINE_MEMBER(connectable);
1229 
1230   if((AGS_LINE_MEMBER_CONNECTED & (line_member->flags)) != 0){
1231     return;
1232   }
1233 
1234   line_member->flags |= AGS_LINE_MEMBER_CONNECTED;
1235 
1236   ags_line_member_find_port(line_member);
1237 
1238   control = gtk_bin_get_child(GTK_BIN(line_member));
1239 
1240   if((AGS_LINE_MEMBER_APPLY_INITIAL & (line_member->flags)) != 0){
1241     GtkAdjustment *adjustment;
1242 
1243     gboolean is_toggled;
1244 
1245     adjustment = NULL;
1246     is_toggled = FALSE;
1247 
1248     if(AGS_IS_DIAL(control)){
1249       adjustment = AGS_DIAL(control)->adjustment;
1250     }else if(GTK_IS_RANGE(control)){
1251       adjustment = gtk_range_get_adjustment(GTK_RANGE(control));
1252     }else if(GTK_IS_SPIN_BUTTON(control)){
1253       adjustment = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(control));
1254     }else if(GTK_IS_TOGGLE_BUTTON(control)){
1255       line_member->active = gtk_toggle_button_get_active((GtkToggleButton *) control);
1256 
1257       is_toggled = TRUE;
1258     }
1259 
1260     if(is_toggled){
1261       ags_line_member_change_port(line_member,
1262 				  &(line_member->active));
1263     }else if(adjustment != NULL){
1264       gdouble value;
1265 
1266       value = gtk_adjustment_get_value(adjustment);
1267 
1268       ags_line_member_change_port(line_member,
1269 				  &value);
1270     }
1271 
1272     line_member->flags &= (~AGS_LINE_MEMBER_APPLY_INITIAL);
1273   }
1274 
1275   /* widget callback */
1276   if(line_member->widget_type == AGS_TYPE_DIAL){
1277     g_signal_connect_after(GTK_WIDGET(control), "value-changed",
1278 			   G_CALLBACK(ags_line_member_dial_changed_callback), line_member);
1279   }else if(g_type_is_a(line_member->widget_type, GTK_TYPE_SCALE)){
1280     g_signal_connect_after(GTK_WIDGET(control), "value-changed",
1281 			   G_CALLBACK(ags_line_member_scale_changed_callback), line_member);
1282   }else if(line_member->widget_type == GTK_TYPE_SPIN_BUTTON){
1283     g_signal_connect_after(GTK_WIDGET(control), "value-changed",
1284 			   G_CALLBACK(ags_line_member_spin_button_changed_callback), line_member);
1285   }else if(line_member->widget_type == GTK_TYPE_CHECK_BUTTON){
1286     g_signal_connect_after(GTK_WIDGET(control), "clicked",
1287 			   G_CALLBACK(ags_line_member_check_button_clicked_callback), line_member);
1288   }else if(line_member->widget_type == GTK_TYPE_TOGGLE_BUTTON){
1289     g_signal_connect_after(GTK_WIDGET(control), "clicked",
1290 			   G_CALLBACK(ags_line_member_toggle_button_clicked_callback), line_member);
1291   }else if(line_member->widget_type == GTK_TYPE_BUTTON){
1292     g_signal_connect_after(GTK_WIDGET(control), "clicked",
1293 			   G_CALLBACK(ags_line_member_button_clicked_callback), line_member);
1294   }
1295 }
1296 
1297 void
ags_line_member_disconnect(AgsConnectable * connectable)1298 ags_line_member_disconnect(AgsConnectable *connectable)
1299 {
1300   AgsLineMember *line_member;
1301   GtkWidget *control;
1302 
1303   line_member = AGS_LINE_MEMBER(connectable);
1304 
1305   if((AGS_LINE_MEMBER_CONNECTED & (line_member->flags)) == 0){
1306     return;
1307   }
1308 
1309   line_member->flags &= (~AGS_LINE_MEMBER_CONNECTED);
1310 
1311   /* widget callback */
1312   control = gtk_bin_get_child(GTK_BIN(line_member));
1313 
1314   if(line_member->widget_type == AGS_TYPE_DIAL){
1315     g_object_disconnect(GTK_WIDGET(control),
1316 			"any_signal::value-changed",
1317 			G_CALLBACK(ags_line_member_dial_changed_callback),
1318 			line_member,
1319 			NULL);
1320   }else if(g_type_is_a(line_member->widget_type, GTK_TYPE_SCALE)){
1321     g_object_disconnect(GTK_WIDGET(control),
1322 			"any_signal::value-changed",
1323 			G_CALLBACK(ags_line_member_scale_changed_callback),
1324 			line_member,
1325 			NULL);
1326   }else if(line_member->widget_type == GTK_TYPE_SPIN_BUTTON){
1327     g_object_disconnect(GTK_WIDGET(control),
1328 			"any_signal::value-changed",
1329 			G_CALLBACK(ags_line_member_spin_button_changed_callback),
1330 			line_member,
1331 			NULL);
1332   }else if(line_member->widget_type == GTK_TYPE_CHECK_BUTTON){
1333     g_object_disconnect(GTK_WIDGET(control),
1334 			"any_signal::clicked",
1335 			G_CALLBACK(ags_line_member_check_button_clicked_callback),
1336 			line_member,
1337 			NULL);
1338   }else if(line_member->widget_type == GTK_TYPE_TOGGLE_BUTTON){
1339     g_object_disconnect(GTK_WIDGET(control),
1340 			"any_signal::clicked",
1341 			G_CALLBACK(ags_line_member_toggle_button_clicked_callback),
1342 			line_member,
1343 			NULL);
1344   }else if(line_member->widget_type == GTK_TYPE_BUTTON){
1345     g_object_disconnect(GTK_WIDGET(control),
1346 			"any_signal::clicked",
1347 			G_CALLBACK(ags_line_member_button_clicked_callback),
1348 			line_member,
1349 			NULL);
1350   }
1351 }
1352 
1353 GtkWidget*
ags_line_member_get_widget(AgsLineMember * line_member)1354 ags_line_member_get_widget(AgsLineMember *line_member)
1355 {
1356   return(gtk_bin_get_child(GTK_BIN(line_member)));
1357 }
1358 
1359 /**
1360  * ags_line_member_set_label:
1361  * @line_member: an #AgsLineMember
1362  * @label: the label of the control
1363  *
1364  * Modify the label of the line member.
1365  */
1366 void
ags_line_member_set_label(AgsLineMember * line_member,gchar * label)1367 ags_line_member_set_label(AgsLineMember *line_member,
1368 			  gchar *label)
1369 {
1370   GtkWidget *child_widget;
1371 
1372   if(g_type_is_a(line_member->widget_type,
1373 		 GTK_TYPE_BUTTON)){
1374     child_widget = gtk_bin_get_child(GTK_BIN(line_member));
1375 
1376     g_object_set(G_OBJECT(child_widget),
1377 		 "label", label,
1378 		 NULL);
1379   }else{
1380     gtk_frame_set_label_widget((GtkFrame *) line_member,
1381 			       g_object_new(GTK_TYPE_LABEL,
1382 					    "wrap", FALSE,
1383 					    "use-markup", TRUE,
1384 					    "label", g_markup_printf_escaped("<small>%s</small>", label),
1385 					    NULL));
1386   }
1387 
1388 
1389   line_member->widget_label = label;
1390 }
1391 
1392 void
ags_line_member_real_change_port(AgsLineMember * line_member,gpointer port_data)1393 ags_line_member_real_change_port(AgsLineMember *line_member,
1394 				 gpointer port_data)
1395 {
1396   if((AGS_LINE_MEMBER_RESET_BY_ATOMIC & (line_member->flags)) != 0){
1397     AgsPort *port;
1398 
1399     GRecMutex *port_mutex;
1400 
1401     GValue value = {0,};
1402 
1403     port = line_member->port;
1404 
1405     if(port == NULL){
1406       g_warning("ags_line_member_change_port() - no port available");
1407       return;
1408     }
1409 
1410     /* get port mutex */
1411     port_mutex = AGS_PORT_GET_OBJ_MUTEX(port);
1412 
1413     /* change */
1414     g_rec_mutex_lock(port_mutex);
1415 
1416     if(!port->port_value_is_pointer){
1417       if(port->port_value_type == G_TYPE_BOOLEAN){
1418 	g_value_init(&value,
1419 		     G_TYPE_BOOLEAN);
1420 
1421 	g_value_set_boolean(&value,
1422 			    ((gboolean *) port_data)[0]);
1423       }else if(port->port_value_type == G_TYPE_INT64){
1424 	g_value_init(&value,
1425 		     G_TYPE_INT64);
1426 	g_value_set_int64(&value,
1427 			  ((gint *) port_data)[0]);
1428       }else if(port->port_value_type == G_TYPE_UINT64){
1429 	g_value_init(&value,
1430 		     G_TYPE_UINT64);
1431 
1432 	g_value_set_uint64(&value,
1433 			   ((guint *) port_data)[0]);
1434       }else if(port->port_value_type == G_TYPE_FLOAT){
1435 	gdouble val;
1436 	gfloat port_val;
1437 
1438 	if(GTK_IS_TOGGLE_BUTTON(gtk_bin_get_child((GtkBin *) line_member))){
1439 	  if(((gboolean *) port_data)[0]){
1440 	    val = 1.0;
1441 	  }else{
1442 	    val = 0.0;
1443 	  }
1444 	}else{
1445 	  val = ((gdouble *) port_data)[0];
1446 	}
1447 
1448 	port_val = (gfloat) val;
1449 
1450 	if(line_member->conversion != NULL){
1451 	  gboolean success;
1452 
1453 	  success = FALSE;
1454 
1455 	  if(AGS_IS_DIAL(gtk_bin_get_child((GtkBin *) line_member))){
1456 	    success = TRUE;
1457 	  }else if(GTK_IS_RANGE(gtk_bin_get_child((GtkBin *) line_member))){
1458 	    success = TRUE;
1459 	  }else if(GTK_IS_SPIN_BUTTON(gtk_bin_get_child((GtkBin *) line_member))){
1460 	    success = TRUE;
1461 	  }else{
1462 	    g_warning("unsupported child type in conversion");
1463 	  }
1464 
1465 	  if(success){
1466 	    port_val = ags_conversion_convert(line_member->conversion,
1467 					      val,
1468 					      FALSE);
1469 	  }
1470 	}
1471 
1472 	g_value_init(&value,
1473 		     G_TYPE_FLOAT);
1474 
1475 	g_value_set_float(&value,
1476 			  (gfloat) port_val);
1477       }else if(port->port_value_type == G_TYPE_DOUBLE){
1478 	gdouble val;
1479 	gdouble port_val;
1480 
1481 	if(GTK_IS_TOGGLE_BUTTON(gtk_bin_get_child((GtkBin *) line_member))){
1482 	  if(((gboolean *) port_data)[0]){
1483 	    val = 1.0;
1484 	  }else{
1485 	    val = 0.0;
1486 	  }
1487 	}else{
1488 	  val = ((gdouble *) port_data)[0];
1489 	}
1490 
1491 	port_val = val;
1492 
1493 	if(line_member->conversion != NULL){
1494 	  gboolean success;
1495 
1496 	  success = FALSE;
1497 
1498 	  if(AGS_IS_DIAL(gtk_bin_get_child((GtkBin *) line_member))){
1499 	    success = TRUE;
1500 	  }else if(GTK_IS_RANGE(gtk_bin_get_child((GtkBin *) line_member))){
1501 	    success = TRUE;
1502 	  }else if(GTK_IS_SPIN_BUTTON(gtk_bin_get_child((GtkBin *) line_member))){
1503 	    success = TRUE;
1504 	  }else{
1505 	    g_warning("unsupported child type in conversion");
1506 	  }
1507 
1508 	  if(success){
1509 	    port_val = ags_conversion_convert(line_member->conversion,
1510 					      val,
1511 					      FALSE);
1512 	  }
1513 	}
1514 
1515 	g_value_init(&value,
1516 		     G_TYPE_DOUBLE);
1517 
1518 	g_value_set_double(&value,
1519 			   port_val);
1520       }
1521     }else{
1522       if(port->port_value_type == G_TYPE_OBJECT){
1523 	g_value_init(&value,
1524 		     G_TYPE_OBJECT);
1525 	g_value_set_object(&value,
1526 			   port_data);
1527       }else{
1528 	if(port->port_value_type == G_TYPE_BOOLEAN ||
1529 	   port->port_value_type == G_TYPE_INT64 ||
1530 	   port->port_value_type == G_TYPE_UINT64 ||
1531 	   port->port_value_type == G_TYPE_FLOAT ||
1532 	   port->port_value_type == G_TYPE_DOUBLE ||
1533 	   port->port_value_type == G_TYPE_POINTER){
1534 	  g_value_init(&value,
1535 		       G_TYPE_POINTER);
1536 
1537 	  g_value_set_pointer(&value,
1538 			      port_data);
1539 
1540 	}
1541       }
1542     }
1543 
1544     g_rec_mutex_unlock(port_mutex);
1545 
1546     ags_port_safe_write(line_member->port,
1547 			&value);
1548 
1549     if((AGS_LINE_MEMBER_APPLY_RECALL & (line_member->flags)) != 0){
1550       ags_port_safe_write(line_member->recall_port,
1551 			  &value);
1552     }
1553   }
1554 
1555   if((AGS_LINE_MEMBER_RESET_BY_TASK & (line_member->flags)) != 0){
1556     AgsTask *task;
1557 
1558     AgsApplicationContext *application_context;
1559 
1560     application_context = ags_application_context_get_instance();
1561 
1562     task = (AgsTask *) g_object_new(line_member->task_type,
1563 				    line_member->control_port, port_data,
1564 				    NULL);
1565 
1566     ags_ui_provider_schedule_task(AGS_UI_PROVIDER(application_context),
1567 				  (AgsTask *) task);
1568   }
1569 }
1570 
1571 /**
1572  * ags_line_change_port:
1573  * @line_member: an #AgsLineMember
1574  * @port_data: the port's value
1575  *
1576  * Is emitted as port's value is modified.
1577  *
1578  * Since: 3.0.0
1579  */
1580 void
ags_line_member_change_port(AgsLineMember * line_member,gpointer port_data)1581 ags_line_member_change_port(AgsLineMember *line_member,
1582 			    gpointer port_data)
1583 {
1584   g_return_if_fail(AGS_IS_LINE_MEMBER(line_member));
1585 
1586   g_object_ref((GObject *) line_member);
1587   g_signal_emit(G_OBJECT(line_member),
1588 		line_member_signals[CHANGE_PORT], 0,
1589 		port_data);
1590   g_object_unref((GObject *) line_member);
1591 }
1592 
1593 AgsPort*
ags_line_member_find_specifier(GList * recall,gchar * specifier)1594 ags_line_member_find_specifier(GList *recall,
1595 			       gchar *specifier)
1596 {
1597   AgsPort *current_port;
1598 
1599   GList *start_port, *port;
1600 
1601   current_port = NULL;
1602 
1603   while(recall != NULL){
1604     if(ags_recall_test_behaviour_flags(recall->data, AGS_SOUND_BEHAVIOUR_BULK_MODE)){
1605       recall = recall->next;
1606 
1607       continue;
1608     }
1609 
1610     g_object_get(recall->data,
1611 		 "port", &start_port,
1612 		 NULL);
1613 
1614     port = ags_port_find_specifier(start_port,
1615 				   specifier);
1616 
1617 #ifdef AGS_DEBUG
1618     g_message("search port in %s", G_OBJECT_TYPE_NAME(recall->data));
1619 #endif
1620 
1621     if(port != NULL){
1622       current_port = port->data;
1623     }
1624 
1625     g_list_free_full(start_port,
1626 		     g_object_unref);
1627 
1628     if(current_port != NULL){
1629       break;
1630     }
1631 
1632     /* iterate */
1633     recall = recall->next;
1634   }
1635 
1636   return(current_port);
1637 }
1638 
1639 GList*
ags_line_member_real_find_port(AgsLineMember * line_member)1640 ags_line_member_real_find_port(AgsLineMember *line_member)
1641 {
1642   AgsMachine *machine;
1643   GtkWidget *parent;
1644 
1645   AgsAudio *audio;
1646   AgsChannel *channel;
1647   AgsPort *audio_port, *channel_port;
1648   AgsPort *recall_audio_port, *recall_channel_port;
1649 
1650   GList *port;
1651   GList *start_list;
1652 
1653   gchar *specifier;
1654 
1655   if(!AGS_IS_LINE_MEMBER(line_member)){
1656     return(NULL);
1657   }
1658 
1659   specifier = line_member->specifier;
1660 
1661   if(specifier == NULL){
1662     return(NULL);
1663   }
1664 
1665   machine = (AgsMachine *) gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1666 						   AGS_TYPE_MACHINE);
1667   parent = gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1668 				   AGS_TYPE_LINE);
1669 
1670   audio = machine->audio;
1671 
1672   if(parent != NULL){
1673     channel = AGS_LINE(parent)->channel;
1674   }else{
1675     parent = gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1676 				     AGS_TYPE_EFFECT_LINE);
1677 
1678     if(parent != NULL){
1679       channel = AGS_EFFECT_LINE(parent)->channel;
1680     }else{
1681       return(NULL);
1682     }
1683   }
1684 
1685   audio_port = NULL;
1686   channel_port = NULL;
1687 
1688   recall_audio_port = NULL;
1689   recall_channel_port = NULL;
1690 
1691   port = NULL;
1692 
1693   /* play context */
1694   g_object_get(channel,
1695 	       "play", &start_list,
1696 	       NULL);
1697 
1698   channel_port = ags_line_member_find_specifier(start_list,
1699 						specifier);
1700 
1701   g_list_free_full(start_list,
1702 		   g_object_unref);
1703 
1704   /* recall context */
1705   g_object_get(channel,
1706 	       "recall", &start_list,
1707 	       NULL);
1708 
1709   recall_channel_port = ags_line_member_find_specifier(start_list,
1710 						       specifier);
1711 
1712   g_list_free_full(start_list,
1713 		   g_object_unref);
1714 
1715   if(channel_port != NULL){
1716     port = g_list_prepend(port,
1717 			  channel_port);
1718   }
1719 
1720   if(recall_channel_port != NULL){
1721     port = g_list_prepend(port,
1722 			  recall_channel_port);
1723   }
1724 
1725   /* search audio */
1726   if(channel_port == NULL &&
1727      recall_channel_port == NULL){
1728     GList *start_list;
1729 
1730     /* play context */
1731     g_object_get(audio,
1732 		 "play", &start_list,
1733 		 NULL);
1734 
1735     audio_port = ags_line_member_find_specifier(start_list,
1736 						specifier);
1737 
1738     g_list_free_full(start_list,
1739 		     g_object_unref);
1740 
1741     /* recall context */
1742     g_object_get(audio,
1743 		 "recall", &start_list,
1744 		 NULL);
1745 
1746     recall_audio_port = ags_line_member_find_specifier(start_list,
1747 						       specifier);
1748 
1749     g_list_free_full(start_list,
1750 		     g_object_unref);
1751 
1752     if(audio_port != NULL){
1753       port = g_list_prepend(port,
1754 			    audio_port);
1755     }
1756 
1757     if(recall_audio_port != NULL){
1758       port = g_list_prepend(port,
1759 			    recall_audio_port);
1760     }
1761   }
1762 
1763   if(channel_port != NULL || recall_channel_port != NULL){
1764     g_object_set(G_OBJECT(line_member),
1765 		 "port", channel_port,
1766 		 NULL);
1767 
1768     g_object_set(G_OBJECT(line_member),
1769 		 "recall-port", recall_channel_port,
1770 		 NULL);
1771   }else if(audio_port != NULL || recall_audio_port != NULL){
1772     g_object_set(G_OBJECT(line_member),
1773 		 "port", audio_port,
1774 		 NULL);
1775 
1776     g_object_set(G_OBJECT(line_member),
1777 		 "recall-port", recall_audio_port,
1778 		 NULL);
1779   }
1780 
1781   return(port);
1782 }
1783 
1784 /**
1785  * ags_line_member_find_port:
1786  * @line_member: an #AgsLineMember
1787  *
1788  * Lookup ports of assigned recalls.
1789  *
1790  * Returns: a #GList-struct containing all related #AgsPort
1791  *
1792  * Since: 3.0.0
1793  */
1794 GList*
ags_line_member_find_port(AgsLineMember * line_member)1795 ags_line_member_find_port(AgsLineMember *line_member)
1796 {
1797   GList *list;
1798 
1799   list = NULL;
1800   g_return_val_if_fail(AGS_IS_LINE_MEMBER(line_member),
1801 		       NULL);
1802 
1803   g_object_ref((GObject *) line_member);
1804   g_signal_emit((GObject *) line_member,
1805 		line_member_signals[FIND_PORT], 0,
1806 		&list);
1807   g_object_unref((GObject *) line_member);
1808 
1809   return(list);
1810 }
1811 
1812 /**
1813  * ags_line_member_chained_event:
1814  * @line_member: an #AgsLineMember
1815  *
1816  * Chain changed control and apply the very same value to grouped
1817  * controls if sticky controls set.
1818  *
1819  * Since: 3.0.0
1820  */
1821 void
ags_line_member_chained_event(AgsLineMember * line_member)1822 ags_line_member_chained_event(AgsLineMember *line_member)
1823 {
1824   AgsMachine *machine;
1825 
1826   if(!AGS_IS_LINE_MEMBER(line_member) ||
1827      (AGS_LINE_MEMBER_BLOCK_CHAINED & (line_member->flags)) != 0){
1828     return;
1829   }
1830 
1831   machine = (AgsMachine *) gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1832 						   AGS_TYPE_MACHINE);
1833 
1834   if((AGS_MACHINE_STICKY_CONTROLS & (machine->flags)) != 0){
1835     AgsPad *pad;
1836     AgsEffectPad *effect_pad;
1837     GtkWidget *child_widget;
1838 
1839     GtkAdjustment *adjustment;
1840 
1841     gboolean is_active;
1842 
1843     pad = (AgsPad *) gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1844 					     AGS_TYPE_PAD);
1845 
1846     effect_pad = (AgsEffectPad *) gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1847 							  AGS_TYPE_EFFECT_PAD);
1848 
1849     is_active = FALSE;
1850 
1851     child_widget = gtk_bin_get_child((GtkBin *) line_member);
1852 
1853     if(AGS_IS_DIAL(child_widget) ||
1854        GTK_IS_SPIN_BUTTON(child_widget) ||
1855        GTK_IS_SCALE(child_widget)){
1856       g_object_get(gtk_bin_get_child((GtkBin *) line_member),
1857 		   "adjustment", &adjustment,
1858 		   NULL);
1859     }else if(GTK_IS_TOGGLE_BUTTON(child_widget)){
1860       is_active = gtk_toggle_button_get_active((GtkToggleButton *) gtk_bin_get_child(GTK_BIN(line_member)));
1861     }
1862 
1863     if(pad != NULL){
1864       AgsLine *line;
1865 
1866       GList *list_line, *list_line_start;
1867       GList *list_line_member, *list_line_member_start;
1868 
1869       line = (AgsLine *) gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1870 						 AGS_TYPE_LINE);
1871 
1872       list_line =
1873 	list_line_start = gtk_container_get_children(GTK_CONTAINER(pad->expander_set));
1874 
1875       while((list_line = ags_line_find_next_grouped(list_line)) != NULL){
1876 	if(list_line->data != line){
1877 	  list_line_member =
1878 	    list_line_member_start = gtk_container_get_children((GtkContainer *) AGS_LINE(list_line->data)->expander->table);
1879 
1880 	  while(list_line_member != NULL){
1881 	    if(AGS_IS_LINE_MEMBER(list_line_member->data) &&
1882 	       !g_strcmp0(line_member->specifier,
1883 			  AGS_LINE_MEMBER(list_line_member->data)->specifier)){
1884 	      AGS_LINE_MEMBER(list_line_member->data)->flags |= AGS_LINE_MEMBER_BLOCK_CHAINED;
1885 
1886 	      child_widget = gtk_bin_get_child(GTK_BIN(list_line_member->data));
1887 
1888 	      if(AGS_IS_DIAL(child_widget)){
1889 		ags_dial_set_value((AgsDial *) child_widget,
1890 				   gtk_adjustment_get_value(adjustment));
1891 	      }else if(GTK_IS_SPIN_BUTTON(child_widget)){
1892 		gtk_spin_button_set_value((GtkSpinButton *) child_widget,
1893 					  gtk_adjustment_get_value(adjustment));
1894 	      }else if(GTK_IS_SCALE(child_widget)){
1895 		gtk_range_set_value((GtkRange *) child_widget,
1896 				    gtk_adjustment_get_value(adjustment));
1897 	      }else if(GTK_IS_TOGGLE_BUTTON(child_widget)){
1898 		gtk_toggle_button_set_active((GtkToggleButton *) child_widget,
1899 					     is_active);
1900 	      }else if(GTK_IS_BUTTON(child_widget)){
1901 		gtk_button_clicked((GtkButton *) child_widget);
1902 	      }
1903 
1904 	      AGS_LINE_MEMBER(list_line_member->data)->flags &= (~AGS_LINE_MEMBER_BLOCK_CHAINED);
1905 
1906 	      break;
1907 	    }
1908 
1909 	    list_line_member = list_line_member->next;
1910 	  }
1911 
1912 	  g_list_free(list_line_member_start);
1913 	}
1914 
1915 	list_line = list_line->next;
1916       }
1917 
1918       g_list_free(list_line_start);
1919     }else if(effect_pad != NULL){
1920       AgsEffectLine *effect_line;
1921 
1922       GList *list_effect_line, *list_effect_line_start;
1923       GList *list_line_member, *list_line_member_start;
1924 
1925       effect_line = (AgsEffectLine *) gtk_widget_get_ancestor(GTK_WIDGET(line_member),
1926 							      AGS_TYPE_EFFECT_LINE);
1927 
1928       list_effect_line =
1929 	list_effect_line_start = gtk_container_get_children(GTK_CONTAINER(effect_pad->grid));
1930 
1931       while((list_effect_line = ags_effect_line_find_next_grouped(list_effect_line)) != NULL){
1932 	if(list_effect_line->data != effect_line){
1933 	  list_line_member =
1934 	    list_line_member_start = gtk_container_get_children((GtkContainer *) AGS_EFFECT_LINE(list_effect_line->data)->grid);
1935 
1936 	  while(list_line_member != NULL){
1937 	    if(!g_strcmp0(line_member->specifier,
1938 			 AGS_LINE_MEMBER(list_line_member->data)->specifier)){
1939 	      AGS_LINE_MEMBER(list_line_member->data)->flags |= AGS_LINE_MEMBER_BLOCK_CHAINED;
1940 
1941 	      child_widget = gtk_bin_get_child(GTK_BIN(list_line_member->data));
1942 
1943 	      if(AGS_IS_DIAL(child_widget)){
1944 		ags_dial_set_value((AgsDial *) child_widget,
1945 				   gtk_adjustment_get_value(adjustment));
1946 	      }else if(GTK_IS_SPIN_BUTTON(child_widget)){
1947 		gtk_spin_button_set_value((GtkSpinButton *) child_widget,
1948 					  gtk_adjustment_get_value(adjustment));
1949 	      }else if(GTK_IS_SCALE(child_widget)){
1950 		gtk_range_set_value((GtkRange *) child_widget,
1951 				    gtk_adjustment_get_value(adjustment));
1952 	      }else if(GTK_IS_TOGGLE_BUTTON(child_widget)){
1953 		gtk_toggle_button_set_active((GtkToggleButton *) child_widget,
1954 					     is_active);
1955 	      }else if(GTK_IS_BUTTON(child_widget)){
1956 		gtk_button_clicked((GtkButton *) child_widget);
1957 	      }
1958 
1959 	      AGS_LINE_MEMBER(list_line_member->data)->flags &= (~AGS_LINE_MEMBER_BLOCK_CHAINED);
1960 
1961 	      break;
1962 	    }
1963 
1964 	    list_line_member = list_line_member->next;
1965 	  }
1966 
1967 	  g_list_free(list_line_member_start);
1968 	}
1969 
1970 	list_effect_line = list_effect_line->next;
1971       }
1972 
1973       g_list_free(list_effect_line_start);
1974     }
1975   }
1976 }
1977 
1978 /**
1979  * ags_line_member_new:
1980  *
1981  * Create a new instance of #AgsLineMember
1982  *
1983  * Returns: the new #AgsLineMember
1984  *
1985  * Since: 3.0.0
1986  */
1987 AgsLineMember*
ags_line_member_new()1988 ags_line_member_new()
1989 {
1990   AgsLineMember *line_member;
1991 
1992   line_member = (AgsLineMember *) g_object_new(AGS_TYPE_LINE_MEMBER,
1993 					       NULL);
1994 
1995   return(line_member);
1996 }
1997