/* * GTimer * * Copyright: * (C) 1999 Craig Knudsen, cknudsen@cknudsen.com * See accompanying file "COPYING". * * 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 * * Description: * Helps you keep track of time spent on different tasks. * * Author: * Craig Knudsen, cknudsen@cknudsen.com, http://www.cknudsen.com * * Home Page: * http://www.cknudsen.com/gtimer/ * * History: * 28-Feb-2003 Added project create/edit window. * 21-Feb-2003 Added project pulldown in task create/edit. * 30-Apr-1999 Fixed bug where \n chars could be included at the * end of a task name (which would screw up the task * data file format and make the task unreachable). * 25-Mar-1999 Fixed bug where adding new tasks messes up the * hide/unhide stuff * 18-Mar-1999 Internationalization * 18-Mar-1998 Added calls to gtk_window_set_wmclass so the windows * behave better for window managers. * 03-Mar-1998 Created * ****************************************************************************/ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #ifdef HAVE_LIBINTL_H #include #else #define gettext(a) a #endif #include "project.h" #include "task.h" #include "gtimer.h" #include "config.h" #ifdef GTIMER_MEMDEBUG #include "memdebug/memdebug.h" #endif extern TaskData **visible_tasks; extern int num_visible_tasks; extern TaskData **tasks; extern int num_tasks; extern GtkWidget *status; extern guint status_id; typedef struct { Project *p; GtkWidget *window; GtkWidget *name; } ProjectEditData; typedef struct { TaskData *taskdata; GtkWidget *window; GtkWidget *name; GtkWidget *projectMenu; } TaskEditData; static void ok_project_callback ( widget, data ) GtkWidget *widget; gpointer data; { ProjectEditData *ed = (ProjectEditData *) data; Project *p; char *str, *ptr; GtkWidget *item = NULL; int project_updated = 0, loop; /* Update existing project */ if ( ed->p ) { str = gtk_entry_get_text ( GTK_ENTRY(ed->name) ); if ( strcmp ( str, ed->p->name ) ) { free ( ed->p->name ); /* remove trailing white space */ for ( ptr = str + strlen ( str ) - 1; isspace ( *ptr ) && ptr > str; ptr-- ) *ptr = '\0'; ed->p->name = (char *) malloc ( strlen ( str ) + 1 ); strcpy ( ed->p->name, str ); project_updated = 1; } gtk_statusbar_push ( GTK_STATUSBAR ( status ), status_id, gettext("Project updated") ); } /* New Project */ else { p = (Project *) malloc ( sizeof ( Project ) ); memset ( p, '\0', sizeof ( Project ) ); str = gtk_entry_get_text ( GTK_ENTRY(ed->name) ); /* remove trailing white space */ ptr = (char *) malloc ( strlen ( str ) + 1 ); strcpy ( ptr, str ); str = ptr; for ( ptr = str + strlen ( str ) - 1; isspace ( *ptr ) && ptr > str; ptr-- ) *ptr = '\0'; p = projectCreate ( str ); projectAdd ( p ); gtk_statusbar_push ( GTK_STATUSBAR ( status ), status_id, gettext("Project added") ); } /* redraw the task list only if we changed a project name */ if ( project_updated ) { for ( loop = 0; ed->p && loop < num_visible_tasks; loop++ ) { if ( visible_tasks[loop]->task->project_id == ed->p->number ) { visible_tasks[loop]->name_updated = 1; visible_tasks[loop]->project_name = ed->p->name; } } update_list (); } gtk_grab_remove ( ed->window ); gtk_widget_destroy ( ed->window ); free ( ed ); } static void cancel_project_callback ( widget, data ) GtkWidget *widget; gpointer data; { ProjectEditData *ed = (ProjectEditData *) data; gtk_grab_remove ( ed->window ); gtk_widget_destroy ( ed->window ); free ( ed ); } /* ** Create the add/edit project window. ** It's an add if project is NULL. */ GtkWidget *create_project_edit_window ( project ) Project *project; { GtkWidget *edit_window; GtkWidget *table; /*GtkTooltips *tooltips;*/ GtkWidget *label, *name_text, *ok_button, *cancel_button; GtkWidget *option, *option_menu, *menuitem; ProjectEditData *ed; char msg[100], *ptr = NULL; ed = (ProjectEditData *) malloc ( sizeof ( ProjectEditData ) ); memset ( ed, 0, sizeof ( ProjectEditData ) ); ed->p = project; ed->window = edit_window = gtk_dialog_new (); gtk_window_set_wmclass ( GTK_WINDOW ( ed->window ), "GTimer", "gtimer" ); if ( project ) sprintf ( msg, "GTimer: %s", gettext ("Edit Project") ); else sprintf ( msg, "GTimer: %s", gettext ("Add Project") ); gtk_window_set_title (GTK_WINDOW (edit_window), msg ); gtk_window_position ( GTK_WINDOW(edit_window), GTK_WIN_POS_MOUSE ); gtk_grab_add ( edit_window ); gtk_widget_realize ( edit_window ); table = gtk_table_new (1, 3, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacings (GTK_TABLE (table), 8); gtk_container_border_width (GTK_CONTAINER (table), 6); gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG (edit_window)->vbox ), table, TRUE, FALSE, 5 ); sprintf ( msg, "%s: ", gettext ( "Project name" ) ); label = gtk_label_new ( msg ); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show ( label ); ed->name = name_text = gtk_entry_new (); gtk_table_attach_defaults (GTK_TABLE (table), name_text, 1, 3, 0, 1); if ( project ) gtk_entry_set_text ( GTK_ENTRY(name_text), project->name ); else { gtk_entry_set_text ( GTK_ENTRY(name_text), gettext ("Unnamed Project") ); gtk_entry_select_region ( GTK_ENTRY(name_text), 0, strlen ( gettext ("Unnamed Project") ) ); } gtk_window_set_focus ( >K_DIALOG ( edit_window )->window, name_text ); gtk_widget_show ( name_text ); gtk_widget_show ( table ); /* add command buttons */ /*tooltips = gtk_tooltips_new ();*/ ok_button = gtk_button_new_with_label ( gettext("Ok") ); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (edit_window)->action_area), ok_button, TRUE, TRUE, 5); gtk_signal_connect (GTK_OBJECT (ok_button), "clicked", GTK_SIGNAL_FUNC (ok_project_callback), ed); GTK_WIDGET_SET_FLAGS (ok_button, GTK_CAN_DEFAULT); gtk_widget_grab_default (ok_button); gtk_widget_show (ok_button); /*gtk_tooltips_set_tips (tooltips, ok_button, "Save this task" );*/ cancel_button = gtk_button_new_with_label ( gettext("Cancel") ); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (edit_window)->action_area), cancel_button, TRUE, TRUE, 5); gtk_signal_connect (GTK_OBJECT (cancel_button), "clicked", GTK_SIGNAL_FUNC (cancel_project_callback), ed); gtk_widget_show (cancel_button); /*gtk_tooltips_set_tips (tooltips, cancel_button, "Edit the selected task" );*/ /* gtk_widget_show (hbox); gtk_widget_show (vbox); */ gtk_widget_show (edit_window); return ( edit_window ); } static int getMenuSelectionIndex ( GtkWidget *w ) { int count = 0; GList *child; GtkMenuShell *menu_shell; GtkBin *bin ; menu_shell = GTK_MENU_SHELL ( gtk_option_menu_get_menu ( GTK_OPTION_MENU( w) ) ); child = menu_shell->children; while ( child ) { bin = GTK_BIN ( child->data ); if ( !bin->child ) return count; child = child->next; count++; } return ( -1 ); } static void ok_task_callback ( widget, data ) GtkWidget *widget; gpointer data; { TaskEditData *ed = (TaskEditData *) data; TaskData *td; char *str, *ptr; GtkWidget *item = NULL; Project *p = NULL, *selp = NULL; int cnt, loop, new_project_id; /* Determine project */ if ( ed->projectMenu ) { cnt = getMenuSelectionIndex ( ed->projectMenu ); for ( loop = 0, p = projectGetFirst(); p != NULL && selp == NULL; p = projectGetNext(), loop++ ) { if ( loop + 1 == cnt ) { selp = p; } } } /* Update existing task */ if ( ed->taskdata ) { str = gtk_entry_get_text ( GTK_ENTRY(ed->name) ); if ( strcmp ( str, ed->taskdata->task->name ) ) { free ( ed->taskdata->task->name ); /* remove trailing white space */ for ( ptr = str + strlen ( str ) - 1; isspace ( *ptr ) && ptr > str; ptr-- ) *ptr = '\0'; ed->taskdata->task->name = (char *) malloc ( strlen ( str ) + 1 ); strcpy ( ed->taskdata->task->name, str ); ed->taskdata->name_updated = 1; } if ( ed->projectMenu ) { new_project_id = selp ? selp->number : -1; if ( ed->taskdata->task->project_id != new_project_id ) { if ( selp == NULL ) ed->taskdata->task->project_id = -1; /* none */ else ed->taskdata->task->project_id = selp->number; ed->taskdata->name_updated = 1; ed->taskdata->project_name = selp ? selp->name : ""; } } gtk_statusbar_push ( GTK_STATUSBAR ( status ), status_id, gettext("Task updated") ); } /* New Task */ else { td = (TaskData *) malloc ( sizeof ( TaskData ) ); memset ( td, '\0', sizeof ( TaskData ) ); td->new_task = 1; str = gtk_entry_get_text ( GTK_ENTRY(ed->name) ); /* remove trailing white space */ ptr = (char *) malloc ( strlen ( str ) + 1 ); strcpy ( ptr, str ); str = ptr; for ( ptr = str + strlen ( str ) - 1; isspace ( *ptr ) && ptr > str; ptr-- ) *ptr = '\0'; td->task = taskCreate ( str ); taskAdd ( td->task ); tasks = (TaskData **) realloc ( tasks, ( num_tasks + 1 ) * sizeof ( TaskData * ) ); tasks[num_tasks] = td; visible_tasks = (TaskData **) realloc ( visible_tasks, ( num_visible_tasks + 1 ) * sizeof ( TaskData * ) ); visible_tasks[num_visible_tasks] = td; num_visible_tasks++; num_tasks++; new_project_id = selp ? selp->number : -1; td->task->project_id = new_project_id; td->project_name = selp ? selp->name : ""; gtk_statusbar_push ( GTK_STATUSBAR ( status ), status_id, gettext("Task updated") ); } gtk_grab_remove ( ed->window ); gtk_widget_destroy ( ed->window ); free ( ed ); /* redraw the task list */ update_list (); } static void cancel_task_callback ( widget, data ) GtkWidget *widget; gpointer data; { TaskEditData *ed = (TaskEditData *) data; gtk_grab_remove ( ed->window ); gtk_widget_destroy ( ed->window ); free ( ed ); } /* ** Create the add/edit window. ** It's an add if taskdata is NULL. */ GtkWidget *create_task_edit_window ( taskdata ) TaskData *taskdata; { GtkWidget *edit_window; GtkWidget *table; /*GtkTooltips *tooltips;*/ GtkWidget *label, *name_text, *ok_button, *cancel_button; GtkWidget *option, *option_menu, *menuitem; TaskEditData *ed; char msg[100], *ptr = NULL; int use_project, loop, selitem; Project *p; ed = (TaskEditData *) malloc ( sizeof ( TaskEditData ) ); memset ( ed, 0, sizeof ( TaskEditData ) ); ed->taskdata = taskdata; ed->window = edit_window = gtk_dialog_new (); gtk_window_set_wmclass ( GTK_WINDOW ( ed->window ), "GTimer", "gtimer" ); if ( taskdata ) sprintf ( msg, "GTimer: %s", gettext ("Edit Task") ); else sprintf ( msg, "GTimer: %s", gettext ("Add Task") ); gtk_window_set_title (GTK_WINDOW (edit_window), msg ); gtk_window_position ( GTK_WINDOW(edit_window), GTK_WIN_POS_MOUSE ); gtk_grab_add ( edit_window ); gtk_widget_realize ( edit_window ); use_project = 0; if ( configGetAttribute ( CONFIG_USE_PROJECTS, &ptr ) == 0 && ptr && ptr[0] == '1' ) use_project = 1; table = gtk_table_new (1, use_project ? 4 : 3, FALSE); gtk_table_set_row_spacings (GTK_TABLE (table), 4); gtk_table_set_col_spacings (GTK_TABLE (table), 8); gtk_container_border_width (GTK_CONTAINER (table), 6); gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG (edit_window)->vbox ), table, TRUE, FALSE, 5 ); if ( use_project ) { sprintf ( msg, "%s: ", gettext ( "Project " ) ); label = gtk_label_new ( msg ); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show ( label ); option = gtk_option_menu_new (); gtk_widget_show ( option ); gtk_table_attach_defaults ( GTK_TABLE (table), option, 1, 3, 0, 1); option_menu = gtk_menu_new (); menuitem = gtk_menu_item_new_with_label ( "None" ); gtk_menu_append ( GTK_MENU ( option_menu ), menuitem ); gtk_widget_show ( menuitem ); selitem = 0; loop = 1; for ( p = projectGetFirst(); p != NULL; p = projectGetNext(), loop++ ) { menuitem = gtk_menu_item_new_with_label ( p->name ); gtk_menu_append ( GTK_MENU ( option_menu ), menuitem ); gtk_widget_show ( menuitem ); gtk_object_set_data ( GTK_OBJECT ( menuitem ), "Project", p ); if ( taskdata && taskdata->task->project_id == p->number ) selitem = loop; } gtk_option_menu_set_menu ( GTK_OPTION_MENU ( option ), option_menu ); if ( selitem > 0 ) gtk_option_menu_set_history ( GTK_OPTION_MENU ( option ), selitem ); ed->projectMenu = option; } sprintf ( msg, "%s: ", gettext ( "Task name" ) ); label = gtk_label_new ( msg ); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0 + use_project, 1 + use_project, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show ( label ); ed->name = name_text = gtk_entry_new (); gtk_table_attach_defaults (GTK_TABLE (table), name_text, 1, 3, 0 + use_project, 1 + use_project); if ( taskdata ) gtk_entry_set_text ( GTK_ENTRY(name_text), taskdata->task->name ); else { gtk_entry_set_text ( GTK_ENTRY(name_text), gettext ("Unnamed Task") ); gtk_entry_select_region ( GTK_ENTRY(name_text), 0, strlen ( gettext ("Unnamed Task") ) ); } gtk_window_set_focus ( >K_DIALOG ( edit_window )->window, name_text ); gtk_widget_show ( name_text ); gtk_widget_show ( table ); /* add command buttons */ /*tooltips = gtk_tooltips_new ();*/ ok_button = gtk_button_new_with_label ( gettext("Ok") ); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (edit_window)->action_area), ok_button, TRUE, TRUE, 5); gtk_signal_connect (GTK_OBJECT (ok_button), "clicked", GTK_SIGNAL_FUNC (ok_task_callback), ed); GTK_WIDGET_SET_FLAGS (ok_button, GTK_CAN_DEFAULT); gtk_widget_grab_default (ok_button); gtk_widget_show (ok_button); /*gtk_tooltips_set_tips (tooltips, ok_button, "Save this task" );*/ cancel_button = gtk_button_new_with_label ( gettext("Cancel") ); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (edit_window)->action_area), cancel_button, TRUE, TRUE, 5); gtk_signal_connect (GTK_OBJECT (cancel_button), "clicked", GTK_SIGNAL_FUNC (cancel_task_callback), ed); gtk_widget_show (cancel_button); /*gtk_tooltips_set_tips (tooltips, cancel_button, "Edit the selected task" );*/ /* gtk_widget_show (hbox); gtk_widget_show (vbox); */ gtk_widget_show (edit_window); return ( edit_window ); }