diff --git a/ChangeLog b/ChangeLog index 9f731ccb02..a6810a4971 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +Tue Jan 11 01:50:41 CET 2000 Sven Neumann + + * libgimp/parasite.c + * app/gimpparasite.c + * app/parasitelist.c: indentation (actually I was searching for a + memleak, but had no luck so far) + + * app/blob.c: raise the maximum image size that will make the ink tool + fail to 16384 x 16384 (sort of fixes bug #4464) + + * Makefile.am + * pixmaps/first.xpm + * pixmaps/last.xpm + * pixmaps/next.xpm + * pixmaps/play.xpm + * pixmaps/prev.xpm + * pixmaps/update.xpm: new icons + + * plug-ins/gap/gap_decode_mpeg_main.c + * plug-ins/gap/gap_decode_xanim.c + * plug-ins/gap/gap_lib.c + * plug-ins/gap/gap_lib.h + * plug-ins/gap/gap_main.c + * plug-ins/gap/gap_pdb_calls.c + * plug-ins/gap/gap_pdb_calls.h + * plug-ins/gap/gap_range_ops.c: changes by Wolfgang Hofer + + * plug-ins/gap/Makefile.am + * plug-ins/gap/gap_navigator_dialog.c: new GAP VCR Navigator brought + to you by Wolfgang Hofer . Needs some work, but is + already very nice, IMHO + Mon Jan 10 01:13:02 PST 2000 Manish Singh * tools/pdbgen/pdb/gimage.pdb: Made appropriate changes to diff --git a/Makefile.am b/Makefile.am index e3cfd30f12..76c803de0e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,12 +41,15 @@ EXTRA_DIST = \ pixmaps/duplicate.xpm \ pixmaps/eek.xpm \ pixmaps/eye.xbm \ + pixmaps/first.xpm \ + pixmaps/last.xpm \ pixmaps/layer.xbm \ pixmaps/linked.xbm \ pixmaps/locked.xbm \ pixmaps/lower.xpm \ pixmaps/mask.xbm \ pixmaps/navbutton.xpm \ + pixmaps/next.xpm \ pixmaps/new.xpm \ pixmaps/no.xpm \ pixmaps/raise.xpm \ @@ -56,11 +59,14 @@ EXTRA_DIST = \ pixmaps/penedit.xpm \ pixmaps/pennorm.xpm \ pixmaps/penstroke.xpm \ + pixmaps/play.xpm \ + pixmaps/prev.xpm \ pixmaps/qmasksel.xpm \ pixmaps/qmasknosel.xpm \ pixmaps/question.xpm \ pixmaps/topath.xpm \ pixmaps/toselection.xpm \ + pixmaps/update.xpm \ pixmaps/yes.xpm \ pixmaps/zoom_in.xpm \ pixmaps/zoom_out.xpm \ diff --git a/app/blob.c b/app/blob.c index eaf508e655..ce6fa0946a 100644 --- a/app/blob.c +++ b/app/blob.c @@ -452,8 +452,24 @@ blob_line (Blob *b, int x0, int y0, int x1, int y1) #define TABLE_SIZE 256 #define ELLIPSE_SHIFT 2 -#define TABLE_SHIFT 14 -#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) +#define TABLE_SHIFT 12 +#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) + +/* + * The choose of this values limits the maximal image_size to + * 16384 x 16384 pixels. The values will overflow as soon as + * x or y > INT_MAX / (1 << (ELLIPSE_SHIFT + TABLE_SHIFT)) / SUBSAMPLE + * + * Alternatively the code could be change the code as follows: + * + * xc_base = floor (xc) + * xc_shift = 0.5 + (xc - xc_base) * (1 << TOTAL_SHIFT); + * + * gint x = xc_base + (xc_shift + c * xp_shift + s * xq_shift + + * (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + * + * which would change the limit from the image to the ellipse size + */ static int trig_initialized = 0; static int trig_table[TABLE_SIZE]; diff --git a/app/core/gimp-parasites.c b/app/core/gimp-parasites.c index c84614ed18..2134bf7992 100644 --- a/app/core/gimp-parasites.c +++ b/app/core/gimp-parasites.c @@ -36,30 +36,33 @@ static ParasiteList *parasites = NULL; void gimp_init_parasites() { - g_return_if_fail(parasites == NULL); - parasites = parasite_list_new(); - gimp_parasiterc_load(); + g_return_if_fail (parasites == NULL); + parasites = parasite_list_new (); + gimp_parasiterc_load (); } void gimp_parasite_attach (Parasite *p) { - parasite_list_add(parasites, p); + parasite_list_add (parasites, p); } void gimp_parasite_detach (const char *name) { - parasite_list_remove(parasites, name); + parasite_list_remove (parasites, name); } Parasite * gimp_parasite_find (const char *name) { - return parasite_list_find(parasites, name); + return parasite_list_find (parasites, name); } -static void list_func(char *key, Parasite *p, char ***cur) +static void +list_func (char *key, + Parasite *p, + char ***cur) { *(*cur)++ = (char *) g_strdup (key); } @@ -67,7 +70,7 @@ static void list_func(char *key, Parasite *p, char ***cur) char ** gimp_parasite_list (gint *count) { - char **list, **cur; + gchar **list, **cur; *count = parasite_list_length (parasites); cur = list = (char **) g_malloc (sizeof (char *) * *count); @@ -77,7 +80,10 @@ gimp_parasite_list (gint *count) return list; } -static void save_func(char *key, Parasite *p, FILE *fp) +static void +save_func (char *key, + Parasite *p, + FILE *fp) { if (parasite_is_persistent (p)) { @@ -115,7 +121,7 @@ static void save_func(char *key, Parasite *p, FILE *fp) } void -gimp_parasiterc_save(void) +gimp_parasiterc_save (void) { char *filename; FILE *fp; @@ -135,17 +141,17 @@ gimp_parasiterc_save(void) fclose (fp); if (rename(gimp_personal_rc_file ("#parasiterc.tmp~"), - gimp_personal_rc_file("parasiterc")) != 0) + gimp_personal_rc_file ("parasiterc")) != 0) unlink(gimp_personal_rc_file ("#parasiterc.tmp~")); } void -gimp_parasiterc_load() +gimp_parasiterc_load (void) { char *filename; filename = gimp_personal_rc_file ("parasiterc"); - app_init_update_status(NULL, filename, -1); + app_init_update_status (NULL, filename, -1); parse_gimprc_file (filename); g_free (filename); } diff --git a/app/core/gimpparasite.c b/app/core/gimpparasite.c index c84614ed18..2134bf7992 100644 --- a/app/core/gimpparasite.c +++ b/app/core/gimpparasite.c @@ -36,30 +36,33 @@ static ParasiteList *parasites = NULL; void gimp_init_parasites() { - g_return_if_fail(parasites == NULL); - parasites = parasite_list_new(); - gimp_parasiterc_load(); + g_return_if_fail (parasites == NULL); + parasites = parasite_list_new (); + gimp_parasiterc_load (); } void gimp_parasite_attach (Parasite *p) { - parasite_list_add(parasites, p); + parasite_list_add (parasites, p); } void gimp_parasite_detach (const char *name) { - parasite_list_remove(parasites, name); + parasite_list_remove (parasites, name); } Parasite * gimp_parasite_find (const char *name) { - return parasite_list_find(parasites, name); + return parasite_list_find (parasites, name); } -static void list_func(char *key, Parasite *p, char ***cur) +static void +list_func (char *key, + Parasite *p, + char ***cur) { *(*cur)++ = (char *) g_strdup (key); } @@ -67,7 +70,7 @@ static void list_func(char *key, Parasite *p, char ***cur) char ** gimp_parasite_list (gint *count) { - char **list, **cur; + gchar **list, **cur; *count = parasite_list_length (parasites); cur = list = (char **) g_malloc (sizeof (char *) * *count); @@ -77,7 +80,10 @@ gimp_parasite_list (gint *count) return list; } -static void save_func(char *key, Parasite *p, FILE *fp) +static void +save_func (char *key, + Parasite *p, + FILE *fp) { if (parasite_is_persistent (p)) { @@ -115,7 +121,7 @@ static void save_func(char *key, Parasite *p, FILE *fp) } void -gimp_parasiterc_save(void) +gimp_parasiterc_save (void) { char *filename; FILE *fp; @@ -135,17 +141,17 @@ gimp_parasiterc_save(void) fclose (fp); if (rename(gimp_personal_rc_file ("#parasiterc.tmp~"), - gimp_personal_rc_file("parasiterc")) != 0) + gimp_personal_rc_file ("parasiterc")) != 0) unlink(gimp_personal_rc_file ("#parasiterc.tmp~")); } void -gimp_parasiterc_load() +gimp_parasiterc_load (void) { char *filename; filename = gimp_personal_rc_file ("parasiterc"); - app_init_update_status(NULL, filename, -1); + app_init_update_status (NULL, filename, -1); parse_gimprc_file (filename); g_free (filename); } diff --git a/app/core/gimpparasitelist.c b/app/core/gimpparasitelist.c index 6e702b7540..71bb4a3cdb 100644 --- a/app/core/gimpparasitelist.c +++ b/app/core/gimpparasitelist.c @@ -45,7 +45,7 @@ parasite_list_init (ParasiteList* list) } static void -parasite_list_class_init(ParasiteListClass *klass) +parasite_list_class_init (ParasiteListClass *klass) { GtkObjectClass *class = GTK_OBJECT_CLASS(klass); GtkType type = class->type; @@ -53,164 +53,192 @@ parasite_list_class_init(ParasiteListClass *klass) class->destroy = parasite_list_destroy; parasite_list_signals[ADD] = - gimp_signal_new("add", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); + gimp_signal_new ("add", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); parasite_list_signals[REMOVE] = - gimp_signal_new("remove", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); + gimp_signal_new ("remove", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); gtk_object_class_add_signals (class, parasite_list_signals, LAST_SIGNAL); } GtkType -parasite_list_get_type() +parasite_list_get_type (void) { static GtkType type = 0; + if (!type) - { - GtkTypeInfo info = { - "ParasiteList", - sizeof (ParasiteList), - sizeof (ParasiteListClass), - (GtkClassInitFunc) parasite_list_class_init, - (GtkObjectInitFunc) parasite_list_init, - NULL, - NULL, - (GtkClassInitFunc) NULL - }; - type = gtk_type_unique(GIMP_TYPE_OBJECT, &info); - } + GtkTypeInfo info = + { + "ParasiteList", + sizeof (ParasiteList), + sizeof (ParasiteListClass), + (GtkClassInitFunc) parasite_list_class_init, + (GtkObjectInitFunc) parasite_list_init, + NULL, + NULL, + (GtkClassInitFunc) NULL + }; + type = gtk_type_unique (GIMP_TYPE_OBJECT, &info); + } return type; } ParasiteList* -parasite_list_new() +parasite_list_new (void) { - ParasiteList *list = gtk_type_new(GIMP_TYPE_PARASITE_LIST); + ParasiteList *list = gtk_type_new (GIMP_TYPE_PARASITE_LIST); list->table = NULL; return list; } static int -free_a_parasite(void *key, void *parasite, void *unused) +free_a_parasite (void *key, + void *parasite, + void *unused) { - parasite_free((Parasite *)parasite); + parasite_free ((Parasite *)parasite); return TRUE; } static void -parasite_list_destroy(GtkObject *obj) +parasite_list_destroy (GtkObject *obj) { ParasiteList *list; - g_return_if_fail(obj != NULL); - g_return_if_fail(GIMP_IS_PARASITE_LIST(obj)); + g_return_if_fail (obj != NULL); + g_return_if_fail (GIMP_IS_PARASITE_LIST(obj)); list = (ParasiteList *)obj; if (list->table) - { - g_hash_table_foreach_remove(list->table, free_a_parasite, NULL); - g_hash_table_destroy(list->table); - } + { + g_hash_table_foreach_remove (list->table, free_a_parasite, NULL); + g_hash_table_destroy (list->table); + } } static void -parasite_copy_one(void *key, void *p, void *data) +parasite_copy_one (void *key, + void *p, + void *data) { ParasiteList *list = (ParasiteList*)data; Parasite *parasite = (Parasite*)p; - parasite_list_add(list, parasite); + parasite_list_add (list, parasite); } ParasiteList* -parasite_list_copy(const ParasiteList *list) +parasite_list_copy (const ParasiteList *list) { ParasiteList *newlist; - newlist = parasite_list_new(); + + newlist = parasite_list_new (); if (list->table) - g_hash_table_foreach(list->table, parasite_copy_one, newlist); + g_hash_table_foreach (list->table, parasite_copy_one, newlist); + return newlist; } void -parasite_list_add(ParasiteList *list, Parasite *p) +parasite_list_add (ParasiteList *list, + Parasite *p) { - g_return_if_fail(list != NULL); + g_return_if_fail (list != NULL); + if (list->table == NULL) - list->table = g_hash_table_new(g_str_hash, g_str_equal); - g_return_if_fail(p != NULL); - g_return_if_fail(p->name != NULL); - parasite_list_remove(list, p->name); - p = parasite_copy(p); - g_hash_table_insert(list->table, p->name, p); + list->table = g_hash_table_new (g_str_hash, g_str_equal); + + g_return_if_fail (p != NULL); + g_return_if_fail (p->name != NULL); + + parasite_list_remove (list, p->name); + p = parasite_copy (p); + g_hash_table_insert (list->table, p->name, p); gtk_signal_emit (GTK_OBJECT(list), parasite_list_signals[ADD], p); } void -parasite_list_remove(ParasiteList *list, const char *name) +parasite_list_remove (ParasiteList *list, + const char *name) { Parasite *p; - g_return_if_fail(list != NULL); + + g_return_if_fail (list != NULL); + if (list->table) - { - p = parasite_list_find(list, name); - if (p) { - g_hash_table_remove(list->table, name); - gtk_signal_emit (GTK_OBJECT(list), parasite_list_signals[REMOVE], p); - parasite_free(p); + p = parasite_list_find(list, name); + if (p) + { + g_hash_table_remove (list->table, name); + gtk_signal_emit (GTK_OBJECT(list), parasite_list_signals[REMOVE], p); + parasite_free (p); + } } - } } gint -parasite_list_length(ParasiteList *list) +parasite_list_length (ParasiteList *list) { - g_return_val_if_fail(list != NULL, 0); + g_return_val_if_fail (list != NULL, 0); + if (!list->table) return 0; - return g_hash_table_size(list->table); + return g_hash_table_size (list->table); } -static void ppcount_func(char *key, Parasite *p, int *count) +static void +ppcount_func (char *key, + Parasite *p, + int *count) { - if (parasite_is_persistent(p)) + if (parasite_is_persistent (p)) *count = *count + 1; } gint -parasite_list_persistent_length(ParasiteList *list) +parasite_list_persistent_length (ParasiteList *list) { int ppcount = 0; - g_return_val_if_fail(list != NULL, 0); + + g_return_val_if_fail (list != NULL, 0); + if (!list->table) return 0; - parasite_list_foreach(list, (GHFunc)ppcount_func, &ppcount); + + parasite_list_foreach (list, (GHFunc)ppcount_func, &ppcount); return ppcount; } void -parasite_list_foreach(ParasiteList *list, GHFunc function, gpointer user_data) +parasite_list_foreach (ParasiteList *list, + GHFunc function, + gpointer user_data) { - g_return_if_fail(list != NULL); + g_return_if_fail (list != NULL); + if (!list->table) return; - g_hash_table_foreach(list->table, function, user_data); + + g_hash_table_foreach (list->table, function, user_data); } Parasite * -parasite_list_find(ParasiteList *list, const char *name) +parasite_list_find (ParasiteList *list, + const char *name) { - g_return_val_if_fail(list != NULL, NULL); + g_return_val_if_fail (list != NULL, NULL); + if (list->table) - return (Parasite *)g_hash_table_lookup(list->table, name); + return (Parasite *)g_hash_table_lookup (list->table, name); else return NULL; } void -parasite_shift_parent(Parasite *p) +parasite_shift_parent (Parasite *p) { if (p == NULL) return; + p->flags = (p->flags >> 8); } diff --git a/app/gimpparasite.c b/app/gimpparasite.c index c84614ed18..2134bf7992 100644 --- a/app/gimpparasite.c +++ b/app/gimpparasite.c @@ -36,30 +36,33 @@ static ParasiteList *parasites = NULL; void gimp_init_parasites() { - g_return_if_fail(parasites == NULL); - parasites = parasite_list_new(); - gimp_parasiterc_load(); + g_return_if_fail (parasites == NULL); + parasites = parasite_list_new (); + gimp_parasiterc_load (); } void gimp_parasite_attach (Parasite *p) { - parasite_list_add(parasites, p); + parasite_list_add (parasites, p); } void gimp_parasite_detach (const char *name) { - parasite_list_remove(parasites, name); + parasite_list_remove (parasites, name); } Parasite * gimp_parasite_find (const char *name) { - return parasite_list_find(parasites, name); + return parasite_list_find (parasites, name); } -static void list_func(char *key, Parasite *p, char ***cur) +static void +list_func (char *key, + Parasite *p, + char ***cur) { *(*cur)++ = (char *) g_strdup (key); } @@ -67,7 +70,7 @@ static void list_func(char *key, Parasite *p, char ***cur) char ** gimp_parasite_list (gint *count) { - char **list, **cur; + gchar **list, **cur; *count = parasite_list_length (parasites); cur = list = (char **) g_malloc (sizeof (char *) * *count); @@ -77,7 +80,10 @@ gimp_parasite_list (gint *count) return list; } -static void save_func(char *key, Parasite *p, FILE *fp) +static void +save_func (char *key, + Parasite *p, + FILE *fp) { if (parasite_is_persistent (p)) { @@ -115,7 +121,7 @@ static void save_func(char *key, Parasite *p, FILE *fp) } void -gimp_parasiterc_save(void) +gimp_parasiterc_save (void) { char *filename; FILE *fp; @@ -135,17 +141,17 @@ gimp_parasiterc_save(void) fclose (fp); if (rename(gimp_personal_rc_file ("#parasiterc.tmp~"), - gimp_personal_rc_file("parasiterc")) != 0) + gimp_personal_rc_file ("parasiterc")) != 0) unlink(gimp_personal_rc_file ("#parasiterc.tmp~")); } void -gimp_parasiterc_load() +gimp_parasiterc_load (void) { char *filename; filename = gimp_personal_rc_file ("parasiterc"); - app_init_update_status(NULL, filename, -1); + app_init_update_status (NULL, filename, -1); parse_gimprc_file (filename); g_free (filename); } diff --git a/app/paint/gimpink-blob.c b/app/paint/gimpink-blob.c index eaf508e655..ce6fa0946a 100644 --- a/app/paint/gimpink-blob.c +++ b/app/paint/gimpink-blob.c @@ -452,8 +452,24 @@ blob_line (Blob *b, int x0, int y0, int x1, int y1) #define TABLE_SIZE 256 #define ELLIPSE_SHIFT 2 -#define TABLE_SHIFT 14 -#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) +#define TABLE_SHIFT 12 +#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) + +/* + * The choose of this values limits the maximal image_size to + * 16384 x 16384 pixels. The values will overflow as soon as + * x or y > INT_MAX / (1 << (ELLIPSE_SHIFT + TABLE_SHIFT)) / SUBSAMPLE + * + * Alternatively the code could be change the code as follows: + * + * xc_base = floor (xc) + * xc_shift = 0.5 + (xc - xc_base) * (1 << TOTAL_SHIFT); + * + * gint x = xc_base + (xc_shift + c * xp_shift + s * xq_shift + + * (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + * + * which would change the limit from the image to the ellipse size + */ static int trig_initialized = 0; static int trig_table[TABLE_SIZE]; diff --git a/app/parasitelist.c b/app/parasitelist.c index 6e702b7540..71bb4a3cdb 100644 --- a/app/parasitelist.c +++ b/app/parasitelist.c @@ -45,7 +45,7 @@ parasite_list_init (ParasiteList* list) } static void -parasite_list_class_init(ParasiteListClass *klass) +parasite_list_class_init (ParasiteListClass *klass) { GtkObjectClass *class = GTK_OBJECT_CLASS(klass); GtkType type = class->type; @@ -53,164 +53,192 @@ parasite_list_class_init(ParasiteListClass *klass) class->destroy = parasite_list_destroy; parasite_list_signals[ADD] = - gimp_signal_new("add", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); + gimp_signal_new ("add", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); parasite_list_signals[REMOVE] = - gimp_signal_new("remove", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); + gimp_signal_new ("remove", GTK_RUN_FIRST, type, 0, gimp_sigtype_pointer); gtk_object_class_add_signals (class, parasite_list_signals, LAST_SIGNAL); } GtkType -parasite_list_get_type() +parasite_list_get_type (void) { static GtkType type = 0; + if (!type) - { - GtkTypeInfo info = { - "ParasiteList", - sizeof (ParasiteList), - sizeof (ParasiteListClass), - (GtkClassInitFunc) parasite_list_class_init, - (GtkObjectInitFunc) parasite_list_init, - NULL, - NULL, - (GtkClassInitFunc) NULL - }; - type = gtk_type_unique(GIMP_TYPE_OBJECT, &info); - } + GtkTypeInfo info = + { + "ParasiteList", + sizeof (ParasiteList), + sizeof (ParasiteListClass), + (GtkClassInitFunc) parasite_list_class_init, + (GtkObjectInitFunc) parasite_list_init, + NULL, + NULL, + (GtkClassInitFunc) NULL + }; + type = gtk_type_unique (GIMP_TYPE_OBJECT, &info); + } return type; } ParasiteList* -parasite_list_new() +parasite_list_new (void) { - ParasiteList *list = gtk_type_new(GIMP_TYPE_PARASITE_LIST); + ParasiteList *list = gtk_type_new (GIMP_TYPE_PARASITE_LIST); list->table = NULL; return list; } static int -free_a_parasite(void *key, void *parasite, void *unused) +free_a_parasite (void *key, + void *parasite, + void *unused) { - parasite_free((Parasite *)parasite); + parasite_free ((Parasite *)parasite); return TRUE; } static void -parasite_list_destroy(GtkObject *obj) +parasite_list_destroy (GtkObject *obj) { ParasiteList *list; - g_return_if_fail(obj != NULL); - g_return_if_fail(GIMP_IS_PARASITE_LIST(obj)); + g_return_if_fail (obj != NULL); + g_return_if_fail (GIMP_IS_PARASITE_LIST(obj)); list = (ParasiteList *)obj; if (list->table) - { - g_hash_table_foreach_remove(list->table, free_a_parasite, NULL); - g_hash_table_destroy(list->table); - } + { + g_hash_table_foreach_remove (list->table, free_a_parasite, NULL); + g_hash_table_destroy (list->table); + } } static void -parasite_copy_one(void *key, void *p, void *data) +parasite_copy_one (void *key, + void *p, + void *data) { ParasiteList *list = (ParasiteList*)data; Parasite *parasite = (Parasite*)p; - parasite_list_add(list, parasite); + parasite_list_add (list, parasite); } ParasiteList* -parasite_list_copy(const ParasiteList *list) +parasite_list_copy (const ParasiteList *list) { ParasiteList *newlist; - newlist = parasite_list_new(); + + newlist = parasite_list_new (); if (list->table) - g_hash_table_foreach(list->table, parasite_copy_one, newlist); + g_hash_table_foreach (list->table, parasite_copy_one, newlist); + return newlist; } void -parasite_list_add(ParasiteList *list, Parasite *p) +parasite_list_add (ParasiteList *list, + Parasite *p) { - g_return_if_fail(list != NULL); + g_return_if_fail (list != NULL); + if (list->table == NULL) - list->table = g_hash_table_new(g_str_hash, g_str_equal); - g_return_if_fail(p != NULL); - g_return_if_fail(p->name != NULL); - parasite_list_remove(list, p->name); - p = parasite_copy(p); - g_hash_table_insert(list->table, p->name, p); + list->table = g_hash_table_new (g_str_hash, g_str_equal); + + g_return_if_fail (p != NULL); + g_return_if_fail (p->name != NULL); + + parasite_list_remove (list, p->name); + p = parasite_copy (p); + g_hash_table_insert (list->table, p->name, p); gtk_signal_emit (GTK_OBJECT(list), parasite_list_signals[ADD], p); } void -parasite_list_remove(ParasiteList *list, const char *name) +parasite_list_remove (ParasiteList *list, + const char *name) { Parasite *p; - g_return_if_fail(list != NULL); + + g_return_if_fail (list != NULL); + if (list->table) - { - p = parasite_list_find(list, name); - if (p) { - g_hash_table_remove(list->table, name); - gtk_signal_emit (GTK_OBJECT(list), parasite_list_signals[REMOVE], p); - parasite_free(p); + p = parasite_list_find(list, name); + if (p) + { + g_hash_table_remove (list->table, name); + gtk_signal_emit (GTK_OBJECT(list), parasite_list_signals[REMOVE], p); + parasite_free (p); + } } - } } gint -parasite_list_length(ParasiteList *list) +parasite_list_length (ParasiteList *list) { - g_return_val_if_fail(list != NULL, 0); + g_return_val_if_fail (list != NULL, 0); + if (!list->table) return 0; - return g_hash_table_size(list->table); + return g_hash_table_size (list->table); } -static void ppcount_func(char *key, Parasite *p, int *count) +static void +ppcount_func (char *key, + Parasite *p, + int *count) { - if (parasite_is_persistent(p)) + if (parasite_is_persistent (p)) *count = *count + 1; } gint -parasite_list_persistent_length(ParasiteList *list) +parasite_list_persistent_length (ParasiteList *list) { int ppcount = 0; - g_return_val_if_fail(list != NULL, 0); + + g_return_val_if_fail (list != NULL, 0); + if (!list->table) return 0; - parasite_list_foreach(list, (GHFunc)ppcount_func, &ppcount); + + parasite_list_foreach (list, (GHFunc)ppcount_func, &ppcount); return ppcount; } void -parasite_list_foreach(ParasiteList *list, GHFunc function, gpointer user_data) +parasite_list_foreach (ParasiteList *list, + GHFunc function, + gpointer user_data) { - g_return_if_fail(list != NULL); + g_return_if_fail (list != NULL); + if (!list->table) return; - g_hash_table_foreach(list->table, function, user_data); + + g_hash_table_foreach (list->table, function, user_data); } Parasite * -parasite_list_find(ParasiteList *list, const char *name) +parasite_list_find (ParasiteList *list, + const char *name) { - g_return_val_if_fail(list != NULL, NULL); + g_return_val_if_fail (list != NULL, NULL); + if (list->table) - return (Parasite *)g_hash_table_lookup(list->table, name); + return (Parasite *)g_hash_table_lookup (list->table, name); else return NULL; } void -parasite_shift_parent(Parasite *p) +parasite_shift_parent (Parasite *p) { if (p == NULL) return; + p->flags = (p->flags >> 8); } diff --git a/app/tools/blob.c b/app/tools/blob.c index eaf508e655..ce6fa0946a 100644 --- a/app/tools/blob.c +++ b/app/tools/blob.c @@ -452,8 +452,24 @@ blob_line (Blob *b, int x0, int y0, int x1, int y1) #define TABLE_SIZE 256 #define ELLIPSE_SHIFT 2 -#define TABLE_SHIFT 14 -#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) +#define TABLE_SHIFT 12 +#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) + +/* + * The choose of this values limits the maximal image_size to + * 16384 x 16384 pixels. The values will overflow as soon as + * x or y > INT_MAX / (1 << (ELLIPSE_SHIFT + TABLE_SHIFT)) / SUBSAMPLE + * + * Alternatively the code could be change the code as follows: + * + * xc_base = floor (xc) + * xc_shift = 0.5 + (xc - xc_base) * (1 << TOTAL_SHIFT); + * + * gint x = xc_base + (xc_shift + c * xp_shift + s * xq_shift + + * (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + * + * which would change the limit from the image to the ellipse size + */ static int trig_initialized = 0; static int trig_table[TABLE_SIZE]; diff --git a/app/tools/gimpinktool-blob.c b/app/tools/gimpinktool-blob.c index eaf508e655..ce6fa0946a 100644 --- a/app/tools/gimpinktool-blob.c +++ b/app/tools/gimpinktool-blob.c @@ -452,8 +452,24 @@ blob_line (Blob *b, int x0, int y0, int x1, int y1) #define TABLE_SIZE 256 #define ELLIPSE_SHIFT 2 -#define TABLE_SHIFT 14 -#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) +#define TABLE_SHIFT 12 +#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT) + +/* + * The choose of this values limits the maximal image_size to + * 16384 x 16384 pixels. The values will overflow as soon as + * x or y > INT_MAX / (1 << (ELLIPSE_SHIFT + TABLE_SHIFT)) / SUBSAMPLE + * + * Alternatively the code could be change the code as follows: + * + * xc_base = floor (xc) + * xc_shift = 0.5 + (xc - xc_base) * (1 << TOTAL_SHIFT); + * + * gint x = xc_base + (xc_shift + c * xp_shift + s * xq_shift + + * (1 << (TOTAL_SHIFT - 1))) >> TOTAL_SHIFT; + * + * which would change the limit from the image to the ellipse size + */ static int trig_initialized = 0; static int trig_table[TABLE_SIZE]; diff --git a/libgimp/gimpparasite.c b/libgimp/gimpparasite.c index 5010487921..2c2e494bf0 100644 --- a/libgimp/gimpparasite.c +++ b/libgimp/gimpparasite.c @@ -33,7 +33,8 @@ #endif #ifdef DEBUG -static void parasite_print(Parasite *p) +static void +parasite_print (Parasite *p) { if (p == NULL) { @@ -53,22 +54,24 @@ static void parasite_print(Parasite *p) #endif Parasite * -parasite_new (const char *name, guint32 flags, - guint32 size, const void *data) +parasite_new (const char *name, + guint32 flags, + guint32 size, + const void *data) { Parasite *p; p = g_new (Parasite, 1); if (name) - p->name = g_strdup(name); + p->name = g_strdup (name); else - { - g_free (p); - return NULL; - } + { + g_free (p); + return NULL; + } p->flags = (flags & 0xFF); p->size = size; if (size) - p->data = g_memdup(data, size); + p->data = g_memdup (data, size); else p->data = NULL; return p; @@ -80,14 +83,15 @@ parasite_free (Parasite *parasite) if (parasite == NULL) return; if (parasite->name) - g_free(parasite->name); + g_free (parasite->name); if (parasite->data) - g_free(parasite->data); - g_free(parasite); + g_free (parasite->data); + g_free (parasite); } int -parasite_is_type (const Parasite *parasite, const char *name) +parasite_is_type (const Parasite *parasite, + const char *name) { if (!parasite || !parasite->name) return FALSE; @@ -104,7 +108,8 @@ parasite_copy (const Parasite *parasite) } int -parasite_compare (const Parasite *a, const Parasite *b) +parasite_compare (const Parasite *a, + const Parasite *b) { if (a && b && a->name && b->name && strcmp(a->name, b->name) == 0 && a->flags == b->flags && a->size == b->size ) @@ -118,7 +123,7 @@ parasite_compare (const Parasite *a, const Parasite *b) } gulong -parasite_flags(const Parasite *p) +parasite_flags (const Parasite *p) { if (p == NULL) return 0; @@ -126,7 +131,7 @@ parasite_flags(const Parasite *p) } int -parasite_is_persistent(const Parasite *p) +parasite_is_persistent (const Parasite *p) { if (p == NULL) return FALSE; @@ -134,7 +139,7 @@ parasite_is_persistent(const Parasite *p) } int -parasite_is_undoable(const Parasite *p) +parasite_is_undoable (const Parasite *p) { if (p == NULL) return FALSE; @@ -142,7 +147,8 @@ parasite_is_undoable(const Parasite *p) } int -parasite_has_flag(const Parasite *p, gulong flag) +parasite_has_flag (const Parasite *p, + gulong flag) { if (p == NULL) return FALSE; @@ -150,23 +156,28 @@ parasite_has_flag(const Parasite *p, gulong flag) } const char * -parasite_name(const Parasite *p) +parasite_name (const Parasite *p) { if (p) return p->name; return NULL; } -void *parasite_data(const Parasite *p) +void * +parasite_data (const Parasite *p) { if (p) return p->data; return NULL; } -long parasite_data_size(const Parasite *p) +long +parasite_data_size (const Parasite *p) { if (p) return p->size; return 0; } + + + diff --git a/libgimp/parasite.c b/libgimp/parasite.c index 5010487921..2c2e494bf0 100644 --- a/libgimp/parasite.c +++ b/libgimp/parasite.c @@ -33,7 +33,8 @@ #endif #ifdef DEBUG -static void parasite_print(Parasite *p) +static void +parasite_print (Parasite *p) { if (p == NULL) { @@ -53,22 +54,24 @@ static void parasite_print(Parasite *p) #endif Parasite * -parasite_new (const char *name, guint32 flags, - guint32 size, const void *data) +parasite_new (const char *name, + guint32 flags, + guint32 size, + const void *data) { Parasite *p; p = g_new (Parasite, 1); if (name) - p->name = g_strdup(name); + p->name = g_strdup (name); else - { - g_free (p); - return NULL; - } + { + g_free (p); + return NULL; + } p->flags = (flags & 0xFF); p->size = size; if (size) - p->data = g_memdup(data, size); + p->data = g_memdup (data, size); else p->data = NULL; return p; @@ -80,14 +83,15 @@ parasite_free (Parasite *parasite) if (parasite == NULL) return; if (parasite->name) - g_free(parasite->name); + g_free (parasite->name); if (parasite->data) - g_free(parasite->data); - g_free(parasite); + g_free (parasite->data); + g_free (parasite); } int -parasite_is_type (const Parasite *parasite, const char *name) +parasite_is_type (const Parasite *parasite, + const char *name) { if (!parasite || !parasite->name) return FALSE; @@ -104,7 +108,8 @@ parasite_copy (const Parasite *parasite) } int -parasite_compare (const Parasite *a, const Parasite *b) +parasite_compare (const Parasite *a, + const Parasite *b) { if (a && b && a->name && b->name && strcmp(a->name, b->name) == 0 && a->flags == b->flags && a->size == b->size ) @@ -118,7 +123,7 @@ parasite_compare (const Parasite *a, const Parasite *b) } gulong -parasite_flags(const Parasite *p) +parasite_flags (const Parasite *p) { if (p == NULL) return 0; @@ -126,7 +131,7 @@ parasite_flags(const Parasite *p) } int -parasite_is_persistent(const Parasite *p) +parasite_is_persistent (const Parasite *p) { if (p == NULL) return FALSE; @@ -134,7 +139,7 @@ parasite_is_persistent(const Parasite *p) } int -parasite_is_undoable(const Parasite *p) +parasite_is_undoable (const Parasite *p) { if (p == NULL) return FALSE; @@ -142,7 +147,8 @@ parasite_is_undoable(const Parasite *p) } int -parasite_has_flag(const Parasite *p, gulong flag) +parasite_has_flag (const Parasite *p, + gulong flag) { if (p == NULL) return FALSE; @@ -150,23 +156,28 @@ parasite_has_flag(const Parasite *p, gulong flag) } const char * -parasite_name(const Parasite *p) +parasite_name (const Parasite *p) { if (p) return p->name; return NULL; } -void *parasite_data(const Parasite *p) +void * +parasite_data (const Parasite *p) { if (p) return p->data; return NULL; } -long parasite_data_size(const Parasite *p) +long +parasite_data_size (const Parasite *p) { if (p) return p->size; return 0; } + + + diff --git a/libgimpbase/gimpparasite.c b/libgimpbase/gimpparasite.c index 5010487921..2c2e494bf0 100644 --- a/libgimpbase/gimpparasite.c +++ b/libgimpbase/gimpparasite.c @@ -33,7 +33,8 @@ #endif #ifdef DEBUG -static void parasite_print(Parasite *p) +static void +parasite_print (Parasite *p) { if (p == NULL) { @@ -53,22 +54,24 @@ static void parasite_print(Parasite *p) #endif Parasite * -parasite_new (const char *name, guint32 flags, - guint32 size, const void *data) +parasite_new (const char *name, + guint32 flags, + guint32 size, + const void *data) { Parasite *p; p = g_new (Parasite, 1); if (name) - p->name = g_strdup(name); + p->name = g_strdup (name); else - { - g_free (p); - return NULL; - } + { + g_free (p); + return NULL; + } p->flags = (flags & 0xFF); p->size = size; if (size) - p->data = g_memdup(data, size); + p->data = g_memdup (data, size); else p->data = NULL; return p; @@ -80,14 +83,15 @@ parasite_free (Parasite *parasite) if (parasite == NULL) return; if (parasite->name) - g_free(parasite->name); + g_free (parasite->name); if (parasite->data) - g_free(parasite->data); - g_free(parasite); + g_free (parasite->data); + g_free (parasite); } int -parasite_is_type (const Parasite *parasite, const char *name) +parasite_is_type (const Parasite *parasite, + const char *name) { if (!parasite || !parasite->name) return FALSE; @@ -104,7 +108,8 @@ parasite_copy (const Parasite *parasite) } int -parasite_compare (const Parasite *a, const Parasite *b) +parasite_compare (const Parasite *a, + const Parasite *b) { if (a && b && a->name && b->name && strcmp(a->name, b->name) == 0 && a->flags == b->flags && a->size == b->size ) @@ -118,7 +123,7 @@ parasite_compare (const Parasite *a, const Parasite *b) } gulong -parasite_flags(const Parasite *p) +parasite_flags (const Parasite *p) { if (p == NULL) return 0; @@ -126,7 +131,7 @@ parasite_flags(const Parasite *p) } int -parasite_is_persistent(const Parasite *p) +parasite_is_persistent (const Parasite *p) { if (p == NULL) return FALSE; @@ -134,7 +139,7 @@ parasite_is_persistent(const Parasite *p) } int -parasite_is_undoable(const Parasite *p) +parasite_is_undoable (const Parasite *p) { if (p == NULL) return FALSE; @@ -142,7 +147,8 @@ parasite_is_undoable(const Parasite *p) } int -parasite_has_flag(const Parasite *p, gulong flag) +parasite_has_flag (const Parasite *p, + gulong flag) { if (p == NULL) return FALSE; @@ -150,23 +156,28 @@ parasite_has_flag(const Parasite *p, gulong flag) } const char * -parasite_name(const Parasite *p) +parasite_name (const Parasite *p) { if (p) return p->name; return NULL; } -void *parasite_data(const Parasite *p) +void * +parasite_data (const Parasite *p) { if (p) return p->data; return NULL; } -long parasite_data_size(const Parasite *p) +long +parasite_data_size (const Parasite *p) { if (p) return p->size; return 0; } + + + diff --git a/pixmaps/first.xpm b/pixmaps/first.xpm new file mode 100644 index 0000000000..c31185dcff --- /dev/null +++ b/pixmaps/first.xpm @@ -0,0 +1,40 @@ +/* XPM */ +static char * first_xpm[] = { +"13 13 24 1", +" c None", +". c #000000", +"+ c #333333", +"@ c #999999", +"# c #FFFFFF", +"$ c #CCCCCC", +"% c #BEBEBE", +"& c #E5E5E5", +"* c #CDCDCD", +"= c #8B8B8B", +"- c #B7B7B7", +"; c #A2A2A2", +"> c #6B6B6B", +", c #636363", +"' c #979797", +") c #A4A4A4", +"! c #9B9B9B", +"~ c #5D5D5D", +"{ c #9F9F9F", +"] c #B2B2B2", +"^ c #757575", +"/ c #8A8A8A", +"( c #666666", +"_ c #898989", +".. . ", +".+ ..@.", +".+ ..@##.", +".+ ..@##$#.", +".+ ..@##$%$#.", +"...@&*=-;$$#.", +".+>,'=)!$%$#.", +"...+~{=-]$$#.", +".+ ..+^/-%$#.", +".+ ..+()$#.", +".+ ..+_@.", +".+ ..+.", +".. . "}; diff --git a/pixmaps/last.xpm b/pixmaps/last.xpm new file mode 100644 index 0000000000..40c2fadf4f --- /dev/null +++ b/pixmaps/last.xpm @@ -0,0 +1,40 @@ +/* XPM */ +static char * last_xpm[] = { +"13 13 24 1", +" c None", +". c #000000", +"+ c #999999", +"@ c #333333", +"# c #FFFFFF", +"$ c #CCCCCC", +"% c #BEBEBE", +"& c #A2A2A2", +"* c #B7B7B7", +"= c #8B8B8B", +"- c #CDCDCD", +"; c #E5E5E5", +"> c #9B9B9B", +", c #A4A4A4", +"' c #979797", +") c #636363", +"! c #6B6B6B", +"~ c #B2B2B2", +"{ c #9F9F9F", +"] c #5D5D5D", +"^ c #8A8A8A", +"/ c #757575", +"( c #666666", +"_ c #898989", +" . ..", +".+.. @.", +".##+.. @.", +".#$##+.. @.", +".#$%$##+.. @.", +".#$$&*=-;+...", +".#$%$>,=')!@.", +".#$$~*={]@...", +".#$%*^/@.. @.", +".#$,(@.. @.", +".+_@.. @.", +".@.. @.", +" . .."}; diff --git a/pixmaps/next.xpm b/pixmaps/next.xpm new file mode 100644 index 0000000000..66a9d3777b --- /dev/null +++ b/pixmaps/next.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char * next_xpm[] = { +"13 13 22 1", +" c None", +". c #000000", +"+ c #333333", +"@ c #FFFFFF", +"# c #999999", +"$ c #CCCCCC", +"% c #A2A2A2", +"& c #B7B7B7", +"* c #8B8B8B", +"= c #CDCDCD", +"- c #E5E5E5", +"; c #9B9B9B", +"> c #A4A4A4", +", c #979797", +"' c #636363", +") c #6B6B6B", +"! c #B2B2B2", +"~ c #9F9F9F", +"{ c #5D5D5D", +"] c #8A8A8A", +"^ c #757575", +"/ c #666666", +" ", +".. . ", +".+ ... ", +".+ .@#.. ", +".+ .$@@#.. ", +".+ .%&*=-#.. ", +".+ .$;>*,')+.", +".+ .!&*~{+.. ", +".+ .&]^+.. ", +".+ ./+.. ", +".+ ... ", +".. . ", +" "}; diff --git a/pixmaps/play.xpm b/pixmaps/play.xpm new file mode 100644 index 0000000000..775ba99921 --- /dev/null +++ b/pixmaps/play.xpm @@ -0,0 +1,39 @@ +/* XPM */ +static char * play_xpm[] = { +"13 13 23 1", +" c None", +". c #000000", +"+ c #4D4D4D", +"@ c #808080", +"# c #666666", +"$ c #5F5F5F", +"% c #515151", +"& c #5C5C5C", +"* c #464646", +"= c #676767", +"- c #737373", +"; c #4E4E4E", +"> c #525252", +", c #4C4C4C", +"' c #323232", +") c #363636", +"! c #1A1A1A", +"~ c #595959", +"{ c #505050", +"] c #2F2F2F", +"^ c #454545", +"/ c #3B3B3B", +"( c #333333", +" . ", +".+.. ", +".@@+.. ", +".@#@@+.. ", +".@#$#@@+.. ", +".@##%&*=-+.. ", +".@#$#;>*,')!.", +".@##~&*{]!.. ", +".@#$&^/!.. ", +".@#>(!.. ", +".+^!.. ", +".!.. ", +" . "}; diff --git a/pixmaps/prev.xpm b/pixmaps/prev.xpm new file mode 100644 index 0000000000..828aba0523 --- /dev/null +++ b/pixmaps/prev.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char * prev_xpm[] = { +"13 13 22 1", +" c None", +". c #000000", +"+ c #333333", +"@ c #999999", +"# c #FFFFFF", +"$ c #CCCCCC", +"% c #E5E5E5", +"& c #CDCDCD", +"* c #8B8B8B", +"= c #B7B7B7", +"- c #A2A2A2", +"; c #6B6B6B", +"> c #636363", +", c #979797", +"' c #A4A4A4", +") c #9B9B9B", +"! c #5D5D5D", +"~ c #9F9F9F", +"{ c #B2B2B2", +"] c #757575", +"^ c #8A8A8A", +"/ c #666666", +" ", +" . ..", +" ... +.", +" ..@#. +.", +" ..@##$. +.", +" ..@%&*=-. +.", +".+;>,*')$. +.", +" ..+!~*={. +.", +" ..+]^=. +.", +" ..+/. +.", +" ... +.", +" . ..", +" "}; diff --git a/pixmaps/update.xpm b/pixmaps/update.xpm new file mode 100644 index 0000000000..d6eb008ad9 --- /dev/null +++ b/pixmaps/update.xpm @@ -0,0 +1,96 @@ +/* XPM */ +static char * update_xpm[] = { +"17 17 76 1", +" c None", +". c #C5C5C5", +"+ c #8A8A8A", +"@ c #000000", +"# c #848484", +"$ c #B2B2B2", +"% c #979797", +"& c #343434", +"* c #272727", +"= c #CCCCCC", +"- c #111111", +"; c #2C2C2C", +"> c #838383", +", c #545454", +"' c #202020", +") c #525252", +"! c #ADADAD", +"~ c #FFFFFF", +"{ c #989898", +"] c #494949", +"^ c #222222", +"/ c #565656", +"( c #535353", +"_ c #262626", +": c #888888", +"< c #C8C8C8", +"[ c #666666", +"} c #C6C6C6", +"| c #1D1D1D", +"1 c #5A5A5A", +"2 c #9A9A9A", +"3 c #8C8C8C", +"4 c #DADADA", +"5 c #999999", +"6 c #969696", +"7 c #141414", +"8 c #282828", +"9 c #555555", +"0 c #C7C7C7", +"a c #4B4B4B", +"b c #2A2A2A", +"c c #B0B0B0", +"d c #BEBEBE", +"e c #919191", +"f c #BBBBBB", +"g c #8F8F8F", +"h c #1C1C1C", +"i c #B7B7B7", +"j c #B6B6B6", +"k c #8B8B8B", +"l c #292929", +"m c #C0C0C0", +"n c #AEAEAE", +"o c #B3B3B3", +"p c #8E8E8E", +"q c #1E1E1E", +"r c #B8B8B8", +"s c #909090", +"t c #BABABA", +"u c #333333", +"v c #2B2B2B", +"w c #0E0E0E", +"x c #4F4F4F", +"y c #939393", +"z c #1F1F1F", +"A c #585858", +"B c #484848", +"C c #505050", +"D c #B9B9B9", +"E c #9D9D9D", +"F c #101010", +"G c #5B5B5B", +"H c #252525", +"I c #808080", +"J c #959595", +"K c #C9C9C9", +" .+@#$ ", +" %&*@=@-;> ", +" ,')!@~@{]^/ ", +" (_:<@=~[@}%|1 ", +" 2|3 @~45@ 67> ", +" 89< @=~=4[@ 0ab ", +"c-5 @~d4d5@ e-f", +"ghi @=~4d4d[@ jhk", +">lm @~=n=o=5@ mb>", +"pqr@=~5=5=5d[@j|+", +"o-%@~5o5o5o55@s-t", +" _@=5[[[[[[[[u@v ", +" :w@@@@@@@@@@@q: ", +" xby< 0yzA ", +" B^C6DmfEaFG ", +" e*FzbH*bI ", +" ok>JK "}; diff --git a/plug-ins/gap/.cvsignore b/plug-ins/gap/.cvsignore index b9b212e113..ac97446b3f 100644 --- a/plug-ins/gap/.cvsignore +++ b/plug-ins/gap/.cvsignore @@ -7,3 +7,4 @@ gap_filter gap_plugins gap_frontends gap_decode_mpeg +gap_navigator_dialog diff --git a/plug-ins/gap/Makefile.am b/plug-ins/gap/Makefile.am index 6750639a41..d1290b5eb6 100644 --- a/plug-ins/gap/Makefile.am +++ b/plug-ins/gap/Makefile.am @@ -99,6 +99,7 @@ libexec_PROGRAMS = \ gap_plugins \ gap_filter \ gap_frontends \ + gap_navigator_dialog \ $(GAP_DECODE_MPEG) EXTRA_PROGRAMS = \ @@ -183,6 +184,20 @@ gap_decode_mpeg_SOURCES = \ gap_pdb_calls.h +gap_navigator_dialog_SOURCES = \ + gap_navigator_dialog.c \ + gap_lib.c \ + gap_lib.h \ + gap_arr_dialog.c \ + gap_arr_dialog.h \ + gap_pdb_calls.c \ + gap_pdb_calls.h \ + gap_layer_copy.c \ + gap_layer_copy.h \ + gap_exchange_image.c \ + gap_exchange_image.h + + AM_CPPFLAGS = \ -DLOCALEDIR=\""$(localedir)"\" diff --git a/plug-ins/gap/gap_decode_mpeg_main.c b/plug-ins/gap/gap_decode_mpeg_main.c index 18ec12d321..a3ecf3da1d 100644 --- a/plug-ins/gap/gap_decode_mpeg_main.c +++ b/plug-ins/gap/gap_decode_mpeg_main.c @@ -58,6 +58,8 @@ /* * Changelog: * + * 2000/01/06 v1.1.14a: hof: save thumbnails .xvpics p_gimp_file_save_thumbnail + * store framerate in video_info file * 1999/11/25 v1.1.11.b: Initial release. [hof] * (based on plug-ins/common/mpeg.c v1.1 99/05/31 by Adam D. Moss) */ @@ -85,6 +87,7 @@ /* GAP includes */ #include "gap_arr_dialog.h" +#include "gap_pdb_calls.h" /* Includes for extra LIBS */ #include "mpeg.h" @@ -164,7 +167,7 @@ query () _("Split MPEG1 movies into single frames (image files on disk) and load 1st frame. audio tracks are ignored"), "Wolfgang Hofer (hof@hotbot.com)", "Wolfgang Hofer", - "1999/11/18", + "2000/01/01", N_("/Video/Split Video to Frames/MPEG1"), NULL, PROC_PLUG_IN, @@ -176,7 +179,7 @@ query () _("Split MPEG1 movies into single frames (image files on disk) and load 1st frame. audio tracks are ignored"), "Wolfgang Hofer (hof@hotbot.com)", "Wolfgang Hofer", - "1999/11/18", + "2000/01/01", N_("/Xtns/Split Video to Frames/MPEG1"), NULL, PROC_EXTENSION, @@ -340,23 +343,36 @@ p_overwrite_dialog(char *filename, gint overwrite_mode) } static gint -MPEG_frame_period_ms(gint mpeg_rate_code) +MPEG_frame_period_ms(gint mpeg_rate_code, char *basename) { + gint l_rc; + gdouble l_framerate; + t_video_info *vin_ptr; + + vin_ptr = p_get_video_info(basename); + l_rc = 0; switch(mpeg_rate_code) { - case 1: return 44; /* ~23 fps */ - case 2: return 42; /* 24 fps */ - case 3: return 40; /* 25 fps */ - case 4: return 33; /* ~30 fps */ - case 5: return 33; /* 30 fps */ - case 6: return 20; /* 50 fps */ - case 7: return 17; /* ~60 fps */ - case 8: return 17; /* 60 fps */ + case 1: l_rc = 44; l_framerate = 23.976; break; + case 2: l_rc = 42; l_framerate = 24.0; break; + case 3: l_rc = 40; l_framerate = 25.0; break; + case 4: l_rc = 33; l_framerate = 29.97; break; + case 5: l_rc = 33; l_framerate = 30.0; break; + case 6: l_rc = 20; l_framerate = 50.0; break; + case 7: l_rc = 17; l_framerate = 59.94; break; + case 8: l_rc = 17; l_framerate = 60.0; break; case 0: /* ? */ default: printf("mpeg: warning - this MPEG has undefined timing.\n"); - return 0; + break; } + if(vin_ptr) + { + if(l_rc != 0) vin_ptr->framerate = l_framerate; + p_set_video_info(vin_ptr ,basename); + g_free(vin_ptr); + } + return(l_rc); } @@ -427,11 +443,11 @@ load_image (char *filename, data = g_malloc(img.Size); - delay = MPEG_frame_period_ms(img.PictureRate); + delay = MPEG_frame_period_ms(img.PictureRate, basename); /* printf(" <%d : %d> %dx%d - d%d - bmp%d - ps%d - s%d\n", - img.PictureRate, MPEG_frame_period_ms(img.PictureRate), + img.PictureRate, delay, img.Width,img.Height,img.Depth,img.BitmapPad,img.PixelSize,img.Size); */ @@ -567,6 +583,7 @@ load_image (char *filename, PARAM_STRING, framename, PARAM_STRING, framename, /* raw name ? */ PARAM_END); + p_gimp_file_save_thumbnail(image_ID, framename); } } diff --git a/plug-ins/gap/gap_decode_xanim.c b/plug-ins/gap/gap_decode_xanim.c index 8cb4457214..b1159e9b32 100644 --- a/plug-ins/gap/gap_decode_xanim.c +++ b/plug-ins/gap/gap_decode_xanim.c @@ -48,6 +48,7 @@ */ /* revision history + * 1.1.14a; 1999/11/22 hof: fixed gcc warning (too many arguments for format) * 1.1.13a; 1999/11/22 hof: first release */ @@ -545,12 +546,12 @@ p_rename_frames(gint32 frame_from, gint32 frame_to, char *basename, char *ext) if (strcmp(l_src_frame, l_dst_frame) != 0) { - /* check overwrire if Destination frame already exsts */ + /* check overwrite if Destination frame already exsts */ l_overwrite_mode = p_overwrite_dialog(l_dst_frame, l_overwrite_mode); if (l_overwrite_mode < 0) { sprintf(global_errlist, - _("frames are not extracted, because overwrite was cancelled"), + _("frames are not extracted, because overwrite of %s was cancelled"), l_dst_frame); return(-1); } diff --git a/plug-ins/gap/gap_lib.c b/plug-ins/gap/gap_lib.c index 630db0d5a9..2dbb3be1ce 100644 --- a/plug-ins/gap/gap_lib.c +++ b/plug-ins/gap/gap_lib.c @@ -28,6 +28,8 @@ */ /* revision history: + * 1.1.14a; 1999/12/18 hof: handle .xvpics on fileops (copy, rename and delete) + * new: p_get_frame_nr, * 1.1.9a; 1999/09/14 hof: handle frame filenames with framenumbers * that are not the 4digit style. (like frame1.xcf) * 1.1.8a; 1999/08/31 hof: for AnimFrame Filtypes != XCF: @@ -53,7 +55,7 @@ */ #include "config.h" -/* SYTEM (UNIX) includes */ +/* SYSTEM (UNIX) includes */ #include #include #include @@ -89,8 +91,8 @@ /* GAP includes */ #include "gap_layer_copy.h" #include "gap_lib.h" +#include "gap_pdb_calls.h" #include "gap_arr_dialog.h" -#include "gap_mov_dialog.h" #include "gap_exchange_image.h" extern int gap_debug; /* ==0 ... dont print debug infos */ @@ -108,6 +110,43 @@ static int p_delete_frame(t_anim_info *ainfo_ptr, long nr); static int p_del(t_anim_info *ainfo_ptr, long cnt); static int p_decide_save_as(gint32 image_id, char *sav_name); +/* ============================================================================ + * p_alloc_fname_thumbnail + * return the thumbnail name (in .xvpics subdir) + * for the given filename + * ============================================================================ + */ +char * +p_alloc_fname_thumbnail(char *name) +{ + int l_len; + int l_idx; + char *l_str; + + if(name == NULL) + { + return(g_strdup("\0")); + } + + l_len = strlen(name); + l_str = g_malloc(l_len+10); + strcpy(l_str, name); + if(l_len > 0) + { + for(l_idx = l_len -1; l_idx > 0; l_idx--) + { + if((name[l_idx] == G_DIR_SEPARATOR) || (name[l_idx] == DIR_ROOT)) + { + l_idx++; + break; + } + } + sprintf(&l_str[l_idx], ".xvpics%s%s", G_DIR_SEPARATOR_S, &name[l_idx]); + } + if(gap_debug) printf("p_alloc_fname_thumbnail: thumbname=%s:\n", l_str ); + return(l_str); +} + /* ============================================================================ * p_strdup_*_underscore * duplicate string and if last char is no underscore add one at end. @@ -226,6 +265,32 @@ int p_file_exists(char *fname) return(1); } /* end p_file_exists */ +/* ============================================================================ + * p_image_file_copy + * (copy the imagefile and its thumbnail) + * ============================================================================ + */ +int p_image_file_copy(char *fname, char *fname_copy) +{ + char *l_from_fname_thumbnail; + char *l_to_fname_thumbnail; + int l_rc; + + l_from_fname_thumbnail = p_alloc_fname_thumbnail(fname); + l_to_fname_thumbnail = p_alloc_fname_thumbnail(fname_copy); + + l_rc = p_file_copy(fname, fname_copy); + if((l_from_fname_thumbnail) + && (l_to_fname_thumbnail)) + { + p_file_copy(l_from_fname_thumbnail, l_to_fname_thumbnail); + } + + if(l_from_fname_thumbnail) g_free(l_from_fname_thumbnail); + if(l_to_fname_thumbnail) g_free(l_to_fname_thumbnail); + return(l_rc); +} + /* ============================================================================ * p_file_copy * ============================================================================ @@ -293,15 +358,23 @@ int p_file_copy(char *fname, char *fname_copy) int p_delete_frame(t_anim_info *ainfo_ptr, long nr) { char *l_fname; + char *l_fname_thumbnail; int l_rc; l_fname = p_alloc_fname(ainfo_ptr->basename, nr, ainfo_ptr->extension); if(l_fname == NULL) { return(1); } + + l_fname_thumbnail = p_alloc_fname_thumbnail(l_fname); + if(l_fname_thumbnail == NULL) { return(1); } if(gap_debug) fprintf(stderr, "\nDEBUG p_delete_frame: %s\n", l_fname); l_rc = remove(l_fname); + + if(gap_debug) fprintf(stderr, "\nDEBUG p_delete_frame: %s\n", l_fname_thumbnail); + remove(l_fname_thumbnail); g_free(l_fname); + g_free(l_fname_thumbnail); return(l_rc); @@ -315,6 +388,8 @@ int p_rename_frame(t_anim_info *ainfo_ptr, long from_nr, long to_nr) { char *l_from_fname; char *l_to_fname; + char *l_from_fname_thumbnail; + char *l_to_fname_thumbnail; int l_rc; l_from_fname = p_alloc_fname(ainfo_ptr->basename, from_nr, ainfo_ptr->extension); @@ -323,12 +398,22 @@ int p_rename_frame(t_anim_info *ainfo_ptr, long from_nr, long to_nr) l_to_fname = p_alloc_fname(ainfo_ptr->basename, to_nr, ainfo_ptr->extension); if(l_to_fname == NULL) { g_free(l_from_fname); return(1); } + l_from_fname_thumbnail = p_alloc_fname_thumbnail(l_from_fname); + if(l_from_fname_thumbnail == NULL) { return(1); } + + l_to_fname_thumbnail = p_alloc_fname_thumbnail(l_to_fname); + if(l_to_fname_thumbnail == NULL) { g_free(l_from_fname_thumbnail); return(1); } + if(gap_debug) fprintf(stderr, "\nDEBUG p_rename_frame: %s ..to.. %s\n", l_from_fname, l_to_fname); l_rc = rename(l_from_fname, l_to_fname); + if(gap_debug) fprintf(stderr, "\nDEBUG p_rename_frame: %s ..to.. %s\n", l_from_fname_thumbnail, l_to_fname_thumbnail); + rename(l_from_fname_thumbnail, l_to_fname_thumbnail); g_free(l_from_fname); g_free(l_to_fname); + g_free(l_from_fname_thumbnail); + g_free(l_to_fname_thumbnail); return(l_rc); @@ -727,6 +812,52 @@ void p_free_ainfo(t_anim_info **ainfo) } +/* ============================================================================ + * p_get_frame_nr + * ============================================================================ + */ +long +p_get_frame_nr_from_name(char *fname) +{ + long number; + int len; + char *basename; + if(fname == NULL) return(-1); + + basename = p_alloc_basename(fname, &number); + if(basename == NULL) return(-1); + + len = strlen(basename); + g_free(basename); + + if(number > 0) return(number); + + if(fname[len] == '0') return(number); +/* + * if(fname[len] == '_') + * { + * if(fname[len+1] == '0') return(TRUE); + * } + */ + return(-1); +} + +long +p_get_frame_nr(gint32 image_id) +{ + char *fname; + long number; + + number = -1; + fname = gimp_image_get_filename(image_id); + if(fname) + { + number = p_get_frame_nr_from_name(fname); + g_free(fname); + } + return (number); +} + /* ============================================================================ * p_chk_framechange * @@ -948,6 +1079,8 @@ gint32 p_save_named_image(gint32 image_id, char *sav_name, GRunModeType run_mode if(gap_debug) fprintf(stderr, "DEBUG: after p_save_named_image: '%s' nlayers=%d image=%d drw=%d run_mode=%d\n", sav_name, (int)l_nlayers, (int)image_id, (int)l_drawable->id, (int)run_mode); + p_gimp_file_save_thumbnail(image_id, sav_name); + g_free (l_layers_list); g_free (l_drawable); @@ -1104,7 +1237,10 @@ int p_save_named_frame(gint32 image_id, char *sav_name) * so lets try a copy ; remove sequence */ if(gap_debug) fprintf(stderr, "DEBUG: p_save_named_frame: RENAME 2nd try\n"); - if(0 == p_file_copy(l_tmpname, sav_name)) remove(l_tmpname); + if(0 == p_file_copy(l_tmpname, sav_name)) + { + remove(l_tmpname); + } else { fprintf(stderr, "ERROR in p_save_named_frame: cant rename %s to %s\n", @@ -1125,6 +1261,8 @@ int p_save_named_frame(gint32 image_id, char *sav_name) } } + p_gimp_file_save_thumbnail(image_id, sav_name); + g_free(l_tmpname); /* free temporary name */ return l_rc; @@ -1461,6 +1599,7 @@ int p_dup(t_anim_info *ainfo_ptr, long cnt, long range_from, long range_to) l_hi = l_lo + l_cnt2; while(l_lo > l_src_nr_max) { + sprintf(g_errtxt, "BEFORE rename frame %ld to %ld\n", l_lo, l_hi); if(0 != p_rename_frame(ainfo_ptr, l_lo, l_hi)) { sprintf(g_errtxt, "Error: could not rename frame %ld to %ld\n", l_lo, l_hi); @@ -1487,7 +1626,7 @@ int p_dup(t_anim_info *ainfo_ptr, long cnt, long range_from, long range_to) l_dup_name = p_alloc_fname(ainfo_ptr->basename, l_hi, ainfo_ptr->extension); if((l_dup_name != NULL) && (l_curr_name != NULL)) { - p_file_copy(l_curr_name, l_dup_name); + p_image_file_copy(l_curr_name, l_dup_name); g_free(l_dup_name); g_free(l_curr_name); } diff --git a/plug-ins/gap/gap_lib.h b/plug-ins/gap/gap_lib.h index a2a1b890df..83fa75c83e 100644 --- a/plug-ins/gap/gap_lib.h +++ b/plug-ins/gap/gap_lib.h @@ -25,6 +25,7 @@ */ /* revision history: + * 1.1.14a; 2000/01/02 hof: new: p_get_frame_nr * 1.1.8a; 1999/08/31 hof: new: p_strdup_del_underscore and p_strdup_add_underscore * 0.99.00; 1999/03/15 hof: prepared for win/dos filename conventions * 0.96.02; 1998/08/05 hof: extended gap_dup (duplicate range instead of singele frame) @@ -77,7 +78,6 @@ typedef struct t_anim_info { long last_frame_nr; } t_anim_info; - /* procedures used in other gap*.c files */ int p_file_exists(char *fname); int p_file_copy(char *fname, char *fname_copy); @@ -98,6 +98,11 @@ char* p_gzip (char *orig_name, char *new_name, char *zip); char* p_strdup_add_underscore(char *name); char* p_strdup_del_underscore(char *name); +long p_get_frame_nr(gint32 image_id); +long p_get_frame_nr_from_name(char *fname); +char *p_alloc_fname_thumbnail(char *name); +int p_image_file_copy(char *fname, char *fname_copy); + /* animation menu fuctions provided by gap_lib.c */ int gap_next(GRunModeType run_mode, gint32 image_id); @@ -113,7 +118,6 @@ int gap_shift(GRunModeType run_mode, gint32 image_id, int nr, long range_from, l void p_msg_win(GRunModeType run_mode, char *msg); - #endif diff --git a/plug-ins/gap/gap_main.c b/plug-ins/gap/gap_main.c index 7fe6631601..a7d106cb58 100644 --- a/plug-ins/gap/gap_main.c +++ b/plug-ins/gap/gap_main.c @@ -38,9 +38,10 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -static char *gap_main_version = "1.1.13a; 1999/11/26"; +static char *gap_main_version = "1.1.14a; 2000/01/01"; /* revision history: + * gimp 1.1.14a; 2000/01/01 hof: bugfix params for gap_dup in noninteractive mode * gimp 1.1.13a; 1999/11/26 hof: splitted frontends for external programs (mpeg encoders) * to gap_frontends_main.c * gimp 1.1.11a; 1999/11/15 hof: changed Menunames (AnimFrames to Video, Submenu Encode) @@ -804,7 +805,7 @@ run (char *name, image_id = param[1].data.d_image; nr = param[3].data.d_int32; /* how often to copy current frame */ - if (n_params != 6) + if (n_params > 5) { range_from = param[4].data.d_int32; /* frame nr to start */ range_to = param[5].data.d_int32; /* frame nr to stop */ diff --git a/plug-ins/gap/gap_navigator_dialog.c b/plug-ins/gap/gap_navigator_dialog.c new file mode 100644 index 0000000000..5edb766634 --- /dev/null +++ b/plug-ins/gap/gap_navigator_dialog.c @@ -0,0 +1,3190 @@ +/* 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 1.st 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) ?? are these values translated or not + * + */ + + +/* revision history: + * version 1.1.14a; 2000.01.08 hof: 1.st release + */ + +static char *gap_navigator_version = "1.1.14a; 2000/01/08"; + + +/* 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 + +/* + * gimp_help_set_help_data is not available for plugins in libgimp 1.1.14 + * workaround: use a copy of gimp-1.1.14/app/gimphelp.c:gimp_help_set_help_data + */ +static void gimp_help_set_help_data (GtkWidget *widget, + gchar *tooltip, + gchar *help_data); +/* + * 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 +{ + GtkTooltips *tool_tips; + 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; + 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; + + 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; +}; + +/* ----------------------- + * 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 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_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"), + "video/frames/play_frame.html", + NULL, 0 }, + { update_xpm, navi_dialog_thumb_update_callback, navi_dialog_update_ext_callbacks, + N_("Smart Update .xvpics\n" + " forced upd"), + "video/frames/play_frame.html", + NULL, 0 }, + { duplicate_xpm, navi_dialog_frames_duplicate_frame_callback, NULL, + N_("Duplicate selected Frames"), + "video/frames/duplicate_frame.html", + NULL, 0 }, + { delete_xpm, navi_dialog_frames_delete_frame_callback, NULL, + N_("Delete selected Frames"), + "video/frames/delete_frame.html", + 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 1.st Frame"), + "video/frames/goto_frame.html", + 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"), + "video/frames/goto_frame.html", + 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"), + "video/frames/goto_frame.html", + NULL, 0 }, + { last_xpm, navi_dialog_vcr_goto_last_callback, NULL, + N_("Goto last Frame"), + "video/frames/goto_frame.html", + 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, GParam *param, + int *nretvals, GParam **retvals); + +GPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run, /* run_proc */ +}; + +/* ------------------------ + * MAIN, query & run + * ------------------------ + */ + +MAIN () + +static void +query () +{ + static GParamDef args_navigator[] = + { + {PARAM_INT32, "run_mode", "Interactive"}, + {PARAM_IMAGE, "image", "(unused)"}, + {PARAM_DRAWABLE, "drawable", "(unused)"}, + }; + static int nargs_navigator = sizeof(args_navigator) / sizeof(args_navigator[0]); + + static GParamDef *return_vals = NULL; + static int nreturn_vals = 0; + + + gimp_install_procedure(PLUGIN_NAME, + _("GAP video navigator dialog"), + "", + "Wolfgang Hofer (hof@hotbot.com)", + "Wolfgang Hofer", + gap_navigator_version, + N_("/Video/VCR Navigator..."), + "RGB*, INDEXED*, GRAY*", + PROC_PLUG_IN, + nargs_navigator, nreturn_vals, + args_navigator, return_vals); +} /* end query */ + + + +static void +run (char *name, + int n_params, + GParam *param, + int *nreturn_vals, + GParam **return_vals) +{ + gint32 l_active_image; + char *l_env; + + static GParam values[2]; + GRunModeType run_mode; + GStatusType status = STATUS_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 == 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(RUN_INTERACTIVE, "Cant open 2 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 != RUN_INTERACTIVE) + { + status = STATUS_CALLING_ERROR; + } + + if (status == STATUS_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 = STATUS_EXECUTION_ERROR; + } + + values[0].type = PARAM_STATUS; + values[0].data.d_status = status; +} + + +/* --------------------------------- + * the navigator callback procedures + * --------------------------------- + */ +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); + + /* hof: I don't know if the value_string in the preferences + * is translated to other languages or not + * and how to handle the value from a plugin + * This version assumes translated values + */ + + 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; + /* hof: I don't know if the value_string in the preferences + * is translated to other languages or not. + * so i did check for the translated values too, + * that may work for most Languages. + */ + else 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); + if(preview_size == 0) + { + printf("Warning: preview-size or video-preview-size could not be detected\n"); + printf(" because the configured value %s\n", value_string); + printf(" does not match with the current translations of\n"); + printf(" %s %s %s %s %s %s\n" + , _("none"), _("tiny"), _("small") + , _("medium"), _("large"), _("huge")); + } + } + + 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, 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 1.st 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) + { + sprintf(frame_nr_to_char, "%04d - %04d" + , (int)naviD->ainfo_ptr->first_frame_nr + , (int)naviD->ainfo_ptr->last_frame_nr ); + gtk_label_set (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 +navi_cancel_callback(GtkWidget *widget, + gpointer client_data) +{ + gtk_main_quit (); +} + +static int +navi_delete_callback(GtkWidget *widget, + GdkEvent *e, + gpointer data) +{ + navi_cancel_callback (widget, data); + + return TRUE; +} + + + +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; + if(naviD->tool_tips == 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) + { + gtk_tooltips_enable (naviD->tool_tips); + } + else + { + gtk_tooltips_disable (naviD->tool_tips); + } + } +} + +static void gimp_help_set_help_data (GtkWidget *widget, + gchar *tooltip, + gchar *help_data) +{ + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (naviD != NULL); + g_return_if_fail (naviD->tool_tips != NULL); + + if (tooltip) + { + gtk_tooltips_set_tip (naviD->tool_tips, widget, tooltip, help_data); + } + else if (help_data) + { + gtk_object_set_data (GTK_OBJECT (widget), "gimp_help_data", help_data); + } +} + + + +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_query_images (&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_malloc(sizeof(OpenFrameImages)); + 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_malloc(sizeof(SelectedRange)); + 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); +} + +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; + GParam *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; + sprintf(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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_INT32, l_from, + PARAM_INT32, l_to, + PARAM_INT32, 3, /* flatten image */ + PARAM_INT32, 1, /* BG_VISIBLE */ + PARAM_INT32, (gint32)naviD->vin_ptr->framerate, + PARAM_STRING, l_frame_name, + PARAM_INT32, 6, /* use all visible layers */ + PARAM_INT32, 0, /* ignore case */ + PARAM_INT32, 0, /* normal selection (no invert) */ + PARAM_STRING, "0", /* select string (ignored) */ + PARAM_END); + + if (return_vals[0].data.d_status == STATUS_SUCCESS) + { + l_new_image_id = return_vals[1].data.d_image; + + if(optimize) + { + return_vals = gimp_run_procedure ("plug_in_animationoptimize", + &nreturn_vals, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, l_new_image_id, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_END); + if (return_vals[0].data.d_status == STATUS_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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, l_new_image_id, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_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; + GParam *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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_INT32, range_list->from, + PARAM_END); + if (return_vals[0].data.d_status == STATUS_SUCCESS) + { + return_vals = gimp_run_procedure ("plug_in_gap_dup", + &nreturn_vals, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_INT32, 1, /* copy block 1 times */ + PARAM_INT32, range_list->from, + PARAM_INT32, range_list->to, + PARAM_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; + GParam *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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_INT32, range_list->from, + PARAM_END); + if (return_vals[0].data.d_status == STATUS_SUCCESS) + { + return_vals = gimp_run_procedure ("plug_in_gap_del", + &nreturn_vals, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_INT32, 1 + (range_list->to - range_list->from), /* number of frames to delete */ + PARAM_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) +{ + GParam *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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_INT32, dst_framenr, + PARAM_END); + navi_update_after_goto(); +} + +void navi_dialog_vcr_goto_first_callback(GtkWidget *w, gpointer data) +{ + GParam *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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_END); + navi_update_after_goto(); +} + +void navi_dialog_vcr_goto_prev_callback(GtkWidget *w, gpointer data) +{ + GParam *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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_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) +{ + GParam *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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_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) +{ + GParam *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, + PARAM_INT32, RUN_NONINTERACTIVE, + PARAM_IMAGE, naviD->active_imageid, + PARAM_DRAWABLE, -1, /* dummy */ + PARAM_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:nothing implemented on GDK_BUTTON_PRESS event\n"); + 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_window_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_pixmap_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 NOT IMPLEMENTED\n" + , (int)bevent->button); + + if (bevent->button == 3) + { +/* + * 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_pixmap (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_pixmap (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 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) + { + sprintf(buf, "min:sec:msec"); + return; + } + + if(naviD->vin_ptr->framerate < 1) + { + sprintf(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; + + sprintf(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); + + gtk_label_set (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; */ + + sprintf(frame_nr_to_char, "%04d", (int)frame_nr); + navi_calc_frametiming(frame_nr, 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); + + gtk_widget_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_pixmap_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 */ + gtk_widget_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; + 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; + sprintf(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) + { + sprintf(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 (); + } + naviD->tooltip_on = -44; + naviD->tool_tips = gtk_tooltips_new (); + navi_dialog_tooltips(); + + /* The main vbox */ + naviD->vbox = gtk_event_box_new (); + + gimp_help_set_help_data (naviD->vbox, NULL, "dialogs/layers/layers.html"); + + 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); + + 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; + guchar *color_cube; + 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 ()); + + gdk_set_use_xshm (gimp_use_xshm ()); + gtk_preview_set_gamma(gimp_gamma()); + gtk_preview_set_install_cmap(gimp_install_cmap()); + color_cube = gimp_color_cube(); + gtk_preview_set_color_cube(color_cube[0], color_cube[1], color_cube[2], color_cube[3]); +/* gtk_widget_set_default_visual (gtk_preview_get_visual ()); + */ + gtk_widget_set_default_colormap(gtk_preview_get_cmap()); + + /* The main shell */ + shell = gtk_dialog_new (); + gtk_window_set_wmclass (GTK_WINDOW (shell), "gap_navigator", "Gimp"); + gtk_window_set_title (GTK_WINDOW (shell), _("Video Navigator")); + + /* 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); + + gtk_signal_connect (GTK_OBJECT (shell), "delete_event", + GTK_SIGNAL_FUNC (navi_delete_callback), + naviD); + + /* The action area */ + gtk_container_set_border_width + (GTK_CONTAINER (GTK_DIALOG (shell)->action_area), 1); + + /* The close button */ + button = gtk_button_new_with_label (_("Close")); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (navi_delete_callback), + naviD); + 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 */ diff --git a/plug-ins/gap/gap_pdb_calls.c b/plug-ins/gap/gap_pdb_calls.c index 17fa84c6cc..bda701150c 100644 --- a/plug-ins/gap/gap_pdb_calls.c +++ b/plug-ins/gap/gap_pdb_calls.c @@ -5,7 +5,7 @@ * IMPORTANT Notes: * some Procedures have changed their Interface from GIMP 1.0.2 to GIMP 1.1 * in that cases the procedure parameters are checked and the call is done - * in 1.0.2 or 1.1 style repectivly. + * in 1.0.2 or 1.1 style respectivly. * * some of these procedures are not available in the official GIMP 1.0.2 releases * (and prior releases) @@ -37,6 +37,8 @@ */ /* revision history: + * version 1.1.14a; 2000/01/09 hof: thumbnail save/load, + * Procedures for video_info file * version 0.98.00; 1998/11/28 hof: 1.st (pre) release (GAP port to GIMP 1.1) */ #include @@ -833,3 +835,263 @@ gint p_gimp_drawable_set_image(gint32 drawable_id, gint32 image_id) return(-1); } /* end p_gimp_drawable_set_image */ +/* ============================================================================ + * p_gimp_gimprc_query + * + * ============================================================================ + */ +char* +p_gimp_gimprc_query(char *key) +{ + GParam *return_vals; + gint nreturn_vals; + char *value; + + return_vals = gimp_run_procedure ("gimp_gimprc_query", + &nreturn_vals, + PARAM_STRING, key, + PARAM_END); + + value = NULL; + if (return_vals[0].data.d_status == STATUS_SUCCESS) + { + if(return_vals[1].data.d_string != NULL) + { + value = g_strdup(return_vals[1].data.d_string); + } + } + gimp_destroy_params (return_vals, nreturn_vals); + + return(value); +} + + +/* ============================================================================ + * p_gimp_file_save_thumbnail + * + * ============================================================================ + */ + +gint +p_gimp_file_save_thumbnail(gint32 image_id, char* filename) +{ + static char *l_called_proc = "gimp_file_save_thumbnail"; + GParam *return_vals; + int nreturn_vals; + + if (p_pdb_procedure_available(l_called_proc) >= 0) + { + return_vals = gimp_run_procedure (l_called_proc, + &nreturn_vals, + PARAM_IMAGE, image_id, + PARAM_STRING, filename, + PARAM_END); + + if (return_vals[0].data.d_status == STATUS_SUCCESS) + { + return (0); + } + printf("GAP: Error: PDB call of %s failed\n", l_called_proc); + } + else + { + printf("GAP: Warning: Procedure %s not found. %s\n", + l_called_proc, + "(cannot save thumbnails)"); + } + return(-1); +} /* end p_gimp_file_save_thumbnail */ + +/* ============================================================================ + * p_gimp_file_load_thumbnail + * + * ============================================================================ + */ + +gint +p_gimp_file_load_thumbnail(char* filename, gint32 *th_width, gint32 *th_height, unsigned char **th_data) +{ + static char *l_called_proc = "gimp_file_load_thumbnail"; + GParam *return_vals; + int nreturn_vals; + int bytesize; + unsigned char *l_ptr; + + *th_data = NULL; + if (p_pdb_procedure_available(l_called_proc) >= 0) + { + return_vals = gimp_run_procedure (l_called_proc, + &nreturn_vals, + PARAM_STRING, filename, + PARAM_END); + + if (return_vals[0].data.d_status == STATUS_SUCCESS) + { + *th_width = return_vals[1].data.d_int32; + *th_height = return_vals[2].data.d_int32; + l_ptr = return_vals[3].data.d_int8array; + + bytesize = 3 * (*th_width) * (*th_height); + *th_data = g_malloc(bytesize); + if((*th_data != NULL) && (l_ptr != NULL)) + { + + /* BUG in the PDB Interface in gimp.1.1.14 + * the return_vals[3].data.d_int8array does NOT really point + * to the thumbnail data (as it should) + * and we retrieve only NONSENSE DATA here. + * + * (maybe we need an additional data length parameter + * as it is usual for array parameter passing ?) + */ + + memcpy(*th_data, l_ptr, bytesize); + return (0); + } + } + printf("GAP: Error: PDB call of %s failed\n", l_called_proc); + } + else + { + printf("GAP: Warning: Procedure %s not found. %s\n", + l_called_proc, + "(cannot load thumbnails)"); + } + return(-1); +} /* end p_gimp_file_load_thumbnail */ + + + +gint p_gimp_image_thumbnail(gint32 image_id, gint32 width, gint32 height, + gint32 *th_width, gint32 *th_height, gint32 *th_bpp, + gint32 *th_data_count, unsigned char **th_data) +{ + static char *l_called_proc = "gimp_image_thumbnail"; + GParam *return_vals; + int nreturn_vals; + unsigned char *l_ptr; + gint32 *l_ptr32; + + *th_data = NULL; + if (p_pdb_procedure_available(l_called_proc) >= 0) + { + return_vals = gimp_run_procedure (l_called_proc, + &nreturn_vals, + PARAM_IMAGE, image_id, + PARAM_INT32, width, + PARAM_INT32, height, + PARAM_END); + + if (return_vals[0].data.d_status == STATUS_SUCCESS) + { + *th_width = return_vals[1].data.d_int32; + *th_height = return_vals[2].data.d_int32; + *th_bpp = return_vals[3].data.d_int32; + *th_data_count = return_vals[4].data.d_int32; + l_ptr = return_vals[5].data.d_int8array; + + *th_data = g_malloc(*th_data_count); + if((*th_data != NULL) && (l_ptr != NULL)) + { + memcpy(*th_data, l_ptr, *th_data_count); + return (0); + } + } + printf("GAP: Error: PDB call of %s failed\n", l_called_proc); + } + else + { + printf("GAP: Warning: Procedure %s not found. %s\n", + l_called_proc, + "(cannot get image thumbnail)"); + } + return(-1); +} /* end p_gimp_image_thumbnail */ + + +/* ============================================================================ + * Procedures to get/set the video_info_file + * ============================================================================ + */ + +char * +p_alloc_video_info_name(char *basename) +{ + int l_len; + char *l_str; + + if(basename == NULL) + { + return(NULL); + } + + l_len = strlen(basename); + l_str = g_malloc(l_len+8); + + sprintf(l_str, "%svin.gap", basename); + return(l_str); +} + +int +p_set_video_info(t_video_info *vin_ptr, char *basename) +{ + FILE *l_fp; + char *l_vin_filename; + int l_rc; + + l_rc = -1; + l_vin_filename = p_alloc_video_info_name(basename); + if(l_vin_filename) + { + l_fp = fopen(l_vin_filename, "w"); + if(l_fp) + { + fprintf(l_fp, "# GIMP / GAP Videoinfo file\n"); + fprintf(l_fp, "(framerate %f) # 1.0 upto 100.0 frames per sec\n", (float)vin_ptr->framerate); + fprintf(l_fp, "(timezoom %d) # 1 upto 100 frames\n", (int)vin_ptr->timezoom ); + fclose(l_fp); + l_rc = 0; + } + g_free(l_vin_filename); + } + return(l_rc); +} + +t_video_info * +p_get_video_info(char *basename) +{ + FILE *l_fp; + char *l_vin_filename; + t_video_info *l_vin_ptr; + char l_buf[4000]; + int l_len; + + l_vin_ptr = g_malloc(sizeof(t_video_info)); + l_vin_ptr->timezoom = 1; + l_vin_ptr->framerate = 24.0; + + l_vin_filename = p_alloc_video_info_name(basename); + if(l_vin_filename) + { + l_fp = fopen(l_vin_filename, "r"); + if(l_fp) + { + while(NULL != fgets(l_buf, 4000-1, l_fp)) + { + l_len = strlen("(framerate "); + if(strncmp(l_buf, "(framerate ", l_len) == 0) + { + l_vin_ptr->framerate = atof(&l_buf[l_len]); + } + l_len = strlen("(timezoom "); + if (strncmp(l_buf, "(timezoom ", l_len ) == 0) + { + l_vin_ptr->timezoom = atol(&l_buf[l_len]); + } + } + fclose(l_fp); + } + g_free(l_vin_filename); + } + return(l_vin_ptr); +} diff --git a/plug-ins/gap/gap_pdb_calls.h b/plug-ins/gap/gap_pdb_calls.h index 9a856e1381..1cbee1ff09 100644 --- a/plug-ins/gap/gap_pdb_calls.h +++ b/plug-ins/gap/gap_pdb_calls.h @@ -21,6 +21,8 @@ */ /* revision history: + * version 1.1.14a; 2000/01/06 hof: thumbnail save/load, + * Procedures for video_info file * version 0.98.00; 1998/11/30 hof: all PDB-calls of GIMP PDB-Procedures */ @@ -29,6 +31,12 @@ #include "libgimp/gimp.h" +typedef struct t_video_info { + gdouble framerate; /* playback rate in frames per second */ + gint32 timezoom; +} t_video_info; + + gint p_pdb_procedure_available(char *proc_name); gint p_get_gimp_selection_bounds (gint32 image_id, gint32 *x1, gint32 *y1, gint32 *x2, gint32 *y2); gint p_gimp_selection_load (gint32 image_id, gint32 channel_id); @@ -53,4 +61,19 @@ gint32 p_gimp_channel_ops_duplicate (gint32 image_ID); gint p_gimp_drawable_set_image(gint32 drawable_id, gint32 image_id); +char* p_gimp_gimprc_query(char *key); + +gint p_gimp_file_save_thumbnail(gint32 image_id, char* filename); +gint p_gimp_file_load_thumbnail(char* filename, gint32 *th_width, gint32 *th_height, unsigned char **th_data); + +gint p_gimp_image_thumbnail(gint32 image_id, gint32 width, gint32 height, + gint32 *th_width, gint32 *th_height, gint32 *th_bpp, + gint32 *th_data_count, unsigned char **th_data); + + + + +char *p_alloc_video_info_name(char *basename); +int p_set_video_info(t_video_info *vin_ptr, char *basename); +t_video_info *p_get_video_info(char *basename); #endif diff --git a/plug-ins/gap/gap_range_ops.c b/plug-ins/gap/gap_range_ops.c index bc913369c6..f7e771302a 100644 --- a/plug-ins/gap/gap_range_ops.c +++ b/plug-ins/gap/gap_range_ops.c @@ -32,6 +32,8 @@ */ /* revision history + * 1.1.14a 2000/01/06 hof: gap_range_to_multilayer: use framerate (from video info file) in framenames + * bugfix: gap_range_to_multilayer: first save current frame * 1.1.10a 1999/10/22 hof: bugfix: have to use the changed PDB-Interface * for gimp_convert_indexed * (with extended dither options and extra dialog window) @@ -74,6 +76,7 @@ /* GAP includes */ #include "gap_layer_copy.h" #include "gap_lib.h" +#include "gap_pdb_calls.h" #include "gap_match.h" #include "gap_arr_dialog.h" #include "gap_resi_dialog.h" @@ -918,9 +921,11 @@ gint32 gap_range_to_multilayer(GRunModeType run_mode, gint32 image_id, gint32 l_rc; long l_from, l_to; t_anim_info *ainfo_ptr; + t_video_info *vin_ptr; gint32 l_sel_mode; gint32 l_sel_case; gint32 l_sel_invert; + gdouble l_framerate; char l_sel_pattern[MAX_LAYERNAME]; @@ -932,8 +937,14 @@ gint32 gap_range_to_multilayer(GRunModeType run_mode, gint32 image_id, { if(run_mode == RUN_INTERACTIVE) { - - strcpy(frame_basename, "frame_[####]"); + l_framerate = 24.0; + vin_ptr = p_get_video_info(ainfo_ptr->basename); + if(vin_ptr) + { + if(vin_ptr->framerate > 0) l_framerate = vin_ptr->framerate; + g_free(vin_ptr); + } + sprintf(frame_basename, "frame_[####] (%dms)", (int)(1000/l_framerate)); framerate = 0; l_rc = p_range_to_multilayer_dialog (ainfo_ptr, &l_from, &l_to, &flatten_mode, &bg_visible, @@ -959,13 +970,17 @@ gint32 gap_range_to_multilayer(GRunModeType run_mode, gint32 image_id, if(l_rc >= 0) { - new_image_id = p_frames_to_multilayer(ainfo_ptr, l_from, l_to, - flatten_mode, bg_visible, - framerate, frame_basename, - l_sel_mode, sel_case, - sel_invert, &l_sel_pattern[0]); - gimp_display_new(new_image_id); - l_rc = new_image_id; + l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename); + if(l_rc >= 0) + { + new_image_id = p_frames_to_multilayer(ainfo_ptr, l_from, l_to, + flatten_mode, bg_visible, + framerate, frame_basename, + l_sel_mode, sel_case, + sel_invert, &l_sel_pattern[0]); + gimp_display_new(new_image_id); + l_rc = new_image_id; + } } } p_free_ainfo(&ainfo_ptr);