/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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. */ #include "gdk/gdkkeysyms.h" #include "appenv.h" #include "channels_dialog.h" #include "colormaps.h" #include "color_panel.h" #include "drawable.h" #include "errors.h" #include "gdisplay.h" #include "gimage.h" #include "gimage_mask.h" #include "gimpdnd.h" #include "gimprc.h" #include "gimpui.h" #include "interface.h" #include "layers_dialogP.h" #include "lc_dialogP.h" #include "menus.h" #include "ops_buttons.h" #include "paint_funcs.h" #include "palette.h" #include "undo.h" #include "libgimp/gimpintl.h" #include "pixmaps/eye.xbm" #include "pixmaps/channel.xbm" #include "pixmaps/new.xpm" #include "pixmaps/raise.xpm" #include "pixmaps/lower.xpm" #include "pixmaps/duplicate.xpm" #include "pixmaps/delete.xpm" #include "pixmaps/toselection.xpm" #include "channel_pvt.h" #define PREVIEW_EVENT_MASK GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | \ GDK_ENTER_NOTIFY_MASK #define BUTTON_EVENT_MASK GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | \ GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK #define CHANNEL_LIST_WIDTH 200 #define CHANNEL_LIST_HEIGHT 150 #define NORMAL 0 #define SELECTED 1 #define INSENSITIVE 2 #define COMPONENT_BASE_ID 0x10000000 typedef struct _ChannelsDialog ChannelsDialog; struct _ChannelsDialog { GtkWidget *vbox; GtkWidget *channel_list; GtkWidget *preview; GtkWidget *ops_menu; GtkAccelGroup *accel_group; gint num_components; gint base_type; ChannelType components[3]; gdouble ratio; gint image_width, image_height; gint gimage_width, gimage_height; /* state information */ GimpImage *gimage; Channel *active_channel; Layer *floating_sel; GSList *channel_widgets; }; typedef struct _ChannelWidget ChannelWidget; struct _ChannelWidget { GtkWidget *eye_widget; GtkWidget *clip_widget; GtkWidget *channel_preview; GtkWidget *list_item; GtkWidget *label; GImage *gimage; Channel *channel; GdkPixmap *channel_pixmap; ChannelType type; gint ID; gint width, height; gboolean visited; GimpDropType drop_type; }; /* channels dialog widget routines */ static void channels_dialog_preview_extents (void); static void channels_dialog_set_menu_sensitivity (void); static void channels_dialog_set_channel (ChannelWidget *); static void channels_dialog_unset_channel (ChannelWidget *); static void channels_dialog_position_channel (Channel *, gint); static void channels_dialog_add_channel (Channel *); static void channels_dialog_remove_channel (ChannelWidget *); static gint channel_list_events (GtkWidget *, GdkEvent *); /* for (un)installing the menu accelarators */ static void channels_dialog_map_callback (GtkWidget *, gpointer); static void channels_dialog_unmap_callback (GtkWidget *, gpointer); /* ops buttons dnd callbacks */ static gboolean channels_dialog_drag_new_channel_callback (GtkWidget *, GdkDragContext *, gint, gint, guint); static gboolean channels_dialog_drag_duplicate_channel_callback (GtkWidget *, GdkDragContext *, gint, gint, guint); static gboolean channels_dialog_drag_channel_to_sel_callback (GtkWidget *, GdkDragContext *, gint, gint, guint); static gboolean channels_dialog_drag_delete_channel_callback (GtkWidget *, GdkDragContext *, gint, gint, guint); /* channel widget function prototypes */ static ChannelWidget *channel_widget_get_ID (Channel *); static ChannelWidget *channel_widget_create (GImage *, Channel *, ChannelType); static gboolean channel_widget_drag_motion_callback (GtkWidget *, GdkDragContext *, gint, gint, guint); static gboolean channel_widget_drag_drop_callback (GtkWidget *, GdkDragContext *, gint, gint, guint); static void channel_widget_drag_begin_callback (GtkWidget *, GdkDragContext *); static void channel_widget_drag_leave_callback (GtkWidget *, GdkDragContext *, guint); static void channel_widget_drag_indicator_callback (GtkWidget *, gpointer); static void channel_widget_drop_color (GtkWidget *, guchar, guchar, guchar, gpointer); static void channel_widget_draw_drop_indicator (ChannelWidget *, GimpDropType); static void channel_widget_delete (ChannelWidget *); static void channel_widget_select_update (GtkWidget *, gpointer); static gint channel_widget_button_events (GtkWidget *, GdkEvent *); static gint channel_widget_preview_events (GtkWidget *, GdkEvent *); static void channel_widget_preview_redraw (ChannelWidget *); static void channel_widget_no_preview_redraw (ChannelWidget *); static void channel_widget_eye_redraw (ChannelWidget *); static void channel_widget_exclusive_visible (ChannelWidget *); static void channel_widget_channel_flush (GtkWidget *, gpointer); /* assorted query dialogs */ static void channels_dialog_new_channel_query (GimpImage *); static void channels_dialog_edit_channel_query (ChannelWidget *); /****************/ /* Local data */ /****************/ static ChannelsDialog *channelsD = NULL; static GdkPixmap *eye_pixmap[] = { NULL, NULL, NULL }; static GdkPixmap *channel_pixmap[] = { NULL, NULL, NULL }; static gint suspend_gimage_notify = 0; /* the ops buttons */ static OpsButtonCallback to_selection_ext_callbacks[] = { channels_dialog_add_channel_to_sel_callback, /* SHIFT */ channels_dialog_sub_channel_from_sel_callback, /* CTRL */ channels_dialog_intersect_channel_with_sel_callback, /* MOD1 */ channels_dialog_intersect_channel_with_sel_callback, /* SHIFT + CTRL */ }; static OpsButton channels_ops_buttons[] = { { new_xpm, channels_dialog_new_channel_callback, NULL, N_("New Channel"), NULL, 0 }, { raise_xpm, channels_dialog_raise_channel_callback, NULL, N_("Raise Channel"), NULL, 0 }, { lower_xpm, channels_dialog_lower_channel_callback, NULL, N_("Lower Channel"), NULL, 0 }, { duplicate_xpm, channels_dialog_duplicate_channel_callback, NULL, N_("Duplicate Channel"), NULL, 0 }, { toselection_xpm, channels_dialog_channel_to_sel_callback, to_selection_ext_callbacks, N_("Channel to Selection \n" " Add " " Subtract " " Intersect"), NULL, 0 }, { delete_xpm, channels_dialog_delete_channel_callback, NULL, N_("Delete Channel"), NULL, 0 }, { NULL, NULL, NULL, NULL, NULL, 0 } }; /* dnd structures */ static GtkTargetEntry channel_color_target_table[] = { GIMP_TARGET_CHANNEL, GIMP_TARGET_COLOR }; static guint n_channel_color_targets = (sizeof (channel_color_target_table) / sizeof (channel_color_target_table[0])); static GtkTargetEntry channel_target_table[] = { GIMP_TARGET_CHANNEL }; static guint n_channel_targets = (sizeof (channel_target_table) / sizeof (channel_target_table[0])); static GtkTargetEntry component_target_table[] = { GIMP_TARGET_COMPONENT }; static guint n_component_targets = (sizeof (component_target_table) / sizeof (component_target_table[0])); /**************************************/ /* Public channels dialog functions */ /**************************************/ GtkWidget * channels_dialog_create () { GtkWidget *vbox; GtkWidget *listbox; GtkWidget *button_box; if (channelsD) return channelsD->vbox; channelsD = g_new (ChannelsDialog, 1); channelsD->preview = NULL; channelsD->gimage = NULL; channelsD->active_channel = NULL; channelsD->floating_sel = NULL; channelsD->channel_widgets = NULL; if (preview_size) { channelsD->preview = gtk_preview_new (GTK_PREVIEW_GRAYSCALE); gtk_preview_size (GTK_PREVIEW (channelsD->preview), preview_size, preview_size); } /* The main vbox */ channelsD->vbox = vbox = gtk_vbox_new (FALSE, 1); gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); /* The layers commands pulldown menu */ menus_get_channels_menu (&channelsD->ops_menu, &channelsD->accel_group); /* The channels listbox */ listbox = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_usize (listbox, CHANNEL_LIST_WIDTH, CHANNEL_LIST_HEIGHT); gtk_box_pack_start (GTK_BOX (vbox), listbox, TRUE, TRUE, 2); channelsD->channel_list = gtk_list_new (); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (listbox), channelsD->channel_list); gtk_list_set_selection_mode (GTK_LIST (channelsD->channel_list), GTK_SELECTION_MULTIPLE); gtk_signal_connect (GTK_OBJECT (channelsD->channel_list), "event", (GtkSignalFunc) channel_list_events, channelsD); gtk_container_set_focus_vadjustment (GTK_CONTAINER (channelsD->channel_list), gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (listbox))); GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (listbox)->vscrollbar, GTK_CAN_FOCUS); gtk_widget_show (channelsD->channel_list); gtk_widget_show (listbox); /* The ops buttons */ button_box = ops_button_box_new (lc_dialog->shell, tool_tips, channels_ops_buttons, OPS_BUTTON_NORMAL); gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 2); gtk_widget_show (button_box); /* Drop to new */ gtk_drag_dest_set (channels_ops_buttons[0].widget, GTK_DEST_DEFAULT_ALL, channel_target_table, n_channel_targets, GDK_ACTION_COPY); gtk_signal_connect (GTK_OBJECT (channels_ops_buttons[0].widget), "drag_drop", GTK_SIGNAL_FUNC (channels_dialog_drag_new_channel_callback), NULL); /* Drop to duplicate */ gtk_drag_dest_set (channels_ops_buttons[3].widget, GTK_DEST_DEFAULT_ALL, channel_target_table, n_channel_targets, GDK_ACTION_COPY); gtk_signal_connect (GTK_OBJECT (channels_ops_buttons[3].widget), "drag_drop", GTK_SIGNAL_FUNC (channels_dialog_drag_duplicate_channel_callback), NULL); /* Drop to channel to selection */ gtk_drag_dest_set (channels_ops_buttons[4].widget, GTK_DEST_DEFAULT_ALL, channel_target_table, n_channel_targets, GDK_ACTION_COPY); gtk_signal_connect (GTK_OBJECT (channels_ops_buttons[4].widget), "drag_drop", GTK_SIGNAL_FUNC (channels_dialog_drag_channel_to_sel_callback), NULL); /* Drop to trahcan */ gtk_drag_dest_set (channels_ops_buttons[5].widget, GTK_DEST_DEFAULT_ALL, channel_target_table, n_channel_targets, GDK_ACTION_COPY); gtk_signal_connect (GTK_OBJECT (channels_ops_buttons[5].widget), "drag_drop", GTK_SIGNAL_FUNC (channels_dialog_drag_delete_channel_callback), NULL); /* Set up signals for map/unmap for the accelerators */ gtk_signal_connect (GTK_OBJECT (channelsD->vbox), "map", (GtkSignalFunc) channels_dialog_map_callback, NULL); gtk_signal_connect (GTK_OBJECT (channelsD->vbox), "unmap", (GtkSignalFunc) channels_dialog_unmap_callback, NULL); gtk_widget_show (vbox); return channelsD->vbox; } void channels_dialog_free () { ChannelWidget *cw; GSList *list; if (!channelsD) return; suspend_gimage_notify++; /* Free all elements in the channels listbox */ gtk_list_clear_items (GTK_LIST (channelsD->channel_list), 0, -1); suspend_gimage_notify--; list = channelsD->channel_widgets; while (list) { cw = (ChannelWidget *) list->data; list = g_slist_next (list); channel_widget_delete (cw); } channelsD->channel_widgets = NULL; channelsD->active_channel = NULL; channelsD->floating_sel = NULL; if (channelsD->preview) gtk_object_sink (GTK_OBJECT (channelsD->preview)); g_free (channelsD); channelsD = NULL; } void channels_dialog_update (GimpImage* gimage) { ChannelWidget *cw; Channel *channel; GSList *list; GList *item_list; if (!channelsD || channelsD->gimage == gimage) return; channelsD->gimage = gimage; suspend_gimage_notify++; /* Free all elements in the channels listbox */ gtk_list_clear_items (GTK_LIST (channelsD->channel_list), 0, -1); suspend_gimage_notify--; list = channelsD->channel_widgets; while (list) { cw = (ChannelWidget *) list->data; list = g_slist_next (list); channel_widget_delete (cw); } channelsD->channel_widgets = NULL; /* Find the preview extents */ channels_dialog_preview_extents (); channelsD->active_channel = NULL; channelsD->floating_sel = NULL; /* The image components */ item_list = NULL; switch ((channelsD->base_type = gimage_base_type (gimage))) { case RGB: cw = channel_widget_create (gimage, NULL, RED_CHANNEL); channelsD->channel_widgets = g_slist_append (channelsD->channel_widgets, cw); item_list = g_list_append (item_list, cw->list_item); channelsD->components[0] = RED_CHANNEL; cw = channel_widget_create (gimage, NULL, GREEN_CHANNEL); channelsD->channel_widgets = g_slist_append (channelsD->channel_widgets, cw); item_list = g_list_append (item_list, cw->list_item); channelsD->components[1] = GREEN_CHANNEL; cw = channel_widget_create (gimage, NULL, BLUE_CHANNEL); channelsD->channel_widgets = g_slist_append (channelsD->channel_widgets, cw); item_list = g_list_append (item_list, cw->list_item); channelsD->components[2] = BLUE_CHANNEL; channelsD->num_components = 3; break; case GRAY: cw = channel_widget_create (gimage, NULL, GRAY_CHANNEL); channelsD->channel_widgets = g_slist_append (channelsD->channel_widgets, cw); item_list = g_list_append (item_list, cw->list_item); channelsD->components[0] = GRAY_CHANNEL; channelsD->num_components = 1; break; case INDEXED: cw = channel_widget_create (gimage, NULL, INDEXED_CHANNEL); channelsD->channel_widgets = g_slist_append (channelsD->channel_widgets, cw); item_list = g_list_append (item_list, cw->list_item); channelsD->components[0] = INDEXED_CHANNEL; channelsD->num_components = 1; break; } /* The auxillary image channels */ for (list = gimage->channels; list; list = g_slist_next (list)) { /* create a channel list item */ channel = (Channel *) list->data; cw = channel_widget_create (gimage, channel, AUXILLARY_CHANNEL); channelsD->channel_widgets = g_slist_append (channelsD->channel_widgets, cw); item_list = g_list_append (item_list, cw->list_item); } /* get the index of the active channel */ if (item_list) gtk_list_insert_items (GTK_LIST (channelsD->channel_list), item_list, 0); } void channels_dialog_flush () { ChannelWidget *cw; GImage *gimage; Channel *channel; GSList *list; gint pos; if (!channelsD || !(gimage = channelsD->gimage)) return; /* Check if the gimage extents have changed */ if ((gimage->width != channelsD->gimage_width) || (gimage->height != channelsD->gimage_height) || (gimage_base_type (gimage) != channelsD->base_type)) { channelsD->gimage = NULL; channels_dialog_update (gimage); return; } /* Set all current channel widgets to visited = FALSE */ for (list = channelsD->channel_widgets; list; list = g_slist_next (list)) { cw = (ChannelWidget *) list->data; cw->visited = FALSE; } /* Add any missing channels */ for (list = gimage->channels; list; list = g_slist_next (list)) { channel = (Channel *) list->data; cw = channel_widget_get_ID (channel); /* If the channel isn't in the channel widget list, add it */ if (cw == NULL) { /* sets visited = TRUE */ channels_dialog_add_channel (channel); } else cw->visited = TRUE; } /* Remove any extraneous auxillary channels */ list = channelsD->channel_widgets; while (list) { cw = (ChannelWidget *) list->data; list = g_slist_next (list); if (cw->visited == FALSE && cw->type == AUXILLARY_CHANNEL) { /* will only be true for auxillary channels */ channels_dialog_remove_channel (cw); } } /* Switch positions of items if necessary */ pos = 0; for (list = gimage->channels; list; list = g_slist_next (list)) { channel = (Channel *) list->data; channels_dialog_position_channel (channel, pos++); } /* Set the active channel */ if (channelsD->active_channel != gimage->active_channel) channelsD->active_channel = gimage->active_channel; /* set the menus if floating sel status has changed */ if (channelsD->floating_sel != gimage->floating_sel) channelsD->floating_sel = gimage->floating_sel; channels_dialog_set_menu_sensitivity (); gtk_container_foreach (GTK_CONTAINER (channelsD->channel_list), channel_widget_channel_flush, NULL); } void channels_dialog_clear () { if (!channelsD) return; ops_button_box_set_insensitive (channels_ops_buttons); suspend_gimage_notify++; gtk_list_clear_items (GTK_LIST (channelsD->channel_list), 0, -1); suspend_gimage_notify--; channelsD->gimage = NULL; } static void channels_dialog_preview_extents () { GImage *gimage; g_return_if_fail (channelsD); if (! (gimage = channelsD->gimage)) return; channelsD->gimage_width = gimage->width; channelsD->gimage_height = gimage->height; /* Get the image width and height variables, based on the gimage */ if (gimage->width > gimage->height) channelsD->ratio = (double) preview_size / (double) gimage->width; else channelsD->ratio = (double) preview_size / (double) gimage->height; if (preview_size) { channelsD->image_width = (int) (channelsD->ratio * gimage->width); channelsD->image_height = (int) (channelsD->ratio * gimage->height); if (channelsD->image_width < 1) channelsD->image_width = 1; if (channelsD->image_height < 1) channelsD->image_height = 1; } else { channelsD->image_width = channel_width; channelsD->image_height = channel_height; } } static void channels_dialog_set_menu_sensitivity () { ChannelWidget *cw; gint fs_sens; gint aux_sens; cw = channel_widget_get_ID (channelsD->active_channel); fs_sens = (channelsD->floating_sel != NULL); if (cw) aux_sens = (cw->type == AUXILLARY_CHANNEL); else aux_sens = FALSE; #define SET_SENSITIVE(menu,condition) \ menus_set_sensitive_glue ("", (menu), (condition) != 0) #define SET_OPS_SENSITIVE(button,condition) \ gtk_widget_set_sensitive (channels_ops_buttons[(button)].widget, \ (condition) != 0) SET_SENSITIVE (N_("/New Channel"), !fs_sens); SET_OPS_SENSITIVE (0, !fs_sens); SET_SENSITIVE (N_("/Raise Channel"), !fs_sens && aux_sens); SET_OPS_SENSITIVE (1, !fs_sens && aux_sens); SET_SENSITIVE (N_("/Lower Channel"), !fs_sens && aux_sens); SET_OPS_SENSITIVE (2, !fs_sens && aux_sens); SET_SENSITIVE (N_("/Duplicate Channel"), !fs_sens && aux_sens); SET_OPS_SENSITIVE (3, !fs_sens && aux_sens); SET_SENSITIVE (N_("/Channel to Selection"), aux_sens); SET_OPS_SENSITIVE (4, aux_sens); SET_SENSITIVE (N_("/Add to Selection"), aux_sens); SET_SENSITIVE (N_("/Subtract From Selection"), aux_sens); SET_SENSITIVE (N_("/Intersect With Selection"), aux_sens); SET_SENSITIVE (N_("/Delete Channel"), !fs_sens && aux_sens); SET_OPS_SENSITIVE (5, !fs_sens && aux_sens); #undef SET_OPS_SENSITIVE #undef SET_SENSITIVE } static void channels_dialog_set_channel (ChannelWidget *channel_widget) { GtkStateType state; gint index; if (!channelsD || !channel_widget) return; /* Make sure the gimage is not notified of this change */ suspend_gimage_notify++; /* get the list item data */ state = channel_widget->list_item->state; if (channel_widget->type == AUXILLARY_CHANNEL) { /* turn on the specified auxillary channel */ index = gimage_get_channel_index (channel_widget->gimage, channel_widget->channel); if ((index >= 0) && (state != GTK_STATE_SELECTED)) { gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), NULL); gtk_list_select_item (GTK_LIST (channelsD->channel_list), index + channelsD->num_components); gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), channel_widget); } } else { if (state != GTK_STATE_SELECTED) { gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), NULL); switch (channel_widget->type) { case RED_CHANNEL: case GRAY_CHANNEL: case INDEXED_CHANNEL: gtk_list_select_item (GTK_LIST (channelsD->channel_list), 0); break; case GREEN_CHANNEL: gtk_list_select_item (GTK_LIST (channelsD->channel_list), 1); break; case BLUE_CHANNEL: gtk_list_select_item (GTK_LIST (channelsD->channel_list), 2); break; case AUXILLARY_CHANNEL: g_error ("error in %s at %d: this shouldn't happen.", __FILE__, __LINE__); break; } gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), channel_widget); } } suspend_gimage_notify--; } static void channels_dialog_unset_channel (ChannelWidget *channel_widget) { GtkStateType state; gint index; if (!channelsD || !channel_widget) return; /* Make sure the gimage is not notified of this change */ suspend_gimage_notify++; /* get the list item data */ state = channel_widget->list_item->state; if (channel_widget->type == AUXILLARY_CHANNEL) { /* turn off the specified auxillary channel */ index = gimage_get_channel_index (channel_widget->gimage, channel_widget->channel); if ((index >= 0) && (state == GTK_STATE_SELECTED)) { gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), NULL); gtk_list_unselect_item (GTK_LIST (channelsD->channel_list), index + channelsD->num_components); gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), channel_widget); } } else { if (state == GTK_STATE_SELECTED) { gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), NULL); switch (channel_widget->type) { case RED_CHANNEL: case GRAY_CHANNEL: case INDEXED_CHANNEL: gtk_list_unselect_item (GTK_LIST (channelsD->channel_list), 0); break; case GREEN_CHANNEL: gtk_list_unselect_item (GTK_LIST (channelsD->channel_list), 1); break; case BLUE_CHANNEL: gtk_list_unselect_item (GTK_LIST (channelsD->channel_list), 2); break; case AUXILLARY_CHANNEL: g_error ("error in %s at %d: this shouldn't happen.", __FILE__, __LINE__); break; } gtk_object_set_user_data (GTK_OBJECT (channel_widget->list_item), channel_widget); } } suspend_gimage_notify--; } static void channels_dialog_position_channel (Channel *channel, gint new_index) { ChannelWidget *channel_widget; GList *list = NULL; channel_widget = channel_widget_get_ID (channel); if (!channelsD || !channel_widget) return; if (new_index == g_slist_index (channelsD->channel_widgets, channel_widget)) return; /* Make sure the gimage is not notified of this change */ suspend_gimage_notify++; /* Remove the channel from the dialog */ list = g_list_append (list, channel_widget->list_item); gtk_list_remove_items (GTK_LIST (channelsD->channel_list), list); channelsD->channel_widgets = g_slist_remove (channelsD->channel_widgets, channel_widget); /* Add it back at the proper index */ gtk_list_insert_items (GTK_LIST (channelsD->channel_list), list, new_index + channelsD->num_components); channelsD->channel_widgets = g_slist_insert (channelsD->channel_widgets, channel_widget, new_index + channelsD->num_components); suspend_gimage_notify--; } static void channels_dialog_add_channel (Channel *channel) { ChannelWidget *channel_widget; GImage *gimage; GList *item_list; gint position; if (!channelsD || !channel || !(gimage = channelsD->gimage)) return; item_list = NULL; channel_widget = channel_widget_create (gimage, channel, AUXILLARY_CHANNEL); item_list = g_list_append (item_list, channel_widget->list_item); position = gimage_get_channel_index (gimage, channel); channelsD->channel_widgets = g_slist_insert (channelsD->channel_widgets, channel_widget, position + channelsD->num_components); gtk_list_insert_items (GTK_LIST (channelsD->channel_list), item_list, position + channelsD->num_components); } static void channels_dialog_remove_channel (ChannelWidget *channel_widget) { GList *list = NULL; if (!channelsD || !channel_widget) return; /* Make sure the gimage is not notified of this change */ suspend_gimage_notify++; /* Remove the requested channel from the dialog */ list = g_list_append (list, channel_widget->list_item); gtk_list_remove_items (GTK_LIST (channelsD->channel_list), list); /* Delete the channel_widget */ channel_widget_delete (channel_widget); suspend_gimage_notify--; } static gint channel_list_events (GtkWidget *widget, GdkEvent *event) { ChannelWidget *channel_widget; GdkEventKey *kevent; GdkEventButton *bevent; GtkWidget *event_widget; event_widget = gtk_get_event_widget (event); if (GTK_IS_LIST_ITEM (event_widget)) { channel_widget = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (event_widget)); switch (event->type) { case GDK_2BUTTON_PRESS: if (channel_widget->type == AUXILLARY_CHANNEL) channels_dialog_edit_channel_query (channel_widget); return TRUE; case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; if (bevent->button == 3 || bevent->button == 2) { gtk_menu_popup (GTK_MENU (channelsD->ops_menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time); return TRUE; } break; case GDK_KEY_PRESS: kevent = (GdkEventKey *) event; switch (kevent->keyval) { case GDK_Up: /* g_print ("up arrow\n"); */ break; case GDK_Down: /* g_print ("down arrow\n"); */ break; default: return FALSE; } return FALSE; default: break; } } return FALSE; } /*******************************/ /* channels dialog callbacks */ /*******************************/ static void channels_dialog_map_callback (GtkWidget *widget, gpointer data) { if (! channelsD) return; gtk_window_add_accel_group (GTK_WINDOW (lc_dialog->shell), channelsD->accel_group); } static void channels_dialog_unmap_callback (GtkWidget *widget, gpointer data) { if (! channelsD) return; gtk_window_remove_accel_group (GTK_WINDOW (lc_dialog->shell), channelsD->accel_group); } void channels_dialog_new_channel_callback (GtkWidget *widget, gpointer data) { if (!channelsD || channelsD->gimage == NULL) return; channels_dialog_new_channel_query (channelsD->gimage); } void channels_dialog_raise_channel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; if (!channelsD || !(gimage = channelsD->gimage)) return; if (gimage->active_channel != NULL) { gimage_raise_channel (gimage, gimage->active_channel); gdisplays_flush (); } } void channels_dialog_lower_channel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; if (!channelsD || !(gimage = channelsD->gimage)) return; if (gimage->active_channel != NULL) { gimage_lower_channel (gimage, gimage->active_channel); gdisplays_flush (); } } void channels_dialog_duplicate_channel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; Channel *active_channel; Channel *new_channel; if (!channelsD || !(gimage = channelsD->gimage)) return; if ((active_channel = gimage_get_active_channel (gimage))) { new_channel = channel_copy (active_channel); gimage_add_channel (gimage, new_channel, -1); gdisplays_flush (); } } void channels_dialog_delete_channel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; if (!channelsD || !(gimage = channelsD->gimage)) return; if (gimage->active_channel != NULL) { gimage_remove_channel (gimage, gimage->active_channel); gdisplays_flush (); } } void channels_dialog_channel_to_sel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; if (!channelsD || !(gimage = channelsD->gimage)) return; if (gimage->active_channel != NULL) { gimage_mask_load (gimage, gimage->active_channel); gdisplays_flush (); } } void channels_dialog_add_channel_to_sel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; Channel *active_channel; Channel *new_channel; if (!channelsD || !(gimage = channelsD->gimage)) return; if ((active_channel = gimage_get_active_channel (gimage))) { new_channel = channel_copy (gimage_get_mask (gimage)); channel_combine_mask (new_channel, active_channel, ADD, 0, 0); /* off x/y */ gimage_mask_load (gimage, new_channel); channel_delete (new_channel); gdisplays_flush (); } } void channels_dialog_sub_channel_from_sel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; Channel *active_channel; Channel *new_channel; if (!channelsD || !(gimage = channelsD->gimage)) return; if ((active_channel = gimage_get_active_channel (gimage))) { new_channel = channel_copy (gimage_get_mask (gimage)); channel_combine_mask (new_channel, active_channel, SUB, 0, 0); /* off x/y */ gimage_mask_load (gimage, new_channel); channel_delete (new_channel); gdisplays_flush (); } } void channels_dialog_intersect_channel_with_sel_callback (GtkWidget *widget, gpointer data) { GImage *gimage; Channel *active_channel; Channel *new_channel; if (!channelsD || !(gimage = channelsD->gimage)) return; if ((active_channel = gimage_get_active_channel (gimage))) { new_channel = channel_copy (gimage_get_mask (gimage)); channel_combine_mask (new_channel, active_channel, INTERSECT, 0, 0); /* off x/y */ gimage_mask_load (gimage, new_channel); channel_delete (new_channel); gdisplays_flush (); } } /*******************************/ /* ops buttons dnd callbacks */ /*******************************/ static gboolean channels_dialog_drag_new_channel_callback (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { GtkWidget *src_widget; gboolean return_val = FALSE; if ((src_widget = gtk_drag_get_source_widget (context))) { ChannelWidget *src; src = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (src_widget)); if (src && src->channel == channelsD->active_channel) { Channel *channel; GImage *gimage; gint width, height; gint off_x, off_y; gimage = channelsD->gimage; width = gimp_drawable_width (GIMP_DRAWABLE (src->channel)); height = gimp_drawable_height (GIMP_DRAWABLE (src->channel)); gimp_drawable_offsets (GIMP_DRAWABLE (src->channel), &off_x, &off_y); /* Start a group undo */ undo_push_group_start (gimage, EDIT_PASTE_UNDO); channel = channel_new (gimage, width, height, _("Empty Channel Copy"), src->channel->opacity, src->channel->col); if (channel) { drawable_fill (GIMP_DRAWABLE (channel), TRANSPARENT_FILL); channel_translate (channel, off_x, off_y); gimage_add_channel (gimage, channel, -1); /* End the group undo */ undo_push_group_end (gimage); gdisplays_flush (); } else { g_message ("channels_dialog_drop_new_channel_callback():\n" "could not allocate new channel"); } return_val = TRUE; } } gtk_drag_finish (context, return_val, FALSE, time); return return_val; } static gboolean channels_dialog_drag_duplicate_channel_callback (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { GtkWidget *src_widget; gboolean return_val = FALSE; if ((src_widget = gtk_drag_get_source_widget (context))) { ChannelWidget *src; src = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (src_widget)); if (src && src->channel == channelsD->active_channel) { channels_dialog_duplicate_channel_callback (widget, NULL); return_val = TRUE; } } gtk_drag_finish (context, return_val, FALSE, time); return return_val; } static gboolean channels_dialog_drag_channel_to_sel_callback (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { GtkWidget *src_widget; gboolean return_val = FALSE; if ((src_widget = gtk_drag_get_source_widget (context))) { ChannelWidget *src; src = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (src_widget)); if (src && src->channel == channelsD->active_channel) { channels_dialog_channel_to_sel_callback (widget, NULL); return_val = TRUE; } } gtk_drag_finish (context, return_val, FALSE, time); return return_val; } static gboolean channels_dialog_drag_delete_channel_callback (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { GtkWidget *src_widget; gboolean return_val = FALSE; if ((src_widget = gtk_drag_get_source_widget (context))) { ChannelWidget *src; src = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (src_widget)); if (src && src->channel == channelsD->active_channel) { channels_dialog_delete_channel_callback (widget, NULL); return_val = TRUE; } } gtk_drag_finish (context, return_val, FALSE, time); return return_val; } /******************************/ /* channel widget functions */ /******************************/ static ChannelWidget * channel_widget_get_ID (Channel *channel) { ChannelWidget *lw; GSList *list; if (!channelsD) return NULL; list = channelsD->channel_widgets; while (list) { lw = (ChannelWidget *) list->data; if (lw->channel == channel) return lw; list = g_slist_next (list); } return NULL; } static ChannelWidget * channel_widget_create (GImage *gimage, Channel *channel, ChannelType type) { ChannelWidget *channel_widget; GtkWidget *list_item; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *alignment; list_item = gtk_list_item_new (); /* Create the channel widget and add it to the list */ channel_widget = g_new (ChannelWidget, 1); channel_widget->gimage = gimage; channel_widget->channel = channel; channel_widget->channel_preview = NULL; channel_widget->channel_pixmap = NULL; channel_widget->type = type; channel_widget->ID = ((type == AUXILLARY_CHANNEL) ? GIMP_DRAWABLE (channel)->ID : (COMPONENT_BASE_ID + type)); channel_widget->list_item = list_item; channel_widget->width = -1; channel_widget->height = -1; channel_widget->visited = TRUE; channel_widget->drop_type = GIMP_DROP_NONE; /* Need to let the list item know about the channel_widget */ gtk_object_set_user_data (GTK_OBJECT (list_item), channel_widget); /* Set up the list item observer */ gtk_signal_connect (GTK_OBJECT (list_item), "select", (GtkSignalFunc) channel_widget_select_update, channel_widget); gtk_signal_connect (GTK_OBJECT (list_item), "deselect", (GtkSignalFunc) channel_widget_select_update, channel_widget); vbox = gtk_vbox_new (FALSE, 1); gtk_container_add (GTK_CONTAINER (list_item), vbox); hbox = gtk_hbox_new (FALSE, 1); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 1); /* Create the visibility toggle button */ alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); gtk_box_pack_start (GTK_BOX (hbox), alignment, FALSE, TRUE, 2); channel_widget->eye_widget = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (channel_widget->eye_widget), eye_width, eye_height); gtk_widget_set_events (channel_widget->eye_widget, BUTTON_EVENT_MASK); gtk_signal_connect (GTK_OBJECT (channel_widget->eye_widget), "event", (GtkSignalFunc) channel_widget_button_events, channel_widget); gtk_object_set_user_data (GTK_OBJECT (channel_widget->eye_widget), channel_widget); gtk_container_add (GTK_CONTAINER (alignment), channel_widget->eye_widget); gtk_widget_show (channel_widget->eye_widget); gtk_widget_show (alignment); /* The preview */ alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); gtk_box_pack_start (GTK_BOX (hbox), alignment, FALSE, FALSE, 2); gtk_widget_show (alignment); channel_widget->channel_preview = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (channel_widget->channel_preview), channelsD->image_width, channelsD->image_height); gtk_widget_set_events (channel_widget->channel_preview, PREVIEW_EVENT_MASK); gtk_signal_connect (GTK_OBJECT (channel_widget->channel_preview), "event", (GtkSignalFunc) channel_widget_preview_events, channel_widget); gtk_object_set_user_data (GTK_OBJECT (channel_widget->channel_preview), channel_widget); gtk_container_add (GTK_CONTAINER (alignment), channel_widget->channel_preview); gtk_widget_show (channel_widget->channel_preview); /* The channel name label */ switch (channel_widget->type) { case RED_CHANNEL: channel_widget->label = gtk_label_new (_("Red")); break; case GREEN_CHANNEL: channel_widget->label = gtk_label_new (_("Green")); break; case BLUE_CHANNEL: channel_widget->label = gtk_label_new (_("Blue")); break; case GRAY_CHANNEL: channel_widget->label = gtk_label_new (_("Gray")); break; case INDEXED_CHANNEL: channel_widget->label = gtk_label_new (_("Indexed")); break; case AUXILLARY_CHANNEL: channel_widget->label = gtk_label_new (channel_get_name (channel)); break; } gtk_box_pack_start (GTK_BOX (hbox), channel_widget->label, FALSE, FALSE, 2); gtk_widget_show (channel_widget->label); if (channel_widget->type == AUXILLARY_CHANNEL) { /* dnd destination */ gtk_drag_dest_set (list_item, GTK_DEST_DEFAULT_ALL, channel_color_target_table, n_channel_color_targets, GDK_ACTION_MOVE | GDK_ACTION_COPY); gimp_dnd_color_dest_set (list_item, channel_widget_drop_color, (gpointer) channel_widget); gtk_signal_connect (GTK_OBJECT (list_item), "drag_leave", GTK_SIGNAL_FUNC (channel_widget_drag_leave_callback), NULL); gtk_signal_connect (GTK_OBJECT (list_item), "drag_motion", GTK_SIGNAL_FUNC (channel_widget_drag_motion_callback), NULL); gtk_signal_connect (GTK_OBJECT (list_item), "drag_drop", GTK_SIGNAL_FUNC (channel_widget_drag_drop_callback), NULL); /* re-paint the drop indicator after drawing the widget */ gtk_signal_connect_after (GTK_OBJECT (list_item), "draw", GTK_SIGNAL_FUNC (channel_widget_drag_indicator_callback), channel_widget); /* dnd source */ gtk_drag_source_set (list_item, GDK_BUTTON1_MASK, channel_target_table, n_channel_targets, GDK_ACTION_MOVE | GDK_ACTION_COPY); gtk_signal_connect (GTK_OBJECT (list_item), "drag_begin", GTK_SIGNAL_FUNC (channel_widget_drag_begin_callback), NULL); gtk_object_set_data (GTK_OBJECT (list_item), "gimp_channel", (gpointer) channel); } else { /* dnd source */ gtk_drag_source_set (list_item, GDK_BUTTON1_MASK, component_target_table, n_component_targets, GDK_ACTION_MOVE | GDK_ACTION_COPY); gtk_object_set_data (GTK_OBJECT (list_item), "gimp_component", (gpointer) gimage); gtk_object_set_data (GTK_OBJECT (list_item), "gimp_component_type", (gpointer) channel_widget->type); } gtk_widget_show (hbox); gtk_widget_show (vbox); gtk_widget_show (list_item); gtk_widget_ref (channel_widget->list_item); return channel_widget; } static gboolean channel_widget_drag_motion_callback (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { ChannelWidget *dest; gint dest_index; GtkWidget *src_widget; ChannelWidget *src; gint src_index; gint difference; GimpDropType drop_type = GIMP_DROP_NONE; GdkDragAction drag_action = GDK_ACTION_DEFAULT; gboolean return_val = FALSE; dest = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); if (dest && (src_widget = gtk_drag_get_source_widget (context))) { src = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (src_widget)); if (src && src->channel == channelsD->active_channel) { src_index = gimage_get_channel_index (channelsD->gimage, src->channel); dest_index = gimage_get_channel_index (channelsD->gimage, dest->channel); difference = dest_index - src_index; drop_type = ((y < widget->allocation.height / 2) ? GIMP_DROP_ABOVE : GIMP_DROP_BELOW); if (difference < 0 && drop_type == GIMP_DROP_BELOW) { dest_index++; } else if (difference > 0 && drop_type == GIMP_DROP_ABOVE) { dest_index--; } if (src_index != dest_index) { drag_action = GDK_ACTION_MOVE; return_val = TRUE; } else { drop_type = GIMP_DROP_NONE; } } else if (gtk_object_get_data (GTK_OBJECT (src_widget), "gimp_dnd_get_color_func")) { drag_action = GDK_ACTION_COPY; return_val = TRUE; drop_type = GIMP_DROP_NONE; } } gdk_drag_status (context, drag_action, time); if (drop_type != dest->drop_type) { channel_widget_draw_drop_indicator (dest, dest->drop_type); channel_widget_draw_drop_indicator (dest, drop_type); dest->drop_type = drop_type; } return return_val; } static void channel_widget_drag_begin_callback (GtkWidget *widget, GdkDragContext *context) { ChannelWidget *channel_widget; channel_widget = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); gimp_dnd_set_drawable_preview_icon (widget, context, GIMP_DRAWABLE (channel_widget->channel), channel_widget->channel_preview->style->black_gc); } static gboolean channel_widget_drag_drop_callback (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { ChannelWidget *dest; gint dest_index; GtkWidget *src_widget; ChannelWidget *src; gint src_index; gint difference; GimpDropType drop_type = GIMP_DROP_NONE; gboolean return_val = FALSE; dest = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); if (dest && (src_widget = gtk_drag_get_source_widget (context))) { src = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (src_widget)); if (src && src->channel == channelsD->active_channel) { src_index = gimage_get_channel_index (channelsD->gimage, src->channel); dest_index = gimage_get_channel_index (channelsD->gimage, dest->channel); difference = dest_index - src_index; drop_type = ((y < widget->allocation.height / 2) ? GIMP_DROP_ABOVE : GIMP_DROP_BELOW); if (difference < 0 && drop_type == GIMP_DROP_BELOW) { dest_index++; } else if (difference > 0 && drop_type == GIMP_DROP_ABOVE) { dest_index--; } if (src_index != dest_index) { gimage_position_channel (channelsD->gimage, src->channel, dest_index); gdisplays_flush (); return_val = TRUE; } } } channel_widget_draw_drop_indicator (dest, dest->drop_type); dest->drop_type = GIMP_DROP_NONE; gtk_drag_finish (context, return_val, FALSE, time); return return_val; } static void channel_widget_drag_leave_callback (GtkWidget *widget, GdkDragContext *context, guint time) { ChannelWidget *channel_widget; channel_widget = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); channel_widget->drop_type = GIMP_DROP_NONE; } static void channel_widget_drag_indicator_callback (GtkWidget *widget, gpointer data) { ChannelWidget *channel_widget; channel_widget = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); channel_widget_draw_drop_indicator (channel_widget, channel_widget->drop_type); } static void channel_widget_drop_color (GtkWidget *widget, guchar r, guchar g, guchar b, gpointer data) { ChannelWidget *channel_widget = (ChannelWidget *) data; Channel *channel = channel_widget->channel; if (r != channel->col[0] || g != channel->col[1] || b != channel->col[2]) { channel->col[0] = r; channel->col[1] = g; channel->col[2] = b; drawable_update (GIMP_DRAWABLE (channel), 0, 0, GIMP_DRAWABLE (channel)->width, GIMP_DRAWABLE (channel)->height); gdisplays_flush (); } } static void channel_widget_draw_drop_indicator (ChannelWidget *channel_widget, GimpDropType drop_type) { static GdkGC *gc = NULL; gint y = 0; if (!gc) { GdkColor fg, bg; gc = gdk_gc_new (channel_widget->list_item->window); fg.pixel = 0xFFFFFFFF; bg.pixel = 0x00000000; gdk_gc_set_function (gc, GDK_INVERT); gdk_gc_set_foreground (gc, &fg); gdk_gc_set_background (gc, &bg); gdk_gc_set_line_attributes (gc, 5, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); } if (drop_type != GIMP_DROP_NONE) { y = ((drop_type == GIMP_DROP_ABOVE) ? 3 : channel_widget->list_item->allocation.height - 4); gdk_draw_line (channel_widget->list_item->window, gc, 2, y, channel_widget->list_item->allocation.width - 3, y); } } static void channel_widget_delete (ChannelWidget *channel_widget) { if (channel_widget->channel_pixmap) gdk_pixmap_unref (channel_widget->channel_pixmap); /* Remove the channel widget from the list */ channelsD->channel_widgets = g_slist_remove (channelsD->channel_widgets, channel_widget); /* Release the widget */ gtk_widget_unref (channel_widget->list_item); g_free (channel_widget); } static void channel_widget_select_update (GtkWidget *widget, gpointer data) { ChannelWidget *channel_widget; if ((channel_widget = (ChannelWidget *) data) == NULL) return; if (suspend_gimage_notify == 0) { if (channel_widget->type == AUXILLARY_CHANNEL) { if (widget->state == GTK_STATE_SELECTED) /* set the gimage's active channel to be this channel */ gimage_set_active_channel (channel_widget->gimage, channel_widget->channel); else /* unset the gimage's active channel */ gimage_unset_active_channel (channel_widget->gimage); gdisplays_flush (); } else if (channel_widget->type != AUXILLARY_CHANNEL) { if (widget->state == GTK_STATE_SELECTED) gimage_set_component_active (channel_widget->gimage, channel_widget->type, TRUE); else gimage_set_component_active (channel_widget->gimage, channel_widget->type, FALSE); } } } static gint channel_widget_button_events (GtkWidget *widget, GdkEvent *event) { ChannelWidget *channel_widget; GtkWidget *event_widget; GdkEventButton *bevent; gint return_val; gint visible; gint width, height; static gboolean button_down = FALSE; static GtkWidget *click_widget = NULL; static gint old_state; static gint exclusive; channel_widget = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); switch (channel_widget->type) { case AUXILLARY_CHANNEL: visible = GIMP_DRAWABLE (channel_widget->channel)->visible; width = GIMP_DRAWABLE (channel_widget->channel)->width; height = GIMP_DRAWABLE (channel_widget->channel)->height; break; default: visible = gimage_get_component_visible (channel_widget->gimage, channel_widget->type); width = channel_widget->gimage->width; height = channel_widget->gimage->height; break; } return_val = FALSE; switch (event->type) { case GDK_EXPOSE: if (widget == channel_widget->eye_widget) channel_widget_eye_redraw (channel_widget); break; case GDK_BUTTON_PRESS: return_val = TRUE; bevent = (GdkEventButton *) event; if (bevent->button == 3) { gtk_menu_popup (GTK_MENU (channelsD->ops_menu), NULL, NULL, NULL, NULL, 3, bevent->time); return TRUE; } button_down = TRUE; click_widget = widget; gtk_grab_add (click_widget); if (widget == channel_widget->eye_widget) { old_state = visible; /* If this was a shift-click, make all/none visible */ if (event->button.state & GDK_SHIFT_MASK) { exclusive = TRUE; channel_widget_exclusive_visible (channel_widget); } else { exclusive = FALSE; if (channel_widget->type == AUXILLARY_CHANNEL) GIMP_DRAWABLE (channel_widget->channel)->visible = !visible; else gimage_set_component_visible (channel_widget->gimage, channel_widget->type, !visible); channel_widget_eye_redraw (channel_widget); } } break; case GDK_BUTTON_RELEASE: return_val = TRUE; button_down = FALSE; gtk_grab_remove (click_widget); if (widget == channel_widget->eye_widget) { if (exclusive) { gdisplays_update_area (channel_widget->gimage, 0, 0, width, height); gdisplays_flush (); } else if (old_state != visible) { gdisplays_update_area (channel_widget->gimage, 0, 0, width, height); gdisplays_flush (); } } break; case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: event_widget = gtk_get_event_widget (event); if (button_down && (event_widget == click_widget)) { if (widget == channel_widget->eye_widget) { if (exclusive) { channel_widget_exclusive_visible (channel_widget); } else { if (channel_widget->type == AUXILLARY_CHANNEL) GIMP_DRAWABLE (channel_widget->channel)->visible = !visible; else gimage_set_component_visible (channel_widget->gimage, channel_widget->type, !visible); channel_widget_eye_redraw (channel_widget); } } } break; default: break; } return return_val; } static gint channel_widget_preview_events (GtkWidget *widget, GdkEvent *event) { ChannelWidget *channel_widget; GdkEventExpose *eevent; GdkEventButton *bevent; gboolean valid; channel_widget = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); switch (event->type) { case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; if (bevent->button == 3) { gtk_menu_popup (GTK_MENU (channelsD->ops_menu), NULL, NULL, NULL, NULL, 3, bevent->time); return TRUE; } break; case GDK_EXPOSE: if (!preview_size) channel_widget_no_preview_redraw (channel_widget); else { switch (channel_widget->type) { case AUXILLARY_CHANNEL: valid = GIMP_DRAWABLE (channel_widget->channel)->preview_valid; break; default: valid = gimage_preview_valid (channel_widget->gimage, channel_widget->type); break; } if (!valid || !channel_widget->channel_pixmap) { channel_widget_preview_redraw (channel_widget); gdk_draw_pixmap (widget->window, widget->style->black_gc, channel_widget->channel_pixmap, 0, 0, 0, 0, channelsD->image_width, channelsD->image_height); } else { eevent = (GdkEventExpose *) event; gdk_draw_pixmap (widget->window, widget->style->black_gc, channel_widget->channel_pixmap, eevent->area.x, eevent->area.y, eevent->area.x, eevent->area.y, eevent->area.width, eevent->area.height); } } break; default: break; } return FALSE; } static void channel_widget_preview_redraw (ChannelWidget *channel_widget) { TempBuf * preview_buf; gint width, height; gint channel; /* allocate the channel widget pixmap */ if (! channel_widget->channel_pixmap) channel_widget->channel_pixmap = gdk_pixmap_new (channel_widget->channel_preview->window, channelsD->image_width, channelsD->image_height, -1); /* determine width and height */ switch (channel_widget->type) { case AUXILLARY_CHANNEL: width = GIMP_DRAWABLE (channel_widget->channel)->width; height = GIMP_DRAWABLE (channel_widget->channel)->height; channel_widget->width = (int) (channelsD->ratio * width); channel_widget->height = (int) (channelsD->ratio * height); preview_buf = channel_preview (channel_widget->channel, channel_widget->width, channel_widget->height); break; default: width = channel_widget->gimage->width; height = channel_widget->gimage->height; channel_widget->width = (int) (channelsD->ratio * width); channel_widget->height = (int) (channelsD->ratio * height); preview_buf = gimage_composite_preview (channel_widget->gimage, channel_widget->type, channel_widget->width, channel_widget->height); break; } switch (channel_widget->type) { case RED_CHANNEL: channel = RED_PIX; break; case GREEN_CHANNEL: channel = GREEN_PIX; break; case BLUE_CHANNEL: channel = BLUE_PIX; break; case GRAY_CHANNEL: channel = GRAY_PIX; break; case INDEXED_CHANNEL: channel = INDEXED_PIX; break; case AUXILLARY_CHANNEL: channel = -1; break; default: channel = -1; break; } render_preview (preview_buf, channelsD->preview, channelsD->image_width, channelsD->image_height, channel); gtk_preview_put (GTK_PREVIEW (channelsD->preview), channel_widget->channel_pixmap, channel_widget->channel_preview->style->black_gc, 0, 0, 0, 0, channelsD->image_width, channelsD->image_height); /* make sure the image has been transfered completely to the pixmap before * we use it again... */ gdk_flush (); } static void channel_widget_no_preview_redraw (ChannelWidget *channel_widget) { GdkPixmap *pixmap; GdkPixmap **pixmap_normal; GdkPixmap **pixmap_selected; GdkPixmap **pixmap_insensitive; GdkColor *color; GtkWidget *widget; GtkStateType state; gchar *bits; int width, height; state = channel_widget->list_item->state; widget = channel_widget->channel_preview; pixmap_normal = &channel_pixmap[NORMAL]; pixmap_selected = &channel_pixmap[SELECTED]; pixmap_insensitive = &channel_pixmap[INSENSITIVE]; bits = (gchar *) channel_bits; width = channel_width; height = channel_height; if (GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) { if (state == GTK_STATE_SELECTED) color = &widget->style->bg[GTK_STATE_SELECTED]; else color = &widget->style->white; } else color = &widget->style->bg[GTK_STATE_INSENSITIVE]; gdk_window_set_background (widget->window, color); if (!*pixmap_normal) { *pixmap_normal = gdk_pixmap_create_from_data (widget->window, bits, width, height, -1, &widget->style->fg[GTK_STATE_SELECTED], &widget->style->bg[GTK_STATE_SELECTED]); *pixmap_selected = gdk_pixmap_create_from_data (widget->window, bits, width, height, -1, &widget->style->fg[GTK_STATE_NORMAL], &widget->style->white); *pixmap_insensitive = gdk_pixmap_create_from_data (widget->window, bits, width, height, -1, &widget->style->fg[GTK_STATE_INSENSITIVE], &widget->style->bg[GTK_STATE_INSENSITIVE]); } if (GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) { if (state == GTK_STATE_SELECTED) pixmap = *pixmap_selected; else pixmap = *pixmap_normal; } else pixmap = *pixmap_insensitive; gdk_draw_pixmap (widget->window, widget->style->black_gc, pixmap, 0, 0, 0, 0, width, height); } static void channel_widget_eye_redraw (ChannelWidget *channel_widget) { GdkPixmap *pixmap; GdkColor *color; GtkStateType state; gboolean visible; state = channel_widget->list_item->state; if (GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) { if (state == GTK_STATE_SELECTED) color = &channel_widget->eye_widget->style->bg[GTK_STATE_SELECTED]; else color = &channel_widget->eye_widget->style->white; } else color = &channel_widget->eye_widget->style->bg[GTK_STATE_INSENSITIVE]; gdk_window_set_background (channel_widget->eye_widget->window, color); switch (channel_widget->type) { case AUXILLARY_CHANNEL: visible = GIMP_DRAWABLE (channel_widget->channel)->visible; break; default: visible = gimage_get_component_visible (channel_widget->gimage, channel_widget->type); break; } if (visible) { if (!eye_pixmap[NORMAL]) { eye_pixmap[NORMAL] = gdk_pixmap_create_from_data (channel_widget->eye_widget->window, (gchar*) eye_bits, eye_width, eye_height, -1, &channel_widget->eye_widget->style->fg[GTK_STATE_NORMAL], &channel_widget->eye_widget->style->white); eye_pixmap[SELECTED] = gdk_pixmap_create_from_data (channel_widget->eye_widget->window, (gchar*) eye_bits, eye_width, eye_height, -1, &channel_widget->eye_widget->style->fg[GTK_STATE_SELECTED], &channel_widget->eye_widget->style->bg[GTK_STATE_SELECTED]); eye_pixmap[INSENSITIVE] = gdk_pixmap_create_from_data (channel_widget->eye_widget->window, (gchar*) eye_bits, eye_width, eye_height, -1, &channel_widget->eye_widget->style->fg[GTK_STATE_INSENSITIVE], &channel_widget->eye_widget->style->bg[GTK_STATE_INSENSITIVE]); } if (GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) { if (state == GTK_STATE_SELECTED) pixmap = eye_pixmap[SELECTED]; else pixmap = eye_pixmap[NORMAL]; } else pixmap = eye_pixmap[INSENSITIVE]; gdk_draw_pixmap (channel_widget->eye_widget->window, channel_widget->eye_widget->style->black_gc, pixmap, 0, 0, 0, 0, eye_width, eye_height); } else { gdk_window_clear (channel_widget->eye_widget->window); } } static void channel_widget_exclusive_visible (ChannelWidget *channel_widget) { GSList *list; ChannelWidget *cw; gboolean visible = FALSE; if (!channelsD) return; /* First determine if _any_ other channel widgets are set to visible */ list = channelsD->channel_widgets; while (list) { cw = (ChannelWidget *) list->data; if (cw != channel_widget) { switch (cw->type) { case AUXILLARY_CHANNEL: visible |= GIMP_DRAWABLE (cw->channel)->visible; break; default: visible |= gimage_get_component_visible (cw->gimage, cw->type); break; } } list = g_slist_next (list); } /* Now, toggle the visibility for all channels except the specified one */ list = channelsD->channel_widgets; while (list) { cw = (ChannelWidget *) list->data; if (cw != channel_widget) switch (cw->type) { case AUXILLARY_CHANNEL: GIMP_DRAWABLE (cw->channel)->visible = !visible; break; default: gimage_set_component_visible (cw->gimage, cw->type, !visible); break; } else switch (cw->type) { case AUXILLARY_CHANNEL: GIMP_DRAWABLE (cw->channel)->visible = TRUE; break; default: gimage_set_component_visible (cw->gimage, cw->type, TRUE); break; } channel_widget_eye_redraw (cw); list = g_slist_next (list); } } static void channel_widget_channel_flush (GtkWidget *widget, gpointer data) { ChannelWidget *channel_widget; int update_preview; channel_widget = (ChannelWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); /*** Sensitivity ***/ /* If there is a floating selection... */ if (channelsD->floating_sel != NULL) { /* to insensitive if this is an auxillary channel */ if (channel_widget->type == AUXILLARY_CHANNEL) { if (GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) gtk_widget_set_sensitive (channel_widget->list_item, FALSE); } /* to sensitive otherwise */ else { if (! GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) gtk_widget_set_sensitive (channel_widget->list_item, TRUE); } } else { /* to insensitive if there is an active channel, and this is a component channel */ if (channel_widget->type != AUXILLARY_CHANNEL && channelsD->active_channel != NULL) { if (GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) gtk_widget_set_sensitive (channel_widget->list_item, FALSE); } /* to sensitive otherwise */ else { if (! GTK_WIDGET_IS_SENSITIVE (channel_widget->list_item)) gtk_widget_set_sensitive (channel_widget->list_item, TRUE); } } /*** Selection ***/ /* If this is an auxillary channel */ if (channel_widget->type == AUXILLARY_CHANNEL) { /* select if this is the active channel */ if (channelsD->active_channel == (channel_widget->channel)) channels_dialog_set_channel (channel_widget); /* unselect if this is not the active channel */ else channels_dialog_unset_channel (channel_widget); } else { /* If the component is active, select. otherwise, deselect */ if (gimage_get_component_active (channel_widget->gimage, channel_widget->type)) channels_dialog_set_channel (channel_widget); else channels_dialog_unset_channel (channel_widget); } switch (channel_widget->type) { case AUXILLARY_CHANNEL: update_preview = !GIMP_DRAWABLE (channel_widget->channel)->preview_valid; break; default: update_preview = !gimage_preview_valid (channel_widget->gimage, channel_widget->type); break; } if (update_preview) gtk_widget_draw (channel_widget->channel_preview, NULL); } /**********************************/ /* The new channel query dialog */ /**********************************/ typedef struct _NewChannelOptions NewChannelOptions; struct _NewChannelOptions { GtkWidget *query_box; GtkWidget *name_entry; ColorPanel *color_panel; GimpImage *gimage; gdouble opacity; }; static gchar *channel_name = NULL; static guchar channel_color[3] = { 0, 0, 0 }; static void new_channel_query_ok_callback (GtkWidget *widget, gpointer data) { NewChannelOptions *options; Channel *new_channel; GImage *gimage; gint i; options = (NewChannelOptions *) data; if (channel_name) g_free (channel_name); channel_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (options->name_entry))); if ((gimage = options->gimage)) { new_channel = channel_new (gimage, gimage->width, gimage->height, channel_name, (gint) (255 * options->opacity) / 100, options->color_panel->color); drawable_fill (GIMP_DRAWABLE (new_channel), TRANSPARENT_FILL); for (i = 0; i < 3; i++) channel_color[i] = options->color_panel->color[i]; gimage_add_channel (gimage, new_channel, -1); gdisplays_flush (); } color_panel_free (options->color_panel); gtk_widget_destroy (options->query_box); g_free (options); } static void new_channel_query_cancel_callback (GtkWidget *widget, gpointer data) { NewChannelOptions *options; options = (NewChannelOptions *) data; color_panel_free (options->color_panel); gtk_widget_destroy (options->query_box); g_free (options); } static void new_channel_query_scale_update (GtkAdjustment *adjustment, gdouble *scale_val) { *scale_val = adjustment->value; } static void channels_dialog_new_channel_query (GimpImage* gimage) { NewChannelOptions *options; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *table; GtkWidget *label; GtkWidget *opacity_scale; GtkObject *opacity_scale_data; /* the new options structure */ options = g_new (NewChannelOptions, 1); options->gimage = gimage; options->opacity = 50.0; options->color_panel = color_panel_new (channel_color, 48, 64); /* The dialog */ options->query_box = gimp_dialog_new (_("New Channel Options"), "new_channel_options", gimp_standard_help_func, "dialogs/channels_dialog.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, _("OK"), new_channel_query_ok_callback, options, NULL, TRUE, FALSE, _("Cancel"), new_channel_query_cancel_callback, options, NULL, FALSE, TRUE, NULL); /* The main hbox */ hbox = gtk_hbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (options->query_box)->vbox), hbox); /* The vbox */ vbox = gtk_vbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); /* The table */ table = gtk_table_new (2, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); /* The name entry hbox, label and entry */ label = gtk_label_new (_("Channel name:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); gtk_widget_show (label); options->name_entry = gtk_entry_new (); gtk_widget_set_usize (options->name_entry, 150, 0); gtk_table_attach_defaults (GTK_TABLE (table), options->name_entry, 1, 2, 0, 1); gtk_entry_set_text (GTK_ENTRY (options->name_entry), (channel_name ? channel_name : _("New Channel"))); gtk_widget_show (options->name_entry); /* The opacity scale */ label = gtk_label_new (_("Fill Opacity:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); gtk_widget_show (label); opacity_scale_data = gtk_adjustment_new (options->opacity, 0.0, 100.0, 1.0, 1.0, 0.0); opacity_scale = gtk_hscale_new (GTK_ADJUSTMENT (opacity_scale_data)); gtk_table_attach_defaults (GTK_TABLE (table), opacity_scale, 1, 2, 1, 2); gtk_scale_set_value_pos (GTK_SCALE (opacity_scale), GTK_POS_TOP); gtk_range_set_update_policy (GTK_RANGE (opacity_scale), GTK_UPDATE_DELAYED); gtk_signal_connect (GTK_OBJECT (opacity_scale_data), "value_changed", (GtkSignalFunc) new_channel_query_scale_update, &options->opacity); gtk_widget_show (opacity_scale); /* The color panel */ gtk_box_pack_start (GTK_BOX (hbox), options->color_panel->color_panel_widget, TRUE, TRUE, 0); gtk_widget_show (options->color_panel->color_panel_widget); gtk_widget_show (table); gtk_widget_show (vbox); gtk_widget_show (hbox); gtk_widget_show (options->query_box); } /****************************************/ /* The edit channel attributes dialog */ /****************************************/ typedef struct _EditChannelOptions EditChannelOptions; struct _EditChannelOptions { GtkWidget *query_box; GtkWidget *name_entry; ChannelWidget *channel_widget; GimpImage *gimage; ColorPanel *color_panel; gdouble opacity; }; static void edit_channel_query_ok_callback (GtkWidget *widget, gpointer data) { EditChannelOptions *options; Channel *channel; gint opacity; gint update = FALSE; gint i; options = (EditChannelOptions *) data; channel = options->channel_widget->channel; opacity = (gint) (255 * options->opacity) / 100; if (options->gimage) { /* Set the new channel name */ channel_set_name (channel, gtk_entry_get_text (GTK_ENTRY (options->name_entry))); gtk_label_set_text (GTK_LABEL (options->channel_widget->label), channel_get_name (channel)); if (channel->opacity != opacity) { channel->opacity = opacity; update = TRUE; } for (i = 0; i < 3; i++) if (options->color_panel->color[i] != channel->col[i]) { channel->col[i] = options->color_panel->color[i]; update = TRUE; } if (update) { drawable_update (GIMP_DRAWABLE (channel), 0, 0, GIMP_DRAWABLE (channel)->width, GIMP_DRAWABLE (channel)->height); gdisplays_flush (); } } color_panel_free (options->color_panel); gtk_widget_destroy (options->query_box); g_free (options); } static void edit_channel_query_cancel_callback (GtkWidget *widget, gpointer data) { EditChannelOptions *options; options = (EditChannelOptions *) data; color_panel_free (options->color_panel); gtk_widget_destroy (options->query_box); g_free (options); } static void channels_dialog_edit_channel_query (ChannelWidget *channel_widget) { EditChannelOptions *options; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *table; GtkWidget *label; GtkWidget *opacity_scale; GtkObject *opacity_scale_data; gint i; /* the new options structure */ options = g_new (EditChannelOptions, 1); options->channel_widget = channel_widget; options->gimage = channel_widget->gimage; options->opacity = (gdouble) channel_widget->channel->opacity / 2.55; for (i = 0; i < 3; i++) channel_color[i] = channel_widget->channel->col[i]; options->color_panel = color_panel_new (channel_color, 48, 64); /* The dialog */ options->query_box = gimp_dialog_new (_("Edit Channel Attributes"), "edit_channel_attributes", gimp_standard_help_func, "dialogs/channels_dialog.html", GTK_WIN_POS_MOUSE, FALSE, TRUE, FALSE, _("OK"), edit_channel_query_ok_callback, options, NULL, TRUE, FALSE, _("Cancel"), edit_channel_query_cancel_callback, options, NULL, FALSE, TRUE, NULL); /* The main hbox */ hbox = gtk_hbox_new (FALSE, 2); gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (options->query_box)->vbox), hbox); /* The vbox */ vbox = gtk_vbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); /* The table */ table = gtk_table_new (2, 3, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 4); gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); /* The name entry */ label = gtk_label_new (_("Channel name:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_widget_show (label); options->name_entry = gtk_entry_new (); gtk_widget_set_usize (options->name_entry, 150, 0); gtk_table_attach_defaults (GTK_TABLE (table), options->name_entry, 1, 2, 0, 1); gtk_entry_set_text (GTK_ENTRY (options->name_entry), channel_get_name (channel_widget->channel)); gtk_widget_show (options->name_entry); /* The opacity scale */ label = gtk_label_new (_("Fill Opacity:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_widget_show (label); opacity_scale_data = gtk_adjustment_new (options->opacity, 0.0, 100.0, 1.0, 1.0, 0.0); opacity_scale = gtk_hscale_new (GTK_ADJUSTMENT (opacity_scale_data)); gtk_table_attach_defaults (GTK_TABLE (table), opacity_scale, 1, 2, 1, 2); gtk_scale_set_value_pos (GTK_SCALE (opacity_scale), GTK_POS_TOP); gtk_signal_connect (GTK_OBJECT (opacity_scale_data), "value_changed", (GtkSignalFunc) new_channel_query_scale_update, &options->opacity); gtk_widget_show (opacity_scale); /* The color panel */ gtk_box_pack_start (GTK_BOX (hbox), options->color_panel->color_panel_widget, TRUE, TRUE, 0); gtk_widget_show (options->color_panel->color_panel_widget); gtk_widget_show (table); gtk_widget_show (vbox); gtk_widget_show (hbox); gtk_widget_show (options->query_box); }