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.
27 * Use intermediate pointers for common subexpressions.
28 * Replace defines with enums.
29 * Other TODOs in code. */
39 #include <glib/gstdio.h>
42 # include <sys/types.h>
43 # include <sys/wait.h>
55 #include "msgwindow.h"
56 #include "filetypes.h"
57 #include "keybindings.h"
63 #include "geanymenubuttonaction.h"
65 /* g_spawn_async_with_pipes doesn't work on Windows */
70 /* Number of editor indicators to draw - limited as this can affect performance */
71 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
74 GeanyBuildInfo build_info
= {GEANY_GBG_FT
, 0, 0, NULL
, GEANY_FILETYPES_NONE
, NULL
, 0};
76 static gchar
*current_dir_entered
= NULL
;
78 typedef struct RunInfo
84 static RunInfo
*run_info
;
87 static const gchar RUN_SCRIPT_CMD
[] = "geany_run_script.bat";
89 static const gchar RUN_SCRIPT_CMD
[] = "./geany_run_script.sh";
92 /* pack group (<8) and command (<32) into a user_data pointer */
93 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
94 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
95 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
96 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
98 static gpointer last_toolbutton_action
= GBO_TO_POINTER(GEANY_GBO_BUILD
);
100 static BuildMenuItems menu_items
= {NULL
, {NULL
, NULL
, NULL
, NULL
}};
104 GtkAction
*run_action
;
105 GtkAction
*compile_action
;
106 GtkAction
*build_action
;
109 GtkWidget
*toolitem_build
;
110 GtkWidget
*toolitem_make_all
;
111 GtkWidget
*toolitem_make_custom
;
112 GtkWidget
*toolitem_make_object
;
113 GtkWidget
*toolitem_set_args
;
117 static guint build_groups_count
[GEANY_GBG_COUNT
] = { 3, 4, 2 };
118 static guint build_items_count
= 9;
121 static void build_exit_cb(GPid child_pid
, gint status
, gpointer user_data
);
122 static gboolean
build_iofunc(GIOChannel
*ioc
, GIOCondition cond
, gpointer data
);
124 static gboolean
build_create_shellscript(const gchar
*fname
, const gchar
*cmd
, gboolean autoclose
, GError
**error
);
125 static GPid
build_spawn_cmd(GeanyDocument
*doc
, const gchar
*cmd
, const gchar
*dir
);
126 static void set_stop_button(gboolean stop
);
127 static void run_exit_cb(GPid child_pid
, gint status
, gpointer user_data
);
128 static void on_set_build_commands_activate(GtkWidget
*w
, gpointer u
);
129 static void on_build_next_error(GtkWidget
*menuitem
, gpointer user_data
);
130 static void on_build_previous_error(GtkWidget
*menuitem
, gpointer user_data
);
131 static void kill_process(GPid
*pid
);
132 static void show_build_result_message(gboolean failure
);
133 static void process_build_output_line(const gchar
*line
, gint color
);
134 static void show_build_commands_dialog(void);
135 static void on_build_menu_item(GtkWidget
*w
, gpointer user_data
);
137 void build_finalize(void)
139 g_free(build_info
.dir
);
140 g_free(build_info
.custom_target
);
142 if (menu_items
.menu
!= NULL
&& GTK_IS_WIDGET(menu_items
.menu
))
143 gtk_widget_destroy(menu_items
.menu
);
147 /* note: copied from keybindings.c, may be able to go away */
148 static void add_menu_accel(GeanyKeyGroup
*group
, guint kb_id
,
149 GtkAccelGroup
*accel_group
, GtkWidget
*menuitem
)
151 GeanyKeyBinding
*kb
= keybindings_get_item(group
, kb_id
);
154 gtk_widget_add_accelerator(menuitem
, "activate", accel_group
,
155 kb
->key
, kb
->mods
, GTK_ACCEL_VISIBLE
);
159 /* convenience routines to access parts of GeanyBuildCommand */
160 static gchar
*id_to_str(GeanyBuildCommand
*bc
, gint id
)
166 case GEANY_BC_COMMAND
:
168 case GEANY_BC_WORKING_DIR
:
169 return bc
->working_dir
;
176 static void set_command(GeanyBuildCommand
*bc
, gint id
, gchar
*str
)
181 SETPTR(bc
->label
, str
);
183 case GEANY_BC_COMMAND
:
184 SETPTR(bc
->command
, str
);
186 case GEANY_BC_WORKING_DIR
:
187 SETPTR(bc
->working_dir
, str
);
195 static const gchar
*config_keys
[GEANY_BC_CMDENTRIES_COUNT
] = {
198 "WD" /* working directory */
201 /*-----------------------------------------------------
203 * Execute commands and handle results
205 *-----------------------------------------------------*/
207 /* the various groups of commands not in the filetype struct */
208 static GeanyBuildCommand
*ft_def
= NULL
;
209 static GeanyBuildCommand
*non_ft_proj
= NULL
;
210 static GeanyBuildCommand
*non_ft_pref
= NULL
;
211 static GeanyBuildCommand
*non_ft_def
= NULL
;
212 static GeanyBuildCommand
*exec_proj
= NULL
;
213 static GeanyBuildCommand
*exec_pref
= NULL
;
214 static GeanyBuildCommand
*exec_def
= NULL
;
215 /* and the regexen not in the filetype structure */
216 static gchar
*regex_pref
= NULL
;
217 /* project non-fileregex string */
218 static gchar
*regex_proj
= NULL
;
220 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
221 #ifndef PRINTBUILDCMDS
222 #define PRINTBUILDCMDS FALSE
224 static gboolean printbuildcmds
= PRINTBUILDCMDS
;
227 /* for debug only, print the commands structures in priority order */
228 static void printfcmds(void)
231 GeanyBuildCommand
**cl
[GEANY_GBG_COUNT
][GEANY_BCS_COUNT
] = {
232 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
233 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
234 { &ft_def
, NULL
, NULL
, NULL
, NULL
, NULL
},
235 { &non_ft_def
, NULL
, NULL
, &non_ft_pref
, NULL
, &non_ft_proj
},
236 { &exec_def
, NULL
, NULL
, &exec_pref
, NULL
, &exec_proj
}
238 GeanyFiletype
*ft
= NULL
;
241 enum GeanyBuildCmdEntries n
;
242 gint cc
[GEANY_BCS_COUNT
];
245 doc
= document_get_current();
250 printf("filetype %s\n",ft
->name
);
251 cl
[GEANY_GBG_FT
][GEANY_BCS_FT
] = &(ft
->filecmds
);
252 cl
[GEANY_GBG_FT
][GEANY_BCS_HOME_FT
] = &(ft
->homefilecmds
);
253 cl
[GEANY_GBG_FT
][GEANY_BCS_PROJ
] = &(ft
->projfilecmds
);
254 cl
[GEANY_GBG_NON_FT
][GEANY_BCS_FT
] = &(ft
->ftdefcmds
);
255 cl
[GEANY_GBG_EXEC
][GEANY_BCS_FT
] = &(ft
->execcmds
);
256 cl
[GEANY_GBG_EXEC
][GEANY_BCS_HOME_FT
] = &(ft
->homeexeccmds
);
257 cl
[GEANY_GBG_EXEC
][GEANY_BCS_PROJ_FT
] = &(ft
->projexeccmds
);
259 for (i
= 0; i
< GEANY_BCS_COUNT
; ++i
)
262 for (j
= 0; j
< GEANY_GBG_COUNT
; ++j
)
264 for (k
= 0; k
< build_groups_count
[j
]; ++k
)
265 if (cl
[j
][i
] != NULL
&& *(cl
[j
][i
]) != NULL
&& (*(cl
[j
][i
]))[k
].exists
)
267 for (n
= 0; n
< GEANY_BC_CMDENTRIES_COUNT
; n
++)
269 if ((*(cl
[j
][i
]))[k
].entries
[n
] != NULL
&&
270 (l
= strlen((*(cl
[j
][i
]))[k
].entries
[n
])) > m
)
279 for (i
= 0; i
< GEANY_GBG_COUNT
; ++i
)
281 for (k
= 0; k
< build_groups_count
[i
]; ++k
)
283 for (l
= 0; l
< 2; ++l
)
286 for (j
= 0; j
< GEANY_BCS_COUNT
; ++j
)
288 if (cl
[i
][j
] != NULL
&& *(cl
[i
][j
]) != NULL
&& (*(cl
[i
][j
]))[k
].exists
)
290 for (n
= 0; n
< GEANY_BC_CMDENTRIES_COUNT
; n
++)
292 if ((*(cl
[i
][j
]))[k
].entries
[i
] != NULL
)
293 printf("%c %*.*s",c
,cc
[j
],cc
[j
],(*(cl
[i
][j
]))[k
].entries
[i
]);
295 printf("%c %*.*s",c
,cc
[j
],cc
[j
]," ");
299 printf("%c %*.*s",c
,cc
[j
],cc
[j
]," ");
311 /* macros to save typing and make the logic visible */
312 #define return_cmd_if(src, cmds)\
313 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
316 if (printbuildcmds) \
317 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
318 return &(cmds[cmdindex]); \
321 #define return_ft_cmd_if(src, cmds)\
322 if (ft != NULL && ft->cmds != NULL \
323 && ft->cmds[cmdindex].exists && below>src)\
326 if (printbuildcmds) \
327 printf("cmd[%d,%d]=%d\n",cmdgrp,cmdindex,src); \
328 return &(ft->cmds[cmdindex]); \
332 /* get the next lowest command taking priority into account */
333 static GeanyBuildCommand
*get_next_build_cmd(GeanyDocument
*doc
, guint cmdgrp
, guint cmdindex
,
334 guint below
, guint
*from
)
336 /* Note: parameter below used in macros above */
337 GeanyFiletype
*ft
= NULL
;
338 guint sink
, *fr
= &sink
;
342 if (cmdgrp
>= GEANY_GBG_COUNT
)
347 doc
= document_get_current();
353 case GEANY_GBG_FT
: /* order proj ft, home ft, ft, defft */
356 return_ft_cmd_if(GEANY_BCS_PROJ
, projfilecmds
);
357 return_ft_cmd_if(GEANY_BCS_PREF
, homefilecmds
);
358 return_ft_cmd_if(GEANY_BCS_FT
, filecmds
);
360 return_cmd_if(GEANY_BCS_DEF
, ft_def
);
362 case GEANY_GBG_NON_FT
: /* order proj, pref, def */
363 return_cmd_if(GEANY_BCS_PROJ
, non_ft_proj
);
364 return_cmd_if(GEANY_BCS_PREF
, non_ft_pref
);
365 return_ft_cmd_if(GEANY_BCS_FT
, ftdefcmds
);
366 return_cmd_if(GEANY_BCS_DEF
, non_ft_def
);
368 case GEANY_GBG_EXEC
: /* order proj, proj ft, pref, home ft, ft, def */
369 return_cmd_if(GEANY_BCS_PROJ
, exec_proj
);
370 return_ft_cmd_if(GEANY_BCS_PROJ_FT
, projexeccmds
);
371 return_cmd_if(GEANY_BCS_PREF
, exec_pref
);
372 return_ft_cmd_if(GEANY_BCS_FT
, homeexeccmds
);
373 return_ft_cmd_if(GEANY_BCS_FT
, execcmds
);
374 return_cmd_if(GEANY_BCS_DEF
, exec_def
);
383 /* shortcut to start looking at the top */
384 static GeanyBuildCommand
*get_build_cmd(GeanyDocument
*doc
, guint grp
, guint cmdindex
, guint
*from
)
386 return get_next_build_cmd(doc
, grp
, cmdindex
, GEANY_BCS_COUNT
, from
);
390 #define return_nonblank_regex(src, ptr)\
392 { *fr = (src); return &(ptr); }
395 /* like get_build_cmd, but for regexen, used by filetypes */
396 gchar
**build_get_regex(GeanyBuildGroup grp
, GeanyFiletype
*ft
, guint
*from
)
398 guint sink
, *fr
= &sink
;
402 if (grp
== GEANY_GBG_FT
)
406 GeanyDocument
*doc
= document_get_current();
412 return_nonblank_regex(GEANY_BCS_PROJ
, ft
->projerror_regex_string
);
413 return_nonblank_regex(GEANY_BCS_HOME_FT
, ft
->homeerror_regex_string
);
414 return_nonblank_regex(GEANY_BCS_FT
, ft
->error_regex_string
);
416 else if (grp
== GEANY_GBG_NON_FT
)
418 return_nonblank_regex(GEANY_BCS_PROJ
, regex_proj
);
419 return_nonblank_regex(GEANY_BCS_PREF
, regex_pref
);
425 static GeanyBuildCommand
**get_build_group_pointer(const GeanyBuildSource src
, const GeanyBuildGroup grp
)
428 GeanyFiletype
*ft
= NULL
;
433 if ((doc
= document_get_current()) == NULL
)
435 if ((ft
= doc
->file_type
) == NULL
)
439 case GEANY_BCS_DEF
: return &(ft
->ftdefcmds
);
440 case GEANY_BCS_FT
: return &(ft
->filecmds
);
441 case GEANY_BCS_HOME_FT
: return &(ft
->homefilecmds
);
442 case GEANY_BCS_PREF
: return &(ft
->homefilecmds
);
443 case GEANY_BCS_PROJ
: return &(ft
->projfilecmds
);
444 default: return NULL
;
447 case GEANY_GBG_NON_FT
:
450 case GEANY_BCS_DEF
: return &(non_ft_def
);
451 case GEANY_BCS_PREF
: return &(non_ft_pref
);
452 case GEANY_BCS_PROJ
: return &(non_ft_proj
);
453 default: return NULL
;
457 if ((doc
= document_get_current()) != NULL
)
461 case GEANY_BCS_DEF
: return &(exec_def
);
462 case GEANY_BCS_FT
: return ft
? &(ft
->execcmds
): NULL
;
463 case GEANY_BCS_HOME_FT
: return ft
? &(ft
->homeexeccmds
): NULL
;
464 case GEANY_BCS_PROJ_FT
: return ft
? &(ft
->projexeccmds
): NULL
;
465 case GEANY_BCS_PREF
: return &(exec_pref
);
466 case GEANY_BCS_PROJ
: return &(exec_proj
);
467 default: return NULL
;
476 /* get pointer to the command group array */
477 static GeanyBuildCommand
*get_build_group(const GeanyBuildSource src
, const GeanyBuildGroup grp
)
479 GeanyBuildCommand
**g
= get_build_group_pointer(src
, grp
);
480 if (g
== NULL
) return NULL
;
485 /** Remove the specified Build menu item.
487 * Makes the specified menu item configuration no longer exist. This
488 * is different to setting fields to blank because the menu item
489 * will be deleted from the configuration file on saving
490 * (except the system filetypes settings @see Build Menu Configuration
491 * section of the Manual).
493 * @param src the source of the menu item to remove.
494 * @param grp the group of the command to remove.
495 * @param cmd the index (from 0) of the command within the group. A negative
496 * value will remove the whole group.
498 * If any parameter is out of range does nothing.
503 void build_remove_menu_item(const GeanyBuildSource src
, const GeanyBuildGroup grp
, const gint cmd
)
505 GeanyBuildCommand
*bc
;
508 bc
= get_build_group(src
, grp
);
513 for (i
= 0; i
< build_groups_count
[grp
]; ++i
)
514 bc
[i
].exists
= FALSE
;
516 else if ((guint
) cmd
< build_groups_count
[grp
])
517 bc
[cmd
].exists
= FALSE
;
521 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
523 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
524 * hidden by higher priority commands.
526 * @param src the source of the specified menu item.
527 * @param grp the group of the specified menu item.
528 * @param cmd the index of the command within the group.
530 * @return a pointer to the @a GeanyBuildCommand structure or @a NULL if it doesn't exist.
531 * This is a pointer to an internal structure and must not be freed.
533 * @see build_menu_update
535 GeanyBuildCommand
*build_get_menu_item(GeanyBuildSource src
, GeanyBuildGroup grp
, guint cmd
)
537 GeanyBuildCommand
*bc
;
539 g_return_val_if_fail(src
< GEANY_BCS_COUNT
, NULL
);
540 g_return_val_if_fail(grp
< GEANY_GBG_COUNT
, NULL
);
541 g_return_val_if_fail(cmd
< build_groups_count
[grp
], NULL
);
543 bc
= get_build_group(src
, grp
);
550 /** Get the string for the menu item field.
552 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
553 * that the menu item will use if activated.
555 * @param grp the group of the specified menu item.
556 * @param cmd the index of the command within the group.
557 * @param fld the field to return
559 * @return a pointer to the constant string or @a NULL if it doesn't exist.
560 * This is a pointer to an internal structure and must not be freed.
563 const gchar
*build_get_current_menu_item(const GeanyBuildGroup grp
, const guint cmd
,
564 const GeanyBuildCmdEntries fld
)
566 GeanyBuildCommand
*c
;
569 g_return_val_if_fail(grp
< GEANY_GBG_COUNT
, NULL
);
570 g_return_val_if_fail(fld
< GEANY_BC_CMDENTRIES_COUNT
, NULL
);
571 g_return_val_if_fail(cmd
< build_groups_count
[grp
], NULL
);
573 c
= get_build_cmd(NULL
, grp
, cmd
, NULL
);
574 if (c
== NULL
) return NULL
;
577 case GEANY_BC_COMMAND
:
583 case GEANY_BC_WORKING_DIR
:
584 str
= c
->working_dir
;
592 /** Set the string for the menu item field.
594 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
596 * @param src the source of the menu item
597 * @param grp the group of the specified menu item.
598 * @param cmd the index of the menu item within the group.
599 * @param fld the field in the menu item command to set
600 * @param val the value to set the field to, is copied
604 void build_set_menu_item(const GeanyBuildSource src
, const GeanyBuildGroup grp
,
605 const guint cmd
, const GeanyBuildCmdEntries fld
, const gchar
*val
)
607 GeanyBuildCommand
**g
;
609 g_return_if_fail(src
< GEANY_BCS_COUNT
);
610 g_return_if_fail(grp
< GEANY_GBG_COUNT
);
611 g_return_if_fail(fld
< GEANY_BC_CMDENTRIES_COUNT
);
612 g_return_if_fail(cmd
< build_groups_count
[grp
]);
614 g
= get_build_group_pointer(src
, grp
);
615 if (g
== NULL
) return;
618 *g
= g_new0(GeanyBuildCommand
, build_groups_count
[grp
]);
622 case GEANY_BC_COMMAND
:
623 SETPTR((*g
)[cmd
].command
, g_strdup(val
));
624 (*g
)[cmd
].exists
= TRUE
;
627 SETPTR((*g
)[cmd
].label
, g_strdup(val
));
628 (*g
)[cmd
].exists
= TRUE
;
630 case GEANY_BC_WORKING_DIR
:
631 SETPTR((*g
)[cmd
].working_dir
, g_strdup(val
));
632 (*g
)[cmd
].exists
= TRUE
;
637 build_menu_update(NULL
);
640 /** Set the string for the menu item field.
642 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
644 * @param grp the group of the specified menu item.
645 * @param cmd the index of the command within the group.
649 void build_activate_menu_item(const GeanyBuildGroup grp
, const guint cmd
)
651 on_build_menu_item(NULL
, GRP_CMD_TO_POINTER(grp
, cmd
));
655 /* Clear all error indicators in all documents. */
656 static void clear_all_errors(void)
662 editor_indicator_clear_errors(documents
[i
]->editor
);
668 static void parse_build_output(const gchar
**output
, gint status
)
671 gchar
*line
, **lines
;
673 for (x
= 0; x
< 2; x
++)
677 lines
= g_strsplit_set(output
[x
], "\r\n", -1);
678 len
= g_strv_length(lines
);
680 for (i
= 0; i
< len
; i
++)
685 while (*line
!= '\0')
686 { /* replace any control characters in the output */
691 process_build_output_line(lines
[i
], COLOR_BLACK
);
698 show_build_result_message(status
!= 0);
702 /* enable build items again */
703 build_menu_update(NULL
);
708 /* Replaces occurences of %e and %p with the appropriate filenames,
709 * %d and %p replacements should be in UTF8 */
710 static gchar
*build_replace_placeholder(const GeanyDocument
*doc
, const gchar
*src
)
713 gchar
*filename
= NULL
;
715 gchar
*executable
= NULL
;
716 gchar
*ret_str
; /* to be freed when not in use anymore */
718 stack
= g_string_new(src
);
719 if (doc
!= NULL
&& doc
->file_name
!= NULL
)
721 filename
= utils_get_utf8_from_locale(doc
->file_name
);
723 /* replace %f with the filename (including extension) */
724 replacement
= g_path_get_basename(filename
);
725 utils_string_replace_all(stack
, "%f", replacement
);
728 /* replace %d with the absolute path of the dir of the current file */
729 replacement
= g_path_get_dirname(filename
);
730 utils_string_replace_all(stack
, "%d", replacement
);
733 /* replace %e with the filename (excluding extension) */
734 executable
= utils_remove_ext_from_filename(filename
);
735 replacement
= g_path_get_basename(executable
);
736 utils_string_replace_all(stack
, "%e", replacement
);
740 /* replace %p with the current project's (absolute) base directory */
741 replacement
= NULL
; /* prevent double free if no replacement found */
744 replacement
= project_get_base_path();
746 else if (strstr(stack
->str
, "%p"))
747 { /* fall back to %d */
748 ui_set_statusbar(FALSE
, _("failed to substitute %%p, no project active"));
749 if (doc
!= NULL
&& filename
!= NULL
)
750 replacement
= g_path_get_dirname(filename
);
753 utils_string_replace_all(stack
, "%p", replacement
);
756 ret_str
= utils_get_utf8_from_locale(stack
->str
);
759 g_string_free(stack
, TRUE
);
761 return ret_str
; /* don't forget to free src also if needed */
765 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
766 * idx document directory */
767 static GPid
build_spawn_cmd(GeanyDocument
*doc
, const gchar
*cmd
, const gchar
*dir
)
769 GError
*error
= NULL
;
772 gchar
*utf8_working_dir
;
774 gchar
*utf8_cmd_string
;
783 if (!((doc
!= NULL
&& NZV(doc
->file_name
)) || NZV(dir
)))
785 geany_debug("Failed to run command with no working directory");
786 ui_set_statusbar(TRUE
, _("Process failed, no working directory"));
791 SETPTR(current_dir_entered
, NULL
);
793 cmd_string
= g_strdup(cmd
);
796 argv
= g_strsplit(cmd_string
, " ", 0);
798 argv
= g_new0(gchar
*, 4);
799 argv
[0] = g_strdup("/bin/sh");
800 argv
[1] = g_strdup("-c");
801 argv
[2] = cmd_string
;
805 utf8_cmd_string
= utils_get_utf8_from_locale(cmd_string
);
806 utf8_working_dir
= NZV(dir
) ? g_strdup(dir
) : g_path_get_dirname(doc
->file_name
);
807 working_dir
= utils_get_locale_from_utf8(utf8_working_dir
);
809 gtk_list_store_clear(msgwindow
.store_compiler
);
810 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
811 msgwin_compiler_add(COLOR_BLUE
, _("%s (in directory: %s)"), utf8_cmd_string
, utf8_working_dir
);
812 g_free(utf8_working_dir
);
813 g_free(utf8_cmd_string
);
815 /* set the build info for the message window */
816 g_free(build_info
.dir
);
817 build_info
.dir
= g_strdup(working_dir
);
818 build_info
.file_type_id
= (doc
== NULL
) ? GEANY_FILETYPES_NONE
: doc
->file_type
->id
;
819 build_info
.message_count
= 0;
822 if (! utils_spawn_sync(working_dir
, argv
, NULL
, G_SPAWN_SEARCH_PATH
,
823 NULL
, NULL
, &output
[0], &output
[1], &status
, &error
))
825 if (! g_spawn_async_with_pipes(working_dir
, argv
, NULL
,
826 G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
, NULL
, NULL
,
827 &(build_info
.pid
), NULL
, &stdout_fd
, &stderr_fd
, &error
))
830 geany_debug("build command spawning failed: %s", error
->message
);
831 ui_set_statusbar(TRUE
, _("Process failed (%s)"), error
->message
);
840 parse_build_output((const gchar
**) output
, status
);
844 if (build_info
.pid
> 0)
846 g_child_watch_add(build_info
.pid
, (GChildWatchFunc
) build_exit_cb
, NULL
);
847 build_menu_update(doc
);
848 ui_progress_bar_start(NULL
);
851 /* use GIOChannels to monitor stdout and stderr */
852 utils_set_up_io_channel(stdout_fd
, G_IO_IN
| G_IO_PRI
| G_IO_ERR
| G_IO_HUP
| G_IO_NVAL
,
853 TRUE
, build_iofunc
, GINT_TO_POINTER(0));
854 utils_set_up_io_channel(stderr_fd
, G_IO_IN
| G_IO_PRI
| G_IO_ERR
| G_IO_HUP
| G_IO_NVAL
,
855 TRUE
, build_iofunc
, GINT_TO_POINTER(1));
861 return build_info
.pid
;
865 /* Returns: NULL if there was an error, or the working directory the script was created in.
866 * vte_cmd_nonscript is the location of a string which is filled with the command to be used
867 * when vc->skip_run_script is set, otherwise it will be set to NULL */
868 static gchar
*prepare_run_script(GeanyDocument
*doc
, gchar
**vte_cmd_nonscript
, guint cmdindex
)
870 gchar
*locale_filename
= NULL
;
871 GeanyBuildCommand
*cmd
= NULL
;
872 gchar
*executable
= NULL
;
873 gchar
*working_dir
= NULL
;
874 const gchar
*cmd_working_dir
;
875 gboolean autoclose
= FALSE
;
876 gboolean result
= FALSE
;
879 GError
*error
= NULL
;
881 if (vte_cmd_nonscript
!= NULL
)
882 *vte_cmd_nonscript
= NULL
;
884 locale_filename
= utils_get_locale_from_utf8(doc
->file_name
);
886 cmd
= get_build_cmd(doc
, GEANY_GBG_EXEC
, cmdindex
, NULL
);
888 cmd_string
= build_replace_placeholder(doc
, cmd
->command
);
889 cmd_working_dir
= cmd
->working_dir
;
890 if (! NZV(cmd_working_dir
))
891 cmd_working_dir
= "%d";
892 working_dir
= build_replace_placeholder(doc
, cmd_working_dir
); /* in utf-8 */
894 /* only test whether working dir exists, don't change it or else Windows support will break
895 * (gspawn-win32-helper.exe is used by GLib and must be in $PATH which means current working
896 * dir where geany.exe was started from, so we can't change it) */
897 if (!NZV(working_dir
) || ! g_file_test(working_dir
, G_FILE_TEST_EXISTS
) ||
898 ! g_file_test(working_dir
, G_FILE_TEST_IS_DIR
))
900 ui_set_statusbar(TRUE
, _("Failed to change the working directory to \"%s\""),
901 NZV(working_dir
) ? working_dir
: "<NULL>" );
902 utils_free_pointers(2, cmd_string
, working_dir
, NULL
);
907 if (vte_info
.have_vte
&& vc
->run_in_vte
)
909 if (vc
->skip_run_script
)
911 if (vte_cmd_nonscript
!= NULL
)
912 *vte_cmd_nonscript
= cmd_string
;
914 utils_free_pointers(2, executable
, locale_filename
, NULL
);
918 /* don't wait for user input at the end of script when we are running in VTE */
923 /* RUN_SCRIPT_CMD should be ok in UTF8 without converting in locale because it
924 * contains no umlauts */
925 tmp
= g_build_filename(working_dir
, RUN_SCRIPT_CMD
, NULL
);
926 result
= build_create_shellscript(tmp
, cmd_string
, autoclose
, &error
);
929 ui_set_statusbar(TRUE
, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
930 NZV(cmd_string
) ? cmd_string
: NULL
, error
->message
);
934 utils_free_pointers(4, cmd_string
, tmp
, executable
, locale_filename
, NULL
);
944 static GPid
build_run_cmd(GeanyDocument
*doc
, guint cmdindex
)
947 gchar
*vte_cmd_nonscript
= NULL
;
948 GError
*error
= NULL
;
950 if (doc
== NULL
|| doc
->file_name
== NULL
)
953 working_dir
= prepare_run_script(doc
, &vte_cmd_nonscript
, cmdindex
);
954 if (working_dir
== NULL
)
957 run_info
[cmdindex
].file_type_id
= doc
->file_type
->id
;
960 if (vte_info
.have_vte
&& vc
->run_in_vte
)
964 if (vc
->skip_run_script
)
966 SETPTR(vte_cmd_nonscript
, utils_get_utf8_from_locale(vte_cmd_nonscript
));
967 vte_cmd
= g_strconcat(vte_cmd_nonscript
, "\n", NULL
);
968 g_free(vte_cmd_nonscript
);
971 vte_cmd
= g_strconcat("\n/bin/sh ", RUN_SCRIPT_CMD
, "\n", NULL
);
973 /* change into current directory if it is not done by default */
974 if (! vc
->follow_path
)
976 /* we need to convert the working_dir back to UTF-8 because the VTE expects it */
977 gchar
*utf8_working_dir
= utils_get_utf8_from_locale(working_dir
);
978 vte_cwd(utf8_working_dir
, TRUE
);
979 g_free(utf8_working_dir
);
981 if (! vte_send_cmd(vte_cmd
))
983 ui_set_statusbar(FALSE
,
984 _("Could not execute the file in the VTE because it probably contains a command."));
985 geany_debug("Could not execute the file in the VTE because it probably contains a command.");
989 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_VTE
);
990 gtk_widget_grab_focus(vc
->vte
);
991 msgwin_show_hide(TRUE
);
993 run_info
[cmdindex
].pid
= 1;
1000 gchar
*locale_term_cmd
= NULL
;
1001 gchar
**term_argv
= NULL
;
1002 guint term_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 term_argv
= g_strsplit(locale_term_cmd
, " ", -1);
1009 term_argv_len
= g_strv_length(term_argv
);
1011 /* check that terminal exists (to prevent misleading error messages) */
1012 if (term_argv
[0] != NULL
)
1014 gchar
*tmp
= term_argv
[0];
1015 /* g_find_program_in_path checks whether tmp exists and is executable */
1016 term_argv
[0] = g_find_program_in_path(tmp
);
1019 if (term_argv
[0] == NULL
)
1021 ui_set_statusbar(TRUE
,
1022 _("Could not find terminal \"%s\" "
1023 "(check path for Terminal tool setting in Preferences)"), tool_prefs
.term_cmd
);
1024 run_info
[cmdindex
].pid
= (GPid
) 1;
1028 argv
= g_new0(gchar
*, term_argv_len
+ 3);
1029 for (i
= 0; i
< term_argv_len
; i
++)
1031 argv
[i
] = g_strdup(term_argv
[i
]);
1034 /* command line arguments only for cmd.exe */
1035 if (strstr(argv
[0], "cmd.exe") != NULL
)
1037 argv
[term_argv_len
] = g_strdup("/Q /C");
1038 argv
[term_argv_len
+ 1] = g_strdup(RUN_SCRIPT_CMD
);
1042 argv
[term_argv_len
] = g_strdup(RUN_SCRIPT_CMD
);
1043 argv
[term_argv_len
+ 1] = NULL
;
1046 argv
[term_argv_len
] = g_strdup("-e");
1047 argv
[term_argv_len
+ 1] = g_strconcat("/bin/sh ", RUN_SCRIPT_CMD
, NULL
);
1049 argv
[term_argv_len
+ 2] = NULL
;
1051 if (! g_spawn_async(working_dir
, argv
, NULL
, G_SPAWN_DO_NOT_REAP_CHILD
,
1052 NULL
, NULL
, &(run_info
[cmdindex
].pid
), &error
))
1054 geany_debug("g_spawn_async() failed: %s", error
->message
);
1055 ui_set_statusbar(TRUE
, _("Process failed (%s)"), error
->message
);
1056 g_unlink(RUN_SCRIPT_CMD
);
1057 g_error_free(error
);
1059 run_info
[cmdindex
].pid
= (GPid
) 0;
1062 if (run_info
[cmdindex
].pid
!= 0)
1064 g_child_watch_add(run_info
[cmdindex
].pid
, (GChildWatchFunc
) run_exit_cb
,
1065 (gpointer
)&(run_info
[cmdindex
]));
1066 build_menu_update(doc
);
1070 g_strfreev(term_argv
);
1071 g_free(locale_term_cmd
);
1074 g_free(working_dir
);
1075 return run_info
[cmdindex
].pid
;
1079 static void process_build_output_line(const gchar
*str
, gint color
)
1085 msg
= g_strdup(str
);
1095 if (build_parse_make_dir(msg
, &tmp
))
1097 SETPTR(current_dir_entered
, tmp
);
1099 msgwin_parse_compiler_error_line(msg
, current_dir_entered
, &filename
, &line
);
1101 if (line
!= -1 && filename
!= NULL
)
1103 GeanyDocument
*doc
= document_find_by_filename(filename
);
1105 /* limit number of indicators */
1106 if (doc
&& editor_prefs
.use_indicators
&&
1107 build_info
.message_count
< GEANY_BUILD_ERR_HIGHLIGHT_MAX
)
1109 if (line
> 0) /* some compilers, like pdflatex report errors on line 0 */
1110 line
--; /* so only adjust the line number if it is greater than 0 */
1111 editor_indicator_set_on_line(doc
->editor
, GEANY_INDICATOR_ERROR
, line
);
1113 build_info
.message_count
++;
1114 color
= COLOR_RED
; /* error message parsed on the line */
1118 msgwin_compiler_add_string(color
, msg
);
1124 static gboolean
build_iofunc(GIOChannel
*ioc
, GIOCondition cond
, gpointer data
)
1126 if (cond
& (G_IO_IN
| G_IO_PRI
))
1131 while ((st
= g_io_channel_read_line(ioc
, &msg
, NULL
, NULL
, NULL
)) == G_IO_STATUS_NORMAL
&& msg
)
1133 gint color
= (GPOINTER_TO_INT(data
)) ? COLOR_DARK_RED
: COLOR_BLACK
;
1135 process_build_output_line(msg
, color
);
1138 if (st
== G_IO_STATUS_ERROR
|| st
== G_IO_STATUS_EOF
) return FALSE
;
1140 if (cond
& (G_IO_ERR
| G_IO_HUP
| G_IO_NVAL
))
1148 gboolean
build_parse_make_dir(const gchar
*string
, gchar
**prefix
)
1157 if ((pos
= strstr(string
, "Entering directory")) != NULL
)
1162 /* get the start of the path */
1163 pos
= strstr(string
, "/");
1168 input
= g_strdup(pos
);
1170 /* kill the ' at the end of the path */
1171 len
= strlen(input
);
1172 input
[len
- 1] = '\0';
1173 input
= g_realloc(input
, len
); /* shorten by 1 */
1179 if (strstr(string
, "Leaving directory") != NULL
)
1189 static void show_build_result_message(gboolean failure
)
1195 msg
= _("Compilation failed.");
1196 msgwin_compiler_add_string(COLOR_BLUE
, msg
);
1197 /* If msgwindow is hidden, user will want to display it to see the error */
1198 if (! ui_prefs
.msgwindow_visible
)
1200 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
1201 msgwin_show_hide(TRUE
);
1204 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow
.notebook
)) != MSG_COMPILER
)
1205 ui_set_statusbar(FALSE
, "%s", msg
);
1209 msg
= _("Compilation finished successfully.");
1210 msgwin_compiler_add_string(COLOR_BLUE
, msg
);
1211 if (! ui_prefs
.msgwindow_visible
||
1212 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow
.notebook
)) != MSG_COMPILER
)
1213 ui_set_statusbar(FALSE
, "%s", msg
);
1219 static void build_exit_cb(GPid child_pid
, gint status
, gpointer user_data
)
1221 gboolean failure
= FALSE
;
1226 if (WIFEXITED(status
))
1228 if (WEXITSTATUS(status
) != EXIT_SUCCESS
)
1231 else if (WIFSIGNALED(status
))
1233 /* the terminating signal: WTERMSIG (status)); */
1237 { /* any other failure occured */
1241 show_build_result_message(failure
);
1244 g_spawn_close_pid(child_pid
);
1247 /* enable build items again */
1248 build_menu_update(NULL
);
1249 ui_progress_bar_stop();
1254 static void run_exit_cb(GPid child_pid
, gint status
, gpointer user_data
)
1256 RunInfo
*run_info_data
= user_data
;
1258 g_spawn_close_pid(child_pid
);
1260 run_info_data
->pid
= 0;
1261 /* reset the stop button and menu item to the original meaning */
1262 build_menu_update(NULL
);
1266 static void set_file_error_from_errno(GError
**error
, gint err
, const gchar
*prefix
)
1268 g_set_error(error
, G_FILE_ERROR
, g_file_error_from_errno(err
), "%s%s%s",
1269 prefix
? prefix
: "", prefix
? ": " : "", g_strerror(err
));
1273 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1274 * fname is the full file name (including path) for the script to create */
1275 static gboolean
build_create_shellscript(const gchar
*fname
, const gchar
*cmd
, gboolean autoclose
, GError
**error
)
1279 gboolean success
= TRUE
;
1281 gchar
*expanded_cmd
;
1284 fp
= g_fopen(fname
, "w");
1287 set_file_error_from_errno(error
, errno
, "Failed to create file");
1291 /* Expand environment variables like %blah%. */
1292 expanded_cmd
= win32_expand_environment_variables(cmd
);
1293 str
= g_strdup_printf("%s\n\n%s\ndel \"%%0\"\n\npause\n", expanded_cmd
, (autoclose
) ? "" : "pause");
1294 g_free(expanded_cmd
);
1296 str
= g_strdup_printf(
1297 "#!/bin/sh\n\nrm $0\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1298 \n\n%s\n", cmd
, (autoclose
) ? "" :
1299 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1300 "dash\ndummy_var=\"\"\nread dummy_var");
1303 if (fputs(str
, fp
) < 0)
1305 set_file_error_from_errno(error
, errno
, "Failed to write file");
1310 if (fclose(fp
) != 0)
1312 if (error
&& ! *error
) /* don't set error twice */
1313 set_file_error_from_errno(error
, errno
, "Failed to close file");
1321 typedef void Callback(GtkWidget
*w
, gpointer u
);
1323 /* run the command catenating cmd_cat if present */
1324 static void build_command(GeanyDocument
*doc
, GeanyBuildGroup grp
, guint cmd
, gchar
*cmd_cat
)
1327 gchar
*full_command
, *subs_command
;
1328 GeanyBuildCommand
*buildcmd
= get_build_cmd(doc
, grp
, cmd
, NULL
);
1331 if (buildcmd
== NULL
)
1334 cmdstr
= buildcmd
->command
;
1336 if (cmd_cat
!= NULL
)
1339 full_command
= g_strconcat(cmdstr
, cmd_cat
, NULL
);
1341 full_command
= g_strdup(cmd_cat
);
1344 full_command
= cmdstr
;
1346 dir
= build_replace_placeholder(doc
, buildcmd
->working_dir
);
1347 subs_command
= build_replace_placeholder(doc
, full_command
);
1348 build_info
.grp
= grp
;
1349 build_info
.cmd
= cmd
;
1350 build_spawn_cmd(doc
, subs_command
, dir
);
1351 g_free(subs_command
);
1353 if (cmd_cat
!= NULL
)
1354 g_free(full_command
);
1355 build_menu_update(doc
);
1360 /*----------------------------------------------------------------
1362 * Create build menu and handle callbacks (&toolbar callbacks)
1364 *----------------------------------------------------------------*/
1365 static void on_make_custom_input_response(const gchar
*input
)
1367 GeanyDocument
*doc
= document_get_current();
1369 SETPTR(build_info
.custom_target
, g_strdup(input
));
1370 build_command(doc
, GBO_TO_GBG(GEANY_GBO_CUSTOM
), GBO_TO_CMD(GEANY_GBO_CUSTOM
),
1371 build_info
.custom_target
);
1375 static void on_build_menu_item(GtkWidget
*w
, gpointer user_data
)
1377 GeanyDocument
*doc
= document_get_current();
1378 GeanyBuildCommand
*bc
;
1379 guint grp
= GPOINTER_TO_GRP(user_data
);
1380 guint cmd
= GPOINTER_TO_CMD(user_data
);
1382 if (doc
&& doc
->changed
)
1384 if (!document_save_file(doc
, FALSE
))
1387 g_signal_emit_by_name(geany_object
, "build-start");
1389 if (grp
== GEANY_GBG_NON_FT
&& cmd
== GBO_TO_CMD(GEANY_GBO_CUSTOM
))
1391 static GtkWidget
*dialog
= NULL
; /* keep dialog for combo history */
1395 dialog
= dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets
.window
),
1396 _("Enter custom text here, all entered text is appended to the command."),
1397 build_info
.custom_target
, &on_make_custom_input_response
);
1401 gtk_widget_show(dialog
);
1405 else if (grp
== GEANY_GBG_EXEC
)
1407 if (run_info
[cmd
].pid
> (GPid
) 1)
1409 kill_process(&run_info
[cmd
].pid
);
1412 bc
= get_build_cmd(doc
, grp
, cmd
, NULL
);
1413 if (bc
!= NULL
&& strcmp(bc
->command
, "builtin") == 0)
1418 uri
= g_strconcat("file:///", g_path_skip_root(doc
->file_name
), NULL
);
1419 utils_open_browser(uri
);
1424 build_run_cmd(doc
, cmd
);
1427 build_command(doc
, grp
, cmd
, NULL
);
1431 /* group codes for menu items other than the known commands
1432 * value order is important, see the following table for use */
1434 /* the rest in each group */
1435 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1436 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1437 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1439 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1440 /* the fixed items */
1441 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1442 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1443 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1444 #define MENU_DONE (MENU_COMMANDS + 1)
1447 static struct BuildMenuItemSpec
{
1448 const gchar
*stock_id
;
1449 const gint key_binding
;
1450 const guint build_grp
;
1451 const guint build_cmd
;
1452 const gchar
*fix_label
;
1454 } build_menu_specs
[] = {
1455 {GTK_STOCK_CONVERT
, GEANY_KEYS_BUILD_COMPILE
, GBO_TO_GBG(GEANY_GBO_COMPILE
),
1456 GBO_TO_CMD(GEANY_GBO_COMPILE
), NULL
, on_build_menu_item
},
1457 {GEANY_STOCK_BUILD
, GEANY_KEYS_BUILD_LINK
, GBO_TO_GBG(GEANY_GBO_BUILD
),
1458 GBO_TO_CMD(GEANY_GBO_BUILD
), NULL
, on_build_menu_item
},
1459 {NULL
, -1, MENU_FT_REST
,
1460 GBO_TO_CMD(GEANY_GBO_BUILD
) + 1, NULL
, on_build_menu_item
},
1461 {NULL
, -1, MENU_SEPARATOR
,
1462 GBF_SEP_1
, NULL
, NULL
},
1463 {NULL
, GEANY_KEYS_BUILD_MAKE
, GBO_TO_GBG(GEANY_GBO_MAKE_ALL
),
1464 GBO_TO_CMD(GEANY_GBO_MAKE_ALL
), NULL
, on_build_menu_item
},
1465 {NULL
, GEANY_KEYS_BUILD_MAKEOWNTARGET
, GBO_TO_GBG(GEANY_GBO_CUSTOM
),
1466 GBO_TO_CMD(GEANY_GBO_CUSTOM
), NULL
, on_build_menu_item
},
1467 {NULL
, GEANY_KEYS_BUILD_MAKEOBJECT
, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT
),
1468 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
), NULL
, on_build_menu_item
},
1469 {NULL
, -1, MENU_NON_FT_REST
,
1470 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
) + 1, NULL
, on_build_menu_item
},
1471 {NULL
, -1, MENU_SEPARATOR
,
1472 GBF_SEP_2
, NULL
, NULL
},
1473 {GTK_STOCK_GO_DOWN
, GEANY_KEYS_BUILD_NEXTERROR
, MENU_NEXT_ERROR
,
1474 GBF_NEXT_ERROR
, N_("_Next Error"), on_build_next_error
},
1475 {GTK_STOCK_GO_UP
, GEANY_KEYS_BUILD_PREVIOUSERROR
, MENU_PREV_ERROR
,
1476 GBF_PREV_ERROR
, N_("_Previous Error"), on_build_previous_error
},
1477 {NULL
, -1, MENU_SEPARATOR
,
1478 GBF_SEP_3
, NULL
, NULL
},
1479 {GTK_STOCK_EXECUTE
, GEANY_KEYS_BUILD_RUN
, GBO_TO_GBG(GEANY_GBO_EXEC
),
1480 GBO_TO_CMD(GEANY_GBO_EXEC
), NULL
, on_build_menu_item
},
1481 {NULL
, -1, MENU_EXEC_REST
,
1482 GBO_TO_CMD(GEANY_GBO_EXEC
) + 1, NULL
, on_build_menu_item
},
1483 {NULL
, -1, MENU_SEPARATOR
,
1484 GBF_SEP_4
, NULL
, NULL
},
1485 {GTK_STOCK_PREFERENCES
, GEANY_KEYS_BUILD_OPTIONS
, MENU_COMMANDS
,
1486 GBF_COMMANDS
, N_("_Set Build Commands"), on_set_build_commands_activate
},
1487 {NULL
, -1, MENU_DONE
,
1492 static void create_build_menu_item(GtkWidget
*menu
, GeanyKeyGroup
*group
, GtkAccelGroup
*ag
,
1493 struct BuildMenuItemSpec
*bs
, const gchar
*lbl
, guint grp
, guint cmd
)
1495 GtkWidget
*item
= gtk_image_menu_item_new_with_mnemonic(lbl
);
1497 if (bs
->stock_id
!= NULL
)
1499 GtkWidget
*image
= gtk_image_new_from_stock(bs
->stock_id
, GTK_ICON_SIZE_MENU
);
1500 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
), image
);
1502 gtk_widget_show(item
);
1503 if (bs
->key_binding
>= 0)
1504 add_menu_accel(group
, (guint
) bs
->key_binding
, ag
, item
);
1505 gtk_container_add(GTK_CONTAINER(menu
), item
);
1508 g_signal_connect(item
, "activate", G_CALLBACK(bs
->cb
), GRP_CMD_TO_POINTER(grp
,cmd
));
1510 menu_items
.menu_item
[grp
][cmd
] = item
;
1514 static void create_build_menu(BuildMenuItems
*build_menu_items
)
1517 GtkAccelGroup
*accel_group
= gtk_accel_group_new();
1518 GeanyKeyGroup
*keygroup
= keybindings_get_core_group(GEANY_KEY_GROUP_BUILD
);
1521 menu
= gtk_menu_new();
1522 build_menu_items
->menu_item
[GEANY_GBG_FT
] = g_new0(GtkWidget
*, build_groups_count
[GEANY_GBG_FT
]);
1523 build_menu_items
->menu_item
[GEANY_GBG_NON_FT
] = g_new0(GtkWidget
*, build_groups_count
[GEANY_GBG_NON_FT
]);
1524 build_menu_items
->menu_item
[GEANY_GBG_EXEC
] = g_new0(GtkWidget
*, build_groups_count
[GEANY_GBG_EXEC
]);
1525 build_menu_items
->menu_item
[GBG_FIXED
] = g_new0(GtkWidget
*, GBF_COUNT
);
1527 for (i
= 0; build_menu_specs
[i
].build_grp
!= MENU_DONE
; ++i
)
1529 struct BuildMenuItemSpec
*bs
= &(build_menu_specs
[i
]);
1530 if (bs
->build_grp
== MENU_SEPARATOR
)
1532 GtkWidget
*item
= gtk_separator_menu_item_new();
1533 gtk_widget_show(item
);
1534 gtk_container_add(GTK_CONTAINER(menu
), item
);
1535 build_menu_items
->menu_item
[GBG_FIXED
][bs
->build_cmd
] = item
;
1537 else if (bs
->fix_label
!= NULL
)
1539 create_build_menu_item(menu
, keygroup
, accel_group
, bs
, _(bs
->fix_label
),
1540 GBG_FIXED
, bs
->build_cmd
);
1542 else if (bs
->build_grp
>= MENU_FT_REST
&& bs
->build_grp
<= MENU_SEPARATOR
)
1544 guint grp
= bs
->build_grp
- GEANY_GBG_COUNT
;
1545 for (j
= bs
->build_cmd
; j
< build_groups_count
[grp
]; ++j
)
1547 GeanyBuildCommand
*bc
= get_build_cmd(NULL
, grp
, j
, NULL
);
1548 const gchar
*lbl
= (bc
== NULL
) ? "" : bc
->label
;
1549 create_build_menu_item(menu
, keygroup
, accel_group
, bs
, lbl
, grp
, j
);
1554 GeanyBuildCommand
*bc
= get_build_cmd(NULL
, bs
->build_grp
, bs
->build_cmd
, NULL
);
1555 const gchar
*lbl
= (bc
== NULL
) ? "" : bc
->label
;
1556 create_build_menu_item(menu
, keygroup
, accel_group
, bs
, lbl
, bs
->build_grp
, bs
->build_cmd
);
1559 build_menu_items
->menu
= menu
;
1560 gtk_widget_show(menu
);
1561 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_build1")), menu
);
1565 /* portability to various GTK versions needs checking
1566 * conforms to description of gtk_accel_label as child of menu item
1567 * NB 2.16 adds set_label but not yet set_label_mnemonic */
1568 static void geany_menu_item_set_label(GtkWidget
*w
, const gchar
*label
)
1570 GtkWidget
*c
= gtk_bin_get_child(GTK_BIN(w
));
1572 gtk_label_set_text_with_mnemonic(GTK_LABEL(c
), label
);
1576 /* * Update the build menu to reflect changes in configuration or status.
1578 * Sets the labels and number of visible items to match the highest
1579 * priority configured commands. Also sets sensitivity if build commands are
1580 * running and switches executes to stop when commands are running.
1582 * @param doc The current document, if available, to save looking it up.
1583 * If @c NULL it will be looked up.
1585 * Call this after modifying any fields of a GeanyBuildCommand structure.
1587 * @see Build Menu Configuration section of the Manual.
1590 void build_menu_update(GeanyDocument
*doc
)
1592 guint i
, cmdcount
, cmd
, grp
;
1593 gboolean vis
= FALSE
;
1594 gboolean have_path
, build_running
, exec_running
, have_errors
, cmd_sensitivity
;
1595 gboolean can_compile
, can_build
, can_make
, run_sensitivity
= FALSE
, run_running
= FALSE
;
1596 GeanyBuildCommand
*bc
;
1598 if (menu_items
.menu
== NULL
)
1599 create_build_menu(&menu_items
);
1601 doc
= document_get_current();
1602 have_path
= doc
!= NULL
&& doc
->file_name
!= NULL
;
1603 build_running
= build_info
.pid
> (GPid
) 1;
1604 have_errors
= gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow
.store_compiler
), NULL
) > 0;
1605 for (i
= 0; build_menu_specs
[i
].build_grp
!= MENU_DONE
; ++i
)
1607 struct BuildMenuItemSpec
*bs
= &(build_menu_specs
[i
]);
1608 switch (bs
->build_grp
)
1610 case MENU_SEPARATOR
:
1613 gtk_widget_show_all(menu_items
.menu_item
[GBG_FIXED
][bs
->build_cmd
]);
1617 gtk_widget_hide(menu_items
.menu_item
[GBG_FIXED
][bs
->build_cmd
]);
1619 case MENU_NEXT_ERROR
:
1620 case MENU_PREV_ERROR
:
1621 gtk_widget_set_sensitive(menu_items
.menu_item
[GBG_FIXED
][bs
->build_cmd
], have_errors
);
1627 default: /* all configurable commands */
1628 if (bs
->build_grp
>= GEANY_GBG_COUNT
)
1630 grp
= bs
->build_grp
- GEANY_GBG_COUNT
;
1631 cmdcount
= build_groups_count
[grp
];
1635 grp
= bs
->build_grp
;
1636 cmdcount
= bs
->build_cmd
+ 1;
1638 for (cmd
= bs
->build_cmd
; cmd
< cmdcount
; ++cmd
)
1640 GtkWidget
*menu_item
= menu_items
.menu_item
[grp
][cmd
];
1642 bc
= get_build_cmd(doc
, grp
, cmd
, NULL
);
1648 if (grp
< GEANY_GBG_EXEC
)
1651 (grp
== GEANY_GBG_FT
&& bc
!= NULL
&& have_path
&& ! build_running
) ||
1652 (grp
== GEANY_GBG_NON_FT
&& bc
!= NULL
&& ! build_running
);
1653 gtk_widget_set_sensitive(menu_item
, cmd_sensitivity
);
1654 if (bc
!= NULL
&& NZV(label
))
1656 geany_menu_item_set_label(menu_item
, label
);
1657 gtk_widget_show_all(menu_item
);
1661 gtk_widget_hide(menu_item
);
1666 exec_running
= run_info
[cmd
].pid
> (GPid
) 1;
1667 cmd_sensitivity
= (bc
!= NULL
) || exec_running
;
1668 gtk_widget_set_sensitive(menu_item
, cmd_sensitivity
);
1669 if (cmd
== GBO_TO_CMD(GEANY_GBO_EXEC
))
1670 run_sensitivity
= cmd_sensitivity
;
1673 image
= gtk_image_new_from_stock(bs
->stock_id
, GTK_ICON_SIZE_MENU
);
1677 image
= gtk_image_new_from_stock(GTK_STOCK_STOP
, GTK_ICON_SIZE_MENU
);
1679 if (cmd
== GBO_TO_CMD(GEANY_GBO_EXEC
))
1680 run_running
= exec_running
;
1681 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item
), image
);
1682 if (bc
!= NULL
&& NZV(label
))
1684 geany_menu_item_set_label(menu_item
, label
);
1685 gtk_widget_show_all(menu_item
);
1689 gtk_widget_hide(menu_item
);
1695 run_sensitivity
&= (doc
!= NULL
);
1696 can_build
= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_BUILD
), NULL
) != NULL
1697 && have_path
&& ! build_running
;
1698 if (widgets
.toolitem_build
!= NULL
)
1699 gtk_widget_set_sensitive(widgets
.toolitem_build
, can_build
);
1701 if (widgets
.toolitem_make_all
!= NULL
)
1702 gtk_widget_set_sensitive(widgets
.toolitem_make_all
,
1703 (can_make
|= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_MAKE_ALL
), NULL
) != NULL
1704 && ! build_running
));
1705 if (widgets
.toolitem_make_custom
!= NULL
)
1706 gtk_widget_set_sensitive(widgets
.toolitem_make_custom
,
1707 (can_make
|= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_CUSTOM
), NULL
) != NULL
1708 && ! build_running
));
1709 if (widgets
.toolitem_make_object
!= NULL
)
1710 gtk_widget_set_sensitive(widgets
.toolitem_make_object
,
1711 (can_make
|= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
), NULL
) != NULL
1712 && ! build_running
));
1713 if (widgets
.toolitem_set_args
!= NULL
)
1714 gtk_widget_set_sensitive(widgets
.toolitem_set_args
, TRUE
);
1716 can_compile
= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_COMPILE
), NULL
) != NULL
1717 && have_path
&& ! build_running
;
1718 gtk_action_set_sensitive(widgets
.compile_action
, can_compile
);
1719 gtk_action_set_sensitive(widgets
.build_action
, can_make
);
1720 gtk_action_set_sensitive(widgets
.run_action
, run_sensitivity
);
1722 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1723 set_stop_button(run_running
);
1728 /* Call build_menu_update() instead of calling this directly. */
1729 static void set_stop_button(gboolean stop
)
1731 const gchar
*button_stock_id
= NULL
;
1732 GtkToolButton
*run_button
;
1734 run_button
= GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1735 if (run_button
!= NULL
)
1736 button_stock_id
= gtk_tool_button_get_stock_id(run_button
);
1738 if (stop
&& utils_str_equal(button_stock_id
, GTK_STOCK_STOP
))
1740 if (! stop
&& utils_str_equal(button_stock_id
, GTK_STOCK_EXECUTE
))
1743 /* use the run button also as stop button */
1746 if (run_button
!= NULL
)
1747 gtk_tool_button_set_stock_id(run_button
, GTK_STOCK_STOP
);
1751 if (run_button
!= NULL
)
1752 gtk_tool_button_set_stock_id(run_button
, GTK_STOCK_EXECUTE
);
1757 static void on_set_build_commands_activate(GtkWidget
*w
, gpointer u
)
1759 /* For now, just show the project dialog */
1761 project_build_properties();
1763 show_build_commands_dialog();
1767 static void on_toolbutton_build_activate(GtkWidget
*menuitem
, gpointer user_data
)
1769 last_toolbutton_action
= user_data
;
1770 g_object_set(widgets
.build_action
, "tooltip", _("Build the current file"), NULL
);
1771 on_build_menu_item(menuitem
, user_data
);
1775 static void on_toolbutton_make_activate(GtkWidget
*menuitem
, gpointer user_data
)
1779 last_toolbutton_action
= user_data
;
1780 if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_MAKE_ALL
))
1781 msg
= _("Build the current file with Make and the default target");
1782 else if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_CUSTOM
))
1783 msg
= _("Build the current file with Make and the specified target");
1784 else if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT
))
1785 msg
= _("Compile the current file with Make");
1788 g_object_set(widgets
.build_action
, "tooltip", msg
, NULL
);
1789 on_build_menu_item(menuitem
, user_data
);
1793 static void kill_process(GPid
*pid
)
1795 /* Unix: SIGQUIT is not the best signal to use because it causes a core dump (this should not
1796 * perforce necessary for just killing a process). But we must use a signal which we can
1797 * ignore because the main process get it too, it is declared to ignore in main.c. */
1801 g_return_if_fail(*pid
!= NULL
);
1802 result
= TerminateProcess(*pid
, 0);
1803 /* TerminateProcess() returns TRUE on success, for the check below we have to convert
1804 * it to FALSE (and vice versa) */
1807 g_return_if_fail(*pid
> 1);
1808 result
= kill(*pid
, SIGQUIT
);
1812 ui_set_statusbar(TRUE
, _("Process could not be stopped (%s)."), g_strerror(errno
));
1816 build_menu_update(NULL
);
1821 static void on_build_next_error(GtkWidget
*menuitem
, gpointer user_data
)
1823 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow
.tree_compiler
),
1824 msgwin_goto_compiler_file_line
))
1826 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
1829 ui_set_statusbar(FALSE
, _("No more build errors."));
1833 static void on_build_previous_error(GtkWidget
*menuitem
, gpointer user_data
)
1835 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow
.tree_compiler
),
1836 msgwin_goto_compiler_file_line
))
1838 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
1841 ui_set_statusbar(FALSE
, _("No more build errors."));
1845 void build_toolbutton_build_clicked(GtkAction
*action
, gpointer unused
)
1847 if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_BUILD
))
1849 on_build_menu_item(NULL
, GBO_TO_POINTER(GEANY_GBO_BUILD
));
1853 on_build_menu_item(NULL
, last_toolbutton_action
);
1858 /*------------------------------------------------------
1860 * Create and handle the build menu configuration dialog
1862 *-------------------------------------------------------*/
1863 typedef struct RowWidgets
1865 GtkWidget
*entries
[GEANY_BC_CMDENTRIES_COUNT
];
1866 GeanyBuildSource src
;
1867 GeanyBuildSource dst
;
1868 GeanyBuildCommand
*cmdsrc
;
1875 static GdkColor
*insensitive_color
;
1877 static void set_row_color(RowWidgets
*r
, GdkColor
*color
)
1879 enum GeanyBuildCmdEntries i
;
1881 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1882 gtk_widget_modify_text(r
->entries
[i
], GTK_STATE_NORMAL
, color
);
1886 static void set_build_command_entry_text(GtkWidget
*wid
, const gchar
*text
)
1888 if (GTK_IS_BUTTON(wid
))
1889 gtk_button_set_label(GTK_BUTTON(wid
), text
);
1891 gtk_entry_set_text(GTK_ENTRY(wid
), text
);
1895 static void on_clear_dialog_row(GtkWidget
*unused
, gpointer user_data
)
1897 RowWidgets
*r
= user_data
;
1899 enum GeanyBuildCmdEntries i
;
1900 GeanyBuildCommand
*bc
= get_next_build_cmd(NULL
, r
->grp
, r
->cmd
, r
->dst
, &src
);
1906 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1908 set_build_command_entry_text(r
->entries
[i
],
1909 id_to_str(bc
,i
) != NULL
? id_to_str(bc
,i
) : "");
1915 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1917 set_build_command_entry_text(r
->entries
[i
], "");
1920 r
->used_dst
= FALSE
;
1921 set_row_color(r
, insensitive_color
);
1926 static void on_clear_dialog_regex_row(GtkEntry
*regex
, gpointer unused
)
1928 gtk_entry_set_text(regex
,"");
1932 static void on_label_button_clicked(GtkWidget
*wid
, gpointer user_data
)
1934 RowWidgets
*r
= user_data
;
1935 GtkWidget
*top_level
= gtk_widget_get_toplevel(wid
);
1936 const gchar
*old
= gtk_button_get_label(GTK_BUTTON(wid
));
1939 if (GTK_WIDGET_TOPLEVEL(top_level
) && GTK_IS_WINDOW(top_level
))
1940 str
= dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level
), NULL
, old
);
1942 str
= dialogs_show_input(_("Set menu item label"), NULL
, NULL
, old
);
1947 gtk_button_set_label(GTK_BUTTON(wid
), str
);
1950 set_row_color(r
, NULL
);
1954 static void on_entry_focus(GtkWidget
*wid
, GdkEventFocus
*unused
, gpointer user_data
)
1956 RowWidgets
*r
= user_data
;
1959 set_row_color(r
, NULL
);
1963 /* Column headings, array NULL-terminated */
1964 static const gchar
*colheads
[] =
1969 N_("Working directory"),
1976 #define DC_ENTRIES 1
1980 static const guint entry_x_padding
= 3;
1981 static const guint entry_y_padding
= 0;
1984 static RowWidgets
*build_add_dialog_row(GeanyDocument
*doc
, GtkTable
*table
, guint row
,
1985 GeanyBuildSource dst
, guint grp
, guint cmd
, gboolean dir
)
1987 GtkWidget
*label
, *clear
, *clearicon
;
1989 GeanyBuildCommand
*bc
;
1991 enum GeanyBuildCmdEntries i
;
1995 text
= g_strdup_printf("%d.", cmd
+ 1);
1996 label
= gtk_label_new(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
;
2006 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2008 gint xflags
= (i
== GEANY_BC_COMMAND
) ? GTK_FILL
| GTK_EXPAND
: GTK_FILL
;
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
);
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
);
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
);
2037 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2039 const gchar
*str
= "";
2043 if ((str
= id_to_str(bc
, i
)) == NULL
)
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
);
2062 typedef struct BuildTableFields
2065 GtkWidget
*fileregex
;
2066 GtkWidget
*nonfileregex
;
2067 gchar
**fileregexstring
;
2068 gchar
**nonfileregexstring
;
2072 GtkWidget
*build_commands_table(GeanyDocument
*doc
, GeanyBuildSource dst
, BuildTableData
*table_data
,
2075 GtkWidget
*label
, *sep
, *clearicon
, *clear
;
2076 BuildTableFields
*fields
;
2080 guint col
, row
, cmdindex
;
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
);
2101 txt
= g_strdup_printf(_("%s commands"), _("No filetype"));
2103 label
= ui_label_new_bold(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
));
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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);*/
2197 *table_data
= fields
;
2198 return GTK_WIDGET(table
);
2202 void build_free_fields(BuildTableData table_data
)
2206 for (cmdindex
= 0; cmdindex
< build_items_count
; ++cmdindex
)
2207 g_free(table_data
->rows
[cmdindex
]);
2208 g_free(table_data
->rows
);
2213 /* string compare where null pointers match null or 0 length strings */
2215 static gint
stcmp(const gchar
*a
, const gchar
*b
)
2217 if (a
== NULL
&& b
== NULL
)
2219 if (a
== NULL
&& b
!= NULL
)
2221 if (a
!= NULL
&& b
== NULL
)
2223 return strcmp(a
, b
);
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
));
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
;
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
;
2273 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
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 && NZV(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 */
2294 SETPTR(*dst
, g_strdup(reg
));
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
);
2322 void build_read_project(GeanyFiletype
*ft
, BuildTableData build_properties
)
2324 BuildDestination menu_dst
;
2328 menu_dst
.dst
[GEANY_GBG_FT
] = &(ft
->projfilecmds
);
2329 menu_dst
.fileregexstr
= &(ft
->projerror_regex_string
);
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
= ®ex_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");
2351 BuildTableData table_data
;
2352 BuildDestination prefdsts
;
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
;
2370 prefdsts
.dst
[GEANY_GBG_FT
] = &(ft
->homefilecmds
);
2371 prefdsts
.fileregexstr
= &(ft
->homeerror_regex_string
);
2372 prefdsts
.dst
[GEANY_GBG_EXEC
] = &(ft
->homeexeccmds
);
2376 prefdsts
.dst
[GEANY_GBG_FT
] = NULL
;
2377 prefdsts
.fileregexstr
= NULL
;
2378 prefdsts
.dst
[GEANY_GBG_EXEC
] = NULL
;
2380 prefdsts
.nonfileregexstr
= ®ex_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
);
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
)
2424 gsize prefixlen
; /* NOTE prefixlen used in macros above */
2425 GeanyBuildCommand
*dstcmd
;
2427 static gchar cmdbuf
[3] = " ";
2430 *dst
= g_new0(GeanyBuildCommand
, build_groups_count
[grp
]);
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
)
2438 return; /* ensure no buffer overflow */
2439 sprintf(cmdbuf
, "%02d", cmd
);
2440 set_key_grp(key
, groups
[grp
]);
2441 set_key_cmd(key
, cmdbuf
);
2442 set_key_fld(key
, "LB");
2444 label
= g_key_file_get_locale_string(config
, build_grp_name
, key
, NULL
, NULL
);
2446 label
= g_key_file_get_string(config
, build_grp_name
, key
, 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
;
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
)
2471 gchar
*value
, *basedir
, *makebasedir
;
2472 gboolean bvalue
= FALSE
;
2474 if (g_key_file_has_group(config
, build_grp_name
))
2479 ft
= (GeanyFiletype
*)p
;
2482 build_load_menu_grp(config
, &(ft
->filecmds
), GEANY_GBG_FT
, NULL
, TRUE
);
2483 build_load_menu_grp(config
, &(ft
->ftdefcmds
), GEANY_GBG_NON_FT
, NULL
, TRUE
);
2484 build_load_menu_grp(config
, &(ft
->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
));
2488 case GEANY_BCS_HOME_FT
:
2489 ft
= (GeanyFiletype
*)p
;
2492 build_load_menu_grp(config
, &(ft
->homefilecmds
), GEANY_GBG_FT
, NULL
, FALSE
);
2493 build_load_menu_grp(config
, &(ft
->homeexeccmds
), GEANY_GBG_EXEC
, NULL
, FALSE
);
2494 SETPTR(ft
->homeerror_regex_string
,
2495 g_key_file_get_string(config
, build_grp_name
, "error_regex", NULL
));
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
));
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
;
2509 ftlist
= g_key_file_get_string_list(config
, build_grp_name
, "filetypes", NULL
, NULL
);
2513 if (pj
->build_filetypes_list
== NULL
)
2514 pj
->build_filetypes_list
= g_ptr_array_new();
2515 g_ptr_array_set_size(pj
->build_filetypes_list
, 0);
2516 for (ftname
= ftlist
; *ftname
!= NULL
; ++ftname
)
2518 ft
= filetypes_lookup_by_name(*ftname
);
2521 gchar
*regkey
= g_strdup_printf("%serror_regex", *ftname
);
2522 g_ptr_array_add(pj
->build_filetypes_list
, ft
);
2523 SETPTR(ft
->projerror_regex_string
,
2524 g_key_file_get_string(config
, build_grp_name
, regkey
, NULL
));
2526 build_load_menu_grp(config
, &(ft
->projfilecmds
), GEANY_GBG_FT
, *ftname
, FALSE
);
2527 build_load_menu_grp(config
, &(ft
->projexeccmds
), GEANY_GBG_EXEC
, *ftname
, FALSE
);
2533 default: /* defaults don't load from config, see build_init */
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 if (NZV(value) && ! type[GBO_TO_CMD(id)].exists) { \
2544 type[GBO_TO_CMD(id)].exists = TRUE; \
2545 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(string)); \
2546 SETPTR(type[GBO_TO_CMD(id)].command, (value)); \
2547 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL); \
2548 type[GBO_TO_CMD(id)].old = TRUE; \
2555 ft
= (GeanyFiletype
*)p
;
2556 value
= g_key_file_get_string(config
, "build_settings", "compiler", NULL
);
2559 if (ft
->filecmds
== NULL
)
2560 ft
->filecmds
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_FT
]);
2561 ASSIGNIF(ft
->filecmds
, GEANY_GBO_COMPILE
, _("_Compile"), value
);
2563 value
= g_key_file_get_string(config
, "build_settings", "linker", NULL
);
2566 if (ft
->filecmds
== NULL
)
2567 ft
->filecmds
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_FT
]);
2568 ASSIGNIF(ft
->filecmds
, GEANY_GBO_BUILD
, _("_Build"), value
);
2570 value
= g_key_file_get_string(config
, "build_settings", "run_cmd", NULL
);
2573 if (ft
->execcmds
== NULL
)
2574 ft
->execcmds
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_EXEC
]);
2575 ASSIGNIF(ft
->execcmds
, GEANY_GBO_EXEC
, _("_Execute"), value
);
2577 if (ft
->error_regex_string
== NULL
)
2578 ft
->error_regex_string
= g_key_file_get_string(config
, "build_settings", "error_regex", NULL
);
2580 case GEANY_BCS_PROJ
:
2581 if (non_ft_pref
== NULL
)
2582 non_ft_pref
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_NON_FT
]);
2583 basedir
= project_get_base_path();
2584 if (basedir
== NULL
)
2585 basedir
= g_strdup("%d");
2586 bvalue
= g_key_file_get_boolean(config
, "project", "make_in_base_path", NULL
);
2588 makebasedir
= g_strdup(basedir
);
2590 makebasedir
= g_strdup("%d");
2591 if (non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_ALL
)].old
)
2592 SETPTR(non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_ALL
)].working_dir
, g_strdup(makebasedir
));
2593 if (non_ft_pref
[GBO_TO_CMD(GEANY_GBO_CUSTOM
)].old
)
2594 SETPTR(non_ft_pref
[GBO_TO_CMD(GEANY_GBO_CUSTOM
)].working_dir
, g_strdup(makebasedir
));
2595 if (non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
)].old
)
2596 SETPTR(non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
)].working_dir
, g_strdup("%d"));
2597 value
= g_key_file_get_string(config
, "project", "run_cmd", NULL
);
2600 if (exec_proj
== NULL
)
2601 exec_proj
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_EXEC
]);
2602 if (! exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].exists
)
2604 exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].exists
= TRUE
;
2605 SETPTR(exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].label
, g_strdup(_("_Execute")));
2606 SETPTR(exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].command
, value
);
2607 SETPTR(exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].working_dir
, g_strdup(basedir
));
2608 exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].old
= TRUE
;
2611 g_free(makebasedir
);
2614 case GEANY_BCS_PREF
:
2615 value
= g_key_file_get_string(config
, "tools", "make_cmd", NULL
);
2618 if (non_ft_pref
== NULL
)
2619 non_ft_pref
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_NON_FT
]);
2620 ASSIGNIF(non_ft_pref
, GEANY_GBO_CUSTOM
, _("Make Custom _Target"),
2621 g_strdup_printf("%s ", value
));
2622 ASSIGNIF(non_ft_pref
, GEANY_GBO_MAKE_OBJECT
, _("Make _Object"),
2623 g_strdup_printf("%s %%e.o",value
));
2624 ASSIGNIF(non_ft_pref
, GEANY_GBO_MAKE_ALL
, _("_Make"), value
);
2633 static guint
build_save_menu_grp(GKeyFile
*config
, GeanyBuildCommand
*src
, gint grp
, gchar
*prefix
)
2636 gsize prefixlen
; /* NOTE prefixlen used in macros above */
2639 enum GeanyBuildCmdEntries i
;
2643 prefixlen
= prefix
== NULL
? 0 : strlen(prefix
);
2644 key
= g_strconcat(prefix
== NULL
? "" : prefix
, fixedkey
, NULL
);
2645 for (cmd
= 0; cmd
< build_groups_count
[grp
]; ++cmd
)
2647 if (src
[cmd
].exists
) ++count
;
2648 if (src
[cmd
].changed
)
2650 static gchar cmdbuf
[4] = " ";
2652 return count
; /* ensure no buffer overflow */
2653 sprintf(cmdbuf
, "%02d", cmd
);
2654 set_key_grp(key
, groups
[grp
]);
2655 set_key_cmd(key
, cmdbuf
);
2656 if (src
[cmd
].exists
)
2658 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2660 set_key_fld(key
, config_keys
[i
]);
2661 g_key_file_set_string(config
, build_grp_name
, key
, id_to_str(&src
[cmd
], i
));
2666 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2668 set_key_fld(key
, config_keys
[i
]);
2669 g_key_file_remove_key(config
, build_grp_name
, key
, NULL
);
2679 typedef struct ForEachData
2682 GPtrArray
*ft_names
;
2686 static void foreach_project_filetype(gpointer data
, gpointer user_data
)
2688 GeanyFiletype
*ft
= data
;
2689 ForEachData
*d
= user_data
;
2691 gchar
*regkey
= g_strdup_printf("%serror_regex", ft
->name
);
2693 i
+= build_save_menu_grp(d
->config
, ft
->projfilecmds
, GEANY_GBG_FT
, ft
->name
);
2694 i
+= build_save_menu_grp(d
->config
, ft
->projexeccmds
, GEANY_GBG_EXEC
, ft
->name
);
2695 if (NZV(ft
->projerror_regex_string
))
2697 g_key_file_set_string(d
->config
, build_grp_name
, regkey
, ft
->projerror_regex_string
);
2701 g_key_file_remove_key(d
->config
, build_grp_name
, regkey
, NULL
);
2704 g_ptr_array_add(d
->ft_names
, ft
->name
);
2708 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2709 void build_save_menu(GKeyFile
*config
, gpointer ptr
, GeanyBuildSource src
)
2717 case GEANY_BCS_HOME_FT
:
2718 ft
= (GeanyFiletype
*)ptr
;
2721 build_save_menu_grp(config
, ft
->homefilecmds
, GEANY_GBG_FT
, NULL
);
2722 build_save_menu_grp(config
, ft
->homeexeccmds
, GEANY_GBG_EXEC
, NULL
);
2723 if (NZV(ft
->homeerror_regex_string
))
2724 g_key_file_set_string(config
, build_grp_name
, "error_regex", ft
->homeerror_regex_string
);
2726 g_key_file_remove_key(config
, build_grp_name
, "error_regex", NULL
);
2728 case GEANY_BCS_PREF
:
2729 build_save_menu_grp(config
, non_ft_pref
, GEANY_GBG_NON_FT
, NULL
);
2730 build_save_menu_grp(config
, exec_pref
, GEANY_GBG_EXEC
, NULL
);
2731 if (NZV(regex_pref
))
2732 g_key_file_set_string(config
, build_grp_name
, "error_regex", regex_pref
);
2734 g_key_file_remove_key(config
, build_grp_name
, "error_regex", NULL
);
2736 case GEANY_BCS_PROJ
:
2737 pj
= (GeanyProject
*)ptr
;
2738 build_save_menu_grp(config
, non_ft_proj
, GEANY_GBG_NON_FT
, NULL
);
2739 build_save_menu_grp(config
, exec_proj
, GEANY_GBG_EXEC
, NULL
);
2740 if (NZV(regex_proj
))
2741 g_key_file_set_string(config
, build_grp_name
, "error_regex", regex_proj
);
2743 g_key_file_remove_key(config
, build_grp_name
, "error_regex", NULL
);
2744 if (pj
->build_filetypes_list
!= NULL
)
2746 data
.config
= config
;
2747 data
.ft_names
= g_ptr_array_new();
2748 g_ptr_array_foreach(pj
->build_filetypes_list
, foreach_project_filetype
, (gpointer
)(&data
));
2749 if (data
.ft_names
->pdata
!= NULL
)
2750 g_key_file_set_string_list(config
, build_grp_name
, "filetypes",
2751 (const gchar
**)(data
.ft_names
->pdata
), data
.ft_names
->len
);
2753 g_key_file_remove_key(config
, build_grp_name
, "filetypes", NULL
);
2754 g_ptr_array_free(data
.ft_names
, TRUE
);
2757 default: /* defaults and GEANY_BCS_FT can't save */
2763 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2764 void build_set_group_count(GeanyBuildGroup grp
, gint count
)
2768 g_return_if_fail(count
>= 0);
2770 if ((guint
) count
> build_groups_count
[grp
])
2771 build_groups_count
[grp
] = (guint
) count
;
2772 for (i
= 0, sum
= 0; i
< GEANY_GBG_COUNT
; ++i
)
2773 sum
+= build_groups_count
[i
];
2774 build_items_count
= sum
;
2778 /** Get the count of commands for the group
2780 * Get the number of commands in the group specified by @a grp.
2782 * @param grp the group of the specified menu item.
2784 * @return a count of the number of commands in the group
2788 guint
build_get_group_count(const GeanyBuildGroup grp
)
2790 g_return_val_if_fail(grp
< GEANY_GBG_COUNT
, 0);
2791 return build_groups_count
[grp
];
2795 static void on_project_close(void)
2797 /* remove project regexen */
2798 SETPTR(regex_proj
, NULL
);
2805 const gchar
*command
;
2806 const gchar
*working_dir
;
2807 GeanyBuildCommand
**ptr
;
2809 } default_cmds
[] = {
2810 { N_("_Make"), "make", NULL
, &non_ft_def
, GBO_TO_CMD(GEANY_GBO_MAKE_ALL
)},
2811 { N_("Make Custom _Target"), "make ", NULL
, &non_ft_def
, GBO_TO_CMD(GEANY_GBO_CUSTOM
)},
2812 { N_("Make _Object"), "make %e.o", NULL
, &non_ft_def
, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
)},
2813 { N_("_Execute"), "./%e", NULL
, &exec_def
, GBO_TO_CMD(GEANY_GBO_EXEC
)},
2814 { NULL
, NULL
, NULL
, NULL
, 0 }
2818 void build_init(void)
2821 GtkWidget
*toolmenu
;
2824 g_signal_connect(geany_object
, "project-close", on_project_close
, NULL
);
2826 ft_def
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_FT
]);
2827 non_ft_def
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_NON_FT
]);
2828 exec_def
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_EXEC
]);
2829 run_info
= g_new0(RunInfo
, build_groups_count
[GEANY_GBG_EXEC
]);
2831 for (cmdindex
= 0; default_cmds
[cmdindex
].command
!= NULL
; ++cmdindex
)
2833 GeanyBuildCommand
*cmd
= &((*(default_cmds
[cmdindex
].ptr
))[ default_cmds
[cmdindex
].index
]);
2835 cmd
->label
= g_strdup(_(default_cmds
[cmdindex
].label
));
2836 cmd
->command
= g_strdup(default_cmds
[cmdindex
].command
);
2837 cmd
->working_dir
= g_strdup(default_cmds
[cmdindex
].working_dir
);
2840 /* create the toolbar Build item sub menu */
2841 toolmenu
= gtk_menu_new();
2842 g_object_ref(toolmenu
);
2844 /* build the code */
2845 item
= ui_image_menu_item_new(GEANY_STOCK_BUILD
, _("_Build"));
2846 gtk_widget_show(item
);
2847 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2848 g_signal_connect(item
, "activate", G_CALLBACK(on_toolbutton_build_activate
),
2849 GBO_TO_POINTER(GEANY_GBO_BUILD
));
2850 widgets
.toolitem_build
= item
;
2852 item
= gtk_separator_menu_item_new();
2853 gtk_widget_show(item
);
2854 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2856 /* build the code with make all */
2857 item
= gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
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_ALL
));
2862 widgets
.toolitem_make_all
= item
;
2864 /* build the code with make custom */
2865 item
= gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target"));
2866 gtk_widget_show(item
);
2867 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2868 g_signal_connect(item
, "activate", G_CALLBACK(on_toolbutton_make_activate
),
2869 GBO_TO_POINTER(GEANY_GBO_CUSTOM
));
2870 widgets
.toolitem_make_custom
= item
;
2872 /* build the code with make object */
2873 item
= gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2874 gtk_widget_show(item
);
2875 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2876 g_signal_connect(item
, "activate", G_CALLBACK(on_toolbutton_make_activate
),
2877 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT
));
2878 widgets
.toolitem_make_object
= item
;
2880 item
= gtk_separator_menu_item_new();
2881 gtk_widget_show(item
);
2882 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2885 item
= ui_image_menu_item_new(GTK_STOCK_PREFERENCES
, _("_Set Build Commands"));
2886 gtk_widget_show(item
);
2887 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2888 g_signal_connect(item
, "activate", G_CALLBACK(on_set_build_commands_activate
), NULL
);
2889 widgets
.toolitem_set_args
= item
;
2891 /* get toolbar action pointers */
2892 widgets
.build_action
= toolbar_get_action_by_name("Build");
2893 widgets
.compile_action
= toolbar_get_action_by_name("Compile");
2894 widgets
.run_action
= toolbar_get_action_by_name("Run");
2895 widgets
.toolmenu
= toolmenu
;
2896 /* set the submenu to the toolbar item */
2897 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets
.build_action
), toolmenu
);