Hi Jefferson,

Thanks for your interest. You may find my patch attached.

If you have any questions, please let me know.

Best regards,
Mario

2018-04-15 20:46 GMT+02:00 Mario Lüder <monsieur.m...@gmail.com>:

> Hi,
>
> I missed the possibility to order images by drag and drop in lighttable. I
> needed this for my image selection process. I have to see the image in the
> right order to decide which one shall be kept and to see the sequence is
> "working".
>
> I have seen on several forums and the bug tracker that others are also
> keen to see this freature.
>
> So I took the time and implemented a simple version of it.
>
> Please see the short screencast on youtube
> https://youtu.be/Z2dwCXXzxbU
>
> The custom sequence is stored for each collection. So you can select an
> other collection and come back later to continue your work.
>
> I would like to post my changes for review. May I submit it to an
> development branch?
>
> Best,
> Mario
>

___________________________________________________________________________
darktable developer mailing list
to unsubscribe send a mail to darktable-dev+unsubscr...@lists.darktable.org

From d129ade50deb51c93159aef53b5298682db04adc Mon Sep 17 00:00:00 2001
From: Mario Lueder <monsieur.m...@gmail.com>
Date: Sun, 15 Apr 2018 20:20:43 +0200
Subject: [PATCH] Added custom image order by drag and drop in lighttable

---
 src/common/collection.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++-
 src/common/collection.h |   6 +-
 src/common/database.c   |  20 +++-
 src/gui/gtk.c           |   4 +-
 src/libs/tools/filter.c |   1 +
 src/views/lighttable.c  |  91 +++++++++++++++++-
 6 files changed, 360 insertions(+), 8 deletions(-)

diff --git a/src/common/collection.c b/src/common/collection.c
index 8fb39e9..4dff27d 100644
--- a/src/common/collection.c
+++ b/src/common/collection.c
@@ -63,6 +63,32 @@ static void _dt_collection_recount_callback_2(gpointer instance, uint8_t id, gpo
 /* determine image offset of specified imgid for the given collection */
 static int dt_collection_image_offset_with_collection(const dt_collection_t *collection, int imgid);
 
+/* insert images from filtered collection into a respected custom sorted collection */
+static void dt_collection_insert_custom_order_images(const dt_collection_t *collection);
+
+gchar * dt_collection_get_current_id()
+{
+  char confname[200];
+  gchar * const separator = g_strdup(":");
+  gchar * collection_id = g_strdup("");
+
+  const int num_rules = dt_conf_get_int("plugins/lighttable/collect/num_rules");
+
+  for (int i = 0; i < num_rules; ++i)
+  {
+    snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", i);
+    gchar * const ruleName = dt_conf_get_string(confname);
+    gchar * new_collection_id = g_strconcat(collection_id, separator, ruleName, NULL);
+    g_free(ruleName);
+    g_free(collection_id);
+    collection_id = new_collection_id;
+  }
+
+  g_free(separator);
+
+  return collection_id;
+}
+
 const dt_collection_t *dt_collection_new(const dt_collection_t *clone)
 {
   dt_collection_t *collection = g_malloc0(sizeof(dt_collection_t));
@@ -185,6 +211,14 @@ int dt_collection_update(const dt_collection_t *collection)
                            wq);
   else if(collection->params.query_flags & COLLECTION_QUERY_USE_ONLY_WHERE_EXT)
     selq = dt_util_dstrcat(selq, "SELECT DISTINCT images.id FROM main.images %s", wq);
+  else if(collection->params.sort == DT_COLLECTION_SORT_CUSTOM_ORDER
+          && (collection->params.query_flags & COLLECTION_QUERY_USE_SORT))
+  {
+    gchar * const collection_id = dt_collection_get_current_id();
+    selq = dt_util_dstrcat(selq, "SELECT DISTINCT id FROM (SELECT * FROM main.images WHERE %s) LEFT JOIN custom_image_order ON (id = img_id AND collection_id = '%s')",
+                           wq, collection_id);
+    g_free(collection_id);
+  }
   else
     selq = dt_util_dstrcat(selq, "SELECT DISTINCT id FROM main.images WHERE %s", wq);
 
@@ -390,6 +424,10 @@ gchar *dt_collection_get_sort_query(const dt_collection_t *collection)
         sq = dt_util_dstrcat(sq, ORDER_BY_QUERY, "folder DESC, filename DESC, version");
         break;
 
+      case DT_COLLECTION_SORT_CUSTOM_ORDER:
+        sq = dt_util_dstrcat(sq, ORDER_BY_QUERY, "position DESC, filename DESC, version");
+        break;
+
       case DT_COLLECTION_SORT_NONE:
         // shouldn't happen
         break;
@@ -427,6 +465,10 @@ gchar *dt_collection_get_sort_query(const dt_collection_t *collection)
         sq = dt_util_dstrcat(sq, ORDER_BY_QUERY, "folder, filename, version");
         break;
 
+      case DT_COLLECTION_SORT_CUSTOM_ORDER:
+        sq = dt_util_dstrcat(sq, ORDER_BY_QUERY, "position, filename, version");
+        break;
+
       case DT_COLLECTION_SORT_NONE:
         // shouldn't happen
         break;
@@ -516,7 +558,6 @@ GList *dt_collection_get(const dt_collection_t *collection, int limit, gboolean
   if((collection->params.query_flags & COLLECTION_QUERY_USE_SORT))
     sq = dt_collection_get_sort_query(collection);
 
-
   sqlite3_stmt *stmt = NULL;
 
   /* build the query string */
@@ -529,6 +570,14 @@ GList *dt_collection_get(const dt_collection_t *collection, int limit, gboolean
           && (collection->params.query_flags & COLLECTION_QUERY_USE_SORT))
     query = dt_util_dstrcat(
         query, "JOIN (SELECT id AS film_rolls_id, folder FROM main.film_rolls) ON film_id = film_rolls_id ");
+  else if(collection->params.sort == DT_COLLECTION_SORT_CUSTOM_ORDER
+          && (collection->params.query_flags & COLLECTION_QUERY_USE_SORT))
+  {
+    gchar * const collection_id = dt_collection_get_current_id();
+    query = dt_util_dstrcat(
+          query, "LEFT JOIN custom_image_order ON (id = img_id AND '%s') ", collection_id);
+    g_free(collection_id);
+  }
 
   if (selected)
     query = dt_util_dstrcat(query, "WHERE id IN (SELECT imgid FROM main.selected_images) %s LIMIT ?1", sq);
@@ -1217,6 +1266,8 @@ void dt_collection_update_query(const dt_collection_t *collection)
     g_free(complete_query);
   }
 
+  /* update the custom sorted collection */
+  dt_collection_insert_custom_order_images(collection);
 
   /* raise signal of collection change, only if this is an original */
   if(!collection->clone) dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED);
@@ -1331,6 +1382,199 @@ static void _dt_collection_recount_callback_2(gpointer instance, uint8_t id, gpo
   }
 }
 
+/* move images with drag and drop */
+void dt_collection_move_before(const int32_t image_id, GList * selected_images)
+{
+  if (!selected_images)
+  {
+    return;
+  }
+
+  const guint selected_images_length = g_list_length(selected_images);
+
+  if (selected_images_length == 0)
+  {
+    return;
+  }
+
+  gchar * const collection_id = dt_collection_get_current_id();
+
+  // TODO: to forget to add images to the end of the list
+
+  // getting the position of the target image
+  int target_image_pos = -1;
+
+  if (image_id >= 0)
+  {
+    sqlite3_stmt *stmt = NULL;
+    gchar *image_pos_query = NULL;
+    image_pos_query = dt_util_dstrcat(
+          image_pos_query,
+          "SELECT position FROM main.custom_image_order WHERE img_id = %i AND collection_id='%s'",
+          image_id, collection_id);
+
+    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), image_pos_query, -1, &stmt, NULL);
+
+    if(sqlite3_step(stmt) == SQLITE_ROW)
+    {
+      target_image_pos = sqlite3_column_int(stmt, 0);
+    }
+
+    sqlite3_finalize(stmt);
+    g_free(image_pos_query);
+  }
+
+  if (target_image_pos >= 0)
+  {
+    DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "BEGIN;", NULL, NULL, NULL);
+    sqlite3_stmt *stmt = NULL;
+
+    // shift image positions to make some space
+    gchar *update_image_pos_query = NULL;
+    update_image_pos_query = dt_util_dstrcat(
+          update_image_pos_query,
+          "UPDATE main.custom_image_order SET position = position + %i WHERE collection_id='%s' AND position >= %i",
+          selected_images_length, collection_id, target_image_pos);
+
+    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), update_image_pos_query, -1, &stmt, NULL);
+    sqlite3_step(stmt);
+    sqlite3_finalize(stmt);
+    g_free(update_image_pos_query);
+    DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "COMMIT;", NULL, NULL, NULL);
+
+    DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "BEGIN;", NULL, NULL, NULL);
+
+    // move images to their intended positons
+    int new_image_pos = target_image_pos;
+
+    gchar *insert_image_pos_query = NULL;
+    insert_image_pos_query = dt_util_dstrcat(
+          insert_image_pos_query,
+          "UPDATE main.custom_image_order SET position = ?1 WHERE img_id = ?2 AND collection_id='%s'",
+          collection_id);
+
+    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), insert_image_pos_query, -1, &stmt, NULL);
+
+    for (const GList * selected_images_iter = selected_images;
+        selected_images_iter != NULL;
+        selected_images_iter = selected_images_iter->next)
+    {
+      const int moved_image_id = GPOINTER_TO_INT(selected_images_iter->data);
+
+      DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, new_image_pos);
+      DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, moved_image_id);
+      sqlite3_step(stmt);
+      sqlite3_reset(stmt);
+      new_image_pos++;
+    }
+    sqlite3_finalize(stmt);
+    g_free(insert_image_pos_query);
+    DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "COMMIT;", NULL, NULL, NULL);
+  }
+  else
+  {
+    // move images to the end of the list
+    sqlite3_stmt *stmt = NULL;
+
+    // get last position
+    int max_position = -1;
+
+    gchar *max_position_query = NULL;
+    max_position_query = dt_util_dstrcat(max_position_query, "SELECT MAX(position) FROM custom_image_order WHERE collection_id = '%s'", collection_id);
+    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), max_position_query, -1, &stmt, NULL);
+
+    if (sqlite3_step(stmt) == SQLITE_ROW)
+    {
+      max_position = sqlite3_column_int(stmt, 0);
+    }
+
+    sqlite3_finalize(stmt);
+    g_free(max_position_query);
+
+    sqlite3_stmt *update_stmt = NULL;
+    gchar *update_query = NULL;
+
+    DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "BEGIN;", NULL, NULL, NULL);
+
+    // move images to last position in custom image order table
+    update_query = dt_util_dstrcat(update_query,
+                                   "UPDATE main.custom_image_order SET position = ?1 WHERE img_id = ?2 AND collection_id='%s'",
+                                   collection_id);
+    DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), update_query, -1, &update_stmt, NULL);
+
+    for (const GList * selected_images_iter = selected_images;
+        selected_images_iter != NULL;
+        selected_images_iter = selected_images_iter->next)
+    {
+      max_position++;
+      const int moved_image_id = GPOINTER_TO_INT(selected_images_iter->data);
+      DT_DEBUG_SQLITE3_BIND_INT(update_stmt, 1, max_position);
+      DT_DEBUG_SQLITE3_BIND_INT(update_stmt, 2, moved_image_id);
+      sqlite3_step(update_stmt);
+      sqlite3_reset(update_stmt);
+    }
+
+    sqlite3_finalize(update_stmt);
+    g_free(update_query);
+    DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "COMMIT;", NULL, NULL, NULL);
+  }
+
+  g_free(collection_id);
+}
+
+static void dt_collection_insert_custom_order_images(const dt_collection_t *collection)
+{
+  sqlite3_stmt *stmt = NULL;
+  gchar * const collection_id = dt_collection_get_current_id();
+  int max_position = -1;
+
+  // get max position
+  gchar *max_position_query = NULL;
+  max_position_query = dt_util_dstrcat(max_position_query, "SELECT MAX(position) FROM custom_image_order WHERE collection_id = '%s'", collection_id);
+  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), max_position_query, -1, &stmt, NULL);
+
+  if (sqlite3_step(stmt) == SQLITE_ROW)
+  {
+    max_position = sqlite3_column_int(stmt, 0);
+  }
+
+  sqlite3_finalize(stmt);
+
+  // get images of filtered collection
+  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "BEGIN;", NULL, NULL, NULL);
+  const gchar * image_collection_query = dt_collection_get_query(collection);
+
+  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), image_collection_query, -1, &stmt, NULL);
+  DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, 0);
+  DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, -1);
+
+  sqlite3_stmt *ins_stmt = NULL;
+  gchar *insert_query = NULL;
+
+  // insert images into custom order table if they don't exist - image id and collection id is key
+  insert_query = dt_util_dstrcat(insert_query, "INSERT OR IGNORE INTO custom_image_order(img_id, collection_id, position) VALUES(?1, '%s', ?2)", collection_id);
+  DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), insert_query, -1, &ins_stmt, NULL);
+
+  while(sqlite3_step(stmt) == SQLITE_ROW)
+  {
+    max_position++;
+    const int img_id = sqlite3_column_int(stmt, 0);
+    DT_DEBUG_SQLITE3_BIND_INT(ins_stmt, 1, img_id);
+    DT_DEBUG_SQLITE3_BIND_INT(ins_stmt, 2, max_position);
+    sqlite3_step(ins_stmt);
+    sqlite3_reset(ins_stmt);
+  }
+
+  DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "COMMIT;", NULL, NULL, NULL);
+
+  sqlite3_finalize(stmt);
+  sqlite3_finalize(ins_stmt);
+
+  g_free(insert_query);
+  g_free(max_position_query);
+  g_free(collection_id);
+}
+
 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
 // vim: shiftwidth=2 expandtab tabstop=2 cindent
 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
diff --git a/src/common/collection.h b/src/common/collection.h
index fcc6e00..5e06db6 100644
--- a/src/common/collection.h
+++ b/src/common/collection.h
@@ -65,7 +65,8 @@ typedef enum dt_collection_sort_t
   DT_COLLECTION_SORT_ID,
   DT_COLLECTION_SORT_COLOR,
   DT_COLLECTION_SORT_GROUP,
-  DT_COLLECTION_SORT_PATH
+  DT_COLLECTION_SORT_PATH,
+  DT_COLLECTION_SORT_CUSTOM_ORDER
 } dt_collection_sort_t;
 
 typedef enum dt_collection_properties_t
@@ -218,6 +219,9 @@ void dt_collection_split_operator_number(const gchar *input, char **number1, cha
 void dt_collection_split_operator_datetime(const gchar *input, char **number1, char **number2,
                                            char **operator);
 
+/* move images with drag and drop */
+void dt_collection_move_before(const int32_t image_id, GList * selected_images);
+
 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
 // vim: shiftwidth=2 expandtab tabstop=2 cindent
 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
diff --git a/src/common/database.c b/src/common/database.c
index 06b4d1a..b635570 100644
--- a/src/common/database.c
+++ b/src/common/database.c
@@ -37,7 +37,7 @@
 
 // whenever _create_*_schema() gets changed you HAVE to bump this version and add an update path to
 // _upgrade_*_schema_step()!
-#define CURRENT_DATABASE_VERSION_LIBRARY 15
+#define CURRENT_DATABASE_VERSION_LIBRARY 16
 #define CURRENT_DATABASE_VERSION_DATA 1
 
 typedef struct dt_database_t
@@ -967,6 +967,18 @@ static int _upgrade_library_schema_step(dt_database_t *db, int version)
 
     sqlite3_exec(db->handle, "COMMIT", NULL, NULL, NULL);
     new_version = 15;
+  }
+  else if(version == 15)
+  {
+    sqlite3_exec(db->handle, "BEGIN TRANSACTION", NULL, NULL, NULL);
+    ////////////////////////////// custom image order
+    TRY_EXEC("CREATE TABLE main.custom_image_order (img_id, INTEGER, collection_id	TEXT, position	INTEGER,"
+                 "PRIMARY KEY(img_id,collection_id));",
+             "[init] can't create a table to store custom image order\n");
+    TRY_EXEC("CREATE INDEX main.custom_image_order_index ON custom_image_order (collection_id, position)",
+             "[init] can't create index for custom image order table\n");
+    sqlite3_exec(db->handle, "COMMIT", NULL, NULL, NULL);
+    new_version = 16;
   } // maybe in the future, see commented out code elsewhere
     //   else if(version == XXX)
     //   {
@@ -1118,6 +1130,12 @@ static void _create_library_schema(dt_database_t *db)
   ////////////////////////////// meta_data
   sqlite3_exec(db->handle, "CREATE TABLE main.meta_data (id INTEGER, key INTEGER, value VARCHAR)", NULL, NULL, NULL);
   sqlite3_exec(db->handle, "CREATE INDEX main.metadata_index ON meta_data (id, key)", NULL, NULL, NULL);
+
+  ////////////////////////////// custom image order
+  sqlite3_exec(db->handle, "CREATE TABLE main.custom_image_order (img_id, INTEGER, collection_id	TEXT, position	INTEGER,"
+               "PRIMARY KEY(img_id,collection_id));", NULL, NULL, NULL);
+  sqlite3_exec(db->handle, "CREATE INDEX main.custom_image_order_index ON custom_image_order (collection_id, position)",
+               NULL, NULL, NULL);
 }
 
 /* create the current database schema and set the version in db_info accordingly */
diff --git a/src/gui/gtk.c b/src/gui/gtk.c
index 05c4a44..e65eb09 100644
--- a/src/gui/gtk.c
+++ b/src/gui/gtk.c
@@ -56,7 +56,6 @@
 #endif
 #include <pthread.h>
 
-
 /*
  * NEW UI API
  */
@@ -838,7 +837,7 @@ static gboolean button_pressed(GtkWidget *w, GdkEventButton *event, gpointer use
   dt_control_button_pressed(event->x, event->y, pressure, event->button, event->type, event->state & 0xf);
   gtk_widget_grab_focus(w);
   gtk_widget_queue_draw(w);
-  return TRUE;
+  return FALSE;
 }
 
 static gboolean button_released(GtkWidget *w, GdkEventButton *event, gpointer user_data)
@@ -1016,6 +1015,7 @@ int dt_gui_gtk_init(dt_gui_gtk_t *gui)
   g_signal_connect(G_OBJECT(widget), "button-press-event", G_CALLBACK(button_pressed), NULL);
   g_signal_connect(G_OBJECT(widget), "button-release-event", G_CALLBACK(button_released), NULL);
   g_signal_connect(G_OBJECT(widget), "scroll-event", G_CALLBACK(scrolled), NULL);
+
   // TODO: left, right, top, bottom:
   // leave-notify-event
 
diff --git a/src/libs/tools/filter.c b/src/libs/tools/filter.c
index abe7156..a33cd86 100644
--- a/src/libs/tools/filter.c
+++ b/src/libs/tools/filter.c
@@ -143,6 +143,7 @@ void gui_init(dt_lib_module_t *self)
   gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), _("color label"));
   gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), _("group"));
   gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), _("full path"));
+  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), _("custom sort"));
 
   /* select the last selected value */
   gtk_combo_box_set_active(GTK_COMBO_BOX(widget), dt_collection_get_sort_field(darktable.collection));
diff --git a/src/views/lighttable.c b/src/views/lighttable.c
index e2225b0..836fc17 100644
--- a/src/views/lighttable.c
+++ b/src/views/lighttable.c
@@ -33,6 +33,7 @@
 #include "control/settings.h"
 #include "dtgtk/button.h"
 #include "gui/accelerators.h"
+#include "gui/drag_and_drop.h"
 #include "gui/draw.h"
 #include "gui/gtk.h"
 #include "libs/lib.h"
@@ -132,6 +133,15 @@ typedef struct dt_library_t
 static GtkTargetEntry target_list[] = { { "text/uri-list", GTK_TARGET_OTHER_APP, 0 } };
 static guint n_targets = G_N_ELEMENTS(target_list);
 
+/* drag and drop callbacks to reorder picture sequence (dnd)*/
+
+static void _dnd_get_picture_reorder(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+                                    GtkSelectionData *selection_data, guint target_type, guint time,
+                                    gpointer data);
+static void _dnd_begin_picture_reorder(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
+
+static gboolean _dnd_drag_picture_motion(GtkWidget *dest_button, GdkDragContext *dc, gint x, gint y, guint time, gpointer user_data);
+
 static void _stop_audio(dt_library_t *lib);
 
 const char *name(dt_view_t *self)
@@ -750,7 +760,6 @@ end_query_cache:
   }
 escape_image_loop:
   cairo_restore(cr);
-
   if(!lib->pan && (iir != 1 || mouse_over_id != -1)) dt_control_set_mouse_over_id(mouse_over_id);
 
   // and now the group borders
@@ -1635,12 +1644,21 @@ static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *context, g
 
 void enter(dt_view_t *self)
 {
-  // init drag&drop of files/folders
+  // init drag&drop of external files/folders into darktable
   gtk_drag_dest_set(dt_ui_center(darktable.gui->ui), GTK_DEST_DEFAULT_ALL, target_list, n_targets,
                     GDK_ACTION_COPY);
   g_signal_connect(dt_ui_center(darktable.gui->ui), "drag-data-received", G_CALLBACK(drag_and_drop_received),
                    self);
 
+  // drag and drop for custom order of picture sequence (dnd)
+  gtk_drag_source_set(dt_ui_center(darktable.gui->ui), GDK_BUTTON1_MASK, target_list_internal, n_targets_internal, GDK_ACTION_MOVE);
+  gtk_drag_dest_set(dt_ui_center(darktable.gui->ui), GTK_DEST_DEFAULT_ALL, target_list_internal, n_targets_internal,
+                    GDK_ACTION_MOVE);
+
+  g_signal_connect(dt_ui_center(darktable.gui->ui), "drag-begin",    G_CALLBACK(_dnd_begin_picture_reorder), (gpointer)self);
+  g_signal_connect(dt_ui_center(darktable.gui->ui), "drag-data-get", G_CALLBACK(_dnd_get_picture_reorder),   (gpointer)self);
+  g_signal_connect(dt_ui_center(darktable.gui->ui), "drag_motion",   G_CALLBACK(_dnd_drag_picture_motion),   (gpointer)self);
+
   /* connect to signals */
   dt_control_signal_connect(darktable.signals, DT_SIGNAL_DEVELOP_MIPMAP_UPDATED,
                             G_CALLBACK(_lighttable_mipmaps_updated_signal_callback), (gpointer)self);
@@ -1824,7 +1842,12 @@ int button_pressed(dt_view_t *self, double x, double y, double pressure, int whi
   lib->select_offset_y = lib->zoom_y;
   lib->select_offset_x += x;
   lib->select_offset_y += y;
-  lib->pan = 1;
+
+  if (dt_control_get_mouse_over_id() < 0)
+  {
+    lib->pan = 1;
+  }
+
   if(which == 1) dt_control_change_cursor(GDK_HAND1);
   if(which == 1 && type == GDK_2BUTTON_PRESS) return 0;
   // image button pressed?
@@ -2439,6 +2462,68 @@ void gui_init(dt_view_t *self)
   g_signal_connect(G_OBJECT(display_profile), "value-changed", G_CALLBACK(display_profile_callback), NULL);
 }
 
+static void _dnd_get_picture_reorder(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+                                    GtkSelectionData *selection_data, guint target_type, guint time,
+                                    gpointer data)
+{
+  GList *selected_images = dt_collection_get_selected(darktable.collection, -1);
+  const int32_t mouse_over_id = dt_control_get_mouse_over_id();
+  dt_collection_move_before(mouse_over_id, selected_images);
+
+  dt_control_button_released(x, y, GDK_BUTTON1_MASK, 0 & 0xf);
+  //gtk_widget_queue_draw(widget);
+  _update_collected_images(darktable.view_manager->proxy.lighttable.view);
+  g_list_free(selected_images);
+}
+
+static void _dnd_begin_picture_reorder(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
+{
+  const int ts = DT_PIXEL_APPLY_DPI(64);
+
+  GList *selected_images = dt_collection_get_selected(darktable.collection, 1);
+
+  // if we are dragging a single image -> use the thumbnail of that image
+  // otherwise use the generic d&d icon
+  // TODO: have something pretty in the 2nd case, too.
+  if(dt_collection_get_selected_count(NULL) == 1 && selected_images)
+  {
+    const int imgid = GPOINTER_TO_INT(selected_images->data);
+
+    dt_mipmap_buffer_t buf;
+    dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size(darktable.mipmap_cache, ts, ts);
+    dt_mipmap_cache_get(darktable.mipmap_cache, &buf, imgid, mip, DT_MIPMAP_BLOCKING, 'r');
+
+    if(buf.buf)
+    {
+      for(size_t i = 3; i < (size_t)4 * buf.width * buf.height; i += 4) buf.buf[i] = UINT8_MAX;
+
+      int w = ts, h = ts;
+      if(buf.width < buf.height)
+        w = (buf.width * ts) / buf.height; // portrait
+      else
+        h = (buf.height * ts) / buf.width; // landscape
+
+      GdkPixbuf *source = gdk_pixbuf_new_from_data(buf.buf, GDK_COLORSPACE_RGB, TRUE, 8, buf.width,
+                                                   buf.height, buf.width * 4, NULL, NULL);
+      GdkPixbuf *scaled = gdk_pixbuf_scale_simple(source, w, h, GDK_INTERP_HYPER);
+      gtk_drag_set_icon_pixbuf(context, scaled, 0, h);
+
+      if(source) g_object_unref(source);
+      if(scaled) g_object_unref(scaled);
+    }
+
+    dt_mipmap_cache_release(darktable.mipmap_cache, &buf);
+  }
+
+  g_list_free(selected_images);
+}
+
+static gboolean _dnd_drag_picture_motion(GtkWidget *dest_button, GdkDragContext *dc, gint x, gint y, guint time, gpointer user_data)
+{
+  dt_control_queue_redraw_center();
+  return FALSE;
+}
+
 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
 // vim: shiftwidth=2 expandtab tabstop=2 cindent
 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
-- 
2.7.4

Reply via email to