Revision 5de30b493da9bb707b0bb211107acf10420cb7d6 authored by Ulrich Pegelow on 30 November 2012, 18:40:36 UTC, committed by Ulrich Pegelow on 30 November 2012, 18:40:36 UTC
1 parent 984f66c
collect.c
/*
This file is part of darktable,
copyright (c) 2009--2011 johannes hanika, henrik andersson.
copyright (c) 2012 Jose Carlos Garcia Sogo
darktable 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 3 of the License, or
(at your option) any later version.
darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/darktable.h"
#include "common/film.h"
#include "common/collection.h"
#include "common/debug.h"
#include "control/conf.h"
#include "control/control.h"
#include "control/jobs.h"
#include "gui/gtk.h"
#include "dtgtk/button.h"
#include "libs/lib.h"
#include "common/metadata.h"
#include "common/utility.h"
#include "libs/collect.h"
#include "views/view.h"
#include <regex.h>
DT_MODULE(1)
#define MAX_RULES 10
#define PARAM_STRING_SIZE 256 // FIXME: is this enough !?
/* Folders code starts here
TODO: Clean it */
//DT_MODULE(1)
typedef struct dt_lib_collect_rule_t
{
long int num;
GtkWidget *hbox;
GtkComboBox *combo;
GtkWidget *text;
GtkWidget *button;
gboolean typing;
}
dt_lib_collect_rule_t;
typedef struct dt_lib_collect_t
{
dt_lib_collect_rule_t rule[MAX_RULES];
int active_rule;
GtkTreeView *view;
GtkTreeModel *treemodel;
gboolean tree_new;
GtkTreeModel *listmodel;
GtkScrolledWindow *scrolledwindow;
// GVolumeMonitor *gv_monitor;
GtkBox *box;
GtkScrolledWindow *sw2;
GPtrArray *labels;
GPtrArray *trees;
struct dt_lib_collect_params_t *params;
}
dt_lib_collect_t;
typedef struct dt_lib_collect_params_t
{
uint32_t rules;
struct
{
uint32_t item:16;
uint32_t mode:16;
char string[PARAM_STRING_SIZE];
} rule[MAX_RULES];
} dt_lib_collect_params_t;
typedef enum dt_lib_collect_cols_t
{
DT_LIB_COLLECT_COL_TEXT=0,
DT_LIB_COLLECT_COL_ID,
DT_LIB_COLLECT_COL_TOOLTIP,
DT_LIB_COLLECT_COL_PATH,
DT_LIB_COLLECT_COL_COUNT,
DT_LIB_COLLECT_NUM_COLS
}
dt_lib_collect_cols_t;
typedef struct _image_t
{
int id;
int filmid;
gchar *path;
gchar *filename;
int exists;
}
_image_t;
static void _lib_collect_gui_update (dt_lib_module_t *d);
static void _lib_folders_update_collection(const gchar *filmroll);
const char*
name ()
{
return _("collect images");
}
void init_presets(dt_lib_module_t *self)
{
}
static void
row_activated (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, dt_lib_collect_t *d);
/* Update the params struct with active ruleset */
static void _lib_collect_update_params(dt_lib_collect_t *d)
{
/* reset params */
dt_lib_collect_params_t *p = d->params;
memset(p,0,sizeof(dt_lib_collect_params_t));
/* for each active rule set update params */
const int active = CLAMP(dt_conf_get_int("plugins/lighttable/collect/num_rules") - 1, 0, (MAX_RULES-1));
char confname[200];
for (int i=0; i<=active; i++)
{
/* get item */
snprintf(confname, 200, "plugins/lighttable/collect/item%1d", i);
p->rule[i].item = dt_conf_get_int(confname);
/* get mode */
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", i);
p->rule[i].mode = dt_conf_get_int(confname);
/* get string */
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", i);
gchar* string = dt_conf_get_string(confname);
if (string != NULL)
{
snprintf(p->rule[i].string,PARAM_STRING_SIZE,"%s", string);
g_free(string);
}
fprintf(stderr,"[%i] %d,%d,%s\n",i, p->rule[i].item, p->rule[i].mode, p->rule[i].string);
}
p->rules = active+1;
}
void *get_params(dt_lib_module_t *self, int *size)
{
_lib_collect_update_params(self->data);
/* allocate a copy of params to return, freed by caller */
*size = sizeof(dt_lib_collect_params_t);
void *p = malloc(*size);
memcpy(p,((dt_lib_collect_t *)self->data)->params,*size);
return p;
}
int set_params(dt_lib_module_t *self, const void *params, int size)
{
/* update conf settings from params */
dt_lib_collect_params_t *p = (dt_lib_collect_params_t *)params;
char confname[200];
for (int i=0; i<p->rules; i++)
{
/* set item */
snprintf(confname, 200, "plugins/lighttable/collect/item%1d", i);
dt_conf_set_int(confname, p->rule[i].item);
/* set mode */
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", i);
dt_conf_set_int(confname, p->rule[i].mode);
/* set string */
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", i);
dt_conf_set_string(confname, p->rule[i].string);
}
/* set number of rules */
snprintf(confname, 200, "plugins/lighttable/collect/num_rules");
dt_conf_set_int(confname, p->rules);
/* update ui */
_lib_collect_gui_update(self);
/* update view */
dt_collection_update_query(darktable.collection);
return 0;
}
uint32_t views()
{
return DT_VIEW_LIGHTTABLE | DT_VIEW_MAP;
}
uint32_t container()
{
return DT_UI_CONTAINER_PANEL_LEFT_CENTER;
}
/* callback for drag and drop */
/*static void _lib_keywords_drag_data_received_callback(GtkWidget *w,
GdkDragContext *dctx,
guint x,
guint y,
GtkSelectionData *data,
guint info,
guint time,
gpointer user_data);
*/
/* set the data for drag and drop, eg the treeview path of drag source */
/*static void _lib_keywords_drag_data_get_callback(GtkWidget *w,
GdkDragContext *dctx,
GtkSelectionData *data,
guint info,
guint time,
gpointer user_data);
*/
/* add keyword to collection rules */
/*static void _lib_keywords_add_collection_rule(GtkTreeView *view, GtkTreePath *tp,
GtkTreeViewColumn *tvc, gpointer user_data);
*/
void _sync_list(gpointer *data, gpointer *user_data)
{
_image_t *img = (_image_t *)data;
if(img->exists == 0)
{
//remove filie
dt_image_remove(img->id);
return;
}
if(img->id == -1)
{
//add file
gchar *fullpath = NULL;
fullpath = dt_util_dstrcat(fullpath, "%s/%s", img->path, img->filename);
/* TODO: Check if JPEGs are set to be ignored */
dt_image_import(img->filmid, fullpath, 1);
g_free(fullpath);
return;
}
}
void view_popup_menu_onSync (GtkWidget *menuitem, gpointer userdata)
{
GtkTreeView *treeview = GTK_TREE_VIEW(userdata);
GtkTreeSelection *selection;
GtkTreeIter iter;
GtkTreeModel *model;
gchar *tree_path = NULL;
gchar *query = NULL;
sqlite3_stmt *stmt, *stmt2;
GList *filelist = NULL;
int count_new = 0;
int count_found = 0;
model = gtk_tree_view_get_model(treeview);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_get_selected(selection, &model, &iter);
gtk_tree_model_get(model, &iter, DT_LIB_COLLECT_COL_PATH, &tree_path, -1);
query = dt_util_dstrcat(query, "select id,folder from film_rolls where folder like '%s%%'", tree_path);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
g_free(query);
query = NULL;
while (sqlite3_step(stmt) == SQLITE_ROW)
{
int film_id;
gchar *path;
GDir *dir;
GError *error;
film_id = sqlite3_column_int(stmt, 0);
path = (gchar *) sqlite3_column_text(stmt, 1);
query = dt_util_dstrcat(query, "select filename,id from images where film_id=%d", film_id);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt2, NULL);
g_free(query);
while (sqlite3_step(stmt2) == SQLITE_ROW)
{
_image_t *img = malloc(sizeof(_image_t));
img->id = sqlite3_column_int(stmt, 1);
img->filmid = film_id;
img->path = path;
img->filename = g_strdup((gchar *)sqlite3_column_text(stmt2, 0));
img->exists = 0;
filelist = g_list_prepend (filelist, (gpointer *)img);
g_free(img->filename);
g_free(img);
}
dir = g_dir_open(path, 0, &error);
/* TODO: check here for error output */
gboolean found = 0;
/* TODO: what happens if there are new subdirs? */
const gchar *name = g_dir_read_name(dir);
while (name != NULL)
{
for (int i=0; i<g_list_length(filelist); i++)
{
_image_t *tmp;
tmp = g_list_nth_data(filelist, i);
if(!g_strcmp0(tmp->filename, name))
{
// Should we check the path as well ??
tmp->exists = 1;
found = 1;
count_found++;
break;
}
}
if (!found)
{
/* TODO: Check if file is supported.
* If it is JPEG check if we should import it */
_image_t *new = malloc(sizeof(_image_t));
new->id = -1;
new->path = g_strdup(path);
new->filename = g_strdup(name);
new->exists = 1;
filelist = g_list_append(filelist, (gpointer *)new);
count_new++;
}
name = g_dir_read_name(dir);
}
}
/* Call now the foreach function that gives the total data */
int count_missing = g_list_length(filelist) - count_new - count_found;
/* Produce the dialog */
GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(win),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"_(There are %d new images and %d deleted images. Do you want to sync this folder?)", count_new,
count_missing);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
{
/* TODO: Get dialog returned options so we can choose only adding or deleting*/
/* Proceed with sync */
for (int j=0; j < g_list_length(filelist); j++)
{
_image_t *img;
img = (_image_t *)g_list_nth_data(filelist, j);
if (img->id == -1)
{
/* This is a new image */
gchar *filename = NULL;
filename = dt_util_dstrcat(filename, "%s/%s", img->path, img->filename);
if(dt_image_import(img->filmid, filename, 0))
dt_control_queue_redraw_center(); //TODO: Set ignore JPEGs according to prefs.
}
else if (img->id != -1 && img->exists == 0)
{
dt_image_remove(img->id);
}
}
}
gtk_widget_destroy (dialog);
}
void view_popup_menu_onSearchFilmroll (GtkWidget *menuitem, gpointer userdata)
{
GtkTreeView *treeview = GTK_TREE_VIEW(userdata);
GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
GtkWidget *filechooser;
GtkTreeSelection *selection;
GtkTreeIter iter, child;
GtkTreeModel *model;
gchar *tree_path = NULL;
gchar *new_path = NULL;
filechooser = gtk_file_chooser_dialog_new (_("search filmroll"),
GTK_WINDOW (win),
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
(char *)NULL);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser), FALSE);
model = gtk_tree_view_get_model(treeview);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_get_selected(selection, &model, &iter);
child = iter;
gtk_tree_model_iter_parent(model, &iter, &child);
gtk_tree_model_get(model, &child, DT_LIB_COLLECT_COL_PATH, &tree_path, -1);
if(tree_path != NULL)
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER (filechooser), tree_path);
else
goto error;
// run the dialog
if (gtk_dialog_run (GTK_DIALOG (filechooser)) == GTK_RESPONSE_ACCEPT)
{
gint id = -1;
sqlite3_stmt *stmt;
gchar *query = NULL;
gchar *uri = NULL;
uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(filechooser));
new_path = g_filename_from_uri(uri, NULL, NULL);
g_free(uri);
if (new_path)
{
gchar *old = NULL;
query = dt_util_dstrcat(query, "select id,folder from film_rolls where folder like '%s%%'", tree_path);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
g_free(query);
while (sqlite3_step(stmt) == SQLITE_ROW)
{
id = sqlite3_column_int(stmt, 0);
old = (gchar *) sqlite3_column_text(stmt, 1);
query = NULL;
query = dt_util_dstrcat(query, "update film_rolls set folder=?1 where id=?2");
gchar trailing[1024];
gchar final[1024];
if (g_strcmp0(old, tree_path))
{
g_snprintf(trailing, 1024, "%s", old + strlen(tree_path)+1);
g_snprintf(final, 1024, "%s/%s", new_path, trailing);
}
else
{
g_snprintf(final, 1024, "%s", new_path);
}
sqlite3_stmt *stmt2;
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt2, NULL);
DT_DEBUG_SQLITE3_BIND_TEXT(stmt2, 1, final, strlen(final), SQLITE_STATIC);
DT_DEBUG_SQLITE3_BIND_INT(stmt2, 2, id);
sqlite3_step(stmt2);
sqlite3_finalize(stmt2);
}
g_free(query);
/* reset filter to display all images, otherwise view may remain empty */
dt_view_filter_reset_to_show_all(darktable.view_manager);
/* update collection to view missing filmroll */
_lib_folders_update_collection(new_path);
dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED);
}
else
goto error;
}
g_free(tree_path);
g_free(new_path);
gtk_widget_destroy (filechooser);
return;
error:
/* Something wrong happened */
gtk_widget_destroy (filechooser);
dt_control_log(_("Problem selecting new path for the filmroll in %s"), tree_path);
g_free(tree_path);
g_free(new_path);
}
void view_popup_menu_onRemove (GtkWidget *menuitem, gpointer userdata)
{
GtkTreeView *treeview = GTK_TREE_VIEW(userdata);
GtkTreeSelection *selection;
GtkTreeIter iter;
GtkTreeModel *model;
gchar *filmroll_path = NULL;
gchar *fullq = NULL;
/* Get info about the filmroll (or parent) selected */
model = gtk_tree_view_get_model(treeview);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_get_selected(selection, &model, &iter);
gtk_tree_model_get(model, &iter, DT_LIB_COLLECT_COL_PATH, &filmroll_path, -1);
/* Clean selected images, and add to the table those which are going to be deleted */
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "delete from selected_images", NULL, NULL, NULL);
fullq = dt_util_dstrcat(fullq, "insert into selected_images select id from images where film_id in (select id from film_rolls where folder like '%s%%')", filmroll_path);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), fullq, NULL, NULL, NULL);
dt_control_remove_images();
}
void
view_popup_menu (GtkWidget *treeview, GdkEventButton *event, gpointer userdata)
{
GtkWidget *menu, *menuitem;
menu = gtk_menu_new();
menuitem = gtk_menu_item_new_with_label(_("search filmroll..."));
g_signal_connect(menuitem, "activate",
(GCallback) view_popup_menu_onSearchFilmroll, treeview);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
#if 0
menuitem = gtk_menu_item_new_with_label(_("sync..."));
g_signal_connect(menuitem, "activate",
(GCallback) view_popup_menu_onSync, treeview);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
#endif
menuitem = gtk_menu_item_new_with_label(_("remove..."));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
g_signal_connect(menuitem, "activate",
(GCallback) view_popup_menu_onRemove, treeview);
gtk_widget_show_all(menu);
/* Note: event can be NULL here when called from view_onPopupMenu;
* gdk_event_get_time() accepts a NULL argument */
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
(event != NULL) ? event->button : 0,
gdk_event_get_time((GdkEvent*)event));
}
gboolean
view_onButtonPressed (GtkWidget *treeview, GdkEventButton *event, gpointer userdata)
{
/* single click with the right mouse button? */
if (event->type == GDK_BUTTON_PRESS && event->button == 3)
{
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
/* Note: gtk_tree_selection_count_selected_rows() does not
* exist in gtk+-2.0, only in gtk+ >= v2.2 ! */
if (gtk_tree_selection_count_selected_rows(selection) <= 1)
{
GtkTreePath *path;
/* Get tree path for row that was clicked */
if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
(gint) event->x,
(gint) event->y,
&path, NULL, NULL, NULL))
{
gtk_tree_selection_unselect_all(selection);
gtk_tree_selection_select_path(selection, path);
gtk_tree_path_free(path);
}
}
view_popup_menu(treeview, event, userdata);
return TRUE; /* we handled this */
}
return FALSE; /* we did not handle this */
}
gboolean
view_onPopupMenu (GtkWidget *treeview, gpointer userdata)
{
view_popup_menu(treeview, NULL, userdata);
return TRUE; /* we handled this */
}
static int
_count_images(const char *path)
{
// FIXME: this function is a major performance problem
// if many folders are counted. until it's cached somehow, it's switched off:
return 0;
#if 0
sqlite3_stmt *stmt = NULL;
gchar query[1024] = {0};
int count = 0;
gchar *escaped_text = NULL;
escaped_text = dt_util_str_replace(path, "'", "''");
snprintf (query, 1024, "select count(id) from images where film_id in (select id from film_rolls where folder like '%s%%')", escaped_text);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
if (sqlite3_step(stmt) == SQLITE_ROW)
count = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
g_free(escaped_text);
return count;
#endif
}
static gboolean
_filmroll_is_present(const gchar *path)
{
return g_file_test(path, G_FILE_TEST_IS_DIR);
}
static void
_show_filmroll_present(GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer user_data)
{
gchar *path, *pch;
gtk_tree_model_get(model, iter, DT_LIB_COLLECT_COL_PATH, &path, -1);
gtk_tree_model_get(model, iter, DT_LIB_COLLECT_COL_TEXT, &pch, -1);
g_object_set(renderer, "text", pch, NULL);
g_object_set(renderer, "strikethrough", TRUE, NULL);
if (!_filmroll_is_present(path))
g_object_set(renderer, "strikethrough-set", TRUE, NULL);
else
g_object_set(renderer, "strikethrough-set", FALSE, NULL);
}
static GtkTreeStore *
_folder_tree ()
{
/* intialize the tree store */
sqlite3_stmt *stmt;
// DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select folder,external_drive from film_rolls order by folder desc", -1, &stmt, NULL);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select folder from film_rolls order by folder desc", -1, &stmt, NULL);
GtkTreeStore *store = gtk_tree_store_new(DT_LIB_COLLECT_NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INT);
// initialize the model with the paths
while (sqlite3_step(stmt) == SQLITE_ROW)
{
int level = 0;
char *value;
GtkTreeIter current, iter;
GtkTreePath *root;
char **pch = g_strsplit((char *)sqlite3_column_text(stmt, 0), "/", -1);
#if 0
char *external = g_strdup((char *)sqlite3_column_text(stmt, 1));
if (external == NULL)
external = g_strdup("Local");
#endif
gboolean found=FALSE;
root = gtk_tree_path_new_first();
gtk_tree_model_get_iter (GTK_TREE_MODEL(store), &iter, root);
//current = iter; // This needs to be deleted if the following code is enabled
#if 0
int children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store),NULL);
for (int k=0;k<children;k++)
{
if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, k))
{
gtk_tree_model_get (GTK_TREE_MODEL(store), &iter, 0, &value, -1);
if (strcmp(value, external)==0)
{
found = TRUE;
current = iter;
break;
}
}
}
if (!found)
{
gtk_tree_store_insert(store, &iter, NULL, 0);
gtk_tree_store_set(store, &iter, 0, external, -1);
current = iter;
}
level=1;
#endif
// g_strsplit returns pch[0] always as an empty string ""
while (pch[level] != NULL)
{
found = FALSE;
int children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store),level>0?¤t:NULL);
/* find child with name, if not found create and continue */
for (int k=0;k<children;k++)
{
if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, level>0?¤t:NULL, k))
{
gtk_tree_model_get (GTK_TREE_MODEL(store), &iter, 0, &value, -1);
if (strcmp(value, pch[level])==0)
{
current = iter;
found = TRUE;
break;
}
}
}
/* lets add new path and assign current */
if (!found)
{
gchar *pth2 = NULL;
pth2 = dt_util_dstrcat(pth2, "/");
for (int i=0; i <= level; i++)
{
if (level > 0 && i != 0)
pth2 = dt_util_dstrcat(pth2, "%s/", pch[i]);
}
snprintf(pth2+strlen(pth2)-1, 1, "%s", "\0");
int count = _count_images(pth2);
gtk_tree_store_insert(store, &iter, level>0?¤t:NULL,0);
gtk_tree_store_set(store, &iter, DT_LIB_COLLECT_COL_TEXT, pch[level], DT_LIB_COLLECT_COL_PATH, pth2, DT_LIB_COLLECT_COL_COUNT, count, -1);
current = iter;
}
level++;
}
}
return store;
}
static gboolean
match_string (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
dt_lib_collect_rule_t *dr = (dt_lib_collect_rule_t *) data;
gchar *str;
const gchar *string;
gboolean visible = FALSE;
if (!dr->typing)
{
visible = TRUE;
return visible;
}
regex_t re;
gchar *pattern = NULL;
int status;
gtk_tree_model_get (model, iter, DT_LIB_COLLECT_COL_PATH, &str, -1);
string = gtk_entry_get_text(GTK_ENTRY(dr->text));
//pattern = dt_util_str_replace (string, "%", ".*");
pattern = dt_util_dstrcat (pattern, "%s%s%s", ".*", string, ".*");
regcomp(&re, pattern, REG_NOSUB);
status = regexec (&re, str, (size_t) 0, NULL, 0);
regfree (&re);
if (!status)
visible = TRUE;
g_free(str);
return visible;
}
static GtkTreeModel *
_create_filtered_model (GtkTreeModel *model, GtkTreeIter iter, dt_lib_collect_rule_t *dr)
{
GtkTreeModel *filter = NULL;
GtkTreePath *path;
GtkTreeIter child;
/* Filter level */
while (gtk_tree_model_iter_has_child(model, &iter))
{
if (gtk_tree_model_iter_n_children(model, &iter) == 1)
{
gtk_tree_model_iter_children(model, &child, &iter);
if (gtk_tree_model_iter_n_children(model, &child) != 0)
iter = child;
else
break;
}
else
break;
}
path = gtk_tree_model_get_path (model, &iter);
/* Create filter and set virtual root */
filter = gtk_tree_model_filter_new (model, path);
gtk_tree_path_free (path);
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER(filter), match_string, dr, NULL);
return filter;
}
static GtkTreeView *
_create_treeview_display (GtkTreeModel *model)
{
GtkTreeView *tree;
GtkCellRenderer *renderer;
GtkTreeViewColumn *col1;
tree = GTK_TREE_VIEW(gtk_tree_view_new ());
/* Create columns */
col1 = gtk_tree_view_column_new();
gtk_tree_view_append_column(tree, col1);
gtk_tree_view_column_set_sizing(col1, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width (col1, 230);
gtk_tree_view_column_set_max_width (col1, 230);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col1, renderer, TRUE);
gtk_tree_view_column_add_attribute(col1, renderer, "text", DT_LIB_COLLECT_COL_TEXT);
gtk_tree_view_column_set_cell_data_func(col1, renderer, _show_filmroll_present, NULL, NULL);
#if 0 // FIXME: count switched off for now, as it is a performance regression (see #8981).
GtkTreeViewColumn *col2 = gtk_tree_view_column_new();
gtk_tree_view_append_column(tree,col2);
GtkCellRenderer *renderer2 = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col2, renderer2, TRUE);
gtk_tree_view_column_add_attribute(col2, renderer2, "text", DT_LIB_COLLECT_COL_COUNT);
#endif
gtk_tree_view_set_model(tree, GTK_TREE_MODEL(model));
gtk_tree_view_set_headers_visible(tree, FALSE);
/* free store, treeview has its own storage now */
g_object_unref(model);
return tree;
}
static void _lib_folders_update_collection(const gchar *filmroll)
{
gchar *complete_query = NULL;
// remove from selected images where not in this query.
sqlite3_stmt *stmt = NULL;
const gchar *cquery = dt_collection_get_query(darktable.collection);
//complete_query = NULL;
if(cquery && cquery[0] != '\0')
{
complete_query = dt_util_dstrcat(complete_query, "delete from selected_images where imgid not in (%s)", cquery);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), complete_query, -1, &stmt, NULL);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, 0);
DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, -1);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
/* free allocated strings */
g_free(complete_query);
}
/* raise signal of collection change, only if this is an orginal */
if (!darktable.collection->clone)
dt_control_signal_raise(darktable.signals, DT_SIGNAL_COLLECTION_CHANGED);
}
#if 0
static void mount_changed (GVolumeMonitor *volume_monitor, GMount *mount, gpointer user_data)
{
dt_lib_module_t *self = (dt_lib_module_t *)user_data;
dt_lib_folders_t *d = (dt_lib_folders_t *)self->data;
d->mounts = g_volume_monitor_get_mounts(d->gv_monitor);
_draw_tree_gui(self);
}
#endif
void destroy_widget (gpointer data)
{
GtkWidget *widget = (GtkWidget *)data;
gtk_widget_destroy(widget);
}
static dt_lib_collect_t*
get_collect (dt_lib_collect_rule_t *r)
{
dt_lib_collect_t *d = (dt_lib_collect_t *)(((char *)r) - r->num*sizeof(dt_lib_collect_rule_t));
return d;
}
static gboolean
changed_callback (GtkEntry *entry, dt_lib_collect_rule_t *dr)
{
// update related list
dt_lib_collect_t *d = get_collect(dr);
sqlite3_stmt *stmt;
GtkTreeIter iter;
//GtkWidget *label;
GtkTreeView *tree;
GtkTreeView *view;
GtkTreeModel *listmodel;
GtkTreeModel *treemodel;
gtk_widget_hide(GTK_WIDGET(d->sw2));
view = d->view;
listmodel = d->listmodel;
g_object_ref(listmodel);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), NULL);
gtk_list_store_clear(GTK_LIST_STORE(listmodel));
gtk_widget_hide(GTK_WIDGET(d->scrolledwindow));
char query[1024];
int property = gtk_combo_box_get_active(dr->combo);
const gchar *text = NULL;
text = gtk_entry_get_text(GTK_ENTRY(dr->text));
gchar *escaped_text = NULL;
if (!dr->typing)
escaped_text = g_strdup("");
else
escaped_text = dt_util_str_replace(text, "'", "''");
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/string%1ld", dr->num);
dt_conf_set_string (confname, text);
snprintf(confname, 200, "plugins/lighttable/collect/item%1ld", dr->num);
dt_conf_set_int (confname, property);
switch(property)
{
case DT_COLLECTION_PROP_FILMROLL: // film roll
snprintf(query, 1024, "select distinct folder, id from film_rolls where folder like '%%%s%%' order by folder desc", escaped_text);
break;
case DT_COLLECTION_PROP_CAMERA: // camera
snprintf(query, 1024, "select distinct maker || ' ' || model as model, 1 from images where maker || ' ' || model like '%%%s%%' order by model", escaped_text);
break;
case DT_COLLECTION_PROP_TAG: // tag
snprintf(query, 1024, "SELECT distinct name, id FROM tags WHERE name LIKE '%%%s%%' ORDER BY UPPER(name)", escaped_text);
break;
case DT_COLLECTION_PROP_HISTORY: // History, 2 hardcoded alternatives
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT,_("altered"),
DT_LIB_COLLECT_COL_ID, 0,
DT_LIB_COLLECT_COL_TOOLTIP,_("altered"),
-1);
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT,_("not altered"),
DT_LIB_COLLECT_COL_ID, 1,
DT_LIB_COLLECT_COL_TOOLTIP,_("not altered"),
-1);
goto entry_key_press_exit;
break;
case DT_COLLECTION_PROP_COLORLABEL: // colorlabels
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT,_("red"),
DT_LIB_COLLECT_COL_ID, 0,
DT_LIB_COLLECT_COL_TOOLTIP, _("red"),
-1);
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT,_("yellow"),
DT_LIB_COLLECT_COL_ID, 1,
DT_LIB_COLLECT_COL_TOOLTIP, _("yellow"),
-1);
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT,_("green"),
DT_LIB_COLLECT_COL_ID, 2,
DT_LIB_COLLECT_COL_TOOLTIP, _("green"),
-1);
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT,_("blue"),
DT_LIB_COLLECT_COL_ID, 3,
DT_LIB_COLLECT_COL_TOOLTIP, _("blue"),
-1);
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT,_("purple"),
DT_LIB_COLLECT_COL_ID, 4,
DT_LIB_COLLECT_COL_TOOLTIP, _("purple"),
-1);
goto entry_key_press_exit;
break;
// TODO: Add empty string for metadata?
// TODO: Autogenerate this code?
case DT_COLLECTION_PROP_TITLE: // title
snprintf(query, 1024, "select distinct value, 1 from meta_data where key = %d and value like '%%%s%%' order by value",
DT_METADATA_XMP_DC_TITLE, escaped_text);
break;
case DT_COLLECTION_PROP_DESCRIPTION: // description
snprintf(query, 1024, "select distinct value, 1 from meta_data where key = %d and value like '%%%s%%' order by value",
DT_METADATA_XMP_DC_DESCRIPTION, escaped_text);
break;
case DT_COLLECTION_PROP_CREATOR: // creator
snprintf(query, 1024, "select distinct value, 1 from meta_data where key = %d and value like '%%%s%%' order by value",
DT_METADATA_XMP_DC_CREATOR, escaped_text);
break;
case DT_COLLECTION_PROP_PUBLISHER: // publisher
snprintf(query, 1024, "select distinct value, 1 from meta_data where key = %d and value like '%%%s%%' order by value",
DT_METADATA_XMP_DC_PUBLISHER, escaped_text);
break;
case DT_COLLECTION_PROP_RIGHTS: // rights
snprintf(query, 1024, "select distinct value, 1 from meta_data where key = %d and value like '%%%s%%'order by value ",
DT_METADATA_XMP_DC_RIGHTS, escaped_text);
break;
case DT_COLLECTION_PROP_LENS: // lens
snprintf(query, 1024, "select distinct lens, 1 from images where lens like '%%%s%%' order by lens", escaped_text);
break;
case DT_COLLECTION_PROP_ISO: // iso
snprintf(query, 1024, "select distinct cast(iso as integer) as iso, 1 from images where iso like '%%%s%%' order by iso", escaped_text);
break;
case DT_COLLECTION_PROP_APERTURE: // aperture
snprintf(query, 1024, "select distinct round(aperture,1) as aperture, 1 from images where aperture like '%%%s%%' order by aperture", escaped_text);
break;
case DT_COLLECTION_PROP_FILENAME: // filename
snprintf(query, 1024, "select distinct filename, 1 from images where filename like '%%%s%%' order by filename", escaped_text);
break;
case DT_COLLECTION_PROP_FOLDERS: // folders
if (!dr->typing || !strlen(escaped_text))
goto folders;
else
{
if (d->trees != NULL)
{
for (int i=0; i<d->trees->len; i++)
{
tree = GTK_TREE_VIEW(g_ptr_array_index (d->trees, i));
GtkTreeModelFilter *modelfilter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model (tree));
gtk_tree_model_filter_refilter (modelfilter);
}
gtk_widget_show(GTK_WIDGET(d->sw2));
}
return FALSE;
}
break;
default: // case 3: // day
snprintf(query, 1024, "SELECT DISTINCT datetime_taken, 1 FROM images WHERE datetime_taken LIKE '%%%s%%' ORDER BY datetime_taken DESC", escaped_text);
break;
}
g_free(escaped_text);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
while(sqlite3_step(stmt) == SQLITE_ROW)
{
gtk_list_store_append(GTK_LIST_STORE(listmodel), &iter);
const char *folder = (const char*)sqlite3_column_text(stmt, 0);
if(property == 0) // film roll
{
folder = dt_image_film_roll_name(folder);
}
gchar *value = (gchar *)sqlite3_column_text(stmt, 0);
gchar *escaped_text = g_markup_escape_text(value, strlen(value));
gtk_list_store_set (GTK_LIST_STORE(listmodel), &iter,
DT_LIB_COLLECT_COL_TEXT, folder,
DT_LIB_COLLECT_COL_ID, sqlite3_column_int(stmt, 1),
DT_LIB_COLLECT_COL_TOOLTIP, escaped_text,
DT_LIB_COLLECT_COL_PATH, value,
-1);
}
sqlite3_finalize(stmt);
goto entry_key_press_exit;
folders:
/* TODO: Only create a new tree if something has changed
* This will allow to cache the node, and not collapse the tree */
treemodel = d->treemodel;
if (d->tree_new)
{
/* We have already inited the GUI once, clean around */
#if 0
if (d->labels != NULL)
{
for (int i=0; i<d->labels->len; i++)
{
label = GTK_WIDGET(g_ptr_array_index (d->labels, i));
g_ptr_array_free(d->labels, TRUE);
}
d->labels = NULL;
}
#endif
if (d->trees != NULL)
{
for (int i=0; i<d->trees->len; i++)
{
tree = GTK_TREE_VIEW(g_ptr_array_index (d->trees, i));
g_ptr_array_free(d->trees, TRUE);
}
d->trees = NULL;
}
/* set the UI */
GtkTreeModel *model2;
GtkTreePath *root = gtk_tree_path_new_first();
gtk_tree_model_get_iter (GTK_TREE_MODEL(treemodel), &iter, root);
int children = 1; // To be deleted if the following code in enabled
#if 0
int children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(treemodel), NULL);
d->labels = g_ptr_array_sized_new(children);
g_ptr_array_set_free_func (d->labels, destroy_widget);
#endif
d->trees = g_ptr_array_sized_new(children);
g_ptr_array_set_free_func (d->trees, destroy_widget);
for (int i=0; i<children; i++)
{
#if 0
GValue value;
memset(&value,0,sizeof(GValue));
gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(treemodel), &iter, NULL, i);
gtk_tree_model_get_value (GTK_TREE_MODEL(treemodel), &iter, 0, &value);
gchar *mount_name = g_value_dup_string(&value);
if (g_strcmp0(mount_name, "Local")==0)
{
label = gtk_label_new (_("local hdd"));
}
else
{
label = gtk_label_new (g_ascii_strdown(mount_name, strlen(mount_name)));
}
g_ptr_array_add(d->labels, (gpointer) label);
gtk_box_pack_start(d->box, GTK_WIDGET(label), FALSE, FALSE, 0);
gtk_widget_show (label);
#endif
model2 = _create_filtered_model(GTK_TREE_MODEL(treemodel), iter, dr);
tree = _create_treeview_display(GTK_TREE_MODEL(model2));
g_ptr_array_add(d->trees, (gpointer) tree);
gtk_box_pack_start(d->box, GTK_WIDGET(tree), FALSE, FALSE, 0);
gtk_widget_show (GTK_WIDGET(tree));
gtk_tree_view_set_headers_visible(tree, FALSE);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_SINGLE);
gtk_tree_view_set_enable_search(tree, TRUE);
gtk_tree_view_set_search_column (tree, DT_LIB_COLLECT_COL_PATH);
g_signal_connect(G_OBJECT (tree), "row-activated", G_CALLBACK (row_activated), d);
g_signal_connect(G_OBJECT (tree), "button-press-event", G_CALLBACK (view_onButtonPressed), NULL);
g_signal_connect(G_OBJECT (tree), "popup-menu", G_CALLBACK (view_onPopupMenu), NULL);
#if 0
g_value_unset(&value);
g_free(mount_name);
#endif
d->tree_new = FALSE;
}
}
else
{
if (d->trees != NULL)
{
for (int i=0; i<d->trees->len; i++)
{
tree = GTK_TREE_VIEW(g_ptr_array_index (d->trees, i));
GtkTreeModelFilter *modelfilter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model (tree));
gtk_tree_model_filter_refilter (modelfilter);
}
}
}
dr->typing = FALSE;
gtk_widget_show(GTK_WIDGET(d->box));
gtk_widget_show(GTK_WIDGET(d->sw2));
g_object_unref(listmodel);
return FALSE;
entry_key_press_exit:
gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(view), DT_LIB_COLLECT_COL_TOOLTIP);
gtk_tree_view_set_model(GTK_TREE_VIEW(view), listmodel);
g_signal_connect(G_OBJECT (view), "row-activated", G_CALLBACK (row_activated), d);
gtk_widget_set_no_show_all(GTK_WIDGET(d->scrolledwindow), FALSE);
gtk_widget_show_all(GTK_WIDGET(d->scrolledwindow));
g_object_unref(listmodel);
return FALSE;
}
static void
_lib_collect_gui_update (dt_lib_module_t *self)
{
dt_lib_collect_t *d = (dt_lib_collect_t *)self->data;
const int old = darktable.gui->reset;
darktable.gui->reset = 1;
const int active = CLAMP(dt_conf_get_int("plugins/lighttable/collect/num_rules") - 1, 0, (MAX_RULES-1));
char confname[200];
gtk_widget_set_no_show_all(GTK_WIDGET(d->scrolledwindow), TRUE);
gtk_widget_set_no_show_all(GTK_WIDGET(d->sw2), TRUE);
for(int i=0; i<MAX_RULES; i++)
{
gtk_widget_set_no_show_all(d->rule[i].hbox, TRUE);
gtk_widget_set_visible(d->rule[i].hbox, FALSE);
}
for(int i=0; i<=active; i++)
{
gtk_widget_set_no_show_all(d->rule[i].hbox, FALSE);
gtk_widget_set_visible(d->rule[i].hbox, TRUE);
gtk_widget_show_all(d->rule[i].hbox);
snprintf(confname, 200, "plugins/lighttable/collect/item%1d", i);
gtk_combo_box_set_active(GTK_COMBO_BOX(d->rule[i].combo), dt_conf_get_int(confname));
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", i);
gchar *text = dt_conf_get_string(confname);
if(text)
{
// TODO: We should stop emission of the signal here
gtk_entry_set_text(GTK_ENTRY(d->rule[i].text), text);
g_free(text);
d->rule[i].typing = FALSE;
}
GtkDarktableButton *button = DTGTK_BUTTON(d->rule[i].button);
if(i == MAX_RULES - 1)
{
// only clear
button->icon = dtgtk_cairo_paint_cancel;
g_object_set(G_OBJECT(button), "tooltip-text", _("clear this rule"), (char *)NULL);
}
else if(i == active)
{
button->icon = dtgtk_cairo_paint_dropdown;
g_object_set(G_OBJECT(button), "tooltip-text", _("clear this rule or add new rules"), (char *)NULL);
}
else
{
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", i+1);
const int mode = dt_conf_get_int(confname);
if(mode == DT_LIB_COLLECT_MODE_AND) button->icon = dtgtk_cairo_paint_and;
if(mode == DT_LIB_COLLECT_MODE_OR) button->icon = dtgtk_cairo_paint_or;
if(mode == DT_LIB_COLLECT_MODE_AND_NOT) button->icon = dtgtk_cairo_paint_andnot;
g_object_set(G_OBJECT(button), "tooltip-text", _("clear this rule"), (char *)NULL);
}
}
// update list of proposals
changed_callback(NULL, d->rule + d->active_rule);
darktable.gui->reset = old;
}
void
gui_reset (dt_lib_module_t *self)
{
dt_conf_set_int("plugins/lighttable/collect/num_rules", 1);
dt_conf_set_int("plugins/lighttable/collect/item0", 0);
dt_conf_set_string("plugins/lighttable/collect/string0", "%");
dt_collection_set_query_flags(darktable.collection,COLLECTION_QUERY_FULL);
dt_collection_update_query(darktable.collection);
}
static void
combo_changed (GtkComboBox *combo, dt_lib_collect_rule_t *d)
{
if(darktable.gui->reset) return;
gtk_entry_set_text(GTK_ENTRY(d->text), "");
dt_lib_collect_t *c = get_collect(d);
c->active_rule = d->num;
changed_callback(NULL, d);
dt_collection_update_query(darktable.collection);
}
static void
row_activated (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, dt_lib_collect_t *d)
{
GtkTreeIter iter;
GtkTreeModel *model = NULL;
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
if(!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
gchar *text;
const int active = d->active_rule;
const int item = gtk_combo_box_get_active(GTK_COMBO_BOX(d->rule[active].combo));
if(item == DT_COLLECTION_PROP_FILMROLL || // get full path for film rolls
item == DT_COLLECTION_PROP_FOLDERS) // or folders
gtk_tree_model_get (model, &iter, DT_LIB_COLLECT_COL_PATH, &text, -1);
else
gtk_tree_model_get (model, &iter, DT_LIB_COLLECT_COL_TEXT, &text, -1);
gtk_entry_set_text(GTK_ENTRY(d->rule[active].text), text);
g_free(text);
dt_lib_collect_rule_t *dr = &(d->rule[active]);
dr->typing = FALSE;
changed_callback(NULL, d->rule + active);
dt_collection_update_query(darktable.collection);
dt_control_queue_redraw_center();
}
static void
entry_activated (GtkWidget *entry, dt_lib_collect_rule_t *d)
{
GtkTreeView *view;
GtkTreeModel *model;
int property, rows;
changed_callback(NULL, d);
dt_lib_collect_t *c = get_collect(d);
property = gtk_combo_box_get_active(d->combo);
if (property != DT_COLLECTION_PROP_FOLDERS)
{
view = c->view;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
}
else
{
model = c->treemodel;
}
rows = gtk_tree_model_iter_n_children(model, NULL);
if(rows == 1)
{
GtkTreeIter iter;
if(gtk_tree_model_get_iter_first(model, &iter))
{
gchar *text;
const int item = gtk_combo_box_get_active(GTK_COMBO_BOX(d->combo));
if(item == DT_COLLECTION_PROP_FILMROLL || // get full path for film rolls
item == DT_COLLECTION_PROP_FOLDERS) // or folders
gtk_tree_model_get (model, &iter, DT_LIB_COLLECT_COL_PATH, &text, -1);
else
gtk_tree_model_get (model, &iter, DT_LIB_COLLECT_COL_TEXT, &text, -1);
gtk_entry_set_text(GTK_ENTRY(d->text), text);
g_free(text);
d->typing = FALSE;
changed_callback(NULL, d);
}
}
dt_collection_update_query(darktable.collection);
}
static void
entry_changed (GtkWidget *entry, gchar *new_text, gint new_length, gpointer *position, dt_lib_collect_rule_t *d)
{
d->typing = TRUE;
}
int
position ()
{
return 400;
}
static void
entry_focus_in_callback (GtkWidget *w, GdkEventFocus *event, dt_lib_collect_rule_t *d)
{
dt_lib_collect_t *c = get_collect(d);
c->active_rule = d->num;
changed_callback(NULL, c->rule + c->active_rule);
}
#if 0
static void
focus_in_callback (GtkWidget *w, GdkEventFocus *event, dt_lib_module_t *self)
{
GtkWidget *win = darktable.gui->widgets.main_window;
GtkEntry *entry = GTK_ENTRY(self->text);
GtkTreeView *view;
int count = 1 + count_film_rolls(gtk_entry_get_text(entry));
int ht = get_font_height(view, "Dreggn");
const int size = MAX(2*ht, MIN(win->allocation.height/2, count*ht));
gtk_widget_set_size_request(view, -1, size);
}
static void
hide_callback (GObject *object,
GParamSpec *param_spec,
GtkWidget *view)
{
GtkExpander *expander;
expander = GTK_EXPANDER (object);
if (!gtk_expander_get_expanded (expander))
gtk_widget_set_size_request(view, -1, -1);
}
#endif
static void
menuitem_and (GtkMenuItem *menuitem, dt_lib_collect_rule_t *d)
{
// add next row with and operator
const int active = CLAMP(dt_conf_get_int("plugins/lighttable/collect/num_rules"), 1, MAX_RULES);
if(active < 10)
{
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", active);
dt_conf_set_int(confname, DT_LIB_COLLECT_MODE_AND);
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", active);
dt_conf_set_string(confname, "");
dt_conf_set_int("plugins/lighttable/collect/num_rules", active+1);
dt_lib_collect_t *c = get_collect(d);
c->active_rule = active;
}
dt_collection_update_query(darktable.collection);
}
static void
menuitem_or (GtkMenuItem *menuitem, dt_lib_collect_rule_t *d)
{
// add next row with or operator
const int active = CLAMP(dt_conf_get_int("plugins/lighttable/collect/num_rules"), 1, MAX_RULES);
if(active < 10)
{
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", active);
dt_conf_set_int(confname, DT_LIB_COLLECT_MODE_OR);
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", active);
dt_conf_set_string(confname, "");
dt_conf_set_int("plugins/lighttable/collect/num_rules", active+1);
dt_lib_collect_t *c = get_collect(d);
c->active_rule = active;
}
dt_collection_update_query(darktable.collection);
}
static void
menuitem_and_not (GtkMenuItem *menuitem, dt_lib_collect_rule_t *d)
{
// add next row with and not operator
const int active = CLAMP(dt_conf_get_int("plugins/lighttable/collect/num_rules"), 1, MAX_RULES);
if(active < 10)
{
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", active);
dt_conf_set_int(confname, DT_LIB_COLLECT_MODE_AND_NOT);
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", active);
dt_conf_set_string(confname, "");
dt_conf_set_int("plugins/lighttable/collect/num_rules", active+1);
dt_lib_collect_t *c = get_collect(d);
c->active_rule = active;
}
dt_collection_update_query(darktable.collection);
}
static void
menuitem_change_and (GtkMenuItem *menuitem, dt_lib_collect_rule_t *d)
{
// add next row with and operator
const int num = d->num + 1;
if(num < 10 && num > 0)
{
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", num);
dt_conf_set_int(confname, DT_LIB_COLLECT_MODE_AND);
}
dt_collection_update_query(darktable.collection);
}
static void
menuitem_change_or (GtkMenuItem *menuitem, dt_lib_collect_rule_t *d)
{
// add next row with or operator
const int num = d->num + 1;
if(num < 10 && num > 0)
{
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", num);
dt_conf_set_int(confname, DT_LIB_COLLECT_MODE_OR);
}
dt_collection_update_query(darktable.collection);
}
static void
menuitem_change_and_not (GtkMenuItem *menuitem, dt_lib_collect_rule_t *d)
{
// add next row with and not operator
const int num = d->num + 1;
if(num < 10 && num > 0)
{
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", num);
dt_conf_set_int(confname, DT_LIB_COLLECT_MODE_AND_NOT);
}
dt_collection_update_query(darktable.collection);
}
static void
collection_updated(gpointer instance,gpointer self)
{
_lib_collect_gui_update((dt_lib_module_t *)self);
}
static void
filmrolls_updated(gpointer instance, gpointer self)
{
dt_lib_module_t *dm = (dt_lib_module_t *)self;
dt_lib_collect_t *d = (dt_lib_collect_t *)dm->data;
// update tree
d->treemodel = GTK_TREE_MODEL(_folder_tree());
d->tree_new = TRUE;
_lib_collect_gui_update((dt_lib_module_t *)self);
}
static void
menuitem_clear (GtkMenuItem *menuitem, dt_lib_collect_rule_t *d)
{
// remove this row, or if 1st, clear text entry box
const int active = CLAMP(dt_conf_get_int("plugins/lighttable/collect/num_rules"), 1, MAX_RULES);
dt_lib_collect_t *c = get_collect(d);
if(active > 1)
{
dt_conf_set_int("plugins/lighttable/collect/num_rules", active-1);
if(c->active_rule >= active-1) c->active_rule = active - 2;
}
else
{
dt_conf_set_int("plugins/lighttable/collect/mode0", DT_LIB_COLLECT_MODE_AND);
dt_conf_set_int("plugins/lighttable/collect/item0", 0);
dt_conf_set_string("plugins/lighttable/collect/string0", "");
}
// move up all still active rules by one.
for(int i=d->num; i<MAX_RULES-1; i++)
{
char confname[200];
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", i+1);
const int mode = dt_conf_get_int(confname);
snprintf(confname, 200, "plugins/lighttable/collect/item%1d", i+1);
const int item = dt_conf_get_int(confname);
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", i+1);
gchar *string = dt_conf_get_string(confname);
if(string)
{
snprintf(confname, 200, "plugins/lighttable/collect/mode%1d", i);
dt_conf_set_int(confname, mode);
snprintf(confname, 200, "plugins/lighttable/collect/item%1d", i);
dt_conf_set_int(confname, item);
snprintf(confname, 200, "plugins/lighttable/collect/string%1d", i);
dt_conf_set_string(confname, string);
g_free(string);
}
}
dt_collection_update_query(darktable.collection);
}
static gboolean
popup_button_callback(GtkWidget *widget, GdkEventButton *event, dt_lib_collect_rule_t *d)
{
if(event->button != 1) return FALSE;
GtkWidget *menu = gtk_menu_new();
GtkWidget *mi;
const int active = CLAMP(dt_conf_get_int("plugins/lighttable/collect/num_rules"), 1, MAX_RULES);
mi = gtk_menu_item_new_with_label(_("clear this rule"));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_clear), d);
if(d->num == active - 1)
{
mi = gtk_menu_item_new_with_label(_("narrow down search"));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_and), d);
mi = gtk_menu_item_new_with_label(_("add more images"));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_or), d);
mi = gtk_menu_item_new_with_label(_("exclude images"));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_and_not), d);
}
else if(d->num < active - 1)
{
mi = gtk_menu_item_new_with_label(_("change to: and"));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_change_and), d);
mi = gtk_menu_item_new_with_label(_("change to: or"));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_change_or), d);
mi = gtk_menu_item_new_with_label(_("change to: except"));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(menuitem_change_and_not), d);
}
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
gtk_widget_show_all(menu);
return TRUE;
}
void
gui_init (dt_lib_module_t *self)
{
dt_lib_collect_t *d = (dt_lib_collect_t *)malloc(sizeof(dt_lib_collect_t));
memset(d, 0, sizeof(dt_lib_collect_t));
self->data = (void *)d;
self->widget = gtk_vbox_new(FALSE, 5);
gtk_widget_set_size_request(self->widget, 100, -1);
d->active_rule = 0;
d->params = (dt_lib_collect_params_t*)malloc(sizeof(dt_lib_collect_params_t));
GtkBox *box;
GtkWidget *w;
for(int i=0; i<MAX_RULES; i++)
{
d->rule[i].num = i;
box = GTK_BOX(gtk_hbox_new(FALSE, 5));
d->rule[i].hbox = GTK_WIDGET(box);
gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(box), TRUE, TRUE, 0);
w = gtk_combo_box_new_text();
d->rule[i].combo = GTK_COMBO_BOX(w);
for(int k=0; k<dt_lib_collect_string_cnt; k++)
gtk_combo_box_append_text(GTK_COMBO_BOX(w), _(dt_lib_collect_string[k]));
g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(combo_changed), d->rule + i);
gtk_box_pack_start(box, w, FALSE, FALSE, 0);
w = gtk_entry_new();
dt_gui_key_accel_block_on_focus(w);
d->rule[i].text = w;
gtk_widget_add_events(w, GDK_FOCUS_CHANGE_MASK);
g_signal_connect(G_OBJECT(w), "focus-in-event", G_CALLBACK(entry_focus_in_callback), d->rule + i);
/* xgettext:no-c-format */
g_object_set(G_OBJECT(w), "tooltip-text", _("type your query, use `%' as wildcard"), (char *)NULL);
gtk_widget_add_events(w, GDK_KEY_PRESS_MASK);
g_signal_connect(G_OBJECT(w), "insert-text", G_CALLBACK(entry_changed), d->rule + i);
g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(changed_callback), d->rule + i);
g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(entry_activated), d->rule + i);
gtk_box_pack_start(box, w, TRUE, TRUE, 0);
w = dtgtk_button_new(dtgtk_cairo_paint_presets, CPF_STYLE_FLAT|CPF_DO_NOT_USE_BORDER);
d->rule[i].button = w;
gtk_widget_set_events(w, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(w), "button-press-event", G_CALLBACK(popup_button_callback), d->rule + i);
gtk_box_pack_start(box, w, FALSE, FALSE, 0);
gtk_widget_set_size_request(w, 13, 13);
}
GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
d->scrolledwindow = GTK_SCROLLED_WINDOW(sw);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
GtkTreeView *view = GTK_TREE_VIEW(gtk_tree_view_new());
d->view = view;
gtk_tree_view_set_headers_visible(view, FALSE);
gtk_widget_set_size_request(GTK_WIDGET(view), -1, 300);
gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(view));
GtkTreeViewColumn *col = gtk_tree_view_column_new();
gtk_tree_view_append_column(view, col);
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer, "text", DT_LIB_COLLECT_COL_TEXT);
GtkTreeModel *listmodel = GTK_TREE_MODEL(gtk_list_store_new(DT_LIB_COLLECT_NUM_COLS, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT));
d->listmodel = listmodel;
gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(sw), TRUE, TRUE, 0);
GtkWidget *vbox = gtk_vbox_new(FALSE, 5);
d->box = GTK_BOX(vbox);
GtkWidget *sw2 = gtk_scrolled_window_new(NULL, NULL);
d->sw2 = GTK_SCROLLED_WINDOW (sw2);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw2), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw2), GTK_WIDGET(d->box));
gtk_widget_set_size_request(GTK_WIDGET(sw2), -1, 300);
gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(sw2), TRUE, TRUE, 0);
d->labels = NULL;
d->trees = NULL;
/* setup proxy */
darktable.view_manager->proxy.module_collect.module = self;
darktable.view_manager->proxy.module_collect.update = _lib_collect_gui_update;
/* set the monitor */
/* TODO: probably we should be using the same for the import code */
// d->gv_monitor = g_volume_monitor_get ();
// g_signal_connect(G_OBJECT(d->gv_monitor), "mount-added", G_CALLBACK(mount_changed), self);
// g_signal_connect(G_OBJECT(d->gv_monitor), "mount-removed", G_CALLBACK(mount_changed), self);
// g_signal_connect(G_OBJECT(d->gv_monitor), "mount-changed", G_CALLBACK(mount_changed), self);
// TODO: This should be done in a more generic place, not gui_init
d->treemodel = GTK_TREE_MODEL(_folder_tree());
d->tree_new = TRUE;
_lib_collect_gui_update(self);
dt_control_signal_connect(darktable.signals,
DT_SIGNAL_COLLECTION_CHANGED,
G_CALLBACK(collection_updated),
self);
dt_control_signal_connect(darktable.signals,
DT_SIGNAL_FILMROLLS_CHANGED,
G_CALLBACK(filmrolls_updated),
self);
}
void
gui_cleanup (dt_lib_module_t *self)
{
dt_lib_collect_t *d = (dt_lib_collect_t *)self->data;
dt_control_signal_disconnect(darktable.signals, G_CALLBACK(collection_updated), self);
dt_control_signal_disconnect(darktable.signals, G_CALLBACK(filmrolls_updated), self);
darktable.view_manager->proxy.module_collect.module = NULL;
g_free(((dt_lib_collect_t*)self->data)->params);
/* cleanup mem */
//g_ptr_array_free(d->labels, TRUE);
if (d->trees != NULL)
g_ptr_array_free(d->trees, TRUE);
/* TODO: Make sure we are cleaning up all allocations */
g_free(self->data);
self->data = NULL;
}
#undef MAX_RULES
// 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-space on;
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...