/* * SwamiUIGenGraph.c - User interface generator control object * * Swami * Copyright (C) 1999-2003 Josh Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include "SwamiUIGenGraph.h" #include "SwamiUIObject.h" #include "pixmap.h" #include "util.h" #include "i18n.h" typedef enum { GENGRAPH_LEVEL, /* line level for a single parameter */ GENGRAPH_ENV1, /* volume envelope */ GENGRAPH_ENV2, /* modulation envelope (pitch/filter) */ GENGRAPH_LFO1, /* modulation LFO (pitch/filter/volume) */ GENGRAPH_LFO2 /* vibrato LFO (pitch) */ } GraphType; typedef enum { GENGRAPH_NONE, GENGRAPH_VOLUME, GENGRAPH_PITCH, GENGRAPH_FILTER } GraphLevelType; #define IS_ENVELOPE(val) (val == GENGRAPH_ENV1 || val == GENGRAPH_ENV2) #define IS_LFO(val) (val == GENGRAPH_LFO1 || val == GENGRAPH_LFO2) struct _GraphCache { double top, bottom, left, right; double width, height; double h_middle, v_middle; }; /* structure bound to canvas group item of a control instance */ typedef struct _GraphCtrl { SwamiUIGenGraph *gengraph; /* for GTK callback convenience */ GraphType type; /* type of graph */ GraphLevelType subtype; /* sub type of graph */ IPZone *zone; /* zone override, NULL to use default */ GnomeCanvasGroup *canvas_group; /* canvas group for this control */ GnomeCanvasItem *canvas_graph; /* canvas item used for graph */ GList *handles; /* list of handles (GraphHandle) */ GnomeCanvasPoints *points; /* points in envelope */ } GraphCtrl; /* structure bound to each handle of a control */ typedef struct _GraphHandle { GnomeCanvasItem *handle_item; /* canvas box item for handle */ int xgen; /* generator for x axis or -1 */ gboolean yctrled; /* set to TRUE if handle has y axis control */ double xstart; /* cached start position of this handle */ } GraphHandle; static void swamiui_gengraph_class_init (SwamiUIGenGraphClass *klass); static void swamiui_gengraph_init (SwamiUIGenGraph *gengraph); static void swamiui_gengraph_cb_zone_gen_change (SwamiObject *swami, IPZone *zone, SwamiUIGenGraph *gengraph); static void swamiui_gengraph_cb_show (GtkWidget *widg, gpointer data); static void gengraph_canvas_size_allocate (GtkWidget *canvas, GtkAllocation *alloc, SwamiUIGenGraph *gengraph); static void gengraph_cb_clist_select_row (GtkCList *clist, gint row, gint column, GdkEventButton *event, SwamiUIGenGraph *gengraph); static void gengraph_cb_clist_unselect_row (GtkCList *clist, gint row, gint column, GdkEventButton *event, SwamiUIGenGraph *gengraph); static void swamiui_gengraph_add_control (SwamiUIGenGraph *gengraph, IPZone *zone, int type, int subtype, char *color); static void swamiui_gengraph_remove_control (GraphCtrl *ctrl); static void swamiui_gengraph_update_all_controls (SwamiUIGenGraph *gengraph); static void swamiui_gengraph_update_control (GraphCtrl *ctrl); float norm_gen (IPZone *zone, int genid); float norm_gen_val (int genval, int genid); float norm_choord (float pos, float lower, float upper); void set_norm_gen (float val, IPZone *zone, int genid); int get_norm_gen_val (float val, IPZone *zone, int genid); void set_gen_realtime (IPZone *zone, int genid, int amount); static gint gengraph_cb_drag_handle_event (GnomeCanvasItem *handle, GdkEvent *event, GraphCtrl *ctrl); static void create_handle (SwamiUIGenGraph *gengraph, GnomeCanvasGroup *group, int xgen, gboolean yctrled, GtkSignalFunc callback); static void move_drag_box (GnomeCanvasItem *item, double x, double y); static gint highlight_drag_box (GnomeCanvasItem *item, GdkEvent *event, gpointer data); /* global vars */ static int gengraph_border = 4; static int gengraph_handle_size = 7; struct _CtrlDescr { char *name; GraphType type; GraphLevelType subtype; char *color; } ctrl_descrs[] = { { N_("Volume Envelope"), GENGRAPH_ENV1, GENGRAPH_VOLUME, "red" }, { N_("Modulation Envelope"), GENGRAPH_ENV2, GENGRAPH_NONE, "green" }, { N_("Pitch Envelope"), GENGRAPH_ENV2, GENGRAPH_PITCH, "blue" }, { N_("Filter Envelope"), GENGRAPH_ENV2, GENGRAPH_FILTER, "yellow" } }; #define CTRL_DESCR_COUNT (sizeof (ctrl_descrs) / sizeof (struct _CtrlDescr)) guint swamiui_gengraph_get_type (void) { static guint obj_type = 0; if (!obj_type) { GtkTypeInfo obj_info = { "SwamiUIGenGraph", sizeof (SwamiUIGenGraph), sizeof (SwamiUIGenGraphClass), (GtkClassInitFunc) swamiui_gengraph_class_init, (GtkObjectInitFunc) swamiui_gengraph_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; obj_type = gtk_type_unique (gtk_hpaned_get_type (), &obj_info); } return obj_type; } static void swamiui_gengraph_class_init (SwamiUIGenGraphClass *klass) { } static void swamiui_gengraph_init (SwamiUIGenGraph *gengraph) { GtkWidget *canvas; GtkWidget *clist; GnomeCanvasGroup *root; GnomeCanvasItem *border; char *text[1] = { NULL }; int i; gtk_paned_set_position (GTK_PANED (gengraph), 120); clist = gtk_clist_new (1); for (i = 0; i < CTRL_DESCR_COUNT; i++) { text[0] = ctrl_descrs[i].name; gtk_clist_append (GTK_CLIST (clist), text); } gtk_paned_add1 (GTK_PANED (gengraph), clist); gtk_widget_show (clist); gtk_clist_set_selection_mode (GTK_CLIST (clist), GTK_SELECTION_EXTENDED); gtk_signal_connect (GTK_OBJECT (clist), "select-row", GTK_SIGNAL_FUNC (gengraph_cb_clist_select_row), gengraph); gtk_signal_connect (GTK_OBJECT (clist), "unselect-row", GTK_SIGNAL_FUNC (gengraph_cb_clist_unselect_row), gengraph); canvas = gnome_canvas_new (); gengraph->canvas = canvas; gengraph->zone = NULL; gengraph->ctrls = NULL; gengraph->cache = g_malloc0 (sizeof (GraphCache)); gtk_paned_add2 (GTK_PANED (gengraph), canvas); /* add border */ root = gnome_canvas_root (GNOME_CANVAS (canvas)); border = gnome_canvas_item_new (root, gnome_canvas_rect_get_type (), "fill_color", NULL, "outline_color", "black", "width_pixels", 0, NULL); gtk_object_set_data (GTK_OBJECT (canvas), "border", border); gtk_signal_connect (GTK_OBJECT (canvas), "size-allocate", GTK_SIGNAL_FUNC (gengraph_canvas_size_allocate), gengraph); gtk_widget_show (canvas); g_signal_connect (swami_object, "zone_gen_change", (GCallback)swamiui_gengraph_cb_zone_gen_change, gengraph); gtk_signal_connect (GTK_OBJECT (gengraph), "show", swamiui_gengraph_cb_show, NULL); } static void swamiui_gengraph_cb_zone_gen_change (SwamiObject *swami, IPZone *zone, SwamiUIGenGraph *gengraph) { if (zone != gengraph->zone) return; if (GTK_WIDGET_VISIBLE (GTK_WIDGET (gengraph))) swamiui_gengraph_update_all_controls (gengraph); else gengraph->queue_update = TRUE; } static void swamiui_gengraph_cb_show (GtkWidget *widg, gpointer data) { SwamiUIGenGraph *gengraph = SWAMIUI_GENGRAPH (widg); if (gengraph->queue_update) { swamiui_gengraph_update_all_controls (gengraph); gengraph->queue_update = FALSE; } } static void gengraph_canvas_size_allocate (GtkWidget *canv_widg, GtkAllocation *alloc, SwamiUIGenGraph *gengraph) { GnomeCanvas *canvas = GNOME_CANVAS (canv_widg); GnomeCanvasItem *border; GraphCache *cache; gnome_canvas_set_scroll_region (canvas, 0, 0, alloc->width, alloc->height); cache = gengraph->cache; gnome_canvas_c2w (canvas, gengraph_border, gengraph_border, &cache->left, &cache->top); gnome_canvas_c2w (canvas, alloc->width - gengraph_border, alloc->height - gengraph_border, &cache->right, &cache->bottom); gnome_canvas_c2w (canvas, alloc->width - gengraph_border * 2, alloc->height - gengraph_border * 2, &cache->width, &cache->height); cache->h_middle = cache->width / 2.0 + cache->left; cache->v_middle = cache->height / 2.0 + cache->top; border = gtk_object_get_data (GTK_OBJECT (canvas), "border"); gnome_canvas_item_set (border, "x1", gengraph->cache->left, "y1", gengraph->cache->top, "x2", gengraph->cache->right, "y2", gengraph->cache->bottom, NULL); swamiui_gengraph_update_all_controls (gengraph); } static void gengraph_cb_clist_select_row (GtkCList *clist, gint row, gint column, GdkEventButton *event, SwamiUIGenGraph *gengraph) { swamiui_gengraph_add_control (gengraph, NULL, ctrl_descrs[row].type, ctrl_descrs[row].subtype, ctrl_descrs[row].color); } static void gengraph_cb_clist_unselect_row (GtkCList *clist, gint row, gint column, GdkEventButton *event, SwamiUIGenGraph *gengraph) { GraphCtrl *ctrl; GList *p; p = gengraph->ctrls; while (p) { ctrl = (GraphCtrl *)(p->data); p = g_list_next (p); if (ctrl->type == ctrl_descrs[row].type && ctrl->subtype == ctrl_descrs[row].subtype) swamiui_gengraph_remove_control (ctrl); } } /** * Create a new generator control object * Returns: new widget of type SwamiUIGenGraph */ GtkWidget * swamiui_gengraph_new (void) { return (GTK_WIDGET (gtk_type_new (swamiui_gengraph_get_type ()))); } /** * Set the default patch item to control * @gengraph Generator graph object * @item Default patch item to control or NULL to disable * (currently only uses Instrument IPZone items). */ void swamiui_gengraph_set_item (SwamiUIGenGraph *gengraph, IPItem *item) { IPZone *zone = NULL; g_return_if_fail (gengraph != NULL); g_return_if_fail (SWAMIUI_IS_GENGRAPH (gengraph)); if (item && INSTP_IS_ZONE (item) && item->parent && INSTP_IS_INST (item->parent)) zone = INSTP_ZONE (item); if (gengraph->zone == zone) return; gengraph->zone = zone; swamiui_gengraph_update_all_controls (gengraph); } /** * Add a control to a generator graph * @gengraph SoundFont generator graph object * @zone SoundFont zone override or NULL to use default * @type Type of graph * @subtype Sub type */ static void swamiui_gengraph_add_control (SwamiUIGenGraph *gengraph, IPZone *zone, int type, int subtype, char *color) { GraphCtrl *ctrl; GnomeCanvasGroup *root, *group; GnomeCanvasItem *item; int i; g_return_if_fail (gengraph != NULL); g_return_if_fail (SWAMIUI_IS_GENGRAPH (gengraph)); root = gnome_canvas_root (GNOME_CANVAS (gengraph->canvas)); group = GNOME_CANVAS_GROUP (gnome_canvas_item_new (root, gnome_canvas_group_get_type (), NULL)); ctrl = g_malloc0 (sizeof (GraphCtrl)); ctrl->gengraph = gengraph; ctrl->type = type; ctrl->subtype = subtype; if (zone) instp_item_ref (INSTP_ITEM (zone)); ctrl->zone = zone; ctrl->canvas_group = group; ctrl->points = gnome_canvas_points_new (6); gtk_object_set_data_full (GTK_OBJECT (group), "bag", ctrl, (GtkDestroyNotify)g_free); /* add to list of controls */ gengraph->ctrls = g_list_append (gengraph->ctrls, ctrl); if (IS_ENVELOPE (type)) { struct { int xgen; gboolean yctrled; } params[6]; for (i = 0; i < 6; i++) { params[i].xgen = -1; params[i].yctrled = FALSE; } item = gnome_canvas_item_new (group, gnome_canvas_line_get_type (), "points", ctrl->points, "fill_color", color, "width_pixels", 1, NULL); if (type == GENGRAPH_ENV1) /* volume envelope */ { params[1].xgen = IPGEN_VOL_ENV_DELAY; params[2].xgen = IPGEN_VOL_ENV_ATTACK; params[3].xgen = IPGEN_VOL_ENV_HOLD; params[4].xgen = IPGEN_VOL_ENV_DECAY; params[5].xgen = IPGEN_VOL_ENV_RELEASE; if (subtype == GENGRAPH_VOLUME) params[4].yctrled = TRUE; } else /* modulation envelope */ { params[1].xgen = IPGEN_MOD_ENV_DELAY; params[2].xgen = IPGEN_MOD_ENV_ATTACK; params[3].xgen = IPGEN_MOD_ENV_HOLD; params[4].xgen = IPGEN_MOD_ENV_DECAY; params[5].xgen = IPGEN_MOD_ENV_RELEASE; if (subtype == GENGRAPH_PITCH) params[2].yctrled = params[3].yctrled = params[4].yctrled = TRUE; else if (subtype == GENGRAPH_FILTER) for (i = 0; i < 6; i++) params[i].yctrled = TRUE; } for (i = 0; i < 6; i++) create_handle (gengraph, group, params[i].xgen, params[i].yctrled, GTK_SIGNAL_FUNC (gengraph_cb_drag_handle_event)); } ctrl->canvas_graph = item; swamiui_gengraph_update_control (ctrl); } static void swamiui_gengraph_remove_control (GraphCtrl *ctrl) { GList **ctrls = &ctrl->gengraph->ctrls; if (IS_ENVELOPE (ctrl->type)) { if (ctrl->zone) instp_item_unref (INSTP_ITEM (ctrl->zone)); gnome_canvas_points_free (ctrl->points); /* NOTE: When group is destroyed a callback frees the GraphCtrl bag */ if (ctrl->canvas_group) gtk_object_destroy (GTK_OBJECT (ctrl->canvas_group)); *ctrls = g_list_remove (*ctrls, ctrl); } } static void swamiui_gengraph_update_all_controls (SwamiUIGenGraph *gengraph) { GList *p; g_return_if_fail (gengraph != NULL); g_return_if_fail (SWAMIUI_IS_GENGRAPH (gengraph)); p = gengraph->ctrls; while (p) { swamiui_gengraph_update_control ((GraphCtrl *)(p->data)); p = g_list_next (p); } } static void swamiui_gengraph_update_control (GraphCtrl *ctrl) { GraphHandle *handle; GraphCache *cache = ctrl->gengraph->cache; IPZone *zone; GList *p; double x; int i; zone = (ctrl->zone ? ctrl->zone : ctrl->gengraph->zone); if (!zone) { gnome_canvas_item_hide (GNOME_CANVAS_ITEM (ctrl->canvas_group)); return; } gnome_canvas_item_show (GNOME_CANVAS_ITEM (ctrl->canvas_group)); if (IS_ENVELOPE (ctrl->type)) { GnomeCanvasPoints *points = ctrl->points; float env_base, env_extent, env_sustain; IPGenAmount amt1, amt2; float temp1, temp2; switch (ctrl->subtype) { case GENGRAPH_NONE: env_base = cache->bottom; env_extent = cache->top; if (ctrl->type == GENGRAPH_ENV1) env_sustain = norm_gen (zone, IPGEN_VOL_ENV_SUSTAIN) * cache->height + cache->top; else env_sustain = norm_gen (zone, IPGEN_MOD_ENV_SUSTAIN) * cache->height + cache->top; break; case GENGRAPH_VOLUME: env_base = cache->bottom; temp1 = norm_gen (zone, IPGEN_ATTENUATION); env_extent = temp1 * cache->height + cache->top; temp1 += norm_gen (zone, IPGEN_VOL_ENV_SUSTAIN); env_sustain = CLAMP (temp1, 0.0, 1.0) * cache->height + cache->top; break; case GENGRAPH_PITCH: env_base = cache->v_middle; env_extent = (1.0 - norm_gen (zone, IPGEN_MOD_ENV_TO_PITCH)) * cache->height + cache->top; env_sustain = (1.0 - norm_gen (zone, IPGEN_MOD_ENV_SUSTAIN)) * (env_extent - env_base) + env_base; break; case GENGRAPH_FILTER: swami_zone_get_gen (swami_object, zone, IPGEN_FILTER_FC, &amt1); temp1 = 1.0 - norm_gen_val (amt1.sword, IPGEN_FILTER_FC); env_base = temp1 * cache->height + cache->top; swami_zone_get_gen (swami_object, zone, IPGEN_MOD_ENV_TO_FILTER_FC, &amt2); instp_genid_offset (IPGEN_FILTER_FC, &amt1, amt2); temp2 = 1.0 - norm_gen_val (amt1.sword, IPGEN_FILTER_FC); env_extent = temp2 * cache->height + cache->top; env_sustain = (1.0 - norm_gen (zone, IPGEN_MOD_ENV_SUSTAIN)) * (env_extent - env_base) + env_base; break; } /* [0,1,5].y = base, [2,3].y = extent, [4].y = sustain */ points->coords[1] = points->coords[3] = points->coords[11] = env_base; points->coords[5] = points->coords[7] = env_extent; points->coords[9] = env_sustain; p = ctrl->handles; for (i = 0, x = cache->left; i < 6; i++) { handle = (GraphHandle *)(p->data); if (handle->xgen != -1) { handle->xstart = x; x += norm_gen (zone, handle->xgen) * (cache->width / 5.0); } if (handle && handle->handle_item) move_drag_box (handle->handle_item, x, points->coords[i * 2 + 1]); points->coords[i * 2] = x; p = g_list_next (p); } gnome_canvas_item_set (ctrl->canvas_graph, "points", points, NULL); } } float norm_gen (IPZone *zone, int genid) { IPGenInfo *geninfo = &instp_gen_info[genid]; IPGenAmount amt; swami_zone_get_gen (swami_object, zone, genid, &amt); amt.sword = CLAMP (amt.sword, geninfo->min, geninfo->max); return (((int)amt.sword - geninfo->min) / (float)(geninfo->max - geninfo->min)); } float norm_gen_val (int genval, int genid) { IPGenInfo *geninfo = &instp_gen_info[genid]; genval = CLAMP (genval, geninfo->min, geninfo->max); return ((genval - geninfo->min) / (float)(geninfo->max - geninfo->min)); } float norm_choord (float pos, float lower, float upper) { float val = (pos - lower) / (upper - lower); return (CLAMP (val, 0.0, 1.0)); } void set_norm_gen (float val, IPZone *zone, int genid) { IPGenInfo *geninfo = &instp_gen_info[genid]; IPGenAmount amt; amt.sword = (int)(val * (geninfo->max - geninfo->min) + 0.5) + geninfo->min; swami_zone_set_gen (swami_object, zone, genid, amt); set_gen_realtime (zone, genid, amt.sword); } int get_norm_gen_val (float val, IPZone *zone, int genid) { IPGenInfo *geninfo = &instp_gen_info[genid]; return ((int)(val * (geninfo->max - geninfo->min) + 0.5) + geninfo->min); } void set_gen_realtime (IPZone *zone, int genid, int amount) { GObject *wavetbl; wavetbl = swami_get_object_by_type (G_OBJECT (swami_object), "SwamiWavetbl"); if (wavetbl) { IPItem *parent; parent = instp_item_parent (INSTP_ITEM (zone)); if (parent) swami_wavetbl_set_gen_realtime (SWAMI_WAVETBL (wavetbl), parent, INSTP_ITEM (zone), genid, amount); } } enum { BASE, EXTENT, SUSTAIN }; /* callback for drag handle events */ static gint gengraph_cb_drag_handle_event (GnomeCanvasItem *handle, GdkEvent *event, GraphCtrl *ctrl) { GraphHandle *bag = gtk_object_get_data (GTK_OBJECT (handle), "bag"); GraphCache *cache = ctrl->gengraph->cache; IPZone *zone; IPGenAmount amt; double x, y; float val; int class; int i; if ((event->type != GDK_MOTION_NOTIFY) || !(event->motion.state & GDK_BUTTON1_MASK)) return (FALSE); zone = (ctrl->zone ? ctrl->zone : ctrl->gengraph->zone); if (!zone) return (FALSE); /* figure out what part (class) of the envelope this control is */ i = g_list_index (ctrl->handles, bag); g_return_val_if_fail (i != -1, FALSE); if (i == 0 || i == 1 || i == 5) class = BASE; else if (i == 2 || i == 3) class = EXTENT; else class = SUSTAIN; gnome_canvas_c2w (GNOME_CANVAS (ctrl->gengraph->canvas), event->motion.x, event->motion.y, &x, &y); if (bag->xgen != -1) { IPGenInfo *geninfo = &instp_gen_info[bag->xgen]; val = ((float)x - bag->xstart) / (cache->width / 5.0); val = CLAMP (val, 0.0, 1.0); amt.sword = (int)(val * (geninfo->max - geninfo->min)) + geninfo->min; swami_zone_set_gen (swami_object, zone, bag->xgen, amt); set_gen_realtime (zone, bag->xgen, amt.sword); } switch (ctrl->subtype) { case GENGRAPH_NONE: if (class == SUSTAIN) { if (ctrl->type == GENGRAPH_ENV1) set_norm_gen (norm_choord (y, cache->top, cache->bottom), zone, IPGEN_VOL_ENV_SUSTAIN); else set_norm_gen (norm_choord (y, cache->top, cache->bottom), zone, IPGEN_MOD_ENV_SUSTAIN); } break; case GENGRAPH_VOLUME: if (class == EXTENT) set_norm_gen (norm_choord (y, cache->top, cache->bottom), zone, IPGEN_ATTENUATION); else if (class == SUSTAIN) { i = get_norm_gen_val (norm_choord (y, cache->top, cache->bottom), zone, IPGEN_ATTENUATION); swami_zone_get_gen (swami_object, zone, IPGEN_ATTENUATION, &amt); i -= amt.sword; instp_units_clamp (IPGEN_VOL_ENV_SUSTAIN, &i, FALSE); amt.sword = i; swami_zone_set_gen (swami_object, zone, IPGEN_VOL_ENV_SUSTAIN, amt); set_gen_realtime (zone, IPGEN_VOL_ENV_SUSTAIN, amt.sword); } break; case GENGRAPH_PITCH: if (class == EXTENT) set_norm_gen (norm_choord (y, cache->bottom, cache->top), zone, IPGEN_MOD_ENV_TO_PITCH); else if (class == SUSTAIN) /* use y choords of point 3 and 5 */ set_norm_gen (norm_choord (y, ctrl->points->coords[7], ctrl->points->coords[11]), zone, IPGEN_MOD_ENV_SUSTAIN); break; case GENGRAPH_FILTER: if (class == BASE) set_norm_gen (norm_choord (y, cache->bottom, cache->top), zone, IPGEN_FILTER_FC); else if (class == EXTENT) { i = get_norm_gen_val (norm_choord (y, cache->bottom, cache->top), zone, IPGEN_FILTER_FC); swami_zone_get_gen (swami_object, zone, IPGEN_FILTER_FC, &amt); i -= amt.sword; instp_units_clamp (IPGEN_MOD_ENV_TO_FILTER_FC, &i, FALSE); amt.sword = i; swami_zone_set_gen (swami_object, zone, IPGEN_MOD_ENV_TO_FILTER_FC, amt); set_gen_realtime (zone, IPGEN_MOD_ENV_TO_FILTER_FC, amt.sword); } else if (class == SUSTAIN) set_norm_gen (norm_choord (y, ctrl->points->coords[7], ctrl->points->coords[11]), zone, IPGEN_MOD_ENV_SUSTAIN); break; } #if 0 if (bag->ygen != -1) { IPGenInfo *geninfo = &instp_gen_info[bag->ygen]; float norm_graph; val = ((float)y - gengraph_border) / height; val = CLAMP (val, 0.0, 1.0); /* volume envelope attenuation and sustain are inverted */ if (bag->ygen != IPGEN_ATTENUATION && bag->ygen != IPGEN_VOL_ENV_SUSTAIN) val = 1.0 - val; amt.sword = (int)(val * (geninfo->max - geninfo->min)) + geninfo->min; swami_zone_set_gen (swami_object, zone, bag->ygen, amt); } #endif swamiui_gengraph_update_control (ctrl); return (FALSE); } static void create_handle (SwamiUIGenGraph *gengraph, GnomeCanvasGroup *group, int xgen, gboolean yctrled, GtkSignalFunc callback) { GnomeCanvasItem *box = NULL; GraphHandle *h; GraphCtrl *ctrl; ctrl = gtk_object_get_data (GTK_OBJECT (group), "bag"); box = gnome_canvas_item_new (group, gnome_canvas_rect_get_type (), "fill_color", NULL, "outline_color", "black", "width_pixels", 0, NULL); gtk_signal_connect (GTK_OBJECT (box), "event", (GtkSignalFunc)highlight_drag_box, gengraph); gtk_signal_connect (GTK_OBJECT (box), "event", callback, ctrl); h = g_malloc0 (sizeof (GraphHandle)); gtk_object_set_data_full (GTK_OBJECT (box), "bag", h, (GtkDestroyNotify)g_free); h->handle_item = box; h->xgen = xgen; h->yctrled = yctrled; ctrl->handles = g_list_append (ctrl->handles, h); } static void move_drag_box (GnomeCanvasItem *item, double x, double y) { gnome_canvas_item_set (item, "x1", x - gengraph_handle_size / 2, "y1", y - gengraph_handle_size / 2, "x2", x + (gengraph_handle_size - 1) / 2, "y2", y + (gengraph_handle_size - 1) / 2, NULL); } static gint highlight_drag_box (GnomeCanvasItem *item, GdkEvent *event, gpointer data) { SwamiUIGenGraph *gengraph = (SwamiUIGenGraph *)data; GraphHandle *h; GdkCursor *cursor; switch (event->type) { case GDK_ENTER_NOTIFY: gnome_canvas_item_set (item, "fill_color", "red", NULL); break; case GDK_LEAVE_NOTIFY: if (!(event->crossing.state & GDK_BUTTON1_MASK)) gnome_canvas_item_set (item, "fill_color", NULL, NULL); // gdk_window_set_cursor (item->canvas->layout.bin_window, NULL); break; case GDK_BUTTON_PRESS: h = gtk_object_get_data (GTK_OBJECT (item), "bag"); if (h->xgen != -1 && h->yctrled) cursor = gdk_cursor_new (GDK_FLEUR); else if (h->xgen != -1) cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); else cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW); gnome_canvas_item_grab (item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->button.time); gdk_cursor_destroy (cursor); break; case GDK_BUTTON_RELEASE: gnome_canvas_item_ungrab (item, event->button.time); g_signal_handlers_block_by_func (swami_object, swamiui_gengraph_cb_zone_gen_change, gengraph); g_signal_emit_by_name (swami_object, "zone_gen_change", gengraph->zone); g_signal_handlers_unblock_by_func (swami_object, swamiui_gengraph_cb_zone_gen_change, gengraph); break; default: break; } return FALSE; }