Revision: 1488
http://geeqie.svn.sourceforge.net/geeqie/?rev=1488&view=rev
Author: nadvornik
Date: 2009-03-08 09:56:40 +0000 (Sun, 08 Mar 2009)
Log Message:
-----------
dnd in keywords tree
improved dnd in advanced exif
Modified Paths:
--------------
trunk/src/advanced_exif.c
trunk/src/bar_exif.c
trunk/src/bar_keywords.c
trunk/src/dnd.h
trunk/src/metadata.c
trunk/src/metadata.h
Modified: trunk/src/advanced_exif.c
===================================================================
--- trunk/src/advanced_exif.c 2009-03-07 17:02:59 UTC (rev 1487)
+++ trunk/src/advanced_exif.c 2009-03-08 09:56:40 UTC (rev 1488)
@@ -45,7 +45,6 @@
GtkWidget *listview;
FileData *fd;
- gchar *sel_key;
};
enum {
@@ -230,38 +229,62 @@
static gint n_exif_drag_types = 1;
-static void advanced_exif_dnd_get(GtkWidget *entry, GdkDragContext *context,
+static void advanced_exif_dnd_get(GtkWidget *listview, GdkDragContext *context,
GtkSelectionData *selection_data, guint
info,
guint time, gpointer data)
{
ExifWin *ew = data;
+ GtkTreeSelection *sel =
gtk_tree_view_get_selection(GTK_TREE_VIEW(listview));
+ GtkTreeIter iter;
- gtk_selection_data_set_text(selection_data, ew->sel_key, -1);
+ if (gtk_tree_selection_get_selected(sel, NULL, &iter))
+ {
+ GtkTreeModel *store =
gtk_tree_view_get_model(GTK_TREE_VIEW(listview));
+ gchar *key;
+
+ gtk_tree_model_get(store, &iter, EXIF_ADVCOL_NAME, &key, -1);
+ gtk_selection_data_set_text(selection_data, key, -1);
+ printf("%s\n",key);
+ g_free(key);
+ }
+
}
+static void advanced_exif_dnd_end(GtkWidget *widget, GdkDragContext *context,
gpointer data)
+{
+ GtkWidget *window = data;
+ gtk_widget_destroy(window);
+}
+
static void advanced_exif_dnd_begin(GtkWidget *listview, GdkDragContext
*context, gpointer data)
{
ExifWin *ew = data;
GtkTreeSelection *sel =
gtk_tree_view_get_selection(GTK_TREE_VIEW(listview));
GtkTreeIter iter;
- ew->sel_key = NULL;
if (gtk_tree_selection_get_selected(sel, NULL, &iter))
{
GtkTreeModel *store =
gtk_tree_view_get_model(GTK_TREE_VIEW(listview));
+ gchar *key;
+ GtkWidget *window;
+ GtkWidget *label;
- gtk_tree_model_get(store, &iter, EXIF_ADVCOL_NAME,
&ew->sel_key, -1);
- printf("%s\n",ew->sel_key);
+ gtk_tree_model_get(store, &iter, EXIF_ADVCOL_NAME, &key, -1);
+
+ window = gtk_window_new(GTK_WINDOW_POPUP);
+ gtk_widget_realize (window);
+
+ label = gtk_label_new(key);
+ gtk_container_add(GTK_CONTAINER (window), label);
+ gtk_widget_show(label);
+ gtk_drag_set_icon_widget(context, window, -15, 10);
+ g_signal_connect(G_OBJECT(listview), "drag_end",
+ G_CALLBACK(advanced_exif_dnd_end), window);
+
+ g_free(key);
}
-
}
-static void advanced_exif_dnd_end(GtkWidget *widget, GdkDragContext *context,
gpointer data)
-{
- ExifWin *ew = data;
- g_free(ew->sel_key);
- ew->sel_key = NULL;
-}
static void advanced_exif_add_column(GtkWidget *listview, const gchar *title,
gint n, gint sizable)
@@ -343,8 +366,6 @@
gtk_tree_view_set_search_column(GTK_TREE_VIEW(ew->listview),
EXIF_ADVCOL_NAME);
-// advanced_exif_add_column_check(ew->listview, "", EXIF_ADVCOL_ENABLED);
-
advanced_exif_add_column(ew->listview, _("Description"),
EXIF_ADVCOL_DESCRIPTION, FALSE);
advanced_exif_add_column(ew->listview, _("Value"), EXIF_ADVCOL_VALUE,
TRUE);
advanced_exif_add_column(ew->listview, _("Name"), EXIF_ADVCOL_NAME,
FALSE);
@@ -352,18 +373,16 @@
advanced_exif_add_column(ew->listview, _("Format"), EXIF_ADVCOL_FORMAT,
FALSE);
advanced_exif_add_column(ew->listview, _("Elements"),
EXIF_ADVCOL_ELEMENTS, FALSE);
- gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(ew->listview),
- GDK_BUTTON1_MASK |
GDK_BUTTON2_MASK,
- advanced_exif_drag_types,
n_exif_drag_types,
- GDK_ACTION_COPY |
GDK_ACTION_MOVE | GDK_ACTION_LINK);
+ gtk_drag_source_set(ew->listview,
+ GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
+ advanced_exif_drag_types, n_exif_drag_types,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
g_signal_connect(G_OBJECT(ew->listview), "drag_data_get",
G_CALLBACK(advanced_exif_dnd_get), ew);
g_signal_connect(G_OBJECT(ew->listview), "drag_begin",
G_CALLBACK(advanced_exif_dnd_begin), ew);
- g_signal_connect(G_OBJECT(ew->listview), "drag_end",
- G_CALLBACK(advanced_exif_dnd_end), ew);
ew->scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ew->scrolled),
GTK_SHADOW_IN);
Modified: trunk/src/bar_exif.c
===================================================================
--- trunk/src/bar_exif.c 2009-03-07 17:02:59 UTC (rev 1487)
+++ trunk/src/bar_exif.c 2009-03-08 09:56:40 UTC (rev 1488)
@@ -350,7 +350,7 @@
gtk_drag_dest_set(pane,
GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT
| GTK_DEST_DEFAULT_DROP,
bar_pane_exif_drop_types, n_exif_entry_drop_types,
- GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect(G_OBJECT(pane), "drag_data_received",
G_CALLBACK(bar_pane_exif_dnd_receive), NULL);
}
Modified: trunk/src/bar_keywords.c
===================================================================
--- trunk/src/bar_keywords.c 2009-03-07 17:02:59 UTC (rev 1487)
+++ trunk/src/bar_keywords.c 2009-03-08 09:56:40 UTC (rev 1488)
@@ -27,6 +27,7 @@
#include "ui_menu.h"
#include "rcfile.h"
#include "layout.h"
+#include "dnd.h"
static const gchar *keyword_favorite_defaults[] = {
N_("Favorite"),
@@ -285,7 +286,10 @@
keyword_list_push(pkd->keyword_view, list);
string_list_free(list);
+ /*
+ keyword_list_push triggers bar_pane_keywords_change which calls
bar_keyword_tree_sync, no need to do it again
bar_keyword_tree_sync(pkd);
+ */
}
void bar_pane_keywords_filter_modify(GtkTreeModel *model, GtkTreeIter *iter,
GValue *value, gint column, gpointer data)
@@ -293,7 +297,7 @@
PaneKeywordsData *pkd = data;
GtkTreeModel *keyword_tree =
gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
GtkTreeIter child_iter;
-
+
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model),
&child_iter, iter);
memset(value, 0, sizeof (GValue));
@@ -440,6 +444,180 @@
*/
}
+
+static GtkTargetEntry bar_pane_keywords_drag_types[] = {
+ { TARGET_APP_KEYWORD_PATH_STRING, GTK_TARGET_SAME_WIDGET,
TARGET_APP_KEYWORD_PATH },
+ { "text/plain", 0, TARGET_TEXT_PLAIN }
+};
+static gint n_keywords_drag_types = 2;
+
+
+static GtkTargetEntry bar_pane_keywords_drop_types[] = {
+ { TARGET_APP_KEYWORD_PATH_STRING, GTK_TARGET_SAME_WIDGET,
TARGET_APP_KEYWORD_PATH },
+ { "text/plain", 0, TARGET_TEXT_PLAIN }
+};
+static gint n_keywords_drop_types = 2;
+
+
+static void bar_pane_keywords_dnd_get(GtkWidget *tree_view, GdkDragContext
*context,
+ GtkSelectionData *selection_data, guint
info,
+ guint time, gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeIter child_iter;
+ GtkTreeModel *keyword_tree;
+
+ GtkTreeSelection *sel =
gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter)) return;
+
+ keyword_tree =
gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
+
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model),
&child_iter, &iter);
+
+ switch (info)
+ {
+ case TARGET_APP_KEYWORD_PATH:
+ {
+ GList *path = keyword_tree_get_path(keyword_tree,
&child_iter);
+ gtk_selection_data_set(selection_data,
selection_data->target,
+ 8, (gpointer) &path,
sizeof(path));
+ break;
+ }
+
+ case TARGET_TEXT_PLAIN:
+ default:
+ {
+ gchar *name = keyword_get_name(keyword_tree,
&child_iter);
+ gtk_selection_data_set_text(selection_data, name, -1);
+printf("name %s\n", name);
+ g_free(name);
+ }
+ break;
+ }
+}
+
+static void bar_pane_keywords_dnd_begin(GtkWidget *treeview, GdkDragContext
*context, gpointer data)
+{
+}
+
+static void bar_pane_keywords_dnd_end(GtkWidget *widget, GdkDragContext
*context, gpointer data)
+{
+}
+
+static void bar_pane_keywords_dnd_receive(GtkWidget *tree_view, GdkDragContext
*context,
+ gint x, gint y,
+ GtkSelectionData *selection_data,
guint info,
+ guint time, gpointer data)
+{
+ GtkTreePath *tpath = NULL;
+ GtkTreeViewDropPosition pos;
+ GtkTreeModel *model;
+
+ GtkTreeModel *keyword_tree;
+ gboolean src_valid = FALSE;
+ gchar *new_keyword = NULL;
+
+ /* iterators for keyword_tree */
+ GtkTreeIter src_kw_iter;
+ GtkTreeIter dest_kw_iter;
+ GtkTreeIter new_kw_iter;
+
+ g_signal_stop_emission_by_name(tree_view, "drag_data_received");
+
+ gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(tree_view), NULL, pos);
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));
+ keyword_tree =
gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
+
+ gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(tree_view), x, y,
&tpath, &pos);
+
+
+ switch (info)
+ {
+ case TARGET_APP_KEYWORD_PATH:
+ {
+ GList *path = *(gpointer *)selection_data->data;
+ src_valid = keyword_tree_get_iter(keyword_tree,
&src_kw_iter, path);
+ string_list_free(path);
+ break;
+ }
+ default:
+ new_keyword = (gchar *)selection_data->data;
+ break;
+ }
+
+ if (tpath)
+ {
+ GtkTreeIter dest_iter;
+ gtk_tree_model_get_iter(model, &dest_iter, tpath);
+ gtk_tree_path_free(tpath);
+
gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model),
&dest_kw_iter, &dest_iter);
+
+
+ if ((pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos ==
GTK_TREE_VIEW_DROP_INTO_OR_AFTER) &&
+ !gtk_tree_model_iter_has_child(keyword_tree, &dest_kw_iter))
+ {
+ gtk_tree_store_append(GTK_TREE_STORE(keyword_tree),
&new_kw_iter, &dest_kw_iter);
+ }
+ else
+ {
+ switch (pos)
+ {
+ case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
+ case GTK_TREE_VIEW_DROP_BEFORE:
+
gtk_tree_store_insert_before(GTK_TREE_STORE(keyword_tree), &new_kw_iter, NULL,
&dest_kw_iter);
+ break;
+ case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
+ case GTK_TREE_VIEW_DROP_AFTER:
+
gtk_tree_store_insert_after(GTK_TREE_STORE(keyword_tree), &new_kw_iter, NULL,
&dest_kw_iter);
+ break;
+ }
+ }
+ }
+ else
+ {
+ gtk_tree_store_append(GTK_TREE_STORE(keyword_tree),
&new_kw_iter, NULL);
+ }
+
+
+ if (src_valid)
+ {
+ keyword_move_recursive(GTK_TREE_STORE(keyword_tree),
&new_kw_iter, &src_kw_iter);
+ }
+
+ if (new_keyword)
+ {
+ keyword_set(GTK_TREE_STORE(keyword_tree), &new_kw_iter,
new_keyword, TRUE);
+ }
+}
+
+static gint bar_pane_keywords_dnd_motion(GtkWidget *tree_view, GdkDragContext
*context,
+ gint x, gint y, guint time, gpointer
data)
+{
+ GtkTreePath *tpath = NULL;
+ GtkTreeViewDropPosition pos;
+ gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(tree_view), x, y,
&tpath, &pos);
+ if (tpath)
+ {
+ GtkTreeModel *model;
+ GtkTreeIter dest_iter;
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));
+ gtk_tree_model_get_iter(model, &dest_iter, tpath);
+ if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE &&
gtk_tree_model_iter_has_child(model, &dest_iter))
+ pos = GTK_TREE_VIEW_DROP_BEFORE;
+
+ if (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER &&
gtk_tree_model_iter_has_child(model, &dest_iter))
+ pos = GTK_TREE_VIEW_DROP_AFTER;
+ }
+
+ gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(tree_view), tpath, pos);
+ gtk_tree_path_free(tpath);
+ gdk_drag_status(context, GDK_ACTION_COPY, time);
+ return TRUE;
+}
+
+
void bar_pane_keywords_close(GtkWidget *bar)
{
PaneKeywordsData *pkd;
@@ -591,6 +769,30 @@
gtk_tree_view_append_column(GTK_TREE_VIEW(pkd->keyword_treeview),
column);
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(pkd->keyword_treeview),
column);
+ gtk_drag_source_set(pkd->keyword_treeview,
+ GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
+ bar_pane_keywords_drag_types, n_keywords_drag_types,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE |
GDK_ACTION_LINK);
+
+ g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_data_get",
+ G_CALLBACK(bar_pane_keywords_dnd_get), pkd);
+
+ g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_begin",
+ G_CALLBACK(bar_pane_keywords_dnd_begin), pkd);
+ g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_end",
+ G_CALLBACK(bar_pane_keywords_dnd_end), pkd);
+
+ gtk_drag_dest_set(pkd->keyword_treeview,
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT
| GTK_DEST_DEFAULT_DROP,
+ bar_pane_keywords_drop_types, n_keywords_drop_types,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+ g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_data_received",
+ G_CALLBACK(bar_pane_keywords_dnd_receive), pkd);
+
+ g_signal_connect(G_OBJECT(pkd->keyword_treeview), "drag_motion",
+ G_CALLBACK(bar_pane_keywords_dnd_motion), pkd);
+
gtk_container_add(GTK_CONTAINER(scrolled), pkd->keyword_treeview);
gtk_widget_show(pkd->keyword_treeview);
Modified: trunk/src/dnd.h
===================================================================
--- trunk/src/dnd.h 2009-03-07 17:02:59 UTC (rev 1487)
+++ trunk/src/dnd.h 2009-03-08 09:56:40 UTC (rev 1488)
@@ -16,10 +16,12 @@
#define TARGET_APP_COLLECTION_MEMBER_STRING "application/x-" GQ_APPNAME_LC
"-collection-member"
#define TARGET_APP_EXIF_ENTRY_STRING "application/x-" GQ_APPNAME_LC
"-exif-entry"
+#define TARGET_APP_KEYWORD_PATH_STRING "application/x-" GQ_APPNAME_LC
"-keyword-path"
enum {
TARGET_APP_COLLECTION_MEMBER,
TARGET_APP_EXIF_ENTRY,
+ TARGET_APP_KEYWORD_PATH,
TARGET_URI_LIST,
TARGET_TEXT_PLAIN
};
Modified: trunk/src/metadata.c
===================================================================
--- trunk/src/metadata.c 2009-03-07 17:02:59 UTC (rev 1487)
+++ trunk/src/metadata.c 2009-03-08 09:56:40 UTC (rev 1488)
@@ -729,6 +729,93 @@
g_free(casefold);
}
+void keyword_copy(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter
*from)
+{
+
+ gchar *mark, *name, *casefold;
+ gboolean is_keyword;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(keyword_tree), from,
KEYWORD_COLUMN_MARK, &mark,
+ KEYWORD_COLUMN_NAME, &name,
+ KEYWORD_COLUMN_CASEFOLD,
&casefold,
+ KEYWORD_COLUMN_IS_KEYWORD,
&is_keyword, -1);
+
+ gtk_tree_store_set(keyword_tree, to, KEYWORD_COLUMN_MARK, mark,
+ KEYWORD_COLUMN_NAME, name,
+ KEYWORD_COLUMN_CASEFOLD,
casefold,
+ KEYWORD_COLUMN_IS_KEYWORD,
is_keyword, -1);
+ g_free(mark);
+ g_free(name);
+ g_free(casefold);
+}
+
+void keyword_copy_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *to,
GtkTreeIter *from)
+{
+ GtkTreeIter from_child;
+
+ keyword_copy(keyword_tree, to, from);
+
+ if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(keyword_tree),
&from_child, from)) return;
+
+ while (TRUE)
+ {
+ GtkTreeIter to_child;
+ gtk_tree_store_append(keyword_tree, &to_child, to);
+ keyword_copy_recursive(keyword_tree, &to_child, &from_child);
+ if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_tree),
&from_child)) return;
+ }
+}
+
+void keyword_move_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *to,
GtkTreeIter *from)
+{
+ keyword_copy_recursive(keyword_tree, to, from);
+ gtk_tree_store_remove(keyword_tree, from);
+}
+
+GList *keyword_tree_get_path(GtkTreeModel *keyword_tree, GtkTreeIter *iter_ptr)
+{
+ GList *path = NULL;
+ GtkTreeIter iter = *iter_ptr;
+
+ while (TRUE)
+ {
+ GtkTreeIter parent;
+ path = g_list_prepend(path, keyword_get_name(keyword_tree,
&iter));
+ if (!gtk_tree_model_iter_parent(keyword_tree, &parent, &iter))
break;
+ iter = parent;
+ }
+ return path;
+}
+
+gboolean keyword_tree_get_iter(GtkTreeModel *keyword_tree, GtkTreeIter
*iter_ptr, GList *path)
+{
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter_first(keyword_tree, &iter)) return FALSE;
+
+ while (TRUE)
+ {
+ GtkTreeIter children;
+ while (TRUE)
+ {
+ gchar *name = keyword_get_name(keyword_tree, &iter);
+ if (strcmp(name, path->data) == 0) break;
+ g_free(name);
+ if (!gtk_tree_model_iter_next(keyword_tree, &iter))
return FALSE;
+ }
+ path = path->next;
+ if (!path)
+ {
+ *iter_ptr = iter;
+ return TRUE;
+ }
+
+ if (!gtk_tree_model_iter_children(keyword_tree, &children,
&iter)) return FALSE;
+ iter = children;
+ }
+}
+
+
static gboolean keyword_tree_is_set_casefold(GtkTreeModel *keyword_tree,
GtkTreeIter iter, GList *casefold_list)
{
if (!casefold_list) return FALSE;
@@ -798,7 +885,6 @@
if (!find_string_in_list_utf8nocase(*kw_list, name))
{
*kw_list = g_list_append(*kw_list, name);
- printf("set %s\n", name);
}
else
{
@@ -823,7 +909,6 @@
if (found)
{
*kw_list = g_list_remove(*kw_list, found);
- printf("remove %s\n", found);
g_free(found);
}
g_free(name);
Modified: trunk/src/metadata.h
===================================================================
--- trunk/src/metadata.h 2009-03-07 17:02:59 UTC (rev 1487)
+++ trunk/src/metadata.h 2009-03-08 09:56:40 UTC (rev 1488)
@@ -54,6 +54,14 @@
gchar *keyword_get_name(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
gchar *keyword_get_casefold(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
gboolean keyword_get_is_keyword(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
+
+void keyword_copy(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter
*from);
+void keyword_copy_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *to,
GtkTreeIter *from);
+void keyword_move_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *to,
GtkTreeIter *from);
+
+GList *keyword_tree_get_path(GtkTreeModel *keyword_tree, GtkTreeIter
*iter_ptr);
+gboolean keyword_tree_get_iter(GtkTreeModel *keyword_tree, GtkTreeIter
*iter_ptr, GList *path);
+
void keyword_set(GtkTreeStore *keyword_tree, GtkTreeIter *iter, const gchar
*name, gboolean is_keyword);
gboolean keyword_tree_is_set(GtkTreeModel *keyword_tree, GtkTreeIter *iter,
GList *kw_list);
void keyword_tree_set(GtkTreeModel *keyword_tree, GtkTreeIter *iter_ptr, GList
**kw_list);
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA
-OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise
-Strategies to boost innovation and cut costs with open source participation
-Receive a $600 discount off the registration fee with the source code: SFAD
http://p.sf.net/sfu/XcvMzF8H
_______________________________________________
Geeqie-svn mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/geeqie-svn