Make code more readable by renaming poorly named macros NZV and NVL
[geany-mirror.git] / src / build.c
blob27f1e407c7e94f8c34ac2b203ae3de0528c41c48
1 /*
2 * build.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6 * Copyright 2009 Lex Trotman <elextr(at)gmail(dot)com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * Build commands and menu items.
26 /* TODO: tidy code:
27 * Use intermediate pointers for common subexpressions.
28 * Replace defines with enums.
29 * Other TODOs in code. */
31 #include "geany.h"
32 #include "build.h"
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <glib/gstdio.h>
41 #ifdef G_OS_UNIX
42 # include <sys/types.h>
43 # include <sys/wait.h>
44 # include <signal.h>
45 #else
46 # include <windows.h>
47 #endif
49 #include "prefs.h"
50 #include "support.h"
51 #include "document.h"
52 #include "utils.h"
53 #include "ui_utils.h"
54 #include "dialogs.h"
55 #include "msgwindow.h"
56 #include "filetypes.h"
57 #include "keybindings.h"
58 #include "vte.h"
59 #include "project.h"
60 #include "editor.h"
61 #include "win32.h"
62 #include "toolbar.h"
63 #include "geanymenubuttonaction.h"
64 #include "gtkcompat.h"
66 /* g_spawn_async_with_pipes doesn't work on Windows */
67 #ifdef G_OS_WIN32
68 #define SYNC_SPAWN
69 #endif
71 /* Number of editor indicators to draw - limited as this can affect performance */
72 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
75 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
77 static gchar *current_dir_entered = NULL;
79 typedef struct RunInfo
81 GPid pid;
82 gint file_type_id;
83 } RunInfo;
85 static RunInfo *run_info;
87 #ifdef G_OS_WIN32
88 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script.bat";
89 #else
90 static const gchar RUN_SCRIPT_CMD[] = "./geany_run_script.sh";
91 #endif
93 /* pack group (<8) and command (<32) into a user_data pointer */
94 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
95 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
96 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
97 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
99 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
101 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
103 static struct
105 GtkAction *run_action;
106 GtkAction *compile_action;
107 GtkAction *build_action;
108 GtkWidget *toolmenu;
110 GtkWidget *toolitem_build;
111 GtkWidget *toolitem_make_all;
112 GtkWidget *toolitem_make_custom;
113 GtkWidget *toolitem_make_object;
114 GtkWidget *toolitem_set_args;
116 widgets;
118 static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
119 static guint build_items_count = 9;
121 #ifndef SYNC_SPAWN
122 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data);
123 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data);
124 #endif
125 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose, GError **error);
126 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
127 static void set_stop_button(gboolean stop);
128 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
129 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
130 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
131 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
132 static void kill_process(GPid *pid);
133 static void show_build_result_message(gboolean failure);
134 static void process_build_output_line(const gchar *line, gint color);
135 static void show_build_commands_dialog(void);
136 static void on_build_menu_item(GtkWidget *w, gpointer user_data);
138 void build_finalize(void)
140 g_free(build_info.dir);
141 g_free(build_info.custom_target);
143 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
144 gtk_widget_destroy(menu_items.menu);
148 /* note: copied from keybindings.c, may be able to go away */
149 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
150 GtkAccelGroup *accel_group, GtkWidget *menuitem)
152 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
154 if (kb->key != 0)
155 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
156 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
160 /* convenience routines to access parts of GeanyBuildCommand */
161 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
163 switch (id)
165 case GEANY_BC_LABEL:
166 return bc->label;
167 case GEANY_BC_COMMAND:
168 return bc->command;
169 case GEANY_BC_WORKING_DIR:
170 return bc->working_dir;
172 g_assert(0);
173 return NULL;
177 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
179 switch (id)
181 case GEANY_BC_LABEL:
182 SETPTR(bc->label, str);
183 break;
184 case GEANY_BC_COMMAND:
185 SETPTR(bc->command, str);
186 break;
187 case GEANY_BC_WORKING_DIR:
188 SETPTR(bc->working_dir, str);
189 break;
190 default:
191 g_assert(0);
196 static const gchar *config_keys[GEANY_BC_CMDENTRIES_COUNT] = {
197 "LB", /* label */
198 "CM", /* command */
199 "WD" /* working directory */
202 /*-----------------------------------------------------
204 * Execute commands and handle results
206 *-----------------------------------------------------*/
208 /* the various groups of commands not in the filetype struct */
209 static GeanyBuildCommand *ft_def = NULL;
210 static GeanyBuildCommand *non_ft_proj = NULL;
211 static GeanyBuildCommand *non_ft_pref = NULL;
212 static GeanyBuildCommand *non_ft_def = NULL;
213 static GeanyBuildCommand *exec_proj = NULL;
214 static GeanyBuildCommand *exec_pref = NULL;
215 static GeanyBuildCommand *exec_def = NULL;
216 /* and the regexen not in the filetype structure */
217 static gchar *regex_pref = NULL;
218 /* project non-fileregex string */
219 static gchar *regex_proj = NULL;
221 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
222 #ifndef PRINTBUILDCMDS
223 #define PRINTBUILDCMDS FALSE
224 #endif
225 static gboolean printbuildcmds = PRINTBUILDCMDS;
228 /* for debug only, print the commands structures in priority order */
229 static void printfcmds(void)
231 #if 0
232 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
233 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
234 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
235 { &ft_def, NULL, NULL, NULL, NULL, NULL },
236 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
237 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
239 GeanyFiletype *ft = NULL;
240 GeanyDocument *doc;
241 gint i, j, k, l, m;
242 enum GeanyBuildCmdEntries n;
243 gint cc[GEANY_BCS_COUNT];
244 gchar c;
246 doc = document_get_current();
247 if (doc != NULL)
248 ft = doc->file_type;
249 if (ft != NULL)
251 printf("filetype %s\n",ft->name);
252 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->filecmds);
253 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->homefilecmds);
254 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->projfilecmds);
255 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->ftdefcmds);
256 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->execcmds);
257 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->homeexeccmds);
258 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->projexeccmds);
260 for (i = 0; i < GEANY_BCS_COUNT; ++i)
262 m = 1;
263 for (j = 0; j < GEANY_GBG_COUNT; ++j)
265 for (k = 0; k < build_groups_count[j]; ++k)
266 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
268 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
270 if ((*(cl[j][i]))[k].entries[n] != NULL &&
271 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
273 m = l;
278 cc[i] = m;
280 for (i = 0; i < GEANY_GBG_COUNT; ++i)
282 for (k = 0; k < build_groups_count[i]; ++k)
284 for (l = 0; l < 2; ++l)
286 c = ' ';
287 for (j = 0; j < GEANY_BCS_COUNT; ++j)
289 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
291 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
293 if ((*(cl[i][j]))[k].entries[i] != NULL)
294 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
295 else
296 printf("%c %*.*s",c,cc[j],cc[j]," ");
299 else
300 printf("%c %*.*s",c,cc[j],cc[j]," ");
301 c = ',';
303 printf("\n");
306 printf("\n");
308 #endif
312 /* macros to save typing and make the logic visible */
313 #define return_cmd_if(src, cmds)\
314 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
316 *fr=src; \
317 if (printbuildcmds) \
318 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
319 return &(cmds[cmdindex]); \
322 #define return_ft_cmd_if(src, cmds)\
323 if (ft != NULL && ft->cmds != NULL \
324 && ft->cmds[cmdindex].exists && below>src)\
326 *fr=src; \
327 if (printbuildcmds) \
328 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
329 return &(ft->cmds[cmdindex]); \
333 /* get the next lowest command taking priority into account */
334 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
335 guint below, guint *from)
337 /* Note: parameter below used in macros above */
338 GeanyFiletype *ft = NULL;
339 guint sink, *fr = &sink;
341 if (printbuildcmds)
342 printfcmds();
343 if (cmdgrp >= GEANY_GBG_COUNT)
344 return NULL;
345 if (from != NULL)
346 fr = from;
347 if (doc == NULL)
348 doc = document_get_current();
349 if (doc != NULL)
350 ft = doc->file_type;
352 switch (cmdgrp)
354 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
355 if (ft != NULL)
357 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
358 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
359 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
361 return_cmd_if(GEANY_BCS_DEF, ft_def);
362 break;
363 case GEANY_GBG_NON_FT: /* order proj, pref, def */
364 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
365 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
366 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
367 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
368 break;
369 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
370 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
371 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
372 return_cmd_if(GEANY_BCS_PREF, exec_pref);
373 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
374 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
375 return_cmd_if(GEANY_BCS_DEF, exec_def);
376 break;
377 default:
378 break;
380 return NULL;
384 /* shortcut to start looking at the top */
385 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
387 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
391 #define return_nonblank_regex(src, ptr)\
392 if (!EMPTY(ptr)) \
393 { *fr = (src); return &(ptr); }
396 /* like get_build_cmd, but for regexen, used by filetypes */
397 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
399 guint sink, *fr = &sink;
401 if (from != NULL)
402 fr = from;
403 if (grp == GEANY_GBG_FT)
405 if (ft == NULL)
407 GeanyDocument *doc = document_get_current();
408 if (doc != NULL)
409 ft = doc->file_type;
411 if (ft == NULL)
412 return NULL;
413 return_nonblank_regex(GEANY_BCS_PROJ, ft->projerror_regex_string);
414 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->homeerror_regex_string);
415 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
417 else if (grp == GEANY_GBG_NON_FT)
419 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
420 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
422 return NULL;
426 static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
428 GeanyDocument *doc;
429 GeanyFiletype *ft = NULL;
431 switch (grp)
433 case GEANY_GBG_FT:
434 if ((doc = document_get_current()) == NULL)
435 return NULL;
436 if ((ft = doc->file_type) == NULL)
437 return NULL;
438 switch (src)
440 case GEANY_BCS_DEF: return &(ft->ftdefcmds);
441 case GEANY_BCS_FT: return &(ft->filecmds);
442 case GEANY_BCS_HOME_FT: return &(ft->homefilecmds);
443 case GEANY_BCS_PREF: return &(ft->homefilecmds);
444 case GEANY_BCS_PROJ: return &(ft->projfilecmds);
445 default: return NULL;
447 break;
448 case GEANY_GBG_NON_FT:
449 switch (src)
451 case GEANY_BCS_DEF: return &(non_ft_def);
452 case GEANY_BCS_PREF: return &(non_ft_pref);
453 case GEANY_BCS_PROJ: return &(non_ft_proj);
454 default: return NULL;
456 break;
457 case GEANY_GBG_EXEC:
458 if ((doc = document_get_current()) != NULL)
459 ft = doc->file_type;
460 switch (src)
462 case GEANY_BCS_DEF: return &(exec_def);
463 case GEANY_BCS_FT: return ft ? &(ft->execcmds): NULL;
464 case GEANY_BCS_HOME_FT: return ft ? &(ft->homeexeccmds): NULL;
465 case GEANY_BCS_PROJ_FT: return ft ? &(ft->projexeccmds): NULL;
466 case GEANY_BCS_PREF: return &(exec_pref);
467 case GEANY_BCS_PROJ: return &(exec_proj);
468 default: return NULL;
470 break;
471 default:
472 return NULL;
477 /* get pointer to the command group array */
478 static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
480 GeanyBuildCommand **g = get_build_group_pointer(src, grp);
481 if (g == NULL) return NULL;
482 return *g;
486 /** Remove the specified Build menu item.
488 * Makes the specified menu item configuration no longer exist. This
489 * is different to setting fields to blank because the menu item
490 * will be deleted from the configuration file on saving
491 * (except the system filetypes settings @see Build Menu Configuration
492 * section of the Manual).
494 * @param src the source of the menu item to remove.
495 * @param grp the group of the command to remove.
496 * @param cmd the index (from 0) of the command within the group. A negative
497 * value will remove the whole group.
499 * If any parameter is out of range does nothing.
501 * Updates the menu.
504 void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
506 GeanyBuildCommand *bc;
507 guint i;
509 bc = get_build_group(src, grp);
510 if (bc == NULL)
511 return;
512 if (cmd < 0)
514 for (i = 0; i < build_groups_count[grp]; ++i)
515 bc[i].exists = FALSE;
517 else if ((guint) cmd < build_groups_count[grp])
518 bc[cmd].exists = FALSE;
522 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
524 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
525 * hidden by higher priority commands.
527 * @param src the source of the specified menu item.
528 * @param grp the group of the specified menu item.
529 * @param cmd the index of the command within the group.
531 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
532 * This is a pointer to an internal structure and must not be freed.
534 * @see build_menu_update
536 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
538 GeanyBuildCommand *bc;
540 g_return_val_if_fail(src < GEANY_BCS_COUNT, NULL);
541 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
542 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
544 bc = get_build_group(src, grp);
545 if (bc == NULL)
546 return NULL;
547 return &(bc[cmd]);
551 /** Get the string for the menu item field.
553 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
554 * that the menu item will use if activated.
556 * @param grp the group of the specified menu item.
557 * @param cmd the index of the command within the group.
558 * @param fld the field to return
560 * @return a pointer to the constant string or @a NULL if it doesn't exist.
561 * This is a pointer to an internal structure and must not be freed.
564 const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
565 const GeanyBuildCmdEntries fld)
567 GeanyBuildCommand *c;
568 gchar *str = NULL;
570 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
571 g_return_val_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT, NULL);
572 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
574 c = get_build_cmd(NULL, grp, cmd, NULL);
575 if (c == NULL) return NULL;
576 switch (fld)
578 case GEANY_BC_COMMAND:
579 str = c->command;
580 break;
581 case GEANY_BC_LABEL:
582 str = c->label;
583 break;
584 case GEANY_BC_WORKING_DIR:
585 str = c->working_dir;
586 break;
587 default:
588 break;
590 return str;
593 /** Set the string for the menu item field.
595 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
597 * @param src the source of the menu item
598 * @param grp the group of the specified menu item.
599 * @param cmd the index of the menu item within the group.
600 * @param fld the field in the menu item command to set
601 * @param val the value to set the field to, is copied
605 void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp,
606 const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
608 GeanyBuildCommand **g;
610 g_return_if_fail(src < GEANY_BCS_COUNT);
611 g_return_if_fail(grp < GEANY_GBG_COUNT);
612 g_return_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT);
613 g_return_if_fail(cmd < build_groups_count[grp]);
615 g = get_build_group_pointer(src, grp);
616 if (g == NULL) return;
617 if (*g == NULL )
619 *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
621 switch (fld)
623 case GEANY_BC_COMMAND:
624 SETPTR((*g)[cmd].command, g_strdup(val));
625 (*g)[cmd].exists = TRUE;
626 break;
627 case GEANY_BC_LABEL:
628 SETPTR((*g)[cmd].label, g_strdup(val));
629 (*g)[cmd].exists = TRUE;
630 break;
631 case GEANY_BC_WORKING_DIR:
632 SETPTR((*g)[cmd].working_dir, g_strdup(val));
633 (*g)[cmd].exists = TRUE;
634 break;
635 default:
636 break;
638 build_menu_update(NULL);
641 /** Activate the menu item.
643 * Activate the menu item specified by @a grp and @a cmd.
645 * @param grp the group of the specified menu item.
646 * @param cmd the index of the command within the group.
650 void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
652 on_build_menu_item(NULL, GRP_CMD_TO_POINTER(grp, cmd));
656 /* Clear all error indicators in all documents. */
657 static void clear_all_errors(void)
659 guint i;
661 foreach_document(i)
663 editor_indicator_clear_errors(documents[i]->editor);
668 #ifdef SYNC_SPAWN
669 static void parse_build_output(const gchar **output, gint status)
671 guint x, i, len;
672 gchar *line, **lines;
674 for (x = 0; x < 2; x++)
676 if (!EMPTY(output[x]))
678 lines = g_strsplit_set(output[x], "\r\n", -1);
679 len = g_strv_length(lines);
681 for (i = 0; i < len; i++)
683 if (!EMPTY(lines[i]))
685 line = lines[i];
686 while (*line != '\0')
687 { /* replace any control characters in the output */
688 if (*line < 32)
689 *line = 32;
690 line++;
692 process_build_output_line(lines[i], COLOR_BLACK);
695 g_strfreev(lines);
699 show_build_result_message(status != 0);
700 utils_beep();
702 build_info.pid = 0;
703 /* enable build items again */
704 build_menu_update(NULL);
706 #endif
709 /* Replaces occurences of %e and %p with the appropriate filenames,
710 * %d and %p replacements should be in UTF8 */
711 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
713 GString *stack;
714 gchar *filename = NULL;
715 gchar *replacement;
716 gchar *executable = NULL;
717 gchar *ret_str; /* to be freed when not in use anymore */
719 stack = g_string_new(src);
720 if (doc != NULL && doc->file_name != NULL)
722 filename = utils_get_utf8_from_locale(doc->file_name);
724 /* replace %f with the filename (including extension) */
725 replacement = g_path_get_basename(filename);
726 utils_string_replace_all(stack, "%f", replacement);
727 g_free(replacement);
729 /* replace %d with the absolute path of the dir of the current file */
730 replacement = g_path_get_dirname(filename);
731 utils_string_replace_all(stack, "%d", replacement);
732 g_free(replacement);
734 /* replace %e with the filename (excluding extension) */
735 executable = utils_remove_ext_from_filename(filename);
736 replacement = g_path_get_basename(executable);
737 utils_string_replace_all(stack, "%e", replacement);
738 g_free(replacement);
741 /* replace %p with the current project's (absolute) base directory */
742 replacement = NULL; /* prevent double free if no replacement found */
743 if (app->project)
745 replacement = project_get_base_path();
747 else if (strstr(stack->str, "%p"))
748 { /* fall back to %d */
749 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
750 if (doc != NULL && filename != NULL)
751 replacement = g_path_get_dirname(filename);
754 utils_string_replace_all(stack, "%p", replacement);
755 g_free(replacement);
757 ret_str = utils_get_utf8_from_locale(stack->str);
758 g_free(executable);
759 g_free(filename);
760 g_string_free(stack, TRUE);
762 return ret_str; /* don't forget to free src also if needed */
766 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
767 * idx document directory */
768 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
770 GError *error = NULL;
771 gchar **argv;
772 gchar *working_dir;
773 gchar *utf8_working_dir;
774 gchar *cmd_string;
775 gchar *utf8_cmd_string;
776 #ifdef SYNC_SPAWN
777 gchar *output[2];
778 gint status;
779 #else
780 gint stdout_fd;
781 gint stderr_fd;
782 #endif
784 if (!((doc != NULL && !EMPTY(doc->file_name)) || !EMPTY(dir)))
786 geany_debug("Failed to run command with no working directory");
787 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
788 return (GPid) 1;
791 clear_all_errors();
792 SETPTR(current_dir_entered, NULL);
794 cmd_string = g_strdup(cmd);
796 #ifdef G_OS_WIN32
797 argv = g_strsplit(cmd_string, " ", 0);
798 #else
799 argv = g_new0(gchar *, 4);
800 argv[0] = g_strdup("/bin/sh");
801 argv[1] = g_strdup("-c");
802 argv[2] = cmd_string;
803 argv[3] = NULL;
804 #endif
806 utf8_cmd_string = utils_get_utf8_from_locale(cmd_string);
807 utf8_working_dir = !EMPTY(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
808 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
810 gtk_list_store_clear(msgwindow.store_compiler);
811 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
812 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
813 g_free(utf8_working_dir);
814 g_free(utf8_cmd_string);
816 /* set the build info for the message window */
817 g_free(build_info.dir);
818 build_info.dir = g_strdup(working_dir);
819 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
820 build_info.message_count = 0;
822 #ifdef SYNC_SPAWN
823 if (! utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
824 NULL, NULL, &output[0], &output[1], &status, &error))
825 #else
826 if (! g_spawn_async_with_pipes(working_dir, argv, NULL,
827 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
828 &(build_info.pid), NULL, &stdout_fd, &stderr_fd, &error))
829 #endif
831 geany_debug("build command spawning failed: %s", error->message);
832 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
833 g_strfreev(argv);
834 g_error_free(error);
835 g_free(working_dir);
836 error = NULL;
837 return (GPid) 0;
840 #ifdef SYNC_SPAWN
841 parse_build_output((const gchar**) output, status);
842 g_free(output[0]);
843 g_free(output[1]);
844 #else
845 if (build_info.pid > 0)
847 g_child_watch_add(build_info.pid, (GChildWatchFunc) build_exit_cb, NULL);
848 build_menu_update(doc);
849 ui_progress_bar_start(NULL);
852 /* use GIOChannels to monitor stdout and stderr */
853 utils_set_up_io_channel(stdout_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
854 TRUE, build_iofunc, GINT_TO_POINTER(0));
855 utils_set_up_io_channel(stderr_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
856 TRUE, build_iofunc, GINT_TO_POINTER(1));
857 #endif
859 g_strfreev(argv);
860 g_free(working_dir);
862 return build_info.pid;
866 /* Returns: NULL if there was an error, or the working directory the script was created in.
867 * vte_cmd_nonscript is the location of a string which is filled with the command to be used
868 * when vc->skip_run_script is set, otherwise it will be set to NULL */
869 static gchar *prepare_run_script(GeanyDocument *doc, gchar **vte_cmd_nonscript, guint cmdindex)
871 gchar *locale_filename = NULL;
872 GeanyBuildCommand *cmd = NULL;
873 gchar *executable = NULL;
874 gchar *working_dir = NULL;
875 const gchar *cmd_working_dir;
876 gboolean autoclose = FALSE;
877 gboolean result = FALSE;
878 gchar *tmp;
879 gchar *cmd_string;
880 GError *error = NULL;
882 if (vte_cmd_nonscript != NULL)
883 *vte_cmd_nonscript = NULL;
885 locale_filename = utils_get_locale_from_utf8(doc->file_name);
887 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
889 cmd_string = build_replace_placeholder(doc, cmd->command);
890 cmd_working_dir = cmd->working_dir;
891 if (EMPTY(cmd_working_dir))
892 cmd_working_dir = "%d";
893 working_dir = build_replace_placeholder(doc, cmd_working_dir); /* in utf-8 */
895 /* only test whether working dir exists, don't change it or else Windows support will break
896 * (gspawn-win32-helper.exe is used by GLib and must be in $PATH which means current working
897 * dir where geany.exe was started from, so we can't change it) */
898 if (EMPTY(working_dir) || ! g_file_test(working_dir, G_FILE_TEST_EXISTS) ||
899 ! g_file_test(working_dir, G_FILE_TEST_IS_DIR))
901 ui_set_statusbar(TRUE, _("Failed to change the working directory to \"%s\""),
902 !EMPTY(working_dir) ? working_dir : "<NULL>" );
903 utils_free_pointers(2, cmd_string, working_dir, NULL);
904 return NULL;
907 #ifdef HAVE_VTE
908 if (vte_info.have_vte && vc->run_in_vte)
910 if (vc->skip_run_script)
912 if (vte_cmd_nonscript != NULL)
913 *vte_cmd_nonscript = cmd_string;
915 utils_free_pointers(2, executable, locale_filename, NULL);
916 return working_dir;
918 else
919 /* don't wait for user input at the end of script when we are running in VTE */
920 autoclose = TRUE;
922 #endif
924 /* RUN_SCRIPT_CMD should be ok in UTF8 without converting in locale because it
925 * contains no umlauts */
926 tmp = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
927 result = build_create_shellscript(tmp, cmd_string, autoclose, &error);
928 if (! result)
930 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
931 !EMPTY(cmd_string) ? cmd_string : NULL, error->message);
932 g_error_free(error);
935 utils_free_pointers(4, cmd_string, tmp, executable, locale_filename, NULL);
937 if (result)
938 return working_dir;
940 g_free(working_dir);
941 return NULL;
945 static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
947 gchar *working_dir;
948 gchar *vte_cmd_nonscript = NULL;
949 GError *error = NULL;
951 if (doc == NULL || doc->file_name == NULL)
952 return (GPid) 0;
954 working_dir = prepare_run_script(doc, &vte_cmd_nonscript, cmdindex);
955 if (working_dir == NULL)
956 return (GPid) 0;
958 run_info[cmdindex].file_type_id = doc->file_type->id;
960 #ifdef HAVE_VTE
961 if (vte_info.have_vte && vc->run_in_vte)
963 gchar *vte_cmd;
965 if (vc->skip_run_script)
967 SETPTR(vte_cmd_nonscript, utils_get_utf8_from_locale(vte_cmd_nonscript));
968 vte_cmd = g_strconcat(vte_cmd_nonscript, "\n", NULL);
969 g_free(vte_cmd_nonscript);
971 else
972 vte_cmd = g_strconcat("\n/bin/sh ", RUN_SCRIPT_CMD, "\n", NULL);
974 /* change into current directory if it is not done by default */
975 if (! vc->follow_path)
977 /* we need to convert the working_dir back to UTF-8 because the VTE expects it */
978 gchar *utf8_working_dir = utils_get_utf8_from_locale(working_dir);
979 vte_cwd(utf8_working_dir, TRUE);
980 g_free(utf8_working_dir);
982 if (! vte_send_cmd(vte_cmd))
984 ui_set_statusbar(FALSE,
985 _("Could not execute the file in the VTE because it probably contains a command."));
986 geany_debug("Could not execute the file in the VTE because it probably contains a command.");
989 /* show the VTE */
990 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
991 gtk_widget_grab_focus(vc->vte);
992 msgwin_show_hide(TRUE);
994 run_info[cmdindex].pid = 1;
996 g_free(vte_cmd);
998 else
999 #endif
1001 gchar *locale_term_cmd = NULL;
1002 gint argv_len, i;
1003 gchar **argv = NULL;
1005 /* get the terminal path */
1006 locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
1007 /* split the term_cmd, so arguments will work too */
1008 if (!g_shell_parse_argv(locale_term_cmd, &argv_len, &argv, NULL))
1010 ui_set_statusbar(TRUE,
1011 _("Could not parse terminal command \"%s\" "
1012 "(check Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
1013 run_info[cmdindex].pid = (GPid) 1;
1014 goto free_strings;
1017 /* check that terminal exists (to prevent misleading error messages) */
1018 if (argv[0] != NULL)
1020 gchar *tmp = argv[0];
1021 /* g_find_program_in_path checks whether tmp exists and is executable */
1022 argv[0] = g_find_program_in_path(tmp);
1023 g_free(tmp);
1025 if (argv[0] == NULL)
1027 ui_set_statusbar(TRUE,
1028 _("Could not find terminal \"%s\" "
1029 "(check path for Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
1030 run_info[cmdindex].pid = (GPid) 1;
1031 goto free_strings;
1034 for (i = 0; i < argv_len; i++)
1036 utils_str_replace_all(&(argv[i]), "%c", RUN_SCRIPT_CMD);
1039 if (! g_spawn_async(working_dir, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1040 NULL, NULL, &(run_info[cmdindex].pid), &error))
1042 geany_debug("g_spawn_async() failed: %s", error->message);
1043 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
1044 g_unlink(RUN_SCRIPT_CMD);
1045 g_error_free(error);
1046 error = NULL;
1047 run_info[cmdindex].pid = (GPid) 0;
1050 if (run_info[cmdindex].pid != 0)
1052 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
1053 (gpointer)&(run_info[cmdindex]));
1054 build_menu_update(doc);
1056 free_strings:
1057 g_strfreev(argv);
1058 g_free(locale_term_cmd);
1061 g_free(working_dir);
1062 return run_info[cmdindex].pid;
1066 static void process_build_output_line(const gchar *str, gint color)
1068 gchar *msg, *tmp;
1069 gchar *filename;
1070 gint line;
1072 msg = g_strdup(str);
1074 g_strchomp(msg);
1076 if (EMPTY(msg))
1078 g_free(msg);
1079 return;
1082 if (build_parse_make_dir(msg, &tmp))
1084 SETPTR(current_dir_entered, tmp);
1086 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
1088 if (line != -1 && filename != NULL)
1090 GeanyDocument *doc = document_find_by_filename(filename);
1092 /* limit number of indicators */
1093 if (doc && editor_prefs.use_indicators &&
1094 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
1096 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
1097 line--; /* so only adjust the line number if it is greater than 0 */
1098 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
1100 build_info.message_count++;
1101 color = COLOR_RED; /* error message parsed on the line */
1103 g_free(filename);
1105 msgwin_compiler_add_string(color, msg);
1106 g_free(msg);
1110 #ifndef SYNC_SPAWN
1111 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data)
1113 if (cond & (G_IO_IN | G_IO_PRI))
1115 gchar *msg;
1116 GIOStatus st;
1118 while ((st = g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL)) == G_IO_STATUS_NORMAL && msg)
1120 gint color = (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK;
1122 process_build_output_line(msg, color);
1123 g_free(msg);
1125 if (st == G_IO_STATUS_ERROR || st == G_IO_STATUS_EOF) return FALSE;
1127 if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
1128 return FALSE;
1130 return TRUE;
1132 #endif
1135 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1137 const gchar *pos;
1139 *prefix = NULL;
1141 if (string == NULL)
1142 return FALSE;
1144 if ((pos = strstr(string, "Entering directory")) != NULL)
1146 gsize len;
1147 gchar *input;
1149 /* get the start of the path */
1150 pos = strstr(string, "/");
1152 if (pos == NULL)
1153 return FALSE;
1155 input = g_strdup(pos);
1157 /* kill the ' at the end of the path */
1158 len = strlen(input);
1159 input[len - 1] = '\0';
1160 input = g_realloc(input, len); /* shorten by 1 */
1161 *prefix = input;
1163 return TRUE;
1166 if (strstr(string, "Leaving directory") != NULL)
1168 *prefix = NULL;
1169 return TRUE;
1172 return FALSE;
1176 static void show_build_result_message(gboolean failure)
1178 gchar *msg;
1180 if (failure)
1182 msg = _("Compilation failed.");
1183 msgwin_compiler_add_string(COLOR_BLUE, msg);
1184 /* If msgwindow is hidden, user will want to display it to see the error */
1185 if (! ui_prefs.msgwindow_visible)
1187 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1188 msgwin_show_hide(TRUE);
1190 else
1191 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1192 ui_set_statusbar(FALSE, "%s", msg);
1194 else
1196 msg = _("Compilation finished successfully.");
1197 msgwin_compiler_add_string(COLOR_BLUE, msg);
1198 if (! ui_prefs.msgwindow_visible ||
1199 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1200 ui_set_statusbar(FALSE, "%s", msg);
1205 #ifndef SYNC_SPAWN
1206 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1208 gboolean failure = FALSE;
1210 #ifdef G_OS_WIN32
1211 failure = status;
1212 #else
1213 if (WIFEXITED(status))
1215 if (WEXITSTATUS(status) != EXIT_SUCCESS)
1216 failure = TRUE;
1218 else if (WIFSIGNALED(status))
1220 /* the terminating signal: WTERMSIG (status)); */
1221 failure = TRUE;
1223 else
1224 { /* any other failure occured */
1225 failure = TRUE;
1227 #endif
1228 show_build_result_message(failure);
1230 utils_beep();
1231 g_spawn_close_pid(child_pid);
1233 build_info.pid = 0;
1234 /* enable build items again */
1235 build_menu_update(NULL);
1236 ui_progress_bar_stop();
1238 #endif
1241 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1243 RunInfo *run_info_data = user_data;
1245 g_spawn_close_pid(child_pid);
1247 run_info_data->pid = 0;
1248 /* reset the stop button and menu item to the original meaning */
1249 build_menu_update(NULL);
1253 static void set_file_error_from_errno(GError **error, gint err, const gchar *prefix)
1255 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(err), "%s%s%s",
1256 prefix ? prefix : "", prefix ? ": " : "", g_strerror(err));
1260 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1261 * fname is the full file name (including path) for the script to create */
1262 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose, GError **error)
1264 FILE *fp;
1265 gchar *str;
1266 gboolean success = TRUE;
1267 #ifdef G_OS_WIN32
1268 gchar *expanded_cmd;
1269 #endif
1271 fp = g_fopen(fname, "w");
1272 if (! fp)
1274 set_file_error_from_errno(error, errno, "Failed to create file");
1275 return FALSE;
1277 #ifdef G_OS_WIN32
1278 /* Expand environment variables like %blah%. */
1279 expanded_cmd = win32_expand_environment_variables(cmd);
1280 str = g_strdup_printf("%s\n\n%s\ndel \"%%0\"\n\npause\n", expanded_cmd, (autoclose) ? "" : "pause");
1281 g_free(expanded_cmd);
1282 #else
1283 str = g_strdup_printf(
1284 "#!/bin/sh\n\nrm $0\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1285 \n\n%s\n", cmd, (autoclose) ? "" :
1286 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1287 "dash\ndummy_var=\"\"\nread dummy_var");
1288 #endif
1290 if (fputs(str, fp) < 0)
1292 set_file_error_from_errno(error, errno, "Failed to write file");
1293 success = FALSE;
1295 g_free(str);
1297 if (fclose(fp) != 0)
1299 if (error && ! *error) /* don't set error twice */
1300 set_file_error_from_errno(error, errno, "Failed to close file");
1301 success = FALSE;
1304 return success;
1308 typedef void Callback(GtkWidget *w, gpointer u);
1310 /* run the command catenating cmd_cat if present */
1311 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
1313 gchar *dir;
1314 gchar *full_command, *subs_command;
1315 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1316 gchar *cmdstr;
1318 if (buildcmd == NULL)
1319 return;
1321 cmdstr = buildcmd->command;
1323 if (cmd_cat != NULL)
1325 if (cmdstr != NULL)
1326 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1327 else
1328 full_command = g_strdup(cmd_cat);
1330 else
1331 full_command = cmdstr;
1333 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1334 subs_command = build_replace_placeholder(doc, full_command);
1335 build_info.grp = grp;
1336 build_info.cmd = cmd;
1337 build_spawn_cmd(doc, subs_command, dir);
1338 g_free(subs_command);
1339 g_free(dir);
1340 if (cmd_cat != NULL)
1341 g_free(full_command);
1342 build_menu_update(doc);
1347 /*----------------------------------------------------------------
1349 * Create build menu and handle callbacks (&toolbar callbacks)
1351 *----------------------------------------------------------------*/
1352 static void on_make_custom_input_response(const gchar *input)
1354 GeanyDocument *doc = document_get_current();
1356 SETPTR(build_info.custom_target, g_strdup(input));
1357 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1358 build_info.custom_target);
1362 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1364 GeanyDocument *doc = document_get_current();
1365 GeanyBuildCommand *bc;
1366 guint grp = GPOINTER_TO_GRP(user_data);
1367 guint cmd = GPOINTER_TO_CMD(user_data);
1369 if (doc && doc->changed)
1371 if (!document_save_file(doc, FALSE))
1372 return;
1374 g_signal_emit_by_name(geany_object, "build-start");
1376 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1378 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1380 if (! dialog)
1382 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1383 _("Enter custom text here, all entered text is appended to the command."),
1384 build_info.custom_target, &on_make_custom_input_response);
1386 else
1388 gtk_widget_show(dialog);
1390 return;
1392 else if (grp == GEANY_GBG_EXEC)
1394 if (run_info[cmd].pid > (GPid) 1)
1396 kill_process(&run_info[cmd].pid);
1397 return;
1399 bc = get_build_cmd(doc, grp, cmd, NULL);
1400 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1402 gchar *uri;
1403 if (doc == NULL)
1404 return;
1405 uri = g_strconcat("file:///", g_path_skip_root(doc->file_name), NULL);
1406 utils_open_browser(uri);
1407 g_free(uri);
1410 else
1411 build_run_cmd(doc, cmd);
1413 else
1414 build_command(doc, grp, cmd, NULL);
1418 /* group codes for menu items other than the known commands
1419 * value order is important, see the following table for use */
1421 /* the rest in each group */
1422 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1423 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1424 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1425 /* the separator */
1426 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1427 /* the fixed items */
1428 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1429 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1430 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1431 #define MENU_DONE (MENU_COMMANDS + 1)
1434 static struct BuildMenuItemSpec {
1435 const gchar *stock_id;
1436 const gint key_binding;
1437 const guint build_grp;
1438 const guint build_cmd;
1439 const gchar *fix_label;
1440 Callback *cb;
1441 } build_menu_specs[] = {
1442 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1443 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1444 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1445 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1446 {NULL, -1, MENU_FT_REST,
1447 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1448 {NULL, -1, MENU_SEPARATOR,
1449 GBF_SEP_1, NULL, NULL},
1450 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1451 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1452 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1453 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1454 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1455 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1456 {NULL, -1, MENU_NON_FT_REST,
1457 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1458 {NULL, -1, MENU_SEPARATOR,
1459 GBF_SEP_2, NULL, NULL},
1460 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1461 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1462 {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1463 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1464 {NULL, -1, MENU_SEPARATOR,
1465 GBF_SEP_3, NULL, NULL},
1466 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1467 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1468 {NULL, -1, MENU_EXEC_REST,
1469 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1470 {NULL, -1, MENU_SEPARATOR,
1471 GBF_SEP_4, NULL, NULL},
1472 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1473 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1474 {NULL, -1, MENU_DONE,
1475 0, NULL, NULL}
1479 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1480 struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
1482 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1484 if (bs->stock_id != NULL)
1486 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1487 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1489 gtk_widget_show(item);
1490 if (bs->key_binding >= 0)
1491 add_menu_accel(group, (guint) bs->key_binding, ag, item);
1492 gtk_container_add(GTK_CONTAINER(menu), item);
1493 if (bs->cb != NULL)
1495 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1497 menu_items.menu_item[grp][cmd] = item;
1501 static void create_build_menu(BuildMenuItems *build_menu_items)
1503 GtkWidget *menu;
1504 GtkAccelGroup *accel_group = gtk_accel_group_new();
1505 GeanyKeyGroup *keygroup = keybindings_get_core_group(GEANY_KEY_GROUP_BUILD);
1506 guint i, j;
1508 menu = gtk_menu_new();
1509 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1510 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1511 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1512 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1514 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1516 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1517 if (bs->build_grp == MENU_SEPARATOR)
1519 GtkWidget *item = gtk_separator_menu_item_new();
1520 gtk_widget_show(item);
1521 gtk_container_add(GTK_CONTAINER(menu), item);
1522 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1524 else if (bs->fix_label != NULL)
1526 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1527 GBG_FIXED, bs->build_cmd);
1529 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1531 guint grp = bs->build_grp - GEANY_GBG_COUNT;
1532 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1534 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1535 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1536 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1539 else
1541 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1542 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1543 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1546 build_menu_items->menu = menu;
1547 gtk_widget_show(menu);
1548 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1552 /* portability to various GTK versions needs checking
1553 * conforms to description of gtk_accel_label as child of menu item
1554 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1555 static void geany_menu_item_set_label(GtkWidget *w, const gchar *label)
1557 GtkWidget *c = gtk_bin_get_child(GTK_BIN(w));
1559 gtk_label_set_text_with_mnemonic(GTK_LABEL(c), label);
1563 /* * Update the build menu to reflect changes in configuration or status.
1565 * Sets the labels and number of visible items to match the highest
1566 * priority configured commands. Also sets sensitivity if build commands are
1567 * running and switches executes to stop when commands are running.
1569 * @param doc The current document, if available, to save looking it up.
1570 * If @c NULL it will be looked up.
1572 * Call this after modifying any fields of a GeanyBuildCommand structure.
1574 * @see Build Menu Configuration section of the Manual.
1577 void build_menu_update(GeanyDocument *doc)
1579 guint i, cmdcount, cmd, grp;
1580 gboolean vis = FALSE;
1581 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1582 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1583 GeanyBuildCommand *bc;
1585 if (menu_items.menu == NULL)
1586 create_build_menu(&menu_items);
1587 if (doc == NULL)
1588 doc = document_get_current();
1589 have_path = doc != NULL && doc->file_name != NULL;
1590 build_running = build_info.pid > (GPid) 1;
1591 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1592 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1594 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1595 switch (bs->build_grp)
1597 case MENU_SEPARATOR:
1598 if (vis == TRUE)
1600 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1601 vis = FALSE;
1603 else
1604 gtk_widget_hide(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1605 break;
1606 case MENU_NEXT_ERROR:
1607 case MENU_PREV_ERROR:
1608 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1609 vis |= TRUE;
1610 break;
1611 case MENU_COMMANDS:
1612 vis |= TRUE;
1613 break;
1614 default: /* all configurable commands */
1615 if (bs->build_grp >= GEANY_GBG_COUNT)
1617 grp = bs->build_grp - GEANY_GBG_COUNT;
1618 cmdcount = build_groups_count[grp];
1620 else
1622 grp = bs->build_grp;
1623 cmdcount = bs->build_cmd + 1;
1625 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1627 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1628 const gchar *label;
1629 bc = get_build_cmd(doc, grp, cmd, NULL);
1630 if (bc)
1631 label = bc->label;
1632 else
1633 label = NULL;
1635 if (grp < GEANY_GBG_EXEC)
1637 cmd_sensitivity =
1638 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1639 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1640 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1641 if (bc != NULL && !EMPTY(label))
1643 geany_menu_item_set_label(menu_item, label);
1644 gtk_widget_show_all(menu_item);
1645 vis |= TRUE;
1647 else
1648 gtk_widget_hide(menu_item);
1650 else
1652 GtkWidget *image;
1653 exec_running = run_info[cmd].pid > (GPid) 1;
1654 cmd_sensitivity = (bc != NULL) || exec_running;
1655 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1656 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1657 run_sensitivity = cmd_sensitivity;
1658 if (! exec_running)
1660 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1662 else
1664 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1666 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1667 run_running = exec_running;
1668 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1669 if (bc != NULL && !EMPTY(label))
1671 geany_menu_item_set_label(menu_item, label);
1672 gtk_widget_show_all(menu_item);
1673 vis |= TRUE;
1675 else
1676 gtk_widget_hide(menu_item);
1682 run_sensitivity &= (doc != NULL);
1683 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1684 && have_path && ! build_running;
1685 if (widgets.toolitem_build != NULL)
1686 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1687 can_make = FALSE;
1688 if (widgets.toolitem_make_all != NULL)
1689 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1690 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1691 && ! build_running));
1692 if (widgets.toolitem_make_custom != NULL)
1693 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1694 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1695 && ! build_running));
1696 if (widgets.toolitem_make_object != NULL)
1697 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1698 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1699 && ! build_running));
1700 if (widgets.toolitem_set_args != NULL)
1701 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1703 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1704 && have_path && ! build_running;
1705 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1706 gtk_action_set_sensitive(widgets.build_action, can_make);
1707 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1709 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1710 set_stop_button(run_running);
1715 /* Call build_menu_update() instead of calling this directly. */
1716 static void set_stop_button(gboolean stop)
1718 const gchar *button_stock_id = NULL;
1719 GtkToolButton *run_button;
1721 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1722 if (run_button != NULL)
1723 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1725 if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
1726 return;
1727 if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
1728 return;
1730 /* use the run button also as stop button */
1731 if (stop)
1733 if (run_button != NULL)
1734 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
1736 else
1738 if (run_button != NULL)
1739 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
1744 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1746 /* For now, just show the project dialog */
1747 if (app->project)
1748 project_build_properties();
1749 else
1750 show_build_commands_dialog();
1754 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1756 last_toolbutton_action = user_data;
1757 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1758 on_build_menu_item(menuitem, user_data);
1762 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1764 gchar *msg;
1766 last_toolbutton_action = user_data;
1767 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1768 msg = _("Build the current file with Make and the default target");
1769 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1770 msg = _("Build the current file with Make and the specified target");
1771 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1772 msg = _("Compile the current file with Make");
1773 else
1774 msg = NULL;
1775 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1776 on_build_menu_item(menuitem, user_data);
1780 static void kill_process(GPid *pid)
1782 gint result;
1784 #ifdef G_OS_WIN32
1785 g_return_if_fail(*pid != NULL);
1786 result = TerminateProcess(*pid, 0);
1787 /* TerminateProcess() returns TRUE on success, for the check below we have to convert
1788 * it to FALSE (and vice versa) */
1789 result = ! result;
1790 #else
1791 g_return_if_fail(*pid > 1);
1792 result = kill(*pid, SIGTERM);
1793 #endif
1795 if (result != 0)
1796 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), g_strerror(errno));
1797 else
1799 *pid = 0;
1800 build_menu_update(NULL);
1805 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1807 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1808 msgwin_goto_compiler_file_line))
1810 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1812 else
1813 ui_set_statusbar(FALSE, _("No more build errors."));
1817 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1819 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1820 msgwin_goto_compiler_file_line))
1822 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1824 else
1825 ui_set_statusbar(FALSE, _("No more build errors."));
1829 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1831 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1833 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1835 else
1837 on_build_menu_item(NULL, last_toolbutton_action);
1842 /*------------------------------------------------------
1844 * Create and handle the build menu configuration dialog
1846 *-------------------------------------------------------*/
1847 typedef struct RowWidgets
1849 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1850 GeanyBuildSource src;
1851 GeanyBuildSource dst;
1852 GeanyBuildCommand *cmdsrc;
1853 guint grp;
1854 guint cmd;
1855 gboolean cleared;
1856 gboolean used_dst;
1857 } RowWidgets;
1859 static GdkColor *insensitive_color;
1861 static void set_row_color(RowWidgets *r, GdkColor *color )
1863 enum GeanyBuildCmdEntries i;
1865 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1866 gtk_widget_modify_text(r->entries[i], GTK_STATE_NORMAL, color);
1870 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1872 if (GTK_IS_BUTTON(wid))
1873 gtk_button_set_label(GTK_BUTTON(wid), text);
1874 else
1875 gtk_entry_set_text(GTK_ENTRY(wid), text);
1879 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1881 RowWidgets *r = user_data;
1882 guint src;
1883 enum GeanyBuildCmdEntries i;
1884 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1886 if (bc != NULL)
1888 r->cmdsrc = bc;
1889 r->src = src;
1890 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1892 set_build_command_entry_text(r->entries[i],
1893 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1896 else
1898 r->cmdsrc = NULL;
1899 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1901 set_build_command_entry_text(r->entries[i], "");
1904 r->used_dst = FALSE;
1905 set_row_color(r, insensitive_color);
1906 r->cleared = TRUE;
1910 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1912 gtk_entry_set_text(regex,"");
1916 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1918 RowWidgets *r = user_data;
1919 GtkWidget *top_level = gtk_widget_get_toplevel(wid);
1920 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1921 gchar *str;
1923 if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
1924 str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
1925 else
1926 str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
1928 if (!str)
1929 return;
1931 gtk_button_set_label(GTK_BUTTON(wid), str);
1932 g_free(str);
1933 r->used_dst = TRUE;
1934 set_row_color(r, NULL);
1938 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1940 RowWidgets *r = user_data;
1942 r->used_dst = TRUE;
1943 set_row_color(r, NULL);
1947 /* Column headings, array NULL-terminated */
1948 static const gchar *colheads[] =
1950 "#",
1951 N_("Label"),
1952 N_("Command"),
1953 N_("Working directory"),
1954 N_("Reset"),
1955 NULL
1958 /* column names */
1959 #define DC_ITEM 0
1960 #define DC_ENTRIES 1
1961 #define DC_CLEAR 4
1962 #define DC_N_COL 5
1964 static const guint entry_x_padding = 3;
1965 static const guint entry_y_padding = 0;
1968 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1969 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
1971 GtkWidget *label, *clear, *clearicon;
1972 RowWidgets *roww;
1973 GeanyBuildCommand *bc;
1974 guint src;
1975 enum GeanyBuildCmdEntries i;
1976 guint column = 0;
1977 gchar *text;
1979 text = g_strdup_printf("%d.", cmd + 1);
1980 label = gtk_label_new(text);
1981 g_free(text);
1982 insensitive_color = &(gtk_widget_get_style(label)->text[GTK_STATE_INSENSITIVE]);
1983 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1984 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1985 roww = g_new0(RowWidgets, 1);
1986 roww->src = GEANY_BCS_COUNT;
1987 roww->grp = grp;
1988 roww->cmd = cmd;
1989 roww->dst = dst;
1990 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1992 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1994 column += 1;
1995 if (i == GEANY_BC_LABEL)
1997 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1998 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1999 gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
2000 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
2002 else
2004 roww->entries[i] = gtk_entry_new();
2005 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
2007 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
2008 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2010 column++;
2011 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2012 clear = gtk_button_new();
2013 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2014 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
2015 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
2016 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2017 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
2018 if (bc != NULL)
2019 roww->src = src;
2021 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2023 const gchar *str = "";
2025 if (bc != NULL )
2027 if ((str = id_to_str(bc, i)) == NULL)
2028 str = "";
2029 else if (dst == src)
2030 roww->used_dst = TRUE;
2032 set_build_command_entry_text(roww->entries[i], str);
2034 if (bc != NULL && (dst > src))
2035 set_row_color(roww, insensitive_color);
2036 if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
2038 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2039 gtk_widget_set_sensitive(roww->entries[i], FALSE);
2040 gtk_widget_set_sensitive(clear, FALSE);
2042 return roww;
2046 typedef struct BuildTableFields
2048 RowWidgets **rows;
2049 GtkWidget *fileregex;
2050 GtkWidget *nonfileregex;
2051 gchar **fileregexstring;
2052 gchar **nonfileregexstring;
2053 } BuildTableFields;
2056 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
2057 GeanyFiletype *ft)
2059 GtkWidget *label, *sep, *clearicon, *clear;
2060 BuildTableFields *fields;
2061 GtkTable *table;
2062 const gchar **ch;
2063 gchar *txt;
2064 guint col, row, cmdindex;
2065 guint cmd;
2066 guint src;
2067 gboolean sensitivity;
2068 guint sep_padding = entry_y_padding + 3;
2070 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
2071 fields = g_new0(BuildTableFields, 1);
2072 fields->rows = g_new0(RowWidgets*, build_items_count);
2073 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
2075 label = gtk_label_new(_(*ch));
2076 gtk_table_attach(table, label, col, col + 1, 0, 1,
2077 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2079 sep = gtk_hseparator_new();
2080 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
2081 entry_x_padding, sep_padding);
2082 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
2083 txt = g_strdup_printf(_("%s commands"), ft->name);
2084 else
2085 txt = g_strdup_printf(_("%s commands"), _("No filetype"));
2087 label = ui_label_new_bold(txt);
2088 g_free(txt);
2089 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2090 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
2091 entry_x_padding, entry_y_padding);
2092 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
2093 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
2094 label = gtk_label_new(_("Error regular expression:"));
2095 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2096 entry_x_padding, entry_y_padding);
2097 fields->fileregex = gtk_entry_new();
2098 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
2099 sensitivity = (ft == NULL) ? FALSE : TRUE;
2100 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
2102 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
2103 if (src > dst)
2104 sensitivity = FALSE;
2106 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2107 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2108 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2109 clear = gtk_button_new();
2110 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2111 g_signal_connect_swapped(clear, "clicked",
2112 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
2113 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2114 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2115 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
2116 gtk_widget_set_sensitive(clear, sensitivity);
2117 ++row;
2118 sep = gtk_hseparator_new();
2119 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2120 entry_x_padding, sep_padding);
2121 ++row;
2122 label = ui_label_new_bold(_("Independent commands"));
2123 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2124 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2125 entry_x_padding, entry_y_padding);
2126 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
2127 fields->rows[cmdindex] = build_add_dialog_row(
2128 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
2129 label = gtk_label_new(_("Error regular expression:"));
2130 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
2131 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2132 fields->nonfileregex = gtk_entry_new();
2133 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
2134 sensitivity = TRUE;
2135 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
2137 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
2138 sensitivity = src > dst ? FALSE : TRUE;
2140 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2141 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2142 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2143 clear = gtk_button_new();
2144 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2145 g_signal_connect_swapped(clear, "clicked",
2146 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
2147 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2148 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2149 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
2150 gtk_widget_set_sensitive(clear, sensitivity);
2151 ++row;
2152 label = gtk_label_new(NULL);
2153 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2154 _("Note: Item 2 opens a dialog and appends the response to the command."));
2155 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2156 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2157 entry_x_padding, entry_y_padding);
2158 ++row;
2159 sep = gtk_hseparator_new();
2160 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2161 entry_x_padding, sep_padding);
2162 ++row;
2163 label = ui_label_new_bold(_("Execute commands"));
2164 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2165 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2166 entry_x_padding, entry_y_padding);
2167 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
2168 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
2169 sep = gtk_hseparator_new();
2170 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2171 entry_x_padding, sep_padding);
2172 ++row;
2173 label = gtk_label_new(NULL);
2174 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2175 _("%d, %e, %f, %p are substituted in command and directory fields, see manual for details."));
2176 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2177 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2178 entry_x_padding, entry_y_padding);
2179 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2180 ++row;
2181 *table_data = fields;
2182 return GTK_WIDGET(table);
2186 void build_free_fields(BuildTableData table_data)
2188 guint cmdindex;
2190 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2191 g_free(table_data->rows[cmdindex]);
2192 g_free(table_data->rows);
2193 g_free(table_data);
2197 /* string compare where null pointers match null or 0 length strings */
2198 #if 0
2199 static gint stcmp(const gchar *a, const gchar *b)
2201 if (a == NULL && b == NULL)
2202 return 0;
2203 if (a == NULL && b != NULL)
2204 return strlen(b);
2205 if (a != NULL && b == NULL)
2206 return strlen(a);
2207 return strcmp(a, b);
2209 #endif
2212 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2214 if (GTK_IS_BUTTON(wid))
2215 return gtk_button_get_label(GTK_BUTTON(wid));
2216 else
2217 return gtk_entry_get_text(GTK_ENTRY(wid));
2221 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
2223 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2224 gboolean changed = FALSE;
2225 enum GeanyBuildCmdEntries i;
2227 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2229 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2231 if (table_data->rows[drow]->cleared)
2233 if (dst->dst[grp] != NULL)
2235 if (*(dst->dst[grp]) == NULL)
2236 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2237 (*(dst->dst[grp]))[cmd].exists = FALSE;
2238 (*(dst->dst[grp]))[cmd].changed = TRUE;
2239 changed = TRUE;
2242 if (table_data->rows[drow]->used_dst == TRUE)
2244 if (dst->dst[grp] != NULL)
2246 if (*(dst->dst[grp]) == NULL)
2247 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2248 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2249 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2250 (*(dst->dst[grp]))[cmd].exists = TRUE;
2251 (*(dst->dst[grp]))[cmd].changed = TRUE;
2252 changed = TRUE;
2255 else
2257 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2258 g_free(entries[i]);
2260 return changed;
2264 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2266 gboolean changed = FALSE;
2267 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2269 if (((src == NULL /* originally there was no regex */
2270 || *src == NULL) /* or it was NULL*/
2271 && !EMPTY(reg)) /* and something was typed */
2272 || (src != NULL /* originally there was a regex*/
2273 && (*src == NULL /* and either it was NULL */
2274 || strcmp(*src, reg) != 0))) /* or it has been changed */
2276 if (dst != NULL)
2278 SETPTR(*dst, g_strdup(reg));
2279 changed = TRUE;
2282 return changed;
2286 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2288 guint cmdindex, cmd;
2289 gboolean changed = FALSE;
2291 if (response == GTK_RESPONSE_ACCEPT)
2293 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2294 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2295 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2296 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2297 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2298 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2299 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2300 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2302 return changed;
2306 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2308 BuildDestination menu_dst;
2310 if (ft != NULL)
2312 menu_dst.dst[GEANY_GBG_FT] = &(ft->projfilecmds);
2313 menu_dst.fileregexstr = &(ft->projerror_regex_string);
2315 else
2317 menu_dst.dst[GEANY_GBG_FT] = NULL;
2318 menu_dst.fileregexstr = NULL;
2320 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2321 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2322 menu_dst.nonfileregexstr = &regex_proj;
2324 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2328 static void show_build_commands_dialog(void)
2330 GtkWidget *dialog, *table, *vbox;
2331 GeanyDocument *doc = document_get_current();
2332 GeanyFiletype *ft = NULL;
2333 const gchar *title = _("Set Build Commands");
2334 gint response;
2335 BuildTableData table_data;
2336 BuildDestination prefdsts;
2338 if (doc != NULL)
2339 ft = doc->file_type;
2340 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2341 GTK_DIALOG_DESTROY_WITH_PARENT,
2342 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2343 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2344 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2345 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2346 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2347 gtk_widget_show_all(dialog);
2348 /* run modally to prevent user changing idx filetype */
2349 response = gtk_dialog_run(GTK_DIALOG(dialog));
2351 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2352 if (ft != NULL)
2354 prefdsts.dst[GEANY_GBG_FT] = &(ft->homefilecmds);
2355 prefdsts.fileregexstr = &(ft->homeerror_regex_string);
2356 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->homeexeccmds);
2358 else
2360 prefdsts.dst[GEANY_GBG_FT] = NULL;
2361 prefdsts.fileregexstr = NULL;
2362 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2364 prefdsts.nonfileregexstr = &regex_pref;
2365 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2366 filetypes_save_commands(ft);
2367 build_free_fields(table_data);
2369 build_menu_update(doc);
2370 gtk_widget_destroy(dialog);
2374 /* Creates the relevant build menu if necessary. */
2375 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2377 BuildMenuItems *items;
2379 items = &menu_items;
2380 if (items->menu == NULL)
2381 create_build_menu(items);
2382 return items;
2386 /*----------------------------------------------------------
2388 * Load and store configuration
2390 * ---------------------------------------------------------*/
2391 static const gchar *build_grp_name = "build-menu";
2393 /* config format for build-menu group is prefix_gg_nn_xx=value
2394 * where gg = FT, NF, EX for the command group
2395 * nn = 2 digit command number
2396 * xx = LB for label, CM for command and WD for working dir */
2397 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2398 static const gchar *fixedkey="xx_xx_xx";
2400 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2401 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2402 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2404 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2405 gchar *prefix, gboolean loc)
2407 guint cmd;
2408 gsize prefixlen; /* NOTE prefixlen used in macros above */
2409 GeanyBuildCommand *dstcmd;
2410 gchar *key;
2411 static gchar cmdbuf[3] = " ";
2413 if (*dst == NULL)
2414 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2415 dstcmd = *dst;
2416 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2417 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2418 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2420 gchar *label;
2421 if (cmd >= 100)
2422 return; /* ensure no buffer overflow */
2423 sprintf(cmdbuf, "%02d", cmd);
2424 set_key_grp(key, groups[grp]);
2425 set_key_cmd(key, cmdbuf);
2426 set_key_fld(key, "LB");
2427 if (loc)
2428 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2429 else
2430 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2431 if (label != NULL)
2433 dstcmd[cmd].exists = TRUE;
2434 SETPTR(dstcmd[cmd].label, label);
2435 set_key_fld(key,"CM");
2436 SETPTR(dstcmd[cmd].command,
2437 g_key_file_get_string(config, build_grp_name, key, NULL));
2438 set_key_fld(key,"WD");
2439 SETPTR(dstcmd[cmd].working_dir,
2440 g_key_file_get_string(config, build_grp_name, key, NULL));
2442 else dstcmd[cmd].exists = FALSE;
2444 g_free(key);
2448 /* for the specified source load new format build menu items or try to make some sense of
2449 * old format setings, not done perfectly but better than ignoring them */
2450 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2452 GeanyFiletype *ft;
2453 GeanyProject *pj;
2454 gchar **ftlist;
2455 gchar *value, *basedir, *makebasedir;
2456 gboolean bvalue = FALSE;
2458 if (g_key_file_has_group(config, build_grp_name))
2460 switch (src)
2462 case GEANY_BCS_FT:
2463 ft = (GeanyFiletype*)p;
2464 if (ft == NULL)
2465 return;
2466 build_load_menu_grp(config, &(ft->filecmds), GEANY_GBG_FT, NULL, TRUE);
2467 build_load_menu_grp(config, &(ft->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2468 build_load_menu_grp(config, &(ft->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2469 SETPTR(ft->error_regex_string,
2470 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2471 break;
2472 case GEANY_BCS_HOME_FT:
2473 ft = (GeanyFiletype*)p;
2474 if (ft == NULL)
2475 return;
2476 build_load_menu_grp(config, &(ft->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2477 build_load_menu_grp(config, &(ft->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2478 SETPTR(ft->homeerror_regex_string,
2479 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2480 break;
2481 case GEANY_BCS_PREF:
2482 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2483 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2484 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2485 break;
2486 case GEANY_BCS_PROJ:
2487 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2488 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2489 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2490 pj = (GeanyProject*)p;
2491 if (p == NULL)
2492 return;
2493 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2494 if (ftlist != NULL)
2496 gchar **ftname;
2497 if (pj->build_filetypes_list == NULL)
2498 pj->build_filetypes_list = g_ptr_array_new();
2499 g_ptr_array_set_size(pj->build_filetypes_list, 0);
2500 for (ftname = ftlist; *ftname != NULL; ++ftname)
2502 ft = filetypes_lookup_by_name(*ftname);
2503 if (ft != NULL)
2505 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2506 g_ptr_array_add(pj->build_filetypes_list, ft);
2507 SETPTR(ft->projerror_regex_string,
2508 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2509 g_free(regkey);
2510 build_load_menu_grp(config, &(ft->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2511 build_load_menu_grp(config, &(ft->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2514 g_free(ftlist);
2516 break;
2517 default: /* defaults don't load from config, see build_init */
2518 break;
2522 /* load old [build_settings] values if there is no value defined by [build-menu] */
2524 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2525 /* TODO: rewrite as function */
2526 #define ASSIGNIF(type, id, string, value) \
2527 if (!EMPTY(value) && ! type[GBO_TO_CMD(id)].exists) { \
2528 type[GBO_TO_CMD(id)].exists = TRUE; \
2529 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(string)); \
2530 SETPTR(type[GBO_TO_CMD(id)].command, (value)); \
2531 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL); \
2532 type[GBO_TO_CMD(id)].old = TRUE; \
2533 } else \
2534 g_free(value);
2536 switch (src)
2538 case GEANY_BCS_FT:
2539 ft = (GeanyFiletype*)p;
2540 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2541 if (value != NULL)
2543 if (ft->filecmds == NULL)
2544 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2545 ASSIGNIF(ft->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2547 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2548 if (value != NULL)
2550 if (ft->filecmds == NULL)
2551 ft->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2552 ASSIGNIF(ft->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2554 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2555 if (value != NULL)
2557 if (ft->execcmds == NULL)
2558 ft->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2559 ASSIGNIF(ft->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2561 if (ft->error_regex_string == NULL)
2562 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2563 break;
2564 case GEANY_BCS_PROJ:
2565 if (non_ft_pref == NULL)
2566 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2567 basedir = project_get_base_path();
2568 if (basedir == NULL)
2569 basedir = g_strdup("%d");
2570 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2571 if (bvalue)
2572 makebasedir = g_strdup(basedir);
2573 else
2574 makebasedir = g_strdup("%d");
2575 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2576 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
2577 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2578 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2579 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2580 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
2581 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2582 if (!EMPTY(value))
2584 if (exec_proj == NULL)
2585 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2586 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2588 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2589 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2590 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
2591 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
2592 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2595 g_free(makebasedir);
2596 g_free(basedir);
2597 break;
2598 case GEANY_BCS_PREF:
2599 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2600 if (value != NULL)
2602 if (non_ft_pref == NULL)
2603 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2604 ASSIGNIF(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
2605 g_strdup_printf("%s ", value));
2606 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2607 g_strdup_printf("%s %%e.o",value));
2608 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2610 break;
2611 default:
2612 break;
2617 static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2619 guint cmd;
2620 gsize prefixlen; /* NOTE prefixlen used in macros above */
2621 gchar *key;
2622 guint count = 0;
2623 enum GeanyBuildCmdEntries i;
2625 if (src == NULL)
2626 return 0;
2627 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2628 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2629 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2631 if (src[cmd].exists) ++count;
2632 if (src[cmd].changed)
2634 static gchar cmdbuf[4] = " ";
2635 if (cmd >= 100)
2636 return count; /* ensure no buffer overflow */
2637 sprintf(cmdbuf, "%02d", cmd);
2638 set_key_grp(key, groups[grp]);
2639 set_key_cmd(key, cmdbuf);
2640 if (src[cmd].exists)
2642 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2644 set_key_fld(key, config_keys[i]);
2645 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2648 else
2650 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2652 set_key_fld(key, config_keys[i]);
2653 g_key_file_remove_key(config, build_grp_name, key, NULL);
2658 g_free(key);
2659 return count;
2663 typedef struct ForEachData
2665 GKeyFile *config;
2666 GPtrArray *ft_names;
2667 } ForEachData;
2670 static void foreach_project_filetype(gpointer data, gpointer user_data)
2672 GeanyFiletype *ft = data;
2673 ForEachData *d = user_data;
2674 guint i = 0;
2675 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2677 i += build_save_menu_grp(d->config, ft->projfilecmds, GEANY_GBG_FT, ft->name);
2678 i += build_save_menu_grp(d->config, ft->projexeccmds, GEANY_GBG_EXEC, ft->name);
2679 if (!EMPTY(ft->projerror_regex_string))
2681 g_key_file_set_string(d->config, build_grp_name, regkey, ft->projerror_regex_string);
2682 i++;
2684 else
2685 g_key_file_remove_key(d->config, build_grp_name, regkey, NULL);
2686 g_free(regkey);
2687 if (i > 0)
2688 g_ptr_array_add(d->ft_names, ft->name);
2692 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2693 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2695 GeanyFiletype *ft;
2696 GeanyProject *pj;
2697 ForEachData data;
2699 switch (src)
2701 case GEANY_BCS_HOME_FT:
2702 ft = (GeanyFiletype*)ptr;
2703 if (ft == NULL)
2704 return;
2705 build_save_menu_grp(config, ft->homefilecmds, GEANY_GBG_FT, NULL);
2706 build_save_menu_grp(config, ft->homeexeccmds, GEANY_GBG_EXEC, NULL);
2707 if (!EMPTY(ft->homeerror_regex_string))
2708 g_key_file_set_string(config, build_grp_name, "error_regex", ft->homeerror_regex_string);
2709 else
2710 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2711 break;
2712 case GEANY_BCS_PREF:
2713 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2714 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2715 if (!EMPTY(regex_pref))
2716 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2717 else
2718 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2719 break;
2720 case GEANY_BCS_PROJ:
2721 pj = (GeanyProject*)ptr;
2722 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2723 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2724 if (!EMPTY(regex_proj))
2725 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2726 else
2727 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2728 if (pj->build_filetypes_list != NULL)
2730 data.config = config;
2731 data.ft_names = g_ptr_array_new();
2732 g_ptr_array_foreach(pj->build_filetypes_list, foreach_project_filetype, (gpointer)(&data));
2733 if (data.ft_names->pdata != NULL)
2734 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2735 (const gchar**)(data.ft_names->pdata), data.ft_names->len);
2736 else
2737 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2738 g_ptr_array_free(data.ft_names, TRUE);
2740 break;
2741 default: /* defaults and GEANY_BCS_FT can't save */
2742 break;
2747 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2748 void build_set_group_count(GeanyBuildGroup grp, gint count)
2750 guint i, sum;
2752 g_return_if_fail(count >= 0);
2754 if ((guint) count > build_groups_count[grp])
2755 build_groups_count[grp] = (guint) count;
2756 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2757 sum += build_groups_count[i];
2758 build_items_count = sum;
2762 /** Get the count of commands for the group
2764 * Get the number of commands in the group specified by @a grp.
2766 * @param grp the group of the specified menu item.
2768 * @return a count of the number of commands in the group
2772 guint build_get_group_count(const GeanyBuildGroup grp)
2774 g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
2775 return build_groups_count[grp];
2779 static void on_project_close(void)
2781 /* remove project regexen */
2782 SETPTR(regex_proj, NULL);
2786 static struct
2788 const gchar *label;
2789 const gchar *command;
2790 const gchar *working_dir;
2791 GeanyBuildCommand **ptr;
2792 gint index;
2793 } default_cmds[] = {
2794 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2795 { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2796 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2797 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2798 { NULL, NULL, NULL, NULL, 0 }
2802 void build_init(void)
2804 GtkWidget *item;
2805 GtkWidget *toolmenu;
2806 gint cmdindex;
2808 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2810 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2811 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2812 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2813 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2815 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2817 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2818 cmd->exists = TRUE;
2819 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2820 cmd->command = g_strdup(default_cmds[cmdindex].command);
2821 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2824 /* create the toolbar Build item sub menu */
2825 toolmenu = gtk_menu_new();
2826 g_object_ref(toolmenu);
2828 /* build the code */
2829 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2830 gtk_widget_show(item);
2831 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2832 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2833 GBO_TO_POINTER(GEANY_GBO_BUILD));
2834 widgets.toolitem_build = item;
2836 item = gtk_separator_menu_item_new();
2837 gtk_widget_show(item);
2838 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2840 /* build the code with make all */
2841 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2842 gtk_widget_show(item);
2843 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2844 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2845 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2846 widgets.toolitem_make_all = item;
2848 /* build the code with make custom */
2849 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2850 gtk_widget_show(item);
2851 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2852 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2853 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2854 widgets.toolitem_make_custom = item;
2856 /* build the code with make object */
2857 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2858 gtk_widget_show(item);
2859 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2860 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2861 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2862 widgets.toolitem_make_object = item;
2864 item = gtk_separator_menu_item_new();
2865 gtk_widget_show(item);
2866 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2868 /* arguments */
2869 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
2870 gtk_widget_show(item);
2871 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2872 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2873 widgets.toolitem_set_args = item;
2875 /* get toolbar action pointers */
2876 widgets.build_action = toolbar_get_action_by_name("Build");
2877 widgets.compile_action = toolbar_get_action_by_name("Compile");
2878 widgets.run_action = toolbar_get_action_by_name("Run");
2879 widgets.toolmenu = toolmenu;
2880 /* set the submenu to the toolbar item */
2881 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);