libanjuta: Move autogen functions from project-wizard to libanjuta
[anjuta.git] / plugins / project-wizard / druid.c
blobd50298a7ba6a8e40086c5e381847081c76f9abc1
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 druid.c
4 Copyright (C) 2004 Sebastien Granjoux
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 2 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * All GUI functions
24 *---------------------------------------------------------------------------*/
26 #include <config.h>
28 #include "druid.h"
30 #include "plugin.h"
31 #include "header.h"
32 #include "property.h"
33 #include "parser.h"
34 #include "install.h"
36 #include <libanjuta/anjuta-debug.h>
37 #include <libanjuta/anjuta-utils.h>
38 #include <libanjuta/anjuta-autogen.h>
39 #include <libanjuta/interfaces/ianjuta-wizard.h>
40 #include <libanjuta/interfaces/ianjuta-editor.h>
41 #include <stdlib.h>
42 #include <glib/gi18n.h>
43 #include <glib.h>
44 #include <gdk/gdk.h>
46 #include <gtk/gtk.h>
48 #include <string.h>
50 /*---------------------------------------------------------------------------*/
52 #define PROJECT_WIZARD_DIRECTORY PACKAGE_DATA_DIR "/project"
54 /* Default property name useable in wizard file
55 *---------------------------------------------------------------------------*/
57 #define ANJUTA_PROJECT_DIRECTORY_PROPERTY "AnjutaProjectDirectory"
58 #define USER_NAME_PROPERTY "UserName"
59 #define EMAIL_ADDRESS_PROPERTY "EmailAddress"
60 #define INDENT_WIDTH_PROPERTY "IndentWidth"
61 #define TAB_WIDTH_PROPERTY "TabWidth"
62 #define USE_TABS_PROPERTY "UseTabs"
64 /* Common editor preferences */
65 #define ANJUTA_PREF_SCHEMA_PREFIX "org.gnome.anjuta."
68 /* Widget and signal name found in glade file
69 *---------------------------------------------------------------------------*/
71 #define GTK_BUILDER_UI_FILE PACKAGE_DATA_DIR "/glade/anjuta-project-wizard.ui"
73 #define NEW_PROJECT_DIALOG "druid_window"
74 #define PROJECT_LIST "project_list"
75 #define PROJECT_BOOK "project_book"
76 #define PROJECT_PAGE "project_page"
77 #define ERROR_PAGE "error_page"
78 #define ERROR_TITLE "error_title"
79 #define PROGRESS_PAGE "progress_page"
80 #define FINISH_PAGE "finish_page"
81 #define FINISH_TEXT "finish_text"
82 #define PROPERTY_PAGE "property_page"
83 #define PROPERTY_TITLE "property_title"
84 #define PROPERTY_TABLE "property_table"
85 #define ERROR_VBOX "error_vbox"
86 #define ERROR_ICON "error_icon"
87 #define ERROR_MESSAGE "error_message"
88 #define ERROR_DETAIL "error_detail"
91 #define PROJECT_PAGE_INDEX 0
93 /*---------------------------------------------------------------------------*/
95 struct _NPWDruid
97 GtkWindow* window;
99 GtkNotebook* project_book;
100 GtkWidget *error_page;
101 GtkWidget *error_title;
102 GtkVBox *error_vbox;
103 GtkWidget *error_extra_widget;
104 GtkImage *error_icon;
105 GtkLabel *error_message;
106 GtkWidget *error_detail;
108 GtkWidget *project_page;
109 GtkWidget *progress_page;
110 GtkWidget *finish_page;
111 GtkWidget *finish_text;
113 const gchar* project_file;
114 NPWPlugin* plugin;
116 GQueue* page_list;
118 GHashTable* values;
119 NPWPageParser* parser;
120 GList* header_list;
121 NPWHeader* header;
122 AnjutaAutogen* gen;
123 gboolean busy;
126 /* column of the icon view */
127 enum {
128 PIXBUF_COLUMN,
129 TEXT_COLUMN,
130 DESC_COLUMN,
131 DATA_COLUMN
134 /* Helper functon
135 *---------------------------------------------------------------------------*/
137 /* Create error page
138 *---------------------------------------------------------------------------*/
140 /* Fill dialog page */
141 static void
142 npw_druid_fill_error_page (NPWDruid* druid, GtkWidget *extra_widget, GtkMessageType type, const gchar *detail, const gchar *mesg,...)
144 GtkAssistant *assistant;
145 GtkWidget *page;
146 va_list args;
147 gchar *message;
148 const gchar *stock_id = NULL;
149 const gchar *title = NULL;
151 assistant = GTK_ASSISTANT (druid->window);
153 /* Add page to assistant after current one */
154 page = druid->error_page;
155 gtk_assistant_insert_page (assistant, page, gtk_assistant_get_current_page (assistant) + 1);
157 /* Set dialog kind */
158 switch (type)
160 case GTK_MESSAGE_INFO:
161 stock_id = GTK_STOCK_DIALOG_INFO;
162 title = _("Information");
163 break;
164 case GTK_MESSAGE_QUESTION:
165 stock_id = GTK_STOCK_DIALOG_QUESTION;
166 title = _("Warning");
167 break;
168 case GTK_MESSAGE_WARNING:
169 stock_id = GTK_STOCK_DIALOG_WARNING;
170 title = _("Warning");
171 break;
172 case GTK_MESSAGE_ERROR:
173 stock_id = GTK_STOCK_DIALOG_ERROR;
174 title = _("Error");
175 break;
176 case GTK_MESSAGE_OTHER:
177 title = _("Message");
178 break;
179 default:
180 g_warning ("Unknown GtkMessageType %u", type);
181 break;
183 gtk_label_set_text (GTK_LABEL (druid->error_title), title);
184 gtk_assistant_set_page_title (assistant, page, title);
185 if (type == GTK_MESSAGE_ERROR)
187 gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_CONTENT);
188 gtk_assistant_set_page_complete (assistant, page, FALSE);
190 else
192 gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_PROGRESS);
193 gtk_assistant_set_page_complete (assistant, page, TRUE);
195 gtk_image_set_from_stock (druid->error_icon, stock_id, GTK_ICON_SIZE_DIALOG);
197 /* Set message */
198 va_start (args, mesg);
199 message = g_strdup_vprintf (mesg, args);
200 va_end (args);
201 gtk_label_set_markup (druid->error_message, message);
202 g_free (message);
204 /* Set detail */
205 if (detail == NULL)
207 gtk_widget_hide (druid->error_detail);
209 else
211 GtkLabel *label;
213 gtk_widget_show (druid->error_detail);
214 label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (druid->error_detail)));
215 gtk_label_set_text (label, detail);
218 if (druid->error_extra_widget)
219 gtk_widget_destroy (druid->error_extra_widget);
220 druid->error_extra_widget = NULL;
222 /* Set extra widget */
223 if (extra_widget)
225 gtk_box_pack_start (GTK_BOX (druid->error_vbox), extra_widget,
226 FALSE, FALSE, 10);
227 gtk_widget_show (extra_widget);
228 druid->error_extra_widget = extra_widget;
232 /* Create finish page
233 *---------------------------------------------------------------------------*/
235 static void
236 cb_druid_add_summary_property (NPWProperty* property, gpointer user_data)
238 GString* text = (GString*)user_data;
240 if (npw_property_get_options (property) & NPW_SUMMARY_OPTION)
242 g_string_append_printf (text, "%s %s\n",
243 npw_property_get_label (property),
244 npw_property_get_value (property));
248 /* Fill last page (summary) */
249 static void
250 npw_druid_fill_summary_page (NPWDruid* druid)
252 NPWPage* page;
253 guint i;
254 GString* text;
255 GtkLabel* label;
257 text = g_string_new (NULL);
258 g_string_append_printf (text, "<b>%s</b>\n\n", _("Confirm the following information:"));
260 /* The project type is translated too, it is something like
261 * generic, GNOME applet, Makefile project... */
262 g_string_append_printf (text, _("Project Type: %s\n"), npw_header_get_name (druid->header));
264 for (i = 0; (page = g_queue_peek_nth (druid->page_list, i)) != NULL; ++i)
266 npw_page_foreach_property (page, (GFunc)cb_druid_add_summary_property, text);
269 label = GTK_LABEL (druid->finish_text);
270 gtk_label_set_markup (label, text->str);
271 g_string_free (text, TRUE);
273 gtk_assistant_set_page_complete (GTK_ASSISTANT (druid->window), druid->finish_page, TRUE);
276 /* Create project selection page
277 *---------------------------------------------------------------------------*/
279 /* Update selected project */
280 static void
281 on_druid_project_update_selected (GtkIconView* view, NPWDruid *druid)
283 GList *selected;
284 NPWHeader* header = NULL;
286 /* No item can be selected when the view is mapped */
287 selected = gtk_icon_view_get_selected_items (view);
288 if (selected != NULL)
290 GtkTreeIter iter;
291 GtkTreeModel *model;
293 model = gtk_icon_view_get_model (view);
294 if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *)selected->data))
296 gtk_tree_model_get (model, &iter, DATA_COLUMN, &header, -1);
298 gtk_tree_path_free ((GtkTreePath *)selected->data);
299 g_list_free (selected);
302 druid->header = header;
303 gtk_assistant_set_page_complete (GTK_ASSISTANT (druid->window), druid->project_page, header != NULL);
306 /* Add a project in the icon list */
307 static void
308 cb_druid_insert_project_icon (gpointer data, gpointer user_data)
310 NPWHeader *header = (NPWHeader *)data;
311 GtkListStore* store = GTK_LIST_STORE (user_data);
312 GtkTreeIter iter;
313 GdkPixbuf *pixbuf;
315 gtk_list_store_append (store, &iter);
316 pixbuf = gdk_pixbuf_new_from_file (npw_header_get_iconfile (header), NULL);
317 gtk_list_store_set (store, &iter, PIXBUF_COLUMN, pixbuf,
318 TEXT_COLUMN, npw_header_get_name (header),
319 DESC_COLUMN, npw_header_get_description (header),
320 DATA_COLUMN, header,
321 -1);
323 g_object_unref (pixbuf);
326 /* Add a page in the project notebook */
327 static void
328 cb_druid_insert_project_page (gpointer value, gpointer user_data)
330 NPWDruid* druid = (NPWDruid*)user_data;
331 GtkBuilder *builder;
332 GtkWidget* label;
333 GtkWidget* child;
334 GtkWidget* assistant;
335 GtkIconView* view;
336 GtkNotebook *book;
337 GtkListStore *store;
338 GList *template_list = (GList *)value;
339 const gchar* category;
341 category = npw_header_get_category ((NPWHeader *)template_list->data);
343 /* Build another complete wizard dialog from the gtk builder file
344 * but keep only the project selection notebook page */
345 builder = gtk_builder_new ();
346 if (!gtk_builder_add_from_file (builder, GTK_BUILDER_UI_FILE, NULL))
348 g_warn_if_reached ();
349 g_object_unref (builder);
350 return;
353 /* Fill icon view */
354 view = GTK_ICON_VIEW (gtk_builder_get_object (builder, PROJECT_LIST));
355 gtk_icon_view_set_pixbuf_column (view, PIXBUF_COLUMN);
356 gtk_icon_view_set_markup_column (view, TEXT_COLUMN);
357 store = gtk_list_store_new (4, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
358 g_list_foreach (template_list, cb_druid_insert_project_icon, store);
359 gtk_icon_view_set_model (view, GTK_TREE_MODEL (store));
361 /* Connect signal to update dialog */
362 g_signal_connect (G_OBJECT (view), "selection-changed", G_CALLBACK (on_druid_project_update_selected), druid);
363 g_signal_connect (G_OBJECT (view), "map", G_CALLBACK (on_druid_project_update_selected), druid);
364 g_signal_connect_swapped (G_OBJECT (view), "item-activated", G_CALLBACK (gtk_assistant_next_page), druid->window);
366 /* Set new page label */
367 assistant = GTK_WIDGET (gtk_builder_get_object (builder, NEW_PROJECT_DIALOG));
368 book = GTK_NOTEBOOK (gtk_builder_get_object (builder, PROJECT_BOOK));
369 child = gtk_notebook_get_nth_page (book, 0);
370 label = gtk_notebook_get_tab_label (book, child);
371 gtk_label_set_text (GTK_LABEL(label), (const gchar *)category);
373 /* Pick up the filled project selection page from the newly created dialog
374 * add it to the wizard project notebook and destroy the dialog
376 gtk_notebook_remove_page (book, 0);
377 gtk_notebook_append_page (druid->project_book, child, label);
378 gtk_widget_destroy (assistant);
380 g_object_unref (builder);
383 /* Fill project selection page */
384 static gboolean
385 npw_druid_fill_selection_page (NPWDruid* druid, const gchar *directory)
387 gchar* dir;
388 const gchar * const * sys_dir;
390 /* Remove all previous data */
391 gtk_notebook_remove_page(druid->project_book, 0);
392 npw_header_list_free (druid->header_list);
393 anjuta_autogen_clear_library_path (druid->gen);
395 /* Create list of projects */
396 druid->header_list = npw_header_list_new ();
398 if (directory != NULL)
400 /* Read project template only in specified directory,
401 * other directories can still be used to get included
402 * files */
403 npw_header_list_readdir (&druid->header_list, directory);
404 anjuta_autogen_set_library_path (druid->gen, directory);
407 dir = g_build_filename (g_get_user_data_dir (), "anjuta", "project", NULL);
408 if (directory == NULL)
410 /* Read project template in user directory,
411 * normally ~/.local/share/anjuta/project,
412 * the first template read override the others */
413 npw_header_list_readdir (&druid->header_list, dir);
415 anjuta_autogen_set_library_path (druid->gen, dir);
416 g_free (dir);
418 for (sys_dir = g_get_system_data_dirs (); *sys_dir != NULL; sys_dir++)
420 dir = g_build_filename (*sys_dir, "anjuta", "project", NULL);
421 if (directory == NULL)
423 /* Read project template in system directory */
424 npw_header_list_readdir (&druid->header_list, dir);
426 anjuta_autogen_set_library_path (druid->gen, dir);
427 g_free (dir);
430 if (directory == NULL)
432 /* Read anjuta installation directory */
433 npw_header_list_readdir (&druid->header_list, PROJECT_WIZARD_DIRECTORY);
435 anjuta_autogen_set_library_path (druid->gen, PROJECT_WIZARD_DIRECTORY);
437 if (g_list_length (druid->header_list) == 0)
439 anjuta_util_dialog_error (GTK_WINDOW (ANJUTA_PLUGIN (druid->plugin)->shell),_("Unable to find any project template in %s"), PROJECT_WIZARD_DIRECTORY);
440 return FALSE;
443 /* Add all necessary notebook page */
444 g_list_foreach (druid->header_list, cb_druid_insert_project_page, druid);
446 gtk_widget_show_all (GTK_WIDGET (druid->project_book));
448 return TRUE;
451 /* Create properties pages
452 *---------------------------------------------------------------------------*/
454 /* Group infomation need by the following call back function */
455 typedef struct _NPWDruidAddPropertyData
457 NPWDruid* druid;
458 guint row;
459 GtkTable *table;
460 GtkWidget *first_entry;
461 } NPWDruidAddPropertyData;
463 static void
464 cb_druid_destroy_widget (GtkWidget* widget, gpointer user_data)
466 gtk_widget_destroy (widget);
469 static void
470 cb_druid_add_property (NPWProperty* property, gpointer user_data)
472 GtkWidget* label;
473 GtkWidget* entry;
474 NPWDruidAddPropertyData* data = (NPWDruidAddPropertyData *)user_data;
475 const gchar* description;
476 GtkAttachOptions attach = GTK_EXPAND|GTK_FILL;
479 entry = npw_property_create_widget (property);
480 if (entry != NULL)
482 /* Not hidden property */
483 description = npw_property_get_description (property);
485 /* Set description tooltip */
486 if (description && (*description != '\0'))
488 gtk_widget_set_tooltip_text (entry, description);
491 label = gtk_label_new (npw_property_get_label (property));
492 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
493 gtk_misc_set_padding (GTK_MISC (label), 6, 6);
495 switch (npw_property_get_type (property))
497 case NPW_PACKAGE_PROPERTY:
498 gtk_table_resize (data->table, data->row + 2, 1);
499 gtk_table_attach (data->table, label, 0, 1, data->row, data->row + 1,
500 (GtkAttachOptions)(GTK_FILL), 0, 0, 0);
501 gtk_table_attach (data->table, entry, 0, 1, data->row + 1, data->row + 2,
502 (GtkAttachOptions)(GTK_EXPAND|GTK_FILL),
503 (GtkAttachOptions)(GTK_EXPAND|GTK_FILL), 0, 0);
504 data->row += 2;
505 break;
506 case NPW_BOOLEAN_PROPERTY:
507 attach = 0;
508 /* Fall through */
509 default:
510 /* Add label and entry */
511 gtk_table_resize (data->table, data->row + 1, 2);
512 gtk_table_attach (data->table, label, 0, 1, data->row, data->row + 1,
513 (GtkAttachOptions)(GTK_FILL), 0, 0, 0);
514 gtk_table_attach (data->table, entry, 1, 2, data->row, data->row + 1,
515 (GtkAttachOptions)(attach), 0, 0, 0);
516 data->row++;
519 /* Set first entry */
520 if (data->first_entry == NULL) data->first_entry = entry;
524 static void
525 npw_druid_fill_property_page (NPWDruid* druid, NPWPage* page)
527 GtkWidget *widget;
528 GList *children;
529 GtkLabel *label;
530 NPWDruidAddPropertyData data;
532 /* Add page to assistant, after current page */
533 widget = gtk_assistant_get_nth_page (GTK_ASSISTANT (druid->window), gtk_assistant_get_current_page (GTK_ASSISTANT (druid->window)) + 1);
535 /* Remove previous widgets */
536 gtk_container_foreach (GTK_CONTAINER (npw_page_get_widget (page)), cb_druid_destroy_widget, NULL);
538 /* Update title */
539 children = gtk_container_get_children (GTK_CONTAINER (widget));
540 label = GTK_LABEL (g_list_nth_data (children, 0));
541 g_list_free (children);
542 gtk_label_set_text (label, npw_page_get_label (page));
543 gtk_assistant_set_page_title (GTK_ASSISTANT (druid->window), widget, npw_page_get_label (page));
545 /* Add new widget */
546 data.druid = druid;
547 data.row = 0;
548 data.table = GTK_TABLE (npw_page_get_widget (page));
549 data.first_entry = NULL;
550 npw_page_foreach_property (page, (GFunc)cb_druid_add_property, &data);
552 /* Move focus on first entry */
553 if (data.first_entry != NULL)
555 gtk_container_set_focus_child (GTK_CONTAINER (data.table), data.first_entry);
558 gtk_widget_show_all (widget);
561 /* Handle page cache
562 *---------------------------------------------------------------------------*/
564 static NPWPage*
565 npw_druid_add_new_page (NPWDruid* druid)
567 gint current;
568 NPWPage* page;
570 /* Get page in cache */
571 current = gtk_assistant_get_current_page (GTK_ASSISTANT (druid->window));
572 page = g_queue_peek_nth (druid->page_list, current);
574 if (page == NULL)
576 /* Page not found in cache, create */
577 GtkBuilder *builder;
578 GtkWidget *widget;
579 GtkAssistantPageType type;
580 GdkPixbuf *pixbuf;
581 GtkWidget *table;
582 GtkAssistant *assistant;
584 /* Build another complete wizard dialog from the gtk builder file
585 * but keep only the property assistant page */
586 builder = gtk_builder_new ();
587 if (!gtk_builder_add_from_file (builder, GTK_BUILDER_UI_FILE, NULL))
589 g_warn_if_reached ();
590 g_object_unref (builder);
592 return NULL;
594 assistant = GTK_ASSISTANT (gtk_builder_get_object (builder, NEW_PROJECT_DIALOG));
595 widget = GTK_WIDGET (gtk_builder_get_object (builder, PROPERTY_PAGE));
596 table = GTK_WIDGET (gtk_builder_get_object (builder, PROPERTY_TABLE));
598 type = gtk_assistant_get_page_type (assistant, widget);
599 pixbuf = gtk_assistant_get_page_header_image (assistant, widget);
600 if (pixbuf) g_object_ref (pixbuf);
601 gtk_container_remove (GTK_CONTAINER (assistant), widget);
602 gtk_assistant_insert_page (GTK_ASSISTANT (druid->window), widget, current + 1);
603 gtk_assistant_set_page_type (GTK_ASSISTANT (druid->window), widget, type);
604 if (pixbuf != NULL)
606 gtk_assistant_set_page_header_image (GTK_ASSISTANT (druid->window), widget, pixbuf);
607 g_object_ref (pixbuf);
609 gtk_assistant_set_page_complete (GTK_ASSISTANT (druid->window), widget, TRUE);
610 gtk_widget_destroy (GTK_WIDGET (assistant));
612 /* Builder get reference on all built widget, so unref it when all work is done */
613 g_object_unref (builder);
615 /* Create new page */
616 page = npw_page_new (druid->values);
617 npw_page_set_widget (page, table);
619 /* Add page in cache */
620 g_queue_push_tail (druid->page_list, page);
623 return page;
626 /* Remove all pages following current page except the summary page*/
628 static void
629 npw_druid_remove_following_page (NPWDruid* druid)
631 gint current;
633 current = gtk_assistant_get_current_page (GTK_ASSISTANT (druid->window));
634 for(;;)
636 GtkWidget *widget;
637 NPWPage* page;
639 widget = gtk_assistant_get_nth_page (GTK_ASSISTANT (druid->window), current + 1);
640 if (widget == druid->finish_page) break;
642 gtk_container_remove (GTK_CONTAINER (druid->window), widget);
644 page = g_queue_pop_nth (druid->page_list, current - 1);
645 if (page != NULL) npw_page_free (page);
649 /* Save properties
650 *---------------------------------------------------------------------------*/
652 typedef struct _NPWSaveValidPropertyData
654 GtkWindow *parent;
655 gboolean modified;
656 GString *error;
657 GString *warning;
659 } NPWSaveValidPropertyData;
661 static void
662 cb_save_valid_property (NPWProperty* property, gpointer user_data)
664 NPWSaveValidPropertyData* data = (NPWSaveValidPropertyData *)user_data;
665 const gchar* value;
666 gboolean modified;
668 /* Get value from widget */
669 modified = npw_property_update_value_from_widget (property);
670 if (modified) data->modified = modified;
671 value = npw_property_get_value (property);
673 /* Check mandatory property */
674 if (modified && (npw_property_get_options (property) & NPW_MANDATORY_OPTION))
676 if ((value == NULL) || (strlen (value) <= 0))
678 g_string_append_printf (data->error,
679 _("\nField \"%s\" is mandatory. Please enter it."),
680 npw_property_get_label (property));
681 npw_property_remove_value (property);
685 /* Check restricted property */
686 if (modified && !npw_property_is_valid_restriction (property))
688 NPWPropertyRestriction restriction = npw_property_get_restriction (property);
690 switch (restriction)
692 case NPW_FILENAME_RESTRICTION:
693 g_string_append_printf (data->error,
694 _("Field \"%s\" must contains only letters, digits or the following characters \"#$:%%+,.=@^_`~\". In addition you cannot have a leading dash. Please fix it."),
695 npw_property_get_label (property));
696 break;
697 case NPW_DIRECTORY_RESTRICTION:
698 g_string_append_printf (data->error,
699 _("Field \"%s\" must contains only letters, digits, the following characters \"#$:%%+,.=@^_`~\" or directory separators. In addition you cannot have a leading dash. Please fix it."),
700 npw_property_get_label (property));
701 break;
702 case NPW_PRINTABLE_RESTRICTION:
703 g_string_append_printf (data->error,
704 _("Field \"%s\" must contains only ASCII printable characters, no accentuated characters by example. Please fix it."),
705 npw_property_get_label (property));
706 break;
707 default:
708 g_string_append_printf (data->error,
709 _("Unknown error."));
712 npw_property_remove_value (property);
715 /* Check exist property */
716 if (modified && (npw_property_get_exist_option (property) == NPW_FALSE))
718 gboolean is_directory = npw_property_get_type (property) == NPW_DIRECTORY_PROPERTY;
719 gboolean exist = (value != NULL) && g_file_test (value, G_FILE_TEST_EXISTS);
720 /* Allow empty directory */
721 if (exist && is_directory)
723 GDir* dir;
725 dir = g_dir_open (value, 0, NULL);
726 if (dir != NULL)
728 if (g_dir_read_name (dir) == NULL) exist = FALSE;
729 g_dir_close (dir);
733 if (exist)
735 g_string_append_printf (data->warning, is_directory ?
736 _("Directory \"%s\" is not empty. Project creation could fail if some files "
737 "cannot be written. Do you want to continue?") :
738 _("File \"%s\" already exists. Do you want to overwrite it?"),
739 value);
744 /* Save values and check them, return TRUE if it's possible to go to
745 * next page */
747 static gboolean
748 npw_druid_save_valid_values (NPWDruid* druid)
750 gint current;
751 NPWPage* page;
752 NPWSaveValidPropertyData data;
753 gboolean ok = TRUE;
755 current = gtk_assistant_get_current_page (GTK_ASSISTANT (druid->window)) - 2;
756 page = g_queue_peek_nth (druid->page_list, current);
757 data.modified = FALSE;
758 data.parent = GTK_WINDOW (druid->window);
759 data.error = g_string_new (NULL);
760 data.warning = g_string_new (NULL);
761 npw_page_foreach_property (page, (GFunc)cb_save_valid_property, &data);
763 if (data.modified) npw_druid_remove_following_page (druid);
765 if (data.error->len)
767 npw_druid_fill_error_page (druid, NULL,
768 GTK_MESSAGE_ERROR,
769 NULL,
770 "<b>%s</b>\n\n%s",
771 _("Invalid entry"),
772 data.error->str);
773 ok = FALSE;
775 else if (data.warning->len)
777 npw_druid_fill_error_page (druid, NULL,
778 GTK_MESSAGE_WARNING,
779 NULL,
780 "<b>%s</b>\n\n%s",
781 _("Dubious entry"),
782 data.warning->str);
783 ok = FALSE;
786 g_string_free (data.error, TRUE);
787 g_string_free (data.warning, TRUE);
789 return ok;
792 /* Druid GUI functions
793 *---------------------------------------------------------------------------*/
795 static void
796 npw_druid_set_busy (NPWDruid *druid, gboolean busy_state)
798 if (druid->busy == busy_state)
799 return;
801 /* Set busy state */
802 if (busy_state)
803 anjuta_status_busy_push (anjuta_shell_get_status (ANJUTA_PLUGIN (druid->plugin)->shell, NULL));
804 else
805 anjuta_status_busy_pop (anjuta_shell_get_status (ANJUTA_PLUGIN (druid->plugin)->shell, NULL));
806 druid->busy = busy_state;
809 /* Druid call backs
810 *---------------------------------------------------------------------------*/
812 static void
813 on_druid_cancel (GtkAssistant* window, NPWDruid* druid)
815 anjuta_plugin_deactivate (ANJUTA_PLUGIN (druid->plugin));
816 npw_druid_free (druid);
819 static void
820 on_druid_close (GtkAssistant* window, NPWDruid* druid)
822 anjuta_plugin_deactivate (ANJUTA_PLUGIN (druid->plugin));
823 npw_druid_free (druid);
826 static gboolean
827 on_project_wizard_key_press_event(GtkWidget *widget, GdkEventKey *event,
828 NPWDruid* druid)
830 if (event->keyval == GDK_KEY_Escape)
832 on_druid_cancel (GTK_ASSISTANT (widget), druid);
833 return TRUE;
835 return FALSE;
838 static void
839 on_druid_get_new_page (AnjutaAutogen* gen, gpointer data)
841 NPWDruid* druid = (NPWDruid *)data;
842 gint current;
843 NPWPage* page;
845 current = gtk_assistant_get_current_page (GTK_ASSISTANT (druid->window));
846 page = g_queue_peek_nth (druid->page_list, current - 1);
848 if (npw_page_get_name (page) == NULL)
850 /* no page, display finish page */
851 npw_druid_fill_summary_page (druid);
853 page = g_queue_pop_nth (druid->page_list, current - 1);
854 if (page != NULL) npw_page_free (page);
855 gtk_container_remove (GTK_CONTAINER (druid->window), gtk_assistant_get_nth_page (GTK_ASSISTANT (druid->window), current + 1));
856 gtk_assistant_set_current_page (GTK_ASSISTANT (druid->window), current + 1);
858 else
860 /* display property page */
861 npw_druid_fill_property_page (druid, page);
863 gtk_assistant_set_current_page (GTK_ASSISTANT (druid->window), current + 1);
867 static void
868 on_druid_parse_page (const gchar* output, gpointer data)
870 GError *error = NULL;
871 NPWPageParser* parser = (NPWPageParser*)data;
873 npw_page_parser_parse (parser, output, strlen (output), &error);
875 if (error)
877 g_warning ("Parser error: %s", error->message);
878 g_error_free (error);
882 static void
883 strip_package_version_info (gpointer data, gpointer user_data)
885 gchar * const pkg = (gchar *) data;
887 if (!data)
888 return;
889 g_strdelimit (pkg, " ", '\0');
892 static void
893 on_install_button_clicked (GtkWidget *button, NPWDruid *druid)
895 GList *missing_programs, *missing_packages;
896 GList *missing_files = NULL;
897 GList *node;
900 missing_programs = npw_header_check_required_programs (druid->header);
901 missing_packages = npw_header_check_required_packages (druid->header);
903 anjuta_util_glist_strings_prefix (missing_programs, "/usr/bin/");
905 /* Search for "pkgconfig(pkg_name)" */
906 g_list_foreach (missing_packages, (GFunc) strip_package_version_info,
907 NULL);
908 missing_files = g_list_concat (missing_files, missing_programs);
910 for (node = missing_packages; node != NULL; node = g_list_next (missing_packages))
912 gchar* pk_pkg_config_string = g_strdup_printf ("pkgconfig(%s)", (gchar*) node->data);
913 missing_files = g_list_append (missing_files, pk_pkg_config_string);
915 g_list_free (missing_packages);
917 if (missing_files)
919 gchar * missing_names = NULL;
921 missing_names = anjuta_util_glist_strings_join (missing_files, ", ");
922 anjuta_util_install_files (missing_names);
924 if (missing_names)
925 g_free (missing_names);
926 anjuta_util_glist_strings_free (missing_files);
930 static gboolean
931 check_and_warn_missing (NPWDruid *druid)
933 GList *missing_programs, *missing_packages;
934 GString *missing_message = NULL;
936 missing_programs = npw_header_check_required_programs (druid->header);
937 missing_packages = npw_header_check_required_packages (druid->header);
939 if (missing_programs || missing_packages)
941 missing_message = g_string_new (NULL);
944 if (missing_programs)
946 gchar *missing_progs;
947 missing_progs = anjuta_util_glist_strings_join (missing_programs,
948 ", ");
949 g_string_append_printf (missing_message,
950 _("\nMissing programs: %s."), missing_progs);
951 g_free (missing_progs);
952 anjuta_util_glist_strings_free (missing_programs);
955 if (missing_packages)
957 gchar *missing_pkgs;
958 missing_pkgs = anjuta_util_glist_strings_join (missing_packages,
959 ", ");
960 g_string_append_printf (missing_message,
961 _("\nMissing packages: %s."), missing_pkgs);
962 g_free (missing_pkgs);
963 anjuta_util_glist_strings_free (missing_packages);
966 if (missing_message)
968 GtkWidget *hbox, *install_button;
969 g_string_prepend (missing_message, _(
970 "Some important programs or development packages required to build "
971 "this project are missing. Please make sure they are "
972 "installed properly before generating the project.\n"));
974 hbox = gtk_hbox_new (FALSE, 0);
975 gtk_widget_show (hbox);
977 #ifdef ENABLE_PACKAGEKIT
978 install_button =
979 gtk_button_new_with_label (_("Install missing packages"));
980 gtk_box_pack_end (GTK_BOX (hbox), install_button, FALSE, FALSE, 10);
981 g_signal_connect (install_button, "clicked",
982 G_CALLBACK (on_install_button_clicked), druid);
983 gtk_widget_show (install_button);
984 #endif
986 npw_druid_fill_error_page (druid, hbox,
987 GTK_MESSAGE_WARNING,
988 /* Translators: Application Manager is the program used to install
989 * new application like apt on Ubuntu, yum on Fedora, zypper on
990 * OpenSuSE and emerge on Gentoo */
991 _("The missing programs are usually part of some distribution "
992 "packages and can be searched for in your Application Manager. "
993 "Similarly, the development packages are contained in special "
994 "packages that your distribution provides to allow development "
995 "of projects based on them. They usually end with a \"-dev\" or "
996 "\"-devel\" suffix in package names and can be found by searching "
997 "in your Application Manager."),
998 "<b>%s</b>\n\n%s",
999 _("Missing components"),
1000 missing_message->str);
1001 g_string_free (missing_message, TRUE);
1004 return !missing_message;
1008 static void
1009 on_druid_real_prepare (GtkAssistant* assistant, GtkWidget *page, NPWDruid* druid)
1011 if (page == druid->progress_page)
1013 gint previous;
1014 gboolean last_warning;
1016 previous = gtk_assistant_get_current_page (assistant) - 1;
1017 last_warning = gtk_assistant_get_nth_page (assistant, previous) == druid->error_page;
1018 if (last_warning)
1020 /* Remove warning page */
1021 gtk_container_remove (GTK_CONTAINER (assistant), druid->error_page);
1022 previous--;
1025 /* Generate the next page */
1026 if (previous == PROJECT_PAGE_INDEX)
1028 const gchar* new_project;
1030 new_project = npw_header_get_filename (druid->header);
1032 if (druid->project_file != new_project)
1034 npw_druid_remove_following_page (druid);
1036 /* Check if necessary programs for this project is installed */
1037 if (!last_warning && !check_and_warn_missing (druid))
1039 gtk_assistant_set_current_page (assistant, gtk_assistant_get_current_page (assistant) + 1);
1040 return;
1043 /* Change project */
1044 druid->project_file = new_project;
1045 anjuta_autogen_set_input_file (druid->gen, druid->project_file, "[+","+]");
1049 else
1051 if (!npw_druid_save_valid_values (druid))
1053 /* Display error */
1054 gtk_assistant_set_current_page (assistant, gtk_assistant_get_current_page (assistant) + 1);
1056 return;
1060 if (g_queue_peek_nth (druid->page_list, previous) == NULL)
1062 /* Regenerate new page */
1063 gtk_assistant_set_page_complete (assistant, page, FALSE);
1064 if (druid->parser != NULL)
1065 npw_page_parser_free (druid->parser);
1066 druid->parser = npw_page_parser_new (npw_druid_add_new_page (druid), druid->project_file, previous);
1068 anjuta_autogen_set_output_callback (druid->gen, on_druid_parse_page, druid->parser);
1069 anjuta_autogen_write_definition_file (druid->gen, druid->values, NULL);
1070 anjuta_autogen_execute (druid->gen, on_druid_get_new_page, druid, NULL);
1072 else
1074 /* Page is already in cache, change the page to display it */
1075 on_druid_get_new_page (NULL, druid);
1078 else if (page == druid->finish_page)
1080 npw_druid_set_busy (druid, FALSE);
1081 gtk_container_remove (GTK_CONTAINER (assistant), druid->error_page);
1082 gtk_container_remove (GTK_CONTAINER (assistant), druid->progress_page);
1084 else
1086 npw_druid_set_busy (druid, FALSE);
1088 if (page != druid->error_page) gtk_container_remove (GTK_CONTAINER (assistant), druid->error_page);
1090 /* Move progress page */
1091 gtk_container_remove (GTK_CONTAINER (assistant), druid->progress_page);
1092 gtk_assistant_insert_page (assistant, druid->progress_page, gtk_assistant_get_current_page (assistant) + 1);
1093 gtk_assistant_set_page_title (assistant, druid->progress_page, "...");
1097 static gboolean
1098 on_druid_delayed_prepare (gpointer data)
1100 NPWDruid *druid = (NPWDruid *)data;
1101 GtkAssistant *assistant;
1102 GtkWidget *page;
1104 assistant = GTK_ASSISTANT (druid->window);
1105 page = gtk_assistant_get_nth_page (assistant, gtk_assistant_get_current_page (assistant));
1106 on_druid_real_prepare (assistant, page, druid);
1108 return FALSE;
1111 static void
1112 on_druid_prepare (GtkAssistant* assistant, GtkWidget *page, NPWDruid* druid)
1114 /* The page change is delayed because in the latest version of
1115 * GtkAssistant, the page switch is not completely done when
1116 * the signal is called. A page change in the signal handler
1117 * will be partialy overwritten */
1118 g_idle_add (on_druid_delayed_prepare, druid);
1122 static void
1123 on_druid_finish (GtkAssistant* assistant, NPWDruid* druid)
1125 NPWInstall* inst;
1126 GList *path;
1128 inst = npw_install_new (druid->plugin);
1129 npw_install_set_property (inst, druid->values);
1130 npw_install_set_wizard_file (inst, npw_header_get_filename (druid->header));
1131 for (path = g_list_last (anjuta_autogen_get_library_paths (druid->gen)); path != NULL; path = g_list_previous (path))
1133 npw_install_set_library_path (inst, (const gchar *)path->data);
1135 npw_install_launch (inst);
1138 static GtkWidget*
1139 npw_druid_create_assistant (NPWDruid* druid, const gchar *directory)
1141 AnjutaShell *shell;
1142 GtkBuilder *builder;
1143 GError* error = NULL;
1144 GtkAssistant *assistant;
1145 GtkWidget *property_page;
1147 g_return_val_if_fail (druid->window == NULL, NULL);
1149 shell = ANJUTA_PLUGIN (druid->plugin)->shell;
1151 /* Create GtkAssistant using GtkBuilder, glade doesn't seem to work*/
1152 builder = gtk_builder_new ();
1153 if (!gtk_builder_add_from_file (builder, GTK_BUILDER_UI_FILE, &error))
1155 g_warning ("Couldn't load builder file: %s", error->message);
1156 g_error_free (error);
1157 return NULL;
1159 anjuta_util_builder_get_objects (builder,
1160 NEW_PROJECT_DIALOG, &assistant,
1161 PROJECT_BOOK, &druid->project_book,
1162 ERROR_VBOX, &druid->error_vbox,
1163 ERROR_TITLE, &druid->error_title,
1164 ERROR_ICON, &druid->error_icon,
1165 ERROR_MESSAGE, &druid->error_message,
1166 ERROR_DETAIL, &druid->error_detail,
1167 PROJECT_PAGE, &druid->project_page,
1168 ERROR_PAGE, &druid->error_page,
1169 PROGRESS_PAGE, &druid->progress_page,
1170 FINISH_PAGE, &druid->finish_page,
1171 FINISH_TEXT, &druid->finish_text,
1172 PROPERTY_PAGE, &property_page,
1173 NULL);
1174 druid->window = GTK_WINDOW (assistant);
1175 g_object_ref (druid->error_page);
1176 g_object_ref (druid->progress_page);
1177 gtk_window_set_transient_for (GTK_WINDOW (assistant), GTK_WINDOW (shell));
1178 g_object_unref (builder);
1180 /* Connect assistant signals */
1181 g_signal_connect (G_OBJECT (assistant), "prepare", G_CALLBACK (on_druid_prepare), druid);
1182 g_signal_connect (G_OBJECT (assistant), "apply", G_CALLBACK (on_druid_finish), druid);
1183 g_signal_connect (G_OBJECT (assistant), "cancel", G_CALLBACK (on_druid_cancel), druid);
1184 g_signal_connect (G_OBJECT (assistant), "close", G_CALLBACK (on_druid_close), druid);
1185 g_signal_connect(G_OBJECT(assistant), "key-press-event", G_CALLBACK(on_project_wizard_key_press_event), druid);
1187 /* Remove property page, will be created later as needed */
1188 gtk_container_remove (GTK_CONTAINER (assistant), property_page);
1190 /* Setup project selection page */
1191 if (!npw_druid_fill_selection_page (druid, directory))
1193 return NULL;
1196 /* Add dialog widget to anjuta status. */
1197 anjuta_status_add_widget (anjuta_shell_get_status (shell, NULL), GTK_WIDGET (assistant));
1199 gtk_window_set_default_size (GTK_WINDOW (assistant),
1200 600, 500);
1202 gtk_widget_show_all (GTK_WIDGET (assistant));
1204 return GTK_WIDGET(assistant);
1207 /* Add default property
1208 *---------------------------------------------------------------------------*/
1210 static void
1211 npw_druid_add_default_property (NPWDruid* druid)
1213 gchar* s;
1214 AnjutaPreferences* pref;
1215 GSettings *settings;
1216 gboolean flag;
1217 gint i;
1219 pref = anjuta_shell_get_preferences (ANJUTA_PLUGIN (druid->plugin)->shell, NULL);
1221 /* Add default base project directory */
1222 g_hash_table_insert (druid->values, g_strdup (ANJUTA_PROJECT_DIRECTORY_PROPERTY), g_strdup (g_get_home_dir()));
1224 /* Add user name */
1225 g_hash_table_insert (druid->values, g_strdup (USER_NAME_PROPERTY), g_strdup (g_get_real_name()));
1227 /* Add Email address */
1228 /* FIXME: We need a default way for the mail */
1229 s = anjuta_util_get_user_mail();
1230 g_hash_table_insert (druid->values, g_strdup (EMAIL_ADDRESS_PROPERTY), s);
1232 /* Add use-tabs property */
1233 settings = g_settings_new (ANJUTA_PREF_SCHEMA_PREFIX IANJUTA_EDITOR_PREF_SCHEMA);
1234 flag = g_settings_get_boolean (settings, IANJUTA_EDITOR_USE_TABS_KEY);
1235 g_hash_table_insert (druid->values, g_strdup (USE_TABS_PROPERTY), g_strdup (flag ? "1" : "0"));
1237 /* Add tab-width property */
1238 i = g_settings_get_int (settings, IANJUTA_EDITOR_TAB_WIDTH_KEY);
1239 g_hash_table_insert (druid->values, g_strdup (TAB_WIDTH_PROPERTY), g_strdup_printf("%d", i));
1241 /* Add indent-width property */
1242 i = g_settings_get_int (settings, IANJUTA_EDITOR_INDENT_WIDTH_KEY);
1243 g_hash_table_insert (druid->values, g_strdup (INDENT_WIDTH_PROPERTY), g_strdup_printf("%d", i));
1244 g_object_unref (settings);
1247 /* Druid public functions
1248 *---------------------------------------------------------------------------*/
1250 NPWDruid*
1251 npw_druid_new (NPWPlugin* plugin, const gchar *directory)
1253 NPWDruid* druid;
1255 /* Check if autogen is present */
1256 if (!anjuta_check_autogen())
1258 anjuta_util_dialog_error (NULL, _("Could not find autogen version 5; please install the autogen package. You can get it from http://autogen.sourceforge.net."));
1259 return NULL;
1262 druid = g_new0(NPWDruid, 1);
1263 druid->plugin = plugin;
1264 druid->project_file = NULL;
1265 druid->busy = FALSE;
1266 druid->page_list = g_queue_new ();
1267 druid->values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_free);
1268 druid->gen = anjuta_autogen_new ();
1269 druid->plugin = plugin;
1270 druid->error_extra_widget = NULL;
1272 if (npw_druid_create_assistant (druid, directory) == NULL)
1274 npw_druid_free (druid);
1276 return NULL;
1279 npw_druid_add_default_property (druid);
1281 return druid;
1284 void
1285 npw_druid_free (NPWDruid* druid)
1287 NPWPage* page;
1289 g_return_if_fail (druid != NULL);
1291 /* Delete page list */
1293 while ((page = (NPWPage *)g_queue_pop_head (druid->page_list)) != NULL)
1295 npw_page_free (page);
1297 g_queue_free (druid->page_list);
1298 g_hash_table_destroy (druid->values);
1299 anjuta_autogen_free (druid->gen);
1300 if (druid->parser != NULL) npw_page_parser_free (druid->parser);
1301 npw_header_list_free (druid->header_list);
1302 gtk_widget_destroy (GTK_WIDGET (druid->window));
1303 g_object_unref (druid->error_page);
1304 g_object_unref (druid->progress_page);
1305 druid->plugin->druid = NULL;
1306 g_free (druid);
1309 void
1310 npw_druid_show (NPWDruid* druid)
1312 g_return_if_fail (druid != NULL);
1314 /* Display dialog box */
1315 if (druid->window) gtk_window_present (druid->window);