/* gap_navigator_dialog.c * by hof (Wolfgang Hofer) * * GAP ... Gimp Animation Plugins * * This Module contains the GAP Video Navigator dialog Window * that provides a VCR-GUI for the GAP basic navigation functions. * */ /* 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. */ /* TODO: * - BUG X11 deadlock if GAP Video Navigator runs another plugin * from the double-click callback procedure in the frame listbox, * and the other plugin opens a new gtk dialog. * (the new dialog does not get the focus) * Current workaround: * save frames of other types than xcf before the * listbox is filled. * (this forces the GAP p_decide_save_as Dialog * when the Navigator is opened or an image != xcf is * selected in the image optionmenu, and sets * the fileformat specific save parameters) * * - BUGFIX or workaround needed: list widget can't handle large lists * (test failed at 1093 items maybe there is a limit of 1092 ??) * * - start of a 2.nd navigator should pop up the 1st one and exit. * x- scroll the listbox (active image should always be in the visible (exposed) area * problem: 1092 limit ! * x- implement the unfinished callback procedures * x- Updatde Button (to create all missing and out of date thumbnails) * x- tooltips * x- multiple selections * x- timezoom and framerate should be stored in a video info file * x- calculate & update for frame timing labels * x- Render Preview defaultIcon for images without thumbnail * and for preview_size 0 (off) * * Events that sould be handled: * - changes of the active_image (in ram) :: update of one frame_widget * x- change of preview_size :: update full frame_widgets list * x- changes of the active_image (on disk) :: update of image_menu + full frame_widgets list * (maybe i'll set a polling timer event to watch the diskfile) * x- close of the active_image (in ram) :: update of image_menu + full frame_widgets list * * - drag & drop * (Problem: gimage struct is not available for plugins, * need a Drag&Drop type that operates on image_id) * - preferences should have additional video_preview_size * (tiny,small,normal,large,huge) * */ /* revision history: * version 1.1.29b; 2000.11.30 hof: new e-mail adress * version 1.1.20a; 2000.04.25 hof: copy/cut/paste menu * version 1.1.14a; 2000.01.08 hof: 1st release */ static char *gap_navigator_version = "1.1.29b; 2000/11/30"; /* SYTEM (UNIX) includes */ #include #include #include #include #include #include #include /* GIMP includes */ #include #include "config.h" #include #include #include #include #include #include #include #include #include #include #include /* * OpsButton is not available for plugins in libgimp 1.1.14 * workaround: include gimp-1.1.14/app/ops_buttons.h /.c */ /* --------------------------------------- start copy of gimp-1.1.14/app/ops_buttons.h */ #ifndef __OPS_BUTTONS_H__ #define __OPS_BUTTONS_H__ typedef enum { OPS_BUTTON_MODIFIER_NONE, OPS_BUTTON_MODIFIER_SHIFT, OPS_BUTTON_MODIFIER_CTRL, OPS_BUTTON_MODIFIER_ALT, OPS_BUTTON_MODIFIER_SHIFT_CTRL, OPS_BUTTON_MODIFIER_LAST } OpsButtonModifier; typedef enum { OPS_BUTTON_NORMAL, OPS_BUTTON_RADIO } OpsButtonType; typedef struct _OpsButton OpsButton; struct _OpsButton { gchar **xpm_data; /* xpm data for the button */ GtkSignalFunc callback; /* callback function */ GtkSignalFunc *ext_callbacks; /* callback functions when * modifiers are pressed */ gchar *tooltip; gchar *private_tip; GtkWidget *widget; /* the button widget */ gint modifier; }; /* Function declarations */ GtkWidget * ops_button_box_new (GtkWidget *parent, OpsButton *ops_button, OpsButtonType ops_type); #endif /* __OPS_BUTTONS_H__ */ /* --------------------------------------- end copy of gimp-1.1.14/app/ops_buttons.h */ /* GAP includes */ #include "gap_lib.h" #include "gap_pdb_calls.h" /* Note: * the PDB call of gimp_file_load_thumbnail has a bug in gimp-1.1.14. * As workaround i use a copy of gimp-1.1.14/app/fileops.c:readXVThumb * to read .xvpics correct directly from file (this is also the faster way) */ guchar* readXVThumb (const gchar *fnam, gint *w, gint *h, gchar **imginfo /* caller frees if != NULL */); /* some definitions used in all dialogs */ #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 MAX_HEIGHT_GTK_SCROLLED_WIN 32767 #define PLUGIN_NAME "plug_in_gap_navigator" #define LIST_WIDTH 200 #define LIST_HEIGHT 150 #define PREVIEW_BPP 3 #define THUMBNAIL_BPP 3 #define MIX_CHANNEL(b, a, m) (((a * m) + (b * (255 - m))) / 255) #define PREVIEW_BG_GRAY1 80 #define PREVIEW_BG_GRAY2 180 #define NUPD_IMAGE_MENU 1 #define NUPD_THUMBFILE_TIMESTAMP 2 #define NUPD_FRAME_NR_CHANGED 4 #define NUPD_PREV_LIST 8 #define NUPD_PREV_LIST_ICONS 16 #define NUPD_ALL 0xffffffff; typedef struct _OpenFrameImages OpenFrameImages; typedef struct _NaviDialog NaviDialog; typedef struct _FrameWidget FrameWidget; typedef struct _SelectedRange SelectedRange; struct _OpenFrameImages{ gint32 image_id; gint32 frame_nr; OpenFrameImages *next; }; struct _NaviDialog { gint tooltip_on; GtkWidget *shell; GtkWidget *vbox; GtkWidget *mode_option_menu; GtkWidget *frame_list; GtkWidget *scrolled_win; GtkWidget *preserve_trans; GtkWidget *framerate_box; GtkWidget *timezoom_box; GtkWidget *image_option_menu; GtkWidget *image_menu; GtkWidget *ops_menu; GtkWidget *copy_menu_item; GtkWidget *cut_menu_item; GtkWidget *pastea_menu_item; GtkWidget *pasteb_menu_item; GtkWidget *paster_menu_item; GtkWidget *clrpaste_menu_item; GtkAccelGroup *accel_group; GtkAdjustment *framerate_data; GtkAdjustment *timezoom_data; GtkWidget *frame_preview; GtkWidget *framerange_number_label; gint waiting_cursor; GdkCursor *cursor_wait; GdkCursor *cursor_acitve; gint32 paste_at_frame; /* -1: use current frame */ gdouble ratio; gdouble preview_size; gint image_width, image_height; gint gimage_width, gimage_height; /* state information */ gint32 active_imageid; gint32 any_imageid; t_anim_info *ainfo_ptr; t_video_info *vin_ptr; GSList *frame_widgets; int timer; int cycle_time; OpenFrameImages *OpenFrameImagesList; int OpenFrameImagesCount; gint32 item_height; }; struct _FrameWidget { GtkWidget *clip_widget; GtkWidget *number_label; GtkWidget *time_label; GtkWidget *frame_preview; GtkWidget *list_item; GtkWidget *label; gint32 image_id; gint32 frame_nr; GdkPixmap *frame_pixmap; gint width, height; /* state information */ gboolean visited; time_t thumb_timestamp; char *thumb_filename; /* GimpDropType drop_type; */ }; struct _SelectedRange { gint32 from; gint32 to; SelectedRange *next; SelectedRange *prev; }; /* ----------------------- * procedure declarations * ----------------------- */ int gap_navigator(gint32 image_id); static void navi_preview_extents (void); static void frames_dialog_flush (void); static void frames_dialog_update (gint32 image_id); static void frame_widget_preview_redraw (FrameWidget *); static void navi_vid_copy_and_cut(gint cut_flag); static gint navi_images_menu_constrain (gint32 image_id, gint32 drawable_id, gpointer data); static void navi_images_menu_callback (gint32 id, gpointer data); static void navi_update_after_goto(void); static SelectedRange *navi_get_selected_ranges(void); static void navi_ops_menu_set_sensitive(void); static void navi_dialog_thumb_update_callback(GtkWidget *w, gpointer data); static void navi_dialog_thumb_updateall_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_play_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_play_optim_callback(GtkWidget *w, gpointer data); static void navi_dialog_frames_duplicate_frame_callback(GtkWidget *w, gpointer data); static void navi_dialog_frames_delete_frame_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_goto_first_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_goto_prev_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_goto_prevblock_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_goto_next_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_goto_nextblock_callback(GtkWidget *w, gpointer data); static void navi_dialog_vcr_goto_last_callback(GtkWidget *w, gpointer data); static void navi_framerate_scale_update(GtkAdjustment *w, gpointer data); static void navi_timezoom_scale_update(GtkAdjustment *w, gpointer data); static gint frame_list_events (GtkWidget *, GdkEvent *, gpointer); static void frame_widget_select_update (GtkWidget *, gpointer); static gint frame_widget_button_events (GtkWidget *, GdkEvent *); static gint frame_widget_preview_events (GtkWidget *, GdkEvent *); static gint navi_dialog_poll(GtkWidget *w, gpointer data); static void navi_dialog_update(gint32 update_flag); static void navi_scroll_to_current_frame_nr(void); static FrameWidget * frame_widget_create (gint32 image_id, gint32 frame_nr); static void frame_widget_delete (FrameWidget *fw); static gint32 navi_get_preview_size(void); static void frames_timing_update (void); static void frame_widget_time_label_update(FrameWidget *fw); static void navi_dialog_tooltips(void); static void navi_set_waiting_cursor(void); static void navi_set_active_cursor(void); /* ----------------------- * Local data * ----------------------- */ static NaviDialog *naviD = NULL; static gint suspend_gimage_notify = 0; static gint32 global_old_active_imageid = -1; /* the ops buttons */ static GtkSignalFunc navi_dialog_update_ext_callbacks[] = { navi_dialog_thumb_updateall_callback, NULL, NULL, NULL }; static GtkSignalFunc navi_dialog_vcr_play_ext_callbacks[] = { navi_dialog_vcr_play_optim_callback, NULL, NULL, NULL }; static GtkSignalFunc navi_dialog_vcr_goto_prev_ext_callbacks[] = { navi_dialog_vcr_goto_prevblock_callback, NULL, NULL, NULL }; static GtkSignalFunc navi_dialog_vcr_goto_next_ext_callbacks[] = { navi_dialog_vcr_goto_nextblock_callback, NULL, NULL, NULL }; static OpsButton frames_ops_buttons[] = { { play_xpm, navi_dialog_vcr_play_callback, navi_dialog_vcr_play_ext_callbacks, N_("Playback \n" " optimized"), "#playback", NULL, 0 }, { update_xpm, navi_dialog_thumb_update_callback, navi_dialog_update_ext_callbacks, N_("Smart Update .xvpics\n" " forced upd"), "#update", NULL, 0 }, { duplicate_xpm, navi_dialog_frames_duplicate_frame_callback, NULL, N_("Duplicate selected Frames"), "#duplicate", NULL, 0 }, { delete_xpm, navi_dialog_frames_delete_frame_callback, NULL, N_("Delete selected Frames"), "#delete", NULL, 0 }, { NULL, NULL, NULL, NULL, NULL, NULL, 0 } }; static OpsButton vcr_ops_buttons[] = { { first_xpm, navi_dialog_vcr_goto_first_callback, NULL, N_("Goto 1st Frame"), "#goto_first", NULL, 0 }, { prev_xpm, navi_dialog_vcr_goto_prev_callback, navi_dialog_vcr_goto_prev_ext_callbacks, N_("Goto prev Frame\n" " use timezoom stepsize"), "#goto_previous", NULL, 0 }, { next_xpm, navi_dialog_vcr_goto_next_callback, navi_dialog_vcr_goto_next_ext_callbacks, N_("Goto next Frame\n" " use timezoom stepsize"), "#goto_next", NULL, 0 }, { last_xpm, navi_dialog_vcr_goto_last_callback, NULL, N_("Goto last Frame"), "#goto_last", NULL, 0 }, { NULL, NULL, NULL, NULL, NULL, NULL, 0 } }; /* ------------------------ * gap DEBUG switch * ------------------------ */ /* int gap_debug = 1; */ /* print debug infos */ /* int gap_debug = 0; */ /* 0: dont print debug infos */ int gap_debug = 0; static void query(void); static void run(char *name, int nparam, GimpParam *param, int *nretvals, GimpParam **retvals); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; /* ------------------------ * MAIN, query & run * ------------------------ */ MAIN () static void query () { static GimpParamDef args_navigator[] = { {GIMP_PDB_INT32, "run_mode", "Interactive"}, {GIMP_PDB_IMAGE, "image", "(unused)"}, {GIMP_PDB_DRAWABLE, "drawable", "(unused)"}, }; static GimpParamDef *return_vals = NULL; static int nreturn_vals = 0; gimp_install_procedure(PLUGIN_NAME, "GAP video navigator dialog", "", "Wolfgang Hofer (hof@gimp.org)", "Wolfgang Hofer", gap_navigator_version, N_("/Video/VCR Navigator..."), "RGB*, INDEXED*, GRAY*", GIMP_PLUGIN, G_N_ELEMENTS (args_navigator), nreturn_vals, args_navigator, return_vals); } /* end query */ static void run (char *name, int n_params, GimpParam *param, int *nreturn_vals, GimpParam **return_vals) { gint32 l_active_image; const gchar *l_env; static GimpParam values[2]; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; gint32 nr; pid_t l_navid_pid; gint32 l_rc; *nreturn_vals = 1; *return_vals = values; nr = 0; l_rc = 0; l_env = g_getenv("GAP_DEBUG"); if(l_env != NULL) { if((*l_env != 'n') && (*l_env != 'N')) gap_debug = 1; } run_mode = param[0].data.d_int32; l_active_image = -1; if(gap_debug) printf("\n\ngap_navigator: debug name = %s\n", name); l_active_image = param[1].data.d_image; if (run_mode == GIMP_RUN_NONINTERACTIVE) { INIT_I18N(); } else { INIT_I18N_UI(); } /* check for other Video navigator Dialog Process */ if (sizeof(pid_t) == gimp_get_data_size(PLUGIN_NAME)) { gimp_get_data(PLUGIN_NAME, &l_navid_pid); if(l_navid_pid != 0) { /* kill with signal 0 checks only if the process is alive (no signal is sent) * returns 0 if alive, 1 if no process with given pid found. */ if (0 == kill(l_navid_pid, 0)) { p_msg_win(GIMP_RUN_INTERACTIVE, _("Cant open two or more Video Navigator Windows.")); l_rc = -1; } } } if(l_rc == 0) { /* set pid data when navigator is running */ l_navid_pid = getpid(); gimp_set_data(PLUGIN_NAME, &l_navid_pid, sizeof(pid_t)); if (strcmp (name, PLUGIN_NAME) == 0) { if (run_mode != GIMP_RUN_INTERACTIVE) { status = GIMP_PDB_CALLING_ERROR; } if (status == GIMP_PDB_SUCCESS) { l_rc = gap_navigator(l_active_image); } } /* set pid data to 0 when navigator stops */ l_navid_pid = 0; gimp_set_data(PLUGIN_NAME, &l_navid_pid, sizeof(pid_t)); } /* ---------- return handling --------- */ if(l_rc < 0) { status = GIMP_PDB_EXECUTION_ERROR; } values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; } /* --------------------------------- * the navigator callback procedures * --------------------------------- */ static void edit_copy_callback (GtkWidget *w, gpointer client_data) { if(gap_debug) printf("edit_copy_callback\n"); navi_vid_copy_and_cut(FALSE /* cut_flag */); } static void edit_cut_callback (GtkWidget *w, gpointer client_data) { if(gap_debug) printf("edit_cut_callback\n"); navi_vid_copy_and_cut(TRUE /* cut_flag */); } static void p_edit_paste_call(gint32 paste_mode) { GimpParam *return_vals; int nreturn_vals; if(naviD->paste_at_frame >= 0) { /* goto the first selected frame */ return_vals = gimp_run_procedure ("plug_in_gap_goto", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, naviD->paste_at_frame, GIMP_PDB_END); if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS) { return; } } return_vals = gimp_run_procedure ("plug_in_gap_video_edit_paste", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, paste_mode, GIMP_PDB_END); navi_update_after_goto(); } static void edit_pasteb_callback (GtkWidget *w, gpointer client_data) { p_edit_paste_call(VID_PASTE_INSERT_BEFORE); } static void edit_pastea_callback (GtkWidget *w, gpointer client_data) { p_edit_paste_call(VID_PASTE_INSERT_AFTER); } static void edit_paster_callback (GtkWidget *w, gpointer client_data) { p_edit_paste_call(VID_PASTE_REPLACE); } static void edit_clrpaste_callback (GtkWidget *w, gpointer client_data) { GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("edit_clrpaste_callback\n"); return_vals = gimp_run_procedure ("plug_in_gap_video_edit_clear", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_END); } void navi_vid_copy_and_cut(gint cut_flag) { SelectedRange *range_list; SelectedRange *range_list2; SelectedRange *range_item; GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_vid_copy_callback\n"); range_list = navi_get_selected_ranges(); if(range_list) { navi_set_waiting_cursor(); p_vid_edit_clear(); /* generate prev linkpointers in the range list */ range_item = NULL; range_list2 = range_list; while(1 == 1) { range_list2->prev = range_item; range_item = range_list2; if(range_list2->next == NULL) { break; } range_list2 = range_list2->next; } while(range_list2) { /* Note: process the ranges from low frame_nummers * upto high frame_numbers. * (the range_list was created in a way that * the highest range is added as 1st list element * so we start at end of the list and to the 1. element using prev linkpinters */ if(gap_debug) printf("Copy Range from:%d to:%d\n" ,(int)range_list2->from ,(int)range_list2->to ); return_vals = gimp_run_procedure ("plug_in_gap_video_edit_copy", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, range_list2->from, GIMP_PDB_INT32, range_list2->to, GIMP_PDB_END); range_item = range_list2; range_list2 = range_list2->prev; } while(range_list) { if(cut_flag) { if(gap_debug) printf("Delete Range from:%d to:%d\n" ,(int)range_list->from ,(int)range_list->to ); return_vals = gimp_run_procedure ("plug_in_gap_goto", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, range_list->from, GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { return_vals = gimp_run_procedure ("plug_in_gap_del", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, 1 + (range_list->to - range_list->from), /* number of frames to delete */ GIMP_PDB_END); } } range_item = range_list; range_list = range_list->next; g_free(range_item); } navi_update_after_goto(); } } /* end navi_dialog_frames_duplicate_frame_callback */ static gint32 navi_get_preview_size(void) { char *value_string; gint32 preview_size; preview_size = 32; /* default preview size if nothing is configured */ value_string = p_gimp_gimprc_query("video-preview-size"); if(value_string == NULL) { value_string = p_gimp_gimprc_query("preview-size"); } if(value_string) { if(gap_debug) printf("navi_get_preview_size value_str:%s:\n", value_string); if (strcmp (value_string, "none") == 0) preview_size = 0; else if (strcmp (value_string, "tiny") == 0) preview_size = 24; else if (strcmp (value_string, "small") == 0) preview_size = 32; else if (strcmp (value_string, "medium") == 0) preview_size = 48; else if (strcmp (value_string, "large") == 0) preview_size = 64; else if (strcmp (value_string, "huge") == 0) preview_size = 128; else preview_size = atol(value_string); g_free(value_string); } else { if(gap_debug) printf("navi_get_preview_size value_str is NULL\n"); } return (preview_size); } static gint navi_check_exist_first_and_last(t_anim_info *ainfo_ptr) { char *l_fname; l_fname = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->last_frame_nr, ainfo_ptr->extension); if (!p_file_exists(l_fname)) { g_free(l_fname); return(FALSE); } g_free(l_fname); l_fname = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->first_frame_nr, ainfo_ptr->extension); if (!p_file_exists(l_fname)) { g_free(l_fname); return(FALSE); } g_free(l_fname); l_fname = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->last_frame_nr+1, ainfo_ptr->extension); if (p_file_exists(l_fname)) { g_free(l_fname); return(FALSE); } g_free(l_fname); l_fname = p_alloc_fname(ainfo_ptr->basename, ainfo_ptr->first_frame_nr-1, ainfo_ptr->extension); if (p_file_exists(l_fname)) { g_free(l_fname); return(FALSE); } g_free(l_fname); return(TRUE); } t_anim_info * navi_get_ainfo(gint32 image_id, t_anim_info *old_ainfo_ptr) { t_anim_info *ainfo_ptr; ainfo_ptr = p_alloc_ainfo(image_id, GIMP_RUN_NONINTERACTIVE); if(ainfo_ptr) { if(old_ainfo_ptr) { if((old_ainfo_ptr->image_id == image_id) && (strcmp(old_ainfo_ptr->basename, ainfo_ptr->basename) == 0)) { if(navi_check_exist_first_and_last(old_ainfo_ptr)) { /* - image_id and name have not changed, * - first and last frame still exist * and are still first and last frame * In that case we can reuse first and last frame_nr * without scanning the directory for frames */ ainfo_ptr->first_frame_nr = old_ainfo_ptr->first_frame_nr; ainfo_ptr->last_frame_nr = old_ainfo_ptr->last_frame_nr; ainfo_ptr->frame_cnt = old_ainfo_ptr->frame_cnt; return(ainfo_ptr); } } } p_dir_ainfo(ainfo_ptr); } return(ainfo_ptr); } void navi_reload_ainfo_force(gint32 image_id) { t_anim_info *old_ainfo_ptr; char frame_nr_to_char[20]; if(gap_debug) printf("navi_reload_ainfo_force image_id:%d\n", (int)image_id); old_ainfo_ptr = naviD->ainfo_ptr; naviD->active_imageid = image_id; naviD->ainfo_ptr = navi_get_ainfo(image_id, old_ainfo_ptr); if((strcmp(naviD->ainfo_ptr->extension, ".xcf") != 0) && (strcmp(naviD->ainfo_ptr->extension, ".xcfgz") != 0) && (global_old_active_imageid != image_id)) { /* for other frameformats than xcf * save the frame a 1st time (to set filetype specific save paramters) * this also is a workaround for a BUG that causes an X11 deadlock * when the save dialog pops up from the double-click callback in the frames listbox */ suspend_gimage_notify++; p_save_named_frame(image_id, naviD->ainfo_ptr->old_filename); suspend_gimage_notify--; } global_old_active_imageid = image_id; if(naviD->framerange_number_label) { g_snprintf(frame_nr_to_char, sizeof(frame_nr_to_char), "%04d - %04d" , (int)naviD->ainfo_ptr->first_frame_nr , (int)naviD->ainfo_ptr->last_frame_nr ); gtk_label_set_text (GTK_LABEL (naviD->framerange_number_label), frame_nr_to_char); } if(old_ainfo_ptr) p_free_ainfo(&old_ainfo_ptr); } void navi_reload_ainfo(gint32 image_id) { if(image_id < 0) navi_reload_ainfo_force(naviD->any_imageid); else navi_reload_ainfo_force(image_id); if(naviD->ainfo_ptr) { if(naviD->vin_ptr) g_free(naviD->vin_ptr); naviD->vin_ptr = p_get_video_info(naviD->ainfo_ptr->basename); gtk_adjustment_set_value(naviD->framerate_data, (gfloat)naviD->vin_ptr->framerate); gtk_adjustment_set_value(naviD->timezoom_data, (gfloat)naviD->vin_ptr->timezoom); } } static gint navi_images_menu_constrain(gint32 image_id, gint32 drawable_id, gpointer data) { if(gap_debug) printf("navi_images_menu_constrain PROCEDURE imageID:%d\n", (int)image_id); if(p_get_frame_nr(image_id) < 0) { return(FALSE); /* reject images without frame number */ } if(naviD) { if(naviD->active_imageid < 0) { /* if there is no valid active_imageid * we nominate the first one that comes along */ naviD->any_imageid = image_id; } } return(TRUE); } /* end navi_images_menu_constrain */ static void navi_images_menu_callback (gint32 image_id, gpointer data) { if(gap_debug) printf("navi_images_menu_callback PROCEDURE imageID:%d\n", (int)image_id); if(naviD) { if(naviD->active_imageid != image_id) { navi_reload_ainfo(image_id); navi_dialog_update(NUPD_FRAME_NR_CHANGED | NUPD_PREV_LIST); navi_scroll_to_current_frame_nr(); } } } static void navid_update_exposed_previews(void) { if(naviD == NULL) return; /* I've tried to send an "expose_event" to frame_preview widgets * using gtk_signal_emit_by_name , but it did not work. * So i decided to use the trick widget_hide and widget_show * to force gtk to (re)expose the widgets in the listbox * That way all visible items in the listbox are refreshed (redraw) */ gtk_widget_hide(naviD->scrolled_win); gtk_widget_show(naviD->scrolled_win); } static void navi_set_waiting_cursor(void) { if(naviD == NULL) return; if(naviD->waiting_cursor) return; naviD->waiting_cursor = TRUE; gdk_window_set_cursor(GTK_WIDGET(naviD->shell)->window, naviD->cursor_wait); } static void navi_set_active_cursor(void) { if(naviD == NULL) return; if(!naviD->waiting_cursor) return; naviD->waiting_cursor = FALSE; gdk_window_set_cursor(GTK_WIDGET(naviD->shell)->window, naviD->cursor_acitve); } static void navi_scroll_to_current_frame_nr(void) { GtkAdjustment *adj; gfloat adj_val; gint index; gint page_size; if(naviD == NULL) return; if(naviD->ainfo_ptr == NULL) return; if(naviD->vin_ptr == NULL) return; if(naviD->scrolled_win == NULL) return; if(gap_debug) printf("navi_scroll_to_current_frame_nr: BEGIN timezoom:%d, item_height:%d\n", (int)naviD->vin_ptr->timezoom, (int)naviD->item_height); adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (naviD->scrolled_win)); adj_val = adj->value; page_size = MAX(adj->page_size, naviD->item_height); index = 0; if(naviD->vin_ptr->timezoom) { index = (naviD->ainfo_ptr->curr_frame_nr - naviD->ainfo_ptr->first_frame_nr) / naviD->vin_ptr->timezoom; } if (index * naviD->item_height < adj->value) { adj_val = index * naviD->item_height; } else if ((index + 1) * naviD->item_height > adj->value + page_size) { adj_val = (index + 1) * naviD->item_height - page_size; } if(adj_val > MAX_HEIGHT_GTK_SCROLLED_WIN - page_size) { adj_val = MAX_HEIGHT_GTK_SCROLLED_WIN - page_size; } if(adj_val != adj->value) { if(gap_debug) printf("navi_scroll_to_current_frame_nr: OLD: %d should be set to adj_val: %d index:%d\n", (int)adj->value, (int)adj_val, (int)index); if(gap_debug) printf("navi_scroll_to_current_frame_nr: adj->lower :%d adj->upper :%d\n", (int)adj->lower, (int)adj->upper); /* gtk_adjustment_set_value(adj, adj_val); */ adj->value = adj_val; gtk_adjustment_value_changed (adj); if(gap_debug) printf("navi_scroll_to_current_frame_nr: NEW : %d should be equal to adj_val: %d adj->page_size:%d\n", (int)adj->value, (int)adj_val, (int)adj->page_size); } } static void navi_dialog_tooltips(void) { char *value_string; gint tooltip_on; if(naviD == NULL) return; tooltip_on = TRUE; value_string = p_gimp_gimprc_query("show-tool-tips"); if(value_string != NULL) { if (strcmp(value_string, "no") == 0) { tooltip_on = FALSE; } } if(naviD->tooltip_on != tooltip_on) { naviD->tooltip_on = tooltip_on; if(tooltip_on) { gimp_help_enable_tooltips (); } else { gimp_help_disable_tooltips (); } } } static gint navi_find_OpenFrameList(OpenFrameImages *search_item) { OpenFrameImages *item_list; item_list = naviD->OpenFrameImagesList; while(item_list) { if((item_list->image_id == search_item->image_id) && (item_list->frame_nr == search_item->frame_nr)) { return(TRUE); /* item found in the list */ } item_list = (OpenFrameImages *)item_list->next; } return(FALSE); } static void navi_free_OpenFrameList(OpenFrameImages *list) { OpenFrameImages *item_list; OpenFrameImages *item_next; item_list = list; while(item_list) { item_next = (OpenFrameImages *)item_list->next; g_free(item_list); item_list = item_next; } } static gint navi_check_image_menu_changes() { int nimages; gint32 *images; gint32 frame_nr; int i; gint l_rc; int item_count; OpenFrameImages *item_list; OpenFrameImages *new_item; l_rc = TRUE; if(naviD->OpenFrameImagesList == NULL) { l_rc = FALSE; } item_list = NULL; item_count = 0; images = gimp_image_list (&nimages); for (i = 0; i < nimages; i++) { frame_nr = p_get_frame_nr(images[i]); /* check for anim frame */ if(frame_nr >= 0) { item_count++; new_item = g_new (OpenFrameImages, 1); new_item->image_id = images[i]; new_item->frame_nr = frame_nr; new_item->next = item_list; item_list = new_item; if(!navi_find_OpenFrameList(new_item)) { l_rc = FALSE; } } } g_free(images); if(item_count != naviD->OpenFrameImagesCount) { l_rc = FALSE; } if(l_rc == TRUE) { navi_free_OpenFrameList(item_list); } else { navi_free_OpenFrameList(naviD->OpenFrameImagesList); naviD->OpenFrameImagesCount = item_count; naviD->OpenFrameImagesList = item_list; } return(l_rc); } static gint navi_refresh_image_menu() { if(naviD) { if(!navi_check_image_menu_changes()) { if(gap_debug) printf("navi_refresh_image_menu ** BEGIN REFRESH\n"); if(naviD->OpenFrameImagesCount != 0) { gtk_widget_set_sensitive(naviD->vbox, TRUE); } naviD->image_menu = gimp_image_menu_new(navi_images_menu_constrain, navi_images_menu_callback, naviD, naviD->active_imageid ); gtk_option_menu_set_menu(GTK_OPTION_MENU(naviD->image_option_menu), naviD->image_menu); gtk_widget_show (naviD->image_menu); if(naviD->OpenFrameImagesCount == 0) { gtk_widget_set_sensitive(naviD->vbox, FALSE); } return(TRUE); } } return(FALSE); } void navi_update_after_goto(void) { if(naviD) { navi_dialog_update(NUPD_IMAGE_MENU | NUPD_FRAME_NR_CHANGED); navi_scroll_to_current_frame_nr(); } gimp_displays_flush(); navi_set_active_cursor(); } static SelectedRange * navi_get_selected_ranges(void) { FrameWidget *fw; GSList *list; GtkStateType state; SelectedRange *new_range; SelectedRange *range_list; range_list = NULL; new_range = NULL; list = naviD->frame_widgets; while (list) { fw = (FrameWidget *) list->data; list = g_slist_next (list); state = fw->list_item->state; if(state == GTK_STATE_SELECTED) { if(new_range == NULL) { new_range = g_new (SelectedRange, 1); new_range->next = range_list; new_range->from = fw->frame_nr; new_range->to = fw->frame_nr; range_list = new_range; } else { new_range->to = fw->frame_nr; } } else { new_range = NULL; } } return(range_list); } void navi_ops_menu_set_sensitive(void) { FrameWidget *fw; GSList *list; GtkStateType state; if(naviD == NULL) { return; } if(p_vid_edit_framecount() > 0) { gtk_widget_set_sensitive(naviD->pastea_menu_item, TRUE); gtk_widget_set_sensitive(naviD->pasteb_menu_item, TRUE); gtk_widget_set_sensitive(naviD->paster_menu_item, TRUE); gtk_widget_set_sensitive(naviD->clrpaste_menu_item, TRUE); } else { gtk_widget_set_sensitive(naviD->pastea_menu_item, FALSE); gtk_widget_set_sensitive(naviD->pasteb_menu_item, FALSE); gtk_widget_set_sensitive(naviD->paster_menu_item, FALSE); gtk_widget_set_sensitive(naviD->clrpaste_menu_item, FALSE); } list = naviD->frame_widgets; while (list) { fw = (FrameWidget *) list->data; list = g_slist_next (list); state = fw->list_item->state; if(state == GTK_STATE_SELECTED) { naviD->paste_at_frame = fw->frame_nr; gtk_widget_set_sensitive(naviD->copy_menu_item, TRUE); gtk_widget_set_sensitive(naviD->cut_menu_item, TRUE); return; } } gtk_widget_set_sensitive(naviD->copy_menu_item, FALSE); gtk_widget_set_sensitive(naviD->cut_menu_item, FALSE); naviD->paste_at_frame = -1; /* nothing selected, use current frame_nr */ } /* end navi_ops_menu_set_sensitive */ static void navi_thumb_update(gint update_all) { gint32 l_frame_nr; gint32 l_image_id; gint l_upd_flag; gint l_any_upd_flag; char *l_image_filename; char *l_thumb_filename; struct stat l_stat_thumb; struct stat l_stat_image; if(naviD == NULL) return; if(naviD->ainfo_ptr == NULL) return; l_any_upd_flag = FALSE; for(l_frame_nr = naviD->ainfo_ptr->first_frame_nr; l_frame_nr <= naviD->ainfo_ptr->last_frame_nr; l_frame_nr++) { l_upd_flag = TRUE; l_image_filename = p_alloc_fname(naviD->ainfo_ptr->basename, l_frame_nr, naviD->ainfo_ptr->extension); l_thumb_filename = p_alloc_fname_thumbnail(l_image_filename); if(!update_all) { if (0 == stat(l_thumb_filename, &l_stat_thumb)) { /* thumbnail filename exists */ if(S_ISREG(l_stat_thumb.st_mode)) { /* and is a regular file */ if (0 == stat(l_image_filename, &l_stat_image)) { if(l_stat_image.st_mtime < l_stat_thumb.st_mtime) { /* time of last modification of image is older (less) * than last modification of the thumbnail * So we can skip the thumbnail Update for this frame */ l_upd_flag = FALSE; } } } } } if(l_upd_flag) { l_any_upd_flag = TRUE; if(gap_debug) printf("navi_thumb_update frame_nr:%d\n", (int)l_frame_nr); l_image_id = p_load_image(l_image_filename); p_gimp_file_save_thumbnail(l_image_id, l_image_filename); gimp_image_delete(l_image_id); } if(l_image_filename) g_free(l_image_filename); if(l_thumb_filename) g_free(l_thumb_filename); } if(l_any_upd_flag ) { navid_update_exposed_previews(); } } static void navi_dialog_thumb_update_callback(GtkWidget *w, gpointer data) { if(gap_debug) printf("navi_dialog_thumb_update_callback\n"); navi_thumb_update(FALSE); } static void navi_dialog_thumb_updateall_callback(GtkWidget *w, gpointer data) { if(gap_debug) printf("navi_dialog_thumb_updateall_callback\n"); navi_thumb_update(TRUE); } static void navi_playback(gint32 optimize) { SelectedRange *range_list; SelectedRange *range_item; gint32 l_from; gint32 l_to; gint32 l_new_image_id; GimpParam *return_vals; int nreturn_vals; char l_frame_name[50]; int l_frame_delay; if(gap_debug) printf("navi_dialog_vcr_play_callback\n"); strcpy(l_frame_name, "frame_[####]"); if(naviD->vin_ptr) { if(naviD->vin_ptr->framerate > 0) { l_frame_delay = 1000 / naviD->vin_ptr->framerate; g_snprintf(l_frame_name, sizeof(l_frame_name), "frame_[####] (%dms)", (int)l_frame_delay); } } l_from = naviD->ainfo_ptr->first_frame_nr; l_to = naviD->ainfo_ptr->last_frame_nr; range_list = navi_get_selected_ranges(); if(range_list) { l_to = naviD->ainfo_ptr->first_frame_nr; l_from = naviD->ainfo_ptr->last_frame_nr; while(range_list) { l_from = MIN(l_from, range_list->from); l_to = MAX(l_to, range_list->to); range_item = range_list; range_list = range_list->next; g_free(range_item); } } return_vals = gimp_run_procedure ("plug_in_gap_range_to_multilayer", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, l_from, GIMP_PDB_INT32, l_to, GIMP_PDB_INT32, 3, /* flatten image */ GIMP_PDB_INT32, 1, /* BG_VISIBLE */ GIMP_PDB_INT32, (gint32)naviD->vin_ptr->framerate, GIMP_PDB_STRING, l_frame_name, GIMP_PDB_INT32, 6, /* use all visible layers */ GIMP_PDB_INT32, 0, /* ignore case */ GIMP_PDB_INT32, 0, /* normal selection (no invert) */ GIMP_PDB_STRING, "0", /* select string (ignored) */ GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { l_new_image_id = return_vals[1].data.d_image; if(optimize) { return_vals = gimp_run_procedure ("plug_in_animationoptimize", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, l_new_image_id, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { #ifdef COMMENT_BLOCK /* sorry, plug_in_animationoptimize does create * a new anim-optimized image, but does not return the id * of the created image so we can't play the optimized * image automatically. */ /* destroy the tmp image */ gimp_image_delete(l_new_image_id); l_new_image_id = return_vals[1].data.d_image; #endif } } /* TODO: here we should start a thread for the playback, * so the navigator is not blocked until playback exits */ return_vals = gimp_run_procedure ("plug_in_animationplay", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, l_new_image_id, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_END); } } static void navi_dialog_vcr_play_callback(GtkWidget *w, gpointer data) { if(gap_debug) printf("navi_dialog_vcr_play_callback\n"); navi_playback(FALSE /* dont not optimize */); } static void navi_dialog_vcr_play_optim_callback(GtkWidget *w, gpointer data) { if(gap_debug) printf("navi_dialog_vcr_play_optim_callback\n"); navi_playback(TRUE /* optimize */); } void navi_dialog_frames_duplicate_frame_callback(GtkWidget *w, gpointer data) { SelectedRange *range_list; SelectedRange *range_item; GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_frames_duplicate_frame_callback\n"); range_list = navi_get_selected_ranges(); if(range_list) { navi_set_waiting_cursor(); while(range_list) { /* Note: process the ranges from high frame_nummers * downto low frame_numbers. * (the range_list was created in a way that * the highest range is added as 1st list element) */ if(gap_debug) printf("Duplicate Range from:%d to:%d\n" ,(int)range_list->from ,(int)range_list->to ); return_vals = gimp_run_procedure ("plug_in_gap_goto", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, range_list->from, GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { return_vals = gimp_run_procedure ("plug_in_gap_dup", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, 1, /* copy block 1 times */ GIMP_PDB_INT32, range_list->from, GIMP_PDB_INT32, range_list->to, GIMP_PDB_END); } range_item = range_list; range_list = range_list->next; g_free(range_item); } navi_update_after_goto(); } } /* end navi_dialog_frames_duplicate_frame_callback */ void navi_dialog_frames_delete_frame_callback(GtkWidget *w, gpointer data) { SelectedRange *range_list; SelectedRange *range_item; GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_frames_delete_frame_callback\n"); range_list = navi_get_selected_ranges(); if(range_list) { navi_set_waiting_cursor(); while(range_list) { /* Note: process the ranges from high frame_nummers * downto low frame_numbers. * (the range_list was created in a way that * the highest range is added as 1st list element) */ if(gap_debug) printf("Delete Range from:%d to:%d\n" ,(int)range_list->from ,(int)range_list->to ); return_vals = gimp_run_procedure ("plug_in_gap_goto", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, range_list->from, GIMP_PDB_END); if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS) { return_vals = gimp_run_procedure ("plug_in_gap_del", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, 1 + (range_list->to - range_list->from), /* number of frames to delete */ GIMP_PDB_END); } range_item = range_list; range_list = range_list->next; g_free(range_item); } navi_update_after_goto(); } } /* end navi_dialog_frames_delete_frame_callback */ void navi_dialog_goto_callback(gint32 dst_framenr) { GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_goto_callback\n"); navi_set_waiting_cursor(); return_vals = gimp_run_procedure ("plug_in_gap_goto", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_INT32, dst_framenr, GIMP_PDB_END); navi_update_after_goto(); } void navi_dialog_vcr_goto_first_callback(GtkWidget *w, gpointer data) { GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_vcr_goto_first_callback\n"); navi_set_waiting_cursor(); return_vals = gimp_run_procedure ("plug_in_gap_first", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_END); navi_update_after_goto(); } void navi_dialog_vcr_goto_prev_callback(GtkWidget *w, gpointer data) { GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_vcr_goto_prev_callback\n"); navi_set_waiting_cursor(); return_vals = gimp_run_procedure ("plug_in_gap_prev", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_END); navi_update_after_goto(); } void navi_dialog_vcr_goto_prevblock_callback(GtkWidget *w, gpointer data) { gint32 dst_framenr; if(gap_debug) printf("navi_dialog_vcr_goto_prevblock_callback\n"); if(naviD->ainfo_ptr == NULL) navi_reload_ainfo(naviD->active_imageid); if(naviD->ainfo_ptr == NULL) return; if(naviD->vin_ptr == NULL) return; dst_framenr = MAX(naviD->ainfo_ptr->curr_frame_nr - naviD->vin_ptr->timezoom, naviD->ainfo_ptr->first_frame_nr); navi_dialog_goto_callback(dst_framenr); } void navi_dialog_vcr_goto_next_callback(GtkWidget *w, gpointer data) { GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_vcr_goto_next_callback\n"); navi_set_waiting_cursor(); return_vals = gimp_run_procedure ("plug_in_gap_next", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_END); navi_update_after_goto(); } void navi_dialog_vcr_goto_nextblock_callback(GtkWidget *w, gpointer data) { gint32 dst_framenr; if(gap_debug) printf("navi_dialog_vcr_goto_nextblock_callback\n"); if(naviD->ainfo_ptr == NULL) navi_reload_ainfo(naviD->active_imageid); if(naviD->ainfo_ptr == NULL) return; if(naviD->vin_ptr == NULL) return; dst_framenr = MIN(naviD->ainfo_ptr->curr_frame_nr + naviD->vin_ptr->timezoom, naviD->ainfo_ptr->last_frame_nr); navi_dialog_goto_callback(dst_framenr); } void navi_dialog_vcr_goto_last_callback(GtkWidget *w, gpointer data) { GimpParam *return_vals; int nreturn_vals; if(gap_debug) printf("navi_dialog_vcr_goto_last_callback\n"); navi_set_waiting_cursor(); return_vals = gimp_run_procedure ("plug_in_gap_last", &nreturn_vals, GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE, GIMP_PDB_IMAGE, naviD->active_imageid, GIMP_PDB_DRAWABLE, -1, /* dummy */ GIMP_PDB_END); navi_update_after_goto(); } static void frames_timing_update (void) { FrameWidget *fw; GSList *list; list = naviD->frame_widgets; while (list) { fw = (FrameWidget *) list->data; list = g_slist_next (list); frame_widget_time_label_update(fw); } } void navi_framerate_scale_update(GtkAdjustment *adjustment, gpointer data) { gdouble framerate; if(naviD == NULL) return; if(naviD->vin_ptr == NULL) return; framerate = (gdouble) (adjustment->value); if(framerate != naviD->vin_ptr->framerate) { naviD->vin_ptr->framerate = framerate; if(naviD->ainfo_ptr) { /* write new framerate to video info file */ p_set_video_info(naviD->vin_ptr, naviD->ainfo_ptr->basename); } frames_timing_update(); } if(gap_debug) printf("navi_framerate_scale_update :%d\n", (int)naviD->vin_ptr->framerate); } void navi_timezoom_scale_update(GtkAdjustment *adjustment, gpointer data) { gint timezoom; GtkAdjustment *adj; gfloat adj_val; if(naviD == NULL) return; if(naviD->vin_ptr == NULL) return; timezoom = (int) (adjustment->value); if(timezoom != naviD->vin_ptr->timezoom) { adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (naviD->scrolled_win)); if(timezoom != 0) { adj_val = (adj->value * (gfloat)naviD->vin_ptr->timezoom) / (gfloat)timezoom; if(adj_val > MAX_HEIGHT_GTK_SCROLLED_WIN) { adj_val = MAX_HEIGHT_GTK_SCROLLED_WIN - adj->page_size; } } else { adj_val = adj->value; } naviD->vin_ptr->timezoom = timezoom; if(naviD->ainfo_ptr) { /* write new timezoom to video info file */ p_set_video_info(naviD->vin_ptr, naviD->ainfo_ptr->basename); } frames_dialog_flush(); gtk_adjustment_set_value(adjustment, (gfloat)timezoom); adj->value = adj_val; gtk_adjustment_value_changed (adj); } if(gap_debug) printf("navi_timezoom_scale_update :%d\n", (int)naviD->vin_ptr->timezoom); } /********************************/ /* frame list events callback */ /********************************/ static gint frame_list_events (GtkWidget *widget, GdkEvent *event, gpointer data) { GdkEventButton *bevent; GtkWidget *event_widget; FrameWidget *frame_widget; event_widget = gtk_get_event_widget (event); if (GTK_IS_LIST_ITEM (event_widget)) { frame_widget = (FrameWidget *) gtk_object_get_user_data (GTK_OBJECT (event_widget)); switch (event->type) { case GDK_BUTTON_PRESS: bevent = (GdkEventButton *) event; if (bevent->button == 3) { if(gap_debug) printf("frame_list_events: GDK_BUTTON_PRESS bevent->button == 3\n"); navi_ops_menu_set_sensitive(); gtk_menu_popup (GTK_MENU (naviD->ops_menu), NULL, NULL, NULL, NULL, 3, bevent->time); return TRUE; } break; case GDK_2BUTTON_PRESS: bevent = (GdkEventButton *) event; if(gap_debug) printf("frame_list_events: GDK_2BUTTON_PRESS event\n"); navi_dialog_goto_callback(frame_widget->frame_nr); return TRUE; default: break; } } return FALSE; } void frames_dialog_flush (void) { if(gap_debug) printf("frames_dialog_flush\n"); if(naviD) { frames_dialog_update(naviD->active_imageid); } } void frames_dialog_update (gint32 image_id) { FrameWidget *fw; GSList *list; GList *item_list; gint32 l_frame_nr; gint32 l_count; gint32 l_warning_flag; gint32 scrolled_window_height; gint32 scrolled_window_height_ok; GtkAdjustment *adj; gint l_waiting_cursor; if(gap_debug) printf("frames_dialog_update image_id:%d\n", (int)image_id); if (! naviD) /* || (naviD->active_imageid == image_id) */ { return; } l_waiting_cursor = naviD->waiting_cursor; if(!naviD->waiting_cursor) navi_set_waiting_cursor(); navi_reload_ainfo(image_id); /* Make sure the gimage is not notified of this change */ suspend_gimage_notify++; scrolled_window_height_ok = 0; /* Free all elements in the frames listbox */ gtk_list_clear_items (GTK_LIST (naviD->frame_list), 0, -1); list = naviD->frame_widgets; while (list) { fw = (FrameWidget *) list->data; list = g_slist_next (list); frame_widget_delete (fw); } if (naviD->frame_widgets) g_warning ("frames_dialog_update(): naviD->frame_widgets not empty!"); naviD->frame_widgets = NULL; /* Find the preview extents */ navi_preview_extents (); /* naviD->active_layer = NULL; */ l_count = 0; l_warning_flag = 0; scrolled_window_height = naviD->item_height; item_list = NULL; for (l_frame_nr = naviD->ainfo_ptr->first_frame_nr; l_frame_nr <= naviD->ainfo_ptr->last_frame_nr; l_frame_nr++) { /* create a frame list item */ if((l_frame_nr == naviD->ainfo_ptr->first_frame_nr) || (l_frame_nr == naviD->ainfo_ptr->last_frame_nr) || (((l_frame_nr - naviD->ainfo_ptr->first_frame_nr) % naviD->vin_ptr->timezoom) == 0) ) { if(scrolled_window_height < MAX_HEIGHT_GTK_SCROLLED_WIN) { fw = frame_widget_create (image_id, l_frame_nr); if(fw) { naviD->frame_widgets = g_slist_append (naviD->frame_widgets, fw); item_list = g_list_append (item_list, fw->list_item); scrolled_window_height_ok = scrolled_window_height; } } else { if(l_warning_flag == 0) { printf("WARNING: GTK listbox can't handle more than %d items of %d pixels height\n" ,(int)l_count, (int)naviD->item_height); printf(" can't display frames upto %d\n", (int)naviD->ainfo_ptr->last_frame_nr); l_warning_flag = 1; } } l_count++; scrolled_window_height += naviD->item_height; } } if(gap_debug) printf("LIST_CREATED: first:%d, last:%d\n, count:%d\n", (int)naviD->ainfo_ptr->first_frame_nr, (int)naviD->ainfo_ptr->last_frame_nr, (int)l_count ); /* get the index of the active frame */ if (item_list) { gtk_list_insert_items (GTK_LIST (naviD->frame_list), item_list, 0); adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (naviD->scrolled_win)); adj->lower = 0; adj->upper = scrolled_window_height_ok; } suspend_gimage_notify--; if(!l_waiting_cursor) navi_set_active_cursor(); } static void frame_widget_select_update (GtkWidget *widget, gpointer data) { FrameWidget *frame_widget; if (! (frame_widget = (FrameWidget *) data)) return; /* Is the list item being selected? */ if (widget->state != GTK_STATE_SELECTED) return; /* Only notify the gimage of an active layer change if necessary */ if (suspend_gimage_notify == 0) { if(gap_debug) printf("frame_widget_select_update NOT IMPLEMENTED (maybe not needed)\n"); } } static gint frame_widget_button_events (GtkWidget *widget, GdkEvent *event) { if(gap_debug) printf("frame_widget_button_events NOT IMPLEMENTED\n"); return -1; } /***********************/ /* Preview functions */ /***********************/ static gint render_current_preview (GtkWidget *preview_widget, FrameWidget *fw) { guchar *l_rowbuf; guchar *l_ptr; gint32 l_x, l_y; gint32 l_ofx, l_ofy; gint32 l_ofs_thpixel; guchar *l_thumbdata_ptr; gint32 l_thumbdata_count; gint32 l_th_width; gint32 l_th_height; gint32 l_th_bpp; gint32 l_thumb_rowstride; gint32 l_pv_width; gint32 l_pv_height; gdouble l_xscale; gdouble l_yscale; gdouble l_ofd; guchar l_alpha; gint l_1; gint l_offset; gint l_rc; if(gap_debug) printf("render_current_preview START\n"); l_rc = FALSE; if(naviD == NULL) return (l_rc); if(naviD->ainfo_ptr == NULL) return(l_rc); l_thumbdata_ptr = NULL; l_pv_width = naviD->image_width; l_pv_height = naviD->image_height; if(gap_debug) printf("render_current_preview BEFORE p_gimp_image_thumbnail w:%d h:%d,\n" ,(int)l_pv_width, (int)l_pv_height); p_gimp_image_thumbnail(naviD->active_imageid, l_pv_width, l_pv_height, &l_th_width, &l_th_height, &l_th_bpp, &l_thumbdata_count, &l_thumbdata_ptr); if(gap_debug) printf("render_current_preview AFTER p_gimp_image_thumbnail th_w:%d th_h:%d, th_bpp:%d, count:%d\n" ,(int)l_th_width, (int)l_th_height, (int)l_th_bpp, (int)l_thumbdata_count); if(l_th_bpp < 3) l_1 = 0; else l_1 = 1; l_rowbuf = g_malloc(PREVIEW_BPP * l_pv_width); if(l_rowbuf == NULL) return(l_rc); if(l_thumbdata_ptr != NULL) { l_xscale = (gdouble)l_th_width / (gdouble)l_pv_width; l_yscale = (gdouble)l_th_height / (gdouble)l_pv_height; l_thumb_rowstride = l_th_bpp * l_th_width; /* render preview */ for(l_y = 0; l_y < l_pv_height; l_y++) { l_ptr = l_rowbuf; l_ofd = (l_yscale * (gdouble)l_y) + 0.5; l_ofy = (gint32)(l_ofd) * l_thumb_rowstride; if(l_y & 0x4) l_offset = 4; else l_offset = 0; if((l_th_bpp > 3) || (l_th_bpp == 2)) { /* thumbdata has alpha channel */ for(l_x = 0; l_x < l_pv_width; l_x++) { l_ofd = (l_xscale * (gdouble)l_x) + 0.5; l_ofx = (gint32)(l_ofd) * l_th_bpp; l_ofs_thpixel = l_ofy + l_ofx; l_alpha = l_thumbdata_ptr[l_ofs_thpixel + (l_th_bpp -1)]; if((l_x + l_offset) & 0x4) { *l_ptr = MIX_CHANNEL (PREVIEW_BG_GRAY2, l_thumbdata_ptr[l_ofs_thpixel], l_alpha); l_ptr[1] = MIX_CHANNEL (PREVIEW_BG_GRAY2, l_thumbdata_ptr[l_ofs_thpixel + l_1], l_alpha); l_ptr[2] = MIX_CHANNEL (PREVIEW_BG_GRAY2, l_thumbdata_ptr[l_ofs_thpixel + l_1 + l_1], l_alpha); } else { *l_ptr = MIX_CHANNEL (PREVIEW_BG_GRAY1, l_thumbdata_ptr[l_ofs_thpixel], l_alpha); l_ptr[1] = MIX_CHANNEL (PREVIEW_BG_GRAY1, l_thumbdata_ptr[l_ofs_thpixel + l_1], l_alpha); l_ptr[2] = MIX_CHANNEL (PREVIEW_BG_GRAY1, l_thumbdata_ptr[l_ofs_thpixel + l_1 + l_1], l_alpha); } l_ptr += PREVIEW_BPP; } } else { for(l_x = 0; l_x < l_pv_width; l_x++) { l_ofd = (l_xscale * (gdouble)l_x) + 0.5; l_ofx = (gint32)(l_ofd) * l_th_bpp; l_ofs_thpixel = l_ofy + l_ofx; *l_ptr = l_thumbdata_ptr[l_ofs_thpixel]; l_ptr[1] = l_thumbdata_ptr[l_ofs_thpixel + l_1]; l_ptr[2] = l_thumbdata_ptr[l_ofs_thpixel + l_1 + l_1]; l_ptr += PREVIEW_BPP; } } gtk_preview_draw_row(GTK_PREVIEW(preview_widget), l_rowbuf, 0, l_y, l_pv_width); } l_rc = TRUE; } if(l_rowbuf) g_free(l_rowbuf); if(l_thumbdata_ptr) g_free(l_thumbdata_ptr); if(gap_debug) printf("render_current_preview END\n"); return (l_rc); } /* end render_current_preview */ static gint render_preview (GtkWidget *preview_widget, FrameWidget *fw) { guchar *l_rowbuf; guchar *l_ptr; gint32 l_x, l_y; gint32 l_ofx, l_ofy; gint32 l_ofs_thpixel; gint32 l_th_width; gint32 l_th_height; gint32 l_thumb_rowstride; gint32 l_pv_width; gint32 l_pv_height; gdouble l_xscale; gdouble l_yscale; gdouble l_ofd; char *l_filename; gint l_rc; guchar *l_raw_thumb; gchar *l_tname; gchar *imginfo = NULL; struct stat l_stat_thumb; if(gap_debug) printf("render_preview (quick)\n"); l_rc = FALSE; l_raw_thumb = NULL; if(naviD == NULL) return (l_rc); if(naviD->ainfo_ptr == NULL) return(l_rc); if(naviD->ainfo_ptr->curr_frame_nr == fw->frame_nr) { /* if this is the currently open image * we first write the thumbnail to disc * so we get an up to date version in the following readXVThumb call */ l_rc = render_current_preview(preview_widget, fw); return(l_rc); } l_filename = p_alloc_fname(naviD->ainfo_ptr->basename, fw->frame_nr, naviD->ainfo_ptr->extension); l_tname = p_alloc_fname_thumbnail(l_filename); if (0 == stat(l_tname, &l_stat_thumb)) { fw->thumb_timestamp = l_stat_thumb.st_mtime; l_raw_thumb = readXVThumb (l_tname, &l_th_width, &l_th_height, &imginfo); if(fw->thumb_filename) g_free(fw->thumb_filename); fw->thumb_filename = l_tname; } else { g_free(l_tname); } l_pv_width = naviD->image_width; l_pv_height = naviD->image_height; l_rowbuf = g_malloc(PREVIEW_BPP * l_pv_width); if(l_rowbuf == NULL) return(l_rc); if(l_raw_thumb != NULL) { l_xscale = (gdouble)l_th_width / (gdouble)l_pv_width; l_yscale = (gdouble)l_th_height / (gdouble)l_pv_height; l_thumb_rowstride = l_th_width; /* raw thumb data BPP is 1 */ /* render preview */ for(l_y = 0; l_y < l_pv_height; l_y++) { l_ptr = l_rowbuf; l_ofd = (l_yscale * (gdouble)l_y) + 0.5; l_ofy = (gint32)(l_ofd) * l_thumb_rowstride; /* check if > thumbnail height !!! */ for(l_x = 0; l_x < l_pv_width; l_x++) { l_ofd = (l_xscale * (gdouble)l_x) + 0.5; l_ofx = (gint32)(l_ofd); /* raw thumb data BPP is 1 */ l_ofs_thpixel = l_ofy + l_ofx; *l_ptr = ((l_raw_thumb[l_ofs_thpixel]>>5)*255)/7; l_ptr[1] = (((l_raw_thumb[l_ofs_thpixel]>>2)&7)*255)/7; l_ptr[2] = (((l_raw_thumb[l_ofs_thpixel])&3)*255)/3; l_ptr += PREVIEW_BPP; } gtk_preview_draw_row(GTK_PREVIEW(preview_widget), l_rowbuf, 0, l_y, l_pv_width); } l_rc = TRUE; } if(l_rowbuf) g_free(l_rowbuf); if(l_raw_thumb) g_free(l_raw_thumb); if(l_filename) g_free(l_filename); return (l_rc); } /* end render_preview */ void render_no_preview (GtkWidget *widget, GdkPixmap *pixmap) { int w, h; int x1, y1, x2, y2; GdkPoint poly[6]; int foldh, foldw; int i; gdk_drawable_get_size (pixmap, &w, &h); x1 = 2; y1 = h / 8 + 2; x2 = w - w / 8 - 2; y2 = h - 2; gdk_draw_rectangle (pixmap, widget->style->bg_gc[GTK_STATE_NORMAL], 1, 0, 0, w, h); /* gdk_draw_rectangle (pixmap, widget->style->black_gc, 0, x1, y1, (x2 - x1), (y2 - y1)); */ foldw = w / 4; foldh = h / 4; x1 = w / 8 + 2; y1 = 2; x2 = w - 2; y2 = h - h / 8 - 2; poly[0].x = x1 + foldw; poly[0].y = y1; poly[1].x = x1 + foldw; poly[1].y = y1 + foldh; poly[2].x = x1; poly[2].y = y1 + foldh; poly[3].x = x1; poly[3].y = y2; poly[4].x = x2; poly[4].y = y2; poly[5].x = x2; poly[5].y = y1; gdk_draw_polygon (pixmap, widget->style->white_gc, 1, poly, 6); gdk_draw_line (pixmap, widget->style->black_gc, x1, y1 + foldh, x1, y2); gdk_draw_line (pixmap, widget->style->black_gc, x1, y2, x2, y2); gdk_draw_line (pixmap, widget->style->black_gc, x2, y2, x2, y1); gdk_draw_line (pixmap, widget->style->black_gc, x1 + foldw, y1, x2, y1); for (i = 0; i < foldw; i++) { gdk_draw_line (pixmap, widget->style->black_gc, x1 + i, y1 + foldh, x1 + i, (foldw == 1) ? y1 : (y1 + (foldh - (foldh * i) / (foldw - 1)))); } } static void frame_widget_preview_redraw (FrameWidget *frame_widget) { GdkPixmap **pixmap; GtkWidget *widget; widget = frame_widget->frame_preview; pixmap = &frame_widget->frame_pixmap; if(gap_debug) printf("frame_widget_preview_redraw image_id: %d, frame_nr:%d\n" ,(int)frame_widget->image_id ,(int)frame_widget->frame_nr); /* determine width and height */ if((frame_widget->width != naviD->image_width) || (frame_widget->height != naviD->image_height)) { frame_widget->width = naviD->image_width; frame_widget->height = naviD->image_height; if (frame_widget->width < 1) frame_widget->width = 1; if (frame_widget->height < 1) frame_widget->height = 1; gtk_drawing_area_size (GTK_DRAWING_AREA (frame_widget->frame_preview), naviD->image_width + 4, naviD->image_height + 4); if(*pixmap) { gdk_drawable_unref(*pixmap); *pixmap = NULL; } } /* allocate the layer widget pixmap */ if (! *pixmap) { *pixmap = gdk_pixmap_new (widget->window, naviD->image_width, naviD->image_height, -1); } if(naviD->preview_size < 1) { /* preview_size is none, render a default icon */ render_no_preview (widget, *pixmap); } else { if(TRUE == render_preview (naviD->frame_preview, frame_widget)) { gtk_preview_put (GTK_PREVIEW (naviD->frame_preview), *pixmap, widget->style->black_gc, 0, 0, 0, 0, naviD->image_width, naviD->image_height); } else { /* frame has no thumbnail (.xvpics), render a default icon */ render_no_preview (widget, *pixmap); } } /* make sure the image has been transfered completely to the pixmap before * we use it again... */ gdk_flush (); } /* end frame_widget_preview_redraw */ static gint frame_widget_preview_events (GtkWidget *widget, GdkEvent *event) { GdkEventExpose *eevent; GdkPixmap **pixmap; GdkEventButton *bevent; FrameWidget *frame_widget; int valid; int sx, sy, dx, dy, w, h; pixmap = NULL; valid = FALSE; frame_widget = (FrameWidget *) gtk_object_get_user_data (GTK_OBJECT (widget)); if (frame_widget->frame_nr < 0) { return FALSE; } pixmap = &frame_widget->frame_pixmap; /* valid = GIMP_DRAWABLE(frame_widget->layer)->preview_valid; */ /* ?? */ switch (event->type) { case GDK_2BUTTON_PRESS: navi_dialog_goto_callback(frame_widget->frame_nr); return TRUE; case GDK_BUTTON_PRESS: /* Control-button press disables the application of the mask */ bevent = (GdkEventButton *) event; if(gap_debug) printf("frame_widget_preview_events GDK_BUTTON_PRESS button:%d\n" , (int)bevent->button); if (bevent->button == 3) { navi_ops_menu_set_sensitive(); gtk_menu_popup (GTK_MENU (naviD->ops_menu), NULL, NULL, NULL, NULL, 3, bevent->time); return TRUE; } if (event->button.state & GDK_CONTROL_MASK) { } /* Alt-button press makes the mask visible instead of the layer */ else if (event->button.state & GDK_MOD1_MASK) { } break; case GDK_EXPOSE: if(gap_debug) printf("frame_widget_preview_events GDK_EXPOSE\n"); navi_preview_extents(); frame_widget_time_label_update(frame_widget); if (!valid || !*pixmap) { frame_widget_preview_redraw (frame_widget); gdk_draw_drawable (widget->window, widget->style->black_gc, *pixmap, 0, 0, 2, 2, naviD->image_width, naviD->image_height); } else { eevent = (GdkEventExpose *) event; w = eevent->area.width; h = eevent->area.height; if (eevent->area.x < 2) { sx = eevent->area.x; dx = 2; w -= (2 - eevent->area.x); } else { sx = eevent->area.x - 2; dx = eevent->area.x; } if (eevent->area.y < 2) { sy = eevent->area.y; dy = 2; h -= (2 - eevent->area.y); } else { sy = eevent->area.y - 2; dy = eevent->area.y; } if ((sx + w) >= naviD->image_width) { w = naviD->image_width - sx; } if ((sy + h) >= naviD->image_height) { h = naviD->image_height - sy; } if ((w > 0) && (h > 0)) { gdk_draw_drawable (widget->window, widget->style->black_gc, *pixmap, sx, sy, dx, dy, w, h); } } /* The boundary indicating whether layer or mask is active */ /* frames do not have boundary */ /* frame_widget_boundary_redraw (frame_widget); */ break; default: break; } return FALSE; } /* end frame_widget_preview_events */ static gint navi_dialog_poll(GtkWidget *w, gpointer data) { gint32 frame_nr; gint32 update_flag; gint32 video_preview_size; if(gap_debug) printf("navi_dialog_poll TIMER POLL\n"); if(naviD) { if(suspend_gimage_notify == 0) { update_flag = NUPD_IMAGE_MENU; video_preview_size = navi_get_preview_size(); if(naviD->preview_size != video_preview_size) { naviD->preview_size = video_preview_size; update_flag = NUPD_ALL; } /* check and enable/disable tooltips */ navi_dialog_tooltips (); frame_nr = p_get_frame_nr(naviD->active_imageid); if(frame_nr < 0 ) { /* no valid frame number, maybe frame was closed */ naviD->active_imageid = -1; update_flag = NUPD_ALL; } else { if(naviD->ainfo_ptr) { update_flag = NUPD_IMAGE_MENU | NUPD_FRAME_NR_CHANGED; } else { update_flag = NUPD_ALL; } } navi_dialog_update(update_flag); } /* restart timer */ naviD->timer = gtk_timeout_add(naviD->cycle_time, (GtkFunction)navi_dialog_poll, NULL); } return FALSE; } static void navid_thumb_timestamp_check(void) { struct stat l_stat_thumb; FrameWidget *fw; GSList *list; GtkAdjustment *adj; gint item_count; if(naviD == NULL) return; if(naviD->ainfo_ptr == NULL) return; adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (naviD->scrolled_win)); if(gap_debug) printf("navid_thumb_timestamp_check: adj->value:%d adj->page_size:%d\n", (int)adj->value, (int)adj->page_size); list = naviD->frame_widgets; item_count = -1; while (list) { item_count++; fw = (FrameWidget *) list->data; list = g_slist_next (list); if((item_count * naviD->item_height) < (adj->value - naviD->image_height)) { continue; /* WIDGET IS NOT EXPOSED */ } if((item_count * naviD->item_height) > (adj->value + adj->page_size)) { return; /* WIDGET (and all further widgets) ARE NOT EXPOSED */ } if(gap_debug) printf("navid_thumb_timestamp_check: Widget IS EXPOSED frame_nr:%04d\n", (int)fw->frame_nr); if(fw->frame_nr == naviD->ainfo_ptr->curr_frame_nr) { /* always redraw the currently opened frame * (the render procedure uses gimp's internal chached thumbnaildata * so the timestamp of the .xvpics thumbnailfile does not matter here) */ gtk_widget_draw(fw->frame_preview, NULL); continue; } /* for the other exposed items we check the tumbnail timestamp */ if(fw->thumb_filename) { if (0 == stat(fw->thumb_filename, &l_stat_thumb)) { if(fw->thumb_timestamp < l_stat_thumb.st_mtime) { gtk_widget_draw(fw->frame_preview, NULL); } } } } } static void navi_dialog_update(gint32 update_flag) { gint32 l_first, l_last; gint l_image_menu_was_changed; l_image_menu_was_changed = FALSE; if(update_flag & NUPD_IMAGE_MENU) { l_image_menu_was_changed = navi_refresh_image_menu(); } if(update_flag & NUPD_FRAME_NR_CHANGED) { l_first = -1; l_last = -1; if(naviD->ainfo_ptr) { l_first = naviD->ainfo_ptr->first_frame_nr; l_last = naviD->ainfo_ptr->last_frame_nr; } navi_reload_ainfo(naviD->active_imageid); navi_preview_extents(); /* we must force a rebuild of the list of frame_widgets * if any frames were deleteted or are created * (outside of the naviagator) */ if(naviD->ainfo_ptr) { if((l_first != naviD->ainfo_ptr->first_frame_nr) || (l_last != naviD->ainfo_ptr->last_frame_nr)) { update_flag |= NUPD_PREV_LIST; } } } if(update_flag & NUPD_PREV_LIST) { frames_dialog_flush(); } else { navid_thumb_timestamp_check(); } } /* ------------------------ * dialog helper procedures * ------------------------ */ static void navi_preview_extents (void) { gint32 width, height; if (!naviD) return; naviD->gimage_width = gimp_image_width(naviD->active_imageid); naviD->gimage_height = gimp_image_height(naviD->active_imageid); /* Get the image width and height variables, based on the gimage */ if (naviD->gimage_width > naviD->gimage_height) { naviD->ratio = (double) naviD->preview_size / (double) naviD->gimage_width; } else { naviD->ratio = (double) naviD->preview_size / (double) naviD->gimage_height; } if (naviD->preview_size > 0) { width = (int) (naviD->ratio * naviD->gimage_width); height = (int) (naviD->ratio * naviD->gimage_height); if (width < 1) width = 1; if (height < 1) height = 1; } else { width = 16; height = 10; } if((naviD->image_width != width) || (naviD->image_height != height)) { naviD->image_width = width; naviD->image_height = height; gtk_preview_size (GTK_PREVIEW (naviD->frame_preview), naviD->image_width, naviD->image_height); } naviD->item_height = 6 + naviD->image_height; if(gap_debug) printf("navi_preview_extents w: %d h:%d\n", (int)naviD->image_width, (int)naviD->image_height); } static void navi_calc_frametiming(gint32 frame_nr, char *buf, gint32 sizeof_buf) { gint32 first; gdouble msec_per_frame; gint32 tmsec; gint32 tms; gint32 tsec; gint32 tmin; first = frame_nr; if(naviD->ainfo_ptr) { first = naviD->ainfo_ptr->first_frame_nr; } if(naviD->vin_ptr == NULL) { g_snprintf(buf, sizeof_buf, "min:sec:msec"); return; } if(naviD->vin_ptr->framerate < 1) { g_snprintf(buf, sizeof_buf, "min:sec:msec"); return; } msec_per_frame = 1000.0 / naviD->vin_ptr->framerate; tmsec = (frame_nr - first) * msec_per_frame; tms = tmsec % 1000; tsec = (tmsec / 1000) % 60; tmin = tmsec / 60000; g_snprintf(buf, sizeof_buf, "%02d:%02d:%03d", (int)tmin, (int)tsec, (int)tms); } static void frame_widget_time_label_update(FrameWidget *fw) { char frame_nr_to_time[20]; navi_calc_frametiming(fw->frame_nr, frame_nr_to_time, sizeof(frame_nr_to_time)); gtk_label_set_text (GTK_LABEL (fw->time_label), frame_nr_to_time); } static FrameWidget * frame_widget_create (gint32 image_id, gint32 frame_nr) { FrameWidget *frame_widget; GtkWidget *list_item; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *alignment; char frame_nr_to_char[20]; char frame_nr_to_time[20]; if(gap_debug) printf("frame_widget_create CREATE image_id:%d, nr:%d\n", (int)image_id, (int)frame_nr); list_item = gtk_list_item_new (); /* create the frame widget and add it to the list */ frame_widget = g_new (FrameWidget, 1); frame_widget->image_id = image_id; frame_widget->frame_nr = frame_nr; frame_widget->frame_preview = NULL; frame_widget->frame_pixmap = NULL; frame_widget->list_item = list_item; frame_widget->width = -1; frame_widget->height = -1; frame_widget->thumb_timestamp = 0; frame_widget->thumb_filename = NULL; frame_widget->visited = TRUE; /* frame_widget->drop_type = GIMP_DROP_NONE; */ g_snprintf(frame_nr_to_char, sizeof(frame_nr_to_char), "%04d", (int)frame_nr); navi_calc_frametiming(frame_nr, frame_nr_to_time, sizeof(frame_nr_to_time)); /* Need to let the list item know about the frame_widget */ gtk_object_set_user_data (GTK_OBJECT (list_item), frame_widget); /* set up the list item observer */ gtk_signal_connect (GTK_OBJECT (list_item), "select", (GtkSignalFunc) frame_widget_select_update, frame_widget); gtk_signal_connect (GTK_OBJECT (list_item), "deselect", (GtkSignalFunc) frame_widget_select_update, frame_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); /* the frame number label */ frame_widget->number_label = gtk_label_new (frame_nr_to_char); gtk_box_pack_start (GTK_BOX (hbox), frame_widget->number_label, FALSE, FALSE, 2); gtk_widget_show (frame_widget->number_label); /* The frame 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); frame_widget->frame_preview = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (frame_widget->frame_preview), naviD->image_width + 4, naviD->image_height + 4); gtk_widget_set_events (frame_widget->frame_preview, PREVIEW_EVENT_MASK); gtk_signal_connect (GTK_OBJECT (frame_widget->frame_preview), "event", (GtkSignalFunc) frame_widget_preview_events, frame_widget); gtk_object_set_user_data (GTK_OBJECT (frame_widget->frame_preview), frame_widget); gtk_container_add (GTK_CONTAINER (alignment), frame_widget->frame_preview); gtk_widget_show (frame_widget->frame_preview); /* the frame timing label */ frame_widget->time_label = gtk_label_new (frame_nr_to_time); gtk_box_pack_start (GTK_BOX (hbox), frame_widget->time_label, FALSE, FALSE, 2); gtk_widget_show (frame_widget->time_label); frame_widget->clip_widget = gtk_drawing_area_new (); gtk_drawing_area_size (GTK_DRAWING_AREA (frame_widget->clip_widget), 1, 2); gtk_widget_set_events (frame_widget->clip_widget, BUTTON_EVENT_MASK); gtk_signal_connect (GTK_OBJECT (frame_widget->clip_widget), "event", (GtkSignalFunc) frame_widget_button_events, frame_widget); gtk_object_set_user_data (GTK_OBJECT (frame_widget->clip_widget), frame_widget); gtk_box_pack_start (GTK_BOX (vbox), frame_widget->clip_widget, FALSE, FALSE, 0); /* gtk_widget_show (frame_widget->clip_widget); */ gtk_object_set_data (GTK_OBJECT (list_item), "gap_framenumber", (gpointer)frame_nr ); gtk_widget_show (hbox); gtk_widget_show (vbox); gtk_widget_show (list_item); g_object_ref (frame_widget->list_item); if(gap_debug) printf("frame_widget_create END image_id:%d, nr:%d\n", (int)image_id, (int)frame_nr); return frame_widget; } static void frame_widget_delete (FrameWidget *fw) { if(gap_debug) printf("frame_widget_delete image_id:%d frame_nr:%d\n" ,(int)fw->image_id ,(int)fw->frame_nr ); if(fw->frame_pixmap) { gdk_drawable_unref(fw->frame_pixmap); } if(fw->thumb_filename) g_free(fw->thumb_filename); /* Remove the layer widget from the list */ naviD->frame_widgets = g_slist_remove (naviD->frame_widgets, fw); /* Release the widget */ g_object_unref(fw->list_item); g_free(fw); } GtkWidget * navi_dialog_create (GtkWidget* shell, gint32 image_id) { GtkWidget *vbox; GtkWidget *util_box; GtkWidget *button_box; GtkWidget *label; GtkWidget *slider; GtkWidget *menu_item; char *l_basename; char frame_nr_to_char[20]; if(gap_debug) printf("navi_dialog_create\n"); if (naviD) { return naviD->vbox; } l_basename = NULL; g_snprintf(frame_nr_to_char, sizeof(frame_nr_to_char), "0000 - 0000"); naviD = g_new (NaviDialog, 1); naviD->waiting_cursor = FALSE; naviD->cursor_wait = gdk_cursor_new (GDK_WATCH); naviD->cursor_acitve = gdk_cursor_new (GDK_TOP_LEFT_ARROW); naviD->shell = shell; naviD->frame_preview = NULL; naviD->OpenFrameImagesList = NULL; naviD->OpenFrameImagesCount = 0; naviD->any_imageid = -1; naviD->frame_widgets = NULL; naviD->cycle_time = 1000; /* polling cylcle of 1 sec */ naviD->timer = 0; naviD->active_imageid = image_id; /* naviD->ainfo_ptr = navi_get_ainfo(naviD->active_imageid, NULL); */ naviD->ainfo_ptr = NULL; naviD->framerange_number_label = NULL; navi_reload_ainfo_force(image_id); if(naviD->ainfo_ptr != NULL) { g_snprintf(frame_nr_to_char, sizeof(frame_nr_to_char), "%04d - %04d" , (int)naviD->ainfo_ptr->first_frame_nr , (int)naviD->ainfo_ptr->last_frame_nr); l_basename = naviD->ainfo_ptr->basename; } naviD->vin_ptr = p_get_video_info(l_basename); naviD->image_width = 0; naviD->image_height = 0; naviD->preview_size = navi_get_preview_size(); if (naviD->preview_size > 0) { naviD->frame_preview = gtk_preview_new (GTK_PREVIEW_COLOR); navi_preview_extents (); } /* creates tooltips */ gimp_help_init (); /* The main vbox */ naviD->vbox = gtk_event_box_new (); vbox = gtk_vbox_new (FALSE, 1); gtk_container_set_border_width (GTK_CONTAINER (vbox), 2); gtk_container_add (GTK_CONTAINER (naviD->vbox), vbox); /* The image menu */ util_box = gtk_hbox_new (FALSE, 1); gtk_box_pack_start (GTK_BOX (vbox), util_box, FALSE, FALSE, 0); /* The popup menu (copy/cut/paste) */ naviD->ops_menu = gtk_menu_new (); /* menu_item copy */ menu_item = gtk_menu_item_new_with_label (_("Copy")); gtk_container_add (GTK_CONTAINER (naviD->ops_menu), menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc) edit_copy_callback, naviD); gtk_widget_show (menu_item); naviD->copy_menu_item = menu_item; /* menu_item cut */ menu_item = gtk_menu_item_new_with_label (_("Cut")); gtk_container_add (GTK_CONTAINER (naviD->ops_menu), menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc) edit_cut_callback, naviD); gtk_widget_show (menu_item); naviD->cut_menu_item = menu_item; /* menu_item paste before */ menu_item = gtk_menu_item_new_with_label (_("Paste before")); gtk_container_add (GTK_CONTAINER (naviD->ops_menu), menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc) edit_pasteb_callback, naviD); gtk_widget_show (menu_item); naviD->pasteb_menu_item = menu_item; /* menu_item copy */ menu_item = gtk_menu_item_new_with_label (_("Paste after")); gtk_container_add (GTK_CONTAINER (naviD->ops_menu), menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc) edit_pastea_callback, naviD); gtk_widget_show (menu_item); naviD->pastea_menu_item = menu_item; /* menu_item copy */ menu_item = gtk_menu_item_new_with_label (_("Paste replace")); gtk_container_add (GTK_CONTAINER (naviD->ops_menu), menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc) edit_paster_callback, naviD); gtk_widget_show (menu_item); naviD->paster_menu_item = menu_item; /* menu_item copy */ menu_item = gtk_menu_item_new_with_label (_("Clear Video Buffer")); gtk_container_add (GTK_CONTAINER (naviD->ops_menu), menu_item); gtk_signal_connect (GTK_OBJECT (menu_item), "activate", (GtkSignalFunc) edit_clrpaste_callback, naviD); gtk_widget_show (menu_item); naviD->clrpaste_menu_item = menu_item; gtk_widget_show (naviD->ops_menu); /* The image menu */ naviD->image_option_menu = gtk_option_menu_new(); naviD->image_menu = NULL; navi_refresh_image_menu(); gtk_box_pack_start (GTK_BOX (util_box), naviD->image_option_menu, FALSE, FALSE, 0); gtk_widget_show (naviD->image_option_menu); gtk_widget_show (naviD->image_menu); gtk_widget_show (util_box); /* the Framerange label */ util_box = gtk_hbox_new (FALSE, 1); gtk_box_pack_start (GTK_BOX (vbox), util_box, FALSE, FALSE, 0); label = gtk_label_new (_("Videoframes:")); gtk_box_pack_start (GTK_BOX (util_box), label, FALSE, FALSE, 2); gtk_widget_show (label); /* the max frame number label */ naviD->framerange_number_label = gtk_label_new (frame_nr_to_char); gtk_box_pack_start (GTK_BOX (util_box), naviD->framerange_number_label, FALSE, FALSE, 2); gtk_widget_show (naviD->framerange_number_label); gtk_widget_show (util_box); /* framerate scale */ naviD->framerate_box = util_box = gtk_hbox_new (FALSE, 1); gtk_box_pack_start (GTK_BOX (vbox), util_box, FALSE, FALSE, 0); label = gtk_label_new (_("Framerate:")); gtk_box_pack_start (GTK_BOX (util_box), label, FALSE, FALSE, 2); gtk_widget_show (label); naviD->framerate_data = GTK_ADJUSTMENT (gtk_adjustment_new (naviD->vin_ptr->framerate, 1.0, 100.0, 1.0, 1.0, 0.0)); slider = gtk_hscale_new (naviD->framerate_data); gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); /* GTK_UPDATE_CONTINUOUS */ gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_RIGHT); gtk_box_pack_start (GTK_BOX (util_box), slider, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (naviD->framerate_data), "value_changed", (GtkSignalFunc) navi_framerate_scale_update, naviD); gtk_widget_show (slider); gimp_help_set_help_data (slider, NULL, "#framerate_scale"); gtk_widget_show (util_box); /* timezoom scale */ naviD->timezoom_box = util_box = gtk_hbox_new (FALSE, 1); gtk_box_pack_start (GTK_BOX (vbox), util_box, FALSE, FALSE, 0); label = gtk_label_new (_("Timezoom:")); gtk_box_pack_start (GTK_BOX (util_box), label, FALSE, FALSE, 2); gtk_widget_show (label); naviD->timezoom_data = GTK_ADJUSTMENT (gtk_adjustment_new ((gdouble)naviD->vin_ptr->timezoom, 1.0, 100.0, 1.0, 1.0, 0.0)); slider = gtk_hscale_new (naviD->timezoom_data); gtk_range_set_update_policy (GTK_RANGE (slider), GTK_UPDATE_DELAYED); gtk_scale_set_value_pos (GTK_SCALE (slider), GTK_POS_RIGHT); gtk_box_pack_start (GTK_BOX (util_box), slider, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (naviD->timezoom_data), "value_changed", (GtkSignalFunc) navi_timezoom_scale_update, naviD); gtk_widget_show (slider); gimp_help_set_help_data (slider, NULL, "#timezoom_scale"); gtk_widget_show (util_box); /* The frames listbox */ naviD->scrolled_win = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (naviD->scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_widget_set_usize (naviD->scrolled_win, LIST_WIDTH, LIST_HEIGHT); gtk_box_pack_start (GTK_BOX (vbox), naviD->scrolled_win, TRUE, TRUE, 2); naviD->frame_list = gtk_list_new (); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (naviD->scrolled_win), naviD->frame_list); gtk_list_set_selection_mode (GTK_LIST (naviD->frame_list), GTK_SELECTION_EXTENDED); /* GTK_SELECTION_BROWSE */ gtk_signal_connect (GTK_OBJECT (naviD->frame_list), "event", (GtkSignalFunc) frame_list_events, naviD); gtk_container_set_focus_vadjustment (GTK_CONTAINER (naviD->frame_list), gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (naviD->scrolled_win))); GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (naviD->scrolled_win)->vscrollbar, GTK_CAN_FOCUS); gtk_widget_show (naviD->frame_list); gtk_widget_show (naviD->scrolled_win); /* The ops buttons */ button_box = ops_button_box_new (naviD->shell, frames_ops_buttons, OPS_BUTTON_NORMAL); gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 2); gtk_widget_show (button_box); /* The VCR ops buttons */ button_box = ops_button_box_new (naviD->shell, vcr_ops_buttons, OPS_BUTTON_NORMAL); gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 2); gtk_widget_show (button_box); gtk_widget_show (vbox); gtk_widget_show (naviD->vbox); if(gap_debug) printf("navi_dialog_create END\n"); return naviD->vbox; } /* --------------------------------- * the navigator MAIN dialog * --------------------------------- */ int gap_navigator(gint32 image_id) { GtkWidget *shell; GtkWidget *button; GtkWidget *subshell; gint argc = 1; gchar **argv = g_new (gchar *, 1); argv[0] = g_strdup ("gap_navigator"); if(gap_debug) fprintf(stderr, "\nSTARTing gap_navigator_dialog\n"); /* Init GTK */ gtk_init (&argc, &argv); gtk_rc_parse (gimp_gtkrc ()); /* The main shell */ shell = gimp_dialog_new (_("Video Navigator"), "gap_navigator", gimp_standard_help_func, "filters/gap_navigator_dialog.html", GTK_WIN_POS_NONE, FALSE, TRUE, FALSE, NULL); gtk_signal_connect (GTK_OBJECT (shell), "delete_event", GTK_SIGNAL_FUNC (gtk_widget_destroy), NULL); gtk_signal_connect (GTK_OBJECT (shell), "destroy", GTK_SIGNAL_FUNC (gtk_main_quit), NULL); /* The subshell (toplevel vbox) */ subshell = gtk_vbox_new (FALSE, 1); gtk_container_add (GTK_CONTAINER (GTK_DIALOG (shell)->vbox), subshell); if(gap_debug) printf("BEFORE navi_dialog_create\n"); /* The naviD dialog structure */ navi_dialog_create (shell, image_id); if(gap_debug) printf("AFTER navi_dialog_create\n"); gtk_box_pack_start (GTK_BOX (subshell), naviD->vbox, TRUE, TRUE, 0); /* The action area */ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (shell)->action_area), 1); /* The close button */ button = gtk_button_new_from_stock (GTK_STOCK_CLOSE); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (shell)); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->action_area), button, TRUE, TRUE, 0); gtk_widget_show (button); gtk_widget_show (subshell); gtk_widget_show (shell); frames_dialog_flush(); navi_scroll_to_current_frame_nr(); naviD->timer = gtk_timeout_add(naviD->cycle_time, (GtkFunction)navi_dialog_poll, NULL); if(gap_debug) printf("BEFORE gtk_main\n"); gtk_main (); gdk_flush (); if(gap_debug) printf("END gap_navigator_dialog\n"); return 0; } /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * Some Code Parts copied from gimp/app directory * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ /* --------------------------------------- start copy of gimp-1.1.14/app/fileops.c readXVThumb */ /* The readXVThumb function source may be re-used under the XFree86-style license. */ guchar* readXVThumb (const gchar *fnam, gint *w, gint *h, gchar **imginfo /* caller frees if != NULL */) { FILE *fp; const gchar *P7_332 = "P7 332"; gchar P7_buf[7]; gchar linebuf[200]; guchar *buf; gint twofivefive; void *ptr; *w = *h = 0; *imginfo = NULL; fp = fopen (fnam, "rb"); if (!fp) return (NULL); fread (P7_buf, 6, 1, fp); if (strncmp(P7_buf, P7_332, 6)!=0) { g_warning("Thumbnail doesn't have the 'P7 332' header."); fclose(fp); return(NULL); } /*newline*/ fread (P7_buf, 1, 1, fp); do { ptr = fgets(linebuf, 199, fp); if ((strncmp(linebuf, "#IMGINFO:", 9) == 0) && (linebuf[9] != '\0') && (linebuf[9] != '\n')) { if (linebuf[strlen(linebuf)-1] == '\n') linebuf[strlen(linebuf)-1] = '\0'; if (linebuf[9] != '\0') { if (*imginfo) g_free(*imginfo); *imginfo = g_strdup (&linebuf[9]); } } } while (ptr && linebuf[0]=='#'); /* keep throwing away comment lines */ if (!ptr) { /* g_warning("Thumbnail ended - not an image?"); */ fclose(fp); return(NULL); } sscanf(linebuf, "%d %d %d\n", w, h, &twofivefive); if (twofivefive!=255) { g_warning("Thumbnail is of funky depth."); fclose(fp); return(NULL); } if ((*w)<1 || (*h)<1 || (*w)>80 || (*h)>60) { g_warning ("Thumbnail size bad. Corrupted?"); fclose(fp); return (NULL); } buf = g_malloc((*w)*(*h)); fread(buf, (*w)*(*h), 1, fp); fclose(fp); return(buf); } /* --------------------------------------- end copy of gimp-1.1.14/app/fileops.c readXVThumb */ /* --------------------------------------- start copy of gimp-1.1.14/app/ops_buttons.c */ static void ops_button_pressed_callback (GtkWidget*, GdkEventButton*, gpointer); static void ops_button_extended_callback (GtkWidget*, gpointer); GtkWidget * ops_button_box_new (GtkWidget *parent, OpsButton *ops_button, OpsButtonType ops_type) { GtkWidget *button; GtkWidget *button_box; GtkWidget *pixmap_widget; GdkPixmap *pixmap; GdkBitmap *mask; GtkStyle *style; GSList *group = NULL; gtk_widget_realize (parent); style = gtk_widget_get_style (parent); button_box = gtk_hbox_new (TRUE, 1); while (ops_button->xpm_data) { pixmap = gdk_pixmap_create_from_xpm_d (parent->window, &mask, &style->bg[GTK_STATE_NORMAL], ops_button->xpm_data); pixmap_widget = gtk_pixmap_new (pixmap, mask); switch (ops_type) { case OPS_BUTTON_NORMAL : button = gtk_button_new (); break; case OPS_BUTTON_RADIO : button = gtk_radio_button_new (group); group = gtk_radio_button_group (GTK_RADIO_BUTTON (button)); gtk_container_set_border_width (GTK_CONTAINER (button), 0); gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE); break; default : button = NULL; /*stop compiler complaints */ g_error ("ops_button_box_new: unknown type %d\n", ops_type); break; } gtk_container_add (GTK_CONTAINER (button), pixmap_widget); if (ops_button->ext_callbacks == NULL) { gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) ops_button->callback, NULL); } else { gtk_signal_connect (GTK_OBJECT (button), "button_press_event", (GtkSignalFunc) ops_button_pressed_callback, ops_button); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) ops_button_extended_callback, ops_button); } gimp_help_set_help_data (button, gettext (ops_button->tooltip), ops_button->private_tip); gtk_box_pack_start (GTK_BOX (button_box), button, TRUE, TRUE, 0); gtk_widget_show (pixmap_widget); gtk_widget_show (button); ops_button->widget = button; ops_button->modifier = OPS_BUTTON_MODIFIER_NONE; ops_button++; } return (button_box); } static void ops_button_pressed_callback (GtkWidget *widget, GdkEventButton *bevent, gpointer client_data) { OpsButton *ops_button; g_return_if_fail (client_data != NULL); ops_button = (OpsButton*)client_data; if (bevent->state & GDK_SHIFT_MASK) { if (bevent->state & GDK_CONTROL_MASK) ops_button->modifier = OPS_BUTTON_MODIFIER_SHIFT_CTRL; else ops_button->modifier = OPS_BUTTON_MODIFIER_SHIFT; } else if (bevent->state & GDK_CONTROL_MASK) ops_button->modifier = OPS_BUTTON_MODIFIER_CTRL; else if (bevent->state & GDK_MOD1_MASK) ops_button->modifier = OPS_BUTTON_MODIFIER_ALT; else ops_button->modifier = OPS_BUTTON_MODIFIER_NONE; } static void ops_button_extended_callback (GtkWidget *widget, gpointer client_data) { OpsButton *ops_button; g_return_if_fail (client_data != NULL); ops_button = (OpsButton*)client_data; if (ops_button->modifier > OPS_BUTTON_MODIFIER_NONE && ops_button->modifier < OPS_BUTTON_MODIFIER_LAST) { if (ops_button->ext_callbacks[ops_button->modifier - 1] != NULL) (ops_button->ext_callbacks[ops_button->modifier - 1]) (widget, NULL); else (ops_button->callback) (widget, NULL); } else (ops_button->callback) (widget, NULL); ops_button->modifier = OPS_BUTTON_MODIFIER_NONE; } /* --------------------------------------- end copy of gimp-1.1.14/app/ops_buttons.c */