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
28 #include "generator.h"
35 #include "galan-compaction.h"
36 #include "galan-comptree-model.h"
38 #define NEWMENU_PATH_PREFIX "/"
40 typedef struct 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
||
66 k
->get_title
== NULL
) {
67 g_warning("ComponentClass must have initialize_instance, paint and "
68 "get_title methods (menupath = %s)", menupath
);
73 m
->menupath
= g_strdup_printf( "%s%s", NEWMENU_PATH_PREFIX
, menupath
);
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
) )
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
),
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
);
142 action
= gtk_action_group_get_action( component_actiongroup
, name
);
144 if( action
== NULL
) {
145 action
= g_object_new( GALAN_TYPE_COMPACTION
,
147 "init_data", init_data
,
149 "name", g_strdup(name
),
151 "short-label", g_strdup(name
),
152 "hide-if-empty", FALSE
,
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
164 galan_comptree_model_lookup( tmodel
, menuitem
, &iter
, TRUE
);
165 gtk_tree_store_set( GTK_TREE_STORE(tmodel
), &iter
, 1, TRUE
, 2, action
, -1 );
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
),
174 GTK_UI_MANAGER_MENUITEM
,
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
;
203 PUBLIC GtkWidget
*comp_get_newmenu(Sheet
*sheet
) {
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>");
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
));
239 c
->saved_x
= c
->saved_y
= 0;
240 c
->width
= c
->height
= 0;
241 c
->connectors
= NULL
;
244 if (k
->initialize_instance
== NULL
) {
245 g_warning("initialize_instance == NULL in comp_new_component of class %s",
250 if (!k
->initialize_instance(c
, init_data
)) {
258 PUBLIC Component
*comp_clone( Component
*c
, Sheet
*sheet
) {
262 if (c
->klass
->clone_instance
== NULL
) {
263 g_warning("clone_instance == NULL in comp_clone of class %s",
264 c
->klass
->class_tag
);
268 clone
= c
->klass
->clone_instance( c
, sheet
);
272 if( sheet
== c
->sheet
) {
273 clone
->x
= c
->x
+ 10;
274 clone
->y
= c
->y
+ 10;
280 sheet_add_component( sheet
, 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
)
298 connX
= g_list_next( connX
);
304 PUBLIC gboolean
comp_kill_component(Component
*c
) {
306 if( !comp_try_unconnect( c
) )
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
);
320 if (c
->klass
->destroy_instance
)
321 c
->klass
->destroy_instance(c
);
327 PUBLIC ConnectorReference
*unpickle_connectorreference(ConnectorReference
*ref
,
328 ObjectStoreItem
*item
) {
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);
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
);
356 PUBLIC Component
*comp_unpickle(ObjectStoreItem
*item
) {
357 Component
*comp
= objectstore_get_object(item
);
358 ObjectStoreItem
*shitem
;
361 comp
= safe_malloc(sizeof(Component
));
362 objectstore_set_object(item
, comp
);
365 char *tag
= objectstore_item_get_string(item
, "class_tag", NULL
);
367 RETURN_VAL_UNLESS(tag
!= NULL
, NULL
);
368 k
= g_hash_table_lookup(componentclasses
, tag
);
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
);
380 comp
->saved_x
= comp
->saved_y
= 0;
382 shitem
= objectstore_item_get_object( item
, "sheet" );
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);
392 objectstore_extract_list_of_items(objectstore_item_get(item
, "connectors"),
394 (objectstore_unpickler_t
) unpickle_connector
);
395 comp
->klass
->unpickle_instance(comp
, item
, item
->db
);
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
);
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
));
423 PUBLIC ObjectStoreItem
*comp_pickle(Component
*c
, ObjectStore
*db
) {
424 ObjectStoreItem
*item
= objectstore_get_item(db
, c
);
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
)
438 c
->klass
->pickle_instance(c
, item
, db
);
444 PUBLIC
void comp_paint_connections(Component
*c
, GdkRectangle
*area
,
445 GdkDrawable
*drawable
, GtkStyle
*style
, GdkColor
*colors
) {
446 GList
*l
= c
->connectors
;
449 Connector
*con
= l
->data
;
450 GList
*o
= con
->refs
;
454 if (!con
->ref
.is_output
)
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
);
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
);
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
);
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
) {
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
) {
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
)
515 if (!src
->is_output
) {
516 ConnectorReference
*tmp
= src
;
521 if (src
->kind
!= dst
->kind
&&
522 src
->kind
!= COMP_ANY_CONNECTOR
&&
523 dst
->kind
!= COMP_ANY_CONNECTOR
)
526 if (g_list_find_custom(comp_get_connector(src
)->refs
, dst
,
527 (GCompareFunc
) connectorreference_equal
) != NULL
)
530 if (src
->c
->klass
->accept_outbound
)
531 if (!src
->c
->klass
->accept_outbound(src
->c
, src
, dst
))
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
);
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
)
551 if (!src
->is_output
) {
552 ConnectorReference
*tmp
= src
;
557 if (src
->kind
!= dst
->kind
&&
558 src
->kind
!= COMP_ANY_CONNECTOR
&&
559 dst
->kind
!= COMP_ANY_CONNECTOR
)
562 if (src
->c
->klass
->unlink_outbound
)
563 if( !src
->c
->klass
->unlink_outbound(src
->c
, src
, dst
) )
566 if (dst
->c
->klass
->unlink_inbound
)
567 if( !dst
->c
->klass
->unlink_inbound(dst
->c
, src
, dst
) )
570 srccon
= comp_get_connector(src
);
571 dstcon
= comp_get_connector(dst
);
572 comp_remove_connection(srccon
, dst
);
573 comp_remove_connection(dstcon
, src
);
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
);
589 if (c
->klass
->get_connector_name
== NULL
)
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
) {
600 sprintf(result
, "%s [%s]", title
, conn_name
);
607 PUBLIC void comp_append_popup(Component *c, GtkWidget *menu) {
612 g_return_if_fail(c->klass->get_title != NULL);
614 if (c->klass->build_popup == NULL)
617 name = c->klass->get_title(c);
618 submenu = c->klass->build_popup(c);
620 item = gtk_menu_item_new_with_label(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
)
634 return c
->klass
->build_popup(c
);
637 PUBLIC Connector
*comp_new_connector(Component
*c
, ConnectorKind kind
,
638 gboolean is_output
, gint queue_number
,
640 Connector
*con
= safe_malloc(sizeof(Connector
));
643 con
->ref
.kind
= kind
;
644 con
->ref
.is_output
= is_output
;
645 con
->ref
.queue_number
= queue_number
;
650 c
->connectors
= g_list_prepend(c
->connectors
, 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
) );
671 PUBLIC
void comp_insert_connection(Connector
*con
, ConnectorReference
*other
) {
672 ConnectorReference
*clone
= safe_malloc(sizeof(ConnectorReference
));
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
);
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
);
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
);
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
;
716 for( connX
= c
->connectors
; connX
; connX
= g_list_next( connX
) ) {
717 Connector
*con
= connX
->data
;
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
);
734 PUBLIC
void done_comp(void) {
735 g_hash_table_destroy(componentclasses
);
736 componentclasses
= NULL
;