/******************************************************************** 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 "tasks.h" #include "threads.h" #include "guiprefs.h" #include "connection.h" #include "tasklist.h" #include "main.h" static GtkWidget *task_list_widget = NULL; static GSList *task_list=NULL; static gboolean have_deferred_update=FALSE; static GHashTable *task_groups=NULL; /* ========================================= */ static void task_destroyed(Task *t,gpointer dummy){ task_list=g_slist_remove(task_list,t); main_window_update_title(); } /* Create a task in an existing task group */ static Task *task_new_in_group(TaskGroup *tg,char *icon_name){ Task *task=task_create_new(icon_name); task_list=g_slist_prepend(task_list,task); gtk_signal_connect(GTK_OBJECT(task),"destroy",GTK_SIGNAL_FUNC(task_destroyed),NULL); task_group_add_task(tg,task); main_window_update_title(); return task; } static void taskgroup_destroyed(TaskGroup *tg,char *name){ g_hash_table_remove(task_groups,name); free(name); } static void task_added(TaskGroup *group,Task *task,gpointer data){ if(task_group_count_tasks(group)==1) gtk_widget_show(GTK_WIDGET(group)); } static void task_removed(TaskGroup *group,Task *task,gpointer data){ if(task_group_count_tasks(group)==0) gtk_widget_hide(GTK_WIDGET(group)); } /* Create a task in a named group, possibly creating it if necesary */ Task *task_new(char *group,char *icon_name){ TaskGroup *tg=g_hash_table_lookup(task_groups,group); if(tg==NULL){ tg=task_group_new(group); g_hash_table_insert(task_groups,group,tg); gtk_signal_connect_after(GTK_OBJECT(tg),"task-add", GTK_SIGNAL_FUNC(task_added),NULL); gtk_signal_connect_after(GTK_OBJECT(tg),"task-remove", GTK_SIGNAL_FUNC(task_removed),NULL); gtk_signal_connect(GTK_OBJECT(tg), "destroy", GTK_SIGNAL_FUNC(taskgroup_destroyed), strdup(group)); gtk_box_pack_start(GTK_BOX(task_list_widget),GTK_WIDGET(tg),FALSE,FALSE,0); gtk_widget_show(GTK_WIDGET(tg)); } return task_new_in_group(tg,icon_name); } static void task_destroy_connection(Task *t,gpointer dummy){ gtk_signal_disconnect_by_data(GTK_OBJECT(t->c),t); } /* Create a task for a connection */ Task *task_new_for_connection(Connection *c,char *icon){ Task *t; g_return_val_if_fail(c!=NULL,NULL); t=task_new(C_NAME(c),icon); t->c=c; gtk_signal_connect_object(GTK_OBJECT(c),"destroy", GTK_SIGNAL_FUNC(gtk_object_destroy), GTK_OBJECT(t)); gtk_signal_connect(GTK_OBJECT(t),"destroy", GTK_SIGNAL_FUNC(task_destroy_connection),t); return t; } void task_persist_after_connection(Task *t){ gtk_signal_disconnect_by_data(GTK_OBJECT(t->c),t); gtk_signal_disconnect_by_func(GTK_OBJECT(t),GTK_SIGNAL_FUNC(task_destroy_connection),t); } /* ========================================= */ static void task_perform_update(Task *task, gboolean *val){ if(task_needs_update(task)){ task_update(task); *val=TRUE; } } static gboolean tasklist_perform_deferred_update(gpointer dummy){ gboolean was_updated=FALSE; g_slist_foreach(task_list,(GFunc)task_perform_update,&was_updated); return have_deferred_update=was_updated; } /* Call this from a thread when you have an updated task */ void tasklist_async_request_update(void){ /* to be *correct*, we should have a mutex lock around this, * but we don't need it as a) the window is very small and * b) we're just going to re-scan for updates the task list */ if(have_deferred_update) return; have_deferred_update=TRUE; gtk_timeout_add(prefs_task_refresh_msec,tasklist_perform_deferred_update,NULL); } /* ========================================= */ void tasklist_cancel_all(void){ /* we make a copy as the removal will trigger the modification of the original list */ GSList *l=g_slist_copy(task_list); g_slist_foreach(l,(GFunc)task_cancel,NULL); g_slist_free(l); } /* ========================================== */ struct tasks_count {int active,count;}; static void count_active_tasks(Task *t, struct tasks_count *tasks){ tasks->count++; if(t->is_running || t->percdone>0.0) tasks->active++; } /* Return a string describing the status of fidelio */ char *tasklist_get_title(void){ static char buf2[256]; char buf1[256]; struct tasks_count tasks={0,0}; int connections_active=0; g_slist_foreach(task_list, (GFunc)count_active_tasks, &tasks); connections_active = count_connections(); set_cancel_task_widget_state(tasks.count != 0); if(tasks.count==0 && connections_active==0){ strcpy(buf2,_("idle")); } else { if(tasks.count==tasks.active){ if(tasks.count==0) { sprintf(buf1,_("no")); } else { sprintf(buf1,_("%d active"),tasks.active); } } else { if(tasks.active==0){ sprintf(buf1,_("%d inactive"),tasks.count); } else { sprintf(buf1,_("%d active of %d"),tasks.active,tasks.count); } } sprintf(buf2,_("%s %s, %d %s"), buf1,(tasks.count > 1)?_("tasks"):_("task"), connections_active,(connections_active > 1)?_("connections"):_("connection")); } return buf2; } /* Create the tasklist (and initialize some things) */ GtkWidget *tasklist_create(void){ g_assert(task_list_widget==NULL); task_groups=g_hash_table_new(g_str_hash,g_str_equal); task_list_widget=gtk_vbox_new(FALSE,0); return task_list_widget; }