3 * A file-manager extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009-2015 Pierre Wieser and others (see AUTHORS)
9 * FileManager-Actions is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * FileManager-Actions is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with FileManager-Actions; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
24 * Frederic Ruaudel <grumz@grumz.net>
25 * Rodrigo Moya <rodrigo@gnome-db.org>
26 * Pierre Wieser <pwieser@trychlos.org>
27 * ... and many others (see AUTHORS)
34 #include <glib/gi18n.h>
38 #include "api/fma-object-api.h"
40 #include "core/fma-gtk-utils.h"
41 #include "core/fma-io-provider.h"
43 #include "base-gtk-utils.h"
44 #include "fma-iproperties-tab.h"
45 #include "fma-main-tab.h"
46 #include "fma-main-window.h"
48 /* private interface data
50 struct _FMAIPropertiesTabInterfacePrivate
{
51 void *empty
; /* so that gcc -pedantic is happy */
54 /* i18n: label of the push button when there is not yet any shortcut */
55 #define NO_SHORTCUT N_( "None" )
57 /* data set against the instance
60 gboolean on_selection_change
;
64 #define IPROPERTIES_TAB_PROP_DATA "fma-iproperties-tab-data"
66 static guint st_initializations
= 0; /* interface initialization count */
68 static GType
register_type( void );
69 static void interface_base_init( FMAIPropertiesTabInterface
*klass
);
70 static void interface_base_finalize( FMAIPropertiesTabInterface
*klass
);
71 static void initialize_window( FMAIPropertiesTab
*instance
);
72 static void on_tree_selection_changed( FMATreeView
*tview
, GList
*selected_items
, FMAIPropertiesTab
*instance
);
73 static void on_main_item_updated( FMAIPropertiesTab
*instance
, FMAIContext
*context
, guint data
, void *empty
);
74 static GtkButton
*get_enabled_button( FMAIPropertiesTab
*instance
);
75 static void on_enabled_toggled( GtkToggleButton
*button
, FMAIPropertiesTab
*instance
);
76 static void on_readonly_toggled( GtkToggleButton
*button
, FMAIPropertiesTab
*instance
);
77 static void on_description_changed( GtkTextBuffer
*buffer
, FMAIPropertiesTab
*instance
);
78 static void on_shortcut_clicked( GtkButton
*button
, FMAIPropertiesTab
*instance
);
79 static void display_provider_name( FMAIPropertiesTab
*instance
, FMAObjectItem
*item
);
80 static IPropertiesData
*get_iproperties_data( FMAIPropertiesTab
*instance
);
81 static void on_instance_finalized( gpointer user_data
, FMAIPropertiesTab
*instance
);
84 fma_iproperties_tab_get_type( void )
86 static GType iface_type
= 0;
89 iface_type
= register_type();
98 static const gchar
*thisfn
= "fma_iproperties_tab_register_type";
101 static const GTypeInfo info
= {
102 sizeof( FMAIPropertiesTabInterface
),
103 ( GBaseInitFunc
) interface_base_init
,
104 ( GBaseFinalizeFunc
) interface_base_finalize
,
113 g_debug( "%s", thisfn
);
115 type
= g_type_register_static( G_TYPE_INTERFACE
, "FMAIPropertiesTab", &info
, 0 );
117 g_type_interface_add_prerequisite( type
, GTK_TYPE_APPLICATION_WINDOW
);
123 interface_base_init( FMAIPropertiesTabInterface
*klass
)
125 static const gchar
*thisfn
= "fma_iproperties_tab_interface_base_init";
127 if( !st_initializations
){
129 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
131 klass
->private = g_new0( FMAIPropertiesTabInterfacePrivate
, 1 );
134 st_initializations
+= 1;
138 interface_base_finalize( FMAIPropertiesTabInterface
*klass
)
140 static const gchar
*thisfn
= "fma_iproperties_tab_interface_base_finalize";
142 st_initializations
-= 1;
144 if( !st_initializations
){
146 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
148 g_free( klass
->private );
153 * fma_iproperties_tab_init:
154 * @instance: this #FMAIPropertiesTab instance.
156 * Initialize the interface
157 * Connect to #BaseWindow signals
160 fma_iproperties_tab_init( FMAIPropertiesTab
*instance
)
162 static const gchar
*thisfn
= "fma_iproperties_tab_init";
163 IPropertiesData
*data
;
165 g_return_if_fail( FMA_IS_IPROPERTIES_TAB( instance
));
167 g_debug( "%s: instance=%p (%s)",
169 ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
));
171 fma_main_tab_init( FMA_MAIN_WINDOW( instance
), TAB_PROPERTIES
);
172 initialize_window( instance
);
174 data
= get_iproperties_data( instance
);
175 data
->on_selection_change
= FALSE
;
177 g_object_weak_ref( G_OBJECT( instance
), ( GWeakNotify
) on_instance_finalized
, NULL
);
181 initialize_window( FMAIPropertiesTab
*instance
)
183 static const gchar
*thisfn
= "fma_iproperties_tab_initialize_window";
184 GtkButton
*enabled_button
;
185 GtkWidget
*label_widget
;
186 GtkTextBuffer
*buffer
;
189 g_return_if_fail( FMA_IS_IPROPERTIES_TAB( instance
));
191 g_debug( "%s: instance=%p (%s)",
192 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
));
194 tview
= fma_main_window_get_items_view( FMA_MAIN_WINDOW( instance
));
197 tview
, TREE_SIGNAL_SELECTION_CHANGED
,
198 G_CALLBACK( on_tree_selection_changed
), instance
);
201 instance
, MAIN_SIGNAL_ITEM_UPDATED
,
202 G_CALLBACK( on_main_item_updated
), NULL
);
204 enabled_button
= get_enabled_button( instance
);
206 enabled_button
, "toggled", G_CALLBACK( on_enabled_toggled
), instance
);
208 label_widget
= fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "ActionDescriptionText" );
209 buffer
= gtk_text_view_get_buffer( GTK_TEXT_VIEW( label_widget
));
211 buffer
, "changed", G_CALLBACK( on_description_changed
), instance
);
213 fma_gtk_utils_connect_widget_by_name(
214 GTK_CONTAINER( instance
), "SuggestedShortcutButton",
215 "clicked", G_CALLBACK( on_shortcut_clicked
), instance
);
217 fma_gtk_utils_connect_widget_by_name(
218 GTK_CONTAINER( instance
), "ActionReadonlyButton",
219 "toggled", G_CALLBACK( on_readonly_toggled
), instance
);
223 on_tree_selection_changed( FMATreeView
*tview
, GList
*selected_items
, FMAIPropertiesTab
*instance
)
225 static const gchar
*thisfn
= "fma_iproperties_tab_on_tree_selection_changed";
226 guint count_selected
;
230 GtkNotebook
*notebook
;
232 GtkWidget
*title_widget
, *label_widget
, *shortcut_button
;
233 GtkButton
*enabled_button
;
234 gboolean enabled_item
;
235 GtkToggleButton
*readonly_button
;
236 GtkTextBuffer
*buffer
;
237 gchar
*label
, *shortcut
;
238 IPropertiesData
*data
;
240 g_return_if_fail( FMA_IS_IPROPERTIES_TAB( instance
));
242 count_selected
= g_list_length( selected_items
);
243 g_debug( "%s: tview=%p, count_selected=%d, instance=%p (%s)",
244 thisfn
, tview
, count_selected
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
));
247 G_OBJECT( instance
),
248 MAIN_PROP_ITEM
, &item
,
249 MAIN_PROP_EDITABLE
, &editable
,
252 g_return_if_fail( !item
|| FMA_IS_OBJECT_ITEM( item
));
254 enable_tab
= ( count_selected
== 1 );
255 fma_main_tab_enable_page( FMA_MAIN_WINDOW( instance
), TAB_PROPERTIES
, enable_tab
);
257 data
= get_iproperties_data( instance
);
258 data
->on_selection_change
= TRUE
;
260 notebook
= GTK_NOTEBOOK( fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "main-notebook" ));
261 page
= gtk_notebook_get_nth_page( notebook
, TAB_ACTION
);
262 title_widget
= fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "ActionPropertiesTitle" );
263 label_widget
= gtk_notebook_get_tab_label( notebook
, page
);
265 if( item
&& FMA_IS_OBJECT_MENU( item
)){
266 gtk_label_set_label( GTK_LABEL( label_widget
), _( "Me_nu" ));
267 gtk_label_set_markup( GTK_LABEL( title_widget
), _( "<b>Menu editable properties</b>" ));
269 gtk_label_set_label( GTK_LABEL( label_widget
), _( "_Action" ));
270 gtk_label_set_markup( GTK_LABEL( title_widget
), _( "<b>Action editable properties</b>" ));
273 enabled_button
= get_enabled_button( instance
);
274 enabled_item
= item
? fma_object_is_enabled( FMA_OBJECT_ITEM( item
)) : FALSE
;
275 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( enabled_button
), enabled_item
);
276 base_gtk_utils_set_editable( G_OBJECT( enabled_button
), editable
);
278 label_widget
= fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "ActionDescriptionText" );
279 buffer
= gtk_text_view_get_buffer( GTK_TEXT_VIEW( label_widget
));
280 label
= item
? fma_object_get_description( item
) : g_strdup( "" );
281 gtk_text_buffer_set_text( buffer
, label
, -1 );
283 base_gtk_utils_set_editable( G_OBJECT( label_widget
), editable
);
285 shortcut_button
= fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "SuggestedShortcutButton" );
286 shortcut
= item
? fma_object_get_shortcut( item
) : g_strdup( "" );
287 if( !shortcut
|| !strlen( shortcut
)){
289 shortcut
= g_strdup( gettext( NO_SHORTCUT
));
291 gtk_button_set_label( GTK_BUTTON( shortcut_button
), shortcut
);
293 base_gtk_utils_set_editable( G_OBJECT( shortcut_button
), editable
);
295 /* TODO: don't know how to edit a shortcut for now */
296 gtk_widget_set_sensitive( shortcut_button
, FALSE
);
298 /* read-only toggle only indicates the intrinsic writability status of this item
299 * _not_ the writability status of the provider
301 readonly_button
= GTK_TOGGLE_BUTTON( fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "ActionReadonlyButton" ));
302 gtk_toggle_button_set_active( readonly_button
, item
? fma_object_is_readonly( item
) : FALSE
);
303 base_gtk_utils_set_editable( G_OBJECT( readonly_button
), FALSE
);
305 label_widget
= fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "ActionItemID" );
306 label
= item
? fma_object_get_id( item
) : g_strdup( "" );
307 gtk_label_set_text( GTK_LABEL( label_widget
), label
);
310 display_provider_name( instance
, item
);
312 data
->on_selection_change
= FALSE
;
316 on_main_item_updated( FMAIPropertiesTab
*instance
, FMAIContext
*context
, guint data
, void *empty
)
318 static const gchar
*thisfn
= "fma_iproperties_tab_on_main_item_updated";
320 if( data
& MAIN_DATA_PROVIDER
){
322 g_debug( "%s: instance=%p, item=%p (%s), data=%u, empty=%p",
323 thisfn
, ( void * ) instance
,
324 ( void * ) context
, G_OBJECT_TYPE_NAME( context
), data
, empty
);
326 display_provider_name( instance
, FMA_OBJECT_ITEM( context
));
331 get_enabled_button( FMAIPropertiesTab
*instance
)
333 return( GTK_BUTTON( fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "ActionEnabledButton" )));
337 on_enabled_toggled( GtkToggleButton
*button
, FMAIPropertiesTab
*instance
)
339 static const gchar
*thisfn
= "fma_iproperties_tab_on_enabled_toggled";
343 IPropertiesData
*data
;
345 data
= get_iproperties_data( instance
);
347 if( !data
->on_selection_change
){
348 g_debug( "%s: button=%p, instance=%p, on_selection_change=%s",
349 thisfn
, ( void * ) button
, ( void * ) instance
, data
->on_selection_change
? "True":"False" );
352 G_OBJECT( instance
),
353 MAIN_PROP_ITEM
, &item
,
354 MAIN_PROP_EDITABLE
, &editable
,
357 if( item
&& FMA_IS_OBJECT_ITEM( item
)){
358 enabled
= gtk_toggle_button_get_active( button
);
361 fma_object_set_enabled( item
, enabled
);
362 g_signal_emit_by_name( G_OBJECT( instance
), MAIN_SIGNAL_ITEM_UPDATED
, item
, 0 );
365 g_signal_handlers_block_by_func(( gpointer
) button
, on_enabled_toggled
, instance
);
366 gtk_toggle_button_set_active( button
, !enabled
);
367 g_signal_handlers_unblock_by_func(( gpointer
) button
, on_enabled_toggled
, instance
);
374 * prevent the user to click on the button
375 * - draw-indicator property: transform the check button (TRUE) into a toggle button (FALSE)
376 * - toggled signal is of type run first
377 * so we can only execute our user signal handler after the object signal handler
378 * has already been executed
379 * - overriding the class handler does not work: the overriding handler is called, we
380 * are able to distinguish between our button and others, but even stopping the signal
381 * emission does not prevent the checkbox to be checked/unchecked
382 * - last trying to set an emission hook, but:
383 * emission of signal "toggled" for instance `0x9ceace0' cannot be stopped from emission hook
385 * so the solution to re-toggle the button inside of the signal handler, if it is not
386 * the most elegant, seems at least working without too drawbacks
389 on_readonly_toggled( GtkToggleButton
*button
, FMAIPropertiesTab
*instance
)
391 static const gchar
*thisfn
= "fma_iproperties_tab_on_readonly_toggled";
393 IPropertiesData
*data
;
395 data
= get_iproperties_data( instance
);
397 if( !data
->on_selection_change
){
398 g_debug( "%s: button=%p, instance=%p, on_selection_change=%s",
399 thisfn
, ( void * ) button
, ( void * ) instance
, data
->on_selection_change
? "True":"False" );
401 active
= gtk_toggle_button_get_active( button
);
403 g_signal_handlers_block_by_func(( gpointer
) button
, on_readonly_toggled
, instance
);
404 gtk_toggle_button_set_active( button
, !active
);
405 g_signal_handlers_unblock_by_func(( gpointer
) button
, on_readonly_toggled
, instance
);
411 on_readonly_toggle_cb( GtkToggleButton
*button
, FMAIPropertiesTab
*instance
)
413 static const gchar
*thisfn
= "fma_iproperties_tab_on_readonly_toggle_cb";
414 g_debug( "%s: button=%p, instance=%p", thisfn
, ( void * ) button
, ( void * ) instance
);
416 if( rdbtn
!= GTK_WIDGET( button
)){
417 g_debug( "%s: not called for our button, calling the default handler", thisfn
);
418 g_signal_chain_from_overridden_handler( button
, instance
);
422 g_debug( "%s: called for our button, stop emission", thisfn
);
423 g_signal_stop_emission_by_name( button
, "toggled" );
427 on_readonly_toggle_hook( GSignalInvocationHint
*ihint
,
428 guint n_param_values
, const GValue
*param_values
, FMAIPropertiesTab
*instance
)
430 static const gchar
*thisfn
= "fma_iproperties_tab_on_readonly_toggle_hook";
432 GtkWidget
*signaled_object
;
434 g_debug( "%s: n_param=%d, instance=%p", thisfn
, n_param_values
, ( void * ) instance
);
437 signaled_object
= ( GtkWidget
* ) g_value_get_object( param_values
);
438 if( signaled_object
){
439 button
= base_window_get_widget( BASE_WINDOW( instance
), "ActionReadonlyButton" );
440 if( button
== signaled_object
){
441 g_debug( "%s: called for our button, stop emission", thisfn
);
442 g_signal_stop_emission_by_name( button
, "toggled" );
453 on_description_changed( GtkTextBuffer
*buffer
, FMAIPropertiesTab
*instance
)
455 static const gchar
*thisfn
= "fma_iproperties_tab_on_description_changed";
457 GtkTextIter start
, end
;
460 g_debug( "%s: buffer=%p, instance=%p", thisfn
, ( void * ) buffer
, ( void * ) instance
);
463 G_OBJECT( instance
),
464 MAIN_PROP_ITEM
, &item
,
468 gtk_text_buffer_get_start_iter( buffer
, &start
);
469 gtk_text_buffer_get_end_iter( buffer
, &end
);
470 text
= gtk_text_buffer_get_text( buffer
, &start
, &end
, TRUE
);
471 fma_object_set_description( item
, text
);
472 g_signal_emit_by_name( G_OBJECT( instance
), MAIN_SIGNAL_ITEM_UPDATED
, item
, 0 );
477 on_shortcut_clicked( GtkButton
*button
, FMAIPropertiesTab
*instance
)
482 G_OBJECT( instance
),
483 MAIN_PROP_ITEM
, &item
,
487 /*g_signal_emit_by_name( G_OBJECT( instance ), MAIN_SIGNAL_TAB_UPDATED, edited, 0 );*/
492 display_provider_name( FMAIPropertiesTab
*instance
, FMAObjectItem
*item
)
494 GtkWidget
*label_widget
;
496 FMAIOProvider
*provider
;
498 label_widget
= fma_gtk_utils_find_widget_by_name( GTK_CONTAINER( instance
), "ActionItemProvider" );
501 provider
= fma_object_get_provider( item
);
503 label
= fma_io_provider_get_name( provider
);
507 label
= g_strdup( "" );
509 gtk_label_set_text( GTK_LABEL( label_widget
), label
);
511 gtk_widget_set_sensitive( label_widget
, item
!= NULL
);
514 static IPropertiesData
*
515 get_iproperties_data( FMAIPropertiesTab
*instance
)
517 IPropertiesData
*data
;
519 data
= ( IPropertiesData
* ) g_object_get_data( G_OBJECT( instance
), IPROPERTIES_TAB_PROP_DATA
);
522 data
= g_new0( IPropertiesData
, 1 );
523 g_object_set_data( G_OBJECT( instance
), IPROPERTIES_TAB_PROP_DATA
, data
);
530 on_instance_finalized( gpointer user_data
, FMAIPropertiesTab
*instance
)
532 static const gchar
*thisfn
= "fma_iproperties_tab_on_instance_finalized";
533 IPropertiesData
*data
;
535 g_debug( "%s: instance=%p, user_data=%p", thisfn
, ( void * ) instance
, ( void * ) user_data
);
537 data
= get_iproperties_data( instance
);