/******************************************************************** Copyright (C) 2001 Bassoukos Tassos 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., 675 Mass Ave, Cambridge, MA 02139, USA. *********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_MALLOC_H #include #endif #include #include #include #include #include "tasks.h" #include "main.h" #include "connection.h" #include "tasklist.h" #include "pixmap.h" /* Tasks have two basic means of removal: the user selected to cancel them (the "cancelled" signal), or a section of code has deemed they are no longer necesary(sp?) (the "destroy" signal). Note that the "cancelled" signal does not automatically destroy the task, that is left to the Task user to manage/decide. Task users that do not differentiate between the two circumstances (because f.e. the process that the Task represents is inherently non-cancellable (f.e receiving an agreement)) should simply call task_on_cancel_destroy() and hook up their clean-up handler on the "destroy" signal. Care should be taken when using tasks from threads, as only task_async_*() are thread-safe (using a hack) in this implementation. The "check-update" signal is used for asynchronous polling of some tasks tha change often (trackers and file transfers f.e.) and should be polled at specific intervals (not working right now...). */ enum { UPDATED, CANCELLED, CHECK_UPDATE, LAST_SIGNAL }; static guint task_signals[LAST_SIGNAL]={0,0,0}; static GtkPackerClass *parent_class=NULL; static void task_updated(Task *task){ /* ack... for some reason, we ssom to geg NaN/inf's as percentages */ if((task->is_changed & TASK_CHANGED_POS) && (task->percdone>=0.0 && task->percdone<=1.0)) gtk_progress_set_percentage(GTK_PROGRESS(task->progressbar_widget),task->percdone); if(task->is_changed & TASK_CHANGED_TEXT) gtk_progress_set_format_string(GTK_PROGRESS(task->progressbar_widget),task->text); task->is_changed=0; task->async_update_requested=FALSE; } static void task_finalize(GtkObject *object){ Task *task; g_return_if_fail(object!=NULL); g_return_if_fail(IS_TASK(object)); task=TASK(object); if(task->icon_name!=NULL) free(task->icon_name); GTK_OBJECT_CLASS(parent_class)->finalize(object); } static void task_class_init(TaskClass *klass){ GtkObjectClass *object_class=(GtkObjectClass*)klass; parent_class=gtk_type_class(GTK_TYPE_PACKER); object_class->finalize=task_finalize; task_signals[UPDATED]= gtk_signal_new("updated", GTK_RUN_LAST|GTK_RUN_NO_RECURSE, object_class->type, GTK_SIGNAL_OFFSET(TaskClass,updated), gtk_marshal_NONE__NONE, GTK_TYPE_NONE,0); task_signals[CANCELLED]= gtk_signal_new("cancelled", GTK_RUN_FIRST|GTK_RUN_NO_RECURSE, object_class->type, GTK_SIGNAL_OFFSET(TaskClass,updated), gtk_marshal_NONE__NONE, GTK_TYPE_NONE,0); task_signals[CHECK_UPDATE]= gtk_signal_new("check-update", GTK_RUN_LAST|GTK_RUN_NO_RECURSE, object_class->type, GTK_SIGNAL_OFFSET(TaskClass,check_update), gtk_marshal_BOOL__NONE, GTK_TYPE_BOOL,0); gtk_object_class_add_signals(object_class,task_signals,LAST_SIGNAL); klass->updated=task_updated; klass->cancelled=NULL; klass->check_update=NULL; } static void task_button_enter(GtkButton *b,Task *task){ pixmap_apply_to_widget(HL_STOCK_PIXMAP_CANCEL_ALL,GTK_PIXMAP(task->icon_widget)); } static void task_button_leave(GtkButton *b,Task *task){ if(task->icon_name) pixmap_apply_to_widget(task->icon_name,GTK_PIXMAP(task->icon_widget)); } static void task_init(Task *t){ GdkPixmap *map; GdkBitmap *mask; t->percdone=0.0; t->icon_name=NULL; t->is_running=FALSE; t->is_changed=0; t->text[0]=0; t->c=NULL; t->progressbar_widget=gtk_progress_bar_new(); gtk_progress_configure(GTK_PROGRESS(t->progressbar_widget),0.0,0.0,1.0); gtk_progress_set_format_string(GTK_PROGRESS(t->progressbar_widget),"---"); gtk_progress_set_show_text(GTK_PROGRESS(t->progressbar_widget),TRUE); pixmap_get("stipple",&map,&mask); t->icon_widget=gtk_pixmap_new(map,mask); t->button_widget=gtk_button_new(); gtk_container_add(GTK_CONTAINER(t->button_widget),t->icon_widget); gtk_signal_connect_object(GTK_OBJECT(t->button_widget),"clicked", GTK_SIGNAL_FUNC(task_cancel),GTK_OBJECT(t)); gtk_button_set_relief(GTK_BUTTON(t->button_widget),GTK_RELIEF_NONE); gtk_signal_connect(GTK_OBJECT(t->button_widget),"enter", GTK_SIGNAL_FUNC(task_button_enter),t); gtk_signal_connect(GTK_OBJECT(t->button_widget),"leave", GTK_SIGNAL_FUNC(task_button_leave),t); gtk_packer_add(GTK_PACKER(t),t->progressbar_widget, GTK_SIDE_RIGHT,GTK_ANCHOR_EAST, GTK_PACK_EXPAND|GTK_FILL_X,0,0,0,0,0); gtk_packer_add(GTK_PACKER(t),t->button_widget, GTK_SIDE_LEFT,GTK_ANCHOR_WEST, 0,0,0,0,0,0); gtk_widget_show(t->progressbar_widget); gtk_widget_show(t->icon_widget); gtk_widget_show(t->button_widget); } GtkType task_get_type(void){ static GtkType task_type=0; if(task_type==0){ static const GtkTypeInfo type_info={ "Task", sizeof(Task), sizeof(TaskClass), (GtkClassInitFunc) task_class_init, (GtkObjectInitFunc) task_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; task_type=gtk_type_unique(GTK_TYPE_PACKER,&type_info); gtk_type_set_chunk_alloc(task_type,8); } return task_type; } GtkWidget *task_add_button(Task *task,char *name){ GtkWidget *w=gtk_button_new(),*p; GdkPixmap *map; GdkBitmap *mask; g_return_val_if_fail(task!=NULL,NULL); g_return_val_if_fail(IS_TASK(task),NULL); g_return_val_if_fail(w!=NULL,NULL); g_return_val_if_fail(GTK_IS_WIDGET(w),NULL); pixmap_get("stipple",&map,&mask); p=gtk_pixmap_new(map,mask); pixmap_apply_to_widget(name,GTK_PIXMAP(p)); gtk_container_add(GTK_CONTAINER(w),p); gtk_button_set_relief(GTK_BUTTON(w),GTK_RELIEF_NONE); gtk_widget_show_all(w); gtk_packer_add(GTK_PACKER(task),w, GTK_SIDE_LEFT,GTK_ANCHOR_WEST, 0,0,0,0,0,0); return w; } void task_set_icon(Task *task,char *icon_name){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); if(task->icon_name!=NULL) free(task->icon_name); task->icon_name=strdup(icon_name); pixmap_apply_to_widget(icon_name,GTK_PIXMAP(task->icon_widget)); } Task *task_create_new(char *icon){ Task *task=(Task *)gtk_type_new(TASK_TYPE); if(icon!=NULL) task_set_icon(task,icon); return task; } void task_on_cancel_destroy(Task *task){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); gtk_signal_connect(GTK_OBJECT(task),"cancelled", GTK_SIGNAL_FUNC(gtk_widget_destroy),NULL); } static void task_set_pos_no_signal(Task *t, gfloat pos){ if(pos<=0.0 || pos>1.0) return; t->is_changed|=TASK_CHANGED_POS; if(t->percdone==0.0){ t->percdone=pos; main_window_update_title(); } else { t->percdone=pos; } } void task_set_pos(Task *task, gfloat pos){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); task_set_pos_no_signal(task,pos); task_update(task); } void task_set_postext(Task *task, gfloat pos, const char *text){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); strncpy(task->text,text,TASK_TEXT_SIZE); task->text[TASK_TEXT_SIZE-1]='\0'; task->is_changed|=TASK_CHANGED_TEXT; task_set_pos_no_signal(task,pos); task_update(task); } void task_set_printf(Task *task, gfloat pos, const char *fmt, ...){ va_list ap; g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); va_start(ap, fmt); g_vsnprintf(task->text, TASK_TEXT_SIZE, fmt, ap); task->is_changed|=TASK_CHANGED_TEXT; task_set_pos_no_signal(task,pos); task_update(task); } void task_async_set_printf(Task *task, gfloat pos, const char *fmt, ...){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); if(fmt!=NULL){ va_list ap; va_start(ap, fmt); g_vsnprintf(task->text, TASK_TEXT_SIZE, fmt, ap); task->is_changed|=TASK_CHANGED_TEXT; } task_set_pos_no_signal(task,pos); task_async_request_update(task); } static int task_do_destroy(Task *task){ g_return_val_if_fail(IS_TASK(task),FALSE); gtk_widget_destroy(GTK_WIDGET(task)); gtk_widget_unref(GTK_WIDGET(task)); return FALSE; } void task_async_destroy(Task *task){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); gtk_widget_ref(GTK_WIDGET(task)); gtk_idle_add((GtkFunction)task_do_destroy,task); } void task_cancel(Task *task){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); gtk_signal_emit(GTK_OBJECT(task),task_signals[CANCELLED]); } gboolean task_needs_update(Task *task){ gboolean needs_update=FALSE; g_return_val_if_fail(task!=NULL,FALSE); g_return_val_if_fail(IS_TASK(task),FALSE); gtk_signal_emit(GTK_OBJECT(task),task_signals[CHECK_UPDATE],&needs_update); return (needs_update || task->is_changed || task->async_update_requested)?TRUE:FALSE; } void task_update(Task *task){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); if(gtk_signal_n_emissions(GTK_OBJECT(task),task_signals[UPDATED])==0) gtk_signal_emit(GTK_OBJECT(task),task_signals[UPDATED]); } void task_async_request_update(Task *task){ g_return_if_fail(task!=NULL); g_return_if_fail(IS_TASK(task)); task->async_update_requested=TRUE; tasklist_async_request_update(); }