diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/actions/file-commands.c ingimp-2.4.7/app/actions/file-commands.c --- gimp-2.4.7/app/actions/file-commands.c 2008-07-10 22:30:01.000000000 +1200 +++ ingimp-2.4.7/app/actions/file-commands.c 2008-09-01 14:28:03.000000000 +1200 @@ -31,6 +31,7 @@ #include "core/gimp.h" #include "core/gimpcontainer.h" #include "core/gimpimage.h" +#include "core/gimpinteraction-logger.h" #include "core/gimpprogress.h" #include "core/gimptemplate.h" @@ -161,6 +162,8 @@ GIMP_OBJECT (imagefile)->name, FALSE, &status, &error); + guilog_last_opened(image, value, GIMP_OBJECT (imagefile)->name); + if (! image && status != GIMP_PDB_CANCEL) { gchar *filename = @@ -207,6 +210,8 @@ const gchar *uri; GimpPlugInProcedure *save_proc = NULL; + guilog_save_image_requested(image, FALSE); + uri = gimp_object_get_name (GIMP_OBJECT (image)); save_proc = gimp_image_get_save_proc (image); @@ -282,6 +287,7 @@ } case GIMP_SAVE_MODE_SAVE_AS: + guilog_save_image_requested(image, TRUE); file_save_dialog_show (display->image, widget, _("Save Image"), FALSE, save_mode == GIMP_SAVE_MODE_SAVE_AND_CLOSE); @@ -573,6 +579,7 @@ if (new_image) { + guilog_revert_image(old_image, new_image); gimp_displays_reconnect (gimp, old_image, new_image); gimp_image_flush (new_image); diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/actions/image-commands.c ingimp-2.4.7/app/actions/image-commands.c --- gimp-2.4.7/app/actions/image-commands.c 2008-07-10 22:30:01.000000000 +1200 +++ ingimp-2.4.7/app/actions/image-commands.c 2008-09-01 14:28:03.000000000 +1200 @@ -40,6 +40,7 @@ #include "core/gimpimage-rotate.h" #include "core/gimpimage-scale.h" #include "core/gimpimage-undo.h" +#include "core/gimpinteraction-logger.h" #include "core/gimpprogress.h" #include "widgets/gimpdialogfactory.h" @@ -439,6 +440,8 @@ new_image = gimp_image_duplicate (display->image); + guilog_image_duplicate(display->image, new_image); + gimp_create_display (new_image->gimp, new_image, shell->unit, diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/actions/plug-in-commands.c ingimp-2.4.7/app/actions/plug-in-commands.c --- gimp-2.4.7/app/actions/plug-in-commands.c 2008-07-10 22:30:01.000000000 +1200 +++ ingimp-2.4.7/app/actions/plug-in-commands.c 2008-09-01 14:28:03.000000000 +1200 @@ -33,6 +33,7 @@ #include "core/gimpdrawable.h" #include "core/gimpimage.h" #include "core/gimpitem.h" +#include "core/gimpinteraction-logger.h" #include "core/gimpparamspecs.h" #include "core/gimpprogress.h" @@ -185,8 +186,11 @@ break; } - if (n_args >= 1) + if (n_args >= 1) { + guilog_plugin_invoked(procedure, FALSE); plug_in_procedure_execute (proc, gimp, display, args, n_args); + guilog_plugin_returned(procedure); + } g_value_array_free (args); } @@ -220,6 +224,7 @@ GIMP_PROCEDURE (procedure)->args, args, 1); + guilog_plugin_invoked(GIMP_PROCEDURE(procedure), TRUE); plug_in_procedure_execute (procedure, gimp, display, args, n_args); g_value_array_free (args); diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/app.c ingimp-2.4.7/app/app.c --- gimp-2.4.7/app/app.c 2008-07-10 22:30:13.000000000 +1200 +++ ingimp-2.4.7/app/app.c 2008-09-01 14:28:03.000000000 +1200 @@ -44,6 +44,7 @@ #include "core/gimp.h" #include "core/gimp-user-install.h" +#include "core/gimpinteraction-logger.h" #include "file/file-open.h" @@ -230,8 +231,10 @@ } #ifndef GIMP_CONSOLE_COMPILATION - if (! no_interface) + if (! no_interface) { gui_post_init (gimp); + guilog_start(gimp); + } #endif batch_run (gimp, batch_interpreter, batch_commands); diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/core/gimp.c ingimp-2.4.7/app/core/gimp.c --- gimp-2.4.7/app/core/gimp.c 2008-07-10 22:29:50.000000000 +1200 +++ ingimp-2.4.7/app/core/gimp.c 2008-09-01 14:28:03.000000000 +1200 @@ -64,6 +64,7 @@ #include "gimpgradient-load.h" #include "gimpimage.h" #include "gimpimagefile.h" +#include "gimpinteraction-logger.h" #include "gimplist.h" #include "gimpmarshal.h" #include "gimppalette.h" @@ -927,6 +928,9 @@ gimp_image_parasite_attach (image, parasite); gimp_parasite_free (parasite); } + guilog_create_image(image, comment); + } else { + guilog_create_image(image, NULL); } return image; diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/core/gimpimage.c ingimp-2.4.7/app/core/gimpimage.c --- gimp-2.4.7/app/core/gimpimage.c 2008-07-10 22:29:50.000000000 +1200 +++ ingimp-2.4.7/app/core/gimpimage.c 2008-09-01 14:28:03.000000000 +1200 @@ -50,6 +50,7 @@ #include "gimpimage-quick-mask.h" #include "gimpimage-undo.h" #include "gimpimage-undo-push.h" +#include "gimpinteraction-logger.h" #include "gimplayer.h" #include "gimplayer-floating-sel.h" #include "gimplayermask.h" @@ -819,6 +820,8 @@ { GimpImage *image = GIMP_IMAGE (object); + guilog_image_dispose(image); + gimp_image_undo_free (image); gimp_container_remove_handler (image->layers, @@ -1890,6 +1893,9 @@ GimpUndo *undo) { g_return_if_fail (GIMP_IS_IMAGE (image)); + + guilog_undo_event(image, event, undo); + g_return_if_fail (((event == GIMP_UNDO_EVENT_UNDO_FREE || event == GIMP_UNDO_EVENT_UNDO_FREEZE || event == GIMP_UNDO_EVENT_UNDO_THAW) && undo == NULL) || diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/core/gimpinteraction-logger.c ingimp-2.4.7/app/core/gimpinteraction-logger.c --- gimp-2.4.7/app/core/gimpinteraction-logger.c 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/app/core/gimpinteraction-logger.c 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,2282 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "config.h" +#include +#include /* For G_OS_WIN32 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "config/gimpbaseconfig.h" + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "core-types.h" + +#include "base/gimphistogram.h" +#include "base/pixel-region.h" +#include "base/temp-buf.h" +#include "base/tile-manager.h" + +#include "paint-funcs/paint-funcs.h" + +#include "gimpcontainer.h" +#include "gimpdrawable-histogram.h" +#include "gimp.h" +#include "gimpimage.h" +#include "gimpimage-colorhash.h" +#include "gimpimage-merge.h" +#include "gimpimage-undo.h" +#include "gimplayer.h" +#include "gimplayermask.h" +#include "gimplist.h" +#include "gimpundo.h" +#include "gimpundostack.h" +#include "gimpcontext.h" +#include "gimptoolinfo.h" +#include "pdb/gimpprocedure.h" +#include "pdb/pdb-types.h" + +#include "display/display-types.h" +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "gimpinteraction-logger.h" + + +/* + * Defines + */ +#define GUI_LOG_VERSION_NUM "2008.4.16" +#define GIMP_GUI_LOG_FILE "GIMP_GUI_LOG_FILE" +#define GIMP_GUI_LOG_FILE_NAME "GIMP_GUI_LOG_FILE_NAME" +#define GIMP_GUI_LOG_USER_ID "GIMP_GUI_LOG_USER_ID" +#define GIMP_GUI_LOG_TIMEZONE "GIMP_GUI_LOG_TIMEZONE" +#define GIMP_GUI_LOG_HEADER_FILE "GIMP_GUI_LOG_HEADER_FILE" +#define GIMP_GUI_LOG_RUN_NUMBER "GIMP_GUI_LOG_RUN_NUMBER" +#define GIMP_GUI_LOG_RUN_TAG "GIMP_GUI_LOG_RUN_TAG" +#define GIMP_GUI_LOG_WRAPPER_VERSION "GIMP_GUI_LOG_WRAPPER_VERSION" +#define GIMP_GUI_LOG_PLATFORM "GIMP_GUI_LOG_PLATFORM" +#define GIMP_GUI_LOG_PLATFORM_SYSTEM "GIMP_GUI_LOG_PLATFORM_SYSTEM" +#define GIMP_GUI_LOG_PLATFORM_RELEASE "GIMP_GUI_LOG_PLATFORM_RELEASE" +#define GIMP_GUI_LOG_PLATFORM_VERSION "GIMP_GUI_LOG_PLATFORM_VERSION" +#define GIMP_GUI_LOG_PLATFORM_MACHINE "GIMP_GUI_LOG_PLATFORM_MACHINE" +#define GIMP_GUI_LOG_PLATFORM_PROCESSOR "GIMP_GUI_LOG_PLATFORM_PROCESSOR" +#define GIMP_GUI_LOG_TEST_URL_PREFIX "GIMP_GUI_LOG_TEST_URL_PREFIX" +#define GIMP_GUI_LOG_COMPRESS_LOG "GIMP_GUI_LOG_COMPRESS_LOG" +#define GIMP_GUI_LOG_LOG_KEY_ACTIVITY "GIMP_GUI_LOG_LOG_KEY_ACTIVITY" +#define GIMP_GUI_LOG_LOG_BUTTON_ACTIVITY "GIMP_GUI_LOG_LOG_BUTTON_ACTIVITY" +#define GIMP_GUI_LOG_SESSION_TAGS "GIMP_GUI_LOG_SESSION_TAGS" +#define GIMP_GUI_LOG_HISTOGRAM_TIMEOUT "GIMP_GUI_LOG_HISTOGRAM_TIMEOUT" +#define GIMP_GUI_LOG_NUM_DISABLED_RUNS "GIMP_GUI_LOG_NUM_DISABLED_RUNS" +#define GIMP_GUI_LOG_CONSENT_VIEW_ORDER "GIMP_GUI_LOG_CONSENT_VIEW_ORDER" +#define GIMP_GUI_LOG_CONSENT_DWELL_TIMES "GIMP_GUI_LOG_CONSENT_DWELL_TIMES" +#define GIMP_GUI_LOG_CONSENT_MAX_SCROLL_VALUES "GIMP_GUI_LOG_CONSENT_MAX_SCROLL_VALUES" +#define GIMP_GUI_LOG_CONSENT_USER_VIEW_ORDER "GIMP_GUI_LOG_CONSENT_USER_VIEW_ORDER" +#define GIMP_GUI_LOG_ONE_TIME_PAD_FILE "GIMP_GUI_LOG_ONE_TIME_PAD_FILE" +#define MAX_LOG_HEADER_LENGTH 20000 +#define MAX_TAG_LENGTH 1024 + +/* + * Global data + */ +static gboolean log_events = FALSE; +static gboolean compress_log = FALSE; +static gboolean log_key_events = TRUE; +static gboolean log_button_events = TRUE; +static GIOChannel* log_file = NULL; +static gzFile compressed_log_file = NULL; + +static guint entry_num = 1; +static GTimeVal log_session_start_time; +static GTimeVal last_undo_time; +static GTimeVal log_item_start_time; +static int histogram_timeout = -1; +static gboolean button_down = FALSE; +static gboolean key_down = FALSE; + +static GSList* dead_images = NULL; +static GSList* registered_windows = NULL; +static GSList* dirty_images = NULL; +static GHashTable* last_size_map = NULL; + +static GHashTable* one_time_pad_map = NULL; +static GSList* one_time_pad_keys = NULL; /* Convenience to grab keys for writing since old lib versions don't have easy access to keys */ +static guint last_one_time_val = 1; +static gchar* one_time_pad_filename = NULL; + +static gchar* test_url_prefix = NULL; + +static GimpProcedure* last_plugin_proc = NULL; + + +#define timestamp_size 20 +static char timestamp[timestamp_size+1]; /* YYYY_MM_DD_HH_MM_SS */ + +/* + * Private functions + */ +GString* guilog_start_log_message(void); +void guilog_log_message(GString* str, gboolean include_elapsed_log_time); +gboolean doc_window_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data); +gboolean doc_window_key_event(GtkWidget *widget, GdkEventKey *event, gpointer data); +gboolean doc_window_focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data); +gboolean doc_window_state_change_event(GtkWidget *widget, GdkEventWindowState *event, gpointer data); +void doc_window_size_requisition_event(GtkWidget *widget, GtkRequisition *requisition, gpointer data); +void doc_window_size_allocate_event(GtkWidget *widget, GtkAllocation *allocation, gpointer data); +void doc_window_showhide_event(GtkWidget *widget, gpointer data); +gboolean generic_window_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data); +gboolean generic_window_key_event(GtkWidget *widget, GdkEventKey *event, gpointer data); +gboolean generic_window_focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data); +gboolean generic_window_state_change_event(GtkWidget *widget, GdkEventWindowState *event, gpointer data); +void generic_window_size_requisition_event(GtkWidget *widget, GtkRequisition *requisition, gpointer data); +void generic_window_size_allocate_event(GtkWidget *widget, GtkAllocation *allocation, gpointer data); +void generic_window_showhide_event(GtkWidget *widget, gpointer data); +gboolean generic_window_destroy_event(GtkWidget *widget, GdkEvent* event, gpointer data); +void log_window_event(const gchar *event_name, GimpDisplayShell *shell); +void action_event(GtkAction *action, gpointer data); + +void context_tool_changed(GimpContext *context, GimpToolInfo *tool_info, gpointer data); +void context_image_changed(GimpContext *context, GimpImage *gimage, gpointer data); +void context_foreground_changed(GimpContext *context, const GimpRGB *fg, gpointer data); +void context_background_changed(GimpContext *context, const GimpRGB *bg, gpointer data); +void context_brush_opacity_changed(GimpContext *context, gdouble opacity, gpointer data); +void context_paint_mode_changed(GimpContext *context, GimpLayerModeEffects paint_mode, gpointer data); +void context_brush_changed(GimpContext *context, gpointer data); +void context_pattern_changed(GimpContext *context, gpointer data); +void context_gradient_changed(GimpContext *context, gpointer data); +void context_palette_changed(GimpContext *context, gpointer data); +void context_font_changed(GimpContext *context, gpointer data); +gboolean gimp_exit_callback(Gimp *gimp, gpointer data); + +void guilog_image_to_string(GString* str, GimpImage* gimage, gboolean write_histogram); +void guilog_write_composite_image_histogram(GString* str, GimpImage* gimage); +void guilog_layers_to_string(GString* str, GimpImage* gimage, gboolean write_histogram); +void guilog_layer_to_string(GString* str, GimpImage* gimage, GimpLayer* layer, gint layer_num, gboolean write_histogram); +void guilog_drawable_to_string(GString* str, GimpImage* gimage, GimpDrawable* drawable, gboolean write_histogram); +void guilog_write_drawable_histogram(GString* str, GimpImage* gimage, GimpDrawable* drawable); +void guilog_log_histogram_channel(GString* str, GimpHistogram* hist, gint channel_num); +void guilog_item_to_string(GString* str, GimpItem* item); +void guilog_generate_string_stats(GString* log_str, const gchar* attrib_prefix, const gchar* string_to_analyze); +gsize write_data_to_log(const voidp buf, unsigned len); +GimpLayer* guilog_flatten_image(GimpImage* gimage); +int isforwardslash(int c); +int isbackwardslash(int c); + +void init_one_time_pad(void); +guint anonymize_hash_key(guint key); + +double guilog_get_elapsed_time(GTimeVal* start_time); +char* guilog_get_timestamp(void); +GString* guilog_xmlify_string(const gchar* text); + +gboolean histogram_callback(gpointer data); + +int guilog_get_char_count(const gchar* text, int(*charFun)(int)); + +/* Public functions */ + +int guilog_start(Gimp* gimp) +{ + GError* error = NULL; + char* file_name = NULL; + char* header_file_name = NULL; + char* user_id_string = NULL; + char* timezone_string = NULL; + char* log_file_name_string = NULL; + char* wrapper_version_string = NULL; + char* platform_string = NULL; + char* platform_system_string = NULL; + char* platform_release_string = NULL; + char* platform_version_string = NULL; + char* platform_machine_string = NULL; + char* platform_processor_string = NULL; + char* compress_log_string = NULL; + char* log_key_activity_string = NULL; + char* log_button_activity_string = NULL; + char* session_tags_string = NULL; + char* histogram_timeout_string = NULL; + char* num_disabled_runs_string = NULL; + int num_disabled_runs = 0; + char* consent_view_order_string = NULL; + int consent_view_order = 0; + char* consent_dwell_times_string = NULL; + char* consent_max_scroll_values_string = NULL; + char* consent_user_view_order_string = NULL; + GString* log_start = NULL; + GdkDisplay* display = NULL; + GimpContext* context = NULL; + + if (log_events) { + /* Already logging... */ + return 0; + } + + /* Mark the start of this logging session for precise timing */ + g_get_current_time(&log_session_start_time); + + /* Get the environmental variables */ + user_id_string = (char*)g_getenv(GIMP_GUI_LOG_USER_ID); + if (!user_id_string) { + user_id_string = "-1"; + } + + timezone_string = (char*)g_getenv(GIMP_GUI_LOG_TIMEZONE); + if (!timezone_string) { + timezone_string = ""; + } + + log_file_name_string = (char*)g_getenv(GIMP_GUI_LOG_FILE_NAME); + if (!log_file_name_string) { + log_file_name_string = ""; + } + + platform_string = (char*)g_getenv(GIMP_GUI_LOG_PLATFORM); + if (!platform_string) { + platform_string = "Unknown"; + } + + wrapper_version_string = (char*)g_getenv(GIMP_GUI_LOG_WRAPPER_VERSION); + if (!wrapper_version_string) { + wrapper_version_string = "Unknown"; + } + + platform_system_string = (char*)g_getenv(GIMP_GUI_LOG_PLATFORM_SYSTEM); + if (!platform_system_string) { + platform_system_string = "Unknown"; + } + + platform_release_string = (char*)g_getenv(GIMP_GUI_LOG_PLATFORM_RELEASE); + if (!platform_release_string) { + platform_release_string = "Unknown"; + } + + platform_version_string = (char*)g_getenv(GIMP_GUI_LOG_PLATFORM_VERSION); + if (!platform_version_string) { + platform_version_string = "Unknown"; + } + + platform_machine_string = (char*)g_getenv(GIMP_GUI_LOG_PLATFORM_MACHINE); + if (!platform_machine_string) { + platform_machine_string = "Unknown"; + } + + platform_processor_string = (char*)g_getenv(GIMP_GUI_LOG_PLATFORM_PROCESSOR); + if (!platform_processor_string) { + platform_processor_string = "Unknown"; + } + + session_tags_string = (char*)g_getenv(GIMP_GUI_LOG_SESSION_TAGS); + if (!session_tags_string) { + session_tags_string = ""; + } + + histogram_timeout_string = (char*)g_getenv(GIMP_GUI_LOG_HISTOGRAM_TIMEOUT); + if (histogram_timeout_string) { + histogram_timeout = atoi(histogram_timeout_string); + } + + num_disabled_runs_string = (char*)g_getenv(GIMP_GUI_LOG_NUM_DISABLED_RUNS); + if (num_disabled_runs_string) { + num_disabled_runs = atoi(num_disabled_runs_string); + } + + consent_view_order_string = (char*)g_getenv(GIMP_GUI_LOG_CONSENT_VIEW_ORDER); + if (consent_view_order_string) { + consent_view_order = atoi(consent_view_order_string); + } + + consent_dwell_times_string = (char*)g_getenv(GIMP_GUI_LOG_CONSENT_DWELL_TIMES); + consent_max_scroll_values_string = (char*)g_getenv(GIMP_GUI_LOG_CONSENT_MAX_SCROLL_VALUES); + consent_user_view_order_string = (char*)g_getenv(GIMP_GUI_LOG_CONSENT_USER_VIEW_ORDER); + + test_url_prefix = (char*)g_getenv(GIMP_GUI_LOG_TEST_URL_PREFIX); + + compress_log_string = (char*)g_getenv(GIMP_GUI_LOG_COMPRESS_LOG); + if (compress_log_string && strlen(compress_log_string)>0 && compress_log_string[0] == '1') { + compress_log = TRUE; + } else { + compress_log = FALSE; + } + + log_key_activity_string = (char*)g_getenv(GIMP_GUI_LOG_LOG_KEY_ACTIVITY); + if (log_key_activity_string && strlen(log_key_activity_string)>0 && log_key_activity_string[0] == '1') { + log_key_events = TRUE; + } else { + log_key_events = FALSE; + } + + log_button_activity_string = (char*)g_getenv(GIMP_GUI_LOG_LOG_BUTTON_ACTIVITY); + if (log_button_activity_string && strlen(log_button_activity_string)>0 && log_button_activity_string[0] == '1') { + log_button_events = TRUE; + } else { + log_button_events = FALSE; + } + + /* init our one-time pad */ + init_one_time_pad(); + + /* Open the log file */ + file_name = (char*)g_getenv(GIMP_GUI_LOG_FILE); + if (file_name) { + if (compress_log) { + compressed_log_file = gzopen(file_name, "wb9"); + } else { + log_file = g_io_channel_new_file(file_name, "w", &error); + } + } + + + if (!log_file && !compressed_log_file) { + /* TODO: Inform wrapper of error */ + return -1; + } + + last_size_map = g_hash_table_new(NULL, NULL); + + log_events = TRUE; + + log_start = g_string_new("\n"); + g_string_append_printf(log_start, + "\n\n", + GUI_LOG_VERSION_NUM, + wrapper_version_string, + user_id_string, + guilog_get_timestamp(), + timezone_string, + log_file_name_string, + GIMP_VERSION, + platform_string, + platform_system_string, + platform_release_string, + platform_version_string, + platform_machine_string, + platform_processor_string, + num_disabled_runs); + + /* Add consent stats, if available */ + if (consent_dwell_times_string && strlen(consent_dwell_times_string) && + consent_max_scroll_values_string && strlen(consent_max_scroll_values_string) && + consent_user_view_order_string && strlen(consent_user_view_order_string)) { + g_string_append_printf(log_start, "\n\n", consent_view_order, consent_dwell_times_string, consent_max_scroll_values_string, consent_user_view_order_string); + } + + /* Add session tags */ + g_string_append(log_start, "\n"); + if (strlen(session_tags_string) > 0) { + GString* xml_safe_string = NULL; + GString* tags_string = g_string_new(session_tags_string); + if ((*tags_string).len > MAX_TAG_LENGTH) { + g_string_truncate(tags_string, MAX_TAG_LENGTH); + } + xml_safe_string = guilog_xmlify_string((*tags_string).str); + g_string_append(log_start, (*xml_safe_string).str); + g_string_free(xml_safe_string, TRUE); + g_string_free(tags_string, TRUE); + } + g_string_append(log_start, "\n\n\n"); + + g_string_append(log_start, "\n"); + /* Check for log header */ + header_file_name = (char*)g_getenv(GIMP_GUI_LOG_HEADER_FILE); + if (header_file_name) { + GIOChannel* header_file = NULL; + header_file = g_io_channel_new_file(header_file_name, "r", &error); + if (header_file) { + gchar* contents = NULL; + gsize length = -1; + g_io_channel_read_to_end(header_file, &contents, &length, &error); + if (length > 0) { + GString* content_string = g_string_new(contents); + GString* xml_safe_string = NULL; + if ((*content_string).len > MAX_LOG_HEADER_LENGTH) { + g_string_truncate(content_string, MAX_LOG_HEADER_LENGTH); + } + xml_safe_string = guilog_xmlify_string((*content_string).str); + g_string_append(log_start, (*xml_safe_string).str); + g_string_free(xml_safe_string, TRUE); + g_string_free(content_string, TRUE); + } + g_io_channel_shutdown(header_file, TRUE, &error); + g_free(contents); + } + } + g_string_append(log_start, "\n\n\n"); + + /* Check for screens */ + g_string_append(log_start, "\n"); + display = gdk_display_get_default(); + if (display) { + GdkScreen* screen = NULL; + GdkRectangle monitor_geometry; + gint screen_num = -1; + gint monitor_num = -1; + gint num_screens = -1; + gint num_monitors = -1; + + num_screens = gdk_display_get_n_screens(display); + for (screen_num = 0; screen_num < num_screens; screen_num++) { + screen = gdk_display_get_screen(display, screen_num); + g_string_append_printf(log_start, "\n", gdk_screen_get_number(screen), gdk_screen_get_width(screen), gdk_screen_get_height(screen), gdk_screen_get_width_mm(screen), gdk_screen_get_height_mm(screen)); + } + screen = gdk_screen_get_default(); + if (screen) { + num_monitors = gdk_screen_get_n_monitors(screen); + for (monitor_num = 0; monitor_num < num_monitors; monitor_num++) { + gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor_geometry); + g_string_append_printf(log_start, "\n", monitor_num, monitor_geometry.x, monitor_geometry.y, monitor_geometry.width, monitor_geometry.height); + } + } + } + g_string_append(log_start, "\n\n"); + + /* Write log start out to file */ + write_data_to_log((*log_start).str, (*log_start).len); + g_string_free(log_start, TRUE); + + + /* Patch into the context and install signal handlers for quitting the GIMP */ + if (gimp) { + context = gimp_get_user_context(gimp); + if (context) { + g_signal_connect(context, "tool_changed", G_CALLBACK (context_tool_changed), (gpointer)0); + /* Doesn't seem to do anything, so let's ditch it for now + g_signal_connect(context, "image_changed", G_CALLBACK (context_image_changed), (gpointer)0); + */ + g_signal_connect(context, "foreground_changed", G_CALLBACK (context_foreground_changed), (gpointer)0); + g_signal_connect(context, "background_changed", G_CALLBACK (context_background_changed), (gpointer)0); + g_signal_connect(context, "opacity_changed", G_CALLBACK (context_brush_opacity_changed), (gpointer)0); + g_signal_connect(context, "paint_mode_changed", G_CALLBACK (context_paint_mode_changed), (gpointer)0); + g_signal_connect(context, "brush_changed", G_CALLBACK (context_brush_changed), (gpointer)0); + g_signal_connect(context, "pattern_changed", G_CALLBACK (context_pattern_changed), (gpointer)0); + g_signal_connect(context, "gradient_changed", G_CALLBACK (context_gradient_changed), (gpointer)0); + g_signal_connect(context, "palette_changed", G_CALLBACK (context_palette_changed), (gpointer)0); + g_signal_connect(context, "font_changed", G_CALLBACK (context_font_changed), (gpointer)0); + } + g_signal_connect_after(gimp, "exit", G_CALLBACK (gimp_exit_callback), NULL); + } + + /* Install histogram callback function that generates histograms in idle time */ + g_timeout_add_full(G_PRIORITY_LOW, 1000, histogram_callback, NULL, NULL); + + return 0; +} + +int guilog_stop(void) +{ + GError* error = NULL; + char* log_end = "\n"; + if (log_file || compressed_log_file) { + write_data_to_log(log_end, strlen(log_end)); + } + if (log_file) { + g_io_channel_shutdown(log_file, TRUE, &error); + } else if (compressed_log_file) { + gzclose(compressed_log_file); + } + log_file = NULL; + compressed_log_file = NULL; + log_events = FALSE; + if (dead_images) { + g_slist_free(dead_images); + dead_images = NULL; + } + if (dirty_images) { + g_slist_free(dirty_images); + dirty_images = NULL; + } + if (registered_windows) { + g_slist_free(registered_windows); + registered_windows = NULL; + } + if (last_size_map) { + g_hash_table_destroy(last_size_map); + last_size_map = NULL; + } + if (one_time_pad_map) { + if (one_time_pad_filename) { + // write pad to file + gzFile f = NULL; + f = gzopen(one_time_pad_filename, "wb9"); + if (f) { + GSList* cur_key = NULL; + + cur_key = one_time_pad_keys; + for (; cur_key; cur_key = g_slist_next(cur_key)) { + guint key = 0; + guint val = 0; + gsize bytes_written; + + key = (guint)cur_key->data; + val = (guint)g_hash_table_lookup(one_time_pad_map, (gpointer)key); + bytes_written = gzwrite(f, (const voidp)&key, sizeof(key)); + bytes_written = gzwrite(f, (const voidp)&val, sizeof(key)); + } + gzclose(f); + } + } + g_slist_free(one_time_pad_keys); + one_time_pad_keys = NULL; + g_hash_table_destroy(one_time_pad_map); + one_time_pad_map = NULL; + } + return 0; +} + +void guilog_register_display_shell ( void *in_shell) +{ + if (log_events) { + GimpDisplayShell *shell = (GimpDisplayShell*)in_shell; + + g_signal_connect(shell, "button-press-event", G_CALLBACK(doc_window_button_event), NULL); + g_signal_connect(shell, "button-release-event", G_CALLBACK(doc_window_button_event), NULL); + + g_signal_connect(shell, "key-press-event", G_CALLBACK(doc_window_key_event), NULL); + g_signal_connect(shell, "key-release-event", G_CALLBACK(doc_window_key_event), NULL); + + g_signal_connect(shell, "focus-in-event", G_CALLBACK(doc_window_focus_event), NULL); + g_signal_connect(shell, "focus-out-event", G_CALLBACK(doc_window_focus_event), NULL); + g_signal_connect(shell, "window-state-event", G_CALLBACK(doc_window_state_change_event), NULL); +/* g_signal_connect(shell, "size-request", G_CALLBACK(doc_window_size_requisition_event), NULL); */ + g_signal_connect(shell, "size-allocate", G_CALLBACK(doc_window_size_allocate_event), NULL); + g_signal_connect(shell, "show", G_CALLBACK (doc_window_showhide_event), (gpointer)1); + g_signal_connect(shell, "hide", G_CALLBACK (doc_window_showhide_event), (gpointer)0); + } +} + +void guilog_register_action ( void *vp_action) +{ + GtkAction* action = vp_action; + /* Don't check for log_events here because we may not be init'ed yet */ + if (action) { + g_signal_connect(action, "activate", G_CALLBACK (action_event), NULL); + } +} + +void guilog_register_window ( void *vp_window) +{ + GtkWidget* widget = vp_window; + /* Don't check for log_events here because we may not be init'ed yet */ + if (widget) { + GtkWindow* window = NULL; + + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return; + } + if (g_slist_find(registered_windows, window)) { + return; + } + registered_windows = g_slist_append(registered_windows, window); + g_signal_connect(window, "button-press-event", G_CALLBACK(generic_window_button_event), NULL); + g_signal_connect(window, "button-release-event", G_CALLBACK(generic_window_button_event), NULL); + g_signal_connect(window, "key-release-event", G_CALLBACK(generic_window_key_event), NULL); + g_signal_connect(window, "focus-in-event", G_CALLBACK(generic_window_focus_event), NULL); + g_signal_connect(window, "focus-out-event", G_CALLBACK(generic_window_focus_event), NULL); + g_signal_connect(window, "window-state-event", G_CALLBACK(generic_window_state_change_event), NULL); + g_signal_connect(window, "size-allocate", G_CALLBACK(generic_window_size_allocate_event), NULL); + g_signal_connect(window, "show", G_CALLBACK (generic_window_showhide_event), (gpointer)1); + g_signal_connect(window, "hide", G_CALLBACK (generic_window_showhide_event), (gpointer)0); + g_signal_connect(window, "destroy", G_CALLBACK (generic_window_destroy_event), NULL); + } +} + +void guilog_template_create_image ( GimpImage *gimage) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, gimage, FALSE); /* New image histogram captured in an undo thaw event */ + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void guilog_create_image ( GimpImage *gimage, + const gchar *comment) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, gimage, FALSE); /* New image histogram captured in an undo thaw event */ + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, FALSE); + } +} + +void guilog_revert_image ( GimpImage *old_gimage, + GimpImage *new_gimage) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, old_gimage, FALSE); + g_string_append(log_str, "\n"); + + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, new_gimage, TRUE); + g_string_append(log_str, "\n"); + + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, TRUE); + } +} + +void guilog_open_image(const gchar *uri, + const gchar *entered_filename, + GimpImage *gimage) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + gboolean write_uri = FALSE; + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, gimage, TRUE); + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, FALSE); + } +} + +void guilog_last_opened ( GimpImage *gimage, + gint index, + const gchar *filename) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append_printf(log_str, "\n"); + guilog_image_to_string(log_str, gimage, FALSE); /* Histogram captured in GimpImageOpen event */ + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, FALSE); + } +} + +void guilog_save_image_requested ( GimpImage *gimage, + gboolean is_save_as) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append_printf(log_str, "\n", is_save_as); + guilog_image_to_string(log_str, gimage, FALSE); + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, FALSE); + } +} + +void guilog_save_image ( GimpImage *gimage, + const gchar *uri, + const gchar *filename, + gboolean save_a_copy, + GimpPDBStatusType status) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + + g_string_append_printf(log_str, "\n"); + guilog_image_to_string(log_str, gimage, TRUE); + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, TRUE); + } +} + +void guilog_image_duplicate ( GimpImage *old_gimage, + GimpImage *new_gimage) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + + /* + * Log the histogram for the new image rather than the old image + */ + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, old_gimage, FALSE); + g_string_append(log_str, "\n"); + + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, new_gimage, FALSE); /* Histogram captured by an undo thaw event */ + g_string_append(log_str, "\n"); + + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, FALSE); + } +} + +void guilog_image_dispose(GimpImage *gimage) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, gimage, TRUE); + g_string_append(log_str, "\n"); + + guilog_log_message(log_str, TRUE); + } + dead_images = g_slist_append(dead_images, (gpointer)(*gimage).ID); + dirty_images = g_slist_remove(dirty_images, (gpointer)gimage); +} + +void guilog_undo_event ( GimpImage *gimage, + GimpUndoEvent event, + GimpUndo *undo) +{ + if (log_events) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + + xml_name = guilog_xmlify_string(gimpObject.name); + g_string_append_printf(log_str, "%s\n", (*xml_name).str); + g_string_free(xml_name, TRUE); + } else { + g_string_append(log_str, ">\n"); + } + guilog_image_to_string(log_str, gimage, event == GIMP_UNDO_EVENT_UNDO_THAW); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + + /* + * Only record histograms for undo pushed events (event 0). + */ + if (event == GIMP_UNDO_EVENT_UNDO_PUSHED && undo) { + switch (undo->undo_type) { + case GIMP_UNDO_GROUP_IMAGE_CROP: + case GIMP_UNDO_GROUP_IMAGE_QUICK_MASK: + case GIMP_UNDO_GROUP_DRAWABLE: + case GIMP_UNDO_GROUP_DRAWABLE_MOD: + case GIMP_UNDO_GROUP_ITEM_VISIBILITY: + case GIMP_UNDO_GROUP_LAYER_ADD_MASK: + case GIMP_UNDO_GROUP_FS_TO_LAYER: + case GIMP_UNDO_GROUP_FS_ANCHOR: + case GIMP_UNDO_GROUP_FS_REMOVE: + case GIMP_UNDO_GROUP_EDIT_PASTE: + case GIMP_UNDO_GROUP_EDIT_CUT: + case GIMP_UNDO_GROUP_TEXT: + case GIMP_UNDO_GROUP_TRANSFORM: + case GIMP_UNDO_GROUP_PAINT: + case GIMP_UNDO_GROUP_MISC: + case GIMP_UNDO_DRAWABLE: + case GIMP_UNDO_DRAWABLE_MOD: + case GIMP_UNDO_LAYER_REMOVE: + case GIMP_UNDO_LAYER_MASK_ADD: + case GIMP_UNDO_LAYER_MASK_REMOVE: + case GIMP_UNDO_LAYER_REPOSITION: + case GIMP_UNDO_LAYER_MODE: + case GIMP_UNDO_LAYER_OPACITY: + case GIMP_UNDO_LAYER_LOCK_ALPHA: + case GIMP_UNDO_TEXT_LAYER: + case GIMP_UNDO_TEXT_LAYER_MODIFIED: + case GIMP_UNDO_CHANNEL_REMOVE: + case GIMP_UNDO_CHANNEL_REPOSITION: + case GIMP_UNDO_CHANNEL_COLOR: + case GIMP_UNDO_VECTORS_REMOVE: + case GIMP_UNDO_VECTORS_MOD: + case GIMP_UNDO_FS_TO_LAYER: + case GIMP_UNDO_FS_RIGOR: + case GIMP_UNDO_FS_RELAX: + case GIMP_UNDO_TRANSFORM: + case GIMP_UNDO_PAINT: + case GIMP_UNDO_INK: + g_get_current_time(&last_undo_time); + if (!g_slist_find(dirty_images, gimage)) { + dirty_images = g_slist_append(dirty_images, (gpointer)gimage); + } + break; + default: + ; + } + } + } +} +void guilog_plugin_invoked(GimpProcedure *proc, + gboolean reshow_last) +{ + if (log_events && proc) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "original_name, proc->proc_type, reshow_last); + g_string_append(log_str, "/>\n"); + last_plugin_proc = proc; + guilog_log_message(log_str, FALSE); + } +} + +/* + * Two different ways of handling plug-ins returning. Scripts return after completing + * and can thus be recorded. Regular plug-ins return immediately and are caught when + * the plug-in closes + */ +void guilog_plugin_returned(GimpProcedure *proc) +{ + if (log_events && proc) { + if (proc->proc_type == GIMP_TEMPORARY && proc == last_plugin_proc) { + GString* log_str = guilog_start_log_message(); + g_string_append_printf(log_str, "\n", proc->original_name, proc->proc_type); + guilog_log_message(log_str, FALSE); + last_plugin_proc = NULL; + } + } +} + +void guilog_plugin_closed(GimpProcedure *proc) +{ + if (log_events && proc) { + if (proc == last_plugin_proc) { + GString* log_str = guilog_start_log_message(); + g_string_append_printf(log_str, "\n", proc->original_name, proc->proc_type); + guilog_log_message(log_str, FALSE); + last_plugin_proc = NULL; + } + } +} + +/* Private functions */ + +gboolean doc_window_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + if (event) { + button_down = (event->state == 0); + } + if (log_events && log_button_events) { + GString *log_str = guilog_start_log_message(); + GimpDisplayShell *shell = (GimpDisplayShell*)widget; + GimpDisplay *gdisp = NULL; + GimpImage *gimage = NULL; + int image_id = -1; + int event_type = -1; + int button_state = -1; + int button = -1; + + if (shell) { + gdisp = shell->display; + } + if (gdisp) { + gimage = gdisp->image; + } + if (gimage) { + image_id = gimage->ID; + } + if (event) { + event_type = event->type; + button_state = event->state; + button = event->button; + } + g_string_append_printf(log_str, "\n", image_id, event_type, button, button_state); + guilog_log_message(log_str, FALSE); + } + return FALSE; +} + +gboolean doc_window_key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + gboolean log_this_event = FALSE; + + if (!key_down) { + log_this_event = TRUE; + } else { + log_this_event = (event && event->state == 0); + } + + if (event) { + key_down = (event->state != 0); + } + + if (log_events && log_key_events && log_this_event) { + GString *log_str = guilog_start_log_message(); + GimpDisplayShell *shell = (GimpDisplayShell*)widget; + GimpDisplay *gdisp = NULL; + GimpImage *gimage = NULL; + int image_id = -1; + int event_type = -1; + int modifier_state = -1; + + if (shell) { + gdisp = shell->display; + } + if (gdisp) { + gimage = gdisp->image; + } + if (gimage) { + image_id = gimage->ID; + } + if (event) { + event_type = event->type; + modifier_state = event->state; + } + g_string_append_printf(log_str, "\n", image_id, event_type, modifier_state); + guilog_log_message(log_str, FALSE); + } + return FALSE; +} + +void doc_window_size_allocate_event(GtkWidget *widget, GtkAllocation *allocation, gpointer data) +{ + if (log_events) { + GString *log_str = NULL; + GimpDisplayShell *shell = (GimpDisplayShell*)widget; + GimpDisplay *gdisp = NULL; + GimpImage *gimage = NULL; + GdkScreen *screen = NULL; + int image_id = -1; + int width = -1; + int height = -1; + int x = -1; + int y = -1; + int screen_num = -1; + int last_value = -1; + + if (!last_size_map) { + return; + } + if (shell) { + gdisp = shell->display; + gtk_window_get_position((GtkWindow*)widget, &x, &y); + screen = gtk_window_get_screen((GtkWindow*)widget); + if (screen) { + screen_num = gdk_screen_get_number(screen); + } + } + if (gdisp) { + gimage = gdisp->image; + } + if (gimage) { + image_id = gimage->ID; + } + if (allocation) { + width = allocation->width; + height = allocation->height; + } + last_value = (int)g_hash_table_lookup(last_size_map, (gconstpointer)widget); + if (last_value != (width*height)) { + g_hash_table_insert(last_size_map, (gpointer)widget, (gpointer)(width*height)); + log_str = guilog_start_log_message(); + g_string_append_printf(log_str, "\n", image_id, width, height, x, y, screen_num); + guilog_log_message(log_str, FALSE); + } + } +} + +void doc_window_size_requisition_event(GtkWidget *widget, GtkRequisition *requisition, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GimpDisplayShell *shell = (GimpDisplayShell*)widget; + GimpDisplay *gdisp = NULL; + GimpImage *gimage = NULL; + int image_id = -1; + int width = -1; + int height = -1; + + if (shell) { + gdisp = shell->display; + } + if (gdisp) { + gimage = gdisp->image; + } + if (gimage) { + image_id = gimage->ID; + } + if (requisition) { + width = requisition->width; + height = requisition->height; + } + g_string_append_printf(log_str, "\n", image_id, width, height); + guilog_log_message(log_str, FALSE); + } +} + +gboolean doc_window_state_change_event(GtkWidget *widget, GdkEventWindowState *event, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GimpDisplayShell *shell = (GimpDisplayShell*)widget; + GimpDisplay *gdisp = NULL; + GimpImage *gimage = NULL; + int image_id = -1; + int changed_mask = -1; + int new_window_state = -1; + + if (shell) { + gdisp = shell->display; + } + if (gdisp) { + gimage = gdisp->image; + } + if (gimage) { + image_id = gimage->ID; + } + if (event) { + changed_mask = event->changed_mask; + new_window_state = event->new_window_state; + } + g_string_append_printf(log_str, "\n", image_id, changed_mask, new_window_state); + guilog_log_message(log_str, FALSE); + } + return FALSE; +} + +gboolean doc_window_focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GimpDisplayShell *shell = (GimpDisplayShell*)widget; + GimpDisplay *gdisp = NULL; + GimpImage *gimage = NULL; + GdkScreen *screen = NULL; + int image_id = -1; + int event_type = -1; + int focus_in = -1; + int x = -1; + int y = -1; + int screen_num = -1; + + if (shell) { + gdisp = shell->display; + gtk_window_get_position((GtkWindow*)widget, &x, &y); + screen = gtk_window_get_screen((GtkWindow*)widget); + if (screen) { + screen_num = gdk_screen_get_number(screen); + } + } + if (gdisp) { + gimage = gdisp->image; + } + if (gimage) { + image_id = gimage->ID; + } + if (event) { + event_type = event->type; + focus_in = event->in; + } + g_string_append_printf(log_str, "\n", image_id, event_type, focus_in, x, y, screen_num); + guilog_log_message(log_str, FALSE); + } + return FALSE; +} + +void doc_window_showhide_event(GtkWidget *widget, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GimpDisplayShell *shell = (GimpDisplayShell*)widget; + GimpDisplay *gdisp = NULL; + GimpImage *gimage = NULL; + int image_id = -1; + int show = (int)data; + + if (shell) { + gdisp = shell->display; + } + if (gdisp) { + gimage = gdisp->image; + } + if (gimage) { + image_id = gimage->ID; + } + + g_string_append_printf(log_str, "\n", image_id, show); + guilog_log_message(log_str, FALSE); + } +} + +void action_event(GtkAction *action, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + char* name = NULL; + GString* xml_safe_string = NULL; + + if (action) { + name = (char*)gtk_action_get_name(action); + } + if (name == NULL) { + name = ""; + } + xml_safe_string = guilog_xmlify_string(name); + g_string_append_printf(log_str, "\n", (*xml_safe_string).str); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_string, TRUE); + } +} + +gboolean generic_window_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) +{ + if (log_events && log_button_events) { + GString *log_str = guilog_start_log_message(); + GtkWindow *window = NULL; + int event_type = -1; + int button_state = -1; + int button = -1; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return FALSE; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return FALSE; + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + if (event) { + event_type = event->type; + button_state = event->state; + button = event->button; + } + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str, event_type, button, button_state); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + } + return FALSE; +} + +gboolean generic_window_key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + if (log_events && log_key_events) { + GString *log_str = guilog_start_log_message(); + GtkWindow *window = NULL; + int event_type = -1; + int modifier_state = -1; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return FALSE; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return FALSE; + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + if (event) { + event_type = event->type; + modifier_state = event->state; + } + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str, event_type, modifier_state); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + } + return FALSE; +} + +void generic_window_size_allocate_event(GtkWidget *widget, GtkAllocation *allocation, gpointer data) +{ + if (log_events) { + GString *log_str = NULL; + GtkWindow *window = NULL; + GdkScreen *screen = NULL; + int width = -1; + int height = -1; + int x = -1; + int y = -1; + int screen_num = -1; + int last_value = -1; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return; + } + screen = gtk_window_get_screen(window); + if (screen) { + screen_num = gdk_screen_get_number(screen); + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + if (!last_size_map) { + return; + } + gtk_window_get_position(window, &x, &y); + if (allocation) { + width = allocation->width; + height = allocation->height; + } + last_value = (int)g_hash_table_lookup(last_size_map, (gconstpointer)window); + if (last_value != (width*height)) { + g_hash_table_insert(last_size_map, (gpointer)window, (gpointer)(width*height)); + log_str = guilog_start_log_message(); + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str, width, height, x, y, screen_num); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + } + } +} + +void generic_window_size_requisition_event(GtkWidget *widget, GtkRequisition *requisition, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GtkWindow *window = NULL; + GdkScreen *screen = NULL; + int width = -1; + int height = -1; + int screen_num = -1; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return; + } + screen = gtk_window_get_screen(window); + if (screen) { + screen_num = gdk_screen_get_number(screen); + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + if (requisition) { + width = requisition->width; + height = requisition->height; + } + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str, width, height, screen_num); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + } +} + +gboolean generic_window_state_change_event(GtkWidget *widget, GdkEventWindowState *event, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GtkWindow *window = NULL; + int changed_mask = -1; + int new_window_state = -1; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return FALSE; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return FALSE; + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + if (event) { + changed_mask = event->changed_mask; + new_window_state = event->new_window_state; + } + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str, changed_mask, new_window_state); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + } + return FALSE; +} + +gboolean generic_window_focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GtkWindow *window = NULL; + GdkScreen *screen = NULL; + int event_type = -1; + int focus_in = -1; + int x = -1; + int y = -1; + int screen_num = -1; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return FALSE; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return FALSE; + } + screen = gtk_window_get_screen(window); + if (screen) { + screen_num = gdk_screen_get_number(screen); + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + gtk_window_get_position((GtkWindow*)widget, &x, &y); + + if (event) { + event_type = event->type; + focus_in = event->in; + } + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str, event_type, focus_in, x, y, screen_num); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + } + return FALSE; +} + +void generic_window_showhide_event(GtkWidget *widget, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GtkWindow *window = NULL; + int show = (int)data; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return; + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str, show); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + } +} +gboolean generic_window_destroy_event(GtkWidget *widget, GdkEvent* event, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + GtkWindow *window = NULL; + char* window_name = NULL; + char* window_role = NULL; + GString* xml_safe_name = NULL; + GString* xml_safe_role = NULL; + + if (!widget) { + return FALSE; + } + window = (GtkWindow*)gtk_widget_get_toplevel(widget); + if (!GTK_WIDGET_TOPLEVEL(window)) { + return FALSE; + } + /* Just to be absolutely sure... */ + if (!GIMP_IS_DISPLAY_SHELL(widget)) { + window_name = (char*)gtk_window_get_title(window); + window_role = (char*)gtk_window_get_role(window); + } + if (!window_name) { + window_name = ""; + } + if (!window_role) { + window_role = ""; + } + xml_safe_name = guilog_xmlify_string(window_name); + xml_safe_role = guilog_xmlify_string(window_role); + + g_string_append_printf(log_str, "\n", xml_safe_name->str, xml_safe_role->str); + guilog_log_message(log_str, FALSE); + g_string_free(xml_safe_name, TRUE); + g_string_free(xml_safe_role, TRUE); + + g_signal_handlers_disconnect_by_func(window, G_CALLBACK(generic_window_button_event), NULL); + g_signal_handlers_disconnect_by_func(window, G_CALLBACK(generic_window_key_event), NULL); + g_signal_handlers_disconnect_by_func(window, G_CALLBACK(generic_window_focus_event), NULL); + g_signal_handlers_disconnect_by_func(window, G_CALLBACK(generic_window_state_change_event), NULL); + g_signal_handlers_disconnect_by_func(window, G_CALLBACK(generic_window_size_allocate_event), NULL); + g_signal_handlers_disconnect_by_func(window, G_CALLBACK (generic_window_showhide_event), (gpointer)1); + g_signal_handlers_disconnect_by_func(window, G_CALLBACK (generic_window_showhide_event), (gpointer)0); + g_signal_handlers_disconnect_by_func(window, G_CALLBACK (generic_window_destroy_event), NULL); + registered_windows = g_slist_remove(registered_windows, window); + } + return FALSE; +} + + +void context_tool_changed(GimpContext *context, GimpToolInfo *tool_info, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + int tool_type = -1; + int tool_options_type = -1; + const gchar* tool_type_name = ""; + int visible = -1; + + if (tool_info) { + tool_type = tool_info->tool_type; + tool_options_type = tool_info->tool_options_type; + tool_type_name = g_type_name(tool_type); + if (!tool_type_name) { + tool_type_name = ""; + } + visible = tool_info->visible; + } + + g_string_append_printf(log_str, "\n", tool_type, tool_type_name, tool_options_type, visible); + guilog_log_message(log_str, FALSE); + } +} + +void context_image_changed(GimpContext *context, GimpImage *gimage, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + int image_id = -1; + + if (gimage) { + image_id = gimage->ID; + } + + g_string_append_printf(log_str, "\n", image_id); + guilog_log_message(log_str, FALSE); + } +} + +void context_foreground_changed(GimpContext *context, const GimpRGB *fg, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_background_changed(GimpContext *context, const GimpRGB *bg, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_brush_opacity_changed(GimpContext *context, gdouble opacity, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_paint_mode_changed(GimpContext *context, GimpLayerModeEffects paint_mode, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_brush_changed(GimpContext *context, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_pattern_changed(GimpContext *context, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_gradient_changed(GimpContext *context, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_palette_changed(GimpContext *context, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} + +void context_font_changed(GimpContext *context, gpointer data) +{ + if (log_events) { + GString *log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, FALSE); + } +} +gboolean gimp_exit_callback(Gimp *gimp, gpointer data) +{ + guilog_stop(); + return FALSE; +} + + +char* guilog_get_timestamp(void) +{ + time_t cur_time; + cur_time = time(NULL); + strftime(timestamp, timestamp_size, "%Y_%m_%d_%H_%M_%S", localtime(&cur_time)); + return timestamp; +} + +double guilog_get_elapsed_time(GTimeVal* start_time) +{ + GTimeVal cur_time; + double cur_time_usec; + double last_time_usec; + + g_get_current_time(&cur_time); + cur_time_usec = (double)cur_time.tv_sec * 1000000.0 + (double)cur_time.tv_usec; + last_time_usec = (double)(*start_time).tv_sec * 1000000.0 + (double)(*start_time).tv_usec; + return cur_time_usec - last_time_usec; +} + + +void guilog_image_to_string(GString* str, GimpImage* gimage, gboolean write_histogram) +{ + g_string_append(str, "\n"); + } else { + GimpContainer* layers = NULL; + layers = (*gimage).layers; + + g_string_append_printf(str, "id=\"%d\" ", (*gimage).ID); + g_string_append_printf(str, "width=\"%d\" ", (*gimage).width); + g_string_append_printf(str, "height=\"%d\" ", (*gimage).height); + g_string_append_printf(str, "xresolution=\"%f\" ", (*gimage).xresolution); + g_string_append_printf(str, "yresolution=\"%f\" ", (*gimage).yresolution); + g_string_append_printf(str, "resolution_unit=\"%d\" ", (*gimage).resolution_unit); + g_string_append_printf(str, "base_type=\"%d\" ", (*gimage).base_type); + g_string_append_printf(str, "num_cols=\"%d\" ", (*gimage).num_cols); + g_string_append_printf(str, "dirty=\"%d\" ", (*gimage).dirty); + g_string_append_printf(str, "dirty_time=\"%u\" ", (*gimage).dirty_time); + g_string_append_printf(str, "instance_count=\"%d\" ", (*gimage).instance_count); + g_string_append_printf(str, "disp_count=\"%d\" ", (*gimage).disp_count); + g_string_append_printf(str, "tattoo_state=\"%d\" ", (*gimage).tattoo_state); + g_string_append_printf(str, "qmask_state=\"%d\" ", (*gimage).quick_mask_state); + g_string_append_printf(str, "qmask_inverted=\"%d\" ", (*gimage).quick_mask_inverted); + g_string_append_printf(str, "group_count=\"%d\" ", (*gimage).group_count); + g_string_append_printf(str, "pushing_undo_group=\"%d\" ", (*gimage).pushing_undo_group); + g_string_append_printf(str, "comp_preview_valid=\"-1\" "); + g_string_append(str, ">\n"); + + /* + * If we should write the histogram, then check whether we have multiple, + * visible layers. If so, we should create a composite of this image that + * is written out. + */ + if (write_histogram && layers) { + GList *list; + int num_visible_layers = 0; + + for (list = GIMP_LIST (gimage->layers)->list; + list; + list = g_list_next (list)) + { + GimpLayer* layer = NULL; + layer = (GimpLayer *) list->data; + + if (gimp_item_get_visible (GIMP_ITEM (layer))) { + num_visible_layers++; + } + } + if (num_visible_layers > 1) { + guilog_write_composite_image_histogram(str, gimage); + } + } + guilog_layers_to_string(str, gimage, write_histogram); + } + g_string_append(str, "\n"); +} + +void guilog_layers_to_string(GString* str, GimpImage* gimage, gboolean write_histogram) +{ + GimpContainer* layers = NULL; + gint i; + + if (!gimage) + return; + + layers = (*gimage).layers; + if (layers) { + for (i = 0; i < (*layers).num_children; i++) { + GimpLayer* this_layer = (GimpLayer*)gimp_container_get_child_by_index(layers, i); + guilog_layer_to_string(str, gimage, this_layer, i, write_histogram); + } + } +} + +void guilog_layer_to_string(GString* str, GimpImage* gimage, GimpLayer* layer, gint layer_num, gboolean write_histogram) +{ + GimpDrawable* drawable; + + g_string_append_printf(str, "\n"); + + drawable = &(*layer).parent_instance; + guilog_drawable_to_string(str, gimage, drawable, write_histogram); + } + g_string_append(str, "\n"); +} + +void guilog_drawable_to_string(GString* str, GimpImage* gimage, GimpDrawable* drawable, gboolean write_histogram) +{ + GimpItem* item; + + g_string_append(str, "\n"); + + if (write_histogram) { + guilog_write_drawable_histogram(str, gimage, drawable); + } + item = &(*drawable).parent_instance; + guilog_item_to_string(str, item); + } + g_string_append(str, "\n"); +} + +void guilog_write_drawable_histogram(GString* str, GimpImage* gimage, GimpDrawable* drawable) +{ + GimpHistogram *hist; + gint hist_n_channels; + gint channel_num; + + if (!(gimage && drawable)) { + return; + } + + hist = gimp_histogram_new(); + gimp_drawable_calculate_histogram(drawable, hist); + hist_n_channels = gimp_histogram_n_channels(hist); + + guilog_log_histogram_channel(str, hist, GIMP_HISTOGRAM_VALUE); + + for (channel_num = 0; channel_num < hist_n_channels; channel_num++) { + guilog_log_histogram_channel(str, hist, GIMP_HISTOGRAM_RED + channel_num); + } + + if (hist_n_channels >= 3) { + guilog_log_histogram_channel(str, hist, GIMP_HISTOGRAM_RGB); + } + + gimp_histogram_free(hist); +} + +void guilog_log_histogram_channel(GString* str, GimpHistogram* hist, gint channel) +{ + gint bin; + gdouble hist_max, + hist_count, + hist_mean, + hist_median, + hist_std_dev; + + hist_count = gimp_histogram_get_count (hist, channel, 0, 255); + hist_max = gimp_histogram_get_maximum (hist, channel); + hist_mean = gimp_histogram_get_mean (hist, channel, 0, 255); + hist_median = gimp_histogram_get_median (hist, channel, 0, 255); + hist_std_dev = gimp_histogram_get_std_dev (hist, channel, 0, 255); + + g_string_append(str, "\n"); + g_string_append(str, "\n"); + for (bin = 0; bin < 256; bin++) { + g_string_append_printf(str, "%f\t", gimp_histogram_get_value(hist, channel, bin)); + } + g_string_append(str, "\n"); + g_string_append(str, "\n"); + g_string_append(str, "\n"); +} + +void guilog_item_to_string(GString* str, GimpItem* item) +{ + g_string_append(str, "\n"); + g_string_append(str, "\n"); +} + +void guilog_write_composite_image_histogram(GString* str, GimpImage* gimage) +{ + + if (gimage) { + GimpLayer* merged_layer = NULL; + + + merged_layer = guilog_flatten_image(gimage); + if (merged_layer) { + g_string_append(str, "\n"); + /* Need to temporarily add the layer for the histogram to work */ + gimp_container_insert(gimage->layers, GIMP_OBJECT(merged_layer), 0); + guilog_write_drawable_histogram(str, gimage, (GimpDrawable*)merged_layer); + /* Remove the layer */ + gimp_container_remove(gimage->layers, GIMP_OBJECT(merged_layer)); + g_string_append(str, "\n"); + g_object_unref (merged_layer); + } + } +} + +/* + * This function ripped from core/gimpimage-merge.c + */ +GimpLayer* guilog_flatten_image(GimpImage* gimage) +{ + GList *list; + GSList *merge_list = NULL; + GSList *reverse_list = NULL; + PixelRegion src1PR, src2PR, maskPR; + PixelRegion *mask; + GimpLayer *merge_layer; + GimpLayer *layer; + GimpLayer *bottom_layer; + guchar bg[4] = {255, 255, 255, 255}; + GimpImageType type; + gint x1, y1, x2, y2; + gint x3, y3, x4, y4; + CombinationMode operation; + gint position; + gboolean active[MAX_CHANNELS] = { TRUE, TRUE, TRUE, TRUE }; + gint off_x, off_y; + + g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL); + + for (list = GIMP_LIST (gimage->layers)->list; + list; + list = g_list_next (list)) + { + layer = (GimpLayer *) list->data; + + if (gimp_item_get_visible (GIMP_ITEM (layer))) + merge_list = g_slist_append (merge_list, layer); + } + + layer = NULL; + type = GIMP_RGBA_IMAGE; + x1 = y1 = 0; + x2 = y2 = 0; + bottom_layer = NULL; + + /* Get the layer extents */ + while (merge_list) + { + layer = (GimpLayer *) merge_list->data; + + gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); + + if (merge_list->next == NULL) + { + x1 = 0; + y1 = 0; + x2 = gimage->width; + y2 = gimage->height; + } + + reverse_list = g_slist_prepend (reverse_list, layer); + merge_list = g_slist_next (merge_list); + } + + if ((x2 - x1) == 0 || (y2 - y1) == 0) + return NULL; + + type = GIMP_IMAGE_TYPE_FROM_BASE_TYPE (gimp_image_base_type (gimage)); + + merge_layer = gimp_layer_new (gimage, (x2 - x1), (y2 - y1), + type, + gimp_object_get_name (GIMP_OBJECT (layer)), + GIMP_OPACITY_OPAQUE, GIMP_NORMAL_MODE); + if (!merge_layer) + { + return NULL; + } + + GIMP_ITEM (merge_layer)->offset_x = x1; + GIMP_ITEM (merge_layer)->offset_y = y1; + + /* init the pixel region */ + pixel_region_init (&src1PR, + gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)), + 0, 0, + gimage->width, gimage->height, + TRUE); + + /* set the region to the background color */ + color_region (&src1PR, bg); + + position = 0; + + bottom_layer = layer; + + while (reverse_list) + { + GimpLayerModeEffects mode; + + layer = (GimpLayer *) reverse_list->data; + + /* determine what sort of operation is being attempted and + * if it's actually legal... + */ + operation = + gimp_image_get_combination_mode (gimp_drawable_type (GIMP_DRAWABLE (merge_layer)), + gimp_drawable_bytes (GIMP_DRAWABLE (layer))); + + if (operation == -1) + { + /* TODO: Verify this is the right way to free layer */ + g_object_unref (merge_layer); + g_slist_free (reverse_list); + g_slist_free (merge_list); + return NULL; + } + + gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); + + x3 = CLAMP (off_x, x1, x2); + y3 = CLAMP (off_y, y1, y2); + x4 = CLAMP (off_x + gimp_item_width (GIMP_ITEM (layer)), x1, x2); + y4 = CLAMP (off_y + gimp_item_height (GIMP_ITEM (layer)), y1, y2); + + /* configure the pixel regions */ + pixel_region_init (&src1PR, + gimp_drawable_get_tiles (GIMP_DRAWABLE (merge_layer)), + (x3 - x1), (y3 - y1), (x4 - x3), (y4 - y3), + TRUE); + pixel_region_init (&src2PR, + gimp_drawable_get_tiles (GIMP_DRAWABLE (layer)), + (x3 - off_x), (y3 - off_y), + (x4 - x3), (y4 - y3), + FALSE); + + if (layer->mask && layer->mask->apply_mask) + { + pixel_region_init (&maskPR, + gimp_drawable_get_tiles (GIMP_DRAWABLE (layer->mask)), + (x3 - off_x), (y3 - off_y), + (x4 - x3), (y4 - y3), + FALSE); + mask = &maskPR; + } + else + { + mask = NULL; + } + + /* DISSOLVE_MODE is special since it is the only mode that does not + * work on the projection with the lower layer, but only locally on + * the layers alpha channel. + */ + mode = layer->mode; + if (layer == bottom_layer && mode != GIMP_DISSOLVE_MODE) + mode = GIMP_NORMAL_MODE; + + combine_regions (&src1PR, &src2PR, &src1PR, mask, NULL, + layer->opacity * 255.999, + mode, + active, + operation); + + reverse_list = g_slist_next (reverse_list); + } + + g_slist_free (reverse_list); + g_slist_free (merge_list); + + return merge_layer; +} + +GString* guilog_start_log_message(void) +{ + GString* log_str = NULL; + + g_get_current_time(&log_item_start_time); + log_str = g_string_new(""); + g_string_append_printf(log_str, "\n", entry_num++, guilog_get_timestamp(), guilog_get_elapsed_time(&log_session_start_time)); + return log_str; +} + +void guilog_log_message(GString* log_str, gboolean include_elapsed_log_time) +{ + double time_diff; + + time_diff = guilog_get_elapsed_time(&log_item_start_time); + + if (include_elapsed_log_time) { + g_string_append_printf(log_str, "\n", time_diff); + } + g_string_append_printf(log_str, "\n\n"); + write_data_to_log((*log_str).str, (*log_str).len); + g_string_free(log_str, TRUE); + if (compress_log && compressed_log_file) { + /* Flush in case the GIMP crashes */ + gzflush(compressed_log_file, Z_SYNC_FLUSH); + } +} + +gsize write_data_to_log(const voidp buf, unsigned len) +{ + gsize bytes_written; + GError* error = NULL; + + if (compress_log) { + bytes_written = gzwrite(compressed_log_file, buf, len); + } else { + g_io_channel_write_chars(log_file, buf, len, &bytes_written, &error); + } + return bytes_written; +} + +gboolean histogram_callback(gpointer data) +{ + GimpImage* gimage = NULL; + double time_diff = 0.0; + time_diff = guilog_get_elapsed_time(&last_undo_time) / 1000000; + + /* Remove us if timeout is less than 0 */ + if (histogram_timeout < 0) { + return FALSE; + } + + if (time_diff > histogram_timeout && dirty_images && !button_down && !key_down) { + gimage = (GimpImage*)dirty_images->data; + dirty_images = g_slist_remove(dirty_images, dirty_images->data); + /* + * Make sure we don't have a disposed image + */ + if (log_events && !g_slist_find(dead_images, (gpointer)(*gimage).ID)) { + GString* log_str = guilog_start_log_message(); + g_string_append(log_str, "\n"); + guilog_image_to_string(log_str, gimage, TRUE); + g_string_append(log_str, "\n"); + guilog_log_message(log_str, TRUE); + } + } + return log_events; +} + +int guilog_get_char_count(const gchar* text, int(*charFun)(int)) +{ + int count = 0; + if (!text) { + return 0; + } + while (*text) { + if (charFun(*text)) { + count++; + } + text++; + } + return count; +} +int isforwardslash(int c) +{ + return c == '/'; +} +int isbackwardslash(int c) +{ + return c == '\\'; +} + +void init_one_time_pad(void) +{ + one_time_pad_map = g_hash_table_new(NULL, NULL); + one_time_pad_filename = (char*)g_getenv(GIMP_GUI_LOG_ONE_TIME_PAD_FILE); + if (one_time_pad_filename && strlen(one_time_pad_filename)) { + if (g_file_test(one_time_pad_filename, G_FILE_TEST_EXISTS)) { + gzFile f = NULL; + f = gzopen(one_time_pad_filename, "r"); + if (f) { + int bytes_read = 0; + do { + guint key = 0; + guint val = 0; + + bytes_read = gzread(f, (voidp)&key, sizeof(key)); + if (bytes_read > 0) { + bytes_read = gzread(f, (voidp)&val, sizeof(val)); + if (bytes_read > 0) { + g_hash_table_insert(one_time_pad_map, (gpointer)key, (gpointer)val); + one_time_pad_keys = g_slist_prepend(one_time_pad_keys, (gpointer)key); + last_one_time_val = MAX(last_one_time_val, val); + } + } + } while (bytes_read > 0); + gzclose(f); + } + } + } +} + +guint anonymize_hash_key(guint key) +{ + if (one_time_pad_map) { + if (!g_hash_table_lookup(one_time_pad_map, (gpointer)key)) { + guint val = ++last_one_time_val; + one_time_pad_keys = g_slist_prepend(one_time_pad_keys, (gpointer)key); + g_hash_table_insert(one_time_pad_map, (gpointer)key, (gpointer)val); + } + return (guint)g_hash_table_lookup(one_time_pad_map, (gpointer)key); + } + return 0; +} + +/* + * Returns a newly allocated string + */ +GString* guilog_xmlify_string(const gchar* text) +{ + GString* new_string = NULL; + new_string = g_string_new(""); + for ( ; *text; text++) { + if (*text == '<') { + g_string_append(new_string, "<"); + } else if (*text == '>') { + g_string_append(new_string, ">"); + } else if (*text == '"') { + g_string_append(new_string, """); + } else if (*text == '\'') { + g_string_append(new_string, "'"); + } else if (*text == '&') { + g_string_append(new_string, "&"); + } else { + g_string_append_c(new_string, *text); + } + } + return new_string; +} +void guilog_generate_string_stats(GString* log_str, const gchar* attrib_prefix, const gchar* string_to_analyze) +{ + int length = -1; + int alpha_count = -1; + int digit_count = -1; + int punct_count = -1; + int space_count = -1; + int forward_slash_count = -1; + int backward_slash_count = -1; + guint name_hash = 0; + + if (string_to_analyze) { + length = strlen(string_to_analyze); + alpha_count = guilog_get_char_count(string_to_analyze, isalpha); + digit_count = guilog_get_char_count(string_to_analyze, isdigit); + punct_count = guilog_get_char_count(string_to_analyze, ispunct); + space_count = guilog_get_char_count(string_to_analyze, isspace); + forward_slash_count = guilog_get_char_count(string_to_analyze, isforwardslash); + backward_slash_count = guilog_get_char_count(string_to_analyze, isbackwardslash); + name_hash = anonymize_hash_key(g_str_hash(string_to_analyze)); + } + g_string_append_printf(log_str, "%slength=\"%d\" ", attrib_prefix, length); + g_string_append_printf(log_str, "%salpha_count=\"%d\" ", attrib_prefix, alpha_count); + g_string_append_printf(log_str, "%sdigit_count=\"%d\" ", attrib_prefix, digit_count); + g_string_append_printf(log_str, "%spunct_count=\"%d\" ", attrib_prefix, punct_count); + g_string_append_printf(log_str, "%sspace_count=\"%d\" ", attrib_prefix, space_count); + g_string_append_printf(log_str, "%sforward_slash_count=\"%d\" ", attrib_prefix, forward_slash_count); + g_string_append_printf(log_str, "%sbackward_slash_count=\"%d\" ", attrib_prefix, backward_slash_count); + g_string_append_printf(log_str, "%shash=\"%u\" ", attrib_prefix, name_hash); +} diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/core/gimpinteraction-logger.h ingimp-2.4.7/app/core/gimpinteraction-logger.h --- gimp-2.4.7/app/core/gimpinteraction-logger.h 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/app/core/gimpinteraction-logger.h 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,66 @@ +/* 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. + */ + +#ifndef __GIMP_INTERACTION_LOGGER_H__ +#define __GIMP_INTERACTION_LOGGER_H__ + +int guilog_start(Gimp* gimp); +int guilog_stop(void); + +void guilog_register_display_shell ( void *shell); +void guilog_template_create_image ( GimpImage *gimage); + +void guilog_create_image ( GimpImage *gimage, + const gchar *comment); + +void guilog_revert_image ( GimpImage *old_gimage, + GimpImage *new_gimage); + +void guilog_open_image (const gchar *uri, + const gchar *entered_filename, + GimpImage *gimage); + +void guilog_last_opened ( GimpImage *gimage, + gint index, + const gchar *filename); + +void guilog_save_image_requested ( GimpImage *gimage, + gboolean is_save_as); + +void guilog_save_image ( GimpImage *gimage, + const gchar *uri, + const gchar *filename, + gboolean save_a_copy, + GimpPDBStatusType status); + +void guilog_image_duplicate ( GimpImage *old_gimage, + GimpImage *new_gimage); + +void guilog_image_dispose ( GimpImage *gimage); + +void guilog_undo_event ( GimpImage *gimage, + GimpUndoEvent event, + GimpUndo *undo); +void guilog_plugin_invoked ( GimpProcedure *proc, + gboolean reshow_last); +void guilog_plugin_returned ( GimpProcedure *proc); +void guilog_plugin_closed ( GimpProcedure *proc); +void guilog_register_action ( void *vp_action); +void guilog_register_window ( void *vp_window); + +#endif /* __GIMP_INTERACTION_LOGGER_H__ */ diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/core/gimptemplate.c ingimp-2.4.7/app/core/gimptemplate.c --- gimp-2.4.7/app/core/gimptemplate.c 2008-07-10 22:29:50.000000000 +1200 +++ ingimp-2.4.7/app/core/gimptemplate.c 2008-09-01 14:28:03.000000000 +1200 @@ -33,6 +33,7 @@ #include "gimp.h" #include "gimpcontext.h" #include "gimpimage.h" +#include "gimpinteraction-logger.h" #include "gimplayer.h" #include "gimpprojection.h" #include "gimptemplate.h" @@ -426,6 +427,8 @@ gimp_create_display (gimp, image, template->unit, 1.0); + guilog_template_create_image(image); + g_object_unref (image); return image; diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/core/Makefile.am ingimp-2.4.7/app/core/Makefile.am --- gimp-2.4.7/app/core/Makefile.am 2008-07-10 22:29:50.000000000 +1200 +++ ingimp-2.4.7/app/core/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -10,6 +10,7 @@ -I$(top_builddir)/app \ -I$(top_srcdir)/app \ $(GDK_PIXBUF_CFLAGS) \ + $(GTK_CFLAGS) \ $(LIBART_CFLAGS) \ $(GLIB_CFLAGS) \ -I$(includedir) @@ -202,6 +203,8 @@ gimpimagefile.h \ gimpimagemap.c \ gimpimagemap.h \ + gimpinteraction-logger.c \ + gimpinteraction-logger.h \ gimpitem.c \ gimpitem.h \ gimpitem-linked.c \ diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/display/gimpdisplayshell.c ingimp-2.4.7/app/display/gimpdisplayshell.c --- gimp-2.4.7/app/display/gimpdisplayshell.c 2008-07-10 22:30:05.000000000 +1200 +++ ingimp-2.4.7/app/display/gimpdisplayshell.c 2008-09-01 14:28:03.000000000 +1200 @@ -40,6 +40,7 @@ #include "core/gimpguide.h" #include "core/gimpimage.h" #include "core/gimpimage-snap.h" +#include "core/gimpinteraction-logger.h" #include "core/gimpprojection.h" #include "core/gimpmarshal.h" #include "core/gimpsamplepoint.h" @@ -1130,6 +1131,8 @@ /* make sure the information is up-to-date */ gimp_display_shell_scale_changed (shell); + guilog_register_display_shell (shell); + return GTK_WIDGET (shell); } diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/file/file-open.c ingimp-2.4.7/app/file/file-open.c --- gimp-2.4.7/app/file/file-open.c 2008-07-10 22:29:56.000000000 +1200 +++ ingimp-2.4.7/app/file/file-open.c 2008-09-01 14:28:03.000000000 +1200 @@ -54,6 +54,7 @@ #include "core/gimpimage-merge.h" #include "core/gimpimage-undo.h" #include "core/gimpimagefile.h" +#include "core/gimpinteraction-logger.h" #include "core/gimplayer.h" #include "core/gimpparamspecs.h" #include "core/gimpprogress.h" @@ -164,6 +165,7 @@ if (image) { file_open_sanitize_image (image, as_new); + guilog_open_image(uri, entered_filename, image); /* Only set the load procedure if it hasn't already been set. */ if (! gimp_image_get_load_proc (image)) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/file/file-save.c ingimp-2.4.7/app/file/file-save.c --- gimp-2.4.7/app/file/file-save.c 2008-07-10 22:29:56.000000000 +1200 +++ ingimp-2.4.7/app/file/file-save.c 2008-09-01 14:28:03.000000000 +1200 @@ -51,6 +51,7 @@ #include "core/gimpdrawable.h" #include "core/gimpimage.h" #include "core/gimpimagefile.h" +#include "core/gimpinteraction-logger.h" #include "core/gimpparamspecs.h" #include "core/gimpprogress.h" @@ -194,6 +195,7 @@ gimp_plug_in_procedure_get_label (file_proc)); } + guilog_save_image(image, uri, filename, save_a_copy, status); g_object_unref (image); out: diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/Makefile.am ingimp-2.4.7/app/Makefile.am --- gimp-2.4.7/app/Makefile.am 2008-07-10 22:30:13.000000000 +1200 +++ ingimp-2.4.7/app/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -32,9 +32,9 @@ if ENABLE_GIMP_CONSOLE -bin_PROGRAMS = gimp-2.4 gimp-console-2.4 +bin_PROGRAMS = ingimp-2.4 ingimp-console-2.4 else -bin_PROGRAMS = gimp-2.4 +bin_PROGRAMS = ingimp-2.4 endif app_sources = \ @@ -52,7 +52,7 @@ units.h \ gimp-intl.h -gimp_2_4_SOURCES = $(app_sources) +ingimp_2_4_SOURCES = $(app_sources) EXTRA_DIST = \ @@ -98,7 +98,7 @@ -u $(SYMPREFIX)gimp_coords_mix \ -u $(SYMPREFIX)gimp_plug_in_manager_restore -gimp_2_4_LDADD = \ +ingimp_2_4_LDADD = \ gui/libappgui.a \ actions/libappactions.a \ dialogs/libappdialogs.a \ @@ -135,18 +135,19 @@ $(DBUS_GLIB_LIBS) \ $(RT_LIBS) \ $(INTLLIBS) \ - $(GIMPICONRC) + $(GIMPICONRC) \ + $(LIBZ) if ENABLE_GIMP_CONSOLE -gimp_console_2_4_SOURCES = $(app_sources) +ingimp_console_2_4_SOURCES = $(app_sources) -gimp_console_2_4_CPPFLAGS = \ +ingimp_console_2_4_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DGIMP_CONSOLE_COMPILATION -gimp_console_2_4_LDADD = \ +ingimp_console_2_4_LDADD = \ widgets/widgets-enums.o \ display/display-enums.o \ display/gimpdisplayoptions.o \ @@ -186,20 +187,20 @@ install-exec-hook: if DEFAULT_BINARY cd $(DESTDIR)$(bindir) \ - && rm -f gimp$(EXEEXT) \ - && $(LN_S) gimp-$(GIMP_APP_VERSION)$(EXEEXT) gimp$(EXEEXT) + && rm -f ingimp$(EXEEXT) \ + && $(LN_S) ingimp-$(GIMP_APP_VERSION)$(EXEEXT) ingimp$(EXEEXT) if ENABLE_GIMP_CONSOLE cd $(DESTDIR)$(bindir) \ - && rm -f gimp-console$(EXEEXT) \ - && $(LN_S) gimp-console-$(GIMP_APP_VERSION)$(EXEEXT) gimp-console$(EXEEXT) + && rm -f ingimp-console$(EXEEXT) \ + && $(LN_S) ingimp-console-$(GIMP_APP_VERSION)$(EXEEXT) ingimp-console$(EXEEXT) endif endif uninstall-local: if DEFAULT_BINARY - rm -f $(DESTDIR)$(bindir)/gimp$(EXEEXT) + rm -f $(DESTDIR)$(bindir)/ingimp$(EXEEXT) if ENABLE_GIMP_CONSOLE - rm -f $(DESTDIR)$(bindir)/gimp-console$(EXEEXT) + rm -f $(DESTDIR)$(bindir)/ingimp-console$(EXEEXT) endif endif @@ -210,7 +211,7 @@ dist-check-gimp-console: else dist-check-gimp-console: - @echo "*** gimp-console must be enabled in order to make dist" + @echo "*** ingimp-console must be enabled in order to make dist" @false endif @@ -218,7 +219,7 @@ # hook to assure that the system gimprc and the gimprc manpage are # uptodate when a release is made # -dist-dump-gimprc: gimp-console-$(GIMP_APP_VERSION)$(EXEEXT) +dist-dump-gimprc: ingimp-console-$(GIMP_APP_VERSION)$(EXEEXT) ./$< --dump-gimprc-system > gimprc.tmp \ && sed -e "s/num-processors [0-9]*/num-processors 1/" \ gimprc.tmp > gimprc.tmp2 \ diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/plug-in/gimpplugin.c ingimp-2.4.7/app/plug-in/gimpplugin.c --- gimp-2.4.7/app/plug-in/gimpplugin.c 2008-07-10 22:30:12.000000000 +1200 +++ ingimp-2.4.7/app/plug-in/gimpplugin.c 2008-09-01 14:28:03.000000000 +1200 @@ -79,6 +79,7 @@ #include "core/gimp.h" #include "core/gimpcontext.h" +#include "core/gimpinteraction-logger.h" #include "core/gimpprogress.h" #include "gimpenvirontable.h" @@ -423,6 +424,8 @@ plug_in->open = FALSE; + guilog_plugin_closed(gimp_plug_in_get_proc_frame(plug_in)->procedure); + if (plug_in->pid) { #ifndef G_OS_WIN32 diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/widgets/gimpdialogfactory.c ingimp-2.4.7/app/widgets/gimpdialogfactory.c --- gimp-2.4.7/app/widgets/gimpdialogfactory.c 2008-07-10 22:30:10.000000000 +1200 +++ ingimp-2.4.7/app/widgets/gimpdialogfactory.c 2008-09-01 14:28:03.000000000 +1200 @@ -31,6 +31,7 @@ #include "widgets-types.h" #include "core/gimpcontext.h" +#include "core/gimpinteraction-logger.h" #include "gimpcursor.h" #include "gimpdialogfactory.h" @@ -891,6 +892,7 @@ G_CALLBACK (gimp_dialog_factory_dialog_configure), factory, 0); + guilog_register_window (dialog); } void diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/app/widgets/gimpmenufactory.c ingimp-2.4.7/app/widgets/gimpmenufactory.c --- gimp-2.4.7/app/widgets/gimpmenufactory.c 2008-07-10 22:30:10.000000000 +1200 +++ ingimp-2.4.7/app/widgets/gimpmenufactory.c 2008-09-01 14:28:03.000000000 +1200 @@ -30,6 +30,7 @@ #include "widgets-types.h" #include "core/gimp.h" +#include "core/gimpinteraction-logger.h" #include "gimpactionfactory.h" #include "gimpmenufactory.h" @@ -218,6 +219,7 @@ gtk_action_set_accel_group (action, accel_group); gtk_action_connect_accelerator (action); + guilog_register_action (action); } g_list_free (actions); diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/configure.in ingimp-2.4.7/configure.in --- gimp-2.4.7/configure.in 2008-08-21 21:26:58.000000000 +1200 +++ ingimp-2.4.7/configure.in 2008-09-01 14:28:03.000000000 +1200 @@ -1908,6 +1908,10 @@ app/vectors/Makefile app/widgets/Makefile app/xcf/Makefile +guilog/Makefile +guilog/wrapper/Makefile +guilog/wrapper/images/Makefile +guilog/consent/Makefile plug-ins/Makefile plug-ins/script-fu/Makefile plug-ins/script-fu/ftx/Makefile diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/ChangeLog.ingimp ingimp-2.4.7/guilog/ChangeLog.ingimp --- gimp-2.4.7/guilog/ChangeLog.ingimp 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/ChangeLog.ingimp 2008-09-01 14:31:38.000000000 +1200 @@ -0,0 +1,57 @@ +2008-09-01: +[ Francois Marier ] + - Port to GIMP 2.4.7 + +2008-05-31: +[ Francois Marier ] + - Port to GIMP 2.4.6 + +2008-03-02: +[ Francois Marier ] + - Port to GIMP 2.4.5 + +2008-01-31: +[ Francois Marier ] + - Port to GIMP 2.4.4 + +2007-11-19: +[ Michael Terry ] + - Port to GIMP 2.4.1 + +2007-09-08: +[ Michael Terry ] + - Switched to a one-time pad scheme for tracking strings across sessions. + This significantly increases anonymity for summarized strings + +2007-08-23: +[ Michael Terry ] + - Added new illustrations describing how ingimp works + +2007-07-25: +[ Michael Terry ] + - Added new code to support pictograms illustrating functionality of ingimp. + This includes new wrapper code as well as some new data being logged via + the C client (specifically, dwell times on each pictogram and the consent + agreement as well as maximum amount of scrolling for each of the latter + items). + +2007-07-15: +[ Michael Terry ] + - Port patch to GIMP 2.2.17 (no changes) + +2007-06-12: + +[ Francois Marier ] + - Display a "logging disabled" confirmation when consent is not given. + +2007-06-10: + +[ Francois Marier ] + - Port patch to GIMP 2.2.16 (no changes) + - Add a ChangeLog file for ingimp + +2007-06-07: + +[ Francois Marier ] + - Display message on console when uploading data + - Make consent optional (disable logging) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/compiling/win_enviro_setup.sh ingimp-2.4.7/guilog/compiling/win_enviro_setup.sh --- gimp-2.4.7/guilog/compiling/win_enviro_setup.sh 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/compiling/win_enviro_setup.sh 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,41 @@ +# Notes +# You've got to make sure everything in the support dir is executable +# So do a chmod -R 755 in the support dir. Note this chmod seems to only +# work when done in cygwin... Go figure... +# Need to actually build zlib-1.2.3 to get the dev libs +# Need gw32c lib +# This one plug-in, wmf, will bomb out at start-up unless the +# following DLLs are renamed to the following names: +# freetype-6.dll, jpeg-62.dll, libpng.dll, zlib-1.dll +# Why it looks for these specific names, I do not know... +# Rename xmlparse.dll to libexpat.dll +# After doing an install, will need to copy all the dll's to the bin directory +# Will also need to copy glib/gtk/pango stuff in etc and lib to corresponding +# gimp directory +# Need to include libpng12.dll or it won't load images (copy libpng13.dll to libpng12.dll) +# I needed to compile lcms 1.17 from source in msys to make 2.4 compile. Also need to remove the "cdecl" from lcms.h file +# Need to add: +#include +# to wmf.c in plug-ins/common before any libwmf includes +#Also see this: +#Since my last post, I found that if I include -lintl in the link for libgimbase-2.0.dll, it works. +# +#I'm guessing GLIB_LIBS needs to include -lintl and -liconv so, I edited glib-2.0.pc and made the Libs line look like this: +# +#Libs: -L${libdir} -lglib-2.0 -lintl -liconv +# +#I then re-ran configure. Now GLIB_LIBS and other vars (GTK_LIBS, GDK_PIXBUFS_LIBS, etc) include -lintl -liconv +# +#From: http://gug.sunsite.dk/forum/?threadid=3986 +# +SUPPORT_DIR=/c/data/ingimp-2.4-support/dev +GLIB_LIB_DIR=${SUPPORT_DIR}/lib/glib-2.0 +GTK_LIB_DIR=${SUPPORT_DIR}/lib/gtk-2.0 +PANGO_LIB_DIR=${SUPPORT_DIR}/lib/pango +EXTRA_PATHS=${SUPPORT_DIR}/bin:${SUPPORT_DIR}/lib:${GLIB_LIB_DIR}:${GTK_LIB_DIR}:${GTK_LIB_DIR}/2.4.0/engines:${GTK_LIB_DIR}/2.4.0/immodules:${GTK_LIB_DIR}/2.4.0/loaders:${PANGO_LIB_DIR}:${PANGO_LIB_DIR}/1.5.0/modules +export PATH=$EXTRA_PATHS:$PATH:/c/cygwin/bin +export LIBRARY_PATH=$LIBRARY_PATH:$EXTRA_PATHS +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$EXTRA_PATHS +# Needed for pkg-config to be able to find its .pc files +export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${SUPPORT_DIR}/lib/pkgconfig +export C_INCLUDE_PATH=${C_INCLUDE_PATH}:${SUPPORT_DIR}/include:${SUPPORT_DIR}/include/freetype2 diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/consent/consent.txt ingimp-2.4.7/guilog/consent/consent.txt --- gimp-2.4.7/guilog/consent/consent.txt 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/consent/consent.txt 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,81 @@ +ingimp Logging Consent Form +University of Waterloo +July 20, 2007 + +Introduction +This software, ingimp (Instrumented GIMP) is part of a research effort actively exploring new ways to contribute to the usability of open source software. This software is modified to automatically record how you use this application. This information will be automatically uploaded to a publicly accessible webserver to enable usability analyses. The purpose of this document is to gain your consent to do this data collection. + +ingimp automatically logs usage information such as the names of commands you use, general descriptions of the types of documents you edit, information about your platform (e.g., whether it is Windows, Linux, etc.), interaction events (when keys are pressed, when mouse buttons are pressed, but NOT the actual key or location of the mouse), and so on. These data are sent to a server and made publicly available for all to view and analyze. Our hope is that the data collected will help inform and focus future development and design efforts. + +Principal Investigator +This study is being conducted by Professor Michael Terry in the School of Computer Science at the University of Waterloo. Questions should be directed to ingimp@cs.uwaterloo.ca. + +Study Description +You must be 18 years or older to participate, or you must obtain the consent of your parent or legal guardian. Participation is completely voluntary and can be stopped at any time by removing this software or discontinuing its use. + +If you choose to participate, you may be asked to fill out a web-based questionnaire. The questionnaire is optional but will help us understand who is using this software. + +Once consent to use this software is given, ingimp will log your usage of the software. This version of the software logs the following information: + +* Your platform and its characteristics (e.g., Windows, Linux, CPU if available, the version of your operating system, if available...) + +* Your timezone + +* Any activity tags you enter at start-up to describe how you will use the software (more information below). + +* The names of commands that you use, but not the parameters used for those commands + +* The number of letters, digits, spaces, punctuation marks, and forward/backward slashes in your file names, but NOT the file names themselves + +* Characteristics of your document after each command has been applied, including: +- the number of layers in the image +- the sizes of the respective layers +- pixel counts that indicate the relative frequency of dark and light pixels in the image (an image histogram), along with statistics for these pixel counts (count, max, mean, median, standard deviation) +- layer visibility, opacity, and combination mode +- the number of letters, digits, spaces, and punctuation marks in your layer names, but NOT the layer names themselves +- the resolution of your image +- whether a layer is linked, floating, or removed + +* Keyboard and mouse activity, but NOT the actual keys pressed or the location of the mouse button. The state of the modifier keys (Shift, Control, Alt, etc.) ARE recorded, but the actual key pressed is NOT recorded. Which mouse button is pressed/released is recorded (button 1, 2, 3), but NOT the location of the cursor + +* When and what tools (paintbrush, eraser, etc.) are selected + +* When foreground/background color, brush opacity, paint mode, pattern, gradient, palette, and fonts change, but NOT the details about what the new selection actually is (e.g., the new foreground color is NOT recorded when it is changed) + +* When a document window loses and gains focus (that is, when it is in front or pushed back) + +* When a document window is resized, maximized, minimized, iconified, deiconified, shown/hidden, and what its new size and location are + +* When documents are created, closed, saved, opened, duplicated -- essentially, whenever they come into and out of existence + +* The amount of time you spend looking at this consent agreement and (if shown) the illustrations describing how this software works. We also log use of the scroll bar when viewing the consent agreement and the accompanying illustrations (if shown) + + +The above list is not exhaustive but representative of the types of information recorded. + +Our goal is to honor your privacy in the choice of data collected and how it is collected. However, there are ways that personal information could inadvertently be logged by ingimp: + +* If you create your own custom scripts, your script names will be recorded because ALL command names are recorded. Thus, if you named your script "john_smiths_acme_script", that script name would be recorded whenever you used it + +* When you first start ingimp, you will have the opportunity to enter "activity tags," or free-form descriptions of how you plan on using the software. These are purely optional. You are free to enter any text in the activity tag field, but should remember that these contents will be available for anyone to see in your log file. Thus, if you enter, "Removing Aunt Edna from John Smith's family photo" in the Activity Tags field, this text will be available for anyone to read in the uploaded log files + +Your privacy is important to us. Consequently, we want to emphasize that we do not intentionally collect any personal information and that our data collection is restricted to activity within this piece of software only. However, as a final assurance, you are free to download, compile, and inspect the source code for this software. The distribution is available at http://www.ingimp.org. You may also inspect a typical log file at: http://www.ingimp.org + +Periodically, the software will attempt to transmit the logging information to a central server where it will be stored indefinitely. The general public will have access to all usage data (including yours) via http://www.ingimp.org. + +Compensation +You will not be compensated for participation in the study. However, the information you provide may lead to more usable software as well as an increased understanding of how users of open source software can contribute to usability efforts. + +Confidentiality and Data Retention +All questionnaire data and logged data will be kept indefinitely by the researcher. All collected data will be made publicly available. You should be aware that the public availability of your log data means that we make no guarantees regarding confidentiality, even though we do not intentionally collect personal information. + +Questions +We offer no support for this software. However, if you have any questions about participation in this study, during or after the study, please contact Dr. Michael Terry at ingimp@cs.uwaterloo.ca. + +Consent +By selecting "I agree to participate in this study," you are indicating your consent to participate in this study and are acknowledging that you are at least 18 years old, or that you are a parent or legal guardian granting permission for your minor to participate in this study. + +Your consent indicates that you have read and understood this consent letter and have had the opportunity to receive any additional details about the study. Further, it indicates you understand that you may withdraw consent at any time by terminating your use of this software. + +This project has been reviewed by, and received ethics clearance through, the Office of Research Ethics at the University of Waterloo. By granting consent, you are indicating that you have been informed that if you have any comments or concerns resulting from participation in this study, you may contact the Director, Office of Research Ethics at +1 (519) 888-4567 ext. 36005, or via email at ssykes@uwaterloo.ca. + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/consent/Makefile.am ingimp-2.4.7/guilog/consent/Makefile.am --- gimp-2.4.7/guilog/consent/Makefile.am 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/consent/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,7 @@ +## Makefile.am for gimp/guilog/consent + +consentdir = $(datadir)/ingimp + +consent_DATA = consent.txt + +EXTRA_DIST = $(consent_DATA) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/CREDITS ingimp-2.4.7/guilog/CREDITS --- gimp-2.4.7/guilog/CREDITS 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/CREDITS 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,17 @@ +CREDITS + +The following have made contributions to ingimp: +- Michael Terry +- Terry Park +- Matthew Kay +- Brad Van Vugt +- Francois Marier +- Tom Ayre +- Brandon Slack +- Jaime Ruiz +- Tom Hazelton + +The Mac port uses files from http://gimp-app.sourceforge.net for the build +and/or distribution. + +The original Tux penguin used in the pictograms is copyright Larry Ewing, Simon Budig and Anja Gerwinski. diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/makedist ingimp-2.4.7/guilog/makedist --- gimp-2.4.7/guilog/makedist 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/makedist 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,42 @@ +#!/bin/bash + +if [ ! -e guilog/ChangeLog.ingimp ] ; then + echo "You must run this script from inside the ingimp source directory." + exit 1 +fi + +ORIGDIR=`basename \`pwd\`` +COPYDIR=`basename \`mktemp -d -p ..\`` +DATE=`date +%Y%m%d` +SRCDIR=$ORIGDIR.$DATE +GIMPDIR=`echo $SRCDIR | perl -pe 's/ingimp-([0-9]+\.[0-9]+\.[0-9]+)\.[0-9]+/gimp-$1/'` + +pushd .. > /dev/null + +# Create a backup copy +rmdir $COPYDIR +mv $ORIGDIR $COPYDIR +cp -pr $COPYDIR $SRCDIR + +# Clean-up the source directory +pushd $SRCDIR > /dev/null +rm -Rf guilog/compiling guilog/wininstall +svn status | grep "^\?" | cut -d' ' -f 7 | xargs rm -rf +find -name ".svn" -type d | xargs rm -rf +popd > /dev/null + +# Create the tarballs +rm -f $SRCDIR.tar.gz $SRCDIR.tar.bz2 +tar zcf $SRCDIR.tar.gz $SRCDIR +tar jcf $SRCDIR.tar.bz2 $SRCDIR + +# Create the patch +if [ -e $GIMPDIR ] ; then + diff -Nurd -x Makefile.in -x configure -x *.gif -x autom4te.cache $GIMPDIR/ $SRCDIR/ > $SRCDIR.diff +fi + +# Restore old source directory +rm -rf $SRCDIR +mv $COPYDIR $ORIGDIR + +popd > /dev/null diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/Makefile.am ingimp-2.4.7/guilog/Makefile.am --- gimp-2.4.7/guilog/Makefile.am 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,5 @@ +## Makefile.am for gimp/guilog + +SUBDIRS = wrapper \ + consent + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/README ingimp-2.4.7/guilog/README --- gimp-2.4.7/guilog/README 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/README 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,4 @@ +ingimp (Instrumented GIMP) README + +Please see the ingimp website (http://www.ingimp.org) for more +information about ingimp. diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wininstall/ingimp_install.iss ingimp-2.4.7/guilog/wininstall/ingimp_install.iss --- gimp-2.4.7/guilog/wininstall/ingimp_install.iss 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wininstall/ingimp_install.iss 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,13 @@ +[Setup] +AppName=ingimp-2.2.17 +AppVerName=ingimp version 2.2.17 / 2007.09.08 +DefaultDirName={pf}\ingimp-2.2.17 +DefaultGroupName=ingimp (Instrumented Gimp) +UninstallDisplayIcon={app}\bin\ingimp.exe +Compression=lzma + +[Files] +Source: "C:\ingimp-2.2.17\*"; DestDir: "{app}"; Flags: recursesubdirs + +[Icons] +Name: "{group}\ingimp-2.2.17"; Filename: "{app}\bin\ingimp.exe" diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/About.py ingimp-2.4.7/guilog/wrapper/About.py --- gimp-2.4.7/guilog/wrapper/About.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/About.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,71 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import time +import Utils +import Config +import UserPrefs + +class About: + def __init__(self, window, prefs): + self.prefs = prefs + window.transient() + self._build_UI(window) + def _build_UI(self, window): + self.window = window + window.protocol('WM_DELETE_WINDOW', self._handle_ok) + window.title("About: ingimp") + window.resizable(width=0, height=0) + + self.logo_image = Utils.get_logo_image() + if self.logo_image: + logo = tk.Label(window, image=self.logo_image) + else: + logo = tk.Label(window, text="ingimp") + + version_label = tk.Label(window, text='Version: '+Config.version) + user_label = tk.Label(window, text='ID: '+self.prefs.user_id) + support_label = tk.Label(window, text='ingimp@cs.uwaterloo.ca') + space1 = tk.Label(window, text=' ') + ok_button = tk.Button(window, command=self._handle_ok, text="OK", padx=40) + + logo.grid(row=0, column=0, rowspan=2) + version_label.grid(row=2, column=0) + user_label.grid(row=3, column=0) + support_label.grid(row=4, column=0) + space1.grid(row=5, column=0) + ok_button.grid(row=6, column=0) + ok_button.focus_set() + self.window.bind("", self._handle_ok) + + def show(self): + Utils.center_window(self.window) + self.window.focus_set() + self.window.mainloop() + self.window.destroy() + def _handle_ok(self, *args): + self.window.withdraw() + self.window.quit() + +if __name__ == '__main__': + prefs = UserPrefs.UserPrefs() + about = About(tk.Tk(), prefs) + about.show() + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Config.py ingimp-2.4.7/guilog/wrapper/Config.py --- gimp-2.4.7/guilog/wrapper/Config.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Config.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,39 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +''' +Configuration data for ingimp. See the prefs file in ~/.ingimp for +user-customizable stuff. +''' +import sys + +version = '2008.04.16' + +wrapper_dir = None +bin_dir = None +logo_filename = None +launch_button_filename = None +webstats_button_filename = None +divider_line_filename = None +consent_filename = None +info_logo_filename = None +pictogram_filenames = [] +gimp_bin = 'ingimp-2.4' +if sys.platform == 'win32': + gimp_bin = gimp_bin + '.exe' diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Config.py.bak ingimp-2.4.7/guilog/wrapper/Config.py.bak --- gimp-2.4.7/guilog/wrapper/Config.py.bak 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Config.py.bak 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,39 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +''' +Configuration data for ingimp. See the prefs file in ~/.ingimp for +user-customizable stuff. +''' +import sys + +version = '2007.09.08' + +wrapper_dir = None +bin_dir = None +logo_filename = None +launch_button_filename = None +webstats_button_filename = None +divider_line_filename = None +consent_filename = None +info_logo_filename = None +pictogram_filenames = [] +gimp_bin = 'ingimp-2.2' +if sys.platform == 'win32': + gimp_bin = gimp_bin + '.exe' diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/ConsentDialog.py.in ingimp-2.4.7/guilog/wrapper/ConsentDialog.py.in --- gimp-2.4.7/guilog/wrapper/ConsentDialog.py.in 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/ConsentDialog.py.in 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,143 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import tkFont +import tkMessageBox +import Utils +import sys +import time + +class ConsentDialog: + def __init__(self, window, consent_filename, include_prev_button): + self.consent_granted = False + self.dwell_time = 0 + self.max_scroll_value = 0 + self.user_cancelled = True + self.user_closed_window = False + self._build_UI(window, consent_filename, include_prev_button) + def _build_UI(self, window, consent_filename, include_prev_button): + self.window = window + window.protocol('WM_DELETE_WINDOW', self._handle_close) + window.title("ingimp Consent") + + self.logo_image = Utils.get_logo_image() + if self.logo_image: + logo = tk.Label(window, image=self.logo_image) + else: + logo = tk.Label(window, text="ingimp") + read_label = tk.Label(window, text="Please read carefully. This is NOT a license agreement") + + try: + f = open(consent_filename) + consent_text = f.read() + f.close() + except: + tkMessageBox.showerror("Installation Error", "Could not find consent document. Exiting.") + sys.exit() + text_panel = tk.Frame(window) + text_box = tk.Text(text_panel) + text_box.config(wrap=tk.WORD) + text_box.insert(tk.END, consent_text) + start_index = text_box.search("You must be 18", "1.0", stopindex=tk.END) + stop_index = text_box.search(". Participation is", "1.0", stopindex=tk.END) + bold_tag = "bold" + text_box.tag_config(bold_tag, underline=1) + text_box.tag_add(bold_tag, start_index, stop_index) + + scrollbar = tk.Scrollbar(text_panel) + scrollbar.config(command=self._handle_scroll) + text_box.config(yscrollcommand=scrollbar.set, state=tk.DISABLED) + text_box.grid(column=0, rowspan=3, columnspan=3, sticky='NWSE') + scrollbar.grid(row=0, column=3, rowspan=3, sticky='NSE') + self.scrollbar = scrollbar + self.text_box = text_box + + radio_panel = tk.Frame(window) + self.radio_var = tk.IntVar(0) + accept_radio = tk.Radiobutton(radio_panel, command=self._handle_radio, variable=self.radio_var, value=1, text="I agree to participate in this study") + reject_radio = tk.Radiobutton(radio_panel, command=self._handle_radio, variable=self.radio_var, value=2, text="I do NOT agree to participate in this study") + accept_radio.grid(row=0, column=0, sticky=tk.W) + reject_radio.grid(row=1, column=0, sticky=tk.W) + self.accept_radio = accept_radio + self.reject_radio = reject_radio + + ok_text = "OK" + cancel_text = "Cancel" + if include_prev_button: + ok_text = "Next" + cancel_text = "Previous" + ok_button = tk.Button(window, command=self._handle_ok, text=ok_text) + ok_button.config(padx=40) + ok_button.config(state=tk.DISABLED) + self.ok_button = ok_button + cancel_button = tk.Button(window, command=self._handle_cancel, text=cancel_text) + cancel_button.config(padx=40) + self.cancel_button = cancel_button + + logo.grid(row=1, column=0, rowspan=2, sticky=tk.N) + read_label.grid(row=0, column=1, sticky=tk.W) + text_panel.grid(row=1, column=1, rowspan=2, columnspan=2, sticky='NWSE') + radio_panel.grid(row=3, column=1, rowspan=2, sticky=tk.W) + cancel_button.grid(row=5, column=0, sticky=tk.W, padx=20, pady=20) + ok_button.grid(row=5, column=2, sticky=tk.E, padx=20, pady=20) + window.grid_columnconfigure(1, weight=1) + window.grid_rowconfigure(1, weight=1) + text_panel.grid_columnconfigure(0, weight=1) + text_panel.grid_rowconfigure(0, weight=1) + def _handle_scroll(self, mode, *args): + if mode == 'moveto': + self.text_box.yview(mode, args[0]) + else: + self.text_box.yview(mode, args[0], args[1]) + self.max_scroll_value = max(self.max_scroll_value, self.scrollbar.get()[1]) + def _handle_ok(self): + self.consent_granted = (self.radio_var.get() == 1) + self.user_cancelled = False + self.window.withdraw() + self.window.quit() + def _handle_cancel(self): + self.window.withdraw() + self.window.quit() + def _handle_close(self): + self.user_closed_window = True + self.window.withdraw() + self.window.quit() + def _handle_radio(self): + self.ok_button.config(state=tk.NORMAL) + self.ok_button.focus_set() + def show(self): + self.radio_var = tk.IntVar(0) + self.accept_radio.config(variable=self.radio_var) + self.reject_radio.config(variable=self.radio_var) + self.ok_button.config(state=tk.DISABLED) + self.window.state("normal") + self.window.lift() + Utils.center_window(self.window) + start_time = time.time() + self.window.mainloop() + self.dwell_time = self.dwell_time + (time.time() - start_time) + return (self.consent_granted, self.max_scroll_value, self.dwell_time) + def destroy(self): + self.window.destroy() + +if __name__ == '__main__': + # No need to use automake on files below; these are only for testing + consent_dialog = ConsentDialog(tk.Tk(), r'c:\ingimp-2.2.17\share\ingimp\consent.txt', True) + print consent_dialog.show() diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/FirstTimeHandler.py ingimp-2.4.7/guilog/wrapper/FirstTimeHandler.py --- gimp-2.4.7/guilog/wrapper/FirstTimeHandler.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/FirstTimeHandler.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,71 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry + +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. +''' + +import Tkinter as tk +import tkFont +import tkMessageBox +import Utils +import sys +import random + +from ConsentDialog import ConsentDialog +from Pictoviewer import Pictoviewer + +VIEW_ORDER_PICTO_CONSENT = 1 +VIEW_ORDER_CONSENT_PICTO = 2 +VIEW_ORDER_CONSENT = 3 + +class FirstTimeHandler: + def __init__(self, consent_filename, info_logo_filename, image_filenames): + self.consent_filename = consent_filename + self.info_logo_filename = info_logo_filename + self.image_filenames = image_filenames + self.view_order = random.randint(1,3) +# self.view_order = 1 + def get_consent(self): + w1 = tk.Toplevel() + w1.withdraw() + w2 = tk.Toplevel() + w2.withdraw() + consent_dialog = ConsentDialog(w1, self.consent_filename, (self.view_order == VIEW_ORDER_PICTO_CONSENT)) + pictoviewer = Pictoviewer(w2, self.info_logo_filename, self.image_filenames) + if self.view_order == VIEW_ORDER_PICTO_CONSENT: + done = False + while not done: + pictoviewer.show() + (consent_granted, consent_max_scroll_value, consent_dwell_time) = consent_dialog.show() + if consent_granted or consent_dialog.user_closed_window: + done = True + else: + (consent_granted, consent_max_scroll_value, consent_dwell_time) = consent_dialog.show() + if consent_granted and self.view_order == VIEW_ORDER_CONSENT_PICTO: + pictoviewer.show() + pictoviewer.destroy() + consent_dialog.destroy() + return (consent_granted, consent_dialog.user_closed_window, self.view_order, [consent_dwell_time] + pictoviewer.dwell_times, [consent_max_scroll_value] + pictoviewer.max_scroll_values, pictoviewer.user_view_order) + +if __name__ == '__main__': + # No need to use automake on these; these are only for testing + consent_file = r'c:\ingimp-2.2.17\share\ingimp\consent.txt' + image_files = [r'c:\data\ingimp-2.2.17\guilog\wrapper\images\picto_image.gif', + r'c:\data\ingimp-2.2.17\guilog\wrapper\images\picto_image-2.gif', + r'c:\data\ingimp-2.2.17\guilog\wrapper\images\picto_image-3.gif',] + handler = FirstTimeHandler(consent_file, image_files) + print handler.get_consent() + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/FlatFilerMaker.py ingimp-2.4.7/guilog/wrapper/FlatFilerMaker.py --- gimp-2.4.7/guilog/wrapper/FlatFilerMaker.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/FlatFilerMaker.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,88 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import Utils +import Config +import UserPrefs +import os +import sys +sys.path.append('../website/parsing') +from guilog_parser import InteractionLog + +class FlatFileMaker: + def __init__(self, window, prefs): + self.prefs = prefs + self._build_UI(window) + def _build_UI(self, window): + self.window = window + window.protocol('WM_DELETE_WINDOW', self._handle_cancel) + window.title("ingimp flat file maker") + + label = tk.Label(window, text="Flattens log files and places them in the chosen directory") +# label.grid(row=0, column=0, sticky=tk.W) + label.pack() + frame = tk.Frame(window) + label = tk.Label(frame, text="Output directory:") + label.grid(row=1, column=0, sticky=tk.W) + self.entry = tk.Entry(frame) + self.entry.insert(tk.END, self.prefs.get(UserPrefs.LOG_DIR)) + self.entry.grid(row=1, column=1, sticky=tk.E) + frame.pack() + + frame = tk.Frame(window) + cancel_button = tk.Button(frame, command=self._handle_cancel, text="Cancel", padx=40) + cancel_button.grid(row=2, column=0, sticky=tk.W) + ok_button = tk.Button(frame, command=self._handle_ok, text="OK", padx=40) + ok_button.grid(row=2, column=1, sticky=tk.E) + ok_button.focus_set() + frame.pack() + self.window.bind("", self._handle_ok) + + def show(self): + Utils.center_window(self.window) + self.window.focus_set() + self.window.mainloop() + self.window.destroy() + def _handle_ok(self, *args): + self.window.withdraw() + self.window.quit() + log_dir = self.prefs.get(UserPrefs.LOG_DIR) + log_files = os.listdir(log_dir) + log_files = filter(lambda x: x.startswith(self.prefs.get(UserPrefs.UPLOADED_PREFIX)), log_files) + log_files = filter(lambda x: x.endswith('.xml'), log_files) + output_dir = self.entry.get() + if log_files: + for f in log_files: + f = os.path.join(log_dir, f) + new_file = os.path.basename(f)[:-4] + '.txt' + new_file = os.path.join(self.entry.get(), new_file) + new_file = open(new_file, 'w') + log = InteractionLog(f) + new_file.write(log.flatten()) + new_file.close() + def _handle_cancel(self): + self.window.withdraw() + self.window.quit() + +if __name__ == '__main__': + prefs = UserPrefs.UserPrefs() + about = FlatFileMaker(tk.Tk(), prefs) + about.show() + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/images/Makefile.am ingimp-2.4.7/guilog/wrapper/images/Makefile.am --- gimp-2.4.7/guilog/wrapper/images/Makefile.am 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/images/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,17 @@ +## Makefile.am for gimp/guilog/wrapper/images + +imagedir = $(datadir)/ingimp + +image_DATA = \ + ingimp_logo.gif \ + ingimp_info_logo.gif \ + webstats_button.gif \ + launch_button.gif \ + divider_line.gif \ + pictogram-0.gif \ + pictogram-1.gif \ + pictogram-2.gif \ + pictogram-3.gif \ + pictogram-4.gif + +EXTRA_DIST = $(image_DATA) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/ingimp-helper.py ingimp-2.4.7/guilog/wrapper/ingimp-helper.py --- gimp-2.4.7/guilog/wrapper/ingimp-helper.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/ingimp-helper.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,49 @@ +#! /usr/bin/env python + +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry + +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. +''' + +''' +This is a helper app for ingimp. Currently, it retrieves URLs to make +the URL plug-in work for the Windows version of the GIMP. +''' + +import urllib +import sys + +def retrieve_file(uri, local_file): + try: + remote_file = urllib.urlopen(uri) + local_file = open(local_file, "wb") + for line in remote_file.readlines(): + local_file.write(line) + remote_file.close() + local_file.close() + except Exception, e: + print "Error in retrieve_file:", str(e) + +if __name__ == '__main__': + for argnum, arg in zip(range(len(sys.argv)), sys.argv): + if arg == "--retrieve": + remote_file = sys.argv[argnum+1] + # Temporary path can have spaces in it, so we need to assume + # every argument after the remote file is part of the local + # file name. Hence, we add spaces + local_file = reduce(lambda x,y: x+" "+y, sys.argv[argnum+2:]) + retrieve_file(remote_file, local_file) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/ingimp.in ingimp-2.4.7/guilog/wrapper/ingimp.in --- gimp-2.4.7/guilog/wrapper/ingimp.in 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/ingimp.in 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,65 @@ +#! /usr/bin/env python + +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry + +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. +''' + +try: + import sys + sys.path.append('.') + + import os + this_dir = os.path.dirname(os.path.abspath(os.path.realpath(sys.argv[0]))) + if sys.platform == 'win32': + wrapper_dir = os.path.abspath(os.path.join(this_dir, '../lib', 'ingimp')) + else: + wrapper_dir = os.path.join('@libdir@', 'ingimp') + + sys.path.append(wrapper_dir) + + import Config + if sys.platform == 'win32': + Config.bin_dir = this_dir + else: + Config.bin_dir = '@bindir@' + Config.wrapper_dir = wrapper_dir + Config.ingimp_helper = os.path.join(Config.bin_dir, 'ingimp-helper') + if sys.platform == 'win32': + Config.ingimp_helper = Config.ingimp_helper + '.exe' + if sys.platform == 'win32': + datadir = os.path.abspath(os.path.join(this_dir, '../share')) + else: + datadir = '@datadir@' + Config.logo_filename = os.path.join(datadir, 'ingimp', 'ingimp_logo.gif') + Config.info_logo_filename = os.path.join(datadir, 'ingimp', 'ingimp_info_logo.gif') + Config.webstats_button_filename = os.path.join(datadir, 'ingimp', 'webstats_button.gif') + Config.launch_button_filename = os.path.join(datadir, 'ingimp', 'launch_button.gif') + Config.divider_line_filename = os.path.join(datadir, 'ingimp', 'divider_line.gif') + Config.consent_filename = os.path.join(datadir, 'ingimp', 'consent.txt') + Config.pictogram_filenames = [] + for i in range(5): + this_pictogram = os.path.join(datadir, 'ingimp', 'pictogram-%d.gif'%i) + Config.pictogram_filenames.append(this_pictogram) + + import Main + Main.Main() +except SystemExit: + pass +except Exception, e: + import tkMessageBox + tkMessageBox.showerror("Launch Error", "Unexpected ingimp error: " + str(e)) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Launcher.py ingimp-2.4.7/guilog/wrapper/Launcher.py --- gimp-2.4.7/guilog/wrapper/Launcher.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Launcher.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,170 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import os +import Config +import UserPrefs +import time +import tkMessageBox +import sys +import platform +from threading import Thread + +GIMP_GUI_LOG_FILE = 'GIMP_GUI_LOG_FILE' +GIMP_GUI_LOG_FILE_NAME = 'GIMP_GUI_LOG_FILE_NAME' # Is uploaded to uniquely ID log +GIMP_GUI_LOG_USER_ID = 'GIMP_GUI_LOG_USER_ID' +GIMP_GUI_LOG_TIMEZONE = 'GIMP_GUI_LOG_TIMEZONE' +GIMP_GUI_LOG_HEADER_FILE = 'GIMP_GUI_LOG_HEADER_FILE' +GIMP_GUI_LOG_WRAPPER_VERSION = 'GIMP_GUI_LOG_WRAPPER_VERSION' +GIMP_GUI_LOG_PLATFORM = 'GIMP_GUI_LOG_PLATFORM' +GIMP_GUI_LOG_PLATFORM_SYSTEM = 'GIMP_GUI_LOG_PLATFORM_SYSTEM' +GIMP_GUI_LOG_PLATFORM_RELEASE = 'GIMP_GUI_LOG_PLATFORM_RELEASE' +GIMP_GUI_LOG_PLATFORM_VERSION = 'GIMP_GUI_LOG_PLATFORM_VERSION' +GIMP_GUI_LOG_PLATFORM_MACHINE = 'GIMP_GUI_LOG_PLATFORM_MACHINE' +GIMP_GUI_LOG_PLATFORM_PROCESSOR = 'GIMP_GUI_LOG_PLATFORM_PROCESSOR' +GIMP_GUI_LOG_COMPRESS_LOG = 'GIMP_GUI_LOG_COMPRESS_LOG' +GIMP_GUI_LOG_HELPER = 'GIMP_GUI_LOG_HELPER' +GIMP_GUI_LOG_TEST_URL_PREFIX = 'GIMP_GUI_LOG_TEST_URL_PREFIX' +GIMP_GUI_LOG_LOG_KEY_ACTIVITY = 'GIMP_GUI_LOG_LOG_KEY_ACTIVITY' +GIMP_GUI_LOG_LOG_BUTTON_ACTIVITY = 'GIMP_GUI_LOG_LOG_BUTTON_ACTIVITY' +GIMP_GUI_LOG_SESSION_TAGS = 'GIMP_GUI_LOG_SESSION_TAGS' +GIMP_GUI_LOG_HISTOGRAM_TIMEOUT = 'GIMP_GUI_LOG_HISTOGRAM_TIMEOUT' +GIMP_GUI_LOG_NUM_DISABLED_RUNS = 'GIMP_GUI_LOG_NUM_DISABLED_RUNS' +GIMP_GUI_LOG_CONSENT_VIEW_ORDER = 'GIMP_GUI_LOG_CONSENT_VIEW_ORDER' +GIMP_GUI_LOG_CONSENT_DWELL_TIMES = 'GIMP_GUI_LOG_CONSENT_DWELL_TIMES' +GIMP_GUI_LOG_CONSENT_MAX_SCROLL_VALUES = 'GIMP_GUI_LOG_CONSENT_MAX_SCROLL_VALUES' +GIMP_GUI_LOG_CONSENT_USER_VIEW_ORDER = 'GIMP_GUI_LOG_CONSENT_USER_VIEW_ORDER' +GIMP_GUI_LOG_ONE_TIME_PAD_FILE = 'GIMP_GUI_LOG_ONE_TIME_PAD_FILE' + + +class Launcher(Thread): + def __init__(self, prefs, view_order, dwell_times, max_scroll_values, user_view_order, done_callback): + Thread.__init__(self) + self.prefs = prefs + self.view_order = view_order + self.dwell_times = dwell_times + self.max_scroll_values = max_scroll_values + self.user_view_order = user_view_order + self._done_callback = done_callback + + def spawn(self, logging_enabled): + self.logging_enabled = logging_enabled + # Always set the helper app or else File->Open Location will fail + os.environ[GIMP_GUI_LOG_HELPER] = Config.ingimp_helper + if self.logging_enabled and not self.prefs.have_killswitch: + log_dir = self.prefs.get(UserPrefs.LOG_DIR) + logfile_prefix = self.prefs.get(UserPrefs.LOGFILE_PREFIX) + log_header_file = self.prefs.get(UserPrefs.LOG_HEADER_FILE) + if not os.path.exists(log_dir): + try: + os.mkdir(log_dir) + except: + tkMessageBox.showerror("Log Error", "Could not create log directory at " + log_dir + ".") + date_str = time.strftime( '%Y%m%d_%H%M%S' ) + log_file_name = self.prefs.user_id + '_' + date_str + log_file = os.path.join(log_dir, logfile_prefix + log_file_name + '.xml') + if self.prefs.get(UserPrefs.COMPRESS_LOG) == '1': + log_file = log_file + '.gz' + # Set environment variables to pass to the GIMP executable + os.environ[GIMP_GUI_LOG_FILE] = os.path.abspath(log_file) + os.environ[GIMP_GUI_LOG_FILE_NAME] = log_file_name + os.environ[GIMP_GUI_LOG_USER_ID] = str(self.prefs.user_id) + os.environ[GIMP_GUI_LOG_TIMEZONE] = str(time.timezone) + os.environ[GIMP_GUI_LOG_WRAPPER_VERSION] = str(Config.version) + os.environ[GIMP_GUI_LOG_PLATFORM] = sys.platform + os.environ[GIMP_GUI_LOG_PLATFORM_SYSTEM] = platform.system() + os.environ[GIMP_GUI_LOG_PLATFORM_RELEASE] = platform.release() + os.environ[GIMP_GUI_LOG_PLATFORM_VERSION] = platform.version() + os.environ[GIMP_GUI_LOG_PLATFORM_MACHINE] = platform.machine() + os.environ[GIMP_GUI_LOG_PLATFORM_PROCESSOR] = platform.processor() + os.environ[GIMP_GUI_LOG_TEST_URL_PREFIX] = self.prefs.get(UserPrefs.TEST_URL_PREFIX) + os.environ[GIMP_GUI_LOG_COMPRESS_LOG] = self.prefs.get(UserPrefs.COMPRESS_LOG) + os.environ[GIMP_GUI_LOG_LOG_KEY_ACTIVITY] = self.prefs.get(UserPrefs.LOG_KEY_ACTIVITY) + os.environ[GIMP_GUI_LOG_LOG_BUTTON_ACTIVITY] = self.prefs.get(UserPrefs.LOG_BUTTON_ACTIVITY) + os.environ[GIMP_GUI_LOG_SESSION_TAGS] = self.prefs.get(UserPrefs.SESSION_TAGS) + os.environ[GIMP_GUI_LOG_HISTOGRAM_TIMEOUT] = self.prefs.get(UserPrefs.HISTOGRAM_TIMEOUT) + os.environ[GIMP_GUI_LOG_NUM_DISABLED_RUNS] = self.prefs.get(UserPrefs.NUM_DISABLED_RUNS) + os.environ[GIMP_GUI_LOG_CONSENT_VIEW_ORDER] = str(self.view_order) + os.environ[GIMP_GUI_LOG_CONSENT_DWELL_TIMES] = '|'.join(map(str, self.dwell_times)) + os.environ[GIMP_GUI_LOG_CONSENT_MAX_SCROLL_VALUES] = '|'.join(map(str, self.max_scroll_values)) + os.environ[GIMP_GUI_LOG_CONSENT_USER_VIEW_ORDER] = '|'.join(map(str, self.user_view_order)) + os.environ[GIMP_GUI_LOG_ONE_TIME_PAD_FILE] = self.prefs.get_one_time_pad_file() + if log_header_file: + os.environ[GIMP_GUI_LOG_HEADER_FILE] = os.path.abspath(log_header_file) + + # Now spawn the thread to start us up + self.start() + + def run(self): + result = False + try: + # We need to set the PATH so dll's can be found in Windows (and potentialy Unix) + # We used to cd into the bin directory and run from there, but that screws up + # the ability to pass in file names on the command line + old_path = os.environ["PATH"] + os.environ["PATH"] = Config.bin_dir + os.path.pathsep + old_path + gimp_bin = os.path.join(Config.bin_dir, Config.gimp_bin) + if sys.platform == 'win32': + self.__pid = os.spawnv(os.P_WAIT, gimp_bin, sys.argv[1:]) + else: + self.__pid = os.spawnv(os.P_WAIT, gimp_bin, [gimp_bin] + sys.argv[1:]) + except Exception, e: + tkMessageBox.showerror("Launch Error", "Could not launch the GIMP.") + else: + result = True + os.environ["PATH"] = old_path + self._clean_environ() + self._done_callback(result) + + def _clean_environ(self): + for var in [GIMP_GUI_LOG_FILE, + GIMP_GUI_LOG_FILE_NAME, + GIMP_GUI_LOG_USER_ID, + GIMP_GUI_LOG_TIMEZONE, + GIMP_GUI_LOG_HEADER_FILE, + GIMP_GUI_LOG_WRAPPER_VERSION, + GIMP_GUI_LOG_PLATFORM, + GIMP_GUI_LOG_PLATFORM_SYSTEM, + GIMP_GUI_LOG_PLATFORM_RELEASE, + GIMP_GUI_LOG_PLATFORM_VERSION, + GIMP_GUI_LOG_PLATFORM_MACHINE, + GIMP_GUI_LOG_PLATFORM_PROCESSOR, + GIMP_GUI_LOG_HELPER, + GIMP_GUI_LOG_TEST_URL_PREFIX, + GIMP_GUI_LOG_COMPRESS_LOG, + GIMP_GUI_LOG_LOG_KEY_ACTIVITY, + GIMP_GUI_LOG_LOG_BUTTON_ACTIVITY, + GIMP_GUI_LOG_SESSION_TAGS, + GIMP_GUI_LOG_HISTOGRAM_TIMEOUT, + GIMP_GUI_LOG_NUM_DISABLED_RUNS, + GIMP_GUI_LOG_CONSENT_VIEW_ORDER, + GIMP_GUI_LOG_CONSENT_DWELL_TIMES, + GIMP_GUI_LOG_CONSENT_MAX_SCROLL_VALUES, + GIMP_GUI_LOG_CONSENT_USER_VIEW_ORDER, + GIMP_GUI_LOG_ONE_TIME_PAD_FILE, + ]: + if var in os.environ: + del os.environ[var] + +if __name__ == '__main__': + import UserPrefs + prefs = UserPrefs.UserPrefs() + launcher = Launcher(prefs) + launcher.spawn(True) + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/MacLib.py ingimp-2.4.7/guilog/wrapper/MacLib.py --- gimp-2.4.7/guilog/wrapper/MacLib.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/MacLib.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,86 @@ +#! /usr/bin/env python + +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +''' +This is a helper app for ingimp. Currently, it will give mac specific +information, such as if X11 exists, if were on a mac system, and +the size of the dock. +''' + +import sys +import os +import pwd +import commands +import webbrowser + +import Tkinter as tk +import MacX11Screen +import UserPrefs + +def x11_fail_display(): + macx11screen = MacX11Screen.create_screen() + macx11screen.show_dialog() + +def is_mac(): + if sys.platform == 'darwin': + return True + return False + +def x11_installed(): + return os.path.isdir('/Applications/Utilities/X11.app') + +def dock_size(): + dock_size = 60 + + # Get the users home directory and location of dock config file + # To my knowledge there is no other place this would be + user_data = pwd.getpwuid(os.getuid()) + user_dock_pref_path = user_data[5] + '/Library/Preferences/com.apple.dock.plist' + + # Possible commands (see below for further explanation) + binary_command = 'plutil -convert xml1 ' + user_dock_pref_path + ' -o /dev/stdout' + reg_command = 'cat ' + user_dock_pref_path + extract_pipe = ' | grep -A 1 tilesize | egrep -o "[0-9]+"' + command = reg_command + extract_pipe + + # Check if the dock file exists, if not assume default value + if not(os.path.isfile(user_dock_pref_path)): return dock_size + + # Check to see if plist file is binary format + # All plist files are text, but as text files are large + # Apple, as of Mac OS X 10.2 introduced binary formatted version + # As of 10.4 this is the default plist type for many applications, + # but not all + if is_binary(user_dock_pref_path): + command = binary_command + extract_pipe + + ret_dock_data = commands.getstatusoutput(command) + + if ret_dock_data[0] == 0: + dock_size = ret_dock_data[1] + return dock_size + +def is_binary(path): + check_command = 'cat ' + path + ' | head -n 1 | grep -o "^bplist"' + ret_data = commands.getstatusoutput(check_command) + if ret_data[0] == 0: + return True + return False diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/MacX11Screen.py ingimp-2.4.7/guilog/wrapper/MacX11Screen.py --- gimp-2.4.7/guilog/wrapper/MacX11Screen.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/MacX11Screen.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,77 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import Utils +import webbrowser +import tkMessageBox +import UserPrefs + +def create_screen(): + macx11screen = MacX11Screen(tk.Tk(), UserPrefs.UserPrefs()) + return macx11screen + +class MacX11Screen: + def __init__(self, window, prefs): + self.window = window + self.prefs = prefs + self.x11_support_url = 'http://www.apple.com/support/downloads/x11formacosx.html' + self.message_text = "Sorry, it appears that X11 is not properly installed" + self.find_text = "You can find a copy of X11 on your Installation DVDs" + self.help_text = "You can also go to the Apple X11 Support Site" + + def _build_UI(self): + self.window.protocol('WM_DELETE_WINDOW', self._handle_quit) + self.window.title("ingimp") + self.window.resizable(width=0, height=0) + + message_label = tk.Label(self.window, text=self.message_text) + message_label.pack() + + find_label = tk.Label(self.window, text=self.find_text) + find_label.pack() + + help_label = tk.Label(self.window, text=self.help_text) + help_label.pack() + + box = tk.Frame(self.window) + web_button = tk.Button(box, text="Go to X11 Apple Support Page", width=30, command=self._handle_web, default=tk.ACTIVE) + web_button.pack() + + quit_button = tk.Button(box, text="Quit", width=10, command=self._handle_quit) + quit_button.pack() + + box.pack() + self.window.bind("", self._handle_web) + self.window.bind("", self._handle_quit) + def _handle_web(self, *args): + webbrowser.open_new(self.x11_support_url) + self._handle_quit() + def _handle_quit(self, *args): + self.window.quit() + def _show_dialog(self): + self._build_UI() + Utils.center_window(self.window) + self.window.mainloop() + self.window.destroy() + def show_dialog(self): + self._show_dialog() +if __name__ == '__main__': + macx11screen = MacX11Screen(tk.Tk(), UserPrefs.UserPrefs()) + macx11screen.show_dialog() \ Pas de fin de ligne à la fin du fichier. diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Main.py ingimp-2.4.7/guilog/wrapper/Main.py --- gimp-2.4.7/guilog/wrapper/Main.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Main.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,110 @@ +#! /usr/bin/env python + +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import sys +import UserPrefs +from Pictoviewer import Pictoviewer +from FirstTimeHandler import FirstTimeHandler +import SplashScreen +import Config +import Launcher +import Tkinter as tk +import Participation +import tkMessageBox +import Uploader + +class Main: + def __init__(self): + self.done = False + self.result = False + self.main() + + def main(self): + self.root_win = tk.Tk() + self.root_win.withdraw() + try: + #Get the default user and configuration + prefs = UserPrefs.UserPrefs() + #show the dialog box if this is the first time + logging_enabled = False + consent_refused = False + startup_cancelled = False + view_order = 0 + dwell_times = [] + max_scroll_values = [] + user_view_order = [] + if not prefs.have_killswitch: + if not prefs.have_consent: + handler = FirstTimeHandler(Config.consent_filename, Config.info_logo_filename, Config.pictogram_filenames) + (got_consent, user_closed_window, view_order, dwell_times, max_scroll_values, user_view_order) = handler.get_consent() + if got_consent: + prefs.set_have_consent() + else: + consent_refused = True + if user_closed_window: + startup_cancelled = True + elif not prefs.shown_pictograms: + picto_viewer = Pictoviewer(tk.Toplevel(), Config.info_logo_filename, Config.pictogram_filenames) + picto_viewer.show() + picto_viewer.destroy() + dwell_times = picto_viewer.dwell_times + max_scroll_values = picto_viewer.max_scroll_values + user_view_order = picto_viewer.user_view_order + if prefs.have_consent and not startup_cancelled: + splash = SplashScreen.SplashScreen(tk.Toplevel(), prefs) + (startup_cancelled, logging_enabled) = splash.show() + if not startup_cancelled: + if consent_refused: + tkMessageBox.showwarning("Logging disabled", "You did not give your consent. Your usage data will NOT be reported until consent is given.") + launcher = Launcher.Launcher(prefs, view_order, dwell_times, max_scroll_values, user_view_order, self._done_callback) + launcher.spawn(logging_enabled) + + # Enter root window's mainloop to handle updates while the GIMP is running + # If we don't do this, then things in Window can hang for a little while + self.root_win.after(1000, self._done_check) + self.root_win.mainloop() + + if self.result: # App launched and quit successfully + if logging_enabled: + print "Uploading collected data..." + uploader = Uploader.Uploader(prefs) + uploader.upload() + print "Finished uploading." + + # Check for participation after quitting + if not prefs.have_killswitch: + participate = Participation.Participation(tk.Toplevel(), prefs) + participate.check_for_participation() + except Exception, e: + tkMessageBox.showerror("ingimp Error", "ingimp Error: " + str(e)) + self.root_win.destroy() + sys.exit() + def _done_callback(self, result): + self.done = True + self.result = result + def _done_check(self): + if not self.done: + self.root_win.after(1000, self._done_check) + else: + self.root_win.quit() + +if __name__ == '__main__': + Main() diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Makefile.am ingimp-2.4.7/guilog/wrapper/Makefile.am --- gimp-2.4.7/guilog/wrapper/Makefile.am 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,44 @@ +## Makefile.am for gimp/guilog/wrapper + +SUBDIRS = images + +wrapperdir = $(libdir)/ingimp + +wrapper_SCRIPTS = \ + ingimp \ + About.py \ + Config.py \ + ConsentDialog.py \ + FirstTimeHandler.py \ + ingimp-helper.py \ + Launcher.py \ + Main.py \ + Participation.py \ + Pictoviewer.py \ + SplashScreen.py \ + Uploader.py \ + UploadDialog.py \ + UserPrefs.py \ + Utils.py + +EXTRA_DIST = $(wrapper_SCRIPTS) + +edit = sed \ + -e 's|@datadir[@]|$(datadir)|g' \ + -e 's|@bindir[@]|$(bindir)|g' \ + -e 's|@libdir[@]|$(libdir)|g' + +ingimp UploadDialog.py ConsentDialog.py: Makefile + rm -f $@ $@.tmp + $(edit) '$(srcdir)/$@.in' >$@.tmp + chmod +x $@.tmp + chmod a-w $@.tmp + mv $@.tmp $@ + +# For the following, we don't need to add $(EXEEXT) to the LN_S ingimp line +# because building the executable is currently a manual process on Windows +install-data-hook: + rm -f $(DESTDIR)$(bindir)/ingimp$(EXEEXT) + rm -f $(DESTDIR)$(bindir)/ingimp-helper$(EXEEXT) + $(LN_S) $(libdir)/ingimp/ingimp $(DESTDIR)$(bindir)/ingimp$(EXEEXT) + $(LN_S) $(libdir)/ingimp/ingimp-helper.py $(DESTDIR)$(bindir)/ingimp-helper$(EXEEXT) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Participation.py ingimp-2.4.7/guilog/wrapper/Participation.py --- gimp-2.4.7/guilog/wrapper/Participation.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Participation.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,88 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import os +import urllib +import urllib2 +import Tkinter as tk +import tkMessageBox +import webbrowser +import Config +import Utils +import UserPrefs + +class Participation: + def __init__(self, window, prefs): + self.window = window + self.prefs = prefs + def _build_UI(self): + self.window.protocol('WM_DELETE_WINDOW', self._handle_cancel) + self.window.title("ingimp") + self.window.resizable(width=0, height=0) + message_label = tk.Label(self.window, text=self.message_text) + message_label.pack(padx=5, pady=5) + + box = tk.Frame(self.window) + cancel_button = tk.Button(box, text="No", width=10, command=self._handle_cancel) + cancel_button.pack(side=tk.LEFT, padx=5, pady=5) + ok_button = tk.Button(box, text="Yes", width=10, command=self._handle_ok, default=tk.ACTIVE) + ok_button.pack(side=tk.LEFT, padx=5, pady=5) + box.pack() + + self.window.bind("", self._handle_ok) + self.window.bind("", self._handle_cancel) + def _handle_ok(self, *args): + webbrowser.open_new(self.interaction_url) + self._handle_cancel() + def _handle_cancel(self, *args): + self.window.withdraw() + self.window.quit() + def _show_dialog(self): + self._build_UI() + Utils.center_window(self.window) + self.window.mainloop() + self.window.destroy() + def check_for_participation(self): + participation_url = self.prefs.get(UserPrefs.PARTICIPATION_URL) + if participation_url: + url_to_open = Utils.get_redirect_url(participation_url) + if url_to_open: + url_to_open = url_to_open + '?' + urllib.urlencode([('id', self.prefs.user_id), ('version', Config.version)]) + try: + f = urllib2.urlopen(url_to_open) + proceed = f.readline().strip() + if (proceed == 'KILL'): + self.prefs.set_have_killswitch() + message_text = f.readline().strip() + tkMessageBox.showinfo("End of Study", message_text) + else: + proceed = (proceed == 'YES') + if proceed: + self.message_text = f.readline().strip() + self.interaction_url = f.readline().strip() + f.close() + if proceed: + if self.message_text and self.interaction_url: + self._show_dialog() + except Exception, e: + pass + +if __name__ == '__main__': + participate = Participation(tk.Tk(), UserPrefs.UserPrefs()) + participate.check_for_participation() diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Pictoviewer.py ingimp-2.4.7/guilog/wrapper/Pictoviewer.py --- gimp-2.4.7/guilog/wrapper/Pictoviewer.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Pictoviewer.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,177 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import tkFont +import tkMessageBox +import Utils +import sys +import os +import time +import re + +class Pictoviewer: + def __init__(self, window, info_logo_filename, image_filenames): + self.max_scroll_values = [0.0] * len(image_filenames) + self.dwell_times = [0.0] * len(image_filenames) + self.user_view_order = [] + self.start_time = 0 + self.min_canvas_width = 960 + self.min_canvas_height = 650 + self.cur_image = None + self.cur_image_num = 0 + self.cur_image_id = None + self.image_filenames = image_filenames + self._build_UI(window, info_logo_filename) + def _build_UI(self, window, info_logo_filename): + self.window = window + window.protocol('WM_DELETE_WINDOW', self._handle_close) + window.title("ingimp Overview") + + self.logo_image = tk.PhotoImage(file=info_logo_filename) + + if self.logo_image: + logo = tk.Label(window, image=self.logo_image) + else: + logo = tk.Label(window, text="ingimp") + + picto_panel = tk.Frame(window) + picto_canvas = tk.Canvas(picto_panel, width=self.min_canvas_width+100, height=self.min_canvas_height, bg='white', relief='sunken') + self.picto_panel = picto_panel + self.picto_canvas = picto_canvas + + ver_scrollbar = tk.Scrollbar(picto_panel) + hor_scrollbar = tk.Scrollbar(picto_panel, orient=tk.HORIZONTAL) + self.ver_scrollbar = ver_scrollbar + self.hor_scrollbar = hor_scrollbar + self.ver_scrollbar.config(command=self._handle_ver_scroll) + self.hor_scrollbar.config(command=self._handle_hor_scroll) + picto_canvas.config(yscrollcommand=ver_scrollbar.set, scrollregion=picto_canvas.bbox(tk.ALL)) + picto_canvas.config(xscrollcommand=hor_scrollbar.set, scrollregion=picto_canvas.bbox(tk.ALL)) + picto_canvas.grid(row=0, column=0, columnspan=3, rowspan=3, sticky='NSWE') + ver_scrollbar.grid(row=0, column=3, rowspan=3, sticky='NSE') + hor_scrollbar.grid(row=3, column=0, columnspan=3, sticky='SWE') + picto_canvas.yview_moveto(0.0) + picto_canvas.xview_moveto(0.0) + + next_button = tk.Button(window, command=self._handle_next, text="Next") + next_button.config(padx=40) + self.next_button = next_button + prev_button = tk.Button(window, command=self._handle_prev, text="Previous") + prev_button.config(padx=40) + self.prev_button = prev_button + + logo.grid(row=0, column=0, columnspan=3, sticky='NWE', pady=15) + picto_panel.grid(sticky='NSWE') + picto_panel.grid(row=2, column=0, columnspan=3, sticky='NWSE') + prev_button.grid(row=3, column=0, sticky=tk.W, padx=20, pady=20) + next_button.grid(row=3, column=2, sticky=tk.E, padx=20, pady=20) + + # These options ensure that the picto_panel and its main contents, the canvas, resize appropriately. + # These options are poorly documented and no doubt a cause for many hours of lost + # productivity trying to figure out why the grid geometry manager does not properly resize its contents + # when resizing a window. + # + picto_panel.grid_rowconfigure(0, weight=1) # Make picto_canvas resizable + picto_panel.grid_columnconfigure(0, weight=1) # Make picto_canvas resizable + window.grid_rowconfigure(2, weight=1) # Make picto_panel resizable + window.grid_columnconfigure(0, weight=1) # Make picto_panel resizable + + self._show_image(0) + def _load_image(self, image_num): + del self.cur_image + self.cur_image = tk.PhotoImage(file=self.image_filenames[image_num]) + if not self.cur_image: + tkMessageBox.showerror("Installation Error", "Could not find picto graphics. Try reinstalling. Exiting.") + sys.exit() + def _show_image(self, image_num): + if self.cur_image_id: + self.picto_canvas.delete(tk.ALL) + self.cur_image_id = None + self._update_dwell_time() + self.user_view_order.append(image_num) + self.start_time = time.time() + self._load_image(image_num) + x = 0 + if self.cur_image.width() < self.cur_image.height(): + x = (self.picto_panel.winfo_width()- self.cur_image.width())/2 + # It would be absolutely fantastic if Canvas had some sort of predictable logic for where it actually places an image on its + # canvas. Really. It would be *great*. But for now, the following code seems to be the only + # way to approximate centering of the image. Why draw the line? Without it, the canvas won't actually center + # the image. Why not? I have no idea. I defy you to tell me why I need to specify an anchor for the image and + # why I can't simply draw it *exactly where I want it*. This is an example of why Tk enjoys a miserable existence + # among other GUI toolkits and why Tk programmers are miserable themselves. + # Wait. I'm getting ahead of myself. This code *only* works after the window has been resized. AND, it only works + # then if the line has been drawn. Tkinter you are the bomb. You are Avril Lavigne to The Clash. + self.cur_image_id = self.picto_canvas.create_image((x,0), image=self.cur_image, anchor="w") + self.picto_canvas.create_line((0,0,0,0)) + self.cur_image_num = image_num + if self.cur_image_num > 0: + self.prev_button.config(state=tk.NORMAL) + else: + self.prev_button.config(state=tk.DISABLED) + self.picto_canvas.config(scrollregion=self.picto_canvas.bbox(tk.ALL)) + self.picto_canvas.yview_moveto(0.0) + self.picto_canvas.xview_moveto(0.0) + def _update_dwell_time(self): + now = time.time() + self.dwell_times[self.cur_image_num] = self.dwell_times[self.cur_image_num] + (now - self.start_time) + def _handle_ver_scroll(self, mode, *args): + if mode == 'moveto': + self.picto_canvas.yview(mode, args[0]) + else: + self.picto_canvas.yview(mode, args[0], args[1]) + self.max_scroll_values[self.cur_image_num] = max(self.max_scroll_values[self.cur_image_num], self.ver_scrollbar.get()[1]) + def _handle_hor_scroll(self, mode, *args): + if mode == 'moveto': + self.picto_canvas.xview(mode, args[0]) + else: + self.picto_canvas.xview(mode, args[0], args[1]) + self.max_scroll_values[self.cur_image_num] = max(self.max_scroll_values[self.cur_image_num], self.hor_scrollbar.get()[1]) + def _handle_next(self): + if (self.cur_image_num + 1) >= len(self.image_filenames): + self._handle_close() + else: + self._show_image(self.cur_image_num+1) + def _handle_prev(self): + if self.cur_image_num > 0: + self._show_image(self.cur_image_num-1) + def _handle_close(self): + self._update_dwell_time() + self.window.withdraw() + self.window.quit() + def show(self): + self.window.state("normal") + self.window.lift() + Utils.center_window(self.window) + self.window.mainloop() + def destroy(self): + self.window.destroy() + +if __name__ == '__main__': + picto_screen = Pictoviewer(tk.Tk(), r'c:\data\ingimp-2.2.17\guilog\wrapper\images\ingimp_info_logo.gif', + [r'c:\data\ingimp-2.2.17\guilog\wrapper\images\pictogram-0.gif', + r'c:\data\ingimp-2.2.17\guilog\wrapper\images\pictogram-1.gif', + r'c:\data\ingimp-2.2.17\guilog\wrapper\images\pictogram-2.gif', + r'c:\data\ingimp-2.2.17\guilog\wrapper\images\pictogram-3.gif', + r'c:\data\ingimp-2.2.17\guilog\wrapper\images\pictogram-4.gif',]) + picto_screen.show() + print picto_screen.dwell_times + print picto_screen.max_scroll_values + print picto_screen.user_view_order diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/SplashScreen.py ingimp-2.4.7/guilog/wrapper/SplashScreen.py --- gimp-2.4.7/guilog/wrapper/SplashScreen.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/SplashScreen.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,166 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import time +import Utils +import Config +import UserPrefs +import About +import webbrowser +import urllib +import tkMessageBox + +class SplashScreen: + def __init__(self, window, prefs): + self.autostart_cancelled = False + self.logging_enabled = True + self.startup_cancelled = True + self.prefs = prefs + self.autostart_delay_in_secs = int(self.prefs.get(UserPrefs.AUTOSTART_DELAY)) + self._build_UI(window) + def _build_UI(self, window): + self.window = window + window.protocol('WM_DELETE_WINDOW', self._handle_close) + window.title("ingimp") + window.resizable(width=0, height=0) + + self.logo_image = Utils.get_logo_image() + if self.logo_image: + logo = tk.Label(window, image=self.logo_image) +# logo = tk.Button(window, image=self.logo_image, command=self._handle_about) + else: + logo = tk.Button(window, text="ingimp", command=self._handle_about) + + space1 = tk.Label(window, text=' ') + + tag_frame = tk.Frame(window) + tag_label = tk.Label(tag_frame, text="Activity tags (logged):") + tag_entry = tk.Entry(tag_frame) + tag_description = tk.Label(tag_frame, text="Example: I will be doing...") + tag_example = tk.Label(tag_frame, text="image correction, graphic design, mashups, ...") + tag_label.grid(row=0, column=0, sticky=tk.W) + tag_entry.grid(row=0, column=1, sticky=tk.W+tk.E) + tag_description.grid(row=1, column=0, sticky=tk.W) + tag_example.grid(row=1, column=1) + tag_entry.insert(tk.END, self.prefs.get(UserPrefs.SESSION_TAGS)) + self.tag_entry = tag_entry + + bottom_frame = tk.Frame(window) + try: + self.webstats_image = tk.PhotoImage(file=Config.webstats_button_filename) + webstats_button = tk.Button(bottom_frame, image=self.webstats_image, text="website & stats", command=self._handle_webstats) + except: + webstats_button = tk.Button(bottom_frame, text="website & stats", command=self._handle_webstats) + + try: + self.launch_image = tk.PhotoImage(file=Config.launch_button_filename) + start_button = tk.Button(bottom_frame, image=self.launch_image, text="Start GIMP Now", command=self._handle_start_now) + except: + start_button = tk.Button(bottom_frame, text="Start GIMP Now", command=self._handle_start_now) + + divider_frame = tk.Frame(window) + try: + self.divider_line_image = tk.PhotoImage(file=Config.divider_line_filename) + divider_line = tk.Label(divider_frame, image=self.divider_line_image) + except Exception, e: + divider_line = tk.Label(divider_frame, text=' ') + divider_line.pack() + + webstats_button.pack(side=tk.LEFT, padx=5, pady=5) + start_button.pack(side=tk.RIGHT, padx=5, pady=5) + + version_label = tk.Label(window, text='Version: '+Config.version) + user_label = tk.Label(window, text='User: '+self.prefs.user_id) + support_label = tk.Label(window, text='ingimp@cs.uwaterloo.ca') + + self.logging_disabled_var = tk.IntVar() + logging_checkbox = tk.Checkbutton(window, variable=self.logging_disabled_var, command=self._stop_autostart, text="Disable application usage logging for this run", padx=10, pady=10) + logging_checkbox.deselect() + self.autostart_label = tk.Label(window, text="Automatically starting in %d seconds" % self.autostart_delay_in_secs, foreground='Black', pady=15) + + logo.grid(row=0, column=0, rowspan=2) + version_label.grid(row=2, column=0, pady=10) + space1.grid(row=3, column=0) + tag_frame.grid(row=4, column=0, padx=15, pady=10, sticky=tk.W+tk.E) + bottom_frame.grid(row=5, column=0, padx=15, sticky=tk.W+tk.E) + logging_checkbox.grid(row=6, column=0, sticky=tk.W) + + tag_entry.focus_set() + + tag_entry.bind("", self._handle_start_now) + self.window.bind("", self._handle_start_now) + + if self.autostart_delay_in_secs < 0: + self._stop_autostart() + + def show(self): + self.window.after(200, self._update_autostart_label) + self.start_time = time.time() + Utils.center_window(self.window) + self.window.mainloop() + self.window.destroy() + self.autostart_cancelled = True + self.logging_enabled = (self.logging_disabled_var.get() == 0) + if not self.logging_enabled: + cur_num = int(self.prefs.get(UserPrefs.NUM_DISABLED_RUNS)) + self.prefs.set(UserPrefs.NUM_DISABLED_RUNS, str(cur_num+1)) + return (self.startup_cancelled, self.logging_enabled) + def _handle_close(self): + self.window.withdraw() + self.window.quit() + def _handle_start_now(self, *args): + self.startup_cancelled = False + self.autostart_cancelled = True + self.window.withdraw() + self.prefs.set(UserPrefs.SESSION_TAGS, self.tag_entry.get()) + self.window.quit() + def _handle_about(self): + self._stop_autostart() + self.window.withdraw() + about = About.About(tk.Toplevel(self.window), self.prefs) + about.show() + self.window.deiconify() + def _handle_webstats(self): + self._stop_autostart() + webstats_url = self.prefs.get(UserPrefs.WEBSTATS_REDIRECT_URL) + webstats_url = Utils.get_redirect_url(webstats_url) + if webstats_url: + webstats_url = webstats_url + '?' + urllib.urlencode([('id', self.prefs.user_id), ('version', Config.version)]) + try: + webbrowser.open_new(webstats_url) + except Exception, e: + tkMessageBox.showerror("Web Error", "Could not open webstats url: "+str(e)) + def _stop_autostart(self): + self.autostart_cancelled = True + self.autostart_label.config(text="") + def _update_autostart_label(self): + if not self.autostart_cancelled: + new_time = self.autostart_delay_in_secs - int(time.time() - self.start_time) + if new_time <= 0: + self._handle_start_now() + else: + self.autostart_label.config(text="Automatically starting in %d seconds" % new_time) + self.window.after(200, self._update_autostart_label) + +if __name__ == '__main__': + prefs = UserPrefs.UserPrefs() + splash_screen = SplashScreen(tk.Tk(), prefs) + splash_screen.show() + print "Logging enabled:", splash_screen.logging_enabled, "Startup cancelled:", splash_screen.startup_cancelled diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/TODO ingimp-2.4.7/guilog/wrapper/TODO --- gimp-2.4.7/guilog/wrapper/TODO 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/TODO 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1 @@ +TODO Items diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/UploadDialog.py.in ingimp-2.4.7/guilog/wrapper/UploadDialog.py.in --- gimp-2.4.7/guilog/wrapper/UploadDialog.py.in 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/UploadDialog.py.in 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,80 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import time +import Utils + +class UploadDialog: + def __init__(self, window): + self.user_cancelled = False + self.done = False + self.cancel_time = 0 + self._build_UI(window) + def _build_UI(self, window): + self.window = window + window.protocol('WM_DELETE_WINDOW', self._handle_cancel) + window.title("ingimp") + window.resizable(width=0, height=0) + + self.logo_image = Utils.get_logo_image() + if self.logo_image: + logo = tk.Label(window, image=self.logo_image) + else: + logo = tk.Label(window, text="ingimp") + + self.percent_done = 0 + self.message_label = tk.Label(window, text="Use reporting 0% complete...") + self.cancel_button = tk.Button(window, command=self._handle_cancel, text="Cancel", padx=40) + + logo.grid(row=0, column=0, rowspan=3) + self.message_label.grid(row=1, column=1, sticky=tk.W) + self.cancel_button.grid(row=3, column=1, sticky=tk.E) + self.cancel_button.focus_set() + def show(self): + Utils.center_window(self.window) + self.window.after(1000, self._check_for_updates) + self.window.mainloop() + self.window.destroy() + def _check_for_updates(self): + if self.user_cancelled: + if (time.time() - self.cancel_time) > 5: + self.done = True + if not self.done: + self.window.after(1000, self._check_for_updates) + if not self.user_cancelled: + self.message_label.config(text="Use reporting %d%% complete" % self.percent_done) + else: + self.window.withdraw() + self.window.quit() + def _handle_cancel(self): + self.user_cancelled = True + self.message_label.config(text="Cancelling...") + self.cancel_button.config(state=tk.DISABLED) + self.cancel_time = time.time() + def set_current_file_num(self, cur_file_num, total_files): + if not self.user_cancelled: + self.percent_done = int(float(cur_file_num) / total_files * 100) + def close(self): + self.done = True + +if __name__ == '__main__': + dialog = UploadDialog(tk.Tk(), os.path.join('@datadir@', 'ingimp', 'ingimp_logo.gif')) + dialog.show() + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Uploader.py ingimp-2.4.7/guilog/wrapper/Uploader.py --- gimp-2.4.7/guilog/wrapper/Uploader.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Uploader.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,119 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import os +import shutil +import Config +import urllib +import urllib2 +import UploadDialog +import UserPrefs +import Utils +import Tkinter as tk +import tkMessageBox +import gzip +import StringIO +import base64 +from threading import Thread + +class Uploader(Thread): + def __init__(self, prefs): + Thread.__init__(self) + self.prefs = prefs + self.log_files = [] + def upload(self): + # Get all log files in the log directory + self.log_dir = self.prefs.get(UserPrefs.LOG_DIR) + self.logfile_prefix = self.prefs.get(UserPrefs.LOGFILE_PREFIX) + self.reporting_url = self.prefs.get(UserPrefs.REPORTING_URL) + self.redirect_url = self.prefs.get(UserPrefs.REDIRECT_URL) + self.keep_local_copy = int(self.prefs.get(UserPrefs.KEEP_LOCAL_COPY)) + self.uploaded_prefix = self.prefs.get(UserPrefs.UPLOADED_PREFIX) + + self.log_files = os.listdir(self.log_dir) + self.log_files = filter(lambda x: x.startswith(self.logfile_prefix), self.log_files) + if self.log_files: + # If no URL is available, we won't try to upload and will + # just leave the log files on disk + if not (self.reporting_url or self.redirect_url): + return + # Check to see if we need to get a redirected URL + if self.reporting_url: + self.server_url = self.reporting_url + else: + self.server_url = Utils.get_redirect_url(self.redirect_url) + if not self.server_url: + return + self.progress_dialog = UploadDialog.UploadDialog(tk.Toplevel()) + # Spawn ourselves as a thread then show the dialog + self.start() +# Don't show a progress dialog per our finding that it is likely to be confusing to users when they see it. +# Also, in most cases, it is difficult to cancel uploads in time and the semantics +# of "cancel" are unclear -- does it completely cancel upload of that log file? +# Or does it just cancel this one, single upload? +# self.progress_dialog.show() + def run(self): + for (this_log, file_num) in zip(self.log_files, range(len(self.log_files))): + if self.progress_dialog.user_cancelled: + self.progress_dialog.close() + return + try: + self.progress_dialog.set_current_file_num(file_num+1, len(self.log_files)+1) + fullname = os.path.join(self.log_dir, this_log) + source_file = None + if not fullname.endswith('.gz'): + string_file = StringIO.StringIO() + f = open(fullname) + gzip_file = gzip.GzipFile(fileobj=string_file, mode='wb') + line = f.readline() + while line: + gzip_file.write(line) + line = f.readline() + gzip_file.close() + f.close() + source_file = string_file + else: + f = open(fullname, 'rb') + source_file = f + source_file.seek(0) + dest_file = StringIO.StringIO() + base64.encode(source_file, dest_file) + source_file.close() + dest_file.seek(0) + request = urllib2.Request(self.server_url, dest_file.getvalue()) + connection = urllib2.urlopen(request) + if self.keep_local_copy: + newname = os.path.join(self.log_dir, self.uploaded_prefix+this_log) + try: + shutil.move(fullname, newname) + except Exception, e: + tkMessageBox.showerror("Upload Error", "Could not rename uploaded log file: "+str(e)) + else: + os.remove(fullname) + if connection: + connection.close() + except Exception, e: + tkMessageBox.showerror("Upload Error", "Could not upload files: "+str(e)) + break + self.progress_dialog.close() + +if __name__ == '__main__': + prefs = UserPrefs.UserPrefs() + u = Uploader(prefs) + u.upload() diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/UserPrefsEditor.py ingimp-2.4.7/guilog/wrapper/UserPrefsEditor.py --- gimp-2.4.7/guilog/wrapper/UserPrefsEditor.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/UserPrefsEditor.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,67 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import Tkinter as tk +import Utils +import Config +import UserPrefs + +class UserPrefsEditor: + def __init__(self, window, prefs): + self.prefs = prefs + self._build_UI(window) + def _build_UI(self, window): + self.window = window + window.protocol('WM_DELETE_WINDOW', self._handle_cancel) + window.title("ingimp prefs editor") + + row_num = 0 + self.pref_fields = {} + for pref_key in UserPrefs._default_user_prefs: + label = tk.Label(window, text=pref_key) + field = tk.Entry(window) + field.insert(tk.END, self.prefs.get(pref_key)) + label.grid(row=row_num, column=0) + field.grid(row=row_num, column=1, sticky=tk.W+tk.E) + row_num = row_num + 1 + self.pref_fields[pref_key] = field + ok_button = tk.Button(window, command=self._handle_ok, text="OK", padx=40) + ok_button.grid(row=row_num, column=1) + ok_button.focus_set() + self.window.bind("", self._handle_ok) + + def show(self): + Utils.center_window(self.window) + self.window.focus_set() + self.window.mainloop() + self.window.destroy() + def _handle_ok(self, *args): + for pref_key in UserPrefs._default_user_prefs: + self.prefs.set(pref_key, self.pref_fields[pref_key].get()) + self.window.withdraw() + self.window.quit() + def _handle_cancel(self): + self.window.withdraw() + self.window.quit() + +if __name__ == '__main__': + prefs = UserPrefs.UserPrefs() + about = UserPrefsEditor(tk.Tk(), prefs) + about.show() + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/UserPrefs.py ingimp-2.4.7/guilog/wrapper/UserPrefs.py --- gimp-2.4.7/guilog/wrapper/UserPrefs.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/UserPrefs.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,189 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import os +import ConfigParser +import random +import sys +import tempfile + +USER_SETTINGS = 'UserSettings' + +AUTOSTART_DELAY = 'autostart_delay_in_secs' # autostart_delay_in_secs is splash screen delay before it automatically starts +REPORTING_URL = 'reporting_url' +REDIRECT_URL = 'redirect_url' +KEEP_LOCAL_COPY = 'keep_local_copy_of_log' +UPLOADED_PREFIX = 'uploaded_prefix' +PARTICIPATION_URL = 'participation_url' +LOGFILE_PREFIX = 'logfile_prefix' +LOG_DIR = 'log_dir' +LOG_HEADER_FILE = 'log_header_file' +WEBSTATS_REDIRECT_URL = 'webstats_redirect_url' +TEST_URL_PREFIX = 'test_url_prefix' +COMPRESS_LOG = 'compress_log' +LOG_KEY_ACTIVITY = 'log_key_activity' +LOG_BUTTON_ACTIVITY = 'log_button_activity' +SESSION_TAGS = 'session_tags' +HISTOGRAM_TIMEOUT = 'histogram_timeout' +NUM_DISABLED_RUNS = 'num_disabled_runs' + +PRIVATE_SETTINGS = 'PrivateSettings' +USER_ID = 'user_id' +CONSENT_ID = 'consent_id' +KILLSWITCH = 'kill_switch' +SHOWN_PICTOGRAMS = 'shown_pictograms' + +''' +Participation URL is the URL queried to see if there are any +opportunities for additional participation + +reporting_url is the URL all log files are sent to. + +If reporting_url is empty, redirect_url causes the wrapper to contact +the redirect URL to get the "real" site to submit log files to. + +If both are None, then no uploads will take place and all log files will +be stored in log_dir, defined below. + +The log_header_file is an optional file that will be included at the +top of a log file. There is a limit to how much text can be included +Example: + log_header_file = 'my_unique_data.txt' + +To ensure a local copy of the log is kept, even after it is uploaded, set keep_local_copy +to True. After the log file is uploaded, its name will be changed to have +uploaded_prefix prepended to it. +''' + +_default_user_prefs = { + AUTOSTART_DELAY : -1, # Turn off autostart by default + REPORTING_URL : '', + REDIRECT_URL : 'http://www.ingimp.org/redirects/logserver.html', + KEEP_LOCAL_COPY : '0', + UPLOADED_PREFIX : 'uploaded_', + PARTICIPATION_URL : 'http://www.ingimp.org/redirects/participation.html', + LOGFILE_PREFIX : 'ingimp_', + LOG_DIR : str(tempfile.gettempdir()), + LOG_HEADER_FILE : '', + WEBSTATS_REDIRECT_URL : 'http://www.ingimp.org/redirects/webstats.html', + TEST_URL_PREFIX : 'http://www.ingimp.org/reality_checks', + COMPRESS_LOG : '1', + LOG_KEY_ACTIVITY : '1', + LOG_BUTTON_ACTIVITY : '1', + SESSION_TAGS : '', + HISTOGRAM_TIMEOUT : '-1', + NUM_DISABLED_RUNS : '0', +} + +class UserPrefs: + def __init__(self): + self.user_id = "" + self.have_consent = False + self.have_killswitch = False + self.shown_pictograms = True + + prefs_dir = os.getenv('APPDATA') + if not prefs_dir: + prefs_dir = os.getenv('USERPROFILE') + if not prefs_dir: + prefs_dir = os.getenv('HOME') + if not prefs_dir: + prefs_dir = '.' + prefs_dir = os.path.join(prefs_dir, '.ingimp') + if not os.path.exists(prefs_dir): + os.mkdir(prefs_dir) + + self.prefs_dir = prefs_dir + self.prefs_file = os.path.join(prefs_dir, 'ingimp.ini') + if not os.path.exists(self.prefs_file): + self._create_prefs_file() + + self.config = ConfigParser.ConfigParser() + self.config.read(self.prefs_file) + self._check_defaults() + + self.user_id = self.config.get(PRIVATE_SETTINGS, USER_ID) + self.have_consent = int(self.config.get(PRIVATE_SETTINGS, CONSENT_ID)) != 0 + self.have_killswitch = int(self.config.get(PRIVATE_SETTINGS, KILLSWITCH)) != 0 + + def get(self, key): + return self.config.get(USER_SETTINGS, key) + def set(self, key, value): + self.config.set(USER_SETTINGS, key, value) + self._write_prefs() + def get_one_time_pad_file(self): + return os.path.join(self.prefs_dir, 'one_time.bin.gz') + + def _check_defaults(self): + write_prefs = False + for section in [USER_SETTINGS, PRIVATE_SETTINGS]: + if not self.config.has_section(section): + self.config.add_section(section) + write_prefs = True + if not self.config.has_option(PRIVATE_SETTINGS, CONSENT_ID): + self.config.set(PRIVATE_SETTINGS, CONSENT_ID, '0') + write_prefs = True + if not self.config.has_option(PRIVATE_SETTINGS, KILLSWITCH): + self.config.set(PRIVATE_SETTINGS, KILLSWITCH, '0') + write_prefs = True + if not self.config.has_option(PRIVATE_SETTINGS, SHOWN_PICTOGRAMS): + self.config.set(PRIVATE_SETTINGS, SHOWN_PICTOGRAMS, '1') + self.shown_pictograms = False + write_prefs = True + for key in _default_user_prefs: + if not self.config.has_option(USER_SETTINGS, key): + self.config.set(USER_SETTINGS, key, str(_default_user_prefs[key])) + write_prefs = True + if write_prefs: + self._write_prefs() + def _create_prefs_file(self): + # Generate random ID for user + # There is a small chance that two people will get identical + # IDs, but I'd rather we generate the IDs locally than require + # a network connection the first time we start up + random.seed() + user_id = str(random.randint(0,sys.maxint)) + random.seed() + user_id = user_id + '_' + str(random.randint(0,sys.maxint)) + config = ConfigParser.ConfigParser() + config.add_section(PRIVATE_SETTINGS) + config.add_section(USER_SETTINGS) + config.set(PRIVATE_SETTINGS, USER_ID, user_id) + config.set(PRIVATE_SETTINGS, CONSENT_ID, '0') + config.set(PRIVATE_SETTINGS, KILLSWITCH, '0') + f = open(self.prefs_file, 'w') + config.write(f) + f.close() + del config + def _write_prefs(self): + f = open(self.prefs_file, 'w') + self.config.write(f) + f.close() + def set_have_consent(self): + self.config.set(PRIVATE_SETTINGS, CONSENT_ID, '1') + self.have_consent = True + self._write_prefs() + def set_have_killswitch(self): + self.config.set(PRIVATE_SETTINGS, KILLSWITCH, '1') + self.have_killswitch = True + self._write_prefs() + +if __name__ == '__main__': + prefs = UserPrefs() + print "ID:", prefs.user_id, "Have consent:", prefs.have_consent, "Kill switch:", prefs.have_killswitch diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/Utils.py ingimp-2.4.7/guilog/wrapper/Utils.py --- gimp-2.4.7/guilog/wrapper/Utils.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/Utils.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,69 @@ +''' +ingimp -- Instrumented GIMP +Copyright (C) 2007 Michael Terry and Terry Park + +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. +''' + +import urllib2 +import Tkinter as tk +import Config +import sys + +_logo_image = None + +def center_window(window): + window.update_idletasks() + screenwidth = window.winfo_screenwidth() + screenheight = window.winfo_screenheight() + width = window.winfo_reqwidth() + height = window.winfo_reqheight() + too_small = False + if width > screenwidth or height > screenheight: + width = min(width, screenwidth) - 30 + height = min(height, screenheight) - 30 + too_small = True + # TODO: Quick hack until more robust code available + if sys.platform.lower().startswith('darwin'): + height = height - 60 + x = (screenwidth-width)/2 + y = (screenheight-height)/3 + if too_small: + y = 0 + if sys.platform.lower().startswith('darwin'): + # Put it below the menubar + if y < 30: + height = height - (30 - y) + y = 30 + window.geometry('%dx%d+%d+%d' % (width, height, x, y)) + +def get_logo_image(): + global _logo_image + if not _logo_image: + try: + _logo_image = tk.PhotoImage(file=Config.logo_filename) + except: + _logo_image = None + return _logo_image + +def get_redirect_url(url): + try: + f = urllib2.urlopen(url) + new_url = f.read() + f.close() + return new_url.strip() + except Exception, e: + return None + diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/guilog/wrapper/WindowsSetup.py ingimp-2.4.7/guilog/wrapper/WindowsSetup.py --- gimp-2.4.7/guilog/wrapper/WindowsSetup.py 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/guilog/wrapper/WindowsSetup.py 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,18 @@ +# To create, python WindowsSetup.py py2exe + +from distutils.core import setup +import py2exe +import Config + +setup( + version = Config.version, + description = "ingimp Windows launcher", + name = "ingimp", + + # targets to build + windows = [{ "script":"ingimp", + "icon_resources":[(1,"../../app/wilber.ico")] + }], + console = ["ingimp-helper.py"], +# options = { 'py2exe': {'excludes' : ['Config'] } }, + ) diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/Makefile.am ingimp-2.4.7/Makefile.am --- gimp-2.4.7/Makefile.am 2008-07-10 22:32:03.000000000 +1200 +++ ingimp-2.4.7/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -28,7 +28,8 @@ $(GIMP_PLUGINS) \ etc \ devel-docs \ - docs + docs \ + guilog bin_SCRIPTS = gimptool-@GIMP_TOOL_VERSION@ @GIMPINSTALL@ diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/plug-ins/common/wmf.c ingimp-2.4.7/plug-ins/common/wmf.c --- gimp-2.4.7/plug-ins/common/wmf.c 2008-07-10 22:31:57.000000000 +1200 +++ ingimp-2.4.7/plug-ins/common/wmf.c 2008-09-01 14:28:03.000000000 +1200 @@ -23,6 +23,7 @@ #include "config.h" +#include #include #include diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/plug-ins/Makefile.am ingimp-2.4.7/plug-ins/Makefile.am --- gimp-2.4.7/plug-ins/Makefile.am 2008-07-10 22:31:58.000000000 +1200 +++ ingimp-2.4.7/plug-ins/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -70,7 +70,7 @@ sgi \ sel2path \ $(twain) \ - $(uri) \ + uri \ $(winicon) \ $(winsnap) \ $(xjt) \ diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/plug-ins/uri/Makefile.am ingimp-2.4.7/plug-ins/uri/Makefile.am --- gimp-2.4.7/plug-ins/uri/Makefile.am 2008-07-10 22:31:39.000000000 +1200 +++ ingimp-2.4.7/plug-ins/uri/Makefile.am 2008-09-01 14:28:03.000000000 +1200 @@ -28,7 +28,7 @@ backend_cflags = $(URI_LIBCURL_CFLAGS) backend_libs = $(URI_LIBCURL_LIBS) else -backend_sources = uri-backend-wget.c +backend_sources = uri-backend-ingimp.c backend_cflags = backend_libs = endif diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/plug-ins/uri/uri-backend-ingimp.c ingimp-2.4.7/plug-ins/uri/uri-backend-ingimp.c --- gimp-2.4.7/plug-ins/uri/uri-backend-ingimp.c 1970-01-01 12:00:00.000000000 +1200 +++ ingimp-2.4.7/plug-ins/uri/uri-backend-ingimp.c 2008-09-01 14:28:03.000000000 +1200 @@ -0,0 +1,143 @@ +/* GIMP - The GNU 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. + */ + +/* Author: Josh MacDonald and Michael Terry. */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "uri-backend.h" + +#include "libgimp/stdplugins-intl.h" + + +#define TIMEOUT 300 +#define BUFSIZE 1024 + + +gboolean +uri_backend_init (const gchar *plugin_name, + gboolean run, + GimpRunMode run_mode, + GError **error) +{ + return TRUE; +} + +void +uri_backend_shutdown (void) +{ +} + +const gchar * +uri_backend_get_load_help (void) +{ + return "Loads a file using ingimp's helper app"; +} + +const gchar * +uri_backend_get_save_help (void) +{ + return NULL; +} + +const gchar * +uri_backend_get_load_protocols (void) +{ + return "http:,https:,ftp:"; +} + +const gchar * +uri_backend_get_save_protocols (void) +{ + return NULL; +} + +gboolean +uri_backend_load_image (const gchar *uri, + const gchar *tmpname, + GimpRunMode run_mode, + GError **error) +{ + gint pid; + gchar *helper = NULL; + + helper = (char*)g_getenv("GIMP_GUI_LOG_HELPER"); + if (!helper) + { + g_set_error (error, 0, 0, "Could not get ingimp helper app name"); + return FALSE; + } + +/* For ingimp, don't rely on wget to get files -- use helper app instead */ +#ifdef G_OS_WIN32 + + spawnl (P_WAIT, + helper, + helper, "--retrieve", uri, tmpname, NULL); + +#else /* G_OS_WIN32 */ + + if ((pid = fork()) < 0) + { + g_set_error (error, 0, 0, "fork() failed: %s", g_strerror (errno)); + return FALSE; + } + else if (pid == 0) + { + execlp (helper, + helper, "--retrieve", uri, tmpname, NULL); + g_set_error (error, 0, 0, "exec() failed: ingimp: %s", g_strerror (errno)); + _exit (127); + } + else + { + int stat_loc; + waitpid(pid, &stat_loc, 0); + } +#endif /* G_OS_WIN32 */ + + return TRUE; +} + +gboolean +uri_backend_save_image (const gchar *uri, + const gchar *tmpname, + GimpRunMode run_mode, + GError **error) +{ + g_set_error (error, 0, 0, "EEK"); + + return FALSE; +} diff -Nurd -x Makefile.in -x configure -x '*.gif' -x '*.xml' -x autom4te.cache -x .svn gimp-2.4.7/README ingimp-2.4.7/README --- gimp-2.4.7/README 2008-07-10 22:32:03.000000000 +1200 +++ ingimp-2.4.7/README 2008-09-01 14:28:03.000000000 +1200 @@ -1,3 +1,8 @@ +NOTE: This is ingimp, a modified version of GIMP that automatically +logs and transmits usability data. See the README in the guilog/ +directory for more information. The rest of this file is the official +README for GIMP. + The GNU Image Manipulation Program Version 2.4 ----------------------------------------------