Merge pull request #382 from cousteaulecommandant/patch-3
[geany-mirror.git] / src / build.c
blobeefdd72196c4ba3f5c2eab3b5989dacb2959ab66
1 /*
2 * build.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6 * Copyright 2009 Lex Trotman <elextr(at)gmail(dot)com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * Build commands and menu items.
26 /* TODO: tidy code:
27 * Use intermediate pointers for common subexpressions.
28 * Replace defines with enums.
29 * Other TODOs in code. */
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
35 #include "app.h"
36 #include "build.h"
37 #include "dialogs.h"
38 #include "document.h"
39 #include "filetypesprivate.h"
40 #include "geanymenubuttonaction.h"
41 #include "geanyobject.h"
42 #include "keybindingsprivate.h"
43 #include "msgwindow.h"
44 #include "prefs.h"
45 #include "projectprivate.h"
46 #include "support.h"
47 #include "toolbar.h"
48 #include "ui_utils.h"
49 #include "utils.h"
50 #include "vte.h"
51 #include "win32.h"
53 #include "gtkcompat.h"
55 #include <stdlib.h>
56 #include <string.h>
57 #include <sys/stat.h>
58 #include <unistd.h>
59 #include <errno.h>
60 #include <glib/gstdio.h>
62 #ifdef G_OS_UNIX
63 # include <sys/types.h>
64 # include <sys/wait.h>
65 # include <signal.h>
66 #else
67 # include <windows.h>
68 #endif
71 /* g_spawn_async_with_pipes doesn't work on Windows */
72 #ifdef G_OS_WIN32
73 #define SYNC_SPAWN
74 #endif
76 /* Number of editor indicators to draw - limited as this can affect performance */
77 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
80 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
82 static gchar *current_dir_entered = NULL;
84 typedef struct RunInfo
86 GPid pid;
87 gint file_type_id;
88 } RunInfo;
90 static RunInfo *run_info;
92 #ifdef G_OS_WIN32
93 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script.bat";
94 #else
95 static const gchar RUN_SCRIPT_CMD[] = "./geany_run_script.sh";
96 #endif
98 /* pack group (<8) and command (<32) into a user_data pointer */
99 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
100 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
101 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
102 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
104 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
106 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
108 static struct
110 GtkAction *run_action;
111 GtkAction *compile_action;
112 GtkAction *build_action;
113 GtkWidget *toolmenu;
115 GtkWidget *toolitem_build;
116 GtkWidget *toolitem_make_all;
117 GtkWidget *toolitem_make_custom;
118 GtkWidget *toolitem_make_object;
119 GtkWidget *toolitem_set_args;
121 widgets;
123 static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
124 static guint build_items_count = 9;
126 #ifndef SYNC_SPAWN
127 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data);
128 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data);
129 #endif
130 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose, GError **error);
131 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
132 static void set_stop_button(gboolean stop);
133 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
134 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
135 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
136 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
137 static void kill_process(GPid *pid);
138 static void show_build_result_message(gboolean failure);
139 static void process_build_output_line(const gchar *line, gint color);
140 static void show_build_commands_dialog(void);
141 static void on_build_menu_item(GtkWidget *w, gpointer user_data);
143 void build_finalize(void)
145 g_free(build_info.dir);
146 g_free(build_info.custom_target);
148 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
149 gtk_widget_destroy(menu_items.menu);
153 /* note: copied from keybindings.c, may be able to go away */
154 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
155 GtkAccelGroup *accel_group, GtkWidget *menuitem)
157 GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
159 if (kb->key != 0)
160 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
161 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
165 /* convenience routines to access parts of GeanyBuildCommand */
166 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
168 switch (id)
170 case GEANY_BC_LABEL:
171 return bc->label;
172 case GEANY_BC_COMMAND:
173 return bc->command;
174 case GEANY_BC_WORKING_DIR:
175 return bc->working_dir;
177 g_assert(0);
178 return NULL;
182 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
184 switch (id)
186 case GEANY_BC_LABEL:
187 SETPTR(bc->label, str);
188 break;
189 case GEANY_BC_COMMAND:
190 SETPTR(bc->command, str);
191 break;
192 case GEANY_BC_WORKING_DIR:
193 SETPTR(bc->working_dir, str);
194 break;
195 default:
196 g_assert(0);
201 static const gchar *config_keys[GEANY_BC_CMDENTRIES_COUNT] = {
202 "LB", /* label */
203 "CM", /* command */
204 "WD" /* working directory */
207 /*-----------------------------------------------------
209 * Execute commands and handle results
211 *-----------------------------------------------------*/
213 /* the various groups of commands not in the filetype struct */
214 static GeanyBuildCommand *ft_def = NULL;
215 static GeanyBuildCommand *non_ft_proj = NULL;
216 static GeanyBuildCommand *non_ft_pref = NULL;
217 static GeanyBuildCommand *non_ft_def = NULL;
218 static GeanyBuildCommand *exec_proj = NULL;
219 static GeanyBuildCommand *exec_pref = NULL;
220 static GeanyBuildCommand *exec_def = NULL;
221 /* and the regexen not in the filetype structure */
222 static gchar *regex_pref = NULL;
223 /* project non-fileregex string */
224 static gchar *regex_proj = NULL;
226 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
227 #ifndef PRINTBUILDCMDS
228 #define PRINTBUILDCMDS FALSE
229 #endif
230 static gboolean printbuildcmds = PRINTBUILDCMDS;
233 /* for debug only, print the commands structures in priority order */
234 static void printfcmds(void)
236 #if 0
237 GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
238 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
239 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
240 { &ft_def, NULL, NULL, NULL, NULL, NULL },
241 { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
242 { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
244 GeanyFiletype *ft = NULL;
245 GeanyDocument *doc;
246 gint i, j, k, l, m;
247 enum GeanyBuildCmdEntries n;
248 gint cc[GEANY_BCS_COUNT];
249 gchar c;
251 doc = document_get_current();
252 if (doc != NULL)
253 ft = doc->file_type;
254 if (ft != NULL)
256 printf("filetype %s\n",ft->name);
257 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->priv->filecmds);
258 cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->priv->homefilecmds);
259 cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->priv->projfilecmds);
260 cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->priv->ftdefcmds);
261 cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->priv->execcmds);
262 cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->priv->homeexeccmds);
263 cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->priv->projexeccmds);
265 for (i = 0; i < GEANY_BCS_COUNT; ++i)
267 m = 1;
268 for (j = 0; j < GEANY_GBG_COUNT; ++j)
270 for (k = 0; k < build_groups_count[j]; ++k)
271 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
273 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
275 if ((*(cl[j][i]))[k].entries[n] != NULL &&
276 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
278 m = l;
283 cc[i] = m;
285 for (i = 0; i < GEANY_GBG_COUNT; ++i)
287 for (k = 0; k < build_groups_count[i]; ++k)
289 for (l = 0; l < 2; ++l)
291 c = ' ';
292 for (j = 0; j < GEANY_BCS_COUNT; ++j)
294 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
296 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
298 if ((*(cl[i][j]))[k].entries[i] != NULL)
299 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
300 else
301 printf("%c %*.*s",c,cc[j],cc[j]," ");
304 else
305 printf("%c %*.*s",c,cc[j],cc[j]," ");
306 c = ',';
308 printf("\n");
311 printf("\n");
313 #endif
317 /* macros to save typing and make the logic visible */
318 #define return_cmd_if(src, cmds)\
319 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
321 *fr=src; \
322 if (printbuildcmds) \
323 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
324 return &(cmds[cmdindex]); \
327 #define return_ft_cmd_if(src, cmds)\
328 if (ft != NULL && ft->priv->cmds != NULL \
329 && ft->priv->cmds[cmdindex].exists && below>src)\
331 *fr=src; \
332 if (printbuildcmds) \
333 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
334 return &(ft->priv->cmds[cmdindex]); \
338 /* get the next lowest command taking priority into account */
339 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
340 guint below, guint *from)
342 /* Note: parameter below used in macros above */
343 GeanyFiletype *ft = NULL;
344 guint sink, *fr = &sink;
346 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
348 if (printbuildcmds)
349 printfcmds();
350 if (cmdgrp >= GEANY_GBG_COUNT)
351 return NULL;
352 if (from != NULL)
353 fr = from;
354 if (doc == NULL)
355 doc = document_get_current();
356 if (doc != NULL)
357 ft = doc->file_type;
359 switch (cmdgrp)
361 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
362 if (ft != NULL)
364 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
365 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
366 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
368 return_cmd_if(GEANY_BCS_DEF, ft_def);
369 break;
370 case GEANY_GBG_NON_FT: /* order proj, pref, def */
371 return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
372 return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
373 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
374 return_cmd_if(GEANY_BCS_DEF, non_ft_def);
375 break;
376 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
377 return_cmd_if(GEANY_BCS_PROJ, exec_proj);
378 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
379 return_cmd_if(GEANY_BCS_PREF, exec_pref);
380 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
381 return_ft_cmd_if(GEANY_BCS_FT, execcmds);
382 return_cmd_if(GEANY_BCS_DEF, exec_def);
383 break;
384 default:
385 break;
387 return NULL;
391 /* shortcut to start looking at the top */
392 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
394 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
398 #define return_nonblank_regex(src, ptr)\
399 if (!EMPTY(ptr)) \
400 { *fr = (src); return &(ptr); }
403 /* like get_build_cmd, but for regexen, used by filetypes */
404 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
406 guint sink, *fr = &sink;
408 if (from != NULL)
409 fr = from;
410 if (grp == GEANY_GBG_FT)
412 if (ft == NULL)
414 GeanyDocument *doc = document_get_current();
415 if (doc != NULL)
416 ft = doc->file_type;
418 if (ft == NULL)
419 return NULL;
420 return_nonblank_regex(GEANY_BCS_PROJ, ft->priv->projerror_regex_string);
421 return_nonblank_regex(GEANY_BCS_HOME_FT, ft->priv->homeerror_regex_string);
422 return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
424 else if (grp == GEANY_GBG_NON_FT)
426 return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
427 return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
429 return NULL;
433 static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
435 GeanyDocument *doc;
436 GeanyFiletype *ft = NULL;
438 switch (grp)
440 case GEANY_GBG_FT:
441 if ((doc = document_get_current()) == NULL)
442 return NULL;
443 if ((ft = doc->file_type) == NULL)
444 return NULL;
445 switch (src)
447 case GEANY_BCS_DEF: return &(ft->priv->ftdefcmds);
448 case GEANY_BCS_FT: return &(ft->priv->filecmds);
449 case GEANY_BCS_HOME_FT: return &(ft->priv->homefilecmds);
450 case GEANY_BCS_PREF: return &(ft->priv->homefilecmds);
451 case GEANY_BCS_PROJ: return &(ft->priv->projfilecmds);
452 default: return NULL;
454 break;
455 case GEANY_GBG_NON_FT:
456 switch (src)
458 case GEANY_BCS_DEF: return &(non_ft_def);
459 case GEANY_BCS_PREF: return &(non_ft_pref);
460 case GEANY_BCS_PROJ: return &(non_ft_proj);
461 default: return NULL;
463 break;
464 case GEANY_GBG_EXEC:
465 if ((doc = document_get_current()) != NULL)
466 ft = doc->file_type;
467 switch (src)
469 case GEANY_BCS_DEF: return &(exec_def);
470 case GEANY_BCS_FT: return ft ? &(ft->priv->execcmds): NULL;
471 case GEANY_BCS_HOME_FT: return ft ? &(ft->priv->homeexeccmds): NULL;
472 case GEANY_BCS_PROJ_FT: return ft ? &(ft->priv->projexeccmds): NULL;
473 case GEANY_BCS_PREF: return &(exec_pref);
474 case GEANY_BCS_PROJ: return &(exec_proj);
475 default: return NULL;
477 break;
478 default:
479 return NULL;
484 /* get pointer to the command group array */
485 static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
487 GeanyBuildCommand **g = get_build_group_pointer(src, grp);
488 if (g == NULL) return NULL;
489 return *g;
493 /** Remove the specified Build menu item.
495 * Makes the specified menu item configuration no longer exist. This
496 * is different to setting fields to blank because the menu item
497 * will be deleted from the configuration file on saving
498 * (except the system filetypes settings @see Build Menu Configuration
499 * section of the Manual).
501 * @param src the source of the menu item to remove.
502 * @param grp the group of the command to remove.
503 * @param cmd the index (from 0) of the command within the group. A negative
504 * value will remove the whole group.
506 * If any parameter is out of range does nothing.
508 * Updates the menu.
511 void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
513 GeanyBuildCommand *bc;
514 guint i;
516 bc = get_build_group(src, grp);
517 if (bc == NULL)
518 return;
519 if (cmd < 0)
521 for (i = 0; i < build_groups_count[grp]; ++i)
522 bc[i].exists = FALSE;
524 else if ((guint) cmd < build_groups_count[grp])
525 bc[cmd].exists = FALSE;
529 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
531 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
532 * hidden by higher priority commands.
534 * @param src the source of the specified menu item.
535 * @param grp the group of the specified menu item.
536 * @param cmd the index of the command within the group.
538 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
539 * This is a pointer to an internal structure and must not be freed.
541 * @see build_menu_update
543 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
545 GeanyBuildCommand *bc;
547 g_return_val_if_fail(src < GEANY_BCS_COUNT, NULL);
548 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
549 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
551 bc = get_build_group(src, grp);
552 if (bc == NULL)
553 return NULL;
554 return &(bc[cmd]);
558 /** Get the string for the menu item field.
560 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
561 * that the menu item will use if activated.
563 * @param grp the group of the specified menu item.
564 * @param cmd the index of the command within the group.
565 * @param fld the field to return
567 * @return a pointer to the constant string or @a NULL if it doesn't exist.
568 * This is a pointer to an internal structure and must not be freed.
571 const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
572 const GeanyBuildCmdEntries fld)
574 GeanyBuildCommand *c;
575 gchar *str = NULL;
577 g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
578 g_return_val_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT, NULL);
579 g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
581 c = get_build_cmd(NULL, grp, cmd, NULL);
582 if (c == NULL) return NULL;
583 switch (fld)
585 case GEANY_BC_COMMAND:
586 str = c->command;
587 break;
588 case GEANY_BC_LABEL:
589 str = c->label;
590 break;
591 case GEANY_BC_WORKING_DIR:
592 str = c->working_dir;
593 break;
594 default:
595 break;
597 return str;
600 /** Set the string for the menu item field.
602 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
604 * @param src the source of the menu item
605 * @param grp the group of the specified menu item.
606 * @param cmd the index of the menu item within the group.
607 * @param fld the field in the menu item command to set
608 * @param val the value to set the field to, is copied
612 void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp,
613 const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
615 GeanyBuildCommand **g;
617 g_return_if_fail(src < GEANY_BCS_COUNT);
618 g_return_if_fail(grp < GEANY_GBG_COUNT);
619 g_return_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT);
620 g_return_if_fail(cmd < build_groups_count[grp]);
622 g = get_build_group_pointer(src, grp);
623 if (g == NULL) return;
624 if (*g == NULL )
626 *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
628 switch (fld)
630 case GEANY_BC_COMMAND:
631 SETPTR((*g)[cmd].command, g_strdup(val));
632 (*g)[cmd].exists = TRUE;
633 break;
634 case GEANY_BC_LABEL:
635 SETPTR((*g)[cmd].label, g_strdup(val));
636 (*g)[cmd].exists = TRUE;
637 break;
638 case GEANY_BC_WORKING_DIR:
639 SETPTR((*g)[cmd].working_dir, g_strdup(val));
640 (*g)[cmd].exists = TRUE;
641 break;
642 default:
643 break;
645 build_menu_update(NULL);
648 /** Activate the menu item.
650 * Activate the menu item specified by @a grp and @a cmd.
652 * @param grp the group of the specified menu item.
653 * @param cmd the index of the command within the group.
657 void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
659 on_build_menu_item(NULL, GRP_CMD_TO_POINTER(grp, cmd));
663 /* Clear all error indicators in all documents. */
664 static void clear_all_errors(void)
666 guint i;
668 foreach_document(i)
670 editor_indicator_clear_errors(documents[i]->editor);
675 #ifdef SYNC_SPAWN
676 static void parse_build_output(const gchar **output, gint status)
678 guint x, i, len;
679 gchar *line, **lines;
681 for (x = 0; x < 2; x++)
683 if (!EMPTY(output[x]))
685 lines = g_strsplit_set(output[x], "\r\n", -1);
686 len = g_strv_length(lines);
688 for (i = 0; i < len; i++)
690 if (!EMPTY(lines[i]))
692 line = lines[i];
693 while (*line != '\0')
694 { /* replace any control characters in the output */
695 if (*line < 32)
696 *line = 32;
697 line++;
699 process_build_output_line(lines[i], COLOR_BLACK);
702 g_strfreev(lines);
706 show_build_result_message(status != 0);
707 utils_beep();
709 build_info.pid = 0;
710 /* enable build items again */
711 build_menu_update(NULL);
713 #endif
716 /* Replaces occurences of %e and %p with the appropriate filenames,
717 * %d and %p replacements should be in UTF8 */
718 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
720 GString *stack;
721 gchar *filename = NULL;
722 gchar *replacement;
723 gchar *executable = NULL;
724 gchar *ret_str; /* to be freed when not in use anymore */
726 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
728 stack = g_string_new(src);
729 if (doc != NULL && doc->file_name != NULL)
731 filename = utils_get_utf8_from_locale(doc->file_name);
733 /* replace %f with the filename (including extension) */
734 replacement = g_path_get_basename(filename);
735 utils_string_replace_all(stack, "%f", replacement);
736 g_free(replacement);
738 /* replace %d with the absolute path of the dir of the current file */
739 replacement = g_path_get_dirname(filename);
740 utils_string_replace_all(stack, "%d", replacement);
741 g_free(replacement);
743 /* replace %e with the filename (excluding extension) */
744 executable = utils_remove_ext_from_filename(filename);
745 replacement = g_path_get_basename(executable);
746 utils_string_replace_all(stack, "%e", replacement);
747 g_free(replacement);
750 /* replace %p with the current project's (absolute) base directory */
751 replacement = NULL; /* prevent double free if no replacement found */
752 if (app->project)
754 replacement = project_get_base_path();
756 else if (strstr(stack->str, "%p"))
757 { /* fall back to %d */
758 ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
759 if (doc != NULL && filename != NULL)
760 replacement = g_path_get_dirname(filename);
763 utils_string_replace_all(stack, "%p", replacement);
764 g_free(replacement);
766 ret_str = utils_get_utf8_from_locale(stack->str);
767 g_free(executable);
768 g_free(filename);
769 g_string_free(stack, TRUE);
771 return ret_str; /* don't forget to free src also if needed */
775 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
776 * idx document directory */
777 static GPid build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
779 GError *error = NULL;
780 gchar **argv;
781 gchar *working_dir;
782 gchar *utf8_working_dir;
783 gchar *utf8_cmd_string;
784 #ifdef SYNC_SPAWN
785 gchar *output[2];
786 gint status;
787 #else
788 gint stdout_fd;
789 gint stderr_fd;
790 #endif
792 g_return_val_if_fail(doc == NULL || doc->is_valid, (GPid) -1);
794 if (!((doc != NULL && !EMPTY(doc->file_name)) || !EMPTY(dir)))
796 geany_debug("Failed to run command with no working directory");
797 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
798 return (GPid) 1;
801 clear_all_errors();
802 SETPTR(current_dir_entered, NULL);
804 #ifdef G_OS_WIN32
805 argv = g_strsplit(cmd, " ", 0);
806 #else
807 argv = g_new0(gchar *, 4);
808 argv[0] = g_strdup("/bin/sh");
809 argv[1] = g_strdup("-c");
810 argv[2] = g_strdup(cmd);
811 argv[3] = NULL;
812 #endif
814 utf8_cmd_string = utils_get_utf8_from_locale(cmd);
815 utf8_working_dir = !EMPTY(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
816 working_dir = utils_get_locale_from_utf8(utf8_working_dir);
818 gtk_list_store_clear(msgwindow.store_compiler);
819 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
820 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
821 g_free(utf8_working_dir);
822 g_free(utf8_cmd_string);
824 /* set the build info for the message window */
825 g_free(build_info.dir);
826 build_info.dir = g_strdup(working_dir);
827 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
828 build_info.message_count = 0;
830 #ifdef SYNC_SPAWN
831 if (! utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
832 NULL, NULL, &output[0], &output[1], &status, &error))
833 #else
834 if (! g_spawn_async_with_pipes(working_dir, argv, NULL,
835 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
836 &(build_info.pid), NULL, &stdout_fd, &stderr_fd, &error))
837 #endif
839 geany_debug("build command spawning failed: %s", error->message);
840 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
841 g_strfreev(argv);
842 g_error_free(error);
843 g_free(working_dir);
844 error = NULL;
845 return (GPid) 0;
848 #ifdef SYNC_SPAWN
849 parse_build_output((const gchar**) output, status);
850 g_free(output[0]);
851 g_free(output[1]);
852 #else
853 if (build_info.pid != 0)
855 g_child_watch_add(build_info.pid, (GChildWatchFunc) build_exit_cb, NULL);
856 build_menu_update(doc);
857 ui_progress_bar_start(NULL);
860 /* use GIOChannels to monitor stdout and stderr */
861 utils_set_up_io_channel(stdout_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
862 TRUE, build_iofunc, GINT_TO_POINTER(0));
863 utils_set_up_io_channel(stderr_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
864 TRUE, build_iofunc, GINT_TO_POINTER(1));
865 #endif
867 g_strfreev(argv);
868 g_free(working_dir);
870 return build_info.pid;
874 /* Returns: NULL if there was an error, or the working directory the script was created in.
875 * vte_cmd_nonscript is the location of a string which is filled with the command to be used
876 * when vc->skip_run_script is set, otherwise it will be set to NULL */
877 static gchar *prepare_run_script(GeanyDocument *doc, gchar **vte_cmd_nonscript, guint cmdindex)
879 GeanyBuildCommand *cmd = NULL;
880 gchar *working_dir = NULL;
881 const gchar *cmd_working_dir;
882 gboolean autoclose = FALSE;
883 gboolean result = FALSE;
884 gchar *tmp;
885 gchar *cmd_string;
886 GError *error = NULL;
888 if (vte_cmd_nonscript != NULL)
889 *vte_cmd_nonscript = NULL;
891 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
893 cmd_string = build_replace_placeholder(doc, cmd->command);
894 cmd_working_dir = cmd->working_dir;
895 if (EMPTY(cmd_working_dir))
896 cmd_working_dir = "%d";
897 working_dir = build_replace_placeholder(doc, cmd_working_dir); /* in utf-8 */
899 /* only test whether working dir exists, don't change it or else Windows support will break
900 * (gspawn-win32-helper.exe is used by GLib and must be in $PATH which means current working
901 * dir where geany.exe was started from, so we can't change it) */
902 if (EMPTY(working_dir) || ! g_file_test(working_dir, G_FILE_TEST_EXISTS) ||
903 ! g_file_test(working_dir, G_FILE_TEST_IS_DIR))
905 ui_set_statusbar(TRUE, _("Failed to change the working directory to \"%s\""),
906 !EMPTY(working_dir) ? working_dir : "<NULL>" );
907 utils_free_pointers(2, cmd_string, working_dir, NULL);
908 return NULL;
911 #ifdef HAVE_VTE
912 if (vte_info.have_vte && vc->run_in_vte)
914 if (vc->skip_run_script)
916 if (vte_cmd_nonscript != NULL)
917 *vte_cmd_nonscript = cmd_string;
918 else
919 g_free(cmd_string);
921 return working_dir;
923 else
924 /* don't wait for user input at the end of script when we are running in VTE */
925 autoclose = TRUE;
927 #endif
929 /* RUN_SCRIPT_CMD should be ok in UTF8 without converting in locale because it
930 * contains no umlauts */
931 tmp = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
932 result = build_create_shellscript(tmp, cmd_string, autoclose, &error);
933 if (! result)
935 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
936 !EMPTY(cmd_string) ? cmd_string : NULL, error->message);
937 g_error_free(error);
940 utils_free_pointers(2, cmd_string, tmp, NULL);
942 if (result)
943 return working_dir;
945 g_free(working_dir);
946 return NULL;
950 static GPid build_run_cmd(GeanyDocument *doc, guint cmdindex)
952 gchar *working_dir;
953 gchar *vte_cmd_nonscript = NULL;
954 GError *error = NULL;
956 if (! DOC_VALID(doc) || doc->file_name == NULL)
957 return (GPid) 0;
959 working_dir = prepare_run_script(doc, &vte_cmd_nonscript, cmdindex);
960 if (working_dir == NULL)
961 return (GPid) 0;
963 run_info[cmdindex].file_type_id = doc->file_type->id;
965 #ifdef HAVE_VTE
966 if (vte_info.have_vte && vc->run_in_vte)
968 gchar *vte_cmd;
970 if (vc->skip_run_script)
972 SETPTR(vte_cmd_nonscript, utils_get_utf8_from_locale(vte_cmd_nonscript));
973 vte_cmd = g_strconcat(vte_cmd_nonscript, "\n", NULL);
974 g_free(vte_cmd_nonscript);
976 else
977 vte_cmd = g_strconcat("\n/bin/sh ", RUN_SCRIPT_CMD, "\n", NULL);
979 /* change into current directory if it is not done by default */
980 if (! vc->follow_path)
982 /* we need to convert the working_dir back to UTF-8 because the VTE expects it */
983 gchar *utf8_working_dir = utils_get_utf8_from_locale(working_dir);
984 vte_cwd(utf8_working_dir, TRUE);
985 g_free(utf8_working_dir);
987 if (! vte_send_cmd(vte_cmd))
989 ui_set_statusbar(FALSE,
990 _("Could not execute the file in the VTE because it probably contains a command."));
991 geany_debug("Could not execute the file in the VTE because it probably contains a command.");
994 /* show the VTE */
995 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
996 gtk_widget_grab_focus(vc->vte);
997 msgwin_show_hide(TRUE);
999 run_info[cmdindex].pid = 1;
1001 g_free(vte_cmd);
1003 else
1004 #endif
1006 gchar *locale_term_cmd = NULL;
1007 gint argv_len, i;
1008 gchar **argv = NULL;
1009 gchar *script_path = NULL;
1011 /* get the terminal path */
1012 locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
1013 /* split the term_cmd, so arguments will work too */
1014 if (!g_shell_parse_argv(locale_term_cmd, &argv_len, &argv, NULL))
1016 ui_set_statusbar(TRUE,
1017 _("Could not parse terminal command \"%s\" "
1018 "(check Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
1019 run_info[cmdindex].pid = (GPid) 1;
1020 script_path = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
1021 g_unlink(script_path);
1022 goto free_strings;
1025 /* check that terminal exists (to prevent misleading error messages) */
1026 if (argv[0] != NULL)
1028 gchar *tmp = argv[0];
1029 /* g_find_program_in_path checks whether tmp exists and is executable */
1030 argv[0] = g_find_program_in_path(tmp);
1031 g_free(tmp);
1033 if (argv[0] == NULL)
1035 ui_set_statusbar(TRUE,
1036 _("Could not find terminal \"%s\" "
1037 "(check path for Terminal tool setting in Preferences)"), tool_prefs.term_cmd);
1038 run_info[cmdindex].pid = (GPid) 1;
1039 script_path = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
1040 g_unlink(script_path);
1041 goto free_strings;
1044 for (i = 0; i < argv_len; i++)
1046 utils_str_replace_all(&(argv[i]), "%c", RUN_SCRIPT_CMD);
1049 if (! g_spawn_async(working_dir, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
1050 NULL, NULL, &(run_info[cmdindex].pid), &error))
1052 geany_debug("g_spawn_async() failed: %s", error->message);
1053 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
1054 g_error_free(error);
1055 script_path = g_build_filename(working_dir, RUN_SCRIPT_CMD, NULL);
1056 g_unlink(script_path);
1057 error = NULL;
1058 run_info[cmdindex].pid = (GPid) 0;
1061 if (run_info[cmdindex].pid != 0)
1063 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
1064 (gpointer)&(run_info[cmdindex]));
1065 build_menu_update(doc);
1067 free_strings:
1068 g_strfreev(argv);
1069 g_free(locale_term_cmd);
1070 g_free(script_path);
1073 g_free(working_dir);
1074 return run_info[cmdindex].pid;
1078 static void process_build_output_line(const gchar *str, gint color)
1080 gchar *msg, *tmp;
1081 gchar *filename;
1082 gint line;
1084 msg = g_strdup(str);
1086 g_strchomp(msg);
1088 if (EMPTY(msg))
1090 g_free(msg);
1091 return;
1094 if (build_parse_make_dir(msg, &tmp))
1096 SETPTR(current_dir_entered, tmp);
1098 msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
1100 if (line != -1 && filename != NULL)
1102 GeanyDocument *doc = document_find_by_filename(filename);
1104 /* limit number of indicators */
1105 if (doc && editor_prefs.use_indicators &&
1106 build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
1108 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
1109 line--; /* so only adjust the line number if it is greater than 0 */
1110 editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
1112 build_info.message_count++;
1113 color = COLOR_RED; /* error message parsed on the line */
1115 g_free(filename);
1117 msgwin_compiler_add_string(color, msg);
1118 g_free(msg);
1122 #ifndef SYNC_SPAWN
1123 static gboolean build_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data)
1125 if (cond & (G_IO_IN | G_IO_PRI))
1127 gchar *msg;
1128 GIOStatus st;
1130 while ((st = g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL)) == G_IO_STATUS_NORMAL && msg)
1132 gint color = (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK;
1134 process_build_output_line(msg, color);
1135 g_free(msg);
1137 if (st == G_IO_STATUS_ERROR || st == G_IO_STATUS_EOF) return FALSE;
1139 if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
1140 return FALSE;
1142 return TRUE;
1144 #endif
1147 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1149 const gchar *pos;
1151 *prefix = NULL;
1153 if (string == NULL)
1154 return FALSE;
1156 if ((pos = strstr(string, "Entering directory")) != NULL)
1158 gsize len;
1159 gchar *input;
1161 /* get the start of the path */
1162 pos = strstr(string, "/");
1164 if (pos == NULL)
1165 return FALSE;
1167 input = g_strdup(pos);
1169 /* kill the ' at the end of the path */
1170 len = strlen(input);
1171 input[len - 1] = '\0';
1172 input = g_realloc(input, len); /* shorten by 1 */
1173 *prefix = input;
1175 return TRUE;
1178 if (strstr(string, "Leaving directory") != NULL)
1180 *prefix = NULL;
1181 return TRUE;
1184 return FALSE;
1188 static void show_build_result_message(gboolean failure)
1190 gchar *msg;
1192 if (failure)
1194 msg = _("Compilation failed.");
1195 msgwin_compiler_add_string(COLOR_BLUE, msg);
1196 /* If msgwindow is hidden, user will want to display it to see the error */
1197 if (! ui_prefs.msgwindow_visible)
1199 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1200 msgwin_show_hide(TRUE);
1202 else
1203 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1204 ui_set_statusbar(FALSE, "%s", msg);
1206 else
1208 msg = _("Compilation finished successfully.");
1209 msgwin_compiler_add_string(COLOR_BLUE, msg);
1210 if (! ui_prefs.msgwindow_visible ||
1211 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1212 ui_set_statusbar(FALSE, "%s", msg);
1217 #ifndef SYNC_SPAWN
1218 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1220 gboolean failure = FALSE;
1222 #ifdef G_OS_WIN32
1223 failure = status;
1224 #else
1225 if (WIFEXITED(status))
1227 if (WEXITSTATUS(status) != EXIT_SUCCESS)
1228 failure = TRUE;
1230 else if (WIFSIGNALED(status))
1232 /* the terminating signal: WTERMSIG (status)); */
1233 failure = TRUE;
1235 else
1236 { /* any other failure occured */
1237 failure = TRUE;
1239 #endif
1240 show_build_result_message(failure);
1242 utils_beep();
1243 g_spawn_close_pid(child_pid);
1245 build_info.pid = 0;
1246 /* enable build items again */
1247 build_menu_update(NULL);
1248 ui_progress_bar_stop();
1250 #endif
1253 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1255 RunInfo *run_info_data = user_data;
1257 g_spawn_close_pid(child_pid);
1259 run_info_data->pid = 0;
1260 /* reset the stop button and menu item to the original meaning */
1261 build_menu_update(NULL);
1265 static void set_file_error_from_errno(GError **error, gint err, const gchar *prefix)
1267 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(err), "%s%s%s",
1268 prefix ? prefix : "", prefix ? ": " : "", g_strerror(err));
1272 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1273 * fname is the full file name (including path) for the script to create */
1274 static gboolean build_create_shellscript(const gchar *fname, const gchar *cmd, gboolean autoclose, GError **error)
1276 FILE *fp;
1277 gchar *str;
1278 gboolean success = TRUE;
1279 #ifdef G_OS_WIN32
1280 gchar *expanded_cmd;
1281 #endif
1283 fp = g_fopen(fname, "w");
1284 if (! fp)
1286 set_file_error_from_errno(error, errno, "Failed to create file");
1287 return FALSE;
1289 #ifdef G_OS_WIN32
1290 /* Expand environment variables like %blah%. */
1291 expanded_cmd = win32_expand_environment_variables(cmd);
1292 str = g_strdup_printf("%s\n\n%s\ndel \"%%0\"\n\npause\n", expanded_cmd, (autoclose) ? "" : "pause");
1293 g_free(expanded_cmd);
1294 #else
1295 str = g_strdup_printf(
1296 "#!/bin/sh\n\nrm $0\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1297 \n\n%s\n", cmd, (autoclose) ? "" :
1298 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1299 "dash\ndummy_var=\"\"\nread dummy_var");
1300 #endif
1302 if (fputs(str, fp) < 0)
1304 set_file_error_from_errno(error, errno, "Failed to write file");
1305 success = FALSE;
1307 g_free(str);
1309 if (fclose(fp) != 0)
1311 if (error && ! *error) /* don't set error twice */
1312 set_file_error_from_errno(error, errno, "Failed to close file");
1313 success = FALSE;
1316 return success;
1320 typedef void Callback(GtkWidget *w, gpointer u);
1322 /* run the command catenating cmd_cat if present */
1323 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
1325 gchar *dir;
1326 gchar *full_command, *subs_command;
1327 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1328 gchar *cmdstr;
1330 if (buildcmd == NULL)
1331 return;
1333 cmdstr = buildcmd->command;
1335 if (cmd_cat != NULL)
1337 if (cmdstr != NULL)
1338 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1339 else
1340 full_command = g_strdup(cmd_cat);
1342 else
1343 full_command = cmdstr;
1345 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1346 subs_command = build_replace_placeholder(doc, full_command);
1347 build_info.grp = grp;
1348 build_info.cmd = cmd;
1349 build_spawn_cmd(doc, subs_command, dir);
1350 g_free(subs_command);
1351 g_free(dir);
1352 if (cmd_cat != NULL)
1353 g_free(full_command);
1354 build_menu_update(doc);
1359 /*----------------------------------------------------------------
1361 * Create build menu and handle callbacks (&toolbar callbacks)
1363 *----------------------------------------------------------------*/
1364 static void on_make_custom_input_response(const gchar *input, gpointer data)
1366 GeanyDocument *doc = document_get_current();
1368 SETPTR(build_info.custom_target, g_strdup(input));
1369 build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
1370 build_info.custom_target);
1374 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1376 GeanyDocument *doc = document_get_current();
1377 GeanyBuildCommand *bc;
1378 guint grp = GPOINTER_TO_GRP(user_data);
1379 guint cmd = GPOINTER_TO_CMD(user_data);
1381 if (doc && doc->changed)
1383 if (!document_save_file(doc, FALSE))
1384 return;
1386 g_signal_emit_by_name(geany_object, "build-start");
1388 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1390 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1392 if (! dialog)
1394 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1395 _("Enter custom text here, all entered text is appended to the command."),
1396 build_info.custom_target, &on_make_custom_input_response, NULL);
1398 else
1400 gtk_widget_show(dialog);
1402 return;
1404 else if (grp == GEANY_GBG_EXEC)
1406 if (run_info[cmd].pid > (GPid) 1)
1408 kill_process(&run_info[cmd].pid);
1409 return;
1411 bc = get_build_cmd(doc, grp, cmd, NULL);
1412 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1414 gchar *uri;
1415 if (doc == NULL)
1416 return;
1417 uri = g_strconcat("file:///", g_path_skip_root(doc->file_name), NULL);
1418 utils_open_browser(uri);
1419 g_free(uri);
1422 else
1423 build_run_cmd(doc, cmd);
1425 else
1426 build_command(doc, grp, cmd, NULL);
1430 /* group codes for menu items other than the known commands
1431 * value order is important, see the following table for use */
1433 /* the rest in each group */
1434 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1435 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1436 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1437 /* the separator */
1438 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1439 /* the fixed items */
1440 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1441 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1442 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1443 #define MENU_DONE (MENU_COMMANDS + 1)
1446 static struct BuildMenuItemSpec {
1447 const gchar *stock_id;
1448 const gint key_binding;
1449 const guint build_grp;
1450 const guint build_cmd;
1451 const gchar *fix_label;
1452 Callback *cb;
1453 } build_menu_specs[] = {
1454 {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
1455 GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
1456 {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
1457 GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
1458 {NULL, -1, MENU_FT_REST,
1459 GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
1460 {NULL, -1, MENU_SEPARATOR,
1461 GBF_SEP_1, NULL, NULL},
1462 {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
1463 GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
1464 {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
1465 GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
1466 {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
1467 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
1468 {NULL, -1, MENU_NON_FT_REST,
1469 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
1470 {NULL, -1, MENU_SEPARATOR,
1471 GBF_SEP_2, NULL, NULL},
1472 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1473 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1474 {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
1475 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1476 {NULL, -1, MENU_SEPARATOR,
1477 GBF_SEP_3, NULL, NULL},
1478 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1479 GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
1480 {NULL, -1, MENU_EXEC_REST,
1481 GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
1482 {NULL, -1, MENU_SEPARATOR,
1483 GBF_SEP_4, NULL, NULL},
1484 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1485 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1486 {NULL, -1, MENU_DONE,
1487 0, NULL, NULL}
1491 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1492 struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
1494 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1496 if (bs->stock_id != NULL)
1498 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1499 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1501 gtk_widget_show(item);
1502 if (bs->key_binding >= 0)
1503 add_menu_accel(group, (guint) bs->key_binding, ag, item);
1504 gtk_container_add(GTK_CONTAINER(menu), item);
1505 if (bs->cb != NULL)
1507 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1509 menu_items.menu_item[grp][cmd] = item;
1513 static void create_build_menu(BuildMenuItems *build_menu_items)
1515 GtkWidget *menu;
1516 GtkAccelGroup *accel_group = gtk_accel_group_new();
1517 GeanyKeyGroup *keygroup = keybindings_get_core_group(GEANY_KEY_GROUP_BUILD);
1518 guint i, j;
1520 menu = gtk_menu_new();
1521 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1522 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1523 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1524 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1526 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1528 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1529 if (bs->build_grp == MENU_SEPARATOR)
1531 GtkWidget *item = gtk_separator_menu_item_new();
1532 gtk_widget_show(item);
1533 gtk_container_add(GTK_CONTAINER(menu), item);
1534 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1536 else if (bs->fix_label != NULL)
1538 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1539 GBG_FIXED, bs->build_cmd);
1541 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1543 guint grp = bs->build_grp - GEANY_GBG_COUNT;
1544 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1546 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1547 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1548 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1551 else
1553 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1554 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1555 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1558 build_menu_items->menu = menu;
1559 gtk_widget_show(menu);
1560 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1564 /* portability to various GTK versions needs checking
1565 * conforms to description of gtk_accel_label as child of menu item
1566 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1567 static void geany_menu_item_set_label(GtkWidget *w, const gchar *label)
1569 GtkWidget *c = gtk_bin_get_child(GTK_BIN(w));
1571 gtk_label_set_text_with_mnemonic(GTK_LABEL(c), label);
1575 /* * Update the build menu to reflect changes in configuration or status.
1577 * Sets the labels and number of visible items to match the highest
1578 * priority configured commands. Also sets sensitivity if build commands are
1579 * running and switches executes to stop when commands are running.
1581 * @param doc The current document, if available, to save looking it up.
1582 * If @c NULL it will be looked up.
1584 * Call this after modifying any fields of a GeanyBuildCommand structure.
1586 * @see Build Menu Configuration section of the Manual.
1589 void build_menu_update(GeanyDocument *doc)
1591 guint i, cmdcount, cmd, grp;
1592 gboolean vis = FALSE;
1593 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1594 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1595 GeanyBuildCommand *bc;
1597 g_return_if_fail(doc == NULL || doc->is_valid);
1599 if (menu_items.menu == NULL)
1600 create_build_menu(&menu_items);
1601 if (doc == NULL)
1602 doc = document_get_current();
1603 have_path = doc != NULL && doc->file_name != NULL;
1604 build_running = build_info.pid > (GPid) 1;
1605 have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1606 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1608 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1609 switch (bs->build_grp)
1611 case MENU_SEPARATOR:
1612 if (vis == TRUE)
1614 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1615 vis = FALSE;
1617 else
1618 gtk_widget_hide(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1619 break;
1620 case MENU_NEXT_ERROR:
1621 case MENU_PREV_ERROR:
1622 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1623 vis |= TRUE;
1624 break;
1625 case MENU_COMMANDS:
1626 vis |= TRUE;
1627 break;
1628 default: /* all configurable commands */
1629 if (bs->build_grp >= GEANY_GBG_COUNT)
1631 grp = bs->build_grp - GEANY_GBG_COUNT;
1632 cmdcount = build_groups_count[grp];
1634 else
1636 grp = bs->build_grp;
1637 cmdcount = bs->build_cmd + 1;
1639 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1641 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1642 const gchar *label;
1643 bc = get_build_cmd(doc, grp, cmd, NULL);
1644 if (bc)
1645 label = bc->label;
1646 else
1647 label = NULL;
1649 if (grp < GEANY_GBG_EXEC)
1651 cmd_sensitivity =
1652 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1653 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1654 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1655 if (bc != NULL && !EMPTY(label))
1657 geany_menu_item_set_label(menu_item, label);
1658 gtk_widget_show_all(menu_item);
1659 vis |= TRUE;
1661 else
1662 gtk_widget_hide(menu_item);
1664 else
1666 GtkWidget *image;
1667 exec_running = run_info[cmd].pid > (GPid) 1;
1668 cmd_sensitivity = (bc != NULL) || exec_running;
1669 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1670 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1671 run_sensitivity = cmd_sensitivity;
1672 if (! exec_running)
1674 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1676 else
1678 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1680 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1681 run_running = exec_running;
1682 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1683 if (bc != NULL && !EMPTY(label))
1685 geany_menu_item_set_label(menu_item, label);
1686 gtk_widget_show_all(menu_item);
1687 vis |= TRUE;
1689 else
1690 gtk_widget_hide(menu_item);
1696 run_sensitivity &= (doc != NULL);
1697 can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
1698 && have_path && ! build_running;
1699 if (widgets.toolitem_build != NULL)
1700 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1701 can_make = FALSE;
1702 if (widgets.toolitem_make_all != NULL)
1703 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1704 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
1705 && ! build_running));
1706 if (widgets.toolitem_make_custom != NULL)
1707 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1708 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
1709 && ! build_running));
1710 if (widgets.toolitem_make_object != NULL)
1711 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1712 (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
1713 && ! build_running));
1714 if (widgets.toolitem_set_args != NULL)
1715 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1717 can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
1718 && have_path && ! build_running;
1719 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1720 gtk_action_set_sensitive(widgets.build_action, can_make);
1721 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1723 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1724 set_stop_button(run_running);
1729 /* Call build_menu_update() instead of calling this directly. */
1730 static void set_stop_button(gboolean stop)
1732 const gchar *button_stock_id = NULL;
1733 GtkToolButton *run_button;
1735 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1736 if (run_button != NULL)
1737 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1739 if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
1740 return;
1741 if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
1742 return;
1744 /* use the run button also as stop button */
1745 if (stop)
1747 if (run_button != NULL)
1748 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
1750 else
1752 if (run_button != NULL)
1753 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
1758 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1760 /* For now, just show the project dialog */
1761 if (app->project)
1762 project_build_properties();
1763 else
1764 show_build_commands_dialog();
1768 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1770 last_toolbutton_action = user_data;
1771 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1772 on_build_menu_item(menuitem, user_data);
1776 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1778 gchar *msg;
1780 last_toolbutton_action = user_data;
1781 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
1782 msg = _("Build the current file with Make and the default target");
1783 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
1784 msg = _("Build the current file with Make and the specified target");
1785 else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
1786 msg = _("Compile the current file with Make");
1787 else
1788 msg = NULL;
1789 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1790 on_build_menu_item(menuitem, user_data);
1794 static void kill_process(GPid *pid)
1796 gint result;
1798 #ifdef G_OS_WIN32
1799 g_return_if_fail(*pid != NULL);
1800 result = TerminateProcess(*pid, 0);
1801 /* TerminateProcess() returns TRUE on success, for the check below we have to convert
1802 * it to FALSE (and vice versa) */
1803 result = ! result;
1804 #else
1805 g_return_if_fail(*pid > 1);
1806 result = kill(*pid, SIGTERM);
1807 #endif
1809 if (result != 0)
1810 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), g_strerror(errno));
1811 else
1813 *pid = 0;
1814 build_menu_update(NULL);
1819 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1821 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1822 msgwin_goto_compiler_file_line))
1824 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1826 else
1827 ui_set_statusbar(FALSE, _("No more build errors."));
1831 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1833 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1834 msgwin_goto_compiler_file_line))
1836 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1838 else
1839 ui_set_statusbar(FALSE, _("No more build errors."));
1843 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1845 if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
1847 on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
1849 else
1851 on_build_menu_item(NULL, last_toolbutton_action);
1856 /*------------------------------------------------------
1858 * Create and handle the build menu configuration dialog
1860 *-------------------------------------------------------*/
1861 typedef struct RowWidgets
1863 GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
1864 GeanyBuildSource src;
1865 GeanyBuildSource dst;
1866 GeanyBuildCommand *cmdsrc;
1867 guint grp;
1868 guint cmd;
1869 gboolean cleared;
1870 gboolean used_dst;
1871 } RowWidgets;
1873 static GdkColor *insensitive_color;
1875 static void set_row_color(RowWidgets *r, GdkColor *color )
1877 enum GeanyBuildCmdEntries i;
1879 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1880 gtk_widget_modify_text(r->entries[i], GTK_STATE_NORMAL, color);
1884 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1886 if (GTK_IS_BUTTON(wid))
1887 gtk_button_set_label(GTK_BUTTON(wid), text);
1888 else
1889 gtk_entry_set_text(GTK_ENTRY(wid), text);
1893 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1895 RowWidgets *r = user_data;
1896 guint src;
1897 enum GeanyBuildCmdEntries i;
1898 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1900 if (bc != NULL)
1902 r->cmdsrc = bc;
1903 r->src = src;
1904 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1906 set_build_command_entry_text(r->entries[i],
1907 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1910 else
1912 r->cmdsrc = NULL;
1913 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1915 set_build_command_entry_text(r->entries[i], "");
1918 r->used_dst = FALSE;
1919 set_row_color(r, insensitive_color);
1920 r->cleared = TRUE;
1924 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1926 gtk_entry_set_text(regex,"");
1930 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1932 RowWidgets *r = user_data;
1933 GtkWidget *top_level = gtk_widget_get_toplevel(wid);
1934 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1935 gchar *str;
1937 if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
1938 str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
1939 else
1940 str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
1942 if (!str)
1943 return;
1945 gtk_button_set_label(GTK_BUTTON(wid), str);
1946 g_free(str);
1947 r->used_dst = TRUE;
1948 set_row_color(r, NULL);
1952 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1954 RowWidgets *r = user_data;
1956 r->used_dst = TRUE;
1957 set_row_color(r, NULL);
1961 /* Column headings, array NULL-terminated */
1962 static const gchar *colheads[] =
1964 "#",
1965 N_("Label"),
1966 N_("Command"),
1967 N_("Working directory"),
1968 N_("Reset"),
1969 NULL
1972 /* column names */
1973 #define DC_ITEM 0
1974 #define DC_ENTRIES 1
1975 #define DC_CLEAR 4
1976 #define DC_N_COL 5
1978 static const guint entry_x_padding = 3;
1979 static const guint entry_y_padding = 0;
1982 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1983 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
1985 GtkWidget *label, *clear, *clearicon;
1986 RowWidgets *roww;
1987 GeanyBuildCommand *bc;
1988 guint src;
1989 enum GeanyBuildCmdEntries i;
1990 guint column = 0;
1991 gchar *text;
1993 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
1995 text = g_strdup_printf("%d.", cmd + 1);
1996 label = gtk_label_new(text);
1997 g_free(text);
1998 insensitive_color = &(gtk_widget_get_style(label)->text[GTK_STATE_INSENSITIVE]);
1999 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
2000 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2001 roww = g_new0(RowWidgets, 1);
2002 roww->src = GEANY_BCS_COUNT;
2003 roww->grp = grp;
2004 roww->cmd = cmd;
2005 roww->dst = dst;
2006 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2008 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
2010 column += 1;
2011 if (i == GEANY_BC_LABEL)
2013 GtkWidget *wid = roww->entries[i] = gtk_button_new();
2014 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
2015 gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
2016 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
2018 else
2020 roww->entries[i] = gtk_entry_new();
2021 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
2023 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
2024 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2026 column++;
2027 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2028 clear = gtk_button_new();
2029 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2030 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
2031 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
2032 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2033 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
2034 if (bc != NULL)
2035 roww->src = src;
2037 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2039 const gchar *str = "";
2041 if (bc != NULL )
2043 if ((str = id_to_str(bc, i)) == NULL)
2044 str = "";
2045 else if (dst == src)
2046 roww->used_dst = TRUE;
2048 set_build_command_entry_text(roww->entries[i], str);
2050 if (bc != NULL && (dst > src))
2051 set_row_color(roww, insensitive_color);
2052 if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
2054 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2055 gtk_widget_set_sensitive(roww->entries[i], FALSE);
2056 gtk_widget_set_sensitive(clear, FALSE);
2058 return roww;
2062 typedef struct BuildTableFields
2064 RowWidgets **rows;
2065 GtkWidget *fileregex;
2066 GtkWidget *nonfileregex;
2067 gchar **fileregexstring;
2068 gchar **nonfileregexstring;
2069 } BuildTableFields;
2072 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
2073 GeanyFiletype *ft)
2075 GtkWidget *label, *sep, *clearicon, *clear;
2076 BuildTableFields *fields;
2077 GtkTable *table;
2078 const gchar **ch;
2079 gchar *txt;
2080 guint col, row, cmdindex;
2081 guint cmd;
2082 guint src;
2083 gboolean sensitivity;
2084 guint sep_padding = entry_y_padding + 3;
2086 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
2087 fields = g_new0(BuildTableFields, 1);
2088 fields->rows = g_new0(RowWidgets*, build_items_count);
2089 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
2091 label = gtk_label_new(_(*ch));
2092 gtk_table_attach(table, label, col, col + 1, 0, 1,
2093 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2095 sep = gtk_hseparator_new();
2096 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
2097 entry_x_padding, sep_padding);
2098 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
2099 txt = g_strdup_printf(_("%s commands"), ft->name);
2100 else
2101 txt = g_strdup_printf(_("%s commands"), _("No filetype"));
2103 label = ui_label_new_bold(txt);
2104 g_free(txt);
2105 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2106 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
2107 entry_x_padding, entry_y_padding);
2108 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
2109 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
2110 label = gtk_label_new(_("Error regular expression:"));
2111 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2112 entry_x_padding, entry_y_padding);
2113 fields->fileregex = gtk_entry_new();
2114 fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
2115 sensitivity = (ft == NULL) ? FALSE : TRUE;
2116 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
2118 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
2119 if (src > dst)
2120 sensitivity = FALSE;
2122 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2123 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2124 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2125 clear = gtk_button_new();
2126 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2127 g_signal_connect_swapped(clear, "clicked",
2128 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
2129 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2130 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2131 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
2132 gtk_widget_set_sensitive(clear, sensitivity);
2133 ++row;
2134 sep = gtk_hseparator_new();
2135 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2136 entry_x_padding, sep_padding);
2137 ++row;
2138 label = ui_label_new_bold(_("Independent commands"));
2139 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2140 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2141 entry_x_padding, entry_y_padding);
2142 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
2143 fields->rows[cmdindex] = build_add_dialog_row(
2144 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
2145 label = gtk_label_new(_("Error regular expression:"));
2146 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
2147 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2148 fields->nonfileregex = gtk_entry_new();
2149 fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
2150 sensitivity = TRUE;
2151 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
2153 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
2154 sensitivity = src > dst ? FALSE : TRUE;
2156 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2157 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2158 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2159 clear = gtk_button_new();
2160 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2161 g_signal_connect_swapped(clear, "clicked",
2162 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
2163 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2164 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2165 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
2166 gtk_widget_set_sensitive(clear, sensitivity);
2167 ++row;
2168 label = gtk_label_new(NULL);
2169 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2170 _("Note: Item 2 opens a dialog and appends the response to the command."));
2171 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2172 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2173 entry_x_padding, entry_y_padding);
2174 ++row;
2175 sep = gtk_hseparator_new();
2176 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2177 entry_x_padding, sep_padding);
2178 ++row;
2179 label = ui_label_new_bold(_("Execute commands"));
2180 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2181 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2182 entry_x_padding, entry_y_padding);
2183 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
2184 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
2185 sep = gtk_hseparator_new();
2186 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2187 entry_x_padding, sep_padding);
2188 ++row;
2189 label = gtk_label_new(NULL);
2190 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2191 _("%d, %e, %f, %p are substituted in command and directory fields, see manual for details."));
2192 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2193 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2194 entry_x_padding, entry_y_padding);
2195 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2196 ++row;
2197 *table_data = fields;
2198 return GTK_WIDGET(table);
2202 void build_free_fields(BuildTableData table_data)
2204 guint cmdindex;
2206 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2207 g_free(table_data->rows[cmdindex]);
2208 g_free(table_data->rows);
2209 g_free(table_data);
2213 /* string compare where null pointers match null or 0 length strings */
2214 #if 0
2215 static gint stcmp(const gchar *a, const gchar *b)
2217 if (a == NULL && b == NULL)
2218 return 0;
2219 if (a == NULL && b != NULL)
2220 return strlen(b);
2221 if (a != NULL && b == NULL)
2222 return strlen(a);
2223 return strcmp(a, b);
2225 #endif
2228 static const gchar *get_build_command_entry_text(GtkWidget *wid)
2230 if (GTK_IS_BUTTON(wid))
2231 return gtk_button_get_label(GTK_BUTTON(wid));
2232 else
2233 return gtk_entry_get_text(GTK_ENTRY(wid));
2237 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
2239 gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
2240 gboolean changed = FALSE;
2241 enum GeanyBuildCmdEntries i;
2243 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2245 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2247 if (table_data->rows[drow]->cleared)
2249 if (dst->dst[grp] != NULL)
2251 if (*(dst->dst[grp]) == NULL)
2252 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2253 (*(dst->dst[grp]))[cmd].exists = FALSE;
2254 (*(dst->dst[grp]))[cmd].changed = TRUE;
2255 changed = TRUE;
2258 if (table_data->rows[drow]->used_dst == TRUE)
2260 if (dst->dst[grp] != NULL)
2262 if (*(dst->dst[grp]) == NULL)
2263 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2264 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2265 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2266 (*(dst->dst[grp]))[cmd].exists = TRUE;
2267 (*(dst->dst[grp]))[cmd].changed = TRUE;
2268 changed = TRUE;
2271 else
2273 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2274 g_free(entries[i]);
2276 return changed;
2280 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2282 gboolean changed = FALSE;
2283 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2285 if (((src == NULL /* originally there was no regex */
2286 || *src == NULL) /* or it was NULL*/
2287 && !EMPTY(reg)) /* and something was typed */
2288 || (src != NULL /* originally there was a regex*/
2289 && (*src == NULL /* and either it was NULL */
2290 || strcmp(*src, reg) != 0))) /* or it has been changed */
2292 if (dst != NULL)
2294 SETPTR(*dst, g_strdup(reg));
2295 changed = TRUE;
2298 return changed;
2302 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2304 guint cmdindex, cmd;
2305 gboolean changed = FALSE;
2307 if (response == GTK_RESPONSE_ACCEPT)
2309 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2310 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2311 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2312 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2313 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2314 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2315 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2316 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2318 return changed;
2322 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2324 BuildDestination menu_dst;
2326 if (ft != NULL)
2328 menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds);
2329 menu_dst.fileregexstr = &(ft->priv->projerror_regex_string);
2331 else
2333 menu_dst.dst[GEANY_GBG_FT] = NULL;
2334 menu_dst.fileregexstr = NULL;
2336 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2337 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2338 menu_dst.nonfileregexstr = &regex_proj;
2340 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2344 static void show_build_commands_dialog(void)
2346 GtkWidget *dialog, *table, *vbox;
2347 GeanyDocument *doc = document_get_current();
2348 GeanyFiletype *ft = NULL;
2349 const gchar *title = _("Set Build Commands");
2350 gint response;
2351 BuildTableData table_data;
2352 BuildDestination prefdsts;
2354 if (doc != NULL)
2355 ft = doc->file_type;
2356 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2357 GTK_DIALOG_DESTROY_WITH_PARENT,
2358 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2359 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2360 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2361 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2362 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2363 gtk_widget_show_all(dialog);
2364 /* run modally to prevent user changing idx filetype */
2365 response = gtk_dialog_run(GTK_DIALOG(dialog));
2367 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2368 if (ft != NULL)
2370 prefdsts.dst[GEANY_GBG_FT] = &(ft->priv->homefilecmds);
2371 prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string);
2372 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds);
2374 else
2376 prefdsts.dst[GEANY_GBG_FT] = NULL;
2377 prefdsts.fileregexstr = NULL;
2378 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2380 prefdsts.nonfileregexstr = &regex_pref;
2381 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2382 filetypes_save_commands(ft);
2383 build_free_fields(table_data);
2385 build_menu_update(doc);
2386 gtk_widget_destroy(dialog);
2390 /* Creates the relevant build menu if necessary. */
2391 BuildMenuItems *build_get_menu_items(gint filetype_idx)
2393 BuildMenuItems *items;
2395 items = &menu_items;
2396 if (items->menu == NULL)
2397 create_build_menu(items);
2398 return items;
2402 /*----------------------------------------------------------
2404 * Load and store configuration
2406 * ---------------------------------------------------------*/
2407 static const gchar *build_grp_name = "build-menu";
2409 /* config format for build-menu group is prefix_gg_nn_xx=value
2410 * where gg = FT, NF, EX for the command group
2411 * nn = 2 digit command number
2412 * xx = LB for label, CM for command and WD for working dir */
2413 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2414 static const gchar *fixedkey="xx_xx_xx";
2416 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2417 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2418 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2420 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2421 gchar *prefix, gboolean loc)
2423 guint cmd;
2424 gsize prefixlen; /* NOTE prefixlen used in macros above */
2425 GeanyBuildCommand *dstcmd;
2426 gchar *key;
2427 static gchar cmdbuf[3] = " ";
2429 if (*dst == NULL)
2430 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2431 dstcmd = *dst;
2432 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2433 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2434 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2436 gchar *label;
2437 if (cmd >= 100)
2438 break; /* ensure no buffer overflow */
2439 sprintf(cmdbuf, "%02u", cmd);
2440 set_key_grp(key, groups[grp]);
2441 set_key_cmd(key, cmdbuf);
2442 set_key_fld(key, "LB");
2443 if (loc)
2444 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2445 else
2446 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2447 if (label != NULL)
2449 dstcmd[cmd].exists = TRUE;
2450 SETPTR(dstcmd[cmd].label, label);
2451 set_key_fld(key,"CM");
2452 SETPTR(dstcmd[cmd].command,
2453 g_key_file_get_string(config, build_grp_name, key, NULL));
2454 set_key_fld(key,"WD");
2455 SETPTR(dstcmd[cmd].working_dir,
2456 g_key_file_get_string(config, build_grp_name, key, NULL));
2458 else dstcmd[cmd].exists = FALSE;
2460 g_free(key);
2464 /* for the specified source load new format build menu items or try to make some sense of
2465 * old format setings, not done perfectly but better than ignoring them */
2466 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2468 GeanyFiletype *ft;
2469 GeanyProject *pj;
2470 gchar **ftlist;
2471 gchar *value, *basedir, *makebasedir;
2472 gboolean bvalue = FALSE;
2474 if (g_key_file_has_group(config, build_grp_name))
2476 switch (src)
2478 case GEANY_BCS_FT:
2479 ft = (GeanyFiletype*)p;
2480 if (ft == NULL)
2481 return;
2482 build_load_menu_grp(config, &(ft->priv->filecmds), GEANY_GBG_FT, NULL, TRUE);
2483 build_load_menu_grp(config, &(ft->priv->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2484 build_load_menu_grp(config, &(ft->priv->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2485 SETPTR(ft->error_regex_string,
2486 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2487 break;
2488 case GEANY_BCS_HOME_FT:
2489 ft = (GeanyFiletype*)p;
2490 if (ft == NULL)
2491 return;
2492 build_load_menu_grp(config, &(ft->priv->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2493 build_load_menu_grp(config, &(ft->priv->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2494 SETPTR(ft->priv->homeerror_regex_string,
2495 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2496 break;
2497 case GEANY_BCS_PREF:
2498 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
2499 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
2500 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2501 break;
2502 case GEANY_BCS_PROJ:
2503 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
2504 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
2505 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2506 pj = (GeanyProject*)p;
2507 if (p == NULL)
2508 return;
2509 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2510 if (ftlist != NULL)
2512 gchar **ftname;
2513 if (pj->priv->build_filetypes_list == NULL)
2514 pj->priv->build_filetypes_list = g_ptr_array_new();
2515 g_ptr_array_set_size(pj->priv->build_filetypes_list, 0);
2516 for (ftname = ftlist; *ftname != NULL; ++ftname)
2518 ft = filetypes_lookup_by_name(*ftname);
2519 if (ft != NULL)
2521 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2522 g_ptr_array_add(pj->priv->build_filetypes_list, ft);
2523 SETPTR(ft->priv->projerror_regex_string,
2524 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2525 g_free(regkey);
2526 build_load_menu_grp(config, &(ft->priv->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2527 build_load_menu_grp(config, &(ft->priv->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2530 g_free(ftlist);
2532 break;
2533 default: /* defaults don't load from config, see build_init */
2534 break;
2538 /* load old [build_settings] values if there is no value defined by [build-menu] */
2540 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2541 /* TODO: rewrite as function */
2542 #define ASSIGNIF(type, id, string, value) \
2543 do { \
2544 gchar *ASSIGNF__value = (value); \
2545 if (!EMPTY(ASSIGNF__value) && ! type[GBO_TO_CMD(id)].exists) { \
2546 type[GBO_TO_CMD(id)].exists = TRUE; \
2547 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(string)); \
2548 SETPTR(type[GBO_TO_CMD(id)].command, ASSIGNF__value); \
2549 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL); \
2550 type[GBO_TO_CMD(id)].old = TRUE; \
2551 } else \
2552 g_free(ASSIGNF__value); \
2553 } while (0)
2555 switch (src)
2557 case GEANY_BCS_FT:
2558 ft = (GeanyFiletype*)p;
2559 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2560 if (value != NULL)
2562 if (ft->priv->filecmds == NULL)
2563 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2564 ASSIGNIF(ft->priv->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2566 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2567 if (value != NULL)
2569 if (ft->priv->filecmds == NULL)
2570 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2571 ASSIGNIF(ft->priv->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2573 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2574 if (value != NULL)
2576 if (ft->priv->execcmds == NULL)
2577 ft->priv->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2578 ASSIGNIF(ft->priv->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2580 if (ft->error_regex_string == NULL)
2581 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2582 break;
2583 case GEANY_BCS_PROJ:
2584 if (non_ft_pref == NULL)
2585 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2586 basedir = project_get_base_path();
2587 if (basedir == NULL)
2588 basedir = g_strdup("%d");
2589 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2590 if (bvalue)
2591 makebasedir = g_strdup(basedir);
2592 else
2593 makebasedir = g_strdup("%d");
2594 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
2595 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
2596 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
2597 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2598 if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
2599 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
2600 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2601 if (!EMPTY(value))
2603 if (exec_proj == NULL)
2604 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2605 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2607 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2608 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2609 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
2610 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
2611 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2614 g_free(makebasedir);
2615 g_free(basedir);
2616 break;
2617 case GEANY_BCS_PREF:
2618 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2619 if (value != NULL)
2621 if (non_ft_pref == NULL)
2622 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2623 ASSIGNIF(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
2624 g_strdup_printf("%s ", value));
2625 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
2626 g_strdup_printf("%s %%e.o",value));
2627 ASSIGNIF(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2629 break;
2630 default:
2631 break;
2636 static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2638 guint cmd;
2639 gsize prefixlen; /* NOTE prefixlen used in macros above */
2640 gchar *key;
2641 guint count = 0;
2642 enum GeanyBuildCmdEntries i;
2644 if (src == NULL)
2645 return 0;
2646 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2647 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2648 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2650 if (src[cmd].exists) ++count;
2651 if (src[cmd].changed)
2653 static gchar cmdbuf[4] = " ";
2654 if (cmd >= 100)
2655 break; /* ensure no buffer overflow */
2656 sprintf(cmdbuf, "%02u", cmd);
2657 set_key_grp(key, groups[grp]);
2658 set_key_cmd(key, cmdbuf);
2659 if (src[cmd].exists)
2661 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2663 set_key_fld(key, config_keys[i]);
2664 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2667 else
2669 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2671 set_key_fld(key, config_keys[i]);
2672 g_key_file_remove_key(config, build_grp_name, key, NULL);
2677 g_free(key);
2678 return count;
2682 typedef struct ForEachData
2684 GKeyFile *config;
2685 GPtrArray *ft_names;
2686 } ForEachData;
2689 static void foreach_project_filetype(gpointer data, gpointer user_data)
2691 GeanyFiletype *ft = data;
2692 ForEachData *d = user_data;
2693 guint i = 0;
2694 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2696 i += build_save_menu_grp(d->config, ft->priv->projfilecmds, GEANY_GBG_FT, ft->name);
2697 i += build_save_menu_grp(d->config, ft->priv->projexeccmds, GEANY_GBG_EXEC, ft->name);
2698 if (!EMPTY(ft->priv->projerror_regex_string))
2700 g_key_file_set_string(d->config, build_grp_name, regkey, ft->priv->projerror_regex_string);
2701 i++;
2703 else
2704 g_key_file_remove_key(d->config, build_grp_name, regkey, NULL);
2705 g_free(regkey);
2706 if (i > 0)
2707 g_ptr_array_add(d->ft_names, ft->name);
2711 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2712 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2714 GeanyFiletype *ft;
2715 GeanyProject *pj;
2716 ForEachData data;
2718 switch (src)
2720 case GEANY_BCS_HOME_FT:
2721 ft = (GeanyFiletype*)ptr;
2722 if (ft == NULL)
2723 return;
2724 build_save_menu_grp(config, ft->priv->homefilecmds, GEANY_GBG_FT, NULL);
2725 build_save_menu_grp(config, ft->priv->homeexeccmds, GEANY_GBG_EXEC, NULL);
2726 if (!EMPTY(ft->priv->homeerror_regex_string))
2727 g_key_file_set_string(config, build_grp_name, "error_regex", ft->priv->homeerror_regex_string);
2728 else
2729 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2730 break;
2731 case GEANY_BCS_PREF:
2732 build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
2733 build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
2734 if (!EMPTY(regex_pref))
2735 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2736 else
2737 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2738 break;
2739 case GEANY_BCS_PROJ:
2740 pj = (GeanyProject*)ptr;
2741 build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
2742 build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
2743 if (!EMPTY(regex_proj))
2744 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2745 else
2746 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2747 if (pj->priv->build_filetypes_list != NULL)
2749 data.config = config;
2750 data.ft_names = g_ptr_array_new();
2751 g_ptr_array_foreach(pj->priv->build_filetypes_list, foreach_project_filetype, (gpointer)(&data));
2752 if (data.ft_names->pdata != NULL)
2753 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2754 (const gchar**)(data.ft_names->pdata), data.ft_names->len);
2755 else
2756 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2757 g_ptr_array_free(data.ft_names, TRUE);
2759 break;
2760 default: /* defaults and GEANY_BCS_FT can't save */
2761 break;
2766 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2767 void build_set_group_count(GeanyBuildGroup grp, gint count)
2769 guint i, sum;
2771 g_return_if_fail(count >= 0);
2773 if ((guint) count > build_groups_count[grp])
2774 build_groups_count[grp] = (guint) count;
2775 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2776 sum += build_groups_count[i];
2777 build_items_count = sum;
2781 /** Get the count of commands for the group
2783 * Get the number of commands in the group specified by @a grp.
2785 * @param grp the group of the specified menu item.
2787 * @return a count of the number of commands in the group
2791 guint build_get_group_count(const GeanyBuildGroup grp)
2793 g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
2794 return build_groups_count[grp];
2798 static void on_project_close(void)
2800 /* remove project regexen */
2801 SETPTR(regex_proj, NULL);
2805 static struct
2807 const gchar *label;
2808 const gchar *command;
2809 const gchar *working_dir;
2810 GeanyBuildCommand **ptr;
2811 gint index;
2812 } default_cmds[] = {
2813 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2814 { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2815 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2816 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2817 { NULL, NULL, NULL, NULL, 0 }
2821 void build_init(void)
2823 GtkWidget *item;
2824 GtkWidget *toolmenu;
2825 gint cmdindex;
2827 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2829 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2830 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2831 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2832 run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
2834 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2836 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2837 cmd->exists = TRUE;
2838 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2839 cmd->command = g_strdup(default_cmds[cmdindex].command);
2840 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2843 /* create the toolbar Build item sub menu */
2844 toolmenu = gtk_menu_new();
2845 g_object_ref(toolmenu);
2847 /* build the code */
2848 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2849 gtk_widget_show(item);
2850 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2851 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2852 GBO_TO_POINTER(GEANY_GBO_BUILD));
2853 widgets.toolitem_build = item;
2855 item = gtk_separator_menu_item_new();
2856 gtk_widget_show(item);
2857 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2859 /* build the code with make all */
2860 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2861 gtk_widget_show(item);
2862 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2863 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2864 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
2865 widgets.toolitem_make_all = item;
2867 /* build the code with make custom */
2868 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2869 gtk_widget_show(item);
2870 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2871 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2872 GBO_TO_POINTER(GEANY_GBO_CUSTOM));
2873 widgets.toolitem_make_custom = item;
2875 /* build the code with make object */
2876 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2877 gtk_widget_show(item);
2878 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2879 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2880 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
2881 widgets.toolitem_make_object = item;
2883 item = gtk_separator_menu_item_new();
2884 gtk_widget_show(item);
2885 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2887 /* arguments */
2888 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
2889 gtk_widget_show(item);
2890 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2891 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2892 widgets.toolitem_set_args = item;
2894 /* get toolbar action pointers */
2895 widgets.build_action = toolbar_get_action_by_name("Build");
2896 widgets.compile_action = toolbar_get_action_by_name("Compile");
2897 widgets.run_action = toolbar_get_action_by_name("Run");
2898 widgets.toolmenu = toolmenu;
2899 /* set the submenu to the toolbar item */
2900 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);