If only given an input filename and no match string, match all changelog entries.
[geany-mirror.git] / src / build.c
bloba82b2cefe9185e38ccb272775bf30c13fe522729
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.
28 /* TODO: tidy code:
29 * Use intermediate pointers for common subexpressions.
30 * Replace defines with enums.
31 * Other TODOs in code. */
33 #include "geany.h"
34 #include "build.h"
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <glib/gstdio.h>
43 #ifdef G_OS_UNIX
44 # include <sys/types.h>
45 # include <sys/wait.h>
46 # include <signal.h>
47 #else
48 # include <windows.h>
49 #endif
51 #include "prefs.h"
52 #include "support.h"
53 #include "document.h"
54 #include "utils.h"
55 #include "ui_utils.h"
56 #include "dialogs.h"
57 #include "msgwindow.h"
58 #include "filetypes.h"
59 #include "keybindings.h"
60 #include "vte.h"
61 #include "project.h"
62 #include "editor.h"
63 #include "win32.h"
64 #include "toolbar.h"
65 #include "geanymenubuttonaction.h"
67 /* Number of editor indicators to draw - limited as this can affect performance */
68 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
71 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
73 static gchar *current_dir_entered = NULL;
75 typedef struct RunInfo
77 GPid pid;
78 gint file_type_id;
79 } RunInfo;
81 static RunInfo *run_info;
83 #ifdef G_OS_WIN32
84 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script.bat";
85 #else
86 static const gchar RUN_SCRIPT_CMD[] = "./geany_run_script.sh";
87 #endif
89 /* pack group (<8) and command (<32) into a user_data pointer */
90 #define GRP_CMD_TO_POINTER(grp, cmd) GINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
91 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
92 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_INT(gptr)&0x1f)
93 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_INT(gptr)&0xe0) >> 5)
95 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
97 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
99 static struct
101 GtkAction *run_action;
102 GtkAction *compile_action;
103 GtkAction *build_action;
104 GtkWidget *toolmenu;
106 GtkWidget *toolitem_build;
107 GtkWidget *toolitem_make_all;
108 GtkWidget *toolitem_make_custom;
109 GtkWidget *toolitem_make_object;
110 GtkWidget *toolitem_set_args;
112 widgets;
114 static gint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
115 static gint build_items_count = 9;
117 #ifndef G_OS_WIN32
118 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data);
119 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data);
120 #endif
121 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose);
122 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
123 static void set_stop_button(gboolean stop);
124 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
125 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
126 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
127 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
128 static void kill_process(GPid *pid);
129 static void show_build_result_message(gboolean failure);
130 static void process_build_output_line(const gchar *line, gint color);
131 static void show_build_commands_dialog(void);
134 void build_finalize(void)
136 g_free(build_info.dir);
137 g_free(build_info.custom_target);
139 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
140 gtk_widget_destroy(menu_items.menu);
144 /* note: copied from keybindings.c, may be able to go away */
145 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
146 GtkAccelGroup *accel_group, GtkWidget *menuitem)
148 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
150 if (kb->key != 0)
151 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
152 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
156 /* convenience routines to access parts of GeanyBuildCommand */
157 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
159 switch (id)
161 case GEANY_BC_LABEL:
162 return bc->label;
163 case GEANY_BC_COMMAND:
164 return bc->command;
165 case GEANY_BC_WORKING_DIR:
166 return bc->working_dir;
168 g_assert(0);
169 return NULL;
173 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
175 switch (id)
177 case GEANY_BC_LABEL:
178 setptr(bc->label, str);
179 break;
180 case GEANY_BC_COMMAND:
181 setptr(bc->command, str);
182 break;
183 case GEANY_BC_WORKING_DIR:
184 setptr(bc->working_dir, str);
185 break;
186 default:
187 g_assert(0);
192 static const gchar *config_keys[] = {
193 [GEANY_BC_LABEL] = "LB",
194 [GEANY_BC_COMMAND] = "CM",
195 [GEANY_BC_WORKING_DIR] = "WD"
198 /*-----------------------------------------------------
200 * Execute commands and handle results
202 *-----------------------------------------------------*/
204 /* the various groups of commands not in the filetype struct */
205 static GeanyBuildCommand *ft_def = NULL;
206 static GeanyBuildCommand *non_ft_proj = NULL;
207 static GeanyBuildCommand *non_ft_pref = NULL;
208 static GeanyBuildCommand *non_ft_def = NULL;
209 static GeanyBuildCommand *exec_proj = NULL;
210 static GeanyBuildCommand *exec_pref = NULL;
211 static GeanyBuildCommand *exec_def = NULL;
212 /* and the regexen not in the filetype structure */
213 static gchar *regex_pref = NULL;
214 /* project non-fileregex string */
215 static gchar *regex_proj = NULL;
217 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
218 #ifndef PRINTBUILDCMDS
219 #define PRINTBUILDCMDS FALSE
220 #endif
221 static gboolean printbuildcmds = PRINTBUILDCMDS;
224 /* for debug only, print the commands structures in priority order */
225 static void printfcmds(void)
227 #if 0
228 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
229 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
230 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
231 { &ft_def, NULL, NULL, NULL, NULL, NULL },
232 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
233 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
235 GeanyFiletype *ft = NULL;
236 GeanyDocument *doc;
237 gint i, j, k, l, m;
238 enum GeanyBuildCmdEntries n;
239 gint cc[GEANY_BCS_COUNT];
240 gchar c;
242 doc = document_get_current();
243 if (doc != NULL)
244 ft = doc->file_type;
245 if (ft != NULL)
247 printf("filetype %s\n",ft->name);
248 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->filecmds);
249 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->homefilecmds);
250 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->projfilecmds);
251 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->ftdefcmds);
252 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->execcmds);
253 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->homeexeccmds);
254 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->projexeccmds);
256 for (i = 0; i < GEANY_BCS_COUNT; ++i)
258 m = 1;
259 for (j = 0; j < GEANY_GBG_COUNT; ++j)
261 for (k = 0; k < build_groups_count[j]; ++k)
262 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
264 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
266 if ((*(cl[j][i]))[k].entries[n] != NULL &&
267 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
269 m = l;
274 cc[i] = m;
276 for (i = 0; i < GEANY_GBG_COUNT; ++i)
278 for (k = 0; k < build_groups_count[i]; ++k)
280 for (l = 0; l < 2; ++l)
282 c = ' ';
283 for (j = 0; j < GEANY_BCS_COUNT; ++j)
285 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
287 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
289 if ((*(cl[i][j]))[k].entries[i] != NULL)
290 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
291 else
292 printf("%c %*.*s",c,cc[j],cc[j]," ");
295 else
296 printf("%c %*.*s",c,cc[j],cc[j]," ");
297 c = ',';
299 printf("\n");
302 printf("\n");
304 #endif
308 /* macros to save typing and make the logic visible */
309 #define return_cmd_if(src, cmds)\
310 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
312 *fr=src; \
313 if (printbuildcmds) \
314 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
315 return &(cmds[cmdindex]); \
318 #define return_ft_cmd_if(src, cmds)\
319 if (ft != NULL && ft->cmds != NULL \
320 && ft->cmds[cmdindex].exists && below>src)\
322 *fr=src; \
323 if (printbuildcmds) \
324 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
325 return &(ft->cmds[cmdindex]); \
329 /* get the next lowest command taking priority into account */
330 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, gint cmdgrp, gint cmdindex,
331 gint below, gint *from)
333 /* Note: parameter below used in macros above */
334 GeanyFiletype *ft = NULL;
335 gint sink, *fr = &sink;
337 if (printbuildcmds)
338 printfcmds();
339 if (cmdgrp >= GEANY_GBG_COUNT)
340 return NULL;
341 if (from != NULL)
342 fr = from;
343 if (doc == NULL)
344 doc = document_get_current();
345 if (doc != NULL)
346 ft = doc->file_type;
348 switch (cmdgrp)
350 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
351 if (ft != NULL)
353 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
354 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
355 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
357 return_cmd_if(GEANY_BCS_DEF, ft_def);
358 break;
359 case GEANY_GBG_NON_FT: /* order proj, pref, def */
360 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
361 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
362 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
363 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
364 break;
365 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
366 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
367 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
368 return_cmd_if(GEANY_BCS_PREF, exec_pref);
369 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
370 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
371 return_cmd_if(GEANY_BCS_DEF, exec_def);
372 break;
373 default:
374 break;
376 return NULL;
380 /* shortcut to start looking at the top */
381 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, gint grp, gint cmdindex, gint *from)
383 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
387 #define return_nonblank_regex(src, ptr)\
388 if (NZV(ptr)) \
389 { *fr = (src); return &(ptr); }
392 /* like get_build_cmd, but for regexen, used by filetypes */
393 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, gint *from)
395 gint sink, *fr = &sink;
397 if (from != NULL)
398 fr = from;
399 if (grp == GEANY_GBG_FT)
401 if (ft == NULL)
403 GeanyDocument *doc = document_get_current();
404 if (doc != NULL)
405 ft = doc->file_type;
407 if (ft == NULL)
408 return NULL;
409 return_nonblank_regex(GEANY_BCS_PROJ, ft->projerror_regex_string);
410 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->homeerror_regex_string);
411 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
413 else if (grp == GEANY_GBG_NON_FT)
415 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
416 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
418 return NULL;
422 /* get pointer to the command group array */
423 static GeanyBuildCommand *get_build_group(GeanyBuildSource src, GeanyBuildGroup grp)
425 GeanyDocument *doc;
426 GeanyFiletype *ft = NULL;
428 switch (grp)
430 case GEANY_GBG_FT:
431 if ((doc = document_get_current()) == NULL)
432 return NULL;
433 if ((ft = doc->file_type) == NULL)
434 return NULL;
436 switch (src)
438 case GEANY_BCS_DEF: return ft->ftdefcmds;
439 case GEANY_BCS_FT: return ft->filecmds;
440 case GEANY_BCS_HOME_FT: return ft->homefilecmds;
441 case GEANY_BCS_PREF: return ft->homefilecmds;
442 case GEANY_BCS_PROJ: return ft->projfilecmds;
443 default: return NULL;
445 break;
446 case GEANY_GBG_NON_FT:
447 switch (src)
449 case GEANY_BCS_DEF: return non_ft_def;
450 case GEANY_BCS_PREF: return non_ft_pref;
451 case GEANY_BCS_PROJ: return non_ft_proj;
452 default: return NULL;
454 break;
455 case GEANY_GBG_EXEC:
456 if ((doc = document_get_current()) != NULL)
457 ft = doc->file_type;
458 switch (src)
460 case GEANY_BCS_DEF: return exec_def;
461 case GEANY_BCS_FT: return ft ? ft->execcmds: NULL;
462 case GEANY_BCS_HOME_FT: return ft ? ft->homeexeccmds: NULL;
463 case GEANY_BCS_PROJ_FT: return ft ? ft->projexeccmds: NULL;
464 case GEANY_BCS_PREF: return exec_pref;
465 case GEANY_BCS_PROJ: return exec_proj;
466 default: return NULL;
468 break;
469 default:
470 return NULL;
475 /* * Remove the specified Build menu item.
477 * Makes the specified menu item configuration no longer exist. This
478 * is different to setting fields to blank because the menu item
479 * will be deleted from the configuration file on saving
480 * (except the system filetypes settings @see Build Menu Configuration
481 * section of the Manual).
483 * @param src the source of the menu item to remove.
484 * @param grp the group of the command to remove.
485 * @param cmd the index (from 0) of the command within the group. A negative
486 * value will remove the whole group.
488 * If any parameter is out of range does nothing.
490 * @see build_menu_update
492 void build_remove_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, gint cmd)
494 GeanyBuildCommand *bc;
495 gint i;
497 bc = get_build_group(src, grp);
498 if (bc == NULL)
499 return;
500 if (cmd < 0)
502 for (i = 0; i < build_groups_count[grp]; ++i)
503 bc[i].exists = FALSE;
505 else if (cmd < build_groups_count[grp])
506 bc[cmd].exists = FALSE;
510 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
512 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
513 * hidden by higher priority commands.
515 * @param src the source of the specified menu item.
516 * @param grp the group of the specified menu item.
517 * @param cmd the index of the command within the group.
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 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, gint cmd)
526 GeanyBuildCommand *bc;
528 if (src >= GEANY_BCS_COUNT || grp >= GEANY_GBG_COUNT || cmd >= build_groups_count[grp])
529 return NULL;
530 bc = get_build_group(src, grp);
531 if (bc == NULL)
532 return NULL;
533 return &(bc[cmd]);
537 /* * Get the @a GeanyBuildCommand structure for the menu item.
539 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
540 * that the menu item will use if activated.
542 * @param grp the group of the specified menu item.
543 * @param cmd the index of the command within the group.
544 * @param src pointer to @a gint to return which source provided the command. Ignored if @a NULL.
545 * Values are one of @a GeanyBuildSource but returns a signed type not the enum.
547 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
548 * This is a pointer to an internal structure and must not be freed.
550 * @see build_menu_update
552 /* parameter checked version of get_build_cmd for external interface */
553 GeanyBuildCommand *build_get_current_menu_item(GeanyBuildGroup grp, gint cmd, gint *src)
555 if (*src >= GEANY_BCS_COUNT || grp >= GEANY_GBG_COUNT || cmd >= build_groups_count[grp])
556 return NULL;
557 return get_build_cmd(NULL, grp, cmd, src);
561 /* Clear all error indicators in all documents. */
562 static void clear_errors(GeanyDocument *doc)
564 guint i;
566 for (i = 0; i < documents_array->len; i++)
568 if (documents[i]->is_valid)
569 editor_indicator_clear_errors(documents[i]->editor);
574 #ifdef G_OS_WIN32
575 static void parse_build_output(const gchar **output, gint status)
577 guint x, i, len;
578 gchar *line, **lines;
580 for (x = 0; x < 2; x++)
582 if (NZV(output[x]))
584 lines = g_strsplit_set(output[x], "\r\n", -1);
585 len = g_strv_length(lines);
587 for (i = 0; i < len; i++)
589 if (NZV(lines[i]))
591 line = lines[i];
592 while (*line != '\0')
593 { /* replace any conrol characters in the output */
594 if (*line < 32)
595 *line = 32;
596 line++;
598 process_build_output_line(lines[i], COLOR_BLACK);
601 g_strfreev(lines);
605 show_build_result_message(status != 0);
606 utils_beep();
608 build_info.pid = 0;
609 /* enable build items again */
610 build_menu_update(NULL);
612 #endif
615 /* Replaces occurences of %e and %p with the appropriate filenames,
616 * %d and %p replacements should be in UTF8 */
617 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
619 GString *stack;
620 gchar *filename = NULL;
621 gchar *replacement;
622 gchar *executable = NULL;
623 gchar *ret_str; /* to be freed when not in use anymore */
625 stack = g_string_new(src);
626 if (doc != NULL && doc->file_name != NULL)
628 filename = utils_get_utf8_from_locale(doc->file_name);
630 /* replace %f with the filename (including extension) */
631 replacement = g_path_get_basename(filename);
632 utils_string_replace_all(stack, "%f", replacement);
633 g_free(replacement);
635 /* replace %d with the absolute path of the dir of the current file */
636 replacement = g_path_get_dirname(filename);
637 utils_string_replace_all(stack, "%d", replacement);
638 g_free(replacement);
640 /* replace %e with the filename (excluding extension) */
641 executable = utils_remove_ext_from_filename(filename);
642 replacement = g_path_get_basename(executable);
643 utils_string_replace_all(stack, "%e", replacement);
644 g_free(replacement);
647 /* replace %p with the current project's (absolute) base directory */
648 replacement = NULL; /* prevent double free if no replacement found */
649 if (app->project)
651 replacement = project_get_base_path();
653 else if (strstr(stack->str, "%p"))
654 { /* fall back to %d */
655 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
656 if (doc != NULL && filename != NULL)
657 replacement = g_path_get_dirname(filename);
660 utils_string_replace_all(stack, "%p", replacement);
661 g_free(replacement);
663 ret_str = utils_get_utf8_from_locale(stack->str);
664 g_free(executable);
665 g_free(filename);
666 g_string_free(stack, TRUE);
668 return ret_str; /* don't forget to free src also if needed */
672 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
673 * idx document directory */
674 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
676 GError *error = NULL;
677 gchar **argv;
678 gchar *working_dir;
679 gchar *utf8_working_dir;
680 gchar *cmd_string;
681 gchar *utf8_cmd_string;
682 #ifdef G_OS_WIN32
683 gchar *output[2];
684 gint status;
685 #else
686 gint stdout_fd;
687 gint stderr_fd;
688 #endif
690 if (!((doc != NULL && NZV(doc->file_name)) || NZV(dir)))
692 geany_debug("Failed to run command with no working directory");
693 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
694 return (GPid) 1;
697 if (doc != NULL)
698 clear_errors(doc);
699 setptr(current_dir_entered, NULL);
701 cmd_string = g_strdup(cmd);
703 #ifdef G_OS_WIN32
704 argv = g_strsplit(cmd_string, " ", 0);
705 #else
706 argv = g_new0(gchar *, 4);
707 argv[0] = g_strdup("/bin/sh");
708 argv[1] = g_strdup("-c");
709 argv[2] = cmd_string;
710 argv[3] = NULL;
711 #endif
713 utf8_cmd_string = utils_get_utf8_from_locale(cmd_string);
714 utf8_working_dir = NZV(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
715 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
717 gtk_list_store_clear(msgwindow.store_compiler);
718 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
719 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
720 g_free(utf8_working_dir);
721 g_free(utf8_cmd_string);
723 /* set the build info for the message window */
724 g_free(build_info.dir);
725 build_info.dir = g_strdup(working_dir);
726 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
727 build_info.message_count = 0;
729 #ifdef G_OS_WIN32
730 if (! utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
731 NULL, NULL, &output[0], &output[1], &status, &error))
732 #else
733 if (! g_spawn_async_with_pipes(working_dir, argv, NULL,
734 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
735 &(build_info.pid), NULL, &stdout_fd, &stderr_fd, &error))
736 #endif
738 geany_debug("g_spawn_async_with_pipes() failed: %s", error->message);
739 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
740 g_strfreev(argv);
741 g_error_free(error);
742 g_free(working_dir);
743 error = NULL;
744 return (GPid) 0;
747 #ifdef G_OS_WIN32
748 parse_build_output((const gchar**) output, status);
749 g_free(output[0]);
750 g_free(output[1]);
751 #else
752 if (build_info.pid > 0)
754 g_child_watch_add(build_info.pid, (GChildWatchFunc) build_exit_cb, NULL);
755 build_menu_update(doc);
756 ui_progress_bar_start(NULL);
759 /* use GIOChannels to monitor stdout and stderr */
760 utils_set_up_io_channel(stdout_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
761 TRUE, build_iofunc, GINT_TO_POINTER(0));
762 utils_set_up_io_channel(stderr_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
763 TRUE, build_iofunc, GINT_TO_POINTER(1));
764 #endif
766 g_strfreev(argv);
767 g_free(working_dir);
769 return build_info.pid;
773 /* Returns: NULL if there was an error, or the working directory the script was created in.
774 * vte_cmd_nonscript is the location of a string which is filled with the command to be used
775 * when vc->skip_run_script is set, otherwise it will be set to NULL */
776 static gchar *prepare_run_script(GeanyDocument *doc, gchar **vte_cmd_nonscript, gint cmdindex)
778 gchar *locale_filename = NULL;
779 GeanyBuildCommand *cmd = NULL;
780 gchar *executable = NULL;
781 gchar *working_dir = NULL;
782 const gchar *cmd_working_dir;
783 gboolean autoclose = FALSE;
784 gboolean result = FALSE;
785 gchar *tmp;
786 gchar *cmd_string;
788 if (vte_cmd_nonscript != NULL)
789 *vte_cmd_nonscript = NULL;
791 locale_filename = utils_get_locale_from_utf8(doc->file_name);
793 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
795 cmd_string = build_replace_placeholder(doc, cmd->command);
796 cmd_working_dir = cmd->working_dir;
797 if (! NZV(cmd_working_dir))
798 cmd_working_dir = "%d";
799 working_dir = build_replace_placeholder(doc, cmd_working_dir); /* in utf-8 */
801 /* only test whether working dir exists, don't change it or else Windows support will break
802 * (gspawn-win32-helper.exe is used by GLib and must be in $PATH which means current working
803 * dir where geany.exe was started from, so we can't change it) */
804 if (!NZV(working_dir) || ! g_file_test(working_dir, G_FILE_TEST_EXISTS) ||
805 ! g_file_test(working_dir, G_FILE_TEST_IS_DIR))
807 ui_set_statusbar(TRUE, _("Failed to change the working directory to \"%s\""),
808 NZV(working_dir) ? working_dir : "<NULL>" );
809 utils_free_pointers(2, cmd_string, working_dir, NULL);
810 return NULL;
813 #ifdef HAVE_VTE
814 if (vte_info.have_vte && vc->run_in_vte)
816 if (vc->skip_run_script)
818 if (vte_cmd_nonscript != NULL)
819 *vte_cmd_nonscript = cmd_string;
821 utils_free_pointers(2, executable, locale_filename, NULL);
822 return working_dir;
824 else
825 /* don't wait for user input at the end of script when we are running in VTE */
826 autoclose = TRUE;
828 #endif
830 /* RUN_SCRIPT_CMD should be ok in UTF8 without converting in locale because it
831 * contains no umlauts */
832 tmp = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
833 result = build_create_shellscript(tmp, cmd_string, autoclose);
834 if (! result)
836 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created)"),
837 NZV(cmd_string) ? cmd_string : NULL);
840 utils_free_pointers(4, cmd_string, tmp, executable, locale_filename, NULL);
842 if (result)
843 return working_dir;
845 g_free(working_dir);
846 return NULL;
850 static GPid build_run_cmd(GeanyDocument *doc, gint cmdindex)
852 gchar *working_dir;
853 gchar *vte_cmd_nonscript = NULL;
854 GError *error = NULL;
856 if (doc == NULL || doc->file_name == NULL)
857 return (GPid) 0;
859 working_dir = prepare_run_script(doc, &vte_cmd_nonscript, cmdindex);
860 if (working_dir == NULL)
861 return (GPid) 0;
863 run_info[cmdindex].file_type_id = doc->file_type->id;
865 #ifdef HAVE_VTE
866 if (vte_info.have_vte && vc->run_in_vte)
868 gchar *vte_cmd;
870 if (vc->skip_run_script)
872 setptr(vte_cmd_nonscript, utils_get_utf8_from_locale(vte_cmd_nonscript));
873 vte_cmd = g_strconcat(vte_cmd_nonscript, "\n", NULL);
874 g_free(vte_cmd_nonscript);
876 else
877 vte_cmd = g_strconcat("\n/bin/sh ", RUN_SCRIPT_CMD, "\n", NULL);
879 /* change into current directory if it is not done by default */
880 if (! vc->follow_path)
882 /* we need to convert the working_dir back to UTF-8 because the VTE expects it */
883 gchar *utf8_working_dir = utils_get_utf8_from_locale(working_dir);
884 vte_cwd(utf8_working_dir, TRUE);
885 g_free(utf8_working_dir);
887 if (! vte_send_cmd(vte_cmd))
889 ui_set_statusbar(FALSE,
890 _("Could not execute the file in the VTE because it probably contains a command."));
891 geany_debug("Could not execute the file in the VTE because it probably contains a command.");
894 /* show the VTE */
895 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
896 gtk_widget_grab_focus(vc->vte);
897 msgwin_show_hide(TRUE);
899 run_info[cmdindex].pid = 1;
901 g_free(vte_cmd);
903 else
904 #endif
906 gchar *locale_term_cmd = NULL;
907 gchar **term_argv = NULL;
908 guint term_argv_len, i;
909 gchar **argv = NULL;
911 /* get the terminal path */
912 locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
913 /* split the term_cmd, so arguments will work too */
914 term_argv = g_strsplit(locale_term_cmd, " ", -1);
915 term_argv_len = g_strv_length(term_argv);
917 /* check that terminal exists (to prevent misleading error messages) */
918 if (term_argv[0] != NULL)
920 gchar *tmp = term_argv[0];
921 /* g_find_program_in_path checks whether tmp exists and is executable */
922 term_argv[0] = g_find_program_in_path(tmp);
923 g_free(tmp);
925 if (term_argv[0] == NULL)
927 ui_set_statusbar(TRUE,
928 _("Could not find terminal \"%s\" "
929 "(check path for Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
930 run_info[cmdindex].pid = (GPid) 1;
931 goto free_strings;
934 argv = g_new0(gchar *, term_argv_len + 3);
935 for (i = 0; i < term_argv_len; i++)
937 argv[i] = g_strdup(term_argv[i]);
939 #ifdef G_OS_WIN32
940 /* command line arguments only for cmd.exe */
941 if (strstr(argv[0], "cmd.exe") != NULL)
943 argv[term_argv_len] = g_strdup("/Q /C");
944 argv[term_argv_len + 1] = g_strdup(RUN_SCRIPT_CMD);
946 else
948 argv[term_argv_len] = g_strdup(RUN_SCRIPT_CMD);
949 argv[term_argv_len + 1] = NULL;
951 #else
952 argv[term_argv_len ] = g_strdup("-e");
953 argv[term_argv_len + 1] = g_strconcat("/bin/sh ", RUN_SCRIPT_CMD, NULL);
954 #endif
955 argv[term_argv_len + 2] = NULL;
957 if (! g_spawn_async(working_dir, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
958 NULL, NULL, &(run_info[cmdindex].pid), &error))
960 geany_debug("g_spawn_async() failed: %s", error->message);
961 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
962 g_unlink(RUN_SCRIPT_CMD);
963 g_error_free(error);
964 error = NULL;
965 run_info[cmdindex].pid = (GPid) 0;
968 if (run_info[cmdindex].pid > 0)
970 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
971 (gpointer)&(run_info[cmdindex]));
972 build_menu_update(doc);
974 free_strings:
975 g_strfreev(argv);
976 g_strfreev(term_argv);
977 g_free(locale_term_cmd);
980 g_free(working_dir);
981 return run_info[cmdindex].pid;
985 static void process_build_output_line(const gchar *str, gint color)
987 gchar *msg, *tmp;
988 gchar *filename;
989 gint line;
991 msg = g_strdup(str);
993 g_strchomp(msg);
995 if (! NZV(msg))
997 g_free(msg);
998 return;
1001 if (build_parse_make_dir(msg, &tmp))
1003 setptr(current_dir_entered, tmp);
1005 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
1007 if (line != -1 && filename != NULL)
1009 GeanyDocument *doc = document_find_by_filename(filename);
1011 /* limit number of indicators */
1012 if (doc && editor_prefs.use_indicators &&
1013 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
1015 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
1016 line--; /* so only adjust the line number if it is greater than 0 */
1017 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
1019 build_info.message_count++;
1020 color = COLOR_RED; /* error message parsed on the line */
1022 g_free(filename);
1024 msgwin_compiler_add_string(color, msg);
1025 g_free(msg);
1029 #ifndef G_OS_WIN32
1030 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data)
1032 if (cond & (G_IO_IN | G_IO_PRI))
1034 gchar *msg;
1035 GIOStatus st;
1037 while ((st = g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL)) == G_IO_STATUS_NORMAL && msg)
1039 gint color = (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK;
1041 process_build_output_line(msg, color);
1042 g_free(msg);
1044 if (st == G_IO_STATUS_ERROR || st == G_IO_STATUS_EOF) return FALSE;
1046 if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
1047 return FALSE;
1049 return TRUE;
1051 #endif
1054 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1056 const gchar *pos;
1058 *prefix = NULL;
1060 if (string == NULL)
1061 return FALSE;
1063 if ((pos = strstr(string, "Entering directory")) != NULL)
1065 gsize len;
1066 gchar *input;
1068 /* get the start of the path */
1069 pos = strstr(string, "/");
1071 if (pos == NULL)
1072 return FALSE;
1074 input = g_strdup(pos);
1076 /* kill the ' at the end of the path */
1077 len = strlen(input);
1078 input[len - 1] = '\0';
1079 input = g_realloc(input, len); /* shorten by 1 */
1080 *prefix = input;
1082 return TRUE;
1085 if (strstr(string, "Leaving directory") != NULL)
1087 *prefix = NULL;
1088 return TRUE;
1091 return FALSE;
1095 static void show_build_result_message(gboolean failure)
1097 gchar *msg;
1099 if (failure)
1101 msg = _("Compilation failed.");
1102 msgwin_compiler_add_string(COLOR_BLUE, msg);
1103 /* If msgwindow is hidden, user will want to display it to see the error */
1104 if (! ui_prefs.msgwindow_visible)
1106 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1107 msgwin_show_hide(TRUE);
1109 else
1110 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1111 ui_set_statusbar(FALSE, "%s", msg);
1113 else
1115 msg = _("Compilation finished successfully.");
1116 msgwin_compiler_add_string(COLOR_BLUE, msg);
1117 if (! ui_prefs.msgwindow_visible ||
1118 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1119 ui_set_statusbar(FALSE, "%s", msg);
1124 #ifndef G_OS_WIN32
1125 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1127 gboolean failure = FALSE;
1129 if (WIFEXITED(status))
1131 if (WEXITSTATUS(status) != EXIT_SUCCESS)
1132 failure = TRUE;
1134 else if (WIFSIGNALED(status))
1136 /* the terminating signal: WTERMSIG (status)); */
1137 failure = TRUE;
1139 else
1140 { /* any other failure occured */
1141 failure = TRUE;
1143 show_build_result_message(failure);
1145 utils_beep();
1146 g_spawn_close_pid(child_pid);
1148 build_info.pid = 0;
1149 /* enable build items again */
1150 build_menu_update(NULL);
1151 ui_progress_bar_stop();
1153 #endif
1156 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1158 RunInfo *run_info_data = user_data;
1160 g_spawn_close_pid(child_pid);
1162 run_info_data->pid = 0;
1163 /* reset the stop button and menu item to the original meaning */
1164 build_menu_update(NULL);
1168 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1169 * fname is the full file name (including path) for the script to create */
1170 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose)
1172 FILE *fp;
1173 gchar *str;
1174 #ifdef G_OS_WIN32
1175 gchar *expanded_cmd;
1176 #endif
1178 fp = g_fopen(fname, "w");
1179 if (! fp)
1180 return FALSE;
1181 #ifdef G_OS_WIN32
1182 /* Expand environment variables like %blah%. */
1183 expanded_cmd = win32_expand_environment_variables(cmd);
1184 str = g_strdup_printf("%s\n\n%s\ndel \"%%0\"\n\npause\n", expanded_cmd, (autoclose) ? "" : "pause");
1185 g_free(expanded_cmd);
1186 #else
1187 str = g_strdup_printf(
1188 "#!/bin/sh\n\nrm $0\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1189 \n\n%s\n", cmd, (autoclose) ? "" :
1190 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1191 "dash\ndummy_var=\"\"\nread dummy_var");
1192 #endif
1194 fputs(str, fp);
1195 g_free(str);
1197 fclose(fp);
1199 return TRUE;
1203 typedef void Callback(GtkWidget *w, gpointer u);
1205 /* run the command catenating cmd_cat if present */
1206 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, gint cmd, gchar *cmd_cat)
1208 gchar *dir;
1209 gchar *full_command, *subs_command;
1210 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1211 gchar *cmdstr;
1213 if (buildcmd == NULL)
1214 return;
1216 cmdstr = buildcmd->command;
1218 if (cmd_cat != NULL)
1220 if (cmdstr != NULL)
1221 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1222 else
1223 full_command = g_strdup(cmd_cat);
1225 else
1226 full_command = cmdstr;
1228 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1229 subs_command = build_replace_placeholder(doc, full_command);
1230 build_info.grp = grp;
1231 build_info.cmd = cmd;
1232 build_spawn_cmd(doc, subs_command, dir);
1233 g_free(subs_command);
1234 g_free(dir);
1235 if (cmd_cat != NULL)
1236 g_free(full_command);
1237 build_menu_update(doc);
1242 /*----------------------------------------------------------------
1244 * Create build menu and handle callbacks (&toolbar callbacks)
1246 *----------------------------------------------------------------*/
1247 static void on_make_custom_input_response(const gchar *input)
1249 GeanyDocument *doc = document_get_current();
1251 setptr(build_info.custom_target, g_strdup(input));
1252 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1253 build_info.custom_target);
1257 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1259 GeanyDocument *doc = document_get_current();
1260 GeanyBuildCommand *bc;
1261 gint grp = GPOINTER_TO_GRP(user_data);
1262 gint cmd = GPOINTER_TO_CMD(user_data);
1264 g_signal_emit_by_name(geany_object, "build-start");
1266 if (doc && doc->changed)
1267 document_save_file(doc, FALSE);
1268 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1270 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1272 if (! dialog)
1274 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1275 _("Enter custom text here, all entered text is appended to the command."),
1276 build_info.custom_target, &on_make_custom_input_response);
1278 else
1280 gtk_widget_show(dialog);
1282 return;
1284 else if (grp == GEANY_GBG_EXEC)
1286 if (run_info[cmd].pid > (GPid) 1)
1288 kill_process(&run_info[cmd].pid);
1289 return;
1291 bc = get_build_cmd(doc, grp, cmd, NULL);
1292 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1294 gchar *uri;
1295 if (doc == NULL)
1296 return;
1297 uri = g_strconcat("file:///", g_path_skip_root(doc->file_name), NULL);
1298 utils_open_browser(uri);
1299 g_free(uri);
1302 else
1303 build_run_cmd(doc, cmd);
1305 else
1306 build_command(doc, grp, cmd, NULL);
1310 /* group codes for menu items other than the known commands
1311 * value order is important, see the following table for use */
1313 /* the rest in each group */
1314 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1315 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1316 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1317 /* the separator */
1318 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1319 /* the fixed items */
1320 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1321 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1322 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1323 #define MENU_DONE (MENU_COMMANDS + 1)
1326 static struct BuildMenuItemSpec {
1327 const gchar *stock_id;
1328 const gint key_binding;
1329 const gint build_grp;
1330 const gint build_cmd;
1331 const gchar *fix_label;
1332 Callback *cb;
1333 } build_menu_specs[] = {
1334 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1335 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1336 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1337 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1338 {NULL, -1, MENU_FT_REST,
1339 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1340 {NULL, -1, MENU_SEPARATOR,
1341 GBF_SEP_1, NULL, NULL},
1342 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1343 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1344 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1345 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1346 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1347 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1348 {NULL, -1, MENU_NON_FT_REST,
1349 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1350 {NULL, -1, MENU_SEPARATOR,
1351 GBF_SEP_2, NULL, NULL},
1352 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1353 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1354 {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1355 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1356 {NULL, -1, MENU_SEPARATOR,
1357 GBF_SEP_3, NULL, NULL},
1358 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1359 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1360 {NULL, -1, MENU_EXEC_REST,
1361 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1362 {NULL, -1, MENU_SEPARATOR,
1363 GBF_SEP_4, NULL, NULL},
1364 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1365 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1366 {NULL, -1, MENU_DONE,
1367 0, NULL, NULL}
1371 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1372 struct BuildMenuItemSpec *bs, const gchar *lbl, gint grp, gint cmd)
1374 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1376 if (bs->stock_id != NULL)
1378 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1379 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1381 gtk_widget_show(item);
1382 if (bs->key_binding >= 0)
1383 add_menu_accel(group, bs->key_binding, ag, item);
1384 gtk_container_add(GTK_CONTAINER(menu), item);
1385 if (bs->cb != NULL)
1387 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1389 menu_items.menu_item[grp][cmd] = item;
1393 static void create_build_menu(BuildMenuItems *build_menu_items)
1395 GtkWidget *menu;
1396 GtkAccelGroup *accel_group = gtk_accel_group_new();
1397 GeanyKeyGroup *keygroup = g_ptr_array_index(keybinding_groups, GEANY_KEY_GROUP_BUILD);
1398 gint i, j;
1400 menu = gtk_menu_new();
1401 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1402 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1403 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1404 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1406 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1408 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1409 if (bs->build_grp == MENU_SEPARATOR)
1411 GtkWidget *item = gtk_separator_menu_item_new();
1412 gtk_widget_show(item);
1413 gtk_container_add(GTK_CONTAINER(menu), item);
1414 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1416 else if (bs->fix_label != NULL)
1418 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1419 GBG_FIXED, bs->build_cmd);
1421 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1423 gint grp = bs->build_grp - GEANY_GBG_COUNT;
1424 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1426 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1427 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1428 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1431 else
1433 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1434 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1435 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1438 build_menu_items->menu = menu;
1439 gtk_widget_show(menu);
1440 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1444 /* portability to various GTK versions needs checking
1445 * conforms to description of gtk_accel_label as child of menu item
1446 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1447 static void geany_menu_item_set_label(GtkWidget *w, const gchar *label)
1449 GtkWidget *c = gtk_bin_get_child(GTK_BIN(w));
1451 gtk_label_set_text_with_mnemonic(GTK_LABEL(c), label);
1455 /* * Update the build menu to reflect changes in configuration or status.
1457 * Sets the labels and number of visible items to match the highest
1458 * priority configured commands. Also sets sensitivity if build commands are
1459 * running and switches executes to stop when commands are running.
1461 * @param doc The current document, if available, to save looking it up.
1462 * If @c NULL it will be looked up.
1464 * Call this after modifying any fields of a GeanyBuildCommand structure.
1466 * @see Build Menu Configuration section of the Manual.
1469 void build_menu_update(GeanyDocument *doc)
1471 gint i, cmdcount, cmd, grp;
1472 gboolean vis = FALSE;
1473 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1474 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1475 GeanyBuildCommand *bc;
1477 if (menu_items.menu == NULL)
1478 create_build_menu(&menu_items);
1479 if (doc == NULL)
1480 doc = document_get_current();
1481 have_path = doc != NULL && doc->file_name != NULL;
1482 build_running = build_info.pid > (GPid) 1;
1483 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1484 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1486 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1487 switch (bs->build_grp)
1489 case MENU_SEPARATOR:
1490 if (vis == TRUE)
1492 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1493 vis = FALSE;
1495 else
1496 gtk_widget_hide_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1497 break;
1498 case MENU_NEXT_ERROR:
1499 case MENU_PREV_ERROR:
1500 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1501 vis |= TRUE;
1502 break;
1503 case MENU_COMMANDS:
1504 vis |= TRUE;
1505 break;
1506 default: /* all configurable commands */
1507 if (bs->build_grp >= GEANY_GBG_COUNT)
1509 grp = bs->build_grp - GEANY_GBG_COUNT;
1510 cmdcount = build_groups_count[grp];
1512 else
1514 grp = bs->build_grp;
1515 cmdcount = bs->build_cmd + 1;
1517 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1519 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1520 const gchar *label;
1521 bc = get_build_cmd(doc, grp, cmd, NULL);
1522 if (bc)
1523 label = bc->label;
1524 else
1525 label = NULL;
1527 if (grp < GEANY_GBG_EXEC)
1529 cmd_sensitivity =
1530 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1531 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1532 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1533 if (bc != NULL && NZV(label))
1535 geany_menu_item_set_label(menu_item, label);
1536 gtk_widget_show_all(menu_item);
1537 vis |= TRUE;
1539 else
1540 gtk_widget_hide_all(menu_item);
1542 else
1544 GtkWidget *image;
1545 exec_running = run_info[cmd].pid > (GPid) 1;
1546 cmd_sensitivity = (bc != NULL) || exec_running;
1547 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1548 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1549 run_sensitivity = cmd_sensitivity;
1550 if (! exec_running)
1552 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1554 else
1556 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1558 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1559 run_running = exec_running;
1560 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1561 if (bc != NULL && NZV(label))
1563 geany_menu_item_set_label(menu_item, label);
1564 gtk_widget_show_all(menu_item);
1565 vis |= TRUE;
1567 else
1568 gtk_widget_hide_all(menu_item);
1574 run_sensitivity &= (doc != NULL);
1575 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1576 && have_path && ! build_running;
1577 if (widgets.toolitem_build != NULL)
1578 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1579 can_make = FALSE;
1580 if (widgets.toolitem_make_all != NULL)
1581 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1582 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1583 && ! build_running));
1584 if (widgets.toolitem_make_custom != NULL)
1585 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1586 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1587 && ! build_running));
1588 if (widgets.toolitem_make_object != NULL)
1589 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1590 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1591 && ! build_running));
1592 if (widgets.toolitem_set_args != NULL)
1593 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1595 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1596 && have_path && ! build_running;
1597 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1598 gtk_action_set_sensitive(widgets.build_action, can_make);
1599 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1601 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1602 set_stop_button(run_running);
1607 /* Call build_menu_update() instead of calling this directly. */
1608 static void set_stop_button(gboolean stop)
1610 const gchar *button_stock_id = NULL;
1611 GtkToolButton *run_button;
1613 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1614 if (run_button != NULL)
1615 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1617 if (stop && utils_str_equal(button_stock_id, "gtk-stop"))
1618 return;
1619 if (! stop && utils_str_equal(button_stock_id, "gtk-execute"))
1620 return;
1622 /* use the run button also as stop button */
1623 if (stop)
1625 if (run_button != NULL)
1626 gtk_tool_button_set_stock_id(run_button, "gtk-stop");
1628 else
1630 if (run_button != NULL)
1631 gtk_tool_button_set_stock_id(run_button, "gtk-execute");
1636 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1638 /* For now, just show the project dialog */
1639 if (app->project)
1640 project_build_properties();
1641 else
1642 show_build_commands_dialog();
1646 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1648 last_toolbutton_action = user_data;
1649 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1650 on_build_menu_item(menuitem, user_data);
1654 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1656 gchar *msg;
1657 gint grp, cmd;
1659 last_toolbutton_action = user_data;
1660 grp = GPOINTER_TO_GRP(user_data);
1661 cmd = GPOINTER_TO_CMD(user_data);
1662 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1663 msg = _("Build the current file with Make and the default target");
1664 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1665 msg = _("Build the current file with Make and the specified target");
1666 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1667 msg = _("Compile the current file with Make");
1668 else
1669 msg = NULL;
1670 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1671 on_build_menu_item(menuitem, user_data);
1675 static void kill_process(GPid *pid)
1677 /* Unix: SIGQUIT is not the best signal to use because it causes a core dump (this should not
1678 * perforce necessary for just killing a process). But we must use a signal which we can
1679 * ignore because the main process get it too, it is declared to ignore in main.c. */
1680 gint result;
1682 #ifdef G_OS_WIN32
1683 g_return_if_fail(*pid != NULL);
1684 result = TerminateProcess(*pid, 0);
1685 /* TerminateProcess() returns TRUE on success, for the check below we have to convert
1686 * it to FALSE (and vice versa) */
1687 result = ! result;
1688 #else
1689 g_return_if_fail(*pid > 1);
1690 result = kill(*pid, SIGQUIT);
1691 #endif
1693 if (result != 0)
1694 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), g_strerror(errno));
1695 else
1697 *pid = 0;
1698 build_menu_update(NULL);
1703 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1705 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1706 msgwin_goto_compiler_file_line))
1708 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1710 else
1711 ui_set_statusbar(FALSE, _("No more build errors."));
1715 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1717 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1718 msgwin_goto_compiler_file_line))
1720 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1722 else
1723 ui_set_statusbar(FALSE, _("No more build errors."));
1727 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1729 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1731 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1733 else
1735 on_build_menu_item(NULL, last_toolbutton_action);
1740 /*------------------------------------------------------
1742 * Create and handle the build menu configuration dialog
1744 *-------------------------------------------------------*/
1745 typedef struct RowWidgets
1747 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1748 GeanyBuildSource src;
1749 GeanyBuildSource dst;
1750 GeanyBuildCommand *cmdsrc;
1751 gint grp;
1752 gint cmd;
1753 gboolean cleared;
1754 gboolean used_dst;
1755 } RowWidgets;
1757 static GdkColor *insensitive_color;
1759 static void set_row_color(RowWidgets *r, GdkColor *color )
1761 enum GeanyBuildCmdEntries i;
1763 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1764 gtk_widget_modify_text(r->entries[i], GTK_STATE_NORMAL, color);
1768 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1770 if (GTK_IS_BUTTON(wid))
1771 gtk_button_set_label(GTK_BUTTON(wid), text);
1772 else
1773 gtk_entry_set_text(GTK_ENTRY(wid), text);
1777 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1779 RowWidgets *r = user_data;
1780 gint src;
1781 enum GeanyBuildCmdEntries i;
1782 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1784 if (bc != NULL)
1786 r->cmdsrc = bc;
1787 r->src = src;
1788 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1790 set_build_command_entry_text(r->entries[i],
1791 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1794 else
1796 r->cmdsrc = NULL;
1797 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1799 set_build_command_entry_text(r->entries[i], "");
1802 r->used_dst = FALSE;
1803 set_row_color(r, insensitive_color);
1804 r->cleared = TRUE;
1808 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1810 gtk_entry_set_text(regex,"");
1814 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1816 RowWidgets *r = user_data;
1817 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1818 /* FIXME: we should pass either build dialog or project dialog instead of NULL for parent */
1819 gchar *str = dialogs_show_input(_("Set menu item label"),
1820 NULL, NULL, old);
1822 if (!str)
1823 return;
1825 gtk_button_set_label(GTK_BUTTON(wid), str);
1826 g_free(str);
1827 r->used_dst = TRUE;
1828 set_row_color(r, NULL);
1832 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1834 RowWidgets *r = user_data;
1836 r->used_dst = TRUE;
1837 set_row_color(r, NULL);
1841 /* Column headings, array NULL-terminated */
1842 static const gchar *colheads[] =
1844 "#",
1845 N_("Label"),
1846 N_("Command"),
1847 N_("Working directory"),
1848 N_("Clear"),
1849 NULL };
1850 /* column names */
1851 #define DC_ITEM 0
1852 #define DC_ENTRIES 1
1853 #define DC_CLEAR 4
1854 #define DC_N_COL 5
1856 static const guint entry_x_padding = 3;
1857 static const guint entry_y_padding = 0;
1860 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1861 GeanyBuildSource dst, gint grp, gint cmd, gboolean dir)
1863 GtkWidget *label, *clear, *clearicon;
1864 RowWidgets *roww;
1865 GeanyBuildCommand *bc;
1866 gint src;
1867 enum GeanyBuildCmdEntries i;
1868 guint column = 0;
1869 gchar *text;
1871 text = g_strdup_printf("%d.", cmd + 1);
1872 label = gtk_label_new(text);
1873 g_free(text);
1874 insensitive_color = &(gtk_widget_get_style(label)->text[GTK_STATE_INSENSITIVE]);
1875 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1876 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1877 roww = g_new0(RowWidgets, 1);
1878 roww->src = GEANY_BCS_COUNT;
1879 roww->grp = grp;
1880 roww->cmd = cmd;
1881 roww->dst = dst;
1882 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1884 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1886 column += 1;
1887 if (i == GEANY_BC_LABEL)
1889 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1890 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1891 ui_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1892 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
1894 else
1896 roww->entries[i] = gtk_entry_new();
1897 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
1899 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1900 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1902 column++;
1903 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1904 clear = gtk_button_new();
1905 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1906 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1907 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1908 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1909 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1910 if (bc != NULL)
1911 roww->src = src;
1913 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1915 const gchar *str = "";
1917 if (bc != NULL )
1919 if ((str = id_to_str(bc, i)) == NULL)
1920 str = "";
1921 else if ((gint)dst == src)
1922 roww->used_dst = TRUE;
1924 set_build_command_entry_text(roww->entries[i], str);
1926 if (bc != NULL && ((gint)dst > src))
1927 set_row_color(roww, insensitive_color);
1928 if (bc != NULL && (src > (gint)dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1930 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1931 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1932 gtk_widget_set_sensitive(clear, FALSE);
1934 return roww;
1938 typedef struct BuildTableFields
1940 RowWidgets **rows;
1941 GtkWidget *fileregex;
1942 GtkWidget *nonfileregex;
1943 gchar **fileregexstring;
1944 gchar **nonfileregexstring;
1945 } BuildTableFields;
1948 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
1949 GeanyFiletype *ft)
1951 GtkWidget *label, *sep, *clearicon, *clear;
1952 BuildTableFields *fields;
1953 GtkTable *table;
1954 const gchar **ch;
1955 gchar *txt;
1956 guint col, row, cmdindex;
1957 gint cmd;
1958 gint src;
1959 gboolean sensitivity;
1960 guint sep_padding = entry_y_padding + 3;
1962 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1963 fields = g_new0(BuildTableFields, 1);
1964 fields->rows = g_new0(RowWidgets*, build_items_count);
1965 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1967 label = gtk_label_new(_(*ch));
1968 gtk_table_attach(table, label, col, col + 1, 0, 1,
1969 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1971 sep = gtk_hseparator_new();
1972 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1973 entry_x_padding, sep_padding);
1974 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1975 txt = g_strdup_printf(_("%s Commands"), ft->title);
1976 else
1977 txt = g_strdup_printf(_("%s Commands"), _("No Filetype"));
1978 label = ui_label_new_bold(txt);
1979 g_free(txt);
1980 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1981 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1982 entry_x_padding, entry_y_padding);
1983 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1984 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1985 label = gtk_label_new(_("Error Regular Expression:"));
1986 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1987 entry_x_padding, entry_y_padding);
1988 fields->fileregex = gtk_entry_new();
1989 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
1990 sensitivity = (ft == NULL) ? FALSE : TRUE;
1991 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1993 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1994 if (src > (gint)dst)
1995 sensitivity = FALSE;
1997 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1998 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1999 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2000 clear = gtk_button_new();
2001 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2002 g_signal_connect_swapped(clear, "clicked",
2003 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
2004 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2005 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2006 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
2007 gtk_widget_set_sensitive(clear, sensitivity);
2008 ++row;
2009 sep = gtk_hseparator_new();
2010 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2011 entry_x_padding, sep_padding);
2012 ++row;
2013 label = ui_label_new_bold(_("Independent Commands"));
2014 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2015 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2016 entry_x_padding, entry_y_padding);
2017 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
2018 fields->rows[cmdindex] = build_add_dialog_row(
2019 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
2020 label = gtk_label_new(_("Error Regular Expression:"));
2021 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
2022 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2023 fields->nonfileregex = gtk_entry_new();
2024 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
2025 sensitivity = TRUE;
2026 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
2028 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
2029 sensitivity = src > (gint)dst ? FALSE : TRUE;
2031 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2032 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2033 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2034 clear = gtk_button_new();
2035 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2036 g_signal_connect_swapped(clear, "clicked",
2037 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
2038 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2039 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2040 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
2041 gtk_widget_set_sensitive(clear, sensitivity);
2042 ++row;
2043 label = gtk_label_new(NULL);
2044 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2045 _("Note: Item 2 opens a dialog and appends the response to the command."));
2046 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2047 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2048 entry_x_padding, entry_y_padding);
2049 ++row;
2050 sep = gtk_hseparator_new();
2051 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2052 entry_x_padding, sep_padding);
2053 ++row;
2054 label = ui_label_new_bold(_("Execute Commands"));
2055 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2056 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2057 entry_x_padding, entry_y_padding);
2058 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
2059 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
2060 sep = gtk_hseparator_new();
2061 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2062 entry_x_padding, sep_padding);
2063 ++row;
2064 label = gtk_label_new(NULL);
2065 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2066 _("%d, %e, %f, %p are substituted in command and directory fields, see manual for details."));
2067 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2068 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2069 entry_x_padding, entry_y_padding);
2070 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2071 ++row;
2072 *table_data = fields;
2073 return GTK_WIDGET(table);
2077 void build_free_fields(BuildTableData table_data)
2079 gint cmdindex;
2081 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2082 g_free(table_data->rows[cmdindex]);
2083 g_free(table_data->rows);
2084 g_free(table_data);
2088 /* string compare where null pointers match null or 0 length strings */
2089 #if 0
2090 static gint stcmp(const gchar *a, const gchar *b)
2092 if (a == NULL && b == NULL)
2093 return 0;
2094 if (a == NULL && b != NULL)
2095 return strlen(b);
2096 if (a != NULL && b == NULL)
2097 return strlen(a);
2098 return strcmp(a, b);
2100 #endif
2103 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2105 if (GTK_IS_BUTTON(wid))
2106 return gtk_button_get_label(GTK_BUTTON(wid));
2107 else
2108 return gtk_entry_get_text(GTK_ENTRY(wid));
2112 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, gint drow, gint grp, gint cmd)
2114 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2115 gboolean changed = FALSE;
2116 GeanyBuildSource src;
2117 enum GeanyBuildCmdEntries i;
2119 src = table_data->rows[drow]->src;
2121 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2123 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2125 if (table_data->rows[drow]->cleared)
2127 if (dst->dst[grp] != NULL)
2129 if (*(dst->dst[grp]) == NULL)
2130 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2131 (*(dst->dst[grp]))[cmd].exists = FALSE;
2132 (*(dst->dst[grp]))[cmd].changed = TRUE;
2133 changed = TRUE;
2136 if (table_data->rows[drow]->used_dst == TRUE)
2138 if (dst->dst[grp] != NULL)
2140 if (*(dst->dst[grp]) == NULL)
2141 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2142 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2143 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2144 (*(dst->dst[grp]))[cmd].exists = TRUE;
2145 (*(dst->dst[grp]))[cmd].changed = TRUE;
2146 changed = TRUE;
2149 else
2151 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2152 g_free(entries[i]);
2154 return changed;
2158 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2160 gboolean changed = FALSE;
2161 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2163 if (((src == NULL /* originally there was no regex */
2164 || *src == NULL) /* or it was NULL*/
2165 && NZV(reg)) /* and something was typed */
2166 || (src != NULL /* originally there was a regex*/
2167 && strcmp(*src, reg) != 0)) /* and it has been changed */
2169 if (dst != NULL)
2171 setptr(*dst, g_strdup(reg));
2172 changed = TRUE;
2175 return changed;
2179 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2181 gint cmdindex, cmd;
2182 gboolean changed = FALSE;
2184 if (response == GTK_RESPONSE_ACCEPT)
2186 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2187 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2188 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2189 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2190 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2191 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2192 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2193 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2195 return changed;
2199 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2201 BuildDestination menu_dst;
2203 if (ft != NULL)
2205 menu_dst.dst[GEANY_GBG_FT] = &(ft->projfilecmds);
2206 menu_dst.fileregexstr = &(ft->projerror_regex_string);
2208 else
2210 menu_dst.dst[GEANY_GBG_FT] = NULL;
2211 menu_dst.fileregexstr = NULL;
2213 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2214 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2215 menu_dst.nonfileregexstr = &regex_proj;
2217 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2221 static void show_build_commands_dialog(void)
2223 GtkWidget *dialog, *table, *vbox;
2224 GeanyDocument *doc = document_get_current();
2225 GeanyFiletype *ft = NULL;
2226 const gchar *title = _("Set Build Commands");
2227 gint response;
2228 BuildTableData table_data;
2229 BuildDestination prefdsts;
2231 if (doc != NULL)
2232 ft = doc->file_type;
2233 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2234 GTK_DIALOG_DESTROY_WITH_PARENT,
2235 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2236 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2237 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2238 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2239 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2240 gtk_widget_show_all(dialog);
2241 /* run modally to prevent user changing idx filetype */
2242 response = gtk_dialog_run(GTK_DIALOG(dialog));
2244 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2245 if (ft != NULL)
2247 prefdsts.dst[GEANY_GBG_FT] = &(ft->homefilecmds);
2248 prefdsts.fileregexstr = &(ft->homeerror_regex_string);
2249 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->homeexeccmds);
2251 else
2253 prefdsts.dst[GEANY_GBG_FT] = NULL;
2254 prefdsts.fileregexstr = NULL;
2255 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2257 prefdsts.nonfileregexstr = &regex_pref;
2258 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2259 ft->home_save_needed = TRUE;
2260 build_free_fields(table_data);
2262 build_menu_update(doc);
2263 gtk_widget_destroy(dialog);
2267 /* Creates the relevant build menu if necessary. */
2268 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2270 BuildMenuItems *items;
2272 items = &menu_items;
2273 if (items->menu == NULL)
2274 create_build_menu(items);
2275 return items;
2279 /*----------------------------------------------------------
2281 * Load and store configuration
2283 * ---------------------------------------------------------*/
2284 static const gchar *build_grp_name = "build-menu";
2286 /* config format for build-menu group is prefix_gg_nn_xx=value
2287 * where gg = FT, NF, EX for the command group
2288 * nn = 2 digit command number
2289 * xx = LB for label, CM for command and WD for working dir */
2290 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2291 static const gchar *fixedkey="xx_xx_xx";
2293 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2294 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2295 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2297 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2298 gchar *prefix, gboolean loc)
2300 gint cmd, prefixlen; /* NOTE prefixlen used in macros above */
2301 GeanyBuildCommand *dstcmd;
2302 gchar *key;
2303 static gchar cmdbuf[3] = " ";
2305 if (*dst == NULL)
2306 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2307 dstcmd = *dst;
2308 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2309 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2310 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2312 gchar *label;
2313 if (cmd < 0 || cmd >= 100)
2314 return; /* ensure no buffer overflow */
2315 sprintf(cmdbuf, "%02d", cmd);
2316 set_key_grp(key, groups[grp]);
2317 set_key_cmd(key, cmdbuf);
2318 set_key_fld(key, "LB");
2319 if (loc)
2320 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2321 else
2322 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2323 if (label != NULL)
2325 dstcmd[cmd].exists = TRUE;
2326 setptr(dstcmd[cmd].label, label);
2327 set_key_fld(key,"CM");
2328 setptr(dstcmd[cmd].command,
2329 g_key_file_get_string(config, build_grp_name, key, NULL));
2330 set_key_fld(key,"WD");
2331 setptr(dstcmd[cmd].working_dir,
2332 g_key_file_get_string(config, build_grp_name, key, NULL));
2334 else dstcmd[cmd].exists = FALSE;
2336 g_free(key);
2340 /* for the specified source load new format build menu items or try to make some sense of
2341 * old format setings, not done perfectly but better than ignoring them */
2342 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2344 GeanyFiletype *ft;
2345 GeanyProject *pj;
2346 gchar **ftlist;
2347 gchar *value, *basedir, *makebasedir;
2348 gboolean bvalue = FALSE;
2350 if (g_key_file_has_group(config, build_grp_name))
2352 switch (src)
2354 case GEANY_BCS_FT:
2355 ft = (GeanyFiletype*)p;
2356 if (ft == NULL)
2357 return;
2358 build_load_menu_grp(config, &(ft->filecmds), GEANY_GBG_FT, NULL, TRUE);
2359 build_load_menu_grp(config, &(ft->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2360 build_load_menu_grp(config, &(ft->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2361 setptr(ft->error_regex_string,
2362 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2363 break;
2364 case GEANY_BCS_HOME_FT:
2365 ft = (GeanyFiletype*)p;
2366 if (ft == NULL)
2367 return;
2368 build_load_menu_grp(config, &(ft->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2369 build_load_menu_grp(config, &(ft->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2370 setptr(ft->homeerror_regex_string,
2371 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2372 break;
2373 case GEANY_BCS_PREF:
2374 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2375 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2376 setptr(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2377 break;
2378 case GEANY_BCS_PROJ:
2379 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2380 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2381 setptr(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2382 pj = (GeanyProject*)p;
2383 if (p == NULL)
2384 return;
2385 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2386 if (ftlist != NULL)
2388 gchar **ftname;
2389 if (pj->build_filetypes_list == NULL)
2390 pj->build_filetypes_list = g_ptr_array_new();
2391 g_ptr_array_set_size(pj->build_filetypes_list, 0);
2392 for (ftname = ftlist; *ftname != NULL; ++ftname)
2394 ft = filetypes_lookup_by_name(*ftname);
2395 if (ft != NULL)
2397 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2398 g_ptr_array_add(pj->build_filetypes_list, ft);
2399 setptr(ft->projerror_regex_string,
2400 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2401 g_free(regkey);
2402 build_load_menu_grp(config, &(ft->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2403 build_load_menu_grp(config, &(ft->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2406 g_free(ftlist);
2408 break;
2409 default: /* defaults don't load from config, see build_init */
2410 break;
2414 /* load old [build_settings] values if there is no value defined by [build-menu] */
2416 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2417 /* TODO: rewrite as function */
2418 #define ASSIGNIF(type, id, string, value) \
2419 if (NZV(value) && ! type[GBO_TO_CMD(id)].exists) { \
2420 type[GBO_TO_CMD(id)].exists = TRUE; \
2421 setptr(type[GBO_TO_CMD(id)].label, g_strdup(string)); \
2422 setptr(type[GBO_TO_CMD(id)].command, (value)); \
2423 setptr(type[GBO_TO_CMD(id)].working_dir, NULL); \
2424 type[GBO_TO_CMD(id)].old = TRUE; \
2425 } else \
2426 g_free(value);
2428 switch (src)
2430 case GEANY_BCS_FT:
2431 ft = (GeanyFiletype*)p;
2432 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2433 if (value != NULL)
2435 if (ft->filecmds == NULL)
2436 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2437 ASSIGNIF(ft->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2439 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2440 if (value != NULL)
2442 if (ft->filecmds == NULL)
2443 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2444 ASSIGNIF(ft->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2446 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2447 if (value != NULL)
2449 if (ft->execcmds == NULL)
2450 ft->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2451 ASSIGNIF(ft->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2453 if (ft->error_regex_string == NULL)
2454 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2455 break;
2456 case GEANY_BCS_PROJ:
2457 if (non_ft_pref == NULL)
2458 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2459 basedir = project_get_base_path();
2460 if (basedir == NULL)
2461 basedir = g_strdup("%d");
2462 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2463 if (bvalue)
2464 makebasedir = g_strdup(basedir);
2465 else
2466 makebasedir = g_strdup("%d");
2467 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2468 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
2469 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2470 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2471 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2472 setptr(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
2473 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2474 if (NZV(value))
2476 if (exec_proj == NULL)
2477 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2478 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2480 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2481 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2482 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
2483 setptr(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
2484 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2487 g_free(makebasedir);
2488 g_free(basedir);
2489 break;
2490 case GEANY_BCS_PREF:
2491 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2492 if (value != NULL)
2494 if (non_ft_pref == NULL)
2495 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2496 ASSIGNIF(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target"),
2497 g_strdup_printf("%s ", value));
2498 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2499 g_strdup_printf("%s %%e.o",value));
2500 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2502 break;
2503 default:
2504 break;
2509 static gint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2511 gint cmd, prefixlen; /* NOTE prefixlen used in macros above */
2512 gchar *key;
2513 gint count = 0;
2514 enum GeanyBuildCmdEntries i;
2516 if (src == NULL)
2517 return 0;
2518 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2519 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2520 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2522 if (src[cmd].exists) ++count;
2523 if (src[cmd].changed)
2525 static gchar cmdbuf[4] = " ";
2526 if (cmd < 0 || cmd >= 100)
2527 return count; /* ensure no buffer overflow */
2528 sprintf(cmdbuf, "%02d", cmd);
2529 set_key_grp(key, groups[grp]);
2530 set_key_cmd(key, cmdbuf);
2531 if (src[cmd].exists)
2533 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2535 set_key_fld(key, config_keys[i]);
2536 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2539 else
2541 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2543 set_key_fld(key, config_keys[i]);
2544 g_key_file_remove_key(config, build_grp_name, key, NULL);
2549 g_free(key);
2550 return count;
2554 typedef struct ForEachData
2556 GKeyFile *config;
2557 GPtrArray *ft_names;
2558 } ForEachData;
2561 static void foreach_project_filetype(gpointer data, gpointer user_data)
2563 GeanyFiletype *ft = data;
2564 ForEachData *d = user_data;
2565 gint i = 0;
2566 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2568 i += build_save_menu_grp(d->config, ft->projfilecmds, GEANY_GBG_FT, ft->name);
2569 i += build_save_menu_grp(d->config, ft->projexeccmds, GEANY_GBG_EXEC, ft->name);
2570 if (NZV(ft->projerror_regex_string))
2572 g_key_file_set_string(d->config, build_grp_name, regkey, ft->projerror_regex_string);
2573 i++;
2575 else
2576 g_key_file_remove_key(d->config, build_grp_name, regkey, NULL);
2577 g_free(regkey);
2578 if (i > 0)
2579 g_ptr_array_add(d->ft_names, ft->name);
2583 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2584 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2586 GeanyFiletype *ft;
2587 GeanyProject *pj;
2588 ForEachData data;
2590 switch (src)
2592 case GEANY_BCS_HOME_FT:
2593 ft = (GeanyFiletype*)ptr;
2594 if (ft == NULL)
2595 return;
2596 build_save_menu_grp(config, ft->homefilecmds, GEANY_GBG_FT, NULL);
2597 build_save_menu_grp(config, ft->homeexeccmds, GEANY_GBG_EXEC, NULL);
2598 if (NZV(ft->homeerror_regex_string))
2599 g_key_file_set_string(config, build_grp_name, "error_regex", ft->homeerror_regex_string);
2600 else
2601 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2602 break;
2603 case GEANY_BCS_PREF:
2604 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2605 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2606 if (NZV(regex_pref))
2607 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2608 else
2609 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2610 break;
2611 case GEANY_BCS_PROJ:
2612 pj = (GeanyProject*)ptr;
2613 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2614 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2615 if (NZV(regex_proj))
2616 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2617 else
2618 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2619 if (pj->build_filetypes_list != NULL)
2621 data.config = config;
2622 data.ft_names = g_ptr_array_new();
2623 g_ptr_array_foreach(pj->build_filetypes_list, foreach_project_filetype, (gpointer)(&data));
2624 if (data.ft_names->pdata != NULL)
2625 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2626 (const gchar**)(data.ft_names->pdata), data.ft_names->len);
2627 else
2628 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2629 g_ptr_array_free(data.ft_names, TRUE);
2631 break;
2632 default: /* defaults and GEANY_BCS_FT can't save */
2633 break;
2638 void build_set_group_count(GeanyBuildGroup grp, gint count)
2640 gint i, sum;
2642 if (count > build_groups_count[grp])
2643 build_groups_count[grp] = count;
2644 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2645 sum += build_groups_count[i];
2646 build_items_count = sum;
2650 gint build_get_group_count(GeanyBuildGroup grp)
2652 return build_groups_count[grp];
2656 static void on_project_close(void)
2658 /* remove project regexen */
2659 setptr(regex_proj, NULL);
2663 static struct
2665 const gchar *label;
2666 const gchar *command;
2667 const gchar *working_dir;
2668 GeanyBuildCommand **ptr;
2669 gint index;
2670 } default_cmds[] = {
2671 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2672 { N_("Make Custom _Target"), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2673 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2674 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2675 { NULL, NULL, NULL, NULL, 0 }
2679 void build_init(void)
2681 GtkWidget *item;
2682 GtkWidget *toolmenu;
2683 gint cmdindex;
2685 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2687 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2688 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2689 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2690 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2692 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2694 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2695 cmd->exists = TRUE;
2696 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2697 cmd->command = g_strdup(default_cmds[cmdindex].command);
2698 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2701 /* create the toolbar Build item sub menu */
2702 toolmenu = gtk_menu_new();
2703 g_object_ref(toolmenu);
2705 /* build the code */
2706 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2707 gtk_widget_show(item);
2708 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2709 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2710 GBO_TO_POINTER(GEANY_GBO_BUILD));
2711 widgets.toolitem_build = item;
2713 item = gtk_separator_menu_item_new();
2714 gtk_widget_show(item);
2715 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2717 /* build the code with make all */
2718 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2719 gtk_widget_show(item);
2720 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2721 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2722 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2723 widgets.toolitem_make_all = item;
2725 /* build the code with make custom */
2726 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target"));
2727 gtk_widget_show(item);
2728 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2729 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2730 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2731 widgets.toolitem_make_custom = item;
2733 /* build the code with make object */
2734 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2735 gtk_widget_show(item);
2736 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2737 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2738 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2739 widgets.toolitem_make_object = item;
2741 item = gtk_separator_menu_item_new();
2742 gtk_widget_show(item);
2743 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2745 /* arguments */
2746 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Menu Commands"));
2747 gtk_widget_show(item);
2748 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2749 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2750 widgets.toolitem_set_args = item;
2752 /* get toolbar action pointers */
2753 widgets.build_action = toolbar_get_action_by_name("Build");
2754 widgets.compile_action = toolbar_get_action_by_name("Compile");
2755 widgets.run_action = toolbar_get_action_by_name("Run");
2756 widgets.toolmenu = toolmenu;
2757 /* set the submenu to the toolbar item */
2758 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);