Implement the run helper as a script
[geany-mirror.git] / src / build.c
blob9ef03ce358c466db07d3e5cb0976cd082fa0d435
1 /*
2 * build.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 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 along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * Build commands and menu items.
26 /* TODO: tidy code:
27 * Use intermediate pointers for common subexpressions.
28 * Replace defines with enums.
29 * Other TODOs in code. */
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
35 #include "app.h"
36 #include "build.h"
37 #include "dialogs.h"
38 #include "document.h"
39 #include "filetypesprivate.h"
40 #include "geanymenubuttonaction.h"
41 #include "geanyobject.h"
42 #include "keybindingsprivate.h"
43 #include "msgwindow.h"
44 #include "prefs.h"
45 #include "projectprivate.h"
46 #include "sciwrappers.h"
47 #include "spawn.h"
48 #include "support.h"
49 #include "toolbar.h"
50 #include "ui_utils.h"
51 #include "utils.h"
52 #include "vte.h"
53 #include "win32.h"
55 #include "gtkcompat.h"
57 #include <stdlib.h>
58 #include <string.h>
59 #include <sys/stat.h>
60 #include <unistd.h>
61 #include <errno.h>
62 #include <glib/gstdio.h>
66 /* Number of editor indicators to draw - limited as this can affect performance */
67 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
70 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
72 static gchar *current_dir_entered = NULL;
74 typedef struct RunInfo
76 GPid pid;
77 gint file_type_id;
78 } RunInfo;
80 static RunInfo *run_info;
82 /* pack group (<8) and command (<32) into a user_data pointer */
83 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_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_UINT(gptr)&0x1f)
86 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(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 guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
108 static guint build_items_count = 9;
110 static void build_exit_cb(GPid pid, gint status, gpointer user_data);
111 static void build_iofunc(GString *string, GIOCondition condition, gpointer data);
112 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
113 static void set_stop_button(gboolean stop);
114 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
115 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
116 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
117 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
118 static void kill_process(GPid *pid);
119 static void show_build_result_message(gboolean failure);
120 static void process_build_output_line(gchar *msg, gint color);
121 static void show_build_commands_dialog(void);
122 static void on_build_menu_item(GtkWidget *w, gpointer user_data);
124 void build_finalize(void)
126 g_free(build_info.dir);
127 g_free(build_info.custom_target);
129 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
130 gtk_widget_destroy(menu_items.menu);
134 /* note: copied from keybindings.c, may be able to go away */
135 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
136 GtkAccelGroup *accel_group, GtkWidget *menuitem)
138 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
140 if (kb->key != 0)
141 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
142 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
146 /* convenience routines to access parts of GeanyBuildCommand */
147 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
149 switch (id)
151 case GEANY_BC_LABEL:
152 return bc->label;
153 case GEANY_BC_COMMAND:
154 return bc->command;
155 case GEANY_BC_WORKING_DIR:
156 return bc->working_dir;
158 g_assert(0);
159 return NULL;
163 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
165 switch (id)
167 case GEANY_BC_LABEL:
168 SETPTR(bc->label, str);
169 break;
170 case GEANY_BC_COMMAND:
171 SETPTR(bc->command, str);
172 break;
173 case GEANY_BC_WORKING_DIR:
174 SETPTR(bc->working_dir, str);
175 break;
176 default:
177 g_assert(0);
182 static const gchar *config_keys[GEANY_BC_CMDENTRIES_COUNT] = {
183 "LB", /* label */
184 "CM", /* command */
185 "WD" /* working directory */
188 /*-----------------------------------------------------
190 * Execute commands and handle results
192 *-----------------------------------------------------*/
194 /* the various groups of commands not in the filetype struct */
195 static GeanyBuildCommand *ft_def = NULL;
196 static GeanyBuildCommand *non_ft_proj = NULL;
197 static GeanyBuildCommand *non_ft_pref = NULL;
198 static GeanyBuildCommand *non_ft_def = NULL;
199 static GeanyBuildCommand *exec_proj = NULL;
200 static GeanyBuildCommand *exec_pref = NULL;
201 static GeanyBuildCommand *exec_def = NULL;
202 /* and the regexen not in the filetype structure */
203 static gchar *regex_pref = NULL;
204 /* project non-fileregex string */
205 static gchar *regex_proj = NULL;
207 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
208 #ifndef PRINTBUILDCMDS
209 #define PRINTBUILDCMDS FALSE
210 #endif
211 static gboolean printbuildcmds = PRINTBUILDCMDS;
214 /* for debug only, print the commands structures in priority order */
215 static void printfcmds(void)
217 #if 0
218 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
219 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
220 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
221 { &ft_def, NULL, NULL, NULL, NULL, NULL },
222 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
223 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
225 GeanyFiletype *ft = NULL;
226 GeanyDocument *doc;
227 gint i, j, k, l, m;
228 enum GeanyBuildCmdEntries n;
229 gint cc[GEANY_BCS_COUNT];
230 gchar c;
232 doc = document_get_current();
233 if (doc != NULL)
234 ft = doc->file_type;
235 if (ft != NULL)
237 printf("filetype %s\n",ft->name);
238 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->priv->filecmds);
239 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->priv->homefilecmds);
240 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->priv->projfilecmds);
241 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->priv->ftdefcmds);
242 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->priv->execcmds);
243 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->priv->homeexeccmds);
244 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->priv->projexeccmds);
246 for (i = 0; i < GEANY_BCS_COUNT; ++i)
248 m = 1;
249 for (j = 0; j < GEANY_GBG_COUNT; ++j)
251 for (k = 0; k < build_groups_count[j]; ++k)
252 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
254 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
256 if ((*(cl[j][i]))[k].entries[n] != NULL &&
257 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
259 m = l;
264 cc[i] = m;
266 for (i = 0; i < GEANY_GBG_COUNT; ++i)
268 for (k = 0; k < build_groups_count[i]; ++k)
270 for (l = 0; l < 2; ++l)
272 c = ' ';
273 for (j = 0; j < GEANY_BCS_COUNT; ++j)
275 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
277 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
279 if ((*(cl[i][j]))[k].entries[i] != NULL)
280 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
281 else
282 printf("%c %*.*s",c,cc[j],cc[j]," ");
285 else
286 printf("%c %*.*s",c,cc[j],cc[j]," ");
287 c = ',';
289 printf("\n");
292 printf("\n");
294 #endif
298 /* macros to save typing and make the logic visible */
299 #define return_cmd_if(src, cmds)\
300 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
302 *fr=src; \
303 if (printbuildcmds) \
304 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
305 return &(cmds[cmdindex]); \
308 #define return_ft_cmd_if(src, cmds)\
309 if (ft != NULL && ft->priv->cmds != NULL \
310 && ft->priv->cmds[cmdindex].exists && below>src)\
312 *fr=src; \
313 if (printbuildcmds) \
314 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
315 return &(ft->priv->cmds[cmdindex]); \
319 /* get the next lowest command taking priority into account */
320 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
321 guint below, guint *from)
323 /* Note: parameter below used in macros above */
324 GeanyFiletype *ft = NULL;
325 guint sink, *fr = &sink;
327 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
329 if (printbuildcmds)
330 printfcmds();
331 if (cmdgrp >= GEANY_GBG_COUNT)
332 return NULL;
333 if (from != NULL)
334 fr = from;
335 if (doc == NULL)
336 doc = document_get_current();
337 if (doc != NULL)
338 ft = doc->file_type;
340 switch (cmdgrp)
342 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
343 if (ft != NULL)
345 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
346 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
347 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
349 return_cmd_if(GEANY_BCS_DEF, ft_def);
350 break;
351 case GEANY_GBG_NON_FT: /* order proj, pref, def */
352 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
353 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
354 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
355 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
356 break;
357 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
358 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
359 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
360 return_cmd_if(GEANY_BCS_PREF, exec_pref);
361 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
362 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
363 return_cmd_if(GEANY_BCS_DEF, exec_def);
364 break;
365 default:
366 break;
368 return NULL;
372 /* shortcut to start looking at the top */
373 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
375 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
379 #define return_nonblank_regex(src, ptr)\
380 if (!EMPTY(ptr)) \
381 { *fr = (src); return &(ptr); }
384 /* like get_build_cmd, but for regexen, used by filetypes */
385 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
387 guint sink, *fr = &sink;
389 if (from != NULL)
390 fr = from;
391 if (grp == GEANY_GBG_FT)
393 if (ft == NULL)
395 GeanyDocument *doc = document_get_current();
396 if (doc != NULL)
397 ft = doc->file_type;
399 if (ft == NULL)
400 return NULL;
401 return_nonblank_regex(GEANY_BCS_PROJ, ft->priv->projerror_regex_string);
402 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->priv->homeerror_regex_string);
403 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
405 else if (grp == GEANY_GBG_NON_FT)
407 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
408 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
410 return NULL;
414 static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
416 GeanyDocument *doc;
417 GeanyFiletype *ft = NULL;
419 switch (grp)
421 case GEANY_GBG_FT:
422 if ((doc = document_get_current()) == NULL)
423 return NULL;
424 if ((ft = doc->file_type) == NULL)
425 return NULL;
426 switch (src)
428 case GEANY_BCS_DEF: return &(ft->priv->ftdefcmds);
429 case GEANY_BCS_FT: return &(ft->priv->filecmds);
430 case GEANY_BCS_HOME_FT: return &(ft->priv->homefilecmds);
431 case GEANY_BCS_PREF: return &(ft->priv->homefilecmds);
432 case GEANY_BCS_PROJ: return &(ft->priv->projfilecmds);
433 default: return NULL;
435 break;
436 case GEANY_GBG_NON_FT:
437 switch (src)
439 case GEANY_BCS_DEF: return &(non_ft_def);
440 case GEANY_BCS_PREF: return &(non_ft_pref);
441 case GEANY_BCS_PROJ: return &(non_ft_proj);
442 default: return NULL;
444 break;
445 case GEANY_GBG_EXEC:
446 if ((doc = document_get_current()) != NULL)
447 ft = doc->file_type;
448 switch (src)
450 case GEANY_BCS_DEF: return &(exec_def);
451 case GEANY_BCS_FT: return ft ? &(ft->priv->execcmds): NULL;
452 case GEANY_BCS_HOME_FT: return ft ? &(ft->priv->homeexeccmds): NULL;
453 case GEANY_BCS_PROJ_FT: return ft ? &(ft->priv->projexeccmds): NULL;
454 case GEANY_BCS_PREF: return &(exec_pref);
455 case GEANY_BCS_PROJ: return &(exec_proj);
456 default: return NULL;
458 break;
459 default:
460 return NULL;
465 /* get pointer to the command group array */
466 static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
468 GeanyBuildCommand **g = get_build_group_pointer(src, grp);
469 if (g == NULL) return NULL;
470 return *g;
474 /** Remove the specified Build menu item.
476 * Makes the specified menu item configuration no longer exist. This
477 * is different to setting fields to blank because the menu item
478 * will be deleted from the configuration file on saving
479 * (except the system filetypes settings @see Build Menu Configuration
480 * section of the Manual).
482 * @param src the source of the menu item to remove.
483 * @param grp the group of the command to remove.
484 * @param cmd the index (from 0) of the command within the group. A negative
485 * value will remove the whole group.
487 * If any parameter is out of range does nothing.
489 * Updates the menu.
492 GEANY_API_SYMBOL
493 void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
495 GeanyBuildCommand *bc;
496 guint i;
498 bc = get_build_group(src, grp);
499 if (bc == NULL)
500 return;
501 if (cmd < 0)
503 for (i = 0; i < build_groups_count[grp]; ++i)
504 bc[i].exists = FALSE;
506 else if ((guint) cmd < build_groups_count[grp])
507 bc[cmd].exists = FALSE;
511 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
513 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
514 * hidden by higher priority commands.
516 * @param src the source of the specified menu item.
517 * @param grp the group of the specified menu item.
518 * @param cmd the index of the command within the group.
520 * @return a pointer to the @a GeanyBuildCommand structure or @c NULL if it doesn't exist.
521 * This is a pointer to an internal structure and must not be freed.
523 * @see build_menu_update
525 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
527 GeanyBuildCommand *bc;
529 g_return_val_if_fail(src < GEANY_BCS_COUNT, NULL);
530 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
531 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
533 bc = get_build_group(src, grp);
534 if (bc == NULL)
535 return NULL;
536 return &(bc[cmd]);
540 /** Get the string for the menu item field.
542 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
543 * that the menu item will use if activated.
545 * @param grp the group of the specified menu item.
546 * @param cmd the index of the command within the group.
547 * @param fld the field to return
549 * @return @nullable a pointer to the constant string or @c NULL if it doesn't exist.
550 * This is a pointer to an internal structure and must not be freed.
553 GEANY_API_SYMBOL
554 const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
555 const GeanyBuildCmdEntries fld)
557 GeanyBuildCommand *c;
558 gchar *str = NULL;
560 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
561 g_return_val_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT, NULL);
562 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
564 c = get_build_cmd(NULL, grp, cmd, NULL);
565 if (c == NULL) return NULL;
566 switch (fld)
568 case GEANY_BC_COMMAND:
569 str = c->command;
570 break;
571 case GEANY_BC_LABEL:
572 str = c->label;
573 break;
574 case GEANY_BC_WORKING_DIR:
575 str = c->working_dir;
576 break;
577 default:
578 break;
580 return str;
583 /** Set the string for the menu item field.
585 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
587 * @param src the source of the menu item
588 * @param grp the group of the specified menu item.
589 * @param cmd the index of the menu item within the group.
590 * @param fld the field in the menu item command to set
591 * @param val the value to set the field to, is copied
594 GEANY_API_SYMBOL
595 void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp,
596 const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
598 GeanyBuildCommand **g;
600 g_return_if_fail(src < GEANY_BCS_COUNT);
601 g_return_if_fail(grp < GEANY_GBG_COUNT);
602 g_return_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT);
603 g_return_if_fail(cmd < build_groups_count[grp]);
605 g = get_build_group_pointer(src, grp);
606 if (g == NULL) return;
607 if (*g == NULL )
609 *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
611 switch (fld)
613 case GEANY_BC_COMMAND:
614 SETPTR((*g)[cmd].command, g_strdup(val));
615 (*g)[cmd].exists = TRUE;
616 break;
617 case GEANY_BC_LABEL:
618 SETPTR((*g)[cmd].label, g_strdup(val));
619 (*g)[cmd].exists = TRUE;
620 break;
621 case GEANY_BC_WORKING_DIR:
622 SETPTR((*g)[cmd].working_dir, g_strdup(val));
623 (*g)[cmd].exists = TRUE;
624 break;
625 default:
626 break;
628 build_menu_update(NULL);
631 /** Activate the menu item.
633 * Activate the menu item specified by @a grp and @a cmd.
635 * @param grp the group of the specified menu item.
636 * @param cmd the index of the command within the group.
639 GEANY_API_SYMBOL
640 void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
642 on_build_menu_item(NULL, GRP_CMD_TO_POINTER(grp, cmd));
646 /* Clear all error indicators in all documents. */
647 static void clear_all_errors(void)
649 guint i;
651 foreach_document(i)
653 editor_indicator_clear_errors(documents[i]->editor);
658 /* Replaces occurrences of %e and %p with the appropriate filenames and
659 * %l with current line number. %d and %p replacements should be in UTF8 */
660 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
662 GString *stack;
663 gchar *replacement;
664 gchar *executable = NULL;
665 gint line_num;
667 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
669 stack = g_string_new(src);
670 if (doc != NULL && doc->file_name != NULL)
672 /* replace %f with the filename (including extension) */
673 replacement = g_path_get_basename(doc->file_name);
674 utils_string_replace_all(stack, "%f", replacement);
675 g_free(replacement);
677 /* replace %d with the absolute path of the dir of the current file */
678 replacement = g_path_get_dirname(doc->file_name);
679 utils_string_replace_all(stack, "%d", replacement);
680 g_free(replacement);
682 /* replace %e with the filename (excluding extension) */
683 executable = utils_remove_ext_from_filename(doc->file_name);
684 replacement = g_path_get_basename(executable);
685 utils_string_replace_all(stack, "%e", replacement);
686 g_free(replacement);
688 /* replace %l with the current 1-based line number */
689 line_num = sci_get_current_line(doc->editor->sci) + 1;
690 replacement = g_strdup_printf("%i", line_num);
691 utils_string_replace_all(stack, "%l", replacement);
692 g_free(replacement);
695 /* replace %p with the current project's (absolute) base directory */
696 replacement = NULL; /* prevent double free if no replacement found */
697 if (app->project)
699 replacement = project_get_base_path();
701 else if (strstr(stack->str, "%p"))
702 { /* fall back to %d */
703 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
704 if (doc != NULL && doc->file_name != NULL)
705 replacement = g_path_get_dirname(doc->file_name);
708 utils_string_replace_all(stack, "%p", replacement);
709 g_free(replacement);
710 g_free(executable);
712 return g_string_free(stack, FALSE); /* don't forget to free src also if needed */
716 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
717 * idx document directory */
718 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
720 GError *error = NULL;
721 gchar *argv[] = { "/bin/sh", "-c", NULL, NULL };
722 gchar *working_dir;
723 gchar *utf8_working_dir;
724 gchar *cmd_string;
726 g_return_if_fail(doc == NULL || doc->is_valid);
728 if ((doc == NULL || EMPTY(doc->file_name)) && EMPTY(dir))
730 geany_debug("Failed to run command with no working directory");
731 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
732 return;
735 clear_all_errors();
736 SETPTR(current_dir_entered, NULL);
738 utf8_working_dir = !EMPTY(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
739 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
741 gtk_list_store_clear(msgwindow.store_compiler);
742 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
743 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), cmd, utf8_working_dir);
744 g_free(utf8_working_dir);
746 #ifdef G_OS_UNIX
747 cmd_string = utils_get_locale_from_utf8(cmd);
748 argv[2] = cmd_string;
749 cmd = NULL; /* under Unix, use argv to start cmd via sh for compatibility */
750 #else
751 /* Expand environment variables like %blah%. */
752 cmd_string = win32_expand_environment_variables(cmd);
753 argv[0] = NULL; /* under Windows, run cmd directly */
754 cmd = cmd_string;
755 #endif
757 /* set the build info for the message window */
758 g_free(build_info.dir);
759 build_info.dir = g_strdup(working_dir);
760 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
761 build_info.message_count = 0;
763 if (!spawn_with_callbacks(working_dir, cmd, argv, NULL, 0, NULL, NULL, build_iofunc,
764 GINT_TO_POINTER(0), 0, build_iofunc, GINT_TO_POINTER(1), 0, build_exit_cb, NULL,
765 &build_info.pid, &error))
767 geany_debug("build command spawning failed: %s", error->message);
768 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
769 g_error_free(error);
772 g_free(working_dir);
773 g_free(cmd_string);
777 /* Returns: NULL if there was an error, or the command to be executed. If Geany is
778 * set to use a run script, the returned value is a path to the script that runs
779 * the command; otherwise the command itself is returned. working_dir is a pointer
780 * to the working directory from which the command is executed. Both strings are
781 * in the locale encoding. */
782 static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
784 GeanyBuildCommand *cmd = NULL;
785 const gchar *cmd_working_dir;
786 gboolean autoclose = FALSE;
787 gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string;
789 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
791 cmd_string_utf8 = build_replace_placeholder(doc, cmd->command);
792 cmd_working_dir = cmd->working_dir;
793 if (EMPTY(cmd_working_dir))
794 cmd_working_dir = "%d";
795 working_dir_utf8 = build_replace_placeholder(doc, cmd_working_dir);
796 *working_dir = utils_get_locale_from_utf8(working_dir_utf8);
798 if (EMPTY(*working_dir) || ! g_file_test(*working_dir, G_FILE_TEST_EXISTS) ||
799 ! g_file_test(*working_dir, G_FILE_TEST_IS_DIR))
801 ui_set_statusbar(TRUE, _("Invalid working directory \"%s\""),
802 !EMPTY(working_dir_utf8) ? working_dir_utf8 : "<NULL>" );
803 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, *working_dir, NULL);
804 return NULL;
807 cmd_string = utils_get_locale_from_utf8(cmd_string_utf8);
809 #ifdef HAVE_VTE
810 if (vte_info.have_vte && vc->run_in_vte)
812 if (vc->skip_run_script)
814 utils_free_pointers(2, cmd_string_utf8, working_dir_utf8, NULL);
815 return cmd_string;
817 else
818 /* don't wait for user input at the end of script when we are running in VTE */
819 autoclose = TRUE;
821 #endif
823 #ifdef G_OS_WIN32
824 /* Expand environment variables like %blah%. */
825 SETPTR(cmd_string, win32_expand_environment_variables(cmd_string));
826 #endif
828 gchar *helper = g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC), "geany-run-helper", NULL);
830 /* escape helper appropriately */
831 #ifdef G_OS_WIN32
832 /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
833 * allowed in paths anyway */
834 SETPTR(helper, g_strdup_printf("\"%s\"", helper));
835 #else
836 SETPTR(helper, g_shell_quote(helper));
837 #endif
838 run_cmd = g_strdup_printf("%s %d %s", helper, autoclose ? 1 : 0, cmd_string);
839 g_free(helper);
841 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
842 return run_cmd;
846 static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
848 gchar *working_dir;
849 gchar *run_cmd = NULL;
851 if (! DOC_VALID(doc) || doc->file_name == NULL)
852 return;
854 run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex);
855 if (run_cmd == NULL)
856 return;
858 run_info[cmdindex].file_type_id = doc->file_type->id;
860 #ifdef HAVE_VTE
861 if (vte_info.have_vte && vc->run_in_vte)
863 gchar *vte_cmd;
865 /* VTE expects commands in UTF-8 */
866 SETPTR(run_cmd, utils_get_utf8_from_locale(run_cmd));
867 SETPTR(working_dir, utils_get_utf8_from_locale(working_dir));
869 if (vc->skip_run_script)
870 vte_cmd = g_strconcat(run_cmd, "\n", NULL);
871 else
872 vte_cmd = g_strconcat("\n/bin/sh ", run_cmd, "\n", NULL);
874 vte_cwd(working_dir, TRUE);
875 if (! vte_send_cmd(vte_cmd))
877 const gchar *msg = _("File not executed because the terminal may contain some input (press Ctrl+C or Enter to clear it).");
878 ui_set_statusbar(FALSE, "%s", msg);
879 geany_debug("%s", msg);
880 if (!vc->skip_run_script)
881 g_unlink(run_cmd);
884 /* show the VTE */
885 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
886 gtk_widget_grab_focus(vc->vte);
887 msgwin_show_hide(TRUE);
889 run_info[cmdindex].pid = 1;
890 g_free(vte_cmd);
892 else
893 #endif
895 gchar *locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
896 GError *error = NULL;
898 #ifdef G_OS_WIN32
899 if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd, 0, 0))
901 /* if passing an argument to cmd.exe, respect its quoting rules */
902 GString *escaped_run_cmd = g_string_new(NULL);
903 for (gchar *p = run_cmd; *p; p++)
905 if (strchr("()%!^\"<>&| ", *p)) // cmd.exe metacharacters
906 g_string_append_c(escaped_run_cmd, '^');
907 g_string_append_c(escaped_run_cmd, *p);
909 SETPTR(run_cmd, g_string_free(escaped_run_cmd, FALSE));
911 #endif
913 utils_str_replace_all(&locale_term_cmd, "%c", run_cmd);
915 if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid),
916 &error))
918 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
919 (gpointer) &(run_info[cmdindex]));
920 build_menu_update(doc);
922 else
924 gchar *utf8_term_cmd = utils_get_utf8_from_locale(locale_term_cmd);
925 ui_set_statusbar(TRUE, _("Cannot execute build command \"%s\": %s. "
926 "Check the Terminal setting in Preferences"), utf8_term_cmd, error->message);
927 g_free(utf8_term_cmd);
928 g_error_free(error);
929 run_info[cmdindex].pid = (GPid) 0;
933 g_free(working_dir);
934 g_free(run_cmd);
938 static void process_build_output_line(gchar *msg, gint color)
940 gchar *tmp;
941 gchar *filename;
942 gint line;
944 g_strchomp(msg);
946 if (EMPTY(msg))
947 return;
949 if (build_parse_make_dir(msg, &tmp))
951 SETPTR(current_dir_entered, tmp);
953 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
955 if (line != -1 && filename != NULL)
957 GeanyDocument *doc = document_find_by_filename(filename);
959 /* limit number of indicators */
960 if (doc && editor_prefs.use_indicators &&
961 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
963 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
964 line--; /* so only adjust the line number if it is greater than 0 */
965 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
967 build_info.message_count++;
968 color = COLOR_RED; /* error message parsed on the line */
970 g_free(filename);
972 msgwin_compiler_add_string(color, msg);
976 static void build_iofunc(GString *string, GIOCondition condition, gpointer data)
978 if (condition & (G_IO_IN | G_IO_PRI))
980 process_build_output_line(string->str,
981 (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK);
986 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
988 const gchar *pos;
990 *prefix = NULL;
992 if (string == NULL)
993 return FALSE;
995 if ((pos = strstr(string, "Entering directory")) != NULL)
997 gsize len;
998 gchar *input;
1000 /* get the start of the path */
1001 pos = strstr(string, "/");
1003 if (pos == NULL)
1004 return FALSE;
1006 input = g_strdup(pos);
1008 /* kill the ' at the end of the path */
1009 len = strlen(input);
1010 input[len - 1] = '\0';
1011 input = g_realloc(input, len); /* shorten by 1 */
1012 *prefix = input;
1014 return TRUE;
1017 if (strstr(string, "Leaving directory") != NULL)
1019 *prefix = NULL;
1020 return TRUE;
1023 return FALSE;
1027 static void show_build_result_message(gboolean failure)
1029 gchar *msg;
1031 if (failure)
1033 msg = _("Compilation failed.");
1034 msgwin_compiler_add_string(COLOR_BLUE, msg);
1035 /* If msgwindow is hidden, user will want to display it to see the error */
1036 if (! ui_prefs.msgwindow_visible)
1038 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1039 msgwin_show_hide(TRUE);
1041 else
1042 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1043 ui_set_statusbar(FALSE, "%s", msg);
1045 else
1047 msg = _("Compilation finished successfully.");
1048 msgwin_compiler_add_string(COLOR_BLUE, msg);
1049 if (! ui_prefs.msgwindow_visible ||
1050 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1051 ui_set_statusbar(FALSE, "%s", msg);
1056 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1058 show_build_result_message(!SPAWN_WIFEXITED(status) || SPAWN_WEXITSTATUS(status) != EXIT_SUCCESS);
1059 utils_beep();
1061 build_info.pid = 0;
1062 /* enable build items again */
1063 build_menu_update(NULL);
1064 ui_progress_bar_stop();
1068 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1070 RunInfo *run_info_data = user_data;
1072 g_spawn_close_pid(child_pid);
1074 run_info_data->pid = 0;
1075 /* reset the stop button and menu item to the original meaning */
1076 build_menu_update(NULL);
1079 typedef void Callback(GtkWidget *w, gpointer u);
1081 /* run the command catenating cmd_cat if present */
1082 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
1084 gchar *dir;
1085 gchar *full_command, *subs_command;
1086 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1087 gchar *cmdstr;
1089 if (buildcmd == NULL)
1090 return;
1092 cmdstr = buildcmd->command;
1094 if (cmd_cat != NULL)
1096 if (cmdstr != NULL)
1097 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1098 else
1099 full_command = g_strdup(cmd_cat);
1101 else
1102 full_command = cmdstr;
1104 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1105 subs_command = build_replace_placeholder(doc, full_command);
1106 build_info.grp = grp;
1107 build_info.cmd = cmd;
1108 build_spawn_cmd(doc, subs_command, dir);
1109 g_free(subs_command);
1110 g_free(dir);
1111 if (cmd_cat != NULL)
1112 g_free(full_command);
1113 build_menu_update(doc);
1114 if (build_info.pid)
1115 ui_progress_bar_start(NULL);
1119 /*----------------------------------------------------------------
1121 * Create build menu and handle callbacks (&toolbar callbacks)
1123 *----------------------------------------------------------------*/
1124 static void on_make_custom_input_response(const gchar *input, gpointer data)
1126 GeanyDocument *doc = document_get_current();
1128 SETPTR(build_info.custom_target, g_strdup(input));
1129 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1130 build_info.custom_target);
1134 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1136 GeanyDocument *doc = document_get_current();
1137 GeanyBuildCommand *bc;
1138 guint grp = GPOINTER_TO_GRP(user_data);
1139 guint cmd = GPOINTER_TO_CMD(user_data);
1141 if (doc && doc->changed)
1143 if (!document_save_file(doc, FALSE))
1144 return;
1146 g_signal_emit_by_name(geany_object, "build-start");
1148 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1150 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1152 if (! dialog)
1154 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1155 _("Enter custom text here, all entered text is appended to the command."),
1156 build_info.custom_target, &on_make_custom_input_response, NULL);
1158 else
1160 gtk_widget_show(dialog);
1162 return;
1164 else if (grp == GEANY_GBG_EXEC)
1166 if (run_info[cmd].pid > (GPid) 1)
1168 kill_process(&run_info[cmd].pid);
1169 return;
1171 bc = get_build_cmd(doc, grp, cmd, NULL);
1172 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1174 gchar *uri;
1175 if (doc == NULL)
1176 return;
1177 uri = g_strconcat("file:///", g_path_skip_root(doc->file_name), NULL);
1178 utils_open_browser(uri);
1179 g_free(uri);
1182 else
1183 build_run_cmd(doc, cmd);
1185 else
1186 build_command(doc, grp, cmd, NULL);
1190 /* group codes for menu items other than the known commands
1191 * value order is important, see the following table for use */
1193 /* the rest in each group */
1194 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1195 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1196 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1197 /* the separator */
1198 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1199 /* the fixed items */
1200 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1201 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1202 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1203 #define MENU_DONE (MENU_COMMANDS + 1)
1206 static struct BuildMenuItemSpec {
1207 const gchar *stock_id;
1208 const gint key_binding;
1209 const guint build_grp;
1210 const guint build_cmd;
1211 const gchar *fix_label;
1212 Callback *cb;
1213 } build_menu_specs[] = {
1214 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1215 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1216 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1217 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1218 {NULL, -1, MENU_FT_REST,
1219 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1220 {NULL, -1, MENU_SEPARATOR,
1221 GBF_SEP_1, NULL, NULL},
1222 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1223 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1224 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1225 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1226 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1227 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1228 {NULL, -1, MENU_NON_FT_REST,
1229 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1230 {NULL, -1, MENU_SEPARATOR,
1231 GBF_SEP_2, NULL, NULL},
1232 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1233 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1234 {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1235 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1236 {NULL, -1, MENU_SEPARATOR,
1237 GBF_SEP_3, NULL, NULL},
1238 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1239 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1240 {NULL, -1, MENU_EXEC_REST,
1241 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1242 {NULL, -1, MENU_SEPARATOR,
1243 GBF_SEP_4, NULL, NULL},
1244 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1245 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1246 {NULL, -1, MENU_DONE,
1247 0, NULL, NULL}
1251 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1252 struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
1254 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1256 if (bs->stock_id != NULL)
1258 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1259 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1261 gtk_widget_show(item);
1262 if (bs->key_binding >= 0)
1263 add_menu_accel(group, (guint) bs->key_binding, ag, item);
1264 gtk_container_add(GTK_CONTAINER(menu), item);
1265 if (bs->cb != NULL)
1267 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1269 menu_items.menu_item[grp][cmd] = item;
1273 static void create_build_menu(BuildMenuItems *build_menu_items)
1275 GtkWidget *menu;
1276 GtkAccelGroup *accel_group = gtk_accel_group_new();
1277 GeanyKeyGroup *keygroup = keybindings_get_core_group(GEANY_KEY_GROUP_BUILD);
1278 guint i, j;
1280 menu = gtk_menu_new();
1281 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1282 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1283 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1284 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1286 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1288 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1289 if (bs->build_grp == MENU_SEPARATOR)
1291 GtkWidget *item = gtk_separator_menu_item_new();
1292 gtk_widget_show(item);
1293 gtk_container_add(GTK_CONTAINER(menu), item);
1294 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1296 else if (bs->fix_label != NULL)
1298 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1299 GBG_FIXED, bs->build_cmd);
1301 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1303 guint grp = bs->build_grp - GEANY_GBG_COUNT;
1304 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1306 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1307 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1308 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1311 else
1313 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1314 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1315 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1318 build_menu_items->menu = menu;
1319 gtk_widget_show(menu);
1320 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1324 /* portability to various GTK versions needs checking
1325 * conforms to description of gtk_accel_label as child of menu item
1326 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1327 static void geany_menu_item_set_label(GtkWidget *w, const gchar *label)
1329 GtkWidget *c = gtk_bin_get_child(GTK_BIN(w));
1331 gtk_label_set_text_with_mnemonic(GTK_LABEL(c), label);
1335 /* * Update the build menu to reflect changes in configuration or status.
1337 * Sets the labels and number of visible items to match the highest
1338 * priority configured commands. Also sets sensitivity if build commands are
1339 * running and switches executes to stop when commands are running.
1341 * @param doc The current document, if available, to save looking it up.
1342 * If @c NULL it will be looked up.
1344 * Call this after modifying any fields of a GeanyBuildCommand structure.
1346 * @see Build Menu Configuration section of the Manual.
1349 void build_menu_update(GeanyDocument *doc)
1351 guint i, cmdcount, cmd, grp;
1352 gboolean vis = FALSE;
1353 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1354 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1355 GeanyBuildCommand *bc;
1357 g_return_if_fail(doc == NULL || doc->is_valid);
1359 if (menu_items.menu == NULL)
1360 create_build_menu(&menu_items);
1361 if (doc == NULL)
1362 doc = document_get_current();
1363 have_path = doc != NULL && doc->file_name != NULL;
1364 build_running = build_info.pid > (GPid) 1;
1365 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1366 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1368 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1369 switch (bs->build_grp)
1371 case MENU_SEPARATOR:
1372 if (vis == TRUE)
1374 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1375 vis = FALSE;
1377 else
1378 gtk_widget_hide(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1379 break;
1380 case MENU_NEXT_ERROR:
1381 case MENU_PREV_ERROR:
1382 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1383 vis |= TRUE;
1384 break;
1385 case MENU_COMMANDS:
1386 vis |= TRUE;
1387 break;
1388 default: /* all configurable commands */
1389 if (bs->build_grp >= GEANY_GBG_COUNT)
1391 grp = bs->build_grp - GEANY_GBG_COUNT;
1392 cmdcount = build_groups_count[grp];
1394 else
1396 grp = bs->build_grp;
1397 cmdcount = bs->build_cmd + 1;
1399 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1401 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1402 const gchar *label;
1403 bc = get_build_cmd(doc, grp, cmd, NULL);
1404 if (bc)
1405 label = bc->label;
1406 else
1407 label = NULL;
1409 if (grp < GEANY_GBG_EXEC)
1411 cmd_sensitivity =
1412 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1413 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1414 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1415 if (bc != NULL && !EMPTY(label))
1417 geany_menu_item_set_label(menu_item, label);
1418 gtk_widget_show_all(menu_item);
1419 vis |= TRUE;
1421 else
1422 gtk_widget_hide(menu_item);
1424 else
1426 GtkWidget *image;
1427 exec_running = run_info[cmd].pid > (GPid) 1;
1428 cmd_sensitivity = (bc != NULL) || exec_running;
1429 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1430 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1431 run_sensitivity = cmd_sensitivity;
1432 if (! exec_running)
1434 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1436 else
1438 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1440 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1441 run_running = exec_running;
1442 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1443 if (bc != NULL && !EMPTY(label))
1445 geany_menu_item_set_label(menu_item, label);
1446 gtk_widget_show_all(menu_item);
1447 vis |= TRUE;
1449 else
1450 gtk_widget_hide(menu_item);
1456 run_sensitivity &= (doc != NULL);
1457 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1458 && have_path && ! build_running;
1459 if (widgets.toolitem_build != NULL)
1460 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1461 can_make = FALSE;
1462 if (widgets.toolitem_make_all != NULL)
1463 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1464 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1465 && ! build_running));
1466 if (widgets.toolitem_make_custom != NULL)
1467 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1468 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1469 && ! build_running));
1470 if (widgets.toolitem_make_object != NULL)
1471 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1472 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1473 && ! build_running));
1474 if (widgets.toolitem_set_args != NULL)
1475 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1477 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1478 && have_path && ! build_running;
1479 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1480 gtk_action_set_sensitive(widgets.build_action, can_make);
1481 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1483 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1484 set_stop_button(run_running);
1489 /* Call build_menu_update() instead of calling this directly. */
1490 static void set_stop_button(gboolean stop)
1492 const gchar *button_stock_id = NULL;
1493 GtkToolButton *run_button;
1495 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1496 if (run_button != NULL)
1497 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1499 if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
1500 return;
1501 if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
1502 return;
1504 /* use the run button also as stop button */
1505 if (stop)
1507 if (run_button != NULL)
1508 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
1510 else
1512 if (run_button != NULL)
1513 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
1518 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1520 /* For now, just show the project dialog */
1521 if (app->project)
1522 project_build_properties();
1523 else
1524 show_build_commands_dialog();
1528 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1530 last_toolbutton_action = user_data;
1531 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1532 on_build_menu_item(menuitem, user_data);
1536 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1538 gchar *msg;
1540 last_toolbutton_action = user_data;
1541 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1542 msg = _("Build the current file with Make and the default target");
1543 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1544 msg = _("Build the current file with Make and the specified target");
1545 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1546 msg = _("Compile the current file with Make");
1547 else
1548 msg = NULL;
1549 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1550 on_build_menu_item(menuitem, user_data);
1554 static void kill_process(GPid *pid)
1556 GError *error = NULL;
1558 if (spawn_kill_process(*pid, &error))
1560 *pid = 0;
1561 build_menu_update(NULL);
1563 else
1565 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), error->message);
1566 g_error_free(error);
1571 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1573 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1574 msgwin_goto_compiler_file_line))
1576 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1578 else
1579 ui_set_statusbar(FALSE, _("No more build errors."));
1583 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1585 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1586 msgwin_goto_compiler_file_line))
1588 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1590 else
1591 ui_set_statusbar(FALSE, _("No more build errors."));
1595 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1597 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1599 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1601 else
1603 on_build_menu_item(NULL, last_toolbutton_action);
1608 /*------------------------------------------------------
1610 * Create and handle the build menu configuration dialog
1612 *-------------------------------------------------------*/
1613 typedef struct RowWidgets
1615 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1616 GeanyBuildSource src;
1617 GeanyBuildSource dst;
1618 GeanyBuildCommand *cmdsrc;
1619 guint grp;
1620 guint cmd;
1621 gboolean cleared;
1622 gboolean used_dst;
1623 } RowWidgets;
1625 #if GTK_CHECK_VERSION(3,0,0)
1626 typedef GdkRGBA InsensitiveColor;
1627 #else
1628 typedef GdkColor InsensitiveColor;
1629 #endif
1630 static InsensitiveColor insensitive_color;
1632 static void set_row_color(RowWidgets *r, InsensitiveColor *color)
1634 enum GeanyBuildCmdEntries i;
1636 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1638 if (i == GEANY_BC_LABEL)
1639 continue;
1641 #if GTK_CHECK_VERSION(3,0,0)
1642 gtk_widget_override_color(r->entries[i], GTK_STATE_FLAG_NORMAL, color);
1643 #else
1644 gtk_widget_modify_text(r->entries[i], GTK_STATE_NORMAL, color);
1645 #endif
1650 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1652 if (GTK_IS_BUTTON(wid))
1653 gtk_button_set_label(GTK_BUTTON(wid), text);
1654 else
1655 gtk_entry_set_text(GTK_ENTRY(wid), text);
1659 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1661 RowWidgets *r = user_data;
1662 guint src;
1663 enum GeanyBuildCmdEntries i;
1664 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1666 if (bc != NULL)
1668 r->cmdsrc = bc;
1669 r->src = src;
1670 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1672 set_build_command_entry_text(r->entries[i],
1673 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1676 else
1678 r->cmdsrc = NULL;
1679 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1681 set_build_command_entry_text(r->entries[i], "");
1684 r->used_dst = FALSE;
1685 set_row_color(r, &insensitive_color);
1686 r->cleared = TRUE;
1690 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1692 gtk_entry_set_text(regex,"");
1696 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1698 RowWidgets *r = user_data;
1699 GtkWidget *top_level = gtk_widget_get_toplevel(wid);
1700 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1701 gchar *str;
1703 if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
1704 str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
1705 else
1706 str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
1708 if (!str)
1709 return;
1711 gtk_button_set_label(GTK_BUTTON(wid), str);
1712 g_free(str);
1713 r->used_dst = TRUE;
1714 set_row_color(r, NULL);
1718 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1720 RowWidgets *r = user_data;
1722 r->used_dst = TRUE;
1723 set_row_color(r, NULL);
1727 /* Column headings, array NULL-terminated */
1728 static const gchar *colheads[] =
1730 "#",
1731 N_("Label"),
1732 N_("Command"),
1733 N_("Working directory"),
1734 N_("Reset"),
1735 NULL
1738 /* column names */
1739 #define DC_ITEM 0
1740 #define DC_ENTRIES 1
1741 #define DC_CLEAR 4
1742 #define DC_N_COL 5
1744 static const guint entry_x_padding = 3;
1745 static const guint entry_y_padding = 0;
1748 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1749 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
1751 GtkWidget *label, *clear, *clearicon;
1752 RowWidgets *roww;
1753 GeanyBuildCommand *bc;
1754 guint src;
1755 enum GeanyBuildCmdEntries i;
1756 guint column = 0;
1757 gchar *text;
1759 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
1761 text = g_strdup_printf("%d.", cmd + 1);
1762 label = gtk_label_new(text);
1763 g_free(text);
1764 #if GTK_CHECK_VERSION(3,0,0)
1766 GtkStyleContext *ctx = gtk_widget_get_style_context(label);
1768 gtk_style_context_save(ctx);
1769 gtk_style_context_get_color(ctx, GTK_STATE_FLAG_INSENSITIVE, &insensitive_color);
1770 gtk_style_context_restore(ctx);
1772 #else
1773 insensitive_color = gtk_widget_get_style(label)->text[GTK_STATE_INSENSITIVE];
1774 #endif
1775 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1776 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1777 roww = g_new0(RowWidgets, 1);
1778 roww->src = GEANY_BCS_COUNT;
1779 roww->grp = grp;
1780 roww->cmd = cmd;
1781 roww->dst = dst;
1782 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1784 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1786 column += 1;
1787 if (i == GEANY_BC_LABEL)
1789 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1790 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1791 gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1792 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
1794 else
1796 roww->entries[i] = gtk_entry_new();
1797 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
1799 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1800 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1802 column++;
1803 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1804 clear = gtk_button_new();
1805 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1806 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1807 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1808 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1809 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1810 if (bc != NULL)
1811 roww->src = src;
1813 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1815 const gchar *str = "";
1817 if (bc != NULL )
1819 if ((str = id_to_str(bc, i)) == NULL)
1820 str = "";
1821 else if (dst == src)
1822 roww->used_dst = TRUE;
1824 set_build_command_entry_text(roww->entries[i], str);
1826 if (bc != NULL && (dst > src))
1827 set_row_color(roww, &insensitive_color);
1828 if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1830 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1831 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1832 gtk_widget_set_sensitive(clear, FALSE);
1834 return roww;
1838 typedef struct BuildTableFields
1840 RowWidgets **rows;
1841 GtkWidget *fileregex;
1842 GtkWidget *nonfileregex;
1843 gchar **fileregexstring;
1844 gchar **nonfileregexstring;
1845 } BuildTableFields;
1848 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
1849 GeanyFiletype *ft)
1851 GtkWidget *label, *sep, *clearicon, *clear;
1852 BuildTableFields *fields;
1853 GtkTable *table;
1854 const gchar **ch;
1855 gchar *txt;
1856 guint col, row, cmdindex;
1857 guint cmd;
1858 guint src;
1859 gboolean sensitivity;
1860 guint sep_padding = entry_y_padding + 3;
1862 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1863 fields = g_new0(BuildTableFields, 1);
1864 fields->rows = g_new0(RowWidgets*, build_items_count);
1865 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1867 label = gtk_label_new(_(*ch));
1868 gtk_table_attach(table, label, col, col + 1, 0, 1,
1869 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1871 sep = gtk_hseparator_new();
1872 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1873 entry_x_padding, sep_padding);
1874 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1875 txt = g_strdup_printf(_("%s commands"), ft->name);
1876 else
1877 txt = g_strdup_printf(_("%s commands"), _("No filetype"));
1879 label = ui_label_new_bold(txt);
1880 g_free(txt);
1881 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1882 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1883 entry_x_padding, entry_y_padding);
1884 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1885 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1886 label = gtk_label_new(_("Error regular expression:"));
1887 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1888 entry_x_padding, entry_y_padding);
1889 fields->fileregex = gtk_entry_new();
1890 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
1891 sensitivity = (ft == NULL) ? FALSE : TRUE;
1892 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1894 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1895 if (src > dst)
1896 sensitivity = FALSE;
1898 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1899 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1900 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1901 clear = gtk_button_new();
1902 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1903 g_signal_connect_swapped(clear, "clicked",
1904 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
1905 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
1906 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1907 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
1908 gtk_widget_set_sensitive(clear, sensitivity);
1909 ++row;
1910 sep = gtk_hseparator_new();
1911 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1912 entry_x_padding, sep_padding);
1913 ++row;
1914 label = ui_label_new_bold(_("Independent commands"));
1915 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1916 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1917 entry_x_padding, entry_y_padding);
1918 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
1919 fields->rows[cmdindex] = build_add_dialog_row(
1920 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
1921 label = gtk_label_new(_("Error regular expression:"));
1922 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
1923 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1924 fields->nonfileregex = gtk_entry_new();
1925 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
1926 sensitivity = TRUE;
1927 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
1929 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
1930 sensitivity = src > dst ? FALSE : TRUE;
1932 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1933 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1934 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1935 clear = gtk_button_new();
1936 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1937 g_signal_connect_swapped(clear, "clicked",
1938 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
1939 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
1940 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1941 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
1942 gtk_widget_set_sensitive(clear, sensitivity);
1943 ++row;
1944 label = gtk_label_new(NULL);
1945 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
1946 _("Note: Item 2 opens a dialog and appends the response to the command."));
1947 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1948 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1949 entry_x_padding, entry_y_padding);
1950 ++row;
1951 sep = gtk_hseparator_new();
1952 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1953 entry_x_padding, sep_padding);
1954 ++row;
1955 label = ui_label_new_bold(_("Execute commands"));
1956 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1957 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1958 entry_x_padding, entry_y_padding);
1959 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
1960 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
1961 sep = gtk_hseparator_new();
1962 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1963 entry_x_padding, sep_padding);
1964 ++row;
1965 label = gtk_label_new(NULL);
1966 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
1967 _("%d, %e, %f, %p, %l are substituted in command and directory fields, see manual for details."));
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 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
1972 ++row;
1973 *table_data = fields;
1974 return GTK_WIDGET(table);
1978 void build_free_fields(BuildTableData table_data)
1980 guint cmdindex;
1982 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
1983 g_free(table_data->rows[cmdindex]);
1984 g_free(table_data->rows);
1985 g_free(table_data);
1989 /* string compare where null pointers match null or 0 length strings */
1990 #if 0
1991 static gint stcmp(const gchar *a, const gchar *b)
1993 if (a == NULL && b == NULL)
1994 return 0;
1995 if (a == NULL && b != NULL)
1996 return strlen(b);
1997 if (a != NULL && b == NULL)
1998 return strlen(a);
1999 return strcmp(a, b);
2001 #endif
2004 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2006 if (GTK_IS_BUTTON(wid))
2007 return gtk_button_get_label(GTK_BUTTON(wid));
2008 else
2009 return gtk_entry_get_text(GTK_ENTRY(wid));
2013 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
2015 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2016 gboolean changed = FALSE;
2017 enum GeanyBuildCmdEntries i;
2019 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2021 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2023 if (table_data->rows[drow]->cleared)
2025 if (dst->dst[grp] != NULL)
2027 if (*(dst->dst[grp]) == NULL)
2028 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2029 (*(dst->dst[grp]))[cmd].exists = FALSE;
2030 (*(dst->dst[grp]))[cmd].changed = TRUE;
2031 changed = TRUE;
2034 if (table_data->rows[drow]->used_dst == TRUE)
2036 if (dst->dst[grp] != NULL)
2038 if (*(dst->dst[grp]) == NULL)
2039 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2040 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2041 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2042 (*(dst->dst[grp]))[cmd].exists = TRUE;
2043 (*(dst->dst[grp]))[cmd].changed = TRUE;
2044 changed = TRUE;
2047 else
2049 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2050 g_free(entries[i]);
2052 return changed;
2056 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2058 gboolean changed = FALSE;
2059 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2061 if (((src == NULL /* originally there was no regex */
2062 || *src == NULL) /* or it was NULL*/
2063 && !EMPTY(reg)) /* and something was typed */
2064 || (src != NULL /* originally there was a regex*/
2065 && (*src == NULL /* and either it was NULL */
2066 || strcmp(*src, reg) != 0))) /* or it has been changed */
2068 if (dst != NULL)
2070 SETPTR(*dst, g_strdup(reg));
2071 changed = TRUE;
2074 return changed;
2078 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2080 guint cmdindex, cmd;
2081 gboolean changed = FALSE;
2083 if (response == GTK_RESPONSE_ACCEPT)
2085 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2086 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2087 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2088 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2089 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2090 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2091 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2092 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2094 return changed;
2098 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2100 BuildDestination menu_dst;
2102 if (ft != NULL)
2104 menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds);
2105 menu_dst.fileregexstr = &(ft->priv->projerror_regex_string);
2107 else
2109 menu_dst.dst[GEANY_GBG_FT] = NULL;
2110 menu_dst.fileregexstr = NULL;
2112 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2113 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2114 menu_dst.nonfileregexstr = &regex_proj;
2116 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2120 static void show_build_commands_dialog(void)
2122 GtkWidget *dialog, *table, *vbox;
2123 GeanyDocument *doc = document_get_current();
2124 GeanyFiletype *ft = NULL;
2125 const gchar *title = _("Set Build Commands");
2126 gint response;
2127 BuildTableData table_data;
2128 BuildDestination prefdsts;
2130 if (doc != NULL)
2131 ft = doc->file_type;
2132 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2133 GTK_DIALOG_DESTROY_WITH_PARENT,
2134 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2135 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2136 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2137 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2138 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2139 gtk_widget_show_all(dialog);
2140 /* run modally to prevent user changing idx filetype */
2141 response = gtk_dialog_run(GTK_DIALOG(dialog));
2143 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2144 if (ft != NULL)
2146 prefdsts.dst[GEANY_GBG_FT] = &(ft->priv->homefilecmds);
2147 prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string);
2148 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds);
2150 else
2152 prefdsts.dst[GEANY_GBG_FT] = NULL;
2153 prefdsts.fileregexstr = NULL;
2154 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2156 prefdsts.nonfileregexstr = &regex_pref;
2157 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2158 filetypes_save_commands(ft);
2159 build_free_fields(table_data);
2161 build_menu_update(doc);
2162 gtk_widget_destroy(dialog);
2166 /* Creates the relevant build menu if necessary. */
2167 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2169 BuildMenuItems *items;
2171 items = &menu_items;
2172 if (items->menu == NULL)
2173 create_build_menu(items);
2174 return items;
2178 /*----------------------------------------------------------
2180 * Load and store configuration
2182 * ---------------------------------------------------------*/
2183 static const gchar *build_grp_name = "build-menu";
2185 /* config format for build-menu group is prefix_gg_nn_xx=value
2186 * where gg = FT, NF, EX for the command group
2187 * nn = 2 digit command number
2188 * xx = LB for label, CM for command and WD for working dir */
2189 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2190 static const gchar *fixedkey="xx_xx_xx";
2192 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2193 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2194 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2196 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2197 gchar *prefix, gboolean loc)
2199 guint cmd;
2200 gsize prefixlen; /* NOTE prefixlen used in macros above */
2201 GeanyBuildCommand *dstcmd;
2202 gchar *key;
2203 static gchar cmdbuf[3] = " ";
2205 if (*dst == NULL)
2206 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2207 dstcmd = *dst;
2208 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2209 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2210 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2212 gchar *label;
2213 if (cmd >= 100)
2214 break; /* ensure no buffer overflow */
2215 sprintf(cmdbuf, "%02u", cmd);
2216 set_key_grp(key, groups[grp]);
2217 set_key_cmd(key, cmdbuf);
2218 set_key_fld(key, "LB");
2219 if (loc)
2220 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2221 else
2222 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2223 if (label != NULL)
2225 dstcmd[cmd].exists = TRUE;
2226 SETPTR(dstcmd[cmd].label, label);
2227 set_key_fld(key,"CM");
2228 SETPTR(dstcmd[cmd].command,
2229 g_key_file_get_string(config, build_grp_name, key, NULL));
2230 set_key_fld(key,"WD");
2231 SETPTR(dstcmd[cmd].working_dir,
2232 g_key_file_get_string(config, build_grp_name, key, NULL));
2234 else dstcmd[cmd].exists = FALSE;
2236 g_free(key);
2240 /* for the specified source load new format build menu items or try to make some sense of
2241 * old format setings, not done perfectly but better than ignoring them */
2242 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2244 GeanyFiletype *ft;
2245 GeanyProject *pj;
2246 gchar **ftlist;
2247 gchar *value, *basedir, *makebasedir;
2248 gboolean bvalue = FALSE;
2250 if (g_key_file_has_group(config, build_grp_name))
2252 switch (src)
2254 case GEANY_BCS_FT:
2255 ft = (GeanyFiletype*)p;
2256 if (ft == NULL)
2257 return;
2258 build_load_menu_grp(config, &(ft->priv->filecmds), GEANY_GBG_FT, NULL, TRUE);
2259 build_load_menu_grp(config, &(ft->priv->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2260 build_load_menu_grp(config, &(ft->priv->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2261 SETPTR(ft->error_regex_string,
2262 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2263 break;
2264 case GEANY_BCS_HOME_FT:
2265 ft = (GeanyFiletype*)p;
2266 if (ft == NULL)
2267 return;
2268 build_load_menu_grp(config, &(ft->priv->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2269 build_load_menu_grp(config, &(ft->priv->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2270 SETPTR(ft->priv->homeerror_regex_string,
2271 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2272 break;
2273 case GEANY_BCS_PREF:
2274 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2275 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2276 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2277 break;
2278 case GEANY_BCS_PROJ:
2279 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2280 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2281 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2282 pj = (GeanyProject*)p;
2283 if (p == NULL)
2284 return;
2285 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2286 if (ftlist != NULL)
2288 gchar **ftname;
2289 if (pj->priv->build_filetypes_list == NULL)
2290 pj->priv->build_filetypes_list = g_ptr_array_new();
2291 g_ptr_array_set_size(pj->priv->build_filetypes_list, 0);
2292 for (ftname = ftlist; *ftname != NULL; ++ftname)
2294 ft = filetypes_lookup_by_name(*ftname);
2295 if (ft != NULL)
2297 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2298 g_ptr_array_add(pj->priv->build_filetypes_list, ft);
2299 SETPTR(ft->priv->projerror_regex_string,
2300 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2301 g_free(regkey);
2302 build_load_menu_grp(config, &(ft->priv->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2303 build_load_menu_grp(config, &(ft->priv->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2306 g_free(ftlist);
2308 break;
2309 default: /* defaults don't load from config, see build_init */
2310 break;
2314 /* load old [build_settings] values if there is no value defined by [build-menu] */
2316 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2317 /* TODO: rewrite as function */
2318 #define ASSIGNIF(type, id, string, value) \
2319 do { \
2320 gchar *ASSIGNF__value = (value); \
2321 if (!EMPTY(ASSIGNF__value) && ! type[GBO_TO_CMD(id)].exists) { \
2322 type[GBO_TO_CMD(id)].exists = TRUE; \
2323 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(string)); \
2324 SETPTR(type[GBO_TO_CMD(id)].command, ASSIGNF__value); \
2325 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL); \
2326 type[GBO_TO_CMD(id)].old = TRUE; \
2327 } else \
2328 g_free(ASSIGNF__value); \
2329 } while (0)
2331 switch (src)
2333 case GEANY_BCS_FT:
2334 ft = (GeanyFiletype*)p;
2335 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2336 if (value != NULL)
2338 if (ft->priv->filecmds == NULL)
2339 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2340 ASSIGNIF(ft->priv->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2342 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2343 if (value != NULL)
2345 if (ft->priv->filecmds == NULL)
2346 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2347 ASSIGNIF(ft->priv->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2349 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2350 if (value != NULL)
2352 if (ft->priv->execcmds == NULL)
2353 ft->priv->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2354 ASSIGNIF(ft->priv->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2356 if (ft->error_regex_string == NULL)
2357 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2358 break;
2359 case GEANY_BCS_PROJ:
2360 if (non_ft_pref == NULL)
2361 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2362 basedir = project_get_base_path();
2363 if (basedir == NULL)
2364 basedir = g_strdup("%d");
2365 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2366 if (bvalue)
2367 makebasedir = g_strdup(basedir);
2368 else
2369 makebasedir = g_strdup("%d");
2370 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2371 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
2372 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2373 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2374 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2375 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
2376 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2377 if (!EMPTY(value))
2379 if (exec_proj == NULL)
2380 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2381 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2383 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2384 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2385 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
2386 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
2387 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2390 g_free(makebasedir);
2391 g_free(basedir);
2392 break;
2393 case GEANY_BCS_PREF:
2394 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2395 if (value != NULL)
2397 if (non_ft_pref == NULL)
2398 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2399 ASSIGNIF(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
2400 g_strdup_printf("%s ", value));
2401 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2402 g_strdup_printf("%s %%e.o",value));
2403 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2405 break;
2406 default:
2407 break;
2412 static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2414 guint cmd;
2415 gsize prefixlen; /* NOTE prefixlen used in macros above */
2416 gchar *key;
2417 guint count = 0;
2418 enum GeanyBuildCmdEntries i;
2420 if (src == NULL)
2421 return 0;
2422 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2423 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2424 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2426 if (src[cmd].exists) ++count;
2427 if (src[cmd].changed)
2429 static gchar cmdbuf[4] = " ";
2430 if (cmd >= 100)
2431 break; /* ensure no buffer overflow */
2432 sprintf(cmdbuf, "%02u", cmd);
2433 set_key_grp(key, groups[grp]);
2434 set_key_cmd(key, cmdbuf);
2435 if (src[cmd].exists)
2437 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2439 set_key_fld(key, config_keys[i]);
2440 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2443 else
2445 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2447 set_key_fld(key, config_keys[i]);
2448 g_key_file_remove_key(config, build_grp_name, key, NULL);
2453 g_free(key);
2454 return count;
2458 typedef struct ForEachData
2460 GKeyFile *config;
2461 GPtrArray *ft_names;
2462 } ForEachData;
2465 static void foreach_project_filetype(gpointer data, gpointer user_data)
2467 GeanyFiletype *ft = data;
2468 ForEachData *d = user_data;
2469 guint i = 0;
2470 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2472 i += build_save_menu_grp(d->config, ft->priv->projfilecmds, GEANY_GBG_FT, ft->name);
2473 i += build_save_menu_grp(d->config, ft->priv->projexeccmds, GEANY_GBG_EXEC, ft->name);
2474 if (!EMPTY(ft->priv->projerror_regex_string))
2476 g_key_file_set_string(d->config, build_grp_name, regkey, ft->priv->projerror_regex_string);
2477 i++;
2479 else
2480 g_key_file_remove_key(d->config, build_grp_name, regkey, NULL);
2481 g_free(regkey);
2482 if (i > 0)
2483 g_ptr_array_add(d->ft_names, ft->name);
2487 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2488 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2490 GeanyFiletype *ft;
2491 GeanyProject *pj;
2492 ForEachData data;
2494 switch (src)
2496 case GEANY_BCS_HOME_FT:
2497 ft = (GeanyFiletype*)ptr;
2498 if (ft == NULL)
2499 return;
2500 build_save_menu_grp(config, ft->priv->homefilecmds, GEANY_GBG_FT, NULL);
2501 build_save_menu_grp(config, ft->priv->homeexeccmds, GEANY_GBG_EXEC, NULL);
2502 if (!EMPTY(ft->priv->homeerror_regex_string))
2503 g_key_file_set_string(config, build_grp_name, "error_regex", ft->priv->homeerror_regex_string);
2504 else
2505 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2506 break;
2507 case GEANY_BCS_PREF:
2508 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2509 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2510 if (!EMPTY(regex_pref))
2511 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2512 else
2513 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2514 break;
2515 case GEANY_BCS_PROJ:
2516 pj = (GeanyProject*)ptr;
2517 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2518 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2519 if (!EMPTY(regex_proj))
2520 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2521 else
2522 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2523 if (pj->priv->build_filetypes_list != NULL)
2525 data.config = config;
2526 data.ft_names = g_ptr_array_new();
2527 g_ptr_array_foreach(pj->priv->build_filetypes_list, foreach_project_filetype, (gpointer)(&data));
2528 if (data.ft_names->pdata != NULL)
2529 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2530 (const gchar**)(data.ft_names->pdata), data.ft_names->len);
2531 else
2532 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2533 g_ptr_array_free(data.ft_names, TRUE);
2535 break;
2536 default: /* defaults and GEANY_BCS_FT can't save */
2537 break;
2542 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2543 void build_set_group_count(GeanyBuildGroup grp, gint count)
2545 guint i, sum;
2547 g_return_if_fail(count >= 0);
2549 if ((guint) count > build_groups_count[grp])
2550 build_groups_count[grp] = (guint) count;
2551 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2552 sum += build_groups_count[i];
2553 build_items_count = sum;
2557 /** Get the count of commands for the group
2559 * Get the number of commands in the group specified by @a grp.
2561 * @param grp the group of the specified menu item.
2563 * @return a count of the number of commands in the group
2566 GEANY_API_SYMBOL
2567 guint build_get_group_count(const GeanyBuildGroup grp)
2569 g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
2570 return build_groups_count[grp];
2574 static void on_project_close(void)
2576 /* remove project regexen */
2577 SETPTR(regex_proj, NULL);
2581 static struct
2583 const gchar *label;
2584 const gchar *command;
2585 const gchar *working_dir;
2586 GeanyBuildCommand **ptr;
2587 gint index;
2588 } default_cmds[] = {
2589 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2590 { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2591 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2592 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2593 { NULL, NULL, NULL, NULL, 0 }
2597 void build_init(void)
2599 GtkWidget *item;
2600 GtkWidget *toolmenu;
2601 gint cmdindex;
2603 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2605 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2606 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2607 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2608 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2610 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2612 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2613 cmd->exists = TRUE;
2614 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2615 cmd->command = g_strdup(default_cmds[cmdindex].command);
2616 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2619 /* create the toolbar Build item sub menu */
2620 toolmenu = gtk_menu_new();
2621 g_object_ref(toolmenu);
2623 /* build the code */
2624 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2625 gtk_widget_show(item);
2626 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2627 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2628 GBO_TO_POINTER(GEANY_GBO_BUILD));
2629 widgets.toolitem_build = item;
2631 item = gtk_separator_menu_item_new();
2632 gtk_widget_show(item);
2633 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2635 /* build the code with make all */
2636 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2637 gtk_widget_show(item);
2638 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2639 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2640 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2641 widgets.toolitem_make_all = item;
2643 /* build the code with make custom */
2644 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2645 gtk_widget_show(item);
2646 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2647 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2648 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2649 widgets.toolitem_make_custom = item;
2651 /* build the code with make object */
2652 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2653 gtk_widget_show(item);
2654 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2655 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2656 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2657 widgets.toolitem_make_object = item;
2659 item = gtk_separator_menu_item_new();
2660 gtk_widget_show(item);
2661 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2663 /* arguments */
2664 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
2665 gtk_widget_show(item);
2666 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2667 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2668 widgets.toolitem_set_args = item;
2670 /* get toolbar action pointers */
2671 widgets.build_action = toolbar_get_action_by_name("Build");
2672 widgets.compile_action = toolbar_get_action_by_name("Compile");
2673 widgets.run_action = toolbar_get_action_by_name("Run");
2674 widgets.toolmenu = toolmenu;
2675 /* set the submenu to the toolbar item */
2676 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);