3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program 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 * This Program 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
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
35 #include <dbus/dbus-glib.h>
38 #include <sys/types.h>
40 #include <glibtop/proclist.h>
41 #include <glibtop/procstate.h>
43 #include <libnautilus-extension/nautilus-file-info.h>
45 #include <api/na-core-utils.h>
46 #include <api/na-object-api.h>
48 #include "na-gnome-vfs-uri.h"
49 #include "na-selected-info.h"
51 /* private interface data
53 struct _NAIContextInterfacePrivate
{
54 void *empty
; /* so that gcc -pedantic is happy */
57 static gboolean st_initialized
= FALSE
;
58 static gboolean st_finalized
= FALSE
;
60 static GType
register_type( void );
61 static void interface_base_init( NAIContextInterface
*klass
);
62 static void interface_base_finalize( NAIContextInterface
*klass
);
64 static void convert_allfiles_mimetype( NAIContext
*context
);
66 static gboolean
v_is_candidate( NAIContext
*object
, guint target
, GList
*selection
);
68 static gboolean
is_candidate_for_target( const NAIContext
*object
, guint target
, GList
*files
);
69 static gboolean
is_candidate_for_show_in( const NAIContext
*object
, guint target
, GList
*files
);
70 static gboolean
is_candidate_for_try_exec( const NAIContext
*object
, guint target
, GList
*files
);
71 static gboolean
is_candidate_for_show_if_registered( const NAIContext
*object
, guint target
, GList
*files
);
72 static gboolean
is_candidate_for_show_if_true( const NAIContext
*object
, guint target
, GList
*files
);
73 static gboolean
is_candidate_for_show_if_running( const NAIContext
*object
, guint target
, GList
*files
);
74 static gboolean
is_candidate_for_mimetypes( const NAIContext
*object
, guint target
, GList
*files
);
75 static gboolean
is_mimetype_of( const gchar
*file_type
, const gchar
*group
, const gchar
*subgroup
);
76 static void split_mimetype( const gchar
*mimetype
, gchar
**group
, gchar
**subgroup
);
77 static gboolean
is_candidate_for_basenames( const NAIContext
*object
, guint target
, GList
*files
);
78 static gboolean
is_candidate_for_selection_count( const NAIContext
*object
, guint target
, GList
*files
);
79 static gboolean
is_candidate_for_schemes( const NAIContext
*object
, guint target
, GList
*files
);
80 static gboolean
is_compatible_scheme( const gchar
*pattern
, const gchar
*scheme
);
81 static gboolean
is_candidate_for_folders( const NAIContext
*object
, guint target
, GList
*files
);
82 static gboolean
is_candidate_for_capabilities( const NAIContext
*object
, guint target
, GList
*files
);
84 static gboolean
is_valid_basenames( const NAIContext
*object
);
85 static gboolean
is_valid_mimetypes( const NAIContext
*object
);
86 static gboolean
is_valid_isfiledir( const NAIContext
*object
);
87 static gboolean
is_valid_schemes( const NAIContext
*object
);
88 static gboolean
is_valid_folders( const NAIContext
*object
);
90 static gboolean
is_positive_assertion( const gchar
*assertion
);
93 * na_icontext_get_type:
95 * Returns: the #GType type of this interface.
98 na_icontext_get_type( void )
100 static GType type
= 0;
103 type
= register_type();
110 * na_icontext_register_type:
112 * Registers this interface.
115 register_type( void )
117 static const gchar
*thisfn
= "na_icontext_register_type";
120 static const GTypeInfo info
= {
121 sizeof( NAIContextInterface
),
122 ( GBaseInitFunc
) interface_base_init
,
123 ( GBaseFinalizeFunc
) interface_base_finalize
,
132 g_debug( "%s", thisfn
);
134 type
= g_type_register_static( G_TYPE_INTERFACE
, "NAIContext", &info
, 0 );
136 g_type_interface_add_prerequisite( type
, G_TYPE_OBJECT
);
142 interface_base_init( NAIContextInterface
*klass
)
144 static const gchar
*thisfn
= "na_icontext_interface_base_init";
146 if( !st_initialized
){
148 g_debug( "%s: klass%p (%s)", thisfn
, ( void * ) klass
, G_OBJECT_CLASS_NAME( klass
));
150 klass
->private = g_new0( NAIContextInterfacePrivate
, 1 );
152 st_initialized
= TRUE
;
157 interface_base_finalize( NAIContextInterface
*klass
)
159 static const gchar
*thisfn
= "na_icontext_interface_base_finalize";
161 if( st_initialized
&& !st_finalized
){
163 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
167 g_free( klass
->private );
172 * na_icontext_is_candidate:
173 * @context: a #NAIContext to be checked.
174 * @target: the current target.
175 * @selection: the currently selected items, as a #GList of NASelectedInfo items.
177 * Determines if the given object may be candidate to be displayed in
178 * the Nautilus context menu, depending of the list of currently selected
181 * This function is called by <methodname>build_nautilus_menus</methodname>
182 * plugin function for each item found in NAPivot items list, and, when this
183 * an action, for each profile of this action.
185 * Returns: %TRUE if this @context succeeds to all tests and is so a
186 * valid candidate to be displayed in Nautilus context menu, %FALSE
192 na_icontext_is_candidate( const NAIContext
*context
, guint target
, GList
*selection
)
194 static const gchar
*thisfn
= "na_icontext_is_candidate";
195 gboolean is_candidate
;
197 g_return_val_if_fail( NA_IS_ICONTEXT( context
), FALSE
);
199 g_debug( "%s: object=%p (%s), target=%d, selection=%p (count=%d)",
200 thisfn
, ( void * ) context
, G_OBJECT_TYPE_NAME( context
), target
, (void * ) selection
, g_list_length( selection
));
202 is_candidate
= v_is_candidate( NA_ICONTEXT( context
), target
, selection
);
206 is_candidate_for_target( context
, target
, selection
) &&
207 is_candidate_for_show_in( context
, target
, selection
) &&
208 is_candidate_for_try_exec( context
, target
, selection
) &&
209 is_candidate_for_show_if_registered( context
, target
, selection
) &&
210 is_candidate_for_show_if_true( context
, target
, selection
) &&
211 is_candidate_for_show_if_running( context
, target
, selection
) &&
212 is_candidate_for_mimetypes( context
, target
, selection
) &&
213 is_candidate_for_basenames( context
, target
, selection
) &&
214 is_candidate_for_selection_count( context
, target
, selection
) &&
215 is_candidate_for_schemes( context
, target
, selection
) &&
216 is_candidate_for_folders( context
, target
, selection
) &&
217 is_candidate_for_capabilities( context
, target
, selection
);
220 return( is_candidate
);
224 * na_icontext_is_valid:
225 * @context: the #NAObjectProfile to be checked.
227 * Returns: %TRUE if this @context is valid, %FALSE else.
229 * This function is part of <methodname>NAIDuplicable::check_status</methodname>
230 * and is called by #NAIDuplicable objects which also implement #NAIContext
231 * interface. It so doesn't make sense of asking the object for its
232 * validity status as it has already been checked before calling the
238 na_icontext_is_valid( const NAIContext
*context
)
242 g_return_val_if_fail( NA_IS_ICONTEXT( context
), FALSE
);
245 is_valid_basenames( context
) &&
246 is_valid_mimetypes( context
) &&
247 is_valid_isfiledir( context
) &&
248 is_valid_schemes( context
) &&
249 is_valid_folders( context
);
255 * na_icontext_is_all_mimetypes:
256 * @context: the #NAIContext object to be checked.
258 * Returns: %TRUE if this @context is valid for all mimetypes, %FALSE else.
263 na_icontext_is_all_mimetypes( const NAIContext
*context
)
266 GSList
*mimetypes
, *im
;
268 g_return_val_if_fail( NA_IS_ICONTEXT( context
), FALSE
);
271 mimetypes
= na_object_get_mimetypes( context
);
273 for( im
= mimetypes
; im
; im
= im
->next
){
274 if( !im
->data
|| !strlen( im
->data
)){
277 const gchar
*imtype
= ( const gchar
* ) im
->data
;
278 if( !strcmp( imtype
, "*" ) ||
279 !strcmp( imtype
, "*/*" ) ||
280 !strcmp( imtype
, "all" ) ||
281 !strcmp( imtype
, "all/*" ) ||
282 !strcmp( imtype
, "all/all" )){
289 na_core_utils_slist_free( mimetypes
);
295 * na_icontext_read_done:
296 * @context: the #NAIContext to be prepared.
298 * Prepares the specified #NAIContext just after it has been read.
303 * This converts a 'all/allfiles' mimetype to 'all/all' + 'file' scheme.
308 * This setup an internal flag when mimetypes is like 'all/all'
309 * in order to optimize computation time;
317 na_icontext_read_done( NAIContext
*context
)
319 convert_allfiles_mimetype( context
);
321 na_object_set_all_mimetypes( context
, na_icontext_is_all_mimetypes( context
));
325 * na_icontext_set_scheme:
326 * @context: the #NAIContext to be updated.
327 * @scheme: name of the scheme.
328 * @selected: whether this scheme is candidate to this @context.
330 * Sets the status of a @scheme relative to this @context.
335 na_icontext_set_scheme( NAIContext
*context
, const gchar
*scheme
, gboolean selected
)
339 g_return_if_fail( NA_IS_ICONTEXT( context
));
341 schemes
= na_object_get_schemes( context
);
342 schemes
= na_core_utils_slist_setup_element( schemes
, scheme
, selected
);
343 na_object_set_schemes( context
, schemes
);
344 na_core_utils_slist_free( schemes
);
348 * na_icontext_set_only_desktop:
349 * @context: the #NAIContext to be updated.
350 * @desktop: name of the desktop environment.
351 * @selected: whether this @desktop is candidate to this @context.
353 * Sets the status of the @desktop relative to this @context for the OnlyShowIn list.
358 na_icontext_set_only_desktop( NAIContext
*context
, const gchar
*desktop
, gboolean selected
)
362 g_return_if_fail( NA_IS_ICONTEXT( context
));
364 desktops
= na_object_get_only_show_in( context
);
365 desktops
= na_core_utils_slist_setup_element( desktops
, desktop
, selected
);
366 na_object_set_only_show_in( context
, desktops
);
367 na_core_utils_slist_free( desktops
);
371 * na_icontext_set_only_desktop:
372 * @context: the #NAIContext to be updated.
373 * @desktop: name of the desktop environment.
374 * @selected: whether this @desktop is candidate to this @context.
376 * Sets the status of the @desktop relative to this @context for the NotShowIn list.
381 na_icontext_set_not_desktop( NAIContext
*context
, const gchar
*desktop
, gboolean selected
)
385 g_return_if_fail( NA_IS_ICONTEXT( context
));
387 desktops
= na_object_get_not_show_in( context
);
388 desktops
= na_core_utils_slist_setup_element( desktops
, desktop
, selected
);
389 na_object_set_not_show_in( context
, desktops
);
390 na_core_utils_slist_free( desktops
);
394 * na_icontext_replace_folder:
395 * @context: the #NAIContext to be updated.
399 * Replaces the @old URI by the @new one.
404 na_icontext_replace_folder( NAIContext
*context
, const gchar
*old
, const gchar
*new )
408 g_return_if_fail( NA_IS_ICONTEXT( context
));
410 folders
= na_object_get_folders( context
);
411 folders
= na_core_utils_slist_remove_utf8( folders
, old
);
412 folders
= g_slist_append( folders
, ( gpointer
) g_strdup( new ));
413 na_object_set_folders( context
, folders
);
414 na_core_utils_slist_free( folders
);
418 * Convert 'all/allfiles' mimetype to 'all/all' + 'file' scheme.
419 * This takes into account
420 * - all/allfiles, allfiles, allfiles/ * and allfiles/all
421 * - negated assertions.
424 convert_allfiles_mimetype( NAIContext
*context
)
426 GSList
*mimetypes
, *im
;
432 mimetypes
= na_object_get_mimetypes( context
);
433 schemes
= na_object_get_schemes( context
);
435 for( im
= mimetypes
; im
; im
= im
->next
){
436 if( !im
->data
|| !strlen( im
->data
)){
440 gchar
*imtype
= ( gchar
* ) im
->data
;
441 gboolean positive
= is_positive_assertion( imtype
);
442 guint i
= ( positive
? 0 : 1 );
444 if( !strcmp( imtype
+i
, "allfiles" ) ||
445 !strcmp( imtype
+i
, "allfiles/*" ) ||
446 !strcmp( imtype
+i
, "allfiles/all" ) ||
447 !strcmp( imtype
+i
, "all/allfiles" )){
450 im
->data
= g_strdup( "all/all" );
451 tmp
= g_strdup_printf( "%sfile", positive
? "" : "!" );
452 schemes
= g_slist_prepend( schemes
, tmp
);
458 na_object_set_mimetypes( context
, mimetypes
);
459 na_object_set_schemes( context
, schemes
);
462 na_core_utils_slist_free( mimetypes
);
463 na_core_utils_slist_free( schemes
);
467 v_is_candidate( NAIContext
*context
, guint target
, GList
*selection
)
469 gboolean is_candidate
;
473 if( NA_ICONTEXT_GET_INTERFACE( context
)->is_candidate
){
474 is_candidate
= NA_ICONTEXT_GET_INTERFACE( context
)->is_candidate( context
, target
, selection
);
477 return( is_candidate
);
481 * whether the given NAIContext object is candidate for this target
482 * target is context menu for location, context menu for selection or toolbar for location
483 * only actions are concerned by this check
486 is_candidate_for_target( const NAIContext
*object
, guint target
, GList
*files
)
488 static const gchar
*thisfn
= "na_icontext_is_candidate_for_target";
491 if( NA_IS_OBJECT_ACTION( object
)){
493 case ITEM_TARGET_LOCATION
:
494 ok
= na_object_is_target_location( object
);
497 case ITEM_TARGET_TOOLBAR
:
498 ok
= na_object_is_target_toolbar( object
);
501 case ITEM_TARGET_SELECTION
:
502 ok
= na_object_is_target_selection( object
);
505 case ITEM_TARGET_ANY
:
510 g_warning( "%s: unknonw target=%d", thisfn
, target
);
516 g_debug( "%s: object is not candidate because target doesn't match (asked=%d)", thisfn
, target
);
517 /*na_object_dump( object );*/
524 * only show in / not show in
525 * only one of these two data may be set
528 is_candidate_for_show_in( const NAIContext
*object
, guint target
, GList
*files
)
530 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_in";
532 GSList
*only_in
= na_object_get_only_show_in( object
);
533 GSList
*not_in
= na_object_get_not_show_in( object
);
536 /* TODO: how-to get current running environment ? */
537 environment
= g_strdup( "GNOME" );
539 if( environment
&& strlen( environment
)){
540 if( only_in
&& g_slist_length( only_in
)){
541 ok
= ( na_core_utils_slist_count( only_in
, environment
) > 0 );
542 } else if( not_in
&& g_slist_length( not_in
)){
543 ok
= ( na_core_utils_slist_count( not_in
, environment
) == 0 );
548 gchar
*only_str
= na_core_utils_slist_to_text( only_in
);
549 gchar
*not_str
= na_core_utils_slist_to_text( not_in
);
550 g_debug( "%s: object is not candidate because OnlyShowIn=%s, NotShowIn=%s", thisfn
, only_str
, not_str
);
555 g_free( environment
);
556 na_core_utils_slist_free( not_in
);
557 na_core_utils_slist_free( only_in
);
563 * if the data is set, it should be the path of an executable file
566 is_candidate_for_try_exec( const NAIContext
*object
, guint target
, GList
*files
)
568 static const gchar
*thisfn
= "na_icontext_is_candidate_for_try_exec";
570 GError
*error
= NULL
;
571 gchar
*tryexec
= na_object_get_try_exec( object
);
573 if( tryexec
&& strlen( tryexec
)){
575 GFile
*file
= g_file_new_for_path( tryexec
);
576 GFileInfo
*info
= g_file_query_info( file
, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
, G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
578 g_debug( "%s: %s", thisfn
, error
->message
);
579 g_error_free( error
);
582 ok
= g_file_info_get_attribute_boolean( info
, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
);
586 g_object_unref( info
);
589 g_object_unref( file
);
593 g_debug( "%s: object is not candidate because TryExec=%s", thisfn
, tryexec
);
602 is_candidate_for_show_if_registered( const NAIContext
*object
, guint target
, GList
*files
)
604 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_if_registered";
606 gchar
*name
= na_object_get_show_if_registered( object
);
608 if( name
&& strlen( name
)){
610 GError
*error
= NULL
;
611 DBusGConnection
*connection
= dbus_g_bus_get( DBUS_BUS_SESSION
, &error
);
615 g_warning( "%s: %s", thisfn
, error
->message
);
616 g_error_free( error
);
620 DBusGProxy
*proxy
= dbus_g_proxy_new_for_name( connection
, name
, NULL
, NULL
);
621 ok
= ( proxy
!= NULL
);
622 dbus_g_connection_unref( connection
);
627 g_debug( "%s: object is not candidate because ShowIfRegistered=%s", thisfn
, name
);
636 is_candidate_for_show_if_true( const NAIContext
*object
, guint target
, GList
*files
)
638 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_if_true";
640 gchar
*command
= na_object_get_show_if_true( object
);
642 if( command
&& strlen( command
)){
644 gchar
*stdout
= NULL
;
645 g_spawn_command_line_sync( command
, &stdout
, NULL
, NULL
, NULL
);
647 if( stdout
&& !strcmp( stdout
, "true" )){
655 g_debug( "%s: object is not candidate because ShowIfTrue=%s", thisfn
, command
);
664 is_candidate_for_show_if_running( const NAIContext
*object
, guint target
, GList
*files
)
666 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_if_running";
669 glibtop_proclist proclist
;
670 glibtop_proc_state procstate
;
673 gchar
*running
= na_object_get_show_if_running( object
);
675 if( running
&& strlen( running
)){
677 searched
= g_path_get_basename( running
);
678 pid_list
= glibtop_get_proclist( &proclist
, GLIBTOP_KERN_PROC_ALL
, 0 );
680 for( i
=0 ; i
<proclist
.number
&& !ok
; ++i
){
681 glibtop_get_proc_state( &procstate
, pid_list
[i
] );
682 /*g_debug( "%s: i=%d, cmd=%s", thisfn, i, procstate.cmd );*/
683 if( strcmp( procstate
.cmd
, searched
) == 0 ){
684 g_debug( "%s: i=%d, cmd=%s", thisfn
, i
, procstate
.cmd
);
694 g_debug( "%s: object is not candidate because ShowIfRunning=%s", thisfn
, running
);
703 * object may embed a list of - possibly negated - mimetypes
704 * each file of the selection must satisfy all conditions of this list
705 * an empty list is considered the same as '*' or '* / *'
707 * most time, we will just have '*' - so try to optimize the code to
708 * be as fast as possible when we don't filter on mimetype
710 * mimetypes are of type : "image/ *; !image/jpeg"
711 * file mimetype must be compatible with at least one positive assertion
712 * (they are ORed), while not being of any negative assertions (they are
715 * here, for each mimetype of the selection, do
716 * . match is FALSE while this mimetype has not matched a positive assertion
717 * . nomatch is FALSE while this mimetype has not matched a negative assertion
718 * we are going to check each mimetype filter
719 * while we have not found a match among positive conditions (i.e. while !match)
720 * we have to check all negative conditions to verify that the current
721 * examined mimetype never match these
724 is_candidate_for_mimetypes( const NAIContext
*object
, guint target
, GList
*files
)
726 static const gchar
*thisfn
= "na_icontext_is_candidate_for_mimetypes";
728 gboolean all
= na_object_get_all_mimetypes( object
);
730 g_debug( "%s: all=%s", thisfn
, all
? "True":"False" );
733 GSList
*mimetypes
= na_object_get_mimetypes( object
);
737 for( it
= files
; it
&& ok
; it
= it
->next
){
738 gchar
*ftype
, *fgroup
, *fsubgroup
;
739 gboolean match
, positive
;
742 ftype
= na_selected_info_get_mime_type( NA_SELECTED_INFO( it
->data
));
745 split_mimetype( ftype
, &fgroup
, &fsubgroup
);
747 for( im
= mimetypes
; im
&& ok
; im
= im
->next
){
748 const gchar
*imtype
= ( const gchar
* ) im
->data
;
749 positive
= is_positive_assertion( imtype
);
751 if( !positive
|| !match
){
752 if( is_mimetype_of( positive
? imtype
: imtype
+1, fgroup
, fsubgroup
)){
753 g_debug( "%s: condition=%s, positive=%s, ftype=%s, fgroup=%s, fsubgroup=%s, matched",
754 thisfn
, imtype
, positive
? "True":"False", ftype
, fgroup
, fsubgroup
);
765 gchar
*mimetypes_str
= na_core_utils_slist_to_text( mimetypes
);
766 g_debug( "%s: no positive match found for Mimetypes=%s", thisfn
, mimetypes_str
);
767 g_free( mimetypes_str
);
772 gchar
*uri
= na_selected_info_get_uri( NA_SELECTED_INFO( it
->data
));
773 g_debug( "%s: null mimetype found for %s", thisfn
, uri
);
783 na_core_utils_slist_free( mimetypes
);
790 * does the file fgroup/fsubgrop have a mimetype which is 'a sort of'
791 * mimetype specified one ?
792 * for example, "image/jpeg" is clearly a sort of "image/ *"
793 * but how to check to see if "msword/xml" is a sort of "application/xml" ??
796 is_mimetype_of( const gchar
*mimetype
, const gchar
*fgroup
, const gchar
*fsubgroup
)
799 gchar
*mgroup
, *msubgroup
;
801 if( !strcmp( mimetype
, "*" ) || !strcmp( mimetype
, "*/*" )){
805 split_mimetype( mimetype
, &mgroup
, &msubgroup
);
807 is_type_of
= ( strcmp( fgroup
, mgroup
) == 0 );
809 if( strcmp( msubgroup
, "*" ) != 0 ){
810 is_type_of
= ( strcmp( fsubgroup
, msubgroup
) == 0 );
817 return( is_type_of
);
821 * split the given mimetype to its group and subgroup components
824 split_mimetype( const gchar
*mimetype
, gchar
**group
, gchar
**subgroup
)
826 GSList
*slist
= na_core_utils_slist_from_split( mimetype
, "/" );
828 *group
= g_strdup(( const gchar
* ) is
->data
);
830 *subgroup
= g_strdup(( const gchar
* ) is
->data
);
831 na_core_utils_slist_free( slist
);
835 is_candidate_for_basenames( const NAIContext
*object
, guint target
, GList
*files
)
837 static const gchar
*thisfn
= "na_icontext_is_candidate_for_basenames";
839 GSList
*basenames
= na_object_get_basenames( object
);
842 if( strcmp( basenames
->data
, "*" ) != 0 || g_slist_length( basenames
) > 1 ){
843 gboolean matchcase
= na_object_get_matchcase( object
);
848 for( it
= files
; it
&& ok
; it
= it
->next
){
849 gchar
*pattern
, *bname
, *bname_utf8
;
850 gboolean match
, positive
;
853 bname
= na_selected_info_get_basename( NA_SELECTED_INFO( it
->data
));
854 bname_utf8
= g_filename_to_utf8( bname
, -1, NULL
, NULL
, NULL
);
856 tmp
= g_ascii_strdown( bname_utf8
, g_utf8_strlen( bname_utf8
, -1 ));
857 g_free( bname_utf8
);
862 for( ib
= basenames
; ib
&& ok
; ib
= ib
->next
){
863 pattern
= matchcase
?
864 g_strdup(( gchar
* ) ib
->data
) :
865 g_ascii_strdown(( gchar
* ) ib
->data
, strlen(( gchar
* ) ib
->data
));
866 positive
= is_positive_assertion( pattern
);
867 pattern_utf8
= g_filename_to_utf8( positive
? pattern
: pattern
+1, -1, NULL
, NULL
, NULL
);
869 if( !positive
|| !match
){
870 if( g_pattern_match_simple( pattern_utf8
, bname_utf8
)){
871 g_debug( "%s: condition=%s, positive=%s, basename=%s: matched",
872 thisfn
, pattern_utf8
, positive
? "True":"False", bname_utf8
);
881 g_free( pattern_utf8
);
886 gchar
*basenames_str
= na_core_utils_slist_to_text( basenames
);
887 g_debug( "%s: no positive match found for Basenames=%s", thisfn
, basenames_str
);
888 g_free( basenames_str
);
892 g_free( bname_utf8
);
897 na_core_utils_slist_free( basenames
);
904 is_candidate_for_selection_count( const NAIContext
*object
, guint target
, GList
*files
)
906 static const gchar
*thisfn
= "na_icontext_is_candidate_for_selection_count";
910 gchar
*selection_count
= na_object_get_selection_count( object
);
912 if( selection_count
&& strlen( selection_count
)){
913 limit
= atoi( selection_count
+1 );
914 count
= g_list_length( files
);
917 switch( selection_count
[0] ){
919 ok
= ( count
< limit
);
922 ok
= ( count
== limit
);
925 ok
= ( count
> limit
);
933 g_debug( "%s: object is not candidate because SelectionCount=%s", thisfn
, selection_count
);
936 g_free( selection_count
);
942 * it is likely that all selected items have the same scheme, because they
943 * are all in the same location and the scheme mainly depends on location
944 * so we have here a great possible optimization by only testing the
945 * first selected item.
946 * note that this optimization may be wrong, for example when ran from the
947 * command-line with a random set of pseudo-selected items
948 * so we take care of only checking _distincts_ schemes of provided selection
949 * against schemes conditions.
952 is_candidate_for_schemes( const NAIContext
*object
, guint target
, GList
*files
)
954 static const gchar
*thisfn
= "na_icontext_is_candidate_for_schemes";
956 GSList
*schemes
= na_object_get_schemes( object
);
959 if( strcmp( schemes
->data
, "*" ) != 0 || g_slist_length( schemes
) > 1 ){
960 GSList
*distincts
= NULL
;
963 for( it
= files
; it
&& ok
; it
= it
->next
){
964 gchar
*scheme
= na_selected_info_get_uri_scheme( NA_SELECTED_INFO( it
->data
));
966 if( na_core_utils_slist_count( distincts
, scheme
) == 0 ){
969 gboolean match
, positive
;
972 distincts
= g_slist_prepend( distincts
, g_strdup( scheme
));
974 for( is
= schemes
; is
&& ok
; is
= is
->next
){
975 pattern
= ( gchar
* ) is
->data
;
976 positive
= is_positive_assertion( pattern
);
978 if( !positive
|| !match
){
979 if( is_compatible_scheme( positive
? pattern
: pattern
+1, scheme
)){
995 na_core_utils_slist_free( distincts
);
999 gchar
*schemes_str
= na_core_utils_slist_to_text( schemes
);
1000 g_debug( "%s: object is not candidate because Schemes=%s", thisfn
, schemes_str
);
1001 g_free( schemes_str
);
1004 na_core_utils_slist_free( schemes
);
1011 is_compatible_scheme( const gchar
*pattern
, const gchar
*scheme
)
1013 gboolean compatible
;
1017 if( strcmp( pattern
, "*" )){
1020 compatible
= ( strcmp( pattern
, scheme
) == 0 );
1023 return( compatible
);
1027 * assumuing here the same sort of optimization than for schemes
1028 * i.e. we assume that all selected items are must probably located
1029 * in the same dirname
1030 * so we take care of only checking _distinct_ dirnames against folder
1034 is_candidate_for_folders( const NAIContext
*object
, guint target
, GList
*files
)
1036 static const gchar
*thisfn
= "na_icontext_is_candidate_for_folders";
1038 GSList
*folders
= na_object_get_folders( object
);
1041 if( strcmp( folders
->data
, "/" ) != 0 || g_slist_length( folders
) > 1 ){
1042 GSList
*distincts
= NULL
;
1045 for( it
= files
; it
&& ok
; it
= it
->next
){
1046 gchar
*dirname
= na_selected_info_get_dirname( NA_SELECTED_INFO( it
->data
));
1048 if( na_core_utils_slist_count( distincts
, dirname
) == 0 ){
1049 g_debug( "%s: distinct dirname=%s", thisfn
, dirname
);
1052 gchar
*dirname_utf8
, *pattern_utf8
;
1053 const gchar
*pattern
;
1054 gboolean match
, positive
;
1055 gboolean has_pattern
;
1057 distincts
= g_slist_prepend( distincts
, g_strdup( dirname
));
1058 dirname_utf8
= g_filename_to_utf8( dirname
, -1, NULL
, NULL
, NULL
);
1061 for( id
= folders
; id
&& ok
; id
= id
->next
){
1062 pattern
= ( const gchar
* ) id
->data
;
1063 positive
= is_positive_assertion( pattern
);
1064 pattern_utf8
= g_filename_to_utf8( positive
? pattern
: pattern
+1, -1, NULL
, NULL
, NULL
);
1065 has_pattern
= ( g_strstr_len( pattern_utf8
, -1, "*" ) != NULL
);
1067 if( !positive
|| !match
){
1068 if(( has_pattern
&& g_pattern_match_simple( pattern_utf8
, dirname_utf8
)) || g_str_has_prefix( dirname_utf8
, pattern_utf8
)){
1069 g_debug( "%s: condition=%s, positive=%s: matched",
1070 thisfn
, pattern
, positive
? "True":"False" );
1077 g_debug( "%s: condition=%s, positive=%s: not matched",
1078 thisfn, pattern_utf8, positive ? "True":"False" );*/
1082 g_free( pattern_utf8
);
1087 g_free( dirname_utf8
);
1093 na_core_utils_slist_free( distincts
);
1097 gchar
*folders_str
= na_core_utils_slist_to_text( folders
);
1098 g_debug( "%s: object is not candidate because Folders=%s", thisfn
, folders_str
);
1099 g_free( folders_str
);
1102 na_core_utils_slist_free( folders
);
1109 is_candidate_for_capabilities( const NAIContext
*object
, guint target
, GList
*files
)
1111 static const gchar
*thisfn
= "na_icontext_is_candidate_for_capabilities";
1113 GSList
*capabilities
= na_object_get_capabilities( object
);
1119 gboolean match
, positive
;
1121 for( it
= files
; it
&& ok
; it
= it
->next
){
1122 for( ic
= capabilities
; ic
&& ok
; ic
= ic
->next
){
1123 cap
= ( const gchar
* ) ic
->data
;
1124 positive
= is_positive_assertion( cap
);
1127 if( !strcmp( positive
? cap
: cap
+1, "Owner" )){
1128 match
= na_selected_info_is_owner( NA_SELECTED_INFO( it
->data
), getlogin());
1130 } else if( !strcmp( positive
? cap
: cap
+1, "Readable" )){
1131 match
= na_selected_info_is_readable( NA_SELECTED_INFO( it
->data
));
1133 } else if( !strcmp( positive
? cap
: cap
+1, "Writable" )){
1134 match
= na_selected_info_is_writable( NA_SELECTED_INFO( it
->data
));
1136 } else if( !strcmp( positive
? cap
: cap
+1, "Executable" )){
1137 match
= na_selected_info_is_executable( NA_SELECTED_INFO( it
->data
));
1139 } else if( !strcmp( positive
? cap
: cap
+1, "Local" )){
1140 match
= na_selected_info_is_local( NA_SELECTED_INFO( it
->data
));
1143 g_warning( "%s: unknown capability %s", thisfn
, cap
);
1146 ok
&= (( positive
&& match
) || ( !positive
&& !match
));
1151 gchar
*capabilities_str
= na_core_utils_slist_to_text( capabilities
);
1152 g_debug( "%s: object is not candidate because Capabilities=%s", thisfn
, capabilities_str
);
1153 g_free( capabilities_str
);
1156 na_core_utils_slist_free( capabilities
);
1163 is_valid_basenames( const NAIContext
*object
)
1168 basenames
= na_object_get_basenames( object
);
1169 valid
= basenames
&& g_slist_length( basenames
) > 0;
1170 na_core_utils_slist_free( basenames
);
1173 na_object_debug_invalid( object
, "basenames" );
1180 is_valid_mimetypes( const NAIContext
*object
)
1185 mimetypes
= na_object_get_mimetypes( object
);
1186 valid
= mimetypes
&& g_slist_length( mimetypes
) > 0;
1187 na_core_utils_slist_free( mimetypes
);
1190 na_object_debug_invalid( object
, "mimetypes" );
1197 is_valid_isfiledir( const NAIContext
*object
)
1200 gboolean isfile
, isdir
;
1202 isfile
= na_object_is_file( object
);
1203 isdir
= na_object_is_dir( object
);
1205 valid
= isfile
|| isdir
;
1208 na_object_debug_invalid( object
, "isfiledir" );
1215 is_valid_schemes( const NAIContext
*object
)
1220 schemes
= na_object_get_schemes( object
);
1221 valid
= schemes
&& g_slist_length( schemes
) > 0;
1222 na_core_utils_slist_free( schemes
);
1225 na_object_debug_invalid( object
, "schemes" );
1232 is_valid_folders( const NAIContext
*object
)
1237 folders
= na_object_get_folders( object
);
1238 valid
= folders
&& g_slist_length( folders
) > 0;
1239 na_core_utils_slist_free( folders
);
1242 na_object_debug_invalid( object
, "folders" );
1249 * "image/ *" is a positive assertion
1250 * "!image/jpeg" is a negative one
1253 is_positive_assertion( const gchar
*assertion
)
1255 gboolean positive
= TRUE
;
1258 gchar
*dupped
= g_strdup( assertion
);
1259 const gchar
*stripped
= g_strstrip( dupped
);
1261 positive
= ( stripped
[0] != '!' );