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 gboolean
v_is_candidate( NAIContext
*object
, guint target
, GList
*selection
);
66 static gboolean
is_candidate_for_target( const NAIContext
*object
, guint target
, GList
*files
);
67 static gboolean
is_candidate_for_show_in( const NAIContext
*object
, guint target
, GList
*files
);
68 static const gchar
*get_running_environment( void );
69 static gboolean
is_candidate_for_try_exec( const NAIContext
*object
, guint target
, GList
*files
);
70 static gboolean
is_candidate_for_show_if_registered( const NAIContext
*object
, guint target
, GList
*files
);
71 static gboolean
is_candidate_for_show_if_true( const NAIContext
*object
, guint target
, GList
*files
);
72 static gboolean
is_candidate_for_show_if_running( const NAIContext
*object
, guint target
, GList
*files
);
73 static gboolean
is_candidate_for_mimetypes( const NAIContext
*object
, guint target
, GList
*files
);
74 static gboolean
is_all_mimetype( const gchar
*mimetype
);
75 static gboolean
is_file_mimetype( const gchar
*mimetype
);
76 static gboolean
is_mimetype_of( const gchar
*file_type
, const gchar
*ftype
, gboolean is_regular
);
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_schemes( const NAIContext
*object
);
87 static gboolean
is_valid_folders( const NAIContext
*object
);
89 static gboolean
is_positive_assertion( const gchar
*assertion
);
92 * na_icontext_get_type:
94 * Returns: the #GType type of this interface.
97 na_icontext_get_type( void )
99 static GType type
= 0;
102 type
= register_type();
109 * na_icontext_register_type:
111 * Registers this interface.
114 register_type( void )
116 static const gchar
*thisfn
= "na_icontext_register_type";
119 static const GTypeInfo info
= {
120 sizeof( NAIContextInterface
),
121 ( GBaseInitFunc
) interface_base_init
,
122 ( GBaseFinalizeFunc
) interface_base_finalize
,
131 g_debug( "%s", thisfn
);
133 type
= g_type_register_static( G_TYPE_INTERFACE
, "NAIContext", &info
, 0 );
135 g_type_interface_add_prerequisite( type
, G_TYPE_OBJECT
);
141 interface_base_init( NAIContextInterface
*klass
)
143 static const gchar
*thisfn
= "na_icontext_interface_base_init";
145 if( !st_initialized
){
147 g_debug( "%s: klass%p (%s)", thisfn
, ( void * ) klass
, G_OBJECT_CLASS_NAME( klass
));
149 klass
->private = g_new0( NAIContextInterfacePrivate
, 1 );
151 st_initialized
= TRUE
;
156 interface_base_finalize( NAIContextInterface
*klass
)
158 static const gchar
*thisfn
= "na_icontext_interface_base_finalize";
160 if( st_initialized
&& !st_finalized
){
162 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
166 g_free( klass
->private );
171 * na_icontext_is_candidate:
172 * @context: a #NAIContext to be checked.
173 * @target: the current target.
174 * @selection: the currently selected items, as a #GList of NASelectedInfo items.
176 * Determines if the given object may be candidate to be displayed in
177 * the Nautilus context menu, depending of the list of currently selected
180 * This function is called by <methodname>build_nautilus_menus</methodname>
181 * plugin function for each item found in NAPivot items list, and, when this
182 * an action, for each profile of this action.
184 * Returns: %TRUE if this @context succeeds to all tests and is so a
185 * valid candidate to be displayed in Nautilus context menu, %FALSE
191 na_icontext_is_candidate( const NAIContext
*context
, guint target
, GList
*selection
)
193 static const gchar
*thisfn
= "na_icontext_is_candidate";
194 gboolean is_candidate
;
196 g_return_val_if_fail( NA_IS_ICONTEXT( context
), FALSE
);
198 g_debug( "%s: object=%p (%s), target=%d, selection=%p (count=%d)",
199 thisfn
, ( void * ) context
, G_OBJECT_TYPE_NAME( context
), target
, (void * ) selection
, g_list_length( selection
));
201 is_candidate
= v_is_candidate( NA_ICONTEXT( context
), target
, selection
);
205 is_candidate_for_target( context
, target
, selection
) &&
206 is_candidate_for_show_in( context
, target
, selection
) &&
207 is_candidate_for_try_exec( context
, target
, selection
) &&
208 is_candidate_for_show_if_registered( context
, target
, selection
) &&
209 is_candidate_for_show_if_true( context
, target
, selection
) &&
210 is_candidate_for_show_if_running( context
, target
, selection
) &&
211 is_candidate_for_mimetypes( context
, target
, selection
) &&
212 is_candidate_for_basenames( context
, target
, selection
) &&
213 is_candidate_for_selection_count( context
, target
, selection
) &&
214 is_candidate_for_schemes( context
, target
, selection
) &&
215 is_candidate_for_folders( context
, target
, selection
) &&
216 is_candidate_for_capabilities( context
, target
, selection
);
219 return( is_candidate
);
223 * na_icontext_is_valid:
224 * @context: the #NAObjectProfile to be checked.
226 * Returns: %TRUE if this @context is valid, %FALSE else.
228 * This function is part of <methodname>NAIDuplicable::check_status</methodname>
229 * and is called by #NAIDuplicable objects which also implement #NAIContext
230 * interface. It so doesn't make sense of asking the object for its
231 * validity status as it has already been checked before calling the
237 na_icontext_is_valid( const NAIContext
*context
)
241 g_return_val_if_fail( NA_IS_ICONTEXT( context
), FALSE
);
244 is_valid_basenames( context
) &&
245 is_valid_mimetypes( context
) &&
246 is_valid_schemes( context
) &&
247 is_valid_folders( context
);
253 * na_icontext_check_mimetypes:
254 * @context: the #NAIContext object to be checked.
256 * Check the current list of mimetypes to see if it covers all mimetypes,
257 * or all regular files, or something else.
262 na_icontext_check_mimetypes( const NAIContext
*context
)
264 static const gchar
*thisfn
= "na_icontext_check_mimetypes";
266 GSList
*mimetypes
, *im
;
268 g_return_if_fail( NA_IS_ICONTEXT( context
));
271 mimetypes
= na_object_get_mimetypes( context
);
273 for( im
= mimetypes
; im
; im
= im
->next
){
274 if( !im
->data
|| !strlen( im
->data
)){
275 g_debug( "%s: empty mimetype for context=%p", thisfn
, ( void * ) context
);
278 const gchar
*imtype
= ( const gchar
* ) im
->data
;
279 if( is_all_mimetype( imtype
)){
283 /* do not break here so that we are able to check all mimetypes */
286 na_object_set_all_mimetypes( context
, is_all
);
288 na_core_utils_slist_free( mimetypes
);
292 * na_icontext_read_done:
293 * @context: the #NAIContext to be prepared.
295 * Prepares the specified #NAIContext just after it has been read.
300 * This setup an internal flag when mimetypes is like 'all/all'
301 * in order to optimize computation time;
309 na_icontext_read_done( NAIContext
*context
)
311 na_object_check_mimetypes( context
);
315 * na_icontext_set_scheme:
316 * @context: the #NAIContext to be updated.
317 * @scheme: name of the scheme.
318 * @selected: whether this scheme is candidate to this @context.
320 * Sets the status of a @scheme relative to this @context.
325 na_icontext_set_scheme( NAIContext
*context
, const gchar
*scheme
, gboolean selected
)
329 g_return_if_fail( NA_IS_ICONTEXT( context
));
331 schemes
= na_object_get_schemes( context
);
332 schemes
= na_core_utils_slist_setup_element( schemes
, scheme
, selected
);
333 na_object_set_schemes( context
, schemes
);
334 na_core_utils_slist_free( schemes
);
338 * na_icontext_set_only_desktop:
339 * @context: the #NAIContext to be updated.
340 * @desktop: name of the desktop environment.
341 * @selected: whether this @desktop is candidate to this @context.
343 * Sets the status of the @desktop relative to this @context for the OnlyShowIn list.
348 na_icontext_set_only_desktop( NAIContext
*context
, const gchar
*desktop
, gboolean selected
)
352 g_return_if_fail( NA_IS_ICONTEXT( context
));
354 desktops
= na_object_get_only_show_in( context
);
355 desktops
= na_core_utils_slist_setup_element( desktops
, desktop
, selected
);
356 na_object_set_only_show_in( context
, desktops
);
357 na_core_utils_slist_free( desktops
);
361 * na_icontext_set_only_desktop:
362 * @context: the #NAIContext to be updated.
363 * @desktop: name of the desktop environment.
364 * @selected: whether this @desktop is candidate to this @context.
366 * Sets the status of the @desktop relative to this @context for the NotShowIn list.
371 na_icontext_set_not_desktop( NAIContext
*context
, const gchar
*desktop
, gboolean selected
)
375 g_return_if_fail( NA_IS_ICONTEXT( context
));
377 desktops
= na_object_get_not_show_in( context
);
378 desktops
= na_core_utils_slist_setup_element( desktops
, desktop
, selected
);
379 na_object_set_not_show_in( context
, desktops
);
380 na_core_utils_slist_free( desktops
);
384 * na_icontext_replace_folder:
385 * @context: the #NAIContext to be updated.
389 * Replaces the @old URI by the @new one.
394 na_icontext_replace_folder( NAIContext
*context
, const gchar
*old
, const gchar
*new )
398 g_return_if_fail( NA_IS_ICONTEXT( context
));
400 folders
= na_object_get_folders( context
);
401 folders
= na_core_utils_slist_remove_utf8( folders
, old
);
402 folders
= g_slist_append( folders
, ( gpointer
) g_strdup( new ));
403 na_object_set_folders( context
, folders
);
404 na_core_utils_slist_free( folders
);
408 v_is_candidate( NAIContext
*context
, guint target
, GList
*selection
)
410 gboolean is_candidate
;
414 if( NA_ICONTEXT_GET_INTERFACE( context
)->is_candidate
){
415 is_candidate
= NA_ICONTEXT_GET_INTERFACE( context
)->is_candidate( context
, target
, selection
);
418 return( is_candidate
);
422 * whether the given NAIContext object is candidate for this target
423 * target is context menu for location, context menu for selection or toolbar for location
424 * only actions are concerned by this check
427 is_candidate_for_target( const NAIContext
*object
, guint target
, GList
*files
)
429 static const gchar
*thisfn
= "na_icontext_is_candidate_for_target";
432 if( NA_IS_OBJECT_ACTION( object
)){
434 case ITEM_TARGET_LOCATION
:
435 ok
= na_object_is_target_location( object
);
438 case ITEM_TARGET_TOOLBAR
:
439 ok
= na_object_is_target_toolbar( object
);
442 case ITEM_TARGET_SELECTION
:
443 ok
= na_object_is_target_selection( object
);
446 case ITEM_TARGET_ANY
:
451 g_warning( "%s: unknonw target=%d", thisfn
, target
);
457 g_debug( "%s: object is not candidate because target doesn't match (asked=%d)", thisfn
, target
);
458 /*na_object_dump( object );*/
465 * only show in / not show in
466 * only one of these two data may be set
469 is_candidate_for_show_in( const NAIContext
*object
, guint target
, GList
*files
)
471 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_in";
473 GSList
*only_in
= na_object_get_only_show_in( object
);
474 GSList
*not_in
= na_object_get_not_show_in( object
);
475 static const gchar
*environment
= NULL
;
478 environment
= get_running_environment();
479 g_debug( "%s: found %s desktop", thisfn
, environment
);
482 if( only_in
&& g_slist_length( only_in
)){
483 ok
= ( na_core_utils_slist_count( only_in
, environment
) > 0 );
484 } else if( not_in
&& g_slist_length( not_in
)){
485 ok
= ( na_core_utils_slist_count( not_in
, environment
) == 0 );
489 gchar
*only_str
= na_core_utils_slist_to_text( only_in
);
490 gchar
*not_str
= na_core_utils_slist_to_text( not_in
);
491 g_debug( "%s: object is not candidate because OnlyShowIn=%s, NotShowIn=%s", thisfn
, only_str
, not_str
);
496 na_core_utils_slist_free( not_in
);
497 na_core_utils_slist_free( only_in
);
503 * Have asked on xdg-list how to identify the currently running desktop environment
505 * For now, just reproduce the xdg-open algorythm from xdg-utils 1.0
507 #define DESKTOP_KDE "KDE"
508 #define DESKTOP_GNOME "GNOME"
509 #define DESKTOP_XFCE "XFCE"
510 #define DESKTOP_ROX "ROX"
511 #define DESKTOP_LXDE "LXDE"
512 #define DESKTOP_OLD "Old"
515 get_running_environment( void )
517 static const gchar
*thisfn
= "na_icontext_get_running_environment";
519 gchar
*output_str
, *error_str
;
524 value
= g_getenv( "KDE_FULL_SESSION" );
525 if( value
&& !strcmp( value
, "true" )){
526 return( DESKTOP_KDE
);
529 value
= g_getenv( "GNOME_DESKTOP_SESSION_ID" );
530 if( value
&& strlen( value
)){
531 return( DESKTOP_GNOME
);
537 if( g_spawn_command_line_sync(
538 "dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager",
539 &output_str
, &error_str
, &exit_status
, &error
)){
540 ok
= ( exit_status
== 0 && output_str
&& strlen( output_str
) && ( !error_str
|| !strlen( error_str
)));
541 g_free( output_str
);
544 return( DESKTOP_GNOME
);
548 g_warning( "%s: dbus-send: %s", thisfn
, error
->message
);
549 g_error_free( error
);
555 if( g_spawn_command_line_sync(
556 "xprop -root _DT_SAVE_MODE", &output_str
, &error_str
, &exit_status
, &error
)){
557 ok
= ( exit_status
== 0 && output_str
&& strlen( output_str
) && ( !error_str
|| !strlen( error_str
)));
559 ok
= ( g_strstr_len( output_str
, -1, "xfce" ) != NULL
);
561 g_free( output_str
);
564 return( DESKTOP_XFCE
);
568 g_warning( "%s: xprop: %s", thisfn
, error
->message
);
569 g_error_free( error
);
572 /* do not know how to identify ROX or LXDE (Hong Jen Yee <pcman.tw (at) gmail.com>)
573 * environments; so other desktops are just identified as 'Old' (legacy systems)
575 return( DESKTOP_OLD
);
579 * if the data is set, it should be the path of an executable file
582 is_candidate_for_try_exec( const NAIContext
*object
, guint target
, GList
*files
)
584 static const gchar
*thisfn
= "na_icontext_is_candidate_for_try_exec";
586 GError
*error
= NULL
;
587 gchar
*tryexec
= na_object_get_try_exec( object
);
589 if( tryexec
&& strlen( tryexec
)){
591 GFile
*file
= g_file_new_for_path( tryexec
);
592 GFileInfo
*info
= g_file_query_info( file
, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
, G_FILE_QUERY_INFO_NONE
, NULL
, &error
);
594 g_debug( "%s: %s", thisfn
, error
->message
);
595 g_error_free( error
);
598 ok
= g_file_info_get_attribute_boolean( info
, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
);
602 g_object_unref( info
);
605 g_object_unref( file
);
609 g_debug( "%s: object is not candidate because TryExec=%s", thisfn
, tryexec
);
618 is_candidate_for_show_if_registered( const NAIContext
*object
, guint target
, GList
*files
)
620 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_if_registered";
622 gchar
*name
= na_object_get_show_if_registered( object
);
624 if( name
&& strlen( name
)){
626 GError
*error
= NULL
;
627 DBusGConnection
*connection
= dbus_g_bus_get( DBUS_BUS_SESSION
, &error
);
631 g_warning( "%s: %s", thisfn
, error
->message
);
632 g_error_free( error
);
636 DBusGProxy
*proxy
= dbus_g_proxy_new_for_name( connection
, name
, NULL
, NULL
);
637 ok
= ( proxy
!= NULL
);
638 dbus_g_connection_unref( connection
);
643 g_debug( "%s: object is not candidate because ShowIfRegistered=%s", thisfn
, name
);
652 is_candidate_for_show_if_true( const NAIContext
*object
, guint target
, GList
*files
)
654 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_if_true";
656 gchar
*command
= na_object_get_show_if_true( object
);
658 if( command
&& strlen( command
)){
660 gchar
*stdout
= NULL
;
661 g_spawn_command_line_sync( command
, &stdout
, NULL
, NULL
, NULL
);
663 if( stdout
&& !strcmp( stdout
, "true" )){
671 g_debug( "%s: object is not candidate because ShowIfTrue=%s", thisfn
, command
);
680 is_candidate_for_show_if_running( const NAIContext
*object
, guint target
, GList
*files
)
682 static const gchar
*thisfn
= "na_icontext_is_candidate_for_show_if_running";
685 glibtop_proclist proclist
;
686 glibtop_proc_state procstate
;
689 gchar
*running
= na_object_get_show_if_running( object
);
691 if( running
&& strlen( running
)){
693 searched
= g_path_get_basename( running
);
694 pid_list
= glibtop_get_proclist( &proclist
, GLIBTOP_KERN_PROC_ALL
, 0 );
696 for( i
=0 ; i
<proclist
.number
&& !ok
; ++i
){
697 glibtop_get_proc_state( &procstate
, pid_list
[i
] );
698 /*g_debug( "%s: i=%d, cmd=%s", thisfn, i, procstate.cmd );*/
699 if( strcmp( procstate
.cmd
, searched
) == 0 ){
700 g_debug( "%s: i=%d, cmd=%s", thisfn
, i
, procstate
.cmd
);
710 g_debug( "%s: object is not candidate because ShowIfRunning=%s", thisfn
, running
);
719 * object may embed a list of - possibly negated - mimetypes
720 * each file of the selection must satisfy all conditions of this list
721 * an empty list is considered the same as '*' or '* / *'
723 * most time, we will just have '*' - so try to optimize the code to
724 * be as fast as possible when we don't filter on mimetype
726 * mimetypes are of type : "image/ *; !image/jpeg"
727 * file mimetype must be compatible with at least one positive assertion
728 * (they are ORed), while not being of any negative assertions (they are
731 * here, for each mimetype of the selection, do
732 * . match is FALSE while this mimetype has not matched a positive assertion
733 * . nomatch is FALSE while this mimetype has not matched a negative assertion
734 * we are going to check each mimetype filter
735 * while we have not found a match among positive conditions (i.e. while !match)
736 * we have to check all negative conditions to verify that the current
737 * examined mimetype never match these
740 is_candidate_for_mimetypes( const NAIContext
*object
, guint target
, GList
*files
)
742 static const gchar
*thisfn
= "na_icontext_is_candidate_for_mimetypes";
744 gboolean all
= na_object_get_all_mimetypes( object
);
746 g_debug( "%s: all=%s", thisfn
, all
? "True":"False" );
749 GSList
*mimetypes
= na_object_get_mimetypes( object
);
753 for( it
= files
; it
&& ok
; it
= it
->next
){
755 gboolean regular
, match
, positive
;
758 ftype
= na_selected_info_get_mime_type( NA_SELECTED_INFO( it
->data
));
759 regular
= na_selected_info_is_regular( NA_SELECTED_INFO( it
->data
));
762 for( im
= mimetypes
; im
&& ok
; im
= im
->next
){
763 const gchar
*imtype
= ( const gchar
* ) im
->data
;
764 positive
= is_positive_assertion( imtype
);
766 if( !positive
|| !match
){
767 if( is_mimetype_of( positive
? imtype
: imtype
+1, ftype
, regular
)){
768 g_debug( "%s: condition=%s, positive=%s, ftype=%s, matched",
769 thisfn
, imtype
, positive
? "True":"False", ftype
);
780 gchar
*mimetypes_str
= na_core_utils_slist_to_text( mimetypes
);
781 g_debug( "%s: no positive match found for Mimetypes=%s", thisfn
, mimetypes_str
);
782 g_free( mimetypes_str
);
787 gchar
*uri
= na_selected_info_get_uri( NA_SELECTED_INFO( it
->data
));
788 g_warning( "%s: null mimetype found for %s", thisfn
, uri
);
796 na_core_utils_slist_free( mimetypes
);
803 is_all_mimetype( const gchar
*mimetype
)
805 return( !strcmp( mimetype
, "*" ) ||
806 !strcmp( mimetype
, "*/*" ) ||
807 !strcmp( mimetype
, "*/all" ) || /* should be considered as invalid */
808 !strcmp( mimetype
, "all" ) ||
809 !strcmp( mimetype
, "all/*" ) ||
810 !strcmp( mimetype
, "all/all" ));
814 is_file_mimetype( const gchar
*mimetype
)
816 return( !strcmp( mimetype
, "allfiles" ) ||
817 !strcmp( mimetype
, "*/allfiles" ) || /* should be considered as invalid */
818 !strcmp( mimetype
, "allfiles/*" ) ||
819 !strcmp( mimetype
, "allfiles/all" ) ||
820 !strcmp( mimetype
, "all/allfiles" ));
824 * does the file fgroup/fsubgroup have a mimetype which is 'a sort of'
825 * mimetype specified one ?
826 * for example, "image/jpeg" is clearly a sort of "image/ *"
828 * content type if the same as the mime type in *nix;
829 * this is not true on Win32 platforms
832 is_mimetype_of( const gchar
*mimetype
, const gchar
*ftype
, gboolean is_regular
)
834 static const gchar
*thisfn
= "na_icontext_is_mimetype_of";
836 gchar
*file_content_type
, *def_content_type
;
838 if( is_all_mimetype( mimetype
)){
842 if( is_file_mimetype( mimetype
) && is_regular
){
847 file_content_type
= g_content_type_from_mime_type( ftype
);
848 def_content_type
= g_content_type_from_mime_type( mimetype
);
850 if( file_content_type
&& def_content_type
){
851 is_type_of
= g_content_type_is_a( file_content_type
, def_content_type
);
852 g_debug( "%s: def_mimetype=%s content_type=%s file_mimetype=%s content_type=%s is_a=%s",
853 thisfn
, mimetype
, def_content_type
, ftype
, file_content_type
,
854 is_type_of
? "True":"False" );
857 g_free( file_content_type
);
858 g_free( def_content_type
);
860 return( is_type_of
);
864 is_candidate_for_basenames( const NAIContext
*object
, guint target
, GList
*files
)
866 static const gchar
*thisfn
= "na_icontext_is_candidate_for_basenames";
868 GSList
*basenames
= na_object_get_basenames( object
);
871 if( strcmp( basenames
->data
, "*" ) != 0 || g_slist_length( basenames
) > 1 ){
872 gboolean matchcase
= na_object_get_matchcase( object
);
877 for( it
= files
; it
&& ok
; it
= it
->next
){
878 gchar
*pattern
, *bname
, *bname_utf8
;
879 gboolean match
, positive
;
882 bname
= na_selected_info_get_basename( NA_SELECTED_INFO( it
->data
));
883 bname_utf8
= g_filename_to_utf8( bname
, -1, NULL
, NULL
, NULL
);
885 tmp
= g_utf8_strdown( bname_utf8
, -1 );
886 g_free( bname_utf8
);
891 for( ib
= basenames
; ib
&& ok
; ib
= ib
->next
){
892 pattern
= matchcase
?
893 g_strdup(( gchar
* ) ib
->data
) :
894 g_utf8_strdown(( gchar
* ) ib
->data
, -1 );
895 positive
= is_positive_assertion( pattern
);
896 pattern_utf8
= g_filename_to_utf8( positive
? pattern
: pattern
+1, -1, NULL
, NULL
, NULL
);
898 if( !positive
|| !match
){
899 if( g_pattern_match_simple( pattern_utf8
, bname_utf8
)){
900 g_debug( "%s: condition=%s, positive=%s, basename=%s: matched",
901 thisfn
, pattern_utf8
, positive
? "True":"False", bname_utf8
);
910 g_free( pattern_utf8
);
915 gchar
*basenames_str
= na_core_utils_slist_to_text( basenames
);
916 g_debug( "%s: no positive match found for Basenames=%s", thisfn
, basenames_str
);
917 g_free( basenames_str
);
921 g_free( bname_utf8
);
926 na_core_utils_slist_free( basenames
);
933 is_candidate_for_selection_count( const NAIContext
*object
, guint target
, GList
*files
)
935 static const gchar
*thisfn
= "na_icontext_is_candidate_for_selection_count";
939 gchar
*selection_count
= na_object_get_selection_count( object
);
941 if( selection_count
&& strlen( selection_count
)){
942 limit
= atoi( selection_count
+1 );
943 count
= g_list_length( files
);
946 switch( selection_count
[0] ){
948 ok
= ( count
< limit
);
951 ok
= ( count
== limit
);
954 ok
= ( count
> limit
);
962 g_debug( "%s: object is not candidate because SelectionCount=%s", thisfn
, selection_count
);
965 g_free( selection_count
);
971 * it is likely that all selected items have the same scheme, because they
972 * are all in the same location and the scheme mainly depends on location
973 * so we have here a great possible optimization by only testing the
974 * first selected item.
975 * note that this optimization may be wrong, for example when ran from the
976 * command-line with a random set of pseudo-selected items
977 * so we take care of only checking _distincts_ schemes of provided selection
978 * against schemes conditions.
981 is_candidate_for_schemes( const NAIContext
*object
, guint target
, GList
*files
)
983 static const gchar
*thisfn
= "na_icontext_is_candidate_for_schemes";
985 GSList
*schemes
= na_object_get_schemes( object
);
988 if( strcmp( schemes
->data
, "*" ) != 0 || g_slist_length( schemes
) > 1 ){
989 GSList
*distincts
= NULL
;
992 for( it
= files
; it
&& ok
; it
= it
->next
){
993 gchar
*scheme
= na_selected_info_get_uri_scheme( NA_SELECTED_INFO( it
->data
));
995 if( na_core_utils_slist_count( distincts
, scheme
) == 0 ){
998 gboolean match
, positive
;
1001 distincts
= g_slist_prepend( distincts
, g_strdup( scheme
));
1003 for( is
= schemes
; is
&& ok
; is
= is
->next
){
1004 pattern
= ( gchar
* ) is
->data
;
1005 positive
= is_positive_assertion( pattern
);
1007 if( !positive
|| !match
){
1008 if( is_compatible_scheme( positive
? pattern
: pattern
+1, scheme
)){
1024 na_core_utils_slist_free( distincts
);
1028 gchar
*schemes_str
= na_core_utils_slist_to_text( schemes
);
1029 g_debug( "%s: object is not candidate because Schemes=%s", thisfn
, schemes_str
);
1030 g_free( schemes_str
);
1033 na_core_utils_slist_free( schemes
);
1040 is_compatible_scheme( const gchar
*pattern
, const gchar
*scheme
)
1042 gboolean compatible
;
1046 if( strcmp( pattern
, "*" )){
1049 compatible
= ( strcmp( pattern
, scheme
) == 0 );
1052 return( compatible
);
1056 * assumuing here the same sort of optimization than for schemes
1057 * i.e. we assume that all selected items are must probably located
1058 * in the same dirname
1059 * so we take care of only checking _distinct_ dirnames against folder
1063 is_candidate_for_folders( const NAIContext
*object
, guint target
, GList
*files
)
1065 static const gchar
*thisfn
= "na_icontext_is_candidate_for_folders";
1067 GSList
*folders
= na_object_get_folders( object
);
1070 if( strcmp( folders
->data
, "/" ) != 0 || g_slist_length( folders
) > 1 ){
1071 GSList
*distincts
= NULL
;
1074 for( it
= files
; it
&& ok
; it
= it
->next
){
1075 gchar
*dirname
= na_selected_info_get_dirname( NA_SELECTED_INFO( it
->data
));
1077 if( na_core_utils_slist_count( distincts
, dirname
) == 0 ){
1078 g_debug( "%s: distinct dirname=%s", thisfn
, dirname
);
1081 gchar
*dirname_utf8
, *pattern_utf8
;
1082 const gchar
*pattern
;
1083 gboolean match
, positive
;
1084 gboolean has_pattern
;
1086 distincts
= g_slist_prepend( distincts
, g_strdup( dirname
));
1087 dirname_utf8
= g_filename_to_utf8( dirname
, -1, NULL
, NULL
, NULL
);
1090 for( id
= folders
; id
&& ok
; id
= id
->next
){
1091 pattern
= ( const gchar
* ) id
->data
;
1092 positive
= is_positive_assertion( pattern
);
1093 pattern_utf8
= g_filename_to_utf8( positive
? pattern
: pattern
+1, -1, NULL
, NULL
, NULL
);
1094 has_pattern
= ( g_strstr_len( pattern_utf8
, -1, "*" ) != NULL
);
1096 if( !positive
|| !match
){
1097 if(( has_pattern
&& g_pattern_match_simple( pattern_utf8
, dirname_utf8
)) || g_str_has_prefix( dirname_utf8
, pattern_utf8
)){
1098 g_debug( "%s: condition=%s, positive=%s: matched",
1099 thisfn
, pattern
, positive
? "True":"False" );
1106 g_debug( "%s: condition=%s, positive=%s: not matched",
1107 thisfn, pattern_utf8, positive ? "True":"False" );*/
1111 g_free( pattern_utf8
);
1116 g_free( dirname_utf8
);
1122 na_core_utils_slist_free( distincts
);
1126 gchar
*folders_str
= na_core_utils_slist_to_text( folders
);
1127 g_debug( "%s: object is not candidate because Folders=%s", thisfn
, folders_str
);
1128 g_free( folders_str
);
1131 na_core_utils_slist_free( folders
);
1138 is_candidate_for_capabilities( const NAIContext
*object
, guint target
, GList
*files
)
1140 static const gchar
*thisfn
= "na_icontext_is_candidate_for_capabilities";
1142 GSList
*capabilities
= na_object_get_capabilities( object
);
1148 gboolean match
, positive
;
1150 for( it
= files
; it
&& ok
; it
= it
->next
){
1151 for( ic
= capabilities
; ic
&& ok
; ic
= ic
->next
){
1152 cap
= ( const gchar
* ) ic
->data
;
1153 positive
= is_positive_assertion( cap
);
1156 if( !strcmp( positive
? cap
: cap
+1, "Owner" )){
1157 match
= na_selected_info_is_owner( NA_SELECTED_INFO( it
->data
), getlogin());
1159 } else if( !strcmp( positive
? cap
: cap
+1, "Readable" )){
1160 match
= na_selected_info_is_readable( NA_SELECTED_INFO( it
->data
));
1162 } else if( !strcmp( positive
? cap
: cap
+1, "Writable" )){
1163 match
= na_selected_info_is_writable( NA_SELECTED_INFO( it
->data
));
1165 } else if( !strcmp( positive
? cap
: cap
+1, "Executable" )){
1166 match
= na_selected_info_is_executable( NA_SELECTED_INFO( it
->data
));
1168 } else if( !strcmp( positive
? cap
: cap
+1, "Local" )){
1169 match
= na_selected_info_is_local( NA_SELECTED_INFO( it
->data
));
1172 g_warning( "%s: unknown capability %s", thisfn
, cap
);
1175 ok
&= (( positive
&& match
) || ( !positive
&& !match
));
1180 gchar
*capabilities_str
= na_core_utils_slist_to_text( capabilities
);
1181 g_debug( "%s: object is not candidate because Capabilities=%s", thisfn
, capabilities_str
);
1182 g_free( capabilities_str
);
1185 na_core_utils_slist_free( capabilities
);
1192 is_valid_basenames( const NAIContext
*object
)
1197 basenames
= na_object_get_basenames( object
);
1198 valid
= basenames
&& g_slist_length( basenames
) > 0;
1199 na_core_utils_slist_free( basenames
);
1202 na_object_debug_invalid( object
, "basenames" );
1210 * that the list is not empty
1211 * that there is not empty item
1212 * that no mimetype is coded as '* / something'
1215 is_valid_mimetypes( const NAIContext
*object
)
1217 static const gchar
*thisfn
= "na_icontext_is_valid_mimetypes";
1219 GSList
*mimetypes
, *it
;
1220 guint count_ok
, count_errs
;
1221 const gchar
*imtype
;
1223 mimetypes
= na_object_get_mimetypes( object
);
1227 for( it
= mimetypes
; it
; it
= it
->next
){
1228 imtype
= ( const gchar
* ) it
->data
;
1230 if( !imtype
|| !strlen( imtype
)){
1231 g_debug( "%s: null or empty mimetype", thisfn
);
1236 if( imtype
[0] == '*' ){
1238 if( imtype
[1] != '/' ){
1239 g_debug( "%s: invalid mimetype: %s", thisfn
, imtype
);
1243 if( imtype
[2] && imtype
[2] != '*' ){
1244 g_debug( "%s: invalid mimetype: %s", thisfn
, imtype
);
1253 valid
= ( count_ok
> 0 && count_errs
== 0 );
1256 na_object_debug_invalid( object
, "mimetypes" );
1259 na_core_utils_slist_free( mimetypes
);
1265 is_valid_schemes( const NAIContext
*object
)
1270 schemes
= na_object_get_schemes( object
);
1271 valid
= schemes
&& g_slist_length( schemes
) > 0;
1272 na_core_utils_slist_free( schemes
);
1275 na_object_debug_invalid( object
, "schemes" );
1282 is_valid_folders( const NAIContext
*object
)
1287 folders
= na_object_get_folders( object
);
1288 valid
= folders
&& g_slist_length( folders
) > 0;
1289 na_core_utils_slist_free( folders
);
1292 na_object_debug_invalid( object
, "folders" );
1299 * "image/ *" is a positive assertion
1300 * "!image/jpeg" is a negative one
1303 is_positive_assertion( const gchar
*assertion
)
1305 gboolean positive
= TRUE
;
1308 gchar
*dupped
= g_strdup( assertion
);
1309 const gchar
*stripped
= g_strstrip( dupped
);
1311 positive
= ( stripped
[0] != '!' );