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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 GdkModifierType accel_mods
;
69 ATPUserTool
*over
; /* Same tool in another storage */
70 ATPUserTool
*next
; /* Next tool in the list */
71 ATPUserTool
*prev
; /* Previous tool in the list */
74 /* Tools helper functions (map enum to string)
75 *---------------------------------------------------------------------------*/
77 ATPEnumType output_type_list
[] = {
78 {ATP_TOUT_SAME
, "Same than output"},
79 {ATP_TOUT_COMMON_PANE
, "Existing message pane"},
80 {ATP_TOUT_NEW_PANE
, "New message pane"},
81 {ATP_TOUT_NEW_BUFFER
, "New buffer"},
82 {ATP_TOUT_REPLACE_BUFFER
, "Replace buffer"},
83 {ATP_TOUT_INSERT_BUFFER
, "Insert in buffer"},
84 {ATP_TOUT_APPEND_BUFFER
, "Append to buffer"},
85 {ATP_TOUT_REPLACE_SELECTION
, "Replace selection"},
86 {ATP_TOUT_POPUP_DIALOG
, "Popup dialog"},
87 {ATP_TOUT_NULL
, "Discard output"},
91 ATPEnumType input_type_list
[] = {
92 {ATP_TIN_NONE
, "None"},
93 {ATP_TIN_BUFFER
, "Current buffer"},
94 {ATP_TIN_SELECTION
, "Current selection"},
95 {ATP_TIN_STRING
, "String"},
96 {ATP_TIN_FILE
, "File"},
100 /*---------------------------------------------------------------------------*/
102 const ATPEnumType
* atp_get_output_type_list (void)
104 return &output_type_list
[1];
107 const ATPEnumType
* atp_get_error_type_list (void)
109 return &output_type_list
[0];
112 const ATPEnumType
* atp_get_input_type_list (void)
114 return &input_type_list
[0];
117 /* Return a copy of the name without mnemonic (underscore) */
119 atp_remove_mnemonic (const gchar
* label
)
125 without
= g_new (char, strlen(label
) + 1);
127 for (src
= label
; *src
!= '\0'; ++src
)
131 /* Remove single underscore */
143 *---------------------------------------------------------------------------*/
146 *---------------------------------------------------------------------------*/
148 /* Remove tool from list but doesn't remove from hash table */
151 atp_user_tool_remove_list (ATPUserTool
* this)
153 g_return_val_if_fail (this, FALSE
);
154 g_return_val_if_fail (this->owner
, FALSE
);
156 /* Remove from storage list */
157 if (this->owner
->list
== this)
159 /* First tool in the list */
160 this->owner
->list
= this->next
;
161 if (this->next
!= NULL
)
163 this->next
->prev
= NULL
;
168 if (this->next
!= NULL
)
170 this->next
->prev
= this->prev
;
172 if (this->prev
!= NULL
)
174 this->prev
->next
= this->next
;
183 /* Add tool in list but doesn't add in hash table */
186 atp_user_tool_append_list (ATPUserTool
*this, ATPUserTool
*tool
)
188 g_return_val_if_fail (tool
, FALSE
);
190 /* Keep storage type ordered */
194 /* Insert at the beginning of list (with right storage) */
195 for (first
= tool
->owner
->list
; first
!= NULL
; first
= first
->next
)
197 if (first
->storage
>= tool
->storage
) break;
202 /* If this is NULL insert at the beginning of the list */
205 tool
->next
= tool
->owner
->list
;
206 if (tool
->next
!= NULL
)
207 tool
->next
->prev
= tool
;
208 tool
->owner
->list
= tool
;
211 else if ((this->storage
== tool
->storage
) || (this->next
== NULL
) || (this->next
->storage
>= tool
->storage
))
213 /* Insert tool in list */
215 tool
->next
= this->next
;
219 tool
->next
->prev
= tool
;
222 else if (this->storage
< tool
->storage
)
225 /* Insert tool at the beginning of storage list */
226 atp_user_tool_append_list (NULL
, tool
);
228 /* Create new tool with the right storage and reorder them */
229 for (prev
= tool
;(prev
= atp_user_tool_previous (prev
)) != this;)
233 clone
= atp_user_tool_new (this->owner
, prev
->name
, tool
->storage
);
234 atp_user_tool_append_list (tool
, clone
);
239 /* Unable to handle this case */
240 g_return_val_if_reached (FALSE
);
247 /* Remove from hash table and list */
250 atp_user_tool_remove (ATPUserTool
*this)
252 if (this->name
!= NULL
)
254 /* Remove from hash table */
257 first
= (ATPUserTool
*)g_hash_table_lookup (this->owner
->hash
, this->name
);
260 /* Unable to find tool */
261 g_return_val_if_reached (FALSE
);
265 if (this->over
== NULL
)
267 g_hash_table_remove (this->owner
->hash
, this->name
);
271 g_hash_table_replace (this->owner
->hash
, this->name
, this->over
);
276 for (; first
->over
!= this; first
= first
->over
)
280 /* Unable to find tool */
284 first
->over
= this->over
;
288 /* Remove from list */
289 return atp_user_tool_remove_list (this);
292 /* Replace tool name */
295 atp_user_tool_replace_name (ATPUserTool
*this, const gchar
*name
)
297 if ((name
!= NULL
) && (g_hash_table_lookup (this->owner
->hash
, name
) != NULL
))
299 /* Name already exist */
303 if (this->name
!= NULL
)
307 first
= (ATPUserTool
*) g_hash_table_lookup (this->owner
->hash
, this->name
);
309 if (first
->over
== NULL
)
311 g_return_val_if_fail (first
== this, FALSE
);
313 g_hash_table_remove (this->owner
->hash
, this->name
);
317 /* Remove tool from override list */
320 g_hash_table_replace (this->owner
->hash
, this->name
, this->over
);
327 for (tool
= first
; tool
->over
!= this; tool
= tool
->over
)
329 /* Tool not in list */
330 g_return_val_if_fail (tool
->over
!= NULL
, FALSE
);
332 tool
->over
= this->over
;
338 this->name
= name
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, name
);
341 /* Add name in hash table */
342 g_hash_table_insert (this->owner
->hash
, this->name
, this);
348 /* Creation and Destruction
349 *---------------------------------------------------------------------------*/
351 /* Create a tool with corresponding name and storage
352 * but does NOT add it in the list */
355 atp_user_tool_new (ATPToolList
*list
, const gchar
*name
, ATPToolStore storage
)
360 g_return_val_if_fail (list
, NULL
);
364 /* Search tool in hash table */
365 first
= (ATPUserTool
*) g_hash_table_lookup (list
->hash
, name
);
368 /* Search tool in the list */
369 for(tool
= first
;; tool
= tool
->over
)
371 if (tool
->storage
== storage
)
373 /* Tool already exist */
377 else if (tool
->storage
> storage
)
379 /* Add tool before */
380 g_return_val_if_fail (tool
== first
, NULL
);
382 tool
= g_chunk_new0(ATPUserTool
, list
->data_pool
);
384 tool
->flags
= ATP_TOOL_ENABLE
;
385 tool
->name
= first
->name
;
386 g_hash_table_replace (list
->hash
, tool
->name
, tool
);
389 else if ((tool
->over
== NULL
) || (tool
->over
->storage
> storage
))
391 /* Add tool after, using previous values as default */
392 first
= g_chunk_new(ATPUserTool
, list
->data_pool
);
393 memcpy(first
, tool
, sizeof (ATPUserTool
));
394 first
->over
= tool
->over
;
396 tool
->menu_item
= NULL
;
404 /* Create new tool */
405 tool
= g_chunk_new0(ATPUserTool
, list
->data_pool
);
406 tool
->flags
= ATP_TOOL_ENABLE
;
407 tool
->name
= g_string_chunk_insert_const (list
->string_pool
, name
);
408 g_hash_table_insert (list
->hash
, tool
->name
, tool
);
413 /* Create stand alone tool */
414 tool
= g_chunk_new0(ATPUserTool
, list
->data_pool
);
415 tool
->flags
= ATP_TOOL_ENABLE
;
418 /* Set default values */
419 tool
->storage
= storage
;
426 atp_user_tool_free (ATPUserTool
*this)
428 g_return_if_fail (this->owner
);
430 atp_user_tool_remove (this);
431 atp_user_tool_deactivate (this);
433 g_chunk_free (this, this->owner
->data_pool
);
438 *---------------------------------------------------------------------------*/
441 atp_user_tool_set_name (ATPUserTool
*this, const gchar
*value
)
443 if ((value
!= this->name
) && ((value
== NULL
) || (this->name
== NULL
) || (strcmp (value
, this->name
) != 0)))
445 return atp_user_tool_replace_name (this, value
);
452 atp_user_tool_get_name (const ATPUserTool
* this)
458 atp_user_tool_set_command (ATPUserTool
* this, const gchar
* value
)
460 this->command
= value
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, value
);
464 atp_user_tool_get_command (const ATPUserTool
* this)
466 return this->command
;
470 atp_user_tool_set_param (ATPUserTool
* this, const gchar
* value
)
472 this->param
= value
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, value
);
476 atp_user_tool_get_param (const ATPUserTool
* this)
482 atp_user_tool_set_working_dir (ATPUserTool
* this, const gchar
* value
)
484 this->working_dir
= value
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, value
);
488 atp_user_tool_get_working_dir (const ATPUserTool
* this)
490 return this->working_dir
;
494 atp_user_tool_set_flag (ATPUserTool
* this, ATPToolFlag flag
)
496 switch (flag
& ATP_OPERATION
)
502 this->flags
&= ~flag
;
508 g_return_if_reached();
511 if ((flag
& ATP_TOOL_ENABLE
) && (this->menu_item
!= NULL
))
513 /* Enable or disable menu item */
514 gtk_widget_set_sensitive (this->menu_item
, this->flags
& ATP_TOOL_ENABLE
);
519 atp_user_tool_get_flag (const ATPUserTool
* this, ATPToolFlag flag
)
521 return this->flags
& flag
? TRUE
: FALSE
;
525 atp_user_tool_set_output (ATPUserTool
*this, ATPOutputType output
)
527 this->output
= output
;
531 atp_user_tool_get_output (const ATPUserTool
*this)
537 atp_user_tool_set_error (ATPUserTool
*this, ATPOutputType error
)
543 atp_user_tool_get_error (const ATPUserTool
*this )
549 atp_user_tool_set_input (ATPUserTool
*this, ATPInputType type
, const gchar
* value
)
552 this->input_string
= value
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, value
);
556 atp_user_tool_get_input (const ATPUserTool
*this )
562 atp_user_tool_get_input_string (const ATPUserTool
*this )
564 return this->input_string
;
568 atp_user_tool_set_accelerator (ATPUserTool
*this, guint key
, GdkModifierType mods
)
570 this->accel_key
= key
;
571 this->accel_mods
= mods
;
575 atp_user_tool_get_accelerator (const ATPUserTool
*this, guint
*key
, GdkModifierType
*mods
)
577 *key
= this->accel_key
;
578 *mods
= this->accel_mods
;
580 return this->accel_key
!= 0;
583 void atp_user_tool_set_icon (ATPUserTool
*this, const gchar
* value
)
585 this->icon
= value
== NULL
? NULL
: g_string_chunk_insert_const (this->owner
->string_pool
, value
);
588 const gchar
* atp_user_tool_get_icon (const ATPUserTool
*this)
593 ATPToolStore
atp_user_tool_get_storage (const ATPUserTool
*this)
595 return this->storage
;
599 atp_user_tool_get_plugin (ATPUserTool
* this)
601 return this->owner
->plugin
;
605 atp_user_tool_is_valid (const ATPUserTool
* this)
607 return (this->command
!= NULL
);
610 /* Additional tool functions
611 *---------------------------------------------------------------------------*/
614 atp_user_tool_next (ATPUserTool
*this)
616 while ((this = this->next
))
618 /* Skip unnamed and overridden tool */
619 if ((this->name
!= NULL
) && (this->over
== NULL
)) break;
626 atp_user_tool_next_in_same_storage (ATPUserTool
*this)
628 ATPToolStore storage
;
630 storage
= this->storage
;
631 while ((this = this->next
))
634 if (this->storage
!= storage
) return NULL
;
635 if (this->name
!= NULL
) break;
642 atp_user_tool_previous (ATPUserTool
*this)
644 while ((this = this->prev
))
646 /* Skip unnamed and overridden tool */
647 if ((this->name
!= NULL
) && (this->over
== NULL
)) break;
654 atp_user_tool_override (const ATPUserTool
*this)
658 for (tool
= g_hash_table_lookup (this->owner
->hash
, this->name
);
659 tool
!= NULL
; tool
= tool
->over
)
661 if (tool
->over
== this) return tool
;
668 atp_user_tool_append_new (ATPUserTool
*this, const gchar
*name
, ATPToolStore storage
)
672 g_return_val_if_fail (this, NULL
);
675 tool
= atp_user_tool_new (this->owner
, name
, storage
);
678 atp_user_tool_append_list (this, tool
);
685 atp_user_tool_clone_new (ATPUserTool
*this, ATPToolStore storage
)
689 g_return_val_if_fail (this, NULL
);
692 tool
= atp_user_tool_new (this->owner
, this->name
, storage
);
697 prev
= atp_user_tool_previous (this);
698 atp_user_tool_append_list (prev
, tool
);
705 atp_user_tool_move_after (ATPUserTool
*this, ATPUserTool
*position
)
707 g_return_val_if_fail (this, FALSE
);
709 if (!atp_user_tool_remove_list (this)) return FALSE
;
710 return atp_user_tool_append_list (position
, this);
714 atp_user_tool_deactivate (ATPUserTool
* this)
716 /* accelerator is destroyed with the widget */
719 gtk_widget_destroy (this->menu_item
);
720 this->menu_item
= NULL
;
725 atp_user_tool_activate (ATPUserTool
*this, GtkMenu
*submenu
, GtkAccelGroup
*group
)
727 /* Remove previous menu */
728 atp_user_tool_deactivate (this);
730 /* Create new menu item */
731 this->menu_item
= gtk_image_menu_item_new_with_mnemonic (this->name
);
732 gtk_widget_set_sensitive (this->menu_item
, this->flags
& ATP_TOOL_ENABLE
);
735 if ((this->menu_item
!= NULL
) && (this->icon
!= NULL
))
738 GdkPixbuf
*scaled_pixbuf
;
741 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (this->menu_item
), GTK_ICON_SIZE_MENU
, &width
, &height
);
743 pixbuf
= gdk_pixbuf_new_from_file (this->icon
, NULL
);
748 scaled_pixbuf
= gdk_pixbuf_scale_simple (pixbuf
, width
, height
, GDK_INTERP_BILINEAR
);
749 icon
= gtk_image_new_from_pixbuf (scaled_pixbuf
);
750 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (this->menu_item
), icon
);
751 g_object_unref (pixbuf
);
752 g_object_unref (scaled_pixbuf
);
756 g_signal_connect (G_OBJECT (this->menu_item
), "activate", G_CALLBACK (atp_user_tool_execute
), this);
758 if (this->accel_key
!= 0)
760 gtk_widget_add_accelerator(this->menu_item
, "activate", group
, this->accel_key
, this->accel_mods
, GTK_ACCEL_VISIBLE
);
763 gtk_menu_shell_append (GTK_MENU_SHELL (submenu
), this->menu_item
);
764 gtk_widget_show(this->menu_item
);
769 /* Tool list object containing all tools
770 *---------------------------------------------------------------------------*/
773 atp_tool_list_construct (ATPToolList
* this, ATPPlugin
* plugin
)
775 this->plugin
= plugin
;
776 this->ui
= anjuta_shell_get_ui (ANJUTA_PLUGIN(plugin
)->shell
, NULL
);
778 this->hash
= g_hash_table_new (g_str_hash
, g_str_equal
);
779 this->string_pool
= g_string_chunk_new (STRING_CHUNK_SIZE
);
780 this->data_pool
= g_mem_chunk_new ("tool pool", sizeof (ATPUserTool
), STRING_CHUNK_SIZE
* sizeof (ATPUserTool
) / 4, G_ALLOC_AND_FREE
);
785 void atp_tool_list_destroy (ATPToolList
* this)
787 g_hash_table_destroy (this->hash
);
788 g_string_chunk_free (this->string_pool
);
789 g_mem_chunk_destroy (this->data_pool
);
792 /*---------------------------------------------------------------------------*/
795 atp_tool_list_last (ATPToolList
*this)
801 for (tool
= this->list
; tool
!= NULL
; tool
= tool
->next
)
803 /* Skip tool not registered */
804 if (tool
->name
!= NULL
)
814 atp_tool_list_last_in_storage (const ATPToolList
*this, ATPToolStore storage
)
820 for (tool
= this->list
; tool
!= NULL
; tool
= tool
->next
)
822 if (tool
->storage
> storage
) break;
823 /* Skip tool not registered */
824 if (tool
->name
!= NULL
)
834 atp_tool_list_first (ATPToolList
*this)
838 for (tool
= this->list
; tool
!= NULL
; tool
= tool
->next
)
840 /* Skip unnamed and overridden tool*/
841 if ((tool
->name
!= NULL
) && (tool
->over
== NULL
))
851 atp_tool_list_first_in_storage (ATPToolList
*this, ATPToolStore storage
)
855 for (tool
= this->list
; tool
!= NULL
; tool
= tool
->next
)
857 /* Skip unamed tool */
858 if ((tool
->name
!= NULL
) && (tool
->storage
== storage
))
868 atp_tool_list_append_new (ATPToolList
*this, const gchar
*name
, ATPToolStore storage
)
872 g_return_val_if_fail (this, NULL
);
875 tool
= atp_user_tool_new (this, name
, storage
);
877 /* Add it in one of the storage list if necessary */
880 atp_user_tool_append_list (atp_tool_list_last_in_storage (this, storage
), tool
);
886 gboolean
atp_tool_list_activate (ATPToolList
*this)
890 GtkAccelGroup
* group
;
892 menu
= GTK_MENU (gtk_menu_item_get_submenu (GTK_MENU_ITEM (gtk_ui_manager_get_widget (GTK_UI_MANAGER(this->ui
), MENU_PLACEHOLDER
))));
893 group
= anjuta_ui_get_accel_group(this->ui
);
895 for (next
= atp_tool_list_first (this); next
!= NULL
; next
= atp_user_tool_next (next
))
897 atp_user_tool_activate (next
, menu
, group
);