1 /*
2 * geanyprj - Alternative project support for geany light IDE.
3 *
4 * Copyright 2008 Yura Siamashka <yurand2@gmail.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <sys/time.h>
21 #include <gdk/gdkkeysyms.h>
22 #include <glib/gstdio.h>
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h" /* for the gettext domain */
26 #endif
27 #include <geanyplugin.h>
28
29 #include "geanyprj.h"
30
31 static struct
32 {
33 GtkWidget *tools_item;
34
35 GtkWidget *new_project;
36 GtkWidget *delete_project;
37
38 GtkWidget *add_file;
39
40 GtkWidget *preferences;
41
42 GtkWidget *find_in_files;
43 } menu_items;
44
45 /* simple struct to keep references to the elements of the properties dialog */
46 typedef struct _PropertyDialogElements
47 {
48 GtkWidget *dialog;
49 GtkWidget *name;
50 GtkWidget *description;
51 GtkWidget *file_name;
52 GtkWidget *base_path;
53 GtkWidget *make_in_base_path;
54 GtkWidget *run_cmd;
55 GtkWidget *regenerate;
56 GtkWidget *type;
57 GtkWidget *patterns;
58 } PropertyDialogElements;
59
60
build_properties_dialog(gboolean properties)61 static PropertyDialogElements *build_properties_dialog(gboolean properties)
62 {
63 GtkWidget *vbox;
64 GtkWidget *table;
65 GtkWidget *image;
66 GtkWidget *button;
67 GtkWidget *bbox;
68 GtkWidget *label;
69 PropertyDialogElements *e;
70 gchar *dir = NULL;
71 gchar *basename = NULL;
72 gint i;
73 GeanyDocument *doc;
74
75 doc = document_get_current();
76
77 if (doc && doc->file_name != NULL && g_path_is_absolute(doc->file_name))
78 {
79 dir = g_path_get_dirname(doc->file_name);
80 }
81 else
82 {
83 dir = g_strdup("");
84 }
85 basename = g_path_get_basename(dir);
86
87 e = g_new0(PropertyDialogElements, 1);
88
89 if (properties)
90 {
91 e->dialog =
92 gtk_dialog_new_with_buttons(_("Project Preferences"),
93 GTK_WINDOW(geany->main_widgets->window),
94 GTK_DIALOG_DESTROY_WITH_PARENT,
95 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
96 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
97 }
98 else
99 {
100 e->dialog =
101 gtk_dialog_new_with_buttons(_("New Project"),
102 GTK_WINDOW(geany->main_widgets->window),
103 GTK_DIALOG_DESTROY_WITH_PARENT,
104 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
105
106 gtk_widget_set_name(e->dialog, "GeanyDialogProject");
107 bbox = gtk_hbox_new(FALSE, 0);
108 button = gtk_button_new();
109 image = gtk_image_new_from_stock("gtk-new", GTK_ICON_SIZE_BUTTON);
110 label = gtk_label_new_with_mnemonic(_("C_reate"));
111 gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 3);
112 gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 3);
113 gtk_container_add(GTK_CONTAINER(button), bbox);
114 gtk_dialog_add_action_widget(GTK_DIALOG(e->dialog), button, GTK_RESPONSE_OK);
115 }
116
117 vbox = ui_dialog_vbox_new(GTK_DIALOG(e->dialog));
118
119 table = gtk_table_new(5, 2, FALSE);
120 gtk_table_set_row_spacings(GTK_TABLE(table), 5);
121 gtk_table_set_col_spacings(GTK_TABLE(table), 10);
122
123 label = gtk_label_new(_("Name:"));
124 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
125
126 e->name = gtk_entry_new();
127 gtk_entry_set_max_length(GTK_ENTRY(e->name), MAX_NAME_LEN);
128 gtk_entry_set_text(GTK_ENTRY(e->name), basename);
129
130 ui_table_add_row(GTK_TABLE(table), 0, label, e->name, NULL);
131
132 label = gtk_label_new(_("Location:"));
133 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
134 e->file_name = gtk_entry_new();
135 gtk_entry_set_width_chars(GTK_ENTRY(e->file_name), 30);
136
137 if (properties)
138 {
139 gtk_widget_set_sensitive(e->file_name, FALSE);
140 ui_table_add_row(GTK_TABLE(table), 1, label, e->file_name, NULL);
141 }
142 else
143 {
144 button = gtk_button_new();
145 image = gtk_image_new_from_stock("gtk-open", GTK_ICON_SIZE_BUTTON);
146 gtk_container_add(GTK_CONTAINER(button), image);
147 bbox = ui_path_box_new(_("Choose Project Location"),
148 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
149 GTK_ENTRY(e->file_name));
150 gtk_entry_set_text(GTK_ENTRY(e->file_name), dir);
151 ui_table_add_row(GTK_TABLE(table), 1, label, bbox, NULL);
152 }
153
154
155 label = gtk_label_new(_("Base path:"));
156 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
157
158 e->base_path = gtk_entry_new();
159 gtk_widget_set_tooltip_text(e->base_path,
160 _("Base directory of all files that make up the project. "
161 "This can be a new path, or an existing directory tree. "
162 "You can use paths relative to the project filename."));
163 bbox = ui_path_box_new(_("Choose Project Base Path"),
164 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_ENTRY(e->base_path));
165 gtk_entry_set_text(GTK_ENTRY(e->base_path), dir);
166
167 ui_table_add_row(GTK_TABLE(table), 2, label, bbox, NULL);
168
169 label = gtk_label_new("");
170 e->regenerate = gtk_check_button_new_with_label(_("Generate file list on load"));
171 gtk_widget_set_tooltip_text(e->regenerate,
172 _("Automatically add files that match project type on project load "
173 "automatically. You can't manually add/remove files if "
174 "you checked this option, since your modification will be lost on "
175 "on next project load"));
176 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(e->regenerate), TRUE);
177 ui_table_add_row(GTK_TABLE(table), 3, label, e->regenerate, NULL);
178
179
180 label = gtk_label_new(_("Type:"));
181 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
182
183 e->type = gtk_combo_box_text_new();
184 for (i = 0; i < NEW_PROJECT_TYPE_SIZE; i++)
185 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(e->type), project_type_string[i]);
186 gtk_combo_box_set_active(GTK_COMBO_BOX(e->type), 0);
187
188 ui_table_add_row(GTK_TABLE(table), 4, label, e->type, NULL);
189
190 gtk_container_add(GTK_CONTAINER(vbox), table);
191 g_free(dir);
192 g_free(basename);
193
194 return e;
195 }
196
197
on_new_project(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer user_data)198 void on_new_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
199 {
200 PropertyDialogElements *e;
201 gint response;
202
203 e = build_properties_dialog(FALSE);
204 gtk_widget_show_all(e->dialog);
205
206 retry:
207 response = gtk_dialog_run(GTK_DIALOG(e->dialog));
208 if (response == GTK_RESPONSE_OK)
209 {
210 gchar *path;
211 struct GeanyPrj *prj;
212
213 path = g_build_filename(gtk_entry_get_text(GTK_ENTRY(e->file_name)), ".geanyprj",
214 NULL);
215
216 if (g_file_test(path, G_FILE_TEST_EXISTS))
217 {
218 ui_set_statusbar(TRUE, _("Project file \"%s\" already exists"), path);
219 g_free(path);
220 goto retry;
221 }
222 prj = geany_project_new();
223
224 geany_project_set_path(prj, path);
225 geany_project_set_base_path(prj, gtk_entry_get_text(GTK_ENTRY(e->base_path)));
226 geany_project_set_name(prj, gtk_entry_get_text(GTK_ENTRY(e->name)));
227 geany_project_set_description(prj, "");
228 geany_project_set_run_cmd(prj, "");
229 geany_project_set_type_int(prj, gtk_combo_box_get_active(GTK_COMBO_BOX(e->type)));
230 geany_project_set_regenerate(prj,
231 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
232 (e->regenerate)));
233
234 geany_project_regenerate_file_list(prj);
235
236 geany_project_save(prj);
237 geany_project_free(prj);
238 document_open_file(path, FALSE, NULL, NULL);
239 }
240
241 gtk_widget_destroy(e->dialog);
242 g_free(e);
243 }
244
245
on_preferences(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer user_data)246 void on_preferences(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
247 {
248 PropertyDialogElements *e;
249 gint response;
250 gchar *project_dir;
251
252 e = build_properties_dialog(TRUE);
253
254 project_dir = g_path_get_dirname(g_current_project->path);
255 gtk_entry_set_text(GTK_ENTRY(e->file_name), project_dir);
256 gtk_entry_set_text(GTK_ENTRY(e->name), g_current_project->name);
257 gtk_entry_set_text(GTK_ENTRY(e->base_path), g_current_project->base_path);
258 gtk_combo_box_set_active(GTK_COMBO_BOX(e->type), g_current_project->type);
259 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(e->regenerate),
260 g_current_project->regenerate);
261
262 gtk_widget_show_all(e->dialog);
263
264 response = gtk_dialog_run(GTK_DIALOG(e->dialog));
265 if (response == GTK_RESPONSE_OK)
266 {
267 geany_project_set_base_path(g_current_project,
268 gtk_entry_get_text(GTK_ENTRY(e->base_path)));
269 geany_project_set_name(g_current_project, gtk_entry_get_text(GTK_ENTRY(e->name)));
270 geany_project_set_description(g_current_project, "");
271 geany_project_set_run_cmd(g_current_project, "");
272 geany_project_set_type_int(g_current_project,
273 gtk_combo_box_get_active(GTK_COMBO_BOX(e->type)));
274 geany_project_set_regenerate(g_current_project,
275 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
276 (e->regenerate)));
277 geany_project_save(g_current_project);
278
279 if (g_current_project->regenerate)
280 {
281 geany_project_regenerate_file_list(g_current_project);
282 }
283 sidebar_refresh();
284 }
285
286 gtk_widget_destroy(e->dialog);
287 g_free(e);
288 g_free(project_dir);
289 }
290
291
on_delete_project(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer user_data)292 void on_delete_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
293 {
294 gchar *path;
295 if (!g_current_project)
296 return;
297
298 if (dialogs_show_question("Do you really wish to delete current project:\n%s?",
299 g_current_project->name))
300 {
301 path = utils_get_locale_from_utf8(g_current_project->path);
302 xproject_close(FALSE);
303 g_unlink(path);
304 g_free(path);
305 }
306 }
307
308
on_add_file(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer user_data)309 void on_add_file(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
310 {
311 GeanyDocument *doc;
312
313 doc = document_get_current();
314 g_return_if_fail(doc && doc->file_name != NULL && g_path_is_absolute(doc->file_name));
315
316 if (!g_current_project)
317 return;
318
319 xproject_add_file(doc->file_name);
320 }
321
322
on_find_in_project(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer user_data)323 void on_find_in_project(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
324 {
325 gchar *dir;
326 if (!g_current_project)
327 return;
328
329 dir = g_path_get_dirname(g_current_project->path);
330 search_show_find_in_files_dialog(dir);
331 g_free(dir);
332 }
333
334
update_menu_items(void)335 static void update_menu_items(void)
336 {
337 gboolean cur_file_exists;
338 gboolean badd_file;
339 GeanyDocument *doc;
340
341 doc = document_get_current();
342 g_return_if_fail(doc != NULL && doc->file_name != NULL);
343
344 cur_file_exists = doc && doc->file_name != NULL && g_path_is_absolute(doc->file_name);
345
346 badd_file = (g_current_project ? TRUE : FALSE) &&
347 !g_current_project->regenerate &&
348 cur_file_exists && !g_hash_table_lookup(g_current_project->tags, doc->file_name);
349
350 gtk_widget_set_sensitive(menu_items.new_project, TRUE);
351 gtk_widget_set_sensitive(menu_items.delete_project, g_current_project ? TRUE : FALSE);
352
353 gtk_widget_set_sensitive(menu_items.add_file, badd_file);
354
355 gtk_widget_set_sensitive(menu_items.preferences, g_current_project ? TRUE : FALSE);
356
357 gtk_widget_set_sensitive(menu_items.find_in_files, g_current_project ? TRUE : FALSE);
358 }
359
360
tools_menu_init(void)361 void tools_menu_init(void)
362 {
363 GtkWidget *item, *image;
364
365 GtkWidget *menu_prj_menu = NULL;
366
367 menu_items.tools_item = gtk_image_menu_item_new_with_mnemonic(_("_Project"));
368 gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), menu_items.tools_item);
369
370 g_signal_connect((gpointer) menu_items.tools_item, "activate", G_CALLBACK(update_menu_items), NULL);
371
372 menu_prj_menu = gtk_menu_new();
373 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_items.tools_item), menu_prj_menu);
374
375 image = gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_MENU);
376 item = gtk_image_menu_item_new_with_mnemonic(_("New Project"));
377 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
378 gtk_widget_show(item);
379 gtk_container_add(GTK_CONTAINER(menu_prj_menu), item);
380 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_new_project), NULL);
381 menu_items.new_project = item;
382
383 image = gtk_image_new_from_stock(GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU);
384 gtk_widget_show(image);
385 item = gtk_image_menu_item_new_with_mnemonic(_("Delete Project"));
386 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
387 gtk_widget_show(item);
388 gtk_container_add(GTK_CONTAINER(menu_prj_menu), item);
389 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_delete_project), NULL);
390 menu_items.delete_project = item;
391
392 gtk_container_add(GTK_CONTAINER(menu_prj_menu), gtk_separator_menu_item_new());
393
394 image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
395 gtk_widget_show(image);
396 item = gtk_image_menu_item_new_with_mnemonic(_("Add File"));
397 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
398 gtk_widget_show(item);
399 gtk_container_add(GTK_CONTAINER(menu_prj_menu), item);
400 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_add_file), NULL);
401 menu_items.add_file = item;
402
403 gtk_container_add(GTK_CONTAINER(menu_prj_menu), gtk_separator_menu_item_new());
404
405 image = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU);
406 gtk_widget_show(image);
407 item = gtk_image_menu_item_new_with_mnemonic(_("Preferences"));
408 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
409 gtk_widget_show(item);
410 gtk_container_add(GTK_CONTAINER(menu_prj_menu), item);
411 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_preferences), NULL);
412 menu_items.preferences = item;
413
414 gtk_container_add(GTK_CONTAINER(menu_prj_menu), gtk_separator_menu_item_new());
415
416 image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
417 gtk_widget_show(image);
418 item = gtk_image_menu_item_new_with_mnemonic(_("Find in Project"));
419 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
420 gtk_widget_show(item);
421 gtk_container_add(GTK_CONTAINER(menu_prj_menu), item);
422 g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_find_in_project), NULL);
423 menu_items.find_in_files = item;
424
425 gtk_widget_show_all(menu_items.tools_item);
426
427 ui_add_document_sensitive(menu_items.tools_item);
428 }
429
430
tools_menu_uninit(void)431 void tools_menu_uninit(void)
432 {
433 gtk_widget_destroy(menu_items.tools_item);
434 }
435