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"
53 #include "gtkcompat.h"
60 #include <glib/gstdio.h>
64 /* Number of editor indicators to draw - limited as this can affect performance */
65 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
68 GeanyBuildInfo build_info
= {GEANY_GBG_FT
, 0, 0, NULL
, GEANY_FILETYPES_NONE
, NULL
, 0};
70 static gchar
*current_dir_entered
= NULL
;
72 typedef struct RunInfo
78 static RunInfo
*run_info
;
81 static const gchar RUN_SCRIPT_CMD
[] = "geany_run_script_XXXXXX.sh";
84 /* Order is important (see GBO_TO_GBG, GBO_TO_CMD below) */
85 /* * Geany Known Build Commands.
86 * These commands are named after their default use.
87 * Only these commands can currently have keybindings.
91 GEANY_GBO_COMPILE
, /* *< default compile file */
92 GEANY_GBO_BUILD
, /* *< default build file */
93 GEANY_GBO_MAKE_ALL
, /* *< default make */
94 GEANY_GBO_CUSTOM
, /* *< default make user specified target */
95 GEANY_GBO_MAKE_OBJECT
, /* *< default make object, make %e.o */
96 GEANY_GBO_EXEC
, /* *< default execute ./%e */
97 GEANY_GBO_COUNT
/* *< count of how many */
100 /* * Convert @c GeanyBuildType to @c GeanyBuildGroup.
102 * This macro converts @c GeanyBuildType enum values (the "known" commands)
103 * to the group they are part of.
105 * @param gbo the @c GeanyBuildType value.
107 * @return the @c GeanyBuildGroup group that @a gbo is in.
109 * Note this is a macro so that it can be used in static initialisers.
111 #define GBO_TO_GBG(gbo) \
112 ((gbo) > GEANY_GBO_EXEC ? GEANY_GBG_COUNT : \
113 ((gbo) >= GEANY_GBO_EXEC ? GEANY_GBG_EXEC : \
114 ((gbo) >= GEANY_GBO_MAKE_ALL ? GEANY_GBG_NON_FT : GEANY_GBG_FT)))
116 /* * Convert @c GeanyBuildType to command index.
118 * This macro converts @c GeanyBuildType enum values (the "known" commands)
119 * to the index within the group.
121 * @param gbo the @c GeanyBuildType value.
123 * @return the index of the @a gbo command in its group.
125 * Note this is a macro so that it can be used in static initialisers.
127 #define GBO_TO_CMD(gbo) \
128 ((gbo) >= GEANY_GBO_COUNT ? (gbo) - GEANY_GBO_COUNT : \
129 ((gbo) >= GEANY_GBO_EXEC ? (gbo) - GEANY_GBO_EXEC : \
130 ((gbo) >= GEANY_GBO_MAKE_ALL ? \
131 (gbo) - GEANY_GBO_MAKE_ALL : (gbo))))
133 /* pack group (<8) and command (<32) into a user_data pointer */
134 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
135 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
136 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
137 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
139 static gpointer last_toolbutton_action
= GBO_TO_POINTER(GEANY_GBO_BUILD
);
141 static BuildMenuItems menu_items
= {NULL
, {NULL
, NULL
, NULL
, NULL
}};
145 GtkAction
*run_action
;
146 GtkAction
*compile_action
;
147 GtkAction
*build_action
;
150 GtkWidget
*toolitem_build
;
151 GtkWidget
*toolitem_make_all
;
152 GtkWidget
*toolitem_make_custom
;
153 GtkWidget
*toolitem_make_object
;
154 GtkWidget
*toolitem_set_args
;
158 static guint build_groups_count
[GEANY_GBG_COUNT
] = { 3, 4, 2 };
159 static guint build_items_count
= 9;
161 static void build_exit_cb(GPid pid
, gint status
, gpointer user_data
);
162 static void build_iofunc(GString
*string
, GIOCondition condition
, gpointer data
);
164 static gchar
*build_create_shellscript(const gchar
*working_dir
, const gchar
*cmd
, gboolean autoclose
, GError
**error
);
166 static void build_spawn_cmd(GeanyDocument
*doc
, const gchar
*cmd
, const gchar
*dir
);
167 static void set_stop_button(gboolean stop
);
168 static void run_exit_cb(GPid child_pid
, gint status
, gpointer user_data
);
169 static void on_set_build_commands_activate(GtkWidget
*w
, gpointer u
);
170 static void on_build_next_error(GtkWidget
*menuitem
, gpointer user_data
);
171 static void on_build_previous_error(GtkWidget
*menuitem
, gpointer user_data
);
172 static void kill_process(GPid
*pid
);
173 static void show_build_result_message(gboolean failure
);
174 static void process_build_output_line(gchar
*msg
, gint color
);
175 static void show_build_commands_dialog(void);
176 static void on_build_menu_item(GtkWidget
*w
, gpointer user_data
);
178 void build_finalize(void)
180 g_free(build_info
.dir
);
181 g_free(build_info
.custom_target
);
183 if (menu_items
.menu
!= NULL
&& GTK_IS_WIDGET(menu_items
.menu
))
184 gtk_widget_destroy(menu_items
.menu
);
188 /* note: copied from keybindings.c, may be able to go away */
189 static void add_menu_accel(GeanyKeyGroup
*group
, guint kb_id
,
190 GtkAccelGroup
*accel_group
, GtkWidget
*menuitem
)
192 GeanyKeyBinding
*kb
= keybindings_get_item(group
, kb_id
);
195 gtk_widget_add_accelerator(menuitem
, "activate", accel_group
,
196 kb
->key
, kb
->mods
, GTK_ACCEL_VISIBLE
);
200 /* convenience routines to access parts of GeanyBuildCommand */
201 static gchar
*id_to_str(GeanyBuildCommand
*bc
, gint id
)
207 case GEANY_BC_COMMAND
:
209 case GEANY_BC_WORKING_DIR
:
210 return bc
->working_dir
;
217 static void set_command(GeanyBuildCommand
*bc
, gint id
, gchar
*str
)
222 SETPTR(bc
->label
, str
);
224 case GEANY_BC_COMMAND
:
225 SETPTR(bc
->command
, str
);
227 case GEANY_BC_WORKING_DIR
:
228 SETPTR(bc
->working_dir
, str
);
236 static const gchar
*config_keys
[GEANY_BC_CMDENTRIES_COUNT
] = {
239 "WD" /* working directory */
242 /*-----------------------------------------------------
244 * Execute commands and handle results
246 *-----------------------------------------------------*/
248 /* the various groups of commands not in the filetype struct */
249 static GeanyBuildCommand
*ft_def
= NULL
;
250 static GeanyBuildCommand
*non_ft_proj
= NULL
;
251 static GeanyBuildCommand
*non_ft_pref
= NULL
;
252 static GeanyBuildCommand
*non_ft_def
= NULL
;
253 static GeanyBuildCommand
*exec_proj
= NULL
;
254 static GeanyBuildCommand
*exec_pref
= NULL
;
255 static GeanyBuildCommand
*exec_def
= NULL
;
256 /* and the regexen not in the filetype structure */
257 static gchar
*regex_pref
= NULL
;
258 /* project non-fileregex string */
259 static gchar
*regex_proj
= NULL
;
261 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
262 #ifndef PRINTBUILDCMDS
263 #define PRINTBUILDCMDS FALSE
265 static gboolean printbuildcmds
= PRINTBUILDCMDS
;
268 /* for debug only, print the commands structures in priority order */
269 static void printfcmds(void)
272 GeanyBuildCommand
**cl
[GEANY_GBG_COUNT
][GEANY_BCS_COUNT
] = {
273 /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
274 * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
275 { &ft_def
, NULL
, NULL
, NULL
, NULL
, NULL
},
276 { &non_ft_def
, NULL
, NULL
, &non_ft_pref
, NULL
, &non_ft_proj
},
277 { &exec_def
, NULL
, NULL
, &exec_pref
, NULL
, &exec_proj
}
279 GeanyFiletype
*ft
= NULL
;
282 enum GeanyBuildCmdEntries n
;
283 gint cc
[GEANY_BCS_COUNT
];
286 doc
= document_get_current();
291 printf("filetype %s\n",ft
->name
);
292 cl
[GEANY_GBG_FT
][GEANY_BCS_FT
] = &(ft
->priv
->filecmds
);
293 cl
[GEANY_GBG_FT
][GEANY_BCS_HOME_FT
] = &(ft
->priv
->homefilecmds
);
294 cl
[GEANY_GBG_FT
][GEANY_BCS_PROJ
] = &(ft
->priv
->projfilecmds
);
295 cl
[GEANY_GBG_NON_FT
][GEANY_BCS_FT
] = &(ft
->priv
->ftdefcmds
);
296 cl
[GEANY_GBG_EXEC
][GEANY_BCS_FT
] = &(ft
->priv
->execcmds
);
297 cl
[GEANY_GBG_EXEC
][GEANY_BCS_HOME_FT
] = &(ft
->priv
->homeexeccmds
);
298 cl
[GEANY_GBG_EXEC
][GEANY_BCS_PROJ_FT
] = &(ft
->priv
->projexeccmds
);
300 for (i
= 0; i
< GEANY_BCS_COUNT
; ++i
)
303 for (j
= 0; j
< GEANY_GBG_COUNT
; ++j
)
305 for (k
= 0; k
< build_groups_count
[j
]; ++k
)
306 if (cl
[j
][i
] != NULL
&& *(cl
[j
][i
]) != NULL
&& (*(cl
[j
][i
]))[k
].exists
)
308 for (n
= 0; n
< GEANY_BC_CMDENTRIES_COUNT
; n
++)
310 if ((*(cl
[j
][i
]))[k
].entries
[n
] != NULL
&&
311 (l
= strlen((*(cl
[j
][i
]))[k
].entries
[n
])) > m
)
320 for (i
= 0; i
< GEANY_GBG_COUNT
; ++i
)
322 for (k
= 0; k
< build_groups_count
[i
]; ++k
)
324 for (l
= 0; l
< 2; ++l
)
327 for (j
= 0; j
< GEANY_BCS_COUNT
; ++j
)
329 if (cl
[i
][j
] != NULL
&& *(cl
[i
][j
]) != NULL
&& (*(cl
[i
][j
]))[k
].exists
)
331 for (n
= 0; n
< GEANY_BC_CMDENTRIES_COUNT
; n
++)
333 if ((*(cl
[i
][j
]))[k
].entries
[i
] != NULL
)
334 printf("%c %*.*s",c
,cc
[j
],cc
[j
],(*(cl
[i
][j
]))[k
].entries
[i
]);
336 printf("%c %*.*s",c
,cc
[j
],cc
[j
]," ");
340 printf("%c %*.*s",c
,cc
[j
],cc
[j
]," ");
352 /* macros to save typing and make the logic visible */
353 #define return_cmd_if(src, cmds)\
354 if (cmds != NULL && cmds[cmdindex].exists && below>src)\
357 if (printbuildcmds) \
358 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
359 return &(cmds[cmdindex]); \
362 #define return_ft_cmd_if(src, cmds)\
363 if (ft != NULL && ft->priv->cmds != NULL \
364 && ft->priv->cmds[cmdindex].exists && below>src)\
367 if (printbuildcmds) \
368 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
369 return &(ft->priv->cmds[cmdindex]); \
373 /* get the next lowest command taking priority into account */
374 static GeanyBuildCommand
*get_next_build_cmd(GeanyDocument
*doc
, guint cmdgrp
, guint cmdindex
,
375 guint below
, guint
*from
)
377 /* Note: parameter below used in macros above */
378 GeanyFiletype
*ft
= NULL
;
379 guint sink
, *fr
= &sink
;
381 g_return_val_if_fail(doc
== NULL
|| doc
->is_valid
, NULL
);
385 if (cmdgrp
>= GEANY_GBG_COUNT
)
390 doc
= document_get_current();
396 case GEANY_GBG_FT
: /* order proj ft, home ft, ft, defft */
399 return_ft_cmd_if(GEANY_BCS_PROJ
, projfilecmds
);
400 return_ft_cmd_if(GEANY_BCS_PREF
, homefilecmds
);
401 return_ft_cmd_if(GEANY_BCS_FT
, filecmds
);
403 return_cmd_if(GEANY_BCS_DEF
, ft_def
);
405 case GEANY_GBG_NON_FT
: /* order proj, pref, def */
406 return_cmd_if(GEANY_BCS_PROJ
, non_ft_proj
);
407 return_cmd_if(GEANY_BCS_PREF
, non_ft_pref
);
408 return_ft_cmd_if(GEANY_BCS_FT
, ftdefcmds
);
409 return_cmd_if(GEANY_BCS_DEF
, non_ft_def
);
411 case GEANY_GBG_EXEC
: /* order proj, proj ft, pref, home ft, ft, def */
412 return_cmd_if(GEANY_BCS_PROJ
, exec_proj
);
413 return_ft_cmd_if(GEANY_BCS_PROJ_FT
, projexeccmds
);
414 return_cmd_if(GEANY_BCS_PREF
, exec_pref
);
415 return_ft_cmd_if(GEANY_BCS_FT
, homeexeccmds
);
416 return_ft_cmd_if(GEANY_BCS_FT
, execcmds
);
417 return_cmd_if(GEANY_BCS_DEF
, exec_def
);
426 /* shortcut to start looking at the top */
427 static GeanyBuildCommand
*get_build_cmd(GeanyDocument
*doc
, guint grp
, guint cmdindex
, guint
*from
)
429 return get_next_build_cmd(doc
, grp
, cmdindex
, GEANY_BCS_COUNT
, from
);
433 #define return_nonblank_regex(src, ptr)\
435 { *fr = (src); return &(ptr); }
438 /* like get_build_cmd, but for regexen, used by filetypes */
439 gchar
**build_get_regex(GeanyBuildGroup grp
, GeanyFiletype
*ft
, guint
*from
)
441 guint sink
, *fr
= &sink
;
445 if (grp
== GEANY_GBG_FT
)
449 GeanyDocument
*doc
= document_get_current();
455 return_nonblank_regex(GEANY_BCS_PROJ
, ft
->priv
->projerror_regex_string
);
456 return_nonblank_regex(GEANY_BCS_HOME_FT
, ft
->priv
->homeerror_regex_string
);
457 return_nonblank_regex(GEANY_BCS_FT
, ft
->error_regex_string
);
459 else if (grp
== GEANY_GBG_NON_FT
)
461 return_nonblank_regex(GEANY_BCS_PROJ
, regex_proj
);
462 return_nonblank_regex(GEANY_BCS_PREF
, regex_pref
);
468 static GeanyBuildCommand
**get_build_group_pointer(const GeanyBuildSource src
, const GeanyBuildGroup grp
)
471 GeanyFiletype
*ft
= NULL
;
476 if ((doc
= document_get_current()) == NULL
)
478 if ((ft
= doc
->file_type
) == NULL
)
482 case GEANY_BCS_DEF
: return &(ft
->priv
->ftdefcmds
);
483 case GEANY_BCS_FT
: return &(ft
->priv
->filecmds
);
484 case GEANY_BCS_HOME_FT
: return &(ft
->priv
->homefilecmds
);
485 case GEANY_BCS_PREF
: return &(ft
->priv
->homefilecmds
);
486 case GEANY_BCS_PROJ
: return &(ft
->priv
->projfilecmds
);
487 default: return NULL
;
490 case GEANY_GBG_NON_FT
:
493 case GEANY_BCS_DEF
: return &(non_ft_def
);
494 case GEANY_BCS_PREF
: return &(non_ft_pref
);
495 case GEANY_BCS_PROJ
: return &(non_ft_proj
);
496 default: return NULL
;
500 if ((doc
= document_get_current()) != NULL
)
504 case GEANY_BCS_DEF
: return &(exec_def
);
505 case GEANY_BCS_FT
: return ft
? &(ft
->priv
->execcmds
): NULL
;
506 case GEANY_BCS_HOME_FT
: return ft
? &(ft
->priv
->homeexeccmds
): NULL
;
507 case GEANY_BCS_PROJ_FT
: return ft
? &(ft
->priv
->projexeccmds
): NULL
;
508 case GEANY_BCS_PREF
: return &(exec_pref
);
509 case GEANY_BCS_PROJ
: return &(exec_proj
);
510 default: return NULL
;
519 /* get pointer to the command group array */
520 static GeanyBuildCommand
*get_build_group(const GeanyBuildSource src
, const GeanyBuildGroup grp
)
522 GeanyBuildCommand
**g
= get_build_group_pointer(src
, grp
);
523 if (g
== NULL
) return NULL
;
528 /** Remove the specified Build menu item.
530 * Makes the specified menu item configuration no longer exist. This
531 * is different to setting fields to blank because the menu item
532 * will be deleted from the configuration file on saving
533 * (except the system filetypes settings @see Build Menu Configuration
534 * section of the Manual).
536 * @param src the source of the menu item to remove.
537 * @param grp the group of the command to remove.
538 * @param cmd the index (from 0) of the command within the group. A negative
539 * value will remove the whole group.
541 * If any parameter is out of range does nothing.
547 void build_remove_menu_item(const GeanyBuildSource src
, const GeanyBuildGroup grp
, const gint cmd
)
549 GeanyBuildCommand
*bc
;
552 bc
= get_build_group(src
, grp
);
557 for (i
= 0; i
< build_groups_count
[grp
]; ++i
)
558 bc
[i
].exists
= FALSE
;
560 else if ((guint
) cmd
< build_groups_count
[grp
])
561 bc
[cmd
].exists
= FALSE
;
565 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
567 * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
568 * hidden by higher priority commands.
570 * @param src the source of the specified menu item.
571 * @param grp the group of the specified menu item.
572 * @param cmd the index of the command within the group.
574 * @return a pointer to the @a GeanyBuildCommand structure or @c NULL if it doesn't exist.
575 * This is a pointer to an internal structure and must not be freed.
577 * @see build_menu_update
579 GeanyBuildCommand
*build_get_menu_item(GeanyBuildSource src
, GeanyBuildGroup grp
, guint cmd
)
581 GeanyBuildCommand
*bc
;
583 g_return_val_if_fail(src
< GEANY_BCS_COUNT
, NULL
);
584 g_return_val_if_fail(grp
< GEANY_GBG_COUNT
, NULL
);
585 g_return_val_if_fail(cmd
< build_groups_count
[grp
], NULL
);
587 bc
= get_build_group(src
, grp
);
594 /** Get the string for the menu item field.
596 * Get the current highest priority command specified by @a grp and @a cmd. This is the one
597 * that the menu item will use if activated.
599 * @param grp the group of the specified menu item.
600 * @param cmd the index of the command within the group.
601 * @param fld the field to return
603 * @return @nullable a pointer to the constant string or @c NULL if it doesn't exist.
604 * This is a pointer to an internal structure and must not be freed.
608 const gchar
*build_get_current_menu_item(const GeanyBuildGroup grp
, const guint cmd
,
609 const GeanyBuildCmdEntries fld
)
611 GeanyBuildCommand
*c
;
614 g_return_val_if_fail(grp
< GEANY_GBG_COUNT
, NULL
);
615 g_return_val_if_fail(fld
< GEANY_BC_CMDENTRIES_COUNT
, NULL
);
616 g_return_val_if_fail(cmd
< build_groups_count
[grp
], NULL
);
618 c
= get_build_cmd(NULL
, grp
, cmd
, NULL
);
619 if (c
== NULL
) return NULL
;
622 case GEANY_BC_COMMAND
:
628 case GEANY_BC_WORKING_DIR
:
629 str
= c
->working_dir
;
637 /** Set the string for the menu item field.
639 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
641 * @param src the source of the menu item
642 * @param grp the group of the specified menu item.
643 * @param cmd the index of the menu item within the group.
644 * @param fld the field in the menu item command to set
645 * @param val the value to set the field to, is copied
649 void build_set_menu_item(const GeanyBuildSource src
, const GeanyBuildGroup grp
,
650 const guint cmd
, const GeanyBuildCmdEntries fld
, const gchar
*val
)
652 GeanyBuildCommand
**g
;
654 g_return_if_fail(src
< GEANY_BCS_COUNT
);
655 g_return_if_fail(grp
< GEANY_GBG_COUNT
);
656 g_return_if_fail(fld
< GEANY_BC_CMDENTRIES_COUNT
);
657 g_return_if_fail(cmd
< build_groups_count
[grp
]);
659 g
= get_build_group_pointer(src
, grp
);
660 if (g
== NULL
) return;
663 *g
= g_new0(GeanyBuildCommand
, build_groups_count
[grp
]);
667 case GEANY_BC_COMMAND
:
668 SETPTR((*g
)[cmd
].command
, g_strdup(val
));
669 (*g
)[cmd
].exists
= TRUE
;
672 SETPTR((*g
)[cmd
].label
, g_strdup(val
));
673 (*g
)[cmd
].exists
= TRUE
;
675 case GEANY_BC_WORKING_DIR
:
676 SETPTR((*g
)[cmd
].working_dir
, g_strdup(val
));
677 (*g
)[cmd
].exists
= TRUE
;
682 build_menu_update(NULL
);
685 /** Activate the menu item.
687 * Activate the menu item specified by @a grp and @a cmd.
689 * @param grp the group of the specified menu item.
690 * @param cmd the index of the command within the group.
694 void build_activate_menu_item(const GeanyBuildGroup grp
, const guint cmd
)
696 on_build_menu_item(NULL
, GRP_CMD_TO_POINTER(grp
, cmd
));
700 /* Clear all error indicators in all documents. */
701 static void clear_all_errors(void)
707 editor_indicator_clear_errors(documents
[i
]->editor
);
712 /* Replaces occurrences of %e and %p with the appropriate filenames and
713 * %l with current line number. %d and %p replacements should be in UTF8 */
714 static gchar
*build_replace_placeholder(const GeanyDocument
*doc
, const gchar
*src
)
718 gchar
*executable
= NULL
;
721 g_return_val_if_fail(doc
== NULL
|| doc
->is_valid
, NULL
);
723 stack
= g_string_new(src
);
724 if (doc
!= NULL
&& doc
->file_name
!= NULL
)
726 /* replace %f with the filename (including extension) */
727 replacement
= g_path_get_basename(doc
->file_name
);
728 utils_string_replace_all(stack
, "%f", replacement
);
731 /* replace %d with the absolute path of the dir of the current file */
732 replacement
= g_path_get_dirname(doc
->file_name
);
733 utils_string_replace_all(stack
, "%d", replacement
);
736 /* replace %e with the filename (excluding extension) */
737 executable
= utils_remove_ext_from_filename(doc
->file_name
);
738 replacement
= g_path_get_basename(executable
);
739 utils_string_replace_all(stack
, "%e", replacement
);
742 /* replace %l with the current 1-based line number */
743 line_num
= sci_get_current_line(doc
->editor
->sci
) + 1;
744 replacement
= g_strdup_printf("%i", line_num
);
745 utils_string_replace_all(stack
, "%l", replacement
);
749 /* replace %p with the current project's (absolute) base directory */
750 replacement
= NULL
; /* prevent double free if no replacement found */
753 replacement
= project_get_base_path();
755 else if (strstr(stack
->str
, "%p"))
756 { /* fall back to %d */
757 ui_set_statusbar(FALSE
, _("failed to substitute %%p, no project active"));
758 if (doc
!= NULL
&& doc
->file_name
!= NULL
)
759 replacement
= g_path_get_dirname(doc
->file_name
);
762 utils_string_replace_all(stack
, "%p", replacement
);
766 return g_string_free(stack
, FALSE
); /* don't forget to free src also if needed */
770 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
771 * idx document directory */
772 static void build_spawn_cmd(GeanyDocument
*doc
, const gchar
*cmd
, const gchar
*dir
)
774 GError
*error
= NULL
;
775 gchar
*argv
[] = { "/bin/sh", "-c", NULL
, NULL
};
777 gchar
*utf8_working_dir
;
780 g_return_if_fail(doc
== NULL
|| doc
->is_valid
);
782 if ((doc
== NULL
|| EMPTY(doc
->file_name
)) && EMPTY(dir
))
784 geany_debug("Failed to run command with no working directory");
785 ui_set_statusbar(TRUE
, _("Process failed, no working directory"));
790 SETPTR(current_dir_entered
, NULL
);
792 utf8_working_dir
= !EMPTY(dir
) ? g_strdup(dir
) : g_path_get_dirname(doc
->file_name
);
793 working_dir
= utils_get_locale_from_utf8(utf8_working_dir
);
795 gtk_list_store_clear(msgwindow
.store_compiler
);
796 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
797 msgwin_compiler_add(COLOR_BLUE
, _("%s (in directory: %s)"), cmd
, utf8_working_dir
);
798 g_free(utf8_working_dir
);
801 cmd_string
= utils_get_locale_from_utf8(cmd
);
802 argv
[2] = cmd_string
;
803 cmd
= NULL
; /* under Unix, use argv to start cmd via sh for compatibility */
805 /* Expand environment variables like %blah%. */
806 cmd_string
= win32_expand_environment_variables(cmd
);
807 argv
[0] = NULL
; /* under Windows, run cmd directly */
811 /* set the build info for the message window */
812 g_free(build_info
.dir
);
813 build_info
.dir
= g_strdup(working_dir
);
814 build_info
.file_type_id
= (doc
== NULL
) ? GEANY_FILETYPES_NONE
: doc
->file_type
->id
;
815 build_info
.message_count
= 0;
817 if (!spawn_with_callbacks(working_dir
, cmd
, argv
, NULL
, 0, NULL
, NULL
, build_iofunc
,
818 GINT_TO_POINTER(0), 0, build_iofunc
, GINT_TO_POINTER(1), 0, build_exit_cb
, NULL
,
819 &build_info
.pid
, &error
))
821 geany_debug("build command spawning failed: %s", error
->message
);
822 ui_set_statusbar(TRUE
, _("Process failed (%s)"), error
->message
);
831 /* Returns: NULL if there was an error, or the command to be executed. If Geany is
832 * set to use a run script, the returned value is a path to the script that runs
833 * the command; otherwise the command itself is returned. working_dir is a pointer
834 * to the working directory from which the command is executed. Both strings are
835 * in the locale encoding. */
836 static gchar
*prepare_run_cmd(GeanyDocument
*doc
, gchar
**working_dir
, guint cmdindex
)
838 GeanyBuildCommand
*cmd
= NULL
;
839 const gchar
*cmd_working_dir
;
840 gboolean autoclose
= FALSE
;
841 gchar
*cmd_string_utf8
, *working_dir_utf8
, *run_cmd
, *cmd_string
;
842 GError
*error
= NULL
;
844 cmd
= get_build_cmd(doc
, GEANY_GBG_EXEC
, cmdindex
, NULL
);
846 cmd_string_utf8
= build_replace_placeholder(doc
, cmd
->command
);
847 cmd_working_dir
= cmd
->working_dir
;
848 if (EMPTY(cmd_working_dir
))
849 cmd_working_dir
= "%d";
850 working_dir_utf8
= build_replace_placeholder(doc
, cmd_working_dir
);
851 *working_dir
= utils_get_locale_from_utf8(working_dir_utf8
);
853 if (EMPTY(*working_dir
) || ! g_file_test(*working_dir
, G_FILE_TEST_EXISTS
) ||
854 ! g_file_test(*working_dir
, G_FILE_TEST_IS_DIR
))
856 ui_set_statusbar(TRUE
, _("Invalid working directory \"%s\""),
857 !EMPTY(working_dir_utf8
) ? working_dir_utf8
: "<NULL>" );
858 utils_free_pointers(3, cmd_string_utf8
, working_dir_utf8
, *working_dir
, NULL
);
862 cmd_string
= utils_get_locale_from_utf8(cmd_string_utf8
);
865 if (vte_info
.have_vte
&& vc
->run_in_vte
)
867 if (vc
->skip_run_script
)
869 utils_free_pointers(2, cmd_string_utf8
, working_dir_utf8
, NULL
);
873 /* don't wait for user input at the end of script when we are running in VTE */
879 /* Expand environment variables like %blah%. */
880 SETPTR(cmd_string
, win32_expand_environment_variables(cmd_string
));
882 gchar
*helper
= g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC
), "geany-run-helper", NULL
);
883 /* escape helper appropriately */
884 /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
885 * allowed in paths anyway */
886 run_cmd
= g_strdup_printf("\"%s\" \"%s\" %d %s", helper
, *working_dir
, autoclose
? 1 : 0, cmd_string
);
889 run_cmd
= build_create_shellscript(*working_dir
, cmd_string
, autoclose
, &error
);
892 ui_set_statusbar(TRUE
, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
893 !EMPTY(cmd_string_utf8
) ? cmd_string_utf8
: NULL
, error
->message
);
895 g_free(*working_dir
);
898 utils_free_pointers(3, cmd_string_utf8
, working_dir_utf8
, cmd_string
, NULL
);
903 static void build_run_cmd(GeanyDocument
*doc
, guint cmdindex
)
906 gchar
*run_cmd
= NULL
;
908 if (! DOC_VALID(doc
) || doc
->file_name
== NULL
)
911 run_cmd
= prepare_run_cmd(doc
, &working_dir
, cmdindex
);
915 run_info
[cmdindex
].file_type_id
= doc
->file_type
->id
;
918 if (vte_info
.have_vte
&& vc
->run_in_vte
)
922 /* VTE expects commands in UTF-8 */
923 SETPTR(run_cmd
, utils_get_utf8_from_locale(run_cmd
));
924 SETPTR(working_dir
, utils_get_utf8_from_locale(working_dir
));
926 if (vc
->skip_run_script
)
927 vte_cmd
= g_strconcat(run_cmd
, "\n", NULL
);
929 vte_cmd
= g_strconcat("\n/bin/sh ", run_cmd
, "\n", NULL
);
931 vte_cwd(working_dir
, TRUE
);
932 if (! vte_send_cmd(vte_cmd
))
934 const gchar
*msg
= _("File not executed because the terminal may contain some input (press Ctrl+C or Enter to clear it).");
935 ui_set_statusbar(FALSE
, "%s", msg
);
936 geany_debug("%s", msg
);
937 if (!vc
->skip_run_script
)
942 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_VTE
);
943 gtk_widget_grab_focus(vc
->vte
);
944 msgwin_show_hide(TRUE
);
946 run_info
[cmdindex
].pid
= 1;
952 gchar
*locale_term_cmd
= utils_get_locale_from_utf8(tool_prefs
.term_cmd
);
953 GError
*error
= NULL
;
956 if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd
, 0, 0))
958 /* if passing an argument to cmd.exe, respect its quoting rules */
959 GString
*escaped_run_cmd
= g_string_new(NULL
);
960 for (gchar
*p
= run_cmd
; *p
; p
++)
962 if (strchr("()%!^\"<>&| ", *p
)) // cmd.exe metacharacters
963 g_string_append_c(escaped_run_cmd
, '^');
964 g_string_append_c(escaped_run_cmd
, *p
);
966 SETPTR(run_cmd
, g_string_free(escaped_run_cmd
, FALSE
));
970 utils_str_replace_all(&locale_term_cmd
, "%c", run_cmd
);
972 if (spawn_async(working_dir
, locale_term_cmd
, NULL
, NULL
, &(run_info
[cmdindex
].pid
),
975 g_child_watch_add(run_info
[cmdindex
].pid
, (GChildWatchFunc
) run_exit_cb
,
976 (gpointer
) &(run_info
[cmdindex
]));
977 build_menu_update(doc
);
981 gchar
*utf8_term_cmd
= utils_get_utf8_from_locale(locale_term_cmd
);
982 ui_set_statusbar(TRUE
, _("Cannot execute build command \"%s\": %s. "
983 "Check the Terminal setting in Preferences"), utf8_term_cmd
, error
->message
);
984 g_free(utf8_term_cmd
);
989 run_info
[cmdindex
].pid
= (GPid
) 0;
998 static void process_build_output_line(gchar
*msg
, gint color
)
1009 if (build_parse_make_dir(msg
, &tmp
))
1011 SETPTR(current_dir_entered
, tmp
);
1013 msgwin_parse_compiler_error_line(msg
, current_dir_entered
, &filename
, &line
);
1015 if (line
!= -1 && filename
!= NULL
)
1017 GeanyDocument
*doc
= document_find_by_filename(filename
);
1019 /* limit number of indicators */
1020 if (doc
&& editor_prefs
.use_indicators
&&
1021 build_info
.message_count
< GEANY_BUILD_ERR_HIGHLIGHT_MAX
)
1023 if (line
> 0) /* some compilers, like pdflatex report errors on line 0 */
1024 line
--; /* so only adjust the line number if it is greater than 0 */
1025 editor_indicator_set_on_line(doc
->editor
, GEANY_INDICATOR_ERROR
, line
);
1027 build_info
.message_count
++;
1028 color
= COLOR_RED
; /* error message parsed on the line */
1032 msgwin_compiler_add_string(color
, msg
);
1036 static void build_iofunc(GString
*string
, GIOCondition condition
, gpointer data
)
1038 if (condition
& (G_IO_IN
| G_IO_PRI
))
1040 process_build_output_line(string
->str
,
1041 (GPOINTER_TO_INT(data
)) ? COLOR_DARK_RED
: COLOR_BLACK
);
1046 gboolean
build_parse_make_dir(const gchar
*string
, gchar
**prefix
)
1055 if ((pos
= strstr(string
, "Entering directory")) != NULL
)
1060 /* get the start of the path */
1061 pos
= strstr(string
, "/");
1066 input
= g_strdup(pos
);
1068 /* kill the ' at the end of the path */
1069 len
= strlen(input
);
1070 input
[len
- 1] = '\0';
1071 input
= g_realloc(input
, len
); /* shorten by 1 */
1077 if (strstr(string
, "Leaving directory") != NULL
)
1087 static void show_build_result_message(gboolean failure
)
1093 msg
= _("Compilation failed.");
1094 msgwin_compiler_add_string(COLOR_BLUE
, msg
);
1095 /* If msgwindow is hidden, user will want to display it to see the error */
1096 if (! ui_prefs
.msgwindow_visible
)
1098 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
1099 msgwin_show_hide(TRUE
);
1102 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow
.notebook
)) != MSG_COMPILER
)
1103 ui_set_statusbar(FALSE
, "%s", msg
);
1107 msg
= _("Compilation finished successfully.");
1108 msgwin_compiler_add_string(COLOR_BLUE
, msg
);
1109 if (! ui_prefs
.msgwindow_visible
||
1110 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow
.notebook
)) != MSG_COMPILER
)
1111 ui_set_statusbar(FALSE
, "%s", msg
);
1116 static void build_exit_cb(GPid child_pid
, gint status
, gpointer user_data
)
1118 show_build_result_message(!SPAWN_WIFEXITED(status
) || SPAWN_WEXITSTATUS(status
) != EXIT_SUCCESS
);
1122 /* enable build items again */
1123 build_menu_update(NULL
);
1124 ui_progress_bar_stop();
1128 static void run_exit_cb(GPid child_pid
, gint status
, gpointer user_data
)
1130 RunInfo
*run_info_data
= user_data
;
1132 g_spawn_close_pid(child_pid
);
1134 run_info_data
->pid
= 0;
1135 /* reset the stop button and menu item to the original meaning */
1136 build_menu_update(NULL
);
1140 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1141 * working_dir and cmd are both in the locale encoding
1142 * it returns the full file name (including path) of the created script in the locale encoding */
1144 static gchar
*build_create_shellscript(const gchar
*working_dir
, const gchar
*cmd
, gboolean autoclose
, GError
**error
)
1148 gboolean success
= TRUE
;
1150 fd
= g_file_open_tmp (RUN_SCRIPT_CMD
, &fname
, error
);
1155 escaped_dir
= g_shell_quote(working_dir
);
1156 str
= g_strdup_printf(
1157 "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1158 \n\n%s\n", escaped_dir
, cmd
, (autoclose
) ? "" :
1159 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1160 "dash\ndummy_var=\"\"\nread dummy_var");
1161 g_free(escaped_dir
);
1163 if (!g_file_set_contents(fname
, str
, -1, error
))
1167 if (success
&& g_chmod(fname
, 0777) != 0)
1173 g_set_error(error
, G_FILE_ERROR
, g_file_error_from_errno(errsv
),
1174 "Failed to make file executable: %s", g_strerror(errsv
));
1192 typedef void Callback(GtkWidget
*w
, gpointer u
);
1194 /* run the command catenating cmd_cat if present */
1195 static void build_command(GeanyDocument
*doc
, GeanyBuildGroup grp
, guint cmd
, gchar
*cmd_cat
)
1198 gchar
*full_command
, *subs_command
;
1199 GeanyBuildCommand
*buildcmd
= get_build_cmd(doc
, grp
, cmd
, NULL
);
1202 if (buildcmd
== NULL
)
1205 cmdstr
= buildcmd
->command
;
1207 if (cmd_cat
!= NULL
)
1210 full_command
= g_strconcat(cmdstr
, cmd_cat
, NULL
);
1212 full_command
= g_strdup(cmd_cat
);
1215 full_command
= cmdstr
;
1217 dir
= build_replace_placeholder(doc
, buildcmd
->working_dir
);
1218 subs_command
= build_replace_placeholder(doc
, full_command
);
1219 build_info
.grp
= grp
;
1220 build_info
.cmd
= cmd
;
1221 build_spawn_cmd(doc
, subs_command
, dir
);
1222 g_free(subs_command
);
1224 if (cmd_cat
!= NULL
)
1225 g_free(full_command
);
1226 build_menu_update(doc
);
1228 ui_progress_bar_start(NULL
);
1232 /*----------------------------------------------------------------
1234 * Create build menu and handle callbacks (&toolbar callbacks)
1236 *----------------------------------------------------------------*/
1237 static void on_make_custom_input_response(const gchar
*input
, gpointer data
)
1239 GeanyDocument
*doc
= document_get_current();
1241 SETPTR(build_info
.custom_target
, g_strdup(input
));
1242 build_command(doc
, GBO_TO_GBG(GEANY_GBO_CUSTOM
), GBO_TO_CMD(GEANY_GBO_CUSTOM
),
1243 build_info
.custom_target
);
1247 static void on_build_menu_item(GtkWidget
*w
, gpointer user_data
)
1249 GeanyDocument
*doc
= document_get_current();
1250 GeanyBuildCommand
*bc
;
1251 guint grp
= GPOINTER_TO_GRP(user_data
);
1252 guint cmd
= GPOINTER_TO_CMD(user_data
);
1254 if (doc
&& doc
->changed
)
1256 if (!document_save_file(doc
, FALSE
))
1259 g_signal_emit_by_name(geany_object
, "build-start");
1261 if (grp
== GEANY_GBG_NON_FT
&& cmd
== GBO_TO_CMD(GEANY_GBO_CUSTOM
))
1263 static GtkWidget
*dialog
= NULL
; /* keep dialog for combo history */
1267 dialog
= dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets
.window
),
1268 _("Enter custom text here, all entered text is appended to the command."),
1269 build_info
.custom_target
, &on_make_custom_input_response
, NULL
);
1273 gtk_widget_show(dialog
);
1277 else if (grp
== GEANY_GBG_EXEC
)
1279 if (run_info
[cmd
].pid
> (GPid
) 1)
1281 kill_process(&run_info
[cmd
].pid
);
1284 bc
= get_build_cmd(doc
, grp
, cmd
, NULL
);
1285 if (bc
!= NULL
&& strcmp(bc
->command
, "builtin") == 0)
1287 const gchar
*uri_file_prefix
;
1291 uri_file_prefix
= utils_get_uri_file_prefix();
1292 uri
= g_strconcat(uri_file_prefix
, doc
->file_name
, NULL
);
1293 utils_open_browser(uri
);
1297 build_run_cmd(doc
, cmd
);
1300 build_command(doc
, grp
, cmd
, NULL
);
1304 /* group codes for menu items other than the known commands
1305 * value order is important, see the following table for use */
1307 /* the rest in each group */
1308 #define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1309 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1310 #define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1312 #define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1313 /* the fixed items */
1314 #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1315 #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1316 #define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1317 #define MENU_DONE (MENU_COMMANDS + 1)
1320 static struct BuildMenuItemSpec
{
1321 const gchar
*stock_id
;
1322 const gint key_binding
;
1323 const guint build_grp
;
1324 const guint build_cmd
;
1325 const gchar
*fix_label
;
1327 } build_menu_specs
[] = {
1328 {GTK_STOCK_CONVERT
, GEANY_KEYS_BUILD_COMPILE
, GBO_TO_GBG(GEANY_GBO_COMPILE
),
1329 GBO_TO_CMD(GEANY_GBO_COMPILE
), NULL
, on_build_menu_item
},
1330 {GEANY_STOCK_BUILD
, GEANY_KEYS_BUILD_LINK
, GBO_TO_GBG(GEANY_GBO_BUILD
),
1331 GBO_TO_CMD(GEANY_GBO_BUILD
), NULL
, on_build_menu_item
},
1332 {NULL
, -1, MENU_FT_REST
,
1333 GBO_TO_CMD(GEANY_GBO_BUILD
) + 1, NULL
, on_build_menu_item
},
1334 {NULL
, -1, MENU_SEPARATOR
,
1335 GBF_SEP_1
, NULL
, NULL
},
1336 {NULL
, GEANY_KEYS_BUILD_MAKE
, GBO_TO_GBG(GEANY_GBO_MAKE_ALL
),
1337 GBO_TO_CMD(GEANY_GBO_MAKE_ALL
), NULL
, on_build_menu_item
},
1338 {NULL
, GEANY_KEYS_BUILD_MAKEOWNTARGET
, GBO_TO_GBG(GEANY_GBO_CUSTOM
),
1339 GBO_TO_CMD(GEANY_GBO_CUSTOM
), NULL
, on_build_menu_item
},
1340 {NULL
, GEANY_KEYS_BUILD_MAKEOBJECT
, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT
),
1341 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
), NULL
, on_build_menu_item
},
1342 {NULL
, -1, MENU_NON_FT_REST
,
1343 GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
) + 1, NULL
, on_build_menu_item
},
1344 {NULL
, -1, MENU_SEPARATOR
,
1345 GBF_SEP_2
, NULL
, NULL
},
1346 {GTK_STOCK_GO_DOWN
, GEANY_KEYS_BUILD_NEXTERROR
, MENU_NEXT_ERROR
,
1347 GBF_NEXT_ERROR
, N_("_Next Error"), on_build_next_error
},
1348 {GTK_STOCK_GO_UP
, GEANY_KEYS_BUILD_PREVIOUSERROR
, MENU_PREV_ERROR
,
1349 GBF_PREV_ERROR
, N_("_Previous Error"), on_build_previous_error
},
1350 {NULL
, -1, MENU_SEPARATOR
,
1351 GBF_SEP_3
, NULL
, NULL
},
1352 {GTK_STOCK_EXECUTE
, GEANY_KEYS_BUILD_RUN
, GBO_TO_GBG(GEANY_GBO_EXEC
),
1353 GBO_TO_CMD(GEANY_GBO_EXEC
), NULL
, on_build_menu_item
},
1354 {NULL
, -1, MENU_EXEC_REST
,
1355 GBO_TO_CMD(GEANY_GBO_EXEC
) + 1, NULL
, on_build_menu_item
},
1356 {NULL
, -1, MENU_SEPARATOR
,
1357 GBF_SEP_4
, NULL
, NULL
},
1358 {GTK_STOCK_PREFERENCES
, GEANY_KEYS_BUILD_OPTIONS
, MENU_COMMANDS
,
1359 GBF_COMMANDS
, N_("_Set Build Commands"), on_set_build_commands_activate
},
1360 {NULL
, -1, MENU_DONE
,
1365 static void create_build_menu_item(GtkWidget
*menu
, GeanyKeyGroup
*group
, GtkAccelGroup
*ag
,
1366 struct BuildMenuItemSpec
*bs
, const gchar
*lbl
, guint grp
, guint cmd
)
1368 GtkWidget
*item
= gtk_image_menu_item_new_with_mnemonic(lbl
);
1370 if (bs
->stock_id
!= NULL
)
1372 GtkWidget
*image
= gtk_image_new_from_stock(bs
->stock_id
, GTK_ICON_SIZE_MENU
);
1373 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
), image
);
1375 gtk_widget_show(item
);
1376 if (bs
->key_binding
>= 0)
1377 add_menu_accel(group
, (guint
) bs
->key_binding
, ag
, item
);
1378 gtk_container_add(GTK_CONTAINER(menu
), item
);
1381 g_signal_connect(item
, "activate", G_CALLBACK(bs
->cb
), GRP_CMD_TO_POINTER(grp
,cmd
));
1383 menu_items
.menu_item
[grp
][cmd
] = item
;
1387 static void create_build_menu(BuildMenuItems
*build_menu_items
)
1390 GtkAccelGroup
*accel_group
= gtk_accel_group_new();
1391 GeanyKeyGroup
*keygroup
= keybindings_get_core_group(GEANY_KEY_GROUP_BUILD
);
1394 menu
= gtk_menu_new();
1395 build_menu_items
->menu_item
[GEANY_GBG_FT
] = g_new0(GtkWidget
*, build_groups_count
[GEANY_GBG_FT
]);
1396 build_menu_items
->menu_item
[GEANY_GBG_NON_FT
] = g_new0(GtkWidget
*, build_groups_count
[GEANY_GBG_NON_FT
]);
1397 build_menu_items
->menu_item
[GEANY_GBG_EXEC
] = g_new0(GtkWidget
*, build_groups_count
[GEANY_GBG_EXEC
]);
1398 build_menu_items
->menu_item
[GBG_FIXED
] = g_new0(GtkWidget
*, GBF_COUNT
);
1400 for (i
= 0; build_menu_specs
[i
].build_grp
!= MENU_DONE
; ++i
)
1402 struct BuildMenuItemSpec
*bs
= &(build_menu_specs
[i
]);
1403 if (bs
->build_grp
== MENU_SEPARATOR
)
1405 GtkWidget
*item
= gtk_separator_menu_item_new();
1406 gtk_widget_show(item
);
1407 gtk_container_add(GTK_CONTAINER(menu
), item
);
1408 build_menu_items
->menu_item
[GBG_FIXED
][bs
->build_cmd
] = item
;
1410 else if (bs
->fix_label
!= NULL
)
1412 create_build_menu_item(menu
, keygroup
, accel_group
, bs
, _(bs
->fix_label
),
1413 GBG_FIXED
, bs
->build_cmd
);
1415 else if (bs
->build_grp
>= MENU_FT_REST
&& bs
->build_grp
<= MENU_SEPARATOR
)
1417 guint grp
= bs
->build_grp
- GEANY_GBG_COUNT
;
1418 for (j
= bs
->build_cmd
; j
< build_groups_count
[grp
]; ++j
)
1420 GeanyBuildCommand
*bc
= get_build_cmd(NULL
, grp
, j
, NULL
);
1421 const gchar
*lbl
= (bc
== NULL
) ? "" : bc
->label
;
1422 create_build_menu_item(menu
, keygroup
, accel_group
, bs
, lbl
, grp
, j
);
1427 GeanyBuildCommand
*bc
= get_build_cmd(NULL
, bs
->build_grp
, bs
->build_cmd
, NULL
);
1428 const gchar
*lbl
= (bc
== NULL
) ? "" : bc
->label
;
1429 create_build_menu_item(menu
, keygroup
, accel_group
, bs
, lbl
, bs
->build_grp
, bs
->build_cmd
);
1432 build_menu_items
->menu
= menu
;
1433 gtk_widget_show(menu
);
1434 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_build1")), menu
);
1438 /* * Update the build menu to reflect changes in configuration or status.
1440 * Sets the labels and number of visible items to match the highest
1441 * priority configured commands. Also sets sensitivity if build commands are
1442 * running and switches executes to stop when commands are running.
1444 * @param doc The current document, if available, to save looking it up.
1445 * If @c NULL it will be looked up.
1447 * Call this after modifying any fields of a GeanyBuildCommand structure.
1449 * @see Build Menu Configuration section of the Manual.
1452 void build_menu_update(GeanyDocument
*doc
)
1454 guint i
, cmdcount
, cmd
, grp
;
1455 gboolean vis
= FALSE
;
1456 gboolean have_path
, build_running
, exec_running
, have_errors
, cmd_sensitivity
;
1457 gboolean can_compile
, can_build
, can_make
, run_sensitivity
= FALSE
, run_running
= FALSE
;
1458 GeanyBuildCommand
*bc
;
1460 g_return_if_fail(doc
== NULL
|| doc
->is_valid
);
1462 if (menu_items
.menu
== NULL
)
1463 create_build_menu(&menu_items
);
1465 doc
= document_get_current();
1466 have_path
= doc
!= NULL
&& doc
->file_name
!= NULL
;
1467 build_running
= build_info
.pid
> (GPid
) 1;
1468 have_errors
= gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow
.store_compiler
), NULL
) > 0;
1469 for (i
= 0; build_menu_specs
[i
].build_grp
!= MENU_DONE
; ++i
)
1471 struct BuildMenuItemSpec
*bs
= &(build_menu_specs
[i
]);
1472 switch (bs
->build_grp
)
1474 case MENU_SEPARATOR
:
1477 gtk_widget_show_all(menu_items
.menu_item
[GBG_FIXED
][bs
->build_cmd
]);
1481 gtk_widget_hide(menu_items
.menu_item
[GBG_FIXED
][bs
->build_cmd
]);
1483 case MENU_NEXT_ERROR
:
1484 case MENU_PREV_ERROR
:
1485 gtk_widget_set_sensitive(menu_items
.menu_item
[GBG_FIXED
][bs
->build_cmd
], have_errors
);
1491 default: /* all configurable commands */
1492 if (bs
->build_grp
>= GEANY_GBG_COUNT
)
1494 grp
= bs
->build_grp
- GEANY_GBG_COUNT
;
1495 cmdcount
= build_groups_count
[grp
];
1499 grp
= bs
->build_grp
;
1500 cmdcount
= bs
->build_cmd
+ 1;
1502 for (cmd
= bs
->build_cmd
; cmd
< cmdcount
; ++cmd
)
1504 GtkWidget
*menu_item
= menu_items
.menu_item
[grp
][cmd
];
1506 bc
= get_build_cmd(doc
, grp
, cmd
, NULL
);
1512 if (grp
< GEANY_GBG_EXEC
)
1515 (grp
== GEANY_GBG_FT
&& bc
!= NULL
&& have_path
&& ! build_running
) ||
1516 (grp
== GEANY_GBG_NON_FT
&& bc
!= NULL
&& ! build_running
);
1517 gtk_widget_set_sensitive(menu_item
, cmd_sensitivity
);
1518 if (bc
!= NULL
&& !EMPTY(label
))
1520 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item
), label
);
1521 gtk_widget_show_all(menu_item
);
1525 gtk_widget_hide(menu_item
);
1530 exec_running
= run_info
[cmd
].pid
> (GPid
) 1;
1531 cmd_sensitivity
= (bc
!= NULL
) || exec_running
;
1532 gtk_widget_set_sensitive(menu_item
, cmd_sensitivity
);
1533 if (cmd
== GBO_TO_CMD(GEANY_GBO_EXEC
))
1534 run_sensitivity
= cmd_sensitivity
;
1537 image
= gtk_image_new_from_stock(bs
->stock_id
, GTK_ICON_SIZE_MENU
);
1541 image
= gtk_image_new_from_stock(GTK_STOCK_STOP
, GTK_ICON_SIZE_MENU
);
1543 if (cmd
== GBO_TO_CMD(GEANY_GBO_EXEC
))
1544 run_running
= exec_running
;
1545 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item
), image
);
1546 if (bc
!= NULL
&& !EMPTY(label
))
1548 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item
), label
);
1549 gtk_widget_show_all(menu_item
);
1553 gtk_widget_hide(menu_item
);
1559 run_sensitivity
&= (doc
!= NULL
);
1560 can_build
= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_BUILD
), NULL
) != NULL
1561 && have_path
&& ! build_running
;
1562 if (widgets
.toolitem_build
!= NULL
)
1563 gtk_widget_set_sensitive(widgets
.toolitem_build
, can_build
);
1565 if (widgets
.toolitem_make_all
!= NULL
)
1566 gtk_widget_set_sensitive(widgets
.toolitem_make_all
,
1567 (can_make
|= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_MAKE_ALL
), NULL
) != NULL
1568 && ! build_running
));
1569 if (widgets
.toolitem_make_custom
!= NULL
)
1570 gtk_widget_set_sensitive(widgets
.toolitem_make_custom
,
1571 (can_make
|= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_CUSTOM
), NULL
) != NULL
1572 && ! build_running
));
1573 if (widgets
.toolitem_make_object
!= NULL
)
1574 gtk_widget_set_sensitive(widgets
.toolitem_make_object
,
1575 (can_make
|= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
), NULL
) != NULL
1576 && ! build_running
));
1577 if (widgets
.toolitem_set_args
!= NULL
)
1578 gtk_widget_set_sensitive(widgets
.toolitem_set_args
, TRUE
);
1580 can_compile
= get_build_cmd(doc
, GEANY_GBG_FT
, GBO_TO_CMD(GEANY_GBO_COMPILE
), NULL
) != NULL
1581 && have_path
&& ! build_running
;
1582 gtk_action_set_sensitive(widgets
.compile_action
, can_compile
);
1583 gtk_action_set_sensitive(widgets
.build_action
, can_make
);
1584 gtk_action_set_sensitive(widgets
.run_action
, run_sensitivity
);
1586 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1587 set_stop_button(run_running
);
1592 /* Call build_menu_update() instead of calling this directly. */
1593 static void set_stop_button(gboolean stop
)
1595 const gchar
*button_stock_id
= NULL
;
1596 GtkToolButton
*run_button
;
1598 run_button
= GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1599 if (run_button
!= NULL
)
1600 button_stock_id
= gtk_tool_button_get_stock_id(run_button
);
1602 if (stop
&& utils_str_equal(button_stock_id
, GTK_STOCK_STOP
))
1604 if (! stop
&& utils_str_equal(button_stock_id
, GTK_STOCK_EXECUTE
))
1607 /* use the run button also as stop button */
1610 if (run_button
!= NULL
)
1611 gtk_tool_button_set_stock_id(run_button
, GTK_STOCK_STOP
);
1615 if (run_button
!= NULL
)
1616 gtk_tool_button_set_stock_id(run_button
, GTK_STOCK_EXECUTE
);
1621 static void on_set_build_commands_activate(GtkWidget
*w
, gpointer u
)
1623 /* For now, just show the project dialog */
1625 project_build_properties();
1627 show_build_commands_dialog();
1631 static void on_toolbutton_build_activate(GtkWidget
*menuitem
, gpointer user_data
)
1633 last_toolbutton_action
= user_data
;
1634 g_object_set(widgets
.build_action
, "tooltip", _("Build the current file"), NULL
);
1635 on_build_menu_item(menuitem
, user_data
);
1639 static void on_toolbutton_make_activate(GtkWidget
*menuitem
, gpointer user_data
)
1643 last_toolbutton_action
= user_data
;
1644 if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_MAKE_ALL
))
1645 msg
= _("Build the current file with Make and the default target");
1646 else if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_CUSTOM
))
1647 msg
= _("Build the current file with Make and the specified target");
1648 else if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT
))
1649 msg
= _("Compile the current file with Make");
1652 g_object_set(widgets
.build_action
, "tooltip", msg
, NULL
);
1653 on_build_menu_item(menuitem
, user_data
);
1657 static void kill_process(GPid
*pid
)
1659 GError
*error
= NULL
;
1661 if (spawn_kill_process(*pid
, &error
))
1664 build_menu_update(NULL
);
1668 ui_set_statusbar(TRUE
, _("Process could not be stopped (%s)."), error
->message
);
1669 g_error_free(error
);
1674 static void on_build_next_error(GtkWidget
*menuitem
, gpointer user_data
)
1676 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow
.tree_compiler
),
1677 msgwin_goto_compiler_file_line
))
1679 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
1682 ui_set_statusbar(FALSE
, _("No more build errors."));
1686 static void on_build_previous_error(GtkWidget
*menuitem
, gpointer user_data
)
1688 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow
.tree_compiler
),
1689 msgwin_goto_compiler_file_line
))
1691 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_COMPILER
);
1694 ui_set_statusbar(FALSE
, _("No more build errors."));
1698 void build_toolbutton_build_clicked(GtkAction
*action
, gpointer unused
)
1700 if (last_toolbutton_action
== GBO_TO_POINTER(GEANY_GBO_BUILD
))
1702 on_build_menu_item(NULL
, GBO_TO_POINTER(GEANY_GBO_BUILD
));
1706 on_build_menu_item(NULL
, last_toolbutton_action
);
1711 /*------------------------------------------------------
1713 * Create and handle the build menu configuration dialog
1715 *-------------------------------------------------------*/
1716 typedef struct RowWidgets
1718 GtkWidget
*entries
[GEANY_BC_CMDENTRIES_COUNT
];
1719 GeanyBuildSource src
;
1720 GeanyBuildSource dst
;
1721 GeanyBuildCommand
*cmdsrc
;
1728 #if GTK_CHECK_VERSION(3,0,0)
1729 typedef GdkRGBA InsensitiveColor
;
1731 typedef GdkColor InsensitiveColor
;
1733 static InsensitiveColor insensitive_color
;
1735 static void set_row_color(RowWidgets
*r
, InsensitiveColor
*color
)
1737 enum GeanyBuildCmdEntries i
;
1739 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1741 if (i
== GEANY_BC_LABEL
)
1744 #if GTK_CHECK_VERSION(3,0,0)
1745 gtk_widget_override_color(r
->entries
[i
], GTK_STATE_FLAG_NORMAL
, color
);
1747 gtk_widget_modify_text(r
->entries
[i
], GTK_STATE_NORMAL
, color
);
1753 static void set_build_command_entry_text(GtkWidget
*wid
, const gchar
*text
)
1755 if (GTK_IS_BUTTON(wid
))
1756 gtk_button_set_label(GTK_BUTTON(wid
), text
);
1758 gtk_entry_set_text(GTK_ENTRY(wid
), text
);
1762 static void on_clear_dialog_row(GtkWidget
*unused
, gpointer user_data
)
1764 RowWidgets
*r
= user_data
;
1766 enum GeanyBuildCmdEntries i
;
1767 GeanyBuildCommand
*bc
= get_next_build_cmd(NULL
, r
->grp
, r
->cmd
, r
->dst
, &src
);
1773 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1775 set_build_command_entry_text(r
->entries
[i
],
1776 id_to_str(bc
,i
) != NULL
? id_to_str(bc
,i
) : "");
1782 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1784 set_build_command_entry_text(r
->entries
[i
], "");
1787 r
->used_dst
= FALSE
;
1788 set_row_color(r
, &insensitive_color
);
1793 static void on_clear_dialog_regex_row(GtkEntry
*regex
, gpointer unused
)
1795 gtk_entry_set_text(regex
,"");
1799 static void on_label_button_clicked(GtkWidget
*wid
, gpointer user_data
)
1801 RowWidgets
*r
= user_data
;
1802 GtkWidget
*top_level
= gtk_widget_get_toplevel(wid
);
1803 const gchar
*old
= gtk_button_get_label(GTK_BUTTON(wid
));
1806 if (gtk_widget_is_toplevel(top_level
) && GTK_IS_WINDOW(top_level
))
1807 str
= dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level
), NULL
, old
);
1809 str
= dialogs_show_input(_("Set menu item label"), NULL
, NULL
, old
);
1814 gtk_button_set_label(GTK_BUTTON(wid
), str
);
1817 set_row_color(r
, NULL
);
1821 static void on_entry_focus(GtkWidget
*wid
, GdkEventFocus
*unused
, gpointer user_data
)
1823 RowWidgets
*r
= user_data
;
1826 set_row_color(r
, NULL
);
1830 /* Column headings, array NULL-terminated */
1831 static const gchar
*colheads
[] =
1836 N_("Working directory"),
1843 #define DC_ENTRIES 1
1847 static const guint entry_x_padding
= 3;
1848 static const guint entry_y_padding
= 0;
1851 static RowWidgets
*build_add_dialog_row(GeanyDocument
*doc
, GtkTable
*table
, guint row
,
1852 GeanyBuildSource dst
, guint grp
, guint cmd
, gboolean dir
)
1854 GtkWidget
*label
, *clear
, *clearicon
;
1856 GeanyBuildCommand
*bc
;
1858 enum GeanyBuildCmdEntries i
;
1862 g_return_val_if_fail(doc
== NULL
|| doc
->is_valid
, NULL
);
1864 text
= g_strdup_printf("%d.", cmd
+ 1);
1865 label
= gtk_label_new(text
);
1867 #if GTK_CHECK_VERSION(3,0,0)
1869 GtkStyleContext
*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
);
1876 insensitive_color
= gtk_widget_get_style(label
)->text
[GTK_STATE_INSENSITIVE
];
1878 gtk_table_attach(table
, label
, column
, column
+ 1, row
, row
+ 1, GTK_FILL
,
1879 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
1880 roww
= g_new0(RowWidgets
, 1);
1881 roww
->src
= GEANY_BCS_COUNT
;
1885 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1887 gint xflags
= (i
== GEANY_BC_COMMAND
) ? GTK_FILL
| GTK_EXPAND
: GTK_FILL
;
1890 if (i
== GEANY_BC_LABEL
)
1892 GtkWidget
*wid
= roww
->entries
[i
] = gtk_button_new();
1893 gtk_button_set_use_underline(GTK_BUTTON(wid
), TRUE
);
1894 gtk_widget_set_tooltip_text(wid
, _("Click to set menu item label"));
1895 g_signal_connect(wid
, "clicked", G_CALLBACK(on_label_button_clicked
), roww
);
1899 roww
->entries
[i
] = gtk_entry_new();
1900 g_signal_connect(roww
->entries
[i
], "focus-in-event", G_CALLBACK(on_entry_focus
), roww
);
1902 gtk_table_attach(table
, roww
->entries
[i
], column
, column
+ 1, row
, row
+ 1, xflags
,
1903 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
1906 clearicon
= gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
);
1907 clear
= gtk_button_new();
1908 gtk_button_set_image(GTK_BUTTON(clear
), clearicon
);
1909 g_signal_connect(clear
, "clicked", G_CALLBACK(on_clear_dialog_row
), roww
);
1910 gtk_table_attach(table
, clear
, column
, column
+ 1, row
, row
+ 1, GTK_FILL
,
1911 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
1912 roww
->cmdsrc
= bc
= get_build_cmd(doc
, grp
, cmd
, &src
);
1916 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1918 const gchar
*str
= "";
1922 if ((str
= id_to_str(bc
, i
)) == NULL
)
1924 else if (dst
== src
)
1925 roww
->used_dst
= TRUE
;
1927 set_build_command_entry_text(roww
->entries
[i
], str
);
1929 if (bc
!= NULL
&& (dst
> src
))
1930 set_row_color(roww
, &insensitive_color
);
1931 if (bc
!= NULL
&& (src
> dst
|| (grp
== GEANY_GBG_FT
&& (doc
== NULL
|| doc
->file_type
== NULL
))))
1933 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
1934 gtk_widget_set_sensitive(roww
->entries
[i
], FALSE
);
1935 gtk_widget_set_sensitive(clear
, FALSE
);
1941 typedef struct BuildTableFields
1944 GtkWidget
*fileregex
;
1945 GtkWidget
*nonfileregex
;
1946 gchar
**fileregexstring
;
1947 gchar
**nonfileregexstring
;
1951 GtkWidget
*build_commands_table(GeanyDocument
*doc
, GeanyBuildSource dst
, BuildTableData
*table_data
,
1954 GtkWidget
*label
, *sep
, *clearicon
, *clear
;
1955 BuildTableFields
*fields
;
1959 guint col
, row
, cmdindex
;
1962 gboolean sensitivity
;
1963 guint sep_padding
= entry_y_padding
+ 3;
1965 table
= GTK_TABLE(gtk_table_new(build_items_count
+ 12, 5, FALSE
));
1966 fields
= g_new0(BuildTableFields
, 1);
1967 fields
->rows
= g_new0(RowWidgets
*, build_items_count
);
1968 for (ch
= colheads
, col
= 0; *ch
!= NULL
; ch
++, col
++)
1970 label
= gtk_label_new(_(*ch
));
1971 gtk_table_attach(table
, label
, col
, col
+ 1, 0, 1,
1972 GTK_FILL
, GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
1974 sep
= gtk_hseparator_new();
1975 gtk_table_attach(table
, sep
, 0, DC_N_COL
, 1, 2, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
1976 entry_x_padding
, sep_padding
);
1977 if (ft
!= NULL
&& ft
->id
!= GEANY_FILETYPES_NONE
)
1978 txt
= g_strdup_printf(_("%s commands"), ft
->name
);
1980 txt
= g_strdup_printf(_("%s commands"), _("No filetype"));
1982 label
= ui_label_new_bold(txt
);
1984 gtk_misc_set_alignment(GTK_MISC(label
), 0.0, 0.5);
1985 gtk_table_attach(table
, label
, 0, DC_N_COL
, 2, 3, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
1986 entry_x_padding
, entry_y_padding
);
1987 for (row
= 3, cmdindex
= 0, cmd
= 0; cmd
< build_groups_count
[GEANY_GBG_FT
]; ++row
, ++cmdindex
, ++cmd
)
1988 fields
->rows
[cmdindex
] = build_add_dialog_row(doc
, table
, row
, dst
, GEANY_GBG_FT
, cmd
, FALSE
);
1989 label
= gtk_label_new(_("Error regular expression:"));
1990 gtk_table_attach(table
, label
, 0, DC_ENTRIES
+ 1, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
1991 entry_x_padding
, entry_y_padding
);
1992 fields
->fileregex
= gtk_entry_new();
1993 fields
->fileregexstring
= build_get_regex(GEANY_GBG_FT
, NULL
, &src
);
1994 sensitivity
= (ft
== NULL
) ? FALSE
: TRUE
;
1995 if (fields
->fileregexstring
!= NULL
&& *(fields
->fileregexstring
) != NULL
)
1997 gtk_entry_set_text(GTK_ENTRY(fields
->fileregex
), *(fields
->fileregexstring
));
1999 sensitivity
= FALSE
;
2001 gtk_table_attach(table
, fields
->fileregex
, DC_ENTRIES
+ 1, DC_CLEAR
, row
, row
+ 1, GTK_FILL
,
2002 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
2003 clearicon
= gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
);
2004 clear
= gtk_button_new();
2005 gtk_button_set_image(GTK_BUTTON(clear
), clearicon
);
2006 g_signal_connect_swapped(clear
, "clicked",
2007 G_CALLBACK(on_clear_dialog_regex_row
), (fields
->fileregex
));
2008 gtk_table_attach(table
, clear
, DC_CLEAR
, DC_CLEAR
+ 1, row
, row
+ 1, GTK_FILL
,
2009 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
2010 gtk_widget_set_sensitive(fields
->fileregex
, sensitivity
);
2011 gtk_widget_set_sensitive(clear
, sensitivity
);
2013 sep
= gtk_hseparator_new();
2014 gtk_table_attach(table
, sep
, 0, DC_N_COL
, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
2015 entry_x_padding
, sep_padding
);
2017 label
= ui_label_new_bold(_("Independent commands"));
2018 gtk_misc_set_alignment(GTK_MISC(label
), 0.0, 0.5);
2019 gtk_table_attach(table
, label
, 0, DC_N_COL
, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
2020 entry_x_padding
, entry_y_padding
);
2021 for (++row
, cmd
= 0; cmd
< build_groups_count
[GEANY_GBG_NON_FT
]; ++row
, ++cmdindex
, ++cmd
)
2022 fields
->rows
[cmdindex
] = build_add_dialog_row(
2023 doc
, table
, row
, dst
, GEANY_GBG_NON_FT
, cmd
, TRUE
);
2024 label
= gtk_label_new(_("Error regular expression:"));
2025 gtk_table_attach(table
, label
, 0, DC_ENTRIES
+ 1, row
, row
+ 1, GTK_FILL
,
2026 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
2027 fields
->nonfileregex
= gtk_entry_new();
2028 fields
->nonfileregexstring
= build_get_regex(GEANY_GBG_NON_FT
, NULL
, &src
);
2030 if (fields
->nonfileregexstring
!= NULL
&& *(fields
->nonfileregexstring
) != NULL
)
2032 gtk_entry_set_text(GTK_ENTRY(fields
->nonfileregex
), *(fields
->nonfileregexstring
));
2033 sensitivity
= src
> dst
? FALSE
: TRUE
;
2035 gtk_table_attach(table
, fields
->nonfileregex
, DC_ENTRIES
+ 1, DC_CLEAR
, row
, row
+ 1, GTK_FILL
,
2036 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
2037 clearicon
= gtk_image_new_from_stock(GTK_STOCK_CLEAR
, GTK_ICON_SIZE_MENU
);
2038 clear
= gtk_button_new();
2039 gtk_button_set_image(GTK_BUTTON(clear
), clearicon
);
2040 g_signal_connect_swapped(clear
, "clicked",
2041 G_CALLBACK(on_clear_dialog_regex_row
), (fields
->nonfileregex
));
2042 gtk_table_attach(table
, clear
, DC_CLEAR
, DC_CLEAR
+ 1, row
, row
+ 1, GTK_FILL
,
2043 GTK_FILL
| GTK_EXPAND
, entry_x_padding
, entry_y_padding
);
2044 gtk_widget_set_sensitive(fields
->nonfileregex
, sensitivity
);
2045 gtk_widget_set_sensitive(clear
, sensitivity
);
2047 label
= gtk_label_new(NULL
);
2048 ui_label_set_markup(GTK_LABEL(label
), "<i>%s</i>",
2049 _("Note: Item 2 opens a dialog and appends the response to the command."));
2050 gtk_misc_set_alignment(GTK_MISC(label
), 0.0, 0.5);
2051 gtk_table_attach(table
, label
, 0, DC_N_COL
, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
2052 entry_x_padding
, entry_y_padding
);
2054 sep
= gtk_hseparator_new();
2055 gtk_table_attach(table
, sep
, 0, DC_N_COL
, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
2056 entry_x_padding
, sep_padding
);
2058 label
= ui_label_new_bold(_("Execute commands"));
2059 gtk_misc_set_alignment(GTK_MISC(label
), 0.0, 0.5);
2060 gtk_table_attach(table
, label
, 0, DC_N_COL
, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
2061 entry_x_padding
, entry_y_padding
);
2062 for (++row
, cmd
= 0; cmd
< build_groups_count
[GEANY_GBG_EXEC
]; ++row
, ++cmdindex
, ++cmd
)
2063 fields
->rows
[cmdindex
] = build_add_dialog_row(doc
, table
, row
, dst
, GEANY_GBG_EXEC
, cmd
, TRUE
);
2064 sep
= gtk_hseparator_new();
2065 gtk_table_attach(table
, sep
, 0, DC_N_COL
, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
2066 entry_x_padding
, sep_padding
);
2068 label
= gtk_label_new(NULL
);
2069 ui_label_set_markup(GTK_LABEL(label
), "<i>%s</i>",
2070 _("%d, %e, %f, %p, %l are substituted in command and directory fields, see manual for details."));
2071 gtk_misc_set_alignment(GTK_MISC(label
), 0.0, 0.5);
2072 gtk_table_attach(table
, label
, 0, DC_N_COL
, row
, row
+ 1, GTK_FILL
, GTK_FILL
| GTK_EXPAND
,
2073 entry_x_padding
, entry_y_padding
);
2074 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2076 *table_data
= fields
;
2077 return GTK_WIDGET(table
);
2081 void build_free_fields(BuildTableData table_data
)
2085 for (cmdindex
= 0; cmdindex
< build_items_count
; ++cmdindex
)
2086 g_free(table_data
->rows
[cmdindex
]);
2087 g_free(table_data
->rows
);
2092 /* string compare where null pointers match null or 0 length strings */
2094 static gint
stcmp(const gchar
*a
, const gchar
*b
)
2096 if (a
== NULL
&& b
== NULL
)
2098 if (a
== NULL
&& b
!= NULL
)
2100 if (a
!= NULL
&& b
== NULL
)
2102 return strcmp(a
, b
);
2107 static const gchar
*get_build_command_entry_text(GtkWidget
*wid
)
2109 if (GTK_IS_BUTTON(wid
))
2110 return gtk_button_get_label(GTK_BUTTON(wid
));
2112 return gtk_entry_get_text(GTK_ENTRY(wid
));
2116 static gboolean
read_row(BuildDestination
*dst
, BuildTableData table_data
, guint drow
, guint grp
, guint cmd
)
2118 gchar
*entries
[GEANY_BC_CMDENTRIES_COUNT
];
2119 gboolean changed
= FALSE
;
2120 enum GeanyBuildCmdEntries i
;
2122 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2124 entries
[i
] = g_strdup(get_build_command_entry_text(table_data
->rows
[drow
]->entries
[i
]));
2126 if (table_data
->rows
[drow
]->cleared
)
2128 if (dst
->dst
[grp
] != NULL
)
2130 if (*(dst
->dst
[grp
]) == NULL
)
2131 *(dst
->dst
[grp
]) = g_new0(GeanyBuildCommand
, build_groups_count
[grp
]);
2132 (*(dst
->dst
[grp
]))[cmd
].exists
= FALSE
;
2133 (*(dst
->dst
[grp
]))[cmd
].changed
= TRUE
;
2137 if (table_data
->rows
[drow
]->used_dst
== TRUE
)
2139 if (dst
->dst
[grp
] != NULL
)
2141 if (*(dst
->dst
[grp
]) == NULL
)
2142 *(dst
->dst
[grp
]) = g_new0(GeanyBuildCommand
, build_groups_count
[grp
]);
2143 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2144 set_command(&(*(dst
->dst
[grp
]))[cmd
], i
, entries
[i
]);
2145 (*(dst
->dst
[grp
]))[cmd
].exists
= TRUE
;
2146 (*(dst
->dst
[grp
]))[cmd
].changed
= TRUE
;
2152 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2159 static gboolean
read_regex(GtkWidget
*regexentry
, gchar
**src
, gchar
**dst
)
2161 gboolean changed
= FALSE
;
2162 const gchar
*reg
= gtk_entry_get_text(GTK_ENTRY(regexentry
));
2164 if (((src
== NULL
/* originally there was no regex */
2165 || *src
== NULL
) /* or it was NULL*/
2166 && !EMPTY(reg
)) /* and something was typed */
2167 || (src
!= NULL
/* originally there was a regex*/
2168 && (*src
== NULL
/* and either it was NULL */
2169 || strcmp(*src
, reg
) != 0))) /* or it has been changed */
2173 SETPTR(*dst
, g_strdup(reg
));
2181 static gboolean
build_read_commands(BuildDestination
*dst
, BuildTableData table_data
, gint response
)
2183 guint cmdindex
, cmd
;
2184 gboolean changed
= FALSE
;
2186 if (response
== GTK_RESPONSE_ACCEPT
)
2188 for (cmdindex
= 0, cmd
= 0; cmd
< build_groups_count
[GEANY_GBG_FT
]; ++cmdindex
, ++cmd
)
2189 changed
|= read_row(dst
, table_data
, cmdindex
, GEANY_GBG_FT
, cmd
);
2190 for (cmd
= 0; cmd
< build_groups_count
[GEANY_GBG_NON_FT
]; ++cmdindex
, ++cmd
)
2191 changed
|= read_row(dst
, table_data
, cmdindex
, GEANY_GBG_NON_FT
, cmd
);
2192 for (cmd
= 0; cmd
< build_groups_count
[GEANY_GBG_EXEC
]; ++cmdindex
, ++cmd
)
2193 changed
|= read_row(dst
, table_data
, cmdindex
, GEANY_GBG_EXEC
, cmd
);
2194 changed
|= read_regex(table_data
->fileregex
, table_data
->fileregexstring
, dst
->fileregexstr
);
2195 changed
|= read_regex(table_data
->nonfileregex
, table_data
->nonfileregexstring
, dst
->nonfileregexstr
);
2201 void build_read_project(GeanyFiletype
*ft
, BuildTableData build_properties
)
2203 BuildDestination menu_dst
;
2207 menu_dst
.dst
[GEANY_GBG_FT
] = &(ft
->priv
->projfilecmds
);
2208 menu_dst
.fileregexstr
= &(ft
->priv
->projerror_regex_string
);
2212 menu_dst
.dst
[GEANY_GBG_FT
] = NULL
;
2213 menu_dst
.fileregexstr
= NULL
;
2215 menu_dst
.dst
[GEANY_GBG_NON_FT
] = &non_ft_proj
;
2216 menu_dst
.dst
[GEANY_GBG_EXEC
] = &exec_proj
;
2217 menu_dst
.nonfileregexstr
= ®ex_proj
;
2219 build_read_commands(&menu_dst
, build_properties
, GTK_RESPONSE_ACCEPT
);
2223 static void show_build_commands_dialog(void)
2225 GtkWidget
*dialog
, *table
, *vbox
;
2226 GeanyDocument
*doc
= document_get_current();
2227 GeanyFiletype
*ft
= NULL
;
2228 const gchar
*title
= _("Set Build Commands");
2230 BuildTableData table_data
;
2231 BuildDestination prefdsts
;
2234 ft
= doc
->file_type
;
2235 dialog
= gtk_dialog_new_with_buttons(title
, GTK_WINDOW(main_widgets
.window
),
2236 GTK_DIALOG_DESTROY_WITH_PARENT
,
2237 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
2238 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
);
2239 table
= build_commands_table(doc
, GEANY_BCS_PREF
, &table_data
, ft
);
2240 vbox
= ui_dialog_vbox_new(GTK_DIALOG(dialog
));
2241 gtk_box_pack_start(GTK_BOX(vbox
), table
, TRUE
, TRUE
, 0);
2242 gtk_widget_show_all(dialog
);
2243 /* run modally to prevent user changing idx filetype */
2244 response
= gtk_dialog_run(GTK_DIALOG(dialog
));
2246 prefdsts
.dst
[GEANY_GBG_NON_FT
] = &non_ft_pref
;
2249 prefdsts
.dst
[GEANY_GBG_FT
] = &(ft
->priv
->homefilecmds
);
2250 prefdsts
.fileregexstr
= &(ft
->priv
->homeerror_regex_string
);
2251 prefdsts
.dst
[GEANY_GBG_EXEC
] = &(ft
->priv
->homeexeccmds
);
2255 prefdsts
.dst
[GEANY_GBG_FT
] = NULL
;
2256 prefdsts
.fileregexstr
= NULL
;
2257 prefdsts
.dst
[GEANY_GBG_EXEC
] = NULL
;
2259 prefdsts
.nonfileregexstr
= ®ex_pref
;
2260 if (build_read_commands(&prefdsts
, table_data
, response
) && ft
!= NULL
)
2261 filetypes_save_commands(ft
);
2262 build_free_fields(table_data
);
2264 build_menu_update(doc
);
2265 gtk_widget_destroy(dialog
);
2269 /* Creates the relevant build menu if necessary. */
2270 BuildMenuItems
*build_get_menu_items(gint filetype_idx
)
2272 BuildMenuItems
*items
;
2274 items
= &menu_items
;
2275 if (items
->menu
== NULL
)
2276 create_build_menu(items
);
2281 /*----------------------------------------------------------
2283 * Load and store configuration
2285 * ---------------------------------------------------------*/
2286 static const gchar
*build_grp_name
= "build-menu";
2288 /* config format for build-menu group is prefix_gg_nn_xx=value
2289 * where gg = FT, NF, EX for the command group
2290 * nn = 2 digit command number
2291 * xx = LB for label, CM for command and WD for working dir */
2292 static const gchar
*groups
[GEANY_GBG_COUNT
] = { "FT", "NF", "EX" };
2293 static const gchar
*fixedkey
="xx_xx_xx";
2295 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2296 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2297 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2299 static void build_load_menu_grp(GKeyFile
*config
, GeanyBuildCommand
**dst
, gint grp
,
2300 gchar
*prefix
, gboolean loc
)
2303 gsize prefixlen
; /* NOTE prefixlen used in macros above */
2304 GeanyBuildCommand
*dstcmd
;
2306 static gchar cmdbuf
[4] = " ";
2309 *dst
= g_new0(GeanyBuildCommand
, build_groups_count
[grp
]);
2311 prefixlen
= prefix
== NULL
? 0 : strlen(prefix
);
2312 key
= g_strconcat(prefix
== NULL
? "" : prefix
, fixedkey
, NULL
);
2313 for (cmd
= 0; cmd
< build_groups_count
[grp
]; ++cmd
)
2317 break; /* ensure no buffer overflow */
2318 sprintf(cmdbuf
, "%02u", cmd
);
2319 set_key_grp(key
, groups
[grp
]);
2320 set_key_cmd(key
, cmdbuf
);
2321 set_key_fld(key
, "LB");
2323 label
= g_key_file_get_locale_string(config
, build_grp_name
, key
, NULL
, NULL
);
2325 label
= g_key_file_get_string(config
, build_grp_name
, key
, NULL
);
2328 dstcmd
[cmd
].exists
= TRUE
;
2329 SETPTR(dstcmd
[cmd
].label
, label
);
2330 set_key_fld(key
,"CM");
2331 SETPTR(dstcmd
[cmd
].command
,
2332 g_key_file_get_string(config
, build_grp_name
, key
, NULL
));
2333 set_key_fld(key
,"WD");
2334 SETPTR(dstcmd
[cmd
].working_dir
,
2335 g_key_file_get_string(config
, build_grp_name
, key
, NULL
));
2337 else dstcmd
[cmd
].exists
= FALSE
;
2343 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
2344 static void assign_cmd(GeanyBuildCommand
*type
, guint id
,
2345 const gchar
*label
, gchar
*value
)
2347 if (!EMPTY(value
) && ! type
[GBO_TO_CMD(id
)].exists
)
2349 type
[GBO_TO_CMD(id
)].exists
= TRUE
;
2350 SETPTR(type
[GBO_TO_CMD(id
)].label
, g_strdup(label
));
2351 SETPTR(type
[GBO_TO_CMD(id
)].command
, value
);
2352 SETPTR(type
[GBO_TO_CMD(id
)].working_dir
, NULL
);
2353 type
[GBO_TO_CMD(id
)].old
= TRUE
;
2359 /* for the specified source load new format build menu items or try to make some sense of
2360 * old format setings, not done perfectly but better than ignoring them */
2361 void build_load_menu(GKeyFile
*config
, GeanyBuildSource src
, gpointer p
)
2366 gchar
*value
, *basedir
, *makebasedir
;
2367 gboolean bvalue
= FALSE
;
2369 if (g_key_file_has_group(config
, build_grp_name
))
2374 ft
= (GeanyFiletype
*)p
;
2377 build_load_menu_grp(config
, &(ft
->priv
->filecmds
), GEANY_GBG_FT
, NULL
, TRUE
);
2378 build_load_menu_grp(config
, &(ft
->priv
->ftdefcmds
), GEANY_GBG_NON_FT
, NULL
, TRUE
);
2379 build_load_menu_grp(config
, &(ft
->priv
->execcmds
), GEANY_GBG_EXEC
, NULL
, TRUE
);
2380 SETPTR(ft
->error_regex_string
,
2381 g_key_file_get_string(config
, build_grp_name
, "error_regex", NULL
));
2383 case GEANY_BCS_HOME_FT
:
2384 ft
= (GeanyFiletype
*)p
;
2387 build_load_menu_grp(config
, &(ft
->priv
->homefilecmds
), GEANY_GBG_FT
, NULL
, FALSE
);
2388 build_load_menu_grp(config
, &(ft
->priv
->homeexeccmds
), GEANY_GBG_EXEC
, NULL
, FALSE
);
2389 SETPTR(ft
->priv
->homeerror_regex_string
,
2390 g_key_file_get_string(config
, build_grp_name
, "error_regex", NULL
));
2392 case GEANY_BCS_PREF
:
2393 build_load_menu_grp(config
, &non_ft_pref
, GEANY_GBG_NON_FT
, NULL
, FALSE
);
2394 build_load_menu_grp(config
, &exec_pref
, GEANY_GBG_EXEC
, NULL
, FALSE
);
2395 SETPTR(regex_pref
, g_key_file_get_string(config
, build_grp_name
, "error_regex", NULL
));
2397 case GEANY_BCS_PROJ
:
2398 build_load_menu_grp(config
, &non_ft_proj
, GEANY_GBG_NON_FT
, NULL
, FALSE
);
2399 build_load_menu_grp(config
, &exec_proj
, GEANY_GBG_EXEC
, NULL
, FALSE
);
2400 SETPTR(regex_proj
, g_key_file_get_string(config
, build_grp_name
, "error_regex", NULL
));
2401 pj
= (GeanyProject
*)p
;
2404 ftlist
= g_key_file_get_string_list(config
, build_grp_name
, "filetypes", NULL
, NULL
);
2408 if (pj
->priv
->build_filetypes_list
== NULL
)
2409 pj
->priv
->build_filetypes_list
= g_ptr_array_new();
2410 g_ptr_array_set_size(pj
->priv
->build_filetypes_list
, 0);
2411 for (ftname
= ftlist
; *ftname
!= NULL
; ++ftname
)
2413 ft
= filetypes_lookup_by_name(*ftname
);
2416 gchar
*regkey
= g_strdup_printf("%serror_regex", *ftname
);
2417 g_ptr_array_add(pj
->priv
->build_filetypes_list
, ft
);
2418 SETPTR(ft
->priv
->projerror_regex_string
,
2419 g_key_file_get_string(config
, build_grp_name
, regkey
, NULL
));
2421 build_load_menu_grp(config
, &(ft
->priv
->projfilecmds
), GEANY_GBG_FT
, *ftname
, FALSE
);
2422 build_load_menu_grp(config
, &(ft
->priv
->projexeccmds
), GEANY_GBG_EXEC
, *ftname
, FALSE
);
2428 default: /* defaults don't load from config, see build_init */
2433 /* load old [build_settings] values if there is no value defined by [build-menu] */
2438 ft
= (GeanyFiletype
*)p
;
2439 value
= g_key_file_get_string(config
, "build_settings", "compiler", NULL
);
2442 if (ft
->priv
->filecmds
== NULL
)
2443 ft
->priv
->filecmds
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_FT
]);
2444 assign_cmd(ft
->priv
->filecmds
, GEANY_GBO_COMPILE
, _("_Compile"), value
);
2446 value
= g_key_file_get_string(config
, "build_settings", "linker", NULL
);
2449 if (ft
->priv
->filecmds
== NULL
)
2450 ft
->priv
->filecmds
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_FT
]);
2451 assign_cmd(ft
->priv
->filecmds
, GEANY_GBO_BUILD
, _("_Build"), value
);
2453 value
= g_key_file_get_string(config
, "build_settings", "run_cmd", NULL
);
2456 if (ft
->priv
->execcmds
== NULL
)
2457 ft
->priv
->execcmds
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_EXEC
]);
2458 assign_cmd(ft
->priv
->execcmds
, GEANY_GBO_EXEC
, _("_Execute"), value
);
2460 if (ft
->error_regex_string
== NULL
)
2461 ft
->error_regex_string
= g_key_file_get_string(config
, "build_settings", "error_regex", NULL
);
2463 case GEANY_BCS_PROJ
:
2464 if (non_ft_pref
== NULL
)
2465 non_ft_pref
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_NON_FT
]);
2466 basedir
= project_get_base_path();
2467 if (basedir
== NULL
)
2468 basedir
= g_strdup("%d");
2469 bvalue
= g_key_file_get_boolean(config
, "project", "make_in_base_path", NULL
);
2471 makebasedir
= g_strdup(basedir
);
2473 makebasedir
= g_strdup("%d");
2474 if (non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_ALL
)].old
)
2475 SETPTR(non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_ALL
)].working_dir
, g_strdup(makebasedir
));
2476 if (non_ft_pref
[GBO_TO_CMD(GEANY_GBO_CUSTOM
)].old
)
2477 SETPTR(non_ft_pref
[GBO_TO_CMD(GEANY_GBO_CUSTOM
)].working_dir
, g_strdup(makebasedir
));
2478 if (non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
)].old
)
2479 SETPTR(non_ft_pref
[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
)].working_dir
, g_strdup("%d"));
2480 value
= g_key_file_get_string(config
, "project", "run_cmd", NULL
);
2483 if (exec_proj
== NULL
)
2484 exec_proj
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_EXEC
]);
2485 if (! exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].exists
)
2487 exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].exists
= TRUE
;
2488 SETPTR(exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].label
, g_strdup(_("_Execute")));
2489 SETPTR(exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].command
, value
);
2490 SETPTR(exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].working_dir
, g_strdup(basedir
));
2491 exec_proj
[GBO_TO_CMD(GEANY_GBO_EXEC
)].old
= TRUE
;
2494 g_free(makebasedir
);
2497 case GEANY_BCS_PREF
:
2498 value
= g_key_file_get_string(config
, "tools", "make_cmd", NULL
);
2501 if (non_ft_pref
== NULL
)
2502 non_ft_pref
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_NON_FT
]);
2503 assign_cmd(non_ft_pref
, GEANY_GBO_CUSTOM
, _("Make Custom _Target..."),
2504 g_strdup_printf("%s ", value
));
2505 assign_cmd(non_ft_pref
, GEANY_GBO_MAKE_OBJECT
, _("Make _Object"),
2506 g_strdup_printf("%s %%e.o",value
));
2507 assign_cmd(non_ft_pref
, GEANY_GBO_MAKE_ALL
, _("_Make"), value
);
2516 static guint
build_save_menu_grp(GKeyFile
*config
, GeanyBuildCommand
*src
, gint grp
, gchar
*prefix
)
2519 gsize prefixlen
; /* NOTE prefixlen used in macros above */
2522 enum GeanyBuildCmdEntries i
;
2526 prefixlen
= prefix
== NULL
? 0 : strlen(prefix
);
2527 key
= g_strconcat(prefix
== NULL
? "" : prefix
, fixedkey
, NULL
);
2528 for (cmd
= 0; cmd
< build_groups_count
[grp
]; ++cmd
)
2530 if (src
[cmd
].exists
) ++count
;
2531 if (src
[cmd
].changed
)
2533 static gchar cmdbuf
[4] = " ";
2535 break; /* ensure no buffer overflow */
2536 sprintf(cmdbuf
, "%02u", cmd
);
2537 set_key_grp(key
, groups
[grp
]);
2538 set_key_cmd(key
, cmdbuf
);
2539 if (src
[cmd
].exists
)
2541 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2543 set_key_fld(key
, config_keys
[i
]);
2544 g_key_file_set_string(config
, build_grp_name
, key
, id_to_str(&src
[cmd
], i
));
2549 for (i
= 0; i
< GEANY_BC_CMDENTRIES_COUNT
; i
++)
2551 set_key_fld(key
, config_keys
[i
]);
2552 g_key_file_remove_key(config
, build_grp_name
, key
, NULL
);
2562 static gboolean
save_project_filetype(GeanyFiletype
*ft
, GKeyFile
*config
)
2565 gchar
*regkey
= g_strdup_printf("%serror_regex", ft
->name
);
2567 i
+= build_save_menu_grp(config
, ft
->priv
->projfilecmds
, GEANY_GBG_FT
, ft
->name
);
2568 i
+= build_save_menu_grp(config
, ft
->priv
->projexeccmds
, GEANY_GBG_EXEC
, ft
->name
);
2569 if (!EMPTY(ft
->priv
->projerror_regex_string
))
2571 g_key_file_set_string(config
, build_grp_name
, regkey
, ft
->priv
->projerror_regex_string
);
2575 g_key_file_remove_key(config
, build_grp_name
, regkey
, NULL
);
2580 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
2581 void build_save_menu(GKeyFile
*config
, gpointer ptr
, GeanyBuildSource src
)
2588 case GEANY_BCS_HOME_FT
:
2589 ft
= (GeanyFiletype
*)ptr
;
2592 build_save_menu_grp(config
, ft
->priv
->homefilecmds
, GEANY_GBG_FT
, NULL
);
2593 build_save_menu_grp(config
, ft
->priv
->homeexeccmds
, GEANY_GBG_EXEC
, NULL
);
2594 if (!EMPTY(ft
->priv
->homeerror_regex_string
))
2595 g_key_file_set_string(config
, build_grp_name
, "error_regex", ft
->priv
->homeerror_regex_string
);
2597 g_key_file_remove_key(config
, build_grp_name
, "error_regex", NULL
);
2599 case GEANY_BCS_PREF
:
2600 build_save_menu_grp(config
, non_ft_pref
, GEANY_GBG_NON_FT
, NULL
);
2601 build_save_menu_grp(config
, exec_pref
, GEANY_GBG_EXEC
, NULL
);
2602 if (!EMPTY(regex_pref
))
2603 g_key_file_set_string(config
, build_grp_name
, "error_regex", regex_pref
);
2605 g_key_file_remove_key(config
, build_grp_name
, "error_regex", NULL
);
2607 case GEANY_BCS_PROJ
:
2608 pj
= (GeanyProject
*)ptr
;
2609 build_save_menu_grp(config
, non_ft_proj
, GEANY_GBG_NON_FT
, NULL
);
2610 build_save_menu_grp(config
, exec_proj
, GEANY_GBG_EXEC
, NULL
);
2611 if (!EMPTY(regex_proj
))
2612 g_key_file_set_string(config
, build_grp_name
, "error_regex", regex_proj
);
2614 g_key_file_remove_key(config
, build_grp_name
, "error_regex", NULL
);
2615 if (pj
->priv
->build_filetypes_list
!= NULL
)
2617 GPtrArray
*ft_names
= g_ptr_array_new();
2618 const GPtrArray
*build_fts
= pj
->priv
->build_filetypes_list
;
2620 for (guint i
= 0; i
< build_fts
->len
; i
++)
2622 ft
= build_fts
->pdata
[i
];
2623 if (save_project_filetype(ft
, config
))
2624 g_ptr_array_add(ft_names
, ft
->name
);
2626 if (ft_names
->pdata
!= NULL
)
2627 g_key_file_set_string_list(config
, build_grp_name
, "filetypes",
2628 (const gchar
**)ft_names
->pdata
, ft_names
->len
);
2630 g_key_file_remove_key(config
, build_grp_name
, "filetypes", NULL
);
2631 g_ptr_array_free(ft_names
, TRUE
);
2634 default: /* defaults and GEANY_BCS_FT can't save */
2640 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
2641 void build_set_group_count(GeanyBuildGroup grp
, gint count
)
2645 g_return_if_fail(count
>= 0);
2647 if ((guint
) count
> build_groups_count
[grp
])
2648 build_groups_count
[grp
] = (guint
) count
;
2649 for (i
= 0, sum
= 0; i
< GEANY_GBG_COUNT
; ++i
)
2650 sum
+= build_groups_count
[i
];
2651 build_items_count
= sum
;
2655 /** Get the count of commands for the group
2657 * Get the number of commands in the group specified by @a grp.
2659 * @param grp the group of the specified menu item.
2661 * @return a count of the number of commands in the group
2665 guint
build_get_group_count(const GeanyBuildGroup grp
)
2667 g_return_val_if_fail(grp
< GEANY_GBG_COUNT
, 0);
2668 return build_groups_count
[grp
];
2672 static void on_project_close(void)
2674 /* remove project regexen */
2675 SETPTR(regex_proj
, NULL
);
2682 const gchar
*command
;
2683 const gchar
*working_dir
;
2684 GeanyBuildCommand
**ptr
;
2686 } default_cmds
[] = {
2687 { N_("_Make"), "make", NULL
, &non_ft_def
, GBO_TO_CMD(GEANY_GBO_MAKE_ALL
)},
2688 { N_("Make Custom _Target..."), "make ", NULL
, &non_ft_def
, GBO_TO_CMD(GEANY_GBO_CUSTOM
)},
2689 { N_("Make _Object"), "make %e.o", NULL
, &non_ft_def
, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
)},
2690 { N_("_Execute"), "./%e", NULL
, &exec_def
, GBO_TO_CMD(GEANY_GBO_EXEC
)},
2691 { NULL
, NULL
, NULL
, NULL
, 0 }
2695 void build_init(void)
2698 GtkWidget
*toolmenu
;
2701 g_signal_connect(geany_object
, "project-close", on_project_close
, NULL
);
2703 ft_def
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_FT
]);
2704 non_ft_def
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_NON_FT
]);
2705 exec_def
= g_new0(GeanyBuildCommand
, build_groups_count
[GEANY_GBG_EXEC
]);
2706 run_info
= g_new0(RunInfo
, build_groups_count
[GEANY_GBG_EXEC
]);
2708 for (cmdindex
= 0; default_cmds
[cmdindex
].command
!= NULL
; ++cmdindex
)
2710 GeanyBuildCommand
*cmd
= &((*(default_cmds
[cmdindex
].ptr
))[ default_cmds
[cmdindex
].index
]);
2712 cmd
->label
= g_strdup(_(default_cmds
[cmdindex
].label
));
2713 cmd
->command
= g_strdup(default_cmds
[cmdindex
].command
);
2714 cmd
->working_dir
= g_strdup(default_cmds
[cmdindex
].working_dir
);
2717 /* create the toolbar Build item sub menu */
2718 toolmenu
= gtk_menu_new();
2719 g_object_ref(toolmenu
);
2721 /* build the code */
2722 item
= ui_image_menu_item_new(GEANY_STOCK_BUILD
, _("_Build"));
2723 gtk_widget_show(item
);
2724 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2725 g_signal_connect(item
, "activate", G_CALLBACK(on_toolbutton_build_activate
),
2726 GBO_TO_POINTER(GEANY_GBO_BUILD
));
2727 widgets
.toolitem_build
= item
;
2729 item
= gtk_separator_menu_item_new();
2730 gtk_widget_show(item
);
2731 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2733 /* build the code with make all */
2734 item
= gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2735 gtk_widget_show(item
);
2736 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2737 g_signal_connect(item
, "activate", G_CALLBACK(on_toolbutton_make_activate
),
2738 GBO_TO_POINTER(GEANY_GBO_MAKE_ALL
));
2739 widgets
.toolitem_make_all
= item
;
2741 /* build the code with make custom */
2742 item
= gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2743 gtk_widget_show(item
);
2744 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2745 g_signal_connect(item
, "activate", G_CALLBACK(on_toolbutton_make_activate
),
2746 GBO_TO_POINTER(GEANY_GBO_CUSTOM
));
2747 widgets
.toolitem_make_custom
= item
;
2749 /* build the code with make object */
2750 item
= gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2751 gtk_widget_show(item
);
2752 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2753 g_signal_connect(item
, "activate", G_CALLBACK(on_toolbutton_make_activate
),
2754 GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT
));
2755 widgets
.toolitem_make_object
= item
;
2757 item
= gtk_separator_menu_item_new();
2758 gtk_widget_show(item
);
2759 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2762 item
= ui_image_menu_item_new(GTK_STOCK_PREFERENCES
, _("_Set Build Commands"));
2763 gtk_widget_show(item
);
2764 gtk_container_add(GTK_CONTAINER(toolmenu
), item
);
2765 g_signal_connect(item
, "activate", G_CALLBACK(on_set_build_commands_activate
), NULL
);
2766 widgets
.toolitem_set_args
= item
;
2768 /* get toolbar action pointers */
2769 widgets
.build_action
= toolbar_get_action_by_name("Build");
2770 widgets
.compile_action
= toolbar_get_action_by_name("Compile");
2771 widgets
.run_action
= toolbar_get_action_by_name("Run");
2772 widgets
.toolmenu
= toolmenu
;
2773 /* set the submenu to the toolbar item */
2774 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets
.build_action
), toolmenu
);
2778 gboolean
build_keybinding(guint key_id
)
2781 BuildMenuItems
*menu_items
;
2782 GeanyDocument
*doc
= document_get_current();
2787 if (!gtk_widget_is_sensitive(ui_lookup_widget(main_widgets
.window
, "menu_build1")))
2790 menu_items
= build_get_menu_items(doc
->file_type
->id
);
2791 /* TODO make it a table??*/
2794 case GEANY_KEYS_BUILD_COMPILE
:
2795 item
= menu_items
->menu_item
[GEANY_GBG_FT
][GBO_TO_CMD(GEANY_GBO_COMPILE
)];
2797 case GEANY_KEYS_BUILD_LINK
:
2798 item
= menu_items
->menu_item
[GEANY_GBG_FT
][GBO_TO_CMD(GEANY_GBO_BUILD
)];
2800 case GEANY_KEYS_BUILD_MAKE
:
2801 item
= menu_items
->menu_item
[GEANY_GBG_NON_FT
][GBO_TO_CMD(GEANY_GBO_MAKE_ALL
)];
2803 case GEANY_KEYS_BUILD_MAKEOWNTARGET
:
2804 item
= menu_items
->menu_item
[GEANY_GBG_NON_FT
][GBO_TO_CMD(GEANY_GBO_CUSTOM
)];
2806 case GEANY_KEYS_BUILD_MAKEOBJECT
:
2807 item
= menu_items
->menu_item
[GEANY_GBG_NON_FT
][GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT
)];
2809 case GEANY_KEYS_BUILD_NEXTERROR
:
2810 item
= menu_items
->menu_item
[GBG_FIXED
][GBF_NEXT_ERROR
];
2812 case GEANY_KEYS_BUILD_PREVIOUSERROR
:
2813 item
= menu_items
->menu_item
[GBG_FIXED
][GBF_PREV_ERROR
];
2815 case GEANY_KEYS_BUILD_RUN
:
2816 item
= menu_items
->menu_item
[GEANY_GBG_EXEC
][GBO_TO_CMD(GEANY_GBO_EXEC
)];
2818 case GEANY_KEYS_BUILD_OPTIONS
:
2819 item
= menu_items
->menu_item
[GBG_FIXED
][GBF_COMMANDS
];
2824 /* Note: For Build menu items it's OK (at the moment) to assume they are in the correct
2825 * sensitive state, but some other menus don't update the sensitive status until
2826 * they are redrawn. */
2827 if (item
&& gtk_widget_is_sensitive(item
))
2828 gtk_menu_item_activate(GTK_MENU_ITEM(item
));