| 867 | | (defun tumme-write-tag (files tag) |
|---|
| 868 | | "For all FILES, writes TAG to the image database." |
|---|
| 869 | | (save-excursion |
|---|
| 870 | | (let (end buf) |
|---|
| 871 | | (setq buf (find-file tumme-db-file)) |
|---|
| 872 | | (if (not (listp files)) |
|---|
| 873 | | (if (stringp files) |
|---|
| 874 | | (setq files (list files)) |
|---|
| 875 | | (error "Files must be a string or a list of strings!"))) |
|---|
| 876 | | (mapcar |
|---|
| 877 | | (lambda (file) |
|---|
| 878 | | (goto-char (point-min)) |
|---|
| 879 | | (if (search-forward-regexp |
|---|
| 880 | | (format "^%s" file) nil t) |
|---|
| 881 | | (progn |
|---|
| 882 | | (end-of-line) |
|---|
| 883 | | (setq end (point)) |
|---|
| 884 | | (beginning-of-line) |
|---|
| 885 | | (when (not (search-forward (format ";%s" tag) end t)) |
|---|
| 886 | | (end-of-line) |
|---|
| 887 | | (insert (format ";%s" tag)))) |
|---|
| 888 | | (goto-char (point-max)) |
|---|
| 889 | | (insert (format "\n%s;%s" file tag)))) |
|---|
| 890 | | files) |
|---|
| 891 | | (save-buffer) |
|---|
| 892 | | (kill-buffer buf)))) |
|---|
| | 871 | (defun tumme-write-tags (file-tags) |
|---|
| | 872 | "Write file tags to database. |
|---|
| | 873 | Write each file and tag in FILE-TAGS to the database. FILE-TAGS |
|---|
| | 874 | is an alist in the following form: |
|---|
| | 875 | ((FILE . TAG) ... )" |
|---|
| | 876 | (let (end file tag) |
|---|
| | 877 | (with-temp-file tumme-db-file |
|---|
| | 878 | (insert-file-contents tumme-db-file) |
|---|
| | 879 | (dolist (elt file-tags) |
|---|
| | 880 | (setq file (car elt) |
|---|
| | 881 | tag (cdr elt)) |
|---|
| | 882 | (goto-char (point-min)) |
|---|
| | 883 | (if (search-forward-regexp (format "^%s.*$" file) nil t) |
|---|
| | 884 | (progn |
|---|
| | 885 | (setq end (point)) |
|---|
| | 886 | (beginning-of-line) |
|---|
| | 887 | (when (not (search-forward (format ";%s" tag) end t)) |
|---|
| | 888 | (end-of-line) |
|---|
| | 889 | (insert (format ";%s" tag)))) |
|---|
| | 890 | (goto-char (point-max)) |
|---|
| | 891 | (insert (format "\n%s;%s" file tag))))))) |
|---|
| 1072 | | (defun tumme-forward-char () |
|---|
| 1073 | | "Move to next image and display properties." |
|---|
| 1074 | | (interactive) |
|---|
| 1075 | | ;; Before we move, make sure that there is an image two positions |
|---|
| 1076 | | ;; forward. |
|---|
| 1077 | | (when (save-excursion |
|---|
| 1078 | | (forward-char 2) |
|---|
| 1079 | | (tumme-image-at-point-p)) |
|---|
| 1080 | | (forward-char) |
|---|
| 1081 | | (while (and (not (eobp)) |
|---|
| 1082 | | (not (tumme-image-at-point-p))) |
|---|
| 1083 | | (forward-char)) |
|---|
| 1084 | | (if tumme-track-movement |
|---|
| 1085 | | (tumme-track-original-file))) |
|---|
| | 1075 | (defun tumme-forward-image (&optional arg) |
|---|
| | 1076 | "Move to next image and display properties. |
|---|
| | 1077 | Optional prefix ARG says how many images to move; default is one |
|---|
| | 1078 | image." |
|---|
| | 1079 | (interactive "p") |
|---|
| | 1080 | (let (pos (steps (or arg 1))) |
|---|
| | 1081 | (dotimes (i steps) |
|---|
| | 1082 | (if (and (not (eobp)) |
|---|
| | 1083 | (save-excursion |
|---|
| | 1084 | (forward-char) |
|---|
| | 1085 | (while (and (not (eobp)) |
|---|
| | 1086 | (not (tumme-image-at-point-p))) |
|---|
| | 1087 | (forward-char)) |
|---|
| | 1088 | (setq pos (point)) |
|---|
| | 1089 | (tumme-image-at-point-p))) |
|---|
| | 1090 | (goto-char pos) |
|---|
| | 1091 | (error "At last image")))) |
|---|
| | 1092 | (when tumme-track-movement |
|---|
| | 1093 | (tumme-track-original-file)) |
|---|
| 1088 | | (defun tumme-backward-char () |
|---|
| 1089 | | "Move to previous image and display properties." |
|---|
| 1090 | | (interactive) |
|---|
| 1091 | | (when (not (bobp)) |
|---|
| 1092 | | (backward-char) |
|---|
| 1093 | | (while (and (not (bobp)) |
|---|
| 1094 | | (not (tumme-image-at-point-p))) |
|---|
| 1095 | | (backward-char)) |
|---|
| 1096 | | (if tumme-track-movement |
|---|
| 1097 | | (tumme-track-original-file))) |
|---|
| | 1096 | (defun tumme-backward-image (&optional arg) |
|---|
| | 1097 | "Move to previous image and display properties. |
|---|
| | 1098 | Optional prefix ARG says how many images to move; default is one |
|---|
| | 1099 | image." |
|---|
| | 1100 | (interactive "p") |
|---|
| | 1101 | (let (pos (steps (or arg 1))) |
|---|
| | 1102 | (dotimes (i steps) |
|---|
| | 1103 | (if (and (not (bobp)) |
|---|
| | 1104 | (save-excursion |
|---|
| | 1105 | (backward-char) |
|---|
| | 1106 | (while (and (not (bobp)) |
|---|
| | 1107 | (not (tumme-image-at-point-p))) |
|---|
| | 1108 | (backward-char)) |
|---|
| | 1109 | (setq pos (point)) |
|---|
| | 1110 | (tumme-image-at-point-p))) |
|---|
| | 1111 | (goto-char pos) |
|---|
| | 1112 | (error "At first image")))) |
|---|
| | 1113 | (when tumme-track-movement |
|---|
| | 1114 | (tumme-track-original-file)) |
|---|
| 2013 | | (defun tumme-write-comment (file comment) |
|---|
| 2014 | | "For FILE, write comment COMMENT in database." |
|---|
| 2015 | | (save-excursion |
|---|
| 2016 | | (let (end buf comment-beg) |
|---|
| 2017 | | (setq buf (find-file tumme-db-file)) |
|---|
| 2018 | | (goto-char (point-min)) |
|---|
| 2019 | | (if (search-forward-regexp |
|---|
| 2020 | | (format "^%s" file) nil t) |
|---|
| 2021 | | (progn |
|---|
| 2022 | | (end-of-line) |
|---|
| 2023 | | (setq end (point)) |
|---|
| 2024 | | (beginning-of-line) |
|---|
| 2025 | | ;; Delete old comment, if any |
|---|
| 2026 | | (cond ((search-forward ";comment:" end t) |
|---|
| 2027 | | (setq comment-beg (match-beginning 0)) |
|---|
| 2028 | | ;; Any tags after the comment? |
|---|
| 2029 | | (if (search-forward ";" end t) |
|---|
| 2030 | | (setq comment-end (- (point) 1)) |
|---|
| 2031 | | (setq comment-end end)) |
|---|
| 2032 | | ;; Delete comment tag and comment |
|---|
| 2033 | | (delete-region comment-beg comment-end))) |
|---|
| 2034 | | ;; Insert new comment |
|---|
| 2035 | | (beginning-of-line) |
|---|
| 2036 | | (if (not (search-forward ";" end t)) |
|---|
| 2037 | | (progn |
|---|
| 2038 | | (end-of-line) |
|---|
| 2039 | | (insert ";"))) |
|---|
| 2040 | | (insert (format "comment:%s;" comment))) |
|---|
| 2041 | | ;; File does not exist in databse - add it. |
|---|
| 2042 | | (goto-char (point-max)) |
|---|
| 2043 | | (insert (format "\n%s;comment:%s" file comment))) |
|---|
| 2044 | | (save-buffer) |
|---|
| 2045 | | (kill-buffer buf)))) |
|---|
| | 2033 | (defun tumme-write-comments (file-comments) |
|---|
| | 2034 | "Write file comments to database. |
|---|
| | 2035 | Write file comments to one or more files. FILE-COMMENTS is an alist on |
|---|
| | 2036 | the following form: |
|---|
| | 2037 | ((FILE . COMMENT) ... )" |
|---|
| | 2038 | (let (end comment-beg-pos comment-end-pos file comment) |
|---|
| | 2039 | (with-temp-file tumme-db-file |
|---|
| | 2040 | (insert-file-contents tumme-db-file) |
|---|
| | 2041 | (dolist (elt file-comments) |
|---|
| | 2042 | (setq file (car elt) |
|---|
| | 2043 | comment (cdr elt)) |
|---|
| | 2044 | (goto-char (point-min)) |
|---|
| | 2045 | (if (search-forward-regexp (format "^%s.*$" file) nil t) |
|---|
| | 2046 | (progn |
|---|
| | 2047 | (setq end (point)) |
|---|
| | 2048 | (beginning-of-line) |
|---|
| | 2049 | ;; Delete old comment, if any |
|---|
| | 2050 | (when (search-forward ";comment:" end t) |
|---|
| | 2051 | (setq comment-beg-pos (match-beginning 0)) |
|---|
| | 2052 | ;; Any tags after the comment? |
|---|
| | 2053 | (if (search-forward ";" end t) |
|---|
| | 2054 | (setq comment-end-pos (- (point) 1)) |
|---|
| | 2055 | (setq comment-end-pos end)) |
|---|
| | 2056 | ;; Delete comment tag and comment |
|---|
| | 2057 | (delete-region comment-beg-pos comment-end-pos)) |
|---|
| | 2058 | ;; Insert new comment |
|---|
| | 2059 | (beginning-of-line) |
|---|
| | 2060 | (unless (search-forward ";" end t) |
|---|
| | 2061 | (end-of-line) |
|---|
| | 2062 | (insert ";")) |
|---|
| | 2063 | (insert (format "comment:%s;" comment))) |
|---|
| | 2064 | ;; File does not exist in database - add it. |
|---|
| | 2065 | (goto-char (point-max)) |
|---|
| | 2066 | (insert (format "\n%s;comment:%s" file comment))))))) |
|---|
| | 2447 | (defvar tumme-widget-list nil |
|---|
| | 2448 | "List to keep track of meta data in edit buffer.") |
|---|
| | 2449 | |
|---|
| | 2450 | ;;;###autoload |
|---|
| | 2451 | (defun tumme-dired-edit-comment-and-tags () |
|---|
| | 2452 | "Edit comment and tags of current or marked image files. |
|---|
| | 2453 | Edit comment and tags for all marked image files in an |
|---|
| | 2454 | easy-to-use form." |
|---|
| | 2455 | (interactive) |
|---|
| | 2456 | (setq tumme-widget-list nil) |
|---|
| | 2457 | ;; Setup buffer. |
|---|
| | 2458 | (let ((files (dired-get-marked-files))) |
|---|
| | 2459 | (switch-to-buffer "*Tumme Edit Meta Data*") |
|---|
| | 2460 | (kill-all-local-variables) |
|---|
| | 2461 | (make-local-variable 'widget-example-repeat) |
|---|
| | 2462 | (let ((inhibit-read-only t)) |
|---|
| | 2463 | (erase-buffer)) |
|---|
| | 2464 | (remove-overlays) |
|---|
| | 2465 | ;; Some help for the user. |
|---|
| | 2466 | (widget-insert |
|---|
| | 2467 | "\nEdit comments and tags for each image. Separate multiple tags |
|---|
| | 2468 | with a comma. Move forward between fields using TAB or RET. |
|---|
| | 2469 | Move to the previous field using backtab (S-TAB). Save by |
|---|
| | 2470 | activating the Save button at the bottom of the form or cancel |
|---|
| | 2471 | the operation by activating the Cancel button.\n\n") |
|---|
| | 2472 | ;; Here comes all images and a comment and tag field for each |
|---|
| | 2473 | ;; image. |
|---|
| | 2474 | (let (thumb-file img comment-widget tag-widget) |
|---|
| | 2475 | |
|---|
| | 2476 | (dolist (file files) |
|---|
| | 2477 | |
|---|
| | 2478 | (setq thumb-file (tumme-thumb-name file) |
|---|
| | 2479 | img (create-image thumb-file)) |
|---|
| | 2480 | |
|---|
| | 2481 | (insert-image img) |
|---|
| | 2482 | (widget-insert "\n\nComment: ") |
|---|
| | 2483 | (setq comment-widget |
|---|
| | 2484 | (widget-create 'editable-field |
|---|
| | 2485 | :size 60 |
|---|
| | 2486 | :format "%v " |
|---|
| | 2487 | :value (or (tumme-get-comment file) ""))) |
|---|
| | 2488 | (widget-insert "\nTags: ") |
|---|
| | 2489 | (setq tag-widget |
|---|
| | 2490 | (widget-create 'editable-field |
|---|
| | 2491 | :size 60 |
|---|
| | 2492 | :format "%v " |
|---|
| | 2493 | :value (or (mapconcat |
|---|
| | 2494 | (lambda (tag) |
|---|
| | 2495 | tag) |
|---|
| | 2496 | (tumme-list-tags file) |
|---|
| | 2497 | ",") ""))) |
|---|
| | 2498 | ;; Save information in all widgets so that we can use it when |
|---|
| | 2499 | ;; the user saves the form. |
|---|
| | 2500 | (setq tumme-widget-list |
|---|
| | 2501 | (append tumme-widget-list |
|---|
| | 2502 | (list (list file comment-widget tag-widget)))) |
|---|
| | 2503 | (widget-insert "\n\n"))) |
|---|
| | 2504 | |
|---|
| | 2505 | ;; Footer with Save and Cancel button. |
|---|
| | 2506 | (widget-insert "\n") |
|---|
| | 2507 | (widget-create 'push-button |
|---|
| | 2508 | :notify |
|---|
| | 2509 | (lambda (&rest ignore) |
|---|
| | 2510 | (tumme-save-information-from-widgets) |
|---|
| | 2511 | (bury-buffer) |
|---|
| | 2512 | (message "Done.")) |
|---|
| | 2513 | "Save") |
|---|
| | 2514 | (widget-insert " ") |
|---|
| | 2515 | (widget-create 'push-button |
|---|
| | 2516 | :notify |
|---|
| | 2517 | (lambda (&rest ignore) |
|---|
| | 2518 | (bury-buffer) |
|---|
| | 2519 | (message "Operation canceled.")) |
|---|
| | 2520 | "Cancel") |
|---|
| | 2521 | (widget-insert "\n") |
|---|
| | 2522 | (use-local-map widget-keymap) |
|---|
| | 2523 | (widget-setup) |
|---|
| | 2524 | ;; Jump to the first widget. |
|---|
| | 2525 | (widget-forward 1))) |
|---|
| | 2526 | |
|---|
| | 2527 | (defun tumme-save-information-from-widgets () |
|---|
| | 2528 | "Save information found in `tumme-widget-list'. |
|---|
| | 2529 | Use the information in `tumme-widget-list' to save comments and |
|---|
| | 2530 | tags to their respective image file. Internal function used by |
|---|
| | 2531 | `tumme-dired-edit-comment-and-tags'." |
|---|
| | 2532 | (let (file comment tag-string tag-list lst) |
|---|
| | 2533 | (tumme-write-comments |
|---|
| | 2534 | (mapcar |
|---|
| | 2535 | (lambda (widget) |
|---|
| | 2536 | (setq file (car widget) |
|---|
| | 2537 | comment (widget-value (cadr widget))) |
|---|
| | 2538 | (cons file comment)) |
|---|
| | 2539 | tumme-widget-list)) |
|---|
| | 2540 | (tumme-write-tags |
|---|
| | 2541 | (dolist (widget tumme-widget-list lst) |
|---|
| | 2542 | (setq file (car widget) |
|---|
| | 2543 | tag-string (widget-value (car (cddr widget))) |
|---|
| | 2544 | tag-list (split-string tag-string ",")) |
|---|
| | 2545 | (dolist (tag tag-list) |
|---|
| | 2546 | (push (cons file tag) lst)))))) |
|---|
| | 2547 | |
|---|