geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

build.c
Go to the documentation of this file.
1/*
2 * build.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2005 The Geany contributors
5 *
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.
10 *
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.
15 *
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.
19 */
20
21/*
22 * Build commands and menu items.
23 */
24/* TODO: tidy code:
25 * Use intermediate pointers for common subexpressions.
26 * Replace defines with enums.
27 * Other TODOs in code. */
28
29#ifdef HAVE_CONFIG_H
30# include "config.h"
31#endif
32
33#include "app.h"
34#include "build.h"
35#include "dialogs.h"
36#include "document.h"
37#include "filetypesprivate.h"
39#include "geanyobject.h"
40#include "keybindingsprivate.h"
41#include "msgwindow.h"
42#include "prefs.h"
43#include "projectprivate.h"
44#include "sciwrappers.h"
45#include "spawn.h"
46#include "support.h"
47#include "toolbar.h"
48#include "ui_utils.h"
49#include "utils.h"
50#include "vte.h"
51#include "win32.h"
52
53#include <stdlib.h>
54#include <string.h>
55#include <sys/stat.h>
56#include <unistd.h>
57#include <errno.h>
58#include <gtk/gtk.h>
59#include <glib/gstdio.h>
60
61
62
63/* Number of editor indicators to draw - limited as this can affect performance */
64#define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
65
66
67GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
68
69static gchar *current_dir_entered = NULL;
70
71typedef struct RunInfo
72{
73 GPid pid;
76
78
79#ifndef G_OS_WIN32
80static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh";
81#endif
82
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.
87 **/
88typedef enum
89{
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 */
98
99/* * Convert @c GeanyBuildType to @c GeanyBuildGroup.
100 *
101 * This macro converts @c GeanyBuildType enum values (the "known" commands)
102 * to the group they are part of.
103 *
104 * @param gbo the @c GeanyBuildType value.
105 *
106 * @return the @c GeanyBuildGroup group that @a gbo is in.
107 *
108 * Note this is a macro so that it can be used in static initialisers.
109 **/
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)))
114
115/* * Convert @c GeanyBuildType to command index.
116 *
117 * This macro converts @c GeanyBuildType enum values (the "known" commands)
118 * to the index within the group.
119 *
120 * @param gbo the @c GeanyBuildType value.
121 *
122 * @return the index of the @a gbo command in its group.
123 *
124 * Note this is a macro so that it can be used in static initialisers.
125 **/
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))))
131
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)
137
139
140static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
141
142static struct
143{
144 GtkAction *run_action;
145 GtkAction *compile_action;
146 GtkAction *build_action;
147 GtkWidget *toolmenu;
148
149 GtkWidget *toolitem_build;
154}
156
157static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
158static guint build_items_count = 9;
159
160static void build_exit_cb(GPid pid, gint status, gpointer user_data);
161static void build_iofunc(GString *string, GIOCondition condition, gpointer data);
162#ifndef G_OS_WIN32
163static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
164#endif
165static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
166static void set_stop_button(gboolean stop);
167static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
168static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
169static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
170static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
171static void kill_process(GPid *pid);
172static void show_build_result_message(gboolean failure);
173static void process_build_output_line(gchar *msg, gint color);
174static void show_build_commands_dialog(void);
175static void on_build_menu_item(GtkWidget *w, gpointer user_data);
176
178{
179 g_free(build_info.dir);
180 g_free(build_info.custom_target);
181
182 if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
183 gtk_widget_destroy(menu_items.menu);
184}
185
186
187/* note: copied from keybindings.c, may be able to go away */
188static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
189 GtkAccelGroup *accel_group, GtkWidget *menuitem)
190{
192
193 if (kb->key != 0)
194 gtk_widget_add_accelerator(menuitem, "activate", accel_group,
195 kb->key, kb->mods, GTK_ACCEL_VISIBLE);
196}
197
198
199/* convenience routines to access parts of GeanyBuildCommand */
200static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
201{
202 switch (id)
203 {
204 case GEANY_BC_LABEL:
205 return bc->label;
206 case GEANY_BC_COMMAND:
207 return bc->command;
209 return bc->working_dir;
210 }
211 g_assert(0);
212 return NULL;
213}
214
215
216static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
217{
218 switch (id)
219 {
220 case GEANY_BC_LABEL:
221 SETPTR(bc->label, str);
222 break;
223 case GEANY_BC_COMMAND:
224 SETPTR(bc->command, str);
225 break;
227 SETPTR(bc->working_dir, str);
228 break;
229 default:
230 g_assert(0);
231 }
232}
233
234
236 "LB", /* label */
237 "CM", /* command */
238 "WD" /* working directory */
239};
240
241/*-----------------------------------------------------
242 *
243 * Execute commands and handle results
244 *
245 *-----------------------------------------------------*/
246
247/* the various groups of commands not in the filetype struct */
248static GeanyBuildCommand *ft_def = NULL;
249static GeanyBuildCommand *non_ft_proj = NULL;
250static GeanyBuildCommand *non_ft_pref = NULL;
251static GeanyBuildCommand *non_ft_def = NULL;
252static GeanyBuildCommand *exec_proj = NULL;
253static GeanyBuildCommand *exec_pref = NULL;
254static GeanyBuildCommand *exec_def = NULL;
255/* and the regexen not in the filetype structure */
256static gchar *regex_pref = NULL;
257/* project non-fileregex string */
258static gchar *regex_proj = NULL;
259
260/* control if build commands are printed by get_build_cmd, for debug purposes only*/
261#ifndef PRINTBUILDCMDS
262#define PRINTBUILDCMDS FALSE
263#endif
265
266
267/* for debug only, print the commands structures in priority order */
268static void printfcmds(void)
269{
270#if 0
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 },
277 };
278 GeanyFiletype *ft = NULL;
279 GeanyDocument *doc;
280 gint i, j, k, l, m;
282 gint cc[GEANY_BCS_COUNT];
283 gchar c;
284
285 doc = document_get_current();
286 if (doc != NULL)
287 ft = doc->file_type;
288 if (ft != NULL)
289 {
290 printf("filetype %s\n",ft->name);
291 cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->priv->filecmds);
298 }
299 for (i = 0; i < GEANY_BCS_COUNT; ++i)
300 {
301 m = 1;
302 for (j = 0; j < GEANY_GBG_COUNT; ++j)
303 {
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)
306 {
307 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
308 {
309 if ((*(cl[j][i]))[k].entries[n] != NULL &&
310 (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
311 {
312 m = l;
313 }
314 }
315 }
316 }
317 cc[i] = m;
318 }
319 for (i = 0; i < GEANY_GBG_COUNT; ++i)
320 {
321 for (k = 0; k < build_groups_count[i]; ++k)
322 {
323 for (l = 0; l < 2; ++l)
324 {
325 c = ' ';
326 for (j = 0; j < GEANY_BCS_COUNT; ++j)
327 {
328 if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
329 {
330 for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
331 {
332 if ((*(cl[i][j]))[k].entries[i] != NULL)
333 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
334 else
335 printf("%c %*.*s",c,cc[j],cc[j]," ");
336 }
337 }
338 else
339 printf("%c %*.*s",c,cc[j],cc[j]," ");
340 c = ',';
341 }
342 printf("\n");
343 }
344 }
345 printf("\n");
346 }
347#endif
348}
349
350
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)\
354 { \
355 *fr=src; \
356 if (printbuildcmds) \
357 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
358 return &(cmds[cmdindex]); \
359 }
360
361#define return_ft_cmd_if(src, cmds)\
362 if (ft != NULL && ft->priv->cmds != NULL \
363 && ft->priv->cmds[cmdindex].exists && below>src)\
364 { \
365 *fr=src; \
366 if (printbuildcmds) \
367 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
368 return &(ft->priv->cmds[cmdindex]); \
369 }
370
371
372/* get the next lowest command taking priority into account */
373static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
374 guint below, guint *from)
375{
376 /* Note: parameter below used in macros above */
377 GeanyFiletype *ft = NULL;
378 guint sink, *fr = &sink;
379
380 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
381
382 if (printbuildcmds)
383 printfcmds();
384 if (cmdgrp >= GEANY_GBG_COUNT)
385 return NULL;
386 if (from != NULL)
387 fr = from;
388 if (doc == NULL)
389 doc = document_get_current();
390 if (doc != NULL)
391 ft = doc->file_type;
392
393 switch (cmdgrp)
394 {
395 case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
396 if (ft != NULL)
397 {
398 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
399 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
401 }
403 break;
404 case GEANY_GBG_NON_FT: /* order proj, pref, def */
407 return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
409 break;
410 case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
412 return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
414 return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
417 break;
418 default:
419 break;
420 }
421 return NULL;
422}
423
424
425/* shortcut to start looking at the top */
426static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
427{
428 return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
429}
430
431
432#define return_nonblank_regex(src, ptr)\
433 if (!EMPTY(ptr)) \
434 { *fr = (src); return &(ptr); }
435
436
437/* like get_build_cmd, but for regexen, used by filetypes */
438gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
439{
440 guint sink, *fr = &sink;
441
442 if (from != NULL)
443 fr = from;
444 if (grp == GEANY_GBG_FT)
445 {
446 if (ft == NULL)
447 {
449 if (doc != NULL)
450 ft = doc->file_type;
451 }
452 if (ft == NULL)
453 return NULL;
457 }
458 else if (grp == GEANY_GBG_NON_FT)
459 {
462 }
463 return NULL;
464}
465
466
467static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
468{
469 GeanyDocument *doc;
470 GeanyFiletype *ft = NULL;
471
472 switch (grp)
473 {
474 case GEANY_GBG_FT:
475 if ((doc = document_get_current()) == NULL)
476 return NULL;
477 if ((ft = doc->file_type) == NULL)
478 return NULL;
479 switch (src)
480 {
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;
487 }
488 break;
489 case GEANY_GBG_NON_FT:
490 switch (src)
491 {
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;
496 }
497 break;
498 case GEANY_GBG_EXEC:
499 if ((doc = document_get_current()) != NULL)
500 ft = doc->file_type;
501 switch (src)
502 {
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;
510 }
511 break;
512 default:
513 return NULL;
514 }
515}
516
517
518/* get pointer to the command group array */
519static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
520{
521 GeanyBuildCommand **g = get_build_group_pointer(src, grp);
522 if (g == NULL) return NULL;
523 return *g;
524}
525
526
527/** Remove the specified Build menu item.
528 *
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).
534 *
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.
539 *
540 * If any parameter is out of range does nothing.
541 *
542 * Updates the menu.
543 *
544 **/
545GEANY_API_SYMBOL
546void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
547{
548 GeanyBuildCommand *bc;
549 guint i;
550
551 bc = get_build_group(src, grp);
552 if (bc == NULL)
553 return;
554 if (cmd < 0)
555 {
556 for (i = 0; i < build_groups_count[grp]; ++i)
557 bc[i].exists = FALSE;
558 }
559 else if ((guint) cmd < build_groups_count[grp])
560 bc[cmd].exists = FALSE;
561}
562
563
564/* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
565 *
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.
568 *
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.
572 *
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.
575 *
576 * @see build_menu_update
577 **/
578GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
579{
580 GeanyBuildCommand *bc;
581
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);
585
586 bc = get_build_group(src, grp);
587 if (bc == NULL)
588 return NULL;
589 return &(bc[cmd]);
590}
591
592
593/** Get the string for the menu item field.
594 *
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.
597 *
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
601 *
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.
604 *
605 **/
606GEANY_API_SYMBOL
607const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
608 const GeanyBuildCmdEntries fld)
609{
610 GeanyBuildCommand *c;
611 gchar *str = NULL;
612
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);
616
617 c = get_build_cmd(NULL, grp, cmd, NULL);
618 if (c == NULL) return NULL;
619 switch (fld)
620 {
621 case GEANY_BC_COMMAND:
622 str = c->command;
623 break;
624 case GEANY_BC_LABEL:
625 str = c->label;
626 break;
628 str = c->working_dir;
629 break;
630 default:
631 break;
632 }
633 return str;
634}
635
636/** Set the string for the menu item field.
637 *
638 * Set the specified field of the command specified by @a src, @a grp and @a cmd.
639 *
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
645 *
646 **/
647GEANY_API_SYMBOL
649 const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
650{
651 GeanyBuildCommand **g;
652
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]);
657
658 g = get_build_group_pointer(src, grp);
659 if (g == NULL) return;
660 if (*g == NULL )
661 {
662 *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
663 }
664 switch (fld)
665 {
666 case GEANY_BC_COMMAND:
667 SETPTR((*g)[cmd].command, g_strdup(val));
668 (*g)[cmd].exists = TRUE;
669 break;
670 case GEANY_BC_LABEL:
671 SETPTR((*g)[cmd].label, g_strdup(val));
672 (*g)[cmd].exists = TRUE;
673 break;
675 SETPTR((*g)[cmd].working_dir, g_strdup(val));
676 (*g)[cmd].exists = TRUE;
677 break;
678 default:
679 break;
680 }
682}
683
684/** Activate the menu item.
685 *
686 * Activate the menu item specified by @a grp and @a cmd.
687 *
688 * @param grp the group of the specified menu item.
689 * @param cmd the index of the command within the group.
690 *
691 **/
692GEANY_API_SYMBOL
693void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
694{
696}
697
698
699/* Clear all error indicators in all documents. */
700static void clear_all_errors(void)
701{
702 guint i;
703
705 {
707 }
708}
709
710
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 */
713static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
714{
715 GString *stack;
716 gchar *replacement;
717 gchar *executable = NULL;
718 gint line_num;
719
720 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
721
722 stack = g_string_new(src);
723 if (doc != NULL && doc->file_name != NULL)
724 {
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);
728 g_free(replacement);
729
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);
733 g_free(replacement);
734
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);
739 g_free(replacement);
740
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);
745 g_free(replacement);
746 }
747
748 /* replace %p with the current project's (absolute) base directory */
749 replacement = NULL; /* prevent double free if no replacement found */
750 if (app->project)
751 {
752 replacement = project_get_base_path();
753 }
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);
759 }
760
761 utils_string_replace_all(stack, "%p", replacement);
762 g_free(replacement);
763 g_free(executable);
764
765 return g_string_free(stack, FALSE); /* don't forget to free src also if needed */
766}
767
768
769/* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
770 * idx document directory */
771static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
772{
773 GError *error = NULL;
774 gchar *argv[] = { "/bin/sh", "-c", NULL, NULL };
775 gchar *working_dir;
776 gchar *utf8_working_dir;
777 gchar *cmd_string;
778
779 g_return_if_fail(doc == NULL || doc->is_valid);
780
781 if ((doc == NULL || EMPTY(doc->file_name)) && EMPTY(dir))
782 {
783 geany_debug("Failed to run command with no working directory");
784 ui_set_statusbar(TRUE, _("Process failed, no working directory"));
785 return;
786 }
787
790
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);
793
794 gtk_list_store_clear(msgwindow.store_compiler);
795 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
796 msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), cmd, utf8_working_dir);
797 g_free(utf8_working_dir);
798
799#ifdef G_OS_UNIX
800 cmd_string = utils_get_locale_from_utf8(cmd);
801 argv[2] = cmd_string;
802 cmd = NULL; /* under Unix, use argv to start cmd via sh for compatibility */
803#else
804 /* Expand environment variables like %blah%. */
805 cmd_string = win32_expand_environment_variables(cmd);
806 argv[0] = NULL; /* under Windows, run cmd directly */
807 cmd = cmd_string;
808#endif
809
810 /* set the build info for the message window */
811 g_free(build_info.dir);
812 build_info.dir = g_strdup(working_dir);
813 build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
814 build_info.message_count = 0;
815
817 GINT_TO_POINTER(0), 0, build_iofunc, GINT_TO_POINTER(1), 0, build_exit_cb, NULL,
818 &build_info.pid, &error))
819 {
820 geany_debug("build command spawning failed: %s", error->message);
821 ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
822 g_error_free(error);
823 }
824
825 g_free(working_dir);
826 g_free(cmd_string);
827}
828
829
830/* Returns: NULL if there was an error, or the command to be executed. If Geany is
831 * set to use a run script, the returned value is a path to the script that runs
832 * the command; otherwise the command itself is returned. working_dir is a pointer
833 * to the working directory from which the command is executed. Both strings are
834 * in the locale encoding. */
835static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
836{
837 GeanyBuildCommand *cmd = NULL;
838 const gchar *cmd_working_dir;
839 gboolean autoclose = FALSE;
840 gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string;
841 GError *error = NULL;
842
843 cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
844
845 cmd_string_utf8 = build_replace_placeholder(doc, cmd->command);
846 cmd_working_dir = cmd->working_dir;
847 if (EMPTY(cmd_working_dir))
848 cmd_working_dir = "%d";
849 working_dir_utf8 = build_replace_placeholder(doc, cmd_working_dir);
850 *working_dir = utils_get_locale_from_utf8(working_dir_utf8);
851
852 if (EMPTY(*working_dir) || ! g_file_test(*working_dir, G_FILE_TEST_EXISTS) ||
853 ! g_file_test(*working_dir, G_FILE_TEST_IS_DIR))
854 {
855 ui_set_statusbar(TRUE, _("Invalid working directory \"%s\""),
856 !EMPTY(working_dir_utf8) ? working_dir_utf8 : "<NULL>" );
857 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, *working_dir, NULL);
858 return NULL;
859 }
860
861 cmd_string = utils_get_locale_from_utf8(cmd_string_utf8);
862
863#ifdef HAVE_VTE
864 if (vte_info.have_vte && vc->run_in_vte)
865 {
866 if (vc->skip_run_script)
867 {
868 utils_free_pointers(2, cmd_string_utf8, working_dir_utf8, NULL);
869 return cmd_string;
870 }
871 else
872 /* don't wait for user input at the end of script when we are running in VTE */
873 autoclose = TRUE;
874 }
875#endif
876
877#ifdef G_OS_WIN32
878 /* Expand environment variables like %blah%. */
879 SETPTR(cmd_string, win32_expand_environment_variables(cmd_string));
880
881 gchar *helper = g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC), "geany-run-helper", NULL);
882 /* escape helper appropriately */
883 /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
884 * allowed in paths anyway */
885 run_cmd = g_strdup_printf("\"%s\" \"%s\" %d %s", helper, *working_dir, autoclose ? 1 : 0, cmd_string);
886 g_free(helper);
887#else
888 run_cmd = build_create_shellscript(*working_dir, cmd_string, autoclose, &error);
889 if (!run_cmd)
890 {
891 ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
892 !EMPTY(cmd_string_utf8) ? cmd_string_utf8 : NULL, error->message);
893 g_error_free(error);
894 g_free(*working_dir);
895 }
896#endif
897 utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
898 return run_cmd;
899}
900
901
902static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
903{
904 gchar *working_dir;
905 gchar *run_cmd = NULL;
906
907 if (! DOC_VALID(doc) || doc->file_name == NULL)
908 return;
909
910 run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex);
911 if (run_cmd == NULL)
912 return;
913
914 run_info[cmdindex].file_type_id = doc->file_type->id;
915
916#ifdef HAVE_VTE
917 if (vte_info.have_vte && vc->run_in_vte)
918 {
919 gchar *vte_cmd;
920
921 /* VTE expects commands in UTF-8 */
922 SETPTR(run_cmd, utils_get_utf8_from_locale(run_cmd));
924
925 if (vc->skip_run_script)
926 vte_cmd = g_strconcat(run_cmd, "\n", NULL);
927 else
928 vte_cmd = g_strconcat("\n/bin/sh ", run_cmd, "\n", NULL);
929
930 vte_cwd(working_dir, TRUE);
931 if (! vte_send_cmd(vte_cmd))
932 {
933 const gchar *msg = _("File not executed because the terminal may contain some input (press Ctrl+C or Enter to clear it).");
934 ui_set_statusbar(FALSE, "%s", msg);
935 geany_debug("%s", msg);
936 if (!vc->skip_run_script)
937 g_unlink(run_cmd);
938 }
939
940 /* show the VTE */
941 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
942 gtk_widget_grab_focus(vc->vte);
943 msgwin_show_hide(TRUE);
944
945 run_info[cmdindex].pid = 1;
946 g_free(vte_cmd);
947 }
948 else
949#endif
950 {
951 gchar *locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
952 GError *error = NULL;
953
954#ifdef G_OS_WIN32
955 if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd, 0, 0))
956 {
957 /* if passing an argument to cmd.exe, respect its quoting rules */
958 GString *escaped_run_cmd = g_string_new(NULL);
959 for (gchar *p = run_cmd; *p; p++)
960 {
961 if (strchr("()%!^\"<>&| ", *p)) // cmd.exe metacharacters
962 g_string_append_c(escaped_run_cmd, '^');
963 g_string_append_c(escaped_run_cmd, *p);
964 }
965 SETPTR(run_cmd, g_string_free(escaped_run_cmd, FALSE));
966 }
967#endif
968
969 utils_str_replace_all(&locale_term_cmd, "%c", run_cmd);
970
971 if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid),
972 &error))
973 {
974 g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
975 (gpointer) &(run_info[cmdindex]));
977 }
978 else
979 {
980 gchar *utf8_term_cmd = utils_get_utf8_from_locale(locale_term_cmd);
981 ui_set_statusbar(TRUE, _("Cannot execute build command \"%s\": %s. "
982 "Check the Terminal setting in Preferences"), utf8_term_cmd, error->message);
983 g_free(utf8_term_cmd);
984 g_error_free(error);
985#ifndef G_OS_WIN32
986 g_unlink(run_cmd);
987#endif
988 run_info[cmdindex].pid = (GPid) 0;
989 }
990 }
991
992 g_free(working_dir);
993 g_free(run_cmd);
994}
995
996
997static void process_build_output_line(gchar *msg, gint color)
998{
999 gchar *tmp;
1000 gchar *filename;
1001 gint line;
1002
1003 g_strchomp(msg);
1004
1005 if (EMPTY(msg))
1006 return;
1007
1008 if (build_parse_make_dir(msg, &tmp))
1009 {
1011 }
1013
1014 if (line != -1 && filename != NULL)
1015 {
1017
1018 /* limit number of indicators */
1019 if (doc && editor_prefs.use_indicators &&
1021 {
1022 if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
1023 line--; /* so only adjust the line number if it is greater than 0 */
1025 }
1026 build_info.message_count++;
1027 color = COLOR_RED; /* error message parsed on the line */
1028
1029 if (build_info.message_count == 1)
1030 {
1031 gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_NEXT_ERROR], TRUE);
1032 gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_PREV_ERROR], TRUE);
1033 }
1034 }
1035 g_free(filename);
1036
1038}
1039
1040
1041static void build_iofunc(GString *string, GIOCondition condition, gpointer data)
1042{
1043 if (condition & (G_IO_IN | G_IO_PRI))
1044 {
1045 process_build_output_line(string->str,
1046 (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK);
1047 }
1048}
1049
1050
1051gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
1052{
1053 const gchar *pos;
1054
1055 *prefix = NULL;
1056
1057 if (string == NULL)
1058 return FALSE;
1059
1060 if ((pos = strstr(string, "Entering directory")) != NULL)
1061 {
1062 gsize len;
1063 gchar *input;
1064
1065 /* get the start of the path */
1066 pos = strstr(string, "/");
1067
1068 if (pos == NULL)
1069 return FALSE;
1070
1071 input = g_strdup(pos);
1072
1073 /* kill the ' at the end of the path */
1074 len = strlen(input);
1075 input[len - 1] = '\0';
1076 input = g_realloc(input, len); /* shorten by 1 */
1077 *prefix = input;
1078
1079 return TRUE;
1080 }
1081
1082 if (strstr(string, "Leaving directory") != NULL)
1083 {
1084 *prefix = NULL;
1085 return TRUE;
1086 }
1087
1088 return FALSE;
1089}
1090
1091
1092static void show_build_result_message(gboolean failure)
1093{
1094 gchar *msg;
1095
1096 if (failure)
1097 {
1098 msg = _("Compilation failed.");
1100 /* If msgwindow is hidden, user will want to display it to see the error */
1101 if (! ui_prefs.msgwindow_visible)
1102 {
1103 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1104 msgwin_show_hide(TRUE);
1105 }
1106 else
1107 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1108 ui_set_statusbar(FALSE, "%s", msg);
1109 }
1110 else
1111 {
1112 msg = _("Compilation finished successfully.");
1114 if (! ui_prefs.msgwindow_visible ||
1115 gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
1116 ui_set_statusbar(FALSE, "%s", msg);
1117 }
1118}
1119
1120
1121static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
1122{
1123 show_build_result_message(!SPAWN_WIFEXITED(status) || SPAWN_WEXITSTATUS(status) != EXIT_SUCCESS);
1124 utils_beep();
1125
1126 build_info.pid = 0;
1127 /* enable build items again */
1130}
1131
1132
1133static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
1134{
1135 RunInfo *run_info_data = user_data;
1136
1137 g_spawn_close_pid(child_pid);
1138
1139 run_info_data->pid = 0;
1140 /* reset the stop button and menu item to the original meaning */
1142}
1143
1144
1145/* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
1146 * working_dir and cmd are both in the locale encoding
1147 * it returns the full file name (including path) of the created script in the locale encoding */
1148#ifndef G_OS_WIN32
1149static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
1150{
1151 gint fd;
1152 gchar *str, *fname;
1153 gboolean success = TRUE;
1154 gchar *escaped_dir;
1155 fd = g_file_open_tmp (RUN_SCRIPT_CMD, &fname, error);
1156 if (fd < 0)
1157 return NULL;
1158 close(fd);
1159
1160 escaped_dir = g_shell_quote(working_dir);
1161 str = g_strdup_printf(
1162 "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
1163 \n\n%s\n", escaped_dir, cmd, (autoclose) ? "" :
1164 "\necho \"Press return to continue\"\n#to be more compatible with shells like "
1165 "dash\ndummy_var=\"\"\nread dummy_var");
1166 g_free(escaped_dir);
1167
1168 if (!g_file_set_contents(fname, str, -1, error))
1169 success = FALSE;
1170 g_free(str);
1171#ifdef __APPLE__
1172 if (success && g_chmod(fname, 0777) != 0)
1173 {
1174 if (error)
1175 {
1176 gint errsv = errno;
1177
1178 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv),
1179 "Failed to make file executable: %s", g_strerror(errsv));
1180 }
1181 success = FALSE;
1182 }
1183#endif
1184
1185 if (!success)
1186 {
1187 g_unlink(fname);
1188 g_free(fname);
1189 fname = NULL;
1190 }
1191
1192 return fname;
1193}
1194#endif
1195
1196
1197typedef void Callback(GtkWidget *w, gpointer u);
1198
1199/* run the command catenating cmd_cat if present */
1200static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
1201{
1202 gchar *dir;
1203 gchar *full_command, *subs_command;
1204 GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
1205 gchar *cmdstr;
1206
1207 if (buildcmd == NULL)
1208 return;
1209
1210 cmdstr = buildcmd->command;
1211
1212 if (cmd_cat != NULL)
1213 {
1214 if (cmdstr != NULL)
1215 full_command = g_strconcat(cmdstr, cmd_cat, NULL);
1216 else
1217 full_command = g_strdup(cmd_cat);
1218 }
1219 else
1220 full_command = cmdstr;
1221
1222 dir = build_replace_placeholder(doc, buildcmd->working_dir);
1223 subs_command = build_replace_placeholder(doc, full_command);
1224 build_info.grp = grp;
1225 build_info.cmd = cmd;
1226 build_spawn_cmd(doc, subs_command, dir);
1227 g_free(subs_command);
1228 g_free(dir);
1229 if (cmd_cat != NULL)
1230 g_free(full_command);
1231 build_menu_update(doc);
1232 if (build_info.pid)
1234}
1235
1236
1237/*----------------------------------------------------------------
1238 *
1239 * Create build menu and handle callbacks (&toolbar callbacks)
1240 *
1241 *----------------------------------------------------------------*/
1242static void on_make_custom_input_response(const gchar *input, gpointer data)
1243{
1245
1246 SETPTR(build_info.custom_target, g_strdup(input));
1248 build_info.custom_target);
1249}
1250
1251
1252static void on_build_menu_item(GtkWidget *w, gpointer user_data)
1253{
1255 GeanyBuildCommand *bc;
1256 guint grp = GPOINTER_TO_GRP(user_data);
1257 guint cmd = GPOINTER_TO_CMD(user_data);
1258
1259 if (doc && doc->changed)
1260 {
1261 if (!document_save_file(doc, FALSE))
1262 return;
1263 }
1264 g_signal_emit_by_name(geany_object, "build-start");
1265
1266 if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
1267 {
1268 static GtkWidget *dialog = NULL; /* keep dialog for combo history */
1269
1270 if (! dialog)
1271 {
1272 dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
1273 _("Enter custom text here, all entered text is appended to the command."),
1275 }
1276 else
1277 {
1278 gtk_widget_show(dialog);
1279 }
1280 return;
1281 }
1282 else if (grp == GEANY_GBG_EXEC)
1283 {
1284 if (run_info[cmd].pid > (GPid) 1)
1285 {
1286 kill_process(&run_info[cmd].pid);
1287 return;
1288 }
1289 bc = get_build_cmd(doc, grp, cmd, NULL);
1290 if (bc != NULL && strcmp(bc->command, "builtin") == 0)
1291 {
1292 const gchar *uri_file_prefix;
1293 gchar *uri;
1294 if (doc == NULL)
1295 return;
1296 uri_file_prefix = utils_get_uri_file_prefix();
1297 uri = g_strconcat(uri_file_prefix, doc->file_name, NULL);
1298 utils_open_browser(uri);
1299 g_free(uri);
1300 }
1301 else
1302 build_run_cmd(doc, cmd);
1303 }
1304 else
1305 build_command(doc, grp, cmd, NULL);
1306}
1307
1308
1309/* group codes for menu items other than the known commands
1310 * value order is important, see the following table for use */
1311
1312/* the rest in each group */
1313#define MENU_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_FT)
1314#define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
1315#define MENU_EXEC_REST (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
1316/* the separator */
1317#define MENU_SEPARATOR (2*GEANY_GBG_COUNT)
1318/* the fixed items */
1319#define MENU_NEXT_ERROR (MENU_SEPARATOR + 1)
1320#define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1)
1321#define MENU_COMMANDS (MENU_PREV_ERROR + 1)
1322#define MENU_DONE (MENU_COMMANDS + 1)
1323
1324
1325static struct BuildMenuItemSpec {
1326 const gchar *stock_id;
1327 const gint key_binding;
1328 const guint build_grp;
1329 const guint build_cmd;
1330 const gchar *fix_label;
1332} build_menu_specs[] = {
1337 {NULL, -1, MENU_FT_REST,
1339 {NULL, -1, MENU_SEPARATOR,
1340 GBF_SEP_1, NULL, NULL},
1347 {NULL, -1, MENU_NON_FT_REST,
1349 {NULL, -1, MENU_SEPARATOR,
1350 GBF_SEP_2, NULL, NULL},
1351 {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
1352 GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
1354 GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
1355 {NULL, -1, MENU_SEPARATOR,
1356 GBF_SEP_3, NULL, NULL},
1357 {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
1359 {NULL, -1, MENU_EXEC_REST,
1361 {NULL, -1, MENU_SEPARATOR,
1362 GBF_SEP_4, NULL, NULL},
1363 {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
1364 GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
1365 {NULL, -1, MENU_DONE,
1366 0, NULL, NULL}
1368
1369
1370static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
1371 struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
1372{
1373 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
1374
1375 if (bs->stock_id != NULL)
1376 {
1377 GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1378 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1379 }
1380 gtk_widget_show(item);
1381 if (bs->key_binding >= 0)
1382 add_menu_accel(group, (guint) bs->key_binding, ag, item);
1383 gtk_container_add(GTK_CONTAINER(menu), item);
1384 if (bs->cb != NULL)
1385 {
1386 g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
1387 }
1388 menu_items.menu_item[grp][cmd] = item;
1389}
1390
1391
1392static void create_build_menu(BuildMenuItems *build_menu_items)
1393{
1394 GtkWidget *menu;
1395 GtkAccelGroup *accel_group = gtk_accel_group_new();
1397 guint i, j;
1398
1399 menu = gtk_menu_new();
1400 build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
1401 build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
1402 build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
1403 build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
1404
1405 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1406 {
1407 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1408 if (bs->build_grp == MENU_SEPARATOR)
1409 {
1410 GtkWidget *item = gtk_separator_menu_item_new();
1411 gtk_widget_show(item);
1412 gtk_container_add(GTK_CONTAINER(menu), item);
1413 build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
1414 }
1415 else if (bs->fix_label != NULL)
1416 {
1417 create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
1418 GBG_FIXED, bs->build_cmd);
1419 }
1420 else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
1421 {
1422 guint grp = bs->build_grp - GEANY_GBG_COUNT;
1423 for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
1424 {
1425 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
1426 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1427 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
1428 }
1429 }
1430 else
1431 {
1432 GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
1433 const gchar *lbl = (bc == NULL) ? "" : bc->label;
1434 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
1435 }
1436 }
1437 build_menu_items->menu = menu;
1438 gtk_widget_show(menu);
1439 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
1440}
1441
1442
1443/* * Update the build menu to reflect changes in configuration or status.
1444 *
1445 * Sets the labels and number of visible items to match the highest
1446 * priority configured commands. Also sets sensitivity if build commands are
1447 * running and switches executes to stop when commands are running.
1448 *
1449 * @param doc The current document, if available, to save looking it up.
1450 * If @c NULL it will be looked up.
1451 *
1452 * Call this after modifying any fields of a GeanyBuildCommand structure.
1453 *
1454 * @see Build Menu Configuration section of the Manual.
1455 *
1456 **/
1458{
1459 guint i, cmdcount, cmd, grp;
1460 gboolean vis = FALSE;
1461 gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
1462 gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
1463 GeanyBuildCommand *bc;
1464
1465 g_return_if_fail(doc == NULL || doc->is_valid);
1466
1467 if (menu_items.menu == NULL)
1469 if (doc == NULL)
1470 doc = document_get_current();
1471 have_path = doc != NULL && doc->file_name != NULL;
1472 build_running = build_info.pid > (GPid) 1;
1473 // note: compiler list store may have been cleared since last build
1474 have_errors = build_info.message_count > 0 &&
1475 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
1476 for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
1477 {
1478 struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
1479 switch (bs->build_grp)
1480 {
1481 case MENU_SEPARATOR:
1482 if (vis == TRUE)
1483 {
1484 gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1485 vis = FALSE;
1486 }
1487 else
1488 gtk_widget_hide(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
1489 break;
1490 case MENU_NEXT_ERROR:
1491 case MENU_PREV_ERROR:
1492 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
1493 vis |= TRUE;
1494 break;
1495 case MENU_COMMANDS:
1496 vis |= TRUE;
1497 break;
1498 default: /* all configurable commands */
1499 if (bs->build_grp >= GEANY_GBG_COUNT)
1500 {
1501 grp = bs->build_grp - GEANY_GBG_COUNT;
1502 cmdcount = build_groups_count[grp];
1503 }
1504 else
1505 {
1506 grp = bs->build_grp;
1507 cmdcount = bs->build_cmd + 1;
1508 }
1509 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
1510 {
1511 GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
1512 const gchar *label;
1513 bc = get_build_cmd(doc, grp, cmd, NULL);
1514 if (bc)
1515 label = bc->label;
1516 else
1517 label = NULL;
1518
1519 if (grp < GEANY_GBG_EXEC)
1520 {
1521 cmd_sensitivity =
1522 (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
1523 (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
1524 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1525 if (bc != NULL && !EMPTY(label))
1526 {
1527 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
1528 gtk_widget_show_all(menu_item);
1529 vis |= TRUE;
1530 }
1531 else
1532 gtk_widget_hide(menu_item);
1533 }
1534 else
1535 {
1536 GtkWidget *image;
1537 exec_running = run_info[cmd].pid > (GPid) 1;
1538 cmd_sensitivity = (bc != NULL) || exec_running;
1539 gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
1540 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1541 run_sensitivity = cmd_sensitivity;
1542 if (! exec_running)
1543 {
1544 image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
1545 }
1546 else
1547 {
1548 image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
1549 }
1550 if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
1551 run_running = exec_running;
1552 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
1553 if (bc != NULL && !EMPTY(label))
1554 {
1555 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
1556 gtk_widget_show_all(menu_item);
1557 vis |= TRUE;
1558 }
1559 else
1560 gtk_widget_hide(menu_item);
1561 }
1562 }
1563 }
1564 }
1565
1566 run_sensitivity &= (doc != NULL);
1568 && have_path && ! build_running;
1569 if (widgets.toolitem_build != NULL)
1570 gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
1571 can_make = FALSE;
1572 if (widgets.toolitem_make_all != NULL)
1573 gtk_widget_set_sensitive(widgets.toolitem_make_all,
1575 && ! build_running));
1576 if (widgets.toolitem_make_custom != NULL)
1577 gtk_widget_set_sensitive(widgets.toolitem_make_custom,
1579 && ! build_running));
1580 if (widgets.toolitem_make_object != NULL)
1581 gtk_widget_set_sensitive(widgets.toolitem_make_object,
1583 && ! build_running));
1584 if (widgets.toolitem_set_args != NULL)
1585 gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
1586
1588 && have_path && ! build_running;
1589 gtk_action_set_sensitive(widgets.compile_action, can_compile);
1590 gtk_action_set_sensitive(widgets.build_action, can_make);
1591 gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
1592
1593 /* show the stop command if a program is running from execute 0 , otherwise show run command */
1594 set_stop_button(run_running);
1595
1596}
1597
1598
1599/* Call build_menu_update() instead of calling this directly. */
1600static void set_stop_button(gboolean stop)
1601{
1602 const gchar *button_stock_id = NULL;
1603 GtkToolButton *run_button;
1604
1605 run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
1606 if (run_button != NULL)
1607 button_stock_id = gtk_tool_button_get_stock_id(run_button);
1608
1609 if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
1610 return;
1611 if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
1612 return;
1613
1614 /* use the run button also as stop button */
1615 if (stop)
1616 {
1617 if (run_button != NULL)
1618 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
1619 }
1620 else
1621 {
1622 if (run_button != NULL)
1623 gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
1624 }
1625}
1626
1627
1628static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
1629{
1630 /* For now, just show the project dialog */
1631 if (app->project)
1633 else
1635}
1636
1637
1638static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
1639{
1640 last_toolbutton_action = user_data;
1641 g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
1642 on_build_menu_item(menuitem, user_data);
1643}
1644
1645
1646static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
1647{
1648 gchar *msg;
1649
1650 last_toolbutton_action = user_data;
1652 msg = _("Build the current file with Make and the default target");
1654 msg = _("Build the current file with Make and the specified target");
1656 msg = _("Compile the current file with Make");
1657 else
1658 msg = NULL;
1659 g_object_set(widgets.build_action, "tooltip", msg, NULL);
1660 on_build_menu_item(menuitem, user_data);
1661}
1662
1663
1664static void kill_process(GPid *pid)
1665{
1666 GError *error = NULL;
1667
1668 if (spawn_kill_process(*pid, &error))
1669 {
1670 *pid = 0;
1672 }
1673 else
1674 {
1675 ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), error->message);
1676 g_error_free(error);
1677 }
1678}
1679
1680
1681static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
1682{
1683 if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
1685 {
1686 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1687 }
1688 else
1689 ui_set_statusbar(FALSE, _("No more build errors."));
1690}
1691
1692
1693static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
1694{
1695 if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
1697 {
1698 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
1699 }
1700 else
1701 ui_set_statusbar(FALSE, _("No more build errors."));
1702}
1703
1704
1705void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
1706{
1708 {
1710 }
1711 else
1712 {
1714 }
1715}
1716
1717
1718/*------------------------------------------------------
1719 *
1720 * Create and handle the build menu configuration dialog
1721 *
1722 *-------------------------------------------------------*/
1723typedef struct RowWidgets
1724{
1728 GeanyBuildCommand *cmdsrc;
1729 guint grp;
1730 guint cmd;
1731 gboolean cleared;
1732 gboolean used_dst;
1734
1735
1736static GdkRGBA insensitive_color;
1737
1738static void set_row_color(RowWidgets *r, GdkRGBA *color)
1739{
1740 enum GeanyBuildCmdEntries i;
1741
1742 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1743 {
1744 if (i == GEANY_BC_LABEL)
1745 continue;
1746
1747 gtk_widget_override_color(r->entries[i], GTK_STATE_FLAG_NORMAL, color);
1748 }
1749}
1750
1751
1752static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
1753{
1754 if (GTK_IS_BUTTON(wid))
1755 gtk_button_set_label(GTK_BUTTON(wid), text);
1756 else
1757 gtk_entry_set_text(GTK_ENTRY(wid), text);
1758}
1759
1760
1761static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
1762{
1763 RowWidgets *r = user_data;
1764 guint src;
1765 enum GeanyBuildCmdEntries i;
1766 GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
1767
1768 if (bc != NULL)
1769 {
1770 r->cmdsrc = bc;
1771 r->src = src;
1772 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1773 {
1775 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
1776 }
1777 }
1778 else
1779 {
1780 r->cmdsrc = NULL;
1781 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1782 {
1784 }
1785 }
1786 r->used_dst = FALSE;
1788 r->cleared = TRUE;
1789}
1790
1791
1792static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
1793{
1794 gtk_entry_set_text(regex,"");
1795}
1796
1797
1798static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
1799{
1800 RowWidgets *r = user_data;
1801 GtkWidget *top_level = gtk_widget_get_toplevel(wid);
1802 const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
1803 gchar *str;
1804
1805 if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
1806 str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
1807 else
1808 str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
1809
1810 if (!str)
1811 return;
1812
1813 gtk_button_set_label(GTK_BUTTON(wid), str);
1814 g_free(str);
1815 r->used_dst = TRUE;
1816 set_row_color(r, NULL);
1817}
1818
1819
1820static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
1821{
1822 RowWidgets *r = user_data;
1823
1824 r->used_dst = TRUE;
1825 set_row_color(r, NULL);
1826}
1827
1828
1829/* Column headings, array NULL-terminated */
1830static const gchar *colheads[] =
1831{
1832 "#",
1833 N_("Label"),
1834 N_("Command"),
1835 N_("Working directory"),
1836 N_("Reset"),
1837 NULL
1838};
1839
1840/* column names */
1841#define DC_ITEM 0
1842#define DC_ENTRIES 1
1843#define DC_CLEAR 4
1844#define DC_N_COL 5
1845
1846static const guint entry_x_padding = 3;
1847static const guint entry_y_padding = 0;
1848
1849
1850static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
1851 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
1852{
1853 GtkWidget *label, *clear, *clearicon;
1854 RowWidgets *roww;
1855 GeanyBuildCommand *bc;
1856 guint src;
1857 enum GeanyBuildCmdEntries i;
1858 guint column = 0;
1859 gchar *text;
1860 GtkStyleContext *ctx;
1861
1862 g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
1863
1864 text = g_strdup_printf("%d.", cmd + 1);
1865 label = gtk_label_new(text);
1866 g_free(text);
1867
1868 ctx = gtk_widget_get_style_context(label);
1869 gtk_style_context_save(ctx);
1870 gtk_style_context_get_color(ctx, GTK_STATE_FLAG_INSENSITIVE, &insensitive_color);
1871 gtk_style_context_restore(ctx);
1872
1873 gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
1874 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1875 roww = g_new0(RowWidgets, 1);
1876 roww->src = GEANY_BCS_COUNT;
1877 roww->grp = grp;
1878 roww->cmd = cmd;
1879 roww->dst = dst;
1880 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1881 {
1882 gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
1883
1884 column += 1;
1885 if (i == GEANY_BC_LABEL)
1886 {
1887 GtkWidget *wid = roww->entries[i] = gtk_button_new();
1888 gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
1889 gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
1890 g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
1891 }
1892 else
1893 {
1894 roww->entries[i] = gtk_entry_new();
1895 g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
1896 }
1897 gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
1898 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1899 }
1900 column++;
1901 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1902 clear = gtk_button_new();
1903 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
1904 g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
1905 gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
1906 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1907 roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
1908 if (bc != NULL)
1909 roww->src = src;
1910
1911 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1912 {
1913 const gchar *str = "";
1914
1915 if (bc != NULL )
1916 {
1917 if ((str = id_to_str(bc, i)) == NULL)
1918 str = "";
1919 else if (dst == src)
1920 roww->used_dst = TRUE;
1921 }
1923 }
1924 if (bc != NULL && (dst > src))
1926 if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
1927 {
1928 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
1929 gtk_widget_set_sensitive(roww->entries[i], FALSE);
1930 gtk_widget_set_sensitive(clear, FALSE);
1931 }
1932 return roww;
1933}
1934
1935
1936typedef struct BuildTableFields
1937{
1939 GtkWidget *fileregex;
1940 GtkWidget *nonfileregex;
1944
1945
1946GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
1947 GeanyFiletype *ft)
1948{
1949 GtkWidget *label, *sep, *clearicon, *clear;
1950 BuildTableFields *fields;
1951 GtkTable *table;
1952 const gchar **ch;
1953 gchar *txt;
1954 guint col, row, cmdindex;
1955 guint cmd;
1956 guint src;
1957 gboolean sensitivity;
1958 guint sep_padding = entry_y_padding + 3;
1959
1960 table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
1961 fields = g_new0(BuildTableFields, 1);
1962 fields->rows = g_new0(RowWidgets*, build_items_count);
1963 for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
1964 {
1965 label = gtk_label_new(_(*ch));
1966 gtk_table_attach(table, label, col, col + 1, 0, 1,
1967 GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1968 }
1969 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1970 gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
1971 entry_x_padding, sep_padding);
1972 if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
1973 txt = g_strdup_printf(_("%s commands"), ft->name);
1974 else
1975 txt = g_strdup_printf(_("%s commands"), _("No filetype"));
1976
1977 label = ui_label_new_bold(txt);
1978 g_free(txt);
1979 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1980 gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
1982 for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
1983 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
1984 label = gtk_label_new(_("Error regular expression:"));
1985 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
1987 fields->fileregex = gtk_entry_new();
1989 sensitivity = (ft == NULL) ? FALSE : TRUE;
1990 if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
1991 {
1992 gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
1993 if (src > dst)
1994 sensitivity = FALSE;
1995 }
1996 gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
1997 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
1998 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1999 clear = gtk_button_new();
2000 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2001 g_signal_connect_swapped(clear, "clicked",
2002 G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
2003 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2004 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2005 gtk_widget_set_sensitive(fields->fileregex, sensitivity);
2006 gtk_widget_set_sensitive(clear, sensitivity);
2007 ++row;
2008 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2009 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2010 entry_x_padding, sep_padding);
2011 ++row;
2012 label = ui_label_new_bold(_("Independent commands"));
2013 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2014 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2016 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
2017 fields->rows[cmdindex] = build_add_dialog_row(
2018 doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
2019 label = gtk_label_new(_("Error regular expression:"));
2020 gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
2021 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2022 fields->nonfileregex = gtk_entry_new();
2024 sensitivity = TRUE;
2025 if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
2026 {
2027 gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
2028 sensitivity = src > dst ? FALSE : TRUE;
2029 }
2030 gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
2031 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2032 clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
2033 clear = gtk_button_new();
2034 gtk_button_set_image(GTK_BUTTON(clear), clearicon);
2035 g_signal_connect_swapped(clear, "clicked",
2036 G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
2037 gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
2038 GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
2039 gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
2040 gtk_widget_set_sensitive(clear, sensitivity);
2041 ++row;
2042 label = gtk_label_new(NULL);
2043 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2044 _("Note: Item 2 opens a dialog and appends the response to the command."));
2045 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2046 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2048 ++row;
2049 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2050 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2051 entry_x_padding, sep_padding);
2052 ++row;
2053 label = ui_label_new_bold(_("Execute commands"));
2054 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2055 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2057 for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
2058 fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
2059 sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
2060 gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2061 entry_x_padding, sep_padding);
2062 ++row;
2063 label = gtk_label_new(NULL);
2064 ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
2065 _("%d, %e, %f, %p, %l are substituted in command and directory fields, see manual for details."));
2066 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2067 gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
2069 /*printf("%d extra rows in dialog\n", row-build_items_count);*/
2070 ++row;
2071 *table_data = fields;
2072 return GTK_WIDGET(table);
2073}
2074
2075
2076void build_free_fields(BuildTableData table_data)
2077{
2078 guint cmdindex;
2079
2080 for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
2081 g_free(table_data->rows[cmdindex]);
2082 g_free(table_data->rows);
2083 g_free(table_data);
2084}
2085
2086
2087/* string compare where null pointers match null or 0 length strings */
2088#if 0
2089static gint stcmp(const gchar *a, const gchar *b)
2090{
2091 if (a == NULL && b == NULL)
2092 return 0;
2093 if (a == NULL && b != NULL)
2094 return strlen(b);
2095 if (a != NULL && b == NULL)
2096 return strlen(a);
2097 return strcmp(a, b);
2098}
2099#endif
2100
2101
2102static const gchar *get_build_command_entry_text(GtkWidget *wid)
2103{
2104 if (GTK_IS_BUTTON(wid))
2105 return gtk_button_get_label(GTK_BUTTON(wid));
2106 else
2107 return gtk_entry_get_text(GTK_ENTRY(wid));
2108}
2109
2110
2111static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
2112{
2114 gboolean changed = FALSE;
2115 enum GeanyBuildCmdEntries i;
2116
2117 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2118 {
2119 entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
2120 }
2121 if (table_data->rows[drow]->cleared)
2122 {
2123 if (dst->dst[grp] != NULL)
2124 {
2125 if (*(dst->dst[grp]) == NULL)
2126 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2127 (*(dst->dst[grp]))[cmd].exists = FALSE;
2128 (*(dst->dst[grp]))[cmd].changed = TRUE;
2129 changed = TRUE;
2130 }
2131 }
2132 if (table_data->rows[drow]->used_dst == TRUE)
2133 {
2134 if (dst->dst[grp] != NULL)
2135 {
2136 if (*(dst->dst[grp]) == NULL)
2137 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2138 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2139 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
2140 (*(dst->dst[grp]))[cmd].exists = TRUE;
2141 (*(dst->dst[grp]))[cmd].changed = TRUE;
2142 changed = TRUE;
2143 }
2144 }
2145 else
2146 {
2147 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2148 g_free(entries[i]);
2149 }
2150 return changed;
2151}
2152
2153
2154static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
2155{
2156 gboolean changed = FALSE;
2157 const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
2158
2159 if (((src == NULL /* originally there was no regex */
2160 || *src == NULL) /* or it was NULL*/
2161 && !EMPTY(reg)) /* and something was typed */
2162 || (src != NULL /* originally there was a regex*/
2163 && (*src == NULL /* and either it was NULL */
2164 || strcmp(*src, reg) != 0))) /* or it has been changed */
2165 {
2166 if (dst != NULL)
2167 {
2168 SETPTR(*dst, g_strdup(reg));
2169 changed = TRUE;
2170 }
2171 }
2172 return changed;
2173}
2174
2175
2176static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
2177{
2178 guint cmdindex, cmd;
2179 gboolean changed = FALSE;
2180
2181 if (response == GTK_RESPONSE_ACCEPT)
2182 {
2183 for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
2184 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
2185 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
2186 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
2187 for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
2188 changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
2189 changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
2190 changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
2191 }
2192 return changed;
2193}
2194
2195
2196void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
2197{
2198 BuildDestination menu_dst;
2199
2200 if (ft != NULL)
2201 {
2202 menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds);
2203 menu_dst.fileregexstr = &(ft->priv->projerror_regex_string);
2204 }
2205 else
2206 {
2207 menu_dst.dst[GEANY_GBG_FT] = NULL;
2208 menu_dst.fileregexstr = NULL;
2209 }
2210 menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
2211 menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
2212 menu_dst.nonfileregexstr = &regex_proj;
2213
2214 build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
2215}
2216
2217
2219{
2220 GtkWidget *dialog, *table, *vbox;
2222 GeanyFiletype *ft = NULL;
2223 const gchar *title = _("Set Build Commands");
2224 gint response;
2225 BuildTableData table_data;
2226 BuildDestination prefdsts;
2227
2228 if (doc != NULL)
2229 ft = doc->file_type;
2230 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
2231 GTK_DIALOG_DESTROY_WITH_PARENT,
2232 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2233 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
2234 table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
2235 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
2236 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
2238 /* run modally to prevent user changing idx filetype */
2239 response = gtk_dialog_run(GTK_DIALOG(dialog));
2240
2241 prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
2242 if (ft != NULL)
2243 {
2244 prefdsts.dst[GEANY_GBG_FT] = &(ft->priv->homefilecmds);
2245 prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string);
2246 prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds);
2247 }
2248 else
2249 {
2250 prefdsts.dst[GEANY_GBG_FT] = NULL;
2251 prefdsts.fileregexstr = NULL;
2252 prefdsts.dst[GEANY_GBG_EXEC] = NULL;
2253 }
2254 prefdsts.nonfileregexstr = &regex_pref;
2255 if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
2257 build_free_fields(table_data);
2258
2259 build_menu_update(doc);
2260 gtk_widget_destroy(dialog);
2261}
2262
2263
2264/* Creates the relevant build menu if necessary. */
2265BuildMenuItems *build_get_menu_items(gint filetype_idx)
2266{
2267 BuildMenuItems *items;
2268
2269 items = &menu_items;
2270 if (items->menu == NULL)
2271 create_build_menu(items);
2272 return items;
2273}
2274
2275
2276/*----------------------------------------------------------
2277 *
2278 * Load and store configuration
2279 *
2280 * ---------------------------------------------------------*/
2281static const gchar *build_grp_name = "build-menu";
2282
2283/* config format for build-menu group is prefix_gg_nn_xx=value
2284 * where gg = FT, NF, EX for the command group
2285 * nn = 2 digit command number
2286 * xx = LB for label, CM for command and WD for working dir */
2287static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
2288static const gchar *fixedkey="xx_xx_xx";
2289
2290#define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
2291#define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
2292#define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
2293
2294static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
2295 gchar *prefix, gboolean loc)
2296{
2297 guint cmd;
2298 gsize prefixlen; /* NOTE prefixlen used in macros above */
2299 GeanyBuildCommand *dstcmd;
2300 gchar *key;
2301 static gchar cmdbuf[4] = " ";
2302
2303 if (*dst == NULL)
2304 *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
2305 dstcmd = *dst;
2306 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2307 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2308 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2309 {
2310 gchar *label;
2311 if (cmd >= 100)
2312 break; /* ensure no buffer overflow */
2313 sprintf(cmdbuf, "%02u", cmd);
2314 set_key_grp(key, groups[grp]);
2315 set_key_cmd(key, cmdbuf);
2316 set_key_fld(key, "LB");
2317 if (loc)
2318 label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
2319 else
2320 label = g_key_file_get_string(config, build_grp_name, key, NULL);
2321 if (label != NULL)
2322 {
2323 dstcmd[cmd].exists = TRUE;
2324 SETPTR(dstcmd[cmd].label, label);
2325 set_key_fld(key,"CM");
2326 SETPTR(dstcmd[cmd].command,
2327 g_key_file_get_string(config, build_grp_name, key, NULL));
2328 set_key_fld(key,"WD");
2329 SETPTR(dstcmd[cmd].working_dir,
2330 g_key_file_get_string(config, build_grp_name, key, NULL));
2331 }
2332 else dstcmd[cmd].exists = FALSE;
2333 }
2334 g_free(key);
2335}
2336
2337
2338/* set GeanyBuildCommand if it doesn't already exist and there is a command */
2339static void assign_cmd(GeanyBuildCommand *type, guint id,
2340 const gchar *label, gchar *value)
2341{
2342 if (!EMPTY(value) && ! type[GBO_TO_CMD(id)].exists)
2343 {
2344 type[GBO_TO_CMD(id)].exists = TRUE;
2345 SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(label));
2346 SETPTR(type[GBO_TO_CMD(id)].command, value);
2347 SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL);
2348 type[GBO_TO_CMD(id)].old = TRUE;
2349 }
2350 else
2351 g_free(value);
2352}
2353
2354/* for the specified source load new format build menu items or try to make some sense of
2355 * old format setings, not done perfectly but better than ignoring them */
2356void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
2357{
2358 GeanyFiletype *ft;
2359 GeanyProject *pj;
2360 gchar **ftlist;
2361 gchar *value, *basedir, *makebasedir;
2362 gboolean bvalue = FALSE;
2363
2364 if (g_key_file_has_group(config, build_grp_name))
2365 {
2366 switch (src)
2367 {
2368 case GEANY_BCS_FT:
2369 ft = (GeanyFiletype*)p;
2370 if (ft == NULL)
2371 return;
2372 build_load_menu_grp(config, &(ft->priv->filecmds), GEANY_GBG_FT, NULL, TRUE);
2373 build_load_menu_grp(config, &(ft->priv->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
2374 build_load_menu_grp(config, &(ft->priv->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
2376 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2377 break;
2378 case GEANY_BCS_HOME_FT:
2379 ft = (GeanyFiletype*)p;
2380 if (ft == NULL)
2381 return;
2382 build_load_menu_grp(config, &(ft->priv->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
2383 build_load_menu_grp(config, &(ft->priv->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
2385 g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2386 break;
2387 case GEANY_BCS_PREF:
2390 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2391 break;
2392 case GEANY_BCS_PROJ:
2395 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
2396 pj = (GeanyProject*)p;
2397 if (p == NULL)
2398 return;
2399 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
2400 if (ftlist != NULL)
2401 {
2402 gchar **ftname;
2403 if (pj->priv->build_filetypes_list == NULL)
2404 pj->priv->build_filetypes_list = g_ptr_array_new();
2405 g_ptr_array_set_size(pj->priv->build_filetypes_list, 0);
2406 for (ftname = ftlist; *ftname != NULL; ++ftname)
2407 {
2408 ft = filetypes_lookup_by_name(*ftname);
2409 if (ft != NULL)
2410 {
2411 gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
2412 g_ptr_array_add(pj->priv->build_filetypes_list, ft);
2414 g_key_file_get_string(config, build_grp_name, regkey, NULL));
2415 g_free(regkey);
2416 build_load_menu_grp(config, &(ft->priv->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
2417 build_load_menu_grp(config, &(ft->priv->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
2418 }
2419 }
2420 g_free(ftlist);
2421 }
2422 break;
2423 default: /* defaults don't load from config, see build_init */
2424 break;
2425 }
2426 }
2427
2428 /* load old [build_settings] values if there is no value defined by [build-menu] */
2429
2430 switch (src)
2431 {
2432 case GEANY_BCS_FT:
2433 ft = (GeanyFiletype*)p;
2434 value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
2435 if (value != NULL)
2436 {
2437 if (ft->priv->filecmds == NULL)
2438 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2439 assign_cmd(ft->priv->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
2440 }
2441 value = g_key_file_get_string(config, "build_settings", "linker", NULL);
2442 if (value != NULL)
2443 {
2444 if (ft->priv->filecmds == NULL)
2445 ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2446 assign_cmd(ft->priv->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
2447 }
2448 value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
2449 if (value != NULL)
2450 {
2451 if (ft->priv->execcmds == NULL)
2452 ft->priv->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2453 assign_cmd(ft->priv->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
2454 }
2455 if (ft->error_regex_string == NULL)
2456 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
2457 break;
2458 case GEANY_BCS_PROJ:
2459 if (non_ft_pref == NULL)
2460 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2461 basedir = project_get_base_path();
2462 if (basedir == NULL)
2463 basedir = g_strdup("%d");
2464 bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
2465 if (bvalue)
2466 makebasedir = g_strdup(basedir);
2467 else
2468 makebasedir = g_strdup("%d");
2472 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
2475 value = g_key_file_get_string(config, "project", "run_cmd", NULL);
2476 if (!EMPTY(value))
2477 {
2478 if (exec_proj == NULL)
2479 exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2480 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
2481 {
2482 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
2483 SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
2486 exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
2487 }
2488 }
2489 g_free(makebasedir);
2490 g_free(basedir);
2491 break;
2492 case GEANY_BCS_PREF:
2493 value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
2494 if (value != NULL)
2495 {
2496 if (non_ft_pref == NULL)
2497 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2498 assign_cmd(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
2499 g_strdup_printf("%s ", value));
2501 g_strdup_printf("%s %%e.o",value));
2502 assign_cmd(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
2503 }
2504 break;
2505 default:
2506 break;
2507 }
2508}
2509
2510
2511static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
2512{
2513 guint cmd;
2514 gsize prefixlen; /* NOTE prefixlen used in macros above */
2515 gchar *key;
2516 guint count = 0;
2517 enum GeanyBuildCmdEntries i;
2518
2519 if (src == NULL)
2520 return 0;
2521 prefixlen = prefix == NULL ? 0 : strlen(prefix);
2522 key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
2523 for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
2524 {
2525 if (src[cmd].exists) ++count;
2526 if (src[cmd].changed)
2527 {
2528 static gchar cmdbuf[4] = " ";
2529 if (cmd >= 100)
2530 break; /* ensure no buffer overflow */
2531 sprintf(cmdbuf, "%02u", cmd);
2532 set_key_grp(key, groups[grp]);
2533 set_key_cmd(key, cmdbuf);
2534 if (src[cmd].exists)
2535 {
2536 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2537 {
2538 set_key_fld(key, config_keys[i]);
2539 g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
2540 }
2541 }
2542 else
2543 {
2544 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
2545 {
2546 set_key_fld(key, config_keys[i]);
2547 g_key_file_remove_key(config, build_grp_name, key, NULL);
2548 }
2549 }
2550 }
2551 }
2552 g_free(key);
2553 return count;
2554}
2555
2556
2557static gboolean save_project_filetype(GeanyFiletype *ft, GKeyFile *config)
2558{
2559 guint i = 0;
2560 gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
2561
2562 i += build_save_menu_grp(config, ft->priv->projfilecmds, GEANY_GBG_FT, ft->name);
2563 i += build_save_menu_grp(config, ft->priv->projexeccmds, GEANY_GBG_EXEC, ft->name);
2565 {
2566 g_key_file_set_string(config, build_grp_name, regkey, ft->priv->projerror_regex_string);
2567 i++;
2568 }
2569 else
2570 g_key_file_remove_key(config, build_grp_name, regkey, NULL);
2571 g_free(regkey);
2572 return (i > 0);
2573}
2574
2575/* TODO: untyped ptr is too ugly (also for build_load_menu) */
2576void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
2577{
2578 GeanyFiletype *ft;
2579 GeanyProject *pj;
2580
2581 switch (src)
2582 {
2583 case GEANY_BCS_HOME_FT:
2584 ft = (GeanyFiletype*)ptr;
2585 if (ft == NULL)
2586 return;
2590 g_key_file_set_string(config, build_grp_name, "error_regex", ft->priv->homeerror_regex_string);
2591 else
2592 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2593 break;
2594 case GEANY_BCS_PREF:
2597 if (!EMPTY(regex_pref))
2598 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
2599 else
2600 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2601 break;
2602 case GEANY_BCS_PROJ:
2603 pj = (GeanyProject*)ptr;
2606 if (!EMPTY(regex_proj))
2607 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
2608 else
2609 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
2610 if (pj->priv->build_filetypes_list != NULL)
2611 {
2612 GPtrArray *ft_names = g_ptr_array_new();
2613 const GPtrArray *build_fts = pj->priv->build_filetypes_list;
2614
2615 for (guint i = 0; i < build_fts->len; i++)
2616 {
2617 ft = build_fts->pdata[i];
2618 if (save_project_filetype(ft, config))
2619 g_ptr_array_add(ft_names, ft->name);
2620 }
2621 if (ft_names->pdata != NULL)
2622 g_key_file_set_string_list(config, build_grp_name, "filetypes",
2623 (const gchar**)ft_names->pdata, ft_names->len);
2624 else
2625 g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
2626 g_ptr_array_free(ft_names, TRUE);
2627 }
2628 break;
2629 default: /* defaults and GEANY_BCS_FT can't save */
2630 break;
2631 }
2632}
2633
2634
2635/* FIXME: count is int only because calling code doesn't handle checking its value itself */
2637{
2638 guint i, sum;
2639
2640 g_return_if_fail(count >= 0);
2641
2642 if ((guint) count > build_groups_count[grp])
2643 build_groups_count[grp] = (guint) count;
2644 for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
2645 sum += build_groups_count[i];
2646 build_items_count = sum;
2647}
2648
2649
2650/** Get the count of commands for the group
2651 *
2652 * Get the number of commands in the group specified by @a grp.
2653 *
2654 * @param grp the group of the specified menu item.
2655 *
2656 * @return a count of the number of commands in the group
2657 *
2658 **/
2659GEANY_API_SYMBOL
2661{
2662 g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
2663 return build_groups_count[grp];
2664}
2665
2666
2667static void on_project_close(void)
2668{
2669 /* remove project regexen */
2671}
2672
2673
2674static struct
2675{
2676 const gchar *label;
2677 const gchar *command;
2678 const gchar *working_dir;
2679 GeanyBuildCommand **ptr;
2680 gint index;
2681} default_cmds[] = {
2682 { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
2683 { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
2684 { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
2685 { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
2686 { NULL, NULL, NULL, NULL, 0 }
2688
2689
2690void build_init(void)
2691{
2692 GtkWidget *item;
2693 GtkWidget *toolmenu;
2694 gint cmdindex;
2695
2696 g_signal_connect(geany_object, "project-close", on_project_close, NULL);
2697
2698 ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
2699 non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
2700 exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
2702
2703 for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
2704 {
2705 GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
2706 cmd->exists = TRUE;
2707 cmd->label = g_strdup(_(default_cmds[cmdindex].label));
2708 cmd->command = g_strdup(default_cmds[cmdindex].command);
2709 cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
2710 }
2711
2712 /* create the toolbar Build item sub menu */
2713 toolmenu = gtk_menu_new();
2714 g_object_ref(toolmenu);
2715
2716 /* build the code */
2717 item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
2718 gtk_widget_show(item);
2719 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2720 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
2722 widgets.toolitem_build = item;
2723
2724 item = gtk_separator_menu_item_new();
2725 gtk_widget_show(item);
2726 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2727
2728 /* build the code with make all */
2729 item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
2730 gtk_widget_show(item);
2731 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2732 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2734 widgets.toolitem_make_all = item;
2735
2736 /* build the code with make custom */
2737 item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
2738 gtk_widget_show(item);
2739 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2740 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2742 widgets.toolitem_make_custom = item;
2743
2744 /* build the code with make object */
2745 item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
2746 gtk_widget_show(item);
2747 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2748 g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
2750 widgets.toolitem_make_object = item;
2751
2752 item = gtk_separator_menu_item_new();
2753 gtk_widget_show(item);
2754 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2755
2756 /* arguments */
2757 item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
2758 gtk_widget_show(item);
2759 gtk_container_add(GTK_CONTAINER(toolmenu), item);
2760 g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
2761 widgets.toolitem_set_args = item;
2762
2763 /* get toolbar action pointers */
2764 widgets.build_action = toolbar_get_action_by_name("Build");
2765 widgets.compile_action = toolbar_get_action_by_name("Compile");
2766 widgets.run_action = toolbar_get_action_by_name("Run");
2767 widgets.toolmenu = toolmenu;
2768 /* set the submenu to the toolbar item */
2770}
2771
2772
2773gboolean build_keybinding(guint key_id)
2774{
2775 GtkWidget *item;
2776 BuildMenuItems *menu_items;
2778
2779 if (doc == NULL)
2780 return TRUE;
2781
2782 if (!gtk_widget_is_sensitive(ui_lookup_widget(main_widgets.window, "menu_build1")))
2783 return TRUE;
2784
2786 /* TODO make it a table??*/
2787 switch (key_id)
2788 {
2791 break;
2794 break;
2797 break;
2800 break;
2803 break;
2805 item = menu_items->menu_item[GBG_FIXED][GBF_NEXT_ERROR];
2806 break;
2808 item = menu_items->menu_item[GBG_FIXED][GBF_PREV_ERROR];
2809 break;
2812 break;
2814 item = menu_items->menu_item[GBG_FIXED][GBF_COMMANDS];
2815 break;
2816 default:
2817 item = NULL;
2818 }
2819 /* Note: For Build menu items it's OK (at the moment) to assume they are in the correct
2820 * sensitive state, but some other menus don't update the sensitive status until
2821 * they are redrawn. */
2822 if (item && gtk_widget_is_sensitive(item))
2823 gtk_menu_item_activate(GTK_MENU_ITEM(item));
2824 return TRUE;
2825}
2826
Contains the GeanyApp.
static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
Definition: build.c:2154
static BuildMenuItems menu_items
Definition: build.c:140
#define GBO_TO_CMD(gbo)
Definition: build.c:126
#define GEANY_BUILD_ERR_HIGHLIGHT_MAX
Definition: build.c:64
void build_free_fields(BuildTableData table_data)
Definition: build.c:2076
static GeanyBuildCommand ** get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
Definition: build.c:467
#define MENU_FT_REST
Definition: build.c:1313
static void on_build_menu_item(GtkWidget *w, gpointer user_data)
Definition: build.c:1252
const gchar * build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd, const GeanyBuildCmdEntries fld)
Get the string for the menu item field.
Definition: build.c:607
static RunInfo * run_info
Definition: build.c:77
static void kill_process(GPid *pid)
Definition: build.c:1664
void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
Definition: build.c:1705
#define MENU_COMMANDS
Definition: build.c:1321
static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
Definition: build.c:2511
GeanyBuildInfo build_info
Definition: build.c:67
static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
Definition: build.c:1761
void build_init(void)
Definition: build.c:2690
#define GPOINTER_TO_CMD(gptr)
Definition: build.c:135
#define set_key_cmd(key, cmd)
Definition: build.c:2291
#define GPOINTER_TO_GRP(gptr)
Definition: build.c:136
static void process_build_output_line(gchar *msg, gint color)
Definition: build.c:997
guint build_get_group_count(const GeanyBuildGroup grp)
Get the count of commands for the group.
Definition: build.c:2660
GtkWidget * toolitem_set_args
Definition: build.c:153
gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
Definition: build.c:1051
static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
Definition: build.c:216
struct RowWidgets RowWidgets
#define DC_ENTRIES
Definition: build.c:1842
static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
Definition: build.c:1798
void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
Definition: build.c:2576
static const gchar * build_grp_name
Definition: build.c:2281
static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp, gchar *prefix, gboolean loc)
Definition: build.c:2294
static GeanyBuildCommand * get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
Definition: build.c:426
static GeanyBuildCommand * non_ft_def
Definition: build.c:251
static const gchar RUN_SCRIPT_CMD[]
Definition: build.c:80
static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag, struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
Definition: build.c:1370
static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
Definition: build.c:771
static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
Definition: build.c:902
#define MENU_EXEC_REST
Definition: build.c:1315
static GeanyBuildCommand * get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex, guint below, guint *from)
Definition: build.c:373
static GeanyBuildCommand * exec_def
Definition: build.c:254
#define return_ft_cmd_if(src, cmds)
Definition: build.c:361
static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
Definition: build.c:1693
static const guint entry_x_padding
Definition: build.c:1846
static void printfcmds(void)
Definition: build.c:268
static GeanyBuildCommand * get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
Definition: build.c:519
static const guint entry_y_padding
Definition: build.c:1847
static const gchar * colheads[]
Definition: build.c:1830
static void build_exit_cb(GPid pid, gint status, gpointer user_data)
Definition: build.c:1121
static const gchar * fixedkey
Definition: build.c:2288
static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
Definition: build.c:1752
GtkAction * build_action
Definition: build.c:146
void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
Activate the menu item.
Definition: build.c:693
const gchar * working_dir
Definition: build.c:2678
static void show_build_result_message(gboolean failure)
Definition: build.c:1092
BuildMenuItems * build_get_menu_items(gint filetype_idx)
Definition: build.c:2265
static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
Definition: build.c:1200
static gboolean save_project_filetype(GeanyFiletype *ft, GKeyFile *config)
Definition: build.c:2557
static gchar * build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
Definition: build.c:1149
static GeanyBuildCommand * exec_proj
Definition: build.c:252
static gpointer last_toolbutton_action
Definition: build.c:138
void Callback(GtkWidget *w, gpointer u)
Definition: build.c:1197
static void clear_all_errors(void)
Definition: build.c:700
static RowWidgets * build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row, GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
Definition: build.c:1850
void build_menu_update(GeanyDocument *doc)
Definition: build.c:1457
static void set_row_color(RowWidgets *r, GdkRGBA *color)
Definition: build.c:1738
static gchar * build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
Definition: build.c:713
#define GBO_TO_GBG(gbo)
Definition: build.c:110
#define MENU_PREV_ERROR
Definition: build.c:1320
void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
Definition: build.c:2356
static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
Definition: build.c:1638
static gchar * current_dir_entered
Definition: build.c:69
static const gchar * get_build_command_entry_text(GtkWidget *wid)
Definition: build.c:2102
static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
Definition: build.c:1628
GtkAction * compile_action
Definition: build.c:145
static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
Definition: build.c:1792
static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
Definition: build.c:1133
GeanyBuildCommand ** ptr
Definition: build.c:2679
GeanyBuildCommand * build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
Definition: build.c:578
GtkWidget * toolitem_make_object
Definition: build.c:152
GtkWidget * toolitem_build
Definition: build.c:149
static struct @81 default_cmds[]
void build_finalize(void)
Definition: build.c:177
#define MENU_NEXT_ERROR
Definition: build.c:1319
void build_set_group_count(GeanyBuildGroup grp, gint count)
Definition: build.c:2636
struct BuildTableFields BuildTableFields
static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
Definition: build.c:2176
static gchar * id_to_str(GeanyBuildCommand *bc, gint id)
Definition: build.c:200
#define MENU_DONE
Definition: build.c:1322
void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
Set the string for the menu item field.
Definition: build.c:648
#define PRINTBUILDCMDS
Definition: build.c:262
static void show_build_commands_dialog(void)
Definition: build.c:2218
static void assign_cmd(GeanyBuildCommand *type, guint id, const gchar *label, gchar *value)
Definition: build.c:2339
static const gchar * groups[GEANY_GBG_COUNT]
Definition: build.c:2287
GtkWidget * build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data, GeanyFiletype *ft)
Definition: build.c:1946
static void add_menu_accel(GeanyKeyGroup *group, guint kb_id, GtkAccelGroup *accel_group, GtkWidget *menuitem)
Definition: build.c:188
#define return_cmd_if(src, cmds)
Definition: build.c:352
GeanyBuildType
Definition: build.c:89
@ GEANY_GBO_MAKE_OBJECT
Definition: build.c:94
@ GEANY_GBO_EXEC
Definition: build.c:95
@ GEANY_GBO_CUSTOM
Definition: build.c:93
@ GEANY_GBO_BUILD
Definition: build.c:91
@ GEANY_GBO_MAKE_ALL
Definition: build.c:92
@ GEANY_GBO_COUNT
Definition: build.c:96
@ GEANY_GBO_COMPILE
Definition: build.c:90
static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
Definition: build.c:1646
const gchar * command
Definition: build.c:2677
#define GRP_CMD_TO_POINTER(grp, cmd)
Definition: build.c:133
#define set_key_fld(key, fld)
Definition: build.c:2292
static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
Definition: build.c:1820
static GeanyBuildCommand * exec_pref
Definition: build.c:253
static GdkRGBA insensitive_color
Definition: build.c:1736
static const gchar * config_keys[GEANY_BC_CMDENTRIES_COUNT]
Definition: build.c:235
void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
Definition: build.c:2196
static void set_stop_button(gboolean stop)
Definition: build.c:1600
static struct @80 widgets
static void on_make_custom_input_response(const gchar *input, gpointer data)
Definition: build.c:1242
#define MENU_SEPARATOR
Definition: build.c:1317
GtkAction * run_action
Definition: build.c:144
static gchar * prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
Definition: build.c:835
static GeanyBuildCommand * non_ft_pref
Definition: build.c:250
struct RunInfo RunInfo
gchar ** build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
Definition: build.c:438
static guint build_items_count
Definition: build.c:158
static gchar * regex_pref
Definition: build.c:256
gint index
Definition: build.c:2680
void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
Remove the specified Build menu item.
Definition: build.c:546
#define DC_CLEAR
Definition: build.c:1843
static struct BuildMenuItemSpec build_menu_specs[]
#define DC_N_COL
Definition: build.c:1844
static gchar * regex_proj
Definition: build.c:258
#define MENU_NON_FT_REST
Definition: build.c:1314
static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
Definition: build.c:2111
GtkWidget * toolitem_make_custom
Definition: build.c:151
static GeanyBuildCommand * ft_def
Definition: build.c:248
static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
Definition: build.c:1681
const gchar * label
Definition: build.c:2676
#define GBO_TO_POINTER(gbo)
Definition: build.c:134
static void create_build_menu(BuildMenuItems *build_menu_items)
Definition: build.c:1392
#define return_nonblank_regex(src, ptr)
Definition: build.c:432
GtkWidget * toolmenu
Definition: build.c:147
static void on_project_close(void)
Definition: build.c:2667
GtkWidget * toolitem_make_all
Definition: build.c:150
gboolean build_keybinding(guint key_id)
Definition: build.c:2773
static guint build_groups_count[GEANY_GBG_COUNT]
Definition: build.c:157
#define set_key_grp(key, grp)
Definition: build.c:2290
static gboolean printbuildcmds
Definition: build.c:264
static GeanyBuildCommand * non_ft_proj
Definition: build.c:249
static void build_iofunc(GString *string, GIOCondition condition, gpointer data)
Definition: build.c:1041
Interface to the Build menu functionality.
GeanyBuildGroup
Groups of Build menu items.
Definition: build.h:35
@ GEANY_GBG_NON_FT
non filetype items.
Definition: build.h:37
@ GEANY_GBG_FT
filetype items
Definition: build.h:36
@ GEANY_GBG_EXEC
execute items
Definition: build.h:38
@ GEANY_GBG_COUNT
count of groups.
Definition: build.h:39
GeanyBuildCmdEntries
The entries of a command for a menu item.
Definition: build.h:56
@ GEANY_BC_COMMAND
The command to run.
Definition: build.h:58
@ GEANY_BC_LABEL
The menu item label, _ marks mnemonic.
Definition: build.h:57
@ GEANY_BC_WORKING_DIR
The directory to run in.
Definition: build.h:59
@ GEANY_BC_CMDENTRIES_COUNT
Count of entries.
Definition: build.h:60
GeanyBuildSource
Build menu item sources in increasing priority.
Definition: build.h:44
@ GEANY_BCS_FT
System filetype values.
Definition: build.h:46
@ GEANY_BCS_HOME_FT
Filetypes in ~/.config/geany/filedefs.
Definition: build.h:47
@ GEANY_BCS_DEF
Default values.
Definition: build.h:45
@ GEANY_BCS_PREF
Preferences file ~/.config/geany/geany.conf.
Definition: build.h:48
@ GEANY_BCS_COUNT
Count of sources.
Definition: build.h:51
@ GEANY_BCS_PROJ_FT
Project file filetype command.
Definition: build.h:49
@ GEANY_BCS_PROJ
Project file if open.
Definition: build.h:50
GtkWidget * dialogs_show_input_persistent(const gchar *title, GtkWindow *parent, const gchar *label_text, const gchar *default_text, GeanyInputCallback input_cb, gpointer input_cb_data)
Definition: dialogs.c:1043
gchar * dialogs_show_input(const gchar *title, GtkWindow *parent, const gchar *label_text, const gchar *default_text)
Asks the user for text input.
Definition: dialogs.c:1067
File related dialogs, miscellaneous dialogs, font dialog.
GeanyDocument * document_get_current(void)
Finds the current document.
Definition: document.c:371
gboolean document_save_file(GeanyDocument *doc, gboolean force)
Saves the document.
Definition: document.c:2103
GeanyDocument * document_find_by_filename(const gchar *utf8_filename)
Finds a document with the given filename.
Definition: document.c:183
GdkColor color
Definition: document.c:3220
Document related actions: new, save, open, etc.
#define DOC_VALID(doc_ptr)
Null-safe way to check GeanyDocument::is_valid.
Definition: document.h:162
#define documents
Wraps GeanyData::documents_array so it can be used with C array syntax.
Definition: document.h:130
#define foreach_document(i)
Iterates all valid document indexes.
Definition: document.h:153
void editor_indicator_clear_errors(GeanyEditor *editor)
Definition: editor.c:4133
gchar * text
Definition: editor.c:83
void editor_indicator_set_on_line(GeanyEditor *editor, gint indic, gint line)
Sets an indicator indic on line.
Definition: editor.c:4175
gint pos
Definition: editor.c:87
GeanyEditorPrefs editor_prefs
Definition: editor.c:77
@ GEANY_INDICATOR_ERROR
Indicator to highlight errors in the document text.
Definition: editor.h:68
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
static void clear(void)
Definition: filebrowser.c:302
@ GEANY_FILETYPES_NONE
Definition: filetypes.h:46
int errno
vString * line
Definition: geany_cobol.c:133
unsigned int count
void geany_menu_button_action_set_menu(GeanyMenubuttonAction *action, GtkWidget *menu)
#define GEANY_MENU_BUTTON_ACTION(obj)
GObject * geany_object
Definition: geanyobject.c:41
GeanyKeyGroup * keybindings_get_core_group(guint id)
Definition: keybindings.c:278
GeanyKeyBinding * keybindings_get_item(GeanyKeyGroup *group, gsize key_id)
Looks up a keybinding item.
Definition: keybindings.c:140
@ GEANY_KEY_GROUP_BUILD
Group.
Definition: keybindings.h:116
@ GEANY_KEYS_BUILD_COMPILE
Keybinding.
Definition: keybindings.h:136
@ GEANY_KEYS_BUILD_PREVIOUSERROR
Keybinding.
Definition: keybindings.h:191
@ GEANY_KEYS_BUILD_LINK
Keybinding.
Definition: keybindings.h:208
@ GEANY_KEYS_BUILD_MAKE
Keybinding.
Definition: keybindings.h:142
@ GEANY_KEYS_BUILD_RUN
Keybinding.
Definition: keybindings.h:159
@ GEANY_KEYS_BUILD_MAKEOWNTARGET
Keybinding.
Definition: keybindings.h:205
@ GEANY_KEYS_BUILD_NEXTERROR
Keybinding.
Definition: keybindings.h:231
@ GEANY_KEYS_BUILD_OPTIONS
Keybinding.
Definition: keybindings.h:214
@ GEANY_KEYS_BUILD_MAKEOBJECT
Keybinding.
Definition: keybindings.h:253
static GOptionEntry entries[]
Definition: libmain.c:118
GeanyApp * app
Definition: libmain.c:86
static gboolean ft_names
Definition: libmain.c:110
void geany_debug(gchar const *format,...)
Definition: log.c:67
void msgwin_parse_compiler_error_line(const gchar *string, const gchar *dir, gchar **filename, gint *line)
Definition: msgwindow.c:1090
void msgwin_show_hide(gboolean show)
Definition: msgwindow.c:372
void msgwin_compiler_add_string(gint msg_color, const gchar *msg)
Adds a new message in the compiler tab treeview in the messages window.
Definition: msgwindow.c:343
MessageWindow msgwindow
Definition: msgwindow.c:66
gboolean msgwin_goto_compiler_file_line(gboolean focus_editor)
Definition: msgwindow.c:796
void msgwin_compiler_add(gint msg_color, const gchar *format,...)
Adds a formatted message in the compiler tab treeview in the messages window.
Definition: msgwindow.c:320
Message window functions (status, compiler, messages windows).
@ MSG_COMPILER
Index of the compiler tab.
Definition: msgwindow.h:47
@ MSG_VTE
Index of the VTE tab.
Definition: msgwindow.h:50
@ COLOR_BLUE
Color blue.
Definition: msgwindow.h:39
@ COLOR_BLACK
Color black.
Definition: msgwindow.h:38
@ COLOR_DARK_RED
Color dark red.
Definition: msgwindow.h:37
@ COLOR_RED
Color red.
Definition: msgwindow.h:36
GeanyToolPrefs tool_prefs
Definition: prefs.c:67
void project_build_properties(void)
Definition: project.c:651
gchar * project_get_base_path(void)
Definition: project.c:1171
#define NULL
Definition: rbtree.h:150
char * strstr(const char *str, const char *substr)
Definition: routines.c:304
gint sci_get_current_line(ScintillaObject *sci)
Gets current line number.
Definition: sciwrappers.c:1190
Wrapper functions for the Scintilla editor widget SCI_* messages.
GtkWidget * close
Definition: sidebar.c:56
gboolean spawn_with_callbacks(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, SpawnFlags spawn_flags, GIOFunc stdin_cb, gpointer stdin_data, SpawnReadFunc stdout_cb, gpointer stdout_data, gsize stdout_max_length, SpawnReadFunc stderr_cb, gpointer stderr_data, gsize stderr_max_length, GChildWatchFunc exit_cb, gpointer exit_data, GPid *child_pid, GError **error)
<simplesect kind="geany:skip"></simplesect> Executes a child program and setups callbacks.
Definition: spawn.c:1158
gboolean spawn_async(const gchar *working_directory, const gchar *command_line, gchar **argv, gchar **envp, GPid *child_pid, GError **error)
Executes a child asynchronously.
Definition: spawn.c:814
gboolean spawn_kill_process(GPid pid, GError **error)
Kills a process.
Definition: spawn.c:289
Portable and convenient process spawning and communication.
#define SPAWN_WIFEXITED(status)
non-zero if the child exited normally
Definition: spawn.h:34
#define SPAWN_WEXITSTATUS(status)
exit status of a child if exited normally
Definition: spawn.h:35
void filetypes_save_commands(GeanyFiletype *ft)
Definition: filetypes.c:1164
GeanyFiletype * filetypes_lookup_by_name(const gchar *name)
Finds a filetype pointer from its name field.
Definition: filetypes.c:1242
StashGroup * group
Definition: stash-example.c:1
const gchar filename[]
Definition: stash-example.c:4
if(!stash_group_load_from_file(group, filename)) g_warning(_("Could not load keyfile %s!")
GtkWidget * dialog
gtk_container_add(GTK_CONTAINER(dialog->vbox), check_button)
gtk_widget_show_all(dialog)
const guint build_grp
Definition: build.c:1328
const gchar * stock_id
Definition: build.c:1326
const gint key_binding
Definition: build.c:1327
Callback * cb
Definition: build.c:1331
const gchar * fix_label
Definition: build.c:1330
const guint build_cmd
Definition: build.c:1329
RowWidgets ** rows
Definition: build.c:1938
gchar ** nonfileregexstring
Definition: build.c:1942
GtkWidget * fileregex
Definition: build.c:1939
GtkWidget * nonfileregex
Definition: build.c:1940
gchar ** fileregexstring
Definition: build.c:1941
struct GeanyProject * project
Currently active project or NULL if none is open.
Definition: app.h:49
Structure for representing an open tab with all its properties.
Definition: document.h:81
gchar * file_name
The UTF-8 encoded file name.
Definition: document.h:92
gboolean changed
Whether this document has been changed since it was last saved.
Definition: document.h:107
GeanyFiletype * file_type
The filetype for this document, it's only a reference to one of the elements of the global filetypes ...
Definition: document.h:101
gboolean is_valid
Flag used to check if this document is valid when iterating GeanyData::documents_array.
Definition: document.h:83
GeanyEditor * editor
The editor associated with the document.
Definition: document.h:98
gboolean use_indicators
Definition: editor.h:113
ScintillaObject * sci
The Scintilla editor GtkWidget.
Definition: editor.h:152
GeanyBuildCommand * ftdefcmds
GeanyBuildCommand * filecmds
GeanyBuildCommand * homeexeccmds
GeanyBuildCommand * homefilecmds
GeanyBuildCommand * execcmds
GeanyBuildCommand * projfilecmds
GeanyBuildCommand * projexeccmds
Represents a filetype.
Definition: filetypes.h:144
gchar * name
Untranslated short name, such as "C", "None".
Definition: filetypes.h:152
GeanyFiletypeID id
Index in filetypes.
Definition: filetypes.h:145
gchar * error_regex_string
Definition: filetypes.h:162
struct GeanyFiletypePrivate * priv
Definition: filetypes.h:171
Represents a single keybinding action.
Definition: keybindings.h:75
GdkModifierType mods
Modifier keys, such as GDK_CONTROL_MASK or 0.
Definition: keybindings.h:77
guint key
Key value in lower-case, such as GDK_KEY_a or 0.
Definition: keybindings.h:76
GtkWidget * window
Main window.
Definition: ui_utils.h:80
GPtrArray * build_filetypes_list
Structure for representing a project.
Definition: project.h:35
struct GeanyProjectPrivate * priv
Definition: project.h:45
gchar * term_cmd
terminal emulator command
Definition: prefs.h:52
gboolean used_dst
Definition: build.c:1732
GeanyBuildSource src
Definition: build.c:1726
GeanyBuildSource dst
Definition: build.c:1727
GtkWidget * entries[GEANY_BC_CMDENTRIES_COUNT]
Definition: build.c:1725
guint grp
Definition: build.c:1729
GeanyBuildCommand * cmdsrc
Definition: build.c:1728
gboolean cleared
Definition: build.c:1731
guint cmd
Definition: build.c:1730
Definition: build.c:72
GPid pid
Definition: build.c:73
gint file_type_id
Definition: build.c:74
Defines internationalization macros.
#define _(String)
Definition: support.h:42
#define N_(String)
Definition: support.h:43
GtkWidget * toolbar_get_widget_by_name(const gchar *name)
Definition: toolbar.c:124
GtkAction * toolbar_get_action_by_name(const gchar *name)
Definition: toolbar.c:152
Toolbar (prefs).
void ui_label_set_markup(GtkLabel *label, const gchar *format,...)
Definition: ui_utils.c:2894
void ui_progress_bar_start(const gchar *text)
Starts a constantly pulsing progressbar in the right corner of the statusbar (if the statusbar is vis...
Definition: ui_utils.c:2822
GtkWidget * ui_dialog_vbox_new(GtkDialog *dialog)
Makes a fixed border for dialogs without increasing the button box border.
Definition: ui_utils.c:1499
gboolean ui_tree_view_find_next(GtkTreeView *treeview, TVMatchCallback cb)
Definition: ui_utils.c:1812
gboolean ui_tree_view_find_previous(GtkTreeView *treeview, TVMatchCallback cb)
Definition: ui_utils.c:1819
GeanyMainWidgets main_widgets
Definition: ui_utils.c:72
void ui_set_statusbar(gboolean log, const gchar *format,...)
Displays text on the statusbar.
Definition: ui_utils.c:168
void ui_progress_bar_stop(void)
Stops a running progress bar and hides the widget again.
Definition: ui_utils.c:2842
UIPrefs ui_prefs
Definition: ui_utils.c:74
GtkWidget * ui_image_menu_item_new(const gchar *stock_id, const gchar *label)
Creates a GtkImageMenuItem with a stock image and a custom label.
Definition: ui_utils.c:1574
GtkWidget * ui_label_new_bold(const gchar *text)
Definition: ui_utils.c:2909
GtkWidget * ui_lookup_widget(GtkWidget *widget, const gchar *widget_name)
Returns a widget from a name in a component, usually created by Glade.
Definition: ui_utils.c:2743
User Interface general utility functions.
#define GEANY_STOCK_BUILD
Definition: ui_utils.h:98
void utils_open_browser(const gchar *uri)
Tries to open the given URI in a browser.
Definition: utils.c:75
guint utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
Definition: utils.c:1529
gchar * utils_get_utf8_from_locale(const gchar *locale_text)
Converts the given string (in locale encoding) into UTF-8 encoding.
Definition: utils.c:1272
void utils_beep(void)
Definition: utils.c:918
const gchar * utils_resource_dir(GeanyResourceDirType type)
Definition: utils.c:2301
void utils_str_replace_all(gchar **haystack, const gchar *needle, const gchar *replacement)
Definition: utils.c:692
const gchar * utils_get_uri_file_prefix(void)
Definition: utils.c:1689
gboolean utils_str_equal(const gchar *a, const gchar *b)
NULL-safe string comparison.
Definition: utils.c:599
gchar * utils_remove_ext_from_filename(const gchar *filename)
Removes the extension from filename and return the result in a newly allocated string.
Definition: utils.c:617
void utils_free_pointers(gsize arg_count,...)
Definition: utils.c:1293
gchar * utils_get_locale_from_utf8(const gchar *utf8_text)
Converts the given UTF-8 encoded string into locale encoding.
Definition: utils.c:1243
General utility functions, non-GTK related.
#define SETPTR(ptr, result)
Assigns result to ptr, then frees the old value.
Definition: utils.h:50
#define EMPTY(ptr)
Returns TRUE if ptr is NULL or *ptr is FALSE.
Definition: utils.h:38