r5094
[geany-mirror.git] / src / build.c
blob89b5c4120b20aadfd7fc9582ef76f62604f29d61
1 /*
2 * build.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6 * Copyright 2009 Lex Trotman <elextr(at)gmail(dot)com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * $Id$
26 * Build commands and menu items.
29 #include "geany.h"
30 #include "build.h"
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <glib/gstdio.h>
39 #ifdef G_OS_UNIX
40 # include <sys/types.h>
41 # include <sys/wait.h>
42 # include <signal.h>
43 #else
44 # include <windows.h>
45 #endif
47 #include "prefs.h"
48 #include "support.h"
49 #include "document.h"
50 #include "utils.h"
51 #include "ui_utils.h"
52 #include "dialogs.h"
53 #include "msgwindow.h"
54 #include "filetypes.h"
55 #include "keybindings.h"
56 #include "vte.h"
57 #include "project.h"
58 #include "editor.h"
59 #include "win32.h"
60 #include "toolbar.h"
61 #include "geanymenubuttonaction.h"
63 /* Number of editor indicators to draw - limited as this can affect performance */
64 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
67 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
69 static gchar *current_dir_entered = NULL;
71 typedef struct RunInfo
73 GPid pid;
74 gint file_type_id;
75 } RunInfo;
77 static RunInfo *run_info;
79 #ifdef G_OS_WIN32
80 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script.bat";
81 #else
82 static const gchar RUN_SCRIPT_CMD[] = "./geany_run_script.sh";
83 #endif
85 /* pack group (<8) and command (<32) into a user_data pointer */
86 #define GRP_CMD_TO_POINTER(grp, cmd) GINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
87 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
88 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_INT(gptr)&0x1f)
89 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_INT(gptr)&0xe0) >> 5)
91 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
93 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
95 static struct
97 GtkAction *run_action;
98 GtkAction *compile_action;
99 GtkAction *build_action;
100 GtkWidget *toolmenu;
102 GtkWidget *toolitem_build;
103 GtkWidget *toolitem_make_all;
104 GtkWidget *toolitem_make_custom;
105 GtkWidget *toolitem_make_object;
106 GtkWidget *toolitem_set_args;
108 widgets;
110 static gint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
111 static gint build_items_count = 9;
113 #ifndef G_OS_WIN32
114 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data);
115 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data);
116 #endif
117 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose);
118 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
119 static void set_stop_button(gboolean stop);
120 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
121 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
122 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
123 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
124 static void kill_process(GPid *pid);
125 static void show_build_result_message(gboolean failure);
126 static void process_build_output_line(const gchar *line, gint color);
127 static void show_build_commands_dialog(void);
130 void build_finalize(void)
132 g_free(build_info.dir);
133 g_free(build_info.custom_target);
135 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
136 gtk_widget_destroy(menu_items.menu);
140 /* note: copied from keybindings.c, may be able to go away */
141 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
142 GtkAccelGroup *accel_group, GtkWidget *menuitem)
144 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
146 if (kb->key != 0)
147 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
148 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
152 /* convenience routines to access parts of GeanyBuildCommand */
153 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
155 return bc->entries[id];
159 static gchar *buildcmd_label(GeanyBuildCommand *bc)
161 return _(id_to_str(bc, GEANY_BC_LABEL));
165 static gchar *buildcmd_cmd(GeanyBuildCommand *bc)
167 return id_to_str(bc, GEANY_BC_COMMAND);
171 static gchar *buildcmd_working_dir(GeanyBuildCommand *bc)
173 return id_to_str(bc, GEANY_BC_WORKING_DIR);
177 static const gchar *config_keys[] = {
178 [GEANY_BC_LABEL] = "LB",
179 [GEANY_BC_COMMAND] = "CM",
180 [GEANY_BC_WORKING_DIR] = "WD"
183 /*-----------------------------------------------------
185 * Execute commands and handle results
187 *-----------------------------------------------------*/
189 /* the various groups of commands not in the filetype struct */
190 static GeanyBuildCommand *ft_def = NULL;
191 static GeanyBuildCommand *non_ft_proj = NULL;
192 static GeanyBuildCommand *non_ft_pref = NULL;
193 static GeanyBuildCommand *non_ft_def = NULL;
194 static GeanyBuildCommand *exec_proj = NULL;
195 static GeanyBuildCommand *exec_pref = NULL;
196 static GeanyBuildCommand *exec_def = NULL;
197 /* and the regexen not in the filetype structure */
198 static gchar *regex_pref = NULL;
199 /* project non-fileregex string */
200 static gchar *regex_proj = NULL;
202 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
203 #ifndef PRINTBUILDCMDS
204 #define PRINTBUILDCMDS FALSE
205 #endif
206 static gboolean printbuildcmds = PRINTBUILDCMDS;
209 /* for debug only, print the commands structures in priority order */
210 static void printfcmds(void)
212 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
213 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
214 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
215 { &ft_def, NULL, NULL, NULL, NULL, NULL },
216 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
217 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
219 GeanyFiletype *ft = NULL;
220 GeanyDocument *doc;
221 gint i, j, k, l, m;
222 enum GeanyBuildCmdEntries n;
223 gint cc[GEANY_BCS_COUNT];
224 gchar c;
226 doc = document_get_current();
227 if (doc != NULL)
228 ft = doc->file_type;
229 if (ft != NULL)
231 printf("filetype %s\n",ft->name);
232 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->filecmds);
233 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->homefilecmds);
234 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->projfilecmds);
235 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->ftdefcmds);
236 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->execcmds);
237 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->homeexeccmds);
238 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->projexeccmds);
240 for (i = 0; i < GEANY_BCS_COUNT; ++i)
242 m = 1;
243 for (j = 0; j < GEANY_GBG_COUNT; ++j)
245 for (k = 0; k < build_groups_count[j]; ++k)
246 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
248 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
250 if ((*(cl[j][i]))[k].entries[n] != NULL &&
251 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
253 m = l;
258 cc[i] = m;
260 for (i = 0; i < GEANY_GBG_COUNT; ++i)
262 for (k = 0; k < build_groups_count[i]; ++k)
264 for (l = 0; l < 2; ++l)
266 c = ' ';
267 for (j = 0; j < GEANY_BCS_COUNT; ++j)
269 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
271 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
273 if ((*(cl[i][j]))[k].entries[i] != NULL)
274 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
275 else
276 printf("%c %*.*s",c,cc[j],cc[j]," ");
279 else
280 printf("%c %*.*s",c,cc[j],cc[j]," ");
281 c = ',';
283 printf("\n");
286 printf("\n");
291 /* macros to save typing and make the logic visible */
292 #define return_cmd_if(src, cmds)\
293 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
295 *fr=src; \
296 if (printbuildcmds) \
297 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
298 return &(cmds[cmdindex]); \
301 #define return_ft_cmd_if(src, cmds)\
302 if (ft != NULL && ft->cmds != NULL \
303 && ft->cmds[cmdindex].exists && below>src)\
305 *fr=src; \
306 if (printbuildcmds) \
307 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
308 return &(ft->cmds[cmdindex]); \
312 /* get the next lowest command taking priority into account */
313 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, gint cmdgrp, gint cmdindex,
314 gint below, gint *from)
316 /* Note: parameter below used in macros above */
317 GeanyFiletype *ft = NULL;
318 gint sink, *fr = &sink;
320 if (printbuildcmds)
321 printfcmds();
322 if (cmdgrp >= GEANY_GBG_COUNT)
323 return NULL;
324 if (from != NULL)
325 fr = from;
326 if (doc == NULL)
327 doc = document_get_current();
328 if (doc != NULL)
329 ft = doc->file_type;
331 switch (cmdgrp)
333 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
334 if (ft != NULL)
336 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
337 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
338 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
340 return_cmd_if(GEANY_BCS_DEF, ft_def);
341 break;
342 case GEANY_GBG_NON_FT: /* order proj, pref, def */
343 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
344 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
345 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
346 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
347 break;
348 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
349 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
350 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
351 return_cmd_if(GEANY_BCS_PREF, exec_pref);
352 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
353 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
354 return_cmd_if(GEANY_BCS_DEF, exec_def);
355 break;
356 default:
357 break;
359 return NULL;
363 /* shortcut to start looking at the top */
364 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, gint grp, gint cmdindex, gint *from)
366 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
370 #define return_nonblank_regex(src, ptr)\
371 if (NZV(ptr)) \
372 { *fr = (src); return &(ptr); }
375 /* like get_build_cmd, but for regexen, used by filetypes */
376 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, gint *from)
378 gint sink, *fr = &sink;
380 if (from != NULL)
381 fr = from;
382 if (grp == GEANY_GBG_FT)
384 if (ft == NULL)
386 GeanyDocument *doc = document_get_current();
387 if (doc != NULL)
388 ft = doc->file_type;
390 if (ft == NULL)
391 return NULL;
392 return_nonblank_regex(GEANY_BCS_PROJ, ft->projerror_regex_string);
393 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->homeerror_regex_string);
394 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
396 else if (grp == GEANY_GBG_NON_FT)
398 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
399 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
401 return NULL;
405 /* get pointer to the command group array */
406 static GeanyBuildCommand *get_build_group(GeanyBuildSource src, GeanyBuildGroup grp)
408 GeanyDocument *doc;
409 GeanyFiletype *ft = NULL;
411 switch (grp)
413 case GEANY_GBG_FT:
414 if ((doc = document_get_current()) == NULL)
415 return NULL;
416 if ((ft = doc->file_type) == NULL)
417 return NULL;
419 switch (src)
421 case GEANY_BCS_DEF: return ft->ftdefcmds;
422 case GEANY_BCS_FT: return ft->filecmds;
423 case GEANY_BCS_HOME_FT: return ft->homefilecmds;
424 case GEANY_BCS_PREF: return ft->homefilecmds;
425 case GEANY_BCS_PROJ: return ft->projfilecmds;
426 default: return NULL;
428 break;
429 case GEANY_GBG_NON_FT:
430 switch (src)
432 case GEANY_BCS_DEF: return non_ft_def;
433 case GEANY_BCS_PREF: return non_ft_pref;
434 case GEANY_BCS_PROJ: return non_ft_proj;
435 default: return NULL;
437 break;
438 case GEANY_GBG_EXEC:
439 if ((doc = document_get_current()) != NULL)
440 ft = doc->file_type;
441 switch (src)
443 case GEANY_BCS_DEF: return exec_def;
444 case GEANY_BCS_FT: return ft ? ft->execcmds: NULL;
445 case GEANY_BCS_HOME_FT: return ft ? ft->homeexeccmds: NULL;
446 case GEANY_BCS_PROJ_FT: return ft ? ft->projexeccmds: NULL;
447 case GEANY_BCS_PREF: return exec_pref;
448 case GEANY_BCS_PROJ: return exec_proj;
449 default: return NULL;
451 break;
452 default:
453 return NULL;
458 /** Remove the specified Build menu item.
460 * Makes the specified menu item configuration no longer exist. This
461 * is different to setting fields to blank because the menu item
462 * will be deleted from the configuration file on saving
463 * (except the system filetypes settings @see Build Menu Configuration
464 * section of the Manual).
466 * @param src the source of the menu item to remove.
467 * @param grp the group of the command to remove.
468 * @param cmd the index (from 0) of the command within the group. A negative
469 * value will remove the whole group.
471 * If any parameter is out of range does nothing.
473 * @see build_menu_update
475 void build_remove_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, gint cmd)
477 GeanyBuildCommand *bc;
478 gint i;
480 bc = get_build_group(src, grp);
481 if (bc == NULL)
482 return;
483 if (cmd < 0)
485 for (i = 0; i < build_groups_count[grp]; ++i)
486 bc[i].exists = FALSE;
488 else if (cmd < build_groups_count[grp])
489 bc[cmd].exists = FALSE;
493 /** Get the @a GeanyBuildCommand structure for the specified Build menu item.
495 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
496 * hidden by higher priority commands.
498 * @param src the source of the specified menu item.
499 * @param grp the group of the specified menu item.
500 * @param cmd the index of the command within the group.
502 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
503 * This is a pointer to an internal structure and must not be freed.
505 * @see build_menu_update
507 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, gint cmd)
509 GeanyBuildCommand *bc;
511 if (src >= GEANY_BCS_COUNT || grp >= GEANY_GBG_COUNT || cmd >= build_groups_count[grp])
512 return NULL;
513 bc = get_build_group(src, grp);
514 if (bc == NULL)
515 return NULL;
516 return &(bc[cmd]);
520 /** Get the @a GeanyBuildCommand structure for the menu item.
522 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
523 * that the menu item will use if activated.
525 * @param grp the group of the specified menu item.
526 * @param cmd the index of the command within the group.
527 * @param src pointer to @a gint to return which source provided the command. Ignored if @a NULL.
528 * Values are one of @a GeanyBuildSource but returns a signed type not the enum.
530 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
531 * This is a pointer to an internal structure and must not be freed.
533 * @see build_menu_update
535 /* parameter checked version of get_build_cmd for external interface */
536 GeanyBuildCommand *build_get_current_menu_item(GeanyBuildGroup grp, gint cmd, gint *src)
538 if (*src >= GEANY_BCS_COUNT || grp >= GEANY_GBG_COUNT || cmd >= build_groups_count[grp])
539 return NULL;
540 return get_build_cmd(NULL, grp, cmd, src);
544 /* Clear all error indicators in all documents. */
545 static void clear_errors(GeanyDocument *doc)
547 guint i;
549 for (i = 0; i < documents_array->len; i++)
551 if (documents[i]->is_valid)
552 editor_indicator_clear_errors(documents[i]->editor);
557 #ifdef G_OS_WIN32
558 static void parse_build_output(const gchar **output, gint status)
560 guint x, i, len;
561 gchar *line, **lines;
563 for (x = 0; x < 2; x++)
565 if (NZV(output[x]))
567 lines = g_strsplit_set(output[x], "\r\n", -1);
568 len = g_strv_length(lines);
570 for (i = 0; i < len; i++)
572 if (NZV(lines[i]))
574 line = lines[i];
575 while (*line != '\0')
576 { /* replace any conrol characters in the output */
577 if (*line < 32)
578 *line = 32;
579 line++;
581 process_build_output_line(lines[i], COLOR_BLACK);
584 g_strfreev(lines);
588 show_build_result_message(status != 0);
589 utils_beep();
591 build_info.pid = 0;
592 /* enable build items again */
593 build_menu_update(NULL);
595 #endif
598 /* Replaces occurences of %e and %p with the appropriate filenames,
599 * %d and %p replacements should be in UTF8 */
600 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
602 GString *stack;
603 gchar *filename = NULL;
604 gchar *replacement;
605 gchar *executable = NULL;
606 gchar *ret_str; /* to be freed when not in use anymore */
608 stack = g_string_new(src);
609 if (doc != NULL && doc->file_name != NULL)
611 filename = utils_get_utf8_from_locale(doc->file_name);
613 /* replace %f with the filename (including extension) */
614 replacement = g_path_get_basename(filename);
615 utils_string_replace_all(stack, "%f", replacement);
616 g_free(replacement);
618 /* replace %d with the absolute path of the dir of the current file */
619 replacement = g_path_get_dirname(filename);
620 utils_string_replace_all(stack, "%d", replacement);
621 g_free(replacement);
623 /* replace %e with the filename (excluding extension) */
624 executable = utils_remove_ext_from_filename(filename);
625 replacement = g_path_get_basename(executable);
626 utils_string_replace_all(stack, "%e", replacement);
627 g_free(replacement);
630 /* replace %p with the current project's (absolute) base directory */
631 replacement = NULL; /* prevent double free if no replacement found */
632 if (app->project)
634 replacement = project_get_base_path();
636 else if (strstr(stack->str, "%p"))
637 { /* fall back to %d */
638 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
639 if (doc != NULL && filename != NULL)
640 replacement = g_path_get_dirname(filename);
643 utils_string_replace_all(stack, "%p", replacement);
644 g_free(replacement);
646 ret_str = utils_get_utf8_from_locale(stack->str);
647 g_free(executable);
648 g_free(filename);
649 g_string_free(stack, TRUE);
651 return ret_str; /* don't forget to free src also if needed */
655 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
656 * idx document directory */
657 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
659 GError *error = NULL;
660 gchar **argv;
661 gchar *working_dir;
662 gchar *utf8_working_dir;
663 gchar *cmd_string;
664 gchar *utf8_cmd_string;
665 #ifdef G_OS_WIN32
666 gchar *output[2];
667 gint status;
668 #else
669 gint stdout_fd;
670 gint stderr_fd;
671 #endif
673 if (!((doc != NULL && NZV(doc->file_name)) || NZV(dir)))
675 geany_debug("Failed to run command with no working directory");
676 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
677 return (GPid) 1;
680 if (doc != NULL)
681 clear_errors(doc);
682 setptr(current_dir_entered, NULL);
684 cmd_string = g_strdup(cmd);
686 #ifdef G_OS_WIN32
687 argv = g_strsplit(cmd_string, " ", 0);
688 #else
689 argv = g_new0(gchar *, 4);
690 argv[0] = g_strdup("/bin/sh");
691 argv[1] = g_strdup("-c");
692 argv[2] = cmd_string;
693 argv[3] = NULL;
694 #endif
696 utf8_cmd_string = utils_get_utf8_from_locale(cmd_string);
697 utf8_working_dir = NZV(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
698 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
700 gtk_list_store_clear(msgwindow.store_compiler);
701 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
702 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
703 g_free(utf8_working_dir);
704 g_free(utf8_cmd_string);
706 /* set the build info for the message window */
707 g_free(build_info.dir);
708 build_info.dir = g_strdup(working_dir);
709 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : FILETYPE_ID(doc->file_type);
710 build_info.message_count = 0;
712 #ifdef G_OS_WIN32
713 if (! utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
714 NULL, NULL, &output[0], &output[1], &status, &error))
715 #else
716 if (! g_spawn_async_with_pipes(working_dir, argv, NULL,
717 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
718 &(build_info.pid), NULL, &stdout_fd, &stderr_fd, &error))
719 #endif
721 geany_debug("g_spawn_async_with_pipes() failed: %s", error->message);
722 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
723 g_strfreev(argv);
724 g_error_free(error);
725 g_free(working_dir);
726 error = NULL;
727 return (GPid) 0;
730 #ifdef G_OS_WIN32
731 parse_build_output((const gchar**) output, status);
732 g_free(output[0]);
733 g_free(output[1]);
734 #else
735 if (build_info.pid > 0)
737 g_child_watch_add(build_info.pid, (GChildWatchFunc) build_exit_cb, NULL);
738 build_menu_update(doc);
739 ui_progress_bar_start(NULL);
742 /* use GIOChannels to monitor stdout and stderr */
743 utils_set_up_io_channel(stdout_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
744 TRUE, build_iofunc, GINT_TO_POINTER(0));
745 utils_set_up_io_channel(stderr_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
746 TRUE, build_iofunc, GINT_TO_POINTER(1));
747 #endif
749 g_strfreev(argv);
750 g_free(working_dir);
752 return build_info.pid;
756 /* Returns: NULL if there was an error, or the working directory the script was created in.
757 * vte_cmd_nonscript is the location of a string which is filled with the command to be used
758 * when vc->skip_run_script is set, otherwise it will be set to NULL */
759 static gchar *prepare_run_script(GeanyDocument *doc, gchar **vte_cmd_nonscript, gint cmdindex)
761 gchar *locale_filename = NULL;
762 gboolean have_project;
763 GeanyProject *project = app->project;
764 GeanyBuildCommand *cmd = NULL;
765 gchar *executable = NULL;
766 gchar *working_dir = NULL;
767 const gchar *cmd_working_dir;
768 gboolean autoclose = FALSE;
769 gboolean result = FALSE;
770 gchar *tmp;
771 gchar *cmd_string;
773 if (vte_cmd_nonscript != NULL)
774 *vte_cmd_nonscript = NULL;
776 locale_filename = utils_get_locale_from_utf8(doc->file_name);
778 have_project = project != NULL;
779 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
781 cmd_string = build_replace_placeholder(doc, buildcmd_cmd(cmd));
782 cmd_working_dir = buildcmd_working_dir(cmd);
783 if (! NZV(cmd_working_dir))
784 cmd_working_dir = "%d";
785 working_dir = build_replace_placeholder(doc, cmd_working_dir); /* in utf-8 */
787 /* only test whether working dir exists, don't change it or else Windows support will break
788 * (gspawn-win32-helper.exe is used by GLib and must be in $PATH which means current working
789 * dir where geany.exe was started from, so we can't change it) */
790 if (!NZV(working_dir) || ! g_file_test(working_dir, G_FILE_TEST_EXISTS) ||
791 ! g_file_test(working_dir, G_FILE_TEST_IS_DIR))
793 ui_set_statusbar(TRUE, _("Failed to change the working directory to \"%s\""),
794 NZV(working_dir) ? working_dir : "<NULL>" );
795 utils_free_pointers(2, cmd_string, working_dir, NULL);
796 return NULL;
799 #ifdef HAVE_VTE
800 if (vte_info.load_vte && vc != NULL && vc->run_in_vte)
802 if (vc->skip_run_script)
804 if (vte_cmd_nonscript != NULL)
805 *vte_cmd_nonscript = cmd_string;
807 utils_free_pointers(2, executable, locale_filename, NULL);
808 return working_dir;
810 else
811 /* don't wait for user input at the end of script when we are running in VTE */
812 autoclose = TRUE;
814 #endif
816 /* RUN_SCRIPT_CMD should be ok in UTF8 without converting in locale because it
817 * contains no umlauts */
818 tmp = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
819 result = build_create_shellscript(tmp, cmd_string, autoclose);
820 if (! result)
822 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created)"),
823 NZV(cmd_string) ? cmd_string : NULL);
826 utils_free_pointers(4, cmd_string, tmp, executable, locale_filename, NULL);
828 if (result)
829 return working_dir;
831 g_free(working_dir);
832 return NULL;
836 static GPid build_run_cmd(GeanyDocument *doc, gint cmdindex)
838 gchar *working_dir;
839 gchar *vte_cmd_nonscript = NULL;
840 GError *error = NULL;
842 if (doc == NULL || doc->file_name == NULL)
843 return (GPid) 0;
845 working_dir = prepare_run_script(doc, &vte_cmd_nonscript, cmdindex);
846 if (working_dir == NULL)
847 return (GPid) 0;
849 run_info[cmdindex].file_type_id = FILETYPE_ID(doc->file_type);
851 #ifdef HAVE_VTE
852 if (vte_info.load_vte && vc != NULL && vc->run_in_vte)
854 gchar *vte_cmd;
856 if (vc->skip_run_script)
858 setptr(vte_cmd_nonscript, utils_get_utf8_from_locale(vte_cmd_nonscript));
859 vte_cmd = g_strconcat(vte_cmd_nonscript, "\n", NULL);
860 g_free(vte_cmd_nonscript);
862 else
863 vte_cmd = g_strconcat("\n/bin/sh ", RUN_SCRIPT_CMD, "\n", NULL);
865 /* change into current directory if it is not done by default */
866 if (! vc->follow_path)
868 /* we need to convert the working_dir back to UTF-8 because the VTE expects it */
869 gchar *utf8_working_dir = utils_get_utf8_from_locale(working_dir);
870 vte_cwd(utf8_working_dir, TRUE);
871 g_free(utf8_working_dir);
873 if (! vte_send_cmd(vte_cmd))
875 ui_set_statusbar(FALSE,
876 _("Could not execute the file in the VTE because it probably contains a command."));
877 geany_debug("Could not execute the file in the VTE because it probably contains a command.");
880 /* show the VTE */
881 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
882 gtk_widget_grab_focus(vc->vte);
883 msgwin_show_hide(TRUE);
885 run_info[cmdindex].pid = 1;
887 g_free(vte_cmd);
889 else
890 #endif
892 gchar *locale_term_cmd = NULL;
893 gchar **term_argv = NULL;
894 guint term_argv_len, i;
895 gchar **argv = NULL;
897 /* get the terminal path */
898 locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
899 /* split the term_cmd, so arguments will work too */
900 term_argv = g_strsplit(locale_term_cmd, " ", -1);
901 term_argv_len = g_strv_length(term_argv);
903 /* check that terminal exists (to prevent misleading error messages) */
904 if (term_argv[0] != NULL)
906 gchar *tmp = term_argv[0];
907 /* g_find_program_in_path checks whether tmp exists and is executable */
908 term_argv[0] = g_find_program_in_path(tmp);
909 g_free(tmp);
911 if (term_argv[0] == NULL)
913 ui_set_statusbar(TRUE,
914 _("Could not find terminal \"%s\" "
915 "(check path for Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
916 run_info[cmdindex].pid = (GPid) 1;
917 goto free_strings;
920 argv = g_new0(gchar *, term_argv_len + 3);
921 for (i = 0; i < term_argv_len; i++)
923 argv[i] = g_strdup(term_argv[i]);
925 #ifdef G_OS_WIN32
926 /* command line arguments only for cmd.exe */
927 if (strstr(argv[0], "cmd.exe") != NULL)
929 argv[term_argv_len] = g_strdup("/Q /C");
930 argv[term_argv_len + 1] = g_strdup(RUN_SCRIPT_CMD);
932 else
934 argv[term_argv_len] = g_strdup(RUN_SCRIPT_CMD);
935 argv[term_argv_len + 1] = NULL;
937 #else
938 argv[term_argv_len ] = g_strdup("-e");
939 argv[term_argv_len + 1] = g_strconcat("/bin/sh ", RUN_SCRIPT_CMD, NULL);
940 #endif
941 argv[term_argv_len + 2] = NULL;
943 if (! g_spawn_async(working_dir, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
944 NULL, NULL, &(run_info[cmdindex].pid), &error))
946 geany_debug("g_spawn_async() failed: %s", error->message);
947 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
948 g_unlink(RUN_SCRIPT_CMD);
949 g_error_free(error);
950 error = NULL;
951 run_info[cmdindex].pid = (GPid) 0;
954 if (run_info[cmdindex].pid > 0)
956 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
957 (gpointer)&(run_info[cmdindex]));
958 build_menu_update(doc);
960 free_strings:
961 g_strfreev(argv);
962 g_strfreev(term_argv);
963 g_free(locale_term_cmd);
966 g_free(working_dir);
967 return run_info[cmdindex].pid;
971 static void process_build_output_line(const gchar *str, gint color)
973 gchar *msg, *tmp;
974 gchar *filename;
975 gint line;
977 msg = g_strdup(str);
979 g_strchomp(msg);
981 if (! NZV(msg))
982 return;
984 if (build_parse_make_dir(msg, &tmp))
986 setptr(current_dir_entered, tmp);
988 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
990 if (line != -1 && filename != NULL)
992 GeanyDocument *doc = document_find_by_filename(filename);
994 /* limit number of indicators */
995 if (doc && editor_prefs.use_indicators &&
996 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
998 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
999 line--; /* so only adjust the line number if it is greater than 0 */
1000 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
1002 build_info.message_count++;
1003 color = COLOR_RED; /* error message parsed on the line */
1005 g_free(filename);
1007 msgwin_compiler_add_string(color, msg);
1008 g_free(msg);
1012 #ifndef G_OS_WIN32
1013 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data)
1015 if (cond & (G_IO_IN | G_IO_PRI))
1017 gchar *msg;
1018 GIOStatus st;
1020 while ((st = g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL)) == G_IO_STATUS_NORMAL && msg)
1022 gint color = (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK;
1024 process_build_output_line(msg, color);
1025 g_free(msg);
1027 if (st == G_IO_STATUS_ERROR || st == G_IO_STATUS_EOF) return FALSE;
1029 if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
1030 return FALSE;
1032 return TRUE;
1034 #endif
1037 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1039 const gchar *pos;
1041 *prefix = NULL;
1043 if (string == NULL)
1044 return FALSE;
1046 if ((pos = strstr(string, "Entering directory")) != NULL)
1048 gsize len;
1049 gchar *input;
1051 /* get the start of the path */
1052 pos = strstr(string, "/");
1054 if (pos == NULL)
1055 return FALSE;
1057 input = g_strdup(pos);
1059 /* kill the ' at the end of the path */
1060 len = strlen(input);
1061 input[len - 1] = '\0';
1062 input = g_realloc(input, len); /* shorten by 1 */
1063 *prefix = input;
1065 return TRUE;
1068 if (strstr(string, "Leaving directory") != NULL)
1070 *prefix = NULL;
1071 return TRUE;
1074 return FALSE;
1078 static void show_build_result_message(gboolean failure)
1080 gchar *msg;
1082 if (failure)
1084 msg = _("Compilation failed.");
1085 msgwin_compiler_add_string(COLOR_BLUE, msg);
1086 /* If msgwindow is hidden, user will want to display it to see the error */
1087 if (! ui_prefs.msgwindow_visible)
1089 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1090 msgwin_show_hide(TRUE);
1092 else
1093 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1094 ui_set_statusbar(FALSE, "%s", msg);
1096 else
1098 msg = _("Compilation finished successfully.");
1099 msgwin_compiler_add_string(COLOR_BLUE, msg);
1100 if (! ui_prefs.msgwindow_visible ||
1101 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1102 ui_set_statusbar(FALSE, "%s", msg);
1107 #ifndef G_OS_WIN32
1108 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1110 gboolean failure = FALSE;
1112 if (WIFEXITED(status))
1114 if (WEXITSTATUS(status) != EXIT_SUCCESS)
1115 failure = TRUE;
1117 else if (WIFSIGNALED(status))
1119 /* the terminating signal: WTERMSIG (status)); */
1120 failure = TRUE;
1122 else
1123 { /* any other failure occured */
1124 failure = TRUE;
1126 show_build_result_message(failure);
1128 utils_beep();
1129 g_spawn_close_pid(child_pid);
1131 build_info.pid = 0;
1132 /* enable build items again */
1133 build_menu_update(NULL);
1134 ui_progress_bar_stop();
1136 #endif
1139 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1141 RunInfo *run_info_data = (RunInfo*)user_data;
1143 g_spawn_close_pid(child_pid);
1145 run_info_data->pid = 0;
1146 /* reset the stop button and menu item to the original meaning */
1147 build_menu_update(NULL);
1151 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1152 * fname is the full file name (including path) for the script to create */
1153 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose)
1155 FILE *fp;
1156 gchar *str;
1157 #ifdef G_OS_WIN32
1158 gchar *expanded_cmd;
1159 #endif
1161 fp = g_fopen(fname, "w");
1162 if (! fp)
1163 return FALSE;
1164 #ifdef G_OS_WIN32
1165 /* Expand environment variables like %blah%. */
1166 expanded_cmd = win32_expand_environment_variables(cmd);
1167 str = g_strdup_printf("%s\n\n%s\ndel \"%%0\"\n\npause\n", expanded_cmd, (autoclose) ? "" : "pause");
1168 g_free(expanded_cmd);
1169 #else
1170 str = g_strdup_printf(
1171 "#!/bin/sh\n\nrm $0\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1172 \n\n%s\n", cmd, (autoclose) ? "" :
1173 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1174 "dash\ndummy_var=\"\"\nread dummy_var");
1175 #endif
1177 fputs(str, fp);
1178 g_free(str);
1180 fclose(fp);
1182 return TRUE;
1186 typedef void Callback(GtkWidget *w, gpointer u);
1188 /* run the command catenating cmd_cat if present */
1189 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, gint cmd, gchar *cmd_cat)
1191 gchar *dir;
1192 gchar *full_command, *subs_command;
1193 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1194 gchar *cmdstr;
1196 if (buildcmd == NULL)
1197 return;
1199 cmdstr = buildcmd_cmd(buildcmd);
1201 if (cmd_cat != NULL)
1203 if (cmdstr != NULL)
1204 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1205 else
1206 full_command = g_strdup(cmd_cat);
1208 else
1209 full_command = cmdstr;
1211 dir = build_replace_placeholder(doc, buildcmd_working_dir(buildcmd));
1212 subs_command = build_replace_placeholder(doc, full_command);
1213 build_info.grp = grp;
1214 build_info.cmd = cmd;
1215 build_spawn_cmd(doc, subs_command, dir);
1216 g_free(subs_command);
1217 g_free(dir);
1218 if (cmd_cat != NULL)
1219 g_free(full_command);
1220 build_menu_update(doc);
1225 /*----------------------------------------------------------------
1227 * Create build menu and handle callbacks (&toolbar callbacks)
1229 *----------------------------------------------------------------*/
1230 static void on_make_custom_input_response(const gchar *input)
1232 GeanyDocument *doc = document_get_current();
1234 setptr(build_info.custom_target, g_strdup(input));
1235 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1236 build_info.custom_target);
1240 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1242 GeanyDocument *doc = document_get_current();
1243 GeanyBuildCommand *bc;
1244 gint grp = GPOINTER_TO_GRP(user_data);
1245 gint cmd = GPOINTER_TO_CMD(user_data);
1247 if (doc && doc->changed)
1248 document_save_file(doc, FALSE);
1249 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1251 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1253 if (! dialog)
1255 dialog = dialogs_show_input_persistent(_("Custom Text"),
1256 _("Enter custom text here, all entered text is appended to the command."),
1257 build_info.custom_target, &on_make_custom_input_response);
1259 else
1261 gtk_widget_show(dialog);
1263 return;
1265 else if (grp == GEANY_GBG_EXEC)
1267 if (run_info[cmd].pid > (GPid) 1)
1269 kill_process(&run_info[cmd].pid);
1270 return;
1272 bc = get_build_cmd(doc, grp, cmd, NULL);
1273 if (bc != NULL && strcmp(buildcmd_cmd(bc), "builtin") == 0)
1275 gchar *uri;
1276 if (doc == NULL)
1277 return;
1278 uri = g_strconcat("file:///", g_path_skip_root(doc->file_name), NULL);
1279 utils_open_browser(uri);
1280 g_free(uri);
1283 else
1284 build_run_cmd(doc, cmd);
1286 else
1287 build_command(doc, grp, cmd, NULL);
1291 /* group codes for menu items other than the known commands
1292 * value order is important, see the following table for use */
1294 /* the rest in each group */
1295 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1296 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1297 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1298 /* the separator */
1299 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1300 /* the fixed items */
1301 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1302 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1303 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1304 #define MENU_DONE (MENU_COMMANDS + 1)
1307 static struct BuildMenuItemSpec {
1308 const gchar *stock_id;
1309 const gint key_binding;
1310 const gint build_grp;
1311 const gint build_cmd;
1312 const gchar *fix_label;
1313 Callback *cb;
1314 } build_menu_specs[] = {
1315 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1316 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1317 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1318 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1319 {NULL, -1, MENU_FT_REST,
1320 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1321 {NULL, -1, MENU_SEPARATOR,
1322 GBF_SEP_1, NULL, NULL},
1323 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1324 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1325 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1326 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1327 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1328 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1329 {NULL, -1, MENU_NON_FT_REST,
1330 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1331 {NULL, -1, MENU_SEPARATOR,
1332 GBF_SEP_2, NULL, NULL},
1333 {NULL, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1334 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1335 {NULL, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1336 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1337 {NULL, -1, MENU_SEPARATOR,
1338 GBF_SEP_3, NULL, NULL},
1339 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1340 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1341 {NULL, -1, MENU_EXEC_REST,
1342 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1343 {NULL, -1, MENU_SEPARATOR,
1344 GBF_SEP_4, NULL, NULL},
1345 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1346 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1347 {NULL, -1, MENU_DONE,
1348 0, NULL, NULL}
1352 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1353 struct BuildMenuItemSpec *bs, const gchar *lbl, gint grp, gint cmd)
1355 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1357 if (bs->stock_id != NULL)
1359 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1360 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1362 gtk_widget_show(item);
1363 if (bs->key_binding >= 0)
1364 add_menu_accel(group, bs->key_binding, ag, item);
1365 gtk_container_add(GTK_CONTAINER(menu), item);
1366 if (bs->cb != NULL)
1368 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1370 menu_items.menu_item[grp][cmd] = item;
1374 static void create_build_menu(BuildMenuItems *build_menu_items)
1376 GtkWidget *menu;
1377 GtkAccelGroup *accel_group = gtk_accel_group_new();
1378 GeanyKeyGroup *keygroup = g_ptr_array_index(keybinding_groups, GEANY_KEY_GROUP_BUILD);
1379 gint i, j;
1381 menu = gtk_menu_new();
1382 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1383 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1384 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1385 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1387 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1389 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1390 if (bs->build_grp == MENU_SEPARATOR)
1392 GtkWidget *item = gtk_separator_menu_item_new();
1393 gtk_widget_show(item);
1394 gtk_container_add(GTK_CONTAINER(menu), item);
1395 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1397 else if (bs->fix_label != NULL)
1399 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1400 GBG_FIXED, bs->build_cmd);
1402 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1404 gint grp = bs->build_grp - GEANY_GBG_COUNT;
1405 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1407 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1408 const gchar *lbl = (bc == NULL) ? "" : buildcmd_label(bc);
1409 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1412 else
1414 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1415 const gchar *lbl = (bc == NULL) ? "" : buildcmd_label(bc);
1416 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1419 build_menu_items->menu = menu;
1420 gtk_widget_show(menu);
1421 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1425 /* portability to various GTK versions needs checking
1426 * conforms to description of gtk_accel_label as child of menu item
1427 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1428 static void geany_menu_item_set_label(GtkWidget *w, const gchar *label)
1430 GtkWidget *c = gtk_bin_get_child(GTK_BIN(w));
1432 gtk_label_set_text_with_mnemonic(GTK_LABEL(c), label);
1436 /** Update the build menu to reflect changes in configuration or status.
1438 * Sets the labels and number of visible items to match the highest
1439 * priority configured commands. Also sets sensitivity if build commands are
1440 * running and switches executes to stop when commands are running.
1442 * @param doc The current document, if available, to save looking it up.
1443 * If @c NULL it will be looked up.
1445 * Call this after modifying any fields of a GeanyBuildCommand structure.
1447 * @see Build Menu Configuration section of the Manual.
1450 void build_menu_update(GeanyDocument *doc)
1452 gint i, cmdcount, cmd, grp;
1453 gboolean vis = FALSE;
1454 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1455 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1456 GeanyBuildCommand *bc;
1458 if (menu_items.menu == NULL)
1459 create_build_menu(&menu_items);
1460 if (doc == NULL)
1461 doc = document_get_current();
1462 have_path = doc != NULL && doc->file_name != NULL;
1463 build_running = build_info.pid > (GPid) 1;
1464 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1465 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1467 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1468 switch (bs->build_grp)
1470 case MENU_SEPARATOR:
1471 if (vis == TRUE)
1473 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1474 vis = FALSE;
1476 else
1477 gtk_widget_hide_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1478 break;
1479 case MENU_NEXT_ERROR:
1480 case MENU_PREV_ERROR:
1481 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1482 vis |= TRUE;
1483 break;
1484 case MENU_COMMANDS:
1485 vis |= TRUE;
1486 break;
1487 default: /* all configurable commands */
1488 if (bs->build_grp >= GEANY_GBG_COUNT)
1490 grp = bs->build_grp - GEANY_GBG_COUNT;
1491 cmdcount = build_groups_count[grp];
1493 else
1495 grp = bs->build_grp;
1496 cmdcount = bs->build_cmd + 1;
1498 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1500 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1501 const gchar *label;
1502 bc = get_build_cmd(doc, grp, cmd, NULL);
1503 if (bc)
1504 label = buildcmd_label(bc);
1505 else
1506 label = NULL;
1508 if (grp < GEANY_GBG_EXEC)
1510 cmd_sensitivity =
1511 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1512 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1513 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1514 if (bc != NULL && NZV(label))
1516 geany_menu_item_set_label(menu_item, label);
1517 gtk_widget_show_all(menu_item);
1518 vis |= TRUE;
1520 else
1521 gtk_widget_hide_all(menu_item);
1523 else
1525 GtkWidget *image;
1526 exec_running = run_info[cmd].pid > (GPid) 1;
1527 cmd_sensitivity = (bc != NULL) || exec_running;
1528 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1529 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1530 run_sensitivity = cmd_sensitivity;
1531 if (! exec_running)
1533 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1535 else
1537 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1539 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1540 run_running = exec_running;
1541 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1542 if (bc != NULL && NZV(label))
1544 geany_menu_item_set_label(menu_item, label);
1545 gtk_widget_show_all(menu_item);
1546 vis |= TRUE;
1548 else
1549 gtk_widget_hide_all(menu_item);
1555 run_sensitivity &= (doc != NULL);
1556 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1557 && have_path && ! build_running;
1558 if (widgets.toolitem_build != NULL)
1559 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1560 can_make = FALSE;
1561 if (widgets.toolitem_make_all != NULL)
1562 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1563 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1564 && ! build_running));
1565 if (widgets.toolitem_make_custom != NULL)
1566 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1567 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1568 && ! build_running));
1569 if (widgets.toolitem_make_object != NULL)
1570 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1571 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1572 && ! build_running));
1573 if (widgets.toolitem_set_args != NULL)
1574 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1576 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1577 && have_path && ! build_running;
1578 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1579 gtk_action_set_sensitive(widgets.build_action, can_make);
1580 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1582 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1583 set_stop_button(run_running);
1588 /* Call build_menu_update() instead of calling this directly. */
1589 static void set_stop_button(gboolean stop)
1591 const gchar *button_stock_id = NULL;
1592 GtkToolButton *run_button;
1594 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1595 if (run_button != NULL)
1596 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1598 if (stop && utils_str_equal(button_stock_id, "gtk-stop"))
1599 return;
1600 if (! stop && utils_str_equal(button_stock_id, "gtk-execute"))
1601 return;
1603 /* use the run button also as stop button */
1604 if (stop)
1606 if (run_button != NULL)
1607 gtk_tool_button_set_stock_id(run_button, "gtk-stop");
1609 else
1611 if (run_button != NULL)
1612 gtk_tool_button_set_stock_id(run_button, "gtk-execute");
1617 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1619 /* For now, just show the project dialog */
1620 if (app->project)
1621 project_build_properties();
1622 else
1623 show_build_commands_dialog();
1627 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1629 last_toolbutton_action = user_data;
1630 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1631 on_build_menu_item(menuitem, user_data);
1635 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1637 gchar *msg;
1638 gint grp, cmd;
1640 last_toolbutton_action = user_data;
1641 grp = GPOINTER_TO_GRP(user_data);
1642 cmd = GPOINTER_TO_CMD(user_data);
1643 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1644 msg = _("Build the current file with Make and the default target");
1645 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1646 msg = _("Build the current file with Make and the specified target");
1647 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1648 msg = _("Compile the current file with Make");
1649 else
1650 msg = NULL;
1651 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1652 on_build_menu_item(menuitem, user_data);
1656 static void kill_process(GPid *pid)
1658 /* Unix: SIGQUIT is not the best signal to use because it causes a core dump (this should not
1659 * perforce necessary for just killing a process). But we must use a signal which we can
1660 * ignore because the main process get it too, it is declared to ignore in main.c. */
1661 gint result;
1663 #ifdef G_OS_WIN32
1664 g_return_if_fail(*pid != NULL);
1665 result = TerminateProcess(*pid, 0);
1666 /* TerminateProcess() returns TRUE on success, for the check below we have to convert
1667 * it to FALSE (and vice versa) */
1668 result = ! result;
1669 #else
1670 g_return_if_fail(*pid > 1);
1671 result = kill(*pid, SIGQUIT);
1672 #endif
1674 if (result != 0)
1675 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), g_strerror(errno));
1676 else
1678 *pid = 0;
1679 build_menu_update(NULL);
1684 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1686 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1687 msgwin_goto_compiler_file_line))
1689 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1691 else
1692 ui_set_statusbar(FALSE, _("No more build errors."));
1696 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1698 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1699 msgwin_goto_compiler_file_line))
1701 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1703 else
1704 ui_set_statusbar(FALSE, _("No more build errors."));
1708 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1710 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1712 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1714 else
1716 on_build_menu_item(NULL, last_toolbutton_action);
1721 /*------------------------------------------------------
1723 * Create and handle the build menu configuration dialog
1725 *-------------------------------------------------------*/
1726 typedef struct RowWidgets
1728 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1729 GeanyBuildSource src;
1730 GeanyBuildSource dst;
1731 GeanyBuildCommand *cmdsrc;
1732 gint grp;
1733 gint cmd;
1734 gboolean cleared;
1735 } RowWidgets;
1738 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1740 if (GTK_IS_BUTTON(wid))
1741 gtk_button_set_label(GTK_BUTTON(wid), text);
1742 else
1743 gtk_entry_set_text(GTK_ENTRY(wid), text);
1747 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1749 RowWidgets *r = (RowWidgets*)user_data;
1750 gint src;
1751 enum GeanyBuildCmdEntries i;
1752 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1754 if (bc != NULL)
1756 r->cmdsrc = bc;
1757 r->src = src;
1758 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1760 set_build_command_entry_text(r->entries[i],
1761 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1764 else
1766 r->cmdsrc = NULL;
1767 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1769 set_build_command_entry_text(r->entries[i], "");
1772 r->cleared = TRUE;
1776 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1778 gtk_entry_set_text(regex,"");
1782 static void on_label_button_clicked(GtkWidget *wid)
1784 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1785 gchar *str = dialogs_show_input(_("Set menu item label"), NULL, old);
1787 gtk_button_set_label(GTK_BUTTON(wid), str);
1788 g_free(str);
1792 /* Column headings, array NULL-terminated */
1793 static const gchar *colheads[] =
1795 N_("Item"),
1796 N_("Label"),
1797 N_("Command"),
1798 N_("Working directory"),
1799 N_("Clear"),
1800 NULL };
1801 /* column names */
1802 #define DC_ITEM 0
1803 #define DC_ENTRIES 1
1804 #define DC_CLEAR 4
1805 #define DC_N_COL 5
1807 static const guint entry_x_padding = 3;
1808 static const guint entry_y_padding = 0;
1811 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1812 GeanyBuildSource dst, gint grp, gint cmd, gboolean dir)
1814 GtkWidget *label, *clear, *clearicon;
1815 RowWidgets *roww;
1816 GeanyBuildCommand *bc;
1817 gint src;
1818 enum GeanyBuildCmdEntries i;
1819 guint column = 0;
1821 label = gtk_label_new(g_strdup_printf("%d:", cmd + 1));
1822 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1823 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1824 roww = g_new0(RowWidgets, 1);
1825 roww->src = GEANY_BCS_COUNT;
1826 roww->grp = grp;
1827 roww->cmd = cmd;
1828 roww->dst = dst;
1829 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1831 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1833 column += 1;
1834 if (i == GEANY_BC_LABEL)
1836 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1837 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1838 ui_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1839 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), NULL);
1841 else
1842 roww->entries[i] = gtk_entry_new();
1843 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1844 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1846 column++;
1847 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1848 clear = gtk_button_new();
1849 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1850 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1851 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1852 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1853 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1854 if (bc != NULL)
1855 roww->src = src;
1857 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1859 const gchar *str = "";
1860 if (bc != NULL && (str = bc->entries[i]) == NULL)
1861 str = "";
1862 set_build_command_entry_text(roww->entries[i], str);
1864 if (bc != NULL && (src > (gint)dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1866 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1867 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1868 gtk_widget_set_sensitive(clear, FALSE);
1870 return roww;
1874 typedef struct BuildTableFields
1876 RowWidgets **rows;
1877 GtkWidget *fileregex;
1878 GtkWidget *nonfileregex;
1879 gchar **fileregexstring;
1880 gchar **nonfileregexstring;
1881 } BuildTableFields;
1884 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
1885 GeanyFiletype *ft)
1887 GtkWidget *label, *sep, *clearicon, *clear;
1888 BuildTableFields *fields;
1889 GtkTable *table;
1890 const gchar **ch;
1891 gchar *txt;
1892 guint col, row, cmdindex;
1893 gint cmd;
1894 gint src;
1895 gboolean sensitivity;
1896 guint sep_padding = entry_y_padding + 3;
1898 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1899 fields = g_new0(BuildTableFields, 1);
1900 fields->rows = g_new0(RowWidgets*, build_items_count);
1901 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1903 label = gtk_label_new(_(*ch));
1904 gtk_table_attach(table, label, col, col + 1, 0, 1,
1905 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1907 sep = gtk_hseparator_new();
1908 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1909 entry_x_padding, sep_padding);
1910 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1911 txt = g_strdup_printf(_("%s Commands"), ft->title);
1912 else
1913 txt = g_strdup_printf(_("%s Commands"), _("No Filetype"));
1914 label = ui_label_new_bold(txt);
1915 g_free(txt);
1916 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1917 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1918 entry_x_padding, entry_y_padding);
1919 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1920 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1921 label = gtk_label_new(_("Error Regular Expression:"));
1922 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1923 entry_x_padding, entry_y_padding);
1924 fields->fileregex = gtk_entry_new();
1925 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
1926 sensitivity = (ft == NULL) ? FALSE : TRUE;
1927 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1929 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1930 if (src > (gint)dst)
1931 sensitivity = FALSE;
1933 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1934 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1935 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1936 clear = gtk_button_new();
1937 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1938 g_signal_connect_swapped(clear, "clicked",
1939 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
1940 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
1941 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1942 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
1943 gtk_widget_set_sensitive(clear, sensitivity);
1944 ++row;
1945 sep = gtk_hseparator_new();
1946 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1947 entry_x_padding, sep_padding);
1948 ++row;
1949 label = ui_label_new_bold(_("Non-Filetype Commands"));
1950 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1951 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1952 entry_x_padding, entry_y_padding);
1953 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
1954 fields->rows[cmdindex] = build_add_dialog_row(
1955 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
1956 label = gtk_label_new(_("Error Regular Expression:"));
1957 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
1958 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1959 fields->nonfileregex = gtk_entry_new();
1960 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
1961 sensitivity = TRUE;
1962 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
1964 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
1965 sensitivity = src > (gint)dst ? FALSE : TRUE;
1967 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1968 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1969 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1970 clear = gtk_button_new();
1971 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1972 g_signal_connect_swapped(clear, "clicked",
1973 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
1974 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
1975 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1976 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
1977 gtk_widget_set_sensitive(clear, sensitivity);
1978 ++row;
1979 label = gtk_label_new(_("Note: Item 2 opens a dialog and appends the response to the command."));
1980 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1981 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1982 entry_x_padding, entry_y_padding);
1983 ++row;
1984 sep = gtk_hseparator_new();
1985 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1986 entry_x_padding, sep_padding);
1987 ++row;
1988 label = ui_label_new_bold(_("Execute Commands"));
1989 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1990 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1991 entry_x_padding, entry_y_padding);
1992 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
1993 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
1994 sep = gtk_hseparator_new();
1995 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1996 entry_x_padding, sep_padding);
1997 ++row;
1998 label = gtk_label_new(
1999 _("%d, %e, %f, %p are substituted in command and directory fields, see manual for details."));
2000 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2001 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2002 entry_x_padding, entry_y_padding);
2003 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2004 ++row;
2005 *table_data = fields;
2006 return GTK_WIDGET(table);
2010 void build_free_fields(BuildTableData table_data)
2012 gint cmdindex;
2014 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2015 g_free(table_data->rows[cmdindex]);
2016 g_free(table_data->rows);
2017 g_free(table_data);
2021 /* string compare where null pointers match null or 0 length strings */
2022 static gint stcmp(const gchar *a, const gchar *b)
2024 if (a == NULL && b == NULL)
2025 return 0;
2026 if (a == NULL && b != NULL)
2027 return strlen(b);
2028 if (a != NULL && b == NULL)
2029 return strlen(a);
2030 return strcmp(a, b);
2034 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2036 if (GTK_IS_BUTTON(wid))
2037 return gtk_button_get_label(GTK_BUTTON(wid));
2038 else
2039 return gtk_entry_get_text(GTK_ENTRY(wid));
2043 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, gint drow, gint grp, gint cmd)
2045 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2046 gboolean changed = FALSE;
2047 GeanyBuildSource src;
2048 enum GeanyBuildCmdEntries i;
2050 src = table_data->rows[drow]->src;
2052 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2054 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2056 if (table_data->rows[drow]->cleared)
2058 if (dst->dst[grp] != NULL)
2060 if (*(dst->dst[grp]) == NULL)
2061 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2062 (*(dst->dst[grp]))[cmd].exists = FALSE;
2063 (*(dst->dst[grp]))[cmd].changed = TRUE;
2064 changed = TRUE;
2067 if (
2069 table_data->rows[drow]->cmdsrc == NULL /* originally there was no content */
2072 NZV(entries[GEANY_BC_LABEL]) /* but now one field has some */
2073 || NZV(entries[GEANY_BC_COMMAND])
2074 || NZV(entries[GEANY_BC_WORKING_DIR])
2079 table_data->rows[drow]->cmdsrc != NULL /* originally there was content */
2081 ( /* and some of it was changed */
2082 stcmp(entries[GEANY_BC_LABEL], table_data->rows[drow]->cmdsrc->entries[GEANY_BC_LABEL]) != 0
2083 || stcmp(entries[GEANY_BC_COMMAND], table_data->rows[drow]->cmdsrc->entries[GEANY_BC_COMMAND]) != 0
2084 || stcmp(entries[GEANY_BC_WORKING_DIR],
2085 table_data->rows[drow]->cmdsrc->entries[GEANY_BC_WORKING_DIR]) != 0
2090 if (dst->dst[grp] != NULL)
2092 if (*(dst->dst[grp]) == NULL)
2093 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2094 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2095 setptr((*(dst->dst[grp]))[cmd].entries[i], entries[i]);
2096 (*(dst->dst[grp]))[cmd].exists = TRUE;
2097 (*(dst->dst[grp]))[cmd].changed = TRUE;
2098 changed = TRUE;
2101 else
2103 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2104 g_free(entries[i]);
2106 return changed;
2110 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2112 gboolean changed = FALSE;
2113 gchar *reg = g_strdup(gtk_entry_get_text(GTK_ENTRY(regexentry)));
2115 if (
2117 (src == NULL /* originally there was no regex */
2118 || *src == NULL /* or it was NULL*/
2120 && NZV(reg) > 0 /* and something was typed */
2122 ||(src != NULL /* originally there was a regex*/
2123 && strcmp(*src, reg) != 0 /* and it has been changed */
2127 if (dst != NULL)
2129 setptr(*dst, reg);
2130 changed = TRUE;
2133 return changed;
2137 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2139 gint cmdindex, cmd;
2140 gboolean changed = FALSE;
2142 if (response == GTK_RESPONSE_ACCEPT)
2144 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2145 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2146 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2147 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2148 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2149 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2150 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2151 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2153 return changed;
2157 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2159 BuildDestination menu_dst;
2161 if (ft != NULL)
2163 menu_dst.dst[GEANY_GBG_FT] = &(ft->projfilecmds);
2164 menu_dst.fileregexstr = &(ft->projerror_regex_string);
2166 else
2168 menu_dst.dst[GEANY_GBG_FT] = NULL;
2169 menu_dst.fileregexstr = NULL;
2171 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2172 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2173 menu_dst.nonfileregexstr = &regex_proj;
2175 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2179 static void show_build_commands_dialog(void)
2181 GtkWidget *dialog, *table, *vbox;
2182 GeanyDocument *doc = document_get_current();
2183 GeanyFiletype *ft = NULL;
2184 const gchar *title = _("Set Build Commands");
2185 gint response;
2186 BuildTableData table_data;
2187 BuildDestination prefdsts;
2189 if (doc != NULL)
2190 ft = doc->file_type;
2191 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2192 GTK_DIALOG_DESTROY_WITH_PARENT,
2193 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2194 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2195 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2196 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2197 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2198 gtk_widget_show_all(dialog);
2199 /* run modally to prevent user changing idx filetype */
2200 response = gtk_dialog_run(GTK_DIALOG(dialog));
2202 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2203 if (ft != NULL)
2205 prefdsts.dst[GEANY_GBG_FT] = &(ft->homefilecmds);
2206 prefdsts.fileregexstr = &(ft->homeerror_regex_string);
2207 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->homeexeccmds);
2209 else
2211 prefdsts.dst[GEANY_GBG_FT] = NULL;
2212 prefdsts.fileregexstr = NULL;
2213 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2215 prefdsts.nonfileregexstr = &regex_pref;
2216 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2217 ft->home_save_needed = TRUE;
2218 build_free_fields(table_data);
2220 build_menu_update(doc);
2221 gtk_widget_destroy(dialog);
2225 /* Creates the relevant build menu if necessary. */
2226 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2228 BuildMenuItems *items;
2230 items = &menu_items;
2231 if (items->menu == NULL)
2232 create_build_menu(items);
2233 return items;
2237 /* set non_ft working directory entries to %p for project */
2238 void build_set_non_ft_wd_to_proj(BuildTableData table_data)
2240 gint i, start, end;
2242 start = build_groups_count[GEANY_GBG_FT];
2243 end = start + build_groups_count[GEANY_GBG_NON_FT];
2244 for (i = start; i < end; ++i)
2245 gtk_entry_set_text(GTK_ENTRY(table_data->rows[i]->entries[GEANY_BC_WORKING_DIR]), "%p");
2249 /*----------------------------------------------------------
2251 * Load and store configuration
2253 * ---------------------------------------------------------*/
2254 static const gchar *build_grp_name = "build-menu";
2256 /* config format for build-menu group is prefix_gg_nn_xx=value
2257 * where gg = FT, NF, EX for the command group
2258 * nn = 2 digit command number
2259 * xx = LB for label, CM for command and WD for working dir */
2260 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2261 static const gchar *fixedkey="xx_xx_xx";
2263 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2264 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2265 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2267 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2268 gchar *prefix, gboolean loc)
2270 gint cmd, prefixlen; /* NOTE prefixlen used in macros above */
2271 GeanyBuildCommand *dstcmd;
2272 gchar *key;
2273 static gchar cmdbuf[3] = " ";
2275 if (*dst == NULL)
2276 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2277 dstcmd = *dst;
2278 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2279 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2280 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2282 gchar *label;
2283 if (cmd < 0 || cmd >= 100)
2284 return; /* ensure no buffer overflow */
2285 sprintf(cmdbuf, "%02d", cmd);
2286 set_key_grp(key, groups[grp]);
2287 set_key_cmd(key, cmdbuf);
2288 set_key_fld(key, "LB");
2289 if (loc)
2290 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2291 else
2292 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2293 if (label != NULL)
2295 dstcmd[cmd].exists = TRUE;
2296 setptr(dstcmd[cmd].entries[GEANY_BC_LABEL], label);
2297 set_key_fld(key,"CM");
2298 setptr(dstcmd[cmd].entries[GEANY_BC_COMMAND],
2299 g_key_file_get_string(config, build_grp_name, key, NULL));
2300 set_key_fld(key,"WD");
2301 setptr(dstcmd[cmd].entries[GEANY_BC_WORKING_DIR],
2302 g_key_file_get_string(config, build_grp_name, key, NULL));
2304 else dstcmd[cmd].exists = FALSE;
2306 g_free(key);
2310 /* for the specified source load new format build menu items or try to make some sense of
2311 * old format setings, not done perfectly but better than ignoring them */
2312 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2314 GeanyFiletype *ft;
2315 GeanyProject *pj;
2316 gchar **ftlist;
2317 gchar *value, *basedir, *makebasedir;
2318 gboolean bvalue = FALSE;
2320 if (g_key_file_has_group(config, build_grp_name))
2322 switch (src)
2324 case GEANY_BCS_FT:
2325 ft = (GeanyFiletype*)p;
2326 if (ft == NULL)
2327 return;
2328 build_load_menu_grp(config, &(ft->filecmds), GEANY_GBG_FT, NULL, TRUE);
2329 build_load_menu_grp(config, &(ft->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2330 build_load_menu_grp(config, &(ft->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2331 setptr(ft->error_regex_string,
2332 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2333 break;
2334 case GEANY_BCS_HOME_FT:
2335 ft = (GeanyFiletype*)p;
2336 if (ft == NULL)
2337 return;
2338 build_load_menu_grp(config, &(ft->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2339 build_load_menu_grp(config, &(ft->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2340 setptr(ft->homeerror_regex_string,
2341 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2342 break;
2343 case GEANY_BCS_PREF:
2344 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2345 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2346 setptr(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2347 break;
2348 case GEANY_BCS_PROJ:
2349 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2350 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2351 setptr(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2352 pj = (GeanyProject*)p;
2353 if (p == NULL)
2354 return;
2355 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2356 if (ftlist != NULL)
2358 gchar **ftname;
2359 if (pj->build_filetypes_list == NULL)
2360 pj->build_filetypes_list = g_ptr_array_new();
2361 g_ptr_array_set_size(pj->build_filetypes_list, 0);
2362 for (ftname = ftlist; *ftname != NULL; ++ftname)
2364 ft = filetypes_lookup_by_name(*ftname);
2365 if (ft != NULL)
2367 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2368 g_ptr_array_add(pj->build_filetypes_list, ft);
2369 setptr(ft->projerror_regex_string,
2370 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2371 g_free(regkey);
2372 build_load_menu_grp(config, &(ft->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2373 build_load_menu_grp(config, &(ft->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2376 g_free(ftlist);
2378 break;
2379 default: /* defaults don't load from config, see build_init */
2380 break;
2384 /* load old [build_settings] values if there is no value defined by [build-menu] */
2386 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2387 #define ASSIGNIF(type, id, string, value) \
2388 if (NZV(value) && ! type[GBO_TO_CMD(id)].exists) { \
2389 type[GBO_TO_CMD(id)].exists = TRUE; \
2390 setptr(type[GBO_TO_CMD(id)].entries[GEANY_BC_LABEL], g_strdup(string)); \
2391 setptr(type[GBO_TO_CMD(id)].entries[GEANY_BC_COMMAND], (value)); \
2392 setptr(type[GBO_TO_CMD(id)].entries[GEANY_BC_WORKING_DIR], NULL); \
2393 type[GBO_TO_CMD(id)].old = TRUE; \
2394 } else \
2395 g_free(value);
2397 switch (src)
2399 case GEANY_BCS_FT:
2400 ft = (GeanyFiletype*)p;
2401 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2402 if (value != NULL)
2404 if (ft->filecmds == NULL)
2405 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2406 ASSIGNIF(ft->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2408 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2409 if (value != NULL)
2411 if (ft->filecmds == NULL)
2412 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2413 ASSIGNIF(ft->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2415 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2416 if (value != NULL)
2418 if (ft->execcmds == NULL)
2419 ft->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2420 ASSIGNIF(ft->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2422 if (ft->error_regex_string == NULL)
2423 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2424 break;
2425 case GEANY_BCS_PROJ:
2426 if (non_ft_pref == NULL)
2427 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2428 basedir = project_get_base_path();
2429 if (basedir == NULL)
2430 basedir = g_strdup("%d");
2431 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2432 if (bvalue)
2433 makebasedir = g_strdup(basedir);
2434 else
2435 makebasedir = g_strdup("%d");
2436 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2437 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].entries[GEANY_BC_WORKING_DIR], g_strdup(makebasedir));
2438 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2439 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].entries[GEANY_BC_WORKING_DIR], g_strdup(makebasedir));
2440 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2441 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].entries[GEANY_BC_WORKING_DIR], g_strdup("%d"));
2442 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2443 if (NZV(value))
2445 if (exec_proj == NULL)
2446 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2447 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2449 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2450 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].entries[GEANY_BC_LABEL], g_strdup(_("_Execute")));
2451 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].entries[GEANY_BC_COMMAND], value);
2452 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].entries[GEANY_BC_WORKING_DIR], g_strdup(basedir));
2453 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2456 g_free(makebasedir);
2457 g_free(basedir);
2458 break;
2459 case GEANY_BCS_PREF:
2460 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2461 if (value != NULL)
2463 if (non_ft_pref == NULL)
2464 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2465 ASSIGNIF(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target"),
2466 g_strdup_printf("%s ", value));
2467 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2468 g_strdup_printf("%s %%e.o",value));
2469 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2471 break;
2472 default:
2473 break;
2478 static gint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2480 gint cmd, prefixlen; /* NOTE prefixlen used in macros above */
2481 gchar *key;
2482 gint count = 0;
2483 enum GeanyBuildCmdEntries i;
2485 if (src == NULL)
2486 return 0;
2487 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2488 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2489 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2491 if (src[cmd].exists) ++count;
2492 if (src[cmd].changed)
2494 static gchar cmdbuf[4] = " ";
2495 if (cmd < 0 || cmd >= 100)
2496 return count; /* ensure no buffer overflow */
2497 sprintf(cmdbuf, "%02d", cmd);
2498 set_key_grp(key, groups[grp]);
2499 set_key_cmd(key, cmdbuf);
2500 if (src[cmd].exists)
2502 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2504 set_key_fld(key, config_keys[i]);
2505 g_key_file_set_string(config, build_grp_name, key, src[cmd].entries[i]);
2508 else
2510 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2512 set_key_fld(key, config_keys[i]);
2513 g_key_file_remove_key(config, build_grp_name, key, NULL);
2518 g_free(key);
2519 return count;
2523 typedef struct ForEachData
2525 GKeyFile *config;
2526 GPtrArray *ft_names;
2527 } ForEachData;
2530 static void foreach_project_filetype(gpointer data, gpointer user_data)
2532 GeanyFiletype *ft = (GeanyFiletype*) data;
2533 ForEachData *d = (ForEachData*) user_data;
2534 gint i = 0;
2535 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2537 i += build_save_menu_grp(d->config, ft->projfilecmds, GEANY_GBG_FT, ft->name);
2538 i += build_save_menu_grp(d->config, ft->projexeccmds, GEANY_GBG_EXEC, ft->name);
2539 if (NZV(ft->projerror_regex_string))
2541 g_key_file_set_string(d->config, build_grp_name, regkey, ft->projerror_regex_string);
2542 i++;
2544 else
2545 g_key_file_remove_key(d->config, build_grp_name, regkey, NULL);
2546 g_free(regkey);
2547 if (i > 0)
2548 g_ptr_array_add(d->ft_names, ft->name);
2552 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2554 GeanyFiletype *ft;
2555 GeanyProject *pj;
2556 ForEachData data;
2557 gchar *regkey;
2559 switch (src)
2561 case GEANY_BCS_HOME_FT:
2562 ft = (GeanyFiletype*)ptr;
2563 if (ft == NULL)
2564 return;
2565 build_save_menu_grp(config, ft->homefilecmds, GEANY_GBG_FT, NULL);
2566 build_save_menu_grp(config, ft->homeexeccmds, GEANY_GBG_EXEC, NULL);
2567 regkey = g_strdup_printf("%serror_regex", ft->name);
2568 if (NZV(ft->homeerror_regex_string))
2569 g_key_file_set_string(config, build_grp_name, regkey, ft->projerror_regex_string);
2570 else
2571 g_key_file_remove_key(config, build_grp_name, regkey, NULL);
2572 g_free(regkey);
2573 break;
2574 case GEANY_BCS_PREF:
2575 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2576 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2577 if (NZV(regex_pref))
2578 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2579 else
2580 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2581 break;
2582 case GEANY_BCS_PROJ:
2583 pj = (GeanyProject*)ptr;
2584 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2585 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2586 if (NZV(regex_proj))
2587 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2588 else
2589 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2590 if (pj->build_filetypes_list != NULL)
2592 data.config = config;
2593 data.ft_names = g_ptr_array_new();
2594 g_ptr_array_foreach(pj->build_filetypes_list, foreach_project_filetype, (gpointer)(&data));
2595 if (data.ft_names->pdata != NULL)
2596 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2597 (const gchar**)(data.ft_names->pdata), data.ft_names->len);
2598 else
2599 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2600 g_ptr_array_free(data.ft_names, TRUE);
2602 break;
2603 default: /* defaults and GEANY_BCS_FT can't save */
2604 break;
2609 void build_set_group_count(GeanyBuildGroup grp, gint count)
2611 gint i, sum;
2613 if (count > build_groups_count[grp])
2614 build_groups_count[grp] = count;
2615 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2616 sum += build_groups_count[i];
2617 build_items_count = sum;
2621 gint build_get_group_count(GeanyBuildGroup grp)
2623 return build_groups_count[grp];
2627 static void on_project_close(void)
2629 /* remove project regexen */
2630 setptr(regex_proj, NULL);
2634 static struct
2636 const gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2637 GeanyBuildCommand **ptr;
2638 gint index;
2639 } default_cmds[] = {
2640 { {N_("_Make"), "make", NULL}, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2641 { {N_("Make Custom _Target"), "make ", NULL}, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2642 { {N_("Make _Object"), "make %e.o", NULL }, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2643 { {N_("_Execute"), "./%e", NULL }, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2644 { {NULL, NULL, NULL}, NULL, 0 }
2648 void build_init(void)
2650 GtkWidget *item;
2651 GtkWidget *toolmenu;
2652 gint cmdindex;
2654 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2656 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2657 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2658 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2659 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2660 for (cmdindex = 0; default_cmds[cmdindex].entries[GEANY_BC_COMMAND] != NULL; ++cmdindex)
2662 enum GeanyBuildCmdEntries k;
2663 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2664 cmd->exists = TRUE;
2665 for (k = 0; k < GEANY_BC_CMDENTRIES_COUNT; k++)
2667 cmd->entries[k] = g_strdup(default_cmds[cmdindex].entries[k]);
2671 /* create the toolbar Build item sub menu */
2672 toolmenu = gtk_menu_new();
2673 g_object_ref(toolmenu);
2675 /* build the code */
2676 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2677 gtk_widget_show(item);
2678 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2679 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2680 GBO_TO_POINTER(GEANY_GBO_BUILD));
2681 widgets.toolitem_build = item;
2683 item = gtk_separator_menu_item_new();
2684 gtk_widget_show(item);
2685 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2687 /* build the code with make all */
2688 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2689 gtk_widget_show(item);
2690 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2691 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2692 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2693 widgets.toolitem_make_all = item;
2695 /* build the code with make custom */
2696 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target"));
2697 gtk_widget_show(item);
2698 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2699 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2700 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2701 widgets.toolitem_make_custom = item;
2703 /* build the code with make object */
2704 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2705 gtk_widget_show(item);
2706 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2707 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2708 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2709 widgets.toolitem_make_object = item;
2711 item = gtk_separator_menu_item_new();
2712 gtk_widget_show(item);
2713 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2715 /* arguments */
2716 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Menu Commands"));
2717 gtk_widget_show(item);
2718 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2719 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2720 widgets.toolitem_set_args = item;
2722 /* get toolbar action pointers */
2723 widgets.build_action = toolbar_get_action_by_name("Build");
2724 widgets.compile_action = toolbar_get_action_by_name("Compile");
2725 widgets.run_action = toolbar_get_action_by_name("Run");
2726 widgets.toolmenu = toolmenu;
2727 /* set the submenu to the toolbar item */
2728 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);