00001 #include <string.h>
00002
00003 #include <gdk/gdk.h>
00004
00005 #include <gtk/gtk.h>
00006
00007 #include <gtk/gtkscrolledwindow.h>
00008 #include <gtk/gtkcellrenderertext.h>
00009
00010 #include <gnome.h>
00011
00012 #include <eel/eel.h>
00013
00014 #include <gtask-ui/gtask-view.h>
00015
00016 #include <gtask-ui/gtask-cell-renderer-basic.h>
00017 #include <gtask-ui/gtask-cell-renderer-progress.h>
00018
00019 #include <gtask-ui/gtask-marshalers.h>
00020
00021 #include "gtask-internals.h"
00022 #include "gtask-ui-util.h"
00023
00026
00027
00028 enum {
00029 COL_TASK,
00030
00031 COL_TITLE,
00032 COL_CATEGORY,
00033
00034 COL_STATUS_MESSAGE,
00035 COL_ERROR_MESSAGE,
00036
00037 COL_PROGRESS,
00038 COL_TIME_LEFT,
00039 COL_ACTIVITY_STATE,
00040
00041 COL_OLD_THUMB_URI,
00042 COL_THUMBNAIL,
00043 COL_IS_THUMBNAIL,
00044
00045 N_TASK_COLUMNS
00046 };
00047
00048 enum {
00049 PROP_0,
00050 PROP_VIEW_MODE
00051 };
00052
00054 typedef struct _GTaskTaskIter {
00055 GTaskGenericTask *task;
00056 GtkTreeIter iter;
00057 } GTaskTaskIter;
00058
00059 struct GTaskGetTaskFoo {
00060 guint num;
00061 GTaskView *view;
00062 GList *tasks;
00063 gboolean with_active;
00064 gboolean with_inactive;
00065 gboolean with_completed;
00066 };
00067
00068 static void
00069 gtask_task_iter_free( GTaskTaskIter *iter ) {
00070 g_object_unref( iter->task );
00071 g_free( iter );
00072 }
00073
00074 static gpointer parent_class;
00075
00076 static void
00077 gtask_view_init( GTaskView *view, gpointer g_class );
00078
00079 static void
00080 gtask_view_class_init( GTaskViewClass *klass );
00081
00082 static void
00083 gtask_view_destroy( GtkObject *obj );
00084
00085 static void
00086 gtask_view_finalize( GObject *obj );
00087
00088 static void
00089 gtask_view_create_basic_mode( GTaskView *view );
00090
00091 static void
00092 gtask_view_create_list_mode( GTaskView *view );
00093
00094 static void
00095 gtask_view_create_task_model( GTaskView *view );
00096
00097 static void
00098 gtask_view_update_task( GTaskView *view, GTaskTaskIter *iter );
00099
00100 static gboolean
00101 gtask_view_button_press( GtkWidget *list,
00102 GdkEventButton *event,
00103 gpointer data );
00104
00105 static void
00106 gtask_view_open_file( GObject *obj, gpointer *data );
00107
00108 static void
00109 gtask_view_open_dir( GObject *obj, gpointer *data );
00110
00111
00112
00113
00114
00115
00116 static void
00117 gtask_view_set_property( GObject *object,
00118 guint param_id,
00119 const GValue *value,
00120 GParamSpec *pspec );
00121
00122 static void
00123 gtask_view_get_property( GObject *object,
00124 guint param_id,
00125 GValue *value,
00126 GParamSpec *pspec );
00127
00128 GType gtask_view_mode_get_type( ) {
00129 static GType type = 0;
00130
00131 if( type == 0 ) {
00132 static const GEnumValue values[] = {
00133 { GTASK_VIEW_MODE_BASIC, "GTASK_VIEW_MODE_BASIC", "basic view" },
00134 { GTASK_VIEW_MODE_LIST, "GTASK_VIEW_MODE_LIST", "list view" },
00135 { 0, NULL, NULL }
00136 };
00137
00138 type = g_enum_register_static( "GTaskViewMode", values );
00139 }
00140
00141 return type;
00142 }
00143
00144 GType gtask_view_get_type( ) {
00145 static GType type = 0;
00146
00147 if( type == 0 ) {
00148 static const GTypeInfo info = {
00149 sizeof( GTaskViewClass ),
00150 NULL,
00151 NULL,
00152 (GClassInitFunc) gtask_view_class_init,
00153 NULL,
00154 NULL,
00155 sizeof( GTaskView ),
00156 0,
00157 (GInstanceInitFunc) gtask_view_init
00158 };
00159
00160 type = g_type_register_static( GTK_TYPE_NOTEBOOK,
00161 "GTaskView",
00162 &info,
00163 0 );
00164 }
00165
00166 return type;
00167 }
00168
00169 static void
00170 gtask_view_init( GTaskView *view, gpointer g_class ) {
00171 GTaskViewPrivate *private;
00172
00173 private = view->private = g_new0( GTaskViewPrivate, 1 );
00174
00175 private->filter_string = g_string_new( NULL );
00176 private->filter_seed = g_ptr_array_new( );
00177 private->filter_columns = g_ptr_array_new( );
00178
00179
00180
00181
00182 g_ptr_array_add( private->filter_columns,
00183 GINT_TO_POINTER( COL_TITLE ) );
00184
00185 g_ptr_array_add( private->filter_columns,
00186 GINT_TO_POINTER( COL_STATUS_MESSAGE ) );
00187
00188 g_ptr_array_add( private->filter_columns,
00189 GINT_TO_POINTER( COL_ERROR_MESSAGE ) );
00190
00191 g_ptr_array_add( private->filter_columns,
00192 GINT_TO_POINTER( COL_CATEGORY ) );
00193
00194 private->task_hash = g_hash_table_new_full( g_str_hash, g_str_equal,
00195 g_free,
00196 (GDestroyNotify) gtask_task_iter_free );
00197
00198 gtk_notebook_set_show_tabs( (GtkNotebook *) view, FALSE );
00199 gtk_notebook_set_show_border( (GtkNotebook *) view, TRUE );
00200
00201 gtask_view_create_task_model( view );
00202 gtask_view_create_basic_mode( view );
00203 gtask_view_create_list_mode( view );
00204
00205 private->view_mode = GTASK_VIEW_MODE_BASIC;
00206
00207 gtk_tree_view_set_model( private->task_view_basic,
00208 (GtkTreeModel *) private->task_model_filter );
00209
00210 gtk_notebook_set_current_page( (GtkNotebook *) view, 0 );
00211
00212 gtk_tree_view_set_model( private->task_view_list, NULL );
00213
00214 private->thumbnailer = gnome_thumbnail_factory_new( GNOME_THUMBNAIL_SIZE_NORMAL );
00215 }
00216
00217 static void
00218 gtask_view_class_init( GTaskViewClass *klass ) {
00219 GObjectClass *gobject_class = G_OBJECT_CLASS( klass );
00220 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS( klass );
00221 GType param_types[1];
00222
00223 parent_class = g_type_class_peek_parent( klass );
00224
00225 gobject_class->finalize = gtask_view_finalize;
00226 gobject_class->set_property = gtask_view_set_property;
00227 gobject_class->get_property = gtask_view_get_property;
00228
00229 gtk_object_class->destroy = gtask_view_destroy;
00230
00231 g_object_class_install_property( gobject_class,
00232 PROP_VIEW_MODE,
00233 g_param_spec_enum( "view-mode",
00234 "View Mode",
00235 "The currently selected view mode for the GTaskView",
00236 GTASK_VIEW_MODE_TYPE,
00237 GTASK_VIEW_MODE_BASIC,
00238 G_PARAM_READWRITE ) );
00239
00240 param_types[0] = G_TYPE_POINTER;
00241
00242 klass->tasks_added_signal_id = g_signal_newv( "tasks-added",
00243 G_TYPE_FROM_CLASS( klass ),
00244 G_SIGNAL_RUN_LAST |
00245 G_SIGNAL_NO_RECURSE |
00246 G_SIGNAL_NO_HOOKS,
00247 NULL,
00248 NULL,
00249 NULL,
00250 gtask_marshal_VOID__POINTER,
00251 G_TYPE_NONE,
00252 1,
00253 param_types );
00254
00255 klass->tasks_updated_signal_id = g_signal_newv( "tasks-updated",
00256 G_TYPE_FROM_CLASS( klass ),
00257 G_SIGNAL_RUN_LAST |
00258 G_SIGNAL_NO_RECURSE |
00259 G_SIGNAL_NO_HOOKS,
00260 NULL,
00261 NULL,
00262 NULL,
00263 gtask_marshal_VOID__POINTER,
00264 G_TYPE_NONE,
00265 1,
00266 param_types );
00267
00268 klass->tasks_removed_signal_id = g_signal_newv( "tasks-removed",
00269 G_TYPE_FROM_CLASS( klass ),
00270 G_SIGNAL_RUN_LAST |
00271 G_SIGNAL_NO_RECURSE |
00272 G_SIGNAL_NO_HOOKS,
00273 NULL,
00274 NULL,
00275 NULL,
00276 gtask_marshal_VOID__POINTER,
00277 G_TYPE_NONE,
00278 1,
00279 param_types );
00280 }
00281
00282 static void
00283 gtask_view_destroy( GtkObject *obj ) {
00284
00285
00286 if( GTK_OBJECT_CLASS( parent_class )->destroy )
00287 GTK_OBJECT_CLASS( parent_class )->destroy( obj );
00288 }
00289
00290 static void
00291 gtask_view_finalize( GObject *obj ) {
00292 GTaskView *view = GTASK_VIEW( obj );
00293
00294 g_free( view->private );
00295
00296 g_string_free( view->private->filter_string, TRUE );
00297
00298 g_ptr_array_free( view->private->filter_seed, TRUE );
00299
00300 if( G_OBJECT_CLASS( parent_class )->finalize )
00301 G_OBJECT_CLASS( parent_class )->finalize( obj );
00302 }
00303
00304 static gboolean
00305 gtask_view_filter_func( GtkTreeModel *model, GtkTreeIter *iter, gpointer data )
00306 {
00307 GTaskView *view = GTASK_VIEW( data );
00308 GTaskViewPrivate *private = view->private;
00309
00310 gpointer *columns = private->filter_columns->pdata;
00311 GPtrArray *filter_seed = private->filter_seed;
00312 guint n_columns = private->filter_columns->len;
00313 guint i;
00314 guint j;
00315 char *low_str;
00316 char *test_str;
00317
00318 GString *tot_str;
00319
00320 g_debug( "IN: gtask_view_filter_func" );
00321
00322
00323 if( filter_seed->len == 0 ) {
00324 g_debug( "LEAVING: gtask_view_filter_func - filter_seed empty" );
00325 return TRUE;
00326 }
00327
00328
00329 if( n_columns < 1 ) {
00330 g_debug( "LEAVING: gtask_view_filter_func - no filter columns set" );
00331 return TRUE;
00332 }
00333
00334 tot_str = g_string_new( NULL );
00335
00336
00337
00338
00339
00340
00341 for( i = 0; i < n_columns; i++ ) {
00342 const char *val;
00343 GValue value = { 0, };
00344
00345 gtk_tree_model_get_value( GTK_TREE_MODEL( private->task_model ), iter,
00346 GPOINTER_TO_INT( columns[i] ), &value );
00347
00348 val = g_value_get_string( &value );
00349
00350 if( !val ) {
00351 g_value_unset( &value );
00352
00353 continue;
00354 }
00355
00356 g_string_append( tot_str, val );
00357
00358 g_value_unset( &value );
00359 }
00360
00361 low_str = g_utf8_strdown( tot_str->str, -1 );
00362 test_str = g_utf8_normalize( low_str, -1, G_NORMALIZE_ALL );
00363
00364 g_string_free( tot_str, TRUE );
00365 g_free( low_str );
00366
00367 for( j = 0; j < filter_seed->len; j++ ) {
00368 char *seed_tmp = filter_seed->pdata[j];
00369
00370 if( g_strrstr( test_str, seed_tmp ) == NULL ) {
00371 g_free( test_str );
00372
00373 g_debug( "LEAVING: gtask_view_filter_func - row will not be shown" );
00374 return FALSE;
00375 }
00376 }
00377
00378 g_free( test_str );
00379 g_debug( "LEAVING: gtask_view_filter_func - row will be shown" );
00380
00381 return TRUE;
00382 }
00383
00384 static void
00385 gtask_view_create_task_model( GTaskView *view ) {
00386 GTaskViewPrivate *private= view->private;
00387 GtkTreeStore *task_model;
00388 GtkTreeModelFilter *task_model_filter;
00389
00390 g_assert( private->task_model == NULL );
00391 g_assert( private->task_model_filter == NULL );
00392
00393 task_model = gtk_tree_store_new( N_TASK_COLUMNS,
00394 GTASK_GENERIC_TASK_TYPE,
00395
00396 G_TYPE_STRING,
00397 G_TYPE_STRING,
00398
00399 G_TYPE_STRING,
00400 G_TYPE_STRING,
00401
00402 G_TYPE_FLOAT,
00403 G_TYPE_LONG,
00404 GTASK_ACTIVITY_STATE_TYPE,
00405
00406 G_TYPE_STRING,
00407 GDK_TYPE_PIXBUF,
00408 G_TYPE_BOOLEAN );
00409
00410 task_model_filter = GTK_TREE_MODEL_FILTER( gtk_tree_model_filter_new( GTK_TREE_MODEL( task_model ), NULL ) );
00411
00412 gtk_tree_model_filter_set_visible_func( task_model_filter,
00413 gtask_view_filter_func,
00414 view,
00415 NULL );
00416
00417 private->task_model = task_model;
00418 private->task_model_filter = task_model_filter;
00419 }
00420
00421 static void
00422 gtask_view_create_basic_mode( GTaskView *view ) {
00423 GTaskViewPrivate *private = view->private;
00424 GtkTreeViewColumn *info_column;
00425 GtkCellRenderer *rend;
00426 GtkTreeView *task_view;
00427 GtkWidget *task_scroll;
00428
00429 g_assert( private->task_view_basic == NULL );
00430
00431 task_scroll = gtk_scrolled_window_new( NULL, NULL );
00432 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( task_scroll ),
00433 GTK_POLICY_NEVER,
00434 GTK_POLICY_AUTOMATIC );
00435
00436 task_view = GTK_TREE_VIEW( gtk_tree_view_new( ) );
00437
00438 gtk_container_add( GTK_CONTAINER( task_scroll ), GTK_WIDGET( task_view ) );
00439
00440 gtk_notebook_append_page( (GtkNotebook *) view,
00441 GTK_WIDGET( task_scroll ),
00442 NULL );
00443
00444 gtk_tree_view_set_headers_visible( task_view, FALSE );
00445 gtk_tree_view_set_rules_hint( task_view, TRUE );
00446 gtk_tree_view_set_reorderable( task_view, TRUE );
00447
00448 info_column = gtk_tree_view_column_new();
00449
00450 rend = GTK_CELL_RENDERER( gtask_cell_renderer_basic_new( ) );
00451
00452 gtk_tree_view_column_pack_start( info_column, rend, TRUE );
00453 gtk_tree_view_column_set_attributes( info_column, rend,
00454 "progress", COL_PROGRESS,
00455 "title", COL_TITLE,
00456 "status-message", COL_STATUS_MESSAGE,
00457 "activity-state", COL_ACTIVITY_STATE,
00458 "preview", COL_THUMBNAIL,
00459 "preview-is-thumb", COL_IS_THUMBNAIL,
00460 "time-left", COL_TIME_LEFT,
00461 NULL );
00462
00463 gtk_tree_view_append_column( GTK_TREE_VIEW( task_view ), info_column );
00464
00465 g_signal_connect( G_OBJECT( task_view ),
00466 "button-press-event",
00467 G_CALLBACK( gtask_view_button_press ),
00468 view );
00469
00470 private->task_view_basic = task_view;
00471 }
00472
00473 static void
00474 gtask_view_create_list_mode( GTaskView *view ) {
00475 GtkNotebook *view_book = (GtkNotebook *) view;
00476 GTaskViewPrivate *private = view->private;
00477 GtkTreeViewColumn *column;
00478 GtkCellRenderer *rend;
00479 GtkTreeView *task_view;
00480 GtkScrolledWindow *task_list_scroll;
00481
00482 g_assert( private->task_view_list == NULL );
00483
00484 task_list_scroll = GTK_SCROLLED_WINDOW( gtk_scrolled_window_new( NULL, NULL ) );
00485
00486 gtk_scrolled_window_set_policy( task_list_scroll,
00487 GTK_POLICY_AUTOMATIC,
00488 GTK_POLICY_AUTOMATIC );
00489
00490 task_view = GTK_TREE_VIEW( gtk_tree_view_new( ) );
00491
00492 gtk_container_add( GTK_CONTAINER( task_list_scroll ),
00493 GTK_WIDGET( task_view ) );
00494
00495 gtk_notebook_append_page( view_book,
00496 GTK_WIDGET( task_list_scroll ),
00497 NULL );
00498
00499 gtk_tree_view_set_headers_visible( task_view, TRUE );
00500
00501 gtk_tree_view_set_rules_hint( task_view, TRUE );
00502 gtk_tree_view_set_reorderable( task_view, TRUE );
00503
00504 rend = gtk_cell_renderer_text_new();
00505
00506 gtk_tree_view_insert_column_with_attributes( task_view,
00507 0,
00508 "Title",
00509 rend,
00510 "text", COL_TITLE,
00511 NULL );
00512 column = gtk_tree_view_get_column( task_view, 0 );
00513 gtk_tree_view_column_set_resizable( column, TRUE );
00514
00515 gtk_tree_view_insert_column_with_attributes( task_view,
00516 1,
00517 "Status Message",
00518 rend,
00519 "text", COL_STATUS_MESSAGE,
00520 NULL );
00521 column = gtk_tree_view_get_column( task_view, 1 );
00522 gtk_tree_view_column_set_resizable( column, TRUE );
00523
00524 gtk_tree_view_insert_column_with_attributes( task_view,
00525 2,
00526 "Category",
00527 rend,
00528 "text", COL_CATEGORY,
00529 NULL );
00530
00531 column = gtk_tree_view_get_column( task_view, 2 );
00532 gtk_tree_view_column_set_resizable( column, TRUE );
00533
00534 rend = GTK_CELL_RENDERER( gtask_cell_renderer_progress_new( ) );
00535
00536 gtk_tree_view_insert_column_with_attributes( task_view,
00537 3,
00538 "Percent Completed",
00539 rend,
00540 "value", COL_PROGRESS,
00541 "activity-state", COL_ACTIVITY_STATE,
00542 NULL );
00543
00544 column = gtk_tree_view_get_column( task_view, 3 );
00545 gtk_tree_view_column_set_resizable( column, TRUE );
00546
00547 g_signal_connect( G_OBJECT( task_view ),
00548 "button-press-event",
00549 G_CALLBACK( gtask_view_button_press ),
00550 view );
00551
00552 private->task_view_list = task_view;
00553 }
00554
00555 static void
00556 test_close_not( gpointer data, GClosure *closure ) {
00557 g_debug( "===> in test_close_not for: %s", (gchar *) data );
00558 g_free( data );
00559 }
00560
00561 static GtkWidget *
00562 gtask_view_create_file_menu_subitem( GTaskView *view,
00563 GTaskFile *file,
00564 guint bound )
00565 {
00566 GTaskViewPrivate *private = view->private;
00567 GtkWidget *file_item;
00568 const gchar *tmp;
00569 gchar *display_name;
00570
00571 GSList *preview_opts;
00572 GSList *preview_opts_tmp;
00573
00574 GdkPixbuf *preview_pixbuf = NULL;
00575
00576 const gchar *file_uri = gtask_file_get_uri( file );
00577
00578
00579 tmp = gtask_file_get_description( file );
00580
00581 if( tmp&& strlen( tmp ) )
00582 display_name = g_strdup( tmp );
00583 else
00584 display_name = g_path_get_basename( file_uri );
00585
00586 file_item = gtk_image_menu_item_new_with_label( display_name );
00587 g_free( display_name );
00588
00589
00590 preview_opts = gtask_ui_get_file_preview_options( file );
00591
00592 preview_opts_tmp = preview_opts;
00593
00594 while( preview_opts_tmp ) {
00595 GTaskPreview *preview = (GTaskPreview *) preview_opts_tmp->data;
00596
00597 preview_pixbuf = gtask_preview_get_pixbuf( preview,
00598 private->thumbnailer );
00599
00600 if( preview_pixbuf )
00601 break;
00602
00603 preview_opts_tmp = g_slist_next( preview_opts_tmp );
00604 }
00605
00606 gtask_preview_list_free( preview_opts );
00607
00608 if( preview_pixbuf ) {
00609 GtkWidget *thumb_widget;
00610 GdkPixbuf *preview_scaled;
00611
00612 preview_scaled = eel_gdk_pixbuf_scale_down_to_fit( preview_pixbuf,
00613 bound,
00614 bound );
00615
00616 thumb_widget = gtk_image_new_from_pixbuf( preview_scaled );
00617
00618 g_object_unref( (GObject *) preview_pixbuf );
00619 g_object_unref( (GObject *) preview_scaled );
00620
00621 if( thumb_widget ) {
00622 gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM( file_item ),
00623 thumb_widget );
00624 }
00625 }
00626
00627 if( gtask_file_get_completed( file ) ) {
00628 gtk_widget_set_sensitive( file_item, TRUE );
00629
00630 g_signal_connect_data( G_OBJECT( file_item ),
00631 "activate",
00632 G_CALLBACK( gtask_view_open_file ),
00633 g_strdup( file_uri ),
00634 (GClosureNotify) test_close_not,
00635 G_CONNECT_AFTER );
00636 } else {
00637 gtk_widget_set_sensitive( file_item, FALSE );
00638 }
00639
00640 return file_item;
00641 }
00642
00643 static GtkMenuItem *
00644 gtask_view_create_file_menu_item( GTaskView *view,
00645 GTaskFileListInfo *file_info,
00646 guint bound )
00647 {
00648 GtkMenuItem *item;
00649 GList *files;
00650
00651 g_assert( file_info != NULL );
00652
00653 item = GTK_MENU_ITEM( gtk_image_menu_item_new_from_stock( GTK_STOCK_OPEN, NULL ) );
00654
00655 files = file_info->files;
00656
00657 if( file_info->num_files == 1 ) {
00658 GTaskFile *file = (GTaskFile *) files->data;
00659 const gchar *file_uri = gtask_file_get_uri( file );
00660
00661 if( gtask_file_get_completed( file ) ) {
00662 g_signal_connect_data( G_OBJECT( item ),
00663 "activate",
00664 G_CALLBACK( gtask_view_open_file ),
00665 g_strdup( file_uri ),
00666 (GClosureNotify) test_close_not,
00667 G_CONNECT_AFTER );
00668
00669 gtk_widget_set_sensitive( (GtkWidget *) item, TRUE );
00670 } else {
00671 gtk_widget_set_sensitive( (GtkWidget *) item, FALSE );
00672 }
00673 } else if( file_info->num_files > 1 ) {
00674 GtkMenuShell *file_menu = GTK_MENU_SHELL( gtk_menu_new( ) );
00675
00676 g_debug( "=====> %d %p", file_info->num_files, files );
00677
00678 while( files ) {
00679 GTaskFile *file = (GTaskFile *) files->data;
00680 GtkWidget *file_sub;
00681
00682 file_sub = gtask_view_create_file_menu_subitem( view, file, bound );
00683
00684 gtk_menu_shell_append( file_menu, file_sub );
00685 files = g_list_next( files );
00686 }
00687
00688 gtk_menu_item_set_submenu( item, (GtkWidget *) file_menu );
00689 } else {
00690 gtk_widget_set_sensitive( (GtkWidget *) item, FALSE );
00691 }
00692
00693 return item;
00694 }
00695
00696 static GtkMenuItem *
00697 gtask_view_create_dir_menu_item( GTaskView *view,
00698 GTaskFileListInfo *file_info )
00699 {
00700 GtkMenuItem *item;
00701 GList *dirs;
00702
00703 g_assert( file_info != NULL );
00704
00705 item = GTK_MENU_ITEM( gtk_menu_item_new_with_mnemonic( "Open Containing _Folder" ) );
00706
00707 dirs = file_info->unique_directories;
00708
00709 if( file_info->num_unique_directories == 1 ) {
00710 const gchar *dir_uri = (gchar *) dirs->data;
00711
00712 g_signal_connect_data( G_OBJECT( item ),
00713 "activate",
00714 G_CALLBACK( gtask_view_open_dir ),
00715 g_strdup( dir_uri ),
00716 (GClosureNotify) test_close_not,
00717 G_CONNECT_AFTER );
00718
00719 gtk_widget_set_sensitive( (GtkWidget *) item, TRUE );
00720 } else if( file_info->num_unique_directories > 1 ) {
00721 GtkMenuShell *dir_menu = GTK_MENU_SHELL( gtk_menu_new( ) );
00722 GtkWidget *dir_item;
00723
00724 while( dirs ) {
00725 gchar *dir_uri = (gchar *) dirs->data;
00726
00727 dir_item = gtk_menu_item_new_with_label( dir_uri );
00728
00729 g_signal_connect_data( G_OBJECT( dir_item ),
00730 "activate",
00731 G_CALLBACK( gtask_view_open_dir ),
00732 g_strdup( dir_uri ),
00733 (GClosureNotify) test_close_not,
00734 G_CONNECT_AFTER );
00735
00736 gtk_menu_shell_append( dir_menu, dir_item );
00737 dirs = g_list_next( dirs );
00738 }
00739
00740 gtk_menu_item_set_submenu( item, (GtkWidget *) dir_menu );
00741 } else {
00742 gtk_widget_set_sensitive( (GtkWidget *) item, FALSE );
00743 }
00744
00747 return item;
00748 }
00749
00750 static gboolean
00751 gtask_view_button_press( GtkWidget *list,
00752 GdkEventButton *event,
00753 gpointer data )
00754 {
00755 GTaskView *view = GTASK_VIEW( data );
00756 GTaskViewPrivate *private = view->private;
00757
00758 g_debug( "IN: gtask_view_button_press" );
00759
00764 if( event->button == 3 && event->type == GDK_BUTTON_PRESS ) {
00765 GTaskGenericTask *task;
00766 GTaskFileListInfo *file_info;
00767
00768 GList *files;
00769 GtkMenuShell *task_menu;
00770 GtkTreePath *path;
00771 GtkMenuItem *item;
00772 GtkTreeIter iter;
00773 GtkTreeModel *task_model = GTK_TREE_MODEL( private->task_model );
00774 PangoLayout *layout;
00775 guint bound;
00776
00777
00778 if( !gtk_tree_view_get_path_at_pos( GTK_TREE_VIEW( list ),
00779 event->x,
00780 event->y,
00781 &path,
00782 NULL, NULL, NULL)) {
00783 return FALSE;
00784 }
00785
00786 gtk_tree_model_get_iter( task_model, &iter, path );
00787
00788 gtk_tree_model_get( task_model, &iter,
00789 COL_TASK, &task,
00790 -1 );
00791
00792 files = gtask_generic_task_get_files( task );
00793
00798 file_info = gtask_ui_get_file_list_info( files );
00799
00800 task_menu = GTK_MENU_SHELL( gtk_menu_new( ) );
00801
00802 g_signal_connect( task_menu,
00803 "selection-done",
00804 G_CALLBACK( gtk_object_destroy ),
00805 NULL );
00806
00807 layout = gtk_widget_create_pango_layout( list, NULL );
00808
00809 pango_layout_get_pixel_size( layout, NULL, &bound );
00810
00811 g_object_unref( (GObject *) layout );
00812
00813 item = gtask_view_create_file_menu_item( view, file_info, bound );
00814 gtk_menu_shell_append( task_menu, (GtkWidget *) item );
00815
00816 item = gtask_view_create_dir_menu_item( view, file_info );
00817 gtk_menu_shell_append( task_menu, (GtkWidget *) item );
00818
00819 gtk_widget_show_all( (GtkWidget *) task_menu );
00820
00821 gtk_menu_popup( (GtkMenu *) task_menu, NULL, NULL, NULL, NULL, 3, event->time);
00822
00823
00824 g_debug( "LEAVING: gtask_view_button_press" );
00825
00826 return TRUE;
00827 }
00828
00829 g_debug( "LEAVING: gtask_view_button_press" );
00830
00831 return FALSE;
00832 }
00833
00834 static void
00835 close_error_window( GtkDialog *dialog, gint arg1, gpointer data ) {
00836 gtk_widget_destroy( GTK_WIDGET( dialog ) );
00837 }
00838
00839 static void
00840 gtask_view_open_file( GObject *obj, gpointer *data ) {
00841 char *argv[3];
00842 char *file = (char *) data;
00843 GError *error = NULL;
00844
00845 argv[0] = "gnome-open";
00846 argv[1] = file;
00847 argv[2] = NULL;
00848
00849 g_debug( "IN: gtask_view_open_file" );
00850
00851 if( !g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error ) )
00852 {
00853 GtkWidget *mess_win;
00854
00857 mess_win = gtk_message_dialog_new( NULL, 0,
00858 GTK_MESSAGE_ERROR,
00859 GTK_BUTTONS_OK,
00860 error->message );
00861
00862 g_signal_connect( mess_win,
00863 "response",
00864 G_CALLBACK( close_error_window ),
00865 NULL );
00866
00867 gtk_widget_show( mess_win );
00868 }
00869
00870 g_debug( "LEAVING: gtask_view_open_file" );
00871 }
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881 static void
00882 gtask_view_open_dir( GObject *obj, gpointer *data ) {
00883 gchar *argv[3];
00884 gchar *dir = (gchar *) data;
00885 GError *error = NULL;
00886
00887 argv[0] = "nautilus";
00888 argv[1] = dir;
00889 argv[2] = NULL;
00890
00891 g_debug( "IN: gtask_view_open_dir" );
00892
00893 if( !g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error ) )
00894 {
00895 GtkWidget *mess_win;
00896
00899 mess_win = gtk_message_dialog_new( NULL, 0,
00900 GTK_MESSAGE_ERROR,
00901 GTK_BUTTONS_OK,
00902 error->message );
00903
00904 g_signal_connect( mess_win,
00905 "response",
00906 G_CALLBACK( close_error_window ),
00907 NULL );
00908
00909 gtk_widget_show( mess_win );
00910 }
00911
00912 g_debug( "LEAVING: gtask_view_open_dir" );
00913 }
00914
00915 static void
00916 gtask_view_set_property( GObject *object,
00917 guint param_id,
00918 const GValue *value,
00919 GParamSpec *pspec )
00920 {
00921 GTaskView *view = GTASK_VIEW( object );
00922
00923 switch( param_id ) {
00924 case PROP_VIEW_MODE:
00925 gtask_view_set_view_mode( view, g_value_get_enum( value ) );
00926 break;
00927 default:
00928 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, param_id, pspec );
00929 }
00930 }
00931
00932 static void
00933 gtask_view_get_property( GObject *object,
00934 guint param_id,
00935 GValue *value,
00936 GParamSpec *pspec )
00937 {
00938 GTaskView *view = GTASK_VIEW( object );
00939
00940 switch( param_id ) {
00941 case PROP_VIEW_MODE:
00942 g_value_set_enum( value, gtask_view_get_view_mode( view ) );
00943 break;
00944 default:
00945 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, param_id, pspec );
00946 }
00947 }
00948
00949 GTaskView *
00950 gtask_view_new( ) {
00951 return GTASK_VIEW( g_object_new( GTASK_VIEW_TYPE, NULL ) );
00952 }
00953
00954 void
00955 gtask_view_set_view_mode( GTaskView *view, GTaskViewMode mode ) {
00956 GTaskViewPrivate *private;
00957
00958 g_return_if_fail( GTASK_IS_VIEW( view ) );
00959
00960 g_debug( "IN: gtask_view_set_view_mode" );
00961
00962 private = view->private;
00963
00964 if( mode == private->view_mode ) {
00965 g_debug( "LEAVING: gtask_view_set_view_mode - new mode same as old one" );
00966 return;
00967 }
00968
00969 private->view_mode = mode;
00970
00971 if( mode == GTASK_VIEW_MODE_BASIC ) {
00972 gtk_tree_view_set_model( private->task_view_basic,
00973 (GtkTreeModel *) private->task_model_filter );
00974
00975 gtk_notebook_set_current_page( (GtkNotebook *) view, 0 );
00976
00977 gtk_tree_view_set_model( private->task_view_list, NULL );
00978 } else {
00979 gtk_tree_view_set_model( private->task_view_list,
00980 (GtkTreeModel *) private->task_model_filter );
00981
00982 gtk_notebook_set_current_page( (GtkNotebook *) view, 1 );
00983
00984 gtk_tree_view_set_model( private->task_view_basic, NULL );
00985 }
00986
00987 g_object_notify( (GObject *) view, "view-mode" );
00988
00989 g_debug( "LEAVING: gtask_view_set_view_mode - mode changed" );
00990 }
00991
00992 GTaskViewMode
00993 gtask_view_get_view_mode( GTaskView *view ) {
00994 g_return_val_if_fail( GTASK_IS_VIEW( view ), GTASK_VIEW_MODE_BASIC );
00995
00996 return view->private->view_mode;
00997 }
00998
00999 void
01000 gtask_view_add_task( GTaskView *view, GTaskGenericTask *task ) {
01001 GTaskViewPrivate *private = view->private;
01002 const char *task_id;
01003 GTaskTaskIter *task_iter;
01004 guint signal_id;
01005 GList *task_list = NULL;
01006
01007 g_return_if_fail( GTASK_IS_VIEW( view ) );
01008 g_return_if_fail( GTASK_IS_GENERIC_TASK( task ) );
01009
01010 g_warning( "IN: gtask_view_add_task" );
01011
01012 task_id = gtask_generic_task_get_id( task );
01013
01014 task_iter = (GTaskTaskIter *) g_hash_table_lookup( private->task_hash,
01015 task_id );
01016
01019
01020 if( !task_iter ) {
01021 task_iter = g_new0( GTaskTaskIter, 1 );
01022
01023 g_object_ref( task );
01024 task_iter->task = task;
01025
01026 gtk_tree_store_append( private->task_model, &(task_iter->iter), NULL );
01027
01028
01029 gtk_tree_store_set( private->task_model, &(task_iter->iter),
01030 COL_ACTIVITY_STATE, GTASK_ACTIVITY_STATE_ACTIVE,
01031 -1 );
01032
01033 g_hash_table_insert( private->task_hash,
01034 g_strdup( task_id ),
01035 task_iter );
01036
01037 signal_id = GTASK_VIEW_GET_CLASS( view )->tasks_added_signal_id;
01038 } else {
01042 g_object_unref( task_iter->task );
01043 g_object_ref( task );
01044 task_iter->task = task;
01045
01046 signal_id = GTASK_VIEW_GET_CLASS( view )->tasks_updated_signal_id;
01047 }
01048
01049 gtask_view_update_task( view, task_iter );
01050
01051 task_list = g_list_append( task_list, task );
01052
01053 g_signal_emit( view, signal_id, 0, task_list );
01054
01055 g_list_free( task_list );
01056
01057 g_warning( "LEAVING: gtask_view_add_task" );
01058 }
01059
01060 static void
01061 gtask_view_update_task_thumbnail( GTaskView *view, GTaskTaskIter *task_iter ) {
01062 GTaskViewPrivate *private = view->private;
01063 GtkTreeIter iter = task_iter->iter;
01064 GTaskGenericTask *task = task_iter->task;
01065 GtkTreeModel *model = GTK_TREE_MODEL( view->private->task_model );
01066
01067 GTaskActivityState state;
01068 const char *old_thumb_uri;
01069 GValue value = { 0, };
01070
01071 GSList *preview_list;
01072 GSList *preview_list_tmp;
01073
01074 g_debug( "IN: gtask_view_update_task_thumbnail" );
01075
01076 state = gtask_generic_task_get_activity_state( task );
01077
01078 gtk_tree_model_get_value( model, &iter, COL_OLD_THUMB_URI, &value );
01079 old_thumb_uri = g_value_get_string( &value );
01080
01081 if( !old_thumb_uri )
01082 old_thumb_uri = "";
01083
01084 preview_list = gtask_ui_get_task_preview_options( task );
01085
01086 preview_list_tmp = preview_list;
01087
01088 while( preview_list_tmp ) {
01089 GTaskPreview *preview = (GTaskPreview *) preview_list_tmp->data;
01090 GdkPixbuf *preview_pixbuf;
01091
01092 if( strcmp( preview->uri, old_thumb_uri ) == 0 )
01093 break;
01094
01095
01096 preview_pixbuf = gtask_preview_get_pixbuf( preview,
01097 private->thumbnailer );
01098
01099 if( preview_pixbuf ) {
01100 gtk_tree_store_set( GTK_TREE_STORE( private->task_model ), &iter,
01101 COL_THUMBNAIL, preview_pixbuf,
01102 COL_OLD_THUMB_URI, preview->uri,
01103 COL_IS_THUMBNAIL, preview->is_thumbnail,
01104 -1 );
01105
01106 g_object_unref( (GObject *) preview_pixbuf );
01107 }
01108
01109 preview_list_tmp = g_slist_next( preview_list_tmp );
01110 }
01111
01112 gtask_preview_list_free( preview_list );
01113
01114 g_value_unset( &value );
01115
01116 g_debug( "LEAVING: gtask_view_update_task_thumbnail" );
01117 }
01118
01119 static void
01120 gtask_view_update_task( GTaskView *view, GTaskTaskIter *task_iter ) {
01121 GtkTreeIter iter = task_iter->iter;
01122 GTaskGenericTask *task = task_iter->task;
01123
01124 GTaskViewPrivate *private = view->private;
01125 GTaskActivityState prev_activity_state;
01126 GTaskActivityState curr_activity_state;
01127
01128 const char *status_msg;
01129 const char *error_msg;
01130 const char *title;
01131 const char *category;
01132 const char *thumb_uri;
01133 gfloat percent;
01134 glong time_left;
01135
01136 GValue val = { 0, };
01137
01138 g_debug( "IN: gtask_view_update_task" );
01139
01140 gtk_tree_model_get_value( GTK_TREE_MODEL( private->task_model ), &iter,
01141 COL_ACTIVITY_STATE, &val );
01142
01143 prev_activity_state = g_value_get_enum( &val );
01144
01145 g_value_unset( &val );
01146
01147 curr_activity_state = gtask_generic_task_get_activity_state( task );
01148
01149 g_warning( "==========> %s : prev-%d : curr-%d", gtask_generic_task_get_title( task ), prev_activity_state, curr_activity_state );
01150
01151 if( curr_activity_state == GTASK_ACTIVITY_STATE_COMPLETED &&
01152 prev_activity_state == GTASK_ACTIVITY_STATE_COMPLETED )
01153 {
01154 gtask_view_update_task_thumbnail( view, task_iter );
01159 g_debug( "LEAVING: gtask_view_update_task - prev status was COMPLETED" );
01160
01161 return;
01162 }
01163
01164 status_msg = gtask_generic_task_get_status_message( task );
01165 error_msg = gtask_generic_task_get_error_message( task );
01166
01167 if( curr_activity_state == GTASK_ACTIVITY_STATE_INACTIVE &&
01168 prev_activity_state == GTASK_ACTIVITY_STATE_INACTIVE )
01169 {
01170 gtk_tree_store_set( private->task_model, &iter,
01171 COL_TASK, task,
01172 COL_STATUS_MESSAGE, status_msg,
01173 COL_ERROR_MESSAGE, error_msg,
01174 -1 );
01175 g_debug( "LEAVING: gtask_view_update_task - last 2 status were INACTIVE" );
01176
01177 return;
01178 }
01179
01180 gtask_view_update_task_thumbnail( view, task_iter );
01181
01182 percent = gtask_generic_task_get_percent_done( task );;
01183 title = gtask_generic_task_get_title( task );
01184 category = gtask_generic_task_get_category( task );
01185 thumb_uri = gtask_generic_task_get_thumbnail_uri( task );
01186 time_left = gtask_generic_task_get_time_left( task );
01187
01188 gtk_tree_store_set( private->task_model, &iter,
01189 COL_TASK, task,
01190 COL_PROGRESS, percent,
01191 COL_TITLE, title,
01192 COL_STATUS_MESSAGE, status_msg,
01193 COL_CATEGORY, category,
01194 COL_ACTIVITY_STATE, curr_activity_state,
01195 COL_TIME_LEFT, time_left,
01196 -1 );
01197
01198 g_debug( "LEAVING: gtask_view_update_task - task updated" );
01199 }
01200
01201 static gboolean
01202 remove_task( gpointer key, gpointer value, gpointer user_data ) {
01203 struct GTaskGetTaskFoo *boo = (struct GTaskGetTaskFoo *) user_data;
01204
01205 GTaskView *view = GTASK_VIEW( boo->view );
01206 GTaskTaskIter *iter = (GTaskTaskIter *) value;
01207 GTaskGenericTask *task = iter->task;
01208 GTaskActivityState state = gtask_generic_task_get_activity_state( task );
01209 gboolean remove = FALSE;
01210
01211 switch( state ) {
01212 case GTASK_ACTIVITY_STATE_ACTIVE:
01213 if( boo->with_active )
01214 remove = TRUE;
01215 break;
01216 case GTASK_ACTIVITY_STATE_INACTIVE:
01217 if( boo->with_inactive )
01218 remove = TRUE;
01219 break;
01220 case GTASK_ACTIVITY_STATE_COMPLETED:
01221 if( boo->with_completed )
01222 remove = TRUE;
01223 break;
01224 default:
01225 break;
01226 }
01227
01228 if( remove ) {
01229 gtk_tree_store_remove( view->private->task_model, &(iter->iter) );
01230
01231 g_object_ref( task );
01232
01233 boo->tasks = g_list_append( boo->tasks, task );
01234
01235 return TRUE;
01236 }
01237
01238 return FALSE;
01239 }
01240
01241 void
01242 gtask_view_clear_tasks( GTaskView *view, GTaskActivityState with_state ) {
01243 GTaskViewPrivate *private;
01244 struct GTaskGetTaskFoo boo = { 0, NULL, NULL };
01245
01246 g_debug( "IN: gtask_view_clear_tasks" );
01247
01248 boo.view = view;
01249 boo.with_active = ( with_state & GTASK_ACTIVITY_STATE_ACTIVE ) != 0;
01250 boo.with_inactive = ( with_state & GTASK_ACTIVITY_STATE_INACTIVE ) != 0;
01251 boo.with_completed = ( with_state & GTASK_ACTIVITY_STATE_COMPLETED ) != 0;
01252
01253 g_return_if_fail( GTASK_IS_VIEW( view ) );
01254
01255 private = view->private;
01256
01257
01258
01259 g_hash_table_foreach_remove( private->task_hash, remove_task, &boo );
01260
01261 g_signal_emit( view,
01262 GTASK_VIEW_GET_CLASS( view )->tasks_removed_signal_id,
01263 0,
01264 boo.tasks );
01265
01266 g_list_foreach( boo.tasks, (GFunc) g_object_unref, NULL );
01267
01268 g_debug( "LEAVING: gtask_view_clear_tasks" );
01269 }
01270
01271 void
01272 gtask_view_set_filter_string( GTaskView *view, const gchar *str ) {
01273 GTaskViewPrivate *private;
01274 PangoLogAttr *attrs;
01275 char *tmp_string;
01276 char *norm_string;
01277 guint word_start = 0;
01278 guint str_len;
01279 guint i;
01280
01281 g_return_if_fail( GTASK_IS_VIEW( view ) );
01282 g_return_if_fail( str != NULL );
01283 g_return_if_fail( g_utf8_validate( str, -1, NULL ) );
01284
01285 g_debug( "IN: gtask_view_set_filter_string" );
01286
01287 private = view->private;
01288
01289 tmp_string = g_utf8_strdown( str, -1 );
01290 norm_string = g_utf8_normalize( tmp_string, -1, G_NORMALIZE_ALL );
01291
01292 for( i = 0; i < private->filter_seed->len; i++ ) {
01293 g_debug( "ffff" );
01294 g_free( private->filter_seed->pdata[i] );
01295 }
01296
01297 g_ptr_array_set_size( private->filter_seed, 0 );
01298
01299 str_len = g_utf8_strlen( norm_string, -1 );
01300 attrs = g_new0( PangoLogAttr, str_len + 1 );
01301
01303 pango_get_log_attrs( norm_string, -1, -1, NULL, attrs, str_len + 1 );
01304
01305 g_debug( "start splitting string" );
01306 for( i = 0; i < str_len + 1; i++ ) {
01307 char *start_word;
01308 char *end_word;
01309
01310 if( attrs[i].is_word_end ) {
01311 char *word;
01312
01313 start_word = g_utf8_offset_to_pointer( norm_string, word_start );
01314 end_word = g_utf8_offset_to_pointer( norm_string, i );
01315
01316 word = g_strndup( start_word, end_word - start_word );
01317
01318 g_ptr_array_add( private->filter_seed, word );
01319 }
01320
01321 if( attrs[i].is_word_start )
01322 word_start = i;
01323 }
01324 g_debug( "end splitting string" );
01325
01326 g_string_assign( private->filter_string, str );
01327
01328 g_free( attrs );
01329 g_free( tmp_string );
01330 g_free( norm_string );
01331
01332
01333
01334 gtk_tree_model_filter_refilter( private->task_model_filter );
01335
01336 g_debug( "LEAVING: gtask_view_set_filter_string" );
01337 }
01338
01339 const gchar *
01340 gtask_view_get_filter_string( GTaskView *view ) {
01341 g_return_val_if_fail( GTASK_IS_VIEW( view ), "" );
01342
01343 return view->private->filter_string->str;
01344 }
01345
01346 static void
01347 sum_tasks( gpointer key, gpointer value, gpointer user_data ) {
01348 struct GTaskGetTaskFoo *boo = (struct GTaskGetTaskFoo *) user_data;
01349
01350 GTaskTaskIter *iter = (GTaskTaskIter *) value;
01351
01352 GTaskGenericTask *task = GTASK_GENERIC_TASK( iter->task );
01353 GTaskActivityState state = gtask_generic_task_get_activity_state( task );
01354
01355 switch( state ) {
01356 case GTASK_ACTIVITY_STATE_ACTIVE:
01357 if( boo->with_active )
01358 boo->num++;
01359 break;
01360 case GTASK_ACTIVITY_STATE_INACTIVE:
01361 if( boo->with_inactive )
01362 boo->num++;
01363 break;
01364 case GTASK_ACTIVITY_STATE_COMPLETED:
01365 if( boo->with_completed )
01366 boo->num++;
01367 break;
01368 default:
01369 break;
01370 }
01371 }
01372
01373 guint
01374 gtask_view_get_number_of_tasks( GTaskView *view,
01375 GTaskActivityState with_state )
01376 {
01377 struct GTaskGetTaskFoo boo = { 0, NULL, NULL, };
01378
01379 g_debug( "IN: gtask_view_get_number_of_tasks" );
01380
01381 boo.with_active = ( with_state & GTASK_ACTIVITY_STATE_ACTIVE ) != 0;
01382 boo.with_inactive = ( with_state & GTASK_ACTIVITY_STATE_INACTIVE ) != 0;
01383 boo.with_completed = ( with_state & GTASK_ACTIVITY_STATE_COMPLETED ) != 0;
01384
01385 g_return_val_if_fail( GTASK_IS_VIEW( view ), 0 );
01386
01387 g_hash_table_foreach( view->private->task_hash, sum_tasks, &boo );
01388
01389 g_debug( "LEAVING: gtask_view_get_number_of_tasks" );
01390
01391 return boo.num;
01392 }
01393
01394