2 * geanyvc.c - Plugin to geany light IDE to work with vc
4 * Copyright 2007 Frank Lanitz <frank(at)frank(dot)uvena(dot)de>
5 * Copyright 2007 Enrico Tröger <enrico.troeger@uvena.de>
6 * Copyright 2007 Nick Treleaven <nick.treleaven@btinternet.com>
7 * Copyright 2007 Yura Siamashka <yurand2@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 /* This plugin allow to works with cvs/svn/git inside geany light IDE. */
29 #include "plugindata.h"
31 #include "filetypes.h"
34 #include "pluginmacros.h"
38 PluginFields
*plugin_fields
;
39 GeanyData
*geany_data
;
43 PLUGIN_INFO(_("VC"), _("Interface to different Version Control systems."), VERSION
,
44 _("Yura Siamashka <yurand2@gmail.com>,\nFrank Lanitz <frank@frank.uvena.de>"));
46 /* Some global variables */
47 static gboolean set_changed_flag
;
48 static gboolean set_add_confirmation
;
49 static gchar
*config_file
;
51 static gboolean enable_cvs
;
52 static gboolean enable_git
;
53 static gboolean enable_svn
;
54 static gboolean enable_svk
;
56 static GSList
*VC
= NULL
;
58 /* The addresses of these strings act as enums, their contents are not used. */
59 const gchar DIRNAME
[] = "*DIRNAME*";
60 const gchar FILENAME
[] = "*FILENAME*";
61 const gchar BASE_FILENAME
[] = "*BASE_FILENAME*";
62 const gchar FILE_LIST
[] = "*FILE_LIST*";
63 const gchar MESSAGE
[] = "*MESSAGE*";
65 /* this string is used when action require to run several commands */
66 const gchar CMD_SEPARATOR
[] = "*CMD-SEPARATOR*";
69 const gchar FILE_STATUS_MODIFIED
[] = "Modified";
70 const gchar FILE_STATUS_ADDED
[] = "Added";
71 const gchar FILE_STATUS_DELETED
[] = "Deleted";
72 const gchar FILE_STATUS_UNKNOWN
[] = "Unknown";
74 void *NO_ENV
[] = { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
76 static void registrate();
79 get_commit_files_null(const gchar
* dir
)
85 free_commit_list(GSList
* lst
)
90 for (tmp
= lst
; tmp
!= NULL
; tmp
= g_slist_next(tmp
))
92 g_free(((CommitItem
*) (tmp
->data
))->path
);
93 g_free((CommitItem
*) (tmp
->data
));
99 find_subdir_path(const gchar
* filename
, const gchar
* subdir
)
101 gboolean ret
= FALSE
;
104 gchar
*base_prev
= g_strdup(":");
106 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
107 base
= g_strdup(filename
);
109 base
= g_path_get_dirname(filename
);
111 while (strcmp(base
, base_prev
) != 0)
113 gitdir
= g_build_path("/", base
, subdir
, NULL
);
114 ret
= g_file_test(gitdir
, G_FILE_TEST_IS_DIR
);
120 base
= g_path_get_dirname(base
);
131 find_subdir(const gchar
* filename
, const gchar
* subdir
)
134 basedir
= find_subdir_path(filename
, subdir
);
144 find_dir(const gchar
* filename
, const char *find
, gboolean recursive
)
156 ret
= find_subdir(filename
, find
);
160 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
161 base
= g_strdup(filename
);
163 base
= g_path_get_dirname(filename
);
164 dir
= g_build_path("/", base
, find
, NULL
);
166 ret
= g_file_test(dir
, G_FILE_TEST_IS_DIR
);
175 static const VC_RECORD
*
176 find_vc(const char *filename
)
180 for (tmp
= VC
; tmp
!= NULL
; tmp
= g_slist_next(tmp
))
182 if (((VC_RECORD
*) tmp
->data
)->in_vc(filename
))
184 return (VC_RECORD
*) tmp
->data
;
191 find_cmd_env(gint cmd_type
, gboolean cmd
, const gchar
* filename
)
194 vc
= find_vc(filename
);
198 return vc
->commands
[cmd_type
];
200 return vc
->envs
[cmd_type
];
205 /* Get list of commands for given command spec*/
207 get_cmd(const gchar
** argv
, const gchar
* filename
, GSList
* filelist
, const gchar
* message
)
213 gchar
*base_filename
;
217 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
219 dir
= g_strdup(filename
);
223 dir
= g_path_get_dirname(filename
);
225 base_filename
= g_path_get_basename(filename
);
229 if (argv
[len
] == NULL
)
234 ret
= g_malloc0(sizeof(gchar
*) * (len
* g_slist_length(filelist
) + 1));
236 ret
= g_malloc0(sizeof(gchar
*) * (len
+ 1));
238 head
= g_slist_alloc();
241 for (i
= 0, j
= 0; i
< len
; i
++, j
++)
243 if (argv
[i
] == CMD_SEPARATOR
)
246 ret
= g_malloc0(sizeof(gchar
*) *
247 (len
* g_slist_length(filelist
) + 1));
249 ret
= g_malloc0(sizeof(gchar
*) * (len
+ 1));
251 head
= g_slist_append(head
, ret
);
253 else if (argv
[i
] == DIRNAME
)
255 ret
[j
] = g_strdup(dir
);
257 else if (argv
[i
] == FILENAME
)
259 ret
[j
] = g_strdup(filename
);
261 else if (argv
[i
] == BASE_FILENAME
)
263 ret
[j
] = g_strdup(base_filename
);
265 else if (argv
[i
] == FILE_LIST
)
267 for (tmp
= filelist
; tmp
!= NULL
; tmp
= g_slist_next(tmp
))
269 ret
[j
] = g_strdup((gchar
*) tmp
->data
);
274 else if (argv
[i
] == MESSAGE
)
276 ret
[j
] = g_strdup(message
);
279 ret
[j
] = g_strdup(argv
[i
]);
282 g_free(base_filename
);
287 get_cmd_int(gint cmd_type
, const gchar
* filename
, GSList
* list
, const gchar
* message
)
294 gchar
*base_filename
;
297 argv
= find_cmd_env(cmd_type
, TRUE
, filename
);
301 return get_cmd(argv
, filename
, list
, message
);
306 find_by_filename(const gchar
* filename
)
310 for (i
= 0; i
< doc_array
->len
; i
++)
312 if (doc_list
[i
].is_valid
&& doc_list
[i
].file_name
&&
313 strcmp(doc_list
[i
].file_name
, filename
) == 0)
320 /* name_prefix should be in UTF-8, and can have a path. */
322 show_output(const gchar
* std_output
, const gchar
* name
, const gchar
* force_encoding
)
329 idx
= find_by_filename(name
);
332 idx
= documents
->new_file(name
,
333 geany_data
->filetypes
[GEANY_FILETYPES_DIFF
],
338 scintilla
->set_text(doc_list
[idx
].sci
, std_output
);
339 book
= GTK_NOTEBOOK(app
->notebook
);
340 page
= gtk_notebook_page_num(book
, GTK_WIDGET(doc_list
[idx
].sci
));
341 gtk_notebook_set_current_page(book
, page
);
343 doc_list
[idx
].changed
= TRUE
;
344 if (!set_changed_flag
)
345 doc_list
[idx
].changed
= FALSE
;
346 documents
->set_text_changed(idx
);
347 documents
->set_encoding(idx
, (force_encoding
? force_encoding
: "utf-8"));
351 ui
->set_statusbar(FALSE
, _("Could not parse the output of the diff"));
356 * Execute command by command spec, return std_out std_err
358 * @argv - command spec
359 * @env - envirounment
360 * @std_out - if not NULL here will be returned standard output converted to utf8 of last command in spec
361 * @std_err - if not NULL here will be returned standard error converted to utf8 of last command in spec
362 * @filename - filename for spec, commands will be running in it's basedir . Used to replace FILENAME, BASE_FILENAME in spec
363 * @list - used to replace FILE_LIST in spec
364 * @message - used to replace MESSAGE in spec
366 * @return - exit code of last command in spec
369 execute_custom_command(const gchar
** argv
, const gchar
** env
, gchar
** std_out
, gchar
** std_err
,
370 const gchar
* filename
, GSList
* list
, const gchar
* message
)
376 GSList
*largv
= get_cmd(argv
, filename
, list
, message
);
377 GError
*error
= NULL
;
378 gchar
*detect_enc
= NULL
;
379 gchar
*force_encoding
= NULL
;
391 if (g_file_test(filename
, G_FILE_TEST_IS_DIR
))
393 dir
= g_strdup(filename
);
397 dir
= g_path_get_dirname(filename
);
400 for (cur
= largv
; cur
!= NULL
; cur
= g_slist_next(cur
))
403 if (cur
!= g_slist_last(largv
))
405 g_spawn_sync(dir
, cur
->data
, (gchar
**) env
,
406 G_SPAWN_SEARCH_PATH
| G_SPAWN_STDOUT_TO_DEV_NULL
|
407 G_SPAWN_STDERR_TO_DEV_NULL
, NULL
, NULL
, NULL
, NULL
, &exit_code
,
412 g_spawn_sync(dir
, cur
->data
, (gchar
**) env
,
413 G_SPAWN_SEARCH_PATH
| (std_out
? 0 :
414 G_SPAWN_STDOUT_TO_DEV_NULL
) | (std_err
?
416 G_SPAWN_STDERR_TO_DEV_NULL
),
417 NULL
, NULL
, std_out
, std_err
, &exit_code
, &error
);
421 fprintf(stderr
, "s_spawn_sync error: %s", error
->message
);
423 ui
->set_statusbar(FALSE
, _("Something went really wrong."));
426 // need to convert output text from the encoding of the original file into
427 // UTF-8 because internally Geany always needs UTF-8
428 if (std_out
&& *std_out
)
433 encodings
->convert_to_utf8_from_charset(*std_out
, -1,
440 encodings
->convert_to_utf8(*std_out
, -1, &detect_enc
));
453 if (std_err
&& *std_err
)
458 encodings
->convert_to_utf8_from_charset(*std_err
, -1,
465 encodings
->convert_to_utf8(*std_err
, -1, &detect_enc
));
478 g_strfreev(cur
->data
);
486 execute_command(const VC_RECORD
* vc
, gchar
** std_out
, gchar
** std_err
, const gchar
* filename
,
487 gint cmd
, GSList
* list
, const gchar
* message
)
489 return execute_custom_command(vc
->commands
[cmd
], vc
->envs
[cmd
], std_out
, std_err
, filename
,
493 /* Callback if menu item for a single file was activated */
495 vcfile_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
498 gchar
*locale_filename
, *text
= NULL
;
502 idx
= documents
->get_cur_idx();
504 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
506 if (doc_list
[idx
].changed
)
508 documents
->save_file(idx
, FALSE
);
511 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
513 vc
= find_vc(doc_list
[idx
].file_name
);
514 g_return_if_fail(vc
);
516 execute_command(vc
, &text
, NULL
, locale_filename
, VC_COMMAND_DIFF_FILE
, NULL
, NULL
);
519 name
= g_strconcat(doc_list
[idx
].file_name
, ".vc.diff", NULL
);
520 show_output(text
, name
, doc_list
[idx
].encoding
);
524 g_free(locale_filename
);
527 /* Make a diff from the current directory */
529 vcdirectory_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
532 gchar
*base_name
= NULL
;
533 gchar
*locale_filename
= NULL
;
537 idx
= documents
->get_cur_idx();
539 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
541 if (doc_list
[idx
].changed
)
543 documents
->save_file(idx
, FALSE
);
546 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
547 base_name
= g_path_get_dirname(locale_filename
);
549 vc
= find_vc(base_name
);
550 g_return_if_fail(vc
);
552 execute_command(vc
, &text
, NULL
, base_name
, VC_COMMAND_DIFF_DIR
, NULL
, NULL
);
555 setptr(base_name
, g_strconcat(base_name
, ".vc.diff", NULL
));
556 show_output(text
, base_name
, NULL
);
561 g_free(locale_filename
);
565 /* Callback if menu item for the current project was activated */
567 vcproject_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
570 gchar
*locale_filename
= NULL
;
575 idx
= documents
->get_cur_idx();
577 g_return_if_fail(project
!= NULL
&& NZV(project
->base_path
));
579 if (DOC_IDX_VALID(idx
) && doc_list
[idx
].changed
&& doc_list
[idx
].file_name
!= NULL
)
581 documents
->save_file(idx
, FALSE
);
584 locale_filename
= utils
->get_locale_from_utf8(project
->base_path
);
586 vc
= find_vc(project
->base_path
);
587 g_return_if_fail(vc
);
589 execute_command(vc
, &text
, NULL
, project
->base_path
, VC_COMMAND_DIFF_DIR
, NULL
, NULL
);
592 name
= g_strconcat(project
->name
, ".vc.diff", NULL
);
593 show_output(text
, name
, NULL
);
597 g_free(locale_filename
);
601 vclog_file_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
604 gchar
*locale_filename
;
605 gchar
*output
= NULL
;
608 idx
= documents
->get_cur_idx();
610 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
612 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
614 ui
->set_statusbar(TRUE
, locale_filename
);
616 vc
= find_vc(doc_list
[idx
].file_name
);
617 g_return_if_fail(vc
);
619 execute_command(vc
, &output
, NULL
, locale_filename
, VC_COMMAND_LOG_FILE
, NULL
, NULL
);
622 show_output(output
, "*VC-LOG*", NULL
);
625 g_free(locale_filename
);
629 vclog_dir_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
632 gchar
*base_name
= NULL
;
633 gchar
*locale_filename
= NULL
;
637 idx
= documents
->get_cur_idx();
639 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
641 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
642 base_name
= g_path_get_dirname(locale_filename
);
644 vc
= find_vc(base_name
);
645 g_return_if_fail(vc
);
647 execute_command(vc
, &text
, NULL
, base_name
, VC_COMMAND_LOG_DIR
, NULL
, NULL
);
650 show_output(text
, "*VC-LOG*", NULL
);
655 g_free(locale_filename
);
659 vclog_project_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
662 gchar
*locale_filename
= NULL
;
666 idx
= documents
->get_cur_idx();
668 g_return_if_fail(project
!= NULL
&& NZV(project
->base_path
));
670 locale_filename
= utils
->get_locale_from_utf8(project
->base_path
);
672 vc
= find_vc(locale_filename
);
673 g_return_if_fail(vc
);
675 execute_command(vc
, &text
, NULL
, locale_filename
, VC_COMMAND_LOG_DIR
, NULL
, NULL
);
678 show_output(text
, "*VC-LOG*", NULL
);
681 g_free(locale_filename
);
684 /* Show status from the current directory */
686 vcstatus_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
689 gchar
*base_name
= NULL
;
690 gchar
*locale_filename
= NULL
;
694 idx
= documents
->get_cur_idx();
696 g_return_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
);
698 if (doc_list
[idx
].changed
)
700 documents
->save_file(idx
, FALSE
);
703 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
704 base_name
= g_path_get_dirname(locale_filename
);
706 vc
= find_vc(doc_list
[idx
].file_name
);
707 g_return_if_fail(vc
);
709 execute_command(vc
, &text
, NULL
, base_name
, VC_COMMAND_STATUS
, NULL
, NULL
);
712 show_output(text
, "*VC-STATUS*", NULL
);
717 g_free(locale_filename
);
721 command_with_question_activated(GtkMenuItem
* menuitem
, gpointer gdata
, gint cmd
,
722 const gchar
* question
, gboolean ask
)
725 gchar
*locale_filename
, *text
;
731 idx
= documents
->get_cur_idx();
733 g_return_val_if_fail(DOC_IDX_VALID(idx
) && doc_list
[idx
].file_name
!= NULL
, FALSE
);
735 if (doc_list
[idx
].changed
)
737 documents
->save_file(idx
, FALSE
);
740 locale_filename
= utils
->get_locale_from_utf8(doc_list
[idx
].file_name
);
744 dialog
= gtk_message_dialog_new(GTK_WINDOW(app
->window
),
745 GTK_DIALOG_DESTROY_WITH_PARENT
,
746 GTK_MESSAGE_QUESTION
,
747 GTK_BUTTONS_YES_NO
, question
, locale_filename
);
748 result
= gtk_dialog_run(GTK_DIALOG(dialog
));
749 gtk_widget_destroy(dialog
);
753 result
= GTK_RESPONSE_YES
;
756 if (result
== GTK_RESPONSE_YES
)
758 dir
= g_path_get_dirname(doc_list
[idx
].file_name
);
760 g_return_if_fail(vc
);
762 execute_command(vc
, &text
, NULL
, locale_filename
, cmd
, NULL
, NULL
);
769 g_free(locale_filename
);
770 return (result
== GTK_RESPONSE_YES
) ? TRUE
: FALSE
;
774 vcrevert_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
778 ret
= command_with_question_activated(menuitem
, gdata
, VC_COMMAND_REVERT_FILE
,
779 _("Do you really want to revert: %s"), TRUE
);
782 idx
= documents
->get_cur_idx();
783 documents
->reload_file(idx
, NULL
);
789 vcadd_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
791 command_with_question_activated(menuitem
, gdata
, VC_COMMAND_ADD
,
792 _("Do you really want to add: %s"), set_add_confirmation
);
796 vcremove_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
800 ret
= command_with_question_activated(menuitem
, gdata
, VC_COMMAND_REMOVE
,
801 _("Do you really want to remove: %s"), TRUE
);
804 documents
->remove(gtk_notebook_get_current_page(GTK_NOTEBOOK(app
->notebook
)));
808 #include "generated/interface.c"
818 static GtkTreeModel
*
819 create_commit_model(const GSList
* commit
)
825 /* create list store */
826 store
= gtk_list_store_new(NUM_COLUMNS
, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_STRING
);
828 /* add data to the list store */
830 for (cur
= commit
; cur
!= NULL
; cur
= g_slist_next(cur
))
832 gtk_list_store_append(store
, &iter
);
833 gtk_list_store_set(store
, &iter
,
835 COLUMN_STATUS
, ((CommitItem
*) (cur
->data
))->status
,
836 COLUMN_PATH
, ((CommitItem
*) (cur
->data
))->path
, -1);
839 return GTK_TREE_MODEL(store
);
843 get_commit_files_foreach(GtkTreeModel
* model
, GtkTreePath
* path
, GtkTreeIter
* iter
,
846 GSList
**files
= (GSList
**) data
;
850 gtk_tree_model_get(model
, iter
, COLUMN_COMMIT
, &commit
, -1);
854 gtk_tree_model_get(model
, iter
, COLUMN_PATH
, &filename
, -1);
855 *files
= g_slist_prepend(*files
, filename
);
860 get_commit_diff_foreach(GtkTreeModel
* model
, GtkTreePath
* path
, GtkTreeIter
* iter
, gpointer data
)
862 gchar
**diff
= (gchar
**) data
;
869 gtk_tree_model_get(model
, iter
, COLUMN_COMMIT
, &commit
, -1);
873 gtk_tree_model_get(model
, iter
, COLUMN_STATUS
, &status
, -1);
875 if (strcmp(status
, FILE_STATUS_MODIFIED
) != 0)
881 gtk_tree_model_get(model
, iter
, COLUMN_PATH
, &filename
, -1);
883 vc
= find_vc(filename
);
884 g_return_val_if_fail(vc
, FALSE
);
886 execute_command(vc
, &tmp
, NULL
, filename
, VC_COMMAND_DIFF_FILE
, NULL
, NULL
);
887 setptr(*diff
, g_strdup_printf("%s%s", *diff
, tmp
));
895 get_commit_diff(GtkTreeView
* treeview
)
897 GtkTreeModel
*model
= gtk_tree_view_get_model(treeview
);
899 gchar
*ret
= g_strdup("");
901 gtk_tree_model_foreach(model
, get_commit_diff_foreach
, &ret
);
906 commit_toggled(GtkCellRendererToggle
* cell
, gchar
* path_str
, gpointer data
)
908 GtkTreeView
*treeview
= GTK_TREE_VIEW(data
);
909 GtkWidget
*diffView
= lookup_widget(GTK_WIDGET(treeview
), "textDiff");
910 GtkTreeModel
*model
= gtk_tree_view_get_model(treeview
);
912 GtkTreePath
*path
= gtk_tree_path_new_from_string(path_str
);
916 /* get toggled iter */
917 gtk_tree_model_get_iter(model
, &iter
, path
);
918 gtk_tree_model_get(model
, &iter
, COLUMN_COMMIT
, &fixed
, -1);
920 /* do something with the value */
924 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
, COLUMN_COMMIT
, fixed
, -1);
926 diff
= get_commit_diff(GTK_TREE_VIEW(treeview
));
927 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(diffView
)), diff
, -1);
930 gtk_tree_path_free(path
);
935 add_commit_columns(GtkTreeView
* treeview
)
937 GtkCellRenderer
*renderer
;
938 GtkTreeViewColumn
*column
;
939 GtkTreeModel
*model
= gtk_tree_view_get_model(treeview
);
941 /* column for fixed toggles */
942 renderer
= gtk_cell_renderer_toggle_new();
943 g_signal_connect(renderer
, "toggled", G_CALLBACK(commit_toggled
), treeview
);
945 column
= gtk_tree_view_column_new_with_attributes("Commit?",
946 renderer
, "active", COLUMN_COMMIT
, NULL
);
948 /* set this column to a fixed sizing (of 50 pixels) */
949 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column
), GTK_TREE_VIEW_COLUMN_FIXED
);
950 gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column
), 70);
951 gtk_tree_view_append_column(treeview
, column
);
953 /* column for bug numbers */
954 renderer
= gtk_cell_renderer_text_new();
955 column
= gtk_tree_view_column_new_with_attributes("Status",
956 renderer
, "text", COLUMN_STATUS
, NULL
);
957 gtk_tree_view_column_set_sort_column_id(column
, COLUMN_STATUS
);
958 gtk_tree_view_append_column(treeview
, column
);
960 /* column for severities */
961 renderer
= gtk_cell_renderer_text_new();
962 column
= gtk_tree_view_column_new_with_attributes("Path",
963 renderer
, "text", COLUMN_PATH
, NULL
);
964 gtk_tree_view_column_set_sort_column_id(column
, COLUMN_PATH
);
965 gtk_tree_view_append_column(treeview
, column
);
969 lookup_widget(GtkWidget
* widget
, const gchar
* widget_name
)
971 GtkWidget
*parent
, *found_widget
;
975 if (GTK_IS_MENU(widget
))
976 parent
= gtk_menu_get_attach_widget(GTK_MENU(widget
));
978 parent
= widget
->parent
;
980 parent
= (GtkWidget
*) g_object_get_data(G_OBJECT(widget
),
987 found_widget
= (GtkWidget
*) g_object_get_data(G_OBJECT(widget
), widget_name
);
989 g_warning("Widget not found: %s", widget_name
);
994 vccommit_activated(GtkMenuItem
* menuitem
, gpointer gdata
)
1000 GtkTreeModel
*model
;
1001 GtkWidget
*commit
= create_commitDialog();
1002 GtkWidget
*treeview
= lookup_widget(commit
, "treeSelect");
1003 GtkWidget
*diffView
= lookup_widget(commit
, "textDiff");
1004 GtkWidget
*messageView
= lookup_widget(commit
, "textCommitMessage");
1005 GtkTextBuffer
*mbuf
;
1008 GSList
*selected_files
= NULL
;
1014 doc
= documents
->get_current();
1015 g_return_if_fail(doc
);
1016 g_return_if_fail(doc
->file_name
);
1017 dir
= g_path_get_dirname(doc
->file_name
);
1020 g_return_if_fail(vc
);
1022 lst
= vc
->get_commit_files(dir
);
1023 model
= create_commit_model(lst
);
1024 gtk_tree_view_set_model(GTK_TREE_VIEW(treeview
), model
);
1025 g_object_unref(model
);
1027 /* add columns to the tree view */
1028 add_commit_columns(GTK_TREE_VIEW(treeview
));
1030 diff
= get_commit_diff(GTK_TREE_VIEW(treeview
));
1031 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(diffView
)), diff
, -1);
1033 gtk_window_maximize(GTK_WINDOW(commit
));
1035 result
= gtk_dialog_run(GTK_DIALOG(commit
));
1036 if (result
== GTK_RESPONSE_APPLY
)
1038 mbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(messageView
));
1039 gtk_text_buffer_get_start_iter(mbuf
, &begin
);
1040 gtk_text_buffer_get_end_iter(mbuf
, &end
);
1041 message
= gtk_text_buffer_get_text(mbuf
, &begin
, &end
, FALSE
);
1042 gtk_tree_model_foreach(model
, get_commit_files_foreach
, &selected_files
);
1043 execute_command(vc
, NULL
, NULL
, dir
, VC_COMMAND_COMMIT
, selected_files
, message
);
1046 gtk_widget_destroy(commit
);
1047 free_commit_list(lst
);
1052 static GtkWidget
*menu_vc_diff_file
= NULL
;
1053 static GtkWidget
*menu_vc_diff_dir
= NULL
;
1054 static GtkWidget
*menu_vc_diff_project
= NULL
;
1055 static GtkWidget
*menu_vc_log_file
= NULL
;
1056 static GtkWidget
*menu_vc_log_dir
= NULL
;
1057 static GtkWidget
*menu_vc_log_project
= NULL
;
1058 static GtkWidget
*menu_vc_status
= NULL
;
1059 static GtkWidget
*menu_vc_revert_file
= NULL
;
1060 static GtkWidget
*menu_vc_add_file
= NULL
;
1061 static GtkWidget
*menu_vc_remove_file
= NULL
;
1062 static GtkWidget
*menu_vc_commit
= NULL
;
1069 gboolean p_have_vc
= FALSE
;
1070 gboolean d_have_vc
= FALSE
;
1071 gboolean f_have_vc
= FALSE
;
1075 doc
= documents
->get_current();
1076 have_file
= doc
&& doc
->file_name
&& g_path_is_absolute(doc
->file_name
);
1080 dir
= g_path_get_dirname(doc
->file_name
);
1081 if (find_cmd_env(VC_COMMAND_DIFF_FILE
, TRUE
, dir
))
1084 if (find_cmd_env(VC_COMMAND_DIFF_FILE
, TRUE
, doc
->file_name
))
1089 if (project
!= NULL
&& NZV(project
->base_path
) &&
1090 find_cmd_env(VC_COMMAND_DIFF_DIR
, TRUE
, project
->base_path
))
1093 gtk_widget_set_sensitive(menu_vc_diff_file
, f_have_vc
);
1094 gtk_widget_set_sensitive(menu_vc_diff_dir
, d_have_vc
);
1095 gtk_widget_set_sensitive(menu_vc_diff_project
, p_have_vc
);
1097 gtk_widget_set_sensitive(menu_vc_log_file
, f_have_vc
);
1098 gtk_widget_set_sensitive(menu_vc_log_dir
, d_have_vc
);
1099 gtk_widget_set_sensitive(menu_vc_log_project
, p_have_vc
);
1101 gtk_widget_set_sensitive(menu_vc_status
, d_have_vc
);
1103 gtk_widget_set_sensitive(menu_vc_revert_file
, f_have_vc
);
1105 gtk_widget_set_sensitive(menu_vc_remove_file
, f_have_vc
);
1106 gtk_widget_set_sensitive(menu_vc_add_file
, d_have_vc
&& !f_have_vc
);
1108 gtk_widget_set_sensitive(menu_vc_commit
, d_have_vc
);
1112 configure(GtkWidget
* parent
)
1114 GtkWidget
*dialog
, *vbox
, *checkbox
, *checkbox2
, *checkbox3
, *checkbox4
, *checkbox5
,
1116 GtkTooltips
*tooltip
= NULL
;
1118 tooltip
= gtk_tooltips_new();
1119 dialog
= gtk_dialog_new_with_buttons(_("Version Control"),
1120 GTK_WINDOW(parent
), GTK_DIALOG_DESTROY_WITH_PARENT
,
1121 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, GTK_STOCK_OK
,
1122 GTK_RESPONSE_ACCEPT
, NULL
);
1123 vbox
= ui
->dialog_vbox_new(GTK_DIALOG(dialog
));
1124 gtk_widget_set_name(dialog
, "GeanyDialog");
1125 gtk_box_set_spacing(GTK_BOX(vbox
), 2);
1128 gtk_check_button_new_with_label(_
1129 ("Set Changed-flag for document tabs created by the plugin"));
1130 gtk_tooltips_set_tip(tooltip
, checkbox
,
1132 ("If this option is activated, every new by the VC-plugin created document tab "
1133 "will be marked as changed. Even this option is useful in some cases, it could cause "
1134 "a big number of annoying \"Do you want to save\"-dialogs."), NULL
);
1135 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox
), FALSE
);
1136 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox
), set_changed_flag
);
1137 gtk_box_pack_start(GTK_BOX(vbox
), checkbox
, FALSE
, FALSE
, 2);
1139 checkbox2
= gtk_check_button_new_with_label(_("Confirm adding new files to a VCS"));
1140 gtk_tooltips_set_tip(tooltip
, checkbox2
,
1142 ("Shows a confirmation dialog on adding a new (created) file to VCS."),
1144 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox2
), FALSE
);
1145 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox2
), set_add_confirmation
);
1146 gtk_box_pack_start(GTK_BOX(vbox
), checkbox2
, TRUE
, FALSE
, 2);
1148 checkbox3
= gtk_check_button_new_with_label(_("Enable CVS"));
1149 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox3
), FALSE
);
1150 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox3
), enable_cvs
);
1151 gtk_box_pack_start(GTK_BOX(vbox
), checkbox3
, TRUE
, FALSE
, 2);
1153 checkbox4
= gtk_check_button_new_with_label(_("Enable GIT"));
1154 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox4
), FALSE
);
1155 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox4
), enable_git
);
1156 gtk_box_pack_start(GTK_BOX(vbox
), checkbox4
, TRUE
, FALSE
, 2);
1158 checkbox5
= gtk_check_button_new_with_label(_("Enable SVN"));
1159 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox5
), FALSE
);
1160 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox5
), enable_svn
);
1161 gtk_box_pack_start(GTK_BOX(vbox
), checkbox5
, TRUE
, FALSE
, 2);
1163 checkbox6
= gtk_check_button_new_with_label(_("Enable SVK"));
1164 gtk_button_set_focus_on_click(GTK_BUTTON(checkbox6
), FALSE
);
1165 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox6
), enable_svk
);
1166 gtk_box_pack_start(GTK_BOX(vbox
), checkbox6
, TRUE
, FALSE
, 2);
1168 gtk_widget_show_all(vbox
);
1170 // run the dialog and check for the response code
1171 if (gtk_dialog_run(GTK_DIALOG(dialog
)) == GTK_RESPONSE_ACCEPT
)
1173 GKeyFile
*config
= g_key_file_new();
1175 gchar
*config_dir
= g_path_get_dirname(config_file
);
1177 set_changed_flag
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox
));
1178 set_add_confirmation
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox2
));
1180 enable_cvs
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox3
));
1181 enable_git
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox4
));
1182 enable_svn
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox5
));
1183 enable_svk
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox6
));
1185 g_key_file_load_from_file(config
, config_file
, G_KEY_FILE_NONE
, NULL
);
1187 g_key_file_set_boolean(config
, "VC", "set_changed_flag", set_changed_flag
);
1188 g_key_file_set_boolean(config
, "VC", "set_add_confirmation", set_add_confirmation
);
1190 g_key_file_set_boolean(config
, "VC", "enable_cvs", enable_cvs
);
1191 g_key_file_set_boolean(config
, "VC", "enable_git", enable_git
);
1192 g_key_file_set_boolean(config
, "VC", "enable_svn", enable_svn
);
1193 g_key_file_set_boolean(config
, "VC", "enable_svk", enable_svk
);
1195 if (!g_file_test(config_dir
, G_FILE_TEST_IS_DIR
)
1196 && utils
->mkdir(config_dir
, TRUE
) != 0)
1198 dialogs
->show_msgbox(GTK_MESSAGE_ERROR
,
1200 ("Plugin configuration directory could not be created."));
1204 // write config to file
1205 data
= g_key_file_to_data(config
, NULL
, NULL
);
1206 utils
->write_file(config_file
, data
);
1211 g_key_file_free(config
);
1215 gtk_widget_destroy(dialog
);
1221 GError
*error
= NULL
;
1222 GKeyFile
*config
= g_key_file_new();
1224 g_key_file_load_from_file(config
, config_file
, G_KEY_FILE_NONE
, NULL
);
1225 set_changed_flag
= g_key_file_get_boolean(config
, "VC", "set_changed_flag", &error
);
1228 // Set default value if something went wrong while reading file
1229 set_changed_flag
= FALSE
;
1230 g_error_free(error
);
1233 set_add_confirmation
= g_key_file_get_boolean(config
, "VC", "set_add_confirmation", &error
);
1236 // Set default value if something went wrong while reading file
1237 set_add_confirmation
= TRUE
;
1238 g_error_free(error
);
1242 enable_cvs
= g_key_file_get_boolean(config
, "VC", "enable_cvs", &error
);
1245 // Set default value if something went wrong while reading file
1247 g_error_free(error
);
1251 enable_git
= g_key_file_get_boolean(config
, "VC", "enable_git", &error
);
1254 // Set default value if something went wrong while reading file
1256 g_error_free(error
);
1260 enable_svn
= g_key_file_get_boolean(config
, "VC", "enable_svn", &error
);
1263 // Set default value if something went wrong while reading file
1265 g_error_free(error
);
1269 enable_svk
= g_key_file_get_boolean(config
, "VC", "enable_svk", &error
);
1272 // Set default value if something went wrong while reading file
1274 g_error_free(error
);
1277 g_key_file_free(config
);
1289 REGISTER_VC(GIT
, enable_git
);
1290 REGISTER_VC(SVN
, enable_svn
);
1291 REGISTER_VC(CVS
, enable_cvs
);
1292 REGISTER_VC(SVK
, enable_svk
);
1295 /* Called by Geany to initialize the plugin */
1297 init(GeanyData
* data
)
1299 GtkWidget
*menu_vc
= NULL
;
1300 GtkWidget
*menu_vc_menu
= NULL
;
1301 GtkTooltips
*tooltips
= NULL
;
1304 config_file
= g_strconcat(app
->configdir
, G_DIR_SEPARATOR_S
, "plugins", G_DIR_SEPARATOR_S
,
1305 "VC", G_DIR_SEPARATOR_S
, "VC.conf", NULL
);
1310 tooltips
= gtk_tooltips_new();
1312 menu_vc
= gtk_image_menu_item_new_with_mnemonic(_("_VC"));
1313 gtk_container_add(GTK_CONTAINER(data
->tools_menu
), menu_vc
);
1315 g_signal_connect((gpointer
) menu_vc
, "activate", G_CALLBACK(update_menu_items
), NULL
);
1317 menu_vc_menu
= gtk_menu_new();
1318 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_vc
), menu_vc_menu
);
1321 menu_vc_diff_file
= gtk_menu_item_new_with_mnemonic(_("Diff From Current File"));
1322 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_diff_file
);
1323 gtk_tooltips_set_tip(tooltips
, menu_vc_diff_file
,
1324 _("Make a diff from the current active file"), NULL
);
1326 g_signal_connect((gpointer
) menu_vc_diff_file
, "activate",
1327 G_CALLBACK(vcfile_activated
), NULL
);
1330 menu_vc_diff_dir
= gtk_menu_item_new_with_mnemonic(_("Diff From Current Directory"));
1331 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_diff_dir
);
1332 gtk_tooltips_set_tip(tooltips
, menu_vc_diff_dir
,
1333 _("Make a diff from the directory of the current active file"), NULL
);
1335 g_signal_connect((gpointer
) menu_vc_diff_dir
, "activate",
1336 G_CALLBACK(vcdirectory_activated
), NULL
);
1339 menu_vc_diff_project
= gtk_menu_item_new_with_mnemonic(_("Diff From Current Project"));
1340 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_diff_project
);
1341 gtk_tooltips_set_tip(tooltips
, menu_vc_diff_project
,
1342 _("Make a diff from the current project's base path"), NULL
);
1344 g_signal_connect((gpointer
) menu_vc_diff_project
, "activate",
1345 G_CALLBACK(vcproject_activated
), NULL
);
1347 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), gtk_separator_menu_item_new());
1350 menu_vc_log_file
= gtk_menu_item_new_with_mnemonic(_("Log Of Current File"));
1351 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_log_file
);
1352 gtk_tooltips_set_tip(tooltips
, menu_vc_log_file
,
1353 _("Shows the log of the current file"), NULL
);
1355 g_signal_connect((gpointer
) menu_vc_log_file
, "activate",
1356 G_CALLBACK(vclog_file_activated
), NULL
);
1358 menu_vc_log_dir
= gtk_menu_item_new_with_mnemonic(_("Log Of Current Directory"));
1359 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_log_dir
);
1360 gtk_tooltips_set_tip(tooltips
, menu_vc_log_dir
,
1361 _("Shows the log of the current directory"), NULL
);
1363 g_signal_connect((gpointer
) menu_vc_log_dir
, "activate",
1364 G_CALLBACK(vclog_dir_activated
), NULL
);
1366 menu_vc_log_project
= gtk_menu_item_new_with_mnemonic(_("Log Of Current Project"));
1367 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_log_project
);
1368 gtk_tooltips_set_tip(tooltips
, menu_vc_log_project
,
1369 _("Shows the log of the current project"), NULL
);
1371 g_signal_connect((gpointer
) menu_vc_log_project
, "activate",
1372 G_CALLBACK(vclog_project_activated
), NULL
);
1374 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), gtk_separator_menu_item_new());
1377 menu_vc_status
= gtk_menu_item_new_with_mnemonic(_("Status"));
1378 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_status
);
1379 gtk_tooltips_set_tip(tooltips
, menu_vc_status
, _("Show status."), NULL
);
1381 g_signal_connect((gpointer
) menu_vc_status
, "activate",
1382 G_CALLBACK(vcstatus_activated
), NULL
);
1384 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), gtk_separator_menu_item_new());
1387 menu_vc_revert_file
= gtk_menu_item_new_with_mnemonic(_("Revert File"));
1388 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_revert_file
);
1389 gtk_tooltips_set_tip(tooltips
, menu_vc_revert_file
,
1390 _("Restore pristine working copy file (undo local edits)."), NULL
);
1392 g_signal_connect((gpointer
) menu_vc_revert_file
, "activate",
1393 G_CALLBACK(vcrevert_activated
), NULL
);
1395 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), gtk_separator_menu_item_new());
1398 menu_vc_add_file
= gtk_menu_item_new_with_mnemonic(_("Add File"));
1399 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_add_file
);
1400 gtk_tooltips_set_tip(tooltips
, menu_vc_add_file
, _("Add file to repository."), NULL
);
1402 g_signal_connect((gpointer
) menu_vc_add_file
, "activate",
1403 G_CALLBACK(vcadd_activated
), NULL
);
1406 menu_vc_remove_file
= gtk_menu_item_new_with_mnemonic(_("Remove File"));
1407 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_remove_file
);
1408 gtk_tooltips_set_tip(tooltips
, menu_vc_remove_file
,
1409 _("Remove file from repository."), NULL
);
1411 g_signal_connect((gpointer
) menu_vc_remove_file
, "activate",
1412 G_CALLBACK(vcremove_activated
), NULL
);
1414 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), gtk_separator_menu_item_new());
1417 menu_vc_commit
= gtk_menu_item_new_with_mnemonic(_("Commit"));
1418 gtk_container_add(GTK_CONTAINER(menu_vc_menu
), menu_vc_commit
);
1419 gtk_tooltips_set_tip(tooltips
, menu_vc_commit
, _("Commit changes."), NULL
);
1421 g_signal_connect((gpointer
) menu_vc_commit
, "activate",
1422 G_CALLBACK(vccommit_activated
), NULL
);
1424 gtk_widget_show_all(menu_vc
);
1426 plugin_fields
->menu_item
= menu_vc
;
1427 plugin_fields
->flags
= PLUGIN_IS_DOCUMENT_SENSITIVE
;
1431 /* Called by Geany before unloading the plugin. */
1435 // remove the menu item added in init()
1436 gtk_widget_destroy(plugin_fields
->menu_item
);
1439 g_free(config_file
);