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