2006-12-13 00:46:14 +00:00
/*
* tools . c - this file is part of Geany , a fast and lightweight IDE
*
2007-01-14 17:36:42 +00:00
* Copyright 2006 - 2007 Enrico Tröger < enrico . troeger @ uvena . de >
* Copyright 2006 - 2007 Nick Treleaven < nick . treleaven @ btinternet . com >
2006-12-13 00:46:14 +00:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* $ Id $
*/
2007-02-24 11:41:56 +00:00
/*
* Miscellaneous code for the Tools menu items .
*/
2006-12-13 00:46:14 +00:00
# include "geany.h"
2007-02-25 14:26:55 +00:00
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# ifdef G_OS_UNIX
# include <sys / types.h>
# include <sys / wait.h>
# include <signal.h>
# endif
# include "tools.h"
2006-12-13 00:46:14 +00:00
# include "support.h"
# include "document.h"
# include "sciwrappers.h"
2007-02-25 14:26:55 +00:00
# include "utils.h"
2006-12-13 00:46:14 +00:00
# include "ui_utils.h"
2007-02-25 14:26:55 +00:00
# include "callbacks.h"
# include "msgwindow.h"
# include "keybindings.h"
2006-12-13 00:46:14 +00:00
2006-12-18 22:55:27 +00:00
2006-12-13 00:46:14 +00:00
enum
{
COLUMN_CHARACTER ,
COLUMN_HTML_NAME ,
N_COLUMNS
} ;
2007-02-25 14:26:55 +00:00
static GtkWidget * sc_dialog = NULL ;
static GtkTreeStore * sc_store = NULL ;
static GtkTreeView * sc_tree = NULL ;
2006-12-13 00:46:14 +00:00
2007-02-25 14:26:55 +00:00
static void sc_on_tools_show_dialog_insert_special_chars_response
2006-12-13 00:46:14 +00:00
( GtkDialog * dialog , gint response , gpointer user_data ) ;
2007-02-25 14:26:55 +00:00
static void sc_on_tree_row_activated
2006-12-13 00:46:14 +00:00
( GtkTreeView * treeview , GtkTreePath * path , GtkTreeViewColumn * col , gpointer user_data ) ;
2007-02-25 14:26:55 +00:00
static void sc_fill_store ( GtkTreeStore * store ) ;
static gboolean sc_insert ( GtkTreeModel * model , GtkTreeIter * iter ) ;
2006-12-13 00:46:14 +00:00
void tools_show_dialog_insert_special_chars ( )
{
2007-02-25 14:26:55 +00:00
if ( sc_dialog = = NULL )
2006-12-13 00:46:14 +00:00
{
gint height ;
GtkCellRenderer * renderer ;
GtkTreeViewColumn * column ;
GtkWidget * swin , * vbox , * label ;
2007-02-25 14:26:55 +00:00
sc_dialog = gtk_dialog_new_with_buttons (
2006-12-13 00:46:14 +00:00
_ ( " Special characters " ) , GTK_WINDOW ( app - > window ) ,
GTK_DIALOG_DESTROY_WITH_PARENT , GTK_STOCK_CANCEL , GTK_RESPONSE_CANCEL ,
_ ( " _Insert " ) , GTK_RESPONSE_OK , NULL ) ;
2007-02-25 14:26:55 +00:00
vbox = ui_dialog_vbox_new ( GTK_DIALOG ( sc_dialog ) ) ;
2006-12-13 00:46:14 +00:00
gtk_box_set_spacing ( GTK_BOX ( vbox ) , 6 ) ;
height = GEANY_WINDOW_MINIMAL_HEIGHT ;
2007-02-25 14:26:55 +00:00
gtk_window_set_default_size ( GTK_WINDOW ( sc_dialog ) , height * 0.8 , height ) ;
gtk_dialog_set_default_response ( GTK_DIALOG ( sc_dialog ) , GTK_RESPONSE_CANCEL ) ;
2006-12-13 00:46:14 +00:00
label = gtk_label_new ( _ ( " Choose a special character from the list below and double click on it or use the button to insert it at the current cursor position. " ) ) ;
gtk_label_set_line_wrap ( GTK_LABEL ( label ) , TRUE ) ;
gtk_misc_set_alignment ( GTK_MISC ( label ) , 0 , 0.5 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , label , FALSE , FALSE , 0 ) ;
2007-02-25 14:26:55 +00:00
sc_tree = GTK_TREE_VIEW ( gtk_tree_view_new ( ) ) ;
2006-12-13 00:46:14 +00:00
2007-02-25 14:26:55 +00:00
sc_store = gtk_tree_store_new ( N_COLUMNS , G_TYPE_STRING , G_TYPE_STRING ) ;
gtk_tree_view_set_model ( GTK_TREE_VIEW ( sc_tree ) ,
GTK_TREE_MODEL ( sc_store ) ) ;
2006-12-13 00:46:14 +00:00
renderer = gtk_cell_renderer_text_new ( ) ;
column = gtk_tree_view_column_new_with_attributes (
_ ( " Character " ) , renderer , " text " , COLUMN_CHARACTER , NULL ) ;
gtk_tree_view_column_set_resizable ( column , TRUE ) ;
2007-02-25 14:26:55 +00:00
gtk_tree_view_append_column ( GTK_TREE_VIEW ( sc_tree ) , column ) ;
2006-12-13 00:46:14 +00:00
renderer = gtk_cell_renderer_text_new ( ) ;
column = gtk_tree_view_column_new_with_attributes (
_ ( " HTML (name) " ) , renderer , " text " , COLUMN_HTML_NAME , NULL ) ;
gtk_tree_view_column_set_resizable ( column , TRUE ) ;
2007-02-25 14:26:55 +00:00
gtk_tree_view_append_column ( GTK_TREE_VIEW ( sc_tree ) , column ) ;
2006-12-13 00:46:14 +00:00
swin = gtk_scrolled_window_new ( NULL , NULL ) ;
gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW ( swin ) , GTK_POLICY_AUTOMATIC ,
GTK_POLICY_AUTOMATIC ) ;
gtk_scrolled_window_add_with_viewport (
2007-02-25 14:26:55 +00:00
GTK_SCROLLED_WINDOW ( swin ) , GTK_WIDGET ( sc_tree ) ) ;
2006-12-13 00:46:14 +00:00
gtk_box_pack_start ( GTK_BOX ( vbox ) , swin , TRUE , TRUE , 0 ) ;
2007-02-25 14:26:55 +00:00
g_signal_connect ( ( gpointer ) sc_tree , " row-activated " ,
G_CALLBACK ( sc_on_tree_row_activated ) , NULL ) ;
2006-12-13 00:46:14 +00:00
2007-02-25 14:26:55 +00:00
g_signal_connect ( ( gpointer ) sc_dialog , " response " ,
G_CALLBACK ( sc_on_tools_show_dialog_insert_special_chars_response ) , NULL ) ;
2006-12-13 00:46:14 +00:00
2007-02-25 14:26:55 +00:00
sc_fill_store ( sc_store ) ;
2006-12-13 00:46:14 +00:00
2006-12-18 22:55:27 +00:00
//gtk_tree_view_expand_all(special_characters_tree);
2007-02-25 14:26:55 +00:00
gtk_tree_view_set_search_column ( sc_tree , COLUMN_HTML_NAME ) ;
2006-12-13 00:46:14 +00:00
}
2007-02-25 14:26:55 +00:00
gtk_widget_show_all ( sc_dialog ) ;
2006-12-13 00:46:14 +00:00
}
// fill the tree model with data
2006-12-18 22:55:27 +00:00
/// TODO move this in a file and make it extendable for more data types
2007-02-25 14:26:55 +00:00
static void sc_fill_store ( GtkTreeStore * store )
2006-12-13 00:46:14 +00:00
{
GtkTreeIter iter ;
GtkTreeIter * parent_iter = NULL ;
guint i ;
gchar * chars [ ] [ 2 ] =
{
2006-12-18 22:55:27 +00:00
{ _ ( " HTML characters " ) , NULL } ,
2006-12-13 00:46:14 +00:00
{ " \" " , " " " } ,
{ " & " , " & " } ,
{ " < " , " < " } ,
{ " > " , " > " } ,
2006-12-18 22:55:27 +00:00
{ _ ( " ISO 8859-1 characters " ) , NULL } ,
2006-12-13 00:46:14 +00:00
{ " " , " " } ,
{ " ¡ " , " ¡ " } ,
{ " ¢ " , " ¢ " } ,
{ " £ " , " £ " } ,
{ " ¤ " , " ¤ " } ,
{ " ¥ " , " ¥ " } ,
{ " ¦ " , " ¦ " } ,
{ " § " , " § " } ,
{ " ¨ " , " ¨ " } ,
{ " © " , " © " } ,
{ " ® " , " ® " } ,
{ " « " , " « " } ,
{ " » " , " » " } ,
{ " ¬ " , " ¬ " } ,
{ " " , " ­ " } ,
{ " ¯ " , " ¯ " } ,
{ " ° " , " ° " } ,
{ " ± " , " ± " } ,
{ " ¹ " , " ¹ " } ,
{ " ² " , " ² " } ,
{ " ³ " , " ³ " } ,
{ " ¼ " , " ¼ " } ,
{ " ½ " , " ½ " } ,
{ " ¾ " , " ¾ " } ,
{ " × " , " × " } ,
{ " ÷ " , " ÷ " } ,
{ " ´ " , " ´ " } ,
{ " µ " , " µ " } ,
{ " ¶ " , " ¶ " } ,
{ " · " , " · " } ,
{ " ¸ " , " ¸ " } ,
{ " ª " , " ª " } ,
{ " º " , " º " } ,
{ " ¿ " , " ¿ " } ,
{ " À " , " À " } ,
{ " Á " , " Á " } ,
{ " Â " , " Â " } ,
{ " Ã " , " Ã " } ,
{ " Ä " , " Ä " } ,
{ " Å " , " Å " } ,
{ " Æ " , " Æ " } ,
{ " Ç " , " Ç " } ,
{ " È " , " È " } ,
{ " É " , " É " } ,
{ " Ê " , " Ê " } ,
{ " Ë " , " Ë " } ,
{ " Ì " , " Ì " } ,
{ " Í " , " Í " } ,
{ " Î " , " Î " } ,
{ " Ï " , " Ï " } ,
{ " Ð " , " Ð " } ,
{ " Ñ " , " Ñ " } ,
{ " Ò " , " Ò " } ,
{ " Ó " , " Ó " } ,
{ " Ô " , " Ô " } ,
{ " Õ " , " Õ " } ,
{ " Ö " , " Ö " } ,
{ " Ø " , " Ø " } ,
{ " Ù " , " Ù " } ,
{ " Ú " , " Ú " } ,
{ " Û " , " Û " } ,
{ " Ü " , " Ü " } ,
{ " Ý " , " Ý " } ,
{ " Þ " , " Þ " } ,
{ " ß " , " ß " } ,
{ " à " , " à " } ,
{ " á " , " á " } ,
{ " â " , " â " } ,
{ " ã " , " ã " } ,
{ " ä " , " ä " } ,
{ " å " , " å " } ,
{ " æ " , " æ " } ,
{ " ç " , " ç " } ,
{ " è " , " è " } ,
{ " é " , " é " } ,
{ " ê " , " ê " } ,
{ " ë " , " ë " } ,
{ " ì " , " ì " } ,
{ " í " , " í " } ,
{ " î " , " î " } ,
{ " ï " , " ï " } ,
{ " ð " , " ð " } ,
{ " ñ " , " ñ " } ,
{ " ò " , " ò " } ,
{ " ó " , " ó " } ,
{ " ô " , " ô " } ,
{ " õ " , " õ " } ,
{ " ö " , " ö " } ,
{ " ø " , " ø " } ,
{ " ù " , " ù " } ,
{ " ú " , " ú " } ,
{ " û " , " û " } ,
{ " ü " , " ü " } ,
{ " ý " , " ý " } ,
{ " þ " , " þ " } ,
{ " ÿ " , " ÿ " } ,
2006-12-18 22:55:27 +00:00
{ _ ( " Greek characters " ) , NULL } ,
{ " Α " , " Α " } ,
{ " α " , " α " } ,
{ " Β " , " Β " } ,
{ " β " , " β " } ,
{ " Γ " , " Γ " } ,
{ " γ " , " γ " } ,
{ " Δ " , " Δ " } ,
{ " δ " , " Δ " } ,
{ " δ " , " δ " } ,
{ " Ε " , " Ε " } ,
{ " ε " , " ε " } ,
{ " Ζ " , " Ζ " } ,
{ " ζ " , " ζ " } ,
{ " Η " , " Η " } ,
{ " η " , " η " } ,
{ " Θ " , " Θ " } ,
{ " θ " , " θ " } ,
{ " Ι " , " Ι " } ,
{ " ι " , " ι " } ,
{ " Κ " , " Κ " } ,
{ " κ " , " κ " } ,
{ " Λ " , " Λ " } ,
{ " λ " , " λ " } ,
{ " Μ " , " Μ " } ,
{ " μ " , " μ " } ,
{ " Ν " , " Ν " } ,
{ " ν " , " ν " } ,
{ " Ξ " , " Ξ " } ,
{ " ξ " , " ξ " } ,
{ " Ο " , " Ο " } ,
{ " ο " , " ο " } ,
{ " Π " , " Π " } ,
{ " π " , " π " } ,
{ " Ρ " , " Ρ " } ,
{ " ρ " , " ρ " } ,
{ " Σ " , " Σ " } ,
{ " ς " , " ς " } ,
{ " σ " , " σ " } ,
{ " Τ " , " Τ " } ,
{ " τ " , " τ " } ,
{ " Υ " , " Υ " } ,
{ " υ " , " υ " } ,
{ " Φ " , " Φ " } ,
{ " φ " , " φ " } ,
{ " Χ " , " Χ " } ,
{ " χ " , " χ " } ,
{ " Ψ " , " Ψ " } ,
{ " ψ " , " ψ " } ,
{ " Ω " , " Ω " } ,
{ " ω " , " ω " } ,
{ " ϑ " , " ϑ " } ,
{ " ϒ " , " ϒ " } ,
{ " ϖ " , " ϖ " } ,
{ _ ( " Mathematical characters " ) , NULL } ,
{ " ∀ " , " ∀ " } ,
{ " ∂ " , " ∂ " } ,
{ " ∃ " , " ∃ " } ,
{ " ∅ " , " ∅ " } ,
{ " ∇ " , " ∇ " } ,
{ " ∈ " , " ∈ " } ,
{ " ∉ " , " ∉ " } ,
{ " ∋ " , " ∋ " } ,
{ " ∏ " , " ∏ " } ,
{ " ∑ " , " ∑ " } ,
{ " − " , " − " } ,
{ " ∗ " , " ∗ " } ,
{ " √ " , " √ " } ,
{ " ∝ " , " ∝ " } ,
{ " ∞ " , " ∞ " } ,
{ " ∠ " , " ∠ " } ,
{ " ∧ " , " ∧ " } ,
{ " ∨ " , " ∨ " } ,
{ " ∩ " , " ∩ " } ,
{ " ∪ " , " ∪ " } ,
{ " ∫ " , " ∫ " } ,
{ " ∴ " , " ∴ " } ,
{ " ∼ " , " ∼ " } ,
{ " ≅ " , " ≅ " } ,
{ " ≈ " , " ≈ " } ,
{ " ≠ " , " ≠ " } ,
{ " ≡ " , " ≡ " } ,
{ " ≤ " , " ≤ " } ,
{ " ≥ " , " ≥ " } ,
{ " ⊂ " , " ⊂ " } ,
{ " ⊃ " , " ⊃ " } ,
{ " ⊄ " , " ⊄ " } ,
{ " ⊆ " , " ⊆ " } ,
{ " ⊇ " , " ⊇ " } ,
{ " ⊕ " , " ⊕ " } ,
{ " ⊗ " , " ⊗ " } ,
{ " ⊥ " , " ⊥ " } ,
{ " ⋅ " , " ⋅ " } ,
{ " ◊ " , " ◊ " } ,
{ _ ( " Technical characters " ) , NULL } ,
{ " ⌈ " , " ⌈ " } ,
{ " ⌉ " , " ⌉ " } ,
{ " ⌊ " , " ⌊ " } ,
{ " ⌋ " , " ⌋ " } ,
{ " 〈 " , " ⟨ " } ,
{ " 〉 " , " ⟩ " } ,
{ _ ( " Arrow characters " ) , NULL } ,
{ " ← " , " ← " } ,
{ " ↑ " , " ↑ " } ,
{ " → " , " → " } ,
{ " ↓ " , " ↓ " } ,
{ " ↔ " , " ↔ " } ,
{ " ↵ " , " ↵ " } ,
{ " ⇐ " , " ⇐ " } ,
{ " ⇑ " , " ⇑ " } ,
{ " ⇒ " , " ⇒ " } ,
{ " ⇓ " , " ⇓ " } ,
{ " ⇔ " , " ⇔ " } ,
{ _ ( " Punctuation characters " ) , NULL } ,
{ " – " , " – " } ,
{ " — " , " — " } ,
{ " ‘ " , " ‘ " } ,
{ " ’ " , " ’ " } ,
{ " ‚ " , " ‚ " } ,
{ " “ " , " “ " } ,
{ " ” " , " ” " } ,
{ " „ " , " „ " } ,
{ " † " , " † " } ,
{ " ‡ " , " ‡ " } ,
{ " … " , " … " } ,
{ " ‰ " , " ‰ " } ,
{ " ‹ " , " ‹ " } ,
{ " › " , " › " } ,
{ _ ( " Miscellaneous characters " ) , NULL } ,
{ " • " , " • " } ,
{ " ′ " , " ′ " } ,
{ " ″ " , " ″ " } ,
{ " ‾ " , " ‾ " } ,
{ " ⁄ " , " ⁄ " } ,
{ " ℘ " , " ℘ " } ,
{ " ℑ " , " ℑ " } ,
{ " ℜ " , " ℜ " } ,
{ " ™ " , " ™ " } ,
{ " € " , " € " } ,
{ " ℵ " , " ℵ " } ,
{ " ♠ " , " ♠ " } ,
{ " ♣ " , " ♣ " } ,
{ " ♥ " , " ♥ " } ,
{ " ♦ " , " ♦ " } ,
{ " Œ " , " Œ " } ,
{ " œ " , " œ " } ,
{ " Š " , " Š " } ,
{ " š " , " š " } ,
{ " Ÿ " , " Ÿ " } ,
{ " ƒ " , " ƒ " } ,
2006-12-13 00:46:14 +00:00
} ;
for ( i = 0 ; i < G_N_ELEMENTS ( chars ) ; i + + )
{
if ( chars [ i ] [ 1 ] = = NULL )
{ // add a category
gtk_tree_store_append ( store , & iter , NULL ) ;
gtk_tree_store_set ( store , & iter , COLUMN_CHARACTER , chars [ i ] [ 0 ] , - 1 ) ;
if ( parent_iter ! = NULL ) gtk_tree_iter_free ( parent_iter ) ;
parent_iter = gtk_tree_iter_copy ( & iter ) ;
}
else
{ // add child to parent_iter
gtk_tree_store_append ( store , & iter , parent_iter ) ;
gtk_tree_store_set ( store , & iter , COLUMN_CHARACTER , chars [ i ] [ 0 ] ,
COLUMN_HTML_NAME , chars [ i ] [ 1 ] , - 1 ) ;
}
}
}
/* just inserts the HTML_NAME coloumn of the selected row at current position
* returns only TRUE if a valid selection ( i . e . no category ) could be found */
2007-02-25 14:26:55 +00:00
static gboolean sc_insert ( GtkTreeModel * model , GtkTreeIter * iter )
2006-12-13 00:46:14 +00:00
{
gint idx = document_get_cur_idx ( ) ;
gboolean result = FALSE ;
if ( DOC_IDX_VALID ( idx ) )
{
gchar * str ;
gint pos = sci_get_current_position ( doc_list [ idx ] . sci ) ;
gtk_tree_model_get ( model , iter , COLUMN_HTML_NAME , & str , - 1 ) ;
if ( str & & * str )
{
sci_insert_text ( doc_list [ idx ] . sci , pos , str ) ;
g_free ( str ) ;
result = TRUE ;
}
}
return result ;
}
2007-02-25 14:26:55 +00:00
static void sc_on_tools_show_dialog_insert_special_chars_response ( GtkDialog * dialog , gint response ,
2006-12-13 00:46:14 +00:00
gpointer user_data )
{
if ( response = = GTK_RESPONSE_OK )
{
GtkTreeSelection * selection ;
GtkTreeModel * model ;
GtkTreeIter iter ;
2007-02-25 14:26:55 +00:00
selection = gtk_tree_view_get_selection ( sc_tree ) ;
2006-12-13 00:46:14 +00:00
if ( gtk_tree_selection_get_selected ( selection , & model , & iter ) )
{
// only hide dialog if selection was not a category
2007-02-25 14:26:55 +00:00
if ( sc_insert ( model , & iter ) )
2006-12-13 00:46:14 +00:00
gtk_widget_hide ( GTK_WIDGET ( dialog ) ) ;
}
}
else
gtk_widget_hide ( GTK_WIDGET ( dialog ) ) ;
}
2007-02-25 14:26:55 +00:00
static void sc_on_tree_row_activated ( GtkTreeView * treeview , GtkTreePath * path ,
2006-12-13 00:46:14 +00:00
GtkTreeViewColumn * col , gpointer user_data )
{
GtkTreeIter iter ;
2007-02-25 14:26:55 +00:00
GtkTreeModel * model = GTK_TREE_MODEL ( sc_store ) ;
2006-12-13 00:46:14 +00:00
if ( gtk_tree_model_get_iter ( model , & iter , path ) )
{
// only hide dialog if selection was not a category
2007-02-25 14:26:55 +00:00
if ( sc_insert ( model , & iter ) )
gtk_widget_hide ( sc_dialog ) ;
2006-12-13 00:46:14 +00:00
else
2006-12-18 22:55:27 +00:00
{ // double click on a category to toggle the expand or collapse it
2007-02-25 14:26:55 +00:00
if ( gtk_tree_view_row_expanded ( sc_tree , path ) )
gtk_tree_view_collapse_row ( sc_tree , path ) ;
2006-12-18 22:55:27 +00:00
else
2007-02-25 14:26:55 +00:00
gtk_tree_view_expand_row ( sc_tree , path , FALSE ) ;
}
}
}
/* custom commands code*/
struct cc_dialog
{
gint count ;
GtkWidget * box ;
} ;
static void cc_add_command ( struct cc_dialog * cc , gint index )
{
GtkWidget * label , * entry , * hbox ;
gchar str [ 6 ] ;
hbox = gtk_hbox_new ( FALSE , 5 ) ;
g_snprintf ( str , 5 , " %d: " , cc - > count ) ;
label = gtk_label_new ( str ) ;
entry = gtk_entry_new ( ) ;
if ( index > = 0 )
gtk_entry_set_text ( GTK_ENTRY ( entry ) , app - > custom_commands [ index ] ) ;
gtk_entry_set_max_length ( GTK_ENTRY ( entry ) , 255 ) ;
gtk_entry_set_width_chars ( GTK_ENTRY ( entry ) , 30 ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , label , FALSE , FALSE , 0 ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , entry , TRUE , TRUE , 0 ) ;
gtk_widget_show_all ( hbox ) ;
gtk_container_add ( GTK_CONTAINER ( cc - > box ) , hbox ) ;
cc - > count + + ;
}
static void cc_on_custom_commands_dlg_add_clicked ( GtkToolButton * toolbutton , struct cc_dialog * cc )
{
cc_add_command ( cc , - 1 ) ;
}
static gboolean cc_iofunc ( GIOChannel * ioc , GIOCondition cond , gpointer data )
{
if ( cond & ( G_IO_IN | G_IO_PRI ) )
{
gint idx = GPOINTER_TO_INT ( data ) ;
gchar * msg = NULL ;
GString * str = g_string_sized_new ( 256 ) ;
while ( g_io_channel_read_line ( ioc , & msg , NULL , NULL , NULL ) & & msg ! = NULL )
{
g_string_append ( str , msg ) ;
g_free ( msg ) ;
}
/// without the following if we replace the selection several hundred times with "" because
/// we get to often in this callback but I don't know why
if ( str - > len > 0 )
{
sci_replace_sel ( doc_list [ idx ] . sci , str - > str ) ;
}
g_string_free ( str , TRUE ) ;
return TRUE ;
}
return FALSE ;
}
static gboolean cc_iofunc_err ( GIOChannel * ioc , GIOCondition cond , gpointer data )
{
if ( cond & ( G_IO_IN | G_IO_PRI ) )
{
gchar * msg = NULL ;
while ( g_io_channel_read_line ( ioc , & msg , NULL , NULL , NULL ) & & msg ! = NULL )
{
g_warning ( " %s: %s " , ( const gchar * ) data , g_strstrip ( msg ) ) ;
g_free ( msg ) ;
}
return TRUE ;
}
return FALSE ;
}
/* Executes command (which should include all necessary command line args) and passes the current
* selection through the standard input of command . The whole output of command replaces the
* current selection . */
void tools_execute_custom_command ( gint idx , const gchar * command )
{
GError * error = NULL ;
GPid pid ;
gchar * * argv ;
gint stdin_fd ;
gint stdout_fd ;
gint stderr_fd ;
g_return_if_fail ( DOC_IDX_VALID ( idx ) & & command ! = NULL ) ;
if ( ! sci_can_copy ( doc_list [ idx ] . sci ) )
return ;
argv = g_strsplit ( command , " " , - 1 ) ;
msgwin_status_add ( _ ( " Passing data and executing custom command: %s " ) , command ) ;
if ( g_spawn_async_with_pipes ( NULL , argv , NULL , G_SPAWN_SEARCH_PATH ,
NULL , NULL , & pid , & stdin_fd , & stdout_fd , & stderr_fd , & error ) )
{
gchar * sel ;
gint len ;
// use GIOChannel to monitor stdout
/// TODO there is something wrong with the whole channel code because the callback is
/// called about several hundred times
utils_set_up_io_channel ( stdout_fd , G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL ,
cc_iofunc , GINT_TO_POINTER ( idx ) ) ;
// copy program's stderr to Geany's stdout to help error tracking
utils_set_up_io_channel ( stderr_fd , G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL ,
cc_iofunc_err , ( gpointer ) command ) ;
// get selection
len = sci_get_selected_text_length ( doc_list [ idx ] . sci ) ;
sel = g_malloc0 ( len ) ;
sci_get_selected_text ( doc_list [ idx ] . sci , sel ) ;
// write data to the command
write ( stdin_fd , sel , len - 1 ) ;
close ( stdin_fd ) ;
g_free ( sel ) ;
}
else
{
geany_debug ( " g_spawn_async_with_pipes() failed: %s " , error - > message ) ;
g_error_free ( error ) ;
}
g_strfreev ( argv ) ;
}
static void cc_show_dialog_custom_commands ( )
{
GtkWidget * dialog , * label , * vbox , * button ;
gint i ;
struct cc_dialog cc ;
dialog = gtk_dialog_new_with_buttons ( _ ( " Set Custom Commands " ) , GTK_WINDOW ( app - > window ) ,
GTK_DIALOG_DESTROY_WITH_PARENT , GTK_STOCK_CANCEL , GTK_RESPONSE_CANCEL ,
GTK_STOCK_OK , GTK_RESPONSE_ACCEPT , NULL ) ;
vbox = ui_dialog_vbox_new ( GTK_DIALOG ( dialog ) ) ;
gtk_box_set_spacing ( GTK_BOX ( vbox ) , 6 ) ;
label = gtk_label_new ( _ ( " You can send the current selection to any of these commands and the output of the command replaces the current selection. " ) ) ;
gtk_label_set_line_wrap ( GTK_LABEL ( label ) , TRUE ) ;
gtk_misc_set_alignment ( GTK_MISC ( label ) , 0 , 0.5 ) ;
gtk_container_add ( GTK_CONTAINER ( vbox ) , label ) ;
cc . count = 1 ;
cc . box = gtk_vbox_new ( FALSE , 0 ) ;
gtk_container_add ( GTK_CONTAINER ( vbox ) , cc . box ) ;
if ( app - > custom_commands = = NULL | | g_strv_length ( app - > custom_commands ) = = 0 )
{
cc_add_command ( & cc , - 1 ) ;
}
else
{
for ( i = 0 ; i < g_strv_length ( app - > custom_commands ) ; i + + )
{
if ( app - > custom_commands [ i ] [ 0 ] = = ' \0 ' )
continue ; // skip empty fields
cc_add_command ( & cc , i ) ;
}
}
button = gtk_button_new_from_stock ( " gtk-add " ) ;
g_signal_connect ( ( gpointer ) button , " clicked " ,
G_CALLBACK ( cc_on_custom_commands_dlg_add_clicked ) , & cc ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , button , FALSE , FALSE , 0 ) ;
gtk_widget_show_all ( vbox ) ;
if ( gtk_dialog_run ( GTK_DIALOG ( dialog ) ) = = GTK_RESPONSE_ACCEPT )
{
// get all hboxes which contain a label and an entry element
GList * children = gtk_container_get_children ( GTK_CONTAINER ( cc . box ) ) ;
GList * tmp ;
GSList * result_list = NULL ;
gint i = 0 ;
gint len = 0 ;
2007-02-26 13:32:34 +00:00
gchar * * result = NULL ;
2007-02-25 14:26:55 +00:00
const gchar * text ;
while ( children ! = NULL )
{
// get the contents of each hbox
tmp = gtk_container_get_children ( GTK_CONTAINER ( children - > data ) ) ;
// first element of the list is the label, so skip it and get the entry element
tmp = tmp - > next ;
text = gtk_entry_get_text ( GTK_ENTRY ( tmp - > data ) ) ;
// if the content of the entry is non-empty, add it to the result array
if ( text [ 0 ] ! = ' \0 ' )
{
result_list = g_slist_append ( result_list , g_strdup ( text ) ) ;
len + + ;
}
children = children - > next ;
2006-12-13 00:46:14 +00:00
}
2007-02-26 13:32:34 +00:00
// create a new null-terminated array but only if there any commands defined
if ( len > 0 )
2007-02-25 14:26:55 +00:00
{
2007-02-26 13:32:34 +00:00
result = g_new ( gchar * , len + 1 ) ;
while ( result_list ! = NULL )
{
result [ i ] = ( gchar * ) result_list - > data ;
2007-02-25 14:26:55 +00:00
2007-02-26 13:32:34 +00:00
result_list = result_list - > next ;
i + + ;
}
result [ len ] = NULL ; // null-terminate the array
2007-02-25 14:26:55 +00:00
}
2007-02-26 13:32:34 +00:00
// set the new array
2007-02-25 14:26:55 +00:00
g_strfreev ( app - > custom_commands ) ;
app - > custom_commands = result ;
2007-02-26 13:32:34 +00:00
// rebuild the menu items
2007-02-25 14:26:55 +00:00
tools_create_insert_custom_command_menu_items ( ) ;
2007-02-26 13:32:34 +00:00
g_slist_free ( result_list ) ;
g_list_free ( children ) ;
2006-12-13 00:46:14 +00:00
}
2007-02-25 14:26:55 +00:00
gtk_widget_destroy ( dialog ) ;
}
/* enable or disable all custom command menu items when the sub menu is opened */
static void cc_on_custom_command_menu_activate ( GtkMenuItem * menuitem , gpointer user_data )
{
gint idx = document_get_cur_idx ( ) ;
gint i , len ;
gboolean enable ;
GList * children ;
if ( ! DOC_IDX_VALID ( idx ) ) return ;
2007-02-26 13:32:34 +00:00
enable = sci_can_copy ( doc_list [ idx ] . sci ) & & ( app - > custom_commands ! = NULL ) ;
2007-02-25 14:26:55 +00:00
children = gtk_container_get_children ( GTK_CONTAINER ( user_data ) ) ;
len = g_list_length ( children ) ;
i = 0 ;
while ( children ! = NULL )
{
if ( i = = ( len - 2 ) )
break ; // stop before the last two elements (the seperator and the set entry)
gtk_widget_set_sensitive ( GTK_WIDGET ( children - > data ) , enable ) ;
children = children - > next ;
i + + ;
}
}
static void cc_on_custom_command_activate ( GtkMenuItem * menuitem , gpointer user_data )
{
gint idx = document_get_cur_idx ( ) ;
gint command_idx ;
if ( ! DOC_IDX_VALID ( idx ) ) return ;
command_idx = GPOINTER_TO_INT ( user_data ) ;
if ( app - > custom_commands = = NULL | |
command_idx < 0 | | command_idx > g_strv_length ( app - > custom_commands ) )
{
cc_show_dialog_custom_commands ( ) ;
return ;
}
// send it through the command and when the command returned the output the current selection
// will be replaced
tools_execute_custom_command ( idx , app - > custom_commands [ command_idx ] ) ;
2006-12-13 00:46:14 +00:00
}
2007-02-25 14:26:55 +00:00
static void cc_insert_custom_command_items ( GtkMenu * me , GtkMenu * mp , gchar * label , gint idx )
{
GtkWidget * item ;
gint key_idx = - 1 ;
switch ( idx )
{
case 0 : key_idx = GEANY_KEYS_EDIT_SENDTOCMD1 ; break ;
case 1 : key_idx = GEANY_KEYS_EDIT_SENDTOCMD2 ; break ;
case 2 : key_idx = GEANY_KEYS_EDIT_SENDTOCMD3 ; break ;
}
item = gtk_menu_item_new_with_label ( label ) ;
if ( key_idx ! = - 1 )
gtk_widget_add_accelerator ( item , " activate " , gtk_accel_group_new ( ) ,
keys [ key_idx ] - > key , keys [ key_idx ] - > mods , GTK_ACCEL_VISIBLE ) ;
gtk_container_add ( GTK_CONTAINER ( me ) , item ) ;
gtk_widget_show ( item ) ;
g_signal_connect ( ( gpointer ) item , " activate " , G_CALLBACK ( cc_on_custom_command_activate ) ,
GINT_TO_POINTER ( idx ) ) ;
item = gtk_menu_item_new_with_label ( label ) ;
if ( key_idx ! = - 1 )
gtk_widget_add_accelerator ( item , " activate " , gtk_accel_group_new ( ) ,
keys [ key_idx ] - > key , keys [ key_idx ] - > mods , GTK_ACCEL_VISIBLE ) ;
gtk_container_add ( GTK_CONTAINER ( mp ) , item ) ;
gtk_widget_show ( item ) ;
g_signal_connect ( ( gpointer ) item , " activate " , G_CALLBACK ( cc_on_custom_command_activate ) ,
GINT_TO_POINTER ( idx ) ) ;
}
void tools_create_insert_custom_command_menu_items ( )
{
GtkMenu * menu_edit = GTK_MENU ( lookup_widget ( app - > window , " send_selection_to2_menu " ) ) ;
GtkMenu * menu_popup = GTK_MENU ( lookup_widget ( app - > popup_menu , " send_selection_to1_menu " ) ) ;
GtkWidget * item ;
GList * me_children ;
GList * mp_children ;
static gboolean signal_set = FALSE ;
// first clean the menus to be able to rebuild them
me_children = gtk_container_get_children ( GTK_CONTAINER ( menu_edit ) ) ;
mp_children = gtk_container_get_children ( GTK_CONTAINER ( menu_popup ) ) ;
while ( me_children ! = NULL )
{
gtk_widget_destroy ( GTK_WIDGET ( me_children - > data ) ) ;
gtk_widget_destroy ( GTK_WIDGET ( mp_children - > data ) ) ;
me_children = me_children - > next ;
mp_children = mp_children - > next ;
}
if ( app - > custom_commands = = NULL | | g_strv_length ( app - > custom_commands ) = = 0 )
{
item = gtk_menu_item_new_with_label ( _ ( " No custom commands defined. " ) ) ;
gtk_container_add ( GTK_CONTAINER ( menu_edit ) , item ) ;
gtk_widget_set_sensitive ( item , FALSE ) ;
gtk_widget_show ( item ) ;
item = gtk_menu_item_new_with_label ( _ ( " No custom commands defined. " ) ) ;
gtk_container_add ( GTK_CONTAINER ( menu_popup ) , item ) ;
gtk_widget_set_sensitive ( item , FALSE ) ;
gtk_widget_show ( item ) ;
}
else
{
gint i ;
gint idx = 0 ;
for ( i = 0 ; i < g_strv_length ( app - > custom_commands ) ; i + + )
{
if ( app - > custom_commands [ i ] [ 0 ] ! = ' \0 ' ) // skip empty fields
{
cc_insert_custom_command_items ( menu_edit , menu_popup , app - > custom_commands [ i ] , idx ) ;
idx + + ;
}
}
}
// separator and Set menu item
item = gtk_separator_menu_item_new ( ) ;
gtk_container_add ( GTK_CONTAINER ( menu_edit ) , item ) ;
gtk_widget_show ( item ) ;
item = gtk_separator_menu_item_new ( ) ;
gtk_container_add ( GTK_CONTAINER ( menu_popup ) , item ) ;
gtk_widget_show ( item ) ;
cc_insert_custom_command_items ( menu_edit , menu_popup , _ ( " Set Custom Commands " ) , - 1 ) ;
if ( ! signal_set )
{
g_signal_connect ( ( gpointer ) lookup_widget ( app - > popup_menu , " send_selection_to1 " ) ,
" activate " , G_CALLBACK ( cc_on_custom_command_menu_activate ) , menu_popup ) ;
g_signal_connect ( ( gpointer ) lookup_widget ( app - > window , " send_selection_to2 " ) ,
" activate " , G_CALLBACK ( cc_on_custom_command_menu_activate ) , menu_edit ) ;
signal_set = TRUE ;
}
}