debug-manager: use g_spawn_sync() instead of fork() and waitpid()
[anjuta.git] / plugins / tools / tool.c
blobbfe3e25e9ccfdaba9596f9e8ffcb84ae9b414c55
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 tool.c
4 Copyright (C) 2005 Sebastien Granjoux
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * Keep all external tool data (ATPUserTool)
23 * All tools belong to a list (ATPToolList) which take care of allocating
24 * memory for them. This tool list is implemented with a linked list of all
25 * tools and a hash table to allow a fast access to a tool from its name.
27 * It is possible to read tools configuration from differents files (typically
28 * a file in a system directory and another one in an user directory) and to
29 * have the same tools (= same name) defined in both files. In this case
30 * both are in the list and each tool have a different storage number and are
31 * linked with a pointer named over.
33 * It is not possible to ordered tool with different storage order. But to
34 * emulate this function, it is possible to create new tools with the same
35 * data in the right storage location.
36 *---------------------------------------------------------------------------*/
38 #include <config.h>
40 #include "tool.h"
42 #include "execute.h"
44 #include <libanjuta/anjuta-ui.h>
46 /*---------------------------------------------------------------------------*/
48 #define STRING_CHUNK_SIZE 256
50 /*---------------------------------------------------------------------------*/
52 struct _ATPUserTool
54 gchar *name;
55 gchar *command; /* Command and parameters are in two variables */
56 gchar *param;
57 gchar *working_dir;
58 ATPToolFlag flags;
59 ATPOutputType output;
60 ATPOutputType error;
61 ATPInputType input;
62 gchar *input_string;
63 ATPToolStore storage;
64 GtkWidget* menu_item;
65 GtkAction *action;
66 GtkActionGroup *action_group;
67 guint accel_key;
68 GdkModifierType accel_mods;
69 gchar *icon;
70 guint merge_id;
71 ATPToolList *owner;
72 ATPUserTool *over; /* Same tool in another storage */
73 ATPUserTool *next; /* Next tool in the list */
74 ATPUserTool *prev; /* Previous tool in the list */
77 /* Tools helper functions (map enum to string)
78 *---------------------------------------------------------------------------*/
80 ATPEnumType output_type_list[] = {
81 {ATP_TOUT_SAME, N_("Same as output")},
82 {ATP_TOUT_COMMON_PANE, N_("Existing message pane")},
83 {ATP_TOUT_NEW_PANE, N_("New message pane")},
84 {ATP_TOUT_NEW_BUFFER, N_("New buffer")},
85 {ATP_TOUT_REPLACE_BUFFER, N_("Replace buffer")},
86 {ATP_TOUT_INSERT_BUFFER, N_("Insert into buffer")},
87 {ATP_TOUT_APPEND_BUFFER, N_("Append to buffer")},
88 {ATP_TOUT_REPLACE_SELECTION, N_("Replace selection")},
89 /* Translators: Checkbox if a dialog should be shown after some operation finishes, so translate as "to pop up a dialog" */
90 {ATP_TOUT_POPUP_DIALOG, N_("Popup dialog")},
91 {ATP_TOUT_NULL, N_("Discard output")},
92 {-1, NULL}
95 ATPEnumType input_type_list[] = {
96 {ATP_TIN_NONE, N_("None")},
97 {ATP_TIN_BUFFER, N_("Current buffer")},
98 {ATP_TIN_SELECTION, N_("Current selection")},
99 {ATP_TIN_STRING, N_("String")},
100 {ATP_TIN_FILE, N_("File")},
101 {-1, NULL}
104 /*---------------------------------------------------------------------------*/
106 const ATPEnumType* atp_get_output_type_list (void)
108 return &output_type_list[1];
111 const ATPEnumType* atp_get_error_type_list (void)
113 return &output_type_list[0];
116 const ATPEnumType* atp_get_input_type_list (void)
118 return &input_type_list[0];
121 /* Return a copy of the name without mnemonic (underscore) */
122 gchar*
123 atp_remove_mnemonic (const gchar* label)
125 const char *src;
126 char *dst;
127 char *without;
129 without = g_new (char, strlen(label) + 1);
130 dst = without;
131 for (src = label; *src != '\0'; ++src)
133 if (*src == '_')
135 /* Remove single underscore */
136 ++src;
138 *dst++ = *src;
140 *dst = *src;
142 return without;
145 /* Tool object
147 *---------------------------------------------------------------------------*/
149 /* Private functions
150 *---------------------------------------------------------------------------*/
152 /* Remove tool from list but doesn't remove from hash table */
154 static gboolean
155 atp_user_tool_remove_list (ATPUserTool* this)
157 g_return_val_if_fail (this, FALSE);
158 g_return_val_if_fail (this->owner, FALSE);
160 /* Remove from storage list */
161 if (this->owner->list == this)
163 /* First tool in the list */
164 this->owner->list = this->next;
165 if (this->next != NULL)
167 this->next->prev = NULL;
170 else
172 if (this->next != NULL)
174 this->next->prev = this->prev;
176 if (this->prev != NULL)
178 this->prev->next = this->next;
181 this->next = NULL;
182 this->prev = NULL;
184 return TRUE;
187 /* Add tool in list but doesn't add in hash table */
189 static gboolean
190 atp_user_tool_append_list (ATPUserTool *this, ATPUserTool *tool)
192 g_return_val_if_fail (tool, FALSE);
194 /* Keep storage type ordered */
195 if (this == NULL)
197 ATPUserTool* first;
198 /* Insert at the beginning of list (with right storage) */
199 for (first = tool->owner->list; first != NULL; first = first->next)
201 if (first->storage >= tool->storage) break;
202 this = first;
206 /* If this is NULL insert at the beginning of the list */
207 if (this == NULL)
209 tool->next = tool->owner->list;
210 if (tool->next != NULL)
211 tool->next->prev = tool;
212 tool->owner->list = tool;
213 tool->prev = NULL;
215 else if ((this->storage == tool->storage) || (this->next == NULL) || (this->next->storage >= tool->storage))
217 /* Insert tool in list */
218 tool->prev = this;
219 tool->next = this->next;
220 this->next = tool;
221 if (tool->next)
223 tool->next->prev = tool;
226 else if (this->storage < tool->storage)
228 ATPUserTool *prev;
229 /* Insert tool at the beginning of storage list */
230 atp_user_tool_append_list (NULL, tool);
232 /* Create new tool with the right storage and reorder them */
233 for (prev = tool;(prev = atp_user_tool_previous (prev)) != this;)
235 ATPUserTool *clone;
237 clone = atp_user_tool_new (this->owner, prev->name, tool->storage);
238 atp_user_tool_append_list (tool, clone);
241 else
243 /* Unable to handle this case */
244 g_return_val_if_reached (FALSE);
245 return FALSE;
248 return TRUE;
251 /* Remove from hash table and list */
253 static gboolean
254 atp_user_tool_remove (ATPUserTool *this)
256 if (this->name != NULL)
258 /* Remove from hash table */
259 ATPUserTool *first;
261 first = (ATPUserTool *)g_hash_table_lookup (this->owner->hash, this->name);
262 if (first == NULL)
264 /* Unable to find tool */
265 g_return_val_if_reached (FALSE);
267 if (first == this)
269 if (this->over == NULL)
271 g_hash_table_remove (this->owner->hash, this->name);
273 else
275 g_hash_table_replace (this->owner->hash, this->name, this->over);
278 else
280 for (; first->over != this; first = first->over)
282 if (first == NULL)
284 /* Unable to find tool */
285 return FALSE;
288 first->over = this->over;
292 /* Remove from list */
293 return atp_user_tool_remove_list (this);
296 /* Replace tool name */
298 static gboolean
299 atp_user_tool_replace_name (ATPUserTool *this, const gchar *name)
301 if ((name != NULL) && (g_hash_table_lookup (this->owner->hash, name) != NULL))
303 /* Name already exist */
304 return FALSE;
307 if (this->name != NULL)
309 ATPUserTool *first;
311 first = (ATPUserTool *) g_hash_table_lookup (this->owner->hash, this->name);
313 if (first->over == NULL)
315 g_return_val_if_fail (first == this, FALSE);
317 g_hash_table_remove (this->owner->hash, this->name);
319 else
321 /* Remove tool from override list */
322 if (first == this)
324 g_hash_table_replace (this->owner->hash, this->name, this->over);
325 this->over = NULL;
327 else
329 ATPUserTool *tool;
331 for (tool = first; tool->over != this; tool = tool->over)
333 /* Tool not in list */
334 g_return_val_if_fail (tool->over != NULL, FALSE);
336 tool->over = this->over;
341 /* Add in list */
342 this->name = name == NULL ? NULL : g_string_chunk_insert_const (this->owner->string_pool, name);
343 if (name != NULL)
345 /* Add name in hash table */
346 g_hash_table_insert (this->owner->hash, this->name, this);
349 return TRUE;
352 /* Creation and Destruction
353 *---------------------------------------------------------------------------*/
355 /* Create a tool with corresponding name and storage
356 * but does NOT add it in the list */
358 ATPUserTool *
359 atp_user_tool_new (ATPToolList *list, const gchar *name, ATPToolStore storage)
361 ATPUserTool *first;
362 ATPUserTool *tool;
364 g_return_val_if_fail (list, NULL);
366 if (name)
368 /* Search tool in hash table */
369 first = (ATPUserTool *) g_hash_table_lookup (list->hash, name);
370 if (first != NULL)
372 /* Search tool in the list */
373 for(tool = first;; tool = tool->over)
375 if (tool->storage == storage)
377 /* Tool already exist */
379 return NULL;
381 else if (tool->storage > storage)
383 /* Add tool before */
384 g_return_val_if_fail (tool == first, NULL);
386 tool = g_slice_new0(ATPUserTool);
387 tool->over = first;
388 tool->flags = ATP_TOOL_ENABLE;
389 tool->name = first->name;
390 g_hash_table_replace (list->hash, tool->name, tool);
391 break;
393 else if ((tool->over == NULL) || (tool->over->storage > storage))
395 /* Add tool after, using previous values as default */
396 first = g_slice_new(ATPUserTool);
397 memcpy(first, tool, sizeof (ATPUserTool));
398 first->over = tool->over;
399 tool->over = first;
400 tool->menu_item = NULL;
401 tool = tool->over;
402 break;
406 else
408 /* Create new tool */
409 tool = g_slice_new0(ATPUserTool);
410 tool->flags = ATP_TOOL_ENABLE;
411 tool->name = g_string_chunk_insert_const (list->string_pool, name);
412 g_hash_table_insert (list->hash, tool->name, tool);
415 else
417 /* Create stand alone tool */
418 tool = g_slice_new0(ATPUserTool);
419 tool->flags = ATP_TOOL_ENABLE;
422 /* Set default values */
423 tool->storage = storage;
424 tool->owner = list;
426 return tool;
429 void
430 atp_user_tool_free (ATPUserTool *this)
432 g_return_if_fail (this->owner);
434 atp_user_tool_remove (this);
435 atp_user_tool_deactivate (this, this->owner->ui);
437 g_slice_free (ATPUserTool, this);
441 /* Access tool data
442 *---------------------------------------------------------------------------*/
444 gboolean
445 atp_user_tool_set_name (ATPUserTool *this, const gchar *value)
447 if ((value != this->name) && ((value == NULL) || (this->name == NULL) || (strcmp (value, this->name) != 0)))
449 return atp_user_tool_replace_name (this, value);
452 return TRUE;
455 const gchar*
456 atp_user_tool_get_name (const ATPUserTool* this)
458 return this->name;
461 void
462 atp_user_tool_set_command (ATPUserTool* this, const gchar* value)
464 this->command = value == NULL ? NULL : g_string_chunk_insert_const (this->owner->string_pool, value);
467 const gchar*
468 atp_user_tool_get_command (const ATPUserTool* this)
470 return this->command;
473 void
474 atp_user_tool_set_param (ATPUserTool* this, const gchar* value)
476 this->param = value == NULL ? NULL : g_string_chunk_insert_const (this->owner->string_pool, value);
479 const gchar*
480 atp_user_tool_get_param (const ATPUserTool* this)
482 return this->param;
485 void
486 atp_user_tool_set_working_dir (ATPUserTool* this, const gchar* value)
488 this->working_dir = value == NULL ? NULL : g_string_chunk_insert_const (this->owner->string_pool, value);
491 const gchar*
492 atp_user_tool_get_working_dir (const ATPUserTool* this)
494 return this->working_dir;
497 void
498 atp_user_tool_set_flag (ATPUserTool* this, ATPToolFlag flag)
500 switch (flag & ATP_OPERATION)
502 case ATP_SET:
503 this->flags |= flag;
504 break;
505 case ATP_CLEAR:
506 this->flags &= ~flag;
507 break;
508 case ATP_TOGGLE:
509 this->flags ^= flag;
510 break;
511 default:
512 g_return_if_reached();
515 if ((flag & ATP_TOOL_ENABLE) && (this->menu_item != NULL))
517 /* Enable or disable menu item */
518 gtk_widget_set_sensitive (this->menu_item, this->flags & ATP_TOOL_ENABLE);
522 gboolean
523 atp_user_tool_get_flag (const ATPUserTool* this, ATPToolFlag flag)
525 return this->flags & flag ? TRUE : FALSE;
528 void
529 atp_user_tool_set_output (ATPUserTool *this, ATPOutputType output)
531 this->output = output;
534 ATPOutputType
535 atp_user_tool_get_output (const ATPUserTool *this)
537 return this->output;
540 void
541 atp_user_tool_set_error (ATPUserTool *this, ATPOutputType error)
543 this->error = error;
546 ATPOutputType
547 atp_user_tool_get_error (const ATPUserTool *this )
549 return this->error;
552 void
553 atp_user_tool_set_input (ATPUserTool *this, ATPInputType type, const gchar* value)
555 this->input = type;
556 this->input_string = value == NULL ? NULL : g_string_chunk_insert_const (this->owner->string_pool, value);
559 ATPInputType
560 atp_user_tool_get_input (const ATPUserTool *this )
562 return this->input;
565 const gchar*
566 atp_user_tool_get_input_string (const ATPUserTool *this )
568 return this->input_string;
571 void
572 atp_user_tool_set_accelerator (ATPUserTool *this, guint key, GdkModifierType mods)
574 this->accel_key = key;
575 this->accel_mods = mods;
578 gboolean
579 atp_user_tool_get_accelerator (const ATPUserTool *this, guint *key, GdkModifierType *mods)
581 *key = this->accel_key;
582 *mods = this->accel_mods;
584 return this->accel_key != 0;
587 void atp_user_tool_set_icon (ATPUserTool *this, const gchar* value)
589 this->icon = value == NULL ? NULL : g_string_chunk_insert_const (this->owner->string_pool, value);
592 const gchar* atp_user_tool_get_icon (const ATPUserTool *this)
594 return this->icon;
597 ATPToolStore atp_user_tool_get_storage (const ATPUserTool *this)
599 return this->storage;
602 ATPPlugin*
603 atp_user_tool_get_plugin (ATPUserTool* this)
605 return this->owner->plugin;
608 gboolean
609 atp_user_tool_is_valid (const ATPUserTool* this)
611 return (this->command != NULL);
614 /* Additional tool functions
615 *---------------------------------------------------------------------------*/
617 ATPUserTool*
618 atp_user_tool_next (ATPUserTool *this)
620 while ((this = this->next))
622 /* Skip unnamed and overridden tool */
623 if ((this->name != NULL) && (this->over == NULL)) break;
626 return this;
629 ATPUserTool*
630 atp_user_tool_next_in_same_storage (ATPUserTool *this)
632 ATPToolStore storage;
634 storage = this->storage;
635 while ((this = this->next))
637 /* Skip unnamed */
638 if (this->storage != storage) return NULL;
639 if (this->name != NULL) break;
642 return this;
645 ATPUserTool*
646 atp_user_tool_previous (ATPUserTool *this)
648 while ((this = this->prev))
650 /* Skip unnamed and overridden tool */
651 if ((this->name != NULL) && (this->over == NULL)) break;
654 return this;
657 ATPUserTool*
658 atp_user_tool_override (const ATPUserTool *this)
660 ATPUserTool* tool;
662 for (tool = g_hash_table_lookup (this->owner->hash, this->name);
663 tool != NULL; tool = tool->over)
665 if (tool->over == this) return tool;
668 return NULL;
671 ATPUserTool*
672 atp_user_tool_append_new (ATPUserTool *this, const gchar *name, ATPToolStore storage)
674 ATPUserTool *tool;
676 g_return_val_if_fail (this, NULL);
678 /* Create tool */
679 tool = atp_user_tool_new (this->owner, name, storage);
680 if (tool)
682 atp_user_tool_append_list (this, tool);
685 return tool;
688 ATPUserTool*
689 atp_user_tool_clone_new (ATPUserTool *this, ATPToolStore storage)
691 ATPUserTool *tool;
693 g_return_val_if_fail (this, NULL);
695 /* Create tool */
696 tool = atp_user_tool_new (this->owner, this->name, storage);
697 if (tool)
699 ATPUserTool *prev;
701 prev = atp_user_tool_previous (this);
702 atp_user_tool_append_list (prev, tool);
705 return tool;
708 gboolean
709 atp_user_tool_move_after (ATPUserTool *this, ATPUserTool *position)
711 g_return_val_if_fail (this, FALSE);
713 if (!atp_user_tool_remove_list (this)) return FALSE;
714 return atp_user_tool_append_list (position, this);
717 void
718 atp_user_tool_deactivate (ATPUserTool* this, AnjutaUI *ui)
720 if (this->merge_id != 0)
722 gtk_ui_manager_remove_ui (GTK_UI_MANAGER (ui), this->merge_id);
723 gtk_action_group_remove_action (this->action_group, this->action);
727 gboolean
728 atp_user_tool_activate (ATPUserTool *this, GtkActionGroup *action_group, AnjutaUI *ui)
730 gchar *menuitem_path;
732 /* Remove previous menu */
733 atp_user_tool_deactivate (this, ui);
735 /* Create new menu item */
736 this->action = gtk_action_new (this->name, this->name, this->name, NULL);
737 this->action_group = action_group;
739 if (this->accel_key != 0)
741 gchar *accelerator;
743 accelerator = gtk_accelerator_name (this->accel_key, this->accel_mods);
744 gtk_action_group_add_action_with_accel (this->action_group, this->action, accelerator);
746 else
748 gtk_action_group_add_action (this->action_group, this->action);
751 this->merge_id = gtk_ui_manager_new_merge_id (GTK_UI_MANAGER (ui));
752 gtk_ui_manager_add_ui (GTK_UI_MANAGER (ui),
753 this->merge_id,
754 MENU_PLACEHOLDER,
755 this->name,
756 this->name,
757 GTK_UI_MANAGER_MENUITEM,
758 FALSE);
760 menuitem_path = g_strconcat (MENU_PLACEHOLDER, "/", this->name, NULL);
761 this->menu_item = gtk_ui_manager_get_widget (GTK_UI_MANAGER (ui), menuitem_path);
762 gtk_action_set_sensitive (this->action, this->flags & ATP_TOOL_ENABLE);
764 /* Add icon */
765 if ((this->menu_item != NULL) && (this->icon != NULL))
767 GdkPixbuf *pixbuf;
768 GdkPixbuf *scaled_pixbuf;
769 gint height, width;
771 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (this->menu_item), GTK_ICON_SIZE_MENU, &width, &height);
773 pixbuf = gdk_pixbuf_new_from_file (this->icon, NULL);
774 if (pixbuf)
776 GtkWidget* icon;
778 scaled_pixbuf = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
779 icon = gtk_image_new_from_pixbuf (scaled_pixbuf);
780 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (this->menu_item), icon);
781 g_object_unref (pixbuf);
782 g_object_unref (scaled_pixbuf);
786 g_signal_connect (G_OBJECT (this->action), "activate", G_CALLBACK (atp_user_tool_execute), this);
788 return TRUE;
791 /* Tool list object containing all tools
792 *---------------------------------------------------------------------------*/
794 ATPToolList *
795 atp_tool_list_construct (ATPToolList* this, ATPPlugin* plugin)
797 this->plugin = plugin;
798 this->ui = anjuta_shell_get_ui (ANJUTA_PLUGIN(plugin)->shell, NULL);
799 this->list = NULL;
800 this->hash = g_hash_table_new (g_str_hash, g_str_equal);
801 this->string_pool = g_string_chunk_new (STRING_CHUNK_SIZE);
803 return this;
806 static void
807 on_remove_tool (gpointer key, gpointer value, gpointer user_data)
809 ATPUserTool* tool = value;
813 ATPUserTool* next = tool->over;
815 g_slice_free (ATPUserTool, tool);
816 tool = next;
817 } while (tool != NULL);
820 void atp_tool_list_destroy (ATPToolList* this)
822 g_hash_table_foreach(this->hash, (GHFunc)on_remove_tool, NULL);
823 g_hash_table_destroy (this->hash);
824 g_string_chunk_free (this->string_pool);
827 /*---------------------------------------------------------------------------*/
829 ATPUserTool *
830 atp_tool_list_last (ATPToolList *this)
832 ATPUserTool *tool;
833 ATPUserTool *last;
835 last = NULL;
836 for (tool = this->list; tool != NULL; tool = tool->next)
838 /* Skip tool not registered */
839 if (tool->name != NULL)
841 last = tool;
845 return last;
848 static ATPUserTool *
849 atp_tool_list_last_in_storage (const ATPToolList *this, ATPToolStore storage)
851 ATPUserTool *tool;
852 ATPUserTool *last;
854 last = NULL;
855 for (tool = this->list; tool != NULL; tool = tool->next)
857 if (tool->storage > storage) break;
858 /* Skip tool not registered */
859 if (tool->name != NULL)
861 last = tool;
865 return last;
868 ATPUserTool*
869 atp_tool_list_first (ATPToolList *this)
871 ATPUserTool *tool;
873 for (tool = this->list; tool != NULL; tool = tool->next)
875 /* Skip unnamed and overridden tool*/
876 if ((tool->name != NULL) && (tool->over == NULL))
878 return tool;
882 return NULL;
885 ATPUserTool*
886 atp_tool_list_first_in_storage (ATPToolList *this, ATPToolStore storage)
888 ATPUserTool *tool;
890 for (tool = this->list; tool != NULL; tool = tool->next)
892 /* Skip unamed tool */
893 if ((tool->name != NULL) && (tool->storage == storage))
895 return tool;
899 return NULL;
902 ATPUserTool *
903 atp_tool_list_append_new (ATPToolList *this, const gchar *name, ATPToolStore storage)
905 ATPUserTool *tool;
907 g_return_val_if_fail (this, NULL);
909 /* Create tool */
910 tool = atp_user_tool_new (this, name, storage);
912 /* Add it in one of the storage list if necessary */
913 if (tool)
915 atp_user_tool_append_list (atp_tool_list_last_in_storage (this, storage), tool);
918 return tool;
921 gboolean
922 atp_tool_list_activate (ATPToolList *this)
924 ATPUserTool *next;
926 for (next = atp_tool_list_first (this); next != NULL; next = atp_user_tool_next (next))
928 atp_user_tool_activate (next, atp_plugin_get_action_group (this->plugin), this->ui);
931 return TRUE;
934 gboolean
935 atp_tool_list_deactivate (ATPToolList *this)
937 ATPUserTool *next;
939 for (next = atp_tool_list_first (this); next != NULL; next = atp_user_tool_next (next))
941 atp_user_tool_deactivate (next, next->owner->ui);
944 return TRUE;