Fix file names of generated tags files for C, PHP and Python
[geany-mirror.git] / src / build.c
blobc6355a47fa7ead0f38a265dc0485f5e5cf9e554a
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 #ifndef G_OS_WIN32
83 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh";
84 #endif
86 /* pack group (<8) and command (<32) into a user_data pointer */
87 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
88 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
89 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
90 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
92 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
94 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
96 static struct
98 GtkAction *run_action;
99 GtkAction *compile_action;
100 GtkAction *build_action;
101 GtkWidget *toolmenu;
103 GtkWidget *toolitem_build;
104 GtkWidget *toolitem_make_all;
105 GtkWidget *toolitem_make_custom;
106 GtkWidget *toolitem_make_object;
107 GtkWidget *toolitem_set_args;
109 widgets;
111 static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
112 static guint build_items_count = 9;
114 static void build_exit_cb(GPid pid, gint status, gpointer user_data);
115 static void build_iofunc(GString *string, GIOCondition condition, gpointer data);
116 #ifndef G_OS_WIN32
117 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
118 #endif
119 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
120 static void set_stop_button(gboolean stop);
121 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
122 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
123 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
124 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
125 static void kill_process(GPid *pid);
126 static void show_build_result_message(gboolean failure);
127 static void process_build_output_line(gchar *msg, gint color);
128 static void show_build_commands_dialog(void);
129 static void on_build_menu_item(GtkWidget *w, gpointer user_data);
131 void build_finalize(void)
133 g_free(build_info.dir);
134 g_free(build_info.custom_target);
136 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
137 gtk_widget_destroy(menu_items.menu);
141 /* note: copied from keybindings.c, may be able to go away */
142 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
143 GtkAccelGroup *accel_group, GtkWidget *menuitem)
145 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
147 if (kb->key != 0)
148 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
149 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
153 /* convenience routines to access parts of GeanyBuildCommand */
154 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
156 switch (id)
158 case GEANY_BC_LABEL:
159 return bc->label;
160 case GEANY_BC_COMMAND:
161 return bc->command;
162 case GEANY_BC_WORKING_DIR:
163 return bc->working_dir;
165 g_assert(0);
166 return NULL;
170 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
172 switch (id)
174 case GEANY_BC_LABEL:
175 SETPTR(bc->label, str);
176 break;
177 case GEANY_BC_COMMAND:
178 SETPTR(bc->command, str);
179 break;
180 case GEANY_BC_WORKING_DIR:
181 SETPTR(bc->working_dir, str);
182 break;
183 default:
184 g_assert(0);
189 static const gchar *config_keys[GEANY_BC_CMDENTRIES_COUNT] = {
190 "LB", /* label */
191 "CM", /* command */
192 "WD" /* working directory */
195 /*-----------------------------------------------------
197 * Execute commands and handle results
199 *-----------------------------------------------------*/
201 /* the various groups of commands not in the filetype struct */
202 static GeanyBuildCommand *ft_def = NULL;
203 static GeanyBuildCommand *non_ft_proj = NULL;
204 static GeanyBuildCommand *non_ft_pref = NULL;
205 static GeanyBuildCommand *non_ft_def = NULL;
206 static GeanyBuildCommand *exec_proj = NULL;
207 static GeanyBuildCommand *exec_pref = NULL;
208 static GeanyBuildCommand *exec_def = NULL;
209 /* and the regexen not in the filetype structure */
210 static gchar *regex_pref = NULL;
211 /* project non-fileregex string */
212 static gchar *regex_proj = NULL;
214 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
215 #ifndef PRINTBUILDCMDS
216 #define PRINTBUILDCMDS FALSE
217 #endif
218 static gboolean printbuildcmds = PRINTBUILDCMDS;
221 /* for debug only, print the commands structures in priority order */
222 static void printfcmds(void)
224 #if 0
225 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
226 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
227 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
228 { &ft_def, NULL, NULL, NULL, NULL, NULL },
229 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
230 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
232 GeanyFiletype *ft = NULL;
233 GeanyDocument *doc;
234 gint i, j, k, l, m;
235 enum GeanyBuildCmdEntries n;
236 gint cc[GEANY_BCS_COUNT];
237 gchar c;
239 doc = document_get_current();
240 if (doc != NULL)
241 ft = doc->file_type;
242 if (ft != NULL)
244 printf("filetype %s\n",ft->name);
245 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->priv->filecmds);
246 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->priv->homefilecmds);
247 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->priv->projfilecmds);
248 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->priv->ftdefcmds);
249 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->priv->execcmds);
250 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->priv->homeexeccmds);
251 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->priv->projexeccmds);
253 for (i = 0; i < GEANY_BCS_COUNT; ++i)
255 m = 1;
256 for (j = 0; j < GEANY_GBG_COUNT; ++j)
258 for (k = 0; k < build_groups_count[j]; ++k)
259 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
261 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
263 if ((*(cl[j][i]))[k].entries[n] != NULL &&
264 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
266 m = l;
271 cc[i] = m;
273 for (i = 0; i < GEANY_GBG_COUNT; ++i)
275 for (k = 0; k < build_groups_count[i]; ++k)
277 for (l = 0; l < 2; ++l)
279 c = ' ';
280 for (j = 0; j < GEANY_BCS_COUNT; ++j)
282 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
284 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
286 if ((*(cl[i][j]))[k].entries[i] != NULL)
287 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
288 else
289 printf("%c %*.*s",c,cc[j],cc[j]," ");
292 else
293 printf("%c %*.*s",c,cc[j],cc[j]," ");
294 c = ',';
296 printf("\n");
299 printf("\n");
301 #endif
305 /* macros to save typing and make the logic visible */
306 #define return_cmd_if(src, cmds)\
307 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
309 *fr=src; \
310 if (printbuildcmds) \
311 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
312 return &(cmds[cmdindex]); \
315 #define return_ft_cmd_if(src, cmds)\
316 if (ft != NULL && ft->priv->cmds != NULL \
317 && ft->priv->cmds[cmdindex].exists && below>src)\
319 *fr=src; \
320 if (printbuildcmds) \
321 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
322 return &(ft->priv->cmds[cmdindex]); \
326 /* get the next lowest command taking priority into account */
327 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
328 guint below, guint *from)
330 /* Note: parameter below used in macros above */
331 GeanyFiletype *ft = NULL;
332 guint sink, *fr = &sink;
334 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
336 if (printbuildcmds)
337 printfcmds();
338 if (cmdgrp >= GEANY_GBG_COUNT)
339 return NULL;
340 if (from != NULL)
341 fr = from;
342 if (doc == NULL)
343 doc = document_get_current();
344 if (doc != NULL)
345 ft = doc->file_type;
347 switch (cmdgrp)
349 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
350 if (ft != NULL)
352 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
353 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
354 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
356 return_cmd_if(GEANY_BCS_DEF, ft_def);
357 break;
358 case GEANY_GBG_NON_FT: /* order proj, pref, def */
359 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
360 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
361 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
362 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
363 break;
364 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
365 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
366 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
367 return_cmd_if(GEANY_BCS_PREF, exec_pref);
368 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
369 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
370 return_cmd_if(GEANY_BCS_DEF, exec_def);
371 break;
372 default:
373 break;
375 return NULL;
379 /* shortcut to start looking at the top */
380 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
382 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
386 #define return_nonblank_regex(src, ptr)\
387 if (!EMPTY(ptr)) \
388 { *fr = (src); return &(ptr); }
391 /* like get_build_cmd, but for regexen, used by filetypes */
392 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
394 guint sink, *fr = &sink;
396 if (from != NULL)
397 fr = from;
398 if (grp == GEANY_GBG_FT)
400 if (ft == NULL)
402 GeanyDocument *doc = document_get_current();
403 if (doc != NULL)
404 ft = doc->file_type;
406 if (ft == NULL)
407 return NULL;
408 return_nonblank_regex(GEANY_BCS_PROJ, ft->priv->projerror_regex_string);
409 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->priv->homeerror_regex_string);
410 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
412 else if (grp == GEANY_GBG_NON_FT)
414 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
415 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
417 return NULL;
421 static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
423 GeanyDocument *doc;
424 GeanyFiletype *ft = NULL;
426 switch (grp)
428 case GEANY_GBG_FT:
429 if ((doc = document_get_current()) == NULL)
430 return NULL;
431 if ((ft = doc->file_type) == NULL)
432 return NULL;
433 switch (src)
435 case GEANY_BCS_DEF: return &(ft->priv->ftdefcmds);
436 case GEANY_BCS_FT: return &(ft->priv->filecmds);
437 case GEANY_BCS_HOME_FT: return &(ft->priv->homefilecmds);
438 case GEANY_BCS_PREF: return &(ft->priv->homefilecmds);
439 case GEANY_BCS_PROJ: return &(ft->priv->projfilecmds);
440 default: return NULL;
442 break;
443 case GEANY_GBG_NON_FT:
444 switch (src)
446 case GEANY_BCS_DEF: return &(non_ft_def);
447 case GEANY_BCS_PREF: return &(non_ft_pref);
448 case GEANY_BCS_PROJ: return &(non_ft_proj);
449 default: return NULL;
451 break;
452 case GEANY_GBG_EXEC:
453 if ((doc = document_get_current()) != NULL)
454 ft = doc->file_type;
455 switch (src)
457 case GEANY_BCS_DEF: return &(exec_def);
458 case GEANY_BCS_FT: return ft ? &(ft->priv->execcmds): NULL;
459 case GEANY_BCS_HOME_FT: return ft ? &(ft->priv->homeexeccmds): NULL;
460 case GEANY_BCS_PROJ_FT: return ft ? &(ft->priv->projexeccmds): NULL;
461 case GEANY_BCS_PREF: return &(exec_pref);
462 case GEANY_BCS_PROJ: return &(exec_proj);
463 default: return NULL;
465 break;
466 default:
467 return NULL;
472 /* get pointer to the command group array */
473 static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
475 GeanyBuildCommand **g = get_build_group_pointer(src, grp);
476 if (g == NULL) return NULL;
477 return *g;
481 /** Remove the specified Build menu item.
483 * Makes the specified menu item configuration no longer exist. This
484 * is different to setting fields to blank because the menu item
485 * will be deleted from the configuration file on saving
486 * (except the system filetypes settings @see Build Menu Configuration
487 * section of the Manual).
489 * @param src the source of the menu item to remove.
490 * @param grp the group of the command to remove.
491 * @param cmd the index (from 0) of the command within the group. A negative
492 * value will remove the whole group.
494 * If any parameter is out of range does nothing.
496 * Updates the menu.
499 GEANY_API_SYMBOL
500 void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
502 GeanyBuildCommand *bc;
503 guint i;
505 bc = get_build_group(src, grp);
506 if (bc == NULL)
507 return;
508 if (cmd < 0)
510 for (i = 0; i < build_groups_count[grp]; ++i)
511 bc[i].exists = FALSE;
513 else if ((guint) cmd < build_groups_count[grp])
514 bc[cmd].exists = FALSE;
518 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
520 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
521 * hidden by higher priority commands.
523 * @param src the source of the specified menu item.
524 * @param grp the group of the specified menu item.
525 * @param cmd the index of the command within the group.
527 * @return a pointer to the @a GeanyBuildCommand structure or @c NULL if it doesn't exist.
528 * This is a pointer to an internal structure and must not be freed.
530 * @see build_menu_update
532 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
534 GeanyBuildCommand *bc;
536 g_return_val_if_fail(src < GEANY_BCS_COUNT, NULL);
537 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
538 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
540 bc = get_build_group(src, grp);
541 if (bc == NULL)
542 return NULL;
543 return &(bc[cmd]);
547 /** Get the string for the menu item field.
549 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
550 * that the menu item will use if activated.
552 * @param grp the group of the specified menu item.
553 * @param cmd the index of the command within the group.
554 * @param fld the field to return
556 * @return @nullable a pointer to the constant string or @c NULL if it doesn't exist.
557 * This is a pointer to an internal structure and must not be freed.
560 GEANY_API_SYMBOL
561 const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
562 const GeanyBuildCmdEntries fld)
564 GeanyBuildCommand *c;
565 gchar *str = NULL;
567 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
568 g_return_val_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT, NULL);
569 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
571 c = get_build_cmd(NULL, grp, cmd, NULL);
572 if (c == NULL) return NULL;
573 switch (fld)
575 case GEANY_BC_COMMAND:
576 str = c->command;
577 break;
578 case GEANY_BC_LABEL:
579 str = c->label;
580 break;
581 case GEANY_BC_WORKING_DIR:
582 str = c->working_dir;
583 break;
584 default:
585 break;
587 return str;
590 /** Set the string for the menu item field.
592 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
594 * @param src the source of the menu item
595 * @param grp the group of the specified menu item.
596 * @param cmd the index of the menu item within the group.
597 * @param fld the field in the menu item command to set
598 * @param val the value to set the field to, is copied
601 GEANY_API_SYMBOL
602 void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp,
603 const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
605 GeanyBuildCommand **g;
607 g_return_if_fail(src < GEANY_BCS_COUNT);
608 g_return_if_fail(grp < GEANY_GBG_COUNT);
609 g_return_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT);
610 g_return_if_fail(cmd < build_groups_count[grp]);
612 g = get_build_group_pointer(src, grp);
613 if (g == NULL) return;
614 if (*g == NULL )
616 *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
618 switch (fld)
620 case GEANY_BC_COMMAND:
621 SETPTR((*g)[cmd].command, g_strdup(val));
622 (*g)[cmd].exists = TRUE;
623 break;
624 case GEANY_BC_LABEL:
625 SETPTR((*g)[cmd].label, g_strdup(val));
626 (*g)[cmd].exists = TRUE;
627 break;
628 case GEANY_BC_WORKING_DIR:
629 SETPTR((*g)[cmd].working_dir, g_strdup(val));
630 (*g)[cmd].exists = TRUE;
631 break;
632 default:
633 break;
635 build_menu_update(NULL);
638 /** Activate the menu item.
640 * Activate the menu item specified by @a grp and @a cmd.
642 * @param grp the group of the specified menu item.
643 * @param cmd the index of the command within the group.
646 GEANY_API_SYMBOL
647 void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
649 on_build_menu_item(NULL, GRP_CMD_TO_POINTER(grp, cmd));
653 /* Clear all error indicators in all documents. */
654 static void clear_all_errors(void)
656 guint i;
658 foreach_document(i)
660 editor_indicator_clear_errors(documents[i]->editor);
665 /* Replaces occurrences of %e and %p with the appropriate filenames and
666 * %l with current line number. %d and %p replacements should be in UTF8 */
667 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
669 GString *stack;
670 gchar *replacement;
671 gchar *executable = NULL;
672 gint line_num;
674 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
676 stack = g_string_new(src);
677 if (doc != NULL && doc->file_name != NULL)
679 /* replace %f with the filename (including extension) */
680 replacement = g_path_get_basename(doc->file_name);
681 utils_string_replace_all(stack, "%f", replacement);
682 g_free(replacement);
684 /* replace %d with the absolute path of the dir of the current file */
685 replacement = g_path_get_dirname(doc->file_name);
686 utils_string_replace_all(stack, "%d", replacement);
687 g_free(replacement);
689 /* replace %e with the filename (excluding extension) */
690 executable = utils_remove_ext_from_filename(doc->file_name);
691 replacement = g_path_get_basename(executable);
692 utils_string_replace_all(stack, "%e", replacement);
693 g_free(replacement);
695 /* replace %l with the current 1-based line number */
696 line_num = sci_get_current_line(doc->editor->sci) + 1;
697 replacement = g_strdup_printf("%i", line_num);
698 utils_string_replace_all(stack, "%l", replacement);
699 g_free(replacement);
702 /* replace %p with the current project's (absolute) base directory */
703 replacement = NULL; /* prevent double free if no replacement found */
704 if (app->project)
706 replacement = project_get_base_path();
708 else if (strstr(stack->str, "%p"))
709 { /* fall back to %d */
710 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
711 if (doc != NULL && doc->file_name != NULL)
712 replacement = g_path_get_dirname(doc->file_name);
715 utils_string_replace_all(stack, "%p", replacement);
716 g_free(replacement);
717 g_free(executable);
719 return g_string_free(stack, FALSE); /* don't forget to free src also if needed */
723 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
724 * idx document directory */
725 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
727 GError *error = NULL;
728 gchar *argv[] = { "/bin/sh", "-c", NULL, NULL };
729 gchar *working_dir;
730 gchar *utf8_working_dir;
731 gchar *cmd_string;
733 g_return_if_fail(doc == NULL || doc->is_valid);
735 if ((doc == NULL || EMPTY(doc->file_name)) && EMPTY(dir))
737 geany_debug("Failed to run command with no working directory");
738 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
739 return;
742 clear_all_errors();
743 SETPTR(current_dir_entered, NULL);
745 utf8_working_dir = !EMPTY(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
746 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
748 gtk_list_store_clear(msgwindow.store_compiler);
749 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
750 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), cmd, utf8_working_dir);
751 g_free(utf8_working_dir);
753 #ifdef G_OS_UNIX
754 cmd_string = utils_get_locale_from_utf8(cmd);
755 argv[2] = cmd_string;
756 cmd = NULL; /* under Unix, use argv to start cmd via sh for compatibility */
757 #else
758 /* Expand environment variables like %blah%. */
759 cmd_string = win32_expand_environment_variables(cmd);
760 argv[0] = NULL; /* under Windows, run cmd directly */
761 cmd = cmd_string;
762 #endif
764 /* set the build info for the message window */
765 g_free(build_info.dir);
766 build_info.dir = g_strdup(working_dir);
767 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
768 build_info.message_count = 0;
770 if (!spawn_with_callbacks(working_dir, cmd, argv, NULL, 0, NULL, NULL, build_iofunc,
771 GINT_TO_POINTER(0), 0, build_iofunc, GINT_TO_POINTER(1), 0, build_exit_cb, NULL,
772 &build_info.pid, &error))
774 geany_debug("build command spawning failed: %s", error->message);
775 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
776 g_error_free(error);
779 g_free(working_dir);
780 g_free(cmd_string);
784 /* Returns: NULL if there was an error, or the command to be executed. If Geany is
785 * set to use a run script, the returned value is a path to the script that runs
786 * the command; otherwise the command itself is returned. working_dir is a pointer
787 * to the working directory from which the command is executed. Both strings are
788 * in the locale encoding. */
789 static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
791 GeanyBuildCommand *cmd = NULL;
792 const gchar *cmd_working_dir;
793 gboolean autoclose = FALSE;
794 gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string;
795 GError *error = NULL;
797 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
799 cmd_string_utf8 = build_replace_placeholder(doc, cmd->command);
800 cmd_working_dir = cmd->working_dir;
801 if (EMPTY(cmd_working_dir))
802 cmd_working_dir = "%d";
803 working_dir_utf8 = build_replace_placeholder(doc, cmd_working_dir);
804 *working_dir = utils_get_locale_from_utf8(working_dir_utf8);
806 if (EMPTY(*working_dir) || ! g_file_test(*working_dir, G_FILE_TEST_EXISTS) ||
807 ! g_file_test(*working_dir, G_FILE_TEST_IS_DIR))
809 ui_set_statusbar(TRUE, _("Invalid working directory \"%s\""),
810 !EMPTY(working_dir_utf8) ? working_dir_utf8 : "<NULL>" );
811 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, *working_dir, NULL);
812 return NULL;
815 cmd_string = utils_get_locale_from_utf8(cmd_string_utf8);
817 #ifdef HAVE_VTE
818 if (vte_info.have_vte && vc->run_in_vte)
820 if (vc->skip_run_script)
822 utils_free_pointers(2, cmd_string_utf8, working_dir_utf8, NULL);
823 return cmd_string;
825 else
826 /* don't wait for user input at the end of script when we are running in VTE */
827 autoclose = TRUE;
829 #endif
831 #ifdef G_OS_WIN32
832 /* Expand environment variables like %blah%. */
833 SETPTR(cmd_string, win32_expand_environment_variables(cmd_string));
835 gchar *helper = g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC), "geany-run-helper", NULL);
836 /* escape helper appropriately */
837 /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
838 * allowed in paths anyway */
839 run_cmd = g_strdup_printf("\"%s\" \"%s\" %d %s", helper, *working_dir, autoclose ? 1 : 0, cmd_string);
840 g_free(helper);
841 #else
842 run_cmd = build_create_shellscript(*working_dir, cmd_string, autoclose, &error);
843 if (!run_cmd)
845 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
846 !EMPTY(cmd_string_utf8) ? cmd_string_utf8 : NULL, error->message);
847 g_error_free(error);
848 g_free(*working_dir);
850 #endif
851 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
852 return run_cmd;
856 static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
858 gchar *working_dir;
859 gchar *run_cmd = NULL;
861 if (! DOC_VALID(doc) || doc->file_name == NULL)
862 return;
864 run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex);
865 if (run_cmd == NULL)
866 return;
868 run_info[cmdindex].file_type_id = doc->file_type->id;
870 #ifdef HAVE_VTE
871 if (vte_info.have_vte && vc->run_in_vte)
873 gchar *vte_cmd;
875 /* VTE expects commands in UTF-8 */
876 SETPTR(run_cmd, utils_get_utf8_from_locale(run_cmd));
877 SETPTR(working_dir, utils_get_utf8_from_locale(working_dir));
879 if (vc->skip_run_script)
880 vte_cmd = g_strconcat(run_cmd, "\n", NULL);
881 else
882 vte_cmd = g_strconcat("\n/bin/sh ", run_cmd, "\n", NULL);
884 vte_cwd(working_dir, TRUE);
885 if (! vte_send_cmd(vte_cmd))
887 const gchar *msg = _("File not executed because the terminal may contain some input (press Ctrl+C or Enter to clear it).");
888 ui_set_statusbar(FALSE, "%s", msg);
889 geany_debug("%s", msg);
890 if (!vc->skip_run_script)
891 g_unlink(run_cmd);
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;
900 g_free(vte_cmd);
902 else
903 #endif
905 gchar *locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
906 GError *error = NULL;
908 #ifdef G_OS_WIN32
909 if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd, 0, 0))
911 /* if passing an argument to cmd.exe, respect its quoting rules */
912 GString *escaped_run_cmd = g_string_new(NULL);
913 for (gchar *p = run_cmd; *p; p++)
915 if (strchr("()%!^\"<>&| ", *p)) // cmd.exe metacharacters
916 g_string_append_c(escaped_run_cmd, '^');
917 g_string_append_c(escaped_run_cmd, *p);
919 SETPTR(run_cmd, g_string_free(escaped_run_cmd, FALSE));
921 #endif
923 utils_str_replace_all(&locale_term_cmd, "%c", run_cmd);
925 if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid),
926 &error))
928 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
929 (gpointer) &(run_info[cmdindex]));
930 build_menu_update(doc);
932 else
934 gchar *utf8_term_cmd = utils_get_utf8_from_locale(locale_term_cmd);
935 ui_set_statusbar(TRUE, _("Cannot execute build command \"%s\": %s. "
936 "Check the Terminal setting in Preferences"), utf8_term_cmd, error->message);
937 g_free(utf8_term_cmd);
938 g_error_free(error);
939 #ifndef G_OS_WIN32
940 g_unlink(run_cmd);
941 #endif
942 run_info[cmdindex].pid = (GPid) 0;
946 g_free(working_dir);
947 g_free(run_cmd);
951 static void process_build_output_line(gchar *msg, gint color)
953 gchar *tmp;
954 gchar *filename;
955 gint line;
957 g_strchomp(msg);
959 if (EMPTY(msg))
960 return;
962 if (build_parse_make_dir(msg, &tmp))
964 SETPTR(current_dir_entered, tmp);
966 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
968 if (line != -1 && filename != NULL)
970 GeanyDocument *doc = document_find_by_filename(filename);
972 /* limit number of indicators */
973 if (doc && editor_prefs.use_indicators &&
974 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
976 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
977 line--; /* so only adjust the line number if it is greater than 0 */
978 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
980 build_info.message_count++;
981 color = COLOR_RED; /* error message parsed on the line */
983 g_free(filename);
985 msgwin_compiler_add_string(color, msg);
989 static void build_iofunc(GString *string, GIOCondition condition, gpointer data)
991 if (condition & (G_IO_IN | G_IO_PRI))
993 process_build_output_line(string->str,
994 (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK);
999 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1001 const gchar *pos;
1003 *prefix = NULL;
1005 if (string == NULL)
1006 return FALSE;
1008 if ((pos = strstr(string, "Entering directory")) != NULL)
1010 gsize len;
1011 gchar *input;
1013 /* get the start of the path */
1014 pos = strstr(string, "/");
1016 if (pos == NULL)
1017 return FALSE;
1019 input = g_strdup(pos);
1021 /* kill the ' at the end of the path */
1022 len = strlen(input);
1023 input[len - 1] = '\0';
1024 input = g_realloc(input, len); /* shorten by 1 */
1025 *prefix = input;
1027 return TRUE;
1030 if (strstr(string, "Leaving directory") != NULL)
1032 *prefix = NULL;
1033 return TRUE;
1036 return FALSE;
1040 static void show_build_result_message(gboolean failure)
1042 gchar *msg;
1044 if (failure)
1046 msg = _("Compilation failed.");
1047 msgwin_compiler_add_string(COLOR_BLUE, msg);
1048 /* If msgwindow is hidden, user will want to display it to see the error */
1049 if (! ui_prefs.msgwindow_visible)
1051 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1052 msgwin_show_hide(TRUE);
1054 else
1055 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1056 ui_set_statusbar(FALSE, "%s", msg);
1058 else
1060 msg = _("Compilation finished successfully.");
1061 msgwin_compiler_add_string(COLOR_BLUE, msg);
1062 if (! ui_prefs.msgwindow_visible ||
1063 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1064 ui_set_statusbar(FALSE, "%s", msg);
1069 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1071 show_build_result_message(!SPAWN_WIFEXITED(status) || SPAWN_WEXITSTATUS(status) != EXIT_SUCCESS);
1072 utils_beep();
1074 build_info.pid = 0;
1075 /* enable build items again */
1076 build_menu_update(NULL);
1077 ui_progress_bar_stop();
1081 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1083 RunInfo *run_info_data = user_data;
1085 g_spawn_close_pid(child_pid);
1087 run_info_data->pid = 0;
1088 /* reset the stop button and menu item to the original meaning */
1089 build_menu_update(NULL);
1093 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1094 * working_dir and cmd are both in the locale encoding
1095 * it returns the full file name (including path) of the created script in the locale encoding */
1096 #ifndef G_OS_WIN32
1097 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
1099 gint fd;
1100 gchar *str, *fname;
1101 gboolean success = TRUE;
1102 gchar *escaped_dir;
1103 fd = g_file_open_tmp (RUN_SCRIPT_CMD, &fname, error);
1104 if (fd < 0)
1105 return NULL;
1106 close(fd);
1108 escaped_dir = g_shell_quote(working_dir);
1109 str = g_strdup_printf(
1110 "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1111 \n\n%s\n", escaped_dir, cmd, (autoclose) ? "" :
1112 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1113 "dash\ndummy_var=\"\"\nread dummy_var");
1114 g_free(escaped_dir);
1116 if (!g_file_set_contents(fname, str, -1, error))
1117 success = FALSE;
1118 g_free(str);
1119 #ifdef __APPLE__
1120 if (success && g_chmod(fname, 0777) != 0)
1122 if (error)
1124 gint errsv = errno;
1126 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv),
1127 "Failed to make file executable: %s", g_strerror(errsv));
1129 success = FALSE;
1131 #endif
1133 if (!success)
1135 g_unlink(fname);
1136 g_free(fname);
1137 fname = NULL;
1140 return fname;
1142 #endif
1145 typedef void Callback(GtkWidget *w, gpointer u);
1147 /* run the command catenating cmd_cat if present */
1148 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
1150 gchar *dir;
1151 gchar *full_command, *subs_command;
1152 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1153 gchar *cmdstr;
1155 if (buildcmd == NULL)
1156 return;
1158 cmdstr = buildcmd->command;
1160 if (cmd_cat != NULL)
1162 if (cmdstr != NULL)
1163 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1164 else
1165 full_command = g_strdup(cmd_cat);
1167 else
1168 full_command = cmdstr;
1170 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1171 subs_command = build_replace_placeholder(doc, full_command);
1172 build_info.grp = grp;
1173 build_info.cmd = cmd;
1174 build_spawn_cmd(doc, subs_command, dir);
1175 g_free(subs_command);
1176 g_free(dir);
1177 if (cmd_cat != NULL)
1178 g_free(full_command);
1179 build_menu_update(doc);
1180 if (build_info.pid)
1181 ui_progress_bar_start(NULL);
1185 /*----------------------------------------------------------------
1187 * Create build menu and handle callbacks (&toolbar callbacks)
1189 *----------------------------------------------------------------*/
1190 static void on_make_custom_input_response(const gchar *input, gpointer data)
1192 GeanyDocument *doc = document_get_current();
1194 SETPTR(build_info.custom_target, g_strdup(input));
1195 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1196 build_info.custom_target);
1200 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1202 GeanyDocument *doc = document_get_current();
1203 GeanyBuildCommand *bc;
1204 guint grp = GPOINTER_TO_GRP(user_data);
1205 guint cmd = GPOINTER_TO_CMD(user_data);
1207 if (doc && doc->changed)
1209 if (!document_save_file(doc, FALSE))
1210 return;
1212 g_signal_emit_by_name(geany_object, "build-start");
1214 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1216 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1218 if (! dialog)
1220 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1221 _("Enter custom text here, all entered text is appended to the command."),
1222 build_info.custom_target, &on_make_custom_input_response, NULL);
1224 else
1226 gtk_widget_show(dialog);
1228 return;
1230 else if (grp == GEANY_GBG_EXEC)
1232 if (run_info[cmd].pid > (GPid) 1)
1234 kill_process(&run_info[cmd].pid);
1235 return;
1237 bc = get_build_cmd(doc, grp, cmd, NULL);
1238 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1240 gchar *uri;
1241 if (doc == NULL)
1242 return;
1243 uri = g_strconcat("file:///", g_path_skip_root(doc->file_name), NULL);
1244 utils_open_browser(uri);
1245 g_free(uri);
1248 else
1249 build_run_cmd(doc, cmd);
1251 else
1252 build_command(doc, grp, cmd, NULL);
1256 /* group codes for menu items other than the known commands
1257 * value order is important, see the following table for use */
1259 /* the rest in each group */
1260 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1261 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1262 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1263 /* the separator */
1264 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1265 /* the fixed items */
1266 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1267 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1268 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1269 #define MENU_DONE (MENU_COMMANDS + 1)
1272 static struct BuildMenuItemSpec {
1273 const gchar *stock_id;
1274 const gint key_binding;
1275 const guint build_grp;
1276 const guint build_cmd;
1277 const gchar *fix_label;
1278 Callback *cb;
1279 } build_menu_specs[] = {
1280 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1281 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1282 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1283 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1284 {NULL, -1, MENU_FT_REST,
1285 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1286 {NULL, -1, MENU_SEPARATOR,
1287 GBF_SEP_1, NULL, NULL},
1288 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1289 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1290 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1291 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1292 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1293 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1294 {NULL, -1, MENU_NON_FT_REST,
1295 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1296 {NULL, -1, MENU_SEPARATOR,
1297 GBF_SEP_2, NULL, NULL},
1298 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1299 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1300 {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1301 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1302 {NULL, -1, MENU_SEPARATOR,
1303 GBF_SEP_3, NULL, NULL},
1304 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1305 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1306 {NULL, -1, MENU_EXEC_REST,
1307 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1308 {NULL, -1, MENU_SEPARATOR,
1309 GBF_SEP_4, NULL, NULL},
1310 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1311 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1312 {NULL, -1, MENU_DONE,
1313 0, NULL, NULL}
1317 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1318 struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
1320 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1322 if (bs->stock_id != NULL)
1324 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1325 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1327 gtk_widget_show(item);
1328 if (bs->key_binding >= 0)
1329 add_menu_accel(group, (guint) bs->key_binding, ag, item);
1330 gtk_container_add(GTK_CONTAINER(menu), item);
1331 if (bs->cb != NULL)
1333 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1335 menu_items.menu_item[grp][cmd] = item;
1339 static void create_build_menu(BuildMenuItems *build_menu_items)
1341 GtkWidget *menu;
1342 GtkAccelGroup *accel_group = gtk_accel_group_new();
1343 GeanyKeyGroup *keygroup = keybindings_get_core_group(GEANY_KEY_GROUP_BUILD);
1344 guint i, j;
1346 menu = gtk_menu_new();
1347 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1348 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1349 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1350 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1352 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1354 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1355 if (bs->build_grp == MENU_SEPARATOR)
1357 GtkWidget *item = gtk_separator_menu_item_new();
1358 gtk_widget_show(item);
1359 gtk_container_add(GTK_CONTAINER(menu), item);
1360 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1362 else if (bs->fix_label != NULL)
1364 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1365 GBG_FIXED, bs->build_cmd);
1367 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1369 guint grp = bs->build_grp - GEANY_GBG_COUNT;
1370 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1372 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1373 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1374 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1377 else
1379 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1380 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1381 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1384 build_menu_items->menu = menu;
1385 gtk_widget_show(menu);
1386 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1390 /* portability to various GTK versions needs checking
1391 * conforms to description of gtk_accel_label as child of menu item
1392 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1393 static void geany_menu_item_set_label(GtkWidget *w, const gchar *label)
1395 GtkWidget *c = gtk_bin_get_child(GTK_BIN(w));
1397 gtk_label_set_text_with_mnemonic(GTK_LABEL(c), label);
1401 /* * Update the build menu to reflect changes in configuration or status.
1403 * Sets the labels and number of visible items to match the highest
1404 * priority configured commands. Also sets sensitivity if build commands are
1405 * running and switches executes to stop when commands are running.
1407 * @param doc The current document, if available, to save looking it up.
1408 * If @c NULL it will be looked up.
1410 * Call this after modifying any fields of a GeanyBuildCommand structure.
1412 * @see Build Menu Configuration section of the Manual.
1415 void build_menu_update(GeanyDocument *doc)
1417 guint i, cmdcount, cmd, grp;
1418 gboolean vis = FALSE;
1419 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1420 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1421 GeanyBuildCommand *bc;
1423 g_return_if_fail(doc == NULL || doc->is_valid);
1425 if (menu_items.menu == NULL)
1426 create_build_menu(&menu_items);
1427 if (doc == NULL)
1428 doc = document_get_current();
1429 have_path = doc != NULL && doc->file_name != NULL;
1430 build_running = build_info.pid > (GPid) 1;
1431 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1432 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1434 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1435 switch (bs->build_grp)
1437 case MENU_SEPARATOR:
1438 if (vis == TRUE)
1440 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1441 vis = FALSE;
1443 else
1444 gtk_widget_hide(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1445 break;
1446 case MENU_NEXT_ERROR:
1447 case MENU_PREV_ERROR:
1448 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1449 vis |= TRUE;
1450 break;
1451 case MENU_COMMANDS:
1452 vis |= TRUE;
1453 break;
1454 default: /* all configurable commands */
1455 if (bs->build_grp >= GEANY_GBG_COUNT)
1457 grp = bs->build_grp - GEANY_GBG_COUNT;
1458 cmdcount = build_groups_count[grp];
1460 else
1462 grp = bs->build_grp;
1463 cmdcount = bs->build_cmd + 1;
1465 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1467 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1468 const gchar *label;
1469 bc = get_build_cmd(doc, grp, cmd, NULL);
1470 if (bc)
1471 label = bc->label;
1472 else
1473 label = NULL;
1475 if (grp < GEANY_GBG_EXEC)
1477 cmd_sensitivity =
1478 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1479 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1480 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1481 if (bc != NULL && !EMPTY(label))
1483 geany_menu_item_set_label(menu_item, label);
1484 gtk_widget_show_all(menu_item);
1485 vis |= TRUE;
1487 else
1488 gtk_widget_hide(menu_item);
1490 else
1492 GtkWidget *image;
1493 exec_running = run_info[cmd].pid > (GPid) 1;
1494 cmd_sensitivity = (bc != NULL) || exec_running;
1495 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1496 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1497 run_sensitivity = cmd_sensitivity;
1498 if (! exec_running)
1500 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1502 else
1504 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1506 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1507 run_running = exec_running;
1508 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1509 if (bc != NULL && !EMPTY(label))
1511 geany_menu_item_set_label(menu_item, label);
1512 gtk_widget_show_all(menu_item);
1513 vis |= TRUE;
1515 else
1516 gtk_widget_hide(menu_item);
1522 run_sensitivity &= (doc != NULL);
1523 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1524 && have_path && ! build_running;
1525 if (widgets.toolitem_build != NULL)
1526 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1527 can_make = FALSE;
1528 if (widgets.toolitem_make_all != NULL)
1529 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1530 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1531 && ! build_running));
1532 if (widgets.toolitem_make_custom != NULL)
1533 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1534 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1535 && ! build_running));
1536 if (widgets.toolitem_make_object != NULL)
1537 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1538 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1539 && ! build_running));
1540 if (widgets.toolitem_set_args != NULL)
1541 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1543 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1544 && have_path && ! build_running;
1545 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1546 gtk_action_set_sensitive(widgets.build_action, can_make);
1547 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1549 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1550 set_stop_button(run_running);
1555 /* Call build_menu_update() instead of calling this directly. */
1556 static void set_stop_button(gboolean stop)
1558 const gchar *button_stock_id = NULL;
1559 GtkToolButton *run_button;
1561 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1562 if (run_button != NULL)
1563 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1565 if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
1566 return;
1567 if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
1568 return;
1570 /* use the run button also as stop button */
1571 if (stop)
1573 if (run_button != NULL)
1574 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
1576 else
1578 if (run_button != NULL)
1579 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
1584 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1586 /* For now, just show the project dialog */
1587 if (app->project)
1588 project_build_properties();
1589 else
1590 show_build_commands_dialog();
1594 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1596 last_toolbutton_action = user_data;
1597 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1598 on_build_menu_item(menuitem, user_data);
1602 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1604 gchar *msg;
1606 last_toolbutton_action = user_data;
1607 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1608 msg = _("Build the current file with Make and the default target");
1609 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1610 msg = _("Build the current file with Make and the specified target");
1611 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1612 msg = _("Compile the current file with Make");
1613 else
1614 msg = NULL;
1615 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1616 on_build_menu_item(menuitem, user_data);
1620 static void kill_process(GPid *pid)
1622 GError *error = NULL;
1624 if (spawn_kill_process(*pid, &error))
1626 *pid = 0;
1627 build_menu_update(NULL);
1629 else
1631 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), error->message);
1632 g_error_free(error);
1637 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1639 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1640 msgwin_goto_compiler_file_line))
1642 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1644 else
1645 ui_set_statusbar(FALSE, _("No more build errors."));
1649 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1651 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1652 msgwin_goto_compiler_file_line))
1654 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1656 else
1657 ui_set_statusbar(FALSE, _("No more build errors."));
1661 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1663 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1665 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1667 else
1669 on_build_menu_item(NULL, last_toolbutton_action);
1674 /*------------------------------------------------------
1676 * Create and handle the build menu configuration dialog
1678 *-------------------------------------------------------*/
1679 typedef struct RowWidgets
1681 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1682 GeanyBuildSource src;
1683 GeanyBuildSource dst;
1684 GeanyBuildCommand *cmdsrc;
1685 guint grp;
1686 guint cmd;
1687 gboolean cleared;
1688 gboolean used_dst;
1689 } RowWidgets;
1691 #if GTK_CHECK_VERSION(3,0,0)
1692 typedef GdkRGBA InsensitiveColor;
1693 #else
1694 typedef GdkColor InsensitiveColor;
1695 #endif
1696 static InsensitiveColor insensitive_color;
1698 static void set_row_color(RowWidgets *r, InsensitiveColor *color)
1700 enum GeanyBuildCmdEntries i;
1702 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1704 if (i == GEANY_BC_LABEL)
1705 continue;
1707 #if GTK_CHECK_VERSION(3,0,0)
1708 gtk_widget_override_color(r->entries[i], GTK_STATE_FLAG_NORMAL, color);
1709 #else
1710 gtk_widget_modify_text(r->entries[i], GTK_STATE_NORMAL, color);
1711 #endif
1716 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1718 if (GTK_IS_BUTTON(wid))
1719 gtk_button_set_label(GTK_BUTTON(wid), text);
1720 else
1721 gtk_entry_set_text(GTK_ENTRY(wid), text);
1725 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1727 RowWidgets *r = user_data;
1728 guint src;
1729 enum GeanyBuildCmdEntries i;
1730 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1732 if (bc != NULL)
1734 r->cmdsrc = bc;
1735 r->src = src;
1736 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1738 set_build_command_entry_text(r->entries[i],
1739 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1742 else
1744 r->cmdsrc = NULL;
1745 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1747 set_build_command_entry_text(r->entries[i], "");
1750 r->used_dst = FALSE;
1751 set_row_color(r, &insensitive_color);
1752 r->cleared = TRUE;
1756 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1758 gtk_entry_set_text(regex,"");
1762 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1764 RowWidgets *r = user_data;
1765 GtkWidget *top_level = gtk_widget_get_toplevel(wid);
1766 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1767 gchar *str;
1769 if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
1770 str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
1771 else
1772 str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
1774 if (!str)
1775 return;
1777 gtk_button_set_label(GTK_BUTTON(wid), str);
1778 g_free(str);
1779 r->used_dst = TRUE;
1780 set_row_color(r, NULL);
1784 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1786 RowWidgets *r = user_data;
1788 r->used_dst = TRUE;
1789 set_row_color(r, NULL);
1793 /* Column headings, array NULL-terminated */
1794 static const gchar *colheads[] =
1796 "#",
1797 N_("Label"),
1798 N_("Command"),
1799 N_("Working directory"),
1800 N_("Reset"),
1801 NULL
1804 /* column names */
1805 #define DC_ITEM 0
1806 #define DC_ENTRIES 1
1807 #define DC_CLEAR 4
1808 #define DC_N_COL 5
1810 static const guint entry_x_padding = 3;
1811 static const guint entry_y_padding = 0;
1814 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1815 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
1817 GtkWidget *label, *clear, *clearicon;
1818 RowWidgets *roww;
1819 GeanyBuildCommand *bc;
1820 guint src;
1821 enum GeanyBuildCmdEntries i;
1822 guint column = 0;
1823 gchar *text;
1825 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
1827 text = g_strdup_printf("%d.", cmd + 1);
1828 label = gtk_label_new(text);
1829 g_free(text);
1830 #if GTK_CHECK_VERSION(3,0,0)
1832 GtkStyleContext *ctx = gtk_widget_get_style_context(label);
1834 gtk_style_context_save(ctx);
1835 gtk_style_context_get_color(ctx, GTK_STATE_FLAG_INSENSITIVE, &insensitive_color);
1836 gtk_style_context_restore(ctx);
1838 #else
1839 insensitive_color = gtk_widget_get_style(label)->text[GTK_STATE_INSENSITIVE];
1840 #endif
1841 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1842 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1843 roww = g_new0(RowWidgets, 1);
1844 roww->src = GEANY_BCS_COUNT;
1845 roww->grp = grp;
1846 roww->cmd = cmd;
1847 roww->dst = dst;
1848 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1850 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1852 column += 1;
1853 if (i == GEANY_BC_LABEL)
1855 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1856 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1857 gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1858 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
1860 else
1862 roww->entries[i] = gtk_entry_new();
1863 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
1865 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1866 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1868 column++;
1869 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1870 clear = gtk_button_new();
1871 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1872 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1873 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1874 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1875 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1876 if (bc != NULL)
1877 roww->src = src;
1879 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1881 const gchar *str = "";
1883 if (bc != NULL )
1885 if ((str = id_to_str(bc, i)) == NULL)
1886 str = "";
1887 else if (dst == src)
1888 roww->used_dst = TRUE;
1890 set_build_command_entry_text(roww->entries[i], str);
1892 if (bc != NULL && (dst > src))
1893 set_row_color(roww, &insensitive_color);
1894 if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1896 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1897 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1898 gtk_widget_set_sensitive(clear, FALSE);
1900 return roww;
1904 typedef struct BuildTableFields
1906 RowWidgets **rows;
1907 GtkWidget *fileregex;
1908 GtkWidget *nonfileregex;
1909 gchar **fileregexstring;
1910 gchar **nonfileregexstring;
1911 } BuildTableFields;
1914 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
1915 GeanyFiletype *ft)
1917 GtkWidget *label, *sep, *clearicon, *clear;
1918 BuildTableFields *fields;
1919 GtkTable *table;
1920 const gchar **ch;
1921 gchar *txt;
1922 guint col, row, cmdindex;
1923 guint cmd;
1924 guint src;
1925 gboolean sensitivity;
1926 guint sep_padding = entry_y_padding + 3;
1928 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1929 fields = g_new0(BuildTableFields, 1);
1930 fields->rows = g_new0(RowWidgets*, build_items_count);
1931 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1933 label = gtk_label_new(_(*ch));
1934 gtk_table_attach(table, label, col, col + 1, 0, 1,
1935 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1937 sep = gtk_hseparator_new();
1938 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1939 entry_x_padding, sep_padding);
1940 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1941 txt = g_strdup_printf(_("%s commands"), ft->name);
1942 else
1943 txt = g_strdup_printf(_("%s commands"), _("No filetype"));
1945 label = ui_label_new_bold(txt);
1946 g_free(txt);
1947 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1948 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1949 entry_x_padding, entry_y_padding);
1950 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1951 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1952 label = gtk_label_new(_("Error regular expression:"));
1953 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1954 entry_x_padding, entry_y_padding);
1955 fields->fileregex = gtk_entry_new();
1956 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
1957 sensitivity = (ft == NULL) ? FALSE : TRUE;
1958 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1960 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1961 if (src > dst)
1962 sensitivity = FALSE;
1964 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1965 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1966 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1967 clear = gtk_button_new();
1968 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1969 g_signal_connect_swapped(clear, "clicked",
1970 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
1971 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
1972 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1973 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
1974 gtk_widget_set_sensitive(clear, sensitivity);
1975 ++row;
1976 sep = gtk_hseparator_new();
1977 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1978 entry_x_padding, sep_padding);
1979 ++row;
1980 label = ui_label_new_bold(_("Independent commands"));
1981 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1982 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1983 entry_x_padding, entry_y_padding);
1984 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
1985 fields->rows[cmdindex] = build_add_dialog_row(
1986 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
1987 label = gtk_label_new(_("Error regular expression:"));
1988 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
1989 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1990 fields->nonfileregex = gtk_entry_new();
1991 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
1992 sensitivity = TRUE;
1993 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
1995 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
1996 sensitivity = src > dst ? FALSE : TRUE;
1998 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1999 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2000 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2001 clear = gtk_button_new();
2002 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2003 g_signal_connect_swapped(clear, "clicked",
2004 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
2005 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2006 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2007 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
2008 gtk_widget_set_sensitive(clear, sensitivity);
2009 ++row;
2010 label = gtk_label_new(NULL);
2011 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2012 _("Note: Item 2 opens a dialog and appends the response to the command."));
2013 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2014 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2015 entry_x_padding, entry_y_padding);
2016 ++row;
2017 sep = gtk_hseparator_new();
2018 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2019 entry_x_padding, sep_padding);
2020 ++row;
2021 label = ui_label_new_bold(_("Execute commands"));
2022 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2023 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2024 entry_x_padding, entry_y_padding);
2025 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
2026 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
2027 sep = gtk_hseparator_new();
2028 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2029 entry_x_padding, sep_padding);
2030 ++row;
2031 label = gtk_label_new(NULL);
2032 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2033 _("%d, %e, %f, %p, %l are substituted in command and directory fields, see manual for details."));
2034 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2035 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2036 entry_x_padding, entry_y_padding);
2037 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2038 ++row;
2039 *table_data = fields;
2040 return GTK_WIDGET(table);
2044 void build_free_fields(BuildTableData table_data)
2046 guint cmdindex;
2048 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2049 g_free(table_data->rows[cmdindex]);
2050 g_free(table_data->rows);
2051 g_free(table_data);
2055 /* string compare where null pointers match null or 0 length strings */
2056 #if 0
2057 static gint stcmp(const gchar *a, const gchar *b)
2059 if (a == NULL && b == NULL)
2060 return 0;
2061 if (a == NULL && b != NULL)
2062 return strlen(b);
2063 if (a != NULL && b == NULL)
2064 return strlen(a);
2065 return strcmp(a, b);
2067 #endif
2070 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2072 if (GTK_IS_BUTTON(wid))
2073 return gtk_button_get_label(GTK_BUTTON(wid));
2074 else
2075 return gtk_entry_get_text(GTK_ENTRY(wid));
2079 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
2081 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2082 gboolean changed = FALSE;
2083 enum GeanyBuildCmdEntries i;
2085 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2087 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2089 if (table_data->rows[drow]->cleared)
2091 if (dst->dst[grp] != NULL)
2093 if (*(dst->dst[grp]) == NULL)
2094 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2095 (*(dst->dst[grp]))[cmd].exists = FALSE;
2096 (*(dst->dst[grp]))[cmd].changed = TRUE;
2097 changed = TRUE;
2100 if (table_data->rows[drow]->used_dst == TRUE)
2102 if (dst->dst[grp] != NULL)
2104 if (*(dst->dst[grp]) == NULL)
2105 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2106 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2107 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2108 (*(dst->dst[grp]))[cmd].exists = TRUE;
2109 (*(dst->dst[grp]))[cmd].changed = TRUE;
2110 changed = TRUE;
2113 else
2115 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2116 g_free(entries[i]);
2118 return changed;
2122 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2124 gboolean changed = FALSE;
2125 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2127 if (((src == NULL /* originally there was no regex */
2128 || *src == NULL) /* or it was NULL*/
2129 && !EMPTY(reg)) /* and something was typed */
2130 || (src != NULL /* originally there was a regex*/
2131 && (*src == NULL /* and either it was NULL */
2132 || strcmp(*src, reg) != 0))) /* or it has been changed */
2134 if (dst != NULL)
2136 SETPTR(*dst, g_strdup(reg));
2137 changed = TRUE;
2140 return changed;
2144 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2146 guint cmdindex, cmd;
2147 gboolean changed = FALSE;
2149 if (response == GTK_RESPONSE_ACCEPT)
2151 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2152 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2153 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2154 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2155 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2156 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2157 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2158 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2160 return changed;
2164 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2166 BuildDestination menu_dst;
2168 if (ft != NULL)
2170 menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds);
2171 menu_dst.fileregexstr = &(ft->priv->projerror_regex_string);
2173 else
2175 menu_dst.dst[GEANY_GBG_FT] = NULL;
2176 menu_dst.fileregexstr = NULL;
2178 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2179 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2180 menu_dst.nonfileregexstr = &regex_proj;
2182 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2186 static void show_build_commands_dialog(void)
2188 GtkWidget *dialog, *table, *vbox;
2189 GeanyDocument *doc = document_get_current();
2190 GeanyFiletype *ft = NULL;
2191 const gchar *title = _("Set Build Commands");
2192 gint response;
2193 BuildTableData table_data;
2194 BuildDestination prefdsts;
2196 if (doc != NULL)
2197 ft = doc->file_type;
2198 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2199 GTK_DIALOG_DESTROY_WITH_PARENT,
2200 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2201 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2202 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2203 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2204 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2205 gtk_widget_show_all(dialog);
2206 /* run modally to prevent user changing idx filetype */
2207 response = gtk_dialog_run(GTK_DIALOG(dialog));
2209 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2210 if (ft != NULL)
2212 prefdsts.dst[GEANY_GBG_FT] = &(ft->priv->homefilecmds);
2213 prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string);
2214 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds);
2216 else
2218 prefdsts.dst[GEANY_GBG_FT] = NULL;
2219 prefdsts.fileregexstr = NULL;
2220 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2222 prefdsts.nonfileregexstr = &regex_pref;
2223 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2224 filetypes_save_commands(ft);
2225 build_free_fields(table_data);
2227 build_menu_update(doc);
2228 gtk_widget_destroy(dialog);
2232 /* Creates the relevant build menu if necessary. */
2233 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2235 BuildMenuItems *items;
2237 items = &menu_items;
2238 if (items->menu == NULL)
2239 create_build_menu(items);
2240 return items;
2244 /*----------------------------------------------------------
2246 * Load and store configuration
2248 * ---------------------------------------------------------*/
2249 static const gchar *build_grp_name = "build-menu";
2251 /* config format for build-menu group is prefix_gg_nn_xx=value
2252 * where gg = FT, NF, EX for the command group
2253 * nn = 2 digit command number
2254 * xx = LB for label, CM for command and WD for working dir */
2255 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2256 static const gchar *fixedkey="xx_xx_xx";
2258 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2259 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2260 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2262 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2263 gchar *prefix, gboolean loc)
2265 guint cmd;
2266 gsize prefixlen; /* NOTE prefixlen used in macros above */
2267 GeanyBuildCommand *dstcmd;
2268 gchar *key;
2269 static gchar cmdbuf[3] = " ";
2271 if (*dst == NULL)
2272 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2273 dstcmd = *dst;
2274 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2275 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2276 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2278 gchar *label;
2279 if (cmd >= 100)
2280 break; /* ensure no buffer overflow */
2281 sprintf(cmdbuf, "%02u", cmd);
2282 set_key_grp(key, groups[grp]);
2283 set_key_cmd(key, cmdbuf);
2284 set_key_fld(key, "LB");
2285 if (loc)
2286 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2287 else
2288 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2289 if (label != NULL)
2291 dstcmd[cmd].exists = TRUE;
2292 SETPTR(dstcmd[cmd].label, label);
2293 set_key_fld(key,"CM");
2294 SETPTR(dstcmd[cmd].command,
2295 g_key_file_get_string(config, build_grp_name, key, NULL));
2296 set_key_fld(key,"WD");
2297 SETPTR(dstcmd[cmd].working_dir,
2298 g_key_file_get_string(config, build_grp_name, key, NULL));
2300 else dstcmd[cmd].exists = FALSE;
2302 g_free(key);
2306 /* for the specified source load new format build menu items or try to make some sense of
2307 * old format setings, not done perfectly but better than ignoring them */
2308 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2310 GeanyFiletype *ft;
2311 GeanyProject *pj;
2312 gchar **ftlist;
2313 gchar *value, *basedir, *makebasedir;
2314 gboolean bvalue = FALSE;
2316 if (g_key_file_has_group(config, build_grp_name))
2318 switch (src)
2320 case GEANY_BCS_FT:
2321 ft = (GeanyFiletype*)p;
2322 if (ft == NULL)
2323 return;
2324 build_load_menu_grp(config, &(ft->priv->filecmds), GEANY_GBG_FT, NULL, TRUE);
2325 build_load_menu_grp(config, &(ft->priv->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2326 build_load_menu_grp(config, &(ft->priv->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2327 SETPTR(ft->error_regex_string,
2328 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2329 break;
2330 case GEANY_BCS_HOME_FT:
2331 ft = (GeanyFiletype*)p;
2332 if (ft == NULL)
2333 return;
2334 build_load_menu_grp(config, &(ft->priv->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2335 build_load_menu_grp(config, &(ft->priv->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2336 SETPTR(ft->priv->homeerror_regex_string,
2337 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2338 break;
2339 case GEANY_BCS_PREF:
2340 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2341 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2342 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2343 break;
2344 case GEANY_BCS_PROJ:
2345 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2346 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2347 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2348 pj = (GeanyProject*)p;
2349 if (p == NULL)
2350 return;
2351 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2352 if (ftlist != NULL)
2354 gchar **ftname;
2355 if (pj->priv->build_filetypes_list == NULL)
2356 pj->priv->build_filetypes_list = g_ptr_array_new();
2357 g_ptr_array_set_size(pj->priv->build_filetypes_list, 0);
2358 for (ftname = ftlist; *ftname != NULL; ++ftname)
2360 ft = filetypes_lookup_by_name(*ftname);
2361 if (ft != NULL)
2363 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2364 g_ptr_array_add(pj->priv->build_filetypes_list, ft);
2365 SETPTR(ft->priv->projerror_regex_string,
2366 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2367 g_free(regkey);
2368 build_load_menu_grp(config, &(ft->priv->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2369 build_load_menu_grp(config, &(ft->priv->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2372 g_free(ftlist);
2374 break;
2375 default: /* defaults don't load from config, see build_init */
2376 break;
2380 /* load old [build_settings] values if there is no value defined by [build-menu] */
2382 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2383 /* TODO: rewrite as function */
2384 #define ASSIGNIF(type, id, string, value) \
2385 do { \
2386 gchar *ASSIGNF__value = (value); \
2387 if (!EMPTY(ASSIGNF__value) && ! type[GBO_TO_CMD(id)].exists) { \
2388 type[GBO_TO_CMD(id)].exists = TRUE; \
2389 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(string)); \
2390 SETPTR(type[GBO_TO_CMD(id)].command, ASSIGNF__value); \
2391 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL); \
2392 type[GBO_TO_CMD(id)].old = TRUE; \
2393 } else \
2394 g_free(ASSIGNF__value); \
2395 } while (0)
2397 switch (src)
2399 case GEANY_BCS_FT:
2400 ft = (GeanyFiletype*)p;
2401 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2402 if (value != NULL)
2404 if (ft->priv->filecmds == NULL)
2405 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2406 ASSIGNIF(ft->priv->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2408 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2409 if (value != NULL)
2411 if (ft->priv->filecmds == NULL)
2412 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2413 ASSIGNIF(ft->priv->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2415 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2416 if (value != NULL)
2418 if (ft->priv->execcmds == NULL)
2419 ft->priv->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2420 ASSIGNIF(ft->priv->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2422 if (ft->error_regex_string == NULL)
2423 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2424 break;
2425 case GEANY_BCS_PROJ:
2426 if (non_ft_pref == NULL)
2427 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2428 basedir = project_get_base_path();
2429 if (basedir == NULL)
2430 basedir = g_strdup("%d");
2431 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2432 if (bvalue)
2433 makebasedir = g_strdup(basedir);
2434 else
2435 makebasedir = g_strdup("%d");
2436 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2437 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
2438 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2439 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2440 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2441 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
2442 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2443 if (!EMPTY(value))
2445 if (exec_proj == NULL)
2446 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2447 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2449 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2450 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2451 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
2452 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
2453 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2456 g_free(makebasedir);
2457 g_free(basedir);
2458 break;
2459 case GEANY_BCS_PREF:
2460 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2461 if (value != NULL)
2463 if (non_ft_pref == NULL)
2464 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2465 ASSIGNIF(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
2466 g_strdup_printf("%s ", value));
2467 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2468 g_strdup_printf("%s %%e.o",value));
2469 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2471 break;
2472 default:
2473 break;
2478 static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2480 guint cmd;
2481 gsize prefixlen; /* NOTE prefixlen used in macros above */
2482 gchar *key;
2483 guint count = 0;
2484 enum GeanyBuildCmdEntries i;
2486 if (src == NULL)
2487 return 0;
2488 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2489 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2490 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2492 if (src[cmd].exists) ++count;
2493 if (src[cmd].changed)
2495 static gchar cmdbuf[4] = " ";
2496 if (cmd >= 100)
2497 break; /* ensure no buffer overflow */
2498 sprintf(cmdbuf, "%02u", cmd);
2499 set_key_grp(key, groups[grp]);
2500 set_key_cmd(key, cmdbuf);
2501 if (src[cmd].exists)
2503 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2505 set_key_fld(key, config_keys[i]);
2506 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2509 else
2511 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2513 set_key_fld(key, config_keys[i]);
2514 g_key_file_remove_key(config, build_grp_name, key, NULL);
2519 g_free(key);
2520 return count;
2524 typedef struct ForEachData
2526 GKeyFile *config;
2527 GPtrArray *ft_names;
2528 } ForEachData;
2531 static void foreach_project_filetype(gpointer data, gpointer user_data)
2533 GeanyFiletype *ft = data;
2534 ForEachData *d = user_data;
2535 guint i = 0;
2536 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2538 i += build_save_menu_grp(d->config, ft->priv->projfilecmds, GEANY_GBG_FT, ft->name);
2539 i += build_save_menu_grp(d->config, ft->priv->projexeccmds, GEANY_GBG_EXEC, ft->name);
2540 if (!EMPTY(ft->priv->projerror_regex_string))
2542 g_key_file_set_string(d->config, build_grp_name, regkey, ft->priv->projerror_regex_string);
2543 i++;
2545 else
2546 g_key_file_remove_key(d->config, build_grp_name, regkey, NULL);
2547 g_free(regkey);
2548 if (i > 0)
2549 g_ptr_array_add(d->ft_names, ft->name);
2553 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2554 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2556 GeanyFiletype *ft;
2557 GeanyProject *pj;
2558 ForEachData data;
2560 switch (src)
2562 case GEANY_BCS_HOME_FT:
2563 ft = (GeanyFiletype*)ptr;
2564 if (ft == NULL)
2565 return;
2566 build_save_menu_grp(config, ft->priv->homefilecmds, GEANY_GBG_FT, NULL);
2567 build_save_menu_grp(config, ft->priv->homeexeccmds, GEANY_GBG_EXEC, NULL);
2568 if (!EMPTY(ft->priv->homeerror_regex_string))
2569 g_key_file_set_string(config, build_grp_name, "error_regex", ft->priv->homeerror_regex_string);
2570 else
2571 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2572 break;
2573 case GEANY_BCS_PREF:
2574 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2575 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2576 if (!EMPTY(regex_pref))
2577 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2578 else
2579 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2580 break;
2581 case GEANY_BCS_PROJ:
2582 pj = (GeanyProject*)ptr;
2583 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2584 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2585 if (!EMPTY(regex_proj))
2586 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2587 else
2588 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2589 if (pj->priv->build_filetypes_list != NULL)
2591 data.config = config;
2592 data.ft_names = g_ptr_array_new();
2593 g_ptr_array_foreach(pj->priv->build_filetypes_list, foreach_project_filetype, (gpointer)(&data));
2594 if (data.ft_names->pdata != NULL)
2595 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2596 (const gchar**)(data.ft_names->pdata), data.ft_names->len);
2597 else
2598 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2599 g_ptr_array_free(data.ft_names, TRUE);
2601 break;
2602 default: /* defaults and GEANY_BCS_FT can't save */
2603 break;
2608 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2609 void build_set_group_count(GeanyBuildGroup grp, gint count)
2611 guint i, sum;
2613 g_return_if_fail(count >= 0);
2615 if ((guint) count > build_groups_count[grp])
2616 build_groups_count[grp] = (guint) count;
2617 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2618 sum += build_groups_count[i];
2619 build_items_count = sum;
2623 /** Get the count of commands for the group
2625 * Get the number of commands in the group specified by @a grp.
2627 * @param grp the group of the specified menu item.
2629 * @return a count of the number of commands in the group
2632 GEANY_API_SYMBOL
2633 guint build_get_group_count(const GeanyBuildGroup grp)
2635 g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
2636 return build_groups_count[grp];
2640 static void on_project_close(void)
2642 /* remove project regexen */
2643 SETPTR(regex_proj, NULL);
2647 static struct
2649 const gchar *label;
2650 const gchar *command;
2651 const gchar *working_dir;
2652 GeanyBuildCommand **ptr;
2653 gint index;
2654 } default_cmds[] = {
2655 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2656 { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2657 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2658 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2659 { NULL, NULL, NULL, NULL, 0 }
2663 void build_init(void)
2665 GtkWidget *item;
2666 GtkWidget *toolmenu;
2667 gint cmdindex;
2669 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2671 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2672 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2673 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2674 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2676 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2678 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2679 cmd->exists = TRUE;
2680 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2681 cmd->command = g_strdup(default_cmds[cmdindex].command);
2682 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2685 /* create the toolbar Build item sub menu */
2686 toolmenu = gtk_menu_new();
2687 g_object_ref(toolmenu);
2689 /* build the code */
2690 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2691 gtk_widget_show(item);
2692 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2693 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2694 GBO_TO_POINTER(GEANY_GBO_BUILD));
2695 widgets.toolitem_build = item;
2697 item = gtk_separator_menu_item_new();
2698 gtk_widget_show(item);
2699 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2701 /* build the code with make all */
2702 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2703 gtk_widget_show(item);
2704 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2705 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2706 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2707 widgets.toolitem_make_all = item;
2709 /* build the code with make custom */
2710 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2711 gtk_widget_show(item);
2712 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2713 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2714 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2715 widgets.toolitem_make_custom = item;
2717 /* build the code with make object */
2718 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
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_OBJECT));
2723 widgets.toolitem_make_object = item;
2725 item = gtk_separator_menu_item_new();
2726 gtk_widget_show(item);
2727 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2729 /* arguments */
2730 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
2731 gtk_widget_show(item);
2732 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2733 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2734 widgets.toolitem_set_args = item;
2736 /* get toolbar action pointers */
2737 widgets.build_action = toolbar_get_action_by_name("Build");
2738 widgets.compile_action = toolbar_get_action_by_name("Compile");
2739 widgets.run_action = toolbar_get_action_by_name("Run");
2740 widgets.toolmenu = toolmenu;
2741 /* set the submenu to the toolbar item */
2742 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);