1 /*
2 * * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>,
3 * * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 /* Documentation:
21 * http://www.sqlite.org/capi3ref.html
22 */
23
24 /* Database layout:
25 *
26 * library
27 * id
28 * filename
29 * identifier
30 *
31 * tags
32 * id
33 * tagname
34 *
35 * phototags
36 * photo
37 * tag
38 * autotag
39 *
40 * version
41 * version
42 */
43 /*
44 #include <glib.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <sqlite3.h>
48 #include <string.h>
49 #include "rawstudio.h"
50 #include "rs-metadata.h"
51 #include "rs-library.h"
52 #include "application.h"
53 */
54
55 #include "rs-library.h"
56 #include "conf_interface.h"
57 #include "config.h"
58 #include "gettext.h"
59 #include <libxml/encoding.h>
60 #include <libxml/xmlwriter.h>
61 #include <sqlite3.h>
62
63 #define LIBRARY_VERSION 2
64 #define TAGS_XML_FILE "tags.xml"
65 #define MAX_SEARCH_RESULTS 1000
66 #include "rs-types.h"
67
68 struct _RSLibrary {
69 GObject parent;
70 gboolean dispose_has_run;
71
72 sqlite3 *db;
73 gchar *error_init;
74
75 /* This mutex must be used when inserting data in a table with an
76 autocrementing column - which is ALWAYS for sqlite */
77 GMutex *id_lock;
78 };
79
80 G_DEFINE_TYPE(RSLibrary, rs_library, G_TYPE_OBJECT)
81
82 static gint library_execute_sql(sqlite3 *db, const gchar *sql);
83 static void library_sqlite_error(sqlite3 *db, const gint result);
84 static gint library_create_tables(sqlite3 *db);
85 static gint library_find_tag_id(RSLibrary *library, const gchar *tagname);
86 static gint library_find_photo_id(RSLibrary *library, const gchar *photo);
87 static void library_photo_add_tag(RSLibrary *library, const gint photo_id, const gint tag_id, const gboolean autotag);
88 static gboolean library_is_photo_tagged(RSLibrary *library, const gint photo_id, const gint tag_id);
89 static gint library_add_photo(RSLibrary *library, const gchar *filename);
90 static gint library_add_tag(RSLibrary *library, const gchar *tagname);
91 static void library_delete_photo(RSLibrary *library, const gint photo_id);
92 static void library_delete_tag(RSLibrary *library, const gint tag_id);
93 static void library_photo_delete_tags(RSLibrary *library, const gint photo_id);
94 static void library_tag_delete_photos(RSLibrary *library, const gint tag_id);
95 static gboolean library_tag_is_used(RSLibrary *library, const gint tag_id);
96 static void library_photo_default_tags(RSLibrary *library, const gint photo_id, RSMetadata *metadata);
97
98 static GtkWidget *tag_search_entry = NULL;
99
100 static void
rs_library_dispose(GObject * object)101 rs_library_dispose(GObject *object)
102 {
103 RSLibrary *library = RS_LIBRARY(object);
104
105 if (!library->dispose_has_run)
106 {
107 library->dispose_has_run = TRUE;
108
109 sqlite3_close(library->db);
110
111 g_mutex_free(library->id_lock);
112 }
113
114 G_OBJECT_CLASS(rs_library_parent_class)->dispose (object);
115 }
116
117 static void
rs_library_finalize(GObject * object)118 rs_library_finalize(GObject *object)
119 {
120 G_OBJECT_CLASS(rs_library_parent_class)->finalize (object);
121 }
122
123 static void
rs_library_class_init(RSLibraryClass * klass)124 rs_library_class_init(RSLibraryClass *klass)
125 {
126 GObjectClass *object_class = G_OBJECT_CLASS(klass);
127
128 sqlite3_config(SQLITE_CONFIG_SERIALIZED);
129 object_class->dispose = rs_library_dispose;
130 object_class->finalize = rs_library_finalize;
131 }
132
133 gboolean
rs_library_has_database_connection(RSLibrary * library)134 rs_library_has_database_connection(RSLibrary *library)
135 {
136 if (library_execute_sql(library->db, "PRAGMA user_version;") == 0)
137 return TRUE;
138 else
139 return FALSE;
140 }
141
142 gchar *
rs_library_get_init_error_msg(RSLibrary * library)143 rs_library_get_init_error_msg(RSLibrary *library)
144 {
145 return g_strdup(library->error_init);
146 }
147
148 static gint
library_set_version(sqlite3 * db,gint version)149 library_set_version(sqlite3 *db, gint version)
150 {
151 sqlite3_stmt *stmt;
152 gint rc;
153
154 rc = sqlite3_prepare_v2(db, "update version set version = ?1;", -1, &stmt, NULL);
155 rc = sqlite3_bind_int(stmt, 1, version);
156 rc = sqlite3_step(stmt);
157 library_sqlite_error(db, rc);
158 sqlite3_finalize(stmt);
159
160 return SQLITE_OK;
161 }
162
163 static void
library_check_version(sqlite3 * db)164 library_check_version(sqlite3 *db)
165 {
166 sqlite3_stmt *stmt, *stmt_update;
167 gint rc, version = 0, id;
168 gchar *filename;
169
170 rc = sqlite3_prepare_v2(db, "SELECT version FROM version", -1, &stmt, NULL);
171 rc = sqlite3_step(stmt);
172 if (rc == SQLITE_ROW)
173 version = sqlite3_column_int(stmt, 0);
174 rc = sqlite3_finalize(stmt);
175
176 while (version < LIBRARY_VERSION)
177 {
178 switch (version)
179 {
180 case 0:
181 /* Alter table library - add identifier column */
182 sqlite3_prepare_v2(db, "alter table library add column identifier varchar(32)", -1, &stmt, NULL);
183 rc = sqlite3_step(stmt);
184 library_sqlite_error(db, rc);
185 sqlite3_finalize(stmt);
186
187 /* Run through all photos in library and insert unique identifier in library */
188 gchar *identifier;
189 sqlite3_prepare_v2(db, "select filename from library", -1, &stmt, NULL);
190 while (sqlite3_step(stmt) == SQLITE_ROW)
191 {
192 filename = (gchar *) sqlite3_column_text(stmt, 0);
193 if (g_file_test(filename, G_FILE_TEST_EXISTS))
194 {
195 identifier = rs_file_checksum(filename);
196 rc = sqlite3_prepare_v2(db, "update library set identifier = ?1 WHERE filename = ?2;", -1, &stmt_update, NULL);
197 rc = sqlite3_bind_text(stmt_update, 1, identifier, -1, SQLITE_TRANSIENT);
198 rc = sqlite3_bind_text(stmt_update, 2, filename, -1, SQLITE_TRANSIENT);
199 rc = sqlite3_step(stmt_update);
200 library_sqlite_error(db, rc);
201 sqlite3_finalize(stmt_update);
202 g_free(identifier);
203 }
204 }
205 sqlite3_finalize(stmt);
206
207 library_set_version(db, version+1);
208 break;
209
210 case 1:
211 library_execute_sql(db, "BEGIN TRANSACTION;");
212 sqlite3_prepare_v2(db, "select id,filename from library", -1, &stmt, NULL);
213 while (sqlite3_step(stmt) == SQLITE_ROW)
214 {
215 id = (gint) sqlite3_column_int(stmt, 0);
216 filename = rs_normalize_path((gchar *) sqlite3_column_text(stmt, 1));
217 if (filename) /* FIXME: This will only work for paths that exists */
218 {
219 rc = sqlite3_prepare_v2(db, "update library set filename = ?1 WHERE id = ?2;", -1, &stmt_update, NULL);
220 rc = sqlite3_bind_text(stmt_update, 1, filename, -1, SQLITE_TRANSIENT);
221 rc = sqlite3_bind_int(stmt_update, 2, id);
222 rc = sqlite3_step(stmt_update);
223 library_sqlite_error(db, rc);
224 sqlite3_finalize(stmt_update);
225 g_free(filename);
226 }
227 }
228 sqlite3_finalize(stmt);
229 library_set_version(db, version+1);
230 library_execute_sql(db, "COMMIT;");
231 break;
232
233 default:
234 /* We should never hit this */
235 g_debug("Some error occured in library_check_version() - please notify developers");
236 break;
237 }
238
239 version++;
240 g_debug("Updated library database to version %d", version);
241 }
242 }
243
244 static void
rs_library_init(RSLibrary * library)245 rs_library_init(RSLibrary *library)
246 {
247 int rc;
248
249 gchar *database = g_strdup_printf("%s/.rawstudio/library.db", g_get_home_dir());
250
251 /* If unable to create database we exit */
252 if(sqlite3_open(database, &(library->db)))
253 {
254 gchar *msg = g_strdup_printf(_("Could not open database %s"), database);
255 g_debug("sqlite3 debug: %s\n", msg);
256 if (library->error_init)
257 g_free(library->error_init);
258 library->error_init = g_strdup(msg);
259 sqlite3_close(library->db);
260 }
261 g_free(database);
262
263 if (rs_library_has_database_connection(library))
264 {
265 /* This is not FULL synchronous mode as default, since all data is re-creatable by local xml files.
266 From the sqlite3 manual:
267 With synchronous OFF (0), SQLite continues without syncing as soon as it has handed data off to
268 the operating system. If the application running SQLite crashes, the data will be safe, but the
269 database might become corrupted if the operating system crashes or the computer loses power before
270 that data has been written to the disk surface. On the other hand,
271 some operations are as much as 50 or more times faster with synchronous OFF. " */
272 library_execute_sql(library->db, "PRAGMA synchronous = OFF;");
273
274 /* Move our journal to memory, we're not doing banking for the Mafia */
275 library_execute_sql(library->db, "PRAGMA journal_mode = memory;");
276
277 /* Place temp tables in memory */
278 library_execute_sql(library->db, "PRAGMA temp_store = memory;");
279
280 rc = library_create_tables(library->db);
281 library_sqlite_error(library->db, rc);
282
283 library_check_version(library->db);
284
285 library->id_lock = g_mutex_new();
286 }
287 }
288
289 RSLibrary *
rs_library_get_singleton(void)290 rs_library_get_singleton(void)
291 {
292 static GStaticMutex singleton_lock = G_STATIC_MUTEX_INIT;
293 static RSLibrary *singleton = NULL;
294
295 g_static_mutex_lock(&singleton_lock);
296 if (!singleton)
297 singleton = g_object_new(RS_TYPE_LIBRARY, NULL);
298 g_static_mutex_unlock(&singleton_lock);
299
300 return singleton;
301 }
302
303 static gint
library_execute_sql(sqlite3 * db,const gchar * sql)304 library_execute_sql(sqlite3 *db, const gchar *sql)
305 {
306 sqlite3_stmt *statement;
307
308 if(SQLITE_OK != sqlite3_prepare(db, sql, -1, &statement, 0))
309 return sqlite3_errcode(db);
310
311 while (SQLITE_ROW == sqlite3_step(statement));
312
313 return sqlite3_finalize(statement);
314 }
315
316 static void
library_sqlite_error(sqlite3 * db,gint result)317 library_sqlite_error(sqlite3 *db, gint result)
318 {
319 if (result != SQLITE_OK && result != SQLITE_DONE)
320 {
321 g_warning("sqlite3 warning: %s\n", sqlite3_errmsg(db));
322 }
323 }
324
325 static gint
library_create_tables(sqlite3 * db)326 library_create_tables(sqlite3 *db)
327 {
328 sqlite3_stmt *stmt;
329 gint rc;
330
331 /* Create table (library) to hold all known photos */
332 sqlite3_prepare_v2(db, "create table library (id integer primary key, filename varchar(1024), identifier varchar(32))", -1, &stmt, NULL);
333 rc = sqlite3_step(stmt);
334 sqlite3_finalize(stmt);
335
336 /* Create table (tags) with all known tags */
337 sqlite3_prepare_v2(db, "create table tags (id integer primary key, tagname varchar(128))", -1, &stmt, NULL);
338 rc = sqlite3_step(stmt);
339 sqlite3_finalize(stmt);
340
341 /* Create table (phototags) to bind tags and photos together */
342 sqlite3_prepare_v2(db, "create table phototags (photo integer, tag integer, autotag integer)", -1, &stmt, NULL);
343 rc = sqlite3_step(stmt);
344 sqlite3_finalize(stmt);
345
346 /* Create table (version) to help keeping track of database version */
347 sqlite3_prepare_v2(db, "create table version (version integer)", -1, &stmt, NULL);
348 rc = sqlite3_step(stmt);
349 sqlite3_finalize(stmt);
350
351 rc = sqlite3_prepare_v2(db, "select * from version", -1, &stmt, NULL);
352 rc = sqlite3_step(stmt);
353 sqlite3_finalize(stmt);
354 if (rc != SQLITE_ROW)
355 {
356 /* Set current version */
357 rc = sqlite3_prepare_v2(db, "insert into version (version) values (?1);", -1, &stmt, NULL);
358 rc = sqlite3_bind_int(stmt, 1, LIBRARY_VERSION);
359 rc = sqlite3_step(stmt);
360 sqlite3_finalize(stmt);
361
362 rc = sqlite3_prepare_v2(db, "select identifier from library", -1, &stmt, NULL);
363 rc = sqlite3_step(stmt);
364 sqlite3_finalize(stmt);
365
366 /* Check if library.identifier exists */
367 if (rc == SQLITE_MISUSE)
368 {
369 library_set_version(db, 0);
370 }
371 }
372
373 return SQLITE_OK;
374 }
375
376 static gint
library_find_tag_id(RSLibrary * library,const gchar * tagname)377 library_find_tag_id(RSLibrary *library, const gchar *tagname)
378 {
379 sqlite3 *db = library->db;
380 sqlite3_stmt *stmt;
381 gint rc, tag_id = -1;
382
383 rc = sqlite3_prepare_v2(db, "SELECT id FROM tags WHERE tagname = ?1;", -1, &stmt, NULL);
384 rc = sqlite3_bind_text(stmt, 1, tagname, -1, SQLITE_TRANSIENT);
385 rc = sqlite3_step(stmt);
386 if (rc == SQLITE_ROW)
387 tag_id = sqlite3_column_int(stmt, 0);
388 rc = sqlite3_finalize(stmt);
389 return tag_id;
390 }
391
392 static gint
library_find_photo_id(RSLibrary * library,const gchar * photo)393 library_find_photo_id(RSLibrary *library, const gchar *photo)
394 {
395 sqlite3 *db = library->db;
396 sqlite3_stmt *stmt;
397 gint rc, photo_id = -1;
398
399 rc = sqlite3_prepare_v2(db, "SELECT id FROM library WHERE filename = ?1;", -1, &stmt, NULL);
400 rc = sqlite3_bind_text(stmt, 1, photo, -1, SQLITE_TRANSIENT);
401 library_sqlite_error(db, rc);
402 rc = sqlite3_step(stmt);
403 if (rc == SQLITE_ROW)
404 photo_id = sqlite3_column_int(stmt, 0);
405 rc = sqlite3_finalize(stmt);
406 return photo_id;
407 }
408
409 static void
library_photo_add_tag(RSLibrary * library,const gint photo_id,const gint tag_id,const gboolean autotag)410 library_photo_add_tag(RSLibrary *library, const gint photo_id, const gint tag_id, const gboolean autotag)
411 {
412 sqlite3 *db = library->db;
413 gint rc;
414 sqlite3_stmt *stmt;
415
416 gint autotag_tag = 0;
417 if (autotag)
418 autotag_tag = 1;
419
420 g_mutex_lock(library->id_lock);
421 rc = sqlite3_prepare_v2(db, "INSERT INTO phototags (photo, tag, autotag) VALUES (?1, ?2, ?3);", -1, &stmt, NULL);
422 rc = sqlite3_bind_int (stmt, 1, photo_id);
423 rc = sqlite3_bind_int (stmt, 2, tag_id);
424 rc = sqlite3_bind_int (stmt, 3, autotag_tag);
425 rc = sqlite3_step(stmt);
426 g_mutex_unlock(library->id_lock);
427 if (rc != SQLITE_DONE)
428 library_sqlite_error(db, rc);
429 sqlite3_finalize(stmt);
430 }
431
432 static gboolean
library_is_photo_tagged(RSLibrary * library,gint photo_id,gint tag_id)433 library_is_photo_tagged(RSLibrary *library, gint photo_id, gint tag_id)
434 {
435 sqlite3 *db = library->db;
436 gint rc;
437 sqlite3_stmt *stmt;
438
439 rc = sqlite3_prepare_v2(db, "SELECT * FROM phototags WHERE photo = ?1 AND tag = ?2;", -1, &stmt, NULL);
440 rc = sqlite3_bind_int (stmt, 1, photo_id);
441 rc = sqlite3_bind_int (stmt, 2, tag_id);
442 rc = sqlite3_step(stmt);
443 sqlite3_finalize(stmt);
444
445 if (rc == SQLITE_ROW)
446 return TRUE;
447 else
448 return FALSE;
449 }
450
451 static void
got_checksum(const gchar * checksum,gpointer user_data)452 got_checksum(const gchar *checksum, gpointer user_data)
453 {
454 RSLibrary *library = rs_library_get_singleton();
455 sqlite3 *db = library->db;
456 sqlite3_stmt *stmt;
457
458 sqlite3_prepare_v2(db, "UPDATE LIBRARY SET identifier=?1 WHERE id=?2;", -1, &stmt, NULL);
459 sqlite3_bind_text(stmt, 1, checksum, -1, SQLITE_TRANSIENT);
460 sqlite3_bind_int(stmt, 2, GPOINTER_TO_INT(user_data));
461 sqlite3_step(stmt);
462 sqlite3_finalize(stmt);
463 }
464
465 static gint
library_add_photo(RSLibrary * library,const gchar * filename)466 library_add_photo(RSLibrary *library, const gchar *filename)
467 {
468 gint id;
469 sqlite3 *db = library->db;
470 gint rc;
471 sqlite3_stmt *stmt;
472
473 g_mutex_lock(library->id_lock);
474 sqlite3_prepare_v2(db, "INSERT INTO library (filename) VALUES (?1);", -1, &stmt, NULL);
475 rc = sqlite3_bind_text(stmt, 1, filename, -1, SQLITE_TRANSIENT);
476 rc = sqlite3_step(stmt);
477 id = sqlite3_last_insert_rowid(db);
478 g_mutex_unlock(library->id_lock);
479 if (rc != SQLITE_DONE)
480 library_sqlite_error(db, rc);
481 sqlite3_finalize(stmt);
482
483 rs_io_idle_read_checksum(filename, -1, got_checksum, GINT_TO_POINTER(id));
484
485 return id;
486 }
487
488 static gint
library_add_tag(RSLibrary * library,const gchar * tagname)489 library_add_tag(RSLibrary *library, const gchar *tagname)
490 {
491 gint id;
492 sqlite3 *db = library->db;
493 gint rc;
494 sqlite3_stmt *stmt;
495
496 g_mutex_lock(library->id_lock);
497 sqlite3_prepare_v2(db, "INSERT INTO tags (tagname) VALUES (?1);", -1, &stmt, NULL);
498 rc = sqlite3_bind_text(stmt, 1, tagname, -1, SQLITE_TRANSIENT);
499 rc = sqlite3_step(stmt);
500 id = sqlite3_last_insert_rowid(db);
501 g_mutex_unlock(library->id_lock);
502 if (rc != SQLITE_DONE)
503 library_sqlite_error(db, rc);
504 sqlite3_finalize(stmt);
505
506 return id;
507 }
508
509 static void
library_delete_photo(RSLibrary * library,gint photo_id)510 library_delete_photo(RSLibrary *library, gint photo_id)
511 {
512 sqlite3 *db = library->db;
513 sqlite3_stmt *stmt;
514 gint rc;
515
516 rc = sqlite3_prepare_v2(db, "DELETE FROM library WHERE id = ?1;", -1, &stmt, NULL);
517 rc = sqlite3_bind_int(stmt, 1, photo_id);
518 library_sqlite_error(db, rc);
519 rc = sqlite3_step(stmt);
520 if (rc != SQLITE_DONE)
521 library_sqlite_error(db, rc);
522 rc = sqlite3_finalize(stmt);
523 }
524
525 static void
library_delete_tag(RSLibrary * library,gint tag_id)526 library_delete_tag(RSLibrary *library, gint tag_id)
527 {
528 sqlite3 *db = library->db;
529 sqlite3_stmt *stmt;
530 gint rc;
531
532 rc = sqlite3_prepare_v2(db, "DELETE FROM library WHERE filename = ?1;", -1, &stmt, NULL);
533 rc = sqlite3_bind_int(stmt, 1, tag_id);
534 library_sqlite_error(db, rc);
535 rc = sqlite3_step(stmt);
536 if (rc != SQLITE_DONE)
537 library_sqlite_error(db, rc);
538 rc = sqlite3_finalize(stmt);
539 }
540
541 static void
library_photo_delete_tags(RSLibrary * library,gint photo_id)542 library_photo_delete_tags(RSLibrary *library, gint photo_id)
543 {
544 sqlite3 *db = library->db;
545 sqlite3_stmt *stmt;
546 gint rc;
547
548 rc = sqlite3_prepare_v2(db, "DELETE FROM phototags WHERE photo = ?1;", -1, &stmt, NULL);
549 rc = sqlite3_bind_int(stmt, 1, photo_id);
550 library_sqlite_error(db, rc);
551 rc = sqlite3_step(stmt);
552 if (rc != SQLITE_DONE)
553 library_sqlite_error(db, rc);
554 rc = sqlite3_finalize(stmt);
555 }
556
557 static void
library_tag_delete_photos(RSLibrary * library,gint tag_id)558 library_tag_delete_photos(RSLibrary *library, gint tag_id)
559 {
560 sqlite3 *db = library->db;
561 sqlite3_stmt *stmt;
562 gint rc;
563
564 rc = sqlite3_prepare_v2(db, "DELETE FROM phototags WHERE tag = ?1;", -1, &stmt, NULL);
565 rc = sqlite3_bind_int(stmt, 1, tag_id);
566 library_sqlite_error(db, rc);
567 rc = sqlite3_step(stmt);
568 if (rc != SQLITE_DONE)
569 library_sqlite_error(db, rc);
570 rc = sqlite3_finalize(stmt);
571 }
572
573 static gboolean
library_tag_is_used(RSLibrary * library,gint tag_id)574 library_tag_is_used(RSLibrary *library, gint tag_id)
575 {
576 sqlite3 *db = library->db;
577 gint rc;
578 sqlite3_stmt *stmt;
579
580 rc = sqlite3_prepare_v2(db, "SELECT * FROM phototags WHERE tag = ?1;", -1, &stmt, NULL);
581 rc = sqlite3_bind_int (stmt, 1, tag_id);
582 rc = sqlite3_step(stmt);
583 sqlite3_finalize(stmt);
584
585 if (rc == SQLITE_ROW)
586 return TRUE;
587 else
588 return FALSE;
589 }
590
591 gint
rs_library_add_photo(RSLibrary * library,const gchar * filename)592 rs_library_add_photo(RSLibrary *library, const gchar *filename)
593 {
594 gint photo_id;
595
596 g_assert(RS_IS_LIBRARY(library));
597 if (!rs_library_has_database_connection(library)) return 0; /* FIXME */
598
599 photo_id = library_find_photo_id(library, filename);
600 if (photo_id == -1)
601 {
602 g_debug("Adding photo to library: %s",filename);
603 photo_id = library_add_photo(library, filename);
604 }
605
606 return photo_id;
607 }
608
609 gint
rs_library_add_tag(RSLibrary * library,const gchar * tagname)610 rs_library_add_tag(RSLibrary *library, const gchar *tagname)
611 {
612 gint tag_id;
613
614 g_assert(RS_IS_LIBRARY(library));
615 if (!rs_library_has_database_connection(library)) return 0; /* FIXME */
616
617 tag_id = library_find_tag_id(library, tagname);
618 if (tag_id == -1)
619 {
620 g_debug("Adding tag to tags: %s",tagname);
621 tag_id = library_add_tag(library, tagname);
622 }
623
624 return tag_id;
625 }
626
627 void
rs_library_photo_add_tag(RSLibrary * library,const gchar * filename,gint tag_id,const gboolean autotag)628 rs_library_photo_add_tag(RSLibrary *library, const gchar *filename, gint tag_id, const gboolean autotag)
629 {
630 g_assert(RS_IS_LIBRARY(library));
631 if (!rs_library_has_database_connection(library)) return;
632
633 gint photo_id;
634
635 if (tag_id == -1)
636 {
637 g_warning("Tag not known...");
638 return;
639 }
640
641 photo_id = library_find_photo_id(library, filename);
642 if (photo_id == -1)
643 {
644 g_warning("Photo not known...");
645 return;
646 }
647
648 if (!library_is_photo_tagged(library, photo_id, tag_id))
649 library_photo_add_tag(library, photo_id, tag_id, autotag);
650
651 return;
652 }
653
654 void
rs_library_delete_photo(RSLibrary * library,const gchar * photo)655 rs_library_delete_photo(RSLibrary *library, const gchar *photo)
656 {
657 g_assert(RS_IS_LIBRARY(library));
658 if (!rs_library_has_database_connection(library)) return;
659
660 gint photo_id = -1;
661
662 photo_id = library_find_photo_id(library, photo);
663 if (photo_id == -1)
664 {
665 g_warning("Photo not known...");
666 return;
667 }
668
669 library_photo_delete_tags(library, photo_id);
670 library_delete_photo(library, photo_id);
671 rs_library_backup_tags(library, photo);
672 }
673
674 gboolean
rs_library_delete_tag(RSLibrary * library,const gchar * tag,const gboolean force)675 rs_library_delete_tag(RSLibrary *library, const gchar *tag, const gboolean force)
676 {
677 g_assert(RS_IS_LIBRARY(library));
678 if (!rs_library_has_database_connection(library)) return FALSE;
679
680 gint tag_id = -1;
681
682 tag_id = library_find_tag_id(library, tag);
683 if (tag_id == -1)
684 {
685 g_warning("Tag not known...");
686 return FALSE;
687 }
688
689 if (library_tag_is_used(library, tag_id))
690 if (force)
691 {
692 library_tag_delete_photos(library, tag_id);
693 library_delete_tag(library, tag_id);
694 }
695 else
696 {
697 g_warning("Tag is in use...");
698 return FALSE;
699 }
700 else
701 library_delete_tag(library, tag_id);
702 return TRUE;
703 }
704
705 GList *
rs_library_search(RSLibrary * library,GList * tags)706 rs_library_search(RSLibrary *library, GList *tags)
707 {
708 g_assert(RS_IS_LIBRARY(library));
709 if (!rs_library_has_database_connection(library)) return NULL;
710
711 sqlite3_stmt *stmt;
712 gint rc;
713 sqlite3 *db = library->db;
714 gchar *tag;
715 gint n, num_tags = g_list_length(tags);
716 GList *photos = NULL;
717 GTimer *gt = g_timer_new();
718 gchar *filename;
719
720 sqlite3_prepare_v2(db, "create temp table filter (photo integer)", -1, &stmt, NULL);
721 rc = sqlite3_step(stmt);
722 sqlite3_finalize(stmt);
723 library_sqlite_error(db, rc);
724
725 for (n = 0; n < num_tags; n++)
726 {
727 tag = (gchar *) g_list_nth_data(tags, n);
728
729 g_mutex_lock(library->id_lock);
730 sqlite3_prepare_v2(db, "insert into filter select phototags.photo from phototags, tags where phototags.tag = tags.id and lower(tags.tagname) = lower(?1) ;", -1, &stmt, NULL);
731 rc = sqlite3_bind_text(stmt, 1, tag, -1, SQLITE_TRANSIENT);
732 rc = sqlite3_step(stmt);
733 sqlite3_finalize(stmt);
734 g_mutex_unlock(library->id_lock);
735 }
736
737 sqlite3_prepare_v2(db, "create temp table result (photo integer, count integer)", -1, &stmt, NULL);
738 rc = sqlite3_step(stmt);
739 sqlite3_finalize(stmt);
740 library_sqlite_error(db, rc);
741
742 g_mutex_lock(library->id_lock);
743 sqlite3_prepare_v2(db, "insert into result select photo, count(photo) from filter group by photo;", -1, &stmt, NULL);
744 rc = sqlite3_step(stmt);
745 sqlite3_finalize(stmt);
746 g_mutex_unlock(library->id_lock);
747 library_sqlite_error(db, rc);
748
749 sqlite3_prepare_v2(db, "select library.filename from library,result where library.id = result.photo and result.count = ?1 order by library.filename;", -1, &stmt, NULL);
750 rc = sqlite3_bind_int(stmt, 1, num_tags);
751
752 gint count = 0;
753 while (sqlite3_step(stmt) == SQLITE_ROW && count < MAX_SEARCH_RESULTS)
754 {
755 filename = g_strdup((gchar *) sqlite3_column_text(stmt, 0));
756 if (g_file_test(filename, G_FILE_TEST_EXISTS))
757 {
758 photos = g_list_append(photos, filename);
759 count++;
760 }
761 }
762 sqlite3_finalize(stmt);
763 library_sqlite_error(db, rc);
764
765 /* Empty filter */
766 sqlite3_prepare_v2(db, "delete from filter;", -1, &stmt, NULL);
767 rc = sqlite3_step(stmt);
768 sqlite3_finalize(stmt);
769 library_sqlite_error(db, rc);
770
771 /* Empty result */
772 sqlite3_prepare_v2(db, "delete from result;", -1, &stmt, NULL);
773 rc = sqlite3_step(stmt);
774 sqlite3_finalize(stmt);
775 library_sqlite_error(db, rc);
776
777 g_debug("Search in library took %.03f seconds", g_timer_elapsed(gt, NULL));
778 g_timer_destroy(gt);
779
780 return photos;
781 }
782
783 static void
library_photo_default_tags(RSLibrary * library,const gint photo_id,RSMetadata * metadata)784 library_photo_default_tags(RSLibrary *library, const gint photo_id, RSMetadata *metadata)
785 {
786 g_assert(RS_IS_LIBRARY(library));
787
788 GList *tags = NULL;
789
790 if (metadata->make_ascii)
791 {
792 GList *temp = rs_split_string(metadata->make_ascii, " ");
793 tags = g_list_concat(tags, temp);
794 }
795 if (metadata->model_ascii)
796 {
797 GList *temp = rs_split_string(metadata->model_ascii, " ");
798 tags = g_list_concat(tags, temp);
799 }
800 if (metadata->lens_min_focal != -1 && metadata->lens_max_focal != -1)
801 {
802 gchar *lens = NULL;
803 if (metadata->lens_min_focal == metadata->lens_max_focal)
804 lens = g_strdup_printf("%dmm",(gint) metadata->lens_min_focal);
805 else
806 lens = g_strdup_printf("%d-%dmm",(gint) metadata->lens_min_focal, (gint) metadata->lens_max_focal);
807 tags = g_list_append(tags, g_strdup(lens));
808 g_free(lens);
809 }
810 if (metadata->focallength > 0)
811 {
812 gchar *text = NULL;
813 if (metadata->focallength < 50)
814 text = g_strdup(_("wideangle"));
815 else
816 text = g_strdup(_("telephoto"));
817 tags = g_list_append(tags, g_strdup(text));
818 g_free(text);
819 }
820 if (metadata->timestamp != -1)
821 {
822 gchar *year = NULL;
823 gchar *month = NULL;
824 GDate *date = g_date_new();
825 g_date_set_time_t(date, metadata->timestamp);
826 year = g_strdup_printf("%d", g_date_get_year(date));
827 gint m = g_date_get_month(date);
828
829 switch (m)
830 {
831 case 1:
832 month = g_strdup(_("January")); /* FIXME: There may be a better way to do this */
833 break;
834 case 2:
835 month = g_strdup(_("February"));
836 break;
837 case 3:
838 month = g_strdup(_("March"));
839 break;
840 case 4:
841 month = g_strdup(_("April"));
842 break;
843 case 5:
844 month = g_strdup(_("May"));
845 break;
846 case 6:
847 month = g_strdup(_("June"));
848 break;
849 case 7:
850 month = g_strdup(_("July"));
851 break;
852 case 8:
853 month = g_strdup(_("August"));
854 break;
855 case 9:
856 month = g_strdup(_("September"));
857 break;
858 case 10:
859 month = g_strdup(_("October"));
860 break;
861 case 11:
862 month = g_strdup(_("November"));
863 break;
864 case 12:
865 month = g_strdup(_("December"));
866 break;
867 }
868
869 tags = g_list_append(tags, g_strdup(year));
870 tags = g_list_append(tags, g_strdup(month));
871
872 g_date_free(date);
873 g_free(year);
874 g_free(month);
875 }
876
877 gint i, j;
878 library_execute_sql(library->db, "BEGIN TRANSACTION;");
879 gint *used_tags = g_malloc(g_list_length(tags) * sizeof(gint));
880 for(i = 0; i < g_list_length(tags); i++)
881 {
882 gchar *tag = (gchar *) g_list_nth_data(tags, i);
883 gint tag_id = rs_library_add_tag(library, tag);
884
885 /* Check if tag has already been added */
886 gboolean used = FALSE;
887 for (j = 0; j < i; j++)
888 if (tag_id == used_tags[j])
889 used = TRUE;
890
891 if (!used)
892 library_photo_add_tag(library, photo_id, tag_id, TRUE);
893 used_tags[i] = tag_id;
894 g_free(tag);
895 }
896 g_free(used_tags);
897 library_execute_sql(library->db, "COMMIT;");
898 g_list_free(tags);
899 }
900
901 GList *
rs_library_photo_tags(RSLibrary * library,const gchar * photo,const gboolean autotag)902 rs_library_photo_tags(RSLibrary *library, const gchar *photo, const gboolean autotag)
903 {
904 g_assert(RS_IS_LIBRARY(library));
905 if (!rs_library_has_database_connection(library)) return NULL;
906
907 sqlite3_stmt *stmt;
908 gint rc;
909 sqlite3 *db = library->db;
910 GList *tags = NULL;
911
912 if (autotag)
913 {
914 sqlite3_prepare_v2(db, "select tags.tagname from library,phototags,tags WHERE library.id=phototags.photo and phototags.tag=tags.id and library.filename = ?1;", -1, &stmt, NULL);
915 rc = sqlite3_bind_text(stmt, 1, photo, -1, NULL);
916 }
917 else
918 {
919 sqlite3_prepare_v2(db, "select tags.tagname from library,phototags,tags WHERE library.id=phototags.photo and phototags.tag=tags.id and library.filename = ?1 and phototags.autotag = 0;", -1, &stmt, NULL);
920 rc = sqlite3_bind_text(stmt, 1, photo, -1, NULL);
921 }
922 while (sqlite3_step(stmt) == SQLITE_ROW)
923 tags = g_list_append(tags, g_strdup((gchar *) sqlite3_column_text(stmt, 0)));
924 sqlite3_finalize(stmt);
925 library_sqlite_error(db, rc);
926
927 return tags;
928 }
929
930 GList *
rs_library_find_tag(RSLibrary * library,const gchar * tag)931 rs_library_find_tag(RSLibrary *library, const gchar *tag)
932 {
933 g_assert(RS_IS_LIBRARY(library));
934 if (!rs_library_has_database_connection(library)) return NULL;
935
936 sqlite3_stmt *stmt;
937 gint rc;
938 sqlite3 *db = library->db;
939 GList *tags = NULL;
940
941 rc = sqlite3_prepare_v2(db, "select tags.tagname from tags WHERE tags.tagname like ?1 order by tags.tagname;", -1, &stmt, NULL);
942 gchar *like = g_strdup_printf("%%%s%%", tag);
943 rc = sqlite3_bind_text(stmt, 1, like, -1, NULL);
944 library_sqlite_error(db, rc);
945
946 while (sqlite3_step(stmt) == SQLITE_ROW)
947 tags = g_list_append(tags, g_strdup((gchar *) sqlite3_column_text(stmt, 0)));
948 sqlite3_finalize(stmt);
949 library_sqlite_error(db, rc);
950
951 g_free(like);
952
953 return tags;
954 }
955
956
957 gboolean
rs_library_set_tag_search(gchar * str)958 rs_library_set_tag_search(gchar *str)
959 {
960 if (!str)
961 return FALSE;
962 gtk_entry_set_text(GTK_ENTRY(tag_search_entry), str);
963 return TRUE;
964 }
965
966 void
rs_library_add_photo_with_metadata(RSLibrary * library,const gchar * photo,RSMetadata * metadata)967 rs_library_add_photo_with_metadata(RSLibrary *library, const gchar *photo, RSMetadata *metadata)
968 {
969 if (!rs_library_has_database_connection(library)) return;
970
971 /* Bail out if we already know the photo */
972 if (library_find_photo_id(library, photo) > -1)
973 return;
974
975 gint photo_id = library_add_photo(library, photo);
976 library_photo_default_tags(library, photo_id, metadata);
977 }
978
979 static GStaticMutex backup_lock = G_STATIC_MUTEX_INIT;
980
981 void
rs_library_backup_tags(RSLibrary * library,const gchar * photo_filename)982 rs_library_backup_tags(RSLibrary *library, const gchar *photo_filename)
983 {
984 if (!rs_library_has_database_connection(library)) return;
985
986 sqlite3 *db = library->db;
987 sqlite3_stmt *stmt;
988 gint rc;
989 gchar *filename = NULL, *checksum, *tag, *t_filename;
990 gint autotag;
991 gchar *directory = g_path_get_dirname(photo_filename);
992 gchar *dotdir = rs_dotdir_get(photo_filename);
993
994 g_static_mutex_lock (&backup_lock);
995
996 if (!dotdir)
997 return;
998 GString *gs = g_string_new(dotdir);
999 g_string_append(gs, G_DIR_SEPARATOR_S);
1000 g_string_append(gs, TAGS_XML_FILE);
1001 gchar *xmlfile = gs->str;
1002 g_string_free(gs, FALSE);
1003
1004 xmlTextWriterPtr writer;
1005
1006 writer = xmlNewTextWriterFilename(xmlfile, 0);
1007 if (!writer)
1008 {
1009 g_free(directory);
1010 g_free(dotdir);
1011 g_free(xmlfile);
1012 g_static_mutex_unlock (&backup_lock);
1013 return;
1014 }
1015
1016 xmlTextWriterSetIndent(writer, 1);
1017 xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
1018 xmlTextWriterStartElement(writer, BAD_CAST "rawstudio-tags");
1019 xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "version", "%d", LIBRARY_VERSION);
1020
1021 const gchar *temp = g_strdup_printf("%s/%%", directory);
1022 rc = sqlite3_prepare_v2(db, "select library.filename,library.identifier,tags.tagname,phototags.autotag from library,phototags,tags where library.filename like ?1 and phototags.photo = library.id and tags.id = phototags.tag order by library.filename;", -1, &stmt, NULL);
1023 rc = sqlite3_bind_text(stmt, 1, temp, -1, SQLITE_TRANSIENT);
1024 library_sqlite_error(db, rc);
1025 while (sqlite3_step(stmt) == SQLITE_ROW)
1026 {
1027 t_filename = g_path_get_basename((gchar *) sqlite3_column_text(stmt, 0));
1028 if (g_strcmp0(t_filename, filename) != 0 || filename == NULL)
1029 {
1030 if (filename != NULL)
1031 xmlTextWriterEndElement(writer);
1032 filename = t_filename;
1033 xmlTextWriterStartElement(writer, BAD_CAST "file");
1034 xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "name", "%s", filename);
1035 checksum = (gchar *) sqlite3_column_text(stmt, 1);
1036 xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "checksum", "%s", checksum);
1037 }
1038
1039 tag = (gchar *) sqlite3_column_text(stmt, 2);
1040 autotag = (gint) sqlite3_column_int(stmt, 3);
1041 xmlTextWriterStartElement(writer, BAD_CAST "tag");
1042 xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "name", "%s", tag);
1043 xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "auto", "%d", autotag);
1044 xmlTextWriterEndElement(writer);
1045 }
1046 xmlTextWriterEndElement(writer);
1047
1048 rc = sqlite3_finalize(stmt);
1049
1050 xmlTextWriterEndDocument(writer);
1051 xmlFreeTextWriter(writer);
1052 g_free(directory);
1053 g_free(dotdir);
1054 g_free(xmlfile);
1055 g_static_mutex_unlock (&backup_lock);
1056 return;
1057 }
1058
1059 void
rs_library_restore_tags(const gchar * directory)1060 rs_library_restore_tags(const gchar *directory)
1061 {
1062 RSLibrary *library = rs_library_get_singleton();
1063
1064 if (!rs_library_has_database_connection(library)) return;
1065
1066 gchar *dotdir = rs_dotdir_get(directory);
1067
1068 if (!dotdir)
1069 return;
1070 GString *gs = g_string_new(dotdir);
1071 g_string_append(gs, G_DIR_SEPARATOR_S);
1072 g_string_append(gs, TAGS_XML_FILE);
1073 gchar *xmlfile = gs->str;
1074 g_string_free(gs, FALSE);
1075
1076 if (!g_file_test(xmlfile, G_FILE_TEST_EXISTS))
1077 {
1078 g_free(dotdir);
1079 g_free(xmlfile);
1080 return;
1081 }
1082
1083 xmlDocPtr doc;
1084 xmlNodePtr cur, cur2;
1085 xmlChar *val;
1086 gint version = 0;
1087
1088 gchar *filename, *identifier, *tagname;
1089 gint autotag, photoid, tagid;
1090
1091 doc = xmlParseFile(xmlfile);
1092 if (!doc)
1093 return;
1094
1095 cur = xmlDocGetRootElement(doc);
1096
1097 if ((!xmlStrcmp(cur->name, BAD_CAST "rawstudio-tags")))
1098 {
1099 val = xmlGetProp(cur, BAD_CAST "version");
1100 if (val)
1101 version = atoi((gchar *) val);
1102 if (version > LIBRARY_VERSION)
1103 {
1104 xmlFree(val);
1105 g_free(dotdir);
1106 g_free(xmlfile);
1107 xmlFreeDoc(doc);
1108 return;
1109 }
1110 }
1111
1112 cur = cur->xmlChildrenNode;
1113 while(cur)
1114 {
1115 if ((!xmlStrcmp(cur->name, BAD_CAST "file")))
1116 {
1117 val = xmlGetProp(cur, BAD_CAST "name");
1118 filename = g_build_filename(directory, val, NULL);
1119 xmlFree(val);
1120
1121 photoid = library_find_photo_id(library, filename);
1122 if ( photoid == -1 && g_file_test(filename, G_FILE_TEST_EXISTS))
1123 {
1124 photoid = rs_library_add_photo(library, filename);
1125
1126 val = xmlGetProp(cur, BAD_CAST "checksum");
1127 identifier = (gchar *) val;
1128
1129 cur2 = cur->xmlChildrenNode;
1130 while(cur2)
1131 {
1132 if ((!xmlStrcmp(cur2->name, BAD_CAST "tag")))
1133 {
1134 val = xmlGetProp(cur2, BAD_CAST "name");
1135 tagname =(gchar*) val;
1136 tagid = library_find_tag_id(library, tagname);
1137 if ( tagid == -1)
1138 tagid = rs_library_add_tag(library, tagname);
1139
1140 val = xmlGetProp(cur2, BAD_CAST "auto");
1141 autotag = atoi((gchar *) val);
1142 xmlFree(val);
1143
1144 library_photo_add_tag(library, photoid, tagid, (autotag == 1));
1145
1146 xmlFree(tagname);
1147 }
1148 cur2 = cur2->next;
1149 }
1150 xmlFree(identifier);
1151 }
1152 g_free(filename);
1153 }
1154 cur = cur->next;
1155 }
1156
1157 g_free(dotdir);
1158 g_free(xmlfile);
1159 xmlFreeDoc(doc);
1160 return;
1161 }
1162