missing commit in generator.h
[galan.git] / src / comp.c
blob1337bed80dbbaf7a862758ec227b82c11f49c770
1 /* gAlan - Graphical Audio Language
2 * Copyright (C) 1999 Tony Garnock-Jones
3 * Copyright (C) 2001 Torben Hohn
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
24 #include <gdk/gdk.h>
25 #include <gtk/gtk.h>
27 #include "global.h"
28 #include "generator.h"
29 #include "comp.h"
30 #include "gencomp.h"
31 #include "sheet.h"
32 #include "gui.h"
33 #include "msgbox.h"
35 #include "galan-compaction.h"
36 #include "galan-comptree-model.h"
38 #define NEWMENU_PATH_PREFIX "/"
40 typedef struct MenuEntry {
41 char *menupath;
42 ComponentClass *k;
43 gpointer init_data;
44 } MenuEntry;
46 PRIVATE GList *menuentries = NULL;
47 PRIVATE gboolean menuentries_dirty = TRUE;
48 PRIVATE GHashTable *componentclasses = NULL;
49 PRIVATE GtkItemFactory *menufact = NULL;
51 PRIVATE GalanCompTreeModel *tmodel = NULL;
53 PRIVATE void newmenu_callback(MenuEntry *p, guint callback_action, GtkWidget *widget) {
55 GtkItemFactory *ifact = gtk_item_factory_from_widget( widget );
56 Sheet *sheet = gtk_object_get_user_data( GTK_OBJECT( ifact ) );
57 sheet_build_new_component(sheet, p->k, p->init_data);
60 PUBLIC void comp_add_newmenu_item(char *menupath, ComponentClass *k, gpointer init_data) {
62 MenuEntry *m = safe_malloc(sizeof(MenuEntry));
64 if (k->initialize_instance == NULL ||
65 k->paint == NULL ||
66 k->get_title == NULL) {
67 g_warning("ComponentClass must have initialize_instance, paint and "
68 "get_title methods (menupath = %s)", menupath);
69 free(m);
70 return;
73 m->menupath = g_strdup_printf( "%s%s", NEWMENU_PATH_PREFIX, menupath );
74 m->k = k;
75 m->init_data = init_data;
77 menuentries = g_list_append(menuentries, m);
78 menuentries_dirty = TRUE;
84 PRIVATE void ensure_path_exists( char *mpath, char *base ) {
86 if( !strcmp( mpath, base ) )
87 return;
89 GtkWidget *menuaction = gtk_ui_manager_get_widget( ui_manager, mpath );
90 if( menuaction == NULL ) {
91 char *updir = g_path_get_dirname( mpath );
92 char *aname = g_path_get_basename( mpath );
93 ensure_path_exists( updir, base );
94 GtkAction *tmpact = gtk_action_new( aname, aname, NULL, NULL );
95 gtk_action_group_add_action( component_actiongroup, tmpact );
97 gtk_ui_manager_add_ui( ui_manager,
98 gtk_ui_manager_new_merge_id( ui_manager ),
99 updir,
100 aname,
101 g_strdup(aname),
102 GTK_UI_MANAGER_MENU,
103 TRUE );
105 //free( aname );
106 //free( updir );
110 PRIVATE void experiment( void ) {
112 tmodel = g_object_new( GALAN_TYPE_COMPTREE_MODEL, NULL );
114 GtkTreeView *tview = GTK_TREE_VIEW( gtk_tree_view_new_with_model( GTK_TREE_MODEL(tmodel) ) );
115 GtkCellRenderer *render = GTK_CELL_RENDERER( gtk_cell_renderer_text_new () );
116 gtk_tree_view_insert_column_with_attributes( tview, -1, "test", render, "text", 0, NULL );
118 static GtkTargetEntry targette = { "galan/CompAction", 0, 234 };
119 gtk_tree_view_enable_model_drag_dest( tview, &targette, 1, GDK_ACTION_COPY );
120 gtk_tree_view_enable_model_drag_source ( tview, GDK_BUTTON1_MASK, &targette, 1, GDK_ACTION_COPY );
123 GtkWindow *win = GTK_WINDOW( gtk_window_new( GTK_WINDOW_TOPLEVEL ) );
124 GtkContainer *scrollo = GTK_CONTAINER( gtk_scrolled_window_new( NULL, NULL ) );
126 gtk_container_add (GTK_CONTAINER (win), GTK_WIDGET(scrollo ) );
127 gtk_container_add (GTK_CONTAINER (scrollo), GTK_WIDGET(tview));
128 gtk_widget_show_all( GTK_WIDGET(win) );
133 PUBLIC void comp_create_action( char *menuitem, ComponentClass *k, gpointer init_data, char *name, char *label )
136 char *long_name = g_path_get_basename( menuitem );
137 char *base = "/ui/MainMenu/AddComp";
138 char *mpath = g_strdup_printf( "%s/%s", base, menuitem );
140 GtkAction *action;
142 action = gtk_action_group_get_action( component_actiongroup, name );
144 if( action == NULL ) {
145 action = g_object_new( GALAN_TYPE_COMPACTION,
146 "klass", k,
147 "init_data", init_data,
149 "name", g_strdup(name),
150 "label", long_name,
151 "short-label", g_strdup(name),
152 "hide-if-empty", FALSE,
154 NULL );
156 gtk_action_group_add_action( component_actiongroup, action );
159 char *dir_path = g_path_get_dirname( mpath);
160 ensure_path_exists( dir_path, base );
162 //XXX: the half baked tree model
163 GtkTreeIter iter;
164 galan_comptree_model_lookup( tmodel, menuitem, &iter, TRUE );
165 gtk_tree_store_set( GTK_TREE_STORE(tmodel), &iter, 1, TRUE, 2, action, -1 );
166 //XXX:
168 //printf( "name = %s\n", gtk_action_get_name( action ) );
169 gtk_ui_manager_add_ui( ui_manager,
170 gtk_ui_manager_new_merge_id( ui_manager ),
171 dir_path,
172 name,
173 name,
174 GTK_UI_MANAGER_MENUITEM,
175 TRUE );
178 //PRIVATE void kill_newmenu(GtkWidget *menu, GtkItemFactory *ifact) {
179 // gtk_object_unref(GTK_OBJECT(ifact));
182 PRIVATE GtkItemFactory *get_new_ifact( void ) {
184 GtkItemFactory *ifact = gtk_item_factory_new(GTK_TYPE_MENU, "<new>", NULL);
185 GList *lst = menuentries;
187 while (lst != NULL) {
188 MenuEntry *m = lst->data;
189 GtkItemFactoryEntry ent = { m->menupath, NULL, newmenu_callback, 0, NULL };
191 gtk_item_factory_create_item(ifact, &ent, m, 1);
193 lst = g_list_next(lst);
195 menuentries_dirty = FALSE;
200 return ifact;
203 PUBLIC GtkWidget *comp_get_newmenu(Sheet *sheet) {
205 GtkWidget *menu;
207 if( menufact == NULL ) {
208 menufact = get_new_ifact();
209 g_object_ref( G_OBJECT(menufact) );
212 if( menuentries_dirty ) {
213 if( menufact != NULL ) {
214 g_object_unref( G_OBJECT(menufact) );
216 menufact = get_new_ifact();
219 gtk_object_set_user_data( GTK_OBJECT( menufact ), sheet );
220 //gtk_signal_connect(GTK_OBJECT(menu), "destroy", GTK_SIGNAL_FUNC(kill_newmenu), ifact);
222 menu = gtk_item_factory_get_widget(menufact, "<new>");
224 return menu;
227 PUBLIC void comp_register_componentclass(ComponentClass *k) {
228 g_hash_table_insert(componentclasses, k->class_tag, k);
231 PUBLIC Component *comp_new_component(ComponentClass *k, gpointer init_data,
232 Sheet *sheet, gint x, gint y) {
233 Component *c = safe_malloc(sizeof(Component));
235 c->klass = k;
236 c->sheet = sheet;
237 c->x = x;
238 c->y = y;
239 c->saved_x = c->saved_y = 0;
240 c->width = c->height = 0;
241 c->connectors = NULL;
242 c->data = NULL;
244 if (k->initialize_instance == NULL) {
245 g_warning("initialize_instance == NULL in comp_new_component of class %s",
246 k->class_tag);
247 return c;
250 if (!k->initialize_instance(c, init_data)) {
251 free(c);
252 return NULL;
255 return c;
258 PUBLIC Component *comp_clone( Component *c, Sheet *sheet ) {
260 Component *clone;
262 if (c->klass->clone_instance == NULL) {
263 g_warning("clone_instance == NULL in comp_clone of class %s",
264 c->klass->class_tag);
265 return NULL;
268 clone = c->klass->clone_instance( c, sheet );
269 if( clone == NULL )
270 return NULL;
272 if( sheet == c->sheet ) {
273 clone->x = c->x + 10;
274 clone->y = c->y + 10;
275 } else {
276 clone->x = c->x;
277 clone->y = c->y;
280 sheet_add_component( sheet, clone );
282 return clone;
286 PRIVATE gboolean comp_try_unconnect( Component *c ) {
287 GList *connX = c->connectors;
289 while( connX != NULL ) {
290 Connector *con = connX->data;
292 while( con->refs != NULL ) {
293 ConnectorReference *ref = con->refs->data;
294 if( comp_unlink( ref, &(con->ref) ) != TRUE )
295 return FALSE;
298 connX = g_list_next( connX );
301 return TRUE;
304 PUBLIC gboolean comp_kill_component(Component *c) {
306 if( !comp_try_unconnect( c ) )
307 return FALSE;
310 while (c->connectors != NULL) {
311 GList *tmp = g_list_next(c->connectors);
312 Connector *con = c->connectors->data;
314 comp_kill_connector(con);
316 g_list_free_1(c->connectors);
317 c->connectors = tmp;
320 if (c->klass->destroy_instance)
321 c->klass->destroy_instance(c);
323 free(c);
324 return TRUE;
327 PUBLIC ConnectorReference *unpickle_connectorreference(ConnectorReference *ref,
328 ObjectStoreItem *item) {
329 if (ref == NULL)
330 ref = safe_malloc(sizeof(ConnectorReference));
332 ref->c = comp_unpickle(objectstore_item_get_object(item, "component"));
333 ref->kind = objectstore_item_get_integer(item, "kind", COMP_NO_CONNECTOR);
334 ref->is_output = objectstore_item_get_integer(item, "is_output", FALSE);
335 ref->queue_number = objectstore_item_get_integer(item, "queue_number", 0);
337 return ref;
340 PUBLIC gpointer unpickler_for_connectorreference(ObjectStoreItem *item) {
341 return unpickle_connectorreference(NULL, item);
344 PRIVATE Connector *unpickle_connector(ObjectStoreItem *item) {
345 Connector *conn = safe_malloc(sizeof(Connector));
347 conn->x = objectstore_item_get_integer(item, "x_coord", 0);
348 conn->y = objectstore_item_get_integer(item, "y_coord", 0);
349 unpickle_connectorreference(&conn->ref, objectstore_item_get_object(item, "source_ref"));
350 conn->refs = objectstore_extract_list_of_items(objectstore_item_get(item, "targets"),
351 item->db, unpickler_for_connectorreference);
352 return conn;
356 PUBLIC Component *comp_unpickle(ObjectStoreItem *item) {
357 Component *comp = objectstore_get_object(item);
358 ObjectStoreItem *shitem;
360 if (comp == NULL) {
361 comp = safe_malloc(sizeof(Component));
362 objectstore_set_object(item, comp);
365 char *tag = objectstore_item_get_string(item, "class_tag", NULL);
366 ComponentClass *k;
367 RETURN_VAL_UNLESS(tag != NULL, NULL);
368 k = g_hash_table_lookup(componentclasses, tag);
369 if (k == NULL) {
370 popup_msgbox("Class not found", MSGBOX_CANCEL, 0, MSGBOX_CANCEL,
371 "Component-class not found: tag = %s", tag);
372 g_message("Component Class not found; tag = %s", tag);
373 free(comp);
374 return NULL;
376 comp->klass = k;
378 comp->data = NULL;
380 comp->saved_x = comp->saved_y = 0;
382 shitem = objectstore_item_get_object( item, "sheet" );
383 if( shitem == NULL )
384 shitem = objectstore_get_root( item->db );
386 comp->sheet = sheet_unpickle( shitem );
387 comp->x = objectstore_item_get_integer(item, "x_coord", 0);
388 comp->y = objectstore_item_get_integer(item, "y_coord", 0);
389 comp->width = objectstore_item_get_integer(item, "width", 70);
390 comp->height = objectstore_item_get_integer(item, "height", 70);
391 comp->connectors =
392 objectstore_extract_list_of_items(objectstore_item_get(item, "connectors"),
393 item->db,
394 (objectstore_unpickler_t) unpickle_connector);
395 comp->klass->unpickle_instance(comp, item, item->db);
398 return comp;
401 PUBLIC ObjectStoreItem *pickle_connectorreference(ConnectorReference *ref, ObjectStore *db) {
402 ObjectStoreItem *item = objectstore_new_item(db, "ConnectorReference", ref);
403 objectstore_item_set_object(item, "component", comp_pickle(ref->c, db));
404 objectstore_item_set_integer(item, "kind", ref->kind);
405 objectstore_item_set_integer(item, "is_output", ref->is_output);
406 objectstore_item_set_integer(item, "queue_number", ref->queue_number);
407 return item;
410 PRIVATE ObjectStoreItem *pickle_connector(Connector *con, ObjectStore *db) {
411 ObjectStoreItem *item = objectstore_new_item(db, "Connector", con);
413 objectstore_item_set_integer(item, "x_coord", con->x);
414 objectstore_item_set_integer(item, "y_coord", con->y);
415 objectstore_item_set_object(item, "source_ref", pickle_connectorreference(&con->ref, db));
416 objectstore_item_set(item, "targets",
417 objectstore_create_list_of_items(con->refs, db,
418 (objectstore_pickler_t)
419 pickle_connectorreference));
420 return item;
423 PUBLIC ObjectStoreItem *comp_pickle(Component *c, ObjectStore *db) {
424 ObjectStoreItem *item = objectstore_get_item(db, c);
426 if (item == NULL) {
427 item = objectstore_new_item(db, "Component", c);
428 objectstore_item_set_string(item, "class_tag", c->klass->class_tag);
429 objectstore_item_set_object(item, "sheet", sheet_pickle(c->sheet, db) );
430 objectstore_item_set_integer(item, "x_coord", c->x);
431 objectstore_item_set_integer(item, "y_coord", c->y);
432 objectstore_item_set_integer(item, "width", c->width);
433 objectstore_item_set_integer(item, "height", c->height);
434 objectstore_item_set(item, "connectors",
435 objectstore_create_list_of_items(c->connectors, db,
436 (objectstore_pickler_t)
437 pickle_connector));
438 c->klass->pickle_instance(c, item, db);
441 return item;
444 PUBLIC void comp_paint_connections(Component *c, GdkRectangle *area,
445 GdkDrawable *drawable, GtkStyle *style, GdkColor *colors) {
446 GList *l = c->connectors;
448 while (l != NULL) {
449 Connector *con = l->data;
450 GList *o = con->refs;
452 l = g_list_next(l);
454 if (!con->ref.is_output)
455 continue;
457 while (o != NULL) {
458 Connector *other = comp_get_connector(o->data);
460 if( other != NULL ) {
461 gdk_draw_line(drawable, style->white_gc,
462 con->x + c->x, con->y + c->y,
463 other->x + other->ref.c->x, other->y + other->ref.c->y);
466 o = g_list_next(o);
471 PUBLIC void comp_paint(Component *c, GdkRectangle *area,
472 GdkDrawable *drawable, GtkStyle *style, GdkColor *colors) {
473 g_return_if_fail(c->klass->paint != NULL);
474 c->klass->paint(c, area, drawable, style, colors);
477 PUBLIC int comp_find_connector(Component *c, gint x, gint y, ConnectorReference *ref) {
478 if (c->klass->find_connector_at)
479 return c->klass->find_connector_at(c, x, y, ref);
480 else
481 return 0;
484 PUBLIC int comp_contains_point(Component *c, gint x, gint y) {
485 if (c->klass->contains_point)
486 return c->klass->contains_point(c, x, y);
487 else
488 return (x >= c->x && y >= c->y &&
489 x < (c->x + c->width) &&
490 y < (c->y + c->height));
493 PRIVATE gint find_connector(Connector *con, ConnectorReference *ref) {
494 return
495 (ref->c != con->ref.c) ||
496 (ref->queue_number != con->ref.queue_number) ||
497 (ref->kind != con->ref.kind) ||
498 (ref->is_output != con->ref.is_output);
501 PUBLIC gint connectorreference_equal(ConnectorReference *r1, ConnectorReference *r2) {
502 return
503 (r1->c != r2->c) ||
504 (r1->queue_number != r2->queue_number) ||
505 (r1->kind != r2->kind) ||
506 (r1->is_output != r2->is_output);
509 PUBLIC void comp_link(ConnectorReference *src, ConnectorReference *dst) {
510 g_return_if_fail(src != NULL && dst != NULL);
512 if (src->is_output == dst->is_output)
513 return;
515 if (!src->is_output) {
516 ConnectorReference *tmp = src;
517 src = dst;
518 dst = tmp;
521 if (src->kind != dst->kind &&
522 src->kind != COMP_ANY_CONNECTOR &&
523 dst->kind != COMP_ANY_CONNECTOR)
524 return;
526 if (g_list_find_custom(comp_get_connector(src)->refs, dst,
527 (GCompareFunc) connectorreference_equal) != NULL)
528 return;
530 if (src->c->klass->accept_outbound)
531 if (!src->c->klass->accept_outbound(src->c, src, dst))
532 return;
534 if (dst->c->klass->accept_inbound)
535 if (!dst->c->klass->accept_inbound(dst->c, src, dst)) {
536 src->c->klass->unlink_outbound(src->c, src, dst);
537 return;
540 comp_insert_connection(comp_get_connector(src), dst);
541 comp_insert_connection(comp_get_connector(dst), src);
544 PUBLIC gboolean comp_unlink(ConnectorReference *src, ConnectorReference *dst) {
545 Connector *srccon, *dstcon;
546 g_return_val_if_fail(src != NULL && dst != NULL, FALSE);
548 if (src->is_output == dst->is_output)
549 return FALSE;
551 if (!src->is_output) {
552 ConnectorReference *tmp = src;
553 src = dst;
554 dst = tmp;
557 if (src->kind != dst->kind &&
558 src->kind != COMP_ANY_CONNECTOR &&
559 dst->kind != COMP_ANY_CONNECTOR)
560 return FALSE;
562 if (src->c->klass->unlink_outbound)
563 if( !src->c->klass->unlink_outbound(src->c, src, dst) )
564 return FALSE;
566 if (dst->c->klass->unlink_inbound)
567 if( !dst->c->klass->unlink_inbound(dst->c, src, dst) )
568 return FALSE;
570 srccon = comp_get_connector(src);
571 dstcon = comp_get_connector(dst);
572 comp_remove_connection(srccon, dst);
573 comp_remove_connection(dstcon, src);
575 return TRUE;
578 PUBLIC char *comp_get_title(Component *c) {
579 g_return_val_if_fail(c->klass->get_title != NULL, NULL);
580 return c->klass->get_title(c);
583 PUBLIC char *comp_get_connector_name(ConnectorReference *ref) {
584 Component *c = ref->c;
585 char *title = comp_get_title(ref->c);
586 char *conn_name;
587 char *result;
589 if (c->klass->get_connector_name == NULL)
590 return title;
592 conn_name = c->klass->get_connector_name(c, ref);
594 result = malloc(strlen(title) + strlen(conn_name) + 4); /* " [" , "]" and '\0' */
595 if (result == NULL) {
596 free(conn_name);
597 return title;
600 sprintf(result, "%s [%s]", title, conn_name);
601 free(conn_name);
602 free(title);
604 return result;
607 PUBLIC void comp_append_popup(Component *c, GtkWidget *menu) {
608 char *name;
609 GtkWidget *submenu;
610 GtkWidget *item;
612 g_return_if_fail(c->klass->get_title != NULL);
614 if (c->klass->build_popup == NULL)
615 return;
617 name = c->klass->get_title(c);
618 submenu = c->klass->build_popup(c);
620 item = gtk_menu_item_new_with_label(name);
621 free(name);
623 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
624 gtk_menu_append(GTK_MENU(menu), item);
625 gtk_widget_show(item);
629 PUBLIC GtkWidget *comp_get_popup(Component *c) {
631 if (c->klass->build_popup == NULL)
632 return NULL;
634 return c->klass->build_popup(c);
637 PUBLIC Connector *comp_new_connector(Component *c, ConnectorKind kind,
638 gboolean is_output, gint queue_number,
639 gint x, gint y) {
640 Connector *con = safe_malloc(sizeof(Connector));
642 con->ref.c = c;
643 con->ref.kind = kind;
644 con->ref.is_output = is_output;
645 con->ref.queue_number = queue_number;
646 con->refs = NULL;
647 con->x = x;
648 con->y = y;
650 c->connectors = g_list_prepend(c->connectors, con);
652 return con;
655 PUBLIC Connector *comp_get_connector(ConnectorReference *ref) {
656 GList *node = g_list_find_custom(ref->c->connectors, ref, (GCompareFunc) find_connector);
658 return (node == NULL) ? NULL : node->data;
661 PUBLIC void comp_kill_connector(Connector *con) {
662 while (con->refs != NULL) {
663 ConnectorReference *ref = con->refs->data;
665 comp_unlink(ref, &(con->ref) );
668 free(con);
671 PUBLIC void comp_insert_connection(Connector *con, ConnectorReference *other) {
672 ConnectorReference *clone = safe_malloc(sizeof(ConnectorReference));
674 *clone = *other;
675 con->refs = g_list_prepend(con->refs, clone);
678 PUBLIC void comp_remove_connection(Connector *con, ConnectorReference *other) {
679 GList *node = g_list_find_custom(con->refs, other, (GCompareFunc) connectorreference_equal);
680 g_return_if_fail(node != NULL);
682 con->refs = g_list_remove_link(con->refs, node);
683 free(node->data);
684 g_list_free_1(node);
687 PRIVATE void clone_connection( ConnectorReference src, ConnectorReference dst, GHashTable *clonemap ) {
688 src.c = g_hash_table_lookup( clonemap, src.c );
689 dst.c = g_hash_table_lookup( clonemap, dst.c );
691 if( src.c && dst.c )
692 comp_link( &src, &dst );
695 PUBLIC void comp_clone_list( GList *lst, Sheet *sheet ) {
697 GHashTable *clonemap = g_hash_table_new( g_direct_hash, g_direct_equal );
699 GList *compX;
701 // Clone all components in list, and generate the map from src to clone
702 for( compX = lst; compX; compX = g_list_next( compX ) ) {
704 Component *c = compX->data;
706 Component *clone = comp_clone( c, sheet );
707 g_hash_table_insert( clonemap, c, clone );
710 // now read out all connections and connect the clones
712 for( compX = lst; compX; compX = g_list_next( compX ) ) {
714 Component *c = compX->data;
715 GList *connX;
716 for( connX = c->connectors; connX; connX = g_list_next( connX ) ) {
717 Connector *con = connX->data;
719 GList *refX;
720 for( refX=con->refs; refX; refX = g_list_next( refX ) ) {
721 ConnectorReference *ref = refX->data;
723 clone_connection( con->ref, *ref, clonemap );
729 PUBLIC void init_comp(void) {
730 componentclasses = g_hash_table_new(g_str_hash, g_str_equal);
731 experiment();
734 PUBLIC void done_comp(void) {
735 g_hash_table_destroy(componentclasses);
736 componentclasses = NULL;