63
75
GParamSpec *pspec);
64
76
static gboolean file_list_button_press (GtkWidget *widget,
65
77
GdkEventButton *event);
78
static void file_list_project_loaded (GiggleFileList *list);
79
static void file_list_status_changed (GiggleFileList *list);
67
81
static void file_list_directory_changed (GObject *object,
69
83
gpointer user_data);
70
static void file_list_managed_files_changed (GiggleFileList *list);
84
static void file_list_files_status_changed (GiggleFileList *list);
72
86
static void file_list_populate (GiggleFileList *list);
73
87
static void file_list_populate_dir (GiggleFileList *list,
114
128
GtkTreeModel *tree_model,
115
129
GtkTreeIter *iter,
131
static void file_list_cell_pixbuf_func (GtkCellLayout *cell_layout,
132
GtkCellRenderer *renderer,
133
GtkTreeModel *tree_model,
136
static void file_list_idle_data_free (IdleLoaderData *data);
118
139
G_DEFINE_TYPE (GiggleFileList, giggle_file_list, GTK_TYPE_TREE_VIEW)
143
165
static guint signals[LAST_SIGNAL] = { 0, };
145
167
static GtkActionEntry menu_items [] = {
146
{ "Diff", NULL, N_("_Diff"), NULL, NULL, G_CALLBACK (file_list_diff_file) },
168
{ "Commit", NULL, N_("_Commit Changes"), NULL, NULL, G_CALLBACK (file_list_diff_file) },
147
169
{ "AddFile", NULL, N_("A_dd file to repository"), NULL, NULL, G_CALLBACK (file_list_add_file) },
148
170
{ "Ignore", GTK_STOCK_ADD, N_("_Add to .gitignore"), NULL, NULL, G_CALLBACK (file_list_ignore_file) },
149
171
{ "Unignore", GTK_STOCK_REMOVE, N_("_Remove from .gitignore"), NULL, NULL, G_CALLBACK (file_list_unignore_file) },
206
230
1, G_TYPE_STRING);
232
signals[PROJECT_LOADED] =
233
g_signal_new ("project-loaded",
234
G_OBJECT_CLASS_TYPE (object_class),
236
G_STRUCT_OFFSET (GiggleFileListClass, project_loaded),
238
g_cclosure_marshal_VOID__VOID,
241
signals[STATUS_CHANGED] =
242
g_signal_new ("status-changed",
243
G_OBJECT_CLASS_TYPE (object_class),
245
G_STRUCT_OFFSET (GiggleFileListClass, status_changed),
247
g_cclosure_marshal_VOID__VOID,
208
250
g_type_class_add_private (object_class, sizeof (GiggleFileListPriv));
212
giggle_file_list_init (GiggleFileList *list)
254
file_list_create_store (GiggleFileList *list)
214
256
GiggleFileListPriv *priv;
215
GtkCellRenderer *renderer;
216
GtkTreeViewColumn *column;
217
GtkActionGroup *action_group;
218
GtkTreeSelection *selection;
220
258
priv = GET_PRIV (list);
222
priv->git = giggle_git_get ();
223
g_signal_connect (priv->git, "notify::project-dir",
224
G_CALLBACK (file_list_directory_changed), list);
225
g_signal_connect_swapped (priv->git, "notify::git-dir",
226
G_CALLBACK (file_list_managed_files_changed), list);
228
priv->icon_theme = gtk_icon_theme_get_default ();
230
priv->store = gtk_tree_store_new (LAST_COL, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF,
231
GIGGLE_TYPE_GIT_IGNORE, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
260
gtk_tree_view_set_model (GTK_TREE_VIEW (list), NULL);
263
g_object_unref (priv->store);
266
if (priv->filter_model) {
267
g_object_unref (priv->filter_model);
270
priv->store = gtk_tree_store_new (LAST_COL, G_TYPE_STRING, G_TYPE_STRING, GIGGLE_TYPE_GIT_LIST_FILES_STATUS,
271
GIGGLE_TYPE_GIT_IGNORE, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
232
272
priv->filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->store), NULL);
234
274
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
242
282
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->store),
243
283
COL_NAME, GTK_SORT_ASCENDING);
245
gtk_tree_view_set_model (GTK_TREE_VIEW (list), priv->filter_model);
287
giggle_file_list_init (GiggleFileList *list)
289
GiggleFileListPriv *priv;
290
GtkCellRenderer *renderer;
291
GtkTreeViewColumn *column;
292
GtkActionGroup *action_group;
293
GtkTreeSelection *selection;
295
priv = GET_PRIV (list);
297
priv->idle_jobs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
298
(GDestroyNotify) file_list_idle_data_free,
299
(GDestroyNotify) g_source_remove);
301
priv->git = giggle_git_get ();
302
g_signal_connect (priv->git, "notify::project-dir",
303
G_CALLBACK (file_list_directory_changed), list);
304
g_signal_connect_swapped (priv->git, "changed",
305
G_CALLBACK (file_list_files_status_changed), list);
307
priv->icon_theme = gtk_icon_theme_get_default ();
247
309
gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (list),
248
310
file_list_search_equal_func,
251
313
column = gtk_tree_view_column_new ();
252
314
gtk_tree_view_column_set_title (column, _("Project"));
254
316
renderer = gtk_cell_renderer_pixbuf_new ();
255
317
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer, FALSE);
256
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (column), renderer,
257
"pixbuf", COL_PIXBUF,
259
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer,
260
file_list_cell_data_sensitive_func,
262
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer,
263
file_list_cell_data_background_func,
319
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer,
320
file_list_cell_pixbuf_func,
266
323
renderer = gtk_cell_renderer_text_new ();
283
339
G_N_ELEMENTS (menu_items), list);
284
340
gtk_action_group_add_toggle_actions (action_group, toggle_menu_items,
285
341
G_N_ELEMENTS (toggle_menu_items), list);
287
343
priv->ui_manager = gtk_ui_manager_new ();
288
344
gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, 0);
383
441
file_list_button_press (GtkWidget *widget,
384
442
GdkEventButton *event)
386
GiggleFileList *list;
387
GiggleFileListPriv *priv;
388
gboolean add, ignore, unignore;
390
GiggleGitIgnore *git_ignore;
392
GtkTreeSelection *selection;
444
GiggleFileList *list;
445
GiggleFileListPriv *priv;
446
gboolean add, ignore, unignore;
448
GiggleGitIgnore *git_ignore;
450
GtkTreeSelection *selection;
455
GiggleGitListFilesStatus status;
398
457
list = GIGGLE_FILE_LIST (widget);
399
458
priv = GET_PRIV (list);
422
481
gtk_tree_model_get_iter (model, &iter, l->data);
424
483
gtk_tree_model_get (model, &iter,
484
COL_FILE_STATUS, &status,
487
add = (status == GIGGLE_GIT_FILE_STATUS_OTHER);
428
489
if (file_list_get_path_and_ignore_for_iter (list, &iter, &file_path, &git_ignore)) {
429
490
if (giggle_git_ignore_path_matches (git_ignore, file_path)) {
443
504
gtk_action_set_sensitive (action, ignore);
444
505
action = gtk_ui_manager_get_action (priv->ui_manager, "/ui/PopupMenu/Unignore");
445
506
gtk_action_set_sensitive (action, unignore);
447
508
gtk_menu_popup (GTK_MENU (priv->popup), NULL, NULL,
448
509
NULL, NULL, event->button, event->time);
458
519
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
459
520
rows = gtk_tree_selection_get_selected_rows (selection, &model);
461
g_assert (rows != NULL);
463
/* there should be just one item selected */
464
gtk_tree_model_get_iter (model, &iter, rows->data);
466
file_list_get_path_and_ignore_for_iter (list, &iter, &file_path, NULL);
467
g_signal_emit (widget, signals[PATH_SELECTED], 0, file_path);
523
gtk_tree_model_get_iter (model, &iter, rows->data);
525
file_list_get_path_and_ignore_for_iter (list, &iter, &file_path, NULL);
526
g_signal_emit (widget, signals[PATH_SELECTED], 0, file_path);
536
file_list_project_loaded (GiggleFileList *list)
538
/* update files status */
539
file_list_files_status_changed (list);
543
file_list_status_changed (GiggleFileList *list)
545
GiggleFileListPriv *priv;
548
if (gtk_tree_view_get_model (GTK_TREE_VIEW (list))) {
552
priv = GET_PRIV (list);
553
gtk_tree_view_set_model (GTK_TREE_VIEW (list), priv->filter_model);
555
/* expand the project folder */
556
path = gtk_tree_path_new_first ();
557
gtk_tree_view_expand_row (GTK_TREE_VIEW (list), path, FALSE);
558
gtk_tree_path_free (path);
476
562
file_list_directory_changed (GObject *object,
477
563
GParamSpec *pspec,
478
564
gpointer user_data)
483
569
list = GIGGLE_FILE_LIST (user_data);
484
570
priv = GET_PRIV (list);
486
gtk_tree_store_clear (priv->store);
572
/* remove old directory idles */
573
g_hash_table_remove_all (priv->idle_jobs);
575
file_list_create_store (list);
487
576
file_list_populate (list);
491
file_list_update_managed (GiggleFileList *file_list,
493
const gchar *parent_path,
580
file_list_update_files_status (GiggleFileList *file_list,
582
GiggleGitListFiles *list_files)
496
GiggleFileListPriv *priv;
499
GiggleGitIgnore *git_ignore;
584
GiggleFileListPriv *priv;
588
GiggleGitListFilesStatus status;
503
590
priv = GET_PRIV (file_list);
513
600
gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
515
COL_GIT_IGNORE, &git_ignore,
601
COL_REL_PATH, &rel_path,
519
path = g_build_filename (parent_path, name, NULL);
520
managed = (g_list_find_custom (files, path, (GCompareFunc) strcmp) != NULL);
605
status = giggle_git_list_files_get_file_status (list_files, rel_path);
522
/* we don't want the project basename included */
523
path = g_strdup ("");
607
status = GIGGLE_GIT_FILE_STATUS_CACHED;
610
if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (priv->store), &iter)) {
611
/* it's a directory */
612
file_list_update_files_status (file_list, &iter, list_files);
613
status = GIGGLE_GIT_FILE_STATUS_CACHED;
527
616
gtk_tree_store_set (priv->store, &iter,
528
COL_MANAGED, managed,
617
COL_FILE_STATUS, status,
532
/* it's a directory */
533
file_list_update_managed (file_list, &iter, path, files);
534
g_object_unref (git_ignore);
539
621
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter);
566
647
gtk_dialog_run (GTK_DIALOG (dialog));
567
648
gtk_widget_destroy (dialog);
569
files = giggle_git_list_files_get_files (GIGGLE_GIT_LIST_FILES (priv->job));
570
file_list_update_managed (list, NULL, NULL, files);
650
file_list_update_files_status (list, NULL, GIGGLE_GIT_LIST_FILES (priv->job));
651
g_signal_emit (list, signals[STATUS_CHANGED], 0);
573
654
g_object_unref (priv->job);
578
file_list_managed_files_changed (GiggleFileList *list)
659
file_list_files_status_changed (GiggleFileList *list)
580
661
GiggleFileListPriv *priv;
582
663
priv = GET_PRIV (list);
584
file_list_update_managed (list, NULL, NULL, NULL);
587
666
giggle_git_cancel_job (priv->git, priv->job);
588
667
g_object_unref (priv->job);
616
695
gtk_widget_show (priv->diff_window);
699
file_list_populate_dir_idle (gpointer user_data)
701
GiggleFileListPriv *priv;
702
GiggleFileList *list;
703
IdleLoaderData *data;
704
GiggleGitIgnore *git_ignore;
707
data = (IdleLoaderData *) user_data;
708
list = g_object_ref (data->list);
709
priv = GET_PRIV (list);
711
full_path = g_build_filename (data->directory, data->rel_path, NULL);
712
git_ignore = giggle_git_ignore_new (full_path);
714
gtk_tree_store_set (priv->store, &data->parent_iter,
715
COL_GIT_IGNORE, git_ignore,
718
file_list_populate_dir (data->list, data->directory,
719
data->rel_path, &data->parent_iter);
721
/* remove this data from the table */
722
g_hash_table_remove (priv->idle_jobs, data);
724
if (g_hash_table_size (priv->idle_jobs) == 0) {
725
/* no remaining jobs */
726
g_signal_emit (list, signals[PROJECT_LOADED], 0);
729
g_object_unref (git_ignore);
730
g_object_unref (list);
737
file_list_idle_data_free (IdleLoaderData *data)
739
g_free (data->directory);
740
g_free (data->rel_path);
741
g_object_unref (data->list);
620
746
file_list_add_element (GiggleFileList *list,
621
747
const gchar *directory,
643
file_list_populate_dir (list, directory, rel_path, &iter);
644
pixbuf = gtk_icon_theme_load_icon (priv->icon_theme,
645
"folder", 16, 0, NULL);;
646
git_ignore = giggle_git_ignore_new (full_path);
648
pixbuf = gtk_icon_theme_load_icon (priv->icon_theme,
649
"text-x-generic", 16, 0, NULL);;
652
766
gtk_tree_store_set (priv->store, &iter,
654
767
COL_NAME, (name) ? name : full_path,
655
768
COL_REL_PATH, rel_path,
656
COL_GIT_IGNORE, git_ignore,
659
g_object_unref (pixbuf);
663
g_object_unref (git_ignore);
772
IdleLoaderData *data;
775
data = g_new0 (IdleLoaderData, 1);
776
data->list = g_object_ref (list);
777
data->directory = g_strdup (directory);
778
data->rel_path = g_strdup (rel_path);
779
data->parent_iter = iter;
781
idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW + 1,
782
file_list_populate_dir_idle,
785
g_hash_table_insert (priv->idle_jobs, data, GUINT_TO_POINTER (idle_id));
666
788
g_free (full_path);
699
821
GiggleFileListPriv *priv;
700
822
const gchar *directory;
703
824
priv = GET_PRIV (list);
704
825
directory = giggle_git_get_project_dir (priv->git);
706
file_list_add_element (list, directory, "", NULL, NULL);
708
/* expand the project folder */
709
path = gtk_tree_path_new_first ();
710
gtk_tree_view_expand_row (GTK_TREE_VIEW (list), path, FALSE);
711
gtk_tree_path_free (path);
828
file_list_add_element (list, directory, "", NULL, NULL);
879
997
gchar *normalized_key, *normalized_str;
880
998
gchar *casefold_key, *casefold_str;
883
1001
gtk_tree_model_get (model, iter, column, &str, -1);
885
1003
normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
1098
/* check whether the file is managed by git */
1216
GiggleGitListFilesStatus status;
1099
1218
gtk_tree_model_get (tree_model, iter,
1100
COL_MANAGED, &value,
1219
COL_FILE_STATUS, &status,
1222
value = !(status == GIGGLE_GIT_FILE_STATUS_OTHER);
1105
1225
if (GTK_IS_CELL_RENDERER_TEXT (renderer)) {
1127
1247
GiggleFileListPriv *priv;
1128
1248
GiggleFileList *file_list;
1129
1249
gboolean highlight;
1131
1252
file_list = GIGGLE_FILE_LIST (data);
1132
1253
priv = GET_PRIV (file_list);
1134
1255
gtk_tree_model_get (tree_model, iter,
1256
COL_REL_PATH, &rel_path,
1135
1257
COL_HIGHLIGHT, &highlight,
1138
g_object_set (G_OBJECT (renderer),
1139
"cell-background-gdk", (highlight) ? &color : NULL,
1260
g_object_set (G_OBJECT (renderer), "cell-background-gdk",
1261
(rel_path && *rel_path && highlight) ? &color : NULL,
1268
file_list_cell_pixbuf_func (GtkCellLayout *layout,
1269
GtkCellRenderer *renderer,
1270
GtkTreeModel *tree_model,
1274
GiggleFileListPriv *priv;
1275
GiggleGitIgnore *git_ignore;
1276
GiggleGitListFilesStatus status;
1277
GdkPixbuf *pixbuf = NULL;
1278
const gchar *icon_name;
1280
file_list_cell_data_sensitive_func (layout, renderer, tree_model, iter, data);
1282
priv = GET_PRIV (data);
1284
gtk_tree_model_get (tree_model, iter,
1285
COL_FILE_STATUS, &status,
1286
COL_GIT_IGNORE, &git_ignore,
1291
icon_name = "folder";
1292
g_object_unref (git_ignore);
1295
case GIGGLE_GIT_FILE_STATUS_OTHER:
1296
case GIGGLE_GIT_FILE_STATUS_CACHED:
1297
icon_name = "text-x-generic";
1299
case GIGGLE_GIT_FILE_STATUS_CHANGED:
1300
icon_name = "gtk-new";
1302
case GIGGLE_GIT_FILE_STATUS_DELETED:
1303
case GIGGLE_GIT_FILE_STATUS_UNMERGED:
1304
icon_name = "gtk-delete";
1306
case GIGGLE_GIT_FILE_STATUS_KILLED:
1307
icon_name = "gtk-stop";
1310
g_assert_not_reached ();
1315
pixbuf = gtk_icon_theme_load_icon (priv->icon_theme,
1316
icon_name, 16, 0, NULL);
1318
g_object_set (renderer, "pixbuf", pixbuf, NULL);
1321
g_object_unref (pixbuf);
1324
g_object_set (renderer, "pixbuf", NULL, NULL);
1285
1470
while (valid) {
1286
1471
gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
1288
1472
COL_REL_PATH, &rel_path,
1289
1473
COL_GIT_IGNORE, &git_ignore,
1292
if (rel_path && *rel_path) {
1293
highlight = (g_list_find_custom (files, rel_path, (GCompareFunc) file_list_compare_prefix) != NULL);
1295
/* we don't want the project basename included */
1476
highlight = (g_list_find_custom (files, rel_path, (GCompareFunc) file_list_compare_prefix) != NULL);
1299
1478
gtk_tree_store_set (priv->store, &iter,
1300
1479
COL_HIGHLIGHT, highlight,
1482
if (highlight && git_ignore) {
1304
1483
/* it's a directory */
1305
1484
file_list_update_highlight (file_list, &iter, rel_path, files);
1306
1485
g_object_unref (git_ignore);
1353
1532
GiggleFileListPriv *priv;
1355
1534
g_return_if_fail (GIGGLE_IS_FILE_LIST (list));
1356
g_return_if_fail (GIGGLE_IS_REVISION (from));
1357
g_return_if_fail (GIGGLE_IS_REVISION (to));
1535
g_return_if_fail (!from || GIGGLE_IS_REVISION (from));
1536
g_return_if_fail (!to || GIGGLE_IS_REVISION (to));
1359
1538
priv = GET_PRIV (list);
1361
1540
/* clear highlights */
1362
1541
file_list_update_highlight (list, NULL, NULL, NULL);
1365
giggle_git_cancel_job (priv->git, priv->job);
1366
g_object_unref (priv->job);
1545
giggle_git_cancel_job (priv->git, priv->job);
1546
g_object_unref (priv->job);
1550
priv->job = giggle_git_diff_tree_new (from, to);
1552
giggle_git_run_job (priv->git,
1554
file_list_job_callback,
1370
priv->job = giggle_git_diff_tree_new (from, to);
1372
giggle_git_run_job (priv->git,
1374
file_list_job_callback,