Fix using return value without checking return status, caused
[geany-mirror.git] / src / build.c
blob64521e8277ec3ff091c9a76ef7eb89950f6bd8ce
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"
64 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
66 static gchar *current_dir_entered = NULL;
68 typedef struct RunInfo
70 GPid pid;
71 gint file_type_id;
72 } RunInfo;
74 static RunInfo *run_info;
76 #ifdef G_OS_WIN32
77 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script.bat";
78 #else
79 static const gchar RUN_SCRIPT_CMD[] = "./geany_run_script.sh";
80 #endif
82 /* pack group (<8) and command (<32) into a user_data pointer */
83 #define GRP_CMD_TO_POINTER(grp, cmd) GINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
84 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
85 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_INT(gptr)&0x1f)
86 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_INT(gptr)&0xe0) >> 5)
88 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
90 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
92 static struct
94 GtkAction *run_action;
95 GtkAction *compile_action;
96 GtkAction *build_action;
97 GtkWidget *toolmenu;
99 GtkWidget *toolitem_build;
100 GtkWidget *toolitem_make_all;
101 GtkWidget *toolitem_make_custom;
102 GtkWidget *toolitem_make_object;
103 GtkWidget *toolitem_set_args;
105 widgets;
107 static gint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
108 static gint build_items_count = 9;
110 #ifndef G_OS_WIN32
111 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data);
112 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data);
113 #endif
114 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose);
115 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
116 static void set_stop_button(gboolean stop);
117 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
118 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
119 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
120 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
121 static void kill_process(GPid *pid);
122 static void show_build_result_message(gboolean failure);
123 static void process_build_output_line(const gchar *line, gint color);
124 static void show_build_commands_dialog(void);
127 void build_finalize(void)
129 g_free(build_info.dir);
130 g_free(build_info.custom_target);
132 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
133 gtk_widget_destroy(menu_items.menu);
137 /* note: copied from keybindings.c, may be able to go away */
138 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
139 GtkAccelGroup *accel_group, GtkWidget *menuitem)
141 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
143 if (kb->key != 0)
144 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
145 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
149 /* convenience routines to access parts of GeanyBuildCommand */
150 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
152 return bc->entries[id];
156 static gchar *buildcmd_label(GeanyBuildCommand *bc)
158 return id_to_str(bc, GEANY_BC_LABEL);
162 static gchar *buildcmd_cmd(GeanyBuildCommand *bc)
164 return id_to_str(bc, GEANY_BC_COMMAND);
168 static gchar *buildcmd_working_dir(GeanyBuildCommand *bc)
170 return id_to_str(bc, GEANY_BC_WORKING_DIR);
174 static const gchar *config_keys[] = {
175 [GEANY_BC_LABEL] = "LB",
176 [GEANY_BC_COMMAND] = "CM",
177 [GEANY_BC_WORKING_DIR] = "WD"
180 /*-----------------------------------------------------
182 * Execute commands and handle results
184 *-----------------------------------------------------*/
186 /* the various groups of commands not in the filetype struct */
187 GeanyBuildCommand *ft_def = NULL;
188 GeanyBuildCommand *non_ft_proj = NULL;
189 GeanyBuildCommand *non_ft_pref = NULL;
190 GeanyBuildCommand *non_ft_def = NULL;
191 GeanyBuildCommand *exec_proj = NULL;
192 GeanyBuildCommand *exec_pref = NULL;
193 GeanyBuildCommand *exec_def = NULL;
194 /* and the regexen not in the filetype structure */
195 gchar *regex_pref = NULL;
196 gchar *regex_proj = NULL;
198 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
199 #ifndef PRINTBUILDCMDS
200 #define PRINTBUILDCMDS FALSE
201 #endif
202 gboolean printbuildcmds = PRINTBUILDCMDS;
204 static GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
205 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
206 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
207 { &ft_def, NULL, NULL, NULL, NULL, NULL },
208 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
209 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
213 /* for debug only, print the commands structures in priority order */
214 static void printfcmds()
216 GeanyFiletype *ft = NULL;
217 GeanyDocument *doc;
218 gint i, j, k, l, m;
219 enum GeanyBuildCmdEntries n;
220 gint cc[GEANY_BCS_COUNT];
221 gchar c;
222 doc = document_get_current();
223 if (doc != NULL)
224 ft = doc->file_type;
225 if (ft != NULL)
227 printf("filetype %s\n",ft->name);
228 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->filecmds);
229 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->homefilecmds);
230 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->projfilecmds);
231 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->ftdefcmds);
232 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->execcmds);
233 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->homeexeccmds);
234 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->projexeccmds);
236 for (i = 0; i < GEANY_BCS_COUNT; ++i)
238 m = 1;
239 for (j = 0; j < GEANY_GBG_COUNT; ++j)
241 for (k = 0; k < build_groups_count[j]; ++k)
242 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
244 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
246 if ((*(cl[j][i]))[k].entries[n] != NULL &&
247 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
249 m = l;
254 cc[i] = m;
256 for (i = 0; i < GEANY_GBG_COUNT; ++i)
258 for (k = 0; k < build_groups_count[i]; ++k)
260 for (l = 0; l < 2; ++l)
262 c = ' ';
263 for (j = 0; j < GEANY_BCS_COUNT; ++j)
265 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
267 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
269 if ((*(cl[i][j]))[k].entries[i] != NULL)
270 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
271 else
272 printf("%c %*.*s",c,cc[j],cc[j]," ");
275 else
276 printf("%c %*.*s",c,cc[j],cc[j]," ");
277 c = ',';
279 printf("\n");
282 printf("\n");
287 /* macros to save typing and make the logic visible */
288 #define return_cmd_if(src, cmds) if (cmds != NULL && cmds[cmdindex].exists && below>src)\
290 *fr=src; \
291 if (printbuildcmds) \
292 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
293 return &(cmds[cmdindex]); \
295 #define return_ft_cmd_if(src, cmds) if (ft != NULL && ft->cmds != NULL \
296 && ft->cmds[cmdindex].exists && below>src)\
298 *fr=src; \
299 if (printbuildcmds) \
300 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
301 return &(ft->cmds[cmdindex]); \
305 /* get the next lowest command taking priority into account */
306 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, gint cmdgrp, gint cmdindex,
307 gint below, gint *from)
309 /* Note: parameter below used in macros above */
311 GeanyFiletype *ft = NULL;
312 gint sink, *fr = &sink;
314 if (printbuildcmds)
315 printfcmds();
316 if (cmdgrp >= GEANY_GBG_COUNT)
317 return NULL;
318 if (from != NULL)
319 fr = from;
320 if (doc == NULL)
321 doc = document_get_current();
322 if (doc != NULL)
323 ft = doc->file_type;
324 switch (cmdgrp)
326 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
327 if (ft != NULL)
329 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
330 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
331 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
333 return_cmd_if(GEANY_BCS_DEF, ft_def);
334 break;
335 case GEANY_GBG_NON_FT: /* order proj, pref, def */
336 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
337 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
338 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
339 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
340 break;
341 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
342 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
343 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
344 return_cmd_if(GEANY_BCS_PREF, exec_pref);
345 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
346 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
347 return_cmd_if(GEANY_BCS_DEF, exec_def);
348 break;
349 default:
350 break;
352 return NULL;
356 /* shortcut to start looking at the top */
357 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, gint grp, gint cmdindex, gint *from)
359 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
363 #define return_nonblank_regex(src, ptr) if (NZV(ptr)) \
364 { *fr = (src); return &(ptr); }
367 /* like get_build_cmd, but for regexen, used by filetypes */
368 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, gint *from)
370 gint sink, *fr = &sink;
371 if (from != NULL)
372 fr = from;
373 if (grp == GEANY_GBG_FT)
375 if (ft == NULL)
377 GeanyDocument *doc = document_get_current();
378 if (doc != NULL)
379 ft = doc->file_type;
381 if (ft == NULL)
382 return NULL;
383 return_nonblank_regex(GEANY_BCS_PROJ, ft->projerror_regex_string);
384 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->homeerror_regex_string);
385 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
387 else if (grp == GEANY_GBG_NON_FT)
389 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
390 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
392 return NULL;
396 /* get pointer to the command group array */
397 static GeanyBuildCommand *get_build_group(GeanyBuildSource src, GeanyBuildGroup grp)
399 GeanyDocument *doc;
400 GeanyFiletype *ft = NULL;
402 switch (grp)
404 case GEANY_GBG_FT:
405 if ((doc = document_get_current()) == NULL)
406 return NULL;
407 if ((ft = doc->file_type) == NULL)
408 return NULL;
410 switch (src)
412 case GEANY_BCS_DEF: return ft->ftdefcmds;
413 case GEANY_BCS_FT: return ft->filecmds;
414 case GEANY_BCS_HOME_FT: return ft->homefilecmds;
415 case GEANY_BCS_PREF: return ft->homefilecmds;
416 case GEANY_BCS_PROJ: return ft->projfilecmds;
417 default: return NULL;
419 break;
420 case GEANY_GBG_NON_FT:
421 switch (src)
423 case GEANY_BCS_DEF: return non_ft_def;
424 case GEANY_BCS_PREF: return non_ft_pref;
425 case GEANY_BCS_PROJ: return non_ft_proj;
426 default: return NULL;
428 break;
429 case GEANY_GBG_EXEC:
430 if ((doc = document_get_current()) != NULL)
431 ft = doc->file_type;
432 switch (src)
434 case GEANY_BCS_DEF: return exec_def;
435 case GEANY_BCS_FT: return ft ? ft->execcmds: NULL;
436 case GEANY_BCS_HOME_FT: return ft ? ft->homeexeccmds: NULL;
437 case GEANY_BCS_PROJ_FT: return ft ? ft->projexeccmds: NULL;
438 case GEANY_BCS_PREF: return exec_pref;
439 case GEANY_BCS_PROJ: return exec_proj;
440 default: return NULL;
442 break;
443 default:
444 return NULL;
449 /** Remove the specified Build menu item.
451 * Makes the specified menu item configuration no longer exist. This
452 * is different to setting fields to blank because the menu item
453 * will be deleted from the configuration file on saving
454 * (except the system filetypes settings @see Build Menu Configuration
455 * section of the Manual).
457 * @param src the source of the menu item to remove.
458 * @param grp the group of the command to remove.
459 * @param cmd the index (from 0) of the command within the group. A negative
460 * value will remove the whole group.
462 * If any parameter is out of range does nothing.
464 * @see build_menu_update
466 void build_remove_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, gint cmd)
468 GeanyBuildCommand *bc;
469 gint i;
470 bc = get_build_group(src, grp);
471 if (bc == NULL)
472 return;
473 if (cmd < 0)
475 for (i = 0; i < build_groups_count[grp]; ++i)
476 bc[i].exists = FALSE;
478 else if (cmd < build_groups_count[grp])
479 bc[cmd].exists = FALSE;
483 /** Get the @a GeanyBuildCommand structure for the specified Build menu item.
485 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
486 * hidden by higher priority commands.
488 * @param src the source of the specified menu item.
489 * @param grp the group of the specified menu item.
490 * @param cmd the index of the command within the group.
492 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
493 * This is a pointer to an internal structure and must not be freed.
495 * @see build_menu_update
497 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, gint cmd)
499 GeanyBuildCommand *bc;
500 if (src >= GEANY_BCS_COUNT || grp >= GEANY_GBG_COUNT || cmd >= build_groups_count[grp])
501 return NULL;
502 bc = get_build_group(src, grp);
503 if (bc == NULL)
504 return NULL;
505 return &(bc[cmd]);
509 /** Get the @a GeanyBuildCommand structure for the menu item.
511 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
512 * that the menu item will use if activated.
514 * @param grp the group of the specified menu item.
515 * @param cmd the index of the command within the group.
516 * @param src pointer to @a gint to return which source provided the command. Ignored if @a NULL.
517 * Values are one of @a GeanyBuildSource but returns a signed type not the enum.
519 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
520 * This is a pointer to an internal structure and must not be freed.
522 * @see build_menu_update
524 /* parameter checked version of get_build_cmd for external interface */
525 GeanyBuildCommand *build_get_current_menu_item(GeanyBuildGroup grp, gint cmd, gint *src)
527 if (*src >= GEANY_BCS_COUNT || grp >= GEANY_GBG_COUNT || cmd >= build_groups_count[grp])
528 return NULL;
529 return get_build_cmd(NULL, grp, cmd, src);
533 /* Clear all error indicators in all documents. */
534 static void clear_errors(GeanyDocument *doc)
536 guint i;
538 for (i = 0; i < documents_array->len; i++)
540 if (documents[i]->is_valid)
541 editor_indicator_clear_errors(documents[i]->editor);
546 #ifdef G_OS_WIN32
547 static void parse_build_output(const gchar **output, gint status)
549 guint x, i, len;
550 gchar *line, **lines;
552 for (x = 0; x < 2; x++)
554 if (NZV(output[x]))
556 lines = g_strsplit_set(output[x], "\r\n", -1);
557 len = g_strv_length(lines);
559 for (i = 0; i < len; i++)
561 if (NZV(lines[i]))
563 line = lines[i];
564 while (*line != '\0')
565 { /* replace any conrol characters in the output */
566 if (*line < 32)
567 *line = 32;
568 line++;
570 process_build_output_line(lines[i], COLOR_BLACK);
573 g_strfreev(lines);
577 show_build_result_message(status != 0);
578 utils_beep();
580 build_info.pid = 0;
581 /* enable build items again */
582 build_menu_update(NULL);
584 #endif
587 /* Replaces occurences of %e and %p with the appropriate filenames,
588 * %d and %p replacements should be in UTF8 */
589 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
591 GString *stack;
592 gchar *filename = NULL;
593 gchar *replacement;
594 gchar *executable = NULL;
595 gchar *ret_str; /* to be freed when not in use anymore */
597 stack = g_string_new(src);
598 if (doc != NULL && doc->file_name != NULL)
600 filename = utils_get_utf8_from_locale(doc->file_name);
602 /* replace %f with the filename (including extension) */
603 replacement = g_path_get_basename(filename);
604 utils_string_replace_all(stack, "%f", replacement);
605 g_free(replacement);
607 /* replace %d with the absolute path of the dir of the current file */
608 replacement = g_path_get_dirname(filename);
609 utils_string_replace_all(stack, "%d", replacement);
610 g_free(replacement);
612 /* replace %e with the filename (excluding extension) */
613 executable = utils_remove_ext_from_filename(filename);
614 replacement = g_path_get_basename(executable);
615 utils_string_replace_all(stack, "%e", replacement);
616 g_free(replacement);
619 /* replace %p with the current project's (absolute) base directory */
620 replacement = NULL; /* prevent double free if no replacement found */
621 if (app->project)
623 replacement = project_get_base_path();
625 else if (strstr(stack->str, "%p"))
626 { /* fall back to %d */
627 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
628 if (doc != NULL && filename != NULL)
629 replacement = g_path_get_dirname(filename);
632 utils_string_replace_all(stack, "%p", replacement);
633 g_free(replacement);
635 ret_str = utils_get_utf8_from_locale(stack->str);
636 g_free(executable);
637 g_free(filename);
638 g_string_free(stack, TRUE);
640 return ret_str; /* don't forget to free src also if needed */
644 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
645 * idx document directory */
646 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
648 GError *error = NULL;
649 gchar **argv;
650 gchar *working_dir;
651 gchar *utf8_working_dir;
652 gchar *cmd_string;
653 gchar *utf8_cmd_string;
654 #ifdef G_OS_WIN32
655 gchar *output[2];
656 gint status;
657 #else
658 gint stdout_fd;
659 gint stderr_fd;
660 #endif
662 if (!((doc != NULL && NZV(doc->file_name)) || NZV(dir)))
664 geany_debug("Failed to run command with no working directory");
665 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
666 return (GPid) 1;
669 if (doc != NULL)
670 clear_errors(doc);
671 setptr(current_dir_entered, NULL);
673 cmd_string = g_strdup(cmd);
675 #ifdef G_OS_WIN32
676 argv = g_strsplit(cmd_string, " ", 0);
677 #else
678 argv = g_new0(gchar *, 4);
679 argv[0] = g_strdup("/bin/sh");
680 argv[1] = g_strdup("-c");
681 argv[2] = cmd_string;
682 argv[3] = NULL;
683 #endif
685 utf8_cmd_string = utils_get_utf8_from_locale(cmd_string);
686 utf8_working_dir = NZV(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
687 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
689 gtk_list_store_clear(msgwindow.store_compiler);
690 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
691 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
692 g_free(utf8_working_dir);
693 g_free(utf8_cmd_string);
695 /* set the build info for the message window */
696 g_free(build_info.dir);
697 build_info.dir = g_strdup(working_dir);
698 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : FILETYPE_ID(doc->file_type);
699 build_info.message_count = 0;
701 #ifdef G_OS_WIN32
702 if (! utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
703 NULL, NULL, &output[0], &output[1], &status, &error))
704 #else
705 if (! g_spawn_async_with_pipes(working_dir, argv, NULL,
706 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
707 &(build_info.pid), NULL, &stdout_fd, &stderr_fd, &error))
708 #endif
710 geany_debug("g_spawn_async_with_pipes() failed: %s", error->message);
711 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
712 g_strfreev(argv);
713 g_error_free(error);
714 g_free(working_dir);
715 error = NULL;
716 return (GPid) 0;
719 #ifdef G_OS_WIN32
720 parse_build_output((const gchar**) output, status);
721 g_free(output[0]);
722 g_free(output[1]);
723 #else
724 if (build_info.pid > 0)
726 g_child_watch_add(build_info.pid, (GChildWatchFunc) build_exit_cb, NULL);
727 build_menu_update(doc);
728 ui_progress_bar_start(NULL);
731 /* use GIOChannels to monitor stdout and stderr */
732 utils_set_up_io_channel(stdout_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
733 TRUE, build_iofunc, GINT_TO_POINTER(0));
734 utils_set_up_io_channel(stderr_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
735 TRUE, build_iofunc, GINT_TO_POINTER(1));
736 #endif
738 g_strfreev(argv);
739 g_free(working_dir);
741 return build_info.pid;
745 /* Returns: NULL if there was an error, or the working directory the script was created in.
746 * vte_cmd_nonscript is the location of a string which is filled with the command to be used
747 * when vc->skip_run_script is set, otherwise it will be set to NULL */
748 static gchar *prepare_run_script(GeanyDocument *doc, gchar **vte_cmd_nonscript, gint cmdindex)
750 gchar *locale_filename = NULL;
751 gboolean have_project;
752 GeanyProject *project = app->project;
753 GeanyBuildCommand *cmd = NULL;
754 gchar *executable = NULL;
755 gchar *working_dir = NULL, *cmd_working_dir;
756 gboolean autoclose = FALSE;
757 gboolean result = FALSE;
758 gchar *tmp;
759 gchar *cmd_string;
761 if (vte_cmd_nonscript != NULL)
762 *vte_cmd_nonscript = NULL;
764 locale_filename = utils_get_locale_from_utf8(doc->file_name);
766 have_project = project != NULL;
767 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
769 cmd_string = build_replace_placeholder(doc, buildcmd_cmd(cmd));
770 cmd_working_dir = buildcmd_working_dir(cmd);
771 if (! NZV(cmd_working_dir))
772 cmd_working_dir = "%d";
773 working_dir = build_replace_placeholder(doc, cmd_working_dir); /* in utf-8 */
775 /* only test whether working dir exists, don't change it or else Windows support will break
776 * (gspawn-win32-helper.exe is used by GLib and must be in $PATH which means current working
777 * dir where geany.exe was started from, so we can't change it) */
778 if (!NZV(working_dir) || ! g_file_test(working_dir, G_FILE_TEST_EXISTS) ||
779 ! g_file_test(working_dir, G_FILE_TEST_IS_DIR))
781 ui_set_statusbar(TRUE, _("Failed to change the working directory to \"%s\""),
782 NZV(working_dir) ? working_dir : "<NULL>" );
783 utils_free_pointers(2, cmd_string, working_dir, NULL);
784 return NULL;
787 #ifdef HAVE_VTE
788 if (vte_info.load_vte && vc != NULL && vc->run_in_vte)
790 if (vc->skip_run_script)
792 if (vte_cmd_nonscript != NULL)
793 *vte_cmd_nonscript = cmd_string;
795 utils_free_pointers(2, executable, locale_filename, NULL);
796 return working_dir;
798 else
799 /* don't wait for user input at the end of script when we are running in VTE */
800 autoclose = TRUE;
802 #endif
804 /* RUN_SCRIPT_CMD should be ok in UTF8 without converting in locale because it
805 * contains no umlauts */
806 tmp = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
807 result = build_create_shellscript(tmp, cmd_string, autoclose);
808 if (! result)
810 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created)"),
811 NZV(cmd_string) ? cmd_string : NULL);
814 utils_free_pointers(4, cmd_string, tmp, executable, locale_filename, NULL);
816 if (result)
817 return working_dir;
819 g_free(working_dir);
820 return NULL;
824 static GPid build_run_cmd(GeanyDocument *doc, gint cmdindex)
826 gchar *working_dir;
827 gchar *vte_cmd_nonscript = NULL;
828 GError *error = NULL;
830 if (doc == NULL || doc->file_name == NULL)
831 return (GPid) 0;
833 working_dir = prepare_run_script(doc, &vte_cmd_nonscript, cmdindex);
834 if (working_dir == NULL)
835 return (GPid) 0;
837 run_info[cmdindex].file_type_id = FILETYPE_ID(doc->file_type);
839 #ifdef HAVE_VTE
840 if (vte_info.load_vte && vc != NULL && vc->run_in_vte)
842 gchar *vte_cmd;
844 if (vc->skip_run_script)
846 setptr(vte_cmd_nonscript, utils_get_utf8_from_locale(vte_cmd_nonscript));
847 vte_cmd = g_strconcat(vte_cmd_nonscript, "\n", NULL);
848 g_free(vte_cmd_nonscript);
850 else
851 vte_cmd = g_strconcat("\n/bin/sh ", RUN_SCRIPT_CMD, "\n", NULL);
853 /* change into current directory if it is not done by default */
854 if (! vc->follow_path)
856 /* we need to convert the working_dir back to UTF-8 because the VTE expects it */
857 gchar *utf8_working_dir = utils_get_utf8_from_locale(working_dir);
858 vte_cwd(utf8_working_dir, TRUE);
859 g_free(utf8_working_dir);
861 if (! vte_send_cmd(vte_cmd))
863 ui_set_statusbar(FALSE,
864 _("Could not execute the file in the VTE because it probably contains a command."));
865 geany_debug("Could not execute the file in the VTE because it probably contains a command.");
868 /* show the VTE */
869 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
870 gtk_widget_grab_focus(vc->vte);
871 msgwin_show_hide(TRUE);
873 run_info[cmdindex].pid = 1;
875 g_free(vte_cmd);
877 else
878 #endif
880 gchar *locale_term_cmd = NULL;
881 gchar **term_argv = NULL;
882 guint term_argv_len, i;
883 gchar **argv = NULL;
885 /* get the terminal path */
886 locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
887 /* split the term_cmd, so arguments will work too */
888 term_argv = g_strsplit(locale_term_cmd, " ", -1);
889 term_argv_len = g_strv_length(term_argv);
891 /* check that terminal exists (to prevent misleading error messages) */
892 if (term_argv[0] != NULL)
894 gchar *tmp = term_argv[0];
895 /* g_find_program_in_path checks whether tmp exists and is executable */
896 term_argv[0] = g_find_program_in_path(tmp);
897 g_free(tmp);
899 if (term_argv[0] == NULL)
901 ui_set_statusbar(TRUE,
902 _("Could not find terminal \"%s\" "
903 "(check path for Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
904 run_info[cmdindex].pid = (GPid) 1;
905 goto free_strings;
908 argv = g_new0(gchar *, term_argv_len + 3);
909 for (i = 0; i < term_argv_len; i++)
911 argv[i] = g_strdup(term_argv[i]);
913 #ifdef G_OS_WIN32
914 /* command line arguments only for cmd.exe */
915 if (strstr(argv[0], "cmd.exe") != NULL)
917 argv[term_argv_len ] = g_strdup("/Q /C");
918 argv[term_argv_len + 1] = g_strdup(RUN_SCRIPT_CMD);
920 else
922 argv[term_argv_len ] = g_strdup(RUN_SCRIPT_CMD);
923 argv[term_argv_len + 1] = NULL;
925 #else
926 argv[term_argv_len ] = g_strdup("-e");
927 argv[term_argv_len + 1] = g_strconcat("/bin/sh ", RUN_SCRIPT_CMD, NULL);
928 #endif
929 argv[term_argv_len + 2] = NULL;
931 if (! g_spawn_async(working_dir, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
932 NULL, NULL, &(run_info[cmdindex].pid), &error))
934 geany_debug("g_spawn_async() failed: %s", error->message);
935 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
936 g_unlink(RUN_SCRIPT_CMD);
937 g_error_free(error);
938 error = NULL;
939 run_info[cmdindex].pid = (GPid) 0;
942 if (run_info[cmdindex].pid > 0)
944 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
945 (gpointer)&(run_info[cmdindex]));
946 build_menu_update(doc);
948 free_strings:
949 g_strfreev(argv);
950 g_strfreev(term_argv);
951 g_free(locale_term_cmd);
954 g_free(working_dir);
955 return run_info[cmdindex].pid;
959 static void process_build_output_line(const gchar *str, gint color)
961 gchar *msg, *tmp;
963 msg = g_strdup(str);
965 g_strchomp(msg);
967 if (! NZV(msg))
968 return;
970 if (editor_prefs.use_indicators && build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
972 gchar *filename;
973 gint line;
975 build_info.message_count++;
977 if (build_parse_make_dir(msg, &tmp))
979 setptr(current_dir_entered, tmp);
981 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
982 if (line != -1 && filename != NULL)
984 GeanyDocument *doc = document_find_by_filename(filename);
986 if (doc)
988 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
989 line--; /* so only adjust the line number if it is greater than 0 */
990 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
992 color = COLOR_RED; /* error message parsed on the line */
994 g_free(filename);
996 msgwin_compiler_add_string(color, msg);
998 g_free(msg);
1002 #ifndef G_OS_WIN32
1003 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data)
1005 if (cond & (G_IO_IN | G_IO_PRI))
1007 gchar *msg;
1009 while (g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL) && msg)
1011 gint color = (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK;
1013 process_build_output_line(msg, color);
1014 g_free(msg);
1017 if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
1018 return FALSE;
1020 return TRUE;
1022 #endif
1025 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1027 const gchar *pos;
1029 *prefix = NULL;
1031 if (string == NULL)
1032 return FALSE;
1034 if ((pos = strstr(string, "Entering directory")) != NULL)
1036 gsize len;
1037 gchar *input;
1039 /* get the start of the path */
1040 pos = strstr(string, "/");
1042 if (pos == NULL)
1043 return FALSE;
1045 input = g_strdup(pos);
1047 /* kill the ' at the end of the path */
1048 len = strlen(input);
1049 input[len - 1] = '\0';
1050 input = g_realloc(input, len); /* shorten by 1 */
1051 *prefix = input;
1053 return TRUE;
1056 if (strstr(string, "Leaving directory") != NULL)
1058 *prefix = NULL;
1059 return TRUE;
1062 return FALSE;
1066 static void show_build_result_message(gboolean failure)
1068 gchar *msg;
1070 if (failure)
1072 msg = _("Compilation failed.");
1073 msgwin_compiler_add_string(COLOR_BLUE, msg);
1074 /* If msgwindow is hidden, user will want to display it to see the error */
1075 if (! ui_prefs.msgwindow_visible)
1077 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1078 msgwin_show_hide(TRUE);
1080 else
1081 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1082 ui_set_statusbar(FALSE, "%s", msg);
1084 else
1086 msg = _("Compilation finished successfully.");
1087 msgwin_compiler_add_string(COLOR_BLUE, msg);
1088 if (! ui_prefs.msgwindow_visible ||
1089 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1090 ui_set_statusbar(FALSE, "%s", msg);
1095 #ifndef G_OS_WIN32
1096 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1098 gboolean failure = FALSE;
1100 if (WIFEXITED(status))
1102 if (WEXITSTATUS(status) != EXIT_SUCCESS)
1103 failure = TRUE;
1105 else if (WIFSIGNALED(status))
1107 /* the terminating signal: WTERMSIG (status)); */
1108 failure = TRUE;
1110 else
1111 { /* any other failure occured */
1112 failure = TRUE;
1114 show_build_result_message(failure);
1116 utils_beep();
1117 g_spawn_close_pid(child_pid);
1119 build_info.pid = 0;
1120 /* enable build items again */
1121 build_menu_update(NULL);
1122 ui_progress_bar_stop();
1124 #endif
1127 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1129 RunInfo *run_info_data = (RunInfo*)user_data;
1131 g_spawn_close_pid(child_pid);
1133 run_info_data->pid = 0;
1134 /* reset the stop button and menu item to the original meaning */
1135 build_menu_update(NULL);
1139 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1140 * fname is the full file name (including path) for the script to create */
1141 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose)
1143 FILE *fp;
1144 gchar *str;
1145 #ifdef G_OS_WIN32
1146 gchar *expanded_cmd;
1147 #endif
1149 fp = g_fopen(fname, "w");
1150 if (! fp)
1151 return FALSE;
1152 #ifdef G_OS_WIN32
1153 /* Expand environment variables like %blah%. */
1154 expanded_cmd = win32_expand_environment_variables(cmd);
1155 str = g_strdup_printf("%s\n\n%s\ndel \"%%0\"\n\npause\n", expanded_cmd, (autoclose) ? "" : "pause");
1156 g_free(expanded_cmd);
1157 #else
1158 str = g_strdup_printf(
1159 "#!/bin/sh\n\nrm $0\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1160 \n\n%s\n", cmd, (autoclose) ? "" :
1161 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1162 "dash\ndummy_var=\"\"\nread dummy_var");
1163 #endif
1165 fputs(str, fp);
1166 g_free(str);
1168 fclose(fp);
1170 return TRUE;
1174 typedef void Callback(GtkWidget *w, gpointer u);
1176 /* run the command catenating cmd_cat if present */
1177 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, gint cmd, gchar *cmd_cat)
1179 gchar *dir;
1180 gchar *full_command, *subs_command;
1181 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1182 gchar *cmdstr;
1184 if (buildcmd == NULL)
1185 return;
1187 cmdstr = buildcmd_cmd(buildcmd);
1189 if (cmd_cat != NULL)
1191 if (cmdstr != NULL)
1192 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1193 else
1194 full_command = g_strdup(cmd_cat);
1196 else
1197 full_command = cmdstr;
1199 dir = build_replace_placeholder(doc, buildcmd_working_dir(buildcmd));
1200 subs_command = build_replace_placeholder(doc, full_command);
1201 build_info.grp = grp;
1202 build_info.cmd = cmd;
1203 build_spawn_cmd(doc, subs_command, dir);
1204 g_free(subs_command);
1205 g_free(dir);
1206 if (cmd_cat != NULL)
1207 g_free(full_command);
1208 build_menu_update(doc);
1213 /*----------------------------------------------------------------
1215 * Create build menu and handle callbacks (&toolbar callbacks)
1217 *----------------------------------------------------------------*/
1218 static void on_make_custom_input_response(const gchar *input)
1220 GeanyDocument *doc = document_get_current();
1221 setptr(build_info.custom_target, g_strdup(input));
1222 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1223 build_info.custom_target);
1227 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1229 GeanyDocument *doc = document_get_current();
1230 GeanyBuildCommand *bc;
1231 gint grp = GPOINTER_TO_GRP(user_data);
1232 gint cmd = GPOINTER_TO_CMD(user_data);
1234 if (doc && doc->changed)
1235 document_save_file(doc, FALSE);
1236 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1238 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1240 if (! dialog)
1242 dialog = dialogs_show_input_persistent(_("Custom Text"),
1243 _("Enter custom text here, all entered text is appended to the command."),
1244 build_info.custom_target, &on_make_custom_input_response);
1246 else
1248 gtk_widget_show(dialog);
1250 return;
1252 else if (grp == GEANY_GBG_EXEC)
1254 if (run_info[cmd].pid > (GPid) 1)
1256 kill_process(&run_info[cmd].pid);
1257 return;
1259 bc = get_build_cmd(doc, grp, cmd, NULL);
1260 if (bc != NULL && strcmp(buildcmd_cmd(bc), "builtin") == 0)
1262 gchar *uri;
1263 if (doc == NULL)
1264 return;
1265 uri = g_strconcat("file:///", g_path_skip_root(doc->file_name), NULL);
1266 utils_open_browser(uri);
1267 g_free(uri);
1270 else
1271 build_run_cmd(doc, cmd);
1273 else
1274 build_command(doc, grp, cmd, NULL);
1278 /* group codes for menu items other than the known commands
1279 * value order is important, see the following table for use */
1281 /* the rest in each group */
1282 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1283 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1284 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1285 /* the separator */
1286 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1287 /* the fixed items */
1288 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1289 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1290 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1291 #define MENU_DONE (MENU_COMMANDS + 1)
1294 static struct BuildMenuItemSpec {
1295 const gchar *stock_id;
1296 const gint key_binding;
1297 const gint build_grp;
1298 const gint build_cmd;
1299 const gchar *fix_label;
1300 Callback *cb;
1301 } build_menu_specs[] = {
1302 { GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1303 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item },
1304 { GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1305 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item },
1306 { NULL, -1, MENU_FT_REST,
1307 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item },
1308 { NULL, -1, MENU_SEPARATOR,
1309 GBF_SEP_1, NULL, NULL },
1310 { NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1311 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item },
1312 { NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1313 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item },
1314 { NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1315 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item },
1316 { NULL, -1, MENU_NON_FT_REST,
1317 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item },
1318 { NULL, -1, MENU_SEPARATOR,
1319 GBF_SEP_2, NULL, NULL },
1320 { NULL, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1321 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error },
1322 { NULL, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1323 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error },
1324 { NULL, -1, MENU_SEPARATOR,
1325 GBF_SEP_3, NULL, NULL },
1326 { GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1327 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item },
1328 { NULL, -1, MENU_EXEC_REST,
1329 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item },
1330 { NULL, -1, MENU_SEPARATOR,
1331 GBF_SEP_4, NULL, NULL },
1332 { GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1333 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate },
1334 { NULL, -1, MENU_DONE,
1335 0, NULL, NULL }
1339 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1340 struct BuildMenuItemSpec *bs, gchar *lbl, gint grp, gint cmd)
1342 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1343 if (bs->stock_id != NULL)
1345 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1346 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1349 gtk_widget_show(item);
1350 if (bs->key_binding >= 0)
1351 add_menu_accel(group, bs->key_binding, ag, item);
1352 gtk_container_add(GTK_CONTAINER(menu), item);
1353 if (bs->cb != NULL)
1355 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1357 menu_items.menu_item[grp][cmd] = item;
1361 static void create_build_menu(BuildMenuItems *build_menu_items)
1363 GtkWidget *menu;
1364 GtkAccelGroup *accel_group = gtk_accel_group_new();
1365 GeanyKeyGroup *keygroup = g_ptr_array_index(keybinding_groups, GEANY_KEY_GROUP_BUILD);
1366 gint i, j;
1368 menu = gtk_menu_new();
1369 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1370 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1371 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1372 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1374 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1376 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1377 if (bs->build_grp == MENU_SEPARATOR)
1379 GtkWidget *item = gtk_separator_menu_item_new();
1380 gtk_widget_show(item);
1381 gtk_container_add(GTK_CONTAINER(menu), item);
1382 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1384 else if (bs->fix_label != NULL)
1386 create_build_menu_item(menu, keygroup, accel_group, bs, gettext(bs->fix_label),
1387 GBG_FIXED, bs->build_cmd);
1389 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1391 gint grp = bs->build_grp - GEANY_GBG_COUNT;
1392 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1394 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1395 gchar *lbl = (bc == NULL) ? "" : buildcmd_label(bc);
1396 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1399 else
1401 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1402 gchar *lbl = (bc == NULL) ? "" : buildcmd_label(bc);
1403 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1406 build_menu_items->menu = menu;
1407 gtk_widget_show(menu);
1408 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1412 /* portability to various GTK versions needs checking
1413 * conforms to description of gtk_accel_label as child of menu item
1414 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1415 static void geany_menu_item_set_label(GtkWidget *w, gchar *label)
1417 GtkWidget *c = gtk_bin_get_child(GTK_BIN(w));
1418 gtk_label_set_text_with_mnemonic(GTK_LABEL(c), label);
1422 /** Update the build menu to reflect changes in configuration or status.
1424 * Sets the labels and number of visible items to match the highest
1425 * priority configured commands. Also sets sensitivity if build commands are
1426 * running and switches executes to stop when commands are running.
1428 * @param doc The current document, if available, to save looking it up.
1429 * If @c NULL it will be looked up.
1431 * Call this after modifying any fields of a GeanyBuildCommand structure.
1433 * @see Build Menu Configuration section of the Manual.
1436 void build_menu_update(GeanyDocument *doc)
1438 gint i, cmdcount, cmd, grp;
1439 gboolean vis = FALSE;
1440 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1441 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1442 GeanyBuildCommand *bc;
1444 if (menu_items.menu == NULL)
1445 create_build_menu(&menu_items);
1446 if (doc == NULL)
1447 doc = document_get_current();
1448 have_path = doc != NULL && doc->file_name != NULL;
1449 build_running = build_info.pid > (GPid) 1;
1450 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1451 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1453 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1454 switch (bs->build_grp)
1456 case MENU_SEPARATOR:
1457 if (vis == TRUE)
1459 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1460 vis = FALSE;
1462 else
1463 gtk_widget_hide_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1464 break;
1465 case MENU_NEXT_ERROR:
1466 case MENU_PREV_ERROR:
1467 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1468 vis |= TRUE;
1469 break;
1470 case MENU_COMMANDS:
1471 vis |= TRUE;
1472 break;
1473 default: /* all configurable commands */
1474 if (bs->build_grp >= GEANY_GBG_COUNT)
1476 grp = bs->build_grp - GEANY_GBG_COUNT;
1477 cmdcount = build_groups_count[grp];
1479 else
1481 grp = bs->build_grp;
1482 cmdcount = bs->build_cmd + 1;
1484 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1486 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1487 gchar *label;
1488 bc = get_build_cmd(doc, grp, cmd, NULL);
1489 if (bc)
1490 label = buildcmd_label(bc);
1491 else
1492 label = NULL;
1494 if (grp < GEANY_GBG_EXEC)
1496 cmd_sensitivity =
1497 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1498 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1499 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1500 if (bc != NULL && NZV(label))
1502 geany_menu_item_set_label(menu_item, label);
1503 gtk_widget_show_all(menu_item);
1504 vis |= TRUE;
1506 else
1507 gtk_widget_hide_all(menu_item);
1509 else
1511 GtkWidget *image;
1512 exec_running = run_info[cmd].pid > (GPid) 1;
1513 cmd_sensitivity = (bc != NULL) || exec_running;
1514 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1515 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1516 run_sensitivity = cmd_sensitivity;
1517 if (! exec_running)
1519 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1521 else
1523 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1525 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1526 run_running = exec_running;
1527 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1528 if (bc != NULL && NZV(label))
1530 geany_menu_item_set_label(menu_item, label);
1531 gtk_widget_show_all(menu_item);
1532 vis |= TRUE;
1534 else
1535 gtk_widget_hide_all(menu_item);
1541 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1542 && have_path && ! build_running;
1543 if (widgets.toolitem_build != NULL)
1544 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1545 can_make = FALSE;
1546 if (widgets.toolitem_make_all != NULL)
1547 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1548 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1549 && ! build_running));
1550 if (widgets.toolitem_make_custom != NULL)
1551 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1552 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1553 && ! build_running));
1554 if (widgets.toolitem_make_object != NULL)
1555 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1556 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1557 && ! build_running));
1558 if (widgets.toolitem_set_args != NULL)
1559 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1561 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1562 && have_path && ! build_running;
1563 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1564 gtk_action_set_sensitive(widgets.build_action, can_make);
1565 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1567 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1568 set_stop_button(run_running);
1573 /* Call build_menu_update() instead of calling this directly. */
1574 static void set_stop_button(gboolean stop)
1576 const gchar *button_stock_id = NULL;
1577 GtkToolButton *run_button;
1579 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1580 if (run_button != NULL)
1581 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1583 if (stop && utils_str_equal(button_stock_id, "gtk-stop"))
1584 return;
1585 if (! stop && utils_str_equal(button_stock_id, "gtk-execute"))
1586 return;
1588 /* use the run button also as stop button */
1589 if (stop)
1591 if (run_button != NULL)
1592 gtk_tool_button_set_stock_id(run_button, "gtk-stop");
1594 else
1596 if (run_button != NULL)
1597 gtk_tool_button_set_stock_id(run_button, "gtk-execute");
1602 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1604 show_build_commands_dialog();
1608 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1610 last_toolbutton_action = user_data;
1611 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1612 on_build_menu_item(menuitem, user_data);
1616 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1618 gchar *msg;
1619 gint grp, cmd;
1621 last_toolbutton_action = user_data;
1622 grp = GPOINTER_TO_GRP(user_data);
1623 cmd = GPOINTER_TO_CMD(user_data);
1624 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1625 msg = _("Build the current file with Make and the default target");
1626 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1627 msg = _("Build the current file with Make and the specified target");
1628 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1629 msg = _("Compile the current file with Make");
1630 else
1631 msg = NULL;
1632 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1633 on_build_menu_item(menuitem, user_data);
1637 static void kill_process(GPid *pid)
1639 /* Unix: SIGQUIT is not the best signal to use because it causes a core dump (this should not
1640 * perforce necessary for just killing a process). But we must use a signal which we can
1641 * ignore because the main process get it too, it is declared to ignore in main.c. */
1642 gint result;
1644 #ifdef G_OS_WIN32
1645 g_return_if_fail(*pid != NULL);
1646 result = TerminateProcess(*pid, 0);
1647 /* TerminateProcess() returns TRUE on success, for the check below we have to convert
1648 * it to FALSE (and vice versa) */
1649 result = ! result;
1650 #else
1651 g_return_if_fail(*pid > 1);
1652 result = kill(*pid, SIGQUIT);
1653 #endif
1655 if (result != 0)
1656 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), g_strerror(errno));
1657 else
1659 *pid = 0;
1660 build_menu_update(NULL);
1665 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1667 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1668 msgwin_goto_compiler_file_line))
1670 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1672 else
1673 ui_set_statusbar(FALSE, _("No more build errors."));
1677 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1679 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1680 msgwin_goto_compiler_file_line))
1682 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1684 else
1685 ui_set_statusbar(FALSE, _("No more build errors."));
1689 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1691 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1693 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1695 else
1697 on_build_menu_item(NULL, last_toolbutton_action);
1702 /*------------------------------------------------------
1704 * Create and handle the build menu configuration dialog
1706 *-------------------------------------------------------*/
1707 typedef struct RowWidgets
1709 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1710 GeanyBuildSource src;
1711 GeanyBuildSource dst;
1712 GeanyBuildCommand *cmdsrc;
1713 gint grp;
1714 gint cmd;
1715 gboolean cleared;
1716 } RowWidgets;
1719 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1721 if (GTK_IS_BUTTON(wid))
1722 gtk_button_set_label(GTK_BUTTON(wid), text);
1723 else
1724 gtk_entry_set_text(GTK_ENTRY(wid), text);
1728 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1730 RowWidgets *r = (RowWidgets*)user_data;
1731 gint src;
1732 enum GeanyBuildCmdEntries i;
1733 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1735 if (bc != NULL)
1737 r->cmdsrc = bc;
1738 r->src = src;
1739 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1741 set_build_command_entry_text(r->entries[i],
1742 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1745 else
1747 r->cmdsrc = NULL;
1748 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1750 set_build_command_entry_text(r->entries[i], "");
1753 r->cleared = TRUE;
1757 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1759 gtk_entry_set_text(regex,"");
1763 static void on_label_button_clicked(GtkWidget *wid)
1765 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1766 gchar *str = dialogs_show_input(_("Set menu item label"), NULL, old);
1768 gtk_button_set_label(GTK_BUTTON(wid), str);
1769 g_free(str);
1773 /* Column headings, array NULL-terminated */
1774 static gchar *colheads[] =
1776 N_("Item"),
1777 N_("Label"),
1778 N_("Command"),
1779 N_("Working directory"),
1780 N_("Clear"),
1781 NULL };
1782 /* column names */
1783 #define DC_ITEM 0
1784 #define DC_ENTRIES 1
1785 #define DC_CLEAR 4
1786 #define DC_N_COL 5
1788 static const int entry_x_padding = 3;
1789 static const int entry_y_padding = 0;
1792 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, gint row,
1793 GeanyBuildSource dst, gint grp, gint cmd, gboolean dir)
1795 GtkWidget *label, *clear, *clearicon;
1796 RowWidgets *roww;
1797 GeanyBuildCommand *bc;
1798 gint src;
1799 enum GeanyBuildCmdEntries i;
1800 gint column = 0;
1802 label = gtk_label_new(g_strdup_printf("%d:", cmd + 1));
1803 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1804 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1805 roww = g_new0(RowWidgets, 1);
1806 roww->src = GEANY_BCS_COUNT;
1807 roww->grp = grp;
1808 roww->cmd = cmd;
1809 roww->dst = dst;
1810 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1812 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1814 column += 1;
1815 if (i == GEANY_BC_LABEL)
1817 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1818 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1819 ui_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1820 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), NULL);
1822 else
1823 roww->entries[i] = gtk_entry_new();
1824 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1825 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1827 column++;
1828 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1829 clear = gtk_button_new();
1830 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1831 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1832 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1833 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1834 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1835 if (bc != NULL)
1836 roww->src = src;
1838 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1840 gchar *str = "";
1841 if (bc != NULL && (str = bc->entries[i]) == NULL)
1842 str = "";
1843 set_build_command_entry_text(roww->entries[i], str);
1845 if (bc != NULL && (src > (gint)dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1847 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1848 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1849 gtk_widget_set_sensitive(clear, FALSE);
1851 return roww;
1855 typedef struct TableFields
1857 RowWidgets **rows;
1858 GtkWidget *fileregex;
1859 GtkWidget *nonfileregex;
1860 gchar **fileregexstring;
1861 gchar **nonfileregexstring;
1862 } TableFields;
1865 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, TableData *table_data,
1866 GeanyFiletype *ft)
1868 GtkWidget *label, *sep, *clearicon, *clear;
1869 TableFields *fields;
1870 GtkTable *table;
1871 gchar **ch, *txt;
1872 gint col, row, cmdindex, cmd;
1873 gint src;
1874 gboolean sensitivity;
1875 gint sep_padding = entry_y_padding + 3;
1877 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1878 fields = g_new0(TableFields, 1);
1879 fields->rows = g_new0(RowWidgets*, build_items_count);
1880 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1882 label = gtk_label_new(gettext(*ch));
1883 gtk_table_attach(table, label, col, col + 1, 0, 1,
1884 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1886 sep = gtk_hseparator_new();
1887 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1888 entry_x_padding, sep_padding);
1889 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1890 txt = g_strdup_printf(_("%s Commands"), ft->title);
1891 else
1892 txt = g_strdup_printf(_("%s Commands"), _("No Filetype"));
1893 label = ui_label_new_bold(txt);
1894 g_free(txt);
1895 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1896 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1897 entry_x_padding, entry_y_padding);
1898 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1899 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1900 label = gtk_label_new(_("Error Regular Expression:"));
1901 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1902 entry_x_padding, entry_y_padding);
1903 fields->fileregex = gtk_entry_new();
1904 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
1905 sensitivity = (ft == NULL) ? FALSE : TRUE;
1906 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1908 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1909 if (src > (gint)dst)
1910 sensitivity = FALSE;
1912 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1913 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1914 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1915 clear = gtk_button_new();
1916 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1917 g_signal_connect_swapped(clear, "clicked",
1918 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
1919 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
1920 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1921 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
1922 gtk_widget_set_sensitive(clear, sensitivity);
1923 ++row;
1924 sep = gtk_hseparator_new();
1925 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1926 entry_x_padding, sep_padding);
1927 ++row;
1928 label = ui_label_new_bold(_("Non-Filetype Commands"));
1929 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1930 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1931 entry_x_padding, entry_y_padding);
1932 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
1933 fields->rows[cmdindex] = build_add_dialog_row(
1934 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
1935 label = gtk_label_new(_("Error Regular Expression:"));
1936 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
1937 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1938 fields->nonfileregex = gtk_entry_new();
1939 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
1940 sensitivity = TRUE;
1941 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
1943 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
1944 sensitivity = src > (gint)dst ? FALSE : TRUE;
1946 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1947 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1948 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1949 clear = gtk_button_new();
1950 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1951 g_signal_connect_swapped(clear, "clicked",
1952 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
1953 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
1954 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1955 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
1956 gtk_widget_set_sensitive(clear, sensitivity);
1957 ++row;
1958 label = gtk_label_new(_("Note: Item 2 opens a dialog and appends the response to the command."));
1959 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1960 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1961 entry_x_padding, entry_y_padding);
1962 ++row;
1963 sep = gtk_hseparator_new();
1964 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1965 entry_x_padding, sep_padding);
1966 ++row;
1967 label = ui_label_new_bold(_("Execute Commands"));
1968 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1969 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1970 entry_x_padding, entry_y_padding);
1971 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
1972 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
1973 sep = gtk_hseparator_new();
1974 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1975 entry_x_padding, sep_padding);
1976 ++row;
1977 label = gtk_label_new(
1978 _("%d, %e, %f, %p are substituted in command and directory fields, see manual for details."));
1979 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1980 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1981 entry_x_padding, entry_y_padding);
1982 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
1983 ++row;
1984 *table_data = fields;
1985 return GTK_WIDGET(table);
1989 void build_free_fields(TableData table_data)
1991 gint cmdindex;
1992 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
1993 g_free(table_data->rows[cmdindex]);
1994 g_free(table_data->rows);
1995 g_free(table_data);
1999 /* string compare where null pointers match null or 0 length strings */
2000 static int stcmp(const gchar *a, const gchar *b)
2002 if (a == NULL && b == NULL)
2003 return 0;
2004 if (a == NULL && b != NULL)
2005 return strlen(b);
2006 if (a != NULL && b == NULL)
2007 return strlen(a);
2008 return strcmp(a, b);
2012 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2014 if (GTK_IS_BUTTON(wid))
2015 return gtk_button_get_label(GTK_BUTTON(wid));
2016 else
2017 return gtk_entry_get_text(GTK_ENTRY(wid));
2021 static gboolean read_row(BuildDestination *dst, TableData table_data, gint drow, gint grp, gint cmd)
2023 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2024 gboolean changed = FALSE;
2025 GeanyBuildSource src;
2026 enum GeanyBuildCmdEntries i;
2028 src = table_data->rows[drow]->src;
2030 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2032 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2034 if (table_data->rows[drow]->cleared)
2036 if (dst->dst[grp] != NULL)
2038 if (*(dst->dst[grp]) == NULL)
2039 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2040 (*(dst->dst[grp]))[cmd].exists = FALSE;
2041 (*(dst->dst[grp]))[cmd].changed = TRUE;
2042 changed = TRUE;
2045 if (
2047 table_data->rows[drow]->cmdsrc == NULL /* originally there was no content */
2050 NZV(entries[GEANY_BC_LABEL]) /* but now one field has some */
2051 || NZV(entries[GEANY_BC_COMMAND])
2052 || NZV(entries[GEANY_BC_WORKING_DIR])
2057 table_data->rows[drow]->cmdsrc != NULL /* originally there was content */
2059 ( /* and some of it was changed */
2060 stcmp(entries[GEANY_BC_LABEL], table_data->rows[drow]->cmdsrc->entries[GEANY_BC_LABEL]) != 0
2061 || stcmp(entries[GEANY_BC_COMMAND], table_data->rows[drow]->cmdsrc->entries[GEANY_BC_COMMAND]) != 0
2062 || stcmp(entries[GEANY_BC_WORKING_DIR],
2063 table_data->rows[drow]->cmdsrc->entries[GEANY_BC_WORKING_DIR]) != 0
2068 if (dst->dst[grp] != NULL)
2070 if (*(dst->dst[grp]) == NULL)
2071 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2072 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2073 setptr((*(dst->dst[grp]))[cmd].entries[i], entries[i]);
2074 (*(dst->dst[grp]))[cmd].exists = TRUE;
2075 (*(dst->dst[grp]))[cmd].changed = TRUE;
2076 changed = TRUE;
2079 else
2081 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2082 g_free(entries[i]);
2084 return changed;
2088 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2090 gboolean changed = FALSE;
2091 gchar *reg = g_strdup(gtk_entry_get_text(GTK_ENTRY(regexentry)));
2092 if (
2094 (src == NULL /* originally there was no regex */
2095 || *src == NULL /* or it was NULL*/
2097 && NZV(reg) > 0 /* and something was typed */
2099 ||(src != NULL /* originally there was a regex*/
2100 && strcmp(*src, reg) != 0 /* and it has been changed */
2104 if (dst != NULL)
2106 setptr(*dst, reg);
2107 changed = TRUE;
2110 return changed;
2114 gboolean build_read_commands(BuildDestination *dst, TableData table_data, gint response)
2116 gint cmdindex, cmd;
2117 gboolean changed = FALSE;
2119 if (response == GTK_RESPONSE_ACCEPT)
2121 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2122 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2123 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2124 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2125 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2126 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2127 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2128 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2130 return changed;
2134 static void show_build_commands_dialog(void)
2136 GtkWidget *dialog, *table, *vbox;
2137 GeanyDocument *doc = document_get_current();
2138 GeanyFiletype *ft = NULL;
2139 gchar *title = _("Set Build Commands");
2140 gint response;
2141 TableData table_data;
2142 BuildDestination prefdsts;
2144 if (doc != NULL)
2145 ft = doc->file_type;
2146 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2147 GTK_DIALOG_DESTROY_WITH_PARENT,
2148 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2149 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2150 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2151 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2152 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2153 gtk_widget_show_all(dialog);
2154 /* run modally to prevent user changing idx filetype */
2155 response = gtk_dialog_run(GTK_DIALOG(dialog));
2157 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2158 if (ft != NULL)
2160 prefdsts.dst[GEANY_GBG_FT] = &(ft->homefilecmds);
2161 prefdsts.fileregexstr = &(ft->homeerror_regex_string);
2162 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->homeexeccmds);
2164 else
2166 prefdsts.dst[GEANY_GBG_FT] = NULL;
2167 prefdsts.fileregexstr = NULL;
2168 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2170 prefdsts.nonfileregexstr = &regex_pref;
2171 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2172 ft->home_save_needed = TRUE;
2173 build_free_fields(table_data);
2175 build_menu_update(doc);
2176 gtk_widget_destroy(dialog);
2180 /* Creates the relevant build menu if necessary. */
2181 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2183 BuildMenuItems *items;
2185 items = &menu_items;
2186 if (items->menu == NULL)
2187 create_build_menu(items);
2188 return items;
2192 /* set non_ft working directory entries to %p for project */
2193 void build_set_non_ft_wd_to_proj(TableData table_data)
2195 gint i, start, end;
2196 start = build_groups_count[GEANY_GBG_FT];
2197 end = start + build_groups_count[GEANY_GBG_NON_FT];
2198 for (i = start; i < end; ++i)
2199 gtk_entry_set_text(GTK_ENTRY(table_data->rows[i]->entries[GEANY_BC_WORKING_DIR]), "%p");
2203 /*----------------------------------------------------------
2205 * Load and store configuration
2207 * ---------------------------------------------------------*/
2208 static const gchar *build_grp_name = "build-menu";
2210 /* config format for build-menu group is prefix_gg_nn_xx=value
2211 * where gg = FT, NF, EX for the command group
2212 * nn = 2 digit command number
2213 * xx = LB for label, CM for command and WD for working dir */
2214 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2215 static const gchar *fixedkey="xx_xx_xx";
2217 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2218 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2219 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2221 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2222 gchar *prefix, gboolean loc)
2224 gint cmd, prefixlen; /* NOTE prefixlen used in macros above */
2225 GeanyBuildCommand *dstcmd;
2226 gchar *key;
2227 static gchar cmdbuf[3] = " ";
2229 if (*dst == NULL)
2230 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2231 dstcmd = *dst;
2232 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2233 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2234 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2236 gchar *label;
2237 if (cmd < 0 || cmd >= 100)
2238 return; /* ensure no buffer overflow */
2239 sprintf(cmdbuf, "%02d", cmd);
2240 set_key_grp(key, groups[grp]);
2241 set_key_cmd(key, cmdbuf);
2242 set_key_fld(key, "LB");
2243 if (loc)
2244 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2245 else
2246 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2247 if (label != NULL)
2249 dstcmd[cmd].exists = TRUE;
2250 setptr(dstcmd[cmd].entries[GEANY_BC_LABEL], label);
2251 set_key_fld(key,"CM");
2252 setptr(dstcmd[cmd].entries[GEANY_BC_COMMAND],
2253 g_key_file_get_string(config, build_grp_name, key, NULL));
2254 set_key_fld(key,"WD");
2255 setptr(dstcmd[cmd].entries[GEANY_BC_WORKING_DIR],
2256 g_key_file_get_string(config, build_grp_name, key, NULL));
2258 else dstcmd[cmd].exists = FALSE;
2260 g_free(key);
2264 /* for the specified source load new format build menu items or try to make some sense of
2265 * old format setings, not done perfectly but better than ignoring them */
2266 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2268 /*gint grp;*/
2269 GeanyFiletype *ft;
2270 GeanyProject *pj;
2271 gchar **ftlist;
2272 gchar *value, *basedir, *makebasedir;
2273 gboolean bvalue = FALSE;
2275 if (g_key_file_has_group(config, build_grp_name))
2277 switch (src)
2279 case GEANY_BCS_FT:
2280 ft = (GeanyFiletype*)p;
2281 if (ft == NULL)
2282 return;
2283 build_load_menu_grp(config, &(ft->filecmds), GEANY_GBG_FT, NULL, TRUE);
2284 build_load_menu_grp(config, &(ft->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2285 build_load_menu_grp(config, &(ft->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2286 setptr(ft->error_regex_string,
2287 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2288 break;
2289 case GEANY_BCS_HOME_FT:
2290 ft = (GeanyFiletype*)p;
2291 if (ft == NULL)
2292 return;
2293 build_load_menu_grp(config, &(ft->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2294 build_load_menu_grp(config, &(ft->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2295 setptr(ft->homeerror_regex_string,
2296 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2297 break;
2298 case GEANY_BCS_PREF:
2299 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2300 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2301 setptr(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2302 break;
2303 case GEANY_BCS_PROJ:
2304 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2305 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2306 setptr(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2307 pj = (GeanyProject*)p;
2308 if (p == NULL)
2309 return;
2310 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2311 if (ftlist != NULL)
2313 gchar **ftname;
2314 if (pj->build_filetypes_list == NULL)
2315 pj->build_filetypes_list = g_ptr_array_new();
2316 g_ptr_array_set_size(pj->build_filetypes_list, 0);
2317 for (ftname = ftlist; *ftname != NULL; ++ftname)
2319 ft = filetypes_lookup_by_name(*ftname);
2320 if (ft != NULL)
2322 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2323 g_ptr_array_add(pj->build_filetypes_list, ft);
2324 setptr(ft->projerror_regex_string,
2325 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2326 g_free(regkey);
2327 build_load_menu_grp(config, &(ft->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2328 build_load_menu_grp(config, &(ft->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2331 g_free(ftlist);
2333 break;
2334 default: /* defaults don't load from config, see build_init */
2335 break;
2339 /* load old [build_settings] values if there is no value defined by [build-menu] */
2341 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2342 #define ASSIGNIF(type, id, string, value) \
2343 if (NZV(value) && ! type[GBO_TO_CMD(id)].exists) { \
2344 type[GBO_TO_CMD(id)].exists = TRUE; \
2345 setptr(type[GBO_TO_CMD(id)].entries[GEANY_BC_LABEL], g_strdup(_(string))); \
2346 setptr(type[GBO_TO_CMD(id)].entries[GEANY_BC_COMMAND], (value)); \
2347 setptr(type[GBO_TO_CMD(id)].entries[GEANY_BC_WORKING_DIR], NULL); \
2348 type[GBO_TO_CMD(id)].old = TRUE; \
2349 } else \
2350 g_free(value);
2352 switch (src)
2354 case GEANY_BCS_FT:
2355 ft = (GeanyFiletype*)p;
2356 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2357 if (value != NULL)
2359 if (ft->filecmds == NULL)
2360 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2361 ASSIGNIF(ft->filecmds, GEANY_GBO_COMPILE, "_Compile", value);
2363 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2364 if (value != NULL)
2366 if (ft->filecmds == NULL)
2367 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2368 ASSIGNIF(ft->filecmds, GEANY_GBO_BUILD, "_Build", value);
2370 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2371 if (value != NULL)
2373 if (ft->execcmds == NULL)
2374 ft->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2375 ASSIGNIF(ft->execcmds, GEANY_GBO_EXEC, "_Execute", value);
2377 if (ft->error_regex_string == NULL)
2378 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2379 break;
2380 case GEANY_BCS_PROJ:
2381 if (non_ft_pref == NULL)
2382 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2383 basedir = project_get_base_path();
2384 if (basedir == NULL)
2385 basedir = g_strdup("%d");
2386 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2387 if (bvalue)
2388 makebasedir = g_strdup(basedir);
2389 else
2390 makebasedir = g_strdup("%d");
2391 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2392 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].entries[GEANY_BC_WORKING_DIR], g_strdup(makebasedir));
2393 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2394 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].entries[GEANY_BC_WORKING_DIR], g_strdup(makebasedir));
2395 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2396 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].entries[GEANY_BC_WORKING_DIR], g_strdup("%d"));
2397 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2398 if (NZV(value))
2400 if (exec_proj == NULL)
2401 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2402 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2404 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2405 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].entries[GEANY_BC_LABEL], g_strdup(_("_Execute")));
2406 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].entries[GEANY_BC_COMMAND], value);
2407 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].entries[GEANY_BC_WORKING_DIR], g_strdup(basedir));
2408 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2411 g_free(makebasedir);
2412 g_free(basedir);
2413 break;
2414 case GEANY_BCS_PREF:
2415 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2416 if (value != NULL)
2418 if (non_ft_pref == NULL)
2419 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2420 ASSIGNIF(non_ft_pref, GEANY_GBO_CUSTOM, "Make Custom _Target",
2421 g_strdup_printf("%s ", value));
2422 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_OBJECT, "Make _Object",
2423 g_strdup_printf("%s %%e.o",value));
2424 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_ALL, "_Make", value);
2426 break;
2427 default:
2428 break;
2433 static gint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2435 gint cmd, prefixlen; /* NOTE prefixlen used in macros above */
2436 gchar *key;
2437 gint count = 0;
2438 enum GeanyBuildCmdEntries i;
2440 if (src == NULL)
2441 return 0;
2442 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2443 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2444 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2446 if (src[cmd].exists) ++count;
2447 if (src[cmd].changed)
2449 static gchar cmdbuf[4] = " ";
2450 if (cmd < 0 || cmd >= 100)
2451 return count; /* ensure no buffer overflow */
2452 sprintf(cmdbuf, "%02d", cmd);
2453 set_key_grp(key, groups[grp]);
2454 set_key_cmd(key, cmdbuf);
2455 if (src[cmd].exists)
2457 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2459 set_key_fld(key, config_keys[i]);
2460 g_key_file_set_string(config, build_grp_name, key, src[cmd].entries[i]);
2463 else
2465 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2467 set_key_fld(key, config_keys[i]);
2468 g_key_file_remove_key(config, build_grp_name, key, NULL);
2473 g_free(key);
2474 return count;
2478 typedef struct ForEachData
2480 GKeyFile *config;
2481 GPtrArray *ft_names;
2482 } ForEachData;
2485 static void foreach_project_filetype(gpointer data, gpointer user_data)
2487 GeanyFiletype *ft = (GeanyFiletype*) data;
2488 ForEachData *d = (ForEachData*) user_data;
2489 gint i = 0;
2490 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2492 i += build_save_menu_grp(d->config, ft->projfilecmds, GEANY_GBG_FT, ft->name);
2493 i += build_save_menu_grp(d->config, ft->projexeccmds, GEANY_GBG_EXEC, ft->name);
2494 if (NZV(ft->projerror_regex_string))
2496 g_key_file_set_string(d->config, build_grp_name, regkey, ft->projerror_regex_string);
2497 i++;
2499 else
2500 g_key_file_remove_key(d->config, build_grp_name, regkey, NULL);
2501 g_free(regkey);
2502 if (i > 0)
2503 g_ptr_array_add(d->ft_names, ft->name);
2507 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2509 GeanyFiletype *ft;
2510 GeanyProject *pj;
2511 ForEachData data;
2512 gchar *regkey;
2514 switch (src)
2516 case GEANY_BCS_HOME_FT:
2517 ft = (GeanyFiletype*)ptr;
2518 if (ft == NULL)
2519 return;
2520 build_save_menu_grp(config, ft->homefilecmds, GEANY_GBG_FT, NULL);
2521 build_save_menu_grp(config, ft->homeexeccmds, GEANY_GBG_EXEC, NULL);
2522 regkey = g_strdup_printf("%serror_regex", ft->name);
2523 if (NZV(ft->homeerror_regex_string))
2524 g_key_file_set_string(config, build_grp_name, regkey, ft->projerror_regex_string);
2525 else
2526 g_key_file_remove_key(config, build_grp_name, regkey, NULL);
2527 g_free(regkey);
2528 break;
2529 case GEANY_BCS_PREF:
2530 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2531 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2532 if (NZV(regex_pref))
2533 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2534 else
2535 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2536 break;
2537 case GEANY_BCS_PROJ:
2538 pj = (GeanyProject*)ptr;
2539 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2540 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2541 if (NZV(regex_proj))
2542 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2543 else
2544 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2545 if (pj->build_filetypes_list != NULL)
2547 data.config = config;
2548 data.ft_names = g_ptr_array_new();
2549 g_ptr_array_foreach(pj->build_filetypes_list, foreach_project_filetype, (gpointer)(&data));
2550 if (data.ft_names->pdata != NULL)
2551 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2552 (const gchar**)(data.ft_names->pdata), data.ft_names->len);
2553 else
2554 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2555 g_ptr_array_free(data.ft_names, TRUE);
2557 break;
2558 default: /* defaults and GEANY_BCS_FT can't save */
2559 break;
2564 void build_set_group_count(GeanyBuildGroup grp, gint count)
2566 gint i, sum;
2567 if (count > build_groups_count[grp])
2568 build_groups_count[grp] = count;
2569 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2570 sum += build_groups_count[i];
2571 build_items_count = sum;
2575 gint build_get_group_count(GeanyBuildGroup grp)
2577 return build_groups_count[grp];
2581 static struct
2583 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2584 GeanyBuildCommand **ptr;
2585 gint index;
2586 } default_cmds[] = {
2587 { {N_("_Make"), "make", NULL}, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2588 { {N_("Make Custom _Target"), "make ", NULL}, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2589 { {N_("Make _Object"), "make %e.o", NULL }, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2590 { {N_("_Execute"), "./%e", NULL }, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2591 { {NULL, NULL, NULL}, NULL, 0 }
2595 void build_init(void)
2597 GtkWidget *item;
2598 GtkWidget *toolmenu;
2599 gint cmdindex;
2601 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2602 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2603 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2604 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2605 for (cmdindex = 0; default_cmds[cmdindex].entries[GEANY_BC_COMMAND] != NULL; ++cmdindex)
2607 enum GeanyBuildCmdEntries k;
2608 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2609 cmd->exists = TRUE;
2610 for (k = 0; k < GEANY_BC_CMDENTRIES_COUNT; k++)
2612 cmd->entries[k] = g_strdup(default_cmds[cmdindex].entries[k]);
2616 /* create the toolbar Build item sub menu */
2617 toolmenu = gtk_menu_new();
2618 g_object_ref(toolmenu);
2620 /* build the code */
2621 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2622 gtk_widget_show(item);
2623 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2624 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2625 GBO_TO_POINTER(GEANY_GBO_BUILD));
2626 widgets.toolitem_build = item;
2628 item = gtk_separator_menu_item_new();
2629 gtk_widget_show(item);
2630 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2632 /* build the code with make all */
2633 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2634 gtk_widget_show(item);
2635 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2636 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2637 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2638 widgets.toolitem_make_all = item;
2640 /* build the code with make custom */
2641 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target"));
2642 gtk_widget_show(item);
2643 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2644 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2645 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2646 widgets.toolitem_make_custom = item;
2648 /* build the code with make object */
2649 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2650 gtk_widget_show(item);
2651 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2652 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2653 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2654 widgets.toolitem_make_object = item;
2656 item = gtk_separator_menu_item_new();
2657 gtk_widget_show(item);
2658 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2660 /* arguments */
2661 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Menu Commands"));
2662 gtk_widget_show(item);
2663 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2664 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2665 widgets.toolitem_set_args = item;
2668 /* get toolbar action pointers */
2669 widgets.build_action = toolbar_get_action_by_name("Build");
2670 widgets.compile_action = toolbar_get_action_by_name("Compile");
2671 widgets.run_action = toolbar_get_action_by_name("Run");
2672 widgets.toolmenu = toolmenu;
2673 /* set the submenu to the toolbar item */
2674 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);