Opening recent projects: Don't ask whether to close current project
[geany-mirror.git] / src / build.c
blob058ec86510f97ca57433daf649649fb7ddc73cdd
1 /*
2 * build.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005 The Geany contributors
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * Build commands and menu items.
24 /* TODO: tidy code:
25 * Use intermediate pointers for common subexpressions.
26 * Replace defines with enums.
27 * Other TODOs in code. */
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include "app.h"
34 #include "build.h"
35 #include "dialogs.h"
36 #include "document.h"
37 #include "filetypesprivate.h"
38 #include "geanymenubuttonaction.h"
39 #include "geanyobject.h"
40 #include "keybindingsprivate.h"
41 #include "msgwindow.h"
42 #include "prefs.h"
43 #include "projectprivate.h"
44 #include "sciwrappers.h"
45 #include "spawn.h"
46 #include "support.h"
47 #include "toolbar.h"
48 #include "ui_utils.h"
49 #include "utils.h"
50 #include "vte.h"
51 #include "win32.h"
53 #include "gtkcompat.h"
55 #include <stdlib.h>
56 #include <string.h>
57 #include <sys/stat.h>
58 #include <unistd.h>
59 #include <errno.h>
60 #include <glib/gstdio.h>
64 /* Number of editor indicators to draw - limited as this can affect performance */
65 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
68 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
70 static gchar *current_dir_entered = NULL;
72 typedef struct RunInfo
74 GPid pid;
75 gint file_type_id;
76 } RunInfo;
78 static RunInfo *run_info;
80 #ifndef G_OS_WIN32
81 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh";
82 #endif
84 /* Order is important (see GBO_TO_GBG, GBO_TO_CMD below) */
85 /* * Geany Known Build Commands.
86 * These commands are named after their default use.
87 * Only these commands can currently have keybindings.
88 **/
89 typedef enum
91 GEANY_GBO_COMPILE, /* *< default compile file */
92 GEANY_GBO_BUILD, /* *< default build file */
93 GEANY_GBO_MAKE_ALL, /* *< default make */
94 GEANY_GBO_CUSTOM, /* *< default make user specified target */
95 GEANY_GBO_MAKE_OBJECT, /* *< default make object, make %e.o */
96 GEANY_GBO_EXEC, /* *< default execute ./%e */
97 GEANY_GBO_COUNT /* *< count of how many */
98 } GeanyBuildType;
100 /* * Convert @c GeanyBuildType to @c GeanyBuildGroup.
102 * This macro converts @c GeanyBuildType enum values (the "known" commands)
103 * to the group they are part of.
105 * @param gbo the @c GeanyBuildType value.
107 * @return the @c GeanyBuildGroup group that @a gbo is in.
109 * Note this is a macro so that it can be used in static initialisers.
111 #define GBO_TO_GBG(gbo) \
112 ((gbo) > GEANY_GBO_EXEC ? GEANY_GBG_COUNT : \
113 ((gbo) >= GEANY_GBO_EXEC ? GEANY_GBG_EXEC : \
114 ((gbo) >= GEANY_GBO_MAKE_ALL ? GEANY_GBG_NON_FT : GEANY_GBG_FT)))
116 /* * Convert @c GeanyBuildType to command index.
118 * This macro converts @c GeanyBuildType enum values (the "known" commands)
119 * to the index within the group.
121 * @param gbo the @c GeanyBuildType value.
123 * @return the index of the @a gbo command in its group.
125 * Note this is a macro so that it can be used in static initialisers.
127 #define GBO_TO_CMD(gbo) \
128 ((gbo) >= GEANY_GBO_COUNT ? (gbo) - GEANY_GBO_COUNT : \
129 ((gbo) >= GEANY_GBO_EXEC ? (gbo) - GEANY_GBO_EXEC : \
130 ((gbo) >= GEANY_GBO_MAKE_ALL ? \
131 (gbo) - GEANY_GBO_MAKE_ALL : (gbo))))
133 /* pack group (<8) and command (<32) into a user_data pointer */
134 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
135 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
136 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
137 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
139 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
141 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
143 static struct
145 GtkAction *run_action;
146 GtkAction *compile_action;
147 GtkAction *build_action;
148 GtkWidget *toolmenu;
150 GtkWidget *toolitem_build;
151 GtkWidget *toolitem_make_all;
152 GtkWidget *toolitem_make_custom;
153 GtkWidget *toolitem_make_object;
154 GtkWidget *toolitem_set_args;
156 widgets;
158 static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
159 static guint build_items_count = 9;
161 static void build_exit_cb(GPid pid, gint status, gpointer user_data);
162 static void build_iofunc(GString *string, GIOCondition condition, gpointer data);
163 #ifndef G_OS_WIN32
164 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
165 #endif
166 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
167 static void set_stop_button(gboolean stop);
168 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
169 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
170 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
171 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
172 static void kill_process(GPid *pid);
173 static void show_build_result_message(gboolean failure);
174 static void process_build_output_line(gchar *msg, gint color);
175 static void show_build_commands_dialog(void);
176 static void on_build_menu_item(GtkWidget *w, gpointer user_data);
178 void build_finalize(void)
180 g_free(build_info.dir);
181 g_free(build_info.custom_target);
183 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
184 gtk_widget_destroy(menu_items.menu);
188 /* note: copied from keybindings.c, may be able to go away */
189 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
190 GtkAccelGroup *accel_group, GtkWidget *menuitem)
192 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
194 if (kb->key != 0)
195 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
196 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
200 /* convenience routines to access parts of GeanyBuildCommand */
201 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
203 switch (id)
205 case GEANY_BC_LABEL:
206 return bc->label;
207 case GEANY_BC_COMMAND:
208 return bc->command;
209 case GEANY_BC_WORKING_DIR:
210 return bc->working_dir;
212 g_assert(0);
213 return NULL;
217 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
219 switch (id)
221 case GEANY_BC_LABEL:
222 SETPTR(bc->label, str);
223 break;
224 case GEANY_BC_COMMAND:
225 SETPTR(bc->command, str);
226 break;
227 case GEANY_BC_WORKING_DIR:
228 SETPTR(bc->working_dir, str);
229 break;
230 default:
231 g_assert(0);
236 static const gchar *config_keys[GEANY_BC_CMDENTRIES_COUNT] = {
237 "LB", /* label */
238 "CM", /* command */
239 "WD" /* working directory */
242 /*-----------------------------------------------------
244 * Execute commands and handle results
246 *-----------------------------------------------------*/
248 /* the various groups of commands not in the filetype struct */
249 static GeanyBuildCommand *ft_def = NULL;
250 static GeanyBuildCommand *non_ft_proj = NULL;
251 static GeanyBuildCommand *non_ft_pref = NULL;
252 static GeanyBuildCommand *non_ft_def = NULL;
253 static GeanyBuildCommand *exec_proj = NULL;
254 static GeanyBuildCommand *exec_pref = NULL;
255 static GeanyBuildCommand *exec_def = NULL;
256 /* and the regexen not in the filetype structure */
257 static gchar *regex_pref = NULL;
258 /* project non-fileregex string */
259 static gchar *regex_proj = NULL;
261 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
262 #ifndef PRINTBUILDCMDS
263 #define PRINTBUILDCMDS FALSE
264 #endif
265 static gboolean printbuildcmds = PRINTBUILDCMDS;
268 /* for debug only, print the commands structures in priority order */
269 static void printfcmds(void)
271 #if 0
272 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
273 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
274 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
275 { &ft_def, NULL, NULL, NULL, NULL, NULL },
276 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
277 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
279 GeanyFiletype *ft = NULL;
280 GeanyDocument *doc;
281 gint i, j, k, l, m;
282 enum GeanyBuildCmdEntries n;
283 gint cc[GEANY_BCS_COUNT];
284 gchar c;
286 doc = document_get_current();
287 if (doc != NULL)
288 ft = doc->file_type;
289 if (ft != NULL)
291 printf("filetype %s\n",ft->name);
292 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->priv->filecmds);
293 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->priv->homefilecmds);
294 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->priv->projfilecmds);
295 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->priv->ftdefcmds);
296 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->priv->execcmds);
297 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->priv->homeexeccmds);
298 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->priv->projexeccmds);
300 for (i = 0; i < GEANY_BCS_COUNT; ++i)
302 m = 1;
303 for (j = 0; j < GEANY_GBG_COUNT; ++j)
305 for (k = 0; k < build_groups_count[j]; ++k)
306 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
308 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
310 if ((*(cl[j][i]))[k].entries[n] != NULL &&
311 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
313 m = l;
318 cc[i] = m;
320 for (i = 0; i < GEANY_GBG_COUNT; ++i)
322 for (k = 0; k < build_groups_count[i]; ++k)
324 for (l = 0; l < 2; ++l)
326 c = ' ';
327 for (j = 0; j < GEANY_BCS_COUNT; ++j)
329 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
331 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
333 if ((*(cl[i][j]))[k].entries[i] != NULL)
334 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
335 else
336 printf("%c %*.*s",c,cc[j],cc[j]," ");
339 else
340 printf("%c %*.*s",c,cc[j],cc[j]," ");
341 c = ',';
343 printf("\n");
346 printf("\n");
348 #endif
352 /* macros to save typing and make the logic visible */
353 #define return_cmd_if(src, cmds)\
354 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
356 *fr=src; \
357 if (printbuildcmds) \
358 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
359 return &(cmds[cmdindex]); \
362 #define return_ft_cmd_if(src, cmds)\
363 if (ft != NULL && ft->priv->cmds != NULL \
364 && ft->priv->cmds[cmdindex].exists && below>src)\
366 *fr=src; \
367 if (printbuildcmds) \
368 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
369 return &(ft->priv->cmds[cmdindex]); \
373 /* get the next lowest command taking priority into account */
374 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
375 guint below, guint *from)
377 /* Note: parameter below used in macros above */
378 GeanyFiletype *ft = NULL;
379 guint sink, *fr = &sink;
381 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
383 if (printbuildcmds)
384 printfcmds();
385 if (cmdgrp >= GEANY_GBG_COUNT)
386 return NULL;
387 if (from != NULL)
388 fr = from;
389 if (doc == NULL)
390 doc = document_get_current();
391 if (doc != NULL)
392 ft = doc->file_type;
394 switch (cmdgrp)
396 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
397 if (ft != NULL)
399 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
400 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
401 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
403 return_cmd_if(GEANY_BCS_DEF, ft_def);
404 break;
405 case GEANY_GBG_NON_FT: /* order proj, pref, def */
406 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
407 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
408 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
409 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
410 break;
411 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
412 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
413 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
414 return_cmd_if(GEANY_BCS_PREF, exec_pref);
415 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
416 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
417 return_cmd_if(GEANY_BCS_DEF, exec_def);
418 break;
419 default:
420 break;
422 return NULL;
426 /* shortcut to start looking at the top */
427 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
429 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
433 #define return_nonblank_regex(src, ptr)\
434 if (!EMPTY(ptr)) \
435 { *fr = (src); return &(ptr); }
438 /* like get_build_cmd, but for regexen, used by filetypes */
439 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
441 guint sink, *fr = &sink;
443 if (from != NULL)
444 fr = from;
445 if (grp == GEANY_GBG_FT)
447 if (ft == NULL)
449 GeanyDocument *doc = document_get_current();
450 if (doc != NULL)
451 ft = doc->file_type;
453 if (ft == NULL)
454 return NULL;
455 return_nonblank_regex(GEANY_BCS_PROJ, ft->priv->projerror_regex_string);
456 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->priv->homeerror_regex_string);
457 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
459 else if (grp == GEANY_GBG_NON_FT)
461 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
462 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
464 return NULL;
468 static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
470 GeanyDocument *doc;
471 GeanyFiletype *ft = NULL;
473 switch (grp)
475 case GEANY_GBG_FT:
476 if ((doc = document_get_current()) == NULL)
477 return NULL;
478 if ((ft = doc->file_type) == NULL)
479 return NULL;
480 switch (src)
482 case GEANY_BCS_DEF: return &(ft->priv->ftdefcmds);
483 case GEANY_BCS_FT: return &(ft->priv->filecmds);
484 case GEANY_BCS_HOME_FT: return &(ft->priv->homefilecmds);
485 case GEANY_BCS_PREF: return &(ft->priv->homefilecmds);
486 case GEANY_BCS_PROJ: return &(ft->priv->projfilecmds);
487 default: return NULL;
489 break;
490 case GEANY_GBG_NON_FT:
491 switch (src)
493 case GEANY_BCS_DEF: return &(non_ft_def);
494 case GEANY_BCS_PREF: return &(non_ft_pref);
495 case GEANY_BCS_PROJ: return &(non_ft_proj);
496 default: return NULL;
498 break;
499 case GEANY_GBG_EXEC:
500 if ((doc = document_get_current()) != NULL)
501 ft = doc->file_type;
502 switch (src)
504 case GEANY_BCS_DEF: return &(exec_def);
505 case GEANY_BCS_FT: return ft ? &(ft->priv->execcmds): NULL;
506 case GEANY_BCS_HOME_FT: return ft ? &(ft->priv->homeexeccmds): NULL;
507 case GEANY_BCS_PROJ_FT: return ft ? &(ft->priv->projexeccmds): NULL;
508 case GEANY_BCS_PREF: return &(exec_pref);
509 case GEANY_BCS_PROJ: return &(exec_proj);
510 default: return NULL;
512 break;
513 default:
514 return NULL;
519 /* get pointer to the command group array */
520 static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
522 GeanyBuildCommand **g = get_build_group_pointer(src, grp);
523 if (g == NULL) return NULL;
524 return *g;
528 /** Remove the specified Build menu item.
530 * Makes the specified menu item configuration no longer exist. This
531 * is different to setting fields to blank because the menu item
532 * will be deleted from the configuration file on saving
533 * (except the system filetypes settings @see Build Menu Configuration
534 * section of the Manual).
536 * @param src the source of the menu item to remove.
537 * @param grp the group of the command to remove.
538 * @param cmd the index (from 0) of the command within the group. A negative
539 * value will remove the whole group.
541 * If any parameter is out of range does nothing.
543 * Updates the menu.
546 GEANY_API_SYMBOL
547 void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
549 GeanyBuildCommand *bc;
550 guint i;
552 bc = get_build_group(src, grp);
553 if (bc == NULL)
554 return;
555 if (cmd < 0)
557 for (i = 0; i < build_groups_count[grp]; ++i)
558 bc[i].exists = FALSE;
560 else if ((guint) cmd < build_groups_count[grp])
561 bc[cmd].exists = FALSE;
565 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
567 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
568 * hidden by higher priority commands.
570 * @param src the source of the specified menu item.
571 * @param grp the group of the specified menu item.
572 * @param cmd the index of the command within the group.
574 * @return a pointer to the @a GeanyBuildCommand structure or @c NULL if it doesn't exist.
575 * This is a pointer to an internal structure and must not be freed.
577 * @see build_menu_update
579 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
581 GeanyBuildCommand *bc;
583 g_return_val_if_fail(src < GEANY_BCS_COUNT, NULL);
584 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
585 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
587 bc = get_build_group(src, grp);
588 if (bc == NULL)
589 return NULL;
590 return &(bc[cmd]);
594 /** Get the string for the menu item field.
596 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
597 * that the menu item will use if activated.
599 * @param grp the group of the specified menu item.
600 * @param cmd the index of the command within the group.
601 * @param fld the field to return
603 * @return @nullable a pointer to the constant string or @c NULL if it doesn't exist.
604 * This is a pointer to an internal structure and must not be freed.
607 GEANY_API_SYMBOL
608 const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
609 const GeanyBuildCmdEntries fld)
611 GeanyBuildCommand *c;
612 gchar *str = NULL;
614 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
615 g_return_val_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT, NULL);
616 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
618 c = get_build_cmd(NULL, grp, cmd, NULL);
619 if (c == NULL) return NULL;
620 switch (fld)
622 case GEANY_BC_COMMAND:
623 str = c->command;
624 break;
625 case GEANY_BC_LABEL:
626 str = c->label;
627 break;
628 case GEANY_BC_WORKING_DIR:
629 str = c->working_dir;
630 break;
631 default:
632 break;
634 return str;
637 /** Set the string for the menu item field.
639 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
641 * @param src the source of the menu item
642 * @param grp the group of the specified menu item.
643 * @param cmd the index of the menu item within the group.
644 * @param fld the field in the menu item command to set
645 * @param val the value to set the field to, is copied
648 GEANY_API_SYMBOL
649 void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp,
650 const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
652 GeanyBuildCommand **g;
654 g_return_if_fail(src < GEANY_BCS_COUNT);
655 g_return_if_fail(grp < GEANY_GBG_COUNT);
656 g_return_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT);
657 g_return_if_fail(cmd < build_groups_count[grp]);
659 g = get_build_group_pointer(src, grp);
660 if (g == NULL) return;
661 if (*g == NULL )
663 *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
665 switch (fld)
667 case GEANY_BC_COMMAND:
668 SETPTR((*g)[cmd].command, g_strdup(val));
669 (*g)[cmd].exists = TRUE;
670 break;
671 case GEANY_BC_LABEL:
672 SETPTR((*g)[cmd].label, g_strdup(val));
673 (*g)[cmd].exists = TRUE;
674 break;
675 case GEANY_BC_WORKING_DIR:
676 SETPTR((*g)[cmd].working_dir, g_strdup(val));
677 (*g)[cmd].exists = TRUE;
678 break;
679 default:
680 break;
682 build_menu_update(NULL);
685 /** Activate the menu item.
687 * Activate the menu item specified by @a grp and @a cmd.
689 * @param grp the group of the specified menu item.
690 * @param cmd the index of the command within the group.
693 GEANY_API_SYMBOL
694 void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
696 on_build_menu_item(NULL, GRP_CMD_TO_POINTER(grp, cmd));
700 /* Clear all error indicators in all documents. */
701 static void clear_all_errors(void)
703 guint i;
705 foreach_document(i)
707 editor_indicator_clear_errors(documents[i]->editor);
712 /* Replaces occurrences of %e and %p with the appropriate filenames and
713 * %l with current line number. %d and %p replacements should be in UTF8 */
714 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
716 GString *stack;
717 gchar *replacement;
718 gchar *executable = NULL;
719 gint line_num;
721 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
723 stack = g_string_new(src);
724 if (doc != NULL && doc->file_name != NULL)
726 /* replace %f with the filename (including extension) */
727 replacement = g_path_get_basename(doc->file_name);
728 utils_string_replace_all(stack, "%f", replacement);
729 g_free(replacement);
731 /* replace %d with the absolute path of the dir of the current file */
732 replacement = g_path_get_dirname(doc->file_name);
733 utils_string_replace_all(stack, "%d", replacement);
734 g_free(replacement);
736 /* replace %e with the filename (excluding extension) */
737 executable = utils_remove_ext_from_filename(doc->file_name);
738 replacement = g_path_get_basename(executable);
739 utils_string_replace_all(stack, "%e", replacement);
740 g_free(replacement);
742 /* replace %l with the current 1-based line number */
743 line_num = sci_get_current_line(doc->editor->sci) + 1;
744 replacement = g_strdup_printf("%i", line_num);
745 utils_string_replace_all(stack, "%l", replacement);
746 g_free(replacement);
749 /* replace %p with the current project's (absolute) base directory */
750 replacement = NULL; /* prevent double free if no replacement found */
751 if (app->project)
753 replacement = project_get_base_path();
755 else if (strstr(stack->str, "%p"))
756 { /* fall back to %d */
757 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
758 if (doc != NULL && doc->file_name != NULL)
759 replacement = g_path_get_dirname(doc->file_name);
762 utils_string_replace_all(stack, "%p", replacement);
763 g_free(replacement);
764 g_free(executable);
766 return g_string_free(stack, FALSE); /* don't forget to free src also if needed */
770 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
771 * idx document directory */
772 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
774 GError *error = NULL;
775 gchar *argv[] = { "/bin/sh", "-c", NULL, NULL };
776 gchar *working_dir;
777 gchar *utf8_working_dir;
778 gchar *cmd_string;
780 g_return_if_fail(doc == NULL || doc->is_valid);
782 if ((doc == NULL || EMPTY(doc->file_name)) && EMPTY(dir))
784 geany_debug("Failed to run command with no working directory");
785 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
786 return;
789 clear_all_errors();
790 SETPTR(current_dir_entered, NULL);
792 utf8_working_dir = !EMPTY(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
793 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
795 gtk_list_store_clear(msgwindow.store_compiler);
796 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
797 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), cmd, utf8_working_dir);
798 g_free(utf8_working_dir);
800 #ifdef G_OS_UNIX
801 cmd_string = utils_get_locale_from_utf8(cmd);
802 argv[2] = cmd_string;
803 cmd = NULL; /* under Unix, use argv to start cmd via sh for compatibility */
804 #else
805 /* Expand environment variables like %blah%. */
806 cmd_string = win32_expand_environment_variables(cmd);
807 argv[0] = NULL; /* under Windows, run cmd directly */
808 cmd = cmd_string;
809 #endif
811 /* set the build info for the message window */
812 g_free(build_info.dir);
813 build_info.dir = g_strdup(working_dir);
814 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
815 build_info.message_count = 0;
817 if (!spawn_with_callbacks(working_dir, cmd, argv, NULL, 0, NULL, NULL, build_iofunc,
818 GINT_TO_POINTER(0), 0, build_iofunc, GINT_TO_POINTER(1), 0, build_exit_cb, NULL,
819 &build_info.pid, &error))
821 geany_debug("build command spawning failed: %s", error->message);
822 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
823 g_error_free(error);
826 g_free(working_dir);
827 g_free(cmd_string);
831 /* Returns: NULL if there was an error, or the command to be executed. If Geany is
832 * set to use a run script, the returned value is a path to the script that runs
833 * the command; otherwise the command itself is returned. working_dir is a pointer
834 * to the working directory from which the command is executed. Both strings are
835 * in the locale encoding. */
836 static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
838 GeanyBuildCommand *cmd = NULL;
839 const gchar *cmd_working_dir;
840 gboolean autoclose = FALSE;
841 gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string;
842 GError *error = NULL;
844 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
846 cmd_string_utf8 = build_replace_placeholder(doc, cmd->command);
847 cmd_working_dir = cmd->working_dir;
848 if (EMPTY(cmd_working_dir))
849 cmd_working_dir = "%d";
850 working_dir_utf8 = build_replace_placeholder(doc, cmd_working_dir);
851 *working_dir = utils_get_locale_from_utf8(working_dir_utf8);
853 if (EMPTY(*working_dir) || ! g_file_test(*working_dir, G_FILE_TEST_EXISTS) ||
854 ! g_file_test(*working_dir, G_FILE_TEST_IS_DIR))
856 ui_set_statusbar(TRUE, _("Invalid working directory \"%s\""),
857 !EMPTY(working_dir_utf8) ? working_dir_utf8 : "<NULL>" );
858 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, *working_dir, NULL);
859 return NULL;
862 cmd_string = utils_get_locale_from_utf8(cmd_string_utf8);
864 #ifdef HAVE_VTE
865 if (vte_info.have_vte && vc->run_in_vte)
867 if (vc->skip_run_script)
869 utils_free_pointers(2, cmd_string_utf8, working_dir_utf8, NULL);
870 return cmd_string;
872 else
873 /* don't wait for user input at the end of script when we are running in VTE */
874 autoclose = TRUE;
876 #endif
878 #ifdef G_OS_WIN32
879 /* Expand environment variables like %blah%. */
880 SETPTR(cmd_string, win32_expand_environment_variables(cmd_string));
882 gchar *helper = g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC), "geany-run-helper", NULL);
883 /* escape helper appropriately */
884 /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
885 * allowed in paths anyway */
886 run_cmd = g_strdup_printf("\"%s\" \"%s\" %d %s", helper, *working_dir, autoclose ? 1 : 0, cmd_string);
887 g_free(helper);
888 #else
889 run_cmd = build_create_shellscript(*working_dir, cmd_string, autoclose, &error);
890 if (!run_cmd)
892 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
893 !EMPTY(cmd_string_utf8) ? cmd_string_utf8 : NULL, error->message);
894 g_error_free(error);
895 g_free(*working_dir);
897 #endif
898 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
899 return run_cmd;
903 static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
905 gchar *working_dir;
906 gchar *run_cmd = NULL;
908 if (! DOC_VALID(doc) || doc->file_name == NULL)
909 return;
911 run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex);
912 if (run_cmd == NULL)
913 return;
915 run_info[cmdindex].file_type_id = doc->file_type->id;
917 #ifdef HAVE_VTE
918 if (vte_info.have_vte && vc->run_in_vte)
920 gchar *vte_cmd;
922 /* VTE expects commands in UTF-8 */
923 SETPTR(run_cmd, utils_get_utf8_from_locale(run_cmd));
924 SETPTR(working_dir, utils_get_utf8_from_locale(working_dir));
926 if (vc->skip_run_script)
927 vte_cmd = g_strconcat(run_cmd, "\n", NULL);
928 else
929 vte_cmd = g_strconcat("\n/bin/sh ", run_cmd, "\n", NULL);
931 vte_cwd(working_dir, TRUE);
932 if (! vte_send_cmd(vte_cmd))
934 const gchar *msg = _("File not executed because the terminal may contain some input (press Ctrl+C or Enter to clear it).");
935 ui_set_statusbar(FALSE, "%s", msg);
936 geany_debug("%s", msg);
937 if (!vc->skip_run_script)
938 g_unlink(run_cmd);
941 /* show the VTE */
942 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
943 gtk_widget_grab_focus(vc->vte);
944 msgwin_show_hide(TRUE);
946 run_info[cmdindex].pid = 1;
947 g_free(vte_cmd);
949 else
950 #endif
952 gchar *locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
953 GError *error = NULL;
955 #ifdef G_OS_WIN32
956 if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd, 0, 0))
958 /* if passing an argument to cmd.exe, respect its quoting rules */
959 GString *escaped_run_cmd = g_string_new(NULL);
960 for (gchar *p = run_cmd; *p; p++)
962 if (strchr("()%!^\"<>&| ", *p)) // cmd.exe metacharacters
963 g_string_append_c(escaped_run_cmd, '^');
964 g_string_append_c(escaped_run_cmd, *p);
966 SETPTR(run_cmd, g_string_free(escaped_run_cmd, FALSE));
968 #endif
970 utils_str_replace_all(&locale_term_cmd, "%c", run_cmd);
972 if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid),
973 &error))
975 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
976 (gpointer) &(run_info[cmdindex]));
977 build_menu_update(doc);
979 else
981 gchar *utf8_term_cmd = utils_get_utf8_from_locale(locale_term_cmd);
982 ui_set_statusbar(TRUE, _("Cannot execute build command \"%s\": %s. "
983 "Check the Terminal setting in Preferences"), utf8_term_cmd, error->message);
984 g_free(utf8_term_cmd);
985 g_error_free(error);
986 #ifndef G_OS_WIN32
987 g_unlink(run_cmd);
988 #endif
989 run_info[cmdindex].pid = (GPid) 0;
993 g_free(working_dir);
994 g_free(run_cmd);
998 static void process_build_output_line(gchar *msg, gint color)
1000 gchar *tmp;
1001 gchar *filename;
1002 gint line;
1004 g_strchomp(msg);
1006 if (EMPTY(msg))
1007 return;
1009 if (build_parse_make_dir(msg, &tmp))
1011 SETPTR(current_dir_entered, tmp);
1013 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
1015 if (line != -1 && filename != NULL)
1017 GeanyDocument *doc = document_find_by_filename(filename);
1019 /* limit number of indicators */
1020 if (doc && editor_prefs.use_indicators &&
1021 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
1023 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
1024 line--; /* so only adjust the line number if it is greater than 0 */
1025 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
1027 build_info.message_count++;
1028 color = COLOR_RED; /* error message parsed on the line */
1030 g_free(filename);
1032 msgwin_compiler_add_string(color, msg);
1036 static void build_iofunc(GString *string, GIOCondition condition, gpointer data)
1038 if (condition & (G_IO_IN | G_IO_PRI))
1040 process_build_output_line(string->str,
1041 (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK);
1046 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1048 const gchar *pos;
1050 *prefix = NULL;
1052 if (string == NULL)
1053 return FALSE;
1055 if ((pos = strstr(string, "Entering directory")) != NULL)
1057 gsize len;
1058 gchar *input;
1060 /* get the start of the path */
1061 pos = strstr(string, "/");
1063 if (pos == NULL)
1064 return FALSE;
1066 input = g_strdup(pos);
1068 /* kill the ' at the end of the path */
1069 len = strlen(input);
1070 input[len - 1] = '\0';
1071 input = g_realloc(input, len); /* shorten by 1 */
1072 *prefix = input;
1074 return TRUE;
1077 if (strstr(string, "Leaving directory") != NULL)
1079 *prefix = NULL;
1080 return TRUE;
1083 return FALSE;
1087 static void show_build_result_message(gboolean failure)
1089 gchar *msg;
1091 if (failure)
1093 msg = _("Compilation failed.");
1094 msgwin_compiler_add_string(COLOR_BLUE, msg);
1095 /* If msgwindow is hidden, user will want to display it to see the error */
1096 if (! ui_prefs.msgwindow_visible)
1098 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1099 msgwin_show_hide(TRUE);
1101 else
1102 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1103 ui_set_statusbar(FALSE, "%s", msg);
1105 else
1107 msg = _("Compilation finished successfully.");
1108 msgwin_compiler_add_string(COLOR_BLUE, msg);
1109 if (! ui_prefs.msgwindow_visible ||
1110 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1111 ui_set_statusbar(FALSE, "%s", msg);
1116 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1118 show_build_result_message(!SPAWN_WIFEXITED(status) || SPAWN_WEXITSTATUS(status) != EXIT_SUCCESS);
1119 utils_beep();
1121 build_info.pid = 0;
1122 /* enable build items again */
1123 build_menu_update(NULL);
1124 ui_progress_bar_stop();
1128 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1130 RunInfo *run_info_data = user_data;
1132 g_spawn_close_pid(child_pid);
1134 run_info_data->pid = 0;
1135 /* reset the stop button and menu item to the original meaning */
1136 build_menu_update(NULL);
1140 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1141 * working_dir and cmd are both in the locale encoding
1142 * it returns the full file name (including path) of the created script in the locale encoding */
1143 #ifndef G_OS_WIN32
1144 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
1146 gint fd;
1147 gchar *str, *fname;
1148 gboolean success = TRUE;
1149 gchar *escaped_dir;
1150 fd = g_file_open_tmp (RUN_SCRIPT_CMD, &fname, error);
1151 if (fd < 0)
1152 return NULL;
1153 close(fd);
1155 escaped_dir = g_shell_quote(working_dir);
1156 str = g_strdup_printf(
1157 "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1158 \n\n%s\n", escaped_dir, cmd, (autoclose) ? "" :
1159 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1160 "dash\ndummy_var=\"\"\nread dummy_var");
1161 g_free(escaped_dir);
1163 if (!g_file_set_contents(fname, str, -1, error))
1164 success = FALSE;
1165 g_free(str);
1166 #ifdef __APPLE__
1167 if (success && g_chmod(fname, 0777) != 0)
1169 if (error)
1171 gint errsv = errno;
1173 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv),
1174 "Failed to make file executable: %s", g_strerror(errsv));
1176 success = FALSE;
1178 #endif
1180 if (!success)
1182 g_unlink(fname);
1183 g_free(fname);
1184 fname = NULL;
1187 return fname;
1189 #endif
1192 typedef void Callback(GtkWidget *w, gpointer u);
1194 /* run the command catenating cmd_cat if present */
1195 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
1197 gchar *dir;
1198 gchar *full_command, *subs_command;
1199 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1200 gchar *cmdstr;
1202 if (buildcmd == NULL)
1203 return;
1205 cmdstr = buildcmd->command;
1207 if (cmd_cat != NULL)
1209 if (cmdstr != NULL)
1210 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1211 else
1212 full_command = g_strdup(cmd_cat);
1214 else
1215 full_command = cmdstr;
1217 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1218 subs_command = build_replace_placeholder(doc, full_command);
1219 build_info.grp = grp;
1220 build_info.cmd = cmd;
1221 build_spawn_cmd(doc, subs_command, dir);
1222 g_free(subs_command);
1223 g_free(dir);
1224 if (cmd_cat != NULL)
1225 g_free(full_command);
1226 build_menu_update(doc);
1227 if (build_info.pid)
1228 ui_progress_bar_start(NULL);
1232 /*----------------------------------------------------------------
1234 * Create build menu and handle callbacks (&toolbar callbacks)
1236 *----------------------------------------------------------------*/
1237 static void on_make_custom_input_response(const gchar *input, gpointer data)
1239 GeanyDocument *doc = document_get_current();
1241 SETPTR(build_info.custom_target, g_strdup(input));
1242 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1243 build_info.custom_target);
1247 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1249 GeanyDocument *doc = document_get_current();
1250 GeanyBuildCommand *bc;
1251 guint grp = GPOINTER_TO_GRP(user_data);
1252 guint cmd = GPOINTER_TO_CMD(user_data);
1254 if (doc && doc->changed)
1256 if (!document_save_file(doc, FALSE))
1257 return;
1259 g_signal_emit_by_name(geany_object, "build-start");
1261 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1263 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1265 if (! dialog)
1267 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1268 _("Enter custom text here, all entered text is appended to the command."),
1269 build_info.custom_target, &on_make_custom_input_response, NULL);
1271 else
1273 gtk_widget_show(dialog);
1275 return;
1277 else if (grp == GEANY_GBG_EXEC)
1279 if (run_info[cmd].pid > (GPid) 1)
1281 kill_process(&run_info[cmd].pid);
1282 return;
1284 bc = get_build_cmd(doc, grp, cmd, NULL);
1285 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1287 const gchar *uri_file_prefix;
1288 gchar *uri;
1289 if (doc == NULL)
1290 return;
1291 uri_file_prefix = utils_get_uri_file_prefix();
1292 uri = g_strconcat(uri_file_prefix, doc->file_name, NULL);
1293 utils_open_browser(uri);
1294 g_free(uri);
1296 else
1297 build_run_cmd(doc, cmd);
1299 else
1300 build_command(doc, grp, cmd, NULL);
1304 /* group codes for menu items other than the known commands
1305 * value order is important, see the following table for use */
1307 /* the rest in each group */
1308 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1309 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1310 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1311 /* the separator */
1312 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1313 /* the fixed items */
1314 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1315 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1316 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1317 #define MENU_DONE (MENU_COMMANDS + 1)
1320 static struct BuildMenuItemSpec {
1321 const gchar *stock_id;
1322 const gint key_binding;
1323 const guint build_grp;
1324 const guint build_cmd;
1325 const gchar *fix_label;
1326 Callback *cb;
1327 } build_menu_specs[] = {
1328 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1329 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1330 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1331 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1332 {NULL, -1, MENU_FT_REST,
1333 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1334 {NULL, -1, MENU_SEPARATOR,
1335 GBF_SEP_1, NULL, NULL},
1336 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1337 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1338 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1339 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1340 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1341 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1342 {NULL, -1, MENU_NON_FT_REST,
1343 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1344 {NULL, -1, MENU_SEPARATOR,
1345 GBF_SEP_2, NULL, NULL},
1346 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1347 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1348 {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1349 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1350 {NULL, -1, MENU_SEPARATOR,
1351 GBF_SEP_3, NULL, NULL},
1352 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1353 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1354 {NULL, -1, MENU_EXEC_REST,
1355 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1356 {NULL, -1, MENU_SEPARATOR,
1357 GBF_SEP_4, NULL, NULL},
1358 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1359 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1360 {NULL, -1, MENU_DONE,
1361 0, NULL, NULL}
1365 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1366 struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
1368 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1370 if (bs->stock_id != NULL)
1372 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1373 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1375 gtk_widget_show(item);
1376 if (bs->key_binding >= 0)
1377 add_menu_accel(group, (guint) bs->key_binding, ag, item);
1378 gtk_container_add(GTK_CONTAINER(menu), item);
1379 if (bs->cb != NULL)
1381 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1383 menu_items.menu_item[grp][cmd] = item;
1387 static void create_build_menu(BuildMenuItems *build_menu_items)
1389 GtkWidget *menu;
1390 GtkAccelGroup *accel_group = gtk_accel_group_new();
1391 GeanyKeyGroup *keygroup = keybindings_get_core_group(GEANY_KEY_GROUP_BUILD);
1392 guint i, j;
1394 menu = gtk_menu_new();
1395 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1396 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1397 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1398 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1400 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1402 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1403 if (bs->build_grp == MENU_SEPARATOR)
1405 GtkWidget *item = gtk_separator_menu_item_new();
1406 gtk_widget_show(item);
1407 gtk_container_add(GTK_CONTAINER(menu), item);
1408 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1410 else if (bs->fix_label != NULL)
1412 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1413 GBG_FIXED, bs->build_cmd);
1415 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1417 guint grp = bs->build_grp - GEANY_GBG_COUNT;
1418 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1420 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1421 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1422 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1425 else
1427 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1428 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1429 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1432 build_menu_items->menu = menu;
1433 gtk_widget_show(menu);
1434 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1438 /* * Update the build menu to reflect changes in configuration or status.
1440 * Sets the labels and number of visible items to match the highest
1441 * priority configured commands. Also sets sensitivity if build commands are
1442 * running and switches executes to stop when commands are running.
1444 * @param doc The current document, if available, to save looking it up.
1445 * If @c NULL it will be looked up.
1447 * Call this after modifying any fields of a GeanyBuildCommand structure.
1449 * @see Build Menu Configuration section of the Manual.
1452 void build_menu_update(GeanyDocument *doc)
1454 guint i, cmdcount, cmd, grp;
1455 gboolean vis = FALSE;
1456 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1457 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1458 GeanyBuildCommand *bc;
1460 g_return_if_fail(doc == NULL || doc->is_valid);
1462 if (menu_items.menu == NULL)
1463 create_build_menu(&menu_items);
1464 if (doc == NULL)
1465 doc = document_get_current();
1466 have_path = doc != NULL && doc->file_name != NULL;
1467 build_running = build_info.pid > (GPid) 1;
1468 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1469 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1471 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1472 switch (bs->build_grp)
1474 case MENU_SEPARATOR:
1475 if (vis == TRUE)
1477 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1478 vis = FALSE;
1480 else
1481 gtk_widget_hide(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1482 break;
1483 case MENU_NEXT_ERROR:
1484 case MENU_PREV_ERROR:
1485 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1486 vis |= TRUE;
1487 break;
1488 case MENU_COMMANDS:
1489 vis |= TRUE;
1490 break;
1491 default: /* all configurable commands */
1492 if (bs->build_grp >= GEANY_GBG_COUNT)
1494 grp = bs->build_grp - GEANY_GBG_COUNT;
1495 cmdcount = build_groups_count[grp];
1497 else
1499 grp = bs->build_grp;
1500 cmdcount = bs->build_cmd + 1;
1502 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1504 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1505 const gchar *label;
1506 bc = get_build_cmd(doc, grp, cmd, NULL);
1507 if (bc)
1508 label = bc->label;
1509 else
1510 label = NULL;
1512 if (grp < GEANY_GBG_EXEC)
1514 cmd_sensitivity =
1515 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1516 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1517 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1518 if (bc != NULL && !EMPTY(label))
1520 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
1521 gtk_widget_show_all(menu_item);
1522 vis |= TRUE;
1524 else
1525 gtk_widget_hide(menu_item);
1527 else
1529 GtkWidget *image;
1530 exec_running = run_info[cmd].pid > (GPid) 1;
1531 cmd_sensitivity = (bc != NULL) || exec_running;
1532 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1533 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1534 run_sensitivity = cmd_sensitivity;
1535 if (! exec_running)
1537 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1539 else
1541 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1543 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1544 run_running = exec_running;
1545 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1546 if (bc != NULL && !EMPTY(label))
1548 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
1549 gtk_widget_show_all(menu_item);
1550 vis |= TRUE;
1552 else
1553 gtk_widget_hide(menu_item);
1559 run_sensitivity &= (doc != NULL);
1560 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1561 && have_path && ! build_running;
1562 if (widgets.toolitem_build != NULL)
1563 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1564 can_make = FALSE;
1565 if (widgets.toolitem_make_all != NULL)
1566 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1567 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1568 && ! build_running));
1569 if (widgets.toolitem_make_custom != NULL)
1570 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1571 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1572 && ! build_running));
1573 if (widgets.toolitem_make_object != NULL)
1574 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1575 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1576 && ! build_running));
1577 if (widgets.toolitem_set_args != NULL)
1578 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1580 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1581 && have_path && ! build_running;
1582 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1583 gtk_action_set_sensitive(widgets.build_action, can_make);
1584 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1586 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1587 set_stop_button(run_running);
1592 /* Call build_menu_update() instead of calling this directly. */
1593 static void set_stop_button(gboolean stop)
1595 const gchar *button_stock_id = NULL;
1596 GtkToolButton *run_button;
1598 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1599 if (run_button != NULL)
1600 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1602 if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
1603 return;
1604 if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
1605 return;
1607 /* use the run button also as stop button */
1608 if (stop)
1610 if (run_button != NULL)
1611 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
1613 else
1615 if (run_button != NULL)
1616 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
1621 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1623 /* For now, just show the project dialog */
1624 if (app->project)
1625 project_build_properties();
1626 else
1627 show_build_commands_dialog();
1631 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1633 last_toolbutton_action = user_data;
1634 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1635 on_build_menu_item(menuitem, user_data);
1639 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1641 gchar *msg;
1643 last_toolbutton_action = user_data;
1644 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1645 msg = _("Build the current file with Make and the default target");
1646 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1647 msg = _("Build the current file with Make and the specified target");
1648 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1649 msg = _("Compile the current file with Make");
1650 else
1651 msg = NULL;
1652 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1653 on_build_menu_item(menuitem, user_data);
1657 static void kill_process(GPid *pid)
1659 GError *error = NULL;
1661 if (spawn_kill_process(*pid, &error))
1663 *pid = 0;
1664 build_menu_update(NULL);
1666 else
1668 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), error->message);
1669 g_error_free(error);
1674 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1676 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1677 msgwin_goto_compiler_file_line))
1679 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1681 else
1682 ui_set_statusbar(FALSE, _("No more build errors."));
1686 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1688 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1689 msgwin_goto_compiler_file_line))
1691 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1693 else
1694 ui_set_statusbar(FALSE, _("No more build errors."));
1698 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1700 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1702 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1704 else
1706 on_build_menu_item(NULL, last_toolbutton_action);
1711 /*------------------------------------------------------
1713 * Create and handle the build menu configuration dialog
1715 *-------------------------------------------------------*/
1716 typedef struct RowWidgets
1718 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1719 GeanyBuildSource src;
1720 GeanyBuildSource dst;
1721 GeanyBuildCommand *cmdsrc;
1722 guint grp;
1723 guint cmd;
1724 gboolean cleared;
1725 gboolean used_dst;
1726 } RowWidgets;
1728 #if GTK_CHECK_VERSION(3,0,0)
1729 typedef GdkRGBA InsensitiveColor;
1730 #else
1731 typedef GdkColor InsensitiveColor;
1732 #endif
1733 static InsensitiveColor insensitive_color;
1735 static void set_row_color(RowWidgets *r, InsensitiveColor *color)
1737 enum GeanyBuildCmdEntries i;
1739 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1741 if (i == GEANY_BC_LABEL)
1742 continue;
1744 #if GTK_CHECK_VERSION(3,0,0)
1745 gtk_widget_override_color(r->entries[i], GTK_STATE_FLAG_NORMAL, color);
1746 #else
1747 gtk_widget_modify_text(r->entries[i], GTK_STATE_NORMAL, color);
1748 #endif
1753 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1755 if (GTK_IS_BUTTON(wid))
1756 gtk_button_set_label(GTK_BUTTON(wid), text);
1757 else
1758 gtk_entry_set_text(GTK_ENTRY(wid), text);
1762 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1764 RowWidgets *r = user_data;
1765 guint src;
1766 enum GeanyBuildCmdEntries i;
1767 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1769 if (bc != NULL)
1771 r->cmdsrc = bc;
1772 r->src = src;
1773 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1775 set_build_command_entry_text(r->entries[i],
1776 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1779 else
1781 r->cmdsrc = NULL;
1782 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1784 set_build_command_entry_text(r->entries[i], "");
1787 r->used_dst = FALSE;
1788 set_row_color(r, &insensitive_color);
1789 r->cleared = TRUE;
1793 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1795 gtk_entry_set_text(regex,"");
1799 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1801 RowWidgets *r = user_data;
1802 GtkWidget *top_level = gtk_widget_get_toplevel(wid);
1803 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1804 gchar *str;
1806 if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
1807 str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
1808 else
1809 str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
1811 if (!str)
1812 return;
1814 gtk_button_set_label(GTK_BUTTON(wid), str);
1815 g_free(str);
1816 r->used_dst = TRUE;
1817 set_row_color(r, NULL);
1821 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1823 RowWidgets *r = user_data;
1825 r->used_dst = TRUE;
1826 set_row_color(r, NULL);
1830 /* Column headings, array NULL-terminated */
1831 static const gchar *colheads[] =
1833 "#",
1834 N_("Label"),
1835 N_("Command"),
1836 N_("Working directory"),
1837 N_("Reset"),
1838 NULL
1841 /* column names */
1842 #define DC_ITEM 0
1843 #define DC_ENTRIES 1
1844 #define DC_CLEAR 4
1845 #define DC_N_COL 5
1847 static const guint entry_x_padding = 3;
1848 static const guint entry_y_padding = 0;
1851 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1852 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
1854 GtkWidget *label, *clear, *clearicon;
1855 RowWidgets *roww;
1856 GeanyBuildCommand *bc;
1857 guint src;
1858 enum GeanyBuildCmdEntries i;
1859 guint column = 0;
1860 gchar *text;
1862 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
1864 text = g_strdup_printf("%d.", cmd + 1);
1865 label = gtk_label_new(text);
1866 g_free(text);
1867 #if GTK_CHECK_VERSION(3,0,0)
1869 GtkStyleContext *ctx = gtk_widget_get_style_context(label);
1871 gtk_style_context_save(ctx);
1872 gtk_style_context_get_color(ctx, GTK_STATE_FLAG_INSENSITIVE, &insensitive_color);
1873 gtk_style_context_restore(ctx);
1875 #else
1876 insensitive_color = gtk_widget_get_style(label)->text[GTK_STATE_INSENSITIVE];
1877 #endif
1878 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1879 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1880 roww = g_new0(RowWidgets, 1);
1881 roww->src = GEANY_BCS_COUNT;
1882 roww->grp = grp;
1883 roww->cmd = cmd;
1884 roww->dst = dst;
1885 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1887 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1889 column += 1;
1890 if (i == GEANY_BC_LABEL)
1892 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1893 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1894 gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1895 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
1897 else
1899 roww->entries[i] = gtk_entry_new();
1900 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
1902 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1903 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1905 column++;
1906 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1907 clear = gtk_button_new();
1908 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1909 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1910 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1911 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1912 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1913 if (bc != NULL)
1914 roww->src = src;
1916 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1918 const gchar *str = "";
1920 if (bc != NULL )
1922 if ((str = id_to_str(bc, i)) == NULL)
1923 str = "";
1924 else if (dst == src)
1925 roww->used_dst = TRUE;
1927 set_build_command_entry_text(roww->entries[i], str);
1929 if (bc != NULL && (dst > src))
1930 set_row_color(roww, &insensitive_color);
1931 if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1933 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1934 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1935 gtk_widget_set_sensitive(clear, FALSE);
1937 return roww;
1941 typedef struct BuildTableFields
1943 RowWidgets **rows;
1944 GtkWidget *fileregex;
1945 GtkWidget *nonfileregex;
1946 gchar **fileregexstring;
1947 gchar **nonfileregexstring;
1948 } BuildTableFields;
1951 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
1952 GeanyFiletype *ft)
1954 GtkWidget *label, *sep, *clearicon, *clear;
1955 BuildTableFields *fields;
1956 GtkTable *table;
1957 const gchar **ch;
1958 gchar *txt;
1959 guint col, row, cmdindex;
1960 guint cmd;
1961 guint src;
1962 gboolean sensitivity;
1963 guint sep_padding = entry_y_padding + 3;
1965 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1966 fields = g_new0(BuildTableFields, 1);
1967 fields->rows = g_new0(RowWidgets*, build_items_count);
1968 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1970 label = gtk_label_new(_(*ch));
1971 gtk_table_attach(table, label, col, col + 1, 0, 1,
1972 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1974 sep = gtk_hseparator_new();
1975 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1976 entry_x_padding, sep_padding);
1977 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1978 txt = g_strdup_printf(_("%s commands"), ft->name);
1979 else
1980 txt = g_strdup_printf(_("%s commands"), _("No filetype"));
1982 label = ui_label_new_bold(txt);
1983 g_free(txt);
1984 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1985 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1986 entry_x_padding, entry_y_padding);
1987 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1988 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1989 label = gtk_label_new(_("Error regular expression:"));
1990 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1991 entry_x_padding, entry_y_padding);
1992 fields->fileregex = gtk_entry_new();
1993 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
1994 sensitivity = (ft == NULL) ? FALSE : TRUE;
1995 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1997 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1998 if (src > dst)
1999 sensitivity = FALSE;
2001 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2002 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2003 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2004 clear = gtk_button_new();
2005 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2006 g_signal_connect_swapped(clear, "clicked",
2007 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
2008 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2009 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2010 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
2011 gtk_widget_set_sensitive(clear, sensitivity);
2012 ++row;
2013 sep = gtk_hseparator_new();
2014 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2015 entry_x_padding, sep_padding);
2016 ++row;
2017 label = ui_label_new_bold(_("Independent commands"));
2018 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2019 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2020 entry_x_padding, entry_y_padding);
2021 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
2022 fields->rows[cmdindex] = build_add_dialog_row(
2023 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
2024 label = gtk_label_new(_("Error regular expression:"));
2025 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
2026 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2027 fields->nonfileregex = gtk_entry_new();
2028 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
2029 sensitivity = TRUE;
2030 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
2032 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
2033 sensitivity = src > dst ? FALSE : TRUE;
2035 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2036 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2037 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2038 clear = gtk_button_new();
2039 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2040 g_signal_connect_swapped(clear, "clicked",
2041 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
2042 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2043 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2044 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
2045 gtk_widget_set_sensitive(clear, sensitivity);
2046 ++row;
2047 label = gtk_label_new(NULL);
2048 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2049 _("Note: Item 2 opens a dialog and appends the response to the command."));
2050 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2051 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2052 entry_x_padding, entry_y_padding);
2053 ++row;
2054 sep = gtk_hseparator_new();
2055 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2056 entry_x_padding, sep_padding);
2057 ++row;
2058 label = ui_label_new_bold(_("Execute commands"));
2059 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2060 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2061 entry_x_padding, entry_y_padding);
2062 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
2063 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
2064 sep = gtk_hseparator_new();
2065 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2066 entry_x_padding, sep_padding);
2067 ++row;
2068 label = gtk_label_new(NULL);
2069 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2070 _("%d, %e, %f, %p, %l are substituted in command and directory fields, see manual for details."));
2071 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2072 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2073 entry_x_padding, entry_y_padding);
2074 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2075 ++row;
2076 *table_data = fields;
2077 return GTK_WIDGET(table);
2081 void build_free_fields(BuildTableData table_data)
2083 guint cmdindex;
2085 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2086 g_free(table_data->rows[cmdindex]);
2087 g_free(table_data->rows);
2088 g_free(table_data);
2092 /* string compare where null pointers match null or 0 length strings */
2093 #if 0
2094 static gint stcmp(const gchar *a, const gchar *b)
2096 if (a == NULL && b == NULL)
2097 return 0;
2098 if (a == NULL && b != NULL)
2099 return strlen(b);
2100 if (a != NULL && b == NULL)
2101 return strlen(a);
2102 return strcmp(a, b);
2104 #endif
2107 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2109 if (GTK_IS_BUTTON(wid))
2110 return gtk_button_get_label(GTK_BUTTON(wid));
2111 else
2112 return gtk_entry_get_text(GTK_ENTRY(wid));
2116 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
2118 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2119 gboolean changed = FALSE;
2120 enum GeanyBuildCmdEntries i;
2122 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2124 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2126 if (table_data->rows[drow]->cleared)
2128 if (dst->dst[grp] != NULL)
2130 if (*(dst->dst[grp]) == NULL)
2131 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2132 (*(dst->dst[grp]))[cmd].exists = FALSE;
2133 (*(dst->dst[grp]))[cmd].changed = TRUE;
2134 changed = TRUE;
2137 if (table_data->rows[drow]->used_dst == TRUE)
2139 if (dst->dst[grp] != NULL)
2141 if (*(dst->dst[grp]) == NULL)
2142 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2143 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2144 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2145 (*(dst->dst[grp]))[cmd].exists = TRUE;
2146 (*(dst->dst[grp]))[cmd].changed = TRUE;
2147 changed = TRUE;
2150 else
2152 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2153 g_free(entries[i]);
2155 return changed;
2159 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2161 gboolean changed = FALSE;
2162 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2164 if (((src == NULL /* originally there was no regex */
2165 || *src == NULL) /* or it was NULL*/
2166 && !EMPTY(reg)) /* and something was typed */
2167 || (src != NULL /* originally there was a regex*/
2168 && (*src == NULL /* and either it was NULL */
2169 || strcmp(*src, reg) != 0))) /* or it has been changed */
2171 if (dst != NULL)
2173 SETPTR(*dst, g_strdup(reg));
2174 changed = TRUE;
2177 return changed;
2181 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2183 guint cmdindex, cmd;
2184 gboolean changed = FALSE;
2186 if (response == GTK_RESPONSE_ACCEPT)
2188 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2189 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2190 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2191 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2192 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2193 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2194 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2195 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2197 return changed;
2201 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2203 BuildDestination menu_dst;
2205 if (ft != NULL)
2207 menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds);
2208 menu_dst.fileregexstr = &(ft->priv->projerror_regex_string);
2210 else
2212 menu_dst.dst[GEANY_GBG_FT] = NULL;
2213 menu_dst.fileregexstr = NULL;
2215 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2216 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2217 menu_dst.nonfileregexstr = &regex_proj;
2219 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2223 static void show_build_commands_dialog(void)
2225 GtkWidget *dialog, *table, *vbox;
2226 GeanyDocument *doc = document_get_current();
2227 GeanyFiletype *ft = NULL;
2228 const gchar *title = _("Set Build Commands");
2229 gint response;
2230 BuildTableData table_data;
2231 BuildDestination prefdsts;
2233 if (doc != NULL)
2234 ft = doc->file_type;
2235 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2236 GTK_DIALOG_DESTROY_WITH_PARENT,
2237 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2238 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2239 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2240 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2241 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2242 gtk_widget_show_all(dialog);
2243 /* run modally to prevent user changing idx filetype */
2244 response = gtk_dialog_run(GTK_DIALOG(dialog));
2246 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2247 if (ft != NULL)
2249 prefdsts.dst[GEANY_GBG_FT] = &(ft->priv->homefilecmds);
2250 prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string);
2251 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds);
2253 else
2255 prefdsts.dst[GEANY_GBG_FT] = NULL;
2256 prefdsts.fileregexstr = NULL;
2257 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2259 prefdsts.nonfileregexstr = &regex_pref;
2260 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2261 filetypes_save_commands(ft);
2262 build_free_fields(table_data);
2264 build_menu_update(doc);
2265 gtk_widget_destroy(dialog);
2269 /* Creates the relevant build menu if necessary. */
2270 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2272 BuildMenuItems *items;
2274 items = &menu_items;
2275 if (items->menu == NULL)
2276 create_build_menu(items);
2277 return items;
2281 /*----------------------------------------------------------
2283 * Load and store configuration
2285 * ---------------------------------------------------------*/
2286 static const gchar *build_grp_name = "build-menu";
2288 /* config format for build-menu group is prefix_gg_nn_xx=value
2289 * where gg = FT, NF, EX for the command group
2290 * nn = 2 digit command number
2291 * xx = LB for label, CM for command and WD for working dir */
2292 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2293 static const gchar *fixedkey="xx_xx_xx";
2295 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2296 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2297 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2299 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2300 gchar *prefix, gboolean loc)
2302 guint cmd;
2303 gsize prefixlen; /* NOTE prefixlen used in macros above */
2304 GeanyBuildCommand *dstcmd;
2305 gchar *key;
2306 static gchar cmdbuf[4] = " ";
2308 if (*dst == NULL)
2309 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2310 dstcmd = *dst;
2311 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2312 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2313 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2315 gchar *label;
2316 if (cmd >= 100)
2317 break; /* ensure no buffer overflow */
2318 sprintf(cmdbuf, "%02u", cmd);
2319 set_key_grp(key, groups[grp]);
2320 set_key_cmd(key, cmdbuf);
2321 set_key_fld(key, "LB");
2322 if (loc)
2323 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2324 else
2325 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2326 if (label != NULL)
2328 dstcmd[cmd].exists = TRUE;
2329 SETPTR(dstcmd[cmd].label, label);
2330 set_key_fld(key,"CM");
2331 SETPTR(dstcmd[cmd].command,
2332 g_key_file_get_string(config, build_grp_name, key, NULL));
2333 set_key_fld(key,"WD");
2334 SETPTR(dstcmd[cmd].working_dir,
2335 g_key_file_get_string(config, build_grp_name, key, NULL));
2337 else dstcmd[cmd].exists = FALSE;
2339 g_free(key);
2343 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2344 static void assign_cmd(GeanyBuildCommand *type, guint id,
2345 const gchar *label, gchar *value)
2347 if (!EMPTY(value) && ! type[GBO_TO_CMD(id)].exists)
2349 type[GBO_TO_CMD(id)].exists = TRUE;
2350 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(label));
2351 SETPTR(type[GBO_TO_CMD(id)].command, value);
2352 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL);
2353 type[GBO_TO_CMD(id)].old = TRUE;
2355 else
2356 g_free(value);
2359 /* for the specified source load new format build menu items or try to make some sense of
2360 * old format setings, not done perfectly but better than ignoring them */
2361 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2363 GeanyFiletype *ft;
2364 GeanyProject *pj;
2365 gchar **ftlist;
2366 gchar *value, *basedir, *makebasedir;
2367 gboolean bvalue = FALSE;
2369 if (g_key_file_has_group(config, build_grp_name))
2371 switch (src)
2373 case GEANY_BCS_FT:
2374 ft = (GeanyFiletype*)p;
2375 if (ft == NULL)
2376 return;
2377 build_load_menu_grp(config, &(ft->priv->filecmds), GEANY_GBG_FT, NULL, TRUE);
2378 build_load_menu_grp(config, &(ft->priv->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2379 build_load_menu_grp(config, &(ft->priv->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2380 SETPTR(ft->error_regex_string,
2381 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2382 break;
2383 case GEANY_BCS_HOME_FT:
2384 ft = (GeanyFiletype*)p;
2385 if (ft == NULL)
2386 return;
2387 build_load_menu_grp(config, &(ft->priv->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2388 build_load_menu_grp(config, &(ft->priv->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2389 SETPTR(ft->priv->homeerror_regex_string,
2390 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2391 break;
2392 case GEANY_BCS_PREF:
2393 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2394 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2395 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2396 break;
2397 case GEANY_BCS_PROJ:
2398 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2399 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2400 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2401 pj = (GeanyProject*)p;
2402 if (p == NULL)
2403 return;
2404 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2405 if (ftlist != NULL)
2407 gchar **ftname;
2408 if (pj->priv->build_filetypes_list == NULL)
2409 pj->priv->build_filetypes_list = g_ptr_array_new();
2410 g_ptr_array_set_size(pj->priv->build_filetypes_list, 0);
2411 for (ftname = ftlist; *ftname != NULL; ++ftname)
2413 ft = filetypes_lookup_by_name(*ftname);
2414 if (ft != NULL)
2416 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2417 g_ptr_array_add(pj->priv->build_filetypes_list, ft);
2418 SETPTR(ft->priv->projerror_regex_string,
2419 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2420 g_free(regkey);
2421 build_load_menu_grp(config, &(ft->priv->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2422 build_load_menu_grp(config, &(ft->priv->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2425 g_free(ftlist);
2427 break;
2428 default: /* defaults don't load from config, see build_init */
2429 break;
2433 /* load old [build_settings] values if there is no value defined by [build-menu] */
2435 switch (src)
2437 case GEANY_BCS_FT:
2438 ft = (GeanyFiletype*)p;
2439 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2440 if (value != NULL)
2442 if (ft->priv->filecmds == NULL)
2443 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2444 assign_cmd(ft->priv->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2446 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2447 if (value != NULL)
2449 if (ft->priv->filecmds == NULL)
2450 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2451 assign_cmd(ft->priv->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2453 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2454 if (value != NULL)
2456 if (ft->priv->execcmds == NULL)
2457 ft->priv->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2458 assign_cmd(ft->priv->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2460 if (ft->error_regex_string == NULL)
2461 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2462 break;
2463 case GEANY_BCS_PROJ:
2464 if (non_ft_pref == NULL)
2465 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2466 basedir = project_get_base_path();
2467 if (basedir == NULL)
2468 basedir = g_strdup("%d");
2469 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2470 if (bvalue)
2471 makebasedir = g_strdup(basedir);
2472 else
2473 makebasedir = g_strdup("%d");
2474 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2475 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
2476 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2477 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2478 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2479 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
2480 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2481 if (!EMPTY(value))
2483 if (exec_proj == NULL)
2484 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2485 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2487 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2488 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2489 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
2490 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
2491 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2494 g_free(makebasedir);
2495 g_free(basedir);
2496 break;
2497 case GEANY_BCS_PREF:
2498 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2499 if (value != NULL)
2501 if (non_ft_pref == NULL)
2502 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2503 assign_cmd(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
2504 g_strdup_printf("%s ", value));
2505 assign_cmd(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2506 g_strdup_printf("%s %%e.o",value));
2507 assign_cmd(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2509 break;
2510 default:
2511 break;
2516 static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2518 guint cmd;
2519 gsize prefixlen; /* NOTE prefixlen used in macros above */
2520 gchar *key;
2521 guint count = 0;
2522 enum GeanyBuildCmdEntries i;
2524 if (src == NULL)
2525 return 0;
2526 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2527 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2528 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2530 if (src[cmd].exists) ++count;
2531 if (src[cmd].changed)
2533 static gchar cmdbuf[4] = " ";
2534 if (cmd >= 100)
2535 break; /* ensure no buffer overflow */
2536 sprintf(cmdbuf, "%02u", cmd);
2537 set_key_grp(key, groups[grp]);
2538 set_key_cmd(key, cmdbuf);
2539 if (src[cmd].exists)
2541 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2543 set_key_fld(key, config_keys[i]);
2544 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2547 else
2549 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2551 set_key_fld(key, config_keys[i]);
2552 g_key_file_remove_key(config, build_grp_name, key, NULL);
2557 g_free(key);
2558 return count;
2562 static gboolean save_project_filetype(GeanyFiletype *ft, GKeyFile *config)
2564 guint i = 0;
2565 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2567 i += build_save_menu_grp(config, ft->priv->projfilecmds, GEANY_GBG_FT, ft->name);
2568 i += build_save_menu_grp(config, ft->priv->projexeccmds, GEANY_GBG_EXEC, ft->name);
2569 if (!EMPTY(ft->priv->projerror_regex_string))
2571 g_key_file_set_string(config, build_grp_name, regkey, ft->priv->projerror_regex_string);
2572 i++;
2574 else
2575 g_key_file_remove_key(config, build_grp_name, regkey, NULL);
2576 g_free(regkey);
2577 return (i > 0);
2580 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2581 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2583 GeanyFiletype *ft;
2584 GeanyProject *pj;
2586 switch (src)
2588 case GEANY_BCS_HOME_FT:
2589 ft = (GeanyFiletype*)ptr;
2590 if (ft == NULL)
2591 return;
2592 build_save_menu_grp(config, ft->priv->homefilecmds, GEANY_GBG_FT, NULL);
2593 build_save_menu_grp(config, ft->priv->homeexeccmds, GEANY_GBG_EXEC, NULL);
2594 if (!EMPTY(ft->priv->homeerror_regex_string))
2595 g_key_file_set_string(config, build_grp_name, "error_regex", ft->priv->homeerror_regex_string);
2596 else
2597 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2598 break;
2599 case GEANY_BCS_PREF:
2600 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2601 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2602 if (!EMPTY(regex_pref))
2603 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2604 else
2605 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2606 break;
2607 case GEANY_BCS_PROJ:
2608 pj = (GeanyProject*)ptr;
2609 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2610 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2611 if (!EMPTY(regex_proj))
2612 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2613 else
2614 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2615 if (pj->priv->build_filetypes_list != NULL)
2617 GPtrArray *ft_names = g_ptr_array_new();
2618 const GPtrArray *build_fts = pj->priv->build_filetypes_list;
2620 for (guint i = 0; i < build_fts->len; i++)
2622 ft = build_fts->pdata[i];
2623 if (save_project_filetype(ft, config))
2624 g_ptr_array_add(ft_names, ft->name);
2626 if (ft_names->pdata != NULL)
2627 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2628 (const gchar**)ft_names->pdata, ft_names->len);
2629 else
2630 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2631 g_ptr_array_free(ft_names, TRUE);
2633 break;
2634 default: /* defaults and GEANY_BCS_FT can't save */
2635 break;
2640 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2641 void build_set_group_count(GeanyBuildGroup grp, gint count)
2643 guint i, sum;
2645 g_return_if_fail(count >= 0);
2647 if ((guint) count > build_groups_count[grp])
2648 build_groups_count[grp] = (guint) count;
2649 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2650 sum += build_groups_count[i];
2651 build_items_count = sum;
2655 /** Get the count of commands for the group
2657 * Get the number of commands in the group specified by @a grp.
2659 * @param grp the group of the specified menu item.
2661 * @return a count of the number of commands in the group
2664 GEANY_API_SYMBOL
2665 guint build_get_group_count(const GeanyBuildGroup grp)
2667 g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
2668 return build_groups_count[grp];
2672 static void on_project_close(void)
2674 /* remove project regexen */
2675 SETPTR(regex_proj, NULL);
2679 static struct
2681 const gchar *label;
2682 const gchar *command;
2683 const gchar *working_dir;
2684 GeanyBuildCommand **ptr;
2685 gint index;
2686 } default_cmds[] = {
2687 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2688 { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2689 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2690 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2691 { NULL, NULL, NULL, NULL, 0 }
2695 void build_init(void)
2697 GtkWidget *item;
2698 GtkWidget *toolmenu;
2699 gint cmdindex;
2701 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2703 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2704 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2705 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2706 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2708 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2710 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2711 cmd->exists = TRUE;
2712 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2713 cmd->command = g_strdup(default_cmds[cmdindex].command);
2714 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2717 /* create the toolbar Build item sub menu */
2718 toolmenu = gtk_menu_new();
2719 g_object_ref(toolmenu);
2721 /* build the code */
2722 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2723 gtk_widget_show(item);
2724 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2725 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2726 GBO_TO_POINTER(GEANY_GBO_BUILD));
2727 widgets.toolitem_build = item;
2729 item = gtk_separator_menu_item_new();
2730 gtk_widget_show(item);
2731 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2733 /* build the code with make all */
2734 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2735 gtk_widget_show(item);
2736 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2737 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2738 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2739 widgets.toolitem_make_all = item;
2741 /* build the code with make custom */
2742 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2743 gtk_widget_show(item);
2744 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2745 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2746 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2747 widgets.toolitem_make_custom = item;
2749 /* build the code with make object */
2750 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2751 gtk_widget_show(item);
2752 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2753 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2754 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2755 widgets.toolitem_make_object = item;
2757 item = gtk_separator_menu_item_new();
2758 gtk_widget_show(item);
2759 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2761 /* arguments */
2762 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
2763 gtk_widget_show(item);
2764 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2765 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2766 widgets.toolitem_set_args = item;
2768 /* get toolbar action pointers */
2769 widgets.build_action = toolbar_get_action_by_name("Build");
2770 widgets.compile_action = toolbar_get_action_by_name("Compile");
2771 widgets.run_action = toolbar_get_action_by_name("Run");
2772 widgets.toolmenu = toolmenu;
2773 /* set the submenu to the toolbar item */
2774 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);
2778 gboolean build_keybinding(guint key_id)
2780 GtkWidget *item;
2781 BuildMenuItems *menu_items;
2782 GeanyDocument *doc = document_get_current();
2784 if (doc == NULL)
2785 return TRUE;
2787 if (!gtk_widget_is_sensitive(ui_lookup_widget(main_widgets.window, "menu_build1")))
2788 return TRUE;
2790 menu_items = build_get_menu_items(doc->file_type->id);
2791 /* TODO make it a table??*/
2792 switch (key_id)
2794 case GEANY_KEYS_BUILD_COMPILE:
2795 item = menu_items->menu_item[GEANY_GBG_FT][GBO_TO_CMD(GEANY_GBO_COMPILE)];
2796 break;
2797 case GEANY_KEYS_BUILD_LINK:
2798 item = menu_items->menu_item[GEANY_GBG_FT][GBO_TO_CMD(GEANY_GBO_BUILD)];
2799 break;
2800 case GEANY_KEYS_BUILD_MAKE:
2801 item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_MAKE_ALL)];
2802 break;
2803 case GEANY_KEYS_BUILD_MAKEOWNTARGET:
2804 item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_CUSTOM)];
2805 break;
2806 case GEANY_KEYS_BUILD_MAKEOBJECT:
2807 item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)];
2808 break;
2809 case GEANY_KEYS_BUILD_NEXTERROR:
2810 item = menu_items->menu_item[GBG_FIXED][GBF_NEXT_ERROR];
2811 break;
2812 case GEANY_KEYS_BUILD_PREVIOUSERROR:
2813 item = menu_items->menu_item[GBG_FIXED][GBF_PREV_ERROR];
2814 break;
2815 case GEANY_KEYS_BUILD_RUN:
2816 item = menu_items->menu_item[GEANY_GBG_EXEC][GBO_TO_CMD(GEANY_GBO_EXEC)];
2817 break;
2818 case GEANY_KEYS_BUILD_OPTIONS:
2819 item = menu_items->menu_item[GBG_FIXED][GBF_COMMANDS];
2820 break;
2821 default:
2822 item = NULL;
2824 /* Note: For Build menu items it's OK (at the moment) to assume they are in the correct
2825 * sensitive state, but some other menus don't update the sensitive status until
2826 * they are redrawn. */
2827 if (item && gtk_widget_is_sensitive(item))
2828 gtk_menu_item_activate(GTK_MENU_ITEM(item));
2829 return TRUE;