Merge pull request #3812 from Biswa96/meson-add-geany-icon
[geany-mirror.git] / src / build.c
blob536ce55e96517e60afc5e353f92e2f8d442f5c6c
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 <stdlib.h>
54 #include <string.h>
55 #include <sys/stat.h>
56 #include <unistd.h>
57 #include <errno.h>
58 #include <gtk/gtk.h>
59 #include <glib/gstdio.h>
63 /* Number of editor indicators to draw - limited as this can affect performance */
64 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
67 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
69 static gchar *current_dir_entered = NULL;
71 typedef struct RunInfo
73 GPid pid;
74 gint file_type_id;
75 } RunInfo;
77 static RunInfo *run_info;
79 #ifndef G_OS_WIN32
80 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh";
81 #endif
83 /* Order is important (see GBO_TO_GBG, GBO_TO_CMD below) */
84 /* * Geany Known Build Commands.
85 * These commands are named after their default use.
86 * Only these commands can currently have keybindings.
87 **/
88 typedef enum
90 GEANY_GBO_COMPILE, /* *< default compile file */
91 GEANY_GBO_BUILD, /* *< default build file */
92 GEANY_GBO_MAKE_ALL, /* *< default make */
93 GEANY_GBO_CUSTOM, /* *< default make user specified target */
94 GEANY_GBO_MAKE_OBJECT, /* *< default make object, make %e.o */
95 GEANY_GBO_EXEC, /* *< default execute ./%e */
96 GEANY_GBO_COUNT /* *< count of how many */
97 } GeanyBuildType;
99 /* * Convert @c GeanyBuildType to @c GeanyBuildGroup.
101 * This macro converts @c GeanyBuildType enum values (the "known" commands)
102 * to the group they are part of.
104 * @param gbo the @c GeanyBuildType value.
106 * @return the @c GeanyBuildGroup group that @a gbo is in.
108 * Note this is a macro so that it can be used in static initialisers.
110 #define GBO_TO_GBG(gbo) \
111 ((gbo) > GEANY_GBO_EXEC ? GEANY_GBG_COUNT : \
112 ((gbo) >= GEANY_GBO_EXEC ? GEANY_GBG_EXEC : \
113 ((gbo) >= GEANY_GBO_MAKE_ALL ? GEANY_GBG_NON_FT : GEANY_GBG_FT)))
115 /* * Convert @c GeanyBuildType to command index.
117 * This macro converts @c GeanyBuildType enum values (the "known" commands)
118 * to the index within the group.
120 * @param gbo the @c GeanyBuildType value.
122 * @return the index of the @a gbo command in its group.
124 * Note this is a macro so that it can be used in static initialisers.
126 #define GBO_TO_CMD(gbo) \
127 ((gbo) >= GEANY_GBO_COUNT ? (gbo) - GEANY_GBO_COUNT : \
128 ((gbo) >= GEANY_GBO_EXEC ? (gbo) - GEANY_GBO_EXEC : \
129 ((gbo) >= GEANY_GBO_MAKE_ALL ? \
130 (gbo) - GEANY_GBO_MAKE_ALL : (gbo))))
132 /* pack group (<8) and command (<32) into a user_data pointer */
133 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
134 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
135 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
136 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
138 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
140 static BuildMenuItems build_menu_items = {NULL, {NULL, NULL, NULL, NULL}};
142 static struct
144 GtkAction *run_action;
145 GtkAction *compile_action;
146 GtkAction *build_action;
147 GtkWidget *toolmenu;
149 GtkWidget *toolitem_build;
150 GtkWidget *toolitem_make_all;
151 GtkWidget *toolitem_make_custom;
152 GtkWidget *toolitem_make_object;
153 GtkWidget *toolitem_set_args;
155 widgets;
157 static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
158 static guint build_items_count = 9;
160 static void build_exit_cb(GPid pid, gint status, gpointer user_data);
161 static void build_iofunc(GString *string, GIOCondition condition, gpointer data);
162 #ifndef G_OS_WIN32
163 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
164 #endif
165 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
166 static void set_stop_button(gboolean stop);
167 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
168 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
169 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
170 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
171 static void kill_process(GPid *pid);
172 static void show_build_result_message(gboolean failure);
173 static void process_build_output_line(gchar *msg, gint color);
174 static void show_build_commands_dialog(void);
175 static void on_build_menu_item(GtkWidget *w, gpointer user_data);
177 void build_finalize(void)
179 g_free(build_info.dir);
180 g_free(build_info.custom_target);
182 if (build_menu_items.menu != NULL && GTK_IS_WIDGET(build_menu_items.menu))
183 gtk_widget_destroy(build_menu_items.menu);
187 /* note: copied from keybindings.c, may be able to go away */
188 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
189 GtkAccelGroup *accel_group, GtkWidget *menuitem)
191 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
193 if (kb->key != 0)
194 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
195 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
199 /* convenience routines to access parts of GeanyBuildCommand */
200 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
202 switch (id)
204 case GEANY_BC_LABEL:
205 return bc->label;
206 case GEANY_BC_COMMAND:
207 return bc->command;
208 case GEANY_BC_WORKING_DIR:
209 return bc->working_dir;
211 g_assert(0);
212 return NULL;
216 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
218 switch (id)
220 case GEANY_BC_LABEL:
221 SETPTR(bc->label, str);
222 break;
223 case GEANY_BC_COMMAND:
224 SETPTR(bc->command, str);
225 break;
226 case GEANY_BC_WORKING_DIR:
227 SETPTR(bc->working_dir, str);
228 break;
229 default:
230 g_assert(0);
235 static const gchar *config_keys[GEANY_BC_CMDENTRIES_COUNT] = {
236 "LB", /* label */
237 "CM", /* command */
238 "WD" /* working directory */
241 /*-----------------------------------------------------
243 * Execute commands and handle results
245 *-----------------------------------------------------*/
247 /* the various groups of commands not in the filetype struct */
248 static GeanyBuildCommand *ft_def = NULL;
249 static GeanyBuildCommand *non_ft_proj = NULL;
250 static GeanyBuildCommand *non_ft_pref = NULL;
251 static GeanyBuildCommand *non_ft_def = NULL;
252 static GeanyBuildCommand *exec_proj = NULL;
253 static GeanyBuildCommand *exec_pref = NULL;
254 static GeanyBuildCommand *exec_def = NULL;
255 /* and the regexen not in the filetype structure */
256 static gchar *regex_pref = NULL;
257 /* project non-fileregex string */
258 static gchar *regex_proj = NULL;
260 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
261 #ifndef PRINTBUILDCMDS
262 #define PRINTBUILDCMDS FALSE
263 #endif
264 static gboolean printbuildcmds = PRINTBUILDCMDS;
267 /* for debug only, print the commands structures in priority order */
268 static void printfcmds(void)
270 #if 0
271 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
272 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
273 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
274 { &ft_def, NULL, NULL, NULL, NULL, NULL },
275 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
276 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
278 GeanyFiletype *ft = NULL;
279 GeanyDocument *doc;
280 gint i, j, k, l, m;
281 enum GeanyBuildCmdEntries n;
282 gint cc[GEANY_BCS_COUNT];
283 gchar c;
285 doc = document_get_current();
286 if (doc != NULL)
287 ft = doc->file_type;
288 if (ft != NULL)
290 printf("filetype %s\n",ft->name);
291 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->priv->filecmds);
292 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->priv->homefilecmds);
293 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->priv->projfilecmds);
294 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->priv->ftdefcmds);
295 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->priv->execcmds);
296 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->priv->homeexeccmds);
297 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->priv->projexeccmds);
299 for (i = 0; i < GEANY_BCS_COUNT; ++i)
301 m = 1;
302 for (j = 0; j < GEANY_GBG_COUNT; ++j)
304 for (k = 0; k < build_groups_count[j]; ++k)
305 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
307 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
309 if ((*(cl[j][i]))[k].entries[n] != NULL &&
310 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
312 m = l;
317 cc[i] = m;
319 for (i = 0; i < GEANY_GBG_COUNT; ++i)
321 for (k = 0; k < build_groups_count[i]; ++k)
323 for (l = 0; l < 2; ++l)
325 c = ' ';
326 for (j = 0; j < GEANY_BCS_COUNT; ++j)
328 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
330 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
332 if ((*(cl[i][j]))[k].entries[i] != NULL)
333 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
334 else
335 printf("%c %*.*s",c,cc[j],cc[j]," ");
338 else
339 printf("%c %*.*s",c,cc[j],cc[j]," ");
340 c = ',';
342 printf("\n");
345 printf("\n");
347 #endif
351 /* macros to save typing and make the logic visible */
352 #define return_cmd_if(src, cmds)\
353 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
355 *fr=src; \
356 if (printbuildcmds) \
357 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
358 return &(cmds[cmdindex]); \
361 #define return_ft_cmd_if(src, cmds)\
362 if (ft != NULL && ft->priv->cmds != NULL \
363 && ft->priv->cmds[cmdindex].exists && below>src)\
365 *fr=src; \
366 if (printbuildcmds) \
367 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
368 return &(ft->priv->cmds[cmdindex]); \
372 /* get the next lowest command taking priority into account */
373 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
374 guint below, guint *from)
376 /* Note: parameter below used in macros above */
377 GeanyFiletype *ft = NULL;
378 guint sink, *fr = &sink;
380 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
382 if (printbuildcmds)
383 printfcmds();
384 if (cmdgrp >= GEANY_GBG_COUNT)
385 return NULL;
386 if (from != NULL)
387 fr = from;
388 if (doc == NULL)
389 doc = document_get_current();
390 if (doc != NULL)
391 ft = doc->file_type;
393 switch (cmdgrp)
395 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
396 if (ft != NULL)
398 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
399 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
400 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
402 return_cmd_if(GEANY_BCS_DEF, ft_def);
403 break;
404 case GEANY_GBG_NON_FT: /* order proj, pref, def */
405 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
406 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
407 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
408 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
409 break;
410 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
411 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
412 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
413 return_cmd_if(GEANY_BCS_PREF, exec_pref);
414 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
415 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
416 return_cmd_if(GEANY_BCS_DEF, exec_def);
417 break;
418 default:
419 break;
421 return NULL;
425 /* shortcut to start looking at the top */
426 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
428 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
432 #define return_nonblank_regex(src, ptr)\
433 if (!EMPTY(ptr)) \
434 { *fr = (src); return &(ptr); }
437 /* like get_build_cmd, but for regexen, used by filetypes */
438 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
440 guint sink, *fr = &sink;
442 if (from != NULL)
443 fr = from;
444 if (grp == GEANY_GBG_FT)
446 if (ft == NULL)
448 GeanyDocument *doc = document_get_current();
449 if (doc != NULL)
450 ft = doc->file_type;
452 if (ft == NULL)
453 return NULL;
454 return_nonblank_regex(GEANY_BCS_PROJ, ft->priv->projerror_regex_string);
455 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->priv->homeerror_regex_string);
456 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
458 else if (grp == GEANY_GBG_NON_FT)
460 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
461 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
463 return NULL;
467 static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
469 GeanyDocument *doc;
470 GeanyFiletype *ft = NULL;
472 switch (grp)
474 case GEANY_GBG_FT:
475 if ((doc = document_get_current()) == NULL)
476 return NULL;
477 if ((ft = doc->file_type) == NULL)
478 return NULL;
479 switch (src)
481 case GEANY_BCS_DEF: return &(ft->priv->ftdefcmds);
482 case GEANY_BCS_FT: return &(ft->priv->filecmds);
483 case GEANY_BCS_HOME_FT: return &(ft->priv->homefilecmds);
484 case GEANY_BCS_PREF: return &(ft->priv->homefilecmds);
485 case GEANY_BCS_PROJ: return &(ft->priv->projfilecmds);
486 default: return NULL;
488 break;
489 case GEANY_GBG_NON_FT:
490 switch (src)
492 case GEANY_BCS_DEF: return &(non_ft_def);
493 case GEANY_BCS_PREF: return &(non_ft_pref);
494 case GEANY_BCS_PROJ: return &(non_ft_proj);
495 default: return NULL;
497 break;
498 case GEANY_GBG_EXEC:
499 if ((doc = document_get_current()) != NULL)
500 ft = doc->file_type;
501 switch (src)
503 case GEANY_BCS_DEF: return &(exec_def);
504 case GEANY_BCS_FT: return ft ? &(ft->priv->execcmds): NULL;
505 case GEANY_BCS_HOME_FT: return ft ? &(ft->priv->homeexeccmds): NULL;
506 case GEANY_BCS_PROJ_FT: return ft ? &(ft->priv->projexeccmds): NULL;
507 case GEANY_BCS_PREF: return &(exec_pref);
508 case GEANY_BCS_PROJ: return &(exec_proj);
509 default: return NULL;
511 break;
512 default:
513 return NULL;
518 /* get pointer to the command group array */
519 static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
521 GeanyBuildCommand **g = get_build_group_pointer(src, grp);
522 if (g == NULL) return NULL;
523 return *g;
527 /** Remove the specified Build menu item.
529 * Makes the specified menu item configuration no longer exist. This
530 * is different to setting fields to blank because the menu item
531 * will be deleted from the configuration file on saving
532 * (except the system filetypes settings @see Build Menu Configuration
533 * section of the Manual).
535 * @param src the source of the menu item to remove.
536 * @param grp the group of the command to remove.
537 * @param cmd the index (from 0) of the command within the group. A negative
538 * value will remove the whole group.
540 * If any parameter is out of range does nothing.
542 * Updates the menu.
545 GEANY_API_SYMBOL
546 void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
548 GeanyBuildCommand *bc;
549 guint i;
551 bc = get_build_group(src, grp);
552 if (bc == NULL)
553 return;
554 if (cmd < 0)
556 for (i = 0; i < build_groups_count[grp]; ++i)
557 bc[i].exists = FALSE;
559 else if ((guint) cmd < build_groups_count[grp])
560 bc[cmd].exists = FALSE;
564 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
566 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
567 * hidden by higher priority commands.
569 * @param src the source of the specified menu item.
570 * @param grp the group of the specified menu item.
571 * @param cmd the index of the command within the group.
573 * @return a pointer to the @a GeanyBuildCommand structure or @c NULL if it doesn't exist.
574 * This is a pointer to an internal structure and must not be freed.
576 * @see build_menu_update
578 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
580 GeanyBuildCommand *bc;
582 g_return_val_if_fail(src < GEANY_BCS_COUNT, NULL);
583 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
584 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
586 bc = get_build_group(src, grp);
587 if (bc == NULL)
588 return NULL;
589 return &(bc[cmd]);
593 /** Get the string for the menu item field.
595 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
596 * that the menu item will use if activated.
598 * @param grp the group of the specified menu item.
599 * @param cmd the index of the command within the group.
600 * @param fld the field to return
602 * @return @nullable a pointer to the constant string or @c NULL if it doesn't exist.
603 * This is a pointer to an internal structure and must not be freed.
606 GEANY_API_SYMBOL
607 const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
608 const GeanyBuildCmdEntries fld)
610 GeanyBuildCommand *c;
611 gchar *str = NULL;
613 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
614 g_return_val_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT, NULL);
615 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
617 c = get_build_cmd(NULL, grp, cmd, NULL);
618 if (c == NULL) return NULL;
619 switch (fld)
621 case GEANY_BC_COMMAND:
622 str = c->command;
623 break;
624 case GEANY_BC_LABEL:
625 str = c->label;
626 break;
627 case GEANY_BC_WORKING_DIR:
628 str = c->working_dir;
629 break;
630 default:
631 break;
633 return str;
636 /** Set the string for the menu item field.
638 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
640 * @param src the source of the menu item
641 * @param grp the group of the specified menu item.
642 * @param cmd the index of the menu item within the group.
643 * @param fld the field in the menu item command to set
644 * @param val the value to set the field to, is copied
647 GEANY_API_SYMBOL
648 void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp,
649 const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
651 GeanyBuildCommand **g;
653 g_return_if_fail(src < GEANY_BCS_COUNT);
654 g_return_if_fail(grp < GEANY_GBG_COUNT);
655 g_return_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT);
656 g_return_if_fail(cmd < build_groups_count[grp]);
658 g = get_build_group_pointer(src, grp);
659 if (g == NULL) return;
660 if (*g == NULL )
662 *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
664 switch (fld)
666 case GEANY_BC_COMMAND:
667 SETPTR((*g)[cmd].command, g_strdup(val));
668 (*g)[cmd].exists = TRUE;
669 break;
670 case GEANY_BC_LABEL:
671 SETPTR((*g)[cmd].label, g_strdup(val));
672 (*g)[cmd].exists = TRUE;
673 break;
674 case GEANY_BC_WORKING_DIR:
675 SETPTR((*g)[cmd].working_dir, g_strdup(val));
676 (*g)[cmd].exists = TRUE;
677 break;
678 default:
679 break;
681 build_menu_update(NULL);
684 /** Activate the menu item.
686 * Activate the menu item specified by @a grp and @a cmd.
688 * @param grp the group of the specified menu item.
689 * @param cmd the index of the command within the group.
692 GEANY_API_SYMBOL
693 void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
695 on_build_menu_item(NULL, GRP_CMD_TO_POINTER(grp, cmd));
699 /* Clear all error indicators in all documents. */
700 static void clear_all_errors(void)
702 guint i;
704 foreach_document(i)
706 editor_indicator_clear_errors(documents[i]->editor);
711 /* Replaces occurrences of %e and %p with the appropriate filenames and
712 * %l with current line number. %d and %p replacements should be in UTF8 */
713 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
715 GString *stack;
716 gchar *replacement;
717 gchar *executable = NULL;
718 gint line_num;
720 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
722 stack = g_string_new(src);
723 if (doc != NULL && doc->file_name != NULL)
725 /* replace %f with the filename (including extension) */
726 replacement = g_path_get_basename(doc->file_name);
727 utils_string_replace_all(stack, "%f", replacement);
728 g_free(replacement);
730 /* replace %d with the absolute path of the dir of the current file */
731 replacement = g_path_get_dirname(doc->file_name);
732 utils_string_replace_all(stack, "%d", replacement);
733 g_free(replacement);
735 /* replace %e with the filename (excluding extension) */
736 executable = utils_remove_ext_from_filename(doc->file_name);
737 replacement = g_path_get_basename(executable);
738 utils_string_replace_all(stack, "%e", replacement);
739 g_free(replacement);
741 /* replace %l with the current 1-based line number */
742 line_num = sci_get_current_line(doc->editor->sci) + 1;
743 replacement = g_strdup_printf("%i", line_num);
744 utils_string_replace_all(stack, "%l", replacement);
745 g_free(replacement);
748 /* replace %p with the current project's (absolute) base directory */
749 replacement = NULL; /* prevent double free if no replacement found */
750 if (app->project)
752 replacement = project_get_base_path();
754 else if (strstr(stack->str, "%p"))
755 { /* fall back to %d */
756 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
757 if (doc != NULL && doc->file_name != NULL)
758 replacement = g_path_get_dirname(doc->file_name);
761 utils_string_replace_all(stack, "%p", replacement);
762 g_free(replacement);
763 g_free(executable);
765 return g_string_free(stack, FALSE); /* don't forget to free src also if needed */
769 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
770 * idx document directory */
771 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
773 GError *error = NULL;
774 const gchar *argv[] = { "/bin/sh", "-c", NULL, NULL };
775 gchar *working_dir;
776 gchar *utf8_working_dir;
777 gchar *cmd_string;
779 g_return_if_fail(doc == NULL || doc->is_valid);
781 if ((doc == NULL || EMPTY(doc->file_name)) && EMPTY(dir))
783 geany_debug("Failed to run command with no working directory");
784 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
785 return;
788 clear_all_errors();
789 SETPTR(current_dir_entered, NULL);
791 utf8_working_dir = !EMPTY(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
792 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
794 gtk_list_store_clear(msgwindow.store_compiler);
795 // reset width after any long error messages
796 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(msgwindow.tree_compiler));
797 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
798 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), cmd, utf8_working_dir);
799 g_free(utf8_working_dir);
801 #ifdef G_OS_UNIX
802 cmd_string = utils_get_locale_from_utf8(cmd);
803 argv[2] = cmd_string;
804 cmd = NULL; /* under Unix, use argv to start cmd via sh for compatibility */
805 #else
806 /* Expand environment variables like %blah%. */
807 cmd_string = win32_expand_environment_variables(cmd);
808 argv[0] = NULL; /* under Windows, run cmd directly */
809 cmd = cmd_string;
810 #endif
812 /* set the build info for the message window */
813 g_free(build_info.dir);
814 build_info.dir = g_strdup(working_dir);
815 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
816 build_info.message_count = 0;
818 if (!spawn_with_callbacks(working_dir, cmd, (gchar **) argv, NULL, 0, NULL, NULL, build_iofunc,
819 GINT_TO_POINTER(0), 0, build_iofunc, GINT_TO_POINTER(1), 0, build_exit_cb, NULL,
820 &build_info.pid, &error))
822 geany_debug("build command spawning failed: %s", error->message);
823 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
824 g_error_free(error);
827 g_free(working_dir);
828 g_free(cmd_string);
832 /* Returns: NULL if there was an error, or the command to be executed. If Geany is
833 * set to use a run script, the returned value is a path to the script that runs
834 * the command; otherwise the command itself is returned. working_dir is a pointer
835 * to the working directory from which the command is executed. Both strings are
836 * in the locale encoding. */
837 static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
839 GeanyBuildCommand *cmd = NULL;
840 const gchar *cmd_working_dir;
841 gboolean autoclose = FALSE;
842 gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string;
843 GError *error = NULL;
845 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
847 cmd_string_utf8 = build_replace_placeholder(doc, cmd->command);
848 cmd_working_dir = cmd->working_dir;
849 if (EMPTY(cmd_working_dir))
850 cmd_working_dir = "%d";
851 working_dir_utf8 = build_replace_placeholder(doc, cmd_working_dir);
852 *working_dir = utils_get_locale_from_utf8(working_dir_utf8);
854 if (EMPTY(*working_dir) || ! g_file_test(*working_dir, G_FILE_TEST_EXISTS) ||
855 ! g_file_test(*working_dir, G_FILE_TEST_IS_DIR))
857 ui_set_statusbar(TRUE, _("Invalid working directory \"%s\""),
858 !EMPTY(working_dir_utf8) ? working_dir_utf8 : "<NULL>" );
859 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, *working_dir, NULL);
860 return NULL;
863 cmd_string = utils_get_locale_from_utf8(cmd_string_utf8);
865 #ifdef HAVE_VTE
866 if (vte_info.have_vte && vte_config.run_in_vte)
868 if (vte_config.skip_run_script)
870 utils_free_pointers(2, cmd_string_utf8, working_dir_utf8, NULL);
871 return cmd_string;
873 else
874 /* don't wait for user input at the end of script when we are running in VTE */
875 autoclose = TRUE;
877 #endif
879 #ifdef G_OS_WIN32
880 /* Expand environment variables like %blah%. */
881 SETPTR(cmd_string, win32_expand_environment_variables(cmd_string));
883 gchar *helper = g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC), "geany-run-helper", NULL);
884 /* escape helper appropriately */
885 /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
886 * allowed in paths anyway */
887 run_cmd = g_strdup_printf("\"%s\" \"%s\" %d %s", helper, *working_dir, autoclose ? 1 : 0, cmd_string);
888 g_free(helper);
889 #else
890 run_cmd = build_create_shellscript(*working_dir, cmd_string, autoclose, &error);
891 if (!run_cmd)
893 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
894 !EMPTY(cmd_string_utf8) ? cmd_string_utf8 : NULL, error->message);
895 g_error_free(error);
896 g_free(*working_dir);
898 #endif
899 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
900 return run_cmd;
904 static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
906 gchar *working_dir;
907 gchar *run_cmd = NULL;
909 if (! DOC_VALID(doc) || doc->file_name == NULL)
910 return;
912 run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex);
913 if (run_cmd == NULL)
914 return;
916 run_info[cmdindex].file_type_id = doc->file_type->id;
918 #ifdef HAVE_VTE
919 if (vte_info.have_vte && vte_config.run_in_vte)
921 gchar *vte_cmd;
923 /* VTE expects commands in UTF-8 */
924 SETPTR(run_cmd, utils_get_utf8_from_locale(run_cmd));
925 SETPTR(working_dir, utils_get_utf8_from_locale(working_dir));
927 if (vte_config.skip_run_script)
928 vte_cmd = g_strconcat(run_cmd, "\n", NULL);
929 else
930 vte_cmd = g_strconcat("\n/bin/sh ", run_cmd, "\n", NULL);
932 vte_cwd(working_dir, TRUE);
933 if (! vte_send_cmd(vte_cmd))
935 const gchar *msg = _("File not executed because the terminal may contain some input (press Ctrl+C or Enter to clear it).");
936 ui_set_statusbar(FALSE, "%s", msg);
937 geany_debug("%s", msg);
938 if (!vte_config.skip_run_script)
939 g_unlink(run_cmd);
942 /* show the VTE */
943 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
944 gtk_widget_grab_focus(vte_config.vte);
945 msgwin_show_hide(TRUE);
947 run_info[cmdindex].pid = 1;
948 g_free(vte_cmd);
950 else
951 #endif
953 gchar *locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
954 GError *error = NULL;
956 #ifdef G_OS_WIN32
957 if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd, 0, 0))
959 /* if passing an argument to cmd.exe, respect its quoting rules */
960 GString *escaped_run_cmd = g_string_new(NULL);
961 for (gchar *p = run_cmd; *p; p++)
963 if (strchr("()%!^\"<>&| ", *p)) // cmd.exe metacharacters
964 g_string_append_c(escaped_run_cmd, '^');
965 g_string_append_c(escaped_run_cmd, *p);
967 SETPTR(run_cmd, g_string_free(escaped_run_cmd, FALSE));
969 #endif
971 utils_str_replace_all(&locale_term_cmd, "%c", run_cmd);
973 if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid),
974 &error))
976 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
977 (gpointer) &(run_info[cmdindex]));
978 build_menu_update(doc);
980 else
982 gchar *utf8_term_cmd = utils_get_utf8_from_locale(locale_term_cmd);
983 ui_set_statusbar(TRUE, _("Cannot execute build command \"%s\": %s. "
984 "Check the Terminal setting in Preferences"), utf8_term_cmd, error->message);
985 g_free(utf8_term_cmd);
986 g_error_free(error);
987 #ifndef G_OS_WIN32
988 g_unlink(run_cmd);
989 #endif
990 run_info[cmdindex].pid = (GPid) 0;
994 g_free(working_dir);
995 g_free(run_cmd);
999 static void process_build_output_line(gchar *msg, gint color)
1001 gchar *tmp;
1002 gchar *filename;
1003 gint line;
1005 g_strchomp(msg);
1007 if (EMPTY(msg))
1008 return;
1010 if (build_parse_make_dir(msg, &tmp))
1012 SETPTR(current_dir_entered, tmp);
1014 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
1016 if (line != -1 && filename != NULL)
1018 GeanyDocument *doc = document_find_by_filename(filename);
1020 /* limit number of indicators */
1021 if (doc && editor_prefs.use_indicators &&
1022 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
1024 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
1025 line--; /* so only adjust the line number if it is greater than 0 */
1026 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
1028 build_info.message_count++;
1029 color = COLOR_RED; /* error message parsed on the line */
1031 if (build_info.message_count == 1)
1033 gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_NEXT_ERROR], TRUE);
1034 gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_PREV_ERROR], TRUE);
1037 g_free(filename);
1039 msgwin_compiler_add_string(color, msg);
1043 static void build_iofunc(GString *string, GIOCondition condition, gpointer data)
1045 if (condition & (G_IO_IN | G_IO_PRI))
1047 process_build_output_line(string->str,
1048 (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK);
1053 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1055 const gchar *pos;
1057 *prefix = NULL;
1059 if (string == NULL)
1060 return FALSE;
1062 if ((pos = strstr(string, "Entering directory")) != NULL)
1064 gsize len;
1065 gchar *input;
1067 /* get the start of the path */
1068 pos = strstr(string, "/");
1070 if (pos == NULL)
1071 return FALSE;
1073 input = g_strdup(pos);
1075 /* kill the ' at the end of the path */
1076 len = strlen(input);
1077 input[len - 1] = '\0';
1078 input = g_realloc(input, len); /* shorten by 1 */
1079 *prefix = input;
1081 return TRUE;
1084 if (strstr(string, "Leaving directory") != NULL)
1086 *prefix = NULL;
1087 return TRUE;
1090 return FALSE;
1094 static void show_build_result_message(gboolean failure)
1096 gchar *msg;
1098 if (failure)
1100 msg = _("Compilation failed.");
1101 msgwin_compiler_add_string(COLOR_BLUE, msg);
1102 /* If msgwindow is hidden, user will want to display it to see the error */
1103 if (! ui_prefs.msgwindow_visible)
1105 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1106 msgwin_show_hide(TRUE);
1108 else
1109 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1110 ui_set_statusbar(FALSE, "%s", msg);
1112 else
1114 msg = _("Compilation finished successfully.");
1115 msgwin_compiler_add_string(COLOR_BLUE, msg);
1116 if (! ui_prefs.msgwindow_visible ||
1117 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1118 ui_set_statusbar(FALSE, "%s", msg);
1123 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1125 show_build_result_message(!SPAWN_WIFEXITED(status) || SPAWN_WEXITSTATUS(status) != EXIT_SUCCESS);
1126 utils_beep();
1128 build_info.pid = 0;
1129 /* enable build items again */
1130 build_menu_update(NULL);
1131 ui_progress_bar_stop();
1135 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1137 RunInfo *run_info_data = user_data;
1139 g_spawn_close_pid(child_pid);
1141 run_info_data->pid = 0;
1142 /* reset the stop button and menu item to the original meaning */
1143 build_menu_update(NULL);
1147 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1148 * working_dir and cmd are both in the locale encoding
1149 * it returns the full file name (including path) of the created script in the locale encoding */
1150 #ifndef G_OS_WIN32
1151 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
1153 gint fd;
1154 gchar *str, *fname;
1155 gboolean success = TRUE;
1156 gchar *escaped_dir;
1157 fd = g_file_open_tmp (RUN_SCRIPT_CMD, &fname, error);
1158 if (fd < 0)
1159 return NULL;
1160 close(fd);
1162 escaped_dir = g_shell_quote(working_dir);
1163 str = g_strdup_printf(
1164 "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1165 \n\n%s\n", escaped_dir, cmd, (autoclose) ? "" :
1166 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1167 "dash\ndummy_var=\"\"\nread dummy_var");
1168 g_free(escaped_dir);
1170 if (!g_file_set_contents(fname, str, -1, error))
1171 success = FALSE;
1172 g_free(str);
1173 #ifdef __APPLE__
1174 if (success && g_chmod(fname, 0777) != 0)
1176 if (error)
1178 gint errsv = errno;
1180 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv),
1181 "Failed to make file executable: %s", g_strerror(errsv));
1183 success = FALSE;
1185 #endif
1187 if (!success)
1189 g_unlink(fname);
1190 g_free(fname);
1191 fname = NULL;
1194 return fname;
1196 #endif
1199 typedef void Callback(GtkWidget *w, gpointer u);
1201 /* run the command catenating cmd_cat if present */
1202 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
1204 gchar *dir;
1205 gchar *full_command, *subs_command;
1206 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1207 gchar *cmdstr;
1209 if (buildcmd == NULL)
1210 return;
1212 cmdstr = buildcmd->command;
1214 if (cmd_cat != NULL)
1216 if (cmdstr != NULL)
1217 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1218 else
1219 full_command = g_strdup(cmd_cat);
1221 else
1222 full_command = cmdstr;
1224 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1225 subs_command = build_replace_placeholder(doc, full_command);
1226 build_info.grp = grp;
1227 build_info.cmd = cmd;
1228 build_spawn_cmd(doc, subs_command, dir);
1229 g_free(subs_command);
1230 g_free(dir);
1231 if (cmd_cat != NULL)
1232 g_free(full_command);
1233 build_menu_update(doc);
1234 if (build_info.pid)
1235 ui_progress_bar_start(NULL);
1239 /*----------------------------------------------------------------
1241 * Create build menu and handle callbacks (&toolbar callbacks)
1243 *----------------------------------------------------------------*/
1244 static void on_make_custom_input_response(const gchar *input, gpointer data)
1246 GeanyDocument *doc = document_get_current();
1248 SETPTR(build_info.custom_target, g_strdup(input));
1249 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1250 build_info.custom_target);
1254 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1256 GeanyDocument *doc = document_get_current();
1257 GeanyBuildCommand *bc;
1258 guint grp = GPOINTER_TO_GRP(user_data);
1259 guint cmd = GPOINTER_TO_CMD(user_data);
1261 if (doc && doc->changed)
1263 if (!document_save_file(doc, FALSE))
1264 return;
1266 g_signal_emit_by_name(geany_object, "build-start");
1268 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1270 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1272 if (! dialog)
1274 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1275 _("Enter custom text here, all entered text is appended to the command."),
1276 build_info.custom_target, &on_make_custom_input_response, NULL);
1278 else
1280 gtk_widget_show(dialog);
1282 return;
1284 else if (grp == GEANY_GBG_EXEC)
1286 if (run_info[cmd].pid > (GPid) 1)
1288 kill_process(&run_info[cmd].pid);
1289 return;
1291 bc = get_build_cmd(doc, grp, cmd, NULL);
1292 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1294 const gchar *uri_file_prefix;
1295 gchar *uri;
1296 if (doc == NULL)
1297 return;
1298 uri_file_prefix = utils_get_uri_file_prefix();
1299 uri = g_strconcat(uri_file_prefix, doc->file_name, NULL);
1300 utils_open_browser(uri);
1301 g_free(uri);
1303 else
1304 build_run_cmd(doc, cmd);
1306 else
1307 build_command(doc, grp, cmd, NULL);
1311 /* group codes for menu items other than the known commands
1312 * value order is important, see the following table for use */
1314 /* the rest in each group */
1315 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1316 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1317 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1318 /* the separator */
1319 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1320 /* the fixed items */
1321 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1322 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1323 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1324 #define MENU_DONE (MENU_COMMANDS + 1)
1327 static struct BuildMenuItemSpec {
1328 const gchar *stock_id;
1329 const gint key_binding;
1330 const guint build_grp;
1331 const guint build_cmd;
1332 const gchar *fix_label;
1333 Callback *cb;
1334 } build_menu_specs[] = {
1335 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1336 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1337 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1338 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1339 {NULL, -1, MENU_FT_REST,
1340 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1341 {NULL, -1, MENU_SEPARATOR,
1342 GBF_SEP_1, NULL, NULL},
1343 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1344 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1345 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1346 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1347 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1348 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1349 {NULL, -1, MENU_NON_FT_REST,
1350 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1351 {NULL, -1, MENU_SEPARATOR,
1352 GBF_SEP_2, NULL, NULL},
1353 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1354 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1355 {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1356 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1357 {NULL, -1, MENU_SEPARATOR,
1358 GBF_SEP_3, NULL, NULL},
1359 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1360 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1361 {NULL, -1, MENU_EXEC_REST,
1362 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1363 {NULL, -1, MENU_SEPARATOR,
1364 GBF_SEP_4, NULL, NULL},
1365 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1366 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1367 {NULL, -1, MENU_DONE,
1368 0, NULL, NULL}
1372 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1373 struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
1375 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1377 if (bs->stock_id != NULL)
1379 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1380 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1382 gtk_widget_show(item);
1383 if (bs->key_binding >= 0)
1384 add_menu_accel(group, (guint) bs->key_binding, ag, item);
1385 gtk_container_add(GTK_CONTAINER(menu), item);
1386 if (bs->cb != NULL)
1388 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1390 build_menu_items.menu_item[grp][cmd] = item;
1394 static void create_build_menu(BuildMenuItems *menu_items)
1396 GtkWidget *menu;
1397 GtkAccelGroup *accel_group = gtk_accel_group_new();
1398 GeanyKeyGroup *keygroup = keybindings_get_core_group(GEANY_KEY_GROUP_BUILD);
1399 guint i, j;
1401 menu = gtk_menu_new();
1402 menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1403 menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1404 menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1405 menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1407 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1409 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1410 if (bs->build_grp == MENU_SEPARATOR)
1412 GtkWidget *item = gtk_separator_menu_item_new();
1413 gtk_widget_show(item);
1414 gtk_container_add(GTK_CONTAINER(menu), item);
1415 menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1417 else if (bs->fix_label != NULL)
1419 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1420 GBG_FIXED, bs->build_cmd);
1422 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1424 guint grp = bs->build_grp - GEANY_GBG_COUNT;
1425 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1427 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1428 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1429 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1432 else
1434 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1435 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1436 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1439 menu_items->menu = menu;
1440 gtk_widget_show(menu);
1441 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1445 /* * Update the build menu to reflect changes in configuration or status.
1447 * Sets the labels and number of visible items to match the highest
1448 * priority configured commands. Also sets sensitivity if build commands are
1449 * running and switches executes to stop when commands are running.
1451 * @param doc The current document, if available, to save looking it up.
1452 * If @c NULL it will be looked up.
1454 * Call this after modifying any fields of a GeanyBuildCommand structure.
1456 * @see Build Menu Configuration section of the Manual.
1459 void build_menu_update(GeanyDocument *doc)
1461 guint i, cmdcount, cmd, grp;
1462 gboolean vis = FALSE;
1463 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1464 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1465 GeanyBuildCommand *bc;
1467 g_return_if_fail(doc == NULL || doc->is_valid);
1469 if (build_menu_items.menu == NULL)
1470 create_build_menu(&build_menu_items);
1471 if (doc == NULL)
1472 doc = document_get_current();
1473 have_path = doc != NULL && doc->file_name != NULL;
1474 build_running = build_info.pid > (GPid) 1;
1475 // note: compiler list store may have been cleared since last build
1476 have_errors = build_info.message_count > 0 &&
1477 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1478 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1480 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1481 switch (bs->build_grp)
1483 case MENU_SEPARATOR:
1484 if (vis == TRUE)
1486 gtk_widget_show_all(build_menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1487 vis = FALSE;
1489 else
1490 gtk_widget_hide(build_menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1491 break;
1492 case MENU_NEXT_ERROR:
1493 case MENU_PREV_ERROR:
1494 gtk_widget_set_sensitive(build_menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1495 vis |= TRUE;
1496 break;
1497 case MENU_COMMANDS:
1498 vis |= TRUE;
1499 break;
1500 default: /* all configurable commands */
1501 if (bs->build_grp >= GEANY_GBG_COUNT)
1503 grp = bs->build_grp - GEANY_GBG_COUNT;
1504 cmdcount = build_groups_count[grp];
1506 else
1508 grp = bs->build_grp;
1509 cmdcount = bs->build_cmd + 1;
1511 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1513 GtkWidget *menu_item = build_menu_items.menu_item[grp][cmd];
1514 const gchar *label;
1515 bc = get_build_cmd(doc, grp, cmd, NULL);
1516 if (bc)
1517 label = bc->label;
1518 else
1519 label = NULL;
1521 if (grp < GEANY_GBG_EXEC)
1523 cmd_sensitivity =
1524 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1525 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1526 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1527 if (bc != NULL && !EMPTY(label))
1529 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
1530 gtk_widget_show_all(menu_item);
1531 vis |= TRUE;
1533 else
1534 gtk_widget_hide(menu_item);
1536 else
1538 GtkWidget *image;
1539 exec_running = run_info[cmd].pid > (GPid) 1;
1540 cmd_sensitivity = (bc != NULL) || exec_running;
1541 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1542 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1543 run_sensitivity = cmd_sensitivity;
1544 if (! exec_running)
1546 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1548 else
1550 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1552 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1553 run_running = exec_running;
1554 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1555 if (bc != NULL && !EMPTY(label))
1557 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
1558 gtk_widget_show_all(menu_item);
1559 vis |= TRUE;
1561 else
1562 gtk_widget_hide(menu_item);
1568 run_sensitivity &= (doc != NULL);
1569 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1570 && have_path && ! build_running;
1571 if (widgets.toolitem_build != NULL)
1572 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1573 can_make = FALSE;
1574 if (widgets.toolitem_make_all != NULL)
1575 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1576 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1577 && ! build_running));
1578 if (widgets.toolitem_make_custom != NULL)
1579 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1580 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1581 && ! build_running));
1582 if (widgets.toolitem_make_object != NULL)
1583 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1584 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1585 && ! build_running));
1586 if (widgets.toolitem_set_args != NULL)
1587 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1589 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1590 && have_path && ! build_running;
1591 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1592 gtk_action_set_sensitive(widgets.build_action, can_make);
1593 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1595 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1596 set_stop_button(run_running);
1601 /* Call build_menu_update() instead of calling this directly. */
1602 static void set_stop_button(gboolean stop)
1604 const gchar *button_stock_id = NULL;
1605 GtkToolButton *run_button;
1607 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1608 if (run_button != NULL)
1609 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1611 if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
1612 return;
1613 if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
1614 return;
1616 /* use the run button also as stop button */
1617 if (stop)
1619 if (run_button != NULL)
1620 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
1622 else
1624 if (run_button != NULL)
1625 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
1630 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1632 /* For now, just show the project dialog */
1633 if (app->project)
1634 project_build_properties();
1635 else
1636 show_build_commands_dialog();
1640 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1642 last_toolbutton_action = user_data;
1643 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1644 on_build_menu_item(menuitem, user_data);
1648 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1650 gchar *msg;
1652 last_toolbutton_action = user_data;
1653 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1654 msg = _("Build the current file with Make and the default target");
1655 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1656 msg = _("Build the current file with Make and the specified target");
1657 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1658 msg = _("Compile the current file with Make");
1659 else
1660 msg = NULL;
1661 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1662 on_build_menu_item(menuitem, user_data);
1666 static void kill_process(GPid *pid)
1668 GError *error = NULL;
1670 if (spawn_kill_process(*pid, &error))
1672 *pid = 0;
1673 build_menu_update(NULL);
1675 else
1677 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), error->message);
1678 g_error_free(error);
1683 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1685 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1686 msgwin_goto_compiler_file_line))
1688 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1690 else
1691 ui_set_statusbar(FALSE, _("No more build errors."));
1695 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1697 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1698 msgwin_goto_compiler_file_line))
1700 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1702 else
1703 ui_set_statusbar(FALSE, _("No more build errors."));
1707 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1709 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1711 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1713 else
1715 on_build_menu_item(NULL, last_toolbutton_action);
1720 /*------------------------------------------------------
1722 * Create and handle the build menu configuration dialog
1724 *-------------------------------------------------------*/
1725 typedef struct RowWidgets
1727 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1728 GeanyBuildSource src;
1729 GeanyBuildSource dst;
1730 GeanyBuildCommand *cmdsrc;
1731 guint grp;
1732 guint cmd;
1733 gboolean cleared;
1734 gboolean used_dst;
1735 } RowWidgets;
1738 static GdkRGBA insensitive_color;
1740 static void set_row_color(RowWidgets *r, GdkRGBA *color)
1742 enum GeanyBuildCmdEntries i;
1744 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1746 if (i == GEANY_BC_LABEL)
1747 continue;
1749 gtk_widget_override_color(r->entries[i], GTK_STATE_FLAG_NORMAL, color);
1754 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1756 if (GTK_IS_BUTTON(wid))
1757 gtk_button_set_label(GTK_BUTTON(wid), text);
1758 else
1759 gtk_entry_set_text(GTK_ENTRY(wid), text);
1763 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1765 RowWidgets *r = user_data;
1766 guint src;
1767 enum GeanyBuildCmdEntries i;
1768 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1770 if (bc != NULL)
1772 r->cmdsrc = bc;
1773 r->src = src;
1774 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1776 set_build_command_entry_text(r->entries[i],
1777 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1780 else
1782 r->cmdsrc = NULL;
1783 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1785 set_build_command_entry_text(r->entries[i], "");
1788 r->used_dst = FALSE;
1789 set_row_color(r, &insensitive_color);
1790 r->cleared = TRUE;
1794 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1796 gtk_entry_set_text(regex,"");
1800 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1802 RowWidgets *r = user_data;
1803 GtkWidget *top_level = gtk_widget_get_toplevel(wid);
1804 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1805 gchar *str;
1807 if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
1808 str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
1809 else
1810 str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
1812 if (!str)
1813 return;
1815 gtk_button_set_label(GTK_BUTTON(wid), str);
1816 g_free(str);
1817 r->used_dst = TRUE;
1818 set_row_color(r, NULL);
1822 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1824 RowWidgets *r = user_data;
1826 r->used_dst = TRUE;
1827 set_row_color(r, NULL);
1831 /* Column headings, array NULL-terminated */
1832 static const gchar *colheads[] =
1834 "#",
1835 N_("Label"),
1836 N_("Command"),
1837 N_("Working directory"),
1838 N_("Reset"),
1839 NULL
1842 /* column names */
1843 #define DC_ITEM 0
1844 #define DC_ENTRIES 1
1845 #define DC_CLEAR 4
1846 #define DC_N_COL 5
1848 static const guint entry_x_padding = 3;
1849 static const guint entry_y_padding = 0;
1852 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1853 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
1855 GtkWidget *label, *clear, *clearicon;
1856 RowWidgets *roww;
1857 GeanyBuildCommand *bc;
1858 guint src;
1859 enum GeanyBuildCmdEntries i;
1860 guint column = 0;
1861 gchar *text;
1862 GtkStyleContext *ctx;
1864 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
1866 text = g_strdup_printf("%d.", cmd + 1);
1867 label = gtk_label_new(text);
1868 g_free(text);
1870 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 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1876 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1877 roww = g_new0(RowWidgets, 1);
1878 roww->src = GEANY_BCS_COUNT;
1879 roww->grp = grp;
1880 roww->cmd = cmd;
1881 roww->dst = dst;
1882 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1884 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1886 column += 1;
1887 if (i == GEANY_BC_LABEL)
1889 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1890 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1891 gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1892 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
1894 else
1896 roww->entries[i] = gtk_entry_new();
1897 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
1899 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1900 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1902 column++;
1903 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1904 clear = gtk_button_new();
1905 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1906 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1907 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1908 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1909 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1910 if (bc != NULL)
1911 roww->src = src;
1913 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1915 const gchar *str = "";
1917 if (bc != NULL )
1919 if ((str = id_to_str(bc, i)) == NULL)
1920 str = "";
1921 else if (dst == src)
1922 roww->used_dst = TRUE;
1924 set_build_command_entry_text(roww->entries[i], str);
1926 if (bc != NULL && (dst > src))
1927 set_row_color(roww, &insensitive_color);
1928 if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1930 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1931 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1932 gtk_widget_set_sensitive(clear, FALSE);
1934 return roww;
1938 typedef struct BuildTableFields
1940 RowWidgets **rows;
1941 GtkWidget *fileregex;
1942 GtkWidget *nonfileregex;
1943 gchar **fileregexstring;
1944 gchar **nonfileregexstring;
1945 } BuildTableFields;
1948 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
1949 GeanyFiletype *ft)
1951 GtkWidget *label, *sep, *clearicon, *clear;
1952 BuildTableFields *fields;
1953 GtkTable *table;
1954 const gchar **ch;
1955 gchar *txt;
1956 guint col, row, cmdindex;
1957 guint cmd;
1958 guint src;
1959 gboolean sensitivity;
1960 guint sep_padding = entry_y_padding + 3;
1962 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1963 fields = g_new0(BuildTableFields, 1);
1964 fields->rows = g_new0(RowWidgets*, build_items_count);
1965 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1967 label = gtk_label_new(_(*ch));
1968 gtk_table_attach(table, label, col, col + 1, 0, 1,
1969 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1971 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1972 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1973 entry_x_padding, sep_padding);
1974 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1975 txt = g_strdup_printf(_("%s commands"), ft->name);
1976 else
1977 txt = g_strdup_printf(_("%s commands"), _("No filetype"));
1979 label = ui_label_new_bold(txt);
1980 g_free(txt);
1981 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1982 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1983 entry_x_padding, entry_y_padding);
1984 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1985 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1986 label = gtk_label_new(_("Error regular expression:"));
1987 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1988 entry_x_padding, entry_y_padding);
1989 fields->fileregex = gtk_entry_new();
1990 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
1991 sensitivity = (ft == NULL) ? FALSE : TRUE;
1992 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1994 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1995 if (src > dst)
1996 sensitivity = FALSE;
1998 gtk_table_attach(table, fields->fileregex, 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->fileregex));
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->fileregex, sensitivity);
2008 gtk_widget_set_sensitive(clear, sensitivity);
2009 ++row;
2010 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2011 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2012 entry_x_padding, sep_padding);
2013 ++row;
2014 label = ui_label_new_bold(_("Independent commands"));
2015 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2016 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2017 entry_x_padding, entry_y_padding);
2018 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
2019 fields->rows[cmdindex] = build_add_dialog_row(
2020 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
2021 label = gtk_label_new(_("Error regular expression:"));
2022 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
2023 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2024 fields->nonfileregex = gtk_entry_new();
2025 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
2026 sensitivity = TRUE;
2027 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
2029 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
2030 sensitivity = src > dst ? FALSE : TRUE;
2032 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2033 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2034 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2035 clear = gtk_button_new();
2036 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2037 g_signal_connect_swapped(clear, "clicked",
2038 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
2039 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2040 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2041 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
2042 gtk_widget_set_sensitive(clear, sensitivity);
2043 ++row;
2044 label = gtk_label_new(NULL);
2045 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2046 _("Note: Item 2 opens a dialog and appends the response to the command."));
2047 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2048 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2049 entry_x_padding, entry_y_padding);
2050 ++row;
2051 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2052 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2053 entry_x_padding, sep_padding);
2054 ++row;
2055 label = ui_label_new_bold(_("Execute commands"));
2056 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2057 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2058 entry_x_padding, entry_y_padding);
2059 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
2060 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
2061 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2062 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2063 entry_x_padding, sep_padding);
2064 ++row;
2065 label = gtk_label_new(NULL);
2066 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2067 _("%d, %e, %f, %p, %l are substituted in command and directory fields, see manual for details."));
2068 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2069 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2070 entry_x_padding, entry_y_padding);
2071 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2072 ++row;
2073 *table_data = fields;
2074 return GTK_WIDGET(table);
2078 void build_free_fields(BuildTableData table_data)
2080 guint cmdindex;
2082 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2083 g_free(table_data->rows[cmdindex]);
2084 g_free(table_data->rows);
2085 g_free(table_data);
2089 /* string compare where null pointers match null or 0 length strings */
2090 #if 0
2091 static gint stcmp(const gchar *a, const gchar *b)
2093 if (a == NULL && b == NULL)
2094 return 0;
2095 if (a == NULL && b != NULL)
2096 return strlen(b);
2097 if (a != NULL && b == NULL)
2098 return strlen(a);
2099 return strcmp(a, b);
2101 #endif
2104 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2106 if (GTK_IS_BUTTON(wid))
2107 return gtk_button_get_label(GTK_BUTTON(wid));
2108 else
2109 return gtk_entry_get_text(GTK_ENTRY(wid));
2113 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
2115 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2116 gboolean changed = FALSE;
2117 enum GeanyBuildCmdEntries i;
2119 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2121 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2123 if (table_data->rows[drow]->cleared)
2125 if (dst->dst[grp] != NULL)
2127 if (*(dst->dst[grp]) == NULL)
2128 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2129 (*(dst->dst[grp]))[cmd].exists = FALSE;
2130 (*(dst->dst[grp]))[cmd].changed = TRUE;
2131 changed = TRUE;
2134 if (table_data->rows[drow]->used_dst == TRUE)
2136 if (dst->dst[grp] != NULL)
2138 if (*(dst->dst[grp]) == NULL)
2139 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2140 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2141 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2142 (*(dst->dst[grp]))[cmd].exists = TRUE;
2143 (*(dst->dst[grp]))[cmd].changed = TRUE;
2144 changed = TRUE;
2147 else
2149 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2150 g_free(entries[i]);
2152 return changed;
2156 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2158 gboolean changed = FALSE;
2159 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2161 if (((src == NULL /* originally there was no regex */
2162 || *src == NULL) /* or it was NULL*/
2163 && !EMPTY(reg)) /* and something was typed */
2164 || (src != NULL /* originally there was a regex*/
2165 && (*src == NULL /* and either it was NULL */
2166 || strcmp(*src, reg) != 0))) /* or it has been changed */
2168 if (dst != NULL)
2170 SETPTR(*dst, g_strdup(reg));
2171 changed = TRUE;
2174 return changed;
2178 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2180 guint cmdindex, cmd;
2181 gboolean changed = FALSE;
2183 if (response == GTK_RESPONSE_ACCEPT)
2185 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2186 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2187 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2188 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2189 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2190 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2191 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2192 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2194 return changed;
2198 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2200 BuildDestination menu_dst;
2202 if (ft != NULL)
2204 menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds);
2205 menu_dst.fileregexstr = &(ft->priv->projerror_regex_string);
2207 else
2209 menu_dst.dst[GEANY_GBG_FT] = NULL;
2210 menu_dst.fileregexstr = NULL;
2212 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2213 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2214 menu_dst.nonfileregexstr = &regex_proj;
2216 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2220 static void show_build_commands_dialog(void)
2222 GtkWidget *dialog, *table, *vbox;
2223 GeanyDocument *doc = document_get_current();
2224 GeanyFiletype *ft = NULL;
2225 const gchar *title = _("Set Build Commands");
2226 gint response;
2227 BuildTableData table_data;
2228 BuildDestination prefdsts;
2230 if (doc != NULL)
2231 ft = doc->file_type;
2232 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2233 GTK_DIALOG_DESTROY_WITH_PARENT,
2234 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2235 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2236 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2237 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2238 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2239 gtk_widget_show_all(dialog);
2240 /* run modally to prevent user changing idx filetype */
2241 response = gtk_dialog_run(GTK_DIALOG(dialog));
2243 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2244 if (ft != NULL)
2246 prefdsts.dst[GEANY_GBG_FT] = &(ft->priv->homefilecmds);
2247 prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string);
2248 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds);
2250 else
2252 prefdsts.dst[GEANY_GBG_FT] = NULL;
2253 prefdsts.fileregexstr = NULL;
2254 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2256 prefdsts.nonfileregexstr = &regex_pref;
2257 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2258 filetypes_save_commands(ft);
2259 build_free_fields(table_data);
2261 build_menu_update(doc);
2262 gtk_widget_destroy(dialog);
2266 /* Creates the relevant build menu if necessary. */
2267 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2269 BuildMenuItems *items;
2271 items = &build_menu_items;
2272 if (items->menu == NULL)
2273 create_build_menu(items);
2274 return items;
2278 /*----------------------------------------------------------
2280 * Load and store configuration
2282 * ---------------------------------------------------------*/
2283 static const gchar *build_grp_name = "build-menu";
2285 /* config format for build-menu group is prefix_gg_nn_xx=value
2286 * where gg = FT, NF, EX for the command group
2287 * nn = 2 digit command number
2288 * xx = LB for label, CM for command and WD for working dir */
2289 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2290 static const gchar *fixedkey="xx_xx_xx";
2292 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2293 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2294 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2296 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2297 gchar *prefix, gboolean loc)
2299 guint cmd;
2300 gsize prefixlen; /* NOTE prefixlen used in macros above */
2301 GeanyBuildCommand *dstcmd;
2302 gchar *key;
2303 static gchar cmdbuf[4] = " ";
2305 if (*dst == NULL)
2306 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2307 dstcmd = *dst;
2308 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2309 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2310 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2312 gchar *label;
2313 if (cmd >= 100)
2314 break; /* ensure no buffer overflow */
2315 sprintf(cmdbuf, "%02u", cmd);
2316 set_key_grp(key, groups[grp]);
2317 set_key_cmd(key, cmdbuf);
2318 set_key_fld(key, "LB");
2319 if (loc)
2320 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2321 else
2322 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2323 if (label != NULL)
2325 dstcmd[cmd].exists = TRUE;
2326 SETPTR(dstcmd[cmd].label, label);
2327 set_key_fld(key,"CM");
2328 SETPTR(dstcmd[cmd].command,
2329 g_key_file_get_string(config, build_grp_name, key, NULL));
2330 set_key_fld(key,"WD");
2331 SETPTR(dstcmd[cmd].working_dir,
2332 g_key_file_get_string(config, build_grp_name, key, NULL));
2334 else dstcmd[cmd].exists = FALSE;
2336 g_free(key);
2340 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2341 static void assign_cmd(GeanyBuildCommand *type, guint id,
2342 const gchar *label, gchar *value)
2344 if (!EMPTY(value) && ! type[GBO_TO_CMD(id)].exists)
2346 type[GBO_TO_CMD(id)].exists = TRUE;
2347 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(label));
2348 SETPTR(type[GBO_TO_CMD(id)].command, value);
2349 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL);
2350 type[GBO_TO_CMD(id)].old = TRUE;
2352 else
2353 g_free(value);
2356 /* for the specified source load new format build menu items or try to make some sense of
2357 * old format setings, not done perfectly but better than ignoring them */
2358 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2360 GeanyFiletype *ft;
2361 GeanyProject *pj;
2362 gchar **ftlist;
2363 gchar *value, *basedir, *makebasedir;
2364 gboolean bvalue = FALSE;
2366 if (g_key_file_has_group(config, build_grp_name))
2368 switch (src)
2370 case GEANY_BCS_FT:
2371 ft = (GeanyFiletype*)p;
2372 if (ft == NULL)
2373 return;
2374 build_load_menu_grp(config, &(ft->priv->filecmds), GEANY_GBG_FT, NULL, TRUE);
2375 build_load_menu_grp(config, &(ft->priv->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2376 build_load_menu_grp(config, &(ft->priv->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2377 SETPTR(ft->error_regex_string,
2378 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2379 break;
2380 case GEANY_BCS_HOME_FT:
2381 ft = (GeanyFiletype*)p;
2382 if (ft == NULL)
2383 return;
2384 build_load_menu_grp(config, &(ft->priv->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2385 build_load_menu_grp(config, &(ft->priv->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2386 SETPTR(ft->priv->homeerror_regex_string,
2387 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2388 break;
2389 case GEANY_BCS_PREF:
2390 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2391 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2392 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2393 break;
2394 case GEANY_BCS_PROJ:
2395 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2396 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2397 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2398 pj = (GeanyProject*)p;
2399 if (p == NULL)
2400 return;
2401 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2402 if (ftlist != NULL)
2404 gchar **ftname;
2405 if (pj->priv->build_filetypes_list == NULL)
2406 pj->priv->build_filetypes_list = g_ptr_array_new();
2407 g_ptr_array_set_size(pj->priv->build_filetypes_list, 0);
2408 for (ftname = ftlist; *ftname != NULL; ++ftname)
2410 ft = filetypes_lookup_by_name(*ftname);
2411 if (ft != NULL)
2413 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2414 g_ptr_array_add(pj->priv->build_filetypes_list, ft);
2415 SETPTR(ft->priv->projerror_regex_string,
2416 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2417 g_free(regkey);
2418 build_load_menu_grp(config, &(ft->priv->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2419 build_load_menu_grp(config, &(ft->priv->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2422 g_free(ftlist);
2424 break;
2425 default: /* defaults don't load from config, see build_init */
2426 break;
2430 /* load old [build_settings] values if there is no value defined by [build-menu] */
2432 switch (src)
2434 case GEANY_BCS_FT:
2435 ft = (GeanyFiletype*)p;
2436 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2437 if (value != NULL)
2439 if (ft->priv->filecmds == NULL)
2440 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2441 assign_cmd(ft->priv->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2443 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2444 if (value != NULL)
2446 if (ft->priv->filecmds == NULL)
2447 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2448 assign_cmd(ft->priv->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2450 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2451 if (value != NULL)
2453 if (ft->priv->execcmds == NULL)
2454 ft->priv->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2455 assign_cmd(ft->priv->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2457 if (ft->error_regex_string == NULL)
2458 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2459 break;
2460 case GEANY_BCS_PROJ:
2461 if (non_ft_pref == NULL)
2462 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2463 basedir = project_get_base_path();
2464 if (basedir == NULL)
2465 basedir = g_strdup("%d");
2466 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2467 if (bvalue)
2468 makebasedir = g_strdup(basedir);
2469 else
2470 makebasedir = g_strdup("%d");
2471 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2472 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
2473 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2474 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2475 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2476 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
2477 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2478 if (!EMPTY(value))
2480 if (exec_proj == NULL)
2481 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2482 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2484 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2485 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2486 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
2487 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
2488 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2491 g_free(makebasedir);
2492 g_free(basedir);
2493 break;
2494 case GEANY_BCS_PREF:
2495 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2496 if (value != NULL)
2498 if (non_ft_pref == NULL)
2499 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2500 assign_cmd(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
2501 g_strdup_printf("%s ", value));
2502 assign_cmd(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2503 g_strdup_printf("%s %%e.o",value));
2504 assign_cmd(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2506 break;
2507 default:
2508 break;
2513 static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2515 guint cmd;
2516 gsize prefixlen; /* NOTE prefixlen used in macros above */
2517 gchar *key;
2518 guint count = 0;
2519 enum GeanyBuildCmdEntries i;
2521 if (src == NULL)
2522 return 0;
2523 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2524 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2525 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2527 if (src[cmd].exists) ++count;
2528 if (src[cmd].changed)
2530 static gchar cmdbuf[4] = " ";
2531 if (cmd >= 100)
2532 break; /* ensure no buffer overflow */
2533 sprintf(cmdbuf, "%02u", cmd);
2534 set_key_grp(key, groups[grp]);
2535 set_key_cmd(key, cmdbuf);
2536 if (src[cmd].exists)
2538 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2540 set_key_fld(key, config_keys[i]);
2541 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2544 else
2546 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2548 set_key_fld(key, config_keys[i]);
2549 g_key_file_remove_key(config, build_grp_name, key, NULL);
2554 g_free(key);
2555 return count;
2559 static gboolean save_project_filetype(GeanyFiletype *ft, GKeyFile *config)
2561 guint i = 0;
2562 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2564 i += build_save_menu_grp(config, ft->priv->projfilecmds, GEANY_GBG_FT, ft->name);
2565 i += build_save_menu_grp(config, ft->priv->projexeccmds, GEANY_GBG_EXEC, ft->name);
2566 if (!EMPTY(ft->priv->projerror_regex_string))
2568 g_key_file_set_string(config, build_grp_name, regkey, ft->priv->projerror_regex_string);
2569 i++;
2571 else
2572 g_key_file_remove_key(config, build_grp_name, regkey, NULL);
2573 g_free(regkey);
2574 return (i > 0);
2577 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2578 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2580 GeanyFiletype *ft;
2581 GeanyProject *pj;
2583 switch (src)
2585 case GEANY_BCS_HOME_FT:
2586 ft = (GeanyFiletype*)ptr;
2587 if (ft == NULL)
2588 return;
2589 build_save_menu_grp(config, ft->priv->homefilecmds, GEANY_GBG_FT, NULL);
2590 build_save_menu_grp(config, ft->priv->homeexeccmds, GEANY_GBG_EXEC, NULL);
2591 if (!EMPTY(ft->priv->homeerror_regex_string))
2592 g_key_file_set_string(config, build_grp_name, "error_regex", ft->priv->homeerror_regex_string);
2593 else
2594 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2595 break;
2596 case GEANY_BCS_PREF:
2597 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2598 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2599 if (!EMPTY(regex_pref))
2600 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2601 else
2602 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2603 break;
2604 case GEANY_BCS_PROJ:
2605 pj = (GeanyProject*)ptr;
2606 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2607 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2608 if (!EMPTY(regex_proj))
2609 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2610 else
2611 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2612 if (pj->priv->build_filetypes_list != NULL)
2614 GPtrArray *ft_names = g_ptr_array_new();
2615 const GPtrArray *build_fts = pj->priv->build_filetypes_list;
2617 for (guint i = 0; i < build_fts->len; i++)
2619 ft = build_fts->pdata[i];
2620 if (save_project_filetype(ft, config))
2621 g_ptr_array_add(ft_names, ft->name);
2623 if (ft_names->pdata != NULL)
2624 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2625 (const gchar**)ft_names->pdata, ft_names->len);
2626 else
2627 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2628 g_ptr_array_free(ft_names, TRUE);
2630 break;
2631 default: /* defaults and GEANY_BCS_FT can't save */
2632 break;
2637 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2638 void build_set_group_count(GeanyBuildGroup grp, gint count)
2640 guint i, sum;
2642 g_return_if_fail(count >= 0);
2644 if ((guint) count > build_groups_count[grp])
2645 build_groups_count[grp] = (guint) count;
2646 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2647 sum += build_groups_count[i];
2648 build_items_count = sum;
2652 /** Get the count of commands for the group
2654 * Get the number of commands in the group specified by @a grp.
2656 * @param grp the group of the specified menu item.
2658 * @return a count of the number of commands in the group
2661 GEANY_API_SYMBOL
2662 guint build_get_group_count(const GeanyBuildGroup grp)
2664 g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
2665 return build_groups_count[grp];
2669 static void on_project_close(void)
2671 /* remove project regexen */
2672 SETPTR(regex_proj, NULL);
2676 static struct
2678 const gchar *label;
2679 const gchar *command;
2680 const gchar *working_dir;
2681 GeanyBuildCommand **ptr;
2682 gint index;
2683 } default_cmds[] = {
2684 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2685 { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2686 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2687 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2688 { NULL, NULL, NULL, NULL, 0 }
2692 void build_init(void)
2694 GtkWidget *item;
2695 GtkWidget *toolmenu;
2696 gint cmdindex;
2698 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2700 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2701 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2702 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2703 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2705 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2707 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2708 cmd->exists = TRUE;
2709 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2710 cmd->command = g_strdup(default_cmds[cmdindex].command);
2711 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2714 /* create the toolbar Build item sub menu */
2715 toolmenu = gtk_menu_new();
2716 g_object_ref(toolmenu);
2718 /* build the code */
2719 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2720 gtk_widget_show(item);
2721 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2722 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2723 GBO_TO_POINTER(GEANY_GBO_BUILD));
2724 widgets.toolitem_build = item;
2726 item = gtk_separator_menu_item_new();
2727 gtk_widget_show(item);
2728 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2730 /* build the code with make all */
2731 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2732 gtk_widget_show(item);
2733 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2734 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2735 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2736 widgets.toolitem_make_all = item;
2738 /* build the code with make custom */
2739 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2740 gtk_widget_show(item);
2741 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2742 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2743 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2744 widgets.toolitem_make_custom = item;
2746 /* build the code with make object */
2747 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2748 gtk_widget_show(item);
2749 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2750 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2751 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2752 widgets.toolitem_make_object = item;
2754 item = gtk_separator_menu_item_new();
2755 gtk_widget_show(item);
2756 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2758 /* arguments */
2759 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
2760 gtk_widget_show(item);
2761 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2762 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2763 widgets.toolitem_set_args = item;
2765 /* get toolbar action pointers */
2766 widgets.build_action = toolbar_get_action_by_name("Build");
2767 widgets.compile_action = toolbar_get_action_by_name("Compile");
2768 widgets.run_action = toolbar_get_action_by_name("Run");
2769 widgets.toolmenu = toolmenu;
2770 /* set the submenu to the toolbar item */
2771 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);
2775 gboolean build_keybinding(guint key_id)
2777 GtkWidget *item;
2778 BuildMenuItems *menu_items;
2779 GeanyDocument *doc = document_get_current();
2781 if (doc == NULL)
2782 return TRUE;
2784 if (!gtk_widget_is_sensitive(ui_lookup_widget(main_widgets.window, "menu_build1")))
2785 return TRUE;
2787 menu_items = build_get_menu_items(doc->file_type->id);
2788 /* TODO make it a table??*/
2789 switch (key_id)
2791 case GEANY_KEYS_BUILD_COMPILE:
2792 item = menu_items->menu_item[GEANY_GBG_FT][GBO_TO_CMD(GEANY_GBO_COMPILE)];
2793 break;
2794 case GEANY_KEYS_BUILD_LINK:
2795 item = menu_items->menu_item[GEANY_GBG_FT][GBO_TO_CMD(GEANY_GBO_BUILD)];
2796 break;
2797 case GEANY_KEYS_BUILD_MAKE:
2798 item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_MAKE_ALL)];
2799 break;
2800 case GEANY_KEYS_BUILD_MAKEOWNTARGET:
2801 item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_CUSTOM)];
2802 break;
2803 case GEANY_KEYS_BUILD_MAKEOBJECT:
2804 item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)];
2805 break;
2806 case GEANY_KEYS_BUILD_NEXTERROR:
2807 item = menu_items->menu_item[GBG_FIXED][GBF_NEXT_ERROR];
2808 break;
2809 case GEANY_KEYS_BUILD_PREVIOUSERROR:
2810 item = menu_items->menu_item[GBG_FIXED][GBF_PREV_ERROR];
2811 break;
2812 case GEANY_KEYS_BUILD_RUN:
2813 item = menu_items->menu_item[GEANY_GBG_EXEC][GBO_TO_CMD(GEANY_GBO_EXEC)];
2814 break;
2815 case GEANY_KEYS_BUILD_OPTIONS:
2816 item = menu_items->menu_item[GBG_FIXED][GBF_COMMANDS];
2817 break;
2818 default:
2819 item = NULL;
2821 /* Note: For Build menu items it's OK (at the moment) to assume they are in the correct
2822 * sensitive state, but some other menus don't update the sensitive status until
2823 * they are redrawn. */
2824 if (item && gtk_widget_is_sensitive(item))
2825 gtk_menu_item_activate(GTK_MENU_ITEM(item));
2826 return TRUE;