1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
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 *---------------------------------------------------------------------------*/
44 #include <libanjuta/anjuta-ui.h>
46 /*---------------------------------------------------------------------------*/
48 #define STRING_CHUNK_SIZE 256
50 /*---------------------------------------------------------------------------*/
55 gchar
*command
; /* Command and parameters are in two variables */
66 GtkActionGroup
*action_group
;
68 GdkModifierType accel_mods
;
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")},
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")},
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) */
123 atp_remove_mnemonic (const gchar
* label
)
129 without
= g_new (char, strlen(label
) + 1);
131 for (src
= label
; *src
!= '\0'; ++src
)
135 /* Remove single underscore */
147 *---------------------------------------------------------------------------*/
150 *---------------------------------------------------------------------------*/
152 /* Remove tool from list but doesn't remove from hash table */
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
;
172 if (this->next
!= NULL
)
174 this->next
->prev
= this->prev
;
176 if (this->prev
!= NULL
)
178 this->prev
->next
= this->next
;
187 /* Add tool in list but doesn't add in hash table */
190 atp_user_tool_append_list (ATPUserTool
*this, ATPUserTool
*tool
)
192 g_return_val_if_fail (tool
, FALSE
);
194 /* Keep storage type ordered */
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;
206 /* If this is NULL insert at the beginning of the list */
209 tool
->next
= tool
->owner
->list
;
210 if (tool
->next
!= NULL
)
211 tool
->next
->prev
= tool
;
212 tool
->owner
->list
= tool
;
215 else if ((this->storage
== tool
->storage
) || (this->next
== NULL
) || (this->next
->storage
>= tool
->storage
))
217 /* Insert tool in list */
219 tool
->next
= this->next
;
223 tool
->next
->prev
= tool
;
226 else if (this->storage
< tool
->storage
)
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;)
237 clone
= atp_user_tool_new (this->owner
, prev
->name
, tool
->storage
);
238 atp_user_tool_append_list (tool
, clone
);
243 /* Unable to handle this case */
244 g_return_val_if_reached (FALSE
);
251 /* Remove from hash table and list */
254 atp_user_tool_remove (ATPUserTool
*this)
256 if (this->name
!= NULL
)
258 /* Remove from hash table */
261 first
= (ATPUserTool
*)g_hash_table_lookup (this->owner
->hash
, this->name
);
264 /* Unable to find tool */
265 g_return_val_if_reached (FALSE
);
269 if (this->over
== NULL
)
271 g_hash_table_remove (this->owner
->hash
, this->name
);
275 g_hash_table_replace (this->owner
->hash
, this->name
, this->over
);
280 for (; first
->over
!= this; first
= first
->over
)
284 /* Unable to find tool */
288 first
->over
= this->over
;
292 /* Remove from list */
293 return atp_user_tool_remove_list (this);
296 /* Replace tool name */
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 */
307 if (this->name
!= NULL
)
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
);
321 /* Remove tool from override list */
324 g_hash_table_replace (this->owner
->hash
, this->name
, this->over
);
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
;
342 this->name
= name
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, name
);
345 /* Add name in hash table */
346 g_hash_table_insert (this->owner
->hash
, this->name
, this);
352 /* Creation and Destruction
353 *---------------------------------------------------------------------------*/
355 /* Create a tool with corresponding name and storage
356 * but does NOT add it in the list */
359 atp_user_tool_new (ATPToolList
*list
, const gchar
*name
, ATPToolStore storage
)
364 g_return_val_if_fail (list
, NULL
);
368 /* Search tool in hash table */
369 first
= (ATPUserTool
*) g_hash_table_lookup (list
->hash
, name
);
372 /* Search tool in the list */
373 for(tool
= first
;; tool
= tool
->over
)
375 if (tool
->storage
== storage
)
377 /* Tool already exist */
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
);
388 tool
->flags
= ATP_TOOL_ENABLE
;
389 tool
->name
= first
->name
;
390 g_hash_table_replace (list
->hash
, tool
->name
, tool
);
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
;
400 tool
->menu_item
= NULL
;
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
);
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
;
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);
442 *---------------------------------------------------------------------------*/
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
);
456 atp_user_tool_get_name (const ATPUserTool
* this)
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
);
468 atp_user_tool_get_command (const ATPUserTool
* this)
470 return this->command
;
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
);
480 atp_user_tool_get_param (const ATPUserTool
* this)
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
);
492 atp_user_tool_get_working_dir (const ATPUserTool
* this)
494 return this->working_dir
;
498 atp_user_tool_set_flag (ATPUserTool
* this, ATPToolFlag flag
)
500 switch (flag
& ATP_OPERATION
)
506 this->flags
&= ~flag
;
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
);
523 atp_user_tool_get_flag (const ATPUserTool
* this, ATPToolFlag flag
)
525 return this->flags
& flag
? TRUE
: FALSE
;
529 atp_user_tool_set_output (ATPUserTool
*this, ATPOutputType output
)
531 this->output
= output
;
535 atp_user_tool_get_output (const ATPUserTool
*this)
541 atp_user_tool_set_error (ATPUserTool
*this, ATPOutputType error
)
547 atp_user_tool_get_error (const ATPUserTool
*this )
553 atp_user_tool_set_input (ATPUserTool
*this, ATPInputType type
, const gchar
* value
)
556 this->input_string
= value
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, value
);
560 atp_user_tool_get_input (const ATPUserTool
*this )
566 atp_user_tool_get_input_string (const ATPUserTool
*this )
568 return this->input_string
;
572 atp_user_tool_set_accelerator (ATPUserTool
*this, guint key
, GdkModifierType mods
)
574 this->accel_key
= key
;
575 this->accel_mods
= mods
;
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)
597 ATPToolStore
atp_user_tool_get_storage (const ATPUserTool
*this)
599 return this->storage
;
603 atp_user_tool_get_plugin (ATPUserTool
* this)
605 return this->owner
->plugin
;
609 atp_user_tool_is_valid (const ATPUserTool
* this)
611 return (this->command
!= NULL
);
614 /* Additional tool functions
615 *---------------------------------------------------------------------------*/
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;
630 atp_user_tool_next_in_same_storage (ATPUserTool
*this)
632 ATPToolStore storage
;
634 storage
= this->storage
;
635 while ((this = this->next
))
638 if (this->storage
!= storage
) return NULL
;
639 if (this->name
!= NULL
) break;
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;
658 atp_user_tool_override (const ATPUserTool
*this)
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
;
672 atp_user_tool_append_new (ATPUserTool
*this, const gchar
*name
, ATPToolStore storage
)
676 g_return_val_if_fail (this, NULL
);
679 tool
= atp_user_tool_new (this->owner
, name
, storage
);
682 atp_user_tool_append_list (this, tool
);
689 atp_user_tool_clone_new (ATPUserTool
*this, ATPToolStore storage
)
693 g_return_val_if_fail (this, NULL
);
696 tool
= atp_user_tool_new (this->owner
, this->name
, storage
);
701 prev
= atp_user_tool_previous (this);
702 atp_user_tool_append_list (prev
, tool
);
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);
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
);
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)
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
);
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
),
757 GTK_UI_MANAGER_MENUITEM
,
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
);
765 if ((this->menu_item
!= NULL
) && (this->icon
!= NULL
))
768 GdkPixbuf
*scaled_pixbuf
;
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
);
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);
791 /* Tool list object containing all tools
792 *---------------------------------------------------------------------------*/
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
);
800 this->hash
= g_hash_table_new (g_str_hash
, g_str_equal
);
801 this->string_pool
= g_string_chunk_new (STRING_CHUNK_SIZE
);
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
);
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 /*---------------------------------------------------------------------------*/
830 atp_tool_list_last (ATPToolList
*this)
836 for (tool
= this->list
; tool
!= NULL
; tool
= tool
->next
)
838 /* Skip tool not registered */
839 if (tool
->name
!= NULL
)
849 atp_tool_list_last_in_storage (const ATPToolList
*this, ATPToolStore storage
)
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
)
869 atp_tool_list_first (ATPToolList
*this)
873 for (tool
= this->list
; tool
!= NULL
; tool
= tool
->next
)
875 /* Skip unnamed and overridden tool*/
876 if ((tool
->name
!= NULL
) && (tool
->over
== NULL
))
886 atp_tool_list_first_in_storage (ATPToolList
*this, ATPToolStore storage
)
890 for (tool
= this->list
; tool
!= NULL
; tool
= tool
->next
)
892 /* Skip unamed tool */
893 if ((tool
->name
!= NULL
) && (tool
->storage
== storage
))
903 atp_tool_list_append_new (ATPToolList
*this, const gchar
*name
, ATPToolStore storage
)
907 g_return_val_if_fail (this, NULL
);
910 tool
= atp_user_tool_new (this, name
, storage
);
912 /* Add it in one of the storage list if necessary */
915 atp_user_tool_append_list (atp_tool_list_last_in_storage (this, storage
), tool
);
922 atp_tool_list_activate (ATPToolList
*this)
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
);
935 atp_tool_list_deactivate (ATPToolList
*this)
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
);