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/machine/ags_pattern_box.h>
21 #include <ags/X/machine/ags_pattern_box_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 
29 #include <gdk/gdkkeysyms.h>
30 
31 #include <atk/atk.h>
32 
33 #include <ags/i18n.h>
34 
35 static GType ags_accessible_pattern_box_get_type(void);
36 void ags_pattern_box_class_init(AgsPatternBoxClass *pattern_box);
37 void ags_accessible_pattern_box_class_init(AtkObject *object);
38 void ags_accessible_pattern_box_action_interface_init(AtkActionIface *action);
39 void ags_pattern_box_connectable_interface_init(AgsConnectableInterface *connectable);
40 void ags_pattern_box_init(AgsPatternBox *pattern_box);
41 void ags_pattern_box_finalize(GObject *gobject);
42 
43 void ags_pattern_box_connect(AgsConnectable *connectable);
44 void ags_pattern_box_disconnect(AgsConnectable *connectable);
45 
46 AtkObject* ags_pattern_box_get_accessible(GtkWidget *widget);
47 void ags_pattern_box_realize(GtkWidget *widget);
48 void ags_pattern_box_show(GtkWidget *widget);
49 void ags_pattern_box_show_all(GtkWidget *widget);
50 
51 gboolean ags_accessible_pattern_box_do_action(AtkAction *action,
52 					      gint i);
53 gint ags_accessible_pattern_box_get_n_actions(AtkAction *action);
54 const gchar* ags_accessible_pattern_box_get_description(AtkAction *action,
55 							gint i);
56 const gchar* ags_accessible_pattern_box_get_name(AtkAction *action,
57 						 gint i);
58 const gchar* ags_accessible_pattern_box_get_keybinding(AtkAction *action,
59 						       gint i);
60 gboolean ags_accessible_pattern_box_set_description(AtkAction *action,
61 						    gint i);
62 gchar* ags_accessible_pattern_box_get_localized_name(AtkAction *action,
63 						     gint i);
64 
65 /**
66  * SECTION:ags_pattern_box
67  * @short_description: pattern box sequencer
68  * @title: AgsPatternBox
69  * @section_id:
70  * @include: ags/X/machine/ags_pattern_box.h
71  *
72  * The #AgsPatternBox is a composite widget to act as pattern box sequencer.
73  */
74 
75 static gpointer ags_pattern_box_parent_class = NULL;
76 static GQuark quark_accessible_object = 0;
77 
78 GtkStyle *pattern_box_style = NULL;
79 GHashTable *ags_pattern_box_led_queue_draw = NULL;
80 
81 GType
ags_pattern_box_get_type(void)82 ags_pattern_box_get_type(void)
83 {
84   static volatile gsize g_define_type_id__volatile = 0;
85 
86   if(g_once_init_enter (&g_define_type_id__volatile)){
87     GType ags_type_pattern_box = 0;
88 
89     static const GTypeInfo ags_pattern_box_info = {
90       sizeof(AgsPatternBoxClass),
91       NULL, /* base_init */
92       NULL, /* base_finalize */
93       (GClassInitFunc) ags_pattern_box_class_init,
94       NULL, /* class_finalize */
95       NULL, /* class_data */
96       sizeof(AgsPatternBox),
97       0,    /* n_preallocs */
98       (GInstanceInitFunc) ags_pattern_box_init,
99     };
100 
101     static const GInterfaceInfo ags_connectable_interface_info = {
102       (GInterfaceInitFunc) ags_pattern_box_connectable_interface_init,
103       NULL, /* interface_finalize */
104       NULL, /* interface_data */
105     };
106 
107     ags_type_pattern_box = g_type_register_static(GTK_TYPE_GRID,
108 						  "AgsPatternBox", &ags_pattern_box_info,
109 						  0);
110 
111     g_type_add_interface_static(ags_type_pattern_box,
112 				AGS_TYPE_CONNECTABLE,
113 				&ags_connectable_interface_info);
114 
115     g_once_init_leave(&g_define_type_id__volatile, ags_type_pattern_box);
116   }
117 
118   return g_define_type_id__volatile;
119 }
120 
121 static GType
ags_accessible_pattern_box_get_type(void)122 ags_accessible_pattern_box_get_type(void)
123 {
124   static GType ags_type_accessible_pattern_box = 0;
125 
126   if(!ags_type_accessible_pattern_box){
127     const GTypeInfo ags_accesssible_pattern_box_info = {
128       sizeof(GtkAccessibleClass),
129       NULL,           /* base_init */
130       NULL,           /* base_finalize */
131       (GClassInitFunc) ags_accessible_pattern_box_class_init,
132       NULL,           /* class_finalize */
133       NULL,           /* class_data */
134       sizeof(GtkAccessible),
135       0,             /* n_preallocs */
136       NULL, NULL
137     };
138 
139     static const GInterfaceInfo atk_action_interface_info = {
140       (GInterfaceInitFunc) ags_accessible_pattern_box_action_interface_init,
141       NULL, /* interface_finalize */
142       NULL, /* interface_data */
143     };
144 
145     ags_type_accessible_pattern_box = g_type_register_static(GTK_TYPE_ACCESSIBLE,
146 							     "AgsAccessiblePatternBox", &ags_accesssible_pattern_box_info,
147 							     0);
148 
149     g_type_add_interface_static(ags_type_accessible_pattern_box,
150 				ATK_TYPE_ACTION,
151 				&atk_action_interface_info);
152   }
153 
154   return(ags_type_accessible_pattern_box);
155 }
156 
157 void
ags_pattern_box_class_init(AgsPatternBoxClass * pattern_box)158 ags_pattern_box_class_init(AgsPatternBoxClass *pattern_box)
159 {
160   GObjectClass *gobject;
161   GtkWidgetClass *widget;
162 
163   ags_pattern_box_parent_class = g_type_class_peek_parent(pattern_box);
164 
165   quark_accessible_object = g_quark_from_static_string("ags-accessible-object");
166 
167   /* GObjectClass */
168   gobject = (GObjectClass *) pattern_box;
169 
170   gobject->finalize = ags_pattern_box_finalize;
171 
172   /* GtkWidget */
173   widget = (GtkWidgetClass *) pattern_box;
174 
175   widget->realize = ags_pattern_box_realize;
176   widget->show = ags_pattern_box_show;
177   widget->show_all = ags_pattern_box_show_all;
178 }
179 
180 void
ags_accessible_pattern_box_class_init(AtkObject * object)181 ags_accessible_pattern_box_class_init(AtkObject *object)
182 {
183   /* empty */
184 }
185 
186 void
ags_accessible_pattern_box_action_interface_init(AtkActionIface * action)187 ags_accessible_pattern_box_action_interface_init(AtkActionIface *action)
188 {
189   action->do_action = ags_accessible_pattern_box_do_action;
190   action->get_n_actions = ags_accessible_pattern_box_get_n_actions;
191   action->get_description = ags_accessible_pattern_box_get_description;
192   action->get_name = ags_accessible_pattern_box_get_name;
193   action->get_keybinding = ags_accessible_pattern_box_get_keybinding;
194   action->set_description = ags_accessible_pattern_box_set_description;
195   action->get_localized_name = ags_accessible_pattern_box_get_localized_name;
196 }
197 
198 void
ags_pattern_box_connectable_interface_init(AgsConnectableInterface * connectable)199 ags_pattern_box_connectable_interface_init(AgsConnectableInterface *connectable)
200 {
201   connectable->connect = ags_pattern_box_connect;
202   connectable->disconnect = ags_pattern_box_disconnect;
203 }
204 
205 void
ags_pattern_box_init(AgsPatternBox * pattern_box)206 ags_pattern_box_init(AgsPatternBox *pattern_box)
207 {
208   GtkToggleButton *toggle_button;
209   GtkRadioButton *radio_button;
210 
211   AgsApplicationContext *application_context;
212 
213   gchar *str;
214 
215   gdouble gui_scale_factor;
216   guint i;
217 
218   application_context = ags_application_context_get_instance();
219 
220   g_object_set(pattern_box,
221 	       "can-focus", TRUE,
222 	       NULL);
223 
224   gtk_widget_set_events((GtkWidget *) pattern_box,
225 			(GDK_CONTROL_MASK
226 			 | GDK_KEY_PRESS_MASK
227 			 | GDK_KEY_RELEASE_MASK));
228 
229   pattern_box->flags = 0;
230 
231   pattern_box->key_mask = 0;
232 
233   pattern_box->n_controls = AGS_PATTERN_BOX_N_CONTROLS;
234   pattern_box->n_indices = AGS_PATTERN_BOX_N_INDICES;
235 
236   /* led */
237   gui_scale_factor = ags_ui_provider_get_gui_scale_factor(AGS_UI_PROVIDER(application_context));
238 
239   pattern_box->active_led = 0;
240   pattern_box->hled_array = (AgsHLedArray *) ags_hled_array_new();
241   g_object_set(pattern_box->hled_array,
242 	       "led-count", pattern_box->n_controls,
243 	       "led-width", (guint) (gui_scale_factor * AGS_PATTERN_BOX_LED_DEFAULT_WIDTH),
244 	       "led-height", (guint) (gui_scale_factor * AGS_PATTERN_BOX_LED_DEFAULT_HEIGHT),
245 	       NULL);
246   gtk_widget_set_size_request((GtkWidget *) pattern_box->hled_array,
247 			      (guint) (gui_scale_factor * pattern_box->n_controls * AGS_PATTERN_BOX_DEFAULT_PAD_WIDTH), (guint) (gui_scale_factor * AGS_PATTERN_BOX_LED_DEFAULT_HEIGHT));
248 
249   gtk_widget_set_valign((GtkWidget *) pattern_box->hled_array,
250 			GTK_ALIGN_CENTER);
251   gtk_widget_set_vexpand((GtkWidget *) pattern_box->hled_array,
252 			 TRUE);
253 
254   gtk_grid_attach((GtkGrid *) pattern_box,
255 		  (GtkWidget *) pattern_box->hled_array,
256 		  0, 0,
257 		  1, 1);
258   gtk_widget_show_all((GtkWidget *) pattern_box->hled_array);
259 
260   if(ags_pattern_box_led_queue_draw == NULL){
261     ags_pattern_box_led_queue_draw = g_hash_table_new_full(g_direct_hash, g_direct_equal,
262 							   NULL,
263 							   NULL);
264   }
265 
266   g_hash_table_insert(ags_pattern_box_led_queue_draw,
267 		      pattern_box, ags_pattern_box_led_queue_draw_timeout);
268   g_timeout_add((guint) floor(AGS_UI_PROVIDER_DEFAULT_TIMEOUT * 1000.0), (GSourceFunc) ags_pattern_box_led_queue_draw_timeout, (gpointer) pattern_box);
269 
270   /* pattern */
271   pattern_box->pattern = (GtkBox *) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,
272 						0);
273 
274   gtk_widget_set_valign((GtkWidget *) pattern_box->pattern,
275 			GTK_ALIGN_CENTER);
276   gtk_widget_set_vexpand((GtkWidget *) pattern_box->pattern,
277 			 TRUE);
278 
279   gtk_grid_attach((GtkGrid *) pattern_box,
280 		   (GtkWidget *) pattern_box->pattern,
281 		   0, 1,
282 		   1, 1);
283 
284   for(i = 0; i < pattern_box->n_controls; i++){
285     toggle_button = (GtkToggleButton *) gtk_toggle_button_new();
286     gtk_widget_set_size_request((GtkWidget *) toggle_button,
287 				gui_scale_factor * AGS_PATTERN_BOX_DEFAULT_PAD_WIDTH, gui_scale_factor * AGS_PATTERN_BOX_DEFAULT_PAD_HEIGHT);
288     gtk_box_pack_start(pattern_box->pattern,
289 		       (GtkWidget *) toggle_button,
290 		       FALSE, FALSE,
291 		       0);
292   }
293 
294   /* page / offset */
295   pattern_box->offset = (GtkBox *) gtk_box_new(GTK_ORIENTATION_VERTICAL,
296 					       0);
297   gtk_grid_attach((GtkGrid *) pattern_box,
298 		  (GtkWidget *) pattern_box->offset,
299 		  1, 0,
300 		  1, 2);
301   radio_button = NULL;
302 
303   for(i = 0; i < pattern_box->n_indices; i++){
304     if(radio_button == NULL){
305       str = g_strdup_printf("%d-%d",
306 			    i * pattern_box->n_controls + 1, (i + 1) * pattern_box->n_controls);
307       radio_button = (GtkRadioButton *) gtk_radio_button_new_with_label(NULL,
308 									str);
309       gtk_box_pack_start(pattern_box->offset,
310 			 (GtkWidget *) radio_button,
311 			 FALSE, FALSE,
312 			 0);
313 
314       g_free(str);
315     }else{
316       str = g_strdup_printf("%d-%d",
317 			    i * pattern_box->n_controls + 1, (i + 1) * pattern_box->n_controls);
318       gtk_box_pack_start(pattern_box->offset,
319 			 (GtkWidget *) gtk_radio_button_new_with_label(gtk_radio_button_get_group(radio_button),
320 								       str),
321 			 FALSE, FALSE,
322 			 0);
323 
324       g_free(str);
325     }
326   }
327 }
328 
329 void
ags_pattern_box_finalize(GObject * gobject)330 ags_pattern_box_finalize(GObject *gobject)
331 {
332   g_hash_table_remove(ags_pattern_box_led_queue_draw,
333 		      gobject);
334 
335   G_OBJECT_CLASS(ags_pattern_box_parent_class)->finalize(gobject);
336 }
337 
338 void
ags_pattern_box_connect(AgsConnectable * connectable)339 ags_pattern_box_connect(AgsConnectable *connectable)
340 {
341   AgsPatternBox *pattern_box;
342 
343   GList *list, *list_start;
344 
345   if((AGS_PATTERN_BOX_CONNECTED & (AGS_PATTERN_BOX(connectable)->flags)) != 0){
346     return;
347   }
348 
349   /* AgsPatternBox */
350   pattern_box = AGS_PATTERN_BOX(connectable);
351 
352   pattern_box->flags |= AGS_PATTERN_BOX_CONNECTED;
353 
354   g_signal_connect_after(G_OBJECT(pattern_box), "focus_in_event",
355 			 G_CALLBACK(ags_pattern_box_focus_in_callback), (gpointer) pattern_box);
356 
357   g_signal_connect_after(G_OBJECT(pattern_box), "focus_out_event",
358 			 G_CALLBACK(ags_pattern_box_focus_out_callback), (gpointer) pattern_box);
359 
360   g_signal_connect(G_OBJECT(pattern_box), "key_press_event",
361 		   G_CALLBACK(ags_pattern_box_key_press_event), (gpointer) pattern_box);
362 
363   g_signal_connect(G_OBJECT(pattern_box), "key_release_event",
364 		   G_CALLBACK(ags_pattern_box_key_release_event), (gpointer) pattern_box);
365 
366   /* connect pattern */
367   list_start =
368     list = gtk_container_get_children((GtkContainer *) pattern_box->pattern);
369 
370   while(list != NULL){
371     g_signal_connect(G_OBJECT(list->data), "clicked",
372 		     G_CALLBACK(ags_pattern_box_pad_callback), (gpointer) pattern_box);
373 
374     list = list->next;
375   }
376 
377   g_list_free(list_start);
378 
379   /* connect pattern offset range */
380   list_start =
381     list = gtk_container_get_children((GtkContainer *) pattern_box->offset);
382 
383   while(list != NULL){
384     g_signal_connect_after(G_OBJECT(list->data), "clicked",
385 			   G_CALLBACK(ags_pattern_box_offset_callback), (gpointer) pattern_box);
386 
387     list = list->next;
388   }
389 
390   g_list_free(list_start);
391 }
392 
393 void
ags_pattern_box_disconnect(AgsConnectable * connectable)394 ags_pattern_box_disconnect(AgsConnectable *connectable)
395 {
396   AgsPatternBox *pattern_box;
397 
398   GList *list, *list_start;
399 
400   if((AGS_PATTERN_BOX_CONNECTED & (AGS_PATTERN_BOX(connectable)->flags)) == 0){
401     return;
402   }
403 
404   /* AgsPatternBox */
405   pattern_box = AGS_PATTERN_BOX(connectable);
406 
407   pattern_box->flags &= (~AGS_PATTERN_BOX_CONNECTED);
408 
409   g_object_disconnect(G_OBJECT(pattern_box),
410 		      "any_signal::focus_in_event",
411 		      G_CALLBACK(ags_pattern_box_focus_in_callback),
412 		      (gpointer) pattern_box,
413 		      "any_signal::focus_out_event",
414 		      G_CALLBACK(ags_pattern_box_focus_out_callback),
415 		      (gpointer) pattern_box,
416 		      "any_signal::key_press_event",
417 		      G_CALLBACK(ags_pattern_box_key_press_event),
418 		      (gpointer) pattern_box,
419 		      "any_signal::key_release_event",
420 		      G_CALLBACK(ags_pattern_box_key_release_event),
421 		      (gpointer) pattern_box,
422 		      NULL);
423 
424   /* connect pattern */
425   list_start =
426     list = gtk_container_get_children((GtkContainer *) pattern_box->pattern);
427 
428   while(list != NULL){
429     g_object_disconnect(G_OBJECT(list->data),
430 			"any_signal::clicked",
431 			G_CALLBACK(ags_pattern_box_pad_callback),
432 			(gpointer) pattern_box,
433 			NULL);
434 
435     list = list->next;
436   }
437 
438   g_list_free(list_start);
439 
440   /* connect pattern offset range */
441   list_start =
442     list = gtk_container_get_children((GtkContainer *) pattern_box->offset);
443 
444   while(list != NULL){
445     g_object_disconnect(G_OBJECT(list->data),
446 			"any_signal::clicked",
447 			G_CALLBACK(ags_pattern_box_offset_callback),
448 			(gpointer) pattern_box,
449 			NULL);
450 
451     list = list->next;
452   }
453 
454   g_list_free(list_start);
455 }
456 
457 AtkObject*
ags_pattern_box_get_accessible(GtkWidget * widget)458 ags_pattern_box_get_accessible(GtkWidget *widget)
459 {
460   AtkObject* accessible;
461 
462   accessible = g_object_get_qdata(G_OBJECT(widget),
463 				  quark_accessible_object);
464 
465   if(!accessible){
466     accessible = g_object_new(ags_accessible_pattern_box_get_type(),
467 			      NULL);
468 
469     g_object_set_qdata(G_OBJECT(widget),
470 		       quark_accessible_object,
471 		       accessible);
472     gtk_accessible_set_widget(GTK_ACCESSIBLE(accessible),
473 			      widget);
474   }
475 
476   return(accessible);
477 }
478 
479 void
ags_pattern_box_realize(GtkWidget * widget)480 ags_pattern_box_realize(GtkWidget *widget)
481 {
482   /* call parent */
483   GTK_WIDGET_CLASS(ags_pattern_box_parent_class)->realize(widget);
484 }
485 
486 void
ags_pattern_box_show(GtkWidget * widget)487 ags_pattern_box_show(GtkWidget *widget)
488 {
489   GTK_WIDGET_CLASS(ags_pattern_box_parent_class)->show(widget);
490 
491   //  ags_pattern_box_draw_matrix(AGS_PATTERN_BOX(widget));
492 }
493 
494 void
ags_pattern_box_show_all(GtkWidget * widget)495 ags_pattern_box_show_all(GtkWidget *widget)
496 {
497   GTK_WIDGET_CLASS(ags_pattern_box_parent_class)->show_all(widget);
498 
499   //  ags_pattern_box_draw_matrix(AGS_PATTERN_BOX(widget));
500 }
501 
502 gboolean
ags_accessible_pattern_box_do_action(AtkAction * action,gint i)503 ags_accessible_pattern_box_do_action(AtkAction *action,
504 				     gint i)
505 {
506   AgsPatternBox *pattern_box;
507 
508   GdkEventKey *key_press, *key_release;
509   GdkEventKey *modifier_press, *modifier_release;
510 
511   if(!(i >= 0 && i < 6)){
512     return(FALSE);
513   }
514 
515   pattern_box = (AgsPatternBox *) gtk_accessible_get_widget(GTK_ACCESSIBLE(action));
516 
517   key_press = (GdkEventKey *) gdk_event_new(GDK_KEY_PRESS);
518   key_release = (GdkEventKey *) gdk_event_new(GDK_KEY_RELEASE);
519 
520   switch(i){
521   case AGS_PATTERN_BOX_MOVE_LEFT:
522     {
523       key_press->keyval =
524 	key_release->keyval = GDK_KEY_Left;
525 
526       /* send event */
527       gtk_widget_event((GtkWidget *) pattern_box,
528 		       (GdkEvent *) key_press);
529       gtk_widget_event((GtkWidget *) pattern_box,
530 		       (GdkEvent *) key_release);
531     }
532     break;
533   case AGS_PATTERN_BOX_MOVE_RIGHT:
534     {
535       key_press->keyval =
536 	key_release->keyval = GDK_KEY_Right;
537 
538       /* send event */
539       gtk_widget_event((GtkWidget *) pattern_box,
540 		       (GdkEvent *) key_press);
541       gtk_widget_event((GtkWidget *) pattern_box,
542 		       (GdkEvent *) key_release);
543     }
544     break;
545   case AGS_PATTERN_BOX_INDEX_DECREMENT:
546     {
547       key_press->keyval =
548 	key_release->keyval = GDK_KEY_Up;
549 
550       /* send event */
551       gtk_widget_event((GtkWidget *) pattern_box,
552 		       (GdkEvent *) key_press);
553       gtk_widget_event((GtkWidget *) pattern_box,
554 		       (GdkEvent *) key_release);
555     }
556     break;
557   case AGS_PATTERN_BOX_INDEX_INCREMENT:
558     {
559       key_press->keyval =
560 	key_release->keyval = GDK_KEY_Down;
561 
562       /* send event */
563       gtk_widget_event((GtkWidget *) pattern_box,
564 		       (GdkEvent *) key_press);
565       gtk_widget_event((GtkWidget *) pattern_box,
566 		       (GdkEvent *) key_release);
567     }
568     break;
569   case AGS_PATTERN_BOX_TOGGLE_PAD:
570     {
571       key_press->keyval =
572 	key_release->keyval = GDK_KEY_space;
573 
574       /* send event */
575       gtk_widget_event((GtkWidget *) pattern_box,
576 		       (GdkEvent *) key_press);
577       gtk_widget_event((GtkWidget *) pattern_box,
578 		       (GdkEvent *) key_release);
579     }
580     break;
581   case AGS_PATTERN_BOX_COPY_PATTERN:
582     {
583       key_press->keyval =
584 	key_release->keyval = GDK_KEY_c;
585 
586       /* create modifier */
587       modifier_press = (GdkEventKey *) gdk_event_new(GDK_KEY_PRESS);
588       modifier_release = (GdkEventKey *) gdk_event_new(GDK_KEY_RELEASE);
589 
590       modifier_press->keyval =
591 	modifier_release->keyval = GDK_KEY_Control_R;
592 
593       /* send event */
594       gtk_widget_event((GtkWidget *) pattern_box,
595 		       (GdkEvent *) modifier_press);
596       gtk_widget_event((GtkWidget *) pattern_box,
597 		       (GdkEvent *) key_press);
598       gtk_widget_event((GtkWidget *) pattern_box,
599 		       (GdkEvent *) key_release);
600       gtk_widget_event((GtkWidget *) pattern_box,
601 		       (GdkEvent *) modifier_release);
602     }
603     break;
604   }
605 
606   return(TRUE);
607 }
608 
609 gint
ags_accessible_pattern_box_get_n_actions(AtkAction * action)610 ags_accessible_pattern_box_get_n_actions(AtkAction *action)
611 {
612   return(6);
613 }
614 
615 const gchar*
ags_accessible_pattern_box_get_description(AtkAction * action,gint i)616 ags_accessible_pattern_box_get_description(AtkAction *action,
617 					   gint i)
618 {
619   static const gchar *actions[] = {
620     "move cursor left",
621     "move cursor right",
622     "decrement pattern index",
623     "increment pattern index",
624     "toggle audio pattern"
625     "copy pattern to clipboard",
626   };
627 
628   if(i >= 0 && i < 6){
629     return(actions[i]);
630   }else{
631     return(NULL);
632   }
633 }
634 
635 const gchar*
ags_accessible_pattern_box_get_name(AtkAction * action,gint i)636 ags_accessible_pattern_box_get_name(AtkAction *action,
637 				    gint i)
638 {
639   static const gchar *actions[] = {
640     "left",
641     "right",
642     "up",
643     "down",
644     "toggle",
645     "copy",
646   };
647 
648   if(i >= 0 && i < 6){
649     return(actions[i]);
650   }else{
651     return(NULL);
652   }
653 }
654 
655 const gchar*
ags_accessible_pattern_box_get_keybinding(AtkAction * action,gint i)656 ags_accessible_pattern_box_get_keybinding(AtkAction *action,
657 					  gint i)
658 {
659   static const gchar *actions[] = {
660     "left",
661     "right",
662     "up",
663     "down",
664     "space",
665     "Ctrl+c",
666   };
667 
668   if(i >= 0 && i < 6){
669     return(actions[i]);
670   }else{
671     return(NULL);
672   }
673 }
674 
675 gboolean
ags_accessible_pattern_box_set_description(AtkAction * action,gint i)676 ags_accessible_pattern_box_set_description(AtkAction *action,
677 					   gint i)
678 {
679   //TODO:JK: implement me
680 
681   return(FALSE);
682 }
683 
684 gchar*
ags_accessible_pattern_box_get_localized_name(AtkAction * action,gint i)685 ags_accessible_pattern_box_get_localized_name(AtkAction *action,
686 					      gint i)
687 {
688   //TODO:JK: implement me
689 
690   return(NULL);
691 }
692 
693 /**
694  * ags_pattern_box_set_pattern:
695  * @pattern_box: the #AgsPatternBox
696  *
697  * Resets the pattern on @pattern_box.
698  *
699  * since: 2.0.0
700  */
701 void
ags_pattern_box_set_pattern(AgsPatternBox * pattern_box)702 ags_pattern_box_set_pattern(AgsPatternBox *pattern_box)
703 {
704   AgsMachine *machine;
705   AgsLine *selected_line;
706 
707   GList *list, *list_start;
708   GList *line, *line_start;
709 
710   guint index0, index1, offset;
711   gboolean is_active;
712   gboolean set_active;
713   guint i;
714 
715   machine = (AgsMachine *) gtk_widget_get_ancestor((GtkWidget *) pattern_box,
716 						   AGS_TYPE_MACHINE);
717 
718   if(machine->selected_input_pad == NULL){
719     return;
720   }
721 
722   index0 = machine->bank_0;
723   index1 = machine->bank_1;
724 
725   /* read boundaries */
726   list = gtk_container_get_children((GtkContainer *) pattern_box->offset);
727 
728   for(i = 0; i < pattern_box->n_indices && !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(list->data)); i++){
729     list = list->next;
730   }
731 
732   offset = i * pattern_box->n_controls;
733 
734   /* get pads */
735   list_start =
736     list = gtk_container_get_children((GtkContainer *) pattern_box->pattern);
737 
738   /* reset */
739   pattern_box->flags |= AGS_PATTERN_BOX_BLOCK_PATTERN;
740 
741   for(i = 0; i < pattern_box->n_controls; i++){
742     set_active = TRUE;
743 
744     line_start =
745       line = gtk_container_get_children(GTK_CONTAINER(AGS_PAD(machine->selected_input_pad)->expander_set));
746 
747     while((line = ags_line_find_next_grouped(line)) != NULL){
748       GList *start_pattern, *pattern;
749 
750       selected_line = AGS_LINE(line->data);
751 
752       g_object_get(selected_line->channel,
753 		   "pattern", &start_pattern,
754 		   NULL);
755 
756       /* check active */
757       pattern = start_pattern;
758 
759       is_active = ags_pattern_get_bit((AgsPattern *) pattern->data, index0, index1, offset + i);
760 
761       g_list_free_full(start_pattern,
762 		       g_object_unref);
763 
764       if(!is_active){
765 	set_active = FALSE;
766 
767 	break;
768       }
769 
770       /* iterate */
771       line = line->next;
772     }
773 
774     g_list_free(line_start);
775 
776     gtk_toggle_button_set_active((GtkToggleButton *) list->data, set_active);
777 
778     list = list->next;
779   }
780 
781   pattern_box->flags &= (~AGS_PATTERN_BOX_BLOCK_PATTERN);
782 
783   g_list_free(list_start);
784 }
785 
786 /**
787  * ags_pattern_box_led_queue_draw_timeout:
788  * @pattern_box: the #AgsPatternBox
789  *
790  * Queue draw led.
791  *
792  * Returns: %TRUE if continue timeout, otherwise %FALSE
793  *
794  * Since: 3.0.0
795  */
796 gboolean
ags_pattern_box_led_queue_draw_timeout(AgsPatternBox * pattern_box)797 ags_pattern_box_led_queue_draw_timeout(AgsPatternBox *pattern_box)
798 {
799   if(g_hash_table_lookup(ags_pattern_box_led_queue_draw,
800 			 pattern_box) != NULL){
801     AgsMachine *machine;
802 
803     AgsAudio *audio;
804     AgsRecallID *recall_id;
805 
806     AgsFxPatternAudio *play_fx_pattern_audio;
807     AgsFxPatternAudioProcessor *play_fx_pattern_audio_processor;
808 
809     GList *start_list, *list;
810     GList *start_recall, *recall;
811 
812     guint64 active_led_new;
813     gboolean success;
814 
815     GRecMutex *play_fx_pattern_audio_processor_mutex;
816 
817     machine = (AgsMachine *) gtk_widget_get_ancestor((GtkWidget *) pattern_box,
818 						     AGS_TYPE_MACHINE);
819 
820     if(machine == NULL){
821       return(TRUE);
822     }
823 
824     audio = machine->audio;
825 
826     /* get some recalls */
827     recall_id = NULL;
828     g_object_get(audio,
829 		 "recall-id", &start_list,
830 		 NULL);
831 
832     list = start_list;
833 
834     success = FALSE;
835 
836     while(list != NULL && !success){
837       AgsRecyclingContext *parent_recycling_context;
838       AgsRecyclingContext *current_recycling_context;
839 
840       parent_recycling_context = NULL;
841       current_recycling_context = NULL;
842 
843       g_object_get(list->data,
844 		   "recycling-context", &current_recycling_context,
845 		   NULL);
846 
847       if(current_recycling_context != NULL){
848 	g_object_get(current_recycling_context,
849 		     "parent", &parent_recycling_context,
850 		     NULL);
851 
852 	if(parent_recycling_context == NULL &&
853 	   ags_recall_id_check_sound_scope(list->data, AGS_SOUND_SCOPE_SEQUENCER)){
854 	  recall_id = list->data;
855 
856 	  g_object_get(audio,
857 		       "play", &start_recall,
858 		       NULL);
859 
860 	  play_fx_pattern_audio = NULL;
861 	  play_fx_pattern_audio_processor = NULL;
862 
863 	  recall = ags_recall_find_type(start_recall,
864 					AGS_TYPE_FX_PATTERN_AUDIO);
865 
866 	  if(recall != NULL){
867 	    play_fx_pattern_audio = AGS_FX_PATTERN_AUDIO(recall->data);
868 	  }
869 
870 	  recall = ags_recall_find_type_with_recycling_context(start_recall,
871 							       AGS_TYPE_FX_PATTERN_AUDIO_PROCESSOR,
872 							       (GObject *) current_recycling_context);
873 
874 	  if(recall != NULL){
875 	    play_fx_pattern_audio_processor = AGS_FX_PATTERN_AUDIO_PROCESSOR(recall->data);
876 	  }
877 
878 
879 	  g_list_free_full(start_recall,
880 			   g_object_unref);
881 
882 	  if(play_fx_pattern_audio == NULL ||
883 	     play_fx_pattern_audio_processor == NULL){
884 	    recall_id = NULL;
885 	  }else{
886 	    success = TRUE;
887 	  }
888 	}
889       }
890 
891       if(parent_recycling_context != NULL){
892 	g_object_unref(parent_recycling_context);
893       }
894 
895       if(current_recycling_context != NULL){
896 	g_object_unref(current_recycling_context);
897       }
898 
899       list = list->next;
900     }
901 
902     g_list_free_full(start_list,
903 		     g_object_unref);
904 
905     if(recall_id == NULL){
906       return(TRUE);
907     }
908 
909     /* active led */
910     play_fx_pattern_audio_processor_mutex = AGS_RECALL_GET_OBJ_MUTEX(play_fx_pattern_audio_processor);
911 
912     g_rec_mutex_lock(play_fx_pattern_audio_processor_mutex);
913 
914     active_led_new = play_fx_pattern_audio_processor->offset_counter;
915 
916     g_rec_mutex_unlock(play_fx_pattern_audio_processor_mutex);
917 
918     pattern_box->active_led = (guint) (active_led_new % pattern_box->n_controls);
919 
920     ags_led_array_unset_all((AgsLedArray *) pattern_box->hled_array);
921     ags_led_array_set_nth((AgsLedArray *) pattern_box->hled_array,
922 			  pattern_box->active_led);
923 
924     return(TRUE);
925   }else{
926     return(FALSE);
927   }
928 }
929 
930 /**
931  * ags_pattern_box_new:
932  *
933  * Create a new instance of #AgsPatternBox
934  *
935  * Returns: the new #AgsPatternBox
936  *
937  * Since: 3.0.0
938  */
939 AgsPatternBox*
ags_pattern_box_new()940 ags_pattern_box_new()
941 {
942   AgsPatternBox *pattern_box;
943 
944   pattern_box = (AgsPatternBox *) g_object_new(AGS_TYPE_PATTERN_BOX,
945 					       NULL);
946 
947   return(pattern_box);
948 }
949