[nautilus-actions] Implements drag and drop
- From: Pierre Wieser <pwieser src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [nautilus-actions] Implements drag and drop
- Date: Sun, 30 Aug 2009 14:46:29 +0000 (UTC)
commit b6bc948a165c81145ea95db57486f17569028be9
Author: Pierre Wieser <pwieser trychlos org>
Date: Sun Aug 30 16:49:04 2009 +0200
Implements drag and drop
For now, only drag to external applications (file-manager, text editor, ...) is implemented.
A new NAActionMenu class is defined, derived from new NAObjectItem class.
Preferences 'Add About item' and 'Display in alphabetical order'.
ChangeLog | 116 ++++
NEWS | 10 +-
TODO | 6 +-
data/nautilus-actions.schemas.in | 33 +-
doc/objects-hierarchy.odg | Bin 14360 -> 14610 bytes
src/common/Makefile.am | 4 +
src/common/na-action-class.h | 6 +-
src/common/na-action-menu.c | 288 ++++++++++
src/common/na-action-menu.h | 78 +++
src/common/na-action-profile.c | 267 +++++------
src/common/na-action.c | 340 ++++---------
src/common/na-action.h | 5 +-
src/common/na-gconf.c | 42 ++-
src/common/na-iduplicable.c | 171 ++++---
src/common/na-iduplicable.h | 74 ++--
src/common/na-iio-provider.c | 38 +-
src/common/na-iio-provider.h | 5 +-
src/common/na-ipivot-consumer.c | 34 ++-
src/common/na-ipivot-consumer.h | 28 +-
src/common/na-iprefs.c | 81 +++-
src/common/na-iprefs.h | 70 +++-
src/common/na-object-item.c | 448 ++++++++++++++++
src/common/na-object-item.h | 89 ++++
src/common/na-object.c | 260 ++++++----
src/common/na-object.h | 68 +--
src/common/na-pivot.c | 64 +++-
src/common/na-utils.c | 85 +++
src/common/na-utils.h | 7 +
src/common/na-xml-writer.c | 220 +++++++-
src/common/na-xml-writer.h | 8 +-
src/nact/Makefile.am | 6 +
src/nact/egg-tree-multi-dnd.c | 505 ++++++++++++++++++
src/nact/egg-tree-multi-dnd.h | 98 ++++
src/nact/nact-application.c | 4 +-
src/nact/nact-assistant-export.c | 55 +--
src/nact/nact-iaction-tab.c | 34 +-
src/nact/nact-iaction-tab.h | 2 +-
src/nact/nact-iactions-list.c | 199 +++----
src/nact/nact-iactions-list.h | 4 +-
src/nact/nact-imenubar.c | 57 +--
src/nact/nact-imenubar.h | 2 +-
src/nact/nact-main-window.c | 160 +++++--
src/nact/nact-preferences-editor.c | 39 +-
src/nact/nact-selection.c | 180 +++++++
src/nact/nact-selection.h | 45 ++
src/nact/nact-tree-model.c | 694 +++++++++++++++++++++++++
src/nact/nact-tree-model.h | 95 ++++
src/nact/nact-window.h | 1 +
src/nact/nautilus-actions-config-tool.actions | 1 +
src/nact/nautilus-actions-config-tool.ui | 37 +-
src/plugin/nautilus-actions.c | 27 +-
51 files changed, 4177 insertions(+), 1013 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index dbb54bb..2a85832 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,119 @@
+2009-08-30 Pierre Wieser <pwieser trychlos org>
+
+ * data/nautilus-actions.schemas.in:
+ Remove schema for 'Use submenu' preference.
+ Add schemas for 'Add About' and 'Alphabetical order' preferences.
+
+ * doc/objects-hierarchy.odg:
+ Update doc to show new NAObjectItem and NAActionMenu classes.
+
+ * src/common/na-action-class.h:
+ NAAction is now derived from NAObjectItem.
+
+ * src/common/na-action-profile.c:
+ Remove PROP_NAPROFILE_NAME and PROP_NAPROFILE_LABEL properties.
+ Remove duplicate() virtual function.
+ Add new() and get_clipboard_id() virtual functions.
+
+ * src/common/na-action.c:
+ Remove PROP_NAACTION_UUID, PROP_NAACTION_LABEL,
+ PROP_NAACTION_TOOLTIP and PROP_NAACTION_ICON properties.
+ Remove duplicate() and check_edited_status() virtual functions.
+ Add new() and get_clipboard_id() virtual functions.
+
+ * src/common/na-action-menu.c:
+ * src/common/na-action-menu.h: New files.
+
+ * src/common/na-gconf.c:
+ Now watches for preferences directory to manage display mode in
+ Nautilus.
+
+ * src/common/na-iduplicable.c:
+ * src/common/na-iduplicable.h:
+ Replace duplicate() virtual function with copy() and new().
+ All interface is now declared with NAIDuplicable instead of NAObject.
+
+ * src/common/na-iio-provider.c:
+ * src/common/na-iio-provider.h:
+ Temporarily sort read actions.
+ Define NA_IIO_PROVIDER_SIGNAL_DISPLAY_ORDER_CHANGED and
+ NA_IIO_PROVIDER_SIGNAL_DISPLAY_ABOUT_CHANGED new signals.
+
+ * src/common/na-ipivot-consumer.c:
+ Take advantage of new signals defined in na-iio-provider.h.
+ Define on_display_about_changed() and on_display_order_changed()
+ new virtual functions.
+
+ * src/common/na-iprefs.c:
+ * src/common/na-iprefs.h (na_iprefs_get_alphabetical_order,
+ na_iprefs_get_add_about_item): New functions.
+
+ * src/common/na-object.c:
+ * src/common/na-object.h (new, copy, get_clipboard_id):
+ New virtual functions.
+
+ * src/common/na-object-item.c:
+ * src/common/na-object-item.h: New files.
+
+ * src/common/na-pivot.c:
+ Take advantage of new signals defined in na-iio-provider.h.
+
+ * src/common/na-utils.c:
+ * src/common/na-utils.h (na_utils_remove_last_level_from_path,
+ na_utils_is_writable_dir, na_utils_exist_file): New functions.
+
+ * src/common/na-xml-writer.c:
+ * src/common/na-xml-writer.h (na_xml_writer_get_output_fname,
+ na_xml_writer_get_xml_buffer, na_xml_writer_output_xml):
+ New functions.
+
+ * src/nact/egg-tree-multi-dnd.c:
+ * src/nact/egg-tree-multi-dnd.h: New files.
+
+ * src/nact/nact-application.c:
+ No more display debug message in appli_get_application_name().
+
+ * src/nact/nact-assistant-export.c
+ (on_actions_list_selection_changed): Use actual parameters types.
+ (is_writable_dir): Removed function.
+
+ * src/nact/nact-iaction-tab.c:
+ * src/nact/nact-iaction-tab.h
+ (get_selected): Removed virtual function.
+ (nact_iaction_tab_set_action): Add selected items parameter.
+
+ * src/nact/nact-iactions-list.c:
+ * src/nact/nact-iactions-list.h:
+ Implements drag from the tree view based on a dedicated
+ NactTreeModel.
+ Remove virtual function on_selection_changed().
+ nact_iactions_list_get_selected_items(),
+ nact_iactions_list_set_dnd_mode(): New functions.
+
+ * nact/nact-imenubar.c:
+ * nact/nact-imenubar.h:
+ Add 'New menu' item in the menubar.
+ Define new insert_item() virtual function.
+
+ * src/nact/nact-main-window.c:
+ * src/nact/nact-main-window.h:
+ Implements NaIPrefs interface.
+ Be kept informed of preferences modifications.
+
+ * src/nact/nact-preferences-editor.c:
+ Replace 'Display as submenu' by 'Add About' and 'Display in
+ alphabetical order' preferences.
+
+ * src/nact/nact-selection.c:
+ * src/nact/nact-selection.h: New files.
+
+ * src/nact/nact-tree-model.c:
+ * src/nact/nact-tree-model.h: New files.
+
+ * src/plugin/nautilus-actions.c:
+ Creates submenus if asked for.
+ Adds 'About Nautilus Actions' item is asked for.
+
2009-08-25 Pierre Wieser <pwieser trychlos org>
* configure.ac: Add a comment about bug #589745.
diff --git a/NEWS b/NEWS
index 306004d..e986f00 100644
--- a/NEWS
+++ b/NEWS
@@ -3,8 +3,14 @@ Version xxxxxx
Release date
- Displays actions as a submenu of Nautilus context menu.
- Menu items have now keyboard accelerators.
+ Actions can be displayed as Nautilus submenus.
+ An 'About Nautilus Actions' item can be added to Nautilus context
+ menu.
+ Menubar items have now keyboard accelerators.
+ Preferences can be edited in NACT user interface.
+ Actions can be freely reordered in NACT user interface.
+ Actions can be exported as XML files by drag and drop to file-
+ manager, text editor, ...
Bugfixes
diff --git a/TODO b/TODO
index 285825a..e1a401f 100644
--- a/TODO
+++ b/TODO
@@ -28,8 +28,6 @@
- changing conditions in IConditionsTab should trigger an update of the
example label
-- use GtkUIManager
-
- propose a patch for GOptions so that the help for an option could be
multiline (by defining an optional maximum width)
@@ -43,3 +41,7 @@
impact on Nautilus behavior
-> send a DBus message would be the most elegant solution
+- open a bug against glib for g_utf8_collate_key_for_filename()
+ (see the 26 exported actions, and the screenshot of Nautilus view)
+
+- open a bug against gnome-packagekit: keep the last window size and position
diff --git a/data/nautilus-actions.schemas.in b/data/nautilus-actions.schemas.in
index 2166c97..79cf694 100644
--- a/data/nautilus-actions.schemas.in
+++ b/data/nautilus-actions.schemas.in
@@ -217,17 +217,6 @@ All schemes used by Nautilus can be used here.</long>
<!-- schemas for preferences -->
<schema>
- <key>/schemas/apps/nautilus-actions/preferences/display-as-submenu</key>
- <owner>nautilus-actions</owner>
- <type>bool</type>
- <locale name="C">
- <short>Display actions as a Nautilus submenu</short>
- <long>When TRUE, the available Nautilus-Actions actions are displayed as a submenu of the Nautilus context menu</long>
- </locale>
- <default>false</default>
- </schema>
-
- <schema>
<key>/schemas/apps/nautilus-actions/preferences/export-assistant</key>
<owner>nautilus-actions</owner>
<type>list</type>
@@ -343,5 +332,27 @@ All schemes used by Nautilus can be used here.</long>
<default>[]</default>
</schema>
+ <schema>
+ <key>/schemas/apps/nautilus-actions/preferences/preferences-add-about-item</key>
+ <owner>nautilus-actions</owner>
+ <type>bool</type>
+ <locale name="C">
+ <short>Add an 'About' item in the Nautilus context menu</short>
+ <long>If TRUE, and if the user has defined a single root menu for its actions, then an 'About Nautilus Actions' will be displayed at end of the first submenu.</long>
+ </locale>
+ <default>true</default>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/nautilus-actions/preferences/preferences-alphabetical-order</key>
+ <owner>nautilus-actions</owner>
+ <type>bool</type>
+ <locale name="C">
+ <short>Sort actions in alphabetical order</short>
+ <long>When TRUE, the actions are maintained in alphabetical order (historical behavior). When FALSE, user is free to reorder them via Nautilus-Actions configuration tool.</long>
+ </locale>
+ <default>true</default>
+ </schema>
+
</schemalist>
</gconfschemafile>
diff --git a/doc/objects-hierarchy.odg b/doc/objects-hierarchy.odg
index 7e4b757..2a2881d 100644
Binary files a/doc/objects-hierarchy.odg and b/doc/objects-hierarchy.odg differ
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index e40fa0b..dede517 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -40,6 +40,8 @@ libnact_la_SOURCES = \
na-action.c \
na-action.h \
na-action-class.h \
+ na-action-menu.c \
+ na-action-menu.h \
na-action-profile.c \
na-action-profile.h \
na-action-profile-class.h \
@@ -58,6 +60,8 @@ libnact_la_SOURCES = \
na-iprefs.h \
na-object.c \
na-object.h \
+ na-object-item.c \
+ na-object-item.h \
na-pivot.c \
na-pivot.h \
na-utils.c \
diff --git a/src/common/na-action-class.h b/src/common/na-action-class.h
index a734f5b..38cf758 100644
--- a/src/common/na-action-class.h
+++ b/src/common/na-action-class.h
@@ -35,7 +35,7 @@
* SECTION: na_action
*/
-#include "na-object.h"
+#include "na-object-item.h"
G_BEGIN_DECLS
@@ -49,7 +49,7 @@ G_BEGIN_DECLS
typedef struct NAActionPrivate NAActionPrivate;
typedef struct {
- NAObject parent;
+ NAObjectItem parent;
NAActionPrivate *private;
}
NAAction;
@@ -57,7 +57,7 @@ typedef struct {
typedef struct NAActionClassPrivate NAActionClassPrivate;
typedef struct {
- NAObjectClass parent;
+ NAObjectItemClass parent;
NAActionClassPrivate *private;
}
NAActionClass;
diff --git a/src/common/na-action-menu.c b/src/common/na-action-menu.c
new file mode 100644
index 0000000..1230cdc
--- /dev/null
+++ b/src/common/na-action-menu.c
@@ -0,0 +1,288 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "na-action.h"
+#include "na-action-menu.h"
+
+/* private class data
+ */
+struct NAActionMenuClassPrivate {
+ void *empty; /* so that gcc -pedantic is happy */
+};
+
+/* private instance data
+ */
+struct NAActionMenuPrivate {
+ gboolean dispose_has_run;
+};
+
+static NAObjectClass *st_parent_class = NULL;
+
+static GType register_type( void );
+static void class_init( NAActionMenuClass *klass );
+static void instance_init( GTypeInstance *instance, gpointer klass );
+static void instance_dispose( GObject *object );
+static void instance_finalize( GObject *object );
+
+static NAObject *object_new( const NAObject *menu );
+static void object_copy( NAObject *target, const NAObject *source );
+static gboolean object_are_equal( const NAObject *a, const NAObject *b );
+static gboolean object_is_valid( const NAObject *menu );
+static void object_dump( const NAObject *menu );
+static gchar *object_get_clipboard_id( const NAObject *menu );
+
+GType
+na_action_menu_get_type( void )
+{
+ static GType action_type = 0;
+
+ if( !action_type ){
+ action_type = register_type();
+ }
+
+ return( action_type );
+}
+
+static GType
+register_type( void )
+{
+ static const gchar *thisfn = "na_action_menu_register_type";
+
+ static GTypeInfo info = {
+ sizeof( NAActionMenuClass ),
+ ( GBaseInitFunc ) NULL,
+ ( GBaseFinalizeFunc ) NULL,
+ ( GClassInitFunc ) class_init,
+ NULL,
+ NULL,
+ sizeof( NAActionMenu ),
+ 0,
+ ( GInstanceInitFunc ) instance_init
+ };
+
+ g_debug( "%s", thisfn );
+
+ return( g_type_register_static( NA_OBJECT_ITEM_TYPE, "NAActionMenu", &info, 0 ));
+}
+
+static void
+class_init( NAActionMenuClass *klass )
+{
+ static const gchar *thisfn = "na_action_menu_class_init";
+ GObjectClass *object_class;
+
+ g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+ st_parent_class = g_type_class_peek_parent( klass );
+
+ object_class = G_OBJECT_CLASS( klass );
+ object_class->dispose = instance_dispose;
+ object_class->finalize = instance_finalize;
+
+ klass->private = g_new0( NAActionMenuClassPrivate, 1 );
+
+ NA_OBJECT_CLASS( klass )->new = object_new;
+ NA_OBJECT_CLASS( klass )->copy = object_copy;
+ NA_OBJECT_CLASS( klass )->are_equal = object_are_equal;
+ NA_OBJECT_CLASS( klass )->is_valid = object_is_valid;
+ NA_OBJECT_CLASS( klass )->dump = object_dump;
+ NA_OBJECT_CLASS( klass )->get_clipboard_id = object_get_clipboard_id;
+}
+
+static void
+instance_init( GTypeInstance *instance, gpointer klass )
+{
+ static const gchar *thisfn = "na_action_menu_instance_init";
+ NAActionMenu *self;
+
+ g_debug( "%s: instance=%p, klass=%p", thisfn, ( void * ) instance, ( void * ) klass );
+ g_assert( NA_IS_ACTION_MENU( instance ));
+ self = NA_ACTION_MENU( instance );
+
+ self->private = g_new0( NAActionMenuPrivate, 1 );
+
+ self->private->dispose_has_run = FALSE;
+}
+
+static void
+instance_dispose( GObject *object )
+{
+ static const gchar *thisfn = "na_action_menu_instance_dispose";
+ NAActionMenu *self;
+
+ g_debug( "%s: object=%p", thisfn, ( void * ) object );
+
+ g_assert( NA_IS_ACTION_MENU( object ));
+ self = NA_ACTION_MENU( object );
+
+ if( !self->private->dispose_has_run ){
+
+ self->private->dispose_has_run = TRUE;
+
+ /* chain up to the parent class */
+ G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ }
+}
+
+static void
+instance_finalize( GObject *object )
+{
+ static const gchar *thisfn = "na_action_menu_instance_finalize";
+ NAActionMenu *self;
+
+ g_debug( "%s: object=%p", thisfn, ( void * ) object );
+ g_assert( NA_IS_ACTION_MENU( object ));
+ self = ( NAActionMenu * ) object;
+
+ g_free( self->private );
+
+ /* chain call to parent class */
+ if((( GObjectClass * ) st_parent_class )->finalize ){
+ G_OBJECT_CLASS( st_parent_class )->finalize( object );
+ }
+}
+
+/**
+ * na_action_menu_new:
+ *
+ * Allocates a new #NAActionMenu object.
+ *
+ * The new #NAActionMenu object is initialized with suitable default values,
+ * but without any profile.
+ *
+ * Returns: the newly allocated #NAActionMenu object.
+ */
+NAActionMenu *
+na_action_menu_new( void )
+{
+ NAActionMenu *menu;
+
+ menu = g_object_new( NA_ACTION_MENU_TYPE, NULL );
+
+ na_action_set_new_uuid( NA_ACTION( menu ));
+
+ na_object_set_label( NA_OBJECT( menu ), NA_ACTION_MENU_DEFAULT_LABEL );
+
+ return( menu );
+}
+
+static NAObject *
+object_new( const NAObject *menu )
+{
+ return( NA_OBJECT( na_action_menu_new()));
+}
+
+void
+object_copy( NAObject *target, const NAObject *source )
+{
+ if( st_parent_class->copy ){
+ st_parent_class->copy( target, source );
+ }
+
+ g_assert( NA_IS_ACTION_MENU( source ));
+ g_assert( NA_IS_ACTION_MENU( target ));
+}
+
+static gboolean
+object_are_equal( const NAObject *a, const NAObject *b )
+{
+ gboolean equal = TRUE;
+
+ if( equal ){
+ if( st_parent_class->are_equal ){
+ equal = st_parent_class->are_equal( a, b );
+ }
+ }
+
+ g_assert( NA_IS_ACTION_MENU( a ));
+ g_assert( NA_IS_ACTION_MENU( b ));
+
+ return( equal );
+}
+
+/*
+ * a valid NAActionMenu requires a not null, not empty label
+ * this is checked here as NAObject doesn't have this condition
+ */
+gboolean
+object_is_valid( const NAObject *menu )
+{
+ gchar *label;
+ gboolean is_valid = TRUE;
+
+ if( is_valid ){
+ if( st_parent_class->is_valid ){
+ is_valid = st_parent_class->is_valid( menu );
+ }
+ }
+
+ g_assert( NA_IS_ACTION_MENU( menu ));
+
+ if( is_valid ){
+ label = na_object_get_label( menu );
+ is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
+ g_free( label );
+ }
+
+ return( is_valid );
+}
+
+static void
+object_dump( const NAObject *menu )
+{
+ static const gchar *thisfn = "na_action_menu_object_dump";
+ /*NAActionMenu *self;*/
+
+ if( st_parent_class->dump ){
+ st_parent_class->dump( menu );
+ }
+
+ g_assert( NA_IS_ACTION_MENU( menu ));
+ /*self = NA_ACTION_MENU( menu );*/
+
+ g_debug( "%s: (nothing to dump)", thisfn );
+}
+
+static gchar *
+object_get_clipboard_id( const NAObject *menu )
+{
+ gchar *uuid;
+ gchar *clipboard_id;
+
+ uuid = na_object_get_id( menu );
+ clipboard_id = g_strdup_printf( "M:%s", uuid );
+ g_free( uuid );
+
+ return( clipboard_id );
+}
diff --git a/src/common/na-action-menu.h b/src/common/na-action-menu.h
new file mode 100644
index 0000000..15e380f
--- /dev/null
+++ b/src/common/na-action-menu.h
@@ -0,0 +1,78 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+#ifndef __NA_ACTION_MENU_H__
+#define __NA_ACTION_MENU_H__
+
+/**
+ * SECTION: na_action_menu
+ * @short_description: #NAActionMenu class definition.
+ * @include: common/na-action-menu.h
+ *
+ * This is a menu.
+ */
+
+#include "na-object-item.h"
+
+G_BEGIN_DECLS
+
+#define NA_ACTION_MENU_TYPE ( na_action_menu_get_type())
+#define NA_ACTION_MENU( object ) ( G_TYPE_CHECK_INSTANCE_CAST( object, NA_ACTION_MENU_TYPE, NAActionMenu ))
+#define NA_ACTION_MENU_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( klass, NA_ACTION_MENU_TYPE, NAActionMenuClass ))
+#define NA_IS_ACTION_MENU( object ) ( G_TYPE_CHECK_INSTANCE_TYPE( object, NA_ACTION_MENU_TYPE ))
+#define NA_IS_ACTION_MENU_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE(( klass ), NA_ACTION_MENU_TYPE ))
+#define NA_ACTION_MENU_GET_CLASS( object ) ( G_TYPE_INSTANCE_GET_CLASS(( object ), NA_ACTION_MENU_TYPE, NAActionMenuClass ))
+
+typedef struct NAActionMenuPrivate NAActionMenuPrivate;
+
+typedef struct {
+ NAObjectItem parent;
+ NAActionMenuPrivate *private;
+}
+ NAActionMenu;
+
+typedef struct NAActionMenuClassPrivate NAActionMenuClassPrivate;
+
+typedef struct {
+ NAObjectItemClass parent;
+ NAActionMenuClassPrivate *private;
+}
+ NAActionMenuClass;
+
+/* i18n: default label for a newly created menu */
+#define NA_ACTION_MENU_DEFAULT_LABEL _( "New Nautilus Menu" )
+
+GType na_action_menu_get_type( void );
+
+NAActionMenu *na_action_menu_new( void );
+
+G_END_DECLS
+
+#endif /* __NA_ACTION_MENU_H__ */
diff --git a/src/common/na-action-profile.c b/src/common/na-action-profile.c
index 3fab8bf..2c64f83 100644
--- a/src/common/na-action-profile.c
+++ b/src/common/na-action-profile.c
@@ -73,8 +73,6 @@ struct NAActionProfilePrivate {
*/
enum {
PROP_NAPROFILE_ACTION = 1,
- PROP_NAPROFILE_NAME,
- PROP_NAPROFILE_LABEL,
PROP_NAPROFILE_PATH,
PROP_NAPROFILE_PARAMETERS,
PROP_NAPROFILE_BASENAMES,
@@ -87,8 +85,6 @@ enum {
};
#define PROP_NAPROFILE_ACTION_STR "na-profile-action"
-#define PROP_NAPROFILE_NAME_STR "na-profile-name"
-#define PROP_NAPROFILE_LABEL_STR "na-profile-desc-name"
#define PROP_NAPROFILE_PATH_STR "na-profile-path"
#define PROP_NAPROFILE_PARAMETERS_STR "na-profile-parameters"
#define PROP_NAPROFILE_BASENAMES_STR "na-profile-basenames"
@@ -109,14 +105,15 @@ static void instance_set_property( GObject *object, guint property_id, cons
static void instance_dispose( GObject *object );
static void instance_finalize( GObject *object );
-static void object_dump( const NAObject *action );
-static void object_dump_list( const gchar *thisfn, const gchar *label, GSList *list );
-static NAObject *object_duplicate( const NAObject *action );
+static int validate_schemes( GSList* schemes2test, NautilusFileInfo* file );
+
+static NAObject *object_new( const NAObject *profile );
static void object_copy( NAObject *target, const NAObject *source );
static gboolean object_are_equal( const NAObject *a, const NAObject *b );
-static gboolean object_is_valid( const NAObject *action );
-
-static int validate_schemes( GSList* schemes2test, NautilusFileInfo* file );
+static gboolean object_is_valid( const NAObject *profile );
+static void object_dump( const NAObject *profile );
+static void object_dump_list( const gchar *thisfn, const gchar *label, GSList *list );
+static gchar *object_get_clipboard_id( const NAObject *profile );
GType
na_action_profile_get_type( void )
@@ -177,20 +174,6 @@ class_init( NAActionProfileClass *klass )
g_object_class_install_property( object_class, PROP_NAPROFILE_ACTION, spec );
spec = g_param_spec_string(
- PROP_NAPROFILE_NAME_STR,
- "Profile name",
- "Internal profile identifiant (ASCII, case insensitive)", "",
- G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
- g_object_class_install_property( object_class, PROP_NAPROFILE_NAME, spec );
-
- spec = g_param_spec_string(
- PROP_NAPROFILE_LABEL_STR,
- "Profile label",
- "Displayable profile's label (UTF-8, localizable)", "",
- G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
- g_object_class_install_property( object_class, PROP_NAPROFILE_LABEL, spec );
-
- spec = g_param_spec_string(
PROP_NAPROFILE_PATH_STR,
"Command path",
"Command path", "",
@@ -255,21 +238,21 @@ class_init( NAActionProfileClass *klass )
klass->private = g_new0( NAActionProfileClassPrivate, 1 );
- NA_OBJECT_CLASS( klass )->dump = object_dump;
- NA_OBJECT_CLASS( klass )->duplicate = object_duplicate;
+ NA_OBJECT_CLASS( klass )->new = object_new;
NA_OBJECT_CLASS( klass )->copy = object_copy;
NA_OBJECT_CLASS( klass )->are_equal = object_are_equal;
NA_OBJECT_CLASS( klass )->is_valid = object_is_valid;
+ NA_OBJECT_CLASS( klass )->dump = object_dump;
+ NA_OBJECT_CLASS( klass )->get_clipboard_id = object_get_clipboard_id;
}
static void
instance_init( GTypeInstance *instance, gpointer klass )
{
- /*static const gchar *thisfn = "na_action_profile_instance_init";
- g_debug( "%s: instance=%p, klass=%p", thisfn, instance, klass );*/
-
+ /*static const gchar *thisfn = "na_action_profile_instance_init";*/
NAActionProfile *self;
+ /*g_debug( "%s: instance=%p, klass=%p", thisfn, ( void * ) instance, ( void * ) klass );*/
g_assert( NA_IS_ACTION_PROFILE( instance ));
self = NA_ACTION_PROFILE( instance );
@@ -307,14 +290,6 @@ instance_get_property( GObject *object, guint property_id, GValue *value, GParam
g_value_set_pointer( value, self->private->action );
break;
- case PROP_NAPROFILE_NAME:
- G_OBJECT_CLASS( st_parent_class )->get_property( object, PROP_NAOBJECT_ID, value, spec );
- break;
-
- case PROP_NAPROFILE_LABEL:
- G_OBJECT_CLASS( st_parent_class )->get_property( object, PROP_NAOBJECT_LABEL, value, spec );
- break;
-
case PROP_NAPROFILE_PATH:
g_value_set_string( value, self->private->path );
break;
@@ -373,14 +348,6 @@ instance_set_property( GObject *object, guint property_id, const GValue *value,
self->private->action = g_value_get_pointer( value );
break;
- case PROP_NAPROFILE_NAME:
- G_OBJECT_CLASS( st_parent_class )->set_property( object, PROP_NAOBJECT_ID, value, spec );
- break;
-
- case PROP_NAPROFILE_LABEL:
- G_OBJECT_CLASS( st_parent_class )->set_property( object, PROP_NAOBJECT_LABEL, value, spec );
- break;
-
case PROP_NAPROFILE_PATH:
g_free( self->private->path );
self->private->path = g_value_dup_string( value );
@@ -431,11 +398,10 @@ instance_set_property( GObject *object, guint property_id, const GValue *value,
static void
instance_dispose( GObject *object )
{
- static const gchar *thisfn = "na_action_profile_instance_dispose";
+ /*static const gchar *thisfn = "na_action_profile_instance_dispose";*/
NAActionProfile *self;
- g_debug( "%s: object=%p", thisfn, ( void * ) object );
-
+ /*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
g_assert( NA_IS_ACTION_PROFILE( object ));
self = NA_ACTION_PROFILE( object );
@@ -444,18 +410,19 @@ instance_dispose( GObject *object )
self->private->dispose_has_run = TRUE;
/* chain up to the parent class */
- G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ if( G_OBJECT_CLASS( st_parent_class )->dispose ){
+ G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ }
}
}
static void
instance_finalize( GObject *object )
{
- static const gchar *thisfn = "na_action_profile_instance_finalize";
+ /*static const gchar *thisfn = "na_action_profile_instance_finalize";*/
NAActionProfile *self;
- g_debug( "%s: object=%p", thisfn, (void * ) object );
-
+ /*g_debug( "%s: object=%p", thisfn, (void * ) object );*/
g_assert( NA_IS_ACTION_PROFILE( object ));
self = ( NAActionProfile * ) object;
@@ -468,7 +435,7 @@ instance_finalize( GObject *object )
g_free( self->private );
/* chain call to parent class */
- if((( GObjectClass * ) st_parent_class )->finalize ){
+ if( G_OBJECT_CLASS( st_parent_class )->finalize ){
G_OBJECT_CLASS( st_parent_class )->finalize( object );
}
}
@@ -531,13 +498,7 @@ na_action_profile_get_action( const NAActionProfile *profile )
gchar *
na_action_profile_get_name( const NAActionProfile *profile )
{
- gchar *id;
-
- g_assert( NA_IS_ACTION_PROFILE( profile ));
-
- g_object_get( G_OBJECT( profile ), PROP_NAPROFILE_NAME_STR, &id, NULL );
-
- return( id );
+ return( na_object_get_id( NA_OBJECT( profile )));
}
/**
@@ -554,13 +515,7 @@ na_action_profile_get_name( const NAActionProfile *profile )
gchar *
na_action_profile_get_label( const NAActionProfile *profile )
{
- gchar *label;
-
- g_assert( NA_IS_ACTION_PROFILE( profile ));
-
- g_object_get( G_OBJECT( profile ), PROP_NAPROFILE_LABEL_STR, &label, NULL );
-
- return( label );
+ return( na_object_get_label( NA_OBJECT( profile )));
}
/**
@@ -806,9 +761,7 @@ na_action_profile_set_action( NAActionProfile *profile, const NAAction *action )
void
na_action_profile_set_name( NAActionProfile *profile, const gchar *name )
{
- g_assert( NA_IS_ACTION_PROFILE( profile ));
-
- g_object_set( G_OBJECT( profile ), PROP_NAPROFILE_NAME_STR, name, NULL );
+ na_object_set_id( NA_OBJECT( profile ), name );
}
/**
@@ -826,9 +779,7 @@ na_action_profile_set_name( NAActionProfile *profile, const gchar *name )
void
na_action_profile_set_label( NAActionProfile *profile, const gchar *label )
{
- g_assert( NA_IS_ACTION_PROFILE( profile ));
-
- g_object_set( G_OBJECT( profile ), PROP_NAPROFILE_LABEL_STR, label, NULL );
+ na_object_set_label( NA_OBJECT( profile ), label );
}
/**
@@ -1480,51 +1431,36 @@ na_action_profile_parse_parameters( const NAActionProfile *profile, GList* files
return( parsed );
}
-static void
-object_dump( const NAObject *object )
+static int
+validate_schemes( GSList* schemes2test, NautilusFileInfo* file )
{
- static const gchar *thisfn = "na_action_profile_object_dump";
- NAActionProfile *self;
+ int retv = 0;
+ GSList* iter;
+ gboolean found = FALSE;
+ gchar *scheme;
- g_assert( NA_IS_ACTION_PROFILE( object ));
- self = NA_ACTION_PROFILE( object );
+ iter = schemes2test;
+ while (iter && !found)
+ {
+ scheme = nautilus_file_info_get_uri_scheme (file);
- if( st_parent_class->dump ){
- st_parent_class->dump( object );
- }
+ if (g_ascii_strncasecmp (scheme, (gchar*)iter->data, strlen ((gchar*)iter->data)) == 0)
+ {
+ found = TRUE;
+ retv = 1;
+ }
- g_debug( "%s: action=%p", thisfn, ( void * ) self->private->action );
- g_debug( "%s: path='%s'", thisfn, self->private->path );
- g_debug( "%s: parameters='%s'", thisfn, self->private->parameters );
- g_debug( "%s: accept_multiple='%s'", thisfn, self->private->accept_multiple ? "True" : "False" );
- g_debug( "%s: is_dir='%s'", thisfn, self->private->is_dir ? "True" : "False" );
- g_debug( "%s: is_file='%s'", thisfn, self->private->is_file ? "True" : "False" );
- g_debug( "%s: match_case='%s'", thisfn, self->private->match_case ? "True" : "False" );
- object_dump_list( thisfn, "basenames", self->private->basenames );
- object_dump_list( thisfn, "mimetypes", self->private->mimetypes );
- object_dump_list( thisfn, " schemes", self->private->schemes );
-}
+ g_free (scheme);
+ iter = iter->next;
+ }
-static void
-object_dump_list( const gchar *thisfn, const gchar *label, GSList *list )
-{
- gchar *string = na_utils_gslist_to_schema( list );
- g_debug( "%s: %s=%s", thisfn, label, string );
- g_free( string );
+ return retv;
}
static NAObject *
-object_duplicate( const NAObject *profile )
+object_new( const NAObject *profile )
{
- NAObject *duplicate;
-
- g_assert( NA_IS_ACTION_PROFILE( profile ));
-
- duplicate = NA_OBJECT( na_action_profile_new());
-
- na_object_copy( duplicate, profile );
-
- return( duplicate );
+ return( NA_OBJECT( na_action_profile_new()));
}
static void
@@ -1534,6 +1470,10 @@ object_copy( NAObject *target, const NAObject *source )
gboolean matchcase, isfile, isdir, multiple;
GSList *basenames, *mimetypes, *schemes;
+ if( st_parent_class->copy ){
+ st_parent_class->copy( target, source );
+ }
+
g_assert( NA_IS_ACTION_PROFILE( target ));
g_assert( NA_IS_ACTION_PROFILE( source ));
@@ -1566,10 +1506,6 @@ object_copy( NAObject *target, const NAObject *source )
na_utils_free_string_list( basenames );
na_utils_free_string_list( mimetypes );
na_utils_free_string_list( schemes );
-
- if( st_parent_class->copy ){
- st_parent_class->copy( target, source );
- }
}
gboolean
@@ -1577,52 +1513,56 @@ object_are_equal( const NAObject *a, const NAObject *b )
{
NAActionProfile *first = NA_ACTION_PROFILE( a );
NAActionProfile *second = NA_ACTION_PROFILE( b );
- gboolean equal;
+ gboolean equal = TRUE;
+
+ if( equal ){
+ if( st_parent_class->are_equal ){
+ equal = st_parent_class->are_equal( a, b );
+ }
+ }
g_assert( NA_IS_ACTION_PROFILE( a ));
g_assert( NA_IS_ACTION_PROFILE( b ));
- equal =
- ( g_utf8_collate( first->private->path, second->private->path ) == 0 ) &&
- ( g_utf8_collate( first->private->parameters, second->private->parameters ) == 0 );
+ if( equal ){
+ equal =
+ ( g_utf8_collate( first->private->path, second->private->path ) == 0 ) &&
+ ( g_utf8_collate( first->private->parameters, second->private->parameters ) == 0 );
+ }
if( equal ){
equal = (( first->private->accept_multiple && second->private->accept_multiple ) ||
( !first->private->accept_multiple && !second->private->accept_multiple ));
}
+
if( equal ){
equal = (( first->private->is_dir && second->private->is_dir ) ||
( !first->private->is_dir && !second->private->is_dir ));
}
+
if( equal ){
equal = (( first->private->is_file && second->private->is_file ) ||
( !first->private->is_file && !second->private->is_file ));
}
+
if( equal ){
equal = na_utils_string_lists_are_equal( first->private->basenames, second->private->basenames ) &&
na_utils_string_lists_are_equal( first->private->mimetypes, second->private->mimetypes ) &&
na_utils_string_lists_are_equal( first->private->schemes, second->private->schemes );
}
- if( equal ){
- if( st_parent_class->are_equal ){
- equal = st_parent_class->are_equal( a, b );
- }
- }
return( equal );
}
+/*
+ * a valid NAActionProfile requires a not null, not empty label
+ * this is checked here as NAObject doesn't have this condition
+ */
gboolean
object_is_valid( const NAObject *profile )
{
gchar *label;
- gboolean is_valid;
-
- g_assert( NA_IS_ACTION_PROFILE( profile ));
-
- g_object_get( G_OBJECT( profile ), PROP_NAPROFILE_LABEL_STR, &label, NULL );
- is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
- g_free( label );
+ gboolean is_valid = TRUE;
if( is_valid ){
if( st_parent_class->is_valid ){
@@ -1630,31 +1570,62 @@ object_is_valid( const NAObject *profile )
}
}
+ g_assert( NA_IS_ACTION_PROFILE( profile ));
+
+ if( is_valid ){
+ label = na_object_get_label( profile );
+ is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
+ g_free( label );
+ }
+
return( is_valid );
}
-static int
-validate_schemes( GSList* schemes2test, NautilusFileInfo* file )
+static void
+object_dump( const NAObject *object )
{
- int retv = 0;
- GSList* iter;
- gboolean found = FALSE;
- gchar *scheme;
-
- iter = schemes2test;
- while (iter && !found)
- {
- scheme = nautilus_file_info_get_uri_scheme (file);
+ static const gchar *thisfn = "na_action_profile_object_dump";
+ NAActionProfile *self;
- if (g_ascii_strncasecmp (scheme, (gchar*)iter->data, strlen ((gchar*)iter->data)) == 0)
- {
- found = TRUE;
- retv = 1;
- }
+ g_assert( NA_IS_ACTION_PROFILE( object ));
+ self = NA_ACTION_PROFILE( object );
- g_free (scheme);
- iter = iter->next;
+ if( st_parent_class->dump ){
+ st_parent_class->dump( object );
}
- return retv;
+ g_debug( "%s: action=%p", thisfn, ( void * ) self->private->action );
+ g_debug( "%s: path='%s'", thisfn, self->private->path );
+ g_debug( "%s: parameters='%s'", thisfn, self->private->parameters );
+ g_debug( "%s: accept_multiple='%s'", thisfn, self->private->accept_multiple ? "True" : "False" );
+ g_debug( "%s: is_dir='%s'", thisfn, self->private->is_dir ? "True" : "False" );
+ g_debug( "%s: is_file='%s'", thisfn, self->private->is_file ? "True" : "False" );
+ g_debug( "%s: match_case='%s'", thisfn, self->private->match_case ? "True" : "False" );
+ object_dump_list( thisfn, "basenames", self->private->basenames );
+ object_dump_list( thisfn, "mimetypes", self->private->mimetypes );
+ object_dump_list( thisfn, " schemes", self->private->schemes );
+}
+
+static void
+object_dump_list( const gchar *thisfn, const gchar *label, GSList *list )
+{
+ gchar *string = na_utils_gslist_to_schema( list );
+ g_debug( "%s: %s=%s", thisfn, label, string );
+ g_free( string );
+}
+
+static gchar *
+object_get_clipboard_id( const NAObject *profile )
+{
+ gchar *uuid;
+ gchar *name;
+ gchar *clipboard_id;
+
+ uuid = na_action_get_uuid( NA_ACTION_PROFILE( profile )->private->action );
+ name = na_object_get_id( profile );
+ clipboard_id = g_strdup_printf( "P:%s/%s", uuid, name );
+ g_free( uuid );
+ g_free( name );
+
+ return( clipboard_id );
}
diff --git a/src/common/na-action.c b/src/common/na-action.c
index 572a9e4..6538007 100644
--- a/src/common/na-action.c
+++ b/src/common/na-action.c
@@ -53,8 +53,6 @@ struct NAActionPrivate {
/* action properties
*/
gchar *version;
- gchar *tooltip;
- gchar *icon;
gboolean enabled;
/* list of action's profiles as NAActionProfile objects
@@ -77,21 +75,13 @@ struct NAActionPrivate {
/* action properties
*/
enum {
- PROP_NAACTION_UUID = 1,
- PROP_NAACTION_LABEL,
- PROP_NAACTION_VERSION,
- PROP_NAACTION_TOOLTIP,
- PROP_NAACTION_ICON,
+ PROP_NAACTION_VERSION = 1,
PROP_NAACTION_ENABLED,
PROP_NAACTION_READONLY,
PROP_NAACTION_PROVIDER
};
-#define PROP_NAACTION_UUID_STR "na-action-uuid"
-#define PROP_NAACTION_LABEL_STR "na-action-label"
#define PROP_NAACTION_VERSION_STR "na-action-version"
-#define PROP_NAACTION_TOOLTIP_STR "na-action-tooltip"
-#define PROP_NAACTION_ICON_STR "na-action-icon"
#define PROP_NAACTION_ENABLED_STR "na-action-enabled"
#define PROP_NAACTION_READONLY_STR "na-action-read-only"
#define PROP_NAACTION_PROVIDER_STR "na-action-provider"
@@ -108,12 +98,12 @@ static void instance_set_property( GObject *object, guint property_id, cons
static void instance_dispose( GObject *object );
static void instance_finalize( GObject *object );
-static void object_check_edited_status( const NAObject *action );
-static void object_dump( const NAObject *action );
-static NAObject *object_duplicate( const NAObject *action );
+static NAObject *object_new( const NAObject *object );
static void object_copy( NAObject *target, const NAObject *source );
static gboolean object_are_equal( const NAObject *a, const NAObject *b );
static gboolean object_is_valid( const NAObject *action );
+static void object_dump( const NAObject *action );
+static gchar *object_get_clipboard_id( const NAObject *object );
static void free_profiles( NAAction *action );
@@ -148,7 +138,7 @@ register_type( void )
g_debug( "%s", thisfn );
- return( g_type_register_static( NA_OBJECT_TYPE, "NAAction", &info, 0 ));
+ return( g_type_register_static( NA_OBJECT_ITEM_TYPE, "NAAction", &info, 0 ));
}
static void
@@ -169,40 +159,12 @@ class_init( NAActionClass *klass )
object_class->get_property = instance_get_property;
spec = g_param_spec_string(
- PROP_NAACTION_UUID_STR,
- "Action UUID",
- "Globally unique identifier (UUID) of the action", "",
- G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
- g_object_class_install_property( object_class, PROP_NAACTION_UUID, spec );
-
- spec = g_param_spec_string(
- PROP_NAACTION_LABEL_STR,
- "Action label",
- "Context menu displayable label", "",
- G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
- g_object_class_install_property( object_class, PROP_NAACTION_LABEL, spec );
-
- spec = g_param_spec_string(
PROP_NAACTION_VERSION_STR,
"Version",
"Version of the schema", "",
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
g_object_class_install_property( object_class, PROP_NAACTION_VERSION, spec );
- spec = g_param_spec_string(
- PROP_NAACTION_TOOLTIP_STR,
- "Action tooltip",
- "Context menu tooltip", "",
- G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
- g_object_class_install_property( object_class, PROP_NAACTION_TOOLTIP, spec );
-
- spec = g_param_spec_string(
- PROP_NAACTION_ICON_STR,
- "Icon name",
- "Context menu displayable icon", "",
- G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
- g_object_class_install_property( object_class, PROP_NAACTION_ICON, spec );
-
spec = g_param_spec_boolean(
PROP_NAACTION_ENABLED_STR,
"Enabled",
@@ -226,22 +188,21 @@ class_init( NAActionClass *klass )
klass->private = g_new0( NAActionClassPrivate, 1 );
- NA_OBJECT_CLASS( klass )->dump = object_dump;
- NA_OBJECT_CLASS( klass )->check_edited_status = object_check_edited_status;
- NA_OBJECT_CLASS( klass )->duplicate = object_duplicate;
+ NA_OBJECT_CLASS( klass )->new = object_new;
NA_OBJECT_CLASS( klass )->copy = object_copy;
NA_OBJECT_CLASS( klass )->are_equal = object_are_equal;
NA_OBJECT_CLASS( klass )->is_valid = object_is_valid;
+ NA_OBJECT_CLASS( klass )->dump = object_dump;
+ NA_OBJECT_CLASS( klass )->get_clipboard_id = object_get_clipboard_id;
}
static void
instance_init( GTypeInstance *instance, gpointer klass )
{
- /*static const gchar *thisfn = "na_action_instance_init";
- g_debug( "%s: instance=%p, klass=%p", thisfn, instance, klass );*/
-
+ /*static const gchar *thisfn = "na_action_instance_init";*/
NAAction *self;
+ /*g_debug( "%s: instance=%p, klass=%p", thisfn, ( void * ) instance, ( void * ) klass );*/
g_assert( NA_IS_ACTION( instance ));
self = NA_ACTION( instance );
@@ -252,8 +213,6 @@ instance_init( GTypeInstance *instance, gpointer klass )
/* initialize suitable default values
*/
self->private->version = g_strdup( NA_ACTION_LATEST_VERSION );
- self->private->tooltip = g_strdup( "" );
- self->private->icon = g_strdup( "" );
self->private->enabled = TRUE;
self->private->read_only = FALSE;
self->private->provider = NULL;
@@ -268,26 +227,10 @@ instance_get_property( GObject *object, guint property_id, GValue *value, GParam
self = NA_ACTION( object );
switch( property_id ){
- case PROP_NAACTION_UUID:
- G_OBJECT_CLASS( st_parent_class )->get_property( object, PROP_NAOBJECT_ID, value, spec );
- break;
-
- case PROP_NAACTION_LABEL:
- G_OBJECT_CLASS( st_parent_class )->get_property( object, PROP_NAOBJECT_LABEL, value, spec );
- break;
-
case PROP_NAACTION_VERSION:
g_value_set_string( value, self->private->version );
break;
- case PROP_NAACTION_TOOLTIP:
- g_value_set_string( value, self->private->tooltip );
- break;
-
- case PROP_NAACTION_ICON:
- g_value_set_string( value, self->private->icon );
- break;
-
case PROP_NAACTION_ENABLED:
g_value_set_boolean( value, self->private->enabled );
break;
@@ -315,29 +258,11 @@ instance_set_property( GObject *object, guint property_id, const GValue *value,
self = NA_ACTION( object );
switch( property_id ){
- case PROP_NAACTION_UUID:
- G_OBJECT_CLASS( st_parent_class )->set_property( object, PROP_NAOBJECT_ID, value, spec );
- break;
-
- case PROP_NAACTION_LABEL:
- G_OBJECT_CLASS( st_parent_class )->set_property( object, PROP_NAOBJECT_LABEL, value, spec );
- break;
-
case PROP_NAACTION_VERSION:
g_free( self->private->version );
self->private->version = g_value_dup_string( value );
break;
- case PROP_NAACTION_TOOLTIP:
- g_free( self->private->tooltip );
- self->private->tooltip = g_value_dup_string( value );
- break;
-
- case PROP_NAACTION_ICON:
- g_free( self->private->icon );
- self->private->icon = g_value_dup_string( value );
- break;
-
case PROP_NAACTION_ENABLED:
self->private->enabled = g_value_get_boolean( value );
break;
@@ -359,10 +284,10 @@ instance_set_property( GObject *object, guint property_id, const GValue *value,
static void
instance_dispose( GObject *object )
{
- static const gchar *thisfn = "na_action_instance_dispose";
+ /*static const gchar *thisfn = "na_action_instance_dispose";*/
NAAction *self;
- g_debug( "%s: object=%p", thisfn, ( void * ) object );
+ /*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
g_assert( NA_IS_ACTION( object ));
self = NA_ACTION( object );
@@ -375,29 +300,29 @@ instance_dispose( GObject *object )
free_profiles( self );
/* chain up to the parent class */
- G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ if( G_OBJECT_CLASS( st_parent_class )->dispose ){
+ G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ }
}
}
static void
instance_finalize( GObject *object )
{
- static const gchar *thisfn = "na_action_instance_finalize";
+ /*static const gchar *thisfn = "na_action_instance_finalize";*/
NAAction *self;
- g_debug( "%s: object=%p", thisfn, ( void * ) object );
+ /*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
g_assert( NA_IS_ACTION( object ));
self = ( NAAction * ) object;
g_free( self->private->version );
- g_free( self->private->tooltip );
- g_free( self->private->icon );
g_free( self->private );
/* chain call to parent class */
- if((( GObjectClass * ) st_parent_class )->finalize ){
+ if( G_OBJECT_CLASS( st_parent_class )->finalize ){
G_OBJECT_CLASS( st_parent_class )->finalize( object );
}
}
@@ -463,13 +388,7 @@ na_action_new_with_profile( void )
gchar *
na_action_get_uuid( const NAAction *action )
{
- gchar *id;
-
- g_assert( NA_IS_ACTION( action ));
-
- g_object_get( G_OBJECT( action ), PROP_NAACTION_UUID_STR, &id, NULL );
-
- return( id );
+ return( na_object_get_id( NA_OBJECT( action )));
}
/**
@@ -486,13 +405,7 @@ na_action_get_uuid( const NAAction *action )
gchar *
na_action_get_label( const NAAction *action )
{
- gchar *label;
-
- g_assert( NA_IS_ACTION( action ));
-
- g_object_get( G_OBJECT( action ), PROP_NAACTION_LABEL_STR, &label, NULL );
-
- return( label );
+ return( na_object_get_label( NA_OBJECT( action )));
}
/**
@@ -532,13 +445,7 @@ na_action_get_version( const NAAction *action )
gchar *
na_action_get_tooltip( const NAAction *action )
{
- gchar *tooltip;
-
- g_assert( NA_IS_ACTION( action ));
-
- g_object_get( G_OBJECT( action ), PROP_NAACTION_TOOLTIP_STR, &tooltip, NULL );
-
- return( tooltip );
+ return( na_object_item_get_tooltip( NA_OBJECT_ITEM( action )));
}
/**
@@ -554,13 +461,7 @@ na_action_get_tooltip( const NAAction *action )
gchar *
na_action_get_icon( const NAAction *action )
{
- gchar *icon;
-
- g_assert( NA_IS_ACTION( action ));
-
- g_object_get( G_OBJECT( action ), PROP_NAACTION_ICON_STR, &icon, NULL );
-
- return( icon );
+ return( na_object_item_get_icon( NA_OBJECT_ITEM( action )));
}
/*
@@ -569,23 +470,7 @@ na_action_get_icon( const NAAction *action )
gchar *
na_action_get_verified_icon_name( const NAAction *action )
{
- gchar *icon_name;
-
- g_assert( NA_IS_ACTION( action ));
-
- g_object_get( G_OBJECT( action ), PROP_NAACTION_ICON_STR, &icon_name, NULL );
-
- if( icon_name[0] == '/' ){
- if( !g_file_test( icon_name, G_FILE_TEST_IS_REGULAR )){
- g_free( icon_name );
- return NULL;
- }
- } else if( strlen( icon_name ) == 0 ){
- g_free( icon_name );
- return NULL;
- }
-
- return( icon_name );
+ return( na_object_item_get_verified_icon_name( NA_OBJECT_ITEM( action )));
}
/**
@@ -673,7 +558,7 @@ na_action_set_new_uuid( NAAction *action )
uuid_generate( uuid );
uuid_unparse_lower( uuid, uuid_str );
- g_object_set( G_OBJECT( action ), PROP_NAACTION_UUID_STR, uuid_str, NULL );
+ na_object_set_id( NA_OBJECT( action ), uuid_str );
}
/**
@@ -700,9 +585,7 @@ na_action_set_new_uuid( NAAction *action )
void
na_action_set_uuid( NAAction *action, const gchar *uuid )
{
- g_assert( NA_IS_ACTION( action ));
-
- g_object_set( G_OBJECT( action ), PROP_NAACTION_UUID_STR, uuid, NULL );
+ na_object_set_id( NA_OBJECT( action ), uuid );
}
/**
@@ -722,9 +605,7 @@ na_action_set_uuid( NAAction *action, const gchar *uuid )
void
na_action_set_label( NAAction *action, const gchar *label )
{
- g_assert( NA_IS_ACTION( action ));
-
- g_object_set( G_OBJECT( action ), PROP_NAACTION_LABEL_STR, label, NULL );
+ na_object_set_label( NA_OBJECT( action ), label );
}
/**
@@ -768,9 +649,7 @@ na_action_set_version( NAAction *action, const gchar *version )
void
na_action_set_tooltip( NAAction *action, const gchar *tooltip )
{
- g_assert( NA_IS_ACTION( action ));
-
- g_object_set( G_OBJECT( action ), PROP_NAACTION_TOOLTIP_STR, tooltip, NULL );
+ na_object_item_set_tooltip( NA_OBJECT_ITEM( action ), tooltip );
}
/**
@@ -786,9 +665,7 @@ na_action_set_tooltip( NAAction *action, const gchar *tooltip )
void
na_action_set_icon( NAAction *action, const gchar *icon )
{
- g_assert( NA_IS_ACTION( action ));
-
- g_object_set( G_OBJECT( action ), PROP_NAACTION_ICON_STR, icon, NULL );
+ na_object_item_set_icon( NA_OBJECT_ITEM( action ), icon );
}
/**
@@ -1024,78 +901,30 @@ na_action_get_profiles_count( const NAAction *action )
return( g_slist_length( action->private->profiles ));
}
-static void
-object_check_edited_status( const NAObject *action )
-{
- GSList *ip;
-
- if( st_parent_class->check_edited_status ){
- st_parent_class->check_edited_status( action );
- }
-
- for( ip = NA_ACTION( action )->private->profiles ; ip ; ip = ip->next ){
- na_object_check_edited_status( NA_OBJECT( ip->data ));
- }
-}
-
-static void
-object_dump( const NAObject *action )
-{
- static const gchar *thisfn = "na_action_object_dump";
- NAAction *self;
- GSList *item;
-
- g_assert( NA_IS_ACTION( action ));
- self = NA_ACTION( action );
-
- if( st_parent_class->dump ){
- st_parent_class->dump( action );
- }
-
- g_debug( "%s: version='%s'", thisfn, self->private->version );
- g_debug( "%s: tooltip='%s'", thisfn, self->private->tooltip );
- g_debug( "%s: icon='%s'", thisfn, self->private->icon );
- g_debug( "%s: enabled='%s'", thisfn, self->private->enabled ? "True" : "False" );
- g_debug( "%s: read-only='%s'", thisfn, self->private->read_only ? "True" : "False" );
- g_debug( "%s: provider=%p", thisfn, ( void * ) self->private->provider );
-
- /* dump profiles */
- g_debug( "%s: %d profile(s) at %p", thisfn, na_action_get_profiles_count( self ), ( void * ) self->private->profiles );
- for( item = self->private->profiles ; item != NULL ; item = item->next ){
- na_object_dump(( const NAObject * ) item->data );
- }
-}
-
static NAObject *
-object_duplicate( const NAObject *action )
+object_new( const NAObject *action )
{
- NAObject *duplicate;
-
- g_assert( NA_IS_ACTION( action ));
-
- duplicate = NA_OBJECT( na_action_new());
-
- na_object_copy( duplicate, action );
-
- return( duplicate );
+ return( NA_OBJECT( na_action_new()));
}
void
object_copy( NAObject *target, const NAObject *source )
{
- gchar *version, *tooltip, *icon;
+ gchar *version;
gboolean enabled, readonly;
gpointer provider;
GSList *ip;
NAActionProfile *profile;
+ if( st_parent_class->copy ){
+ st_parent_class->copy( target, source );
+ }
+
g_assert( NA_IS_ACTION( source ));
g_assert( NA_IS_ACTION( target ));
g_object_get( G_OBJECT( source ),
PROP_NAACTION_VERSION_STR, &version,
- PROP_NAACTION_TOOLTIP_STR, &tooltip,
- PROP_NAACTION_ICON_STR, &icon,
PROP_NAACTION_ENABLED_STR, &enabled,
PROP_NAACTION_READONLY_STR, &readonly,
PROP_NAACTION_PROVIDER_STR, &provider,
@@ -1103,50 +932,52 @@ object_copy( NAObject *target, const NAObject *source )
g_object_set( G_OBJECT( target ),
PROP_NAACTION_VERSION_STR, version,
- PROP_NAACTION_TOOLTIP_STR, tooltip,
- PROP_NAACTION_ICON_STR, icon,
PROP_NAACTION_ENABLED_STR, enabled,
PROP_NAACTION_READONLY_STR, readonly,
PROP_NAACTION_PROVIDER_STR, provider,
NULL );
- g_free( tooltip );
g_free( version );
for( ip = NA_ACTION( source )->private->profiles ; ip ; ip = ip->next ){
profile = NA_ACTION_PROFILE( na_object_duplicate( NA_OBJECT( ip->data )));
na_action_attach_profile( NA_ACTION( target ), profile );
}
-
- if( st_parent_class->copy ){
- st_parent_class->copy( target, source );
- }
}
static gboolean
object_are_equal( const NAObject *a, const NAObject *b )
{
- NAAction *first = NA_ACTION( a );
- NAAction *second = NA_ACTION( b );
- gboolean equal;
+ NAAction *first, *second;
+ gboolean equal = TRUE;
NAActionProfile *first_profile, *second_profile;
gchar *first_name, *second_name;
+ if( equal ){
+ if( st_parent_class->are_equal ){
+ equal = st_parent_class->are_equal( a, b );
+ }
+ }
+
g_assert( NA_IS_ACTION( a ));
+ first = NA_ACTION( a );
+
g_assert( NA_IS_ACTION( b ));
+ second = NA_ACTION( b );
- equal =
- ( g_utf8_collate( first->private->version, second->private->version ) == 0 ) &&
- ( g_utf8_collate( first->private->tooltip, second->private->tooltip ) == 0 ) &&
- ( g_utf8_collate( first->private->icon, second->private->icon ) == 0 );
+ if( equal ){
+ equal = ( g_utf8_collate( first->private->version, second->private->version ) == 0 );
+ }
if( equal ){
equal = ( first->private->enabled && second->private->enabled ) ||
( !first->private->enabled && !second->private->enabled );
}
+
if( equal ){
equal = ( g_slist_length( first->private->profiles ) == g_slist_length( second->private->profiles ));
}
+
if( equal ){
GSList *ip;
for( ip = first->private->profiles ; ip && equal ; ip = ip->next ){
@@ -1161,6 +992,7 @@ object_are_equal( const NAObject *a, const NAObject *b )
g_free( first_name );
}
}
+
if( equal ){
GSList *ip;
for( ip = second->private->profiles ; ip && equal ; ip = ip->next ){
@@ -1175,39 +1007,66 @@ object_are_equal( const NAObject *a, const NAObject *b )
g_free( second_name );
}
}
- if( equal ){
- if( st_parent_class->are_equal ){
- equal = st_parent_class->are_equal( a, b );
- }
- }
return( equal );
}
+/*
+ * a valid NAAction requires a not null, not empty label
+ * this is checked here as NAObject doesn't have this condition
+ */
gboolean
object_is_valid( const NAObject *action )
{
gchar *label;
- gboolean is_valid;
+ gboolean is_valid = TRUE;
GSList *ip;
+ if( is_valid ){
+ if( st_parent_class->is_valid ){
+ is_valid = st_parent_class->is_valid( action );
+ }
+ }
+
g_assert( NA_IS_ACTION( action ));
- g_object_get( G_OBJECT( action ), PROP_NAACTION_LABEL_STR, &label, NULL );
- is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
- g_free( label );
+ if( is_valid ){
+ label = na_action_get_label( NA_ACTION( action ));
+ is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
+ g_free( label );
+ }
for( ip = NA_ACTION( action )->private->profiles ; ip && is_valid ; ip = ip->next ){
is_valid = na_object_is_valid( NA_OBJECT( ip->data ));
}
- if( is_valid ){
- if( st_parent_class->is_valid ){
- is_valid = st_parent_class->is_valid( action );
- }
+ return( is_valid );
+}
+
+static void
+object_dump( const NAObject *action )
+{
+ static const gchar *thisfn = "na_action_object_dump";
+ NAAction *self;
+ GSList *item;
+
+ if( st_parent_class->dump ){
+ st_parent_class->dump( action );
}
- return( is_valid );
+ g_assert( NA_IS_ACTION( action ));
+ self = NA_ACTION( action );
+
+ g_debug( "%s: version='%s'", thisfn, self->private->version );
+ g_debug( "%s: enabled='%s'", thisfn, self->private->enabled ? "True" : "False" );
+ g_debug( "%s: read-only='%s'", thisfn, self->private->read_only ? "True" : "False" );
+ g_debug( "%s: provider=%p", thisfn, ( void * ) self->private->provider );
+
+ /* dump profiles */
+ g_debug( "%s: %d profile(s) at %p", thisfn, na_action_get_profiles_count( self ), ( void * ) self->private->profiles );
+ for( item = self->private->profiles ; item != NULL ; item = item->next ){
+ na_object_dump(( const NAObject * ) item->data );
+ }
}
static void
@@ -1217,3 +1076,16 @@ free_profiles( NAAction *action )
action->private->profiles = NULL;
}
+
+static gchar *
+object_get_clipboard_id( const NAObject *action )
+{
+ gchar *uuid;
+ gchar *clipboard_id;
+
+ uuid = na_object_get_id( action );
+ clipboard_id = g_strdup_printf( "A:%s", uuid );
+ g_free( uuid );
+
+ return( clipboard_id );
+}
diff --git a/src/common/na-action.h b/src/common/na-action.h
index c9a80a8..9b0f525 100644
--- a/src/common/na-action.h
+++ b/src/common/na-action.h
@@ -48,7 +48,10 @@
G_BEGIN_DECLS
+/* i18n: default label for a newly created action */
#define NA_ACTION_DEFAULT_LABEL _( "New Nautilus action" )
+
+/* i18n: default label for a newly created profile */
#define NA_ACTION_PROFILE_DEFAULT_LABEL _( "Default profile" )
NAAction *na_action_new( void );
@@ -69,7 +72,7 @@ void na_action_set_uuid( NAAction *action, const gchar *uuid );
void na_action_set_label( NAAction *action, const gchar *label );
void na_action_set_version( NAAction *action, const gchar *version );
void na_action_set_tooltip( NAAction *action, const gchar *tooltip );
-void na_action_set_icon( NAAction *action, const gchar *icon_name );
+void na_action_set_icon( NAAction *action, const gchar *icon );
void na_action_set_enabled( NAAction *action, gboolean enabled );
void na_action_set_readonly( NAAction *action, gboolean readonly );
void na_action_set_provider( NAAction *action, const NAIIOProvider *provider );
diff --git a/src/common/na-gconf.c b/src/common/na-gconf.c
index 80656b1..42a5b0c 100644
--- a/src/common/na-gconf.c
+++ b/src/common/na-gconf.c
@@ -41,6 +41,7 @@
#include "na-gconf.h"
#include "na-gconf-keys.h"
#include "na-iio-provider.h"
+#include "na-iprefs.h"
#include "na-utils.h"
/* private class data
@@ -115,7 +116,7 @@ static guint install_gconf_watch( NAGConf *gconf );
static void install_gconf_watched_dir( NAGConf *gconf );
static void remove_gconf_watch( NAGConf *gconf );
static void remove_gconf_watched_dir( NAGConf *gconf );
-static void action_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data );
+static void gconf_dir_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data );
GType
na_gconf_get_type( void )
@@ -1115,7 +1116,7 @@ install_gconf_watch( NAGConf *gconf )
gconf_client_notify_add(
gconf->private->gconf,
NA_GCONF_CONFIG_PATH,
- ( GConfClientNotifyFunc ) action_changed_cb,
+ ( GConfClientNotifyFunc ) gconf_dir_changed_cb,
gconf,
NULL,
&error
@@ -1140,7 +1141,15 @@ install_gconf_watched_dir( NAGConf *gconf )
gconf->private->gconf, NA_GCONF_CONFIG_PATH, GCONF_CLIENT_PRELOAD_RECURSIVE, &error );
if( error ){
- g_warning( "%s: error=%s", thisfn, error->message );
+ g_warning( "%s: path=%s, error=%s", thisfn, NA_GCONF_CONFIG_PATH, error->message );
+ g_error_free( error );
+ }
+
+ gconf_client_add_dir(
+ gconf->private->gconf, NA_GCONF_PREFS_PATH, GCONF_CLIENT_PRELOAD_ONELEVEL, &error );
+
+ if( error ){
+ g_warning( "%s: path=%s, error=%s", thisfn, NA_GCONF_PREFS_PATH, error->message );
g_error_free( error );
}
}
@@ -1188,20 +1197,41 @@ remove_gconf_watched_dir( NAGConf *gconf )
* if the modification is made elsewhere (an action is imported as a
* xml file in gconf, or gconf is directly edited), we'd have to rely
* only on the standard watch mechanism
+ *
+ * Please note that this handler is also called when a preference is
+ * changed, in order to advertise Nautilus when the display mode of
+ * actions is modified. So we have to check the entry to see if a
+ * message is needed to be sent.
*/
static void
-action_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data )
+gconf_dir_changed_cb( GConfClient *client, guint cnxn_id, GConfEntry *entry, gpointer user_data )
{
/*static const gchar *thisfn = "action_changed_cb";*/
/*g_debug( "%s: client=%p, cnxnid=%u, entry=%p, user_data=%p", thisfn, client, cnxn_id, entry, user_data );*/
NAGConf *gconf;
NAPivotNotify *npn;
+ const char *key;
g_assert( NA_IS_GCONF( user_data ));
gconf = NA_GCONF( user_data );
g_assert( NA_IS_IIO_PROVIDER( gconf ));
- npn = entry_to_notify( entry );
- g_signal_emit_by_name( gconf->private->notified, NA_IIO_PROVIDER_SIGNAL_ACTION_CHANGED, npn );
+ key = gconf_entry_get_key( entry );
+
+ if( !strncmp( key, NA_GCONF_CONFIG_PATH, strlen( NA_GCONF_CONFIG_PATH ))){
+ npn = entry_to_notify( entry );
+ g_signal_emit_by_name( gconf->private->notified, NA_IIO_PROVIDER_SIGNAL_ACTION_CHANGED, npn );
+ return;
+ }
+
+ if( strstr( key, PREFS_DISPLAY_ALPHABETICAL_ORDER ) != NULL ){
+ g_signal_emit_by_name( gconf->private->notified, NA_IIO_PROVIDER_SIGNAL_DISPLAY_ORDER_CHANGED, NULL );
+ return;
+ }
+
+ if( strstr( key, PREFS_ADD_ABOUT_ITEM ) != NULL ){
+ g_signal_emit_by_name( gconf->private->notified, NA_IIO_PROVIDER_SIGNAL_DISPLAY_ABOUT_CHANGED, NULL );
+ return;
+ }
}
diff --git a/src/common/na-iduplicable.c b/src/common/na-iduplicable.c
index 3ba412e..d443940 100644
--- a/src/common/na-iduplicable.c
+++ b/src/common/na-iduplicable.c
@@ -40,26 +40,27 @@ struct NAIDuplicableInterfacePrivate {
void *empty; /* so that gcc -pedantic is happy */
};
-/* data set against GObject
+/* data set against NAIDuplicable-implementated instance
*/
#define PROP_IDUPLICABLE_ORIGIN "na-iduplicable-origin"
#define PROP_IDUPLICABLE_ISMODIFIED "na-iduplicable-is-modified"
#define PROP_IDUPLICABLE_ISVALID "na-iduplicable-is-valid"
-static GType register_type( void );
-static void interface_base_init( NAIDuplicableInterface *klass );
-static void interface_base_finalize( NAIDuplicableInterface *klass );
+static GType register_type( void );
+static void interface_base_init( NAIDuplicableInterface *klass );
+static void interface_base_finalize( NAIDuplicableInterface *klass );
-static NAObject *v_duplicate( const NAObject *object );
-static gboolean v_are_equal( const NAObject *a, const NAObject *b );
-static gboolean v_is_valid( const NAObject *object );
+static void v_copy( NAIDuplicable *target, const NAIDuplicable *source );
+static NAIDuplicable *v_new( const NAIDuplicable *object );
+static gboolean v_are_equal( const NAIDuplicable *a, const NAIDuplicable *b );
+static gboolean v_is_valid( const NAIDuplicable *object );
-static NAObject *get_origin( const NAObject *object );
-static void set_origin( const NAObject *object, const NAObject *origin );
-static gboolean get_modified( const NAObject *object );
-static void set_modified( const NAObject *object, gboolean is_modified );
-static gboolean get_valid( const NAObject *object );
-static void set_valid( const NAObject *object, gboolean is_valid );
+static NAIDuplicable *get_origin( const NAIDuplicable *object );
+static void set_origin( const NAIDuplicable *object, const NAIDuplicable *origin );
+static gboolean get_modified( const NAIDuplicable *object );
+static void set_modified( const NAIDuplicable *object, gboolean is_modified );
+static gboolean get_valid( const NAIDuplicable *object );
+static void set_valid( const NAIDuplicable *object, gboolean is_valid );
GType
na_iduplicable_get_type( void )
@@ -132,17 +133,17 @@ interface_base_finalize( NAIDuplicableInterface *klass )
/**
* na_iduplicable_init:
- * @object: the #NAObject object to be initialized.
+ * @object: the #NAIDuplicable object to be initialized.
*
* Initializes the properties of a IDuplicable object.
*
- * This function should be called when creating the object, e.g. from
- * instance_init().
+ * This function should be called by the implementor when creating the
+ * object, e.g. from instance_init().
*/
void
-na_iduplicable_init( NAObject *object )
+na_iduplicable_init( NAIDuplicable *object )
{
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
set_origin( object, NULL );
@@ -152,20 +153,23 @@ na_iduplicable_init( NAObject *object )
/**
* na_iduplicable_dump:
- * @object: the #NAObject object to be duplicated.
+ * @object: the #NAIDuplicable object to be dumped.
*
* Dumps via g_debug the properties of the object.
+ *
+ * We ouput here only the data we set ourselves againt the
+ * #NAIDuplicable-implemented object.
*/
void
-na_iduplicable_dump( const NAObject *object )
+na_iduplicable_dump( const NAIDuplicable *object )
{
static const gchar *thisfn = "na_iduplicable_dump";
- NAObject *origin = NULL;
+ NAIDuplicable *origin = NULL;
gboolean modified = FALSE;
- gboolean valid = TRUE;
+ gboolean valid = FALSE; /* may a NULL object be valid ? */
if( object ){
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
origin = get_origin( object );
@@ -180,9 +184,9 @@ na_iduplicable_dump( const NAObject *object )
/**
* na_iduplicable_duplicate:
- * @object: the #NAObject object to be duplicated.
+ * @object: the #NAIDuplicable object to be duplicated.
*
- * Exactly duplicates a #NAObject-derived object.
+ * Exactly duplicates a #NAIDuplicable-implemented object.
* Properties %PROP_IDUPLICABLE_ORIGIN, %PROP_IDUPLICABLE_ISMODIFIED
* and %PROP_IDUPLICABLE_ISVALID are initialized to their default
* values.
@@ -190,25 +194,28 @@ na_iduplicable_dump( const NAObject *object )
* As %PROP_IDUPLICABLE_ISVALID property is set to %TRUE without any
* further check, this suppose that only valid objects are duplicated.
*
- * Returns: a new #NAObject.
+ * Returns: a new #NAIDuplicable.
*/
-NAObject *
-na_iduplicable_duplicate( const NAObject *object )
+NAIDuplicable *
+na_iduplicable_duplicate( const NAIDuplicable *object )
{
static const gchar *thisfn = "na_iduplicable_duplicate";
- NAObject *dup = NULL;
+ NAIDuplicable *dup = NULL;
g_debug( "%s: object=%p", thisfn, ( void * ) object );
if( object ){
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
- dup = v_duplicate( object );
+ dup = v_new( object );
- set_origin( dup, object );
- set_modified( dup, FALSE );
- set_valid( dup, TRUE );
+ if( dup ){
+ v_copy( dup, object );
+ set_origin( dup, object );
+ set_modified( dup, FALSE );
+ set_valid( dup, TRUE );
+ }
}
return( dup );
@@ -216,10 +223,10 @@ na_iduplicable_duplicate( const NAObject *object )
/**
* na_iduplicable_check_edited_status:
- * @object: the #NAObject object to be checked.
+ * @object: the #NAIDuplicable object to be checked.
*
- * Checks the edition status of the #NAObject object, and set up the
- * corresponding %PROP_IDUPLICABLE_ISMODIFIED and
+ * Checks the edition status of the #NAIDuplicable object, and set up
+ * the corresponding %PROP_IDUPLICABLE_ISMODIFIED and
* %PROP_IDUPLICABLE_ISVALID properties.
*
* This function is supposed to be called each time the object may have
@@ -228,16 +235,16 @@ na_iduplicable_duplicate( const NAObject *object )
* then only return the current value of the properties.
*/
void
-na_iduplicable_check_edited_status( const NAObject *object )
+na_iduplicable_check_edited_status( const NAIDuplicable *object )
{
/*static const gchar *thisfn = "na_iduplicable_check_edited_status";
g_debug( "%s: object=%p", thisfn, object );*/
gboolean modified = TRUE;
- NAObject *origin;
+ NAIDuplicable *origin;
gboolean valid;
if( object ){
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
origin = get_origin( object );
@@ -253,7 +260,7 @@ na_iduplicable_check_edited_status( const NAObject *object )
/**
* na_iduplicable_is_modified:
- * @object: the #NAObject object whose status is to be returned.
+ * @object: the #NAIDuplicable object whose status is to be returned.
*
* Returns the current value of the %PROP_IDUPLICABLE_ISMODIFIED
* property without rechecking the edition status itself.
@@ -262,14 +269,14 @@ na_iduplicable_check_edited_status( const NAObject *object )
* the original one.
*/
gboolean
-na_iduplicable_is_modified( const NAObject *object )
+na_iduplicable_is_modified( const NAIDuplicable *object )
{
/*static const gchar *thisfn = "na_iduplicable_is_modified";
g_debug( "%s: object=%p", thisfn, object );*/
gboolean is_modified = FALSE;
if( object ){
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
is_modified = get_modified( object );
@@ -280,7 +287,7 @@ na_iduplicable_is_modified( const NAObject *object )
/**
* na_iduplicable_is_valid:
- * @object: the #NAObject object whose status is to be returned.
+ * @object: the #NAIDuplicable object whose status is to be returned.
*
* Returns the current value of the %PROP_IDUPLICABLE_ISVALID property
* without rechecking the edition status itself.
@@ -288,14 +295,14 @@ na_iduplicable_is_modified( const NAObject *object )
* Returns: %TRUE is the provided object is valid.
*/
gboolean
-na_iduplicable_is_valid( const NAObject *object )
+na_iduplicable_is_valid( const NAIDuplicable *object )
{
/*static const gchar *thisfn = "na_iduplicable_is_valid";
g_debug( "%s: object=%p", thisfn, object );*/
gboolean is_valid = FALSE;
if( object ){
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
is_valid = get_valid( object );
@@ -306,21 +313,21 @@ na_iduplicable_is_valid( const NAObject *object )
/**
* na_iduplicable_get_origin:
- * @object: the #NAObject object whose origin is to be returned.
+ * @object: the #NAIDuplicable object whose origin is to be returned.
*
- * Returns the origin of a duplicated #NAObject.
+ * Returns the origin of a duplicated #NAIDuplicable.
*
- * Returns: the original #NAObject, or NULL.
+ * Returns: the original #NAIDuplicable, or NULL.
*/
-NAObject *
-na_iduplicable_get_origin( const NAObject *object )
+NAIDuplicable *
+na_iduplicable_get_origin( const NAIDuplicable *object )
{
/*static const gchar *thisfn = "na_iduplicable_is_valid";
g_debug( "%s: object=%p", thisfn, object );*/
- NAObject *origin = NULL;
+ NAIDuplicable *origin = NULL;
if( object ){
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
origin = get_origin( object );
@@ -331,93 +338,95 @@ na_iduplicable_get_origin( const NAObject *object )
/**
* na_iduplicable_set_origin:
- * @object: the #NAObject object whose origin is to be returned.
- * @origin: the new original #NAObject.
+ * @object: the #NAIDuplicable object whose origin is to be returned.
+ * @origin: the new original #NAIDuplicable.
*
- * Sets the new origin of a duplicated #NAObject.
+ * Sets the new origin of a duplicated #NAIDuplicable.
*/
void
-na_iduplicable_set_origin( NAObject *object, const NAObject *origin )
+na_iduplicable_set_origin( NAIDuplicable *object, const NAIDuplicable *origin )
{
/*static const gchar *thisfn = "na_iduplicable_is_valid";
g_debug( "%s: object=%p", thisfn, object );*/
if( object ){
- g_assert( NA_IS_OBJECT( object ));
+ g_assert( G_IS_OBJECT( object ));
g_assert( NA_IS_IDUPLICABLE( object ));
set_origin( object, origin );
}
}
-static NAObject *
-v_duplicate( const NAObject *object )
+static void
+v_copy( NAIDuplicable *target, const NAIDuplicable *source )
{
- NAIDuplicable *instance = NA_IDUPLICABLE( object );
+ if( NA_IDUPLICABLE_GET_INTERFACE( target )->copy ){
+ NA_IDUPLICABLE_GET_INTERFACE( target )->copy( target, source );
+ }
+}
- if( NA_IDUPLICABLE_GET_INTERFACE( instance )->duplicate ){
- return( NA_IDUPLICABLE_GET_INTERFACE( instance )->duplicate( object ));
+static NAIDuplicable *
+v_new( const NAIDuplicable *object )
+{
+ if( NA_IDUPLICABLE_GET_INTERFACE( object )->new ){
+ return( NA_IDUPLICABLE_GET_INTERFACE( object )->new( object ));
}
return( NULL );
}
static gboolean
-v_are_equal( const NAObject *a, const NAObject *b )
+v_are_equal( const NAIDuplicable *a, const NAIDuplicable *b )
{
- NAIDuplicable *instance = NA_IDUPLICABLE( a );
-
- if( NA_IDUPLICABLE_GET_INTERFACE( instance )->are_equal ){
- return( NA_IDUPLICABLE_GET_INTERFACE( instance )->are_equal( a, b ));
+ if( NA_IDUPLICABLE_GET_INTERFACE( a )->are_equal ){
+ return( NA_IDUPLICABLE_GET_INTERFACE( a )->are_equal( a, b ));
}
return( TRUE );
}
static gboolean
-v_is_valid( const NAObject *object )
+v_is_valid( const NAIDuplicable *object )
{
- NAIDuplicable *instance = NA_IDUPLICABLE( object );
-
- if( NA_IDUPLICABLE_GET_INTERFACE( instance )->is_valid ){
- return( NA_IDUPLICABLE_GET_INTERFACE( instance )->is_valid( object ));
+ if( NA_IDUPLICABLE_GET_INTERFACE( object )->is_valid ){
+ return( NA_IDUPLICABLE_GET_INTERFACE( object )->is_valid( object ));
}
return( TRUE );
}
-static NAObject *
-get_origin( const NAObject *object )
+static NAIDuplicable *
+get_origin( const NAIDuplicable *object )
{
- return( NA_OBJECT( g_object_get_data( G_OBJECT( object ), PROP_IDUPLICABLE_ORIGIN )));
+ return( NA_IDUPLICABLE( g_object_get_data( G_OBJECT( object ), PROP_IDUPLICABLE_ORIGIN )));
}
static void
-set_origin( const NAObject *object, const NAObject *origin )
+set_origin( const NAIDuplicable *object, const NAIDuplicable *origin )
{
g_object_set_data( G_OBJECT( object ), PROP_IDUPLICABLE_ORIGIN, ( gpointer ) origin );
}
static gboolean
-get_modified( const NAObject *object )
+get_modified( const NAIDuplicable *object )
{
return(( gboolean ) GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( object ), PROP_IDUPLICABLE_ISMODIFIED )));
}
static void
-set_modified( const NAObject *object, gboolean is_modified )
+set_modified( const NAIDuplicable *object, gboolean is_modified )
{
g_object_set_data( G_OBJECT( object ), PROP_IDUPLICABLE_ISMODIFIED, GUINT_TO_POINTER( is_modified ));
}
static gboolean
-get_valid( const NAObject *object )
+get_valid( const NAIDuplicable *object )
{
return(( gboolean ) GPOINTER_TO_UINT( g_object_get_data( G_OBJECT( object ), PROP_IDUPLICABLE_ISVALID )));
}
static void
-set_valid( const NAObject *object, gboolean is_valid )
+set_valid( const NAIDuplicable *object, gboolean is_valid )
{
g_object_set_data( G_OBJECT( object ), PROP_IDUPLICABLE_ISVALID, GUINT_TO_POINTER( is_valid ));
}
diff --git a/src/common/na-iduplicable.h b/src/common/na-iduplicable.h
index 0536b46..65b7421 100644
--- a/src/common/na-iduplicable.h
+++ b/src/common/na-iduplicable.h
@@ -46,7 +46,7 @@
* will stay in life at least as long as the duplicated one.
*/
-#include "na-object.h"
+#include <glib-object.h>
G_BEGIN_DECLS
@@ -64,65 +64,75 @@ typedef struct {
NAIDuplicableInterfacePrivate *private;
/**
- * duplicate:
- * @object: the #NAObject object to be duplicated.
+ * get_new_instance:
+ * @nstance: a #NAIDuplicable instance of the klass of which we
+ * want a new instance.
*
- * Allocates an exact copy of the specified #NAObject object.
+ * Returns a new instance of the same class.
*
- * The implementor should define a duplicate()-equivalent virtual
- * function in order the new #NAObject-derived object be allocated
- * with the right most-derived class.
- *
- * The implementor should also define a copy()-equivalent virtual
- * function so that each class in the derivation hierarchy be able
- * to copy its own data and properties to the target instance.
+ * Returns: a newly allocated #NAIDuplicable object.
+ */
+ NAIDuplicable * ( *new ) ( const NAIDuplicable *object );
+
+ /**
+ * copy:
+ * @target: the #NAIDuplicable target of the copy.
+ * @source: the #NAIDuplicable source of the copy
*
- * Returns: a newly allocated #NAObject object, which is an exact
- * copy of @object.
+ * Copies data from @source to @ŧarget, so that @target becomes an
+ * exact copy of @source.
*/
- NAObject * ( *duplicate )( const NAObject *object );
+ void ( *copy ) ( NAIDuplicable *target, const NAIDuplicable *source );
/**
* are_equal:
- * @a: a first #NAObject object.
- * @b: a second #NAObject object to be compared to the first one.
+ * @a: a first #NAIDuplicable object.
+ * @b: a second #NAIDuplicable object to be compared to the first
+ * one.
*
* Compares the two objects.
*
* The implementor should define a are_equal()-equivalent virtual
- * function so that each #NAObject-derived class be able to check
- * for identity.
+ * function so that each #NAIDuplicable-derived class be able to
+ * check for identity.
*
* Returns: %TRUE if @a and @b are identical, %FALSE else.
*/
- gboolean ( *are_equal )( const NAObject *a, const NAObject *b );
+ gboolean ( *are_equal )( const NAIDuplicable *a, const NAIDuplicable *b );
/**
* is_valid:
- * @object: the #NAObject object to be checked.
+ * @object: the #NAIDuplicable object to be checked.
*
* Checks @object for validity.
*
* The implementor should define a is_valid()-equivalent virtual
- * function so that each #NAObject-derived class be able to check
- * for validity.
+ * function so that each #NAIDuplicable-derived class be able to
+ * check for validity.
*
* Returns: %TRUE if @object is valid, %FALSE else.
*/
- gboolean ( *is_valid ) ( const NAObject *object );
+ gboolean ( *is_valid ) ( const NAIDuplicable *object );
}
NAIDuplicableInterface;
-GType na_iduplicable_get_type( void );
+GType na_iduplicable_get_type( void );
+
+void na_iduplicable_init( NAIDuplicable *object );
+
+void na_iduplicable_dump( const NAIDuplicable *object );
+
+NAIDuplicable *na_iduplicable_duplicate( const NAIDuplicable *object );
+
+void na_iduplicable_check_edited_status( const NAIDuplicable *object );
+
+gboolean na_iduplicable_is_modified( const NAIDuplicable *object );
+
+gboolean na_iduplicable_is_valid( const NAIDuplicable *object );
+
+NAIDuplicable *na_iduplicable_get_origin( const NAIDuplicable *object );
-void na_iduplicable_init( NAObject *object );
-void na_iduplicable_dump( const NAObject *object );
-NAObject *na_iduplicable_duplicate( const NAObject *object );
-void na_iduplicable_check_edited_status( const NAObject *object );
-gboolean na_iduplicable_is_modified( const NAObject *object );
-gboolean na_iduplicable_is_valid( const NAObject *object );
-NAObject *na_iduplicable_get_origin( const NAObject *object );
-void na_iduplicable_set_origin( NAObject *object, const NAObject *origin );
+void na_iduplicable_set_origin( NAIDuplicable *object, const NAIDuplicable *origin );
G_END_DECLS
diff --git a/src/common/na-iio-provider.c b/src/common/na-iio-provider.c
index 23b92f7..7f94f58 100644
--- a/src/common/na-iio-provider.c
+++ b/src/common/na-iio-provider.c
@@ -48,7 +48,6 @@ static void interface_base_finalize( NAIIOProviderInterface *klass );
static gboolean do_is_willing_to_write( const NAIIOProvider *instance );
static gboolean do_is_writable( const NAIIOProvider *instance, const NAAction *action );
static guint write_action( const NAIIOProvider *instance, NAAction *action, gchar **message );
-static GSList *sort_actions( const NAPivot *pivot, GSList *actions );
static gint compare_actions_label_alpha_fn( const NAAction *a, const NAAction *b );
/**
@@ -173,7 +172,28 @@ na_iio_provider_read_actions( const NAPivot *pivot )
}
}
- return( sort_actions( pivot, actions ));
+ /* TODO: should be done only if prefs is set */
+ return( na_iio_provider_sort_actions( pivot, actions ));
+}
+
+/**
+ * na_iio_provider_sort_action:
+ * @pivot: the #NAPivot object which owns the list of registered I/O
+ * storage providers.
+ * @actions: the list of #NAAction action to be sorted.
+ *
+ * Sorts the list of actions in alphabetical order of their label.
+ *
+ * Returns: the sorted list.
+ */
+GSList *
+na_iio_provider_sort_actions( const NAPivot *pivot, GSList *actions )
+{
+ GSList *sorted;
+
+ sorted = g_slist_sort( actions, ( GCompareFunc ) compare_actions_label_alpha_fn );
+
+ return( sorted );
}
/**
@@ -319,20 +339,6 @@ write_action( const NAIIOProvider *provider, NAAction *action, gchar **message )
return( NA_IIO_PROVIDER_GET_INTERFACE( provider )->write_action( provider, action, message ));
}
-/*
- * sort the actions so that they are in the same order than when they
- * are displayed in NACT
- */
-static GSList *
-sort_actions( const NAPivot *pivot, GSList *actions )
-{
- GSList *sorted;
-
- sorted = g_slist_sort( actions, ( GCompareFunc ) compare_actions_label_alpha_fn );
-
- return( sorted );
-}
-
static gint
compare_actions_label_alpha_fn( const NAAction *a, const NAAction *b )
{
diff --git a/src/common/na-iio-provider.h b/src/common/na-iio-provider.h
index 87481f3..cfbaca5 100644
--- a/src/common/na-iio-provider.h
+++ b/src/common/na-iio-provider.h
@@ -131,12 +131,15 @@ typedef struct {
GType na_iio_provider_get_type( void );
GSList *na_iio_provider_read_actions( const NAPivot *pivot );
+GSList *na_iio_provider_sort_actions( const NAPivot *pivot, GSList *actions );
guint na_iio_provider_write_action( const NAPivot *pivot, NAAction *action, gchar **message );
guint na_iio_provider_delete_action( const NAPivot *pivot, const NAAction *action, gchar **message );
/* modification notification message to NAPivot
*/
-#define NA_IIO_PROVIDER_SIGNAL_ACTION_CHANGED "notify_pivot_of_action_changed"
+#define NA_IIO_PROVIDER_SIGNAL_ACTION_CHANGED "notify-consumer-of-action-change"
+#define NA_IIO_PROVIDER_SIGNAL_DISPLAY_ORDER_CHANGED "notify-consumer-of-display-order-change"
+#define NA_IIO_PROVIDER_SIGNAL_DISPLAY_ABOUT_CHANGED "notify-consumer-of-display-about-change"
/* return code of update/write/delete operations
*/
diff --git a/src/common/na-ipivot-consumer.c b/src/common/na-ipivot-consumer.c
index a98c554..a080329 100644
--- a/src/common/na-ipivot-consumer.c
+++ b/src/common/na-ipivot-consumer.c
@@ -151,7 +151,8 @@ na_ipivot_consumer_delay_notify( NAIPivotConsumer *instance )
* @instance: the #NAIPivotConsumer instance to be notified of the end
* of the modifications.
*
- * Notifies the consumers that the actions have been modified.
+ * Notifies the consumers that the actions have been modified on one of
+ * the underlying storage subsystems.
*/
void na_ipivot_consumer_notify( NAIPivotConsumer *instance )
{
@@ -166,6 +167,37 @@ void na_ipivot_consumer_notify( NAIPivotConsumer *instance )
}
}
+/**
+ * na_ipivot_consumer_notify:
+ * @instance: the #NAIPivotConsumer instance to be notified of the end
+ * of the modifications.
+ *
+ * Notifies the consumers that the display order has been changed.
+ */
+void
+na_ipivot_consumer_notify_display_order_change( NAIPivotConsumer *instance )
+{
+ if( NA_IPIVOT_CONSUMER_GET_INTERFACE( instance )->on_display_order_changed ){
+ NA_IPIVOT_CONSUMER_GET_INTERFACE( instance )->on_display_order_changed( instance, NULL );
+ }
+}
+
+/**
+ * na_ipivot_consumer_notify:
+ * @instance: the #NAIPivotConsumer instance to be notified of the end
+ * of the modifications.
+ *
+ * Notifies the consumers that the setting of the display of an 'About'
+ * item in the Nautilus context menu has been changed.
+ */
+void
+na_ipivot_consumer_notify_display_about_change( NAIPivotConsumer *instance )
+{
+ if( NA_IPIVOT_CONSUMER_GET_INTERFACE( instance )->on_display_about_changed ){
+ NA_IPIVOT_CONSUMER_GET_INTERFACE( instance )->on_display_about_changed( instance, NULL );
+ }
+}
+
static gboolean
is_notify_allowed( const NAIPivotConsumer *instance )
{
diff --git a/src/common/na-ipivot-consumer.h b/src/common/na-ipivot-consumer.h
index dc3ce56..5d27b3b 100644
--- a/src/common/na-ipivot-consumer.h
+++ b/src/common/na-ipivot-consumer.h
@@ -69,7 +69,31 @@ typedef struct {
* a bunch of modifications. At this time, the embedded list of
* #NAAction has been updated to be up to date.
*/
- void ( *on_actions_changed )( NAIPivotConsumer *instance, gpointer user_data );
+ void ( *on_actions_changed ) ( NAIPivotConsumer *instance, gpointer user_data );
+
+ /**
+ * on_display_about_changed:
+ * @instance: the #NAIPivotConsumer instance which implements this
+ * interface.
+ * user_data: user data set when emitting the message. Currently,
+ * not used.
+ *
+ * This function is triggered each time the setting of the display
+ * of an 'About' item in the Nautilus context menu is changed.
+ */
+ void ( *on_display_about_changed )( NAIPivotConsumer *instance, gpointer user_data );
+
+ /**
+ * on_display_order_changed:
+ * @instance: the #NAIPivotConsumer instance which implements this
+ * interface.
+ * user_data: user data set when emitting the message. Currently,
+ * not used.
+ *
+ * This function is triggered each time the display order preference
+ * is changed.
+ */
+ void ( *on_display_order_changed )( NAIPivotConsumer *instance, gpointer user_data );
}
NAIPivotConsumerInterface;
@@ -78,6 +102,8 @@ GType na_ipivot_consumer_get_type( void );
void na_ipivot_consumer_delay_notify( NAIPivotConsumer *instance );
void na_ipivot_consumer_notify( NAIPivotConsumer *instance );
+void na_ipivot_consumer_notify_display_order_change( NAIPivotConsumer *instance );
+void na_ipivot_consumer_notify_display_about_change( NAIPivotConsumer *instance );
G_END_DECLS
diff --git a/src/common/na-iprefs.c b/src/common/na-iprefs.c
index b4f0a6d..18a5ab1 100644
--- a/src/common/na-iprefs.c
+++ b/src/common/na-iprefs.c
@@ -48,7 +48,7 @@ static GType register_type( void );
static void interface_base_init( NAIPrefsInterface *klass );
static void interface_base_finalize( NAIPrefsInterface *klass );
-static gboolean read_key_bool( NAIPrefs *instance, const gchar *name );
+static gboolean read_key_bool( NAIPrefs *instance, const gchar *name, gboolean default_value );
static void write_key_bool( NAIPrefs *instance, const gchar *name, gboolean value );
GType
@@ -123,41 +123,106 @@ interface_base_finalize( NAIPrefsInterface *klass )
}
/**
- * Get/set a named boolean.
+ * na_iprefs_get_alphabetical_order:
+ * @instance: this #NAIPrefs interface instance.
*
- * @window: this NAWindow-derived window.
+ * Returns: #TRUE if the actions are to be maintained in alphabetical
+ * order of their label, #FALSE else.
+ *
+ * Note: this function returns a suitable default value if the key is
+ * not found in GConf preferences.
+ *
+ * Note: please take care of keeping the default value synchronized with
+ * those defined in schemas.
+ */
+gboolean na_iprefs_get_alphabetical_order( NAIPrefs *instance )
+{
+ return( read_key_bool( instance, PREFS_DISPLAY_ALPHABETICAL_ORDER, TRUE ));
+}
+
+/**
+ * na_iprefs_get_add_about_item:
+ * @instance: this #NAIPrefs interface instance.
+ *
+ * Returns: #TRUE if an "About Nautilus Actions" item may be added to
+ * the first level of Nautilus context submenus (if any), #FALSE else.
+ *
+ * Note: this function returns a suitable default value if the key is
+ * not found in GConf preferences.
+ *
+ * Note: please take care of keeping the default value synchronized with
+ * those defined in schemas.
+ */
+gboolean na_iprefs_get_add_about_item( NAIPrefs *instance )
+{
+ return( read_key_bool( instance, PREFS_ADD_ABOUT_ITEM, TRUE ));
+}
+
+/**
+ * Get a named boolean.
+ * @instance: this #NAIPrefs interface instance.
+ * @name: the name of the key to be read.
+ *
+ * Returns: the boolean attached to the @name key.
+ *
+ * Note that this returns #FALSE if the key doesn't exist.
+ * See na_iprefs_get_alphabetical_order() and
+ * na_iprefs_get_add_about_item() to get suitable default values.
*/
gboolean
na_iprefs_get_bool( NAIPrefs *instance, const gchar *name )
{
- return( read_key_bool( instance, name ));
+ return( read_key_bool( instance, name, FALSE ));
}
+/**
+ * Set a named boolean.
+ * @instance: this #NAIPrefs interface instance.
+ * @name: the name of the key to be read.
+ *
+ * Records the specified boolean in the GConf preferences.
+ */
void
na_iprefs_set_bool( NAIPrefs *instance, const gchar *name, gboolean value )
{
write_key_bool( instance, name, value );
}
+/*
+ * note that don't rely on having correctly installed the schema for the key
+ */
static gboolean
-read_key_bool( NAIPrefs *instance, const gchar *name )
+read_key_bool( NAIPrefs *instance, const gchar *name, gboolean default_value )
{
static const gchar *thisfn = "na_iprefs_read_key_bool";
GError *error = NULL;
gchar *path;
- gboolean value;
+ GConfValue *value;
+ gboolean ret;
+
+ ret = default_value;
path = g_strdup_printf( "%s/%s", NA_GCONF_PREFS_PATH, name );
- value = gconf_client_get_bool( NA_IPREFS_GET_INTERFACE( instance )->private->client, path, &error );
+ value = gconf_client_get_without_default( NA_IPREFS_GET_INTERFACE( instance )->private->client, path, &error );
+ /*g_debug( "%s: path=%s, value=%p", thisfn, path, ( void * ) value );*/
if( error ){
g_warning( "%s: name=%s, %s", thisfn, name, error->message );
g_error_free( error );
+ if( value ){
+ gconf_value_free( value );
+ value = NULL;
+ }
+ }
+
+ if( value ){
+ ret = gconf_value_get_bool( value );
+ gconf_value_free( value );
}
g_free( path );
- return( value );
+ return( ret );
}
static void
diff --git a/src/common/na-iprefs.h b/src/common/na-iprefs.h
index e8abe26..6d2ed40 100644
--- a/src/common/na-iprefs.h
+++ b/src/common/na-iprefs.h
@@ -31,11 +31,67 @@
#ifndef __NA_IPREFS_H__
#define __NA_IPREFS_H__
-/*
- * NAIPrefs interface definition.
+/**
+ * SECTION: na_iprefs
+ * @short_description: #NAIPrefs interface definition.
+ * @include: common/na-iprefs.h
*
* This interface is to be implemented by all modules which wish take
- * benefit of preferences management.
+ * benefit of preferences management. It only manages preferences which
+ * are used by the plugin, and used/updated in the NACT user interface.
+ *
+ * Displaying the actions.
+ *
+ * - actions in alphabetical order: yes/no
+ * Nautilus-Actions used to display the actions in alphabetical order.
+ * Starting with 1.12.x, Nautilus-Actions lets the user rearrange
+ * himself the order of its actions.
+ * Defaults to yes to stay compatible with previous versions.
+ *
+ * Actions can be organized in a set of submenus. In this case, the
+ * 'alphabetical order' preferences is also satisfied, on a level
+ * basis.
+ * This is not a preference: as submenus are available, user is free
+ * to define some in NACT ; plugin will take care of them.
+ *
+ * Defined order is saved in the same time than actions. So
+ * considering the following operations:
+ *
+ * a) set preference to 'no'
+ * b) rearrange the items in any order
+ * c) save
+ * d) set preference to 'yes'
+ * -> the items are reordered in alphabetical order
+ * e) set preference to 'no'
+ * -> the previous order is restaured (as it has been previously
+ * saved)
+ *
+ * but
+ *
+ * a) set preference to 'no'
+ * b) rearrange the items in any order
+ * c) set preference to 'yes'
+ * -> the items are reordered in alphabetical order
+ * d) save
+ * e) set preference to 'no'
+ * -> the items stay in alphabetical order, as the previous save
+ * has removed the previous order.
+ *
+ * - adding a 'About Nautilus Actions' item at end of actions: yes/no
+ * This is used only when there is a root submenu, i.e. when the
+ * Nautilus context menu will only display one item (the root
+ * submenu). Only in this case, and if preference is 'yes', the we
+ * will add the About item at the end of the first level of submenu.
+ *
+ * Note that, as a convenience, the NACT user interface provides the
+ * user with a standard item (Nautilus Actions actions) which can be
+ * used as a root menu.
+ *
+ * No 'About' item is added when user organize its actions so that
+ * Nautilus context menu will have several entries at the first level.
+ *
+ * In all cases, the plugin takes care of providing actions to Nautilus
+ * if the same order than those they are displayed in NACT.
*/
#include <glib-object.h>
@@ -61,6 +117,9 @@ typedef struct {
GType na_iprefs_get_type( void );
+gboolean na_iprefs_get_alphabetical_order( NAIPrefs *instance );
+gboolean na_iprefs_get_add_about_item( NAIPrefs *instance );
+
gboolean na_iprefs_get_bool( NAIPrefs *instance, const gchar *key );
void na_iprefs_set_bool( NAIPrefs *instance, const gchar *key, gboolean value );
@@ -68,9 +127,10 @@ void na_iprefs_set_bool( NAIPrefs *instance, const gchar *key, gboolean valu
*/
#define NA_GCONF_PREFS_PATH NAUTILUS_ACTIONS_CONFIG_GCONF_BASEDIR "/" NA_GCONF_SCHEMA_PREFERENCES
-/* Preference keys managed by IPrefs interface
+/* GConf Preference keys managed by IPrefs interface
*/
-#define PREFS_DISPLAY_AS_SUBMENU "display-as-submenu"
+#define PREFS_DISPLAY_ALPHABETICAL_ORDER "preferences-alphabetical-order"
+#define PREFS_ADD_ABOUT_ITEM "preferences-add-about-item"
G_END_DECLS
diff --git a/src/common/na-object-item.c b/src/common/na-object-item.c
new file mode 100644
index 0000000..326081e
--- /dev/null
+++ b/src/common/na-object-item.c
@@ -0,0 +1,448 @@
+/*
+ * Nautilus ObjectItems
+ * A Nautilus extension which offers configurable context menu object_items.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "na-object-item.h"
+#include "na-utils.h"
+
+/* private class data
+ */
+struct NAObjectItemClassPrivate {
+ void *empty; /* so that gcc -pedantic is happy */
+};
+
+/* private instance data
+ */
+struct NAObjectItemPrivate {
+ gboolean dispose_has_run;
+
+ /* object_item properties
+ */
+ gchar *tooltip;
+ gchar *icon;
+};
+
+#define PROP_NAOBJECT_ITEM_TOOLTIP_STR "na-object-item-tooltip"
+#define PROP_NAOBJECT_ITEM_ICON_STR "na-object-item-icon"
+
+static NAObjectClass *st_parent_class = NULL;
+
+static GType register_type( void );
+static void class_init( NAObjectItemClass *klass );
+static void instance_init( GTypeInstance *instance, gpointer klass );
+static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
+static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
+static void instance_dispose( GObject *object );
+static void instance_finalize( GObject *object );
+
+static void object_copy( NAObject *target, const NAObject *source );
+static gboolean object_are_equal( const NAObject *a, const NAObject *b );
+static gboolean object_is_valid( const NAObject *object_item );
+static void object_dump( const NAObject *object_item );
+
+GType
+na_object_item_get_type( void )
+{
+ static GType object_type = 0;
+
+ if( !object_type ){
+ object_type = register_type();
+ }
+
+ return( object_type );
+}
+
+static GType
+register_type( void )
+{
+ static const gchar *thisfn = "na_object_item_register_type";
+
+ static GTypeInfo info = {
+ sizeof( NAObjectItemClass ),
+ ( GBaseInitFunc ) NULL,
+ ( GBaseFinalizeFunc ) NULL,
+ ( GClassInitFunc ) class_init,
+ NULL,
+ NULL,
+ sizeof( NAObjectItem ),
+ 0,
+ ( GInstanceInitFunc ) instance_init
+ };
+
+ g_debug( "%s", thisfn );
+
+ return( g_type_register_static( NA_OBJECT_TYPE, "NAObjectItem", &info, 0 ));
+}
+
+static void
+class_init( NAObjectItemClass *klass )
+{
+ static const gchar *thisfn = "na_object_item_class_init";
+ GObjectClass *object_class;
+ GParamSpec *spec;
+
+ g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+ st_parent_class = g_type_class_peek_parent( klass );
+
+ object_class = G_OBJECT_CLASS( klass );
+ object_class->dispose = instance_dispose;
+ object_class->finalize = instance_finalize;
+ object_class->set_property = instance_set_property;
+ object_class->get_property = instance_get_property;
+
+ spec = g_param_spec_string(
+ PROP_NAOBJECT_ITEM_TOOLTIP_STR,
+ "Item tooltip",
+ "Context menu tooltip of the item", "",
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
+ g_object_class_install_property( object_class, PROP_NAOBJECT_ITEM_TOOLTIP, spec );
+
+ spec = g_param_spec_string(
+ PROP_NAOBJECT_ITEM_ICON_STR,
+ "Icon name",
+ "Context menu displayable icon for the item", "",
+ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE );
+ g_object_class_install_property( object_class, PROP_NAOBJECT_ITEM_ICON, spec );
+
+ klass->private = g_new0( NAObjectItemClassPrivate, 1 );
+
+ NA_OBJECT_CLASS( klass )->new = NULL;
+ NA_OBJECT_CLASS( klass )->copy = object_copy;
+ NA_OBJECT_CLASS( klass )->are_equal = object_are_equal;
+ NA_OBJECT_CLASS( klass )->is_valid = object_is_valid;
+ NA_OBJECT_CLASS( klass )->dump = object_dump;
+}
+
+static void
+instance_init( GTypeInstance *instance, gpointer klass )
+{
+ /*static const gchar *thisfn = "na_object_item_instance_init";*/
+ NAObjectItem *self;
+
+ /*g_debug( "%s: instance=%p, klass=%p", thisfn, ( void * ) instance, ( void * ) klass );*/
+ g_assert( NA_IS_OBJECT_ITEM( instance ));
+ self = NA_OBJECT_ITEM( instance );
+
+ self->private = g_new0( NAObjectItemPrivate, 1 );
+
+ self->private->dispose_has_run = FALSE;
+
+ /* initialize suitable default values
+ */
+ self->private->tooltip = g_strdup( "" );
+ self->private->icon = g_strdup( "" );
+}
+
+static void
+instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec )
+{
+ NAObjectItem *self;
+
+ g_assert( NA_IS_OBJECT_ITEM( object ));
+ self = NA_OBJECT_ITEM( object );
+
+ switch( property_id ){
+ case PROP_NAOBJECT_ITEM_TOOLTIP:
+ g_value_set_string( value, self->private->tooltip );
+ break;
+
+ case PROP_NAOBJECT_ITEM_ICON:
+ g_value_set_string( value, self->private->icon );
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
+ break;
+ }
+}
+
+static void
+instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec )
+{
+ NAObjectItem *self;
+
+ g_assert( NA_IS_OBJECT_ITEM( object ));
+ self = NA_OBJECT_ITEM( object );
+
+ switch( property_id ){
+ case PROP_NAOBJECT_ITEM_TOOLTIP:
+ g_free( self->private->tooltip );
+ self->private->tooltip = g_value_dup_string( value );
+ break;
+
+ case PROP_NAOBJECT_ITEM_ICON:
+ g_free( self->private->icon );
+ self->private->icon = g_value_dup_string( value );
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
+ break;
+ }
+}
+
+static void
+instance_dispose( GObject *object )
+{
+ /*static const gchar *thisfn = "na_object_item_instance_dispose";*/
+ NAObjectItem *self;
+
+ /*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
+ g_assert( NA_IS_OBJECT_ITEM( object ));
+ self = NA_OBJECT_ITEM( object );
+
+ if( !self->private->dispose_has_run ){
+
+ self->private->dispose_has_run = TRUE;
+
+ /* chain up to the parent class */
+ if( G_OBJECT_CLASS( st_parent_class )->dispose ){
+ G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ }
+ }
+}
+
+static void
+instance_finalize( GObject *object )
+{
+ /*static const gchar *thisfn = "na_object_item_instance_finalize";*/
+ NAObjectItem *self;
+
+ /*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
+ g_assert( NA_IS_OBJECT_ITEM( object ));
+ self = NA_OBJECT_ITEM( object );
+
+ g_free( self->private->tooltip );
+ g_free( self->private->icon );
+
+ g_free( self->private );
+
+ /* chain call to parent class */
+ if( G_OBJECT_CLASS( st_parent_class )->finalize ){
+ G_OBJECT_CLASS( st_parent_class )->finalize( object );
+ }
+}
+
+/**
+ * na_object_item_get_tooltip:
+ * @item: the #NAObjectItem object to be requested.
+ *
+ * Returns the tooltip which will be display in the Nautilus context
+ * menu item for this @item.
+ *
+ * Returns: the tooltip of the @item as a newly allocated string. This
+ * returned string must be g_free() by the caller.
+ */
+gchar *
+na_object_item_get_tooltip( const NAObjectItem *item )
+{
+ gchar *tooltip;
+
+ g_assert( NA_IS_OBJECT_ITEM( item ));
+
+ g_object_get( G_OBJECT( item ), PROP_NAOBJECT_ITEM_TOOLTIP_STR, &tooltip, NULL );
+
+ return( tooltip );
+}
+
+/**
+ * na_object_item_get_icon:
+ * @item: the #NAObjectItem object to be requested.
+ *
+ * Returns the name of the icon attached to the Nautilus context menu
+ * item for this @item.
+ *
+ * Returns: the icon name as a newly allocated string. This returned
+ * string must be g_free() by the caller.
+ */
+gchar *
+na_object_item_get_icon( const NAObjectItem *item )
+{
+ gchar *icon;
+
+ g_assert( NA_IS_OBJECT_ITEM( item ));
+
+ g_object_get( G_OBJECT( item ), PROP_NAOBJECT_ITEM_ICON_STR, &icon, NULL );
+
+ return( icon );
+}
+
+/*
+ * TODO: remove this function
+ */
+gchar *
+na_object_item_get_verified_icon_name( const NAObjectItem *item )
+{
+ gchar *icon_name;
+
+ g_assert( NA_IS_OBJECT_ITEM( item ));
+
+ g_object_get( G_OBJECT( item ), PROP_NAOBJECT_ITEM_ICON_STR, &icon_name, NULL );
+
+ if( icon_name[0] == '/' ){
+ if( !g_file_test( icon_name, G_FILE_TEST_IS_REGULAR )){
+ g_free( icon_name );
+ return NULL;
+ }
+ } else if( strlen( icon_name ) == 0 ){
+ g_free( icon_name );
+ return NULL;
+ }
+
+ return( icon_name );
+}
+
+/**
+ * na_object_item_set_tooltip:
+ * @item: the #NAObjectItem object to be updated.
+ * @tooltip: the tooltip to be set.
+ *
+ * Sets a new tooltip for the @item. Tooltip will be displayed by
+ * Nautilus when the user move its mouse over the Nautilus context menu
+ * item.
+ *
+ * #NAObjectItem takes a copy of the provided tooltip. This later may
+ * so be g_free() by the caller after this function returns.
+ */
+void
+na_object_item_set_tooltip( NAObjectItem *item, const gchar *tooltip )
+{
+ g_assert( NA_IS_OBJECT_ITEM( item ));
+
+ g_object_set( G_OBJECT( item ), PROP_NAOBJECT_ITEM_TOOLTIP_STR, tooltip, NULL );
+}
+
+/**
+ * na_object_item_set_icon:
+ * @item: the #NAObjectItem object to be updated.
+ * @icon: the icon name to be set.
+ *
+ * Sets a new icon name for the @item.
+ *
+ * #NAObjectItem takes a copy of the provided icon name. This later may
+ * so be g_free() by the caller after this function returns.
+ */
+void
+na_object_item_set_icon( NAObjectItem *item, const gchar *icon )
+{
+ g_assert( NA_IS_OBJECT_ITEM( item ));
+
+ g_object_set( G_OBJECT( item ), PROP_NAOBJECT_ITEM_ICON_STR, icon, NULL );
+}
+
+void
+object_copy( NAObject *target, const NAObject *source )
+{
+ gchar *tooltip, *icon;
+
+ if( st_parent_class->copy ){
+ st_parent_class->copy( target, source );
+ }
+
+ g_assert( NA_IS_OBJECT_ITEM( source ));
+ g_assert( NA_IS_OBJECT_ITEM( target ));
+
+ g_object_get( G_OBJECT( source ),
+ PROP_NAOBJECT_ITEM_TOOLTIP_STR, &tooltip,
+ PROP_NAOBJECT_ITEM_ICON_STR, &icon,
+ NULL );
+
+ g_object_set( G_OBJECT( target ),
+ PROP_NAOBJECT_ITEM_TOOLTIP_STR, tooltip,
+ PROP_NAOBJECT_ITEM_ICON_STR, icon,
+ NULL );
+
+ g_free( tooltip );
+ g_free( icon );
+}
+
+static gboolean
+object_are_equal( const NAObject *a, const NAObject *b )
+{
+ NAObjectItem *first, *second;
+ gboolean equal = TRUE;
+
+ if( equal ){
+ if( st_parent_class->are_equal ){
+ equal = st_parent_class->are_equal( a, b );
+ }
+ }
+
+ if( equal ){
+ g_assert( NA_IS_OBJECT_ITEM( a ));
+ first = NA_OBJECT_ITEM( a );
+
+ g_assert( NA_IS_OBJECT_ITEM( b ));
+ second = NA_OBJECT_ITEM( b );
+
+ equal =
+ ( g_utf8_collate( first->private->tooltip, second->private->tooltip ) == 0 ) &&
+ ( g_utf8_collate( first->private->icon, second->private->icon ) == 0 );
+ }
+
+ return( equal );
+}
+
+gboolean
+object_is_valid( const NAObject *item )
+{
+ gboolean is_valid = TRUE;
+
+ if( is_valid ){
+ if( st_parent_class->is_valid ){
+ is_valid = st_parent_class->is_valid( item );
+ }
+ }
+
+ return( is_valid );
+}
+
+static void
+object_dump( const NAObject *item )
+{
+ static const gchar *thisfn = "na_object_item_object_dump";
+ NAObjectItem *self;
+
+ if( st_parent_class->dump ){
+ st_parent_class->dump( item );
+ }
+
+ g_assert( NA_IS_OBJECT_ITEM( item ));
+ self = NA_OBJECT_ITEM( item );
+
+ g_debug( "%s: tooltip='%s'", thisfn, self->private->tooltip );
+ g_debug( "%s: icon='%s'", thisfn, self->private->icon );
+}
diff --git a/src/common/na-object-item.h b/src/common/na-object-item.h
new file mode 100644
index 0000000..a1dc710
--- /dev/null
+++ b/src/common/na-object-item.h
@@ -0,0 +1,89 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+#ifndef __NA_OBJECT_ITEM_H__
+#define __NA_OBJECT_ITEM_H__
+
+/**
+ * SECTION: na_object_item
+ * @short_description: #NAObjectItem class definition.
+ * @include: common/na-object-item.h
+ *
+ * Derived from #NAObject class, this class is built to be used as a
+ * base class for objects which have a tooltip and an icon.
+ */
+
+#include "na-object.h"
+
+G_BEGIN_DECLS
+
+#define NA_OBJECT_ITEM_TYPE ( na_object_item_get_type())
+#define NA_OBJECT_ITEM( object ) ( G_TYPE_CHECK_INSTANCE_CAST( object, NA_OBJECT_ITEM_TYPE, NAObjectItem ))
+#define NA_OBJECT_ITEM_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST( klass, NA_OBJECT_ITEM_TYPE, NAObjectItemClass ))
+#define NA_IS_OBJECT_ITEM( object ) ( G_TYPE_CHECK_INSTANCE_TYPE( object, NA_OBJECT_ITEM_TYPE ))
+#define NA_IS_OBJECT_ITEM_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE(( klass ), NA_OBJECT_ITEM_TYPE ))
+#define NA_OBJECT_ITEM_GET_CLASS( object ) ( G_TYPE_INSTANCE_GET_CLASS(( object ), NA_OBJECT_ITEM_TYPE, NAObjectItemClass ))
+
+typedef struct NAObjectItemPrivate NAObjectItemPrivate;
+
+typedef struct {
+ NAObject parent;
+ NAObjectItemPrivate *private;
+}
+ NAObjectItem;
+
+typedef struct NAObjectItemClassPrivate NAObjectItemClassPrivate;
+
+typedef struct {
+ NAObjectClass parent;
+ NAObjectItemClassPrivate *private;
+}
+ NAObjectItemClass;
+
+/* object properties
+ * used in derived classes to access the properties
+ */
+enum {
+ PROP_NAOBJECT_ITEM_TOOLTIP = 1,
+ PROP_NAOBJECT_ITEM_ICON
+};
+
+GType na_object_item_get_type( void );
+
+gchar *na_object_item_get_tooltip( const NAObjectItem *item );
+gchar *na_object_item_get_icon( const NAObjectItem *item );
+gchar *na_object_item_get_verified_icon_name( const NAObjectItem *item );
+
+void na_object_item_set_tooltip( NAObjectItem *item, const gchar *tooltip );
+void na_object_item_set_icon( NAObjectItem *item, const gchar *icon_name );
+
+G_END_DECLS
+
+#endif /* __NA_OBJECT_ITEM_H__ */
diff --git a/src/common/na-object.c b/src/common/na-object.c
index 2ce37b8..87d4023 100644
--- a/src/common/na-object.c
+++ b/src/common/na-object.c
@@ -58,31 +58,31 @@ struct NAObjectPrivate {
static GObjectClass *st_parent_class = NULL;
-static GType register_type( void );
-static void class_init( NAObjectClass *klass );
-static void iduplicable_iface_init( NAIDuplicableInterface *iface );
-static void instance_init( GTypeInstance *instance, gpointer klass );
-static void instance_constructed( GObject *object );
-static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
-static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
-static void instance_dispose( GObject *object );
-static void instance_finalize( GObject *object );
-
-static void v_check_edited_status( const NAObject *object );
-static NAObject *v_duplicate( const NAObject *object );
-static void v_copy( NAObject *target, const NAObject *source );
-static gboolean v_are_equal( const NAObject *a, const NAObject *b );
-static gboolean v_is_valid( const NAObject *object );
-
-static void do_dump( const NAObject *object );
-static void do_check_edited_status( const NAObject *object );
-static void do_copy( NAObject *target, const NAObject *source );
-static gboolean do_are_equal( const NAObject *a, const NAObject *b );
-static gboolean do_is_valid( const NAObject *object );
-
-static NAObject *iduplicable_duplicate( const NAObject *object );
-static gboolean iduplicable_are_equal( const NAObject *a, const NAObject *b );
-static gboolean iduplicable_is_valid( const NAObject *object );
+static GType register_type( void );
+static void class_init( NAObjectClass *klass );
+static void iduplicable_iface_init( NAIDuplicableInterface *iface );
+static void instance_init( GTypeInstance *instance, gpointer klass );
+static void instance_constructed( GObject *object );
+static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
+static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
+static void instance_dispose( GObject *object );
+static void instance_finalize( GObject *object );
+
+static NAIDuplicable *iduplicable_new( const NAIDuplicable *object );
+static void iduplicable_copy( NAIDuplicable *target, const NAIDuplicable *source );
+static gboolean iduplicable_are_equal( const NAIDuplicable *a, const NAIDuplicable *b );
+static gboolean iduplicable_is_valid( const NAIDuplicable *object );
+
+static NAObject *v_new( const NAObject *object );
+static void v_copy( NAObject *target, const NAObject *source );
+static gchar *v_get_clipboard_id( const NAObject *object );
+static gboolean v_are_equal( const NAObject *a, const NAObject *b );
+static gboolean v_is_valid( const NAObject *object );
+
+static void do_copy( NAObject *target, const NAObject *source );
+static gboolean do_are_equal( const NAObject *a, const NAObject *b );
+static gboolean do_is_valid( const NAObject *object );
+static void do_dump( const NAObject *object );
GType
na_object_get_type( void )
@@ -114,7 +114,7 @@ register_type( void )
( GInstanceInitFunc ) instance_init
};
- static const GInterfaceInfo idupicable_iface_info = {
+ static const GInterfaceInfo iduplicable_iface_info = {
( GInterfaceInitFunc ) iduplicable_iface_init,
NULL,
NULL
@@ -124,7 +124,7 @@ register_type( void )
type = g_type_register_static( G_TYPE_OBJECT, "NAObject", &info, 0 );
- g_type_add_interface_static( type, NA_IDUPLICABLE_TYPE, &idupicable_iface_info );
+ g_type_add_interface_static( type, NA_IDUPLICABLE_TYPE, &iduplicable_iface_info );
return( type );
}
@@ -163,12 +163,11 @@ class_init( NAObjectClass *klass )
klass->private = g_new0( NAObjectClassPrivate, 1 );
- klass->dump = do_dump;
- klass->check_edited_status = do_check_edited_status;
- klass->duplicate = NULL;
+ klass->new = NULL;
klass->copy = do_copy;
klass->are_equal = do_are_equal;
klass->is_valid = do_is_valid;
+ klass->dump = do_dump;
}
static void
@@ -178,7 +177,8 @@ iduplicable_iface_init( NAIDuplicableInterface *iface )
g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
- iface->duplicate = iduplicable_duplicate;
+ iface->copy = iduplicable_copy;
+ iface->new = iduplicable_new;
iface->are_equal = iduplicable_are_equal;
iface->is_valid = iduplicable_is_valid;
}
@@ -186,10 +186,10 @@ iduplicable_iface_init( NAIDuplicableInterface *iface )
static void
instance_init( GTypeInstance *instance, gpointer klass )
{
- /*static const gchar *thisfn = "na_object_instance_init";
- g_debug( "%s: instance=%p, klass=%p", thisfn, instance, klass );*/
+ /*static const gchar *thisfn = "na_object_instance_init";*/
NAObject *self;
+ /*g_debug( "%s: instance=%p, klass=%p", thisfn, ( void * ) instance, ( void * ) klass );*/
g_assert( NA_IS_OBJECT( instance ));
self = NA_OBJECT( instance );
@@ -201,10 +201,10 @@ instance_init( GTypeInstance *instance, gpointer klass )
static void
instance_constructed( GObject *object )
{
- na_iduplicable_init( NA_OBJECT( object ));
+ na_iduplicable_init( NA_IDUPLICABLE( object ));
/* chain call to parent class */
- if( st_parent_class->constructed ){
+ if( G_OBJECT_CLASS( st_parent_class )->constructed ){
G_OBJECT_CLASS( st_parent_class )->constructed( object );
}
}
@@ -270,7 +270,9 @@ instance_dispose( GObject *object )
self->private->dispose_has_run = TRUE;
/* chain up to the parent class */
- G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ if( G_OBJECT_CLASS( st_parent_class )->dispose ){
+ G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ }
}
}
@@ -288,7 +290,7 @@ instance_finalize( GObject *object )
g_free( self->private );
/* chain call to parent class */
- if( st_parent_class->finalize ){
+ if( G_OBJECT_CLASS( st_parent_class )->finalize ){
G_OBJECT_CLASS( st_parent_class )->finalize( object );
}
}
@@ -316,13 +318,31 @@ na_object_dump( const NAObject *object )
* Exactly duplicates a #NAObject-derived object.
*
* Returns: the new #NAObject.
+ *
+ * na_object_duplicate( origin )
+ * +- na_iduplicable_duplicate( origin )
+ * | +- dup = duplicate( origin )
+ * | | +- dup = v_get_new_object() -> interface get_new_object
+ * | | +- v_copy( dup, origin ) -> interface copy
+ * | |
+ * | +- set_origin( dup, origin )
+ * | +- set_modified( dup, FALSE )
+ * | +- set_valid( dup, FALSE )
+ * |
+ * +- na_object_check_edited_status
*/
NAObject *
na_object_duplicate( const NAObject *object )
{
+ NAIDuplicable *duplicate;
+
g_assert( NA_IS_OBJECT( object ));
- return( na_iduplicable_duplicate( object ));
+ duplicate = na_iduplicable_duplicate( NA_IDUPLICABLE( object ));
+
+ na_object_check_edited_status( NA_OBJECT( duplicate ));
+
+ return( NA_OBJECT( duplicate ));
}
/**
@@ -342,6 +362,23 @@ na_object_copy( NAObject *target, const NAObject *source )
}
/**
+ * na_object_get_clipboard_id:
+ * @object: the #NAObject-derived object for which we will get a id.
+ *
+ * Returns: a newly allocated string which contains an id for the
+ * #NAobject. This id is suitable for the internal clipboard.
+ *
+ * The returned string should be g_free() by the caller.
+ */
+gchar *
+na_object_get_clipboard_id( const NAObject *object )
+{
+ g_assert( NA_IS_OBJECT( object ));
+
+ return( v_get_clipboard_id( object ));
+}
+
+/**
* na_object_check_edited_status:
* @object: the #NAObject object to be checked.
*
@@ -349,13 +386,19 @@ na_object_copy( NAObject *target, const NAObject *source )
*
* Internally set some properties which may be requested later. This
* two-steps check-request let us optimize some work in the UI.
+ *
+ * na_object_check_edited_status( object )
+ * +- na_iduplicable_check_edited_status( object )
+ * +- get_origin( object )
+ * +- modified_status = v_are_equal( origin, object ) -> interface are_equal
+ * +- valid_status = v_is_valid( object ) -> interface is_valid
*/
void
na_object_check_edited_status( const NAObject *object )
{
g_assert( NA_IS_OBJECT( object ));
- v_check_edited_status( object );
+ na_iduplicable_check_edited_status( NA_IDUPLICABLE( object ));
}
/**
@@ -397,22 +440,6 @@ na_object_is_valid( const NAObject *object )
}
/**
- * na_object_get_origin:
- * @object: the #NAObject object whose status is requested.
- *
- * Returns the original object which was at the origin of @object.
- *
- * Returns: a #NAObject, or NULL.
- */
-NAObject *
-na_object_get_origin( const NAObject *object )
-{
- g_assert( NA_IS_OBJECT( object ));
-
- return( na_iduplicable_get_origin( object ));
-}
-
-/**
* na_object_get_modified_status:
* @object: the #NAObject object whose status is requested.
*
@@ -432,7 +459,7 @@ na_object_get_modified_status( const NAObject *object )
{
g_assert( NA_IS_OBJECT( object ));
- return( na_iduplicable_is_modified( object ));
+ return( na_iduplicable_is_modified( NA_IDUPLICABLE( object )));
}
/**
@@ -454,23 +481,23 @@ na_object_get_valid_status( const NAObject *object )
{
g_assert( NA_IS_OBJECT( object ));
- return( na_iduplicable_is_valid( object ));
+ return( na_iduplicable_is_valid( NA_IDUPLICABLE( object )));
}
/**
- * na_object_set_origin:
+ * na_object_get_origin:
* @object: the #NAObject object whose status is requested.
- * @origin: a #NAObject which will be set as the new origin of @object.
*
- * Sets the new origin of @object.
+ * Returns the original object which was at the origin of @object.
+ *
+ * Returns: a #NAObject, or NULL.
*/
-void
-na_object_set_origin( NAObject *object, const NAObject *origin )
+NAObject *
+na_object_get_origin( const NAObject *object )
{
g_assert( NA_IS_OBJECT( object ));
- g_assert( NA_IS_OBJECT( origin ) || !origin );
- na_iduplicable_set_origin( object, origin );
+ return( NA_OBJECT( na_iduplicable_get_origin( NA_IDUPLICABLE( object ))));
}
/**
@@ -518,6 +545,22 @@ na_object_get_label( const NAObject *object )
}
/**
+ * na_object_set_origin:
+ * @object: the #NAObject object whose status is requested.
+ * @origin: a #NAObject which will be set as the new origin of @object.
+ *
+ * Sets the new origin of @object.
+ */
+void
+na_object_set_origin( NAObject *object, const NAObject *origin )
+{
+ g_assert( NA_IS_OBJECT( object ));
+ g_assert( NA_IS_OBJECT( origin ) || !origin );
+
+ na_iduplicable_set_origin( NA_IDUPLICABLE( object ), NA_IDUPLICABLE( origin ));
+}
+
+/**
* na_object_set_id:
* @object: the #NAObject object whose internal identifiant is to be
* set.
@@ -530,6 +573,7 @@ void
na_object_set_id( NAObject *object, const gchar *id )
{
g_assert( NA_IS_OBJECT( object ));
+
g_object_set( G_OBJECT( object ), PROP_NAOBJECT_ID_STR, id, NULL );
}
@@ -544,25 +588,39 @@ void
na_object_set_label( NAObject *object, const gchar *label )
{
g_assert( NA_IS_OBJECT( object ));
+
g_object_set( G_OBJECT( object ), PROP_NAOBJECT_LABEL_STR, label, NULL );
}
+static NAIDuplicable *
+iduplicable_new( const NAIDuplicable *object )
+{
+ return( NA_IDUPLICABLE( v_new( NA_OBJECT( object ))));
+}
+
static void
-v_check_edited_status( const NAObject *object )
+iduplicable_copy( NAIDuplicable *target, const NAIDuplicable *source )
{
- if( NA_OBJECT_GET_CLASS( object )->check_edited_status ){
- NA_OBJECT_GET_CLASS( object )->check_edited_status( object );
+ v_copy( NA_OBJECT( target ), NA_OBJECT( source ));
+}
- } else {
- do_check_edited_status( object );
- }
+static gboolean
+iduplicable_are_equal( const NAIDuplicable *a, const NAIDuplicable *b )
+{
+ return( v_are_equal( NA_OBJECT( a ), NA_OBJECT( b )));
+}
+
+static gboolean
+iduplicable_is_valid( const NAIDuplicable *object )
+{
+ return( v_is_valid( NA_OBJECT( object )));
}
static NAObject *
-v_duplicate( const NAObject *object )
+v_new( const NAObject *object )
{
- if( NA_OBJECT_GET_CLASS( object )->duplicate ){
- return( NA_OBJECT_GET_CLASS( object )->duplicate( object ));
+ if( NA_OBJECT_GET_CLASS( object )->new ){
+ return( NA_OBJECT_GET_CLASS( object )->new( object ));
}
return( NULL );
@@ -576,6 +634,16 @@ v_copy( NAObject *target, const NAObject *source )
}
}
+static gchar *
+v_get_clipboard_id( const NAObject *object )
+{
+ if( NA_OBJECT_GET_CLASS( object )->get_clipboard_id ){
+ return( NA_OBJECT_GET_CLASS( object )->get_clipboard_id( object ));
+ }
+
+ return( NULL );
+}
+
static gboolean
v_are_equal( const NAObject *a, const NAObject *b )
{
@@ -597,26 +665,6 @@ v_is_valid( const NAObject *object )
}
static void
-do_dump( const NAObject *object )
-{
- static const char *thisfn = "na_object_do_dump";
-
- g_assert( NA_IS_OBJECT( object ));
-
- g_debug( "%s: object=%p", thisfn, ( void * ) object );
- g_debug( "%s: id=%s", thisfn, object->private->id );
- g_debug( "%s: label=%s", thisfn, object->private->label );
-
- na_iduplicable_dump( object );
-}
-
-static void
-do_check_edited_status( const NAObject *object )
-{
- na_iduplicable_check_edited_status( object );
-}
-
-static void
do_copy( NAObject *target, const NAObject *source )
{
gchar *id, *label;
@@ -642,26 +690,26 @@ do_are_equal( const NAObject *a, const NAObject *b )
return( TRUE );
}
+/*
+ * from NAObject point of view, a valid object requires an id
+ * (not null, not empty)
+ */
static gboolean
do_is_valid( const NAObject *object )
{
return( object->private->id && strlen( object->private->id ));
}
-static NAObject *
-iduplicable_duplicate( const NAObject *object )
+static void
+do_dump( const NAObject *object )
{
- return( v_duplicate( object ));
-}
+ static const char *thisfn = "na_object_do_dump";
-static gboolean
-iduplicable_are_equal( const NAObject *a, const NAObject *b )
-{
- return( v_are_equal( a, b ));
-}
+ g_assert( NA_IS_OBJECT( object ));
-static gboolean
-iduplicable_is_valid( const NAObject *object )
-{
- return( v_is_valid( object ));
+ g_debug( "%s: object=%p", thisfn, ( void * ) object );
+ g_debug( "%s: id=%s", thisfn, object->private->id );
+ g_debug( "%s: label=%s", thisfn, object->private->label );
+
+ na_iduplicable_dump( NA_IDUPLICABLE( object ));
}
diff --git a/src/common/na-object.h b/src/common/na-object.h
index 047f302..af63318 100644
--- a/src/common/na-object.h
+++ b/src/common/na-object.h
@@ -74,42 +74,12 @@ typedef struct {
NAObjectClassPrivate *private;
/**
- * dump:
- * @object: the #NAObject-derived object to be dumped.
- *
- * Dumps via g_debug the content of the object.
- *
- * In order to get a down-to-top display, the derived class
- * implementation should call its parent class before actually
- * dumping its own data and properties.
- */
- void ( *dump ) ( const NAObject *object );
-
- /**
- * check_edited_status:
- * @object: the #NAObject-derived object to be checked.
+ * new:
+ * @object: a #NAObject-derived object.
*
- * Checks a #NAObject-derived object for modification and validity
- * status.
+ * Returns: a newly allocated object of the same class that @object.
*/
- void ( *check_edited_status )( const NAObject *object );
-
- /**
- * duplicate:
- * @object: the #NAObject-derived object to be dumped.
- *
- * Duplicates a #NAObject-derived object.
- *
- * As the most-derived class will actually allocate the new object
- * with the right class, it shouldn't call its parent class.
- *
- * Copying data and properties should then be done via the
- * na_object_copy() function.
- *
- * Returns: a newly allocated object, which is an exact copy of
- * @object.
- */
- NAObject * ( *duplicate ) ( const NAObject *object );
+ NAObject * ( *new ) ( const NAObject *object );
/**
* copy:
@@ -153,11 +123,31 @@ typedef struct {
* Returns: %TRUE if @object is valid, %FALSE else.
*/
gboolean ( *is_valid ) ( const NAObject *object );
+
+ /**
+ * dump:
+ * @object: the #NAObject-derived object to be dumped.
+ *
+ * Dumps via g_debug the content of the object.
+ *
+ * In order to get a down-to-top display, the derived class
+ * implementation should call its parent class before actually
+ * dumping its own data and properties.
+ */
+ void ( *dump ) ( const NAObject *object );
+
+ /**
+ * get_clipboard_id:
+ * @object: the #NAObject-derived object whose id is to be retrieved.
+ *
+ * Returns: an id suitable for the internal clipboard.
+ */
+ gchar * ( *get_clipboard_id ) ( const NAObject *object );
}
NAObjectClass;
/* object properties
- * used in derived classes to access to the properties
+ * used in derived classes to access the properties
*/
enum {
PROP_NAOBJECT_ID = 1,
@@ -170,19 +160,19 @@ void na_object_dump( const NAObject *object );
NAObject *na_object_duplicate( const NAObject *object );
void na_object_copy( NAObject *target, const NAObject *source );
+gchar *na_object_get_clipboard_id( const NAObject *object );
+
void na_object_check_edited_status( const NAObject *object );
gboolean na_object_are_equal( const NAObject *a, const NAObject *b );
gboolean na_object_is_valid( const NAObject *object );
-
-NAObject *na_object_get_origin( const NAObject *object );
gboolean na_object_get_modified_status( const NAObject *object );
gboolean na_object_get_valid_status( const NAObject *object );
-void na_object_set_origin( NAObject *object, const NAObject *origin );
-
+NAObject *na_object_get_origin( const NAObject *object );
gchar *na_object_get_id( const NAObject *object );
gchar *na_object_get_label( const NAObject *object );
+void na_object_set_origin( NAObject *object, const NAObject *origin );
void na_object_set_id( NAObject *object, const gchar *id );
void na_object_set_label( NAObject *object, const gchar *label );
diff --git a/src/common/na-pivot.c b/src/common/na-pivot.c
index 2f308fb..223f226 100644
--- a/src/common/na-pivot.c
+++ b/src/common/na-pivot.c
@@ -73,6 +73,8 @@ struct NAPivotPrivate {
enum {
ACTION_CHANGED,
+ DISPLAY_ORDER_CHANGE,
+ DISPLAY_ABOUT_CHANGE,
LAST_SIGNAL
};
@@ -95,6 +97,9 @@ static void action_changed_handler( NAPivot *pivot, gpointer user_data );
static gboolean on_actions_changed_timeout( gpointer user_data );
static gulong time_val_diff( const GTimeVal *recent, const GTimeVal *old );
+static void on_display_order_change( NAPivot *pivot, gpointer user_data );
+static void on_display_about_change( NAPivot *pivot, gpointer user_data );
+
GType
na_pivot_get_type( void )
{
@@ -164,6 +169,30 @@ class_init( NAPivotClass *klass )
1,
G_TYPE_POINTER
);
+ st_signals[ DISPLAY_ORDER_CHANGE ] = g_signal_new_class_handler(
+ NA_IIO_PROVIDER_SIGNAL_DISPLAY_ORDER_CHANGED,
+ G_TYPE_FROM_CLASS( klass ),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ ( GCallback ) on_display_order_change,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER
+ );
+ st_signals[ DISPLAY_ABOUT_CHANGE ] = g_signal_new_class_handler(
+ NA_IIO_PROVIDER_SIGNAL_DISPLAY_ABOUT_CHANGED,
+ G_TYPE_FROM_CLASS( klass ),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ ( GCallback ) on_display_about_change,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_POINTER
+ );
}
static void
@@ -399,7 +428,7 @@ na_pivot_get_duplicate_actions( const NAPivot *pivot )
list = g_slist_prepend( list, na_object_duplicate( NA_OBJECT( ia->data )));
}
- return( list );
+ return( g_slist_reverse( list ));
}
/**
@@ -715,3 +744,36 @@ na_pivot_free_notify( NAPivotNotify *npn )
g_free( npn );
}
}
+
+static void
+on_display_order_change( NAPivot *self, gpointer user_data )
+{
+ static const gchar *thisfn = "na_pivot_on_display_order_change";
+ GSList *ic;
+
+ g_debug( "%s: self=%p, data=%p", thisfn, ( void * ) self, ( void * ) user_data );
+ g_assert( NA_IS_PIVOT( self ));
+ g_assert( user_data );
+
+ if( self->private->dispose_has_run ){
+ return;
+ }
+
+ for( ic = self->private->notified ; ic ; ic = ic->next ){
+ na_ipivot_consumer_notify( NA_IPIVOT_CONSUMER( ic->data ));
+ }
+}
+
+static void
+on_display_about_change( NAPivot *self, gpointer user_data )
+{
+ static const gchar *thisfn = "na_pivot_on_display_order_change";
+
+ g_debug( "%s: self=%p, data=%p", thisfn, ( void * ) self, ( void * ) user_data );
+ g_assert( NA_IS_PIVOT( self ));
+ g_assert( user_data );
+
+ if( self->private->dispose_has_run ){
+ return;
+ }
+}
diff --git a/src/common/na-utils.c b/src/common/na-utils.c
index 0362ff3..e3723cc 100644
--- a/src/common/na-utils.c
+++ b/src/common/na-utils.c
@@ -32,6 +32,7 @@
#include <config.h>
#endif
+#include <gio/gio.h>
#include <glib-object.h>
#include <string.h>
@@ -363,3 +364,87 @@ na_utils_gstring_joinv( const gchar *start, const gchar *separator, gchar **list
return( g_string_free( tmp_string, FALSE ));
}
+
+gchar *
+na_utils_remove_last_level_from_path( const gchar *path )
+{
+ int p;
+ const char *ptr = path;
+ char *new_path;
+
+ if( path == NULL ){
+ return( NULL );
+ }
+
+ p = strlen( path ) - 1;
+ if( p < 0 ){
+ return( NULL );
+ }
+
+ while(( p > 0 ) && ( ptr[p] != '/' )){
+ p--;
+ }
+
+ if(( p == 0 ) && ( ptr[p] == '/' )){
+ p++;
+ }
+
+ new_path = g_strndup( path, ( guint ) p );
+
+ return( new_path );
+}
+
+gboolean
+na_utils_is_writable_dir( const gchar *uri )
+{
+ static const gchar *thisfn = "na_utils_is_writable_dir";
+ GFile *file;
+ GError *error = NULL;
+ GFileInfo *info;
+ GFileType type;
+ gboolean writable;
+
+ if( !uri || !strlen( uri )){
+ return( FALSE );
+ }
+
+ file = g_file_new_for_uri( uri );
+ info = g_file_query_info( file,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE, NULL, &error );
+
+ if( error ){
+ g_warning( "%s: g_file_query_info error: %s", thisfn, error->message );
+ g_error_free( error );
+ g_object_unref( file );
+ return( FALSE );
+ }
+
+ type = g_file_info_get_file_type( info );
+ if( type != G_FILE_TYPE_DIRECTORY ){
+ g_warning( "%s: %s is not a directory", thisfn, uri );
+ g_object_unref( info );
+ return( FALSE );
+ }
+
+ writable = g_file_info_get_attribute_boolean( info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE );
+ if( !writable ){
+ g_warning( "%s: %s is not writable", thisfn, uri );
+ }
+ g_object_unref( info );
+
+ return( writable );
+}
+
+gboolean
+na_utils_exist_file( const gchar *uri )
+{
+ GFile *file;
+ gboolean exists;
+
+ file = g_file_new_for_uri( uri );
+ exists = g_file_query_exists( file, NULL );
+ g_object_unref( file );
+
+ return( exists );
+}
diff --git a/src/common/na-utils.h b/src/common/na-utils.h
index 7c57cf3..5902a0d 100644
--- a/src/common/na-utils.h
+++ b/src/common/na-utils.h
@@ -59,6 +59,13 @@ gchar *na_utils_path_to_key( const gchar *path );
*/
gchar * na_utils_gstring_joinv( const gchar *start, const gchar *separator, gchar **list );
+/*
+ * path manipulations
+ */
+gchar *na_utils_remove_last_level_from_path( const gchar *path );
+gboolean na_utils_is_writable_dir( const gchar *uri );
+gboolean na_utils_exist_file( const gchar *uri );
+
G_END_DECLS
#endif /* __NA_UTILS_H__ */
diff --git a/src/common/na-xml-writer.c b/src/common/na-xml-writer.c
index 5d1043c..cc2f83d 100644
--- a/src/common/na-xml-writer.c
+++ b/src/common/na-xml-writer.c
@@ -32,6 +32,7 @@
#include <config.h>
#endif
+#include <gio/gio.h>
#include <libxml/tree.h>
#include "na-action-profile.h"
@@ -72,7 +73,7 @@ static void instance_dispose( GObject *object );
static void instance_finalize( GObject *object );
static NAXMLWriter *xml_writer_new( const gchar *uuid );
-static xmlDocPtr create_xml_schema( NAXMLWriter *writer, gint format, NAAction *action );
+static xmlDocPtr create_xml_schema( NAXMLWriter *writer, gint format, const NAAction *action );
static void create_schema_entry(
NAXMLWriter *writer,
gint format,
@@ -85,7 +86,7 @@ static void create_schema_entry(
gboolean is_l10n_value,
const gchar *short_desc,
const gchar *long_desc );
-static xmlDocPtr create_xml_dump( NAXMLWriter *writer, gint format, NAAction *action );
+static xmlDocPtr create_xml_dump( NAXMLWriter *writer, gint format, const NAAction *action );
static void create_dump_entry(
NAXMLWriter *writer,
gint format,
@@ -268,46 +269,35 @@ xml_writer_new( const gchar *uuid )
/**
* na_xml_writer_export:
- * @action:
+ * @action: the #NAAction to be exported.
* @folder: the directoy where to write the output XML file.
* If NULL, the output will be directed to stdout.
+ * @format: the export format.
+ * @msg: pointer to a buffer which will receive error messages.
*
* Export the specified action as an XML file.
*
* Returns: the written filename, or NULL if written to stdout.
*/
gchar *
-na_xml_writer_export( NAAction *action, const gchar *folder, gint format, gchar **msg )
+na_xml_writer_export( const NAAction *action, const gchar *folder, gint format, gchar **msg )
{
- gchar *uuid;
- NAXMLWriter *writer;
- xmlDocPtr doc = NULL;
gchar *filename = NULL;
gboolean free_filename = FALSE;
-
-
- uuid = action ? na_action_get_uuid( action ) : NULL;
- writer = xml_writer_new( uuid );
- g_free( uuid );
+ gchar *xml_buffer;
switch( format ){
case FORMAT_GCONFSCHEMAFILE_V1:
- doc = create_xml_schema( writer, format, action );
- filename = g_strdup_printf( "%s/config_%s.schemas", folder, writer->private->uuid );
- break;
-
case FORMAT_GCONFSCHEMAFILE_V2:
- doc = create_xml_schema( writer, format, action );
- filename = g_strdup_printf( "%s/config-%s.schema", folder, writer->private->uuid );
+ filename = na_xml_writer_get_output_fname( action, folder, format );
break;
/* this is the format used by nautilus-actions-new utility,
* and that's why this option takes care of a NULL folder
*/
case FORMAT_GCONFENTRY:
- doc = create_xml_dump( writer, format, action );
if( folder ){
- filename = g_strdup_printf( "%s/action-%s.xml", folder, writer->private->uuid );
+ filename = na_xml_writer_get_output_fname( action, folder, format );
} else {
filename = g_strdup( "-" );
free_filename = TRUE;
@@ -319,7 +309,6 @@ na_xml_writer_export( NAAction *action, const gchar *folder, gint format, gchar
* folder, or an output filename
*/
case FORMAT_GCONFSCHEMA:
- doc = create_gconf_schema( writer );
if( folder ){
filename = g_strdup( folder );
} else {
@@ -329,28 +318,201 @@ na_xml_writer_export( NAAction *action, const gchar *folder, gint format, gchar
break;
}
- g_assert( doc );
g_assert( filename );
- if( xmlSaveFormatFileEnc( filename, doc, "UTF-8", 1 ) == -1 ){
- g_free( filename );
- filename = NULL;
- }
+ xml_buffer = na_xml_writer_get_xml_buffer( action, format );
+
+ na_xml_writer_output_xml( xml_buffer, filename );
+
+ g_free( xml_buffer );
if( free_filename ){
g_free( filename );
filename = NULL;
}
+ return( filename );
+}
+
+/**
+ * na_xml_writer_get_output_fname:
+ * @action: the #NAAction to be exported.
+ * @folder: the uri of the directoy where to write the output XML file.
+ * @format: the export format.
+ *
+ * Returns: a filename suitable for writing the output XML.
+ *
+ * As we don't want overwrite already existing files, the candidate
+ * filename is incremented until we find an available filename.
+ *
+ * The returned string should be g_free() by the caller.
+ *
+ * Note that this function is always subject to race condition, as it
+ * is possible, though very unlikely, that the given file be created
+ * between our test of inexistance and the actual write.
+ */
+gchar *
+na_xml_writer_get_output_fname( const NAAction *action, const gchar *folder, gint format )
+{
+ gchar *uuid;
+ gchar *canonical_fname = NULL;
+ gchar *canonical_ext = NULL;
+ gchar *candidate_fname;
+ gint counter;
+
+ g_return_val_if_fail( action, NULL );
+ g_return_val_if_fail( folder, NULL );
+ g_return_val_if_fail( strlen( folder ), NULL );
+
+ uuid = na_action_get_uuid( action );
+
+ switch( format ){
+ case FORMAT_GCONFSCHEMAFILE_V1:
+ canonical_fname = g_strdup_printf( "config_%s", uuid );
+ canonical_ext = g_strdup( "schemas" );
+ break;
+
+ case FORMAT_GCONFSCHEMAFILE_V2:
+ canonical_fname = g_strdup_printf( "config-%s", uuid );
+ canonical_ext = g_strdup( "schema" );
+ break;
+
+ case FORMAT_GCONFENTRY:
+ canonical_fname = g_strdup_printf( "action-%s", uuid );
+ canonical_ext = g_strdup( "xml" );
+ break;
+ }
+
+ g_free( uuid );
+ g_return_val_if_fail( canonical_fname, NULL );
+
+ candidate_fname = g_strdup_printf( "%s/%s.%s", folder, canonical_fname, canonical_ext );
+
+ if( !na_utils_exist_file( candidate_fname )){
+ g_free( canonical_fname );
+ g_free( canonical_ext );
+ return( candidate_fname );
+ }
+
+ for( counter = 0 ; ; ++counter ){
+ g_free( candidate_fname );
+ candidate_fname = g_strdup_printf( "%s/%s_%d.%s", folder, canonical_fname, counter, canonical_ext );
+ if( !na_utils_exist_file( candidate_fname )){
+ break;
+ }
+ }
+
+ g_free( canonical_fname );
+ g_free( canonical_ext );
+
+ return( candidate_fname );
+}
+
+/**
+ * na_xml_writer_get_xml_buffer:
+ * @action: the #NAAction to be exported.
+ * @format: the export format.
+ *
+ * Returns: a buffer which contains the XML output.
+ *
+ * The returned string should be g_free() by the caller.
+ */
+gchar *
+na_xml_writer_get_xml_buffer( const NAAction *action, gint format )
+{
+ gchar *uuid;
+ NAXMLWriter *writer;
+ xmlDocPtr doc = NULL;
+ xmlChar *text;
+ int textlen;
+ gchar *buffer;
+
+ g_return_val_if_fail( action, NULL );
+
+ uuid = na_action_get_uuid( action );
+ writer = xml_writer_new( uuid );
+ g_free( uuid );
+
+ switch( format ){
+ case FORMAT_GCONFSCHEMAFILE_V1:
+ case FORMAT_GCONFSCHEMAFILE_V2:
+ doc = create_xml_schema( writer, format, action );
+ break;
+
+ case FORMAT_GCONFENTRY:
+ doc = create_xml_dump( writer, format, action );
+ break;
+
+ case FORMAT_GCONFSCHEMA:
+ doc = create_gconf_schema( writer );
+ break;
+ }
+
+ g_assert( doc );
+
+ xmlDocDumpFormatMemoryEnc( doc, &text, &textlen, "UTF-8", 1 );
+ buffer = g_strdup(( const gchar * ) text );
+
+ xmlFree( text );
xmlFreeDoc (doc);
xmlCleanupParser();
g_object_unref( writer );
- return( filename );
+ return( buffer );
+}
+
+/**
+ * na_xml_writer_output_xml:
+ * @action: the #NAAction to be exported.
+ * @filename: the uri of the output filename
+ *
+ * Exports an action to the given filename.
+ */
+void
+na_xml_writer_output_xml( const gchar *xml, const gchar *filename )
+{
+ static const gchar *thisfn = "na_xml_writer_output_xml";
+ GFile *file;
+ GFileOutputStream *stream;
+ GError *error = NULL;
+
+ g_assert( filename );
+
+ file = g_file_new_for_uri( filename );
+
+ stream = g_file_create( file, G_FILE_CREATE_REPLACE_DESTINATION, NULL, &error );
+ if( error ){
+ g_warning( "%s: %s", thisfn, error->message );
+ g_error_free( error );
+ g_object_unref( stream );
+ g_object_unref( file );
+ return;
+ }
+
+ g_output_stream_write( G_OUTPUT_STREAM( stream ), xml, g_utf8_strlen( xml, -1 ), NULL, &error );
+ if( error ){
+ g_warning( "%s: %s", thisfn, error->message );
+ g_error_free( error );
+ g_object_unref( stream );
+ g_object_unref( file );
+ return;
+ }
+
+ g_output_stream_close( G_OUTPUT_STREAM( stream ), NULL, &error );
+ if( error ){
+ g_warning( "%s: %s", thisfn, error->message );
+ g_error_free( error );
+ g_object_unref( stream );
+ g_object_unref( file );
+ return;
+ }
+
+ g_object_unref( stream );
+ g_object_unref( file );
}
static xmlDocPtr
-create_xml_schema( NAXMLWriter *writer, gint format, NAAction *action )
+create_xml_schema( NAXMLWriter *writer, gint format, const NAAction *action )
{
xmlDocPtr doc;
xmlNodePtr root_node, list_node;
@@ -528,7 +690,7 @@ create_schema_entry( NAXMLWriter *writer,
}
static xmlDocPtr
-create_xml_dump( NAXMLWriter *writer, gint format, NAAction *action )
+create_xml_dump( NAXMLWriter *writer, gint format, const NAAction *action )
{
xmlDocPtr doc;
xmlNodePtr root_node, list_node;
diff --git a/src/common/na-xml-writer.h b/src/common/na-xml-writer.h
index efbd22a..a7b9781 100644
--- a/src/common/na-xml-writer.h
+++ b/src/common/na-xml-writer.h
@@ -71,7 +71,13 @@ typedef struct {
GType na_xml_writer_get_type( void );
-gchar *na_xml_writer_export( NAAction *action, const gchar *folder, gint format, gchar **msg );
+gchar *na_xml_writer_export( const NAAction *action, const gchar *folder, gint format, gchar **msg );
+
+gchar *na_xml_writer_get_output_fname( const NAAction *action, const gchar *folder, gint format );
+
+gchar *na_xml_writer_get_xml_buffer( const NAAction *action, gint format );
+
+void na_xml_writer_output_xml( const gchar *xml, const gchar *filename );
G_END_DECLS
diff --git a/src/nact/Makefile.am b/src/nact/Makefile.am
index 7f75b9f..98f7d47 100644
--- a/src/nact/Makefile.am
+++ b/src/nact/Makefile.am
@@ -45,6 +45,8 @@ nautilus_actions_config_tool_SOURCES = \
base-window.c \
base-window.h \
base-window-class.h \
+ egg-tree-multi-dnd.c \
+ egg-tree-multi-dnd.h \
nact-application.c \
nact-application.h \
nact-assistant.c \
@@ -72,8 +74,12 @@ nautilus_actions_config_tool_SOURCES = \
nact-main-window.h \
nact-preferences-editor.c \
nact-preferences-editor.h \
+ nact-selection.c \
+ nact-selection.h \
nact-statusbar.c \
nact-statusbar.h \
+ nact-tree-model.c \
+ nact-tree-model.h \
nact-window.c \
nact-window.h \
nact-xml-reader.c \
diff --git a/src/nact/egg-tree-multi-dnd.c b/src/nact/egg-tree-multi-dnd.c
new file mode 100644
index 0000000..226d785
--- /dev/null
+++ b/src/nact/egg-tree-multi-dnd.c
@@ -0,0 +1,505 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+/* eggtreemultidnd.c
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "egg-tree-multi-dnd.h"
+
+#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
+
+/*static GtkTargetEntry target_table[] = {
+ { "XdndDirectSave0", 0, 0 },
+ { "XdndNautilusActions0", 0, 1 }
+};*/
+
+typedef struct
+{
+ guint pressed_button;
+ gint x;
+ gint y;
+ guint motion_notify_handler;
+ guint button_release_handler;
+ guint drag_data_get_handler;
+ GSList *event_list;
+ gboolean pending_event;
+}
+ EggTreeMultiDndData;
+
+static GType register_type( void );
+
+static GtkTargetList *v_get_target_list( EggTreeMultiDragSource *drag_source );
+static void v_free_target_list( EggTreeMultiDragSource *drag_source, GtkTargetList *list );
+static GdkDragAction v_get_drag_actions( EggTreeMultiDragSource *drag_source );
+
+static gboolean on_button_press_event( GtkWidget *widget, GdkEventButton *event, EggTreeMultiDragSource *drag_source );
+static gboolean on_button_release_event( GtkWidget *widget, GdkEventButton *event, EggTreeMultiDragSource *drag_source );
+static gboolean on_motion_event( GtkWidget *widget, GdkEventMotion *event, EggTreeMultiDragSource *drag_source );
+static gboolean on_drag_data_get( GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time );
+
+static void stop_drag_check( GtkWidget *widget );
+static void selection_foreach( GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **path_list );
+static void path_list_free( GList *path_list );
+/*static void set_context_data( GdkDragContext *context, GList *path_list );
+static GList *get_context_data( GdkDragContext *context );*/
+static void set_treeview_data( GtkWidget *treeview, GList *path_list );
+static GList *get_treeview_data( GtkWidget *treeview );
+
+GType
+egg_tree_multi_drag_source_get_type( void )
+{
+ static GType our_type = 0;
+
+ if( !our_type ){
+ our_type = register_type();
+ }
+
+ return( our_type );
+}
+
+static GType
+register_type( void )
+{
+ GType type;
+
+ static const GTypeInfo our_info = {
+ sizeof( EggTreeMultiDragSourceIface ), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ type = g_type_register_static( G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0 );
+
+ return( type );
+}
+
+/**
+ * egg_tree_multi_drag_add_drag_support:
+ */
+void
+egg_tree_multi_drag_add_drag_support( EggTreeMultiDragSource *drag_source, GtkTreeView *tree_view )
+{
+ g_return_if_fail( GTK_IS_TREE_VIEW( tree_view ));
+
+ g_signal_connect( G_OBJECT( tree_view ),
+ "button_press_event",
+ G_CALLBACK( on_button_press_event),
+ drag_source );
+}
+
+/**
+ * egg_tree_multi_drag_source_row_draggable:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row on which user is initiating a drag
+ *
+ * Asks the #EggTreeMultiDragSource whether a particular row can be used as
+ * the source of a DND operation. If the source doesn't implement
+ * this interface, the row is assumed draggable.
+ *
+ * Return value: %TRUE if the row can be dragged
+ **/
+gboolean
+egg_tree_multi_drag_source_row_draggable( EggTreeMultiDragSource *drag_source, GList *path_list )
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE( drag_source );
+
+ g_return_val_if_fail( EGG_IS_TREE_MULTI_DRAG_SOURCE( drag_source ), FALSE );
+ g_return_val_if_fail( iface->row_draggable != NULL, FALSE );
+ g_return_val_if_fail( path_list != NULL, FALSE );
+
+ return(( *iface->row_draggable )( drag_source, path_list ));
+}
+
+/**
+ * egg_tree_multi_drag_source_drag_data_get:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was dragged
+ * @selection_data: a #EggSelectionData to fill with data from the dragged row
+ *
+ * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
+ * representation of the row at @path. @selection_data->target gives
+ * the required type of the data. Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if data of the required type was provided
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_get( EggTreeMultiDragSource *drag_source,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ GList *path_list,
+ guint info )
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE( drag_source );
+
+ g_return_val_if_fail( EGG_IS_TREE_MULTI_DRAG_SOURCE( drag_source ), FALSE );
+ g_return_val_if_fail( iface->drag_data_get != NULL, FALSE );
+ g_return_val_if_fail( path_list != NULL, FALSE );
+ g_return_val_if_fail( selection_data != NULL, FALSE );
+
+ return(( *iface->drag_data_get )( drag_source, context, selection_data, path_list, info ));
+}
+
+/**
+ * egg_tree_multi_drag_source_drag_data_delete:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was being dragged
+ *
+ * Asks the #EggTreeMultiDragSource to delete the row at @path, because
+ * it was moved somewhere else via drag-and-drop. Returns %FALSE
+ * if the deletion fails because @path no longer exists, or for
+ * some model-specific reason. Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if the row was successfully deleted
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_delete( EggTreeMultiDragSource *drag_source, GList *path_list )
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE( drag_source );
+
+ g_return_val_if_fail( EGG_IS_TREE_MULTI_DRAG_SOURCE( drag_source ), FALSE );
+ g_return_val_if_fail( iface->drag_data_delete != NULL, FALSE );
+ g_return_val_if_fail( path_list != NULL, FALSE );
+
+ return(( *iface->drag_data_delete )( drag_source, path_list ));
+}
+
+static GtkTargetList *
+v_get_target_list( EggTreeMultiDragSource *drag_source )
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE( drag_source );
+
+ if( iface->get_target_list ){
+ return( iface->get_target_list( drag_source ));
+ }
+
+ return( NULL );
+}
+
+static void
+v_free_target_list( EggTreeMultiDragSource *drag_source, GtkTargetList *list )
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE( drag_source );
+
+ if( iface->free_target_list ){
+ iface->free_target_list( drag_source, list );
+
+ } else {
+ gtk_target_list_unref( list );
+ }
+}
+
+static GdkDragAction
+v_get_drag_actions( EggTreeMultiDragSource *drag_source )
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE( drag_source );
+
+ if( iface->get_drag_actions ){
+ return( iface->get_drag_actions( drag_source ));
+ }
+
+ return( 0 );
+}
+
+static gboolean
+on_button_press_event( GtkWidget *widget, GdkEventButton *event, EggTreeMultiDragSource *drag_source )
+{
+ GtkTreeView *tree_view;
+ GtkTreePath *path = NULL;
+ GtkTreeViewColumn *column = NULL;
+ gint cell_x, cell_y;
+ GtkTreeSelection *selection;
+ EggTreeMultiDndData *priv_data;
+ gboolean call_parent;
+
+ if( event->window != gtk_tree_view_get_bin_window( GTK_TREE_VIEW( widget ))){
+ return( FALSE );
+ }
+
+ if( event->button == 3 ){
+ return( FALSE );
+ }
+
+ tree_view = GTK_TREE_VIEW( widget );
+ priv_data = g_object_get_data( G_OBJECT( tree_view ), EGG_TREE_MULTI_DND_STRING );
+ if( priv_data == NULL ){
+ priv_data = g_new0( EggTreeMultiDndData, 1 );
+ priv_data->pending_event = FALSE;
+ g_object_set_data( G_OBJECT( tree_view ), EGG_TREE_MULTI_DND_STRING, priv_data );
+ }
+
+ if( g_slist_find( priv_data->event_list, event )){
+ return( FALSE );
+ }
+
+ if( priv_data->pending_event ){
+ /* save the event to be propagated in order */
+ priv_data->event_list = g_slist_append( priv_data->event_list, gdk_event_copy(( GdkEvent * ) event ));
+ return( TRUE );
+ }
+
+ if( event->type == GDK_2BUTTON_PRESS ){
+ return( FALSE );
+ }
+
+ gtk_tree_view_get_path_at_pos( tree_view, event->x, event->y, &path, &column, &cell_x, &cell_y );
+
+ if( path ){
+ selection = gtk_tree_view_get_selection( tree_view );
+
+ call_parent = ( event->state & ( GDK_CONTROL_MASK | GDK_SHIFT_MASK ) ||
+ !gtk_tree_selection_path_is_selected( selection, path) ||
+ event->button != 1 );
+
+ if( call_parent ){
+ ( GTK_WIDGET_GET_CLASS( tree_view ))->button_press_event( widget, event );
+ }
+
+ if( gtk_tree_selection_path_is_selected( selection, path )){
+ priv_data->pressed_button = event->button;
+ priv_data->x = event->x;
+ priv_data->y = event->y;
+ priv_data->pending_event = TRUE;
+
+ if( !call_parent ){
+ priv_data->event_list = g_slist_append( priv_data->event_list, gdk_event_copy(( GdkEvent * ) event ));
+ }
+
+ priv_data->motion_notify_handler =
+ g_signal_connect( G_OBJECT( tree_view ),
+ "motion_notify_event",
+ G_CALLBACK( on_motion_event ),
+ drag_source );
+
+ priv_data->button_release_handler =
+ g_signal_connect( G_OBJECT( tree_view ),
+ "button_release_event",
+ G_CALLBACK( on_button_release_event ),
+ drag_source );
+
+ if( priv_data->drag_data_get_handler == 0 ){
+ priv_data->drag_data_get_handler =
+ g_signal_connect( G_OBJECT( tree_view ),
+ "drag_data_get",
+ G_CALLBACK( on_drag_data_get ),
+ NULL );
+ }
+ }
+
+ gtk_tree_path_free (path);
+
+ /* We called the default handler so we don't let the default handler run */
+ return( TRUE );
+ }
+
+ return( FALSE );
+}
+
+static gboolean
+on_button_release_event( GtkWidget *widget, GdkEventButton *event, EggTreeMultiDragSource *drag_source )
+{
+ EggTreeMultiDndData *priv_data;
+ GSList *l;
+
+ priv_data = g_object_get_data( G_OBJECT( widget ), EGG_TREE_MULTI_DND_STRING );
+
+ for( l = priv_data->event_list ; l != NULL ; l = l->next ){
+ gtk_propagate_event( widget, l->data );
+ }
+
+ stop_drag_check( widget );
+
+ return( FALSE );
+}
+
+static gboolean
+on_motion_event( GtkWidget *widget, GdkEventMotion *event, EggTreeMultiDragSource *drag_source )
+{
+ EggTreeMultiDndData *priv_data;
+
+ priv_data = g_object_get_data( G_OBJECT( widget ), EGG_TREE_MULTI_DND_STRING );
+
+ if( gtk_drag_check_threshold( widget, priv_data->x, priv_data->y, event->x, event->y )){
+
+ GList *path_list = NULL;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GdkDragContext *context;
+
+ stop_drag_check( widget );
+
+ selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ));
+ gtk_tree_selection_selected_foreach( selection, ( GtkTreeSelectionForeachFunc ) selection_foreach, &path_list );
+ path_list = g_list_reverse( path_list );
+
+ model = gtk_tree_view_get_model( GTK_TREE_VIEW( widget ));
+
+ if( egg_tree_multi_drag_source_row_draggable( EGG_TREE_MULTI_DRAG_SOURCE( model ), path_list )){
+
+ GtkTargetList *target_list = v_get_target_list( drag_source );
+ GdkDragAction actions = v_get_drag_actions( drag_source );
+
+ context = gtk_drag_begin(
+ widget, target_list, actions, priv_data->pressed_button, ( GdkEvent * ) event );
+ /*set_context_data( context, path_list );*/
+
+ set_treeview_data( widget, path_list );
+
+ gtk_drag_set_icon_default( context );
+
+ v_free_target_list( drag_source, target_list );
+
+ } else {
+ path_list_free( path_list );
+ }
+ }
+
+ return( TRUE );
+}
+
+static gboolean
+on_drag_data_get( GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, guint info, guint time )
+{
+ static const gchar *thisfn = "on_drag_data_get";
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GList *path_list;
+
+ g_debug( "%s: widget=%p, context=%p, selection_data=%p, info=%d, time=%d",
+ thisfn, ( void * ) widget, ( void * ) context, ( void * ) selection_data, info, time );
+
+ tree_view = GTK_TREE_VIEW( widget );
+ model = gtk_tree_view_get_model( tree_view );
+ g_assert( model );
+ g_assert( EGG_IS_TREE_MULTI_DRAG_SOURCE( model ));
+
+ /*path_list = get_context_data( context );*/
+ path_list = get_treeview_data( widget );
+ if( path_list == NULL ){
+ return( FALSE );
+ }
+
+ /* We can implement the GTK_TREE_MODEL_ROW target generically for
+ * any model; for DragSource models there are some other targets
+ * we also support.
+ */
+ return( egg_tree_multi_drag_source_drag_data_get(
+ EGG_TREE_MULTI_DRAG_SOURCE( model ), context, selection_data, path_list, info ));
+}
+
+static void
+stop_drag_check( GtkWidget *widget )
+{
+ EggTreeMultiDndData *priv_data;
+ GSList *l;
+
+ priv_data = g_object_get_data( G_OBJECT( widget ), EGG_TREE_MULTI_DND_STRING );
+
+ for( l = priv_data->event_list ; l != NULL ; l = l->next ){
+ gdk_event_free( l->data );
+ }
+
+ g_slist_free( priv_data->event_list );
+ priv_data->event_list = NULL;
+ priv_data->pending_event = FALSE;
+
+ g_signal_handler_disconnect( widget, priv_data->motion_notify_handler );
+ g_signal_handler_disconnect( widget, priv_data->button_release_handler );
+}
+
+static void
+selection_foreach( GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **path_list )
+{
+ *path_list = g_list_prepend( *path_list, gtk_tree_row_reference_new( model, path ));
+}
+
+static void
+path_list_free( GList *path_list )
+{
+ g_list_foreach( path_list, ( GFunc ) gtk_tree_row_reference_free, NULL );
+ g_list_free( path_list );
+}
+
+/*static void
+set_context_data( GdkDragContext *context, GList *path_list )
+{
+ g_object_set_data_full(
+ G_OBJECT( context ),
+ "egg-tree-view-multi-source-row", path_list, ( GDestroyNotify ) path_list_free );
+}
+
+static GList *
+get_context_data( GdkDragContext *context )
+{
+ return( g_object_get_data( G_OBJECT( context ), "egg-tree-view-multi-source-row" ));
+}*/
+
+static void
+set_treeview_data( GtkWidget *treeview, GList *path_list )
+{
+ g_object_set_data_full(
+ G_OBJECT( treeview ),
+ "egg-tree-view-multi-source-row", path_list, ( GDestroyNotify ) path_list_free );
+}
+
+static GList *
+get_treeview_data( GtkWidget *treeview )
+{
+ return( g_object_get_data( G_OBJECT( treeview ), "egg-tree-view-multi-source-row" ));
+}
diff --git a/src/nact/egg-tree-multi-dnd.h b/src/nact/egg-tree-multi-dnd.h
new file mode 100644
index 0000000..55f307b
--- /dev/null
+++ b/src/nact/egg-tree-multi-dnd.h
@@ -0,0 +1,98 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+/* eggtreednd.h
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Adapted by Pierre Wieser for the needs of Nautilus Actions
+ */
+
+#ifndef __EGG_TREE_MULTI_DND_H__
+#define __EGG_TREE_MULTI_DND_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE ( egg_tree_multi_drag_source_get_type())
+#define EGG_TREE_MULTI_DRAG_SOURCE( object ) ( G_TYPE_CHECK_INSTANCE_CAST(( object ), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource ))
+#define EGG_IS_TREE_MULTI_DRAG_SOURCE( object ) ( G_TYPE_CHECK_INSTANCE_TYPE(( object ), EGG_TYPE_TREE_MULTI_DRAG_SOURCE ))
+#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE( object ) ( G_TYPE_INSTANCE_GET_INTERFACE(( object ), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface ))
+
+typedef struct EggTreeMultiDragSource EggTreeMultiDragSource;
+typedef struct EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface;
+
+struct EggTreeMultiDragSourceIface
+{
+ GTypeInterface g_iface;
+
+ /* vtable - not signals */
+ gboolean ( *row_draggable ) ( EggTreeMultiDragSource *drag_source, GList *path_list );
+ gboolean ( *drag_data_get ) ( EggTreeMultiDragSource *drag_source, GdkDragContext *context, GtkSelectionData *selection_data, GList *path_list, guint info );
+ gboolean ( *drag_data_delete )( EggTreeMultiDragSource *drag_source, GList *path_list );
+ GtkTargetList * ( *get_target_list ) ( EggTreeMultiDragSource *drag_source );
+ void ( *free_target_list )( EggTreeMultiDragSource *drag_source, GtkTargetList *list );
+ GdkDragAction ( *get_drag_actions )( EggTreeMultiDragSource *drag_source );
+};
+
+GType egg_tree_multi_drag_source_get_type( void ) G_GNUC_CONST;
+
+/* initialize drag support on the treeview */
+void egg_tree_multi_drag_add_drag_support( EggTreeMultiDragSource *drag_source, GtkTreeView *tree_view );
+
+/* returns whether the given row can be dragged */
+gboolean egg_tree_multi_drag_source_row_draggable( EggTreeMultiDragSource *drag_source, GList *path_list );
+
+/* Fills in selection_data with type selection_data->target based on the row
+ * denoted by path, returns TRUE if it does anything
+ */
+gboolean egg_tree_multi_drag_source_drag_data_get( EggTreeMultiDragSource *drag_source, GdkDragContext *context, GtkSelectionData *selection_data, GList *path_list, guint info );
+
+/* deletes the given row, or returns FALSE if it can't */
+gboolean egg_tree_multi_drag_source_drag_data_delete( EggTreeMultiDragSource *drag_source, GList *path_list );
+
+G_END_DECLS
+
+#endif /* __EGG_TREE_MULTI_DND_H__ */
diff --git a/src/nact/nact-application.c b/src/nact/nact-application.c
index 0835b42..58d762a 100644
--- a/src/nact/nact-application.c
+++ b/src/nact/nact-application.c
@@ -332,9 +332,9 @@ appli_initialize_application( BaseApplication *application )
static gchar *
appli_get_application_name( BaseApplication *application )
{
- static const gchar *thisfn = "nact_application_appli_get_application_name";
+ /*static const gchar *thisfn = "nact_application_appli_get_application_name";*/
- g_debug( "%s: application=%p", thisfn, ( void * ) application );
+ /*g_debug( "%s: application=%p", thisfn, ( void * ) application );*/
/* i18n: this is the application name, used in window title */
return( g_strdup( _( "Nautilus Actions Configuration Tool" )));
diff --git a/src/nact/nact-assistant-export.c b/src/nact/nact-assistant-export.c
index ddc6a74..499dbb0 100644
--- a/src/nact/nact-assistant-export.c
+++ b/src/nact/nact-assistant-export.c
@@ -110,13 +110,12 @@ static void assist_runtime_init_intro( NactAssistantExport *window, G
static void assist_initial_load_actions_list( NactAssistantExport *window, GtkAssistant *assistant );
static void assist_runtime_init_actions_list( NactAssistantExport *window, GtkAssistant *assistant );
-static void on_actions_list_selection_changed( GtkTreeSelection *selection, gpointer user_data );
+static void on_actions_list_selection_changed( NactIActionsList *instance, GSList *selected_items );
static void assist_initial_load_target_folder( NactAssistantExport *window, GtkAssistant *assistant );
static void assist_runtime_init_target_folder( NactAssistantExport *window, GtkAssistant *assistant );
static GtkFileChooser *get_folder_chooser( NactAssistantExport *window );
static void on_folder_selection_changed( GtkFileChooser *chooser, gpointer user_data );
-static gboolean is_writable_dir( const gchar *uri );
static void assist_initial_load_format( NactAssistantExport *window, GtkAssistant *assistant );
static void assist_runtime_init_format( NactAssistantExport *window, GtkAssistant *assistant );
@@ -506,7 +505,7 @@ assist_runtime_init_actions_list( NactAssistantExport *window, GtkAssistant *ass
}
static void
-on_actions_list_selection_changed( GtkTreeSelection *selection, gpointer user_data )
+on_actions_list_selection_changed( NactIActionsList *instance, GSList *selected_items )
{
/*static const gchar *thisfn = "nact_assistant_export_on_actions_list_selection_changed";
g_debug( "%s: selection=%p, user_data=%p", thisfn, selection, user_data );*/
@@ -516,12 +515,12 @@ on_actions_list_selection_changed( GtkTreeSelection *selection, gpointer user_da
gboolean enabled;
GtkWidget *content;
- g_assert( NACT_IS_ASSISTANT_EXPORT( user_data ));
- assistant = GTK_ASSISTANT( base_window_get_toplevel_dialog( BASE_WINDOW( user_data )));
+ g_assert( NACT_IS_ASSISTANT_EXPORT( instance ));
+ assistant = GTK_ASSISTANT( base_window_get_toplevel_dialog( BASE_WINDOW( instance )));
pos = gtk_assistant_get_current_page( assistant );
if( pos == ASSIST_PAGE_ACTIONS_SELECTION ){
- enabled = ( gtk_tree_selection_count_selected_rows( selection ) > 0 );
+ enabled = ( g_slist_length( selected_items ) > 0 );
content = gtk_assistant_get_nth_page( assistant, pos );
gtk_assistant_set_page_complete( assistant, content, enabled );
@@ -590,7 +589,7 @@ on_folder_selection_changed( GtkFileChooser *chooser, gpointer user_data )
uri = gtk_file_chooser_get_uri( chooser );
g_debug( "%s: uri=%s", thisfn, uri );
- enabled = ( uri && strlen( uri ) && is_writable_dir( uri ));
+ enabled = ( uri && strlen( uri ) && na_utils_is_writable_dir( uri ));
if( enabled ){
assist = NACT_ASSISTANT_EXPORT( user_data );
@@ -607,48 +606,6 @@ on_folder_selection_changed( GtkFileChooser *chooser, gpointer user_data )
}
}
-static gboolean
-is_writable_dir( const gchar *uri )
-{
- static const gchar *thisfn = "nact_assistant_export_is_writable_dir";
- GFile *file;
- GError *error = NULL;
- GFileInfo *info;
- GFileType type;
- gboolean writable;
-
- if( !uri || !strlen( uri )){
- return( FALSE );
- }
-
- file = g_file_new_for_uri( uri );
- info = g_file_query_info( file,
- G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," G_FILE_ATTRIBUTE_STANDARD_TYPE,
- G_FILE_QUERY_INFO_NONE, NULL, &error );
-
- if( error ){
- g_warning( "%s: g_file_query_info error: %s", thisfn, error->message );
- g_error_free( error );
- g_object_unref( file );
- return( FALSE );
- }
-
- type = g_file_info_get_file_type( info );
- if( type != G_FILE_TYPE_DIRECTORY ){
- g_warning( "%s: %s is not a directory", thisfn, uri );
- g_object_unref( info );
- return( FALSE );
- }
-
- writable = g_file_info_get_attribute_boolean( info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE );
- if( !writable ){
- g_warning( "%s: %s is not writable", thisfn, uri );
- }
- g_object_unref( info );
-
- return( writable );
-}
-
static void
assist_initial_load_format( NactAssistantExport *window, GtkAssistant *assistant )
{
diff --git a/src/nact/nact-iaction-tab.c b/src/nact/nact-iaction-tab.c
index 1f2237c..06a8308 100644
--- a/src/nact/nact-iaction-tab.c
+++ b/src/nact/nact-iaction-tab.c
@@ -61,7 +61,6 @@ static GType register_type( void );
static void interface_base_init( NactIActionTabInterface *klass );
static void interface_base_finalize( NactIActionTabInterface *klass );
-static NAObject *v_get_selected( NactWindow *window );
static NAAction *v_get_edited_action( NactWindow *window );
static void v_field_modified( NactWindow *window );
@@ -214,11 +213,9 @@ nact_iaction_tab_dispose( NactWindow *dialog )
* than one profile
*/
void
-nact_iaction_tab_set_action( NactWindow *dialog, const NAAction *action )
+nact_iaction_tab_set_action( NactWindow *dialog, const NAAction *action, GSList *selected_items )
{
- /*static const gchar *thisfn = "nact_iaction_tab_set_action";
- g_debug( "%s: dialog=%p, action=%p", thisfn, dialog, action );*/
-
+ static const gchar *thisfn = "nact_iaction_tab_set_action";
NAObject *current;
gboolean enabled;
GtkWidget *label_widget, *tooltip_widget, *icon_widget, *button;
@@ -226,11 +223,16 @@ nact_iaction_tab_set_action( NactWindow *dialog, const NAAction *action )
GtkButton *enabled_button;
gboolean enabled_action;
- current = v_get_selected( dialog );
- enabled = ( action != NULL );
- if( NA_IS_ACTION_PROFILE( current)){
- if( na_action_get_profiles_count( action ) > 1 ){
- enabled = FALSE;
+ g_debug( "%s: dialog=%p, action=%p, selected_items=%p",
+ thisfn, ( void * ) dialog, ( void * ) action, ( void * ) selected_items );
+
+ enabled = ( action != NULL && selected_items != NULL && g_slist_length( selected_items ) == 1 );
+ if( enabled ){
+ current = NA_OBJECT( selected_items->data );
+ if( NA_IS_ACTION_PROFILE( current)){
+ if( na_action_get_profiles_count( action ) > 1 ){
+ enabled = FALSE;
+ }
}
}
@@ -279,18 +281,6 @@ nact_iaction_tab_has_label( NactWindow *window )
return( g_utf8_strlen( label, -1 ) > 0 );
}
-static NAObject *
-v_get_selected( NactWindow *window )
-{
- g_assert( NACT_IS_IACTION_TAB( window ));
-
- if( NACT_IACTION_TAB_GET_INTERFACE( window )->get_selected ){
- return( NACT_IACTION_TAB_GET_INTERFACE( window )->get_selected( window ));
- }
-
- return( NULL );
-}
-
static NAAction *
v_get_edited_action( NactWindow *window )
{
diff --git a/src/nact/nact-iaction-tab.h b/src/nact/nact-iaction-tab.h
index 9c27655..9d4d72a 100644
--- a/src/nact/nact-iaction-tab.h
+++ b/src/nact/nact-iaction-tab.h
@@ -68,7 +68,7 @@ void nact_iaction_tab_runtime_init( NactWindow *dialog );
void nact_iaction_tab_all_widgets_showed( NactWindow *dialog );
void nact_iaction_tab_dispose( NactWindow *dialog );
-void nact_iaction_tab_set_action( NactWindow *dialog, const NAAction *action );
+void nact_iaction_tab_set_action( NactWindow *dialog, const NAAction *action, GSList *selected_items );
gboolean nact_iaction_tab_has_label( NactWindow *window );
G_END_DECLS
diff --git a/src/nact/nact-iactions-list.c b/src/nact/nact-iactions-list.c
index 28191f7..2693319 100644
--- a/src/nact/nact-iactions-list.c
+++ b/src/nact/nact-iactions-list.c
@@ -35,8 +35,11 @@
#include <gdk/gdkkeysyms.h>
#include <string.h>
+#include <common/na-iprefs.h>
+
#include "nact-application.h"
#include "nact-iactions-list.h"
+#include "nact-tree-model.h"
/* private interface data
*/
@@ -44,28 +47,19 @@ struct NactIActionsListInterfacePrivate {
void *empty; /* so that gcc -pedantic is happy */
};
-/* column ordering
- */
-enum {
- IACTIONS_LIST_ICON_COLUMN = 0,
- IACTIONS_LIST_LABEL_COLUMN,
- IACTIONS_LIST_NAOBJECT_COLUMN,
- IACTIONS_LIST_N_COLUMN
-};
-
/* data set against GObject
*/
#define IS_EDITION_MODE "iactions-list-edition-mode"
#define ACCEPT_MULTIPLE_SELECTION "iactions-list-accept-multiple-selection"
#define IS_FILLING_LIST "iactions-list-is-filling-list"
#define SEND_SELECTION_CHANGED_MESSAGE "iactions-list-send-selection-changed-message"
+#define HAVE_DND_MODE "iactions-list-dnd-mode"
static GType register_type( void );
static void interface_base_init( NactIActionsListInterface *klass );
static void interface_base_finalize( NactIActionsListInterface *klass );
static GSList *v_get_actions( NactWindow *window );
-static void v_on_selection_changed( GtkTreeSelection *selection, gpointer user_data );
static gboolean v_on_button_press_event( GtkWidget *widget, GdkEventButton *event, gpointer data );
static gboolean v_on_key_pressed_event( GtkWidget *widget, GdkEventKey *event, gpointer data );
static gboolean v_is_modified_action( NactWindow *window, const NAAction *action );
@@ -73,11 +67,10 @@ static gboolean v_is_valid_action( NactWindow *window, const NAAction *action
static gboolean v_is_modified_profile( NactWindow *window, const NAActionProfile *profile );
static gboolean v_is_valid_profile( NactWindow *window, const NAActionProfile *profile );
+static void on_selection_changed( GtkTreeSelection *selection, NactIActionsList *instance );
static void display_label( GtkTreeViewColumn *column, GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, NactWindow *window );
static void setup_action( GtkWidget *treeview, GtkTreeStore *model, GtkTreeIter *iter, NAAction *action );
static void setup_profile( GtkWidget *treeview, GtkTreeStore *model, GtkTreeIter *iter, NAActionProfile *profile );
-static gint sort_actions_list( GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, NactWindow *window );
-static gboolean filter_visible( GtkTreeModel *model, GtkTreeIter *iter, gpointer data );
static GtkWidget *get_actions_list_widget( NactWindow *window );
static GSList *get_expanded_rows( NactWindow *window );
static void expand_rows( NactWindow *window, GSList *expanded );
@@ -155,14 +148,19 @@ interface_base_finalize( NactIActionsListInterface *klass )
/**
* Allocates and initializes the ActionsList widget.
+ *
+ * GtkTreeView is created with NactTreeModel model
+ * NactTreeModel
+ * implements EggTreeMultiDragSourceIface
+ * is derived from GtkTreeModelFilter
+ * GtkTreeModelFilter is built on top of GtkTreeStore
*/
void
nact_iactions_list_initial_load( NactWindow *window )
{
static const gchar *thisfn = "nact_iactions_list_initial_load";
GtkWidget *widget, *label;
- GtkTreeStore *ts_model;
- GtkTreeModel *tmf_model;
+ NactTreeModel *model;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
@@ -179,28 +177,10 @@ nact_iactions_list_initial_load( NactWindow *window )
nact_iactions_list_set_send_selection_changed_on_fill_list( window, FALSE );
nact_iactions_list_set_is_filling_list( window, FALSE );
- /* create the model */
- ts_model = gtk_tree_store_new(
- IACTIONS_LIST_N_COLUMN, GDK_TYPE_PIXBUF, G_TYPE_STRING, NA_OBJECT_TYPE );
-
- gtk_tree_sortable_set_default_sort_func(
- GTK_TREE_SORTABLE( ts_model ),
- ( GtkTreeIterCompareFunc ) sort_actions_list, window, NULL );
-
- gtk_tree_sortable_set_sort_column_id(
- GTK_TREE_SORTABLE( ts_model ),
- IACTIONS_LIST_LABEL_COLUMN, GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID );
-
- tmf_model = gtk_tree_model_filter_new( GTK_TREE_MODEL( ts_model ), NULL );
-
- gtk_tree_model_filter_set_visible_func(
- GTK_TREE_MODEL_FILTER( tmf_model ), ( GtkTreeModelFilterVisibleFunc ) filter_visible, window, NULL );
-
- gtk_tree_view_set_model( GTK_TREE_VIEW( widget ), tmf_model );
+ model = nact_tree_model_new( NACT_MAIN_WINDOW( window ));
+ gtk_tree_view_set_model( GTK_TREE_VIEW( widget ), GTK_TREE_MODEL( model ));
gtk_tree_view_set_enable_tree_lines( GTK_TREE_VIEW( widget ), TRUE );
-
- g_object_unref( tmf_model );
- g_object_unref( ts_model );
+ g_object_unref( model );
/* create visible columns on the tree view */
column = gtk_tree_view_column_new_with_attributes(
@@ -237,13 +217,14 @@ nact_iactions_list_runtime_init( NactWindow *window )
g_assert( GTK_IS_WIDGET( widget ));
nact_iactions_list_fill( window, TRUE );
+ nact_tree_model_runtime_init_dnd( NACT_MAIN_WINDOW( window ), GTK_TREE_VIEW( widget ));
- /* set up selection */
+ /* set up selection control */
nact_window_signal_connect(
window,
G_OBJECT( gtk_tree_view_get_selection( GTK_TREE_VIEW( widget ))),
"changed",
- G_CALLBACK( v_on_selection_changed ));
+ G_CALLBACK( on_selection_changed ));
/* catch press 'Enter' */
nact_window_signal_connect(
@@ -344,6 +325,9 @@ nact_iactions_list_fill( NactWindow *window, gboolean keep_expanded )
* - whose uuid is the requested uuid
* - or whose label is the most close of the required label (if uuid is not found)
*
+ * if label is NULL, then we consider that the action must be found, and we
+ * explore the whole list for the uuid ; if not found, we select the last item.
+ *
* if we want select a profile
* - set type = NA_ACTION_PROFILE_TYPE
* - set uuid = uuid of the parent action
@@ -385,7 +369,7 @@ nact_iactions_list_set_selection( NactWindow *window, GType type, const gchar *u
if( !uuid || !strlen( uuid )){
g_debug( "%s: null or empty uuid: unselect all", thisfn );
gtk_tree_selection_unselect_all( selection );
- v_on_selection_changed( selection, window );
+ on_selection_changed( selection, NACT_IACTIONS_LIST( window ));
return;
}
@@ -394,7 +378,7 @@ nact_iactions_list_set_selection( NactWindow *window, GType type, const gchar *u
if( !iterok ){
g_debug( "%s: empty actions list: unselect all", thisfn );
gtk_tree_selection_unselect_all( selection );
- v_on_selection_changed( selection, window );
+ on_selection_changed( selection, NACT_IACTIONS_LIST( window ));
return;
}
@@ -407,7 +391,7 @@ nact_iactions_list_set_selection( NactWindow *window, GType type, const gchar *u
nb_profiles = na_action_get_profiles_count( NA_ACTION( iter_object ));
if( type == NA_ACTION_TYPE || ( ret_uuid == 0 && nb_profiles == 1 )){
- ret_label = g_utf8_collate( iter_label, label );
+ ret_label = label ? g_utf8_collate( iter_label, label ) : -1;
if( ret_uuid == 0 || ret_label > 0 ){
g_debug( "%s: selecting action iter_object=%p, ret_uuid=%d, ret_label=%d", thisfn, ( void * ) iter_object, ret_uuid, ret_label );
@@ -469,7 +453,7 @@ nact_iactions_list_select_first( NactWindow *window )
if( !iterok ){
g_debug( "%s: empty actions list: unselect all", thisfn );
gtk_tree_selection_unselect_all( selection );
- v_on_selection_changed( selection, window );
+ on_selection_changed( selection, NACT_IACTIONS_LIST( window ));
return;
}
@@ -488,6 +472,7 @@ nact_iactions_list_set_focus( NactWindow *window )
/**
* Returns the currently selected action or profile.
+ * TODO: remove this function
*/
NAObject *
nact_iactions_list_get_selected_object( NactWindow *window )
@@ -517,34 +502,47 @@ nact_iactions_list_get_selected_object( NactWindow *window )
*
* The returned GSList should be freed by the caller (g_slist_free),
* without freing not unref any of the contained objects.
+ * TODO: remove this function
*/
GSList *
nact_iactions_list_get_selected_actions( NactWindow *window )
{
- GSList *actions = NULL;
+ return( nact_iactions_list_get_selected_items( NACT_IACTIONS_LIST( window )));
+}
+
+/**
+ * Returns the currently selected actions when in export mode.
+ *
+ * The returned GSList should be g_slist_free() by the caller,
+ * without freing nor unref any of the contained objects.
+ */
+GSList *
+nact_iactions_list_get_selected_items( NactIActionsList *instance )
+{
+ GSList *items = NULL;
GtkWidget *treeview;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
GList *it, *listrows;
- NAAction *action;
+ NAObject *object;
GtkTreePath *path;
- treeview = get_actions_list_widget( window );
+ treeview = get_actions_list_widget( NACT_WINDOW( instance ));
selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( treeview ));
listrows = gtk_tree_selection_get_selected_rows( selection, &model );
for( it = listrows ; it ; it = it->next ){
path = ( GtkTreePath * ) it->data;
gtk_tree_model_get_iter( model, &iter, path );
- gtk_tree_model_get( model, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, &action, -1 );
- actions = g_slist_prepend( actions, action );
+ gtk_tree_model_get( model, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
+ items = g_slist_prepend( items, object );
}
g_list_foreach( listrows, ( GFunc ) gtk_tree_path_free, NULL );
g_list_free( listrows );
- return( g_slist_reverse( actions ));
+ return( g_slist_reverse( items ));
}
/*void
@@ -635,24 +633,29 @@ nact_iactions_list_toggle_collapse( NactWindow *window, const NAAction *action )
void
nact_iactions_list_update_selected( NactWindow *window, NAAction *action )
{
- GtkWidget *treeview = get_actions_list_widget( window );
- GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( treeview ));
-
+ GtkWidget *treeview;
+ GtkTreeSelection *selection;
+ GList *listrows, *irow;
GtkTreeModel *tm_model;
+ GtkTreePath *path;
GtkTreeIter iter;
NAObject *object;
- GtkTreePath *path;
GtkTreeModelFilter *tmf_model;
GtkTreeStore *ts_model;
GtkTreeIter ts_iter;
GSList *profiles, *ip;
- if( gtk_tree_selection_get_selected( selection, &tm_model, &iter )){
+ treeview = get_actions_list_widget( window );
+ selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( treeview ));
+ listrows = gtk_tree_selection_get_selected_rows( selection, &tm_model );
+
+ for( irow = listrows ; irow ; irow = irow->next ){
+ path = ( GtkTreePath * ) irow->data;
+ gtk_tree_model_get_iter( tm_model, &iter, path );
gtk_tree_model_get( tm_model, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
g_assert( object );
-
- path = gtk_tree_model_get_path( tm_model, &iter );
+ g_assert( NA_IS_OBJECT( object ));
tmf_model = GTK_TREE_MODEL_FILTER( gtk_tree_view_get_model( GTK_TREE_VIEW( treeview )));
ts_model = GTK_TREE_STORE( gtk_tree_model_filter_get_model( tmf_model ));
@@ -730,6 +733,16 @@ nact_iactions_list_set_is_filling_list( NactWindow *window, gboolean is_filling
g_object_set_data( G_OBJECT( window ), IS_FILLING_LIST, GINT_TO_POINTER( is_filling ));
}
+/**
+ * Does the TreeView implements Drag&Drop ?
+ */
+void
+nact_iactions_list_set_dnd_mode( NactWindow *window, gboolean have_dnd )
+{
+ g_assert( NACT_IS_IACTIONS_LIST( window ));
+ g_object_set_data( G_OBJECT( window ), HAVE_DND_MODE, GINT_TO_POINTER( have_dnd ));
+}
+
static GSList *
v_get_actions( NactWindow *window )
{
@@ -756,28 +769,6 @@ v_set_sorted_actions( NactWindow *window, GSList *actions )
}
}*/
-static void
-v_on_selection_changed( GtkTreeSelection *selection, gpointer user_data )
-{
- NactIActionsList *instance;
- gboolean send_message, is_filling;
-
- g_assert( NACT_IS_IACTIONS_LIST( user_data ));
- g_assert( BASE_IS_WINDOW( user_data ));
-
- instance = NACT_IACTIONS_LIST( user_data );
-
- g_assert( NACT_IS_WINDOW( user_data ));
- send_message = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( user_data ), SEND_SELECTION_CHANGED_MESSAGE ));
- is_filling = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( user_data ), IS_FILLING_LIST ));
-
- if( send_message || !is_filling ){
- if( NACT_IACTIONS_LIST_GET_INTERFACE( instance )->on_selection_changed ){
- NACT_IACTIONS_LIST_GET_INTERFACE( instance )->on_selection_changed( selection, user_data );
- }
- }
-}
-
static gboolean
v_on_button_press_event( GtkWidget *widget, GdkEventButton *event, gpointer user_data )
{
@@ -879,6 +870,24 @@ v_is_valid_profile( NactWindow *window, const NAActionProfile *profile )
return( FALSE );
}
+static void
+on_selection_changed( GtkTreeSelection *selection, NactIActionsList *instance )
+{
+ gboolean send_message, is_filling;
+ GSList *selected_items;
+
+ send_message = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( instance ), SEND_SELECTION_CHANGED_MESSAGE ));
+ is_filling = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( instance ), IS_FILLING_LIST ));
+
+ selected_items = nact_iactions_list_get_selected_items( instance );
+
+ if( send_message || !is_filling ){
+ if( NACT_IACTIONS_LIST_GET_INTERFACE( instance )->on_selection_changed ){
+ NACT_IACTIONS_LIST_GET_INTERFACE( instance )->on_selection_changed( instance, selected_items );
+ }
+ }
+}
+
/*
* action modified: italic
* action not saveable: red
@@ -974,44 +983,6 @@ setup_profile( GtkWidget *treeview, GtkTreeStore *model, GtkTreeIter *iter, NAAc
g_free( label );
}
-static gint
-sort_actions_list( GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, NactWindow *window )
-{
- gchar *labela, *labelb;
- gint ret;
-
- gtk_tree_model_get( model, a, IACTIONS_LIST_LABEL_COLUMN, &labela, -1 );
- gtk_tree_model_get( model, b, IACTIONS_LIST_LABEL_COLUMN, &labelb, -1 );
-
- ret = g_utf8_collate( labela, labelb );
-
- g_free( labela );
- g_free( labelb );
-
- return( ret );
-}
-
-static gboolean
-filter_visible( GtkTreeModel *model, GtkTreeIter *iter, gpointer data )
-{
- NAObject *object;
- NAAction *action;
-
- gtk_tree_model_get( model, iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
-
- if( object ){
- if( NA_IS_ACTION( object )){
- return( TRUE );
- }
-
- g_assert( NA_IS_ACTION_PROFILE( object ));
- action = na_action_profile_get_action( NA_ACTION_PROFILE( object ));
- return( na_action_get_profiles_count( action ) > 1 );
- }
-
- return( FALSE );
-}
-
static GtkWidget *
get_actions_list_widget( NactWindow *window )
{
diff --git a/src/nact/nact-iactions-list.h b/src/nact/nact-iactions-list.h
index f591a0f..17cf327 100644
--- a/src/nact/nact-iactions-list.h
+++ b/src/nact/nact-iactions-list.h
@@ -60,7 +60,7 @@ typedef struct {
/* api */
GSList * ( *get_actions ) ( NactWindow *window );
- void ( *on_selection_changed ) ( GtkTreeSelection *selection, gpointer user_data );
+ void ( *on_selection_changed ) ( NactIActionsList *instance, GSList *selected_items );
gboolean ( *on_button_press_event )( GtkWidget *widget, GdkEventButton *event, gpointer data );
gboolean ( *on_key_pressed_event ) ( GtkWidget *widget, GdkEventKey *event, gpointer data );
gboolean ( *on_double_click ) ( GtkWidget *widget, GdkEventButton *event, gpointer data );
@@ -80,6 +80,7 @@ void nact_iactions_list_runtime_init( NactWindow *window );
void nact_iactions_list_fill( NactWindow *window, gboolean keep_expanded );
NAObject *nact_iactions_list_get_selected_object( NactWindow *window );
GSList * nact_iactions_list_get_selected_actions( NactWindow *window );
+GSList *nact_iactions_list_get_selected_items( NactIActionsList *instance );
void nact_iactions_list_set_selection( NactWindow *window, GType type, const gchar *uuid, const gchar *label );
void nact_iactions_list_select_first( NactWindow *window );
/*void nact_iactions_list_set_focus( NactWindow *window );*/
@@ -92,6 +93,7 @@ void nact_iactions_list_set_edition_mode( NactWindow *window, gboolean mode
void nact_iactions_list_set_multiple_selection( NactWindow *window, gboolean multiple );
void nact_iactions_list_set_send_selection_changed_on_fill_list( NactWindow *window, gboolean send_message );
void nact_iactions_list_set_is_filling_list( NactWindow *window, gboolean is_filling );
+void nact_iactions_list_set_dnd_mode( NactWindow *window, gboolean have_dnd );
G_END_DECLS
diff --git a/src/nact/nact-imenubar.c b/src/nact/nact-imenubar.c
index ba58a22..867ac04 100644
--- a/src/nact/nact-imenubar.c
+++ b/src/nact/nact-imenubar.c
@@ -59,9 +59,9 @@ static void interface_base_finalize( NactIMenubarInterface *klass );
static void on_file_menu_selected( GtkMenuItem *item, NactWindow *window );
static void on_new_action_activated( GtkMenuItem *item, NactWindow *window );
static void on_new_profile_activated( GtkMenuItem *item, NactWindow *window );
+static void on_new_menu_activated( GtkMenuItem *item, NactWindow *window );
static void on_save_activated( GtkMenuItem *item, NactWindow *window );
static void on_quit_activated( GtkMenuItem *item, NactWindow *window );
-static void add_action( NactWindow *window, NAAction *action );
static void add_profile( NactWindow *window, NAAction *action, NAActionProfile *profile );
static void on_edit_menu_selected( GtkMenuItem *item, NactWindow *window );
@@ -81,7 +81,7 @@ static void on_about_activated( GtkMenuItem *item, NactWindow *window );
static void on_menu_item_selected( GtkItem *item, NactWindow *window );
static void on_menu_item_deselected( GtkItem *item, NactWindow *window );
-static void v_add_action( NactWindow *window, NAAction *action );
+static void v_insert_item( NactWindow *window, NAAction *action );
static void v_add_profile( NactWindow *window, NAActionProfile *profile );
static void v_remove_action( NactWindow *window, NAAction *action );
static GSList *v_get_deleted_actions( NactWindow *window );
@@ -111,6 +111,10 @@ static const GtkActionEntry entries[] = {
/* i18n: tooltip displayed in the status bar when selecting the 'New profile' item */
N_( "Define a new profile attached to the current action." ),
G_CALLBACK( on_new_profile_activated ) },
+ { "NewMenuItem", NULL, N_( "_New _menu" ), "<Ctrl>M",
+ /* i18n: tooltip displayed in the status bar when selecting the 'New menu' item */
+ N_( "Insert a new menu at the current position." ),
+ G_CALLBACK( on_new_menu_activated ) },
{ "SaveItem", GTK_STOCK_SAVE, NULL, NULL,
/* i18n: tooltip displayed in the status bar when selecting 'Save' item */
N_( "Record all the modified actions. Invalid actions will be silently ignored." ),
@@ -169,6 +173,9 @@ static const MenuOnSelectedStruct menu_callback[] = {
};
/* associates action with menu to be able to build their path
+ * should also be done by parsing the XML definition file (beurk !)
+ * or if Gtk+ would give the path of a given action (maybe in the
+ * future ??)
*/
typedef struct {
char *menu;
@@ -179,6 +186,7 @@ typedef struct {
static const MenuActionStruct menu_actions[] = {
{ "FileMenu", "NewActionItem" },
{ "FileMenu", "NewProfileItem" },
+ { "FileMenu", "NewMenuItem" },
{ "FileMenu", "SaveItem" },
{ "FileMenu", "QuitItem" },
{ "EditMenu", "DuplicateItem" },
@@ -384,7 +392,7 @@ static void
on_new_action_activated( GtkMenuItem *item, NactWindow *window )
{
NAAction *action = na_action_new_with_profile();
- add_action( window, action );
+ v_insert_item( window, action );
}
static void
@@ -412,6 +420,13 @@ on_new_profile_activated( GtkMenuItem *item, NactWindow *window )
}
static void
+on_new_menu_activated( GtkMenuItem *item, NactWindow *window )
+{
+ NAActionMenu *menu = na_action_menu_new();
+ v_insert_item( window, NA_ACTION( menu ));
+}
+
+static void
on_save_activated( GtkMenuItem *item, NactWindow *window )
{
static const gchar *thisfn = "nact_imenubar_on_save_activated";
@@ -523,25 +538,6 @@ on_quit_activated( GtkMenuItem *item, NactWindow *window )
}
static void
-add_action( NactWindow *window, NAAction *action )
-{
- gchar *uuid, *label;
-
- na_object_check_edited_status( NA_OBJECT( action ));
- v_add_action( window, action );
-
- v_update_actions_list( window );
-
- uuid = na_action_get_uuid( action );
- label = na_action_get_label( action );
- v_select_actions_list( window, NA_ACTION_TYPE, uuid, label );
- g_free( label );
- g_free( uuid );
-
- v_setup_dialog_title( window );
-}
-
-static void
add_profile( NactWindow *window, NAAction *action, NAActionProfile *profile )
{
gchar *uuid, *label;
@@ -617,7 +613,7 @@ on_duplicate_activated( GtkMenuItem *item, NactWindow *window )
g_free( dup_label );
g_free( label );
- add_action( window, NA_ACTION( dup ));
+ v_insert_item( window, NA_ACTION( dup ));
} else {
g_assert( NA_IS_ACTION_PROFILE( object ));
@@ -643,14 +639,13 @@ on_delete_activated( GtkMenuItem *item, NactWindow *window )
GType type;
NAAction *action;
- g_debug( "%s: item=%p, window=%p", thisfn, ( void * ) item, ( void * ) window );
-
object = v_get_selected( window );
- g_debug( "%s: object=%p", thisfn, ( void * ) object );
if( !object ){
return;
}
+ g_debug( "%s: item=%p, window=%p", thisfn, ( void * ) item, ( void * ) window );
+
if( NA_IS_ACTION( object )){
uuid = na_action_get_uuid( NA_ACTION( object ));
label = na_action_get_label( NA_ACTION( object ));
@@ -727,7 +722,7 @@ on_import_activated( GtkMenuItem *item, NactWindow *window )
GSList *list = nact_assistant_import_run( window );
GSList *ia;
for( ia = list ; ia ; ia = ia->next ){
- add_action( window, NA_ACTION( ia->data ));
+ v_insert_item( window, NA_ACTION( ia->data ));
}
}
@@ -786,10 +781,12 @@ on_menu_item_deselected( GtkItem *item, NactWindow *window )
}
static void
-v_add_action( NactWindow *window, NAAction *action )
+v_insert_item( NactWindow *window, NAAction *action )
{
- if( NACT_IMENUBAR_GET_INTERFACE( window )->add_action ){
- NACT_IMENUBAR_GET_INTERFACE( window )->add_action( window, action );
+ na_object_check_edited_status( NA_OBJECT( action ));
+
+ if( NACT_IMENUBAR_GET_INTERFACE( window )->insert_item ){
+ NACT_IMENUBAR_GET_INTERFACE( window )->insert_item( window, action );
}
}
diff --git a/src/nact/nact-imenubar.h b/src/nact/nact-imenubar.h
index 55c4f4b..bbff770 100644
--- a/src/nact/nact-imenubar.h
+++ b/src/nact/nact-imenubar.h
@@ -55,7 +55,7 @@ typedef struct {
NactIMenubarInterfacePrivate *private;
/* api */
- void ( *add_action ) ( NactWindow *window, NAAction* action );
+ void ( *insert_item ) ( NactWindow *window, NAAction* action );
void ( *add_profile ) ( NactWindow *window, NAActionProfile *profile );
void ( *remove_action ) ( NactWindow *window, NAAction *action );
GSList * ( *get_deleted_actions ) ( NactWindow *window );
diff --git a/src/nact/nact-main-window.c b/src/nact/nact-main-window.c
index d937f05..bf524e5 100644
--- a/src/nact/nact-main-window.c
+++ b/src/nact/nact-main-window.c
@@ -40,6 +40,7 @@
#include <common/na-pivot.h>
#include <common/na-iio-provider.h>
#include <common/na-ipivot-consumer.h>
+#include <common/na-iprefs.h>
#include "nact-application.h"
#include "nact-iactions-list.h"
@@ -83,6 +84,7 @@ static void iconditions_tab_iface_init( NactIConditionsTabInterface
static void iadvanced_tab_iface_init( NactIAdvancedTabInterface *iface );
static void imenubar_iface_init( NactIMenubarInterface *iface );
static void ipivot_consumer_iface_init( NAIPivotConsumerInterface *iface );
+static void iprefs_iface_init( NAIPrefsInterface *iface );
static void instance_init( GTypeInstance *instance, gpointer klass );
static void instance_dispose( GObject *application );
static void instance_finalize( GObject *application );
@@ -95,11 +97,11 @@ static void on_initial_load_toplevel( BaseWindow *window );
static void on_runtime_init_toplevel( BaseWindow *window );
static void setup_dialog_title( NactWindow *window );
-static void on_actions_list_selection_changed( GtkTreeSelection *selection, gpointer user_data );
+static void on_actions_list_selection_changed( NactIActionsList *instance, GSList *selected_items );
static gboolean on_actions_list_double_click( GtkWidget *widget, GdkEventButton *event, gpointer data );
static gboolean on_actions_list_enter_key_pressed( GtkWidget *widget, GdkEventKey *event, gpointer data );
-static void set_current_action( NactMainWindow *window );
-static void set_current_profile( NactMainWindow *window, gboolean set_action );
+static void set_current_action( NactMainWindow *window, GSList *selected_items );
+static void set_current_profile( NactMainWindow *window, gboolean set_action, GSList *selected_items );
static NAAction *get_edited_action( NactWindow *window );
static NAActionProfile *get_edited_profile( NactWindow *window );
static void on_modified_field( NactWindow *window );
@@ -112,7 +114,7 @@ static void get_isfiledir( NactWindow *window, gboolean *isfile, gbo
static gboolean get_multiple( NactWindow *window );
static GSList *get_schemes( NactWindow *window );
-static void add_action( NactWindow *window, NAAction *action );
+static void insert_item( NactWindow *window, NAAction *action );
static void add_profile( NactWindow *window, NAActionProfile *profile );
static void remove_action( NactWindow *window, NAAction *action );
static GSList *get_deleted_actions( NactWindow *window );
@@ -126,6 +128,8 @@ static gint count_modified_actions( NactWindow *window );
static void reload_actions( NactWindow *window );
static GSList *free_actions( GSList *actions );
static void on_actions_changed( NAIPivotConsumer *instance, gpointer user_data );
+static void on_display_order_changed( NAIPivotConsumer *instance, gpointer user_data );
+static void sort_actions_list( NactMainWindow *window );
GType
nact_main_window_get_type( void )
@@ -193,12 +197,18 @@ register_type( void )
NULL
};
- static const GInterfaceInfo pivot_consumer_iface_info = {
+ static const GInterfaceInfo ipivot_consumer_iface_info = {
( GInterfaceInitFunc ) ipivot_consumer_iface_init,
NULL,
NULL
};
+ static const GInterfaceInfo iprefs_iface_info = {
+ ( GInterfaceInitFunc ) iprefs_iface_init,
+ NULL,
+ NULL
+ };
+
g_debug( "%s", thisfn );
type = g_type_register_static( NACT_WINDOW_TYPE, "NactMainWindow", &info, 0 );
@@ -215,7 +225,9 @@ register_type( void )
g_type_add_interface_static( type, NACT_IMENUBAR_TYPE, &imenubar_iface_info );
- g_type_add_interface_static( type, NA_IPIVOT_CONSUMER_TYPE, &pivot_consumer_iface_info );
+ g_type_add_interface_static( type, NA_IPIVOT_CONSUMER_TYPE, &ipivot_consumer_iface_info );
+
+ g_type_add_interface_static( type, NA_IPREFS_TYPE, &iprefs_iface_info );
return( type );
}
@@ -273,7 +285,6 @@ iaction_tab_iface_init( NactIActionTabInterface *iface )
g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
- iface->get_selected = nact_iactions_list_get_selected_object;
iface->get_edited_action = get_edited_action;
iface->field_modified = on_modified_field;
}
@@ -321,7 +332,7 @@ imenubar_iface_init( NactIMenubarInterface *iface )
g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
- iface->add_action = add_action;
+ iface->insert_item = insert_item;
iface->add_profile = add_profile;
iface->remove_action = remove_action;
iface->get_deleted_actions = get_deleted_actions;
@@ -345,6 +356,15 @@ ipivot_consumer_iface_init( NAIPivotConsumerInterface *iface )
g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
iface->on_actions_changed = on_actions_changed;
+ iface->on_display_order_changed = on_display_order_changed;
+}
+
+static void
+iprefs_iface_init( NAIPrefsInterface *iface )
+{
+ static const gchar *thisfn = "nact_main_window_iprefs_iface_init";
+
+ g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
}
static void
@@ -503,6 +523,7 @@ on_initial_load_toplevel( BaseWindow *window )
NactMainWindow *wnd;
gint pos;
GtkWidget *pane;
+ /*gboolean alpha_order;*/
/* call parent class at the very beginning */
if( BASE_WINDOW_CLASS( st_parent_class )->initial_load_toplevel ){
@@ -518,8 +539,12 @@ on_initial_load_toplevel( BaseWindow *window )
g_assert( NACT_IS_IACTIONS_LIST( window ));
nact_iactions_list_initial_load( NACT_WINDOW( window ));
nact_iactions_list_set_edition_mode( NACT_WINDOW( window ), TRUE );
- nact_iactions_list_set_multiple_selection( NACT_WINDOW( window ), FALSE );
nact_iactions_list_set_send_selection_changed_on_fill_list( NACT_WINDOW( window ), FALSE );
+ nact_iactions_list_set_multiple_selection( NACT_WINDOW( window ), TRUE );
+
+ /*alpha_order = na_iprefs_get_alphabetical_order( NA_IPREFS( window ));
+ nact_iactions_list_set_multiple_selection( NACT_WINDOW( window ), !alpha_order );
+ nact_iactions_list_set_dnd_mode( NACT_WINDOW( window ), !alpha_order );*/
g_assert( NACT_IS_IACTION_TAB( window ));
nact_iaction_tab_initial_load( NACT_WINDOW( window ));
@@ -589,7 +614,9 @@ on_runtime_init_toplevel( BaseWindow *window )
/* forces a no-selection when the list is initially empty
*/
if( !wnd->private->initial_count ){
- set_current_action( NACT_MAIN_WINDOW( window ));
+ set_current_action( NACT_MAIN_WINDOW( window ), NULL );
+ } else {
+ nact_iactions_list_select_first( NACT_WINDOW( window ));
}
}
@@ -620,38 +647,36 @@ setup_dialog_title( NactWindow *window )
g_free( title );
}
-/*
- * note that the IActionsList tree store may return an action or a profile
- */
static void
-on_actions_list_selection_changed( GtkTreeSelection *selection, gpointer user_data )
+on_actions_list_selection_changed( NactIActionsList *instance, GSList *selected_items )
{
static const gchar *thisfn = "nact_main_window_on_actions_list_selection_changed";
NactMainWindow *window;
NAObject *object;
+ gint count;
- g_debug( "%s: selection=%p, user_data=%p", thisfn, ( void * ) selection, ( void * ) user_data );
+ g_debug( "%s: instance=%p, selected_items=%p", thisfn, ( void * ) instance, ( void * ) selected_items );
- g_assert( NACT_IS_MAIN_WINDOW( user_data ));
- window = NACT_MAIN_WINDOW( user_data );
+ g_assert( NACT_IS_MAIN_WINDOW( instance ));
+ window = NACT_MAIN_WINDOW( instance );
- object = nact_iactions_list_get_selected_object( NACT_WINDOW( window ));
- g_debug( "%s: object=%p", thisfn, ( void * ) object );
+ count = g_slist_length( selected_items );
- if( object ){
+ if( count == 1 ){
+ object = NA_OBJECT( selected_items->data );
if( NA_IS_ACTION( object )){
window->private->edited_action = NA_ACTION( object );
- set_current_action( window );
+ set_current_action( window, selected_items );
} else {
g_assert( NA_IS_ACTION_PROFILE( object ));
window->private->edited_profile = NA_ACTION_PROFILE( object );
- set_current_profile( window, TRUE );
+ set_current_profile( window, TRUE, selected_items );
}
} else {
window->private->edited_action = NULL;
- set_current_action( window );
+ set_current_action( window, selected_items );
}
}
@@ -680,11 +705,14 @@ on_actions_list_enter_key_pressed( GtkWidget *widget, GdkEventKey *event, gpoint
* if there is only one profile, we also setup the profile
*/
static void
-set_current_action( NactMainWindow *window )
+set_current_action( NactMainWindow *window, GSList *selected_items )
{
- g_debug( "set_current_action: current=%p", ( void * ) window->private->edited_action );
+ static const gchar *thisfn = "nact_main_window_set_current_action";
- nact_iaction_tab_set_action( NACT_WINDOW( window ), window->private->edited_action );
+ g_debug( "%s: window=%p, current=%p, selected_items=%p",
+ thisfn, ( void * ) window, ( void * ) window->private->edited_action, ( void * ) selected_items );
+
+ nact_iaction_tab_set_action( NACT_WINDOW( window ), window->private->edited_action, selected_items );
window->private->edited_profile = NULL;
@@ -694,16 +722,21 @@ set_current_action( NactMainWindow *window )
}
}
- set_current_profile( window, FALSE );
+ set_current_profile( window, FALSE, selected_items );
}
static void
-set_current_profile( NactMainWindow *window, gboolean set_action )
+set_current_profile( NactMainWindow *window, gboolean set_action, GSList *selected_items )
{
+ static const gchar *thisfn = "nact_main_window_set_current_profile";
+
+ g_debug( "%s: window=%p, set_action=%s, selected_items=%p",
+ thisfn, ( void * ) window, set_action ? "True":"False", ( void * ) selected_items );
+
if( window->private->edited_profile && set_action ){
NAAction *action = NA_ACTION( na_action_profile_get_action( window->private->edited_profile ));
window->private->edited_action = action;
- nact_iaction_tab_set_action( NACT_WINDOW( window ), window->private->edited_action );
+ nact_iaction_tab_set_action( NACT_WINDOW( window ), window->private->edited_action, selected_items );
}
nact_icommand_tab_set_profile( NACT_WINDOW( window ), window->private->edited_profile );
@@ -789,11 +822,44 @@ get_schemes( NactWindow *window )
return( nact_iadvanced_tab_get_schemes( window ));
}
+/*
+ * insert an item (action or menu) in the list:
+ * - the last position if the list is sorted, and sort it
+ * - at the current position if the list is not sorted
+ *
+ * set the selection on the new item
+ */
static void
-add_action( NactWindow *window, NAAction *action )
+insert_item( NactWindow *window, NAAction *item )
{
NactMainWindow *wnd = NACT_MAIN_WINDOW( window );
- wnd->private->actions = g_slist_prepend( wnd->private->actions, ( gpointer ) action );
+ gboolean alpha_order;
+ gchar *uuid;
+ NAAction *current;
+ gint index;
+
+ alpha_order = na_iprefs_get_alphabetical_order( NA_IPREFS( window ));
+ if( alpha_order ){
+ wnd->private->actions = g_slist_prepend( wnd->private->actions, ( gpointer ) item );
+ sort_actions_list( wnd );
+
+ } else {
+ current = get_edited_action( window );
+ if( current ){
+ index = g_slist_index( wnd->private->actions, current );
+ g_assert( index >= 0 );
+ wnd->private->actions = g_slist_insert( wnd->private->actions, item, index );
+ } else {
+ g_assert( g_slist_length( wnd->private->actions ) == 0 );
+ wnd->private->actions = g_slist_prepend( wnd->private->actions, ( gpointer ) item );
+ }
+ }
+
+ nact_iactions_list_fill( window, TRUE );
+
+ uuid = na_action_get_uuid( item );
+ nact_iactions_list_set_selection( window, NA_ACTION_TYPE, uuid, NULL );
+ g_free( uuid );
}
static void
@@ -984,3 +1050,35 @@ on_actions_changed( NAIPivotConsumer *instance, gpointer user_data )
nact_iactions_list_fill( NACT_WINDOW( instance ), TRUE );
}
}
+
+/*
+ * called by NAPivot via NAIPivotConsumer whenever the
+ * "sort in alphabetical order" preference is modified.
+ */
+static void
+on_display_order_changed( NAIPivotConsumer *instance, gpointer user_data )
+{
+ static const gchar *thisfn = "nact_main_window_on_display_order_changed";
+ /*NactMainWindow *self;*/
+ gboolean alpha_order;
+
+ g_debug( "%s: instance=%p, user_data=%p", thisfn, ( void * ) instance, ( void * ) user_data );
+ g_assert( NACT_IS_MAIN_WINDOW( instance ));
+ /*self = NACT_MAIN_WINDOW( instance );*/
+
+ alpha_order = na_iprefs_get_alphabetical_order( NA_IPREFS( instance ));
+
+ nact_iactions_list_set_multiple_selection( NACT_WINDOW( instance ), !alpha_order );
+ nact_iactions_list_set_dnd_mode( NACT_WINDOW( instance ), !alpha_order );
+}
+
+static void
+sort_actions_list( NactMainWindow *window )
+{
+ NactApplication *application;
+ NAPivot *pivot;
+
+ application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
+ pivot = nact_application_get_pivot( application );
+ window->private->actions = na_iio_provider_sort_actions( pivot, window->private->actions );
+}
diff --git a/src/nact/nact-preferences-editor.c b/src/nact/nact-preferences-editor.c
index 5b04776..0a1ca58 100644
--- a/src/nact/nact-preferences-editor.c
+++ b/src/nact/nact-preferences-editor.c
@@ -71,7 +71,8 @@ static void on_runtime_init_dialog( BaseWindow *dialog );
static void on_all_widgets_showed( BaseWindow *dialog );
/*static void setup_buttons( NactPreferencesEditor *dialog, gboolean is_modified );
static void on_modified_field( NactWindow *dialog );*/
-static void on_submenu_toggled( GtkToggleButton *button, NactWindow *window );
+static void on_sort_alpha_toggled( GtkToggleButton *button, NactWindow *window );
+static void on_add_about_toggled( GtkToggleButton *button, NactWindow *window );
static void on_cancel_clicked( GtkButton *button, NactWindow *window );
static void on_ok_clicked( GtkButton *button, NactWindow *window );
static void save_preferences( NactPreferencesEditor *editor );
@@ -290,7 +291,7 @@ on_runtime_init_dialog( BaseWindow *dialog )
{
static const gchar *thisfn = "nact_preferences_editor_on_runtime_init_dialog";
NactPreferencesEditor *editor;
- gboolean as_submenu;
+ gboolean sort_alpha, add_about_item;
GtkWidget *button;
/* call parent class at the very beginning */
@@ -302,11 +303,15 @@ on_runtime_init_dialog( BaseWindow *dialog )
g_assert( NACT_IS_PREFERENCES_EDITOR( dialog ));
editor = NACT_PREFERENCES_EDITOR( dialog );
- as_submenu = na_iprefs_get_bool( NA_IPREFS( editor ), PREFS_DISPLAY_AS_SUBMENU );
- button = base_window_get_widget( dialog, "AsSubmenuButton" );
- gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( button ), as_submenu );
+ sort_alpha = na_iprefs_get_alphabetical_order( NA_IPREFS( editor ));
+ button = base_window_get_widget( dialog, "SortAlphabeticalButton" );
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( button ), sort_alpha );
+ nact_window_signal_connect_by_name( NACT_WINDOW( editor ), "SortAlphabeticalButton", "toggled", G_CALLBACK( on_sort_alpha_toggled ));
- nact_window_signal_connect_by_name( NACT_WINDOW( editor ), "AsSubmenuButton", "toggled", G_CALLBACK( on_submenu_toggled ));
+ add_about_item = na_iprefs_get_add_about_item( NA_IPREFS( editor ));
+ button = base_window_get_widget( dialog, "AddAboutButton" );
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( button ), add_about_item );
+ nact_window_signal_connect_by_name( NACT_WINDOW( editor ), "AddAboutButton", "toggled", G_CALLBACK( on_add_about_toggled ));
nact_window_signal_connect_by_name( NACT_WINDOW( editor ), "CancelButton", "clicked", G_CALLBACK( on_cancel_clicked ));
nact_window_signal_connect_by_name( NACT_WINDOW( editor ), "OKButton", "clicked", G_CALLBACK( on_ok_clicked ));
@@ -371,7 +376,14 @@ on_modified_field( NactWindow *window )
}*/
static void
-on_submenu_toggled( GtkToggleButton *button, NactWindow *window )
+on_sort_alpha_toggled( GtkToggleButton *button, NactWindow *window )
+{
+ g_assert( NACT_IS_PREFERENCES_EDITOR( window ));
+ /*NactPreferencesEditor *editor = NACT_PREFERENCES_EDITOR( window );*/
+}
+
+static void
+on_add_about_toggled( GtkToggleButton *button, NactWindow *window )
{
g_assert( NACT_IS_PREFERENCES_EDITOR( window ));
/*NactPreferencesEditor *editor = NACT_PREFERENCES_EDITOR( window );*/
@@ -394,9 +406,16 @@ on_ok_clicked( GtkButton *button, NactWindow *window )
static void
save_preferences( NactPreferencesEditor *editor )
{
- GtkWidget *button = base_window_get_widget( BASE_WINDOW( editor ), "AsSubmenuButton" );
- gboolean as_submenu = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( button ));
- na_iprefs_set_bool( NA_IPREFS( editor ), PREFS_DISPLAY_AS_SUBMENU, as_submenu );
+ GtkWidget *button;
+ gboolean enabled;
+
+ button = base_window_get_widget( BASE_WINDOW( editor ), "SortAlphabeticalButton" );
+ enabled = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( button ));
+ na_iprefs_set_bool( NA_IPREFS( editor ), PREFS_DISPLAY_ALPHABETICAL_ORDER, enabled );
+
+ button = base_window_get_widget( BASE_WINDOW( editor ), "AddAboutButton" );
+ enabled = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( button ));
+ na_iprefs_set_bool( NA_IPREFS( editor ), PREFS_ADD_ABOUT_ITEM, enabled );
}
static gboolean
diff --git a/src/nact/nact-selection.c b/src/nact/nact-selection.c
new file mode 100644
index 0000000..70edc85
--- /dev/null
+++ b/src/nact/nact-selection.c
@@ -0,0 +1,180 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <common/na-action.h>
+#include <common/na-action-profile.h>
+#include <common/na-xml-names.h>
+#include <common/na-xml-writer.h>
+
+#include "nact-selection.h"
+
+static void export_action( const gchar *uri, const NAAction *action, GSList **exported );
+static gchar *get_action_xml_buffer( const NAAction *action, GSList **exported );
+
+/**
+ *
+ */
+char *
+nact_selection_get_data_for_intern_use( GSList *selected_items )
+{
+ GString *data;
+ GSList *item;
+
+ data = g_string_new( "" );
+
+ for( item = selected_items ; item ; item = item->next ){
+ gchar *chunk = na_object_get_clipboard_id( NA_OBJECT( item->data ));
+ if( chunk && strlen( chunk )){
+ data = g_string_append( data, chunk );
+ data = g_string_append( data, "\n" );
+ }
+ g_free( chunk );
+ }
+
+ return( g_string_free( data, FALSE ));
+}
+
+/**
+ * Get text/plain from selected actions.
+ *
+ * This is called when we drop or paste a selection onto an application
+ * willing to deal with Xdnd protocol, for text/plain or application/xml
+ * mime types.
+ *
+ * Selected items may include menus, actions and profiles.
+ * For now, we only exports actions as XML files.
+ */
+char *
+nact_selection_get_data_for_extern_use( GSList *selected_items )
+{
+ GSList *item;
+ GSList *exported = NULL;
+ GString *data;
+ gchar *chunk;
+
+ data = g_string_new( "" );
+
+ for( item = selected_items ; item ; item = item->next ){
+ NAObject *item_object = NA_OBJECT( item->data );
+ chunk = NULL;
+
+ if( NA_IS_ACTION( item_object )){
+ chunk = get_action_xml_buffer( NA_ACTION( item_object ), &exported );
+
+ } else if( NA_IS_ACTION_PROFILE( item_object )){
+ NAAction *action = na_action_profile_get_action( NA_ACTION_PROFILE( item_object ));
+ chunk = get_action_xml_buffer( action, &exported );
+ }
+
+ if( chunk && strlen( chunk )){
+ data = g_string_append( data, chunk );
+ }
+ g_free( chunk );
+ }
+
+ g_slist_free( exported );
+ return( g_string_free( data, FALSE ));
+}
+
+/**
+ * Exports selected actions.
+ *
+ * This is called when we drop or paste a selection onto an application
+ * willing to deal with XdndDirectSave (XDS) protocol.
+ *
+ * Selected items may include menus, actions and profiles.
+ * For now, we only exports actions as XML files.
+ */
+void
+nact_selection_export_items( const gchar *uri, GSList *items )
+{
+ GSList *item;
+ GSList *exported = NULL;
+
+ for( item = items ; item ; item = item->next ){
+ NAObject *item_object = NA_OBJECT( item->data );
+
+ if( NA_IS_ACTION( item_object )){
+ export_action( uri, NA_ACTION( item_object ), &exported );
+
+ } else if( NA_IS_ACTION_PROFILE( item_object )){
+ NAAction *action = na_action_profile_get_action( NA_ACTION_PROFILE( item_object ));
+ export_action( uri, action, &exported );
+ }
+ }
+
+ g_slist_free( exported );
+}
+
+static void
+export_action( const gchar *uri, const NAAction *action, GSList **exported )
+{
+ gint index;
+ gchar *fname, *buffer;
+
+ index = g_slist_index( *exported, ( gconstpointer ) action );
+ if( index != -1 ){
+ return;
+ }
+
+ fname = na_xml_writer_get_output_fname( action, uri, FORMAT_GCONFENTRY );
+ buffer = na_xml_writer_get_xml_buffer( action, FORMAT_GCONFENTRY );
+
+ na_xml_writer_output_xml( buffer, fname );
+
+ g_free( buffer );
+ g_free( fname );
+
+ *exported = g_slist_prepend( *exported, ( gpointer ) action );
+}
+
+static gchar *
+get_action_xml_buffer( const NAAction *action, GSList **exported )
+{
+ gint index;
+ gchar *buffer;
+
+ index = g_slist_index( *exported, ( gconstpointer ) action );
+ if( index != -1 ){
+ return( NULL );
+ }
+
+ buffer = na_xml_writer_get_xml_buffer( action, FORMAT_GCONFENTRY );
+
+ *exported = g_slist_prepend( *exported, ( gpointer ) action );
+
+ return( buffer );
+}
diff --git a/src/nact/nact-selection.h b/src/nact/nact-selection.h
new file mode 100644
index 0000000..0ac54c6
--- /dev/null
+++ b/src/nact/nact-selection.h
@@ -0,0 +1,45 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+#ifndef __NACT_DND_H__
+#define __NACT_DND_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+char *nact_selection_get_data_for_intern_use( GSList *selected_items );
+char *nact_selection_get_data_for_extern_use( GSList *selected_items );
+
+void nact_selection_export_items( const gchar *uri, GSList *items );
+
+G_END_DECLS
+
+#endif /* __NACT_DND_H__ */
diff --git a/src/nact/nact-tree-model.c b/src/nact/nact-tree-model.c
new file mode 100644
index 0000000..a35d8da
--- /dev/null
+++ b/src/nact/nact-tree-model.c
@@ -0,0 +1,694 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+/*
+ * Adapted from File-Roller:fr-list-model.c
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <common/na-action.h>
+#include <common/na-iprefs.h>
+#include <common/na-utils.h>
+
+#include "egg-tree-multi-dnd.h"
+#include "nact-iactions-list.h"
+#include "nact-selection.h"
+#include "nact-tree-model.h"
+
+/*
+ * call once egg_tree_multi_drag_add_drag_support( treeview ) at init time (before gtk_main)
+ *
+ * when we start with drag
+ * call once egg_tree_multi_dnd_on_button_press_event( treeview, event, drag_source )
+ * call many egg_tree_multi_dnd_on_motion_event( treeview, event, drag_source )
+ * until mouse quits the selected area
+ *
+ * as soon as mouse has quitted the selected area
+ * call once egg_tree_multi_dnd_stop_drag_check( treeview )
+ * call once nact_tree_model_imulti_drag_source_row_draggable: drag_source=0x92a0d70, path_list=0x9373c90
+ * call once nact_selection_on_drag_begin( treeview, context, main_window )
+ *
+ * when we drop (e.g. in Nautilus)
+ * call once egg_tree_multi_drag_drag_data_get( treeview, context, selection_data, info=0, time )
+ * call once nact_tree_model_imulti_drag_source_drag_data_get( drag_source, context, selection_data, path_list, atom=XdndDirectSave0 )
+ * call once nact_selection_on_drag_end( treeview, context, main_window )
+ */
+
+/* private class data
+ */
+struct NactTreeModelClassPrivate {
+ void *empty; /* so that gcc -pedantic is happy */
+};
+
+/* private instance data
+ */
+struct NactTreeModelPrivate {
+ gboolean dispose_has_run;
+ NactMainWindow *window;
+ gchar *drag_dest_uri;
+ GSList *drag_items;
+};
+
+#define MAX_XDS_ATOM_VAL_LEN 4096
+#define TEXT_ATOM gdk_atom_intern( "text/plain", FALSE )
+#define XDS_ATOM gdk_atom_intern( "XdndDirectSave0", FALSE )
+#define XDS_FILENAME "xds.txt"
+#define XNACT_ATOM gdk_atom_intern( "XdndNautilusActions", FALSE )
+
+enum {
+ NACT_XCHANGE_FORMAT_NACT = 0,
+ NACT_XCHANGE_FORMAT_XDS,
+ NACT_XCHANGE_FORMAT_APPLICATION_XML,
+ NACT_XCHANGE_FORMAT_TEXT_PLAIN
+
+};
+
+/* as a dnd source, we provide
+ * - a special XdndNautilusAction format for internal move/copy
+ * - a XdndDirectSave, suitable for exporting to a file manager
+ * (note that Nautilus recognized the "XdndDirectSave0" format as XDS
+ * protocol)
+ * - a text (xml) format, to go to clipboard or a text editor
+ */
+static GtkTargetEntry dnd_source_formats[] = {
+ { "XdndNautilusActions", GTK_TARGET_SAME_WIDGET, NACT_XCHANGE_FORMAT_NACT },
+ { "XdndDirectSave0", GTK_TARGET_OTHER_APP, NACT_XCHANGE_FORMAT_XDS },
+ { "application/xml", GTK_TARGET_OTHER_APP, NACT_XCHANGE_FORMAT_APPLICATION_XML },
+ { "text/plain", GTK_TARGET_OTHER_APP, NACT_XCHANGE_FORMAT_TEXT_PLAIN },
+};
+
+/*static GtkTargetEntry dnd_dest_targets[] = {
+ { "XdndNautilusActions0", 0, 0 },
+ { "XdndDirectSave0", 0, 2 }
+};*/
+
+static GtkTreeModelFilterClass *st_parent_class = NULL;
+
+static GType register_type( void );
+static void class_init( NactTreeModelClass *klass );
+static void imulti_drag_source_init( EggTreeMultiDragSourceIface *iface );
+static void idrag_dest_init( GtkTreeDragDestIface *iface );
+static void instance_init( GTypeInstance *instance, gpointer klass );
+static void instance_dispose( GObject *application );
+static void instance_finalize( GObject *application );
+
+static gboolean imulti_drag_source_row_draggable( EggTreeMultiDragSource *drag_source, GList *path_list );
+static gboolean imulti_drag_source_drag_data_get( EggTreeMultiDragSource *drag_source, GdkDragContext *context, GtkSelectionData *selection_data, GList *path_list, guint info );
+static gboolean imulti_drag_source_drag_data_delete( EggTreeMultiDragSource *drag_source, GList *path_list );
+static GtkTargetList *imulti_drag_source_get_target_list( EggTreeMultiDragSource *drag_source );
+static GdkDragAction imulti_drag_source_get_drag_actions( EggTreeMultiDragSource *drag_source );
+
+static gboolean idrag_dest_drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data );
+static gboolean idrag_dest_row_drop_possible( GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data );
+
+static gboolean on_drag_begin( GtkWidget *widget, GdkDragContext *context, NactWindow *window );
+static void on_drag_end( GtkWidget *widget, GdkDragContext *context, NactWindow *window );
+
+static gint sort_actions_list( GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, NactWindow *window );
+static gboolean filter_visible( GtkTreeModel *model, GtkTreeIter *iter, gpointer data );
+/*static gboolean nautilus_xds_dnd_is_valid_xds_context( GdkDragContext *context );
+static gboolean context_offers_target( GdkDragContext *context, GdkAtom target );*/
+static char *get_xds_atom_value( GdkDragContext *context );
+
+GType
+nact_tree_model_get_type( void )
+{
+ static GType model_type = 0;
+
+ if( !model_type ){
+ model_type = register_type();
+ }
+
+ return( model_type );
+}
+
+static GType
+register_type (void)
+{
+ static const gchar *thisfn = "nact_tree_model_register_type";
+ GType type;
+
+ static const GTypeInfo info = {
+ sizeof( NactTreeModelClass ),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ ( GClassInitFunc ) class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof( NactTreeModel ),
+ 0,
+ ( GInstanceInitFunc ) instance_init
+ };
+
+ static const GInterfaceInfo imulti_drag_source_info = {
+ ( GInterfaceInitFunc ) imulti_drag_source_init,
+ NULL,
+ NULL
+ };
+
+ static const GInterfaceInfo idrag_dest_info = {
+ ( GInterfaceInitFunc ) idrag_dest_init,
+ NULL,
+ NULL
+ };
+
+ g_debug( "%s", thisfn );
+
+ type = g_type_register_static( GTK_TYPE_TREE_MODEL_FILTER, "NactTreeModel", &info, 0 );
+
+ g_type_add_interface_static( type, EGG_TYPE_TREE_MULTI_DRAG_SOURCE, &imulti_drag_source_info );
+
+ g_type_add_interface_static( type, GTK_TYPE_TREE_DRAG_DEST, &idrag_dest_info );
+
+ return( type );
+}
+
+static void
+class_init( NactTreeModelClass *klass )
+{
+ static const gchar *thisfn = "nact_tree_model_class_init";
+ GObjectClass *object_class;
+
+ g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
+
+ st_parent_class = g_type_class_peek_parent( klass );
+
+ object_class = G_OBJECT_CLASS( klass );
+ object_class->dispose = instance_dispose;
+ object_class->finalize = instance_finalize;
+
+ klass->private = g_new0( NactTreeModelClassPrivate, 1 );
+}
+
+static void
+imulti_drag_source_init( EggTreeMultiDragSourceIface *iface )
+{
+ static const gchar *thisfn = "nact_tree_model_imulti_drag_source_init";
+
+ g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
+
+ iface->row_draggable = imulti_drag_source_row_draggable;
+ iface->drag_data_get = imulti_drag_source_drag_data_get;
+ iface->drag_data_delete = imulti_drag_source_drag_data_delete;
+ iface->get_target_list = imulti_drag_source_get_target_list;
+ iface->free_target_list = NULL;
+ iface->get_drag_actions = imulti_drag_source_get_drag_actions;
+}
+
+static void
+idrag_dest_init( GtkTreeDragDestIface *iface )
+{
+ static const gchar *thisfn = "nact_tree_model_idrag_dest_init";
+
+ g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
+
+ iface->drag_data_received = idrag_dest_drag_data_received;
+ iface->row_drop_possible = idrag_dest_row_drop_possible;
+}
+
+static void
+instance_init( GTypeInstance *instance, gpointer klass )
+{
+ /*static const gchar *thisfn = "nact_tree_model_instance_init";*/
+ NactTreeModel *self;
+
+ /*g_debug( "%s: instance=%p, klass=%p", thisfn, ( void * ) instance, ( void * ) klass );*/
+ g_assert( NACT_IS_TREE_MODEL( instance ));
+ self = NACT_TREE_MODEL( instance );
+
+ self->private = g_new0( NactTreeModelPrivate, 1 );
+
+ self->private->dispose_has_run = FALSE;
+}
+
+static void
+instance_dispose( GObject *object )
+{
+ /*static const gchar *thisfn = "nact_tree_model_instance_dispose";*/
+ NactTreeModel *self;
+
+ /*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
+ g_assert( NACT_IS_TREE_MODEL( object ));
+ self = NACT_TREE_MODEL( object );
+
+ if( !self->private->dispose_has_run ){
+
+ self->private->dispose_has_run = TRUE;
+
+ /* chain up to the parent class */
+ if( G_OBJECT_CLASS( st_parent_class )->dispose ){
+ G_OBJECT_CLASS( st_parent_class )->dispose( object );
+ }
+ }
+}
+
+static void
+instance_finalize( GObject *object )
+{
+ /*static const gchar *thisfn = "nact_tree_model_instance_finalize";*/
+ NactTreeModel *self;
+
+ /*g_debug( "%s: object=%p", thisfn, ( void * ) object );*/
+ g_assert( NACT_IS_TREE_MODEL( object ));
+ self = NACT_TREE_MODEL( object );
+
+ g_free( self->private->drag_dest_uri );
+ g_slist_free( self->private->drag_items );
+
+ g_free( self->private );
+
+ /* chain call to parent class */
+ if( G_OBJECT_CLASS( st_parent_class )->finalize ){
+ G_OBJECT_CLASS( st_parent_class )->finalize( object );
+ }
+}
+
+NactTreeModel *
+nact_tree_model_new( NactMainWindow *window )
+{
+ GtkTreeStore *ts_model;
+ NactTreeModel *model;
+ gboolean alpha_order;
+
+ ts_model = gtk_tree_store_new(
+ IACTIONS_LIST_N_COLUMN, GDK_TYPE_PIXBUF, G_TYPE_STRING, NA_OBJECT_TYPE );
+
+ gtk_tree_sortable_set_default_sort_func(
+ GTK_TREE_SORTABLE( ts_model ),
+ ( GtkTreeIterCompareFunc ) sort_actions_list, window, NULL );
+
+ alpha_order = na_iprefs_get_alphabetical_order( NA_IPREFS( window ));
+
+ if( alpha_order ){
+ gtk_tree_sortable_set_sort_column_id(
+ GTK_TREE_SORTABLE( ts_model ),
+ IACTIONS_LIST_LABEL_COLUMN, GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID );
+ }
+
+ /* create the filter model */
+
+ model = g_object_new( NACT_TREE_MODEL_TYPE, "child-model", ts_model, NULL );
+
+ gtk_tree_model_filter_set_visible_func(
+ GTK_TREE_MODEL_FILTER( model ), ( GtkTreeModelFilterVisibleFunc ) filter_visible, window, NULL );
+
+ model->private->window = window;
+
+ return( model );
+}
+
+/**
+ * nact_tree_model_runtime_init_dnd:
+ * @window: the #NactMainWindow window.
+ * @widget: the #GtkTreeView which implements this #NactTreeModel.
+ *
+ * Initializes the drag & drop features.
+ *
+ * We use drag and drop:
+ * - inside of treeview, for duplicating items, or moving items between
+ * menus
+ * - from treeview to the outside world (e.g. Nautilus) to export actions
+ * - from outside world (e.g. Nautilus) to import actions
+ */
+void
+nact_tree_model_runtime_init_dnd( NactMainWindow *window, GtkTreeView *widget )
+{
+ NactTreeModel *model;
+
+ model = NACT_TREE_MODEL( gtk_tree_view_get_model( widget ));
+ g_assert( NACT_IS_TREE_MODEL( model ));
+
+ nact_window_signal_connect(
+ NACT_WINDOW( window ),
+ G_OBJECT( widget ),
+ "drag_begin",
+ G_CALLBACK( on_drag_begin ));
+
+ nact_window_signal_connect(
+ NACT_WINDOW( window ),
+ G_OBJECT( widget ),
+ "drag_end",
+ G_CALLBACK( on_drag_end ));
+
+ egg_tree_multi_drag_add_drag_support( EGG_TREE_MULTI_DRAG_SOURCE( model ), GTK_TREE_VIEW( widget ));
+
+ /*gtk_drag_source_set(
+ GTK_WIDGET( widget ),
+ GDK_BUTTON1_MASK,
+ dnd_source_formats, G_N_ELEMENTS( dnd_source_formats ),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE
+ );*/
+
+ /*gtk_drag_dest_set(
+ GTK_WIDGET( widget ), GTK_DEST_DEFAULT_ALL,
+ dnd_dest_targets, G_N_ELEMENTS( dnd_dest_targets ), GDK_ACTION_COPY | GDK_ACTION_MOVE );*/
+}
+
+/*
+ * all rows are draggable
+ */
+static gboolean
+imulti_drag_source_row_draggable( EggTreeMultiDragSource *drag_source, GList *path_list )
+{
+ static const gchar *thisfn = "nact_tree_model_imulti_drag_source_row_draggable";
+ /*FrWindow *window;
+ GtkTreeModel *model;
+ GList *scan;*/
+
+ g_debug( "%s: drag_source=%p, path_list=%p", thisfn, ( void * ) drag_source, ( void * ) path_list );
+
+ /*window = g_object_get_data (G_OBJECT (drag_source), "FrWindow");
+ g_return_val_if_fail (window != NULL, FALSE);
+
+ model = fr_window_get_list_store (window);
+
+ for (scan = path_list; scan; scan = scan->next) {
+ GtkTreeRowReference *reference = scan->data;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ FileData *fdata;
+
+ path = gtk_tree_row_reference_get_path (reference);
+ if (path == NULL)
+ continue;
+
+ if (! gtk_tree_model_get_iter (model, &iter, path))
+ continue;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_FILE_DATA, &fdata,
+ -1);
+
+ if (fdata != NULL)
+ return TRUE;
+ }*/
+
+ return( TRUE );
+}
+
+/*
+ * drag_data_get is called when we release the selected items onto the
+ * destination
+ *
+ * if some rows are selected
+ * here, we only provide id. of dragged rows :
+ * M:uuid
+ * A:uuid
+ * P:uuid/name
+ * this is suitable and sufficient for the internal clipboard
+ *
+ * when exporting to the outside, we should prepare to export the items
+ */
+static gboolean
+imulti_drag_source_drag_data_get( EggTreeMultiDragSource *drag_source,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ GList *path_list,
+ guint info )
+{
+ static const gchar *thisfn = "nact_tree_model_imulti_drag_source_drag_data_get";
+ gchar *atom_name;
+ NactTreeModel *model;
+ GSList *selected_items;
+ gchar *data;
+ gboolean ret = FALSE;
+ gchar *dest_folder, *folder;
+ gboolean is_writable;
+
+ atom_name = gdk_atom_name( selection_data->target );
+ g_debug( "%s: drag_source=%p, context=%p, selection_data=%p, path_list=%p, atom=%s",
+ thisfn, ( void * ) drag_source, ( void * ) context, ( void * ) selection_data, ( void * ) path_list,
+ atom_name );
+ g_free( atom_name );
+
+ model = NACT_TREE_MODEL( drag_source );
+ g_assert( model->private->window );
+
+ selected_items = nact_iactions_list_get_selected_items( NACT_IACTIONS_LIST( model->private->window ));
+ if( !selected_items ){
+ return( FALSE );
+ }
+ if( !g_slist_length( selected_items )){
+ g_slist_free( selected_items );
+ return( FALSE );
+ }
+
+ switch( info ){
+ case NACT_XCHANGE_FORMAT_NACT:
+ data = nact_selection_get_data_for_intern_use( selected_items );
+ gtk_selection_data_set( selection_data, selection_data->target, 8, ( guchar * ) data, strlen( data ));
+ g_free( data );
+ ret = TRUE;
+ break;
+
+ case NACT_XCHANGE_FORMAT_XDS:
+ folder = get_xds_atom_value( context );
+ dest_folder = na_utils_remove_last_level_from_path( folder );
+ g_free( folder );
+ is_writable = na_utils_is_writable_dir( dest_folder );
+ gtk_selection_data_set( selection_data, selection_data->target, 8, ( guchar * )( is_writable ? "S" : "F" ), 1 );
+ if( is_writable ){
+ model->private->drag_dest_uri = g_strdup( dest_folder );
+ model->private->drag_items = g_slist_copy( selected_items );
+ }
+ g_free( dest_folder );
+ ret = TRUE;
+ break;
+
+ case NACT_XCHANGE_FORMAT_APPLICATION_XML:
+ case NACT_XCHANGE_FORMAT_TEXT_PLAIN:
+ data = nact_selection_get_data_for_extern_use( selected_items );
+ gtk_selection_data_set( selection_data, selection_data->target, 8, ( guchar * ) data, strlen( data ));
+ g_free( data );
+ ret = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ g_slist_free( selected_items );
+ return( ret );
+}
+
+static gboolean
+imulti_drag_source_drag_data_delete( EggTreeMultiDragSource *drag_source, GList *path_list )
+{
+ static const gchar *thisfn = "nact_tree_model_imulti_drag_source_drag_data_delete";
+
+ g_debug( "%s: drag_source=%p, path_list=%p", thisfn, ( void * ) drag_source, ( void * ) path_list );
+
+ return( TRUE );
+}
+
+static GtkTargetList *
+imulti_drag_source_get_target_list( EggTreeMultiDragSource *drag_source )
+{
+ GtkTargetList *target_list;
+
+ target_list = gtk_target_list_new( dnd_source_formats, G_N_ELEMENTS( dnd_source_formats ));
+
+ return( target_list );
+}
+
+static GdkDragAction
+imulti_drag_source_get_drag_actions( EggTreeMultiDragSource *drag_source )
+{
+ return( GDK_ACTION_COPY | GDK_ACTION_MOVE );
+}
+
+static gboolean
+idrag_dest_drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data )
+{
+ static const gchar *thisfn = "nact_tree_model_idrag_dest_drag_data_received";
+
+ g_debug( "%s: drag_dest=%p, dest=%p, selection_data=%p", thisfn, ( void * ) drag_dest, ( void * ) dest, ( void * ) selection_data );
+
+ return( FALSE );
+}
+
+static gboolean
+idrag_dest_row_drop_possible( GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data )
+{
+ static const gchar *thisfn = "nact_tree_model_idrag_dest_row_drop_possible";
+
+ g_debug( "%s: drag_dest=%p, dest_path=%p, selection_data=%p", thisfn, ( void * ) drag_dest, ( void * ) dest_path, ( void * ) selection_data );
+
+ return( TRUE );
+}
+
+static gboolean
+on_drag_begin( GtkWidget *widget, GdkDragContext *context, NactWindow *window )
+{
+ static const gchar *thisfn = "nact_selection_on_drag_begin";
+ NactTreeModel *model;
+
+ g_debug( "%s: widget=%p, context=%p, window=%p",
+ thisfn, ( void * ) widget, ( void * ) context, ( void * ) window );
+
+ model = NACT_TREE_MODEL( gtk_tree_view_get_model( GTK_TREE_VIEW( widget )));
+
+ g_free( model->private->drag_dest_uri );
+ model->private->drag_dest_uri = NULL;
+
+ g_slist_free( model->private->drag_items );
+ model->private->drag_items = NULL;
+
+ gdk_property_change(
+ context->source_window,
+ XDS_ATOM, TEXT_ATOM, 8, GDK_PROP_MODE_REPLACE, ( guchar * ) XDS_FILENAME, strlen( XDS_FILENAME ));
+
+ return( FALSE );
+}
+
+static void
+on_drag_end( GtkWidget *widget, GdkDragContext *context, NactWindow *window )
+{
+ static const gchar *thisfn = "nact_selection_on_drag_end";
+ NactTreeModel *model;
+
+ g_debug( "%s: widget=%p, context=%p, window=%p",
+ thisfn, ( void * ) widget, ( void * ) context, ( void * ) window );
+
+ model = NACT_TREE_MODEL( gtk_tree_view_get_model( GTK_TREE_VIEW( widget )));
+
+ if( model->private->drag_dest_uri && model->private->drag_items && g_slist_length( model->private->drag_items )){
+ nact_selection_export_items( model->private->drag_dest_uri, model->private->drag_items );
+ }
+
+ g_free( model->private->drag_dest_uri );
+ model->private->drag_dest_uri = NULL;
+
+ g_slist_free( model->private->drag_items );
+ model->private->drag_items = NULL;
+
+ gdk_property_delete( context->source_window, XDS_ATOM );
+}
+
+static gint
+sort_actions_list( GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, NactWindow *window )
+{
+ static const gchar *thisfn = "nact_tree_model_sort_actions_list";
+ gchar *labela, *labelb;
+ gint ret;
+
+ g_debug( "%s: model=%p, a=%p, b=%p, window=%p", thisfn, ( void * ) model, ( void * ) a, ( void * ) b, ( void * ) window );
+
+ gtk_tree_model_get( model, a, IACTIONS_LIST_LABEL_COLUMN, &labela, -1 );
+ gtk_tree_model_get( model, b, IACTIONS_LIST_LABEL_COLUMN, &labelb, -1 );
+
+ ret = g_utf8_collate( labela, labelb );
+
+ g_free( labela );
+ g_free( labelb );
+
+ return( ret );
+}
+
+static gboolean
+filter_visible( GtkTreeModel *model, GtkTreeIter *iter, gpointer data )
+{
+ /*static const gchar *thisfn = "nact_tree_model_filter_visible";*/
+ NAObject *object;
+ NAAction *action;
+
+ /*g_debug( "%s: model=%p, iter=%p, data=%p", thisfn, ( void * ) model, ( void * ) iter, ( void * ) data );*/
+
+ gtk_tree_model_get( model, iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
+
+ if( object ){
+ if( NA_IS_ACTION( object )){
+ return( TRUE );
+ }
+
+ g_assert( NA_IS_ACTION_PROFILE( object ));
+ action = na_action_profile_get_action( NA_ACTION_PROFILE( object ));
+ return( na_action_get_profiles_count( action ) > 1 );
+ }
+
+ return( FALSE );
+}
+
+/*
+ * from FileRoller
+ */
+/* The following three functions taken from bugzilla
+ * (http://bugzilla.gnome.org/attachment.cgi?id=49362&action=view)
+ * Author: Christian Neumair
+ * Copyright: 2005 Free Software Foundation, Inc
+ * License: GPL
+ */
+/*static gboolean
+nautilus_xds_dnd_is_valid_xds_context (GdkDragContext *context)
+{
+ char *tmp;
+ gboolean ret;
+
+ g_return_val_if_fail (context != NULL, FALSE);
+
+ tmp = NULL;
+ if (context_offers_target (context, XDS_ATOM)) {
+ tmp = get_xds_atom_value (context);
+ }
+
+ ret = (tmp != NULL);
+ g_free (tmp);
+
+ return ret;
+}
+
+static gboolean
+context_offers_target (GdkDragContext *context,
+ GdkAtom target)
+{
+ return (g_list_find (context->targets, target) != NULL);
+}*/
+
+static char *
+get_xds_atom_value (GdkDragContext *context)
+{
+ char *ret;
+
+ g_return_val_if_fail (context != NULL, NULL);
+ g_return_val_if_fail (context->source_window != NULL, NULL);
+
+ gdk_property_get (context->source_window,
+ XDS_ATOM, TEXT_ATOM,
+ 0, MAX_XDS_ATOM_VAL_LEN,
+ FALSE, NULL, NULL, NULL,
+ (unsigned char **) &ret);
+
+ return ret;
+}
diff --git a/src/nact/nact-tree-model.h b/src/nact/nact-tree-model.h
new file mode 100644
index 0000000..a2b773c
--- /dev/null
+++ b/src/nact/nact-tree-model.h
@@ -0,0 +1,95 @@
+/*
+ * Nautilus Actions
+ * A Nautilus extension which offers configurable context menu actions.
+ *
+ * Copyright (C) 2005 The GNOME Foundation
+ * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
+ * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
+ *
+ * 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 Library; see the file COPYING. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place,
+ * Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Frederic Ruaudel <grumz grumz net>
+ * Rodrigo Moya <rodrigo gnome-db org>
+ * Pierre Wieser <pwieser trychlos org>
+ * ... and many others (see AUTHORS)
+ */
+
+/*
+ * Adapted from File-Roller:fr-list-model.h
+ */
+
+/**
+ * SECTION: nact_tree_model
+ *
+ * NactTreeModel is derived from GtkTreeModelFilter in order to be able
+ * to selectively display profiles, whether an action has one or more
+ * profiles.
+ *
+ * NactTreeModel implements EggTreeMultiDragSource and GtkTreeDragDest
+ * interfaces.
+ *
+ * The GtkTreeModelFilter base class embeds a GtkTreeStore.
+ */
+#ifndef __NACT_TREE_MODEL_H__
+#define __NACT_TREE_MODEL_H__
+
+#include <gtk/gtk.h>
+
+#include "nact-main-window.h"
+
+G_BEGIN_DECLS
+
+#define NACT_TREE_MODEL_TYPE ( nact_tree_model_get_type())
+#define NACT_TREE_MODEL( object ) ( G_TYPE_CHECK_INSTANCE_CAST(( object ), NACT_TREE_MODEL_TYPE, NactTreeModel ))
+#define NACT_TREE_MODEL_CLASS( klass ) ( G_TYPE_CHECK_CLASS_CAST(( klass ), NACT_TREE_MODEL_TYPE, NactTreeModelClass ))
+#define NACT_IS_TREE_MODEL( object ) ( G_TYPE_CHECK_INSTANCE_TYPE(( object ), NACT_TREE_MODEL_TYPE ))
+#define NACT_IS_TREE_MODEL_CLASS( klass ) ( G_TYPE_CHECK_CLASS_TYPE(( klass ), NACT_TREE_MODEL_TYPE ))
+#define NACT_TREE_MODEL_GET_CLASS( object ) ( G_TYPE_INSTANCE_GET_CLASS(( object ), NACT_TREE_MODEL_TYPE, NactTreeModelClass ))
+
+typedef struct NactTreeModelPrivate NactTreeModelPrivate;
+
+typedef struct {
+ GtkTreeModelFilter parent;
+ NactTreeModelPrivate *private;
+}
+ NactTreeModel;
+
+typedef struct NactTreeModelClassPrivate NactTreeModelClassPrivate;
+
+typedef struct {
+ GtkTreeModelFilterClass parent;
+ NactTreeModelClassPrivate *private;
+}
+ NactTreeModelClass;
+
+/* column ordering of the tree view
+ */
+enum {
+ IACTIONS_LIST_ICON_COLUMN = 0,
+ IACTIONS_LIST_LABEL_COLUMN,
+ IACTIONS_LIST_NAOBJECT_COLUMN,
+ IACTIONS_LIST_N_COLUMN
+};
+
+GType nact_tree_model_get_type( void );
+
+NactTreeModel *nact_tree_model_new( NactMainWindow *window );
+void nact_tree_model_runtime_init_dnd( NactMainWindow *window, GtkTreeView *widget );
+
+G_END_DECLS
+
+#endif /* __NACT_TREE_MODEL_H__ */
diff --git a/src/nact/nact-window.h b/src/nact/nact-window.h
index c92a1c0..67279a2 100644
--- a/src/nact/nact-window.h
+++ b/src/nact/nact-window.h
@@ -39,6 +39,7 @@
*/
#include <common/na-action.h>
+#include <common/na-action-menu.h>
#include <common/na-action-profile.h>
#include <common/na-pivot.h>
diff --git a/src/nact/nautilus-actions-config-tool.actions b/src/nact/nautilus-actions-config-tool.actions
index 459b654..61ac0d8 100644
--- a/src/nact/nautilus-actions-config-tool.actions
+++ b/src/nact/nautilus-actions-config-tool.actions
@@ -4,6 +4,7 @@
<menu action="FileMenu">
<menuitem action="NewActionItem" />
<menuitem action="NewProfileItem" />
+ <menuitem action="NewMenuItem" />
<menuitem action="SaveItem" />
<separator />
<menuitem action="QuitItem" />
diff --git a/src/nact/nautilus-actions-config-tool.ui b/src/nact/nautilus-actions-config-tool.ui
index 637f706..3857952 100644
--- a/src/nact/nautilus-actions-config-tool.ui
+++ b/src/nact/nautilus-actions-config-tool.ui
@@ -1009,10 +1009,10 @@ Defining several profiles lets you have several commands, each applying with a d
<child>
<object class="GtkFileChooserWidget" id="filechooserwidget1">
<property name="visible">True</property>
+ <property name="select_multiple">True</property>
<property name="local_only">False</property>
- <property name="preview_widget_active">False</property>
<property name="use_preview_label">False</property>
- <property name="select_multiple">True</property>
+ <property name="preview_widget_active">False</property>
</object>
</child>
<child>
@@ -1116,9 +1116,9 @@ to extend a selection.</property>
<object class="GtkFileChooserWidget" id="ExportFolderChooser">
<property name="visible">True</property>
<property name="local_only">False</property>
- <property name="action">select-folder</property>
- <property name="preview_widget_active">False</property>
<property name="use_preview_label">False</property>
+ <property name="preview_widget_active">False</property>
+ <property name="action">select-folder</property>
</object>
<packing>
<property name="position">0</property>
@@ -1743,15 +1743,16 @@ The exported file may later be imported via :
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
- <property name="border_width">5</property>
+ <property name="border_width">10</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
- <object class="GtkCheckButton" id="AsSubmenuButton">
- <property name="label" translatable="yes">Display available actions as a Nautilus _submenu</property>
+ <object class="GtkCheckButton" id="SortAlphabeticalButton">
+ <property name="label" translatable="yes">_Sort actions in alphabetical order</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">When checked, actions appear in Nautilus context menu in alphabetical order of their label. Unchecked, this option lets you reorder yourself the actions.</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
@@ -1759,6 +1760,20 @@ The exported file may later be imported via :
<property name="position">0</property>
</packing>
</child>
+ <child>
+ <object class="GtkCheckButton" id="AddAboutButton">
+ <property name="label" translatable="yes">Add an '_About Nautilus Actions' item in the Nautilus context menu</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">When checked, and if only a single entry is defined as a root menu in the Nautilus context menu, then add an "About Nautilus Actions" at the end of the first level of your submenu.</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
</child>
</object>
@@ -1824,16 +1839,16 @@ The exported file may later be imported via :
</object>
<object class="GtkSizeGroup" id="CommandLabelSizeGroup">
<widgets>
- <widget name="ProfileLabelLabel"/>
- <widget name="CommandPathLabel"/>
- <widget name="CommandParametersLabel"/>
<widget name="CommandExamplePreLabel"/>
+ <widget name="CommandParametersLabel"/>
+ <widget name="CommandPathLabel"/>
+ <widget name="ProfileLabelLabel"/>
</widgets>
</object>
<object class="GtkSizeGroup" id="CommandButtonSizeGroup">
<widgets>
- <widget name="CommandPathButton"/>
<widget name="CommandLegendButton"/>
+ <widget name="CommandPathButton"/>
</widgets>
</object>
</interface>
diff --git a/src/plugin/nautilus-actions.c b/src/plugin/nautilus-actions.c
index 0fcdaa4..711b8a9 100644
--- a/src/plugin/nautilus-actions.c
+++ b/src/plugin/nautilus-actions.c
@@ -74,8 +74,8 @@ static void instance_finalize( GObject *object );
static GList *get_background_items( NautilusMenuProvider *provider, GtkWidget *window, NautilusFileInfo *current_folder );
static GList *get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files );
static NautilusMenuItem *create_menu_item( NAAction *action, NAActionProfile *profile, GList *files );
-static NautilusMenuItem *create_sub_menu( NautilusMenu **menu );
-static void add_post_submenu( NautilusMenu *menu );
+/*static NautilusMenuItem *create_sub_menu( NautilusMenu **menu );*/
+static void add_about_item( NautilusMenu *menu );
static void execute_action( NautilusMenuItem *item, NAActionProfile *profile );
static void actions_changed_handler( NAIPivotConsumer *instance, gpointer user_data );
@@ -281,7 +281,8 @@ get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files
NautilusMenuItem *item;
GSList *actions = NULL;
gchar *label, *uuid;
- gboolean have_submenu;
+ gint submenus = 0;
+ gboolean add_about;
g_debug( "%s: provider=%p, window=%p, files=%p, count=%d",
thisfn, ( void * ) provider, ( void * ) window, ( void * ) files, g_list_length( files ));
@@ -294,8 +295,6 @@ get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files
return(( GList * ) NULL );
}
- have_submenu = na_iprefs_get_bool( NA_IPREFS( self ), PREFS_DISPLAY_AS_SUBMENU );
-
if( !self->private->dispose_has_run ){
actions = na_pivot_get_actions( self->private->pivot );
@@ -333,22 +332,24 @@ get_file_items( NautilusMenuProvider *provider, GtkWidget *window, GList *files
if( na_action_profile_is_candidate( profile, files )){
item = create_menu_item( action, profile, files );
+ items = g_list_append( items, item );
- if( have_submenu ){
+ /*if( have_submenu ){
if( !menu ){
items = g_list_append( items, create_sub_menu( &menu ));
}
nautilus_menu_append_item( menu, item );
} else {
- items = g_list_append( items, item );
- }
+ }*/
break;
}
}
}
- if( have_submenu ){
- add_post_submenu( menu );
+
+ add_about = na_iprefs_get_add_about_item( NA_IPREFS( self ));
+ if( submenus == 1 && add_about ){
+ add_about_item( menu );
}
}
@@ -398,7 +399,7 @@ create_menu_item( NAAction *action, NAActionProfile *profile, GList *files )
return( item );
}
-static NautilusMenuItem *
+/*static NautilusMenuItem *
create_sub_menu( NautilusMenu **menu )
{
NautilusMenuItem *item;
@@ -418,10 +419,10 @@ create_sub_menu( NautilusMenu **menu )
g_free( icon_name );
return( item );
-}
+}*/
static void
-add_post_submenu( NautilusMenu *menu )
+add_about_item( NautilusMenu *menu )
{
gchar *icon_name = na_about_get_icon_name();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]