f.meta.cc (fotoxx-23.0) | : | f.meta.cc (fotoxx-23.1) |
---|---|---|
/******************************************************************************* * | /******************************************************************************* * | |
Fotoxx edit photos and manage collections | Fotoxx - edit photos and manage collections | |
Copyright 2007-2023 Michael Cornelison | Copyright 2007-2023 Michael Cornelison | |
source code URL: https://kornelix.net | source code URL: https://kornelix.net | |
contact: mkornelix@gmail.com | contact: mkornelix@gmail.com | |
This program is free software: you can redistribute it and/or modify | This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. See https://www.gnu.org/licenses | (at your option) any later version. See https://www.gnu.org/licenses | |
skipping to change at line 70 | skipping to change at line 70 | |
del_tag remove tag from a tag list | del_tag remove tag from a tag list | |
add_recentag add tag to recent tags list, remove oldest if need ed | add_recentag add tag to recent tags list, remove oldest if need ed | |
load_deftags load defined tags list from tags file and image in dex | load_deftags load defined tags list from tags file and image in dex | |
save_deftags save defined tags list to tags file | save_deftags save defined tags list to tags file | |
find_deftag check if given tag is in defined tags list | find_deftag check if given tag is in defined tags list | |
add_deftag add new tag to defined tags list or change categor y | add_deftag add new tag to defined tags list or change categor y | |
del_deftag remove tag from defined tags list | del_deftag remove tag from defined tags list | |
del_defcatg remove category from defined tags list (if no tags assigned) | del_defcatg remove category from defined tags list (if no tags assigned) | |
deftags_stuff stuff defined tags into dialog text widget | deftags_stuff stuff defined tags into dialog text widget | |
defcats_stuff stuff defined categories into dialog combobox widg et | defcats_stuff stuff defined categories into dialog combobox widg et | |
tag_orphans report tags defined and not used in any image file | ||
load_filemeta load image file metadata into memory (indexed data only) | load_filemeta load image file metadata into memory (indexed data only) | |
save_filemeta save metadata to image file EXIF and to image inde x | save_filemeta save metadata to image file metadata and to image index | |
update_image_index update index data for current image file | update_image_index update index data for current image file | |
delete_image_index delete index record for deleted image file | delete_image_index delete index record for deleted image file | |
load_Iglocs load geocoordinates table from image files | load_Iglocs load geocoordinates table from image files | |
load_Cglocs load geocoordinates table from world cities file | load_Cglocs load geocoordinates table from world cities file | |
find_Iglocs find geocoordinates using image data | find_Iglocs find geocoordinates using image data | |
find_Cglocs find geocoordinates using cities table | find_Cglocs find geocoordinates using cities table | |
put_geolocs put new location data in geolocations table | put_geolocs put new location data in geolocations table | |
validate_latlong validate earth coordinates data | validate_latlong validate earth coordinates data | |
skipping to change at line 110 | skipping to change at line 109 | |
------------------------ | ------------------------ | |
m_load_netmap initialize net map | m_load_netmap initialize net map | |
netmap_paint_dots paint red dots on map where images are located | netmap_paint_dots paint red dots on map where images are located | |
m_netmap_zoomin zoom net map in on image location | m_netmap_zoomin zoom net map in on image location | |
netmap_zoomto callable with input zoom level | netmap_zoomto callable with input zoom level | |
netmapscale get net map scale at zoom level | netmapscale get net map scale at zoom level | |
netmap_mousefunc respond to clicks on net map | netmap_mousefunc respond to clicks on net map | |
find_netmap_images find images | find_netmap_images find images | |
m_netmap_locs save and recall net map locs (center, zoom level) | m_netmap_locs save and recall net map locs (center, zoom level) | |
EXIF store and retrieve | metadata store and retrieve | |
----------------------- | --------------------------- | |
exif_get get image metadata from list of keys | meta_get1 get image file metadata from list of keys | |
exif_put update image metadata from list of keys and data | meta_getN same for multiple files, using multiple threads | |
exif_copy copy metadata from file to file, with revisions | meta_put update image metadata from list of keys and data | |
exif_server start exiftool server process, send data requests | meta_copy copy metadata from file to file, with revisions | |
exif_tagdate yyyy-mm-dd hh:mm:ss to yyyymmddhhmmss | meta_tagdate yyyy-mm-dd hh:mm:ss to yyyymmddhhmmss | |
tag_exifdate yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss | tag_metadate yyyymmddhhmmss to yyyy-mm-dd hh:mm:ss | |
Image index functions | Image index functions | |
--------------------- | --------------------- | |
get_xxrec get image index record for image file | get_xxrec get image index record for image file | |
put_xxrec add or update index record for an image file | put_xxrec add or update index record for an image file | |
read_xxrec_seq read all index records sequentially, one per call | read_xxrec_seq read all index records sequentially, one per call | |
write_xxrec_seq write all index records sequentially | write_xxrec_seq write all index records sequentially | |
******************************************************************************** */ | ******************************************************************************** */ | |
#define EX extern // enable extern declarations | #define EX extern // enable extern declarations | |
#include "fotoxx.h" // (variables in fotoxx.h are refs) | #include "fotoxx.h" // (variables in fotoxx.h are refs) | |
/******************************************************************************* */ | /******************************************************************************* */ | |
char *pdate_metadate(cchar *pdate); | ch *pdate_metadate(ch *pdate); | |
// "yyyy-mm-dd" to "yyyymmdd" | // "yyyy-mm-dd" to "yyyymmdd" | |
char *ptime_metatime(cchar *ptime); | ch *ptime_metatime(ch *ptime); | |
// "hh:mm[:ss]" to "hhmmss" | // "hh:mm[:ss]" to "hhmmss" | |
char *pdatetime_metadatetime(cchar *pdatetime); | ch *pdatetime_metadatetime(ch *pdatetime); | |
// yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss | // yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss | |
int add_tag(char *tag, char *taglist, int maxcc); | int add_tag(ch *tag, ch *taglist, int maxcc); | |
// add tag if unique and enough space | // add tag if unique and enough space | |
int del_tag(char *tag, char *taglist); | int del_tag(ch *tag, ch *taglist); | |
// remove tag from tag list | // remove tag from tag list | |
int add_recentag(char *tag); | int add_recentag(ch *tag); | |
// add tag to recent tags, keep recent | // add tag to recent tags, keep recent | |
void load_deftags(int force); // load tags_deftags from index data | void load_deftags(int force); // load tags_deftags from index data | |
void save_deftags(); // tags_deftags[] >> defined_tags file | void save_deftags(); // tags_deftags[] >> defined_tags file | |
int find_deftag(char *tag); | int find_deftag(ch *tag); | |
// find tag in tags_deftags[] | // find tag in tags_deftags[] | |
int add_deftag(char *catg, char *tag); | int add_deftag(ch *catg, ch *tag); | |
// add tag to tags_deftags[] | // add tag to tags_deftags[] | |
int del_deftag(char *tag); | int del_deftag(ch *tag); | |
// remove tag from tags_deftags[] | // remove tag from tags_deftags[] | |
int del_defcatg(char *catg); | int del_defcatg(ch *catg); | |
// remove category from tags_deftags[] | // remove category from tags_deftags[] | |
void deftags_stuff(zdialog *zd, cchar *catg); | void deftags_stuff(zdialog *zd, ch *catg); | |
// tags_deftags[] >> zd widget deftags | // tags_deftags[] >> zd widget deftags | |
void defcats_stuff(zdialog *zd); // defined categories >> " widget defcats | void defcats_stuff(zdialog *zd); // defined categories >> " widget defcats | |
int load_Iglocs(); // load image geocoordinates table | int load_Iglocs(); // load image geocoordinates table | |
int load_Cglocs(); // load cities geocoordinates table | int load_Cglocs(); // load cities geocoordinates table | |
int glocs_compare(cchar *rec1, cchar *rec2); // compare geocoordinate records | int glocs_compare(ch *rec1, ch *rec2); // compare geocoordinate records | |
int find_Iglocs(zdialog *zd); // find geocoordinates using image data | int find_Iglocs(zdialog *zd); // find geocoordinates using image data | |
int find_Cglocs(zdialog *zd); // find geocoordinates using cities table | int find_Cglocs(zdialog *zd); // find geocoordinates using cities table | |
int put_geolocs(zdialog *zd); // Update geolocations table in memory | int put_geolocs(zdialog *zd); // Update geolocations table in memory | |
int validate_latlong(char *lati, char *longi, float &flat, float &flong); // convert and validate earth coordinates | int validate_latlong(ch *lati, ch *longi, float &flat, float &flong); // convert and validate earth coordinates | |
float earth_distance(float lat1, float long1, float lat2, float long2); // compute distance from earth coordinates | float earth_distance(float lat1, float long1, float lat2, float long2); // compute distance from earth coordinates | |
int get_gallerymap(); // get map coordinates for gallery files | int get_gallerymap(); // get map coordinates for gallery files | |
void netmap_zoomto(float flati, float flongi, int zoomlev); // zoom net map to location and zoom level | void netmap_zoomto(float flati, float flongi, int zoomlev); // zoom net map to location and zoom level | |
float netmapscale(int zoomlev, float flat, float flong); // net map scale at given zoom and location | float netmapscale(int zoomlev, float flat, float flong); // net map scale at given zoom and location | |
namespace meta_names | namespace meta_names | |
{ | { | |
char meta_pdate[16]; | ch meta_pdate[16]; | |
// image (photo) date, yyyymmddhhmmss | // image (photo) date, yyyymmddhhmmss | |
char meta_rating[4]; | ch meta_rating[4]; | |
// image rating in stars, "0" to "5" | // image rating in stars, "0" to "5" | |
char meta_wwhh[16]; | ch meta_wwhh[16]; | |
// image width/height, "2345x1234" | // image width/height, "2345x1234" | |
char meta_tags[tagFcc]; | ch meta_tags[filetagsXcc]; | |
// tags for current image file | // tags for current image file | |
char meta_title[exif_maxcc]; | ch meta_title[metadataXcc]; | |
// image title | // image title | |
char meta_description[exif_maxcc]; | ch meta_description[metadataXcc]; | |
// image description | // image description | |
char meta_location[100], meta_country[100]; | ch meta_location[100], meta_country[100]; | |
// geolocs: location, country | // geolocs: location, country | |
char meta_lati[20], meta_longi[20]; | ch meta_lati[20], meta_longi[20]; | |
// geolocs: earth coordinates (-123.4567) | // geolocs: earth coordinates (-123.4567) | |
char p_meta_pdate[16]; | ch p_meta_pdate[16]; | |
// previous file metadata | // previous file metadata | |
char p_meta_rating[4]; | ch p_meta_rating[4]; | |
char p_meta_tags[tagFcc]; | ch p_meta_tags[filetagsXcc]; | |
char p_meta_title[exif_maxcc]; | ch p_meta_title[metadataXcc]; | |
char p_meta_description[exif_maxcc]; | ch p_meta_description[metadataXcc]; | |
char p_meta_location[100], p_meta_country[100]; | ch p_meta_location[100], p_meta_country[100]; | |
// exif "City" is Fotoxx "Location" | // metadata "City" is Fotoxx "Location" | |
char p_meta_lati[20], p_meta_longi[20]; | ch p_meta_lati[20], p_meta_longi[20]; | |
char *xmeta_data[MXmax]; // indexed metadata (xmeta_key[]) | ch *xmeta_data[xmetamaxkeys]; // indexed metadata (xmeta_key[]) | |
char *tags_deftags[maxtagcats]; | ch *tags_deftags[maxtagcats]; | |
// defined tags: catg: tag1, ... tagN, | // defined tags: catg: tag1, ... tagN, | |
char tags_recentags[tagRcc] = ""; | ch tags_recentags[recenttagsXcc] = ""; | |
// recently added tags list | // recently added tags list | |
zdialog *zd_mapgeotags = 0; // zdialog wanting geotags via map click | zdialog *zd_mapgeotags = 0; // zdialog wanting geotags via map click | |
struct glocs_t { // geolocations table, memory DB | struct glocs_t { // geolocations table, memory DB | |
char *location, *country; // maps locations <-> earth coordinates | ch *location, *country; // maps locations <-> earth coordinates | |
float flati, flongi; // " float, 7 digit precision | float flati, flongi; // " float, 7 digit precision | |
}; | }; | |
glocs_t **Iglocs = 0; // image geolocations table | glocs_t **Iglocs = 0; // image geolocations table | |
int NIglocs = 0; // size of geolocations table | int NIglocs = 0; // size of geolocations table | |
glocs_t **Cglocs = 0; // city geolocations table | glocs_t **Cglocs = 0; // city geolocations table | |
int NCglocs = 0; // size of geolocations table | int NCglocs = 0; // size of geolocations table | |
struct gallerymap_t { // geocoordinates for gallery files | struct gallerymap_t { // geocoordinates for gallery files | |
char *file; | ch *file; | |
float flati, flongi; | float flati, flongi; | |
}; | }; | |
gallerymap_t *gallerymap = 0; | gallerymap_t *gallerymap = 0; | |
int Ngallerymap = 0; | int Ngallerymap = 0; | |
} | } | |
using namespace meta_names; | using namespace meta_names; | |
/******************************************************************************* */ | /******************************************************************************* */ | |
skipping to change at line 221 | skipping to change at line 220 | |
// Dialog to select metadata items (for index, view, report). | // Dialog to select metadata items (for index, view, report). | |
// Input list is replaced by user-edited list. | // Input list is replaced by user-edited list. | |
// exclude: exclude items indexed by default. | // exclude: exclude items indexed by default. | |
// returns 0/1 = no changes / changes made | // returns 0/1 = no changes / changes made | |
namespace select_meta_keys_names | namespace select_meta_keys_names | |
{ | { | |
GtkWidget *mtext1, *mtext2; | GtkWidget *mtext1, *mtext2; | |
int Fexclude, Fchange; | int Fexclude, Fchange; | |
zdialog *zd; | zdialog *zd; | |
char *pp; | ch *pp; | |
cchar *excludes = | ch *excludes = | |
iptc_keywords_key "-" iptc_rating_key "-" exif_date_key "-" | meta_tags_key "-" meta_rating_key "-" meta_date_key "-" | |
iptc_title_key "-" iptc_description_key "-" | meta_title_key "-" meta_description_key "-" | |
exif_location_key "-" exif_country_key "-" | meta_location_key "-" meta_country_key "-" | |
exif_lati_key "-" exif_longi_key ; | meta_lati_key "-" meta_longi_key ; | |
} | } | |
int select_meta_keys(zlist_t *mlist, int maxout, int exclude) | int select_meta_keys(zlist_t *mlist, int maxout, int exclude) | |
{ | { | |
using namespace select_meta_keys_names; | using namespace select_meta_keys_names; | |
void select_meta_keys_clickfunc1(GtkWidget *, int line, int pos, int kbkey); | void select_meta_keys_clickfunc1(GtkWidget *, int line, int pos, int kbkey); | |
void select_meta_keys_clickfunc2(GtkWidget *, int line, int pos, int kbkey); | void select_meta_keys_clickfunc2(GtkWidget *, int line, int pos, int kbkey); | |
int zstat, ii, nn; | int zstat, ii, nn; | |
char *pp, ppc[80]; | ch *pp, ppc[80]; | |
zlist_t *picklist; | zlist_t *picklist; | |
Fexclude = exclude; | Fexclude = exclude; | |
Fchange = 0; | Fchange = 0; | |
/*** | /*** | |
__________________________________________________________ | __________________________________________________________ | |
| Select Metadata Items | | | Select Metadata Items | | |
| | | | | | |
| click to select click to unselect | | | click to select click to unselect | | |
skipping to change at line 344 | skipping to change at line 343 | |
zdialog_free(zd); | zdialog_free(zd); | |
return 1; // return "changes made" | return 1; // return "changes made" | |
} | } | |
// get clicked tag name from input list and insert into output list | // get clicked tag name from input list and insert into output list | |
void select_meta_keys_clickfunc1(GtkWidget *widget, int line, int pos, int kbkey ) | void select_meta_keys_clickfunc1(GtkWidget *widget, int line, int pos, int kbkey ) | |
{ | { | |
using namespace select_meta_keys_names; | using namespace select_meta_keys_names; | |
char *pp, ppc[80]; | ch *pp, ppc[80]; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
pp = textwidget_line(widget,line,1); // get clicked line, highlight | pp = textwidget_line(widget,line,1); // get clicked line, highlight | |
if (! pp || ! *pp) return; | if (! pp || ! *pp) return; | |
textwidget_highlight_line(widget,line); | textwidget_highlight_line(widget,line); | |
skipping to change at line 381 | skipping to change at line 380 | |
Fchange = 1; | Fchange = 1; | |
return; | return; | |
} | } | |
// get clicked tag name from output list and remove it | // get clicked tag name from output list and remove it | |
void select_meta_keys_clickfunc2(GtkWidget *widget, int line, int pos, int kbkey ) | void select_meta_keys_clickfunc2(GtkWidget *widget, int line, int pos, int kbkey ) | |
{ | { | |
using namespace select_meta_keys_names; | using namespace select_meta_keys_names; | |
char *pp; | ch *pp; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
pp = textwidget_line(widget,line,1); // get clicked line | pp = textwidget_line(widget,line,1); // get clicked line | |
if (! pp || ! *pp) return; | if (! pp || ! *pp) return; | |
textwidget_delete(widget,line); // delete line | textwidget_delete(widget,line); // delete line | |
Fchange = 1; | Fchange = 1; | |
return; | return; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// menu function and popup dialog to show EXIF/IPTC data | // menu function and popup dialog to show metadata | |
// window is updated when navigating to another image | // window is updated when navigating to another image | |
int metadata_report_type = 1; | int metadata_report_type = 1; | |
// called by f_open() if zd_metaview is defined | // called by f_open() if zd_metaview is defined | |
void meta_view(int type) | void meta_view(int type) | |
{ | { | |
if (type) metadata_report_type = type; | if (type) metadata_report_type = type; | |
if (metadata_report_type == 2) | if (metadata_report_type == 2) | |
m_meta_view_long(0,0); | m_meta_view_long(0,0); | |
else | else | |
m_meta_view_short(0,0); | m_meta_view_short(0,0); | |
return; | return; | |
} | } | |
// menu function - metadata short report | // menu function - metadata short report | |
void m_meta_view_short(GtkWidget *, cchar *menu) | void m_meta_view_short(GtkWidget *, ch *menu) | |
{ | { | |
int meta_view_dialog_event(zdialog *zd, cchar *event); | int meta_view_dialog_event(zdialog *zd, ch *event); | |
#define vNK 20 | #define vNK 20 | |
char *kval[vNK]; | ch *kname[vNK] = { | |
cchar *kname[vNK] = | "ImageSize", "FileSize", | |
{ "ImageSize", "FileSize", | meta_date_key, "FileModifyDate", | |
exif_date_key, "FileModifyDate", | ||
"Make", "Model", | "Make", "Model", | |
exif_focal_length_35_key, exif_focal_length_key, | meta_focal_length_35_key, meta_focal_length_key, | |
"ExposureTime", "FNumber", "ISO", | "ExposureTime", "FNumber", "ISO", | |
exif_location_key, exif_country_key, | meta_location_key, meta_country_key, | |
exif_lati_key, exif_longi_key, | meta_lati_key, meta_longi_key, | |
iptc_keywords_key, iptc_rating_key, | meta_tags_key, meta_rating_key, | |
iptc_title_key, iptc_description_key, | meta_title_key, meta_description_key, | |
exif_edithist_key | meta_edithist_key }; | |
}; | ||
ch *kval[vNK]; | ||
char *focallength, chsec[12]; | ||
char *text1, **text2; | ch *focallength, chsec[12]; | |
static char *file, *filen; | ch *text1, **text2; | |
static ch *file, *filen; | ||
float fsecs; | float fsecs; | |
int err, ii, nn, cc; | int err, ii, nn, cc; | |
cchar *editdelims = "|"; | ch *editdelims = "|"; | |
GtkWidget *widget; | GtkWidget *widget; | |
FILE *fid; | FILE *fid; | |
int nkx = 0; | int nkx = 0; | |
char *knamex[MVmax]; | ch *knamex[maxviewkeys]; | |
char *kvalx[MVmax]; | ch *kvalx[maxviewkeys]; | |
// extra metadata items | // extra metadata items | |
char buff[100], *pp; | ch buff[metakeyXcc], *pp; | |
F1_help_topic = "view meta"; | F1_help_topic = "view meta"; | |
Plog(1,"m_meta_view_short \n"); | Plog(1,"m_meta_view_short \n"); | |
if (FGWM != 'F' && FGWM != 'G') return; | if (FGWM != 'F' && FGWM != 'G') return; | |
if (clicked_file) { // use clicked file if present | if (clicked_file) { // use clicked file if present | |
file = clicked_file; | file = clicked_file; | |
clicked_file = 0; | clicked_file = 0; | |
} | } | |
else if (curr_file) file = zstrdup(curr_file,"meta-view"); | else if (curr_file) file = zstrdup(curr_file,"meta-view"); | |
else return; | else return; | |
// if (FGWM == 'G') gallery(0,"paint",-1); | ||
// if gallery view, repaint remove 22.50 | ||
if (metadata_report_type != 1) { | if (metadata_report_type != 1) { | |
if (zd_metaview) zdialog_free(zd_metaview); | if (zd_metaview) zdialog_free(zd_metaview); | |
zd_metaview = 0; | zd_metaview = 0; | |
metadata_report_type = 1; | metadata_report_type = 1; | |
} | } | |
if (zd_metaview) zdialog_free(zd_metaview); | if (! zd_metaview) | |
zd_metaview = zdialog_new("View Metadata",Mwin,"Extras","Cancel",null); | // create if not already 23.1 | |
zdialog_add_widget(zd_metaview,"scrwin","scroll","dialog",0,"expand"); | { | |
zdialog_add_widget(zd_metaview,"text","exifdata","scroll",0,"expand"); | zd_metaview = zdialog_new("View Metadata",Mwin,"Extras","Cancel",null); | |
zdialog_resize(zd_metaview,550,350); | zdialog_add_widget(zd_metaview,"scrwin","scroll","dialog",0,"expand"); | |
zdialog_run(zd_metaview,meta_view_dialog_event,"save"); | zdialog_add_widget(zd_metaview,"text","metadata","scroll",0,"expand"); | |
zdialog_resize(zd_metaview,550,350); | ||
zdialog_run(zd_metaview,meta_view_dialog_event,"save"); | ||
} | ||
widget = zdialog_gtkwidget(zd_metaview,"exifdata"); | widget = zdialog_gtkwidget(zd_metaview,"metadata"); // clear prior report | |
textwidget_clear(widget); | textwidget_clear(widget); | |
err = exif_get(file,kname,kval,vNK); | err = meta_get1(file,(ch **) kname,kval,vNK); | |
if (err) { | if (err) return; | |
zmessageACK(Mwin,"exif failure"); | ||
return; | ||
} | ||
filen = strrchr(file,'/'); // get file name without folder | filen = strrchr(file,'/'); // get file name without folder | |
if (filen) filen++; | if (filen) filen++; | |
else filen = file; | else filen = file; | |
if (kval[2] && strlen(kval[2]) > 19) kval[2][19] = 0; // truncate dates to yyyy-mm-dd hh:mm:ss | if (kval[2] && strlen(kval[2]) > 19) kval[2][19] = 0; // truncate dates to yyyy-mm-dd hh:mm:ss | |
if (kval[3] && strlen(kval[3]) > 19) kval[3][19] = 0; | if (kval[3] && strlen(kval[3]) > 19) kval[3][19] = 0; | |
textwidget_append(widget,0,"File %s \n",filen); | textwidget_append(widget,0,"File %s \n",filen); | |
textwidget_append(widget,0,"Size %s %s \n",kval[0],kval[1]); | textwidget_append(widget,0,"Size %s %s \n",kval[0],kval[1]); | |
skipping to change at line 515 | skipping to change at line 511 | |
if (kval[6]) focallength = kval[6]; // focal length, 35mm equivalent | if (kval[6]) focallength = kval[6]; // focal length, 35mm equivalent | |
else if (kval[7]) focallength = kval[7]; // focal length, actual mm | else if (kval[7]) focallength = kval[7]; // focal length, actual mm | |
else focallength = 0; // missing | else focallength = 0; // missing | |
strcpy(chsec,"null"); | strcpy(chsec,"null"); | |
if (kval[8]) { | if (kval[8]) { | |
fsecs = atofz(kval[8]); // convert 0.008 seconds to 1/125 etc. | fsecs = atofz(kval[8]); // convert 0.008 seconds to 1/125 etc. | |
if (fsecs > 0 && fsecs <= 0.5) { | if (fsecs > 0 && fsecs <= 0.5) { | |
fsecs = 1/fsecs; | fsecs = 1/fsecs; | |
snprintf(chsec,12,"1/%.0f",fsecs); | snprintf(chsec,12,"1/%.0f",fsecs); | |
} | } | |
else if (fsecs > 0) | else if (fsecs > 0.5 && fsecs < 2) | |
snprintf(chsec,12,"%.0f",fsecs); | // 23/1 | |
snprintf(chsec,12,"%.1f",fsecs); | ||
else snprintf(chsec,12,"%.0f",fsecs); | ||
} | } | |
textwidget_append(widget,0,"Exposure %s %s sec F%s ISO %s \n", | textwidget_append(widget,0,"Exposure %s mm %s sec F%s ISO %s \n", | |
focallength,kval[8],kval[9],kval[10]); | focallength,chsec,kval[9],kval[10]); | |
} | } | |
if (kval[11] || kval[12] || kval[13] || kval[14]) // geotag data | if (kval[11] || kval[12] || kval[13] || kval[14]) // geotag data | |
textwidget_append(widget,0,"Location %s %s %s %s \n", | textwidget_append(widget,0,"Location %s %s %s %s \n", | |
kval[11],kval[12],kval[13],kval[14]); | kval[11],kval[12],kval[13],kval[14]); | |
if (kval[15]) { // tags | if (kval[15]) { // tags | |
cc = strlen(kval[15]) - 1; | cc = strlen(kval[15]) - 1; | |
if (kval[15][cc] == ',') kval[15][cc] = 0; // 23.0 | if (kval[15][cc] == ',') kval[15][cc] = 0; // 23.0 | |
textwidget_append(widget,0,"keywords %s \n",kval[15]); | textwidget_append(widget,0,"tags %s \n",kval[15]); | |
} | } | |
if (kval[16]) // rating | if (kval[16]) // rating | |
textwidget_append(widget,0,"Rating %s \n",kval[16]); | textwidget_append(widget,0,"Rating %s \n",kval[16]); | |
if (kval[17]) { // title | if (kval[17]) { // title | |
nn = breakup_text(kval[17],text2,0,60,80); | nn = breakup_text(kval[17],text2,0,60,80); | |
textwidget_append(widget,0,"Title %s \n",text2[0]); | textwidget_append(widget,0,"Title %s \n",text2[0]); | |
for (ii = 1; ii < nn; ii++) | for (ii = 1; ii < nn; ii++) | |
textwidget_append(widget,0," %s \n",text2[ii]); | textwidget_append(widget,0," %s \n",text2[ii]); | |
skipping to change at line 557 | skipping to change at line 554 | |
textwidget_append(widget,0,"Description %s \n",text2[0]); | textwidget_append(widget,0,"Description %s \n",text2[0]); | |
for (ii = 1; ii < nn; ii++) | for (ii = 1; ii < nn; ii++) | |
textwidget_append(widget,0," %s \n",text2[ii]); | textwidget_append(widget,0," %s \n",text2[ii]); | |
for (ii = 0; ii < nn; ii++) | for (ii = 0; ii < nn; ii++) | |
zfree(text2[ii]); | zfree(text2[ii]); | |
zfree(text2); | zfree(text2); | |
} | } | |
if (kval[19]) { // edit history log | if (kval[19]) { // edit history log | |
cc = strlen(kval[19]) + 100; | cc = strlen(kval[19]) + 100; | |
text1 = (char *) zmalloc(cc,"meta-view"); | text1 = (ch *) zmalloc(cc,"meta-view"); | |
repl_1str(kval[19],text1,"|","\n"); | repl_1str(kval[19],text1,"|","\n"); | |
nn = breakup_text(text1,text2,editdelims,60,80); | nn = breakup_text(text1,text2,editdelims,60,80); | |
textwidget_append(widget,0,"Edits %s \n",text2[0]); | textwidget_append(widget,0,"Edits %s \n",text2[0]); | |
for (ii = 1; ii < nn; ii++) | for (ii = 1; ii < nn; ii++) | |
textwidget_append(widget,0," %s \n",text2[ii]); | textwidget_append(widget,0," %s \n",text2[ii]); | |
for (ii = 0; ii < nn; ii++) | for (ii = 0; ii < nn; ii++) | |
zfree(text2[ii]); | zfree(text2[ii]); | |
zfree(text2); | zfree(text2); | |
zfree(text1); | zfree(text1); | |
} | } | |
for (ii = 0; ii < vNK; ii++) // free memory | for (ii = 0; ii < vNK; ii++) // free memory | |
if (kval[ii]) zfree(kval[ii]); | if (kval[ii]) zfree(kval[ii]); | |
// append extra report items if any | // append extra report items if any | |
fid = fopen(meta_view_extras_file,"r"); | fid = fopen(meta_view_extras_file,"r"); | |
if (! fid) goto finished; // no extras file | if (! fid) goto finished; // no extras file | |
for (nkx = 0; nkx < MVmax; nkx++) { | for (nkx = 0; nkx < maxviewkeys; nkx++) { | |
// get list of user extras | // get list of user extras | |
pp = fgets_trim(buff,100,fid,1); | pp = fgets_trim(buff,metakeyXcc,fid,1); | |
if (! pp) break; | if (! pp) break; | |
strCompress(pp); | strCompress(pp); | |
if (*pp <= ' ') { nkx--; continue; } | if (*pp <= ' ') { nkx--; continue; } | |
knamex[nkx] = zstrdup(pp,"meta-view"); | knamex[nkx] = zstrdup(pp,"meta-view"); | |
} | } | |
fclose(fid); | fclose(fid); | |
if (nkx == 0) goto finished; // empty file | if (nkx == 0) goto finished; // empty file | |
err = exif_get(file,(cchar **) knamex,kvalx,nkx); | err = meta_get1(file,knamex,kvalx,nkx); | |
// get all items at once | // get all items at once | |
if (err) { | if (err) goto finished; | |
zmessageACK(Mwin,"exif failure"); | ||
goto finished; | ||
} | ||
textwidget_append(widget,0,"\n"); // blank line | textwidget_append(widget,0,"\n"); // blank line | |
for (ii = 0; ii < nkx; ii++) // report user extra items | for (ii = 0; ii < nkx; ii++) // report user extra items | |
if (kvalx[ii]) | if (kvalx[ii]) | |
textwidget_append(widget,0,"%-24s : %s \n",knamex[ii],kvalx[ii]); | textwidget_append(widget,0,"%s : %s \n",knamex[ii],kvalx[ii]); | |
for (ii = 0; ii < nkx; ii++) { // free memory | for (ii = 0; ii < nkx; ii++) { // free memory | |
zfree(knamex[ii]); | zfree(knamex[ii]); | |
if (kvalx[ii]) zfree(kvalx[ii]); | if (kvalx[ii]) zfree(kvalx[ii]); | |
} | } | |
finished: | finished: | |
zfree(file); | zfree(file); | |
return; | return; | |
} | } | |
// menu function - metadata long report | // menu function - metadata long report | |
void m_meta_view_long(GtkWidget *, cchar *menu) | void m_meta_view_long(GtkWidget *, ch *menu) | |
{ | { | |
int meta_view_dialog_event(zdialog *zd, cchar *event); | int meta_view_dialog_event(zdialog *zd, ch *event); | |
FILE *fid; | FILE *fid; | |
char *file, *pp, buff[1000]; | ch *file, *pp, buff[1000]; | |
GtkWidget *widget; | GtkWidget *widget; | |
cchar *tooloptions = "-m -S -c \"%+.5f\" -d \"%Y-%m-%d %H:%M:%S\""; // 22.50 | ch *tooloptions = "-m -S -n -c \"%+.5f\" -d \"%Y-%m-%d %H:%M:%S\" "; // 22.50 | |
F1_help_topic = "view meta"; | F1_help_topic = "view meta"; | |
Plog(1,"m_meta_view_long \n"); | Plog(1,"m_meta_view_long \n"); | |
if (FGWM != 'F' && FGWM != 'G') return; | if (FGWM != 'F' && FGWM != 'G') return; | |
if (clicked_file) { // use clicked file if present | if (clicked_file) { // use clicked file if present | |
file = clicked_file; | file = clicked_file; | |
clicked_file = 0; | clicked_file = 0; | |
skipping to change at line 643 | skipping to change at line 637 | |
if (metadata_report_type != 2) { | if (metadata_report_type != 2) { | |
if (zd_metaview) zdialog_free(zd_metaview); | if (zd_metaview) zdialog_free(zd_metaview); | |
zd_metaview = 0; | zd_metaview = 0; | |
metadata_report_type = 2; | metadata_report_type = 2; | |
} | } | |
if (zd_metaview) zdialog_free(zd_metaview); | if (zd_metaview) zdialog_free(zd_metaview); | |
zd_metaview = zdialog_new("View All Metadata",Mwin,"OK",null); | zd_metaview = zdialog_new("View All Metadata",Mwin,"OK",null); | |
zdialog_add_widget(zd_metaview,"scrwin","scroll","dialog",0,"expand"); | zdialog_add_widget(zd_metaview,"scrwin","scroll","dialog",0,"expand"); | |
zdialog_add_widget(zd_metaview,"text","exifdata","scroll",0,"expand|wrap"); | zdialog_add_widget(zd_metaview,"text","metadata","scroll",0,"expand|wrap"); | |
zdialog_resize(zd_metaview,700,700); | zdialog_resize(zd_metaview,700,700); | |
zdialog_run(zd_metaview,meta_view_dialog_event,"save"); | zdialog_run(zd_metaview,meta_view_dialog_event,"save"); | |
widget = zdialog_gtkwidget(zd_metaview,"exifdata"); | widget = zdialog_gtkwidget(zd_metaview,"metadata"); | |
gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),0); // disable widget editing | gtk_text_view_set_editable(GTK_TEXT_VIEW(widget),0); // disable widget editing | |
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); // disable text wrap | gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget),GTK_WRAP_NONE); // disable text wrap | |
textwidget_clear(widget); | textwidget_clear(widget); | |
snprintf(command,CCC,"exiftool %s \"%s\" ",tooloptions,file); // exiftool command | snprintf(command,CCC,"exiftool %s \"%s\" ",tooloptions,file); // exiftool command | |
fid = popen(command,"r"); // get command outputs | fid = popen(command,"r"); // get command outputs | |
if (fid) { | if (fid) { | |
while ((pp = fgets_trim(buff,1000,fid))) | while ((pp = fgets_trim(buff,1000,fid))) | |
textwidget_append(widget,0,"%s\n",pp); // add to report window | textwidget_append(widget,0,"%s\n",pp); // add to report window | |
pclose(fid); | pclose(fid); | |
} | } | |
zfree(file); | zfree(file); | |
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int meta_view_dialog_event(zdialog *zd, cchar *event) | int meta_view_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
zlist_t *mlist; | zlist_t *mlist; | |
int zstat, nn; | int zstat, nn; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
zstat = zd->zstat; | zstat = zd->zstat; | |
if (! zstat) return 1; // wait for completion | if (! zstat) return 1; // wait for completion | |
zdialog_free(zd); // kill dialog | zdialog_free(zd); // kill dialog | |
zd_metaview = 0; | zd_metaview = 0; | |
if (metadata_report_type != 1) return 1; // not short report | if (metadata_report_type != 1) return 1; // not short report | |
if (zstat != 1) return 1; // not [extras] button | if (zstat != 1) return 1; // not [extras] button | |
mlist = zlist_from_file(meta_view_extras_file); // get metadata extras list | mlist = zlist_from_file(meta_view_extras_file); // get metadata extras list | |
if (! mlist) mlist = zlist_new(0); | if (! mlist) mlist = zlist_new(0); | |
nn = select_meta_keys(mlist,MVmax,1); // user edit of extras list | nn = select_meta_keys(mlist,maxviewkeys,1); // user edit of extras list | |
if (nn) zlist_to_file(mlist,meta_view_extras_file); // update extras file | if (nn) zlist_to_file(mlist,meta_view_extras_file); // update extras file | |
zlist_delete(mlist); | zlist_delete(mlist); | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// edit metadata menu function | // edit metadata menu function | |
void m_meta_edit_main(GtkWidget *, cchar *menu) | void m_meta_edit_main(GtkWidget *, ch *menu) | |
{ | { | |
void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | |
void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | |
void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | |
void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) ; | void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) ; | |
int editmeta_dialog_event(zdialog *zd, cchar *event); | int editmeta_dialog_event(zdialog *zd, ch *event); | |
GtkWidget *widget; | GtkWidget *widget; | |
zdialog *zd; | zdialog *zd; | |
char *ppv, pdate2[12], ptime2[12]; | ch *ppv, pdate2[12], ptime2[12]; | |
char cctext[exif_maxcc+50]; | ch cctext[metadataXcc+50]; | |
char RN[4] = "R0"; | ch RN[4] = "R0"; | |
int err, ii; | int err, ii; | |
F1_help_topic = "edit meta"; | F1_help_topic = "edit meta"; | |
Plog(1,"m_meta_edit_main \n"); | Plog(1,"m_meta_edit_main \n"); | |
if (FGWM != 'F' && FGWM != 'G') return; | if (FGWM != 'F' && FGWM != 'G') return; | |
if (clicked_file) { // use clicked file if present | if (clicked_file) { // use clicked file if present | |
if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry | if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry | |
skipping to change at line 738 | skipping to change at line 732 | |
} | } | |
err = access(curr_file,W_OK); // test file can be written by me | err = access(curr_file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
zmessageACK(Mwin,"%s: %s","no write permission",curr_file); | zmessageACK(Mwin,"%s: %s","no write permission",curr_file); | |
return; | return; | |
} | } | |
load_Iglocs(); // initialize image geolocs[] data | load_Iglocs(); // initialize image geolocs[] data | |
// if (FGWM == 'G') gallery(0,"paint",-1); | ||
// if gallery view, repaint remove 22.50 | ||
/*** | /*** | |
___________________________________________________________ | ___________________________________________________________ | |
| Edit Metadata | | | Edit Metadata | | |
| | | | | | |
| File: filename.jpg | | | File: filename.jpg | | |
| Title [________________________________________________] | | | Title [________________________________________________] | | |
| Description [__________________________________________] | | | Description [__________________________________________] | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| Image Date: [__________] Time: [________] [prev] | | | Image Date: [__________] Time: [________] [prev] | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
skipping to change at line 911 | skipping to change at line 903 | |
widget = zdialog_gtkwidget(zd,"deftags"); | widget = zdialog_gtkwidget(zd,"deftags"); | |
textwidget_set_eventfunc(widget,edit_deftags_clickfunc); | textwidget_set_eventfunc(widget,edit_deftags_clickfunc); | |
zdialog_resize(zd,400,700); // run dialog | zdialog_resize(zd,400,700); // run dialog | |
zdialog_run(zd,editmeta_dialog_event,0); | zdialog_run(zd,editmeta_dialog_event,0); | |
} | } | |
zd = zd_editmeta; // edit metadata active | zd = zd_editmeta; // edit metadata active | |
zd_mapgeotags = zd; // map clicks active | zd_mapgeotags = zd; // map clicks active | |
ppv = (char *) strrchr(curr_file,'/'); | ppv = (ch *) strrchr(curr_file,'/'); | |
zdialog_stuff(zd,"file",ppv+1); // stuff dialog fields from curr. file | zdialog_stuff(zd,"file",ppv+1); // stuff dialog fields from curr. file | |
metadate_pdate(meta_pdate,pdate2,ptime2); // "yyyymmddhhmmss" to | metadate_pdate(meta_pdate,pdate2,ptime2); // "yyyymmddhhmmss" to | |
zdialog_stuff(zd,"date",pdate2); // "yyyy-mm-dd" and "hh:mm:ss" | zdialog_stuff(zd,"date",pdate2); // "yyyy-mm-dd" and "hh:mm:ss" | |
zdialog_stuff(zd,"time",ptime2); | zdialog_stuff(zd,"time",ptime2); | |
for (ii = 0; ii <= 5; ii++) { // set all rating radio buttons OFF | for (ii = 0; ii <= 5; ii++) { // set all rating radio buttons OFF | |
RN[1] = '0' + ii; | RN[1] = '0' + ii; | |
zdialog_stuff(zd,RN,0); | zdialog_stuff(zd,RN,0); | |
} | } | |
skipping to change at line 945 | skipping to change at line 937 | |
zdialog_stuff(zd,"imagetags",meta_tags); | zdialog_stuff(zd,"imagetags",meta_tags); | |
zdialog_stuff(zd,"recentags",tags_recentags); | zdialog_stuff(zd,"recentags",tags_recentags); | |
return; | return; | |
} | } | |
// mouse click functions for various text widgets for tags | // mouse click functions for various text widgets for tags | |
void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // existing image tag was clicked | void edit_imagetags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // existing image tag was clicked | |
{ | { | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;",end); | txtag = textwidget_word(widget,line,pos,",;",end); | |
if (! txtag) return; | if (! txtag) return; | |
del_tag(txtag,meta_tags); // remove tag from image | del_tag(txtag,meta_tags); // remove tag from image | |
zdialog_stuff(zd_editmeta,"imagetags",meta_tags); | zdialog_stuff(zd_editmeta,"imagetags",meta_tags); | |
Fmetamod++; // note change | Fmetamod++; // note change | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // recent tag was clicked | void edit_recentags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // recent tag was clicked | |
{ | { | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;",end); | txtag = textwidget_word(widget,line,pos,",;",end); | |
if (! txtag) return; | if (! txtag) return; | |
add_tag(txtag,meta_tags,tagFcc); // add recent tag to image | add_tag(txtag,meta_tags,filetagsXcc); // add recent tag to image | |
zdialog_stuff(zd_editmeta,"imagetags",meta_tags); | zdialog_stuff(zd_editmeta,"imagetags",meta_tags); | |
Fmetamod++; // note change | Fmetamod++; // note change | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked | void edit_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked | |
{ | { | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;",end); | txtag = textwidget_word(widget,line,pos,",;",end); | |
if (! txtag) return; | if (! txtag) return; | |
add_tag(txtag,meta_tags,tagFcc); // add matching tag to image | add_tag(txtag,meta_tags,filetagsXcc); // add matching tag to image | |
Fmetamod++; // note change | Fmetamod++; // note change | |
add_recentag(txtag); // and add to recent tags | add_recentag(txtag); // and add to recent tags | |
zdialog_stuff(zd_editmeta,"imagetags",meta_tags); // update dialog widgets | zdialog_stuff(zd_editmeta,"imagetags",meta_tags); // update dialog widgets | |
zdialog_stuff(zd_editmeta,"recentags",tags_recentags); | zdialog_stuff(zd_editmeta,"recentags",tags_recentags); | |
zdialog_stuff(zd_editmeta,"newtag",""); | zdialog_stuff(zd_editmeta,"newtag",""); | |
zdialog_stuff(zd_editmeta,"matchtags",""); | zdialog_stuff(zd_editmeta,"matchtags",""); | |
zdialog_goto(zd_editmeta,"newtag"); // put focus back on newtag widget | zdialog_goto(zd_editmeta,"newtag"); // put focus back on newtag widget | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // defined tag was clicked | void edit_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // defined tag was clicked | |
{ | { | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;:",end); | txtag = textwidget_word(widget,line,pos,",;:",end); | |
if (! txtag || end == ':') return; // nothing or tag category, ignore | if (! txtag || end == ':') return; // nothing or tag category, ignore | |
add_tag(txtag,meta_tags,tagFcc); // add new tag to image | add_tag(txtag,meta_tags,filetagsXcc); // add new tag to image | |
zdialog_stuff(zd_editmeta,"imagetags",meta_tags); // from defined tags list | zdialog_stuff(zd_editmeta,"imagetags",meta_tags); // from defined tags list | |
Fmetamod++; // note change | Fmetamod++; // note change | |
add_recentag(txtag); // and add to recent tags | add_recentag(txtag); // and add to recent tags | |
zdialog_stuff(zd_editmeta,"recentags",tags_recentags); | zdialog_stuff(zd_editmeta,"recentags",tags_recentags); | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int editmeta_dialog_event(zdialog *zd, cchar *event) | int editmeta_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
char pdate2[12], ptime2[12]; | ch pdate2[12], ptime2[12]; | |
// yyyy-mm-dd and hh:mm:ss | // yyyy-mm-dd and hh:mm:ss | |
char *metadate, *metatime; | ch *metadate, *metatime; | |
// yyyymmdd and hhmmss | // yyyymmdd and hhmmss | |
int ii, jj, nn, nt, cc1, cc2, ff, err; | int ii, jj, nn, nt, cc1, cc2, ff, err; | |
char *pp1, *pp2; | ch *pp1, *pp2; | |
char catgname[tagcc]; | ch catgname[tagXcc]; | |
char newtag[tagcc], matchtags[20][tagcc]; | ch newtag[tagXcc], matchtags[20][tagXcc]; | |
char matchtagstext[(tagcc+2)*20]; | ch matchtagstext[(tagXcc+2)*20]; | |
char cctext[exif_maxcc+50]; | ch cctext[metadataXcc+50]; | |
char RN[4] = "R0"; | ch RN[4] = "R0"; | |
char location[100], country[100], lati[20], longi[20]; | ch location[100], country[100], lati[20], longi[20]; | |
float flati, flongi; | float flati, flongi; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (! curr_file) zd->zstat = 3; // current file gone | if (! curr_file) zd->zstat = 3; // current file gone | |
if (strmatch(event,"cancel")) zd->zstat = 3; | if (strmatch(event,"cancel")) zd->zstat = 3; | |
if (zstrstr("date time title description",event)) // note change but process later | if (zstrstr("date time title description",event)) // note change but process later | |
Fmetamod++; | Fmetamod++; | |
skipping to change at line 1117 | skipping to change at line 1109 | |
{ | { | |
zdialog_stuff(zd,"location",""); // erase dialog fields | zdialog_stuff(zd,"location",""); // erase dialog fields | |
zdialog_stuff(zd,"country",""); | zdialog_stuff(zd,"country",""); | |
zdialog_stuff(zd,"lati",""); | zdialog_stuff(zd,"lati",""); | |
zdialog_stuff(zd,"longi",""); | zdialog_stuff(zd,"longi",""); | |
Fmetamod++; | Fmetamod++; | |
return 1; | return 1; | |
} | } | |
if (strmatch(event,"defcats")) { // new tag category selection | if (strmatch(event,"defcats")) { // new tag category selection | |
zdialog_fetch(zd,"defcats",catgname,tagcc); | zdialog_fetch(zd,"defcats",catgname,tagXcc); | |
deftags_stuff(zd,catgname); | deftags_stuff(zd,catgname); | |
} | } | |
if (strmatch(event,"newtag")) // new tag is being typed in | if (strmatch(event,"newtag")) // new tag is being typed in | |
{ | { | |
zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog | zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog | |
zdialog_fetch(zd,"newtag",newtag,tagcc); // get chars. typed so far | zdialog_fetch(zd,"newtag",newtag,tagXcc); // get chars. typed so far | |
cc1 = strlen(newtag); | cc1 = strlen(newtag); | |
for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters | for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters | |
if (strchr(",:;",newtag[ii])) continue; | if (strchr(",:;",newtag[ii])) continue; | |
newtag[jj++] = newtag[ii]; | newtag[jj++] = newtag[ii]; | |
} | } | |
if (jj < cc1) { // something was removed | if (jj < cc1) { // something was removed | |
newtag[jj] = 0; | newtag[jj] = 0; | |
cc1 = jj; | cc1 = jj; | |
skipping to change at line 1180 | skipping to change at line 1172 | |
strcpy(pp1,", "); | strcpy(pp1,", "); | |
pp1 += 2; | pp1 += 2; | |
} | } | |
zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog | zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog | |
return 1; | return 1; | |
} | } | |
if (strmatch(event,"add")) // enter new tag finished | if (strmatch(event,"add")) // enter new tag finished | |
{ | { | |
zdialog_fetch(zd,"newtag",newtag,tagcc); // get finished tag | zdialog_fetch(zd,"newtag",newtag,tagXcc); // get finished tag | |
cc1 = strlen(newtag); | cc1 = strlen(newtag); | |
if (! cc1) return 1; | if (! cc1) return 1; | |
if (newtag[cc1-1] == '\n') { // remove newline character | if (newtag[cc1-1] == '\n') { // remove newline character | |
cc1--; | cc1--; | |
newtag[cc1] = 0; | newtag[cc1] = 0; | |
} | } | |
for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories | for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories | |
{ | { | |
pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, | pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, | |
skipping to change at line 1212 | skipping to change at line 1204 | |
if (strmatchcaseN(newtag,pp1,cc1)) { // entered tag matches deftag | if (strmatchcaseN(newtag,pp1,cc1)) { // entered tag matches deftag | |
strncpy(newtag,pp1,cc1); // use deftag upper/lower case | strncpy(newtag,pp1,cc1); // use deftag upper/lower case | |
ff = 1; | ff = 1; | |
break; | break; | |
} | } | |
} | } | |
if (ff) break; | if (ff) break; | |
} | } | |
add_tag(newtag,meta_tags,tagFcc); // add to image tag list | add_tag(newtag,meta_tags,filetagsXcc); // add to image tag list | |
Fmetamod++; // note change | Fmetamod++; // note change | |
add_recentag(newtag); // and add to recent tags | add_recentag(newtag); // and add to recent tags | |
if (! ff) { // if new tag, add to defined tags | if (! ff) { // if new tag, add to defined tags | |
add_deftag((char *) "nocatg",newtag); | add_deftag("nocatg",newtag); | |
deftags_stuff(zd,"ALL"); | deftags_stuff(zd,"ALL"); | |
} | } | |
zdialog_stuff(zd,"newtag",""); // update dialog widgets | zdialog_stuff(zd,"newtag",""); // update dialog widgets | |
zdialog_stuff(zd,"imagetags",meta_tags); | zdialog_stuff(zd,"imagetags",meta_tags); | |
zdialog_stuff(zd,"recentags",tags_recentags); | zdialog_stuff(zd,"recentags",tags_recentags); | |
zdialog_stuff(zd,"matchtags",""); | zdialog_stuff(zd,"matchtags",""); | |
zdialog_goto(zd,"newtag"); // put focus back on newtag widget | zdialog_goto(zd,"newtag"); // put focus back on newtag widget | |
return 1; | return 1; | |
skipping to change at line 1254 | skipping to change at line 1246 | |
if (RN[1] == *p_meta_rating) zdialog_stuff(zd,RN,1); // for ratings "0" to "5" | if (RN[1] == *p_meta_rating) zdialog_stuff(zd,RN,1); // for ratings "0" to "5" | |
else zdialog_stuff(zd,RN,0); | else zdialog_stuff(zd,RN,0); | |
} | } | |
zdialog_stuff(zd,"location",p_meta_location); // get last-used geotags | zdialog_stuff(zd,"location",p_meta_location); // get last-used geotags | |
zdialog_stuff(zd,"country",p_meta_country); | zdialog_stuff(zd,"country",p_meta_country); | |
zdialog_stuff(zd,"lati",p_meta_lati); | zdialog_stuff(zd,"lati",p_meta_lati); | |
zdialog_stuff(zd,"longi",p_meta_longi); | zdialog_stuff(zd,"longi",p_meta_longi); | |
zdialog_stuff(zd,"imagetags",p_meta_tags); // stuff tags | zdialog_stuff(zd,"imagetags",p_meta_tags); // stuff tags | |
strncpy0(meta_tags,p_meta_tags,tagFcc); | strncpy0(meta_tags,p_meta_tags,filetagsXcc); | |
repl_1str(p_meta_title,cctext,"\\n","\n"); // stuff title | repl_1str(p_meta_title,cctext,"\\n","\n"); // stuff title | |
zdialog_stuff(zd,"title",cctext); | zdialog_stuff(zd,"title",cctext); | |
repl_1str(p_meta_description,cctext,"\\n","\n"); // stuff description | repl_1str(p_meta_description,cctext,"\\n","\n"); // stuff description | |
zdialog_stuff(zd,"description",cctext); | zdialog_stuff(zd,"description",cctext); | |
Fmetamod++; | Fmetamod++; | |
return 1; | return 1; | |
} | } | |
skipping to change at line 1301 | skipping to change at line 1293 | |
} | } | |
else *meta_pdate = 0; // leave empty | else *meta_pdate = 0; // leave empty | |
strcpy(meta_rating,"0"); | strcpy(meta_rating,"0"); | |
for (ii = 0; ii <= 5; ii++) { // get which rating radio button ON | for (ii = 0; ii <= 5; ii++) { // get which rating radio button ON | |
RN[1] = '0' + ii; | RN[1] = '0' + ii; | |
zdialog_fetch(zd,RN,jj); | zdialog_fetch(zd,RN,jj); | |
if (jj) meta_rating[0] = '0' + ii; // set corresponding rating | if (jj) meta_rating[0] = '0' + ii; // set corresponding rating | |
} | } | |
zdialog_fetch(zd,"title",cctext,exif_maxcc); // get new title | zdialog_fetch(zd,"title",cctext,metadataXcc); // get new title | |
repl_1str(cctext,meta_title,"\n","\\n"); // replace newlines with "\n" | repl_1str(cctext,meta_title,"\n","\\n"); // replace newlines with "\n" | |
zdialog_fetch(zd,"description",cctext,exif_maxcc); // get new description | zdialog_fetch(zd,"description",cctext,metadataXcc); // get new description | |
repl_1str(cctext,meta_description,"\n","\\n"); // replace newlines with "\n" | repl_1str(cctext,meta_description,"\n","\\n"); // replace newlines with "\n" | |
zdialog_fetch(zd,"location",location,100); // get location from dialog | zdialog_fetch(zd,"location",location,100); // get location from dialog | |
zdialog_fetch(zd,"country",country,100); | zdialog_fetch(zd,"country",country,100); | |
strTrim2(location); | strTrim2(location); | |
strTrim2(country); | strTrim2(country); | |
if (*location && ! strmatch(location,"null")) { | if (*location && ! strmatch(location,"null")) { | |
*location = toupper(*location); // capitalize | *location = toupper(*location); // capitalize | |
zdialog_stuff(zd,"location",location); | zdialog_stuff(zd,"location",location); | |
skipping to change at line 1334 | skipping to change at line 1326 | |
strTrim2(longi); | strTrim2(longi); | |
if (*lati || *longi) { // if coordinates present, validate | if (*lati || *longi) { // if coordinates present, validate | |
err = validate_latlong(lati,longi,flati,flongi); | err = validate_latlong(lati,longi,flati,flongi); | |
if (err) { | if (err) { | |
zmessageACK(Mwin,"bad latitude/longitude: %s %s",lati,longi); | zmessageACK(Mwin,"bad latitude/longitude: %s %s",lati,longi); | |
return 1; | return 1; | |
} | } | |
} | } | |
strncpy0(meta_location,location,100); // save geotags in image file EXIF | strncpy0(meta_location,location,100); // save geotags in image file metadata | |
strncpy0(meta_country,country,100); // and in image index file | strncpy0(meta_country,country,100); // and in image index file | |
strncpy0(meta_lati,lati,20); | strncpy0(meta_lati,lati,20); | |
strncpy0(meta_longi,longi,20); | strncpy0(meta_longi,longi,20); | |
put_geolocs(zd); // update geolocs table in memory | put_geolocs(zd); // update geolocs table in memory | |
save_filemeta(curr_file); // save metadata changes to image file | save_filemeta(curr_file); // save metadata changes to image file | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// edit EXIF/IPTC data - add or change specified EXIF/IPTC/etc. keydata | // edit metadata - add or change specified meta/etc. keydata | |
namespace meta_edit_any_names | namespace meta_edit_any_names | |
{ | { | |
char kname[80]; | ch kname[metakeyXcc]; | |
char kdata[exif_maxcc]; | ch kdata[metadataXcc]; | |
} | } | |
// menu function | // menu function | |
void m_meta_edit_any(GtkWidget *, cchar *menu) | void m_meta_edit_any(GtkWidget *, ch *menu) | |
{ | { | |
using namespace meta_edit_any_names; | using namespace meta_edit_any_names; | |
int meta_edit_any_dialog_event(zdialog *zd, cchar *event); | int meta_edit_any_dialog_event(zdialog *zd, ch *event); | |
void meta_edit_any_clickfunc(GtkWidget *, int line, int pos, int kbkey); | void meta_edit_any_clickfunc(GtkWidget *, int line, int pos, int kbkey); | |
GtkWidget *mtext; | GtkWidget *mtext; | |
int err; | int err; | |
zdialog *zd; | zdialog *zd; | |
cchar *pp1[1]; | ch *pp1[1]; | |
char *pp2[1], *pp; | ch *pp2[1], *pp; | |
F1_help_topic = "edit any meta"; | F1_help_topic = "edit any meta"; | |
Plog(1,"m_meta_edit_any \n"); | Plog(1,"m_meta_edit_any \n"); | |
if (FGWM != 'F' && FGWM != 'G') return; | if (FGWM != 'F' && FGWM != 'G') return; | |
if (clicked_file) { // use clicked file if present | if (clicked_file) { // use clicked file if present | |
if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry | if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry | |
f_open(clicked_file,0,0,1,0); | f_open(clicked_file,0,0,1,0); | |
skipping to change at line 1394 | skipping to change at line 1386 | |
zd_editanymeta = 0; | zd_editanymeta = 0; | |
return; | return; | |
} | } | |
err = access(curr_file,W_OK); // test file can be written by me | err = access(curr_file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
zmessageACK(Mwin,"%s: %s","no write permission",curr_file); | zmessageACK(Mwin,"%s: %s","no write permission",curr_file); | |
return; | return; | |
} | } | |
// if (FGWM == 'G') gallery(0,"paint",-1); | ||
// if gallery view, repaint remove 22.50 | ||
/*** | /*** | |
____________________________________________________________________ | ____________________________________________________________________ | |
| Click to Select | File: filename.jpg | | | Click to Select | File: filename.jpg | | |
|------------------------------| | | |------------------------------| | | |
| (metadata list) | key name [________________________] | | | (metadata list) | key name [________________________] | | |
| | key value [_______________________] | | | | key value [_______________________] | | |
| | | | | | | | |
| | [fetch] [update] [delete] | | | | [fetch] [update] [delete] | | |
| | | | | | | | |
| | | | | | | | |
skipping to change at line 1420 | skipping to change at line 1410 | |
| [Short List] [Full List] [OK] | | | [Short List] [Full List] [OK] | | |
|____________________________________________________________________| | |____________________________________________________________________| | |
***/ | ***/ | |
if (! zd_editanymeta) // popup dialog if not already | if (! zd_editanymeta) // popup dialog if not already | |
{ | { | |
zd = zdialog_new("Edit Any Metadata",Mwin,"Short List","Full List","OK",nu ll); | zd = zdialog_new("Edit Any Metadata",Mwin,"Short List","Full List","OK",nu ll); | |
zd_editanymeta = zd; | zd_editanymeta = zd; | |
zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); | zdialog_add_widget(zd,"hbox","hb1","dialog",0,"expand"); | |
zdialog_add_widget(zd,"vbox","vb1","hb1",0,"expand|space=3"); | zdialog_add_widget(zd,"vbox","vb1","hb1",0,"space=3"); | |
zdialog_add_widget(zd,"label","lab1","vb1","click to select","size=25"); | zdialog_add_widget(zd,"label","lab1","vb1","click to select","size=30"); | |
zdialog_add_widget(zd,"frame","frb1","vb1",0,"expand"); | zdialog_add_widget(zd,"frame","frb1","vb1",0,"expand"); | |
zdialog_add_widget(zd,"scrwin","scrb1","frb1",0,"expand"); | zdialog_add_widget(zd,"scrwin","scrb1","frb1",0,"expand"); | |
zdialog_add_widget(zd,"text","mtext","scrb1"); | zdialog_add_widget(zd,"text","mtext","scrb1"); | |
zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=3"); | zdialog_add_widget(zd,"vbox","vb2","hb1",0,"expand|space=3"); | |
zdialog_add_widget(zd,"hbox","hbf","vb2",0,"space=6"); | zdialog_add_widget(zd,"hbox","hbf","vb2",0,"space=6"); | |
zdialog_add_widget(zd,"label","labf","hbf","File:","space=3"); | zdialog_add_widget(zd,"label","labf","hbf","File:","space=3"); | |
zdialog_add_widget(zd,"label","file","hbf","filename.jpg","space=5"); | zdialog_add_widget(zd,"label","file","hbf","filename.jpg","space=5"); | |
zdialog_add_widget(zd,"hbox","hbkey","vb2",0,"space=2"); | zdialog_add_widget(zd,"hbox","hbkey","vb2",0,"space=2"); | |
zdialog_add_widget(zd,"label","labkey","hbkey","key name","space=5"); | zdialog_add_widget(zd,"label","labkey","hbkey","key name","space=5"); | |
zdialog_add_widget(zd,"zentry","kname","hbkey",0,"expand"); | zdialog_add_widget(zd,"zentry","kname","hbkey",0,"size=30"); | |
zdialog_add_widget(zd,"hbox","hbdata","vb2",0,"space=2"); | zdialog_add_widget(zd,"hbox","hbdata","vb2",0,"space=2"); | |
zdialog_add_widget(zd,"label","labdata","hbdata","key value","space=5"); | zdialog_add_widget(zd,"label","labdata","hbdata","key value","space=5"); | |
zdialog_add_widget(zd,"zentry","kdata","hbdata",0,"expand"); | zdialog_add_widget(zd,"zedit","kdata","hbdata",0,"expand"); | |
zdialog_add_widget(zd,"hbox","hbb","vb2",0,"space=10"); | zdialog_add_widget(zd,"hbox","hbb","vb2",0,"space=10"); | |
zdialog_add_widget(zd,"label","space","hbb",0,"expand"); | zdialog_add_widget(zd,"label","space","hbb",0,"expand"); | |
zdialog_add_widget(zd,"button","fetch","hbb","fetch","space=3"); | zdialog_add_widget(zd,"button","fetch","hbb","fetch","space=3"); | |
zdialog_add_widget(zd,"button","update","hbb","update","space=3"); | zdialog_add_widget(zd,"button","update","hbb","update","space=3"); | |
zdialog_add_widget(zd,"button","delete","hbb","delete","space=3"); | zdialog_add_widget(zd,"button","delete","hbb","delete","space=3"); | |
zdialog_resize(zd,700,400); | zdialog_resize(zd,700,400); | |
zdialog_run(zd,meta_edit_any_dialog_event,0); // start dialog | zdialog_run(zd,meta_edit_any_dialog_event,0); // start dialog | |
mtext = zdialog_gtkwidget(zd,"mtext"); // make clickable metadata list | mtext = zdialog_gtkwidget(zd,"mtext"); // make clickable metadata list | |
skipping to change at line 1460 | skipping to change at line 1450 | |
zd = zd_editanymeta; // dialog can stay open | zd = zd_editanymeta; // dialog can stay open | |
pp = strrchr(curr_file,'/'); // stuff file name in dialog | pp = strrchr(curr_file,'/'); // stuff file name in dialog | |
if (pp) zdialog_stuff(zd,"file",pp+1); | if (pp) zdialog_stuff(zd,"file",pp+1); | |
zdialog_send_event(zd,"initz"); // initz. dialog key list | zdialog_send_event(zd,"initz"); // initz. dialog key list | |
if (*kname) // update current key value | if (*kname) // update current key value | |
{ | { | |
pp1[0] = kname; // look for key data | pp1[0] = kname; // look for key data | |
exif_get(curr_file,pp1,pp2,1); | meta_get1(curr_file,pp1,pp2,1); | |
if (pp2[0]) { | if (pp2[0]) { | |
strncpy0(kdata,pp2[0],exif_maxcc); | strncpy0(kdata,pp2[0],metadataXcc); | |
zfree(pp2[0]); | zfree(pp2[0]); | |
} | } | |
else *kdata = 0; | else *kdata = 0; | |
zdialog_stuff(zd,"kdata",kdata); // stuff into dialog | zdialog_stuff(zd,"kdata",kdata); // stuff into dialog | |
} | } | |
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int meta_edit_any_dialog_event(zdialog *zd, cchar *event) | int meta_edit_any_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
using namespace meta_edit_any_names; | using namespace meta_edit_any_names; | |
GtkWidget *mtext; | GtkWidget *mtext; | |
char buff[1000]; | ch buff[1000]; | |
FILE *fid; | FILE *fid; | |
char *pp, *ppp; | ch *pp, *ppp; | |
cchar *pp1[1]; | ch *pp1[1]; | |
char *pp2[1]; | ch *pp2[1]; | |
int err; | int err; | |
static int whichlist = 1; // 1/2 = short/full list | static int whichlist = 1; // 1/2 = short/full list | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (strmatch(event,"initz")) | if (strmatch(event,"initz")) | |
{ | { | |
if (whichlist == 1) zd->zstat = 1; | if (whichlist == 1) zd->zstat = 1; | |
if (whichlist == 2) zd->zstat = 2; | if (whichlist == 2) zd->zstat = 2; | |
} | } | |
if (! curr_file) return 1; | if (! curr_file) return 1; | |
if (strmatch(event,"fetch")) | if (strmatch(event,"fetch")) | |
{ | { | |
zdialog_fetch(zd,"kname",kname,80); // get key name from dialog | zdialog_fetch(zd,"kname",kname,metakeyXcc); // get key name from dialog | |
strCompress(kname); | strCompress(kname); | |
pp1[0] = kname; // look for key data | pp1[0] = kname; // look for key data | |
exif_get(curr_file,pp1,pp2,1); | meta_get1(curr_file,pp1,pp2,1); | |
if (pp2[0]) { | if (pp2[0]) { | |
strncpy0(kdata,pp2[0],exif_maxcc); | strncpy0(kdata,pp2[0],metadataXcc); | |
zfree(pp2[0]); | zfree(pp2[0]); | |
} | } | |
else *kdata = 0; | else *kdata = 0; | |
zdialog_stuff(zd,"kdata",kdata); // stuff into dialog | zdialog_stuff(zd,"kdata",kdata); // stuff into dialog | |
} | } | |
if (strmatch(event,"update")) | if (strmatch(event,"update")) | |
{ | { | |
zdialog_fetch(zd,"kname",kname,80); | zdialog_fetch(zd,"kname",kname,metakeyXcc); | |
// get key name from dialog | // get key name from dialog | |
zdialog_fetch(zd,"kdata",kdata,exif_maxcc); | zdialog_fetch(zd,"kdata",kdata,metadataXcc); | |
strCompress(kname); | strCompress(kname); | |
pp1[0] = kname; | pp1[0] = kname; | |
pp2[0] = kdata; | pp2[0] = kdata; | |
err = exif_put(curr_file,pp1,(cchar **) pp2,1); | err = meta_put(curr_file,pp1,pp2,1); | |
// change metadata in image file | // change metadata in image file | |
if (err) zmessageACK(Mwin,"exif update error"); | if (err) zmessageACK(Mwin,"metadata update error"); | |
// 23.0 | // 23.0 | |
load_filemeta(curr_file); // update image index in case | load_filemeta(curr_file); // update image index in case | |
update_image_index(curr_file); // searchable metadata item updated | update_image_index(curr_file); // searchable metadata item updated | |
if (zd_metaview) meta_view(0); // update exif view if active | if (zd_metaview) meta_view(0); // update metadata view if active | |
} | } | |
if (strmatch(event,"delete")) | if (strmatch(event,"delete")) | |
{ | { | |
zdialog_fetch(zd,"kname",kname,80); // get key name from dialog | zdialog_fetch(zd,"kname",kname,metakeyXcc); // get key name from dialog | |
zdialog_stuff(zd,"kdata",""); // clear key data in dialog | zdialog_stuff(zd,"kdata",""); // clear key data in dialog | |
*kdata = 0; // and in memory | *kdata = 0; // and in memory | |
strCompress(kname); | strCompress(kname); | |
pp1[0] = kname; | pp1[0] = kname; | |
pp2[0] = kdata; | pp2[0] = kdata; | |
err = exif_put(curr_file,pp1,(cchar **) pp2,1); | err = meta_put(curr_file,pp1,pp2,1); | |
// change metadata in image file | // change metadata in image file | |
if (err) zmessageACK(Mwin,"exif update error"); | if (err) zmessageACK(Mwin,"metadata update error"); | |
// 23.0 | // 23.0 | |
load_filemeta(curr_file); // update image index in case | load_filemeta(curr_file); // update image index in case | |
update_image_index(curr_file); // searchable metadata item updated | update_image_index(curr_file); // searchable metadata item updated | |
if (zd_metaview) meta_view(0); // update exif view if active | if (zd_metaview) meta_view(0); // update metadata view if active | |
} | } | |
if (! zd->zstat) return 1; // wait for completion | if (! zd->zstat) return 1; // wait for completion | |
if (zd->zstat == 1) // short list | if (zd->zstat == 1) // short list | |
{ | { | |
zd->zstat = 0; // keep dialog active | zd->zstat = 0; // keep dialog active | |
mtext = zdialog_gtkwidget(zd,"mtext"); // make clickable metadata list | mtext = zdialog_gtkwidget(zd,"mtext"); // make clickable metadata list | |
textwidget_clear(mtext); | textwidget_clear(mtext); | |
skipping to change at line 1594 | skipping to change at line 1584 | |
return 1; | return 1; | |
} | } | |
// get clicked tag name from list and insert into dialog | // get clicked tag name from list and insert into dialog | |
void meta_edit_any_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | void meta_edit_any_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | |
{ | { | |
using namespace meta_edit_any_names; | using namespace meta_edit_any_names; | |
char *pp, *pp2[1]; | ch *pp, *pp2[1]; | |
cchar *pp1[1]; | ch *pp1[1]; | |
if (! zd_editanymeta) return; | if (! zd_editanymeta) return; | |
if (! curr_file) return; | if (! curr_file) return; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
pp = textwidget_line(widget,line,1); // get clicked line, highlight | pp = textwidget_line(widget,line,1); // get clicked line, highlight | |
if (! pp || ! *pp) return; | if (! pp || ! *pp) return; | |
textwidget_highlight_line(widget,line); | textwidget_highlight_line(widget,line); | |
zdialog_stuff(zd_editanymeta,"kname",pp); | zdialog_stuff(zd_editanymeta,"kname",pp); | |
zdialog_fetch(zd_editanymeta,"kname",kname,80); // get key name from dialog | zdialog_fetch(zd_editanymeta,"kname",kname,metakeyXcc); // get key name from dialog | |
strCompress(kname); | strCompress(kname); | |
pp1[0] = kname; // look for key data | pp1[0] = kname; // look for key data | |
exif_get(curr_file,pp1,pp2,1); | meta_get1(curr_file,pp1,pp2,1); | |
if (pp2[0]) { | if (pp2[0]) { | |
strncpy0(kdata,pp2[0],exif_maxcc); | strncpy0(kdata,pp2[0],metadataXcc); | |
zfree(pp2[0]); | zfree(pp2[0]); | |
} | } | |
else *kdata = 0; | else *kdata = 0; | |
zdialog_stuff(zd_editanymeta,"kdata",kdata); // stuff into dialog | zdialog_stuff(zd_editanymeta,"kdata",kdata); // stuff into dialog | |
return; | return; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// delete EXIF/IPTC data, specific key or all data | // delete metadata, specific key or all data | |
void m_meta_delete(GtkWidget *, cchar *menu) | void m_meta_delete(GtkWidget *, ch *menu) | |
{ | { | |
int meta_delete_dialog_event(zdialog *zd, cchar *event); | int meta_delete_dialog_event(zdialog *zd, ch *event); | |
zdialog *zd; | zdialog *zd; | |
char *pp; | ch *pp; | |
int err; | int err; | |
F1_help_topic = "delete meta"; | F1_help_topic = "delete meta"; | |
Plog(1,"m_meta_delete \n"); | Plog(1,"m_meta_delete \n"); | |
if (FGWM != 'F' && FGWM != 'G') return; | if (FGWM != 'F' && FGWM != 'G') return; | |
if (clicked_file) { // use clicked file if present | if (clicked_file) { // use clicked file if present | |
if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry | if (! curr_file || ! strmatch(clicked_file,curr_file)) // avoid f_open() re-entry | |
skipping to change at line 1658 | skipping to change at line 1648 | |
} | } | |
if (! curr_file) return; | if (! curr_file) return; | |
err = access(curr_file,W_OK); // test file can be written by me | err = access(curr_file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
zmessageACK(Mwin,"%s: %s","no write permission",curr_file); | zmessageACK(Mwin,"%s: %s","no write permission",curr_file); | |
return; | return; | |
} | } | |
// if (FGWM == 'G') gallery(0,"paint",-1); | ||
// if gallery view, repaint remove 22.50 | ||
/*** | /*** | |
_________________________________________ | _________________________________________ | |
| Delete Metadata | | | Delete Metadata | | |
| | | | | | |
| File: [______________________________] | | | File: [______________________________] | | |
| | | | | | |
| (o) ALL (o) One Key: [______________] | | | (o) ALL (o) One Key: [______________] | | |
| | | | | | |
| [apply] [cancel] | | | [apply] [cancel] | | |
|_________________________________________| | |_________________________________________| | |
skipping to change at line 1689 | skipping to change at line 1677 | |
zdialog_add_widget(zd,"label","file","hbf",0,"space=5"); | zdialog_add_widget(zd,"label","file","hbf",0,"space=5"); | |
zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); | zdialog_add_widget(zd,"hbox","hb1","dialog",0,"space=3"); | |
zdialog_add_widget(zd,"radio","kall","hb1","All","space=5"); | zdialog_add_widget(zd,"radio","kall","hb1","All","space=5"); | |
zdialog_add_widget(zd,"radio","key1","hb1","One Key:"); | zdialog_add_widget(zd,"radio","key1","hb1","One Key:"); | |
zdialog_add_widget(zd,"zentry","kdata","hb1",0,"size=20"); | zdialog_add_widget(zd,"zentry","kdata","hb1",0,"size=20"); | |
zdialog_stuff(zd,"key1",1); | zdialog_stuff(zd,"key1",1); | |
zdialog_run(zd,meta_delete_dialog_event,"parent"); | zdialog_run(zd,meta_delete_dialog_event,"parent"); | |
} | } | |
zd = zd_deletemeta; | zd = zd_deletemeta; | |
pp = (char *) ""; | pp = ""; | |
if (curr_file) { | if (curr_file) { | |
pp = strrchr(curr_file,'/'); | pp = strrchr(curr_file,'/'); | |
if (pp) pp++; | if (pp) pp++; | |
else pp = curr_file; | else pp = curr_file; | |
} | } | |
zdialog_stuff(zd,"file",pp); | zdialog_stuff(zd,"file",pp); | |
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int meta_delete_dialog_event(zdialog *zd, cchar *event) | int meta_delete_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
int kall, key1; | int kall, key1; | |
char *file; | ch *file; | |
char kdata[200]; | ch kdata[200]; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (! zd->zstat) return 1; // wait for completion | if (! zd->zstat) return 1; // wait for completion | |
if (zd->zstat != 1) { // canceled | if (zd->zstat != 1) { // canceled | |
zdialog_free(zd); | zdialog_free(zd); | |
zd_deletemeta = 0; | zd_deletemeta = 0; | |
return 1; | return 1; | |
} | } | |
skipping to change at line 1741 | skipping to change at line 1729 | |
if (kall) // update file metadata | if (kall) // update file metadata | |
zshell("log ack","exiftool -m -q -overwrite_original -all= \"%s\"",file); | zshell("log ack","exiftool -m -q -overwrite_original -all= \"%s\"",file); | |
else if (key1) | else if (key1) | |
zshell("log ack","exiftool -m -q -overwrite_original -%s= \"%s\"",kdata,f ile); | zshell("log ack","exiftool -m -q -overwrite_original -%s= \"%s\"",kdata,f ile); | |
zfree(file); | zfree(file); | |
load_filemeta(curr_file); // update image index in case a | load_filemeta(curr_file); // update image index in case a | |
update_image_index(curr_file); // searchable metadata deleted | update_image_index(curr_file); // searchable metadata deleted | |
if (zd_metaview) meta_view(0); // update exif view if active | if (zd_metaview) meta_view(0); // update metadata view if active | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// copy EXIF/IPTC data from one image to another | // copy metadata from one image to another | |
void m_meta_copy(GtkWidget *, cchar *menu) | void m_meta_copy(GtkWidget *, ch *menu) | |
{ | { | |
int meta_copy_dialog_event(zdialog *zd, cchar *event); | int meta_copy_dialog_event(zdialog *zd, ch *event); | |
F1_help_topic = "copy meta"; | F1_help_topic = "copy meta"; | |
Plog(1,"m_meta_copy \n"); | Plog(1,"m_meta_copy \n"); | |
m_viewmode(0,"G"); | m_viewmode(0,"G"); | |
/*** | /*** | |
_______________________________________________ | _______________________________________________ | |
| Copy Metadata | | | Copy Metadata | | |
skipping to change at line 1789 | skipping to change at line 1777 | |
zdialog_add_widget(zd,"button","tbrowse","hbt","Browse","space=3"); | zdialog_add_widget(zd,"button","tbrowse","hbt","Browse","space=3"); | |
zdialog_resize(zd,400,0); | zdialog_resize(zd,400,0); | |
zdialog_run(zd,meta_copy_dialog_event,"parent"); | zdialog_run(zd,meta_copy_dialog_event,"parent"); | |
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int meta_copy_dialog_event(zdialog *zd, cchar *event) | int meta_copy_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
int err = 0; | int err = 0; | |
char *pp; | ch *pp; | |
char sfile[XFCC], tfile[XFCC]; | ch sfile[XFCC], tfile[XFCC]; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (strmatch(event,"sbrowse")) // choose source file | if (strmatch(event,"sbrowse")) // choose source file | |
{ | { | |
zdialog_show(zd,0); | zdialog_show(zd,0); | |
pp = gallery_select1(0); | pp = gallery_select1(0); | |
if (pp) zdialog_stuff(zd,"sfile",pp); | if (pp) zdialog_stuff(zd,"sfile",pp); | |
if (pp) zfree(pp); | if (pp) zfree(pp); | |
zdialog_show(zd,1); | zdialog_show(zd,1); | |
skipping to change at line 1844 | skipping to change at line 1832 | |
return 1; | return 1; | |
} | } | |
err = access(tfile,W_OK); // test target file permissions | err = access(tfile,W_OK); // test target file permissions | |
if (err) { | if (err) { | |
zmessageACK(Mwin,"no write permission: %s",tfile); | zmessageACK(Mwin,"no write permission: %s",tfile); | |
return 1; | return 1; | |
} | } | |
Plog(1,"copy metadata from %s \n to %s \n",sfile,tfile); | Plog(1,"copy metadata from %s \n to %s \n",sfile,tfile); | |
err = exif_copy(sfile,tfile,0,0,0); | err = meta_copy(sfile,tfile,0,0,0); | |
// copy the data | // copy the data | |
if (err) zmessageACK(Mwin,"exif update error: %s",tfile); | if (err) zmessageACK(Mwin,"metadata update error: %s",tfile); | |
// 23.0 | // 23.0 | |
zdialog_free(zd); // done | zdialog_free(zd); // done | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// menu function | // menu function | |
// fix malformed metadata that prevents metadata edit functions from working | // fix malformed metadata that prevents metadata edit functions from working | |
void m_meta_fix(GtkWidget *, cchar *menu) | void m_meta_fix(GtkWidget *, ch *menu) | |
{ | { | |
int meta_fix_dialog_event(zdialog *zd, cchar *event); // 23.0 | int meta_fix_dialog_event(zdialog *zd, ch *event); // 23.0 | |
int yn; | int yn; | |
char *pp, command[XFCC+100]; | ch *pp, command[XFCC+100]; | |
cchar *tooloptions = "-all= -tagsfromfile @ -all:all -unsafe " | ch *tooloptions = "-all= -tagsfromfile @ -all:all -unsafe " | |
"-icc_profile -overwrite_original"; | "-icc_profile -overwrite_original"; | |
F1_help_topic = "fix meta"; | F1_help_topic = "fix meta"; | |
Plog(1,"m_meta_fix \n"); | Plog(1,"m_meta_fix \n"); | |
if (FGWM != 'F') return; | if (FGWM != 'F') return; | |
if (! curr_file) return; | if (! curr_file) return; | |
pp = strrchr(curr_file,'/'); | pp = strrchr(curr_file,'/'); | |
skipping to change at line 1890 | skipping to change at line 1878 | |
zmessageACK(Mwin,"completed"); | zmessageACK(Mwin,"completed"); | |
return; | return; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// manage tags function - auxiliary dialog | // manage tags function - auxiliary dialog | |
zdialog *zdmanagetags = 0; | zdialog *zdmanagetags = 0; | |
void m_meta_manage_tags(GtkWidget *, cchar *menu) | void m_meta_manage_tags(GtkWidget *, ch *menu) | |
{ | { | |
void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | |
int managetags_dialog_event(zdialog *zd, cchar *event); | int managetags_dialog_event(zdialog *zd, ch *event); | |
GtkWidget *widget; | GtkWidget *widget; | |
zdialog *zd; | zdialog *zd; | |
F1_help_topic = "manage tags"; | F1_help_topic = "manage tags"; | |
Plog(1,"m_meta_manage_tags \n"); | Plog(1,"m_meta_manage_tags \n"); | |
/*** | /*** | |
______________________________________________________________ | ______________________________________________________________ | |
skipping to change at line 1920 | skipping to change at line 1908 | |
| | category1: tag11, tag12, tag13 ... | | | | | category1: tag11, tag12, tag13 ... | | | |
| | category2: tag21, tag22, tag23 ... | | | | | category2: tag21, tag22, tag23 ... | | | |
| | ... | | | | | ... | | | |
| | | | | | | | | | |
| | | | | | | | | | |
| | | | | | | | | | |
| | | | | | | | | | |
| | | | | | | | | | |
| |__________________________________________________________| | | | |__________________________________________________________| | | |
| | | | | | |
| [orphan tags] [ OK ] | | | [ OK ] | | |
|______________________________________________________________| | |______________________________________________________________| | |
***/ | ***/ | |
if (zdmanagetags) return; | if (zdmanagetags) return; | |
zd = zdialog_new("Manage Tags",Mwin,"orphan tags","OK",null); | zd = zdialog_new("Manage Tags",Mwin,"OK",null); | |
zdmanagetags = zd; | zdmanagetags = zd; | |
zdialog_add_widget(zd,"hbox","hb7","dialog",0,"space=3"); | zdialog_add_widget(zd,"hbox","hb7","dialog",0,"space=3"); | |
zdialog_add_widget(zd,"label","labcatg","hb7","category","space=5"); | zdialog_add_widget(zd,"label","labcatg","hb7","category","space=5"); | |
zdialog_add_widget(zd,"zentry","catg","hb7",0,"size=12"); | zdialog_add_widget(zd,"zentry","catg","hb7",0,"size=12"); | |
zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); | zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); | |
zdialog_add_widget(zd,"label","labtag","hb7","tag","space=5"); | zdialog_add_widget(zd,"label","labtag","hb7","tag","space=5"); | |
zdialog_add_widget(zd,"zentry","tag","hb7",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","tag","hb7",0,"size=20|expand"); | |
zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); | zdialog_add_widget(zd,"label","space","hb7",0,"space=5"); | |
zdialog_add_widget(zd,"button","create","hb7","Create"); | zdialog_add_widget(zd,"button","create","hb7","Create"); | |
skipping to change at line 1964 | skipping to change at line 1952 | |
zdialog_wait(zd); | zdialog_wait(zd); | |
zdialog_free(zd); | zdialog_free(zd); | |
return; | return; | |
} | } | |
// mouse click functions for widget having tags | // mouse click functions for widget having tags | |
void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // tag or tag category was clicked | void manage_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // tag or tag category was clicked | |
{ | { | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;:",end); | txtag = textwidget_word(widget,line,pos,",;:",end); | |
if (! txtag) return; | if (! txtag) return; | |
if (end == ':') zdialog_stuff(zdmanagetags,"catg",txtag); // selected category >> dialog widget | if (end == ':') zdialog_stuff(zdmanagetags,"catg",txtag); // selected category >> dialog widget | |
else zdialog_stuff(zdmanagetags,"tag",txtag); // selected tag >> dialog widget | else zdialog_stuff(zdmanagetags,"tag",txtag); // selected tag >> dialog widget | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int managetags_dialog_event(zdialog *zd, cchar *event) | int managetags_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
void tag_orphans(GtkWidget *); | ||
char tag[tagcc], catg[tagcc]; | ch tag[tagXcc], catg[tagXcc]; | |
int err, changed = 0; | int err, changed = 0; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (zd->zstat) | if (zd->zstat) // [OK] or [x] | |
{ | { | |
if (zd->zstat == 1) { | zdialog_free(zd); | |
// report orphan tags | zdmanagetags = 0; | |
zd->zstat = 0; | return 1; | |
// keep dialog active | ||
tag_orphans(zd->parent); | ||
// GTK fails to position FIXME | ||
return 1; | ||
} | ||
else { | ||
// [OK] or [x] | ||
zdialog_free(zd); | ||
zdmanagetags = 0; | ||
return 1; | ||
} | ||
} | } | |
if (strmatch(event,"create")) { // add new tag to defined tags | if (strmatch(event,"create")) { // add new tag to defined tags | |
zdialog_fetch(zd,"catg",catg,tagcc); | zdialog_fetch(zd,"catg",catg,tagXcc); | |
zdialog_fetch(zd,"tag",tag,tagcc); | zdialog_fetch(zd,"tag",tag,tagXcc); | |
err = add_deftag(catg,tag); | err = add_deftag(catg,tag); | |
if (! err) changed++; | if (! err) changed++; | |
} | } | |
if (strmatch(event,"delete")) { // remove tag from defined tags | if (strmatch(event,"delete")) { // remove tag from defined tags | |
zdialog_fetch(zd,"tag",tag,tagcc); | zdialog_fetch(zd,"tag",tag,tagXcc); | |
zdialog_fetch(zd,"catg",catg,tagcc); | zdialog_fetch(zd,"catg",catg,tagXcc); | |
if (*tag) { | if (*tag) { | |
del_deftag(tag); | del_deftag(tag); | |
changed++; | changed++; | |
} | } | |
else if (*catg) { | else if (*catg) { | |
del_defcatg(catg); | del_defcatg(catg); | |
changed++; | changed++; | |
} | } | |
} | } | |
skipping to change at line 2043 | skipping to change at line 2022 | |
deftags_stuff(zd_batchtags,"ALL"); | deftags_stuff(zd_batchtags,"ALL"); | |
} | } | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// Choose metadata keys for captions on top of the current image. | // Choose metadata keys for captions on top of the current image. | |
void m_meta_choose_caps(GtkWidget *, cchar *menu) // 22.20 | void m_meta_choose_caps(GtkWidget *, ch *menu) // 22.20 | |
{ | { | |
zlist_t *Zcapskeys; | zlist_t *Zcapskeys; | |
F1_help_topic = "captions"; | F1_help_topic = "captions"; | |
Plog(1,"m_meta_choose_caps \n"); | Plog(1,"m_meta_choose_caps \n"); | |
Zcapskeys = zlist_from_file(capskeys_file); // get current metadata keys | Zcapskeys = zlist_from_file(capskeys_file); // get current metadata keys | |
if (! Zcapskeys) { | if (! Zcapskeys) { | |
Zcapskeys = zlist_new(0); // file missing, make default list | Zcapskeys = zlist_new(0); // file missing, make default list | |
zlist_append(Zcapskeys,"filename",1); | zlist_append(Zcapskeys,"filename",1); | |
} | } | |
select_meta_keys(Zcapskeys,maxcaps,0); // user edit key list | select_meta_keys(Zcapskeys,maxcapkeys,0); // user edit key list | |
zlist_to_file(Zcapskeys,capskeys_file); // save changes to file | zlist_to_file(Zcapskeys,capskeys_file); // save changes to file | |
meta_show_caps(1); | meta_show_caps(1); | |
return; | return; | |
} | } | |
// show captions text on current image | // show captions text on current image | |
void meta_show_caps(int show) // 22.20 | void meta_show_caps(int show) // 22.20 | |
{ | { | |
zlist_t *Zcapskeys; | zlist_t *Zcapskeys; | |
char *pp, *metakeys[maxcaps], *metatext[maxcaps]; | ch *pp, *mkeys[maxcapkeys], *metatext[maxcapkeys]; | |
char captext1[maxcapcc], **captext2; | ch captext1[capsXcc], **captext2; | |
int ii, nn, Ncaps, cc1, cc2; | int ii, nn, Ncaps, cc1, cc2; | |
if (! curr_file) return; | if (! curr_file) return; | |
if (! show) | if (! show) | |
{ | { | |
erase_toptext(1); | erase_toptext(1); | |
Fpaintnow(); | Fpaintnow(); | |
Fcaps = 0; | Fcaps = 0; | |
return; | return; | |
} | } | |
Zcapskeys = zlist_from_file(capskeys_file); // get current metadata keys | Zcapskeys = zlist_from_file(capskeys_file); // get current metadata keys | |
if (! Zcapskeys) { | if (! Zcapskeys) { | |
Zcapskeys = zlist_new(maxcaps); // file missing, make default list | Zcapskeys = zlist_new(maxcapkeys); // file missing, make default list | |
zlist_append(Zcapskeys,"filename",1); | zlist_append(Zcapskeys,"filename",1); | |
} | } | |
Ncaps = zlist_count(Zcapskeys); // key count | Ncaps = zlist_count(Zcapskeys); // key count | |
if (! Ncaps) return; | if (! Ncaps) return; | |
for (ii = 0; ii < Ncaps; ii++) { // get metadata keys | for (ii = 0; ii < Ncaps; ii++) { // get metadata keys | |
pp = zlist_get(Zcapskeys,ii); | pp = zlist_get(Zcapskeys,ii); | |
metakeys[ii] = zstrdup(pp,"capskeys"); | mkeys[ii] = zstrdup(pp,"capskeys"); | |
} | } | |
exif_get(curr_file,(cchar **) metakeys,metatext,Ncaps); // get metadata text for input keys | meta_get1(curr_file,mkeys,metatext,Ncaps); // get metadata text for input keys | |
cc1 = 0; | cc1 = 0; | |
for (ii = 0; ii < Ncaps; ii++) // put text strings together | for (ii = 0; ii < Ncaps; ii++) // put text strings together | |
{ // with \n separators | { // with \n separators | |
if (! metatext[ii]) continue; | if (! metatext[ii]) continue; | |
cc2 = strlen(metatext[ii]); | cc2 = strlen(metatext[ii]); | |
if (cc1 + 2 + cc2 > maxcapcc) cc2 = maxcapcc - cc1 - 2; | if (cc1 + 2 + cc2 > capsXcc) cc2 = capsXcc - cc1 - 2; | |
if (cc2 < 1) break; | if (cc2 < 1) break; | |
if (cc1 > 0) captext1[cc1++] = '\n'; | if (cc1 > 0) captext1[cc1++] = '\n'; | |
strncpy(captext1+cc1,metatext[ii],cc2); | strncpy(captext1+cc1,metatext[ii],cc2); | |
cc1 += cc2; | cc1 += cc2; | |
} | } | |
captext1[cc1] = 0; | captext1[cc1] = 0; | |
nn = breakup_text(captext1,captext2,0,ovtxcc[0],ovtxcc[1]); // break into lines within user limits | nn = breakup_text(captext1,captext2,0,ovtxcc[0],ovtxcc[1]); // break into lines within user limits | |
cc1 = 0; | cc1 = 0; | |
for (ii = 0; ii < nn; ii++) { // combine lines with \n separators | for (ii = 0; ii < nn; ii++) { // combine lines with \n separators | |
cc2 = strlen(captext2[ii]); | cc2 = strlen(captext2[ii]); | |
if (cc1 + cc2 + 2 > maxcapcc) cc2 = maxcapcc - cc1 - 2; | if (cc1 + cc2 + 2 > capsXcc) cc2 = capsXcc - cc1 - 2; | |
if (cc2 < 1) break; | if (cc2 < 1) break; | |
if (cc1) captext1[cc1++] = '\n'; | if (cc1) captext1[cc1++] = '\n'; | |
strcpy(captext1+cc1,captext2[ii]); | strcpy(captext1+cc1,captext2[ii]); | |
cc1 += cc2; | cc1 += cc2; | |
zfree(captext2[ii]); | zfree(captext2[ii]); | |
} | } | |
erase_toptext(1); | erase_toptext(1); | |
add_toptext(1,0,0,captext1,zfuncs::appfont); | add_toptext(1,0,0,captext1,zfuncs::appfont); | |
Fpaintnow(); | Fpaintnow(); | |
Fcaps = 1; | Fcaps = 1; | |
return; | return; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// toggle display of metadata text at the top of the displayed image file | // toggle display of metadata text at the top of the displayed image file | |
void m_meta_toggle_caps(GtkWidget *, cchar *menu) // 22.20 | void m_meta_toggle_caps(GtkWidget *, ch *menu) // 22.20 | |
{ | { | |
F1_help_topic = "captions"; | F1_help_topic = "captions"; | |
Fcaps = 1 - Fcaps; | Fcaps = 1 - Fcaps; | |
meta_show_caps(Fcaps); | meta_show_caps(Fcaps); | |
return; | return; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// menu function - add and remove tags for many files at once | // menu function - add and remove tags for many files at once | |
namespace batchtags | namespace batchtags | |
{ | { | |
char addtags[tagMcc]; | ch addtags[batchtagsXcc]; | |
// tags to add, list | // tags to add, list | |
char deltags[tagMcc]; | ch deltags[batchtagsXcc]; | |
// tags to remove, list | // tags to remove, list | |
int radadd, raddel; // dialog radio buttons | int radadd, raddel; // dialog radio buttons | |
char countmess[80]; | ch countmess[80]; | |
} | } | |
void m_batch_tags(GtkWidget *, cchar *menu) // combine batch add/del tags | void m_batch_tags(GtkWidget *, ch *menu) // combine batch add/del tags | |
{ | { | |
using namespace batchtags; | using namespace batchtags; | |
void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ); | void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ); | |
void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ); | void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ); | |
void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbk ey); | void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbk ey); | |
void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ); | void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ); | |
int batch_tags_dialog_event(zdialog *zd, cchar *event); | int batch_tags_dialog_event(zdialog *zd, ch *event); | |
char *ptag, *file; | ch *ptag, *file; | |
int zstat, ii, jj, err; | int zstat, ii, jj, err; | |
zdialog *zd, *zd2; | zdialog *zd, *zdpop; | |
GtkWidget *widget; | GtkWidget *widget; | |
F1_help_topic = "batch tags"; | F1_help_topic = "batch tags"; | |
Plog(1,"m_batch_tags \n"); | Plog(1,"m_batch_tags \n"); | |
if (Fblock("batch_tags","block edits")) return; // check pending, block | if (Fblock("batch_tags","block edits")) return; // check pending, block | |
/*** | /*** | |
________________________________________________________ | ________________________________________________________ | |
skipping to change at line 2290 | skipping to change at line 2269 | |
zstat = zdialog_wait(zd); // wait for dialog completion | zstat = zdialog_wait(zd); // wait for dialog completion | |
zdialog_free(zd); | zdialog_free(zd); | |
zd_batchtags = 0; | zd_batchtags = 0; | |
if (zstat != 1) { // cancel | if (zstat != 1) { // cancel | |
Fblock("batch_tags",0); | Fblock("batch_tags",0); | |
return; | return; | |
} | } | |
zd2 = popup_report_open("Batch Tags",Mwin,500,200,0,0,"OK",0); // status report popup window 22.15 | zdpop = popup_report_open("Batch Tags",Mwin,500,200,0,0,"OK",0); // status report popup window 22.15 | |
for (ii = 0; ii < GScount; ii++) // loop all selected files | for (ii = 0; ii < GScount; ii++) // loop all selected files | |
{ | { | |
zmainloop(10); // keep GTK alive | zmainloop(10); // keep GTK alive | |
if (! zdialog_valid(zdpop)) break; | ||
// report canceled 23.1 | ||
file = GSfiles[ii]; // display image | file = GSfiles[ii]; // display image | |
err = f_open(file,0,0,0); | err = f_open(file,0,0,0); | |
if (err) continue; | if (err) continue; | |
popup_report_write2(zd2,0,"%s \n",file); // report progress | popup_report_write2(zdpop,0,"%s \n",file); // report progress | |
err = access(file,W_OK); // test file can be written by me | err = access(file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
popup_report_write2(zd2,0,"%s \n","no write permission"); | popup_report_write2(zdpop,0,"%s \n","no write permission"); | |
continue; | continue; | |
} | } | |
for (jj = 1; ; jj++) // remove tags if present | for (jj = 1; ; jj++) // remove tags if present | |
{ | { | |
ptag = (char *) substring(deltags,",;",jj); | ptag = (ch *) substring(deltags,",;",jj); | |
if (! ptag) break; | if (! ptag) break; | |
if (*ptag == 0) continue; | if (*ptag == 0) continue; | |
err = del_tag(ptag,meta_tags); | err = del_tag(ptag,meta_tags); | |
if (err) continue; | if (err) continue; | |
} | } | |
for (jj = 1; ; jj++) // add new tags unless already | for (jj = 1; ; jj++) // add new tags unless already | |
{ | { | |
ptag = (char *) substring(addtags,",;",jj); | ptag = (ch *) substring(addtags,",;",jj); | |
if (! ptag) break; | if (! ptag) break; | |
if (*ptag == 0) continue; | if (*ptag == 0) continue; | |
err = add_tag(ptag,meta_tags,tagFcc); | err = add_tag(ptag,meta_tags,filetagsXcc); | |
if (err == 2) { | if (err == 2) { | |
zmessageACK(Mwin,"%s \n too many tags",file); | zmessageACK(Mwin,"%s \n too many tags",file); | |
break; | break; | |
} | } | |
} | } | |
save_filemeta(file); // save tag changes | save_filemeta(file); // save tag changes | |
} | } | |
popup_report_write2(zd2,0," *** %s \n","COMPLETED"); | if (! zdialog_valid(zdpop)) | |
// 23.1 | ||
Plog(0,"*** report cancelled \n"); | ||
// if (FGWM == 'G') gallery(0,"paint",-1); | else { | |
// refresh gallery remove 22.50 | popup_report_write2(zdpop,0,"\n *** COMPLETED \n"); | |
popup_report_bottom(zdpop); | ||
} | ||
load_deftags(1); // update defined tags list | load_deftags(1); // update defined tags list | |
Fblock("batch_tags",0); | Fblock("batch_tags",0); | |
return; | return; | |
} | } | |
// mouse click functions for widgets holding tags | // mouse click functions for widgets holding tags | |
void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the add list was clicked | void batch_addtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the add list was clicked | |
{ | { | |
using namespace batchtags; | using namespace batchtags; | |
char *txtag, end; | ch *txtag, end; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;",end); | txtag = textwidget_word(widget,line,pos,",;",end); | |
if (! txtag) return; | if (! txtag) return; | |
del_tag(txtag,addtags); // remove tag from list | del_tag(txtag,addtags); // remove tag from list | |
zdialog_stuff(zd_batchtags,"addtags",addtags); | zdialog_stuff(zd_batchtags,"addtags",addtags); | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the remove list was clicked | void batch_deltags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a tag in the remove list was clicked | |
{ | { | |
using namespace batchtags; | using namespace batchtags; | |
char *txtag, end; | ch *txtag, end; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;",end); | txtag = textwidget_word(widget,line,pos,",;",end); | |
if (! txtag) return; | if (! txtag) return; | |
del_tag(txtag,deltags); // remove tag from list | del_tag(txtag,deltags); // remove tag from list | |
zdialog_stuff(zd_batchtags,"deltags",deltags); | zdialog_stuff(zd_batchtags,"deltags",deltags); | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked | void batch_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // matching tag was clicked | |
{ | { | |
using namespace batchtags; | using namespace batchtags; | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;",end); | txtag = textwidget_word(widget,line,pos,",;",end); | |
if (! txtag) return; | if (! txtag) return; | |
zdialog_fetch(zd_batchtags,"radadd",radadd); // which radio button? | zdialog_fetch(zd_batchtags,"radadd",radadd); // which radio button? | |
if (radadd) { | if (radadd) { | |
add_tag(txtag,addtags,tagMcc); // add recent tag to tag add list | add_tag(txtag,addtags,batchtagsXcc); // add recent tag to tag add list | |
zdialog_stuff(zd_batchtags,"addtags",addtags); | zdialog_stuff(zd_batchtags,"addtags",addtags); | |
} | } | |
else { | else { | |
add_tag(txtag,deltags,tagMcc); // add recent tag to tag remove list | add_tag(txtag,deltags,batchtagsXcc); // add recent tag to tag remove list | |
zdialog_stuff(zd_batchtags,"deltags",deltags); | zdialog_stuff(zd_batchtags,"deltags",deltags); | |
} | } | |
zdialog_stuff(zd_batchtags,"newtag",""); // clear newtag and matchtags | zdialog_stuff(zd_batchtags,"newtag",""); // clear newtag and matchtags | |
zdialog_stuff(zd_batchtags,"matchtags",""); | zdialog_stuff(zd_batchtags,"matchtags",""); | |
zdialog_goto(zd_batchtags,"newtag"); // put focus back on newtag widget | zdialog_goto(zd_batchtags,"newtag"); // put focus back on newtag widget | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a defined tag was clicked | void batch_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) // a defined tag was clicked | |
{ | { | |
using namespace batchtags; | using namespace batchtags; | |
char *txtag, end; | ch *txtag, end; | |
int radadd; | int radadd; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;:",end); | txtag = textwidget_word(widget,line,pos,",;:",end); | |
if (! txtag || end == ':') return; // nothing or tag category, ignore | if (! txtag || end == ':') return; // nothing or tag category, ignore | |
zdialog_fetch(zd_batchtags,"radadd",radadd); // which radio button? | zdialog_fetch(zd_batchtags,"radadd",radadd); // which radio button? | |
if (radadd) { | if (radadd) { | |
add_tag(txtag,addtags,tagMcc); // add defined tag to tag add list | add_tag(txtag,addtags,batchtagsXcc); // add defined tag to tag add list | |
zdialog_stuff(zd_batchtags,"addtags",addtags); | zdialog_stuff(zd_batchtags,"addtags",addtags); | |
} | } | |
else { | else { | |
add_tag(txtag,deltags,tagMcc); // add defined tag to tag remove list | add_tag(txtag,deltags,batchtagsXcc); // add defined tag to tag remove list | |
zdialog_stuff(zd_batchtags,"deltags",deltags); | zdialog_stuff(zd_batchtags,"deltags",deltags); | |
} | } | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
// batchTags dialog event function | // batchTags dialog event function | |
int batch_tags_dialog_event(zdialog *zd, cchar *event) | int batch_tags_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
using namespace batchtags; | using namespace batchtags; | |
char catgname[tagcc]; | ch catgname[tagXcc]; | |
int ii, jj, nt, cc1, cc2, ff; | int ii, jj, nt, cc1, cc2, ff; | |
char *pp1, *pp2; | ch *pp1, *pp2; | |
char newtag[tagcc], matchtags[20][tagcc]; | ch newtag[tagXcc], matchtags[20][tagXcc]; | |
char matchtagstext[(tagcc+2)*20]; | ch matchtagstext[(tagXcc+2)*20]; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (strmatch(event,"cancel")) zd->zstat = 3; | if (strmatch(event,"cancel")) zd->zstat = 3; | |
if (zd->zstat) // dialog completed | if (zd->zstat) // dialog completed | |
{ | { | |
if (zd->zstat == 1) { // proceed | if (zd->zstat == 1) { // proceed | |
if (! GScount || (*addtags <= ' ' && *deltags <= ' ')) { | if (! GScount || (*addtags <= ' ' && *deltags <= ' ')) { | |
zmessageACK(Mwin,"specify files and tags"); | zmessageACK(Mwin,"specify files and tags"); | |
zd->zstat = 0; // keep dialog active | zd->zstat = 0; // keep dialog active | |
skipping to change at line 2492 | skipping to change at line 2477 | |
snprintf(countmess,80,"%d image files selected",GScount); | snprintf(countmess,80,"%d image files selected",GScount); | |
zdialog_stuff(zd,"labcount",countmess); | zdialog_stuff(zd,"labcount",countmess); | |
} | } | |
if (zstrstr("radadd raddel",event)) { // get state of radio buttons | if (zstrstr("radadd raddel",event)) { // get state of radio buttons | |
zdialog_fetch(zd,"radadd",radadd); | zdialog_fetch(zd,"radadd",radadd); | |
zdialog_fetch(zd,"raddel",raddel); | zdialog_fetch(zd,"raddel",raddel); | |
} | } | |
if (strmatch(event,"defcats")) { // new tag category selection | if (strmatch(event,"defcats")) { // new tag category selection | |
zdialog_fetch(zd,"defcats",catgname,tagcc); | zdialog_fetch(zd,"defcats",catgname,tagXcc); | |
deftags_stuff(zd,catgname); | deftags_stuff(zd,catgname); | |
} | } | |
if (strmatch(event,"newtag")) // new tag is being typed in | if (strmatch(event,"newtag")) // new tag is being typed in | |
{ | { | |
zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog | zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog | |
zdialog_fetch(zd,"newtag",newtag,tagcc); // get chars. typed so far | zdialog_fetch(zd,"newtag",newtag,tagXcc); // get chars. typed so far | |
cc1 = strlen(newtag); | cc1 = strlen(newtag); | |
for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters | for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters | |
if (strchr(",:;",newtag[ii])) continue; | if (strchr(",:;",newtag[ii])) continue; | |
newtag[jj++] = newtag[ii]; | newtag[jj++] = newtag[ii]; | |
} | } | |
if (jj < cc1) { // something was removed | if (jj < cc1) { // something was removed | |
newtag[jj] = 0; | newtag[jj] = 0; | |
cc1 = jj; | cc1 = jj; | |
skipping to change at line 2555 | skipping to change at line 2540 | |
strcpy(pp1,", "); | strcpy(pp1,", "); | |
pp1 += 2; | pp1 += 2; | |
} | } | |
zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog | zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog | |
return 1; | return 1; | |
} | } | |
if (strmatch(event,"add")) // enter new tag finished | if (strmatch(event,"add")) // enter new tag finished | |
{ | { | |
zdialog_fetch(zd,"newtag",newtag,tagcc); // get finished tag | zdialog_fetch(zd,"newtag",newtag,tagXcc); // get finished tag | |
cc1 = strlen(newtag); | cc1 = strlen(newtag); | |
if (! cc1) return 1; | if (! cc1) return 1; | |
if (newtag[cc1-1] == '\n') { // remove newline character | if (newtag[cc1-1] == '\n') { // remove newline character | |
cc1--; | cc1--; | |
newtag[cc1] = 0; | newtag[cc1] = 0; | |
} | } | |
for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories | for (ii = ff = 0; ii < maxtagcats; ii++) // loop all categories | |
{ | { | |
pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, | pp2 = tags_deftags[ii]; // category: aaaaaa, bbbbb, ... tagN, | |
skipping to change at line 2588 | skipping to change at line 2573 | |
strncpy(newtag,pp1,cc1); // use deftag upper/lower case | strncpy(newtag,pp1,cc1); // use deftag upper/lower case | |
ff = 1; | ff = 1; | |
break; | break; | |
} | } | |
} | } | |
if (ff) break; | if (ff) break; | |
} | } | |
if (! ff) { // if new tag, add to defined tags | if (! ff) { // if new tag, add to defined tags | |
add_deftag((char *) "nocatg",newtag); | add_deftag("nocatg",newtag); | |
deftags_stuff(zd,"ALL"); | deftags_stuff(zd,"ALL"); | |
} | } | |
add_tag(newtag,addtags,tagMcc); // add to tag add list | add_tag(newtag,addtags,batchtagsXcc); // add to tag add list | |
zdialog_stuff(zd_batchtags,"addtags",addtags); | zdialog_stuff(zd_batchtags,"addtags",addtags); | |
zdialog_stuff(zd,"newtag",""); // update dialog widgets | zdialog_stuff(zd,"newtag",""); // update dialog widgets | |
zdialog_stuff(zd,"matchtags",""); | zdialog_stuff(zd,"matchtags",""); | |
zdialog_goto(zd,"newtag"); // put focus back on newtag widget | zdialog_goto(zd,"newtag"); // put focus back on newtag widget | |
return 1; | return 1; | |
} | } | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// menu function - rename multiple tags for selected image files | // menu function - rename multiple tags for selected image files | |
namespace batchrenametags | namespace batchrenametags | |
{ | { | |
int Ntags; // count, 1-100 | int Ntags; // count, 1-100 | |
char *oldtags[100]; | ch *oldtags[100]; | |
// tags to rename | // tags to rename | |
char *newtags[100]; | ch *newtags[100]; | |
// corresponding new name | // corresponding new name | |
#define tpcc (tagcc+tagcc+10) | #define tpcc (tagXcc+tagXcc+10) | |
zdialog *zd; | zdialog *zd; | |
} | } | |
// menu function | // menu function | |
void m_batch_rename_tags(GtkWidget *, cchar *menu) | void m_batch_rename_tags(GtkWidget *, ch *menu) | |
{ | { | |
using namespace batchrenametags; | using namespace batchrenametags; | |
void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | |
void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | |
int batchrenametags_dialog_event(zdialog *zd, cchar *event); | int batchrenametags_dialog_event(zdialog *zd, ch *event); | |
char *file; | ch *file; | |
int ii, jj, kk, ff, err, yn; | int ii, jj, kk, ff, err, yn; | |
int zstat, Nfiles, Nlist; | int zstat, Nfiles, Nlist; | |
GtkWidget *widget; | GtkWidget *widget; | |
char **filelist; | ch **filelist; | |
char *pp, *filetag; | ch *pp, *filetag; | |
char *oldtaglist[100], *newtaglist[100]; | ch *oldtaglist[100], *newtaglist[100]; | |
xxrec_t *xxrec; | xxrec_t *xxrec; | |
zdialog *zd2; | zdialog *zdpop; | |
F1_help_topic = "batch rename tags"; | F1_help_topic = "batch rename tags"; | |
Plog(1,"m_batch_rename_tags \n"); | Plog(1,"m_batch_rename_tags \n"); | |
if (! Findexvalid) { | if (! Findexvalid) { | |
zmessageACK(Mwin,"image index disabled"); // no image index | zmessageACK(Mwin,"image index disabled"); // no image index | |
return; | return; | |
} | } | |
skipping to change at line 2723 | skipping to change at line 2708 | |
textwidget_set_eventfunc(widget,batchrenametags_taglist_clickfunc); | textwidget_set_eventfunc(widget,batchrenametags_taglist_clickfunc); | |
zdialog_resize(zd,700,400); // run dialog | zdialog_resize(zd,700,400); // run dialog | |
zdialog_run(zd,batchrenametags_dialog_event,0); | zdialog_run(zd,batchrenametags_dialog_event,0); | |
zstat = zdialog_wait(zd); // wait for dialog completion | zstat = zdialog_wait(zd); // wait for dialog completion | |
zdialog_free(zd); | zdialog_free(zd); | |
zd = 0; | zd = 0; | |
if (zstat != 1) goto cleanup; // [cancel] | if (zstat != 1) goto cleanup; // [cancel] | |
filelist = (char **) zmalloc(Nxxrec * sizeof(char *),"batch-rename-tags"); // find all affected image files | filelist = (ch **) zmalloc(Nxxrec * sizeof(ch *),"batch-rename-tags"); // find all affected image files | |
Nfiles = 0; | Nfiles = 0; | |
zd2 = popup_report_open("rename tags",Mwin,500,300,0,0,"OK",0); // log report 22/15 | zdpop = popup_report_open("rename tags",Mwin,500,300,0,0,"OK",0); // log report 22/15 | |
for (ii = 0; ii < Nxxrec; ii++) // loop all index recs | for (ii = 0; ii < Nxxrec; ii++) // loop all index recs | |
{ | { | |
zmainloop(10); // keep GTK alive | zmainloop(10); // keep GTK alive | |
xxrec = xxrec_tab[ii]; | xxrec = xxrec_tab[ii]; | |
if (! xxrec->tags) continue; // search for tags to rename | if (! xxrec->tags) continue; // search for tags to rename | |
ff = 0; | ff = 0; | |
for (jj = 1; ; jj++) { | for (jj = 1; ; jj++) { | |
pp = (char *) substring(xxrec->tags,',',jj); | pp = (ch *) substring(xxrec->tags,',',jj); | |
if (! pp) break; | if (! pp) break; | |
for (kk = 0; kk < Ntags; kk++) { | for (kk = 0; kk < Ntags; kk++) { | |
if (strmatchcase(pp,oldtags[kk])) { // this file has one or more tags | if (strmatchcase(pp,oldtags[kk])) { // this file has one or more tags | |
ff = 1; // that will be renamed | ff = 1; // that will be renamed | |
break; | break; | |
} | } | |
} | } | |
if (ff) break; | if (ff) break; | |
} | } | |
if (ff) { | if (ff) { | |
filelist[Nfiles] = zstrdup(xxrec->file,"batch-rename-tags"); // add to list of files to process | filelist[Nfiles] = zstrdup(xxrec->file,"batch-rename-tags"); // add to list of files to process | |
Nfiles++; | Nfiles++; | |
popup_report_write2(zd2,0,"file included: %s \n",xxrec->file); | popup_report_write2(zdpop,0,"file included: %s \n",xxrec->file); | |
} | } | |
} | } | |
yn = zmessageYN(Mwin,"%d tags to rename \n" | yn = zmessageYN(Mwin,"%d tags to rename \n" | |
"in %d image files. \n" | "in %d image files. \n" | |
"Proceed?",Ntags,Nfiles); | "Proceed?",Ntags,Nfiles); | |
if (! yn) goto cleanup; | if (! yn) goto cleanup; | |
if (! Ntags) goto cleanup; | if (! Ntags) goto cleanup; | |
for (ii = 0; ii < Nfiles; ii++) // loop all selected files | for (ii = 0; ii < Nfiles; ii++) // loop all selected files | |
{ | { | |
zmainloop(); // keep GTK alive | zmainloop(); // keep GTK alive | |
if (! zdialog_valid(zdpop)) break; | ||
// report canceled 23.1 | ||
file = filelist[ii]; // open image file | file = filelist[ii]; // open image file | |
err = f_open(file,0,0,0); | err = f_open(file,0,0,0); | |
if (err) continue; | if (err) continue; | |
popup_report_write2(zd2,0,"%s \n",file); // report progress | popup_report_write2(zdpop,0,"%s \n",file); // report progress | |
err = access(file,W_OK); // test file can be written by me | err = access(file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
popup_report_write2(zd2,0,"%s \n","no write permission"); | popup_report_write2(zdpop,0,"%s \n","no write permission"); | |
continue; | continue; | |
} | } | |
Nlist = 0; | Nlist = 0; | |
for (jj = 1; ; jj++) { // loop file tags | for (jj = 1; ; jj++) { // loop file tags | |
filetag = (char *) substring(meta_tags,',',jj); | filetag = (ch *) substring(meta_tags,',',jj); | |
if (! filetag) break; | if (! filetag) break; | |
for (kk = 0; kk < Ntags; kk++) { // loop tag replacement list | for (kk = 0; kk < Ntags; kk++) { // loop tag replacement list | |
if (strmatchcase(filetag,oldtags[kk])) { // file tag matches tag to replace | if (strmatchcase(filetag,oldtags[kk])) { // file tag matches tag to replace | |
oldtaglist[Nlist] = oldtags[kk]; // save old and new tags | oldtaglist[Nlist] = oldtags[kk]; // save old and new tags | |
newtaglist[Nlist] = newtags[kk]; | newtaglist[Nlist] = newtags[kk]; | |
Nlist++; | Nlist++; | |
break; // next file tag | break; // next file tag | |
} | } | |
} | } | |
} | } | |
for (jj = 0; jj < Nlist; jj++) // remove old tags | for (jj = 0; jj < Nlist; jj++) // remove old tags | |
err = del_tag(oldtaglist[jj],meta_tags); | err = del_tag(oldtaglist[jj],meta_tags); | |
for (jj = 0; jj < Nlist; jj++) { // add new tags | for (jj = 0; jj < Nlist; jj++) { // add new tags | |
if (! newtaglist[jj]) continue; // must be after removals | if (! newtaglist[jj]) continue; // must be after removals | |
popup_report_write2(zd2,0,"%s \n",newtaglist[jj]); | popup_report_write2(zdpop,0,"%s \n",newtaglist[jj]); | |
err = add_tag(newtaglist[jj],meta_tags,tagFcc); | err = add_tag(newtaglist[jj],meta_tags,filetagsXcc); | |
if (err && err != 1) popup_report_write2(zd2,1,"ERROR \n"); | if (err && err != 1) popup_report_write2(zdpop,1,"ERROR \n"); | |
// ignore already there, else report | // ignore already there, else report | |
} | } | |
save_filemeta(file); // save tag changes | save_filemeta(file); // save tag changes | |
} | } | |
popup_report_write2(zd2,0," *** %s \n","COMPLETED"); | if (! zdialog_valid(zdpop)) | |
// 23.1 | ||
Plog(0,"*** report cancelled \n"); | ||
else | ||
popup_report_write2(zdpop,0," *** %s \n","COMPLETED"); | ||
load_deftags(1); // update tag list | load_deftags(1); // update tag list | |
cleanup: // free resources | cleanup: // free resources | |
Fblock("batch_rename_tags",0); | Fblock("batch_rename_tags",0); | |
for (ii = 0; ii < Ntags; ii++) { | for (ii = 0; ii < Ntags; ii++) { | |
zfree(oldtags[ii]); | zfree(oldtags[ii]); | |
zfree(newtags[ii]); | zfree(newtags[ii]); | |
} | } | |
for (ii = 0; ii < Nfiles; ii++) | for (ii = 0; ii < Nfiles; ii++) | |
zfree(filelist[ii]); | zfree(filelist[ii]); | |
if (filelist) zfree(filelist); | if (filelist) zfree(filelist); | |
// if (FGWM == 'G') gallery(0,"paint",-1); | ||
// if gallery view, repaint remove 22.50 | ||
return; | return; | |
} | } | |
// a defined tag was clicked | // a defined tag was clicked | |
void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | void batchrenametags_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | |
{ | { | |
using namespace batchrenametags; | using namespace batchrenametags; | |
char *txtag, end; | ch *txtag, end; | |
char tagname[tagcc]; | ch tagname[tagXcc]; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;:",end); // clicked word | txtag = textwidget_word(widget,line,pos,",;:",end); // clicked word | |
if (! txtag || end == ':') return; // nothing or tag category, ignore | if (! txtag || end == ':') return; // nothing or tag category, ignore | |
snprintf(tagname,tagcc," %s ",txtag); // add spaces for appearance | snprintf(tagname,tagXcc," %s ",txtag); // add spaces for appearance | |
zdialog_stuff(zd,"oldtag",tagname); | zdialog_stuff(zd,"oldtag",tagname); | |
zdialog_stuff(zd,"newtag",""); | zdialog_stuff(zd,"newtag",""); | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
// a tag list line was clicked | // a tag list line was clicked | |
void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | void batchrenametags_taglist_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | |
skipping to change at line 2884 | skipping to change at line 2872 | |
widget = zdialog_gtkwidget(zd,"taglist"); // rewrite dialog tag list | widget = zdialog_gtkwidget(zd,"taglist"); // rewrite dialog tag list | |
textwidget_clear(widget); | textwidget_clear(widget); | |
for (int ii = 0; ii < Ntags; ii++) | for (int ii = 0; ii < Ntags; ii++) | |
textwidget_append2(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); | textwidget_append2(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); | |
return; | return; | |
} | } | |
// batch rename tags dialog event function | // batch rename tags dialog event function | |
int batchrenametags_dialog_event(zdialog *zd, cchar *event) | int batchrenametags_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
using namespace batchrenametags; | using namespace batchrenametags; | |
char catgname[tagcc]; | ch catgname[tagXcc]; | |
char oldtag[tagcc], newtag[tagcc]; | ch oldtag[tagXcc], newtag[tagXcc]; | |
GtkWidget *widget; | GtkWidget *widget; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (strmatch(event,"cancel")) zd->zstat = 2; | if (strmatch(event,"cancel")) zd->zstat = 2; | |
if (zd->zstat) return 1; // dialog completed | if (zd->zstat) return 1; // dialog completed | |
if (strmatch(event,"defcats")) { // new tag category selection | if (strmatch(event,"defcats")) { // new tag category selection | |
zdialog_fetch(zd,"defcats",catgname,tagcc); | zdialog_fetch(zd,"defcats",catgname,tagXcc); | |
deftags_stuff(zd,catgname); | deftags_stuff(zd,catgname); | |
} | } | |
if (strmatch(event,"addtags")) { // [ --> ] button pressed | if (strmatch(event,"addtags")) { // [ --> ] button pressed | |
zdialog_fetch(zd,"oldtag",oldtag,tagcc); | zdialog_fetch(zd,"oldtag",oldtag,tagXcc); | |
// save new pair of tag names | // save new pair of tag names | |
zdialog_fetch(zd,"newtag",newtag,tagcc); | zdialog_fetch(zd,"newtag",newtag,tagXcc); | |
strTrim2(oldtag); | strTrim2(oldtag); | |
strTrim2(newtag); | strTrim2(newtag); | |
if (*oldtag <= ' ' || *newtag <= ' ') return 1; | if (*oldtag <= ' ' || *newtag <= ' ') return 1; | |
if (Ntags == 100) { | if (Ntags == 100) { | |
zmessageACK(Mwin,"max tags exceeded"); | zmessageACK(Mwin,"max tags exceeded"); | |
return 1; | return 1; | |
} | } | |
oldtags[Ntags] = zstrdup(oldtag,"batch-rename-tags"); | oldtags[Ntags] = zstrdup(oldtag,"batch-rename-tags"); | |
newtags[Ntags] = zstrdup(newtag,"batch-rename-tags"); | newtags[Ntags] = zstrdup(newtag,"batch-rename-tags"); | |
Ntags++; | Ntags++; | |
skipping to change at line 2929 | skipping to change at line 2917 | |
for (int ii = 0; ii < Ntags; ii++) | for (int ii = 0; ii < Ntags; ii++) | |
textwidget_append2(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); | textwidget_append2(widget,0,"%s >> %s\n",oldtags[ii],newtags[ii]); | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// batch change or shift photo date/time | // batch change or shift photo date/time | |
void m_batch_photo_date_time(GtkWidget *, cchar *menu) | void m_batch_photo_date_time(GtkWidget *, ch *menu) | |
{ | { | |
int batch_photo_time_dialog_event(zdialog *zd, cchar *event); | int batch_photo_time_dialog_event(zdialog *zd, ch *event); | |
cchar *kname[1] = { "DateTimeOriginal" }; | ch *kname[1] = { "DateTimeOriginal" }; | |
char *kvalue[1]; | ch *kvalue[1]; | |
char text[100]; | ch text[100]; | |
char *file, olddatetime[24], newdatetime[24]; | ch *file, olddatetime[24], newdatetime[24]; | |
// exif format "yyyy-mm-dd hh:mm:ss" | // metadata format "yyyy-mm-dd hh:mm:ss" | |
int ii, nn, cc, err, zstat; | int ii, nn, cc, err, zstat; | |
int Fyearonly, Fdateonly; | int Fyearonly, Fdateonly; | |
int Fsetnew, Fshift, Ftest; // check boxes | int Fsetnew, Fshift, Ftest; // check boxes | |
time_t timep; | time_t timep; | |
struct tm DTold, DTnew; // old and new date/time | struct tm DTold, DTnew; // old and new date/time | |
int s_years, s_mons, s_mdays, s_hours, s_mins, s_secs; // shift amounts | int s_years, s_mons, s_mdays, s_hours, s_mins, s_secs; // shift amounts | |
zdialog *zd, *zd2; | zdialog *zd, *zdpop; | |
F1_help_topic = "batch photo date"; | F1_help_topic = "batch photo date"; | |
Plog(1,"m_batch_photo_date_time \n"); | Plog(1,"m_batch_photo_date_time \n"); | |
if (Fblock("batch_photo_DT","block edits")) return; // check pending, block | if (Fblock("batch_photo_DT","block edits")) return; // check pending, block | |
/*** | /*** | |
__________________________________________________ | __________________________________________________ | |
| Batch Photo Date/Time | | | Batch Photo Date/Time | | |
skipping to change at line 3106 | skipping to change at line 3094 | |
zdialog_fetch(zd,"s_years",s_years); // inputs are shifted date/time values | zdialog_fetch(zd,"s_years",s_years); // inputs are shifted date/time values | |
zdialog_fetch(zd,"s_mons",s_mons); | zdialog_fetch(zd,"s_mons",s_mons); | |
zdialog_fetch(zd,"s_mdays",s_mdays); | zdialog_fetch(zd,"s_mdays",s_mdays); | |
zdialog_fetch(zd,"s_hours",s_hours); | zdialog_fetch(zd,"s_hours",s_hours); | |
zdialog_fetch(zd,"s_mins",s_mins); | zdialog_fetch(zd,"s_mins",s_mins); | |
zdialog_fetch(zd,"s_secs",s_secs); | zdialog_fetch(zd,"s_secs",s_secs); | |
} | } | |
zdialog_free(zd); | zdialog_free(zd); | |
zd2 = popup_report_open("Photo Date/Time",Mwin,500,200,0,0,"OK",0); // log report 22.15 | zdpop = popup_report_open("Photo Date/Time",Mwin,500,200,0,0,"OK",0); // log report 22.15 | |
if (Fshift) { | if (Fshift) { | |
popup_report_write2(zd2,0,"changes: year mon day hours mins secs \n"); | popup_report_write2(zdpop,0,"changes: year mon day hours mins secs \n"); | |
popup_report_write2(zd2,0," %4d %3d %3d %5d %4d %4d \n", | popup_report_write2(zdpop,0," %4d %3d %3d %5d %4d %4d \n", | |
s_years,s_mons,s_mdays,s_hours,s_mins,s_secs); | s_years,s_mons,s_mdays,s_hours,s_mins,s_secs); | |
} | } | |
for (ii = 0; ii < GScount; ii++) // loop all selected files | for (ii = 0; ii < GScount; ii++) // loop all selected files | |
{ | { | |
zmainloop(); // keep GTK alive | zmainloop(); // keep GTK alive | |
if (! zdialog_valid(zdpop)) break; | ||
// report canceled 23.1 | ||
file = GSfiles[ii]; | file = GSfiles[ii]; | |
err = f_open(file,0,0,0); // open image file | err = f_open(file,0,0,0); // open image file | |
if (err) continue; | if (err) continue; | |
popup_report_write2(zd2,0,"\n"); | popup_report_write2(zdpop,0,"\n"); | |
// report progress | // report progress | |
popup_report_write2(zd2,0,"%s \n",file); | popup_report_write2(zdpop,0,"%s \n",file); | |
err = access(file,W_OK); // test file can be written by me | err = access(file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
popup_report_write2(zd2,0,"%s \n","no write permission"); | popup_report_write2(zdpop,0,"%s \n","no write permission"); | |
continue; | continue; | |
} | } | |
exif_get(curr_file,kname,(char **) kvalue,1); // metadata >> yyyy-mm-dd hh:mm:ss | meta_get1(curr_file,(ch **) kname,kvalue,1); // metadata >> yyyy-mm-dd hh:mm:ss | |
if (! kvalue[0] && Fshift) { // ignore if Fsetnew | if (! kvalue[0] && Fshift) { // ignore if Fsetnew | |
popup_report_write2(zd2,0," *** no date/time available \n"); | popup_report_write2(zdpop,0," *** no date/time available \n"); | |
continue; | continue; | |
} | } | |
if (kvalue[0]) { | if (kvalue[0]) { | |
strncpy0(olddatetime,kvalue[0],20); // yyyy-mm-dd hh:mm:ss | strncpy0(olddatetime,kvalue[0],20); // yyyy-mm-dd hh:mm:ss | |
zfree(kvalue[0]); | zfree(kvalue[0]); | |
} | } | |
else strcpy(olddatetime,"0000-01-01 00:00:00"); // missing old date/time | else strcpy(olddatetime,"0000-01-01 00:00:00"); // missing old date/time | |
nn = sscanf(olddatetime,"%d-%d-%d %d:%d:%d", // yyyy-mm-dd hh:mm:ss >> DTnew | nn = sscanf(olddatetime,"%d-%d-%d %d:%d:%d", // yyyy-mm-dd hh:mm:ss >> DTnew | |
&DTold.tm_year, &DTold.tm_mon, &DTold.tm_mday, | &DTold.tm_year, &DTold.tm_mon, &DTold.tm_mday, | |
&DTold.tm_hour, &DTold.tm_min, &DTold.tm_sec); | &DTold.tm_hour, &DTold.tm_min, &DTold.tm_sec); | |
DTold.tm_mon -= 1; // mktime month is 0-11 | DTold.tm_mon -= 1; // mktime month is 0-11 | |
if (nn != 6 && Fshift) { | if (nn != 6 && Fshift) { | |
popup_report_write2(zd2,0," *** EXIF date/time invalid \n"); | popup_report_write2(zdpop,0," *** metadata date/time invalid \n"); | |
continue; | continue; | |
} | } | |
if (nn != 6) strcpy(olddatetime,"0000-01-01 00:00:00"); // missing old date/time | if (nn != 6) strcpy(olddatetime,"0000-01-01 00:00:00"); // missing old date/time | |
if (Fsetnew) // set new date/time | if (Fsetnew) // set new date/time | |
{ | { | |
if (Fyearonly) // change year only, leave rest | if (Fyearonly) // change year only, leave rest | |
{ | { | |
DTnew.tm_mon = DTold.tm_mon; // >> revised DTnew | DTnew.tm_mon = DTold.tm_mon; // >> revised DTnew | |
skipping to change at line 3186 | skipping to change at line 3176 | |
DTnew.tm_year = DTold.tm_year + s_years; | DTnew.tm_year = DTold.tm_year + s_years; | |
DTnew.tm_mon = DTold.tm_mon + s_mons; | DTnew.tm_mon = DTold.tm_mon + s_mons; | |
DTnew.tm_mday = DTold.tm_mday + s_mdays; | DTnew.tm_mday = DTold.tm_mday + s_mdays; | |
DTnew.tm_hour = DTold.tm_hour + s_hours; | DTnew.tm_hour = DTold.tm_hour + s_hours; | |
DTnew.tm_min = DTold.tm_min + s_mins; | DTnew.tm_min = DTold.tm_min + s_mins; | |
DTnew.tm_sec = DTold.tm_sec + s_secs; | DTnew.tm_sec = DTold.tm_sec + s_secs; | |
} | } | |
timep = mktime(&DTnew); | timep = mktime(&DTnew); | |
if (timep < 0) { | if (timep < 0) { | |
popup_report_write2(zd2,0," %s *** date/time conversion failed \n",old datetime); | popup_report_write2(zdpop,0," %s *** date/time conversion failed \n",o lddatetime); | |
continue; | continue; | |
} | } | |
DTnew = *localtime(&timep); | DTnew = *localtime(&timep); | |
snprintf(newdatetime,20,"%04d-%02d-%02d %02d:%02d:%02d", // DTnew >> yyyy-mm-dd hh:mm:ss | snprintf(newdatetime,20,"%04d-%02d-%02d %02d:%02d:%02d", // DTnew >> yyyy-mm-dd hh:mm:ss | |
DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) | DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) | |
DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); | DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); | |
olddatetime[4] = olddatetime[7] = newdatetime[4] = newdatetime[7] = '-'; // format: yyyy-mm-dd 22.50 | olddatetime[4] = olddatetime[7] = newdatetime[4] = newdatetime[7] = '-'; // format: yyyy-mm-dd 22.50 | |
popup_report_write2(zd2,0," %s %s \n",olddatetime,newdatetime); | popup_report_write2(zdpop,0," %s %s \n",olddatetime,newdatetime); | |
if (Ftest) continue; // test only, no file updates | if (Ftest) continue; // test only, no file updates | |
newdatetime[4] = newdatetime[7] = '-'; | newdatetime[4] = newdatetime[7] = '-'; | |
// format: yyyy-mm-dd for EXIF | // format: yyyy-mm-dd for metadata | |
kvalue[0] = (char *) &newdatetime; | kvalue[0] = (ch *) &newdatetime; | |
err = exif_put(curr_file,kname,(cchar **) kvalue,1); | err = meta_put(curr_file,(ch **) kname,kvalue,1); | |
// yyyy-mm-dd hh:mm:ss >> metadata | // yyyy-mm-dd hh:mm:ss >> metadata | |
if (err) { | if (err) { | |
popup_report_write2(zd2,0," *** exif update error \n"); // 23.0 | popup_report_write2(zdpop,0," *** metadata update error \n"); // 23.0 | |
continue; | continue; | |
} | } | |
load_filemeta(curr_file); // get all indexed data for file | load_filemeta(curr_file); // get all indexed data for file | |
snprintf(meta_pdate,16,"%04d%02d%02d%02d%02d%02d", // update photo date, yyyymmddhhmmss | snprintf(meta_pdate,16,"%04d%02d%02d%02d%02d%02d", // update photo date, yyyymmddhhmmss | |
DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) | DTnew.tm_year, DTnew.tm_mon+1, DTnew.tm_mday, // (tm_mon 0-11 >> 1-12) | |
DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); | DTnew.tm_hour, DTnew.tm_min, DTnew.tm_sec); | |
update_image_index(curr_file); // update image index rec. | update_image_index(curr_file); // update image index rec. | |
} | } | |
popup_report_write2(zd2,0," *** %s \n","COMPLETED"); | if (! zdialog_valid(zdpop)) | |
// 23.1 | ||
Plog(0,"*** report cancelled \n"); | ||
else | ||
popup_report_write2(zdpop,0," *** %s \n","COMPLETED"); | ||
Fblock("batch_photo_DT",0); | Fblock("batch_photo_DT",0); | |
// if (FGWM == 'G') gallery(0,"paint",-1); | ||
// remove 22.50 | ||
m_batch_photo_date_time(0,0); | ||
// repeat | ||
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int batch_photo_time_dialog_event(zdialog *zd, cchar *event) | int batch_photo_time_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
char countmess[80]; | ch countmess[80]; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (strmatch(event,"files")) // select images to process | if (strmatch(event,"files")) // select images to process | |
{ | { | |
zdialog_show(zd,0); // hide parent dialog | zdialog_show(zd,0); // hide parent dialog | |
gallery_select(); // get new file list | gallery_select(); // get new file list | |
zdialog_show(zd,1); | zdialog_show(zd,1); | |
snprintf(countmess,80,"%d image files selected",GScount); | snprintf(countmess,80,"%d image files selected",GScount); | |
skipping to change at line 3256 | skipping to change at line 3246 | |
zdialog_stuff(zd,"Fsetnew",0); | zdialog_stuff(zd,"Fsetnew",0); | |
zdialog_stuff(zd,"Fshift",0); | zdialog_stuff(zd,"Fshift",0); | |
zdialog_stuff(zd,event,1); | zdialog_stuff(zd,event,1); | |
} | } | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// batch add or change any EXIF/IPTC metadata | // batch add or change any meta metadata | |
namespace batchchangemeta | namespace batchchangemeta | |
{ | { | |
zdialog *zd; | zdialog *zd; | |
} | } | |
// menu function | // menu function | |
void m_batch_change_meta(GtkWidget *, cchar *menu) | void m_batch_change_meta(GtkWidget *, ch *menu) | |
{ | { | |
using namespace batchchangemeta; | using namespace batchchangemeta; | |
int batch_change_meta_dialog_event(zdialog *zd, cchar *event); | int batch_change_meta_dialog_event(zdialog *zd, ch *event); | |
void batch_change_meta_clickfunc(GtkWidget *, int line, int pos, int kbkey); | void batch_change_meta_clickfunc(GtkWidget *, int line, int pos, int kbkey); | |
int ii, jj, err, zstat, nkeys; | int ii, jj, err, zstat, nkeys; | |
char knameN[12] = "knameN", kvalueN[12] = "kvalueN"; | ch knameN[8] = "knameN", kvalN[8] = "kvalN"; | |
char kname[80], kvalue[exif_maxcc]; | ch kname[metakeyXcc], kval[metadataXcc]; | |
cchar *pp1[10], *pp2[10]; | ch *pp1[10], *pp2[10]; | |
char *file, text[100]; | ch *file, text[100]; | |
GtkWidget *mtext; | GtkWidget *mtext; | |
static int nx, ftf = 1; | static int nx, ftf = 1; | |
static char **itemlist; | static ch **itemlist; | |
zdialog *zd2; | zdialog *zdpop; | |
F1_help_topic = "batch change meta"; | F1_help_topic = "batch change meta"; | |
Plog(1,"m_batch_change_meta \n"); | Plog(1,"m_batch_change_meta \n"); | |
if (Fblock("batch_change_meta","block edits")) return; // check pending, block | if (Fblock("batch_change_meta","block edits")) return; // check pending, block | |
if (ftf) { | if (ftf) { | |
ftf = 0; | ftf = 0; | |
nx = zreadfile(meta_picklist_file,itemlist); // get list of metadata items | nx = zreadfile(meta_picklist_file,itemlist); // get list of metadata items | |
skipping to change at line 3331 | skipping to change at line 3321 | |
zdialog_add_widget(zd,"frame","fr1","vb1",0,"expand"); | zdialog_add_widget(zd,"frame","fr1","vb1",0,"expand"); | |
zdialog_add_widget(zd,"scrwin","scr1","fr1"); | zdialog_add_widget(zd,"scrwin","scr1","fr1"); | |
zdialog_add_widget(zd,"text","mtext","scr1"); | zdialog_add_widget(zd,"text","mtext","scr1"); | |
zdialog_add_widget(zd,"hbox","hbfiles","vb2",0,"space=3"); | zdialog_add_widget(zd,"hbox","hbfiles","vb2",0,"space=3"); | |
zdialog_add_widget(zd,"button","files","hbfiles","Select Files","space=5"); | zdialog_add_widget(zd,"button","files","hbfiles","Select Files","space=5"); | |
zdialog_add_widget(zd,"label","labcount","hbfiles","no files selected","space =10"); | zdialog_add_widget(zd,"label","labcount","hbfiles","no files selected","space =10"); | |
zdialog_add_widget(zd,"hbox","hbkeys","vb2",0,"space=5"); | zdialog_add_widget(zd,"hbox","hbkeys","vb2",0,"space=5"); | |
zdialog_add_widget(zd,"vbox","vbname","hbkeys"); | zdialog_add_widget(zd,"vbox","vbname","hbkeys"); | |
zdialog_add_widget(zd,"vbox","vbvalue","hbkeys",0,"expand"); | zdialog_add_widget(zd,"vbox","vbval","hbkeys",0,"expand"); | |
zdialog_add_widget(zd,"label","labkey","vbname","key name"); | zdialog_add_widget(zd,"label","labkey","vbname","key name"); | |
zdialog_add_widget(zd,"label","labdata","vbvalue","key value"); | zdialog_add_widget(zd,"label","labdata","vbval","key value"); | |
zdialog_add_widget(zd,"zentry","kname0","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname0","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname1","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname1","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname2","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname2","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname3","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname3","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname4","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname4","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname5","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname5","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname6","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname6","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname7","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname7","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname8","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname8","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kname9","vbname",0,"size=20"); | zdialog_add_widget(zd,"zentry","kname9","vbname",0,"size=20"); | |
zdialog_add_widget(zd,"zentry","kvalue0","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval0","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue1","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval1","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue2","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval2","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue3","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval3","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue4","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval4","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue5","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval5","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue6","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval6","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue7","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval7","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue8","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval8","vbval",0,"size=20|expand"); | |
zdialog_add_widget(zd,"zentry","kvalue9","vbvalue",0,"size=20|expand"); | zdialog_add_widget(zd,"zentry","kval9","vbval",0,"size=20|expand"); | |
snprintf(text,100,"%d image files selected",GScount); // show selected files count | snprintf(text,100,"%d image files selected",GScount); // show selected files count | |
zdialog_stuff(zd,"labcount",text); | zdialog_stuff(zd,"labcount",text); | |
mtext = zdialog_gtkwidget(zd,"mtext"); // make clickable metadata list | mtext = zdialog_gtkwidget(zd,"mtext"); // make clickable metadata list | |
textwidget_clear(mtext); | textwidget_clear(mtext); | |
for (ii = 0; ii < nx; ii++) // stuff metadata pick list | for (ii = 0; ii < nx; ii++) // stuff metadata pick list | |
textwidget_append(mtext,0,"%s \n",itemlist[ii]); | textwidget_append(mtext,0,"%s \n",itemlist[ii]); | |
skipping to change at line 3376 | skipping to change at line 3366 | |
nkeys = 0; // nothing selected | nkeys = 0; // nothing selected | |
zstat = zdialog_run(zd,batch_change_meta_dialog_event,0); // run dialog | zstat = zdialog_run(zd,batch_change_meta_dialog_event,0); // run dialog | |
retry: | retry: | |
zstat = zdialog_wait(zd); // wait for completion | zstat = zdialog_wait(zd); // wait for completion | |
if (zstat != 2) goto cleanup; // not [apply] | if (zstat != 2) goto cleanup; // not [apply] | |
for (ii = jj = 0; ii < 10; ii++) | for (ii = jj = 0; ii < 10; ii++) | |
{ | { | |
knameN[7] = '0' + ii; | knameN[5] = '0' + ii; | |
kvalueN[8] = '0' + ii; | kvalN[4] = '0' + ii; | |
zdialog_fetch(zd,knameN,kname,80); | zdialog_fetch(zd,knameN,kname,metakeyXcc); | |
zdialog_fetch(zd,kvalueN,kvalue,exif_maxcc); | zdialog_fetch(zd,kvalN,kval,metadataXcc); | |
strCompress(kname); | strCompress(kname); | |
if (*kname <= ' ') continue; | if (*kname <= ' ') continue; | |
pp1[jj] = zstrdup(kname,"batch-metadata"); | pp1[jj] = zstrdup(kname,"batch-metadata"); | |
pp2[jj] = zstrdup(kvalue,"batch-metadata"); | pp2[jj] = zstrdup(kval,"batch-metadata"); | |
jj++; | jj++; | |
} | } | |
nkeys = jj; | nkeys = jj; | |
if (nkeys == 0) { | if (nkeys == 0) { | |
zmessageACK(Mwin,"enter key names"); | zmessageACK(Mwin,"enter key names"); | |
zd->zstat = 0; | zd->zstat = 0; | |
goto retry; | goto retry; | |
} | } | |
if (GScount == 0) { | if (GScount == 0) { | |
zmessageACK(Mwin,"no files selected"); | zmessageACK(Mwin,"no files selected"); | |
zd->zstat = 0; | zd->zstat = 0; | |
goto retry; | goto retry; | |
} | } | |
zd2 = popup_report_open("Batch Metadata",Mwin,500,200,0,0,"OK",0); // log report 22.15 | zdpop = popup_report_open("Batch Metadata",Mwin,500,200,0,0,"OK",0); // log report 22.15 | |
for (ii = 0; ii < nkeys; ii++) | for (ii = 0; ii < nkeys; ii++) | |
{ | { | |
if (*pp2[ii]) popup_report_write2(zd2,0,"%s = %s \n",pp1[ii],pp2[ii]); | if (*pp2[ii]) popup_report_write2(zdpop,0,"%s = %s \n",pp1[ii],pp2[ii]); | |
else popup_report_write2(zd2,0,"%s = DELETED \n",pp1[ii]); | else popup_report_write2(zdpop,0,"%s = DELETED \n",pp1[ii]); | |
} | } | |
ii = zdialog_choose(Mwin,"parent","Proceed","Proceed","Cancel",null); | ii = zdialog_choose(Mwin,"parent","Proceed","Proceed","Cancel",null); | |
if (ii != 1) { | if (ii != 1) { | |
zd->zstat = 0; // cancel | zd->zstat = 0; // cancel | |
popup_report_close(zd2,0); | popup_report_close(zdpop,0); | |
goto retry; | goto retry; | |
} | } | |
zdialog_free(zd); | zdialog_free(zd); | |
zd = 0; | zd = 0; | |
for (ii = 0; ii < GScount; ii++) // loop all selected files | for (ii = 0; ii < GScount; ii++) // loop all selected files | |
{ | { | |
zmainloop(); // keep GTK alive | zmainloop(); // keep GTK alive | |
if (! zdialog_valid(zdpop)) break; | ||
// report canceled 23.1 | ||
file = GSfiles[ii]; // display image | file = GSfiles[ii]; // display image | |
err = f_open(file,0,0,0); | err = f_open(file,0,0,0); | |
if (err) continue; | if (err) continue; | |
popup_report_write2(zd2,0,"%s \n",file); // report progress | popup_report_write2(zdpop,0,"%s \n",file); // report progress | |
err = access(file,W_OK); // test file can be written by me | err = access(file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
popup_report_write2(zd2,1," *** no write permission \n"); | popup_report_write2(zdpop,1," *** no write permission \n"); | |
continue; | continue; | |
} | } | |
err = exif_put(curr_file,pp1,pp2,nkeys); // change metadata in image file | err = meta_put(curr_file,pp1,pp2,nkeys); // change metadata in image file | |
if (err) { | if (err) { | |
popup_report_write2(zd2,1," *** exif update error \n"); // 23.0 | popup_report_write2(zdpop,1," *** metadata update error \n"); // 23.0 | |
continue; | continue; | |
} | } | |
load_filemeta(curr_file); // update image index in case | load_filemeta(curr_file); // update image index in case | |
update_image_index(curr_file); // indexed metadata updated | update_image_index(curr_file); // indexed metadata updated | |
if (zd_metaview) meta_view(0); // update exif view if active | if (zd_metaview) meta_view(0); // update metadata view if active | |
} | } | |
popup_report_write2(zd2,0," *** %s \n","COMPLETED"); | if (! zdialog_valid(zdpop)) | |
// 23.1 | ||
Plog(0,"*** report cancelled \n"); | ||
else | ||
popup_report_write2(zdpop,0," *** COMPLETED \n"); | ||
cleanup: | cleanup: | |
if (zd) zdialog_free(zd); // kill dialog | if (zd) zdialog_free(zd); // kill dialog | |
zd = 0; | zd = 0; | |
for (ii = 0; ii < nkeys; ii++) { // free memory | for (ii = 0; ii < nkeys; ii++) { // free memory | |
zfree((char *) pp1[ii]); | zfree((ch *) pp1[ii]); | |
zfree((char *) pp2[ii]); | zfree((ch *) pp2[ii]); | |
} | } | |
nkeys = 0; | nkeys = 0; | |
Fblock("batch_change_meta",0); | Fblock("batch_change_meta",0); | |
// if (FGWM == 'G') gallery(0,"paint",-1); // refresh gallery remove 22.50 | ||
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int batch_change_meta_dialog_event(zdialog *zd, cchar *event) | int batch_change_meta_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
using namespace batchchangemeta; | using namespace batchchangemeta; | |
char countmess[80]; | ch countmess[80]; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (zd->zstat == 1) // full list | if (zd->zstat == 1) // full list | |
{ | { | |
zd->zstat = 0; // keep dialog active | zd->zstat = 0; // keep dialog active | |
zmessageACK(Mwin,"The command: $ man Image::ExifTool::TagNames \n" | zmessageACK(Mwin,"The command: $ man Image::ExifTool::TagNames \n" | |
"will show over 15000 \"standard\" tag names"); | "will show over 15000 \"standard\" tag/key names"); | |
return 1; | return 1; | |
} | } | |
if (strmatch(event,"files")) // select images to process | if (strmatch(event,"files")) // select images to process | |
{ | { | |
zdialog_show(zd,0); // hide parent dialog | zdialog_show(zd,0); // hide parent dialog | |
gallery_select(); // get image file list | gallery_select(); // get image file list | |
zdialog_show(zd,1); | zdialog_show(zd,1); | |
snprintf(countmess,80,"%d image files selected",GScount); | snprintf(countmess,80,"%d image files selected",GScount); | |
zdialog_stuff(zd,"labcount",countmess); | zdialog_stuff(zd,"labcount",countmess); | |
} | } | |
return 1; | return 1; | |
} | } | |
// get clicked tag name from short list and insert into dialog | // get clicked key name from short list and insert into dialog | |
void batch_change_meta_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ) | void batch_change_meta_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ) | |
{ | { | |
using namespace batchchangemeta; | using namespace batchchangemeta; | |
int ii; | int ii; | |
char *pp; | ch *pp; | |
char knameX[12] = "knameX"; | ch knameX[8] = "knameX"; | |
char kname[80]; | ch kname[metakeyXcc]; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
pp = textwidget_line(widget,line,1); // get clicked line, highlight | pp = textwidget_line(widget,line,1); // get clicked line, highlight | |
if (! pp || ! *pp) return; | if (! pp || ! *pp) return; | |
textwidget_highlight_line(widget,line); | textwidget_highlight_line(widget,line); | |
for (ii = 0; ii < 10; ii++) { // find 1st empty dialog key name | for (ii = 0; ii < 10; ii++) { // find 1st empty dialog key name | |
knameX[7] = '0' + ii; | knameX[5] = '0' + ii; | |
zdialog_fetch(zd,knameX,kname,80); | zdialog_fetch(zd,knameX,kname,metakeyXcc); | |
if (*kname <= ' ') break; | if (*kname <= ' ') break; | |
} | } | |
if (ii < 10) zdialog_stuff(zd,knameX,pp); | if (ii < 10) zdialog_stuff(zd,knameX,pp); | |
return; | return; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// batch report metadata for selected image files | // batch report metadata for selected image files | |
// menu function | // menu function | |
void m_batch_report_meta(GtkWidget *, cchar *menu) | void m_batch_report_meta(GtkWidget *, ch *menu) | |
{ | { | |
int batch_report_meta_dialog_event(zdialog *zd, cchar *event); | int batch_report_meta_dialog_event(zdialog *zd, ch *event); | |
zdialog *zd, *zd2; | zdialog *zd, *zdpop; | |
char *file, text[100]; | ch *file, text[100]; | |
int zstat, ff, ii, err; | int zstat, ff, ii, err; | |
int brm, brmx = 20; | int brm, brmx = 20; | |
char **itemlist, *knamex[brmx], *kvalx[brmx]; | ch **itemlist, *knamex[brmx], *kvalx[brmx]; | |
F1_help_topic = "batch report meta"; | F1_help_topic = "batch report meta"; | |
Plog(1,"m_batch_report_meta \n"); | Plog(1,"m_batch_report_meta \n"); | |
if (Fblock(0,"blocked edits")) return; // check nothing pending | if (Fblock(0,"blocked edits")) return; // check nothing pending | |
/*** | /*** | |
____________________________________________ | ____________________________________________ | |
| Batch Report Metadata | | | Batch Report Metadata | | |
skipping to change at line 3592 | skipping to change at line 3586 | |
for (ii = 0; ii < brm; ii++) | for (ii = 0; ii < brm; ii++) | |
knamex[ii] = itemlist[ii]; | knamex[ii] = itemlist[ii]; | |
if (itemlist) zfree(itemlist); | if (itemlist) zfree(itemlist); | |
if (! brm) { | if (! brm) { | |
zmessageACK(Mwin,"no metadata items to report"); | zmessageACK(Mwin,"no metadata items to report"); | |
return; | return; | |
} | } | |
zd2 = popup_report_open("metadata report",Mwin,600,400,0,0,"Save","OK",0); // log report 22.15 | zdpop = popup_report_open("metadata report",Mwin,600,400,0,0,"Save","OK",0); // log report 22.15 | |
for (ff = 0; ff < GScount; ff++) // loop selected files | for (ff = 0; ff < GScount; ff++) // loop selected files | |
{ | { | |
zmainloop(); // keep GTK alive | zmainloop(); // keep GTK alive | |
popup_report_write2(zd2,0,"\n"); | if (! zdialog_valid(zdpop)) break; | |
// blank line separator | // report canceled 23.1 | |
popup_report_write2(zdpop,0,"\n"); | ||
// blank line separator | ||
file = GSfiles[ff]; | file = GSfiles[ff]; | |
popup_report_write2(zd2,0,"%s \n",file); | popup_report_write2(zdpop,0,"%s \n",file); | |
if (image_file_type(file) != IMAGE) { // file deleted? | if (image_file_type(file) != IMAGE) { // file deleted? | |
popup_report_write2(zd2,0,"*** invalid file \n"); | popup_report_write2(zdpop,0,"*** invalid file \n"); | |
continue; | continue; | |
} | } | |
err = exif_get(file,(cchar **) knamex,kvalx,brm); | err = meta_get1(file,knamex,kvalx,brm); | |
// get all report items | // get all report items | |
if (err) { | if (err) continue; | |
popup_report_write2(zd2,0,"exif failure"); | ||
continue; | ||
} | ||
for (ii = 0; ii < brm; ii++) // output keyword names and values | for (ii = 0; ii < brm; ii++) // output key names and values | |
if (kvalx[ii]) | if (kvalx[ii]) | |
popup_report_write2(zd2,0,"%-24s : %s \n",knamex[ii],kvalx[ii]); | popup_report_write2(zdpop,0,"%-24s : %s \n",knamex[ii],kvalx[ii]); | |
for (ii = 0; ii < brm; ii++) // free memory | for (ii = 0; ii < brm; ii++) // free memory | |
if (kvalx[ii]) zfree(kvalx[ii]); | if (kvalx[ii]) zfree(kvalx[ii]); | |
} | } | |
popup_report_write2(zd2,0," *** %s \n","COMPLETED"); | if (! zdialog_valid(zdpop)) | |
// 23.1 | ||
Plog(0,"*** report cancelled \n"); | ||
else | ||
popup_report_write2(zdpop,0," *** %s \n","COMPLETED"); | ||
return; | return; | |
} | } | |
// dialog event and completion function | // dialog event and completion function | |
int batch_report_meta_dialog_event(zdialog *zd, cchar *event) | int batch_report_meta_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
char countmess[80]; | ch countmess[80]; | |
zlist_t *mlist; | zlist_t *mlist; | |
int nn; | int nn; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (zd->zstat) zdialog_destroy(zd); | if (zd->zstat) zdialog_destroy(zd); | |
if (strmatch(event,"files")) // select images to process | if (strmatch(event,"files")) // select images to process | |
{ | { | |
zdialog_show(zd,0); // hide parent dialog | zdialog_show(zd,0); // hide parent dialog | |
skipping to change at line 3653 | skipping to change at line 3649 | |
zdialog_show(zd,1); | zdialog_show(zd,1); | |
snprintf(countmess,80,"%d image files selected",GScount); | snprintf(countmess,80,"%d image files selected",GScount); | |
zdialog_stuff(zd,"labcount",countmess); | zdialog_stuff(zd,"labcount",countmess); | |
} | } | |
if (strmatch(event,"edit")) // select metadata items to report | if (strmatch(event,"edit")) // select metadata items to report | |
{ | { | |
mlist = zlist_from_file(meta_report_items_file); // load metadata report list | mlist = zlist_from_file(meta_report_items_file); // load metadata report list | |
if (! mlist) mlist = zlist_new(0); | if (! mlist) mlist = zlist_new(0); | |
nn = select_meta_keys(mlist,MRmax,0); // user edit of metadata list | nn = select_meta_keys(mlist,maxbatchkeys,0); // user edit of metadata list | |
if (nn) zlist_to_file(mlist,meta_report_items_file); // replace file | if (nn) zlist_to_file(mlist,meta_report_items_file); // replace file | |
zlist_delete(mlist); | zlist_delete(mlist); | |
} | } | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// move metadata from selected input keys to a selected output key. | // move metadata from selected input keys to a selected output key. | |
// purpose: consolidate multiple overlapping/redundant keys into a chosen key. | // purpose: consolidate multiple overlapping/redundant keys into a chosen key. | |
// (compensate EXIF/IPTC/XML standards chaos) | // (compensate metadata standards chaos) | |
namespace batch_meta_mover_names | namespace batch_meta_mover_names | |
{ | { | |
int Nfiles, Fallfiles, Freportonly; | int Nfiles, Fallfiles, Freportonly; | |
char inputkeys[200], outputkey[50]; | ch inputkeys[200], outputkey[50]; | |
cchar *inkeys[10], *outkey[1], *outkdata[1]; | ch *inkeys[10], *outkey[1], *outkdata[1]; | |
char *inkdata[10]; | ch *inkdata[10]; | |
zdialog *zd, *zd2; | zdialog *zd, *zdpop; | |
} | } | |
// menu function | // menu function | |
void m_batch_meta_mover(GtkWidget *, cchar *menu) | void m_batch_meta_mover(GtkWidget *, ch *menu) | |
{ | { | |
using namespace batch_meta_mover_names; | using namespace batch_meta_mover_names; | |
int batch_meta_mover_dialog_event(zdialog *zd, cchar *event); | int batch_meta_mover_dialog_event(zdialog *zd, ch *event); | |
cchar *title = "Metadata Mover"; | ch *title = "Metadata Mover"; | |
cchar *text1 = "Move input keys to output key \n" | ch *text1 = "Move input keys to output key \n" | |
"Input keys may include output key \n" | "Input keys may include output key \n" | |
"Output data order is input key order."; | "Output data order is input key order."; | |
cchar *text2 = "Report only - make no data changes."; | ch *text2 = "Report only - make no data changes."; | |
char outputkdata[2000]; | ch outputkdata[2000]; | |
char *file, *pp; | ch *file, *pp; | |
int zstat, yn, Fvalid, err; | int zstat, yn, Fvalid, err; | |
int Nkeys, cc, ii, iimax, jj; | int Nkeys, cc, ii, iimax, jj; | |
F1_help_topic = "batch move meta"; | F1_help_topic = "batch move meta"; | |
Plog(1,"m_batch_meta_mover"); | Plog(1,"m_batch_meta_mover"); | |
if (Fblock("batch_meta_mover","block edits")) return; // check pending, block | if (Fblock("batch_meta_mover","block edits")) return; // check pending, block | |
/*** | /*** | |
skipping to change at line 3774 | skipping to change at line 3770 | |
retry: | retry: | |
zd->zstat = 0; | zd->zstat = 0; | |
zstat = zdialog_wait(zd); | zstat = zdialog_wait(zd); | |
if (zstat != 1) { | if (zstat != 1) { | |
zdialog_free(zd); | zdialog_free(zd); | |
goto report_end; | goto report_end; | |
} | } | |
if (zd2) popup_report_close(zd2,0); | if (zdpop) popup_report_close(zdpop,0); | |
zmainsleep(0.5); | zmainsleep(0.5); | |
zd2 = popup_report_open("batch metadata mover",Mwin,800,600,0,0,"OK",0); // log report 22.15 | zdpop = popup_report_open("batch metadata mover",Mwin,800,600,0,0,"OK",0); // log report 22.15 | |
Fvalid = 1; | Fvalid = 1; | |
if (Fallfiles) | if (Fallfiles) | |
popup_report_write(zd2,0,"all files will be processed \n"); | popup_report_write(zdpop,0,"all files will be processed \n"); | |
else if (Nfiles) | else if (Nfiles) | |
popup_report_write(zd2,0,"%d files will be processed \n",Nfiles); | popup_report_write(zdpop,0,"%d files will be processed \n",Nfiles); | |
else { | else { | |
popup_report_write(zd2,1,"*** no files are selected \n"); | popup_report_write(zdpop,1,"*** no files are selected \n"); | |
Fvalid = 0; | Fvalid = 0; | |
} | } | |
popup_report_write(zd2,0,"input keys: %s \n",inputkeys); | popup_report_write(zdpop,0,"input keys: %s \n",inputkeys); | |
popup_report_write(zd2,0,"output key: %s \n",outputkey); | popup_report_write(zdpop,0,"output key: %s \n",outputkey); | |
for (ii = 0; ii < 10; ii++) { // parse input key list | for (ii = 0; ii < 10; ii++) { // parse input key list | |
pp = substring(inputkeys,",",ii+1); | pp = substring(inputkeys,",",ii+1); | |
if (! pp) break; | if (! pp) break; | |
inkeys[ii] = zstrdup(pp,"meta-mover"); | inkeys[ii] = zstrdup(pp,"meta-mover"); | |
if (strlen(pp) < 2) { | if (strlen(pp) < 2) { | |
popup_report_write(zd2,1,"*** input key %s is invalid \n",pp); | popup_report_write(zdpop,1,"*** input key %s is invalid \n",pp); | |
Fvalid = 0; | Fvalid = 0; | |
} | } | |
} | } | |
Nkeys = ii; | Nkeys = ii; | |
if (Nkeys < 1) { | if (Nkeys < 1) { | |
popup_report_write(zd2,1,"*** input keys are missing \n"); | popup_report_write(zdpop,1,"*** input keys are missing \n"); | |
Fvalid = 0; | Fvalid = 0; | |
} | } | |
if (strlen(outputkey) < 2) { | if (strlen(outputkey) < 2) { | |
popup_report_write(zd2,1,"*** output key is missing \n"); | popup_report_write(zdpop,1,"*** output key is missing \n"); | |
Fvalid = 0; | Fvalid = 0; | |
} | } | |
if (! Fvalid) goto retry; | if (! Fvalid) goto retry; | |
yn = zmessageYN(Mwin,"continue?"); | yn = zmessageYN(Mwin,"continue?"); | |
if (! yn) goto retry; | if (! yn) goto retry; | |
zdialog_free(zd); | zdialog_free(zd); | |
Fescape = 1; | Fwatchescape = 1; | |
// can interrupt with escape key | // can interrupt with escape key 23.1 | |
Fescape = 0; | ||
for (ii = 0; ii < Nkeys; ii++) // initz. no input key metadata | for (ii = 0; ii < Nkeys; ii++) // initz. no input key metadata | |
inkdata[ii] = 0; | inkdata[ii] = 0; | |
iimax = 0; // set up file loop | iimax = 0; // set up file loop | |
if (Nfiles) iimax = Nfiles; | if (Nfiles) iimax = Nfiles; | |
if (Fallfiles) iimax = Nxxrec; | if (Fallfiles) iimax = Nxxrec; | |
for (ii = 0; ii < iimax; ii++) // loop all files | for (ii = 0; ii < iimax; ii++) // loop all files | |
{ | { | |
zmainloop(); | zmainloop(); | |
if (Fescape > 1) { | if (! zdialog_valid(zdpop)) break; | |
// escape key interrupt | // report canceled 23.1 | |
popup_report_write2(zd2,1,"*** interrupted \n"); | ||
if (Fescape) { | ||
// escape key interrupt | ||
popup_report_write2(zdpop,1,"*** interrupted \n"); | ||
Fwatchescape = Fescape = 0; | ||
break; | break; | |
} | } | |
file = 0; | file = 0; | |
if (Nfiles) file = GSfiles[ii]; // selected files loop | if (Nfiles) file = GSfiles[ii]; // selected files loop | |
if (Fallfiles) file = xxrec_tab[ii]->file; // all files loop | if (Fallfiles) file = xxrec_tab[ii]->file; // all files loop | |
popup_report_write(zd2,0,"------------------------- \n"); | popup_report_write(zdpop,0,"------------------------- \n"); | |
popup_report_write(zd2,0,"file: %s \n",file); | popup_report_write(zdpop,0,"file: %s \n",file); | |
// log file name | // log file name | |
err = exif_get(file,inkeys,inkdata,Nkeys); | err = meta_get1(file,inkeys,inkdata,Nkeys); | |
// get input keys metadata | // get input keys metadata | |
if (err) { | if (err) continue; | |
popup_report_write(zd2,1," *** metadata error \n"); | ||
continue; | ||
} | ||
for (jj = 0; jj < Nkeys; jj++) // log input keys having data | for (jj = 0; jj < Nkeys; jj++) // log input keys having data | |
if (inkdata[jj]) | if (inkdata[jj]) | |
popup_report_write(zd2,0,"input key: %s data: %s \n", | popup_report_write(zdpop,0,"input key: %s data: %s \n", | |
inkeys[jj], inkdata[jj]); | inkeys[jj], inkdata[jj]); | |
*outputkdata = 0; | *outputkdata = 0; | |
for (jj = 0; jj < Nkeys; jj++) { | for (jj = 0; jj < Nkeys; jj++) { | |
if (inkdata[jj]) // catenate input data with "\n" delims | if (inkdata[jj]) // catenate input data with "\n" delims | |
strncatv(outputkdata,2000,inkdata[jj],"\\n",0); // (real EOL not allowed in metadata) | strncatv(outputkdata,2000,inkdata[jj],"\\n",0); // (real EOL not allowed in metadata) | |
} | } | |
cc = strlen(outputkdata); // catenated input keys data | cc = strlen(outputkdata); // catenated input keys data | |
if (cc) { | if (cc) { | |
cc -= 2; | cc -= 2; | |
outputkdata[cc] = 0; // remove last "\n" | outputkdata[cc] = 0; // remove last "\n" | |
} | } | |
outkey[0] = outputkey; // set output key metadata from inputs | outkey[0] = outputkey; // set output key metadata from inputs | |
outkdata[0] = outputkdata; | outkdata[0] = outputkdata; | |
if (cc == 0) outkdata[0] = 0; // no inputs --> erase output | if (cc == 0) outkdata[0] = 0; // no inputs --> erase output | |
popup_report_write2(zd2,0,"output key: %s data: %s \n", // log output key and data | popup_report_write2(zdpop,0,"output key: %s data: %s \n", // log output key and data | |
outputkey, outputkdata); | outputkey, outputkdata); | |
if (! Freportonly) { // set output key data | if (! Freportonly) { // set output key data | |
err = exif_put(file,outkey,outkdata,1); | err = meta_put(file,outkey,outkdata,1); | |
if (err) popup_report_write2(zd2,1," *** exif update error \n"); | if (err) popup_report_write2(zdpop,1," *** metadata update error \n"); | |
// 23.0 | // 23.0 | |
} | } | |
for (jj = 0; jj < Nkeys; jj++) { // free memory | for (jj = 0; jj < Nkeys; jj++) { // free memory | |
if (inkdata[jj]) zfree(inkdata[jj]); | if (inkdata[jj]) zfree(inkdata[jj]); | |
inkdata[jj] = 0; | inkdata[jj] = 0; | |
} | } | |
} | } | |
popup_report_write2(zd2,1," *** %s \n","COMPLETED"); | if (! zdialog_valid(zdpop)) | |
// 23.1 | ||
Plog(0,"*** report cancelled \n"); | ||
else popup_report_write2(zdpop,1," *** COMPLETED \n"); | ||
report_end: | report_end: | |
Fblock("batch_meta_mover",0); | Fblock("batch_meta_mover",0); | |
Fescape = 0; | ||
return; | return; | |
} | } | |
// dialog event and completion callback function | // dialog event and completion callback function | |
int batch_meta_mover_dialog_event(zdialog *zd, cchar *event) | int batch_meta_mover_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
using namespace batch_meta_mover_names; | using namespace batch_meta_mover_names; | |
char countmess[80]; | ch countmess[80]; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (strmatch(event,"selectfiles")) // select files to process | if (strmatch(event,"selectfiles")) // select files to process | |
{ | { | |
zdialog_show(zd,0); // hide parent dialog | zdialog_show(zd,0); // hide parent dialog | |
gallery_select(); // get image file list | gallery_select(); // get image file list | |
zdialog_show(zd,1); | zdialog_show(zd,1); | |
Nfiles = GScount; | Nfiles = GScount; | |
skipping to change at line 3941 | skipping to change at line 3939 | |
zdialog_fetch(zd,"Freportonly",Freportonly); | zdialog_fetch(zd,"Freportonly",Freportonly); | |
} | } | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// batch geotags - set geotags for multiple image files | // batch geotags - set geotags for multiple image files | |
void m_batch_geotags(GtkWidget *, cchar *menu) | void m_batch_geotags(GtkWidget *, ch *menu) | |
{ | { | |
int batch_geotags_dialog_event(zdialog *zd, cchar *event); | int batch_geotags_dialog_event(zdialog *zd, ch *event); | |
int ii, err; | int ii, err; | |
char *file; | ch *file; | |
char location[100], country[100]; | ch location[100], country[100]; | |
char lati[20], longi[20], text[100]; | ch lati[20], longi[20], text[100]; | |
zdialog *zd, *zd2; | zdialog *zd, *zdpop; | |
F1_help_topic = "batch geotags"; | F1_help_topic = "batch geotags"; | |
Plog(1,"m_batch_geotags \n"); | Plog(1,"m_batch_geotags \n"); | |
if (! Findexvalid) { | if (! Findexvalid) { | |
zmessageACK(Mwin,"image index disabled"); // no image index | zmessageACK(Mwin,"image index disabled"); // no image index | |
return; | return; | |
} | } | |
skipping to change at line 4023 | skipping to change at line 4021 | |
zdialog_fetch(zd,"location",location,100); // get location from dialog | zdialog_fetch(zd,"location",location,100); // get location from dialog | |
zdialog_fetch(zd,"country",country,100); | zdialog_fetch(zd,"country",country,100); | |
zdialog_fetch(zd,"lati",lati,20); // and latitude, longitude | zdialog_fetch(zd,"lati",lati,20); // and latitude, longitude | |
zdialog_fetch(zd,"longi",longi,20); | zdialog_fetch(zd,"longi",longi,20); | |
zdialog_free(zd); // kill dialog | zdialog_free(zd); // kill dialog | |
zd = zd_mapgeotags = 0; | zd = zd_mapgeotags = 0; | |
if (GScount == 0) goto cleanup; | if (GScount == 0) goto cleanup; | |
zd2 = popup_report_open("Adding Geotags",Mwin,500,200,0,0,"OK",0); // log report 22.15 | zdpop = popup_report_open("Adding Geotags",Mwin,500,200,0,0,"OK",0); // log report 22.15 | |
for (ii = 0; ii < GScount; ii++) // loop all selected files | for (ii = 0; ii < GScount; ii++) // loop all selected files | |
{ | { | |
zmainloop(); // keep GTK alive | zmainloop(); // keep GTK alive | |
if (! zdialog_valid(zdpop)) break; | ||
// report canceled 23.1 | ||
file = GSfiles[ii]; // display image | file = GSfiles[ii]; // display image | |
err = f_open(file,0,0,0); | err = f_open(file,0,0,0); | |
if (err) continue; | if (err) continue; | |
err = access(file,W_OK); // test file can be written by me | err = access(file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
popup_report_write2(zd2,0,"%s: %s \n","no write permission",file); | popup_report_write2(zdpop,0,"%s: %s \n","no write permission",file); | |
continue; | continue; | |
} | } | |
*meta_location = *meta_country = *meta_lati = *meta_longi = 0; | *meta_location = *meta_country = *meta_lati = *meta_longi = 0; | |
if (*location) strncpy0(meta_location,location,100); // save geotags in image file EXIF | if (*location) strncpy0(meta_location,location,100); // save geotags in image file metadata | |
if (*country) strncpy0(meta_country,country,100); // and in image index file | if (*country) strncpy0(meta_country,country,100); // and in image index file | |
if (*lati) strncpy0(meta_lati,lati,20); // do not stuff missing data | if (*lati) strncpy0(meta_lati,lati,20); // do not stuff missing data | |
if (*longi) strncpy0(meta_longi,longi,20); | if (*longi) strncpy0(meta_longi,longi,20); | |
Fmetamod++; | Fmetamod++; | |
save_filemeta(file); // update file metadata & index | save_filemeta(file); // update file metadata & index | |
popup_report_write2(zd2,0,"%s \n",file); // report progress | popup_report_write2(zdpop,0,"%s \n",file); // report progress | |
} | } | |
popup_report_write2(zd2,0," *** %s \n","COMPLETED"); | if (! zdialog_valid(zdpop)) | |
// 23.1 | ||
Plog(0,"*** report cancelled \n"); | ||
else popup_report_write2(zdpop,0," *** COMPLETED \n"); | ||
cleanup: | cleanup: | |
Fblock("batch_geotags",0); | Fblock("batch_geotags",0); | |
if (zd) zdialog_free(zd); | if (zd) zdialog_free(zd); | |
zd_mapgeotags = 0; | zd_mapgeotags = 0; | |
// if (FGWM == 'G') gallery(0,"paint",-1); // refresh gallery remove 22.50 | ||
return; | return; | |
} | } | |
// batch_geotags dialog event function | // batch_geotags dialog event function | |
int batch_geotags_dialog_event(zdialog *zd, cchar *event) | int batch_geotags_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
int yn, zstat, err; | int yn, zstat, err; | |
char countmess[80]; | ch countmess[80]; | |
char location[100], country[100]; | ch location[100], country[100]; | |
char lati[20], longi[20]; | ch lati[20], longi[20]; | |
float flati, flongi; | float flati, flongi; | |
if (strmatch(event,"escape")) zd->zstat = -2; // escape key | if (strmatch(event,"escape")) zd->zstat = -2; // escape key | |
if (strmatch(event,"files")) // select images to add tags | if (strmatch(event,"files")) // select images to add tags | |
{ | { | |
zdialog_show(zd,0); // hide parent dialog | zdialog_show(zd,0); // hide parent dialog | |
gallery_select(); // get file list from user | gallery_select(); // get file list from user | |
zdialog_show(zd,1); | zdialog_show(zd,1); | |
skipping to change at line 4170 | skipping to change at line 4171 | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// Group images by location and date, with a count of images in each group. | // Group images by location and date, with a count of images in each group. | |
// Click on a group to get a thumbnail gallery of all images in the group. | // Click on a group to get a thumbnail gallery of all images in the group. | |
namespace locs_names | namespace locs_names | |
{ | { | |
struct grec_t { // image geotags data | struct grec_t { // image geotags data | |
char *location, *country; | ch *location, *country; | |
// group location | // group location | |
char pdate[12]; | ch pdate[12]; | |
// nominal group date, yyyymmdd | // nominal group date, yyyymmdd | |
int lodate, hidate; // range, days since 0 CE | int lodate, hidate; // range, days since 0 CE | |
int count; // images in group | int count; // images in group | |
}; | }; | |
grec_t *grec = 0; | grec_t *grec = 0; | |
zlist_t *filelist = 0; | zlist_t *filelist = 0; | |
int Ngrec = 0; | int Ngrec = 0; | |
int locs_groupby, locs_daterange; | int locs_groupby, locs_daterange; | |
int Fusesearch, Nsearch; | int Fusesearch, Nsearch; | |
int pline; | int pline; | |
int locs_comp(cchar *rec1, cchar *rec2); | int locs_comp(ch *rec1, ch *rec2); | |
int locs_comp2(cchar *rec1, cchar *rec2); | int locs_comp2(ch *rec1, ch *rec2); | |
void locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | void locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | |
int locs_getdays(cchar *date); | int locs_getdays(ch *date); | |
} | } | |
// menu function | // menu function | |
void m_meta_places_dates(GtkWidget *, cchar *) | void m_meta_places_dates(GtkWidget *, ch *) | |
{ | { | |
using namespace locs_names; | using namespace locs_names; | |
zdialog *zd, *zd2; | zdialog *zd, *zdpop; | |
int zstat, ii, jj, cc, cc1, cc2; | int zstat, ii, jj, cc, cc1, cc2; | |
int ww, iix, iig, newgroup; | int ww, iix, iig, newgroup; | |
char country[100], location[100], pdate[12]; | ch country[100], location[100], pdate[12]; | |
char albumfile[200]; | ch albumfile[200]; | |
xxrec_t *xxrec; | xxrec_t *xxrec; | |
F1_help_topic = "places/dates"; | F1_help_topic = "places/dates"; | |
Plog(1,"m_meta_places_dates \n"); | Plog(1,"m_meta_places_dates \n"); | |
if (! Findexvalid) { | if (! Findexvalid) { | |
zmessageACK(Mwin,"image index disabled"); // no image index | zmessageACK(Mwin,"image index disabled"); // no image index | |
return; | return; | |
} | } | |
skipping to change at line 4351 | skipping to change at line 4352 | |
strncpy0(grec[ii].pdate,xxrec->pdate,9); // photo date, truncate to yyyymmdd | strncpy0(grec[ii].pdate,xxrec->pdate,9); // photo date, truncate to yyyymmdd | |
if (grec[ii].pdate[0] == 0) strcpy(grec[ii].pdate,"null"); | if (grec[ii].pdate[0] == 0) strcpy(grec[ii].pdate,"null"); | |
grec[ii].lodate = locs_getdays(xxrec->pdate); // days since 0 CE | grec[ii].lodate = locs_getdays(xxrec->pdate); // days since 0 CE | |
grec[ii].hidate = grec[ii].lodate; | grec[ii].hidate = grec[ii].lodate; | |
} | } | |
Ngrec = Nxxrec; | Ngrec = Nxxrec; | |
} | } | |
if (Ngrec > 1) // sort grecs by country/location/date | if (Ngrec > 1) // sort grecs by country/location/date | |
HeapSort((char *) grec, sizeof(grec_t), Ngrec, locs_comp); | HeapSort((ch *) grec, sizeof(grec_t), Ngrec, locs_comp); | |
iig = 0; // 1st group from grec[0] | iig = 0; // 1st group from grec[0] | |
grec[iig].count = 1; // group count = 1 | grec[iig].count = 1; // group count = 1 | |
for (iix = 1; iix < Ngrec; iix++) // scan following grecs | for (iix = 1; iix < Ngrec; iix++) // scan following grecs | |
{ | { | |
newgroup = 0; | newgroup = 0; | |
if (! strmatch(grec[iix].country,grec[iig].country)) | if (! strmatch(grec[iix].country,grec[iig].country)) | |
newgroup = 1; // new country >> new group | newgroup = 1; // new country >> new group | |
skipping to change at line 4396 | skipping to change at line 4397 | |
} | } | |
} | } | |
Ngrec = iig + 1; // unique groups count | Ngrec = iig + 1; // unique groups count | |
if (locs_groupby == 1) ww = 350; // group country | if (locs_groupby == 1) ww = 350; // group country | |
if (locs_groupby == 2) ww = 600; // group country/location | if (locs_groupby == 2) ww = 600; // group country/location | |
if (locs_groupby == 3) ww = 650; // group country/location/date-range | if (locs_groupby == 3) ww = 650; // group country/location/date-range | |
if (locs_groupby == 4) ww = 650; // group date-range/country/location | if (locs_groupby == 4) ww = 650; // group date-range/country/location | |
zd2 = popup_report_open("Image Locations",Mwin,ww,400, // write groups to popup window | zdpop = popup_report_open("Image Locations",Mwin,ww,400, // write groups to popup window | |
1,locs_clickfunc,"Find","OK",0); // 22.15 | 1,locs_clickfunc,"Find","OK",0); // 22.15 | |
if (locs_groupby == 1) // group by country | if (locs_groupby == 1) // group by country | |
{ | { | |
popup_report_header(zd2,1,"%-30s %5s","Country","Count"); | popup_report_header(zdpop,1,"%-30s %5s","Country","Count"); | |
for (iig = 0; iig < Ngrec; iig++) | for (iig = 0; iig < Ngrec; iig++) | |
{ | { | |
utf8substring(country,grec[iig].country,0,30); | utf8substring(country,grec[iig].country,0,30); | |
cc1 = 30 + strlen(country) - utf8len(country); | cc1 = 30 + strlen(country) - utf8len(country); | |
popup_report_write2(zd2,0,"%-*s %5d \n",cc1,country,grec[iig].count); | popup_report_write2(zdpop,0,"%-*s %5d \n",cc1,country,grec[iig].count) ; | |
} | } | |
} | } | |
if (locs_groupby == 2) // group by country/location | if (locs_groupby == 2) // group by country/location | |
{ | { | |
popup_report_header(zd2,1,"%-30s %-30s %5s","Country","Location","Count" ); | popup_report_header(zdpop,1,"%-30s %-30s %5s","Country","Location","Coun t"); | |
for (iig = 0; iig < Ngrec; iig++) | for (iig = 0; iig < Ngrec; iig++) | |
{ | { | |
utf8substring(country,grec[iig].country,0,30); | utf8substring(country,grec[iig].country,0,30); | |
cc1 = 30 + strlen(country) - utf8len(country); | cc1 = 30 + strlen(country) - utf8len(country); | |
utf8substring(location,grec[iig].location,0,30); | utf8substring(location,grec[iig].location,0,30); | |
cc2 = 30 + strlen(location) - utf8len(location); | cc2 = 30 + strlen(location) - utf8len(location); | |
popup_report_write2(zd2,0,"%-*s %-*s %5d \n", | popup_report_write2(zdpop,0,"%-*s %-*s %5d \n", | |
cc1,country,cc2,location,grec[iig].count); | cc1,country,cc2,location,grec[iig].count); | |
} | } | |
} | } | |
if (locs_groupby == 3) // group by country/location/date-range | if (locs_groupby == 3) // group by country/location/date-range | |
{ | { | |
popup_report_header(zd2,1,"%-26s %-26s %-10s %5s","Country","Location" ,"Date","Count"); | popup_report_header(zdpop,1,"%-26s %-26s %-10s %5s","Country","Locatio n","Date","Count"); | |
for (iig = 0; iig < Ngrec; iig++) | for (iig = 0; iig < Ngrec; iig++) | |
{ | { | |
utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names | utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names | |
cc1 = 26 + strlen(country) - utf8len(country); | cc1 = 26 + strlen(country) - utf8len(country); | |
utf8substring(location,grec[iig].location,0,26); | utf8substring(location,grec[iig].location,0,26); | |
cc2 = 26 + strlen(location) - utf8len(location); | cc2 = 26 + strlen(location) - utf8len(location); | |
strncpy0(pdate,grec[iig].pdate,9); // date, yyyymmdd | strncpy0(pdate,grec[iig].pdate,9); // date, yyyymmdd | |
if (! strmatch(pdate,"null")) { | if (! strmatch(pdate,"null")) { | |
memmove(pdate+8,pdate+6,2); // convert to yyyy-mm-dd 22.50 | memmove(pdate+8,pdate+6,2); // convert to yyyy-mm-dd 22.50 | |
memmove(pdate+5,pdate+4,2); | memmove(pdate+5,pdate+4,2); | |
pdate[4] = pdate[7] = '-'; | pdate[4] = pdate[7] = '-'; | |
pdate[10] = 0; | pdate[10] = 0; | |
} | } | |
popup_report_write2(zd2,0,"%-*s %-*s %-10s %6d \n", | popup_report_write2(zdpop,0,"%-*s %-*s %-10s %6d \n", | |
cc1,country,cc2,location,pdate,grec[iig].count); | cc1,country,cc2,location,pdate,grec[iig].count); | |
} | } | |
} | } | |
if (locs_groupby == 4) // group by date-range/country/location | if (locs_groupby == 4) // group by date-range/country/location | |
{ | { | |
if (Ngrec > 1) // re-sort by date/country/location | if (Ngrec > 1) // re-sort by date/country/location | |
HeapSort((char *) grec, sizeof(grec_t), Ngrec, locs_comp2); | HeapSort((ch *) grec, sizeof(grec_t), Ngrec, locs_comp2); | |
popup_report_header(zd2,1,"%-10s %-26s %-26s %5s","Date","Country","Lo cation","Count"); | popup_report_header(zdpop,1,"%-10s %-26s %-26s %5s","Date","Country"," Location","Count"); | |
for (iig = 0; iig < Ngrec; iig++) | for (iig = 0; iig < Ngrec; iig++) | |
{ | { | |
utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names | utf8substring(country,grec[iig].country,0,26); // get graphic cc for UTF-8 names | |
cc1 = 26 + strlen(country) - utf8len(country); | cc1 = 26 + strlen(country) - utf8len(country); | |
utf8substring(location,grec[iig].location,0,26); | utf8substring(location,grec[iig].location,0,26); | |
cc2 = 26 + strlen(location) - utf8len(location); | cc2 = 26 + strlen(location) - utf8len(location); | |
strncpy0(pdate,grec[iig].pdate,9); // date, yyyymmdd | strncpy0(pdate,grec[iig].pdate,9); // date, yyyymmdd | |
if (! strmatch(pdate,"null")) { | if (! strmatch(pdate,"null")) { | |
memmove(pdate+8,pdate+6,2); // convert to yyyy-mm-dd 22.50 | memmove(pdate+8,pdate+6,2); // convert to yyyy-mm-dd 22.50 | |
memmove(pdate+5,pdate+4,2); | memmove(pdate+5,pdate+4,2); | |
pdate[4] = pdate[7] = '-'; | pdate[4] = pdate[7] = '-'; | |
pdate[10] = 0; | pdate[10] = 0; | |
} | } | |
popup_report_write2(zd2,0,"%-10s %-*s %-*s %6d \n", | popup_report_write2(zdpop,0,"%-10s %-*s %-*s %6d \n", | |
pdate,cc1,country,cc2,location,grec[iig].count); | pdate,cc1,country,cc2,location,grec[iig].count); | |
} | } | |
} | } | |
pline = 0; // initial report line | pline = 0; // initial report line | |
Fblock("places_dates",0); | Fblock("places_dates",0); | |
return; | return; | |
} | } | |
// Compare 2 grec records by geotags and date, | // Compare 2 grec records by geotags and date, | |
// return < 0 = 0 > 0 for rec1 < = > rec2. | // return < 0 = 0 > 0 for rec1 < = > rec2. | |
int locs_names::locs_comp(cchar *rec1, cchar *rec2) | int locs_names::locs_comp(ch *rec1, ch *rec2) | |
{ | { | |
using namespace locs_names; | using namespace locs_names; | |
int ii; | int ii; | |
char * country1 = ((grec_t *) rec1)->country; | ch * country1 = ((grec_t *) rec1)->country; | |
// compare countries | // compare countries | |
char * country2 = ((grec_t *) rec2)->country; | ch * country2 = ((grec_t *) rec2)->country; | |
ii = strcmp(country1,country2); | ii = strcmp(country1,country2); | |
if (ii) return ii; | if (ii) return ii; | |
char * loc1 = ((grec_t *) rec1)->location; | ch * loc1 = ((grec_t *) rec1)->location; | |
// compare cities | // compare cities | |
char * loc2 = ((grec_t *) rec2)->location; | ch * loc2 = ((grec_t *) rec2)->location; | |
ii = strcmp(loc1,loc2); | ii = strcmp(loc1,loc2); | |
if (ii) return ii; | if (ii) return ii; | |
int date1 = ((grec_t *) rec1)->lodate; // compare dates | int date1 = ((grec_t *) rec1)->lodate; // compare dates | |
int date2 = ((grec_t *) rec2)->lodate; | int date2 = ((grec_t *) rec2)->lodate; | |
ii = date1 - date2; | ii = date1 - date2; | |
return ii; | return ii; | |
} | } | |
// Compare 2 grec records by date and geotags, | // Compare 2 grec records by date and geotags, | |
// return < 0 = 0 > 0 for rec1 < = > rec2. | // return < 0 = 0 > 0 for rec1 < = > rec2. | |
int locs_names::locs_comp2(cchar *rec1, cchar *rec2) | int locs_names::locs_comp2(ch *rec1, ch *rec2) | |
{ | { | |
using namespace locs_names; | using namespace locs_names; | |
int ii; | int ii; | |
int date1 = ((grec_t *) rec1)->lodate; // compare dates | int date1 = ((grec_t *) rec1)->lodate; // compare dates | |
int date2 = ((grec_t *) rec2)->lodate; | int date2 = ((grec_t *) rec2)->lodate; | |
ii = date1 - date2; | ii = date1 - date2; | |
if (ii) return ii; | if (ii) return ii; | |
char * country1 = ((grec_t *) rec1)->country; | ch * country1 = ((grec_t *) rec1)->country; | |
// compare countries | // compare countries | |
char * country2 = ((grec_t *) rec2)->country; | ch * country2 = ((grec_t *) rec2)->country; | |
ii = strcmp(country1,country2); | ii = strcmp(country1,country2); | |
if (ii) return ii; | if (ii) return ii; | |
char * loc1 = ((grec_t *) rec1)->location; | ch * loc1 = ((grec_t *) rec1)->location; | |
// compare cities | // compare cities | |
char * loc2 = ((grec_t *) rec2)->location; | ch * loc2 = ((grec_t *) rec2)->location; | |
ii = strcmp(loc1,loc2); | ii = strcmp(loc1,loc2); | |
return ii; | return ii; | |
} | } | |
// convert yyyymmdd date into days from 0001 C.E. | // convert yyyymmdd date into days from 0001 C.E. | |
int locs_names::locs_getdays(cchar *pdate) | int locs_names::locs_getdays(ch *pdate) | |
{ | { | |
using namespace locs_names; | using namespace locs_names; | |
int year, month, day; | int year, month, day; | |
char temp[8]; | ch temp[8]; | |
int montab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; | int montab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; | |
int elaps; | int elaps; | |
if (strmatch(pdate,"null")) return 0; | if (strmatch(pdate,"null")) return 0; | |
year = month = day = 0; | year = month = day = 0; | |
strncpy0(temp,pdate,5); | strncpy0(temp,pdate,5); | |
year = atoi(temp); | year = atoi(temp); | |
if (year <= 0) year = 1; | if (year <= 0) year = 1; | |
skipping to change at line 4574 | skipping to change at line 4575 | |
} | } | |
// Receive clicks on report window and generate gallery of images | // Receive clicks on report window and generate gallery of images | |
// matching the selected country/location/date | // matching the selected country/location/date | |
void locs_names::locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | void locs_names::locs_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | |
{ | { | |
using namespace locs_names; | using namespace locs_names; | |
int ii, jj, lodate, hidate, datex; | int ii, jj, lodate, hidate, datex; | |
char location[100], country[100]; | ch location[100], country[100]; | |
char places_file[200]; | ch places_file[200]; | |
FILE *fid; | FILE *fid; | |
xxrec_t *xxrec; | xxrec_t *xxrec; | |
if (Fblock("places_dates","block edits")) return; // check pending, block | if (Fblock("places_dates","block edits")) return; // check pending, block | |
if (line >= 0) // line clicked | if (line >= 0) // line clicked | |
{ | { | |
textwidget_scroll(widget,line); // keep line on screen | textwidget_scroll(widget,line); // keep line on screen | |
textwidget_highlight_line(widget,line); // highlight | textwidget_highlight_line(widget,line); // highlight | |
pline = line; // remember last line selected | pline = line; // remember last line selected | |
skipping to change at line 4695 | skipping to change at line 4696 | |
// Click on a report line to get a thumbnail gallery of images. | // Click on a report line to get a thumbnail gallery of images. | |
namespace timeline_names | namespace timeline_names | |
{ | { | |
int Fusesearch, Nsearch = 0; | int Fusesearch, Nsearch = 0; | |
zlist_t *filelist; | zlist_t *filelist; | |
int Nyears = 2100; | int Nyears = 2100; | |
int Nperds = 12 * Nyears; | int Nperds = 12 * Nyears; | |
int Nyears2 = 0; | int Nyears2 = 0; | |
cchar *months = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov De c"; | ch *months = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov De c"; | |
int colpos[14] = { 0, 6, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58, 63, 68 }; | int colpos[14] = { 0, 6, 13, 18, 23, 28, 33, 38, 43, 48, 53, 58, 63, 68 }; | |
} | } | |
// menu function | // menu function | |
void m_meta_timeline(GtkWidget *, cchar *) | void m_meta_timeline(GtkWidget *, ch *) | |
{ | { | |
using namespace timeline_names; | using namespace timeline_names; | |
void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey); | |
char albumfile[200]; | ch albumfile[200]; | |
int Ycount[Nyears], Pcount[Nperds]; // image counts per year and period | int Ycount[Nyears], Pcount[Nperds]; // image counts per year and period | |
int Mcount, Ecount; // counts for missing and invalid dates | int Mcount, Ecount; // counts for missing and invalid dates | |
int ii, jj, cc; | int ii, jj, cc; | |
int yy, mm, pp; | int yy, mm, pp; | |
char pdate[8], nnnnn[8], buff[100]; | ch pdate[8], nnnnn[8], buff[100]; | |
xxrec_t *xxrec; | xxrec_t *xxrec; | |
zdialog *zd2; | zdialog *zdpop; | |
F1_help_topic = "timeline"; | F1_help_topic = "timeline"; | |
Plog(1,"m_meta_timeline \n"); | Plog(1,"m_meta_timeline \n"); | |
if (! Findexvalid) { | if (! Findexvalid) { | |
zmessageACK(Mwin,"image index disabled"); // no image index | zmessageACK(Mwin,"image index disabled"); // no image index | |
return; | return; | |
} | } | |
skipping to change at line 4810 | skipping to change at line 4811 | |
++Ecount; // invalid, add to error count | ++Ecount; // invalid, add to error count | |
continue; | continue; | |
} | } | |
++Ycount[yy]; // add to year totals | ++Ycount[yy]; // add to year totals | |
pp = yy * 12 + mm - 1; // add to period totals | pp = yy * 12 + mm - 1; // add to period totals | |
++Pcount[pp]; | ++Pcount[pp]; | |
} | } | |
} | } | |
zd2 = popup_report_open("Image Timeline",Mwin,600,400,1, // write report to popup window | zdpop = popup_report_open("Image Timeline",Mwin,600,400,1, // write report to popup window | |
timeline_clickfunc,"OK",0); // 22.15 | timeline_clickfunc,"OK",0); // 22.15 | |
popup_report_header(zd2,1,"year count %s",months); // "year count Jan Feb ... " | popup_report_header(zdpop,1,"year count %s",months); // "year count Jan Feb ... " | |
if (Mcount) | if (Mcount) | |
popup_report_write2(zd2,0,"null %-6d \n",Mcount); // images with no date | popup_report_write2(zdpop,0,"null %-6d \n",Mcount); // images with no date | |
if (Ecount) | if (Ecount) | |
popup_report_write2(zd2,0,"invalid %-4d \n",Ecount); // images with invalid date | popup_report_write2(zdpop,0,"invalid %-4d \n",Ecount); // images with invalid date | |
Nyears2 = 0; | Nyears2 = 0; | |
for (yy = 0; yy < Nyears; yy++) // loop years | for (yy = 0; yy < Nyears; yy++) // loop years | |
{ | { | |
if (! Ycount[yy]) continue; // omit years without images | if (! Ycount[yy]) continue; // omit years without images | |
snprintf(buff,100,"%04d %-6d ",yy,Ycount[yy]); // output "yyyy NNNNNN " | snprintf(buff,100,"%04d %-6d ",yy,Ycount[yy]); // output "yyyy NNNNNN " | |
cc = 13; | cc = 13; | |
for (mm = 0; mm < 12; mm++) { // loop months 0 - 11 | for (mm = 0; mm < 12; mm++) { // loop months 0 - 11 | |
pp = yy * 12 + mm; // period | pp = yy * 12 + mm; // period | |
snprintf(nnnnn,6,"%-5d",Pcount[pp]); // output "NNNNN" | snprintf(nnnnn,6,"%-5d",Pcount[pp]); // output "NNNNN" | |
memcpy(buff+cc,nnnnn,5); | memcpy(buff+cc,nnnnn,5); | |
cc += 5; | cc += 5; | |
} | } | |
buff[cc] = 0; | buff[cc] = 0; | |
popup_report_write2(zd2,0,"%s \n",buff); | popup_report_write2(zdpop,0,"%s \n",buff); | |
Nyears2++; // count reported years | Nyears2++; // count reported years | |
} | } | |
Fblock("timeline",0); | Fblock("timeline",0); | |
return; | return; | |
} | } | |
// Receive clicks on report window and generate gallery of images | // Receive clicks on report window and generate gallery of images | |
// matching the selected period | // matching the selected period | |
void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | void timeline_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | |
{ | { | |
using namespace timeline_names; | using namespace timeline_names; | |
int ii, jj, cc; | int ii, jj, cc; | |
int Fnull = 0, Finvalid = 0; | int Fnull = 0, Finvalid = 0; | |
int yy, mm; | int yy, mm; | |
static int pline, ppos; | static int pline, ppos; | |
char *txline, pdate[8], *pp, end; | ch *txline, pdate[8], *pp, end; | |
char albumfile[200]; | ch albumfile[200]; | |
FILE *fid; | FILE *fid; | |
xxrec_t *xxrec; | xxrec_t *xxrec; | |
static int busy = 0; | static int busy = 0; | |
if (busy) return; // stop re-entry 22.50 | if (busy) return; // stop re-entry 22.50 | |
busy++; | busy++; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
goto retx; | goto retx; | |
skipping to change at line 5041 | skipping to change at line 5042 | |
gallery(0,"paint",0); | gallery(0,"paint",0); | |
m_viewmode(0,"G"); | m_viewmode(0,"G"); | |
retx: | retx: | |
busy = 0; | busy = 0; | |
return; | return; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// Search image tags, geotags, dates, ratings, titles, descriptions | // Search image tags, geotags, dates, ratings, titles, descriptions // overhauled 23.0 | |
// to find matching images. This is fast using the image index. | // to find matching images. This is fast using the image index. | |
// Search also any other metadata, but relatively slow. | // Search also any other metadata, but relatively slow. | |
namespace search_images | namespace search_images | |
{ | { | |
zdialog *zdsearchimages = 0; // search images dialog | zdialog *zdsearchimages = 0; // search images dialog | |
FILE *srfid; // search results file | ||
char searchDateFrom[20] = ""; | ch searchDateFrom[20] = ""; | |
// search images | // search images | |
char searchDateTo[20] = ""; | ch searchDateTo[20] = ""; | |
// format is "yyyy-mm-dd hh:mm" | // format is "yyyy-mm-dd hh:mm" | |
char searchdatelo[16], searchdatehi[16]; | ch searchdatelo[16], searchdatehi[16]; | |
// format is yyyymmddhhmmss | // format is yyyymmddhhmmss | |
char searchRatingFrom[4] = ""; | ch searchRatingFrom[4] = ""; | |
char searchRatingTo[4] = ""; | ch searchRatingTo[4] = ""; | |
char searchtags[tagScc] = ""; | ch searchtags[searchtagsXcc] = ""; | |
// search tags list | // search tags list | |
char searchtext[tagScc] = ""; | ch searchtext[searchtagsXcc] = ""; | |
// search title and description text list | // search title and description text list | |
char searchfiles[tagScc] = ""; | ch searchfiles[searchtagsXcc] = ""; | |
// search files list | // search files list | |
char searchLocations[200] = ""; // search locations | ch searchLocations[200] = ""; // search locations | |
int Fscanall, Fscancurr, Fnewset, Faddset, Fremset; | int Fscanall, Fscancurr, Fnewset, Faddset, Fremset; | |
int Ftext, Ffiles, Ftags, Frating, Flocs; | int Ftext, Ffiles, Ftags, Frating, Flocs; | |
int Fdates, Fphotodate, Ffiledate, Fnulldate; | int Fdates, Fphotodate, Ffiledate, Fnulldate; | |
int Flastver, Forglast, Fallvers, Fnochange; | int Flastver, Forglast, Fallvers, Fnochange; | |
int Falltags, Falltext, Fallfiles, Falllocs; | int Falltags, Falltext, Fallfiles, Falllocs; | |
int Frepgallery, Frepmeta, Fautosearch; | int Frepgallery, Frepmeta, Fautosearch; | |
int Nmatch, Ncurrset; | ||
// current set (gallery) count | ||
int Xthread1, Xthread2; | ||
int Nsearchkeys = 0; | #define maxNkeys 4 | |
char *searchkeys[3]; | // max. Nkeys (search dialog entries) | |
// metadata keys to search | int Nkeys = 0; | |
char *searchvals[3]; | // search keys in use (user selections) | |
// data values to search for | ch *srchkeys[maxNkeys]; | |
char matchtype[3]; | // metadata keys to search | |
// match type: string or number < = > | ch *machvals[maxNkeys]; | |
int keyindexed[3]; | // data values to search for | |
// key included in indexed metadata | ch machtyp[maxNkeys]; | |
int Fallkeysindexed; | // match type: string or number < = > | |
// all search keys are indexed | int keyindexed[maxNkeys]; | |
// key included in indexed metadata | ||
FILE *srfid; | ||
// search results file | ||
ch **scanfiles = 0; | ||
// files to scan | ||
ch **passfiles = 0; | ||
// files passing extract criteria | ||
int Nscan = 0, Npass = 0; | ||
int Ncurrset; | ||
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// Search function for use in scripts | // Search function for use in scripts | |
// $ fotoxx -m autosearch settingsfile | // $ fotoxx -m autosearch settingsfile | |
// A search is performed using the specified search settings file. | // A search is performed using the specified search settings file. | |
// Upon completion, "search results: <filename>" is written to stdout, | // Upon completion, "search results: <filename>" is written to stdout, | |
// where <filename> is a file containing a list of all image files | // where <filename> is a file containing a list of all image files | |
// found - those matching the parameters in the search settings file. | // found - those matching the parameters in the search settings file. | |
// A search settings file is made using the search dialog the [save] button. | // A search settings file is made using the search dialog the [save] button. | |
void m_autosearch(GtkWidget *, cchar *) | void m_autosearch(GtkWidget *, ch *) | |
{ | { | |
using namespace search_images; | using namespace search_images; | |
FILE *fid; | FILE *fid; | |
zdialog *zd; | zdialog *zd; | |
char paramsfile[200]; | ch paramsfile[200]; | |
Plog(1,"m_autosearch \n"); | Plog(1,"m_autosearch \n"); | |
snprintf(paramsfile,200,"%s/%s",search_settings_folder,commandparam); // 22.1 | snprintf(paramsfile,200,"%s/%s",search_settings_folder,commandparam); | |
Plog(1,"search parameters: %s \n",paramsfile); | Plog(1,"search parameters: %s \n",paramsfile); | |
fid = fopen(paramsfile,"r"); // open parameters file | fid = fopen(paramsfile,"r"); // open parameters file | |
if (! fid) zexit(0,"%s: %s",commandparam,strerror(errno)); | if (! fid) zexit(0,"%s: %s",commandparam,strerror(errno)); | |
Fautosearch = 1; | Fautosearch = 1; | |
m_search_images(0,0); // open search dialog | m_search_images(0,0); // open search dialog | |
zd = zdsearchimages; | zd = zdsearchimages; | |
zdialog_load_widgets(zd,null,null,fid); // load parameters into dialog | zdialog_load_widgets(zd,null,null,fid); // load parameters into dialog | |
skipping to change at line 5122 | skipping to change at line 5126 | |
zdialog_send_event(zd,"proceed"); // execute search | zdialog_send_event(zd,"proceed"); // execute search | |
zdialog_wait(zd); // wait for completion | zdialog_wait(zd); // wait for completion | |
Plog(1,"search results: %s/search_results \n",get_zhomedir()); // output file | Plog(1,"search results: %s/search_results \n",get_zhomedir()); // output file | |
zexit(0,"autosearch exit"); | zexit(0,"autosearch exit"); | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// menu function // combine main + meta search dialogs 23.0 | // menu function // combine main + meta search dialogs | |
void m_search_images(GtkWidget *, cchar *) | void m_search_images(GtkWidget *, ch *) | |
{ | { | |
using namespace search_images; | using namespace search_images; | |
void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int k bkey); | void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int k bkey); | |
void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kb key); | void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kb key); | |
void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbke y); | |
int searchimages_dialog_event(zdialog*, cchar *event); | int search_dialog_event(zdialog*, ch *event); | |
zdialog *zd; | zdialog *zd; | |
GtkWidget *widget; | GtkWidget *widget; | |
int ii, nk; | int ii, nk; | |
// 23.0 | static ch **mlist = 0; | |
static char **mlist = 0; | ch matchx[8] = "matchx"; | |
char matchx[8] = "matchx"; | ||
F1_help_topic = "search images"; | F1_help_topic = "search images"; | |
Plog(1,"m_search_images \n"); | Plog(1,"m_search_images \n"); | |
if (! Findexvalid) { | if (! Findexvalid) { | |
zmessageACK(Mwin,"image index disabled"); // no image index | zmessageACK(Mwin,"image index disabled"); // no image index | |
return; | return; | |
} | } | |
if (Findexvalid == 1) zmessage_post_bold(Mwin,"20/10",2,"image index not upda ted"); | if (Findexvalid == 1) zmessage_post_bold(Mwin,"20/10",2,"image index not upda ted"); | |
if (Fblock("search","block edits")) return; // check pending, block | if (Fblock("search","block edits")) return; // check pending, block | |
/*** | /*** | |
_________________________________________________________________ | _________________________________________________________________ | |
| Search Images | | | Search Images | | |
| | | | | | |
| images to search: (o) all (o) current set only | | | images to search: (o) all (o) current set only | | |
| matching images: (o) make new set (o) add to set (o) remove | | | matching images: (o) make new set (o) add to set (o) remove | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| [x] select last version only [x] original + last version | | | [x] select last version only [x] original + last version | | |
| [x] original + all versions [x] no change | | | [x] original + all versions [x] no change | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| report type: (o) gallery (o) metadata | | | report type: (o) gallery (o) metadata | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| date range [______________] [______________] | | | date range [______________] [______________] | | |
| (o) photo date (o) file date (yyyy-mm-dd) | | | (o) photo date (o) file date (yyyy-mm-dd) | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| rating range (stars) [__] [__] | | | rating range (stars) [__] [__] | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| all/any | | | all/any | | |
| search tags [_______________________________________] (o) (o) | | | search tags [_______________________________________] (o) (o) | | |
| search text [_______________________________________] (o) (o) | | | search text [_______________________________________] (o) (o) | | |
| search files [_______________________________________] (o) (o) | | | search files [_______________________________________] (o) (o) | | |
| search locations [___________________________________] (o) (o) | | | search locations [___________________________________] (o) (o) | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| Keyname Condition Match Values | | | Keyname Condition Match Values X | | |
// 23.0 | | [_____________|v] [ report ] [________________________] [x] | | |
| [_____________|v] [ report ] [____________________________] | | // erasers 23.1 | |
| [_____________|v] [ matches ] [____________________________] | | | [_____________|v] [ matches ] [________________________] [x] | | |
| [_____________|v] [ number = ] [____________________________] | | | [_____________|v] [ number = ] [________________________] [x] | | |
| [_____________|v] [ number >= ] [________________________] [x] | | ||
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| Enter Search Tag [________________________] | | | Enter Search Tag [________________________] | | |
| Matching Tags [______________________________________________] | | | Matching Tags [______________________________________________] | | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | | |
| Defined Tags Category [____________________________________|v] | | | Defined Tags Category [____________________________________|v] | | |
| | | | | | | | | | |
| | | | | | | | | | |
| | | | | | | | | | |
| | | | | | | | | | |
| | | | | | | | | | |
skipping to change at line 5280 | skipping to change at line 5285 | |
zdialog_add_widget(zd,"hbox","hblocs","dialog","space=1"); | zdialog_add_widget(zd,"hbox","hblocs","dialog","space=1"); | |
zdialog_add_widget(zd,"label","lablocs","hblocs","search locations","space=3" ); | zdialog_add_widget(zd,"label","lablocs","hblocs","search locations","space=3" ); | |
zdialog_add_widget(zd,"zentry","searchlocs","hblocs",0,"expand"); | zdialog_add_widget(zd,"zentry","searchlocs","hblocs",0,"expand"); | |
zdialog_add_widget(zd,"radio","alllocs","hblocs",0,"space=5"); | zdialog_add_widget(zd,"radio","alllocs","hblocs",0,"space=5"); | |
zdialog_add_widget(zd,"radio","anylocs","hblocs",0,"space=5"); | zdialog_add_widget(zd,"radio","anylocs","hblocs",0,"space=5"); | |
zdialog_add_ttip(zd,"searchlocs","enter cities, countries"); | zdialog_add_ttip(zd,"searchlocs","enter cities, countries"); | |
zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); | zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); | |
zdialog_add_widget(zd,"hbox","hbmeta","dialog"); | zdialog_add_widget(zd,"hbox","hbmeta","dialog"); | |
// 23.0 | zdialog_add_widget(zd,"vbox","vbkey","hbmeta",0,"space=5|homog"); | |
zdialog_add_widget(zd,"vbox","vbmkey","hbmeta",0,"space=5|homog"); | zdialog_add_widget(zd,"vbox","vbmatch","hbmeta",0,"space=5|homog"); | |
zdialog_add_widget(zd,"vbox","vbmcond","hbmeta",0,"space=5|homog"); | zdialog_add_widget(zd,"vbox","vbvalue","hbmeta",0,"space=5|homog|expand"); | |
zdialog_add_widget(zd,"vbox","vbmval","hbmeta",0,"space=5|homog|expand"); | zdialog_add_widget(zd,"vbox","vbclear","hbmeta",0,"space=5|homog"); | |
zdialog_add_widget(zd,"label","lab1","vbmkey","Keyname"); | zdialog_add_widget(zd,"label","lab1","vbkey","Keyname"); | |
zdialog_add_widget(zd,"combo","key0","vbmkey"); | zdialog_add_widget(zd,"label","lab2","vbmatch","Condition"); | |
zdialog_add_widget(zd,"combo","key1","vbmkey"); | zdialog_add_widget(zd,"label","lab3","vbvalue","Match Values"); | |
zdialog_add_widget(zd,"combo","key2","vbmkey"); | zdialog_add_widget(zd,"label","lab0","vbclear","X"); | |
zdialog_add_widget(zd,"label","lab2","vbmcond","Condition"); | zdialog_add_widget(zd,"combo","key0","vbkey",0,"size=15"); | |
zdialog_add_widget(zd,"combo","match0","vbmcond"); | // must match maxNkeys (now 4) | |
zdialog_add_widget(zd,"combo","match1","vbmcond"); | zdialog_add_widget(zd,"combo","key1","vbkey",0,"size=15"); | |
zdialog_add_widget(zd,"combo","match2","vbmcond"); | zdialog_add_widget(zd,"combo","key2","vbkey",0,"size=15"); | |
zdialog_add_widget(zd,"combo","key3","vbkey",0,"size=15"); | ||
zdialog_add_widget(zd,"label","lab3","vbmval","Match Values"); | ||
zdialog_add_widget(zd,"zentry","value0","vbmval",0,"expand"); | zdialog_add_widget(zd,"combo","match0","vbmatch"); | |
zdialog_add_widget(zd,"zentry","value1","vbmval",0,"expand"); | zdialog_add_widget(zd,"combo","match1","vbmatch"); | |
zdialog_add_widget(zd,"zentry","value2","vbmval",0,"expand"); | zdialog_add_widget(zd,"combo","match2","vbmatch"); | |
zdialog_add_widget(zd,"combo","match3","vbmatch"); | ||
zdialog_add_widget(zd,"zentry","value0","vbvalue",0,"expand"); | ||
zdialog_add_widget(zd,"zentry","value1","vbvalue",0,"expand"); | ||
zdialog_add_widget(zd,"zentry","value2","vbvalue",0,"expand"); | ||
zdialog_add_widget(zd,"zentry","value3","vbvalue",0,"expand"); | ||
zdialog_add_widget(zd,"button","clear0","vbclear","x"); | ||
// 23.1 | ||
zdialog_add_widget(zd,"button","clear1","vbclear","x"); | ||
zdialog_add_widget(zd,"button","clear2","vbclear","x"); | ||
zdialog_add_widget(zd,"button","clear3","vbclear","x"); | ||
zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); | zdialog_add_widget(zd,"hsep","sep","dialog",0,"space=2"); | |
zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); | zdialog_add_widget(zd,"hbox","hbnt","dialog",0,"space=1"); | |
zdialog_add_widget(zd,"label","labnt","hbnt","Enter Search Tag","space=3"); | zdialog_add_widget(zd,"label","labnt","hbnt","Enter Search Tag","space=3"); | |
zdialog_add_widget(zd,"zentry","entertag","hbnt",0,"size=20"); | zdialog_add_widget(zd,"zentry","entertag","hbnt",0,"size=20"); | |
zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); | zdialog_add_widget(zd,"hbox","hbmt","dialog",0,"space=1"); | |
zdialog_add_widget(zd,"label","labmt","hbmt","Matching Tags","space=3"); | zdialog_add_widget(zd,"label","labmt","hbmt","Matching Tags","space=3"); | |
zdialog_add_widget(zd,"text","matchtags","hbmt",0,"wrap|expand"); | zdialog_add_widget(zd,"text","matchtags","hbmt",0,"wrap|expand"); | |
skipping to change at line 5322 | skipping to change at line 5338 | |
zdialog_add_widget(zd,"hbox","hbdt1","dialog"); | zdialog_add_widget(zd,"hbox","hbdt1","dialog"); | |
zdialog_add_widget(zd,"label","labdt","hbdt1","Defined Tags Category","space= 3"); | zdialog_add_widget(zd,"label","labdt","hbdt1","Defined Tags Category","space= 3"); | |
zdialog_add_widget(zd,"combo","defcats","hbdt1",0,"expand|space=10|size=20"); | zdialog_add_widget(zd,"combo","defcats","hbdt1",0,"expand|space=10|size=20"); | |
zdialog_add_widget(zd,"hbox","hbdt2","dialog",0,"expand"); | zdialog_add_widget(zd,"hbox","hbdt2","dialog",0,"expand"); | |
zdialog_add_widget(zd,"frame","frdt2","hbdt2",0,"expand|space=3"); | zdialog_add_widget(zd,"frame","frdt2","hbdt2",0,"expand|space=3"); | |
zdialog_add_widget(zd,"scrwin","swdt2","frdt2",0,"expand"); | zdialog_add_widget(zd,"scrwin","swdt2","frdt2",0,"expand"); | |
zdialog_add_widget(zd,"text","deftags","swdt2",0,"wrap"); | zdialog_add_widget(zd,"text","deftags","swdt2",0,"wrap"); | |
if (Fautosearch) { // autosearch caller | if (Fautosearch) { // autosearch caller | |
zdialog_run(zd,searchimages_dialog_event,"save"); // bypass interactive stuff | zdialog_run(zd,search_dialog_event,"save"); // bypass interactive stuff | |
Fblock("search",0); // caller cleans up dialog | Fblock("search",0); // caller cleans up dialog | |
return; | return; | |
} | } | |
widget = zdialog_gtkwidget(zd,"searchtags"); // tag widget mouse/KB event function | widget = zdialog_gtkwidget(zd,"searchtags"); // tag widget mouse/KB event function | |
textwidget_set_eventfunc(widget,search_searchtags_clickfunc); | textwidget_set_eventfunc(widget,search_searchtags_clickfunc); | |
widget = zdialog_gtkwidget(zd,"matchtags"); | widget = zdialog_gtkwidget(zd,"matchtags"); | |
textwidget_set_eventfunc(widget,search_matchtags_clickfunc); | textwidget_set_eventfunc(widget,search_matchtags_clickfunc); | |
skipping to change at line 5358 | skipping to change at line 5374 | |
zdialog_stuff(zd,"nochange",1); | zdialog_stuff(zd,"nochange",1); | |
zdialog_stuff(zd,"alltags",0); | zdialog_stuff(zd,"alltags",0); | |
zdialog_stuff(zd,"anytags",1); | zdialog_stuff(zd,"anytags",1); | |
zdialog_stuff(zd,"alltext",0); | zdialog_stuff(zd,"alltext",0); | |
zdialog_stuff(zd,"anytext",1); | zdialog_stuff(zd,"anytext",1); | |
zdialog_stuff(zd,"allfiles",0); | zdialog_stuff(zd,"allfiles",0); | |
zdialog_stuff(zd,"anyfiles",1); | zdialog_stuff(zd,"anyfiles",1); | |
zdialog_stuff(zd,"alllocs",0); | zdialog_stuff(zd,"alllocs",0); | |
zdialog_stuff(zd,"anylocs",1); | zdialog_stuff(zd,"anylocs",1); | |
if (! searchkeys[0]) // first call initialization | if (! srchkeys[0]) // first call initialization | |
{ | { | |
for (ii = 0; ii < 3; ii++) { | for (ii = 0; ii < maxNkeys; ii++) { | |
searchkeys[ii] = (char *) zmalloc(80,"search"); | srchkeys[ii] = (ch *) zmalloc(metakeyXcc,"search"); | |
searchvals[ii] = (char *) zmalloc(100,"search"); | machvals[ii] = (ch *) zmalloc(metadataXcc,"search"); | |
*searchkeys[ii] = *searchvals[ii] = 0; | ||
} | } | |
} | } | |
nk = zreadfile(meta_picklist_file,mlist); // get metadata picklist | nk = zreadfile(meta_picklist_file,mlist); // get metadata picklist | |
zdialog_stuff(zd,"key0","Clear"); | ||
// add "clear" choice | ||
zdialog_stuff(zd,"key1","Clear"); | ||
zdialog_stuff(zd,"key2","Clear"); | ||
for (ii = 0; ii < nk; ii++) { | for (ii = 0; ii < nk; ii++) { | |
zdialog_stuff(zd,"key0",mlist[ii]); // metadata picklist > key picklist | zdialog_stuff(zd,"key0",mlist[ii]); // metadata picklist > key picklist | |
zdialog_stuff(zd,"key1",mlist[ii]); | zdialog_stuff(zd,"key1",mlist[ii]); | |
zdialog_stuff(zd,"key2",mlist[ii]); | zdialog_stuff(zd,"key2",mlist[ii]); | |
zdialog_stuff(zd,"key3",mlist[ii]); | ||
} | } | |
zreadfile_free(mlist); | ||
zdialog_stuff(zd,"key0","(other)"); // add "other" choice | zdialog_stuff(zd,"key0","(other)"); // add "other" choice | |
zdialog_stuff(zd,"key1","(other)"); | zdialog_stuff(zd,"key1","(other)"); | |
zdialog_stuff(zd,"key2","(other)"); | zdialog_stuff(zd,"key2","(other)"); | |
zdialog_stuff(zd,"key3","(other)"); | ||
zdialog_stuff(zd,"key0",""); // clear picklist choices | zdialog_stuff(zd,"key0",""); // clear picklist choices | |
zdialog_stuff(zd,"key1",""); | zdialog_stuff(zd,"key1",""); | |
zdialog_stuff(zd,"key2",""); | zdialog_stuff(zd,"key2",""); | |
zdialog_stuff(zd,"key3",""); | ||
for (ii = 0; ii < 3; ii++) { // add operator options | for (ii = 0; ii < maxNkeys; ii++) { // add operator options | |
matchx[5] = '0' + ii; | matchx[5] = '0' + ii; | |
zdialog_stuff(zd,matchx,"report"); | zdialog_stuff(zd,matchx,"report"); | |
zdialog_stuff(zd,matchx,"matches"); | zdialog_stuff(zd,matchx,"matches"); | |
zdialog_stuff(zd,matchx,"contains"); | zdialog_stuff(zd,matchx,"contains"); | |
zdialog_stuff(zd,matchx,"number ="); | zdialog_stuff(zd,matchx,"number ="); | |
zdialog_stuff(zd,matchx,"number =>"); | zdialog_stuff(zd,matchx,"number =>"); | |
zdialog_stuff(zd,matchx,"number <="); | zdialog_stuff(zd,matchx,"number <="); | |
} | } | |
zdialog_restore_inputs(zd); // preload prior user inputs | zdialog_restore_inputs(zd); // preload prior user inputs | |
zdialog_fetch(zd,"searchtags",searchtags,tagScc); | zdialog_fetch(zd,"searchtags",searchtags,searchtagsXcc); | |
strcat(searchtags," "); // trailing blank after "tagname," | strcat(searchtags," "); // trailing blank after "tagname," | |
load_deftags(0); // stuff defined tags into dialog | load_deftags(0); // stuff defined tags into dialog | |
deftags_stuff(zd,"ALL"); | deftags_stuff(zd,"ALL"); | |
defcats_stuff(zd); // and defined categories | defcats_stuff(zd); // and defined categories | |
zdialog_resize(zd,0,800); // start dialog | zdialog_resize(zd,0,800); // start dialog | |
zdialog_run(zd,searchimages_dialog_event,"save"); | zdialog_run(zd,search_dialog_event,"save"); | |
zdialog_wait(zd); // wait for dialog completion | zdialog_wait(zd); // wait for dialog completion | |
zdialog_free(zd); | zdialog_free(zd); | |
Fblock("search",0); | Fblock("search",0); | |
return; | return; | |
} | } | |
// search tag was clicked | // search tag was clicked | |
void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ) | void search_searchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey ) | |
{ | { | |
using namespace search_images; | using namespace search_images; | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;:",end); | txtag = textwidget_word(widget,line,pos,",;:",end); | |
if (! txtag) return; | if (! txtag) return; | |
del_tag(txtag,searchtags); // remove from search list | del_tag(txtag,searchtags); // remove from search list | |
skipping to change at line 5443 | skipping to change at line 5459 | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
// matching tag was clicked | // matching tag was clicked | |
void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | void search_matchtags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | |
{ | { | |
using namespace search_images; | using namespace search_images; | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;",end); | txtag = textwidget_word(widget,line,pos,",;",end); | |
if (! txtag) return; | if (! txtag) return; | |
add_tag(txtag,searchtags,tagScc); // add to search tag list | add_tag(txtag,searchtags,searchtagsXcc); // add to search tag list | |
zdialog_stuff(zdsearchimages,"entertag",""); // update dialog widgets | zdialog_stuff(zdsearchimages,"entertag",""); // update dialog widgets | |
zdialog_stuff(zdsearchimages,"matchtags",""); | zdialog_stuff(zdsearchimages,"matchtags",""); | |
zdialog_stuff(zdsearchimages,"searchtags",searchtags); | zdialog_stuff(zdsearchimages,"searchtags",searchtags); | |
zdialog_goto(zdsearchimages,"entertag"); // focus back to entertag widget | zdialog_goto(zdsearchimages,"entertag"); // focus back to entertag widget | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
// defined tag was clicked | // defined tag was clicked | |
void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | void search_deftags_clickfunc(GtkWidget *widget, int line, int pos, int kbkey) | |
{ | { | |
using namespace search_images; | using namespace search_images; | |
char *txtag, end = 0; | ch *txtag, end = 0; | |
if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | if (kbkey == GDK_KEY_F1) { // key F1 pressed, show help | |
showz_docfile(Mwin,"userguide",F1_help_topic); | showz_docfile(Mwin,"userguide",F1_help_topic); | |
return; | return; | |
} | } | |
txtag = textwidget_word(widget,line,pos,",;:",end); | txtag = textwidget_word(widget,line,pos,",;:",end); | |
if (! txtag || end == ':') return; // nothing or tag category, ignore | if (! txtag || end == ':') return; // nothing or tag category, ignore | |
add_tag(txtag,searchtags,tagScc); // add to search tag list | add_tag(txtag,searchtags,searchtagsXcc); // add to search tag list | |
zdialog_stuff(zdsearchimages,"searchtags",searchtags); | zdialog_stuff(zdsearchimages,"searchtags",searchtags); | |
zfree(txtag); | zfree(txtag); | |
return; | return; | |
} | } | |
// search images dialog event and completion callback function | // search images dialog event and completion callback function | |
int searchimages_dialog_event(zdialog *zd, cchar *event) | int search_dialog_event(zdialog *zd, ch *event) | |
{ | { | |
using namespace search_images; | using namespace search_images; | |
int datetimeOK(char *datetime); | int datetimeOK(ch *datetime); | |
int searchimages_metadata_dialog(zdialog *zd); | int search_metadata_dialog(zdialog *zd); | |
int searchimages_metadata_report(void); | void search_main(); | |
void *searchimages_thread(void *); | void search_xmeta(); | |
void search_nxmeta(); | ||
cchar dateLoDefault[20] = "0000-01-01 00:00:00"; | void search_add_related_files(void); | |
// start of time | int search_metadata_report(void); | |
cchar dateHiDefault[20] = "2099-12-31 23:59:59"; | ||
// end of time | ch dateLoDefault[20] = "0000-01-01 00:00:00"; | |
// start of time | ||
char *file, *file2, **vlist; | ch dateHiDefault[20] = "2099-12-31 23:59:59"; | |
char **flist, buffer[XFCC]; | // end of time | |
char mm[4] = "mm"; | ||
int ii, jj, kk, err, cc; | ch *file; | |
int nt, cc1, cc2, ff, nf; | ch mm[4] = "mm"; | |
int ii, jj, err, cc; | ||
int nt, cc1, cc2, ff; | ||
float fnum; | float fnum; | |
char *pp, *pp1, *pp2; | ch *pp, *pp1, *pp2; | |
char entertag[tagcc], matchtags[20][tagcc]; | ch entertag[tagXcc], matchtags[20][tagXcc]; | |
char matchtagstext[(tagcc+2)*20]; | ch matchtagstext[(tagXcc+2)*20]; | |
char catgname[tagcc]; | ch catgname[tagXcc]; | |
char albumfile[200]; | ch albumfile[200]; | |
char keyx[8] = "keyx", valuex[8] = "valuex", matchx[8] = "matchx"; | ch keyx[8] = "keyx", valuex[8] = "valuex", matchx[8] = "matchx"; | |
char wname[8], temp[100]; | ch wname[8], temp[100]; | |
zlist_t *ZLsearch; | ||
if (strmatch(event,"escape")) { // escape key | if (strmatch(event,"escape")) { // escape key | |
if (Fescape) Fescape++; // kill running search | if (Fwatchescape) Fescape = 1; // kill running search | |
else zd->zstat = -1; // else kill dialog | else zd->zstat = -1; // else kill dialog | |
return 1; | return 1; | |
} | } | |
if (strmatch(event,"proceed")) zd->zstat = 4; // "proceed" from autosearch | if (strmatch(event,"proceed")) zd->zstat = 4; // "proceed" from autosearch | |
if (zd->zstat == 1) { // [load] settings from file | if (zd->zstat == 1) { // [load] settings from file | |
zd->zstat = 0; | zd->zstat = 0; | |
zdialog_load_widgets(zd,null,"saved_searches",null); | zdialog_load_widgets(zd,null,"saved_searches",null); | |
zdialog_fetch(zd,"searchtags",searchtags,tagScc); | zdialog_fetch(zd,"searchtags",searchtags,searchtagsXcc); | |
strcat(searchtags," "); // trailing blank after "tagname," | strcat(searchtags," "); // trailing blank after "tagname," | |
return 1; | return 1; | |
} | } | |
if (zd->zstat == 2) { // [save] settings to file | if (zd->zstat == 2) { // [save] settings to file | |
zd->zstat = 0; | zd->zstat = 0; | |
zdialog_save_widgets(zd,null,"saved_searches",null); | zdialog_save_widgets(zd,null,"saved_searches",null); | |
return 1; | return 1; | |
} | } | |
skipping to change at line 5573 | skipping to change at line 5590 | |
zdialog_stuff(zd,"ratingfrom",""); | zdialog_stuff(zd,"ratingfrom",""); | |
zdialog_stuff(zd,"ratingto",""); | zdialog_stuff(zd,"ratingto",""); | |
zdialog_stuff(zd,"searchtags",""); | zdialog_stuff(zd,"searchtags",""); | |
zdialog_stuff(zd,"searchtext",""); | zdialog_stuff(zd,"searchtext",""); | |
zdialog_stuff(zd,"searchfiles",""); | zdialog_stuff(zd,"searchfiles",""); | |
zdialog_stuff(zd,"searchlocs",""); | zdialog_stuff(zd,"searchlocs",""); | |
*searchtags = 0; | *searchtags = 0; | |
Flocs = 0; | Flocs = 0; | |
*searchLocations = 0; | *searchLocations = 0; | |
Nsearchkeys = 0; | Nkeys = 0; | |
for (ii = 0; ii < 3; ii++) { // erase metadata entries | for (ii = 0; ii < maxNkeys; ii++) { // erase metadata entries | |
keyx[3] = '0' + ii; | keyx[3] = '0' + ii; | |
valuex[5] = '0' + ii; | valuex[5] = '0' + ii; | |
matchx[5] = '0' + ii; | matchx[5] = '0' + ii; | |
zdialog_stuff(zd,keyx,""); | zdialog_stuff(zd,keyx,""); | |
zdialog_stuff(zd,matchx,""); | zdialog_stuff(zd,matchx,""); | |
zdialog_stuff(zd,valuex,""); | zdialog_stuff(zd,valuex,""); | |
} | } | |
return 1; | return 1; | |
} | } | |
skipping to change at line 5606 | skipping to change at line 5623 | |
zdialog_stuff(zd,"org+last",0); | zdialog_stuff(zd,"org+last",0); | |
zdialog_stuff(zd,"allvers",0); | zdialog_stuff(zd,"allvers",0); | |
zdialog_stuff(zd,"nochange",0); | zdialog_stuff(zd,"nochange",0); | |
zdialog_stuff(zd,event,1); | zdialog_stuff(zd,event,1); | |
} | } | |
if (strmatch(event,"entertag")) // new tag is being typed in | if (strmatch(event,"entertag")) // new tag is being typed in | |
{ | { | |
zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog | zdialog_stuff(zd,"matchtags",""); // clear matchtags in dialog | |
zdialog_fetch(zd,"entertag",entertag,tagcc); // get chars. typed so far | zdialog_fetch(zd,"entertag",entertag,tagXcc); // get chars. typed so far | |
cc1 = strlen(entertag); | cc1 = strlen(entertag); | |
for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters | for (ii = jj = 0; ii <= cc1; ii++) { // remove foul characters | |
if (strchr(",:;",entertag[ii])) continue; | if (strchr(",:;",entertag[ii])) continue; | |
entertag[jj++] = entertag[ii]; | entertag[jj++] = entertag[ii]; | |
} | } | |
if (jj < cc1) { // something was removed | if (jj < cc1) { // something was removed | |
entertag[jj] = 0; | entertag[jj] = 0; | |
cc1 = jj; | cc1 = jj; | |
skipping to change at line 5660 | skipping to change at line 5677 | |
pp1 += strlen(pp1); | pp1 += strlen(pp1); | |
strcpy(pp1,", "); | strcpy(pp1,", "); | |
pp1 += 2; | pp1 += 2; | |
} | } | |
zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog | zdialog_stuff(zd,"matchtags",matchtagstext); // stuff matchtags in dialog | |
return 1; | return 1; | |
} | } | |
if (strmatch(event,"defcats")) { // new tag category selection | if (strmatch(event,"defcats")) { // new tag category selection | |
zdialog_fetch(zd,"defcats",catgname,tagcc); | zdialog_fetch(zd,"defcats",catgname,tagXcc); | |
deftags_stuff(zd,catgname); | deftags_stuff(zd,catgname); | |
} | } | |
if (strstr(event,"key")) // metadata key was selected 23.0 | if (strstr(event,"key")) // metadata keyN selected | |
{ | { | |
snprintf(wname,8,"match%c",event[3]); // widget keyN >> widget matchN | snprintf(wname,8,"match%c",event[3]); // widget keyN >> widget matchN | |
zdialog_stuff(zd,wname,"report"); // set "report" default operator | zdialog_stuff(zd,wname,"report"); // set "report" default operator | |
} | } | |
for (ii = 0; ii < 3; ii++) // if "clear", erase entries | if (strstr(event,"clear")) // metadata clearN selected 23.1 | |
{ | { | |
keyx[3] = '0' + ii; | ii = event[5]; | |
valuex[5] = '0' + ii; | // character N | |
matchx[5] = '0' + ii; | keyx[3] = ii; | |
zdialog_fetch(zd,keyx,temp,100); | valuex[5] = ii; | |
if (strmatch(temp,"Clear")) { | matchx[5] = ii; | |
zdialog_stuff(zd,keyx,""); | zdialog_stuff(zd,keyx,""); | |
zdialog_stuff(zd,matchx,""); | zdialog_stuff(zd,matchx,""); | |
zdialog_stuff(zd,valuex,""); | zdialog_stuff(zd,valuex,""); | |
} | ||
} | } | |
for (ii = 0; ii < 3; ii++) // if "(other)" get key name | for (ii = 0; ii < maxNkeys; ii++) // if "(other)" get key name | |
{ | { | |
keyx[3] = '0' + ii; | keyx[3] = '0' + ii; | |
valuex[5] = '0' + ii; | valuex[5] = '0' + ii; | |
zdialog_fetch(zd,keyx,temp,100); | zdialog_fetch(zd,keyx,temp,100); | |
if (strmatch(temp,"(other)")) { | if (strmatch(temp,"(other)")) { | |
pp = zdialog_text1(zd->parent,"enter key name",0); | pp = zdialog_text1(zd->parent,"enter key name",0); | |
if (pp) { | if (pp) { | |
zdialog_stuff(zd,keyx,pp); | zdialog_stuff(zd,keyx,pp); | |
zfree(pp); | zfree(pp); | |
} | } | |
else zdialog_stuff(zd,keyx,""); | else zdialog_stuff(zd,keyx,""); | |
} | } | |
} | } | |
for (ii = 0; ii < 3; ii++) // if key is only to be reported, | for (ii = 0; ii < maxNkeys; ii++) // if key is only to be reported, | |
{ // clear irrelevant match criteria | { // clear irrelevant match criteria | |
valuex[5] = '0' + ii; | valuex[5] = '0' + ii; | |
matchx[5] = '0' + ii; | matchx[5] = '0' + ii; | |
zdialog_fetch(zd,matchx,temp,20); | zdialog_fetch(zd,matchx,temp,20); | |
if (strmatch(temp,"report")) | if (strmatch(temp,"report")) | |
zdialog_stuff(zd,valuex,""); | zdialog_stuff(zd,valuex,""); | |
} | } | |
return 1; // wait for dialog completion | return 1; // wait for dialog completion | |
validate: | ||
// Inputs are complete. Validate all inputs. ---------------------------------- - | // Inputs are complete. Validate all inputs. ---------------------------------- - | |
validate: | ||
zdialog_fetch(zd,"allimages",Fscanall); // search all images | zdialog_fetch(zd,"allimages",Fscanall); // search all images | |
zdialog_fetch(zd,"currset",Fscancurr); // search current set (gallery) | zdialog_fetch(zd,"currset",Fscancurr); // search current set (gallery) | |
zdialog_fetch(zd,"newset",Fnewset); // matching images --> new set | zdialog_fetch(zd,"newset",Fnewset); // matching images --> new set | |
zdialog_fetch(zd,"addset",Faddset); // add matching image to set | zdialog_fetch(zd,"addset",Faddset); // add matching image to set | |
zdialog_fetch(zd,"remset",Fremset); // remove matching images from set | zdialog_fetch(zd,"remset",Fremset); // remove matching images from set | |
if (Fremset && Fscanall) { // illogical search | if (Fremset && Fscanall) { // illogical search | |
zmessageACK(Mwin,"to remove images from current set, \n" | zmessageACK(Mwin,"to remove images from current set, \n" | |
"search current set"); | "search current set"); | |
zd->zstat = 0; // keep dialog active | zd->zstat = 0; // keep dialog active | |
skipping to change at line 5745 | skipping to change at line 5760 | |
zdialog_fetch(zd,"lastver",Flastver); // get last versions only | zdialog_fetch(zd,"lastver",Flastver); // get last versions only | |
zdialog_fetch(zd,"org+last",Forglast); // get original + last version | zdialog_fetch(zd,"org+last",Forglast); // get original + last version | |
zdialog_fetch(zd,"allvers",Fallvers); // get all vers. of matching image | zdialog_fetch(zd,"allvers",Fallvers); // get all vers. of matching image | |
zdialog_fetch(zd,"datefrom",searchDateFrom,20); // get search date range | zdialog_fetch(zd,"datefrom",searchDateFrom,20); // get search date range | |
zdialog_fetch(zd,"dateto",searchDateTo,20); | zdialog_fetch(zd,"dateto",searchDateTo,20); | |
zdialog_fetch(zd,"photodate",Fphotodate); // photo or file date | zdialog_fetch(zd,"photodate",Fphotodate); // photo or file date | |
zdialog_fetch(zd,"filedate",Ffiledate); | zdialog_fetch(zd,"filedate",Ffiledate); | |
zdialog_fetch(zd,"ratingfrom",searchRatingFrom,2); // get search rating range | zdialog_fetch(zd,"ratingfrom",searchRatingFrom,2); // get search rating range | |
zdialog_fetch(zd,"ratingto",searchRatingTo,2); | zdialog_fetch(zd,"ratingto",searchRatingTo,2); | |
zdialog_fetch(zd,"searchtags",searchtags,tagScc); | zdialog_fetch(zd,"searchtags",searchtags,searchtagsXcc); | |
// get search tags | // get search tags | |
zdialog_fetch(zd,"searchtext",searchtext,tagScc); | zdialog_fetch(zd,"searchtext",searchtext,searchtagsXcc); | |
// get search text* | // get search text* | |
zdialog_fetch(zd,"searchfiles",searchfiles,tagScc); | zdialog_fetch(zd,"searchfiles",searchfiles,searchtagsXcc); | |
// get search /path*/file* | // get search /path*/file* | |
zdialog_fetch(zd,"searchlocs",searchLocations,200); // get search locations | zdialog_fetch(zd,"searchlocs",searchLocations,200); // get search locations | |
zdialog_fetch(zd,"alltags",Falltags); // get match all/any options | zdialog_fetch(zd,"alltags",Falltags); // get match all/any options | |
zdialog_fetch(zd,"alltext",Falltext); | zdialog_fetch(zd,"alltext",Falltext); | |
zdialog_fetch(zd,"allfiles",Fallfiles); | zdialog_fetch(zd,"allfiles",Fallfiles); | |
zdialog_fetch(zd,"alllocs",Falllocs); | zdialog_fetch(zd,"alllocs",Falllocs); | |
Fdates = 0; | Fdates = 0; | |
if (*searchDateFrom) Fdates++; // search date from was given | if (*searchDateFrom) Fdates++; // search date from was given | |
else strcpy(searchDateFrom,dateLoDefault); // else search from start of time | else strcpy(searchDateFrom,dateLoDefault); // else search from start of time | |
skipping to change at line 5838 | skipping to change at line 5853 | |
Ftext = 0; | Ftext = 0; | |
if (! blank_null(searchtext)) Ftext = 1; // search text was given | if (! blank_null(searchtext)) Ftext = 1; // search text was given | |
Ftags = 0; | Ftags = 0; | |
if (! blank_null(searchtags)) Ftags = 1; // search tags was given | if (! blank_null(searchtags)) Ftags = 1; // search tags was given | |
Flocs = 0; | Flocs = 0; | |
if (*searchLocations) Flocs = 1; // search locations was given | if (*searchLocations) Flocs = 1; // search locations was given | |
Nsearchkeys = 0; // validate search metadata keys 23.0 | Nkeys = 0; // validate search metadata keys | |
for (ii = jj = 0; ii < 3; ii++) | for (ii = jj = 0; ii < maxNkeys; ii++) | |
{ | { | |
keyx[3] = '0' + ii; | keyx[3] = '0' + ii; | |
matchx[5] = '0' + ii; | matchx[5] = '0' + ii; | |
valuex[5] = '0' + ii; | valuex[5] = '0' + ii; | |
zdialog_fetch(zd,keyx,searchkeys[ii],80); | zdialog_fetch(zd,keyx,srchkeys[ii],metakeyXcc); | |
// get search key | // get search key | |
strCompress(searchkeys[ii]); | strCompress(srchkeys[ii]); | |
// remove all blanks from key names | // remove all blanks from key names | |
if (*searchkeys[ii] <= ' ') { | if (*srchkeys[ii] <= ' ') { | |
zdialog_stuff(zd,matchx,""); // empty search key position | zdialog_stuff(zd,matchx,""); // empty search key position | |
zdialog_stuff(zd,valuex,""); | zdialog_stuff(zd,valuex,""); | |
continue; | continue; | |
} | } | |
memmove(searchkeys[jj],searchkeys[ii],80); // repack blank keys | memmove(srchkeys[jj],srchkeys[ii],metakeyXcc); // repack blank keys | |
zdialog_fetch(zd,matchx,temp,20); // get corresp. match type | zdialog_fetch(zd,matchx,temp,20); // get corresp. match type | |
if (strmatch(temp,"report")) matchtype[jj] = 'r'; | if (strmatch(temp,"report")) machtyp[jj] = 'r'; | |
else if (strmatch(temp,"matches")) matchtype[jj] = 'm'; | else if (strmatch(temp,"matches")) machtyp[jj] = 'm'; | |
else if (strmatch(temp,"contains")) matchtype[jj] = 'c'; | else if (strmatch(temp,"contains")) machtyp[jj] = 'c'; | |
else if (strmatch(temp,"number =")) matchtype[jj] = '='; | else if (strmatch(temp,"number =")) machtyp[jj] = '='; | |
else if (strmatch(temp,"number =>")) matchtype[jj] = '>'; | else if (strmatch(temp,"number =>")) machtyp[jj] = '>'; | |
else if (strmatch(temp,"number <=")) matchtype[jj] = '<'; | else if (strmatch(temp,"number <=")) machtyp[jj] = '<'; | |
else { | else { | |
zdialog_stuff(zd,matchx,"report"); // unspecified >> report | zdialog_stuff(zd,matchx,"report"); // unspecified >> report | |
matchtype[jj] = 'r'; | machtyp[jj] = 'r'; | |
} | } | |
zdialog_fetch(zd,valuex,searchvals[ii],100); | zdialog_fetch(zd,valuex,machvals[ii],100); | |
// get corresp. match value | // get corresp. match value | |
strTrim2(searchvals[jj],searchvals[ii]); | strTrim2(machvals[jj],machvals[ii]); | |
// trim leading and trailing blanks | // trim leading and trailing blanks | |
if (strstr(temp,"number")) { // check numeric values | if (strstr(temp,"number")) { // check numeric values | |
err = convSF(searchvals[jj],fnum); | err = convSF(machvals[jj],fnum); | |
if (err) { | if (err) { | |
snprintf(temp,100,"need numeric match value: %s",searchkeys[jj]); | snprintf(temp,100,"need numeric match value: %s",srchkeys[jj]); | |
zmessageACK(Mwin,temp); | zmessageACK(Mwin,temp); | |
zd->zstat = 0; | zd->zstat = 0; | |
return 1; | return 1; | |
} | } | |
} | } | |
if (ii > jj) *searchkeys[ii] = *searchvals[ii] = 0; | if (ii > jj) *srchkeys[ii] = *machvals[ii] = 0; | |
jj++; | jj++; | |
} | } | |
Nsearchkeys = jj; // keys found, no blanks | Nkeys = jj; // search keys count | |
for (ii = 0; ii < Nsearchkeys; ii++) { // loop search keys 23.0 | for (ii = 0; ii < Nkeys; ii++) { // loop search keys | |
keyindexed[ii] = 0; | keyindexed[ii] = 0; | |
for (jj = 0; jj < MXmax; jj++) { // compare to indexed meta keys | for (jj = 0; jj < xmetamaxkeys; jj++) { // compare to indexed meta keys | |
if (! xmeta_keys[jj]) break; | if (! xmeta_keys[jj]) break; | |
if (strmatchcase(searchkeys[ii],xmeta_keys[jj])) | if (strmatchcase(srchkeys[ii],xmeta_keys[jj])) | |
keyindexed[ii] = 1; // found, search key is indexed | keyindexed[ii] = 1; // found, search key is indexed | |
} | } | |
} | } | |
Fallkeysindexed = 1; | // Begin search ------------------------------------------------------------ | |
// all search keys indexed | - | |
for (ii = 0; ii < Nsearchkeys; ii++) | // Scan all files or current set (gallery) | |
if (! keyindexed[ii]) Fallkeysindexed = 0; | // Test files against main select criteria in search dialog | |
// NOT | ||
if (Fscanall) Nscan = Nxxrec; | ||
// scan all files | ||
if (Fscancurr) Nscan = navi::Gfiles; | ||
// scan current set (current gallery) | ||
if (! Nscan) { | ||
Ncurrset = 0; | ||
goto search_complete; | ||
} | ||
cc = Nscan * sizeof(ch *); | ||
// list of files to scan | ||
scanfiles = (ch **) zmalloc(cc,"search"); | ||
// Begin search. Use up to NST exiftool/perl threads. ---------------------- | for (ii = 0; ii < Nscan; ii++) | |
- | // create scanfiles[] list | |
{ | ||
file = 0; | ||
if (Fscanall) file = xxrec_tab[ii]->file; | ||
if (Fscancurr) file = gallery(0,"getR",ii); | ||
scanfiles[ii] = file; | ||
} | ||
zadd_locked(Ffuncbusy,+1); | zadd_locked(Ffuncbusy,+1); | |
Fescape = 1; | Fwatchescape = 1; | |
// killable with escape key | // killable with escape key | |
m_viewmode(0,"F"); | ||
search_main(); | ||
// test main select criteria | ||
search_xmeta(); | ||
// test indexed metadata select criteria | ||
search_nxmeta(); | ||
// test non-indexed metadata select criteria | ||
zadd_locked(Ffuncbusy,-1); | ||
if (Fescape > 1) goto usercancel; | ||
// user killed search | ||
Plog(1,"matching files: %d \n",Npass); | ||
if (Fnewset) { /* do nothing */ } | ||
// new set: no change in passfiles[] | ||
if (Faddset) | ||
// add to set | ||
{ | ||
cc = (navi::Gfiles + Npass) * sizeof(ch *); | ||
// passfiles = gallery + passfiles | ||
ch **passfiles2 = (ch **) zmalloc(cc+1,"search"); | ||
for (ii = jj = 0; ii < navi::Gfiles; ii++) | ||
passfiles2[jj++] = gallery(0,"getR",ii); | ||
for (ii = 0; ii < Npass; ii++) | ||
passfiles2[jj++] = passfiles[ii]; | ||
Npass = jj; | ||
zfree(passfiles); | ||
passfiles = passfiles2; | ||
HeapSort4(passfiles,Npass,zstrcmp2); | ||
// sort passfiles | ||
for (ii = jj = 0; ii < Npass; ii++) { | ||
// eliminate duplicates | ||
if (strmatch(passfiles[ii],passfiles[jj])) continue; | ||
passfiles[++jj] = passfiles[ii]; | ||
} | ||
Npass = jj + 1; | ||
} | ||
if (Fremset) | ||
// remove from set | ||
{ | ||
cc = (navi::Gfiles + Npass) * sizeof(ch *); | ||
// passfiles = gallery - matching passfiles | ||
ch **passfiles2 = (ch **) zmalloc(cc+1,"search"); | ||
for (ii = jj = 0; ii < navi::Gfiles; ii++) | ||
passfiles2[jj++] = gallery(0,"getR",ii); | ||
for (ii = 0; ii < Npass; ii++) | ||
passfiles2[jj++] = passfiles[ii]; | ||
Npass = jj; | ||
zfree(passfiles); | ||
passfiles = passfiles2; | ||
HeapSort4(passfiles,Npass,zstrcmp2); | ||
// sort passfiles | ||
for (ii = 0; ii < Npass-1; ii++) { | ||
// duplicate pairs = null | ||
if (strmatch(passfiles[ii],passfiles[ii+1])) { | ||
passfiles[ii] = passfiles[ii+1] = 0; | ||
ii++; | ||
} | ||
} | ||
for (ii = jj = 0; ii < Npass; ii++) { | ||
// remove null pairs | ||
if (passfiles[ii]) | ||
passfiles[jj++] = passfiles[ii]; | ||
} | ||
Npass = jj; | ||
} | ||
if (Npass) | ||
{ | ||
srfid = fopen(searchresults_file,"w"); | ||
// open new output file | ||
if (! srfid) goto filerror; | ||
for (ii = 0; ii < Npass; ii++) | ||
// passfiles[] --> search results file | ||
{ | ||
file = passfiles[ii]; | ||
cc = fprintf(srfid,"%s\n",file); | ||
if (! cc) break; | ||
} | ||
fclose(srfid); | ||
if (! cc) goto filerror; | ||
} | ||
Ncurrset = Npass; | ||
// current set, including last results | ||
// add related files if wanted (original, last version only ...) -------------- | ||
- | ||
if (Ncurrset) search_add_related_files(); | ||
// search complete ------------------------------------------------------------ | ||
- | ||
search_complete: | ||
Fwatchescape = Fescape = 0; | ||
zdialog_free(zd); | ||
if (scanfiles) zfree(scanfiles); | ||
if (passfiles) zfree(passfiles); | ||
scanfiles = passfiles = 0; | ||
Plog(1,"search count: %d \n", Ncurrset); | ||
if (Ncurrset == 0) { | ||
if (Fnewset || Faddset) zmessageACK(Mwin,"nothing found"); | ||
if (Fremset) zmessageACK(Mwin,"nothing left, no change made"); | ||
return 1; | ||
} | ||
snprintf(albumfile,200,"%s/search_results",albums_folder); | ||
// save search results in the | ||
err = cp_copy(searchresults_file,albumfile); | ||
// album "search_results" | ||
if (err) zmessageACK(Mwin,strerror(err)); | ||
navi::gallerytype = SEARCH; | ||
// normal search results | ||
gallery(searchresults_file,"initF",0); | ||
// generate gallery of matching files | ||
if (Frepmeta) { | ||
// metadata report format | ||
search_metadata_report(); | ||
// get metadata from image files | ||
navi::gallerytype = META; | ||
// report | ||
} | ||
gallery(0,"paint",0); | ||
// position at top | ||
m_viewmode(0,"G"); | ||
return 1; | ||
usercancel: | ||
// cancel via escape key | ||
zmessage_post_bold(Mwin,"parent",1,"function canceled"); | ||
Fwatchescape = Fescape = 0; | ||
zdialog_free(zd); | ||
if (scanfiles) zfree(scanfiles); | ||
if (passfiles) zfree(passfiles); | ||
scanfiles = passfiles = 0; | ||
return 1; | ||
filerror: | ||
zmessageACK(Mwin,"file error: %s",strerror(errno)); | ||
Fwatchescape = Fescape = 0; | ||
zdialog_free(zd); | ||
if (scanfiles) zfree(scanfiles); | ||
if (passfiles) zfree(passfiles); | ||
scanfiles = passfiles = 0; | ||
return 1; | ||
} | ||
// Test image files against main selection criteria | ||
// Mark matching files | ||
void search_main() | ||
{ | ||
using namespace search_images; | ||
int ii, jj, ff, cc, iis, iit, iif; | ||
int Nmatch, Nnomatch, match1; | ||
ch *pps, *ppf, *ppt; | ||
ch *file; | ||
xxrec_t *xxrec; | ||
if (! Nscan) { | ||
Npass = 0; | ||
return; | ||
} | ||
for (ff = 0; ff < Nscan; ff++) | ||
// loop through files to scan | ||
{ | ||
file = scanfiles[ff]; | ||
xxrec = get_xxrec(file); | ||
if (! xxrec) goto nomatch; | ||
if (Ffiles) | ||
// file name match is wanted | ||
{ | ||
Nmatch = Nnomatch = 0; | ||
for (ii = 1; ; ii++) | ||
{ | ||
pps = substringR(searchfiles," ,",ii); | ||
// step thru search file names | ||
if (! pps) break; | ||
if (strchr(pps,'*')) { | ||
if (MatchWildCase(pps,file) == 0) Nmatch++; | ||
// use wildcard matching | ||
else Nnomatch++; | ||
} | ||
else { | ||
if (strcasestr(file,pps)) Nmatch++; | ||
// use substring matching | ||
else Nnomatch++; | ||
} | ||
zfree(pps); | ||
} | ||
if (Nmatch == 0) goto nomatch; | ||
// no match any file | ||
if (Fallfiles && Nnomatch) goto nomatch; | ||
// no match all files | ||
} | ||
if (Fnulldate && ! strmatch(xxrec->pdate,"")) goto nomatch; | ||
// missing photo date wanted | ||
if (Fdates) | ||
// date match is wanted | ||
{ | ||
if (Fphotodate) { | ||
if (strcmp(xxrec->pdate,searchdatelo) < 0) goto nomatch; | ||
// test photo date | ||
if (strcmp(xxrec->pdate,searchdatehi) > 0) goto nomatch; | ||
} | ||
if (Ffiledate) { | ||
// test file mod date | ||
if (strcmp(xxrec->fdate,searchdatelo) < 0) goto nomatch; | ||
if (strcmp(xxrec->fdate,searchdatehi) > 0) goto nomatch; | ||
} | ||
} | ||
if (Ftags) | ||
// tags match is wanted | ||
{ | ||
if (! xxrec->tags) goto nomatch; | ||
// should not happen | ||
Nmatch = Nnomatch = 0; | ||
for (iis = 1; ; iis++) | ||
// step thru search tags | ||
{ | ||
pps = substringR(searchtags,",;",iis); | ||
// delimited | ||
if (! pps) break; | ||
if (*pps == 0) { | ||
zfree(pps); | ||
continue; | ||
} | ||
for (iif = 1; ; iif++) | ||
// step thru file tags (delimited) | ||
{ | ||
ppf = substringR(xxrec->tags,",;",iif); | ||
if (! ppf) { Nnomatch++; break; } | ||
// count matches and fails | ||
if (*ppf == 0) { | ||
zfree(ppf); | ||
continue; | ||
} | ||
if (strmatch(pps,ppf)) { | ||
Nmatch++; | ||
zfree(ppf); | ||
break; | ||
} | ||
else zfree(ppf); | ||
} | ||
zfree(pps); | ||
} | ||
if (Nmatch == 0) goto nomatch; | ||
// no match to any tag | ||
if (Falltags && Nnomatch) goto nomatch; | ||
// no match to all tags | ||
} | ||
if (Frating) | ||
// rating match is wanted | ||
{ | ||
if (*searchRatingFrom && xxrec->rating[0] < *searchRatingFrom) goto nom | ||
atch; | ||
if (*searchRatingTo && xxrec->rating[0] > *searchRatingTo) goto nomatch | ||
; | ||
} | ||
if (Ftext) | ||
// text match is wanted | ||
{ | ||
// opt. wildcard '*' match | ||
Nmatch = Nnomatch = 0; | ||
for (iis = 1; ; iis++) | ||
// step through search words | ||
{ | ||
match1 = 0; | ||
pps = substringR(searchtext,' ',iis); | ||
if (! pps) break; | ||
m_viewmode(0,"G"); | for (iit = 1; ; iit++) | |
// gallery view | // step through title words | |
{ | ||
ppt = substringR(xxrec->title," ,.;:?/'\"",iit); | ||
// delimiters: blank , . ; : ? / ' " | ||
if (! ppt) break; | ||
if (MatchWildCase(pps,ppt) == 0) match1 = 1; | ||
// match search amd title words | ||
zfree(ppt); | ||
if (match1) break; | ||
} | ||
if (! match1) | ||
{ | ||
for (iit = 1; ; iit++) | ||
// step through description words | ||
{ | ||
ppt = substringR(xxrec->desc," ,.;:?/'\"",iit); | ||
if (! ppt) break; | ||
if (MatchWildCase(pps,ppt) == 0) match1 = 1; | ||
// match search and description words | ||
zfree(ppt); | ||
if (match1) break; | ||
} | ||
} | ||
if (match1) Nmatch++; | ||
// count words matched and not matched | ||
else Nnomatch++; | ||
zfree(pps); | ||
} | ||
if (Nmatch == 0) goto nomatch; | ||
// no match to any word | ||
if (Falltext && Nnomatch) goto nomatch; | ||
// no match to all words | ||
} | ||
if (Flocs ) | ||
// location match is wanted | ||
{ | ||
Nmatch = Nnomatch = 0; | ||
for (iis = 1; ; iis++) | ||
// step thru search locations | ||
{ | ||
pps = substringR(searchLocations,", ",iis); | ||
// comma or blank delimiter | ||
if (! pps) break; | ||
if (strcasestr(xxrec->location,pps)) Nmatch++; | ||
// no special treatment for "null" | ||
else if (strcasestr(xxrec->country,pps)) Nmatch++; | ||
else Nnomatch++; | ||
zfree(pps); | ||
} | ||
if (! Nmatch) goto nomatch; | ||
// no match found | ||
if (Falllocs && Nnomatch) goto nomatch; | ||
} | ||
// match: | ||
continue; | ||
// file passed main select criteria | ||
if (! (Fscanall && Faddset)) | nomatch: | |
// current set will be replaced | // file does not match | |
remove(searchresults_file); | scanfiles[ff] = 0; | |
// remove current set | // remove from scanfiles list | |
continue; | ||
} | ||
cc = Nscan * sizeof(ch *); | ||
passfiles = (ch **) zmalloc(cc,"search"); | ||
for (ii = jj = 0; ii < Nscan; ii++) | ||
// passfiles[] = remaining scanfiles[] | ||
if (scanfiles[ii]) | ||
passfiles[jj++] = scanfiles[ii]; | ||
Npass = jj; | ||
// count of passed files | ||
return; | ||
} | ||
srfid = fopen(searchresults_file,"a"); | // test indexed metadata against select criteria | |
// open output file, append | ||
if (! srfid) goto filerror; | void search_xmeta() | |
{ | ||
using namespace search_images; | ||
int searchmeta_test1(ch *keydata, ch machtyp, ch *machvals); | ||
ch *kname[maxNkeys], *kvals[maxNkeys], kmach[maxNkeys]; | ||
ch *xkey[xmetamaxkeys], *xval[xmetamaxkeys]; | ||
int ii, jj, ff, cc, NK, NX, pass; | ||
ch *xmeta, *pps, *ppf; | ||
ch *file; | ||
xxrec_t *xxrec; | ||
Xthread1 = Xthread2 = 0; | if (! Npass) return; | |
// started/completed thread counters | ||
Nmatch = 0; | ||
// matching images found | ||
if (Fscanall) | for (ii = jj = 0; ii < Nkeys; ii++) // indexed metadata select criteria | |
{ | { | |
progress_reset(Nxxrec); | if (keyindexed[ii] == 1) { | |
// start progress monitor | kname[jj] = srchkeys[ii]; | |
kvals[jj] = machvals[ii]; | ||
kmach[jj] = machtyp[ii]; | ||
jj++; | ||
} | ||
} | ||
NK = jj; | ||
if (NK == 0) return; | ||
// no indexed search keys | ||
for (ii = 0; ii < xmetamaxkeys; ii++) | ||
{ | ||
xkey[ii] = (ch *) zmalloc(metakeyXcc,"searchX"); | ||
xval[ii] = (ch *) zmalloc(metadataXcc,"searchX"); | ||
} | ||
for (ff = 0; ff < Npass; ff++) | ||
// loop through files to scan | ||
{ | ||
file = passfiles[ff]; | ||
// get indexed metadata for file | ||
xxrec = get_xxrec(file); | ||
xmeta = xxrec->xmeta; | ||
if (! xmeta) { | ||
// no metadata | ||
for (ii = 0; ii < NK; ii++) { | ||
// loop search keys | ||
pass = searchmeta_test1(0,kmach[ii],kvals[ii]); | ||
// test for "null" select criteria | ||
if (! pass) goto nomatch; | ||
} | ||
goto match; | ||
} | ||
pps = xmeta; | ||
for (ii = 0; ii < xmetamaxkeys; ii++) | ||
// unpack indexed metadata | ||
{ | ||
// to xkey[], xval[] | ||
ppf = strchr(pps,'='); | ||
if (! ppf) break; | ||
cc = ppf-pps; | ||
if (cc > 79) break; | ||
strncpy0(xkey[ii],pps,cc+1); | ||
pps = ppf + 1; | ||
ppf = strchr(pps,'^'); | ||
if (! ppf) break; | ||
cc = ppf - pps; | ||
if (cc > 99) break; | ||
strncpy0(xval[ii],pps,cc+1); | ||
pps = ppf + 1; | ||
while (*pps == ' ') pps++; | ||
} | ||
for (ii = 0; ii < Nxxrec; ii++) | NX = ii; | |
// scann all images | // xmeta keys found | |
pass = 1; | ||
// assume metadata match | ||
for (ii = 0; ii < NK; ii++) | ||
// loop search keys | ||
{ | { | |
if (Fescape > 1) break; | for (jj = 0; jj < NX; jj++) | |
// killed by user | // find matching file xmeta key | |
{ | ||
if (strmatchcase(kname[ii],xkey[jj])) { | ||
// if found, test metadata | ||
pass = searchmeta_test1(xval[jj],kmach[ii],kvals[ii]); | ||
// against select criteria | ||
if (! pass) goto nomatch; | ||
// fail, no more testing needed | ||
break; | ||
} | ||
} | ||
if (jj == NX) { | ||
// search key not present in file | ||
pass = searchmeta_test1(0,kmach[ii],kvals[ii]); | ||
// test for "null" select criteria | ||
if (! pass) goto nomatch; | ||
} | ||
} | ||
match: | ||
// file metadata matches | ||
continue; | ||
// select criteria | ||
nomatch: | ||
// metadata does not match | ||
passfiles[ff] = 0; | ||
// remove file from list | ||
continue; | ||
} | ||
for (ii = jj = 0; ii < Npass; ii++) | ||
// remove null files from list | ||
if (passfiles[ii]) | ||
passfiles[jj++] = passfiles[ii]; | ||
Npass = jj; | ||
// count of passed files | ||
for (ii = 0; ii < xmetamaxkeys; ii++) | ||
{ | ||
zfree(xkey[ii]); | ||
zfree(xval[ii]); | ||
} | ||
return; | ||
} | ||
// test non-indexed metadata against select criteria | ||
while (Xthread1 - Xthread2 == NST) zsleep(0.001); | void search_nxmeta() | |
// wait for free thread | { | |
Xthread1++; | using namespace search_images; | |
// incr. busy thread counter | ||
start_detached_thread(searchimages_thread,&Nval[ii]); | ||
// start thread for image ii | ||
zmainloop(10); | int searchmeta_test1(ch *keydata, ch machtyp, ch *machvals); | |
// avoid GTK 'not responding' | ||
progress_add(0,1); | ch *kname[maxNkeys], *kvals[maxNkeys], kmach[maxNkeys]; | |
// incr. progress | ch **kdata; | |
ch *xkey[xmetamaxkeys], *xval[xmetamaxkeys]; | ||
int ii, jj, jj1, jj2; | ||
int ff, cc, NK, pass; | ||
if (! Npass) return; | ||
for (ii = jj = 0; ii < Nkeys; ii++) | ||
// get non-indexed metadata | ||
{ | ||
// select criteria | ||
if (keyindexed[ii] == 0) { | ||
kname[jj] = srchkeys[ii]; | ||
// search keys | ||
kvals[jj] = machvals[ii]; | ||
// match values | ||
kmach[jj] = machtyp[ii]; | ||
// match type | ||
jj++; | ||
} | } | |
} | } | |
if (Fscancurr) | NK = jj; | |
if (NK == 0) return; | ||
// no non-indexed search keys | ||
cc = Npass * NK * sizeof(ch *); | ||
// allocate space for returned data | ||
kdata = (ch **) zmalloc(cc,"search"); | ||
meta_getN(passfiles, Npass, kname, kdata, NK); | ||
// get non-indexed metadata for all files | ||
for (ff = 0; ff < Npass; ff++) | ||
// loop through files to scan | ||
{ | { | |
progress_reset(navi::Gfiles); | jj1 = ff * NK; | |
// start progress monitor | // kdata[] range for file[ff] | |
jj2 = jj1 + NK; | ||
for (ii = 0; ii < navi::Gfiles; ii++) | for (ii = 0, jj = jj1; jj < jj2; ii++, jj++) { | |
// scan current gallery | // get key names and values for file[ff] | |
xkey[ii] = kname[ii]; | ||
xval[ii] = kdata[jj]; | ||
} | ||
for (ii = 0; ii < NK; ii++) | ||
// loop search keys | ||
{ | { | |
if (Fescape > 1) break; | for (jj = 0; jj < NK; jj++) | |
// killed by user | // find matching file key data | |
{ | ||
if (strmatchcase(kname[ii],xkey[jj])) { | ||
// if found, test metadata | ||
pass = searchmeta_test1(xval[jj],kmach[ii],kvals[ii]); | ||
// against select criteria | ||
if (! pass) goto nomatch; | ||
// fail, no more testing needed | ||
break; | ||
} | ||
} | ||
} | ||
continue; | ||
// file metadata fits criteria, next file | ||
nomatch: | ||
// metadata does not match | ||
passfiles[ff] = 0; | ||
// remove file from pass list | ||
continue; | ||
// next file | ||
} | ||
for (ii = 0; ii < Npass * NK; ii++) | ||
// free memory from meta_getN() | ||
if (kdata[ii]) zfree(kdata[ii]); | ||
zfree(kdata); | ||
for (ii = jj = 0; ii < Npass; ii++) | ||
// remove unmatching files from list | ||
if (passfiles[ii]) | ||
passfiles[jj++] = passfiles[ii]; | ||
Npass = jj; | ||
// count of passed files | ||
return; | ||
} | ||
// test a single metadata key/value against select criteria | ||
while (Xthread1 - Xthread2 == NST) zsleep(0.001); | int searchmeta_test1(ch *keydata, ch machtyp, ch *machvals) | |
// wait for free thread | { | |
Xthread1++; | using namespace search_images; | |
// incr. busy thread counter | ||
start_detached_thread(searchimages_thread,&Nval[ii]); | int nth, n1, n2, n3, mm; | |
// start thread for image ii | ch *pps, *ppm; | |
float Fkeydata = 0, Fsearchval; | ||
if (machtyp == 'r') return 1; | ||
// key value reported, not tested | ||
if (*machvals <= ' ') return 1; | ||
// no match values, pass | ||
if (! keydata) { | ||
// no metadata present | ||
if (strmatch(machvals,"null")) return 1; | ||
// search for empty data, pass | ||
return 0; | ||
// fail | ||
} | ||
if (strchr("= > <",machtyp)) { | ||
// real value, look for N/N format 23.0 | ||
Fkeydata = atofz(keydata); | ||
n1 = sscanf(keydata,"%d/%d",&n2,&n3); | ||
if (n1 == 2) Fkeydata = 1.0 * n2 / n3; | ||
} | ||
for (nth = 1; ; nth++) | ||
// loop all search values | ||
{ | ||
pps = substringR(machvals,',',nth); | ||
// comma delimiter | ||
if (! pps) return 0; | ||
// no more, no match found | ||
if (machtyp == 'm') { | ||
// key matches any [wild] value | ||
mm = MatchWildCase(pps,keydata); | ||
// match not case sensitive | ||
zfree(pps); | ||
if (mm == 0) return 1; | ||
} | ||
else if (machtyp == 'c') { | ||
// key contains any value | ||
ppm = strcasestr(keydata,pps); | ||
// match not case sensitive | ||
zfree(pps); | ||
if (ppm) return 1; | ||
} | ||
else if (machtyp == '=') { | ||
// numeric key equals any value | ||
Fsearchval = atofz(pps); | ||
zfree(pps); | ||
if (Fkeydata == Fsearchval) return 1; | ||
// found match | ||
} | ||
else if (machtyp == '>') { | ||
// numeric key >= one value | ||
Fsearchval = atofz(pps); | ||
zfree(pps); | ||
if (Fkeydata >= Fsearchval) return 1; | ||
// found match | ||
} | ||
else if (machtyp == '<') { | ||
// numeric key <= one value | ||
Fsearchval = atofz(pps); | ||
zfree(pps); | ||
if (Fkeydata <= Fsearchval) return 1; | ||
// found match | ||
} | ||
zmainloop(10); | else { | |
// avoid GTK 'not responding' | Plog(0,"searchmeta invalid machtyp %c \n",machtyp); | |
progress_add(0,1); | zfree(pps); | |
// incr. progress | return 0; | |
} | } | |
} | } | |
} | ||
while (Xthread1 != Xthread2) zsleep(0.01); | // add related files to search results if wanted | |
// wait for last thread | ||
fclose(srfid); | ||
// close output file | ||
progress_reset(0); | ||
// stop progress monitor | ||
exif_server(0,0,0); | ||
// kill server herd | ||
if (Fescape > 1) goto usercancel; | void search_add_related_files() | |
// user killed search | { | |
using namespace search_images; | ||
if (Fscanall && Fnewset) Ncurrset = Nmatch; | int cc, ii, kk, nf, jj, Nmatch; | |
// matched images = new current set | ch **flist = 0; | |
if (Fscanall && Faddset) Ncurrset += Nmatch; | ch *file, *file2; | |
// matched imaged added to current set | ch **vlist; | |
if (Fscancurr && Fnewset) Ncurrset = Nmatch; | ch buffer[XFCC]; | |
// matched images = new current set | ch *pp; | |
if (Fscancurr && Fremset) Ncurrset -= Nmatch; | ||
// matched images removed from current set | ||
if (! Ncurrset) goto search_complete; | ||
ZLsearch = zlist_from_file(searchresults_file); | ||
// sort search results file list | ||
if (! ZLsearch) goto search_complete; | ||
// (multithread search > sequence lost) | ||
zlist_sort(ZLsearch); | ||
zlist_to_file(ZLsearch,searchresults_file); | ||
zlist_delete(ZLsearch); | ||
// Include only last version of matching images ---------------------------- - | // Include only last version of matching images ---------------------------- - | |
if (Flastver && Ncurrset) | if (Flastver && Ncurrset) | |
{ | { | |
cc = Ncurrset * sizeof(char *); | cc = Ncurrset * sizeof(ch *); | |
flist = (char **) zmalloc(cc,"search"); | flist = (ch **) zmalloc(cc,"search"); | |
srfid = fopen(searchresults_file,"r"); // read file of selected image files | srfid = fopen(searchresults_file,"r"); // read file of selected image files | |
if (! srfid) goto filerror; | if (! srfid) goto filerror; | |
for (ii = 0; ii < Ncurrset; ii++) // build file list in memory | for (ii = 0; ii < Ncurrset; ii++) // build file list in memory | |
{ | { | |
file = fgets_trim(buffer,XFCC,srfid); | file = fgets_trim(buffer,XFCC,srfid); | |
if (! file) break; | if (! file) break; | |
flist[ii] = zstrdup(file,"search"); | flist[ii] = zstrdup(file,"search"); | |
} | } | |
skipping to change at line 6021 | skipping to change at line 6588 | |
file2 = file_newest_version(file); // insure last version | file2 = file_newest_version(file); // insure last version | |
if (! file2) continue; // should not happen | if (! file2) continue; // should not happen | |
fprintf(srfid,"%s\n",file2); | fprintf(srfid,"%s\n",file2); | |
zfree(file); | zfree(file); | |
zfree(file2); | zfree(file2); | |
Nmatch++; | Nmatch++; | |
} | } | |
fclose(srfid); | fclose(srfid); | |
zfree(flist); | zfree(flist); | |
flist = 0; | ||
Ncurrset = Nmatch; // new current set count | Ncurrset = Nmatch; // new current set count | |
} | } | |
// Include original and last version of matching images -------------------- - | // Include original and last version of matching images -------------------- - | |
if (Forglast && Ncurrset) | if (Forglast && Ncurrset) | |
{ | { | |
cc = Ncurrset * sizeof(char *); | cc = Ncurrset * sizeof(ch *); | |
// first, reduce selected images | // first, reduce selected images | |
flist = (char **) zmalloc(cc,"search"); | flist = (ch **) zmalloc(cc,"search"); | |
// to one version each | // to one version each | |
srfid = fopen(searchresults_file,"r"); // read file of selected image files | srfid = fopen(searchresults_file,"r"); // read file of selected image files | |
if (! srfid) goto filerror; | if (! srfid) goto filerror; | |
for (ii = 0; ii < Ncurrset; ii++) // build file list in memory | for (ii = 0; ii < Ncurrset; ii++) // build file list in memory | |
{ | { | |
file = fgets_trim(buffer,XFCC,srfid); | file = fgets_trim(buffer,XFCC,srfid); | |
flist[ii] = zstrdup(file,"search"); | flist[ii] = zstrdup(file,"search"); | |
} | } | |
skipping to change at line 6069 | skipping to change at line 6637 | |
for (ii = 0; ii < Ncurrset; ii++) | for (ii = 0; ii < Ncurrset; ii++) | |
{ | { | |
if (flist[ii]) { | if (flist[ii]) { | |
fprintf(srfid,"%s\n",flist[ii]); | fprintf(srfid,"%s\n",flist[ii]); | |
zfree(flist[ii]); | zfree(flist[ii]); | |
} | } | |
} | } | |
fclose(srfid); | fclose(srfid); | |
zfree(flist); | zfree(flist); | |
flist = 0; | ||
flist = (char **) zmalloc(maxgallery * sizeof(char *),"search"); // new list for original + last ver. | flist = (ch **) zmalloc(maxgallery * sizeof(ch *),"search"); // new list for original + last ver. | |
srfid = fopen(searchresults_file,"r"); // read file of selected image files | srfid = fopen(searchresults_file,"r"); // read file of selected image files | |
if (! srfid) goto filerror; | if (! srfid) goto filerror; | |
for (ii = Nmatch = 0; Nmatch < maxgallery-2; ii++) // get all versions for selected files | for (ii = Nmatch = 0; Nmatch < maxgallery-2; ii++) // get all versions for selected files | |
{ | { | |
file = fgets_trim(buffer,XFCC,srfid); | file = fgets_trim(buffer,XFCC,srfid); | |
if (! file) break; | if (! file) break; | |
vlist = file_all_versions(file,nf); | vlist = file_all_versions(file,nf); | |
if (! vlist) continue; | if (! vlist) continue; | |
skipping to change at line 6104 | skipping to change at line 6673 | |
if (! srfid) goto filerror; | if (! srfid) goto filerror; | |
for (ii = 0; ii < Nmatch; ii++) // write all versions to file | for (ii = 0; ii < Nmatch; ii++) // write all versions to file | |
{ | { | |
fprintf(srfid,"%s\n",flist[ii]); | fprintf(srfid,"%s\n",flist[ii]); | |
zfree(flist[ii]); | zfree(flist[ii]); | |
} | } | |
fclose(srfid); | fclose(srfid); | |
zfree(flist); | zfree(flist); | |
flist = 0; | ||
Ncurrset = Nmatch; // new current set count | Ncurrset = Nmatch; // new current set count | |
} | } | |
// Include original and all versions of matching images -------------------- - | // Include original and all versions of matching images -------------------- - | |
if (Fallvers && Ncurrset) | if (Fallvers && Ncurrset) | |
{ | { | |
cc = Ncurrset * sizeof(char *); | cc = Ncurrset * sizeof(ch *); | |
// first, reduce selected images | // first, reduce selected images | |
flist = (char **) zmalloc(cc,"search"); | flist = (ch **) zmalloc(cc,"search"); | |
// to one version each | // to one version each | |
srfid = fopen(searchresults_file,"r"); // read file of selected image files | srfid = fopen(searchresults_file,"r"); // read file of selected image files | |
if (! srfid) goto filerror; | if (! srfid) goto filerror; | |
for (ii = 0; ii < Ncurrset; ii++) // build file list in memory | for (ii = 0; ii < Ncurrset; ii++) // build file list in memory | |
{ | { | |
file = fgets_trim(buffer,XFCC,srfid); | file = fgets_trim(buffer,XFCC,srfid); | |
flist[ii] = zstrdup(file,"search"); | flist[ii] = zstrdup(file,"search"); | |
} | } | |
skipping to change at line 6152 | skipping to change at line 6722 | |
for (ii = 0; ii < Ncurrset; ii++) | for (ii = 0; ii < Ncurrset; ii++) | |
{ | { | |
if (flist[ii]) { | if (flist[ii]) { | |
fprintf(srfid,"%s\n",flist[ii]); | fprintf(srfid,"%s\n",flist[ii]); | |
zfree(flist[ii]); | zfree(flist[ii]); | |
} | } | |
} | } | |
fclose(srfid); | fclose(srfid); | |
zfree(flist); | zfree(flist); | |
flist = 0; | ||
flist = (char **) zmalloc(maxgallery * sizeof(char *),"search"); // new list for all versions | flist = (ch **) zmalloc(maxgallery * sizeof(ch *),"search"); // new list for all versions | |
srfid = fopen(searchresults_file,"r"); // read file of selected image files | srfid = fopen(searchresults_file,"r"); // read file of selected image files | |
if (! srfid) goto filerror; | if (! srfid) goto filerror; | |
for (ii = Nmatch = 0; Nmatch < maxgallery-100; ii++) // get all versions for selected files | for (ii = Nmatch = 0; Nmatch < maxgallery-100; ii++) // get all versions for selected files | |
{ | { | |
file = fgets_trim(buffer,XFCC,srfid); | file = fgets_trim(buffer,XFCC,srfid); | |
if (! file) break; | if (! file) break; | |
vlist = file_all_versions(file,nf); | vlist = file_all_versions(file,nf); | |
if (! vlist) continue; | if (! vlist) continue; | |
skipping to change at line 6185 | skipping to change at line 6756 | |
if (! srfid) goto filerror; | if (! srfid) goto filerror; | |
for (ii = 0; ii < Nmatch; ii++) // write all versions to file | for (ii = 0; ii < Nmatch; ii++) // write all versions to file | |
{ | { | |
fprintf(srfid,"%s\n",flist[ii]); | fprintf(srfid,"%s\n",flist[ii]); | |
zfree(flist[ii]); | zfree(flist[ii]); | |
} | } | |
fclose(srfid); | fclose(srfid); | |
zfree(flist); | zfree(flist); | |
flist = 0; | ||
Ncurrset = Nmatch; // new current set count | Ncurrset = Nmatch; // new current set count | |
} | } | |
// Search complete. -------------------------------------------------------- | return; | |
- | ||
search_complete: | ||
Plog(1,"search count: %d \n", Ncurrset); | ||
if (Ncurrset == 0) zmessageACK(Mwin,"nothing found"); | ||
zadd_locked(Ffuncbusy,-1); | ||
Fescape = 0; | ||
progress_reset(0); | ||
// progress counter end | ||
if (Ncurrset == 0) { | ||
// no images found | ||
zd->zstat = 0; | ||
// stay in search dialog | ||
return 1; | ||
} | ||
snprintf(albumfile,200,"%s/search_results",albums_folder); | ||
// save search results in the | ||
err = cp_copy(searchresults_file,albumfile); | ||
// album "search_results" | ||
if (err) zmessageACK(Mwin,strerror(err)); | ||
navi::gallerytype = SEARCH; | ||
// normal search results | ||
gallery(searchresults_file,"initF",0); | ||
// generate gallery of matching files | ||
if (Frepmeta) { | ||
// metadata report format | ||
searchimages_metadata_report(); | ||
// get metadata from image files | ||
navi::gallerytype = META; | ||
// report | ||
} | ||
gallery(0,"paint",0); | ||
// position at top | ||
m_viewmode(0,"G"); | ||
zdialog_free(zd); | ||
return 1; | ||
usercancel: | ||
// cancel via escape key | ||
zmessage_post_bold(Mwin,"parent",1,"function canceled"); | ||
zadd_locked(Ffuncbusy,-1); | ||
Fescape = 0; | ||
progress_reset(0); | ||
// 22.35 | ||
zdialog_free(zd); | ||
return 1; | ||
filerror: | filerror: | |
zmessageACK(Mwin,"file error: %s",strerror(errno)); | zmessageACK(Mwin,"file error: %s",strerror(errno)); | |
zadd_locked(Ffuncbusy,-1); | fclose(srfid); | |
Fescape = 0; | if (flist) zfree(flist); | |
progress_reset(0); | return; | |
// 22.35 | ||
zdialog_free(zd); | ||
return 1; | ||
} | } | |
// search images, thread process | // Report selected files using a gallery window layout | |
// arg is xxrec[*] index of image to test for selection or exclusion | // with image thumbnails and selected metadata text. | |
void * searchimages_thread(void *arg) | int search_metadata_report() | |
{ | { | |
using namespace search_images; | using namespace search_images; | |
using namespace navi; | ||
int searchimages_select(xxrec_t *xxrec); | int ff, ii, jj, cc; | |
ch *file = 0, **repfiles = 0, **keyvals = 0; | ||
int ii, match = 0; | ch pdate[12], ptime[12]; | |
ch wwhh[16], fsize[16]; | ||
ch text1[2000], text2[200]; | ||
// note text1 limit | ||
xxrec_t *xxrec; | xxrec_t *xxrec; | |
char *file; | ||
ii = *((int *) arg); | ||
// test xxrec_tab[ii] | ||
if (Fscanall && Fnewset) | ||
// search all, make NEW set from matches | ||
{ | ||
xxrec = xxrec_tab[ii]; | ||
match = searchimages_select(xxrec); | ||
// test against select criteria | ||
if (match) fprintf(srfid,"%s\n",xxrec->file); | ||
// match: add to current set | ||
} | ||
if (Fscanall && Faddset) | ||
// search all, ADD matches to current set | ||
{ | ||
xxrec = xxrec_tab[ii]; | ||
match = searchimages_select(xxrec); | ||
// test against select criteria | ||
if (match) fprintf(srfid,"%s\n",xxrec->file); | ||
// match: add to current set | ||
} | ||
if (Fscancurr && Fnewset) | ||
// search current set, KEEP matching images | ||
{ | ||
file = gallery(0,"get",ii); | ||
if (! file) goto exit_thread; | ||
if (*file == '!') { | ||
// skip folders | ||
zfree(file); | ||
goto exit_thread; | ||
} | ||
xxrec = get_xxrec(file); | ||
if (! xxrec) { | ||
// no image index rec? | ||
zfree(file); | ||
goto exit_thread; | ||
} | ||
match = searchimages_select(xxrec); | ||
// test against select criteria | ||
if (match) fprintf(srfid,"%s\n",xxrec->file); | ||
// match: add to current set | ||
} | ||
if (Fscancurr && Fremset) | ||
// search current set, REMOVE matching images | ||
{ | ||
file = gallery(0,"get",ii); | ||
if (! file) goto exit_thread; | ||
if (*file == '!') { | ||
// skip folders | ||
zfree(file); | ||
goto exit_thread; | ||
} | ||
xxrec = get_xxrec(file); | if (! Gfiles) { | |
if (! xxrec) { | // curr. gallery files | |
// no image index rec? | Plog(1,"metadata report, 0 files \n"); | |
zfree(file); | return 0; | |
goto exit_thread; | ||
} | ||
match = searchimages_select(xxrec); | ||
// test against select criteria | ||
if (! match) fprintf(srfid,"%s\n",xxrec->file); | ||
// NOT match: add to current set | ||
} | } | |
if (match) zadd_locked(Nmatch,+1); | if (Nkeys) | |
// count matching images | ||
exit_thread: | ||
zadd_locked(Xthread2,+1); | ||
return 0; | ||
} | ||
// Test a given image file against selection criteria, return match status. | ||
int searchimages_select(xxrec_t *xxrec) | ||
{ | ||
using namespace search_images; | ||
int searchimages_metadata_select(xxrec_t *xxrec); | ||
char *pps, *ppf, *ppt; | ||
int iis, iif, iit; | ||
int Nmatch, Nnomatch, match1; | ||
if (Ffiles) | ||
// file name match is wanted | ||
{ | { | |
Nmatch = Nnomatch = 0; | cc = Gfiles * sizeof(ch *); | |
// make file list from curr. gallery | ||
for (iis = 1; ; iis++) | repfiles = (ch **) zmalloc(cc,"search"); | |
{ | ||
pps = substringR(searchfiles,' ',iis); | ||
// step thru search file names | ||
if (! pps) break; | ||
if (strchr(pps,'*')) { | ||
if (MatchWildCase(pps,xxrec->file) == 0) Nmatch++; | ||
// use wildcard matching | ||
else Nnomatch++; | ||
} | ||
else { | for (ff = 0; ff < Gfiles; ff++) | |
if (strcasestr(xxrec->file,pps)) Nmatch++; | repfiles[ff] = gallery(0,"getR",ff); | |
// use substring matching | ||
else Nnomatch++; | ||
} | ||
zfree(pps); | cc = Gfiles * Nkeys * sizeof(ch **); | |
} | // allocate pointers for returned metadata | |
keyvals = (ch **) zmalloc(cc,"search"); | ||
if (Nmatch == 0) return 0; | meta_getN(repfiles,Gfiles,srchkeys,keyvals,Nkeys); | |
// no match any file | // get Nkeys keyvals per repfile | |
if (Fallfiles && Nnomatch) return 0; | ||
// no match all files | ||
} | } | |
if (Fnulldate && ! strmatch(xxrec->pdate,"")) return 0; | for (ff = 0; ff < Gfiles; ff++) | |
// missing photo date wanted | // scan all images in gallery | |
if (Fdates) | ||
// date match is wanted | ||
{ | { | |
if (Fphotodate) { | file = gallery(0,"getR",ff); | |
if (strcmp(xxrec->pdate,searchdatelo) < 0) return 0; | if (! file) continue; | |
// test photo date | ||
if (strcmp(xxrec->pdate,searchdatehi) > 0) return 0; | ||
} | ||
if (Ffiledate) { | xxrec = get_xxrec(file); | |
// test file mod date | // get metadata available in index table | |
if (strcmp(xxrec->fdate,searchdatelo) < 0) return 0; | if (! xxrec) continue; | |
if (strcmp(xxrec->fdate,searchdatehi) > 0) return 0; | ||
} | ||
} | ||
if (Ftags) | metadate_pdate(xxrec->pdate,pdate,ptime); | |
// tags match is wanted | snprintf(text2,200,"photo date: %s \n",pdate); | |
{ | strcpy(text1,text2); | |
if (! xxrec->tags) return 0; | cc = strlen(text1); | |
// should not happen | ||
Nmatch = Nnomatch = 0; | snprintf(wwhh,16,"%dx%d",xxrec->ww,xxrec->hh); | |
snprintf(fsize,16,"%.2fmb",xxrec->fsize/MEGA); | ||
snprintf(text2,200,"Rating: %s Size: %s %s \n", | ||
xxrec->rating, wwhh, fsize); | ||
strcpy(text1+cc,text2); | ||
cc += strlen(text2); | ||
for (iis = 1; ; iis++) | snprintf(text2,200,"tags: %s\n",xxrec->tags); | |
// step thru search tags | strcpy(text1+cc,text2); | |
{ | cc += strlen(text2); | |
pps = substringR(searchtags,",;",iis); | ||
// delimited | ||
if (! pps) break; | ||
if (*pps == 0) { | ||
zfree(pps); | ||
continue; | ||
} | ||
for (iif = 1; ; iif++) | snprintf(text2,200,"Location: %s\n",xxrec->location); | |
// step thru file tags (delimited) | strcpy(text1+cc,text2); | |
{ | cc += strlen(text2); | |
ppf = substringR(xxrec->tags,",;",iif); | ||
if (! ppf) { Nnomatch++; break; } | ||
// count matches and fails | ||
if (*ppf == 0) { | ||
zfree(ppf); | ||
continue; | ||
} | ||
if (strmatch(pps,ppf)) { | ||
Nmatch++; | ||
zfree(ppf); | ||
break; | ||
} | ||
else zfree(ppf); | ||
} | ||
zfree(pps); | snprintf(text2,200,"title: %s\n",xxrec->title); | |
} | strcpy(text1+cc,text2); | |
cc += strlen(text2); | ||
if (Nmatch == 0) return 0; | snprintf(text2,200,"description: %s\n",xxrec->desc); | |
// no match to any tag | strcpy(text1+cc,text2); | |
if (Falltags && Nnomatch) return 0; | cc += strlen(text2); | |
// no match to all tags | ||
} | ||
if (Frating) | if (Gindex[ff].mdata1) zfree(Gindex[ff].mdata1); | |
// rating match is wanted | // standard metadata >> gallery index | |
{ | Gindex[ff].mdata1 = zstrdup(text1,"search"); | |
if (*searchRatingFrom && xxrec->rating[0] < *searchRatingFrom) return 0; | ||
if (*searchRatingTo && xxrec->rating[0] > *searchRatingTo) return 0; | ||
} | ||
if (Ftext) | if (Gindex[ff].mdata2) zfree(Gindex[ff].mdata2); | |
// text match is wanted | // clear user selected metadata | |
{ | Gindex[ff].mdata2 = 0; | |
// opt. wildcard '*' match | ||
Nmatch = Nnomatch = 0; | ||
for (iis = 1; ; iis++) // step through search words | if (Nkeys) // other metadata to report | |
{ | { | |
match1 = 0; | ii = ff * Nkeys; // metadata values for this file | |
pps = substringR(searchtext,' ',iis); | for (cc = jj = 0; jj < Nkeys; jj++, ii++) | |
if (! pps) break; | ||
for (iit = 1; ; iit++) | ||
// step through title words | ||
{ | { | |
ppt = substringR(xxrec->title," ,.;:?/'\"",iit); | snprintf(text2,200,"%s: %s \n",srchkeys[jj], keyvals[ii]); | |
// delimiters: blank , . ; : ? / ' " | if (cc + strlen(text2) > 1999) break; | |
if (! ppt) break; | strcpy(text1+cc,text2); | |
if (MatchWildCase(pps,ppt) == 0) match1 = 1; | cc += strlen(text2); | |
// match search amd title words | ||
zfree(ppt); | ||
if (match1) break; | ||
} | ||
if (! match1) | ||
{ | ||
for (iit = 1; ; iit++) | ||
// step through description words | ||
{ | ||
ppt = substringR(xxrec->desc," ,.;:?/'\"",iit); | ||
if (! ppt) break; | ||
if (MatchWildCase(pps,ppt) == 0) match1 = 1; | ||
// match search and description words | ||
zfree(ppt); | ||
if (match1) break; | ||
} | ||
} | ||
if (match1) Nmatch++; | ||
// count words matched and not matched | ||
else Nnomatch++; | ||
zfree(pps); | ||
} | ||
if (Nmatch == 0) return 0; | ||
// no match to any word | ||
if (Falltext && Nnomatch) return 0; | ||
// no match to all words | ||
} | ||
if (Flocs ) | ||
// location match is wanted | ||
{ | ||
Nmatch = Nnomatch = 0; | ||
for (iis = 1; ; iis++) | ||
// step thru search locations | ||
{ | ||
pps = substringR(searchLocations,", ",iis); | ||
// comma or blank delimiter | ||
if (! pps) break; | ||
if (strcasestr(xxrec->location,pps)) Nmatch++; | ||
// no special treatment for "null" | ||
else if (strcasestr(xxrec->country,pps)) Nmatch++; | ||
else Nnomatch++; | ||
zfree(pps); | ||
} | ||
if (! Nmatch) return 0; | ||
// no match found | ||
if (Falllocs && Nnomatch) return 0; | ||
} | ||
if (! searchimages_metadata_select(xxrec)) return 0; | ||
return 1; | ||
// match | ||
} | ||
// Test image file metadata against metadata select criteria. | ||
// indexed metadata record: xmeta: keyname1=keyvalue1^ keyname2=keyvalue2^ ... | ||
int searchimages_metadata_select(xxrec_t *xxrec) | ||
{ | ||
using namespace search_images; | ||
int searchmeta_test1(cchar *kval, cchar matchtype, cchar *searchvals); | ||
char *kvals[3]; | ||
int ii, jj, cc, nx = 0, pass = 1; | ||
cchar *pps, *ppf; | ||
char *file = xxrec->file; | ||
char *xmeta = xxrec->xmeta; | ||
// indexed metadata list | ||
char xkey[MXmax][80], xval[MXmax][100]; | ||
pps = xmeta; | ||
// unpack indexed metadata | ||
for (ii = 0; ii < MXmax; ii++) { | ||
// to xkey[], xval[] | ||
ppf = strchr(pps,'='); | ||
if (! ppf) break; | ||
cc = ppf-pps; | ||
if (cc > 79) break; | ||
strncpy0(xkey[ii],pps,cc+1); | ||
pps = ppf + 1; | ||
ppf = strchr(pps,'^'); | ||
if (! ppf) break; | ||
cc = ppf - pps; | ||
if (cc > 99) break; | ||
strncpy0(xval[ii],pps,cc+1); | ||
pps = ppf + 1; | ||
while (*pps == ' ') pps++; | ||
} | ||
nx = ii; | ||
// xmeta keys found | ||
for (ii = 0; ii < Nsearchkeys; ii++) { | ||
// loop search keys | ||
for (jj = 0; jj < nx; jj++) { | ||
// find xmeta key | ||
if (strmatchcase(searchkeys[ii],xkey[jj])) { | ||
// if found, test xmeta data | ||
pass = searchmeta_test1(xval[jj],matchtype[ii],searchvals[ii]); | ||
// against search criteria | ||
if (! pass) return 0; | ||
// fail, no more testing needed | ||
break; | ||
} | } | |
} | ||
if (jj == nx && keyindexed[ii]) { | ||
// indexed search key, not present | ||
pass = searchmeta_test1(0,matchtype[ii],searchvals[ii]); | ||
// test for "null" select criteria | ||
if (! pass) return 0; | ||
// fail | ||
} | ||
} | ||
if (Fallkeysindexed) return 1; | ||
// all search keys indexed, pass | ||
exif_get(file,(cchar **) searchkeys,kvals,Nsearchkeys); | ||
// get metadata from image file | ||
for (ii = 0; ii < Nsearchkeys; ii++) { | ||
// loop search keys | ||
pass = searchmeta_test1(kvals[ii],matchtype[ii],searchvals[ii]); | ||
// test image data | ||
if (! pass) break; | ||
// fail, no more testing needed | ||
} | ||
for (ii = 0; ii < Nsearchkeys; ii++) | ||
// free memory | ||
if (kvals[ii]) zfree(kvals[ii]); | ||
return pass; | ||
// all values pass | ||
} | ||
// test a single metadata key/value against select criteria | ||
int searchmeta_test1(cchar *kval, cchar matchtype, cchar *searchvals) | ||
{ | ||
using namespace search_images; | ||
int nth, n1, n2, n3; | ||
char *pps; | ||
float fkval = 0, fsearchval; | ||
if (matchtype == 'r') return 1; | ||
// key value reported, not tested | ||
if (! kval) | ||
// no metadata present | ||
{ | ||
if (strmatch(searchvals,"null")) return 1; | ||
// search for empty data, pass | ||
return 0; | ||
// fail | ||
} | ||
if (strchr("= > <",matchtype)) { | Gindex[ff].mdata2 = zstrdup(text1,"search"); | |
// real value, look for N/N format 23.0 | // user metadata >> gallery index | |
fkval = atofz(kval); | ||
n1 = sscanf(kval,"%d/%d",&n2,&n3); | ||
if (n1 == 2) fkval = 1.0 * n2 / n3; | ||
} | ||
if (*searchvals <= ' ') return 1; | ||
// no search values, pass | ||
for (nth = 1; ; nth++) | ||
// loop all search values | ||
{ | ||
pps = substringR(searchvals,',',nth); | ||
// comma delimiter | ||
if (! pps) return 0; | ||
// no more, no match found | ||
if (matchtype == 'm') { | ||
// key matches any [wild] value | ||
if (MatchWildCase(pps,kval) == 0) goto ret1; | ||
// match not case sensitive | ||
} | ||
else if (matchtype == 'c') { | ||
// key contains any value | ||
if (strcasestr(kval,pps)) goto ret1; | ||
// match not case sensitive | ||
} | ||
else if (matchtype == '=') { | ||
// numeric key equals any value | ||
fsearchval = atofz(pps); | ||
if (fkval == fsearchval) goto ret1; | ||
// found match | ||
} | ||
else if (matchtype == '>') { | ||
// numeric key >= one value | ||
fsearchval = atofz(pps); | ||
if (fkval >= fsearchval) goto ret1; | ||
// found match | ||
} | ||
else if (matchtype == '<') { | ||
// numeric key <= one value | ||
fsearchval = atofz(pps); | ||
if (fkval <= fsearchval) goto ret1; | ||
// found match | ||
} | ||
else { | ||
Plog(0,"searchmeta invalid matchtype %c \n",matchtype); | ||
zfree(pps); | ||
return 0; | ||
} | } | |
} | } | |
ret1: | Gmdrows = 6 + Nkeys; | |
zfree(pps); | // report rows | |
return 1; | ||
} | ||
// Report selected metadata using a gallery window layout | ||
// with image thumbnails and selected metadata text. | ||
namespace searchimages_metadata_report_names | ||
{ | ||
int Nkeys; | ||
cchar *kname[20]; | ||
int MRthread1, MRthread2; | ||
} | ||
int searchimages_metadata_report() | ||
{ | ||
using namespace search_images; | ||
using namespace searchimages_metadata_report_names; | ||
using namespace navi; | ||
void * searchimages_metadata_report_thread(void *arg); | ||
int ii; | ||
if (! Gfiles) { | ||
// curr. gallery files | ||
Plog(1,"metadata report, 0 files \n"); | ||
return 0; | ||
} | ||
for (ii = 0; ii < Nsearchkeys; ii++) | ||
// key names from user | ||
kname[ii] = searchkeys[ii]; | ||
Nkeys = Nsearchkeys; | ||
// total keys to extract | ||
progress_reset(Gfiles); | ||
// initz. progress counter | ||
MRthread1 = MRthread2 = 0; | ||
// started/completed thread counters | ||
Fescape = 1; | ||
// killable via escape key | ||
for (ii = 0; ii < Gfiles; ii++) | ||
// scan all images in gallery | ||
{ | ||
if (Fescape > 1) break; | ||
// killed by user | ||
while (MRthread1 - MRthread2 == NST) zsleep(0.001); | ||
// wait for free thread | ||
MRthread1++; | ||
// incr. busy thread counter | ||
start_detached_thread(searchimages_metadata_report_thread,&Nval[ii]); | ||
// start thread for image ii | ||
zmainloop(10); | ||
// avoid GTK 'not responding' | ||
progress_add(0,1); | ||
// incr. progress | ||
} | ||
Fescape = 0; | ||
while (MRthread1 != MRthread2) zsleep(0.01); | ||
// wait for last thread | ||
exif_server(0,0,0); | ||
// kill server herd | ||
progress_reset(0); | ||
// stop progress monitor | ||
gallerytype = META; | ||
// gallery type = search results/metadata | ||
return 0; | ||
} | ||
// metadata report thread function | ||
void * searchimages_metadata_report_thread(void *arg) | ||
{ | ||
using namespace search_images; | ||
using namespace searchimages_metadata_report_names; | ||
using namespace navi; | ||
int ii, jj, cc; | ||
char *file; | ||
xxrec_t *xxrec; | ||
char pdate[12], ptime[12]; | ||
char wwhh[16], fsize[16]; | ||
char text1[2000], text2[200]; | ||
// note text1 limit | ||
char *kvals[20]; | ||
ii = *((int *) arg); | ||
// gallery index and report index | ||
file = gallery(0,"get",ii); | ||
if (! file) goto threadexit; | ||
xxrec = get_xxrec(file); | ||
// get metadata which is always reported | ||
if (! xxrec) goto threadexit; | ||
metadate_pdate(xxrec->pdate,pdate,ptime); | ||
snprintf(text2,200,"photo date: %s \n",pdate); | ||
strcpy(text1,text2); | ||
cc = strlen(text1); | ||
snprintf(wwhh,16,"%dx%d",xxrec->ww,xxrec->hh); | ||
snprintf(fsize,16,"%.2fmb",xxrec->fsize/MEGA); | ||
snprintf(text2,200,"Rating: %s Size: %s %s \n", | ||
xxrec->rating, wwhh, fsize); | ||
strcpy(text1+cc,text2); | ||
cc += strlen(text2); | ||
snprintf(text2,200,"keywords: %s\n",xxrec->tags); | ||
strcpy(text1+cc,text2); | ||
cc += strlen(text2); | ||
snprintf(text2,200,"Location: %s\n",xxrec->location); | ||
strcpy(text1+cc,text2); | ||
cc += strlen(text2); | ||
snprintf(text2,200,"title: %s\n",xxrec->title); | ||
strcpy(text1+cc,text2); | ||
cc += strlen(text2); | ||
snprintf(text2,200,"description: %s\n",xxrec->desc); | ||
strcpy(text1+cc,text2); | ||
cc += strlen(text2); | ||
if (Gindex[ii].mdata1) zfree(Gindex[ii].mdata1); | ||
// standard metadata >> gallery index | ||
Gindex[ii].mdata1 = zstrdup(text1,"search"); | ||
if (Gindex[ii].mdata2) zfree(Gindex[ii].mdata2); | ||
// clear user selected metadata 22.20 | ||
Gindex[ii].mdata2 = 0; | ||
if (Nkeys) | if (Nkeys) | |
{ | { | |
exif_get(file,kname,kvals,Nkeys); | zfree(repfiles); | |
// get metadata for user search keys | ||
for (cc = jj = 0; jj < Nkeys; jj++) | ||
{ | ||
snprintf(text2,200,"%s: %s \n",kname[jj], kvals[jj]); | ||
strcpy(text1+cc,text2); | ||
cc += strlen(text2); | ||
if (cc > 1800) break; | ||
// see text1 size above | ||
} | ||
for (jj = 0; jj < Nkeys; jj++) | ||
// free memory | ||
if (kvals[jj]) zfree(kvals[jj]); | ||
Gindex[ii].mdata2 = zstrdup(text1,"search"); | for (ii = 0; ii < Gfiles * Nkeys; ii++) | |
// user metadata >> gallery index 22.20 | // free keyvals memory | |
if (keyvals[ii]) zfree(keyvals[ii]); | ||
zfree(keyvals); | ||
} | } | |
Gmdrows = 6 + Nkeys; | gallerytype = META; | |
// report rows | // gallery type = search results/metadata | |
zfree(file); | ||
threadexit: | ||
zadd_locked(MRthread2,+1); | ||
return 0; | return 0; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// Convert date format from "yyyy-mm-dd" to "yyyymmdd". | // Convert date format from "yyyy-mm-dd" to "yyyymmdd". | |
// Missing month or day ("yyyy" or "yyyy-mm") is replaced with "01". | // Missing month or day ("yyyy" or "yyyy-mm") is replaced with "01". | |
// Output user message and return null if input is not valid. | // Output user message and return null if input is not valid. | |
char * pdate_metadate(cchar *pdate) // "yyyy-mm-dd" >> "yyyymmdd" | ch * pdate_metadate(ch *pdate) // "yyyy-mm-dd" >> "yyyymmdd" | |
{ | { | |
int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
int cc, year, mon, day; | int cc, year, mon, day; | |
char pdate2[12]; | ch pdate2[12]; | |
static char mdate[12]; | static ch mdate[12]; | |
cc = strlen(pdate); | cc = strlen(pdate); | |
if (cc > 10) goto badformat; | if (cc > 10) goto badformat; | |
strcpy(pdate2,pdate); | strcpy(pdate2,pdate); | |
if (cc == 4) // conv. "yyyy" to "yyyy-01-01" | if (cc == 4) // conv. "yyyy" to "yyyy-01-01" | |
strcat(pdate2,"-01-01"); | strcat(pdate2,"-01-01"); | |
else if (cc == 7) // conv. "yyyy-mm" to "yyyy-mm-01" | else if (cc == 7) // conv. "yyyy-mm" to "yyyy-mm-01" | |
strcat(pdate2,"-01"); | strcat(pdate2,"-01"); | |
skipping to change at line 6798 | skipping to change at line 6929 | |
baddate: | baddate: | |
zmessageACK(Mwin,"date is invalid"); | zmessageACK(Mwin,"date is invalid"); | |
return 0; | return 0; | |
} | } | |
// Convert time format from "hh:mm:ss" to "hhmmss". | // Convert time format from "hh:mm:ss" to "hhmmss". | |
// Missing seconds ("hh:mm") are replaced with zero ("hhmm00"). | // Missing seconds ("hh:mm") are replaced with zero ("hhmm00"). | |
// Output user message and return null if input not valid. | // Output user message and return null if input not valid. | |
char * ptime_metatime(cchar *ptime) // "hh:mm[:ss]" >> "hhmmss" | ch * ptime_metatime(ch *ptime) // "hh:mm[:ss]" >> "hhmmss" | |
{ | { | |
int cc, hour, min, sec; | int cc, hour, min, sec; | |
char ptime2[12]; | ch ptime2[12]; | |
static char mtime[8]; | static ch mtime[8]; | |
cc = strlen(ptime); | cc = strlen(ptime); | |
if (cc > 8) goto badformat; | if (cc > 8) goto badformat; | |
strcpy(ptime2,ptime); | strcpy(ptime2,ptime); | |
if (cc == 5) strcat(ptime2,":00"); // conv. "hh:mm" to "hh:mm:00" | if (cc == 5) strcat(ptime2,":00"); // conv. "hh:mm" to "hh:mm:00" | |
if (strlen(ptime2) != 8) goto badformat; | if (strlen(ptime2) != 8) goto badformat; | |
if (ptime2[2] != ':' || ptime2[5] != ':') goto badformat; | if (ptime2[2] != ':' || ptime2[5] != ':') goto badformat; | |
skipping to change at line 6837 | skipping to change at line 6968 | |
zmessageACK(Mwin,"time format is HH:MM [:SS]"); | zmessageACK(Mwin,"time format is HH:MM [:SS]"); | |
return 0; | return 0; | |
badtime: | badtime: | |
zmessageACK(Mwin,"time is invalid"); | zmessageACK(Mwin,"time is invalid"); | |
return 0; | return 0; | |
} | } | |
// convert yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss | // convert yyyy-mm-dd hh:mm[:ss] to yyyymmddhhmmss | |
char * pdatetime_metadatetime(cchar *pdatetime) | ch * pdatetime_metadatetime(ch *pdatetime) | |
{ | { | |
char pdate[12], ptime[12]; | ch pdate[12], ptime[12]; | |
static char metadatetime[20]; | static ch metadatetime[20]; | |
char *pp; | ch *pp; | |
int cc; | int cc; | |
strncpy0(pdate,pdatetime,11); // yyyy-mm-dd | strncpy0(pdate,pdatetime,11); // yyyy-mm-dd | |
strncpy0(ptime,pdatetime+11,9); // hh:mm[:ss] | strncpy0(ptime,pdatetime+11,9); // hh:mm[:ss] | |
cc = strlen(ptime); | cc = strlen(ptime); | |
if (cc == 5) strcat(ptime,":00"); // hh:mm >> hh:mm:00 | if (cc == 5) strcat(ptime,":00"); // hh:mm >> hh:mm:00 | |
else if (cc != 8) return 0; | else if (cc != 8) return 0; | |
pp = pdate_metadate(pdate); | pp = pdate_metadate(pdate); | |
skipping to change at line 6864 | skipping to change at line 6995 | |
pp = ptime_metatime(ptime); | pp = ptime_metatime(ptime); | |
if (! pp) return 0; | if (! pp) return 0; | |
strncpy0(metadatetime+8,pp,7); | strncpy0(metadatetime+8,pp,7); | |
return metadatetime; | return metadatetime; | |
} | } | |
// Convert metadata date/time "yyyymmddhhmmss" to "yyyy-mm-dd" and "hh:mm:ss" | // Convert metadata date/time "yyyymmddhhmmss" to "yyyy-mm-dd" and "hh:mm:ss" | |
void metadate_pdate(cchar *metadate, char *pdate, char *ptime) | void metadate_pdate(ch *metadate, ch *pdate, ch *ptime) | |
{ | { | |
if (*metadate) | if (*metadate) | |
{ | { | |
if (strmatch(metadate,"null")) { | if (strmatch(metadate,"null")) { | |
strcpy(pdate,"null"); | strcpy(pdate,"null"); | |
*ptime = 0; | *ptime = 0; | |
} | } | |
else { | else { | |
memcpy(pdate,metadate,4); // yyyymmdd to yyyy-mm-dd | memcpy(pdate,metadate,4); // yyyymmdd to yyyy-mm-dd | |
memcpy(pdate+5,metadate+4,2); | memcpy(pdate+5,metadate+4,2); | |
skipping to change at line 6895 | skipping to change at line 7026 | |
} | } | |
} | } | |
else *pdate = *ptime = 0; // missing | else *pdate = *ptime = 0; // missing | |
return; | return; | |
} | } | |
// validate a date/time string formatted "yyyy-mm-dd hh:mm[:ss]" | // validate a date/time string formatted "yyyy-mm-dd hh:mm[:ss]" | |
// valid year is 0000 to 2099 | // valid year is 0000 to 2099 | |
// return 0 if bad, 1 if OK | // return 0 if bad, 1 if OK | |
int datetimeOK(char *datetime) // format changed | int datetimeOK(ch *datetime) // format changed | |
{ | { | |
int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | int monlim[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
int cc, year, mon, day, hour, min, sec; | int cc, year, mon, day, hour, min, sec; | |
cc = strlen(datetime); | cc = strlen(datetime); | |
if (cc != 16 && cc != 19) return 0; | if (cc != 16 && cc != 19) return 0; | |
if (datetime[4] != '-') return 0; | if (datetime[4] != '-') return 0; | |
if (datetime[7] != '-') return 0; | if (datetime[7] != '-') return 0; | |
if (datetime[13] != ':') return 0; | if (datetime[13] != ':') return 0; | |
skipping to change at line 6933 | skipping to change at line 7064 | |
return 1; | return 1; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// add input tag to output tag list if not already there and enough room | // add input tag to output tag list if not already there and enough room | |
// returns: 0 = added OK 1 = already there (case ignored) | // returns: 0 = added OK 1 = already there (case ignored) | |
// 2 = overflow 3 = bad tag name 4 = null tag | // 2 = overflow 3 = bad tag name 4 = null tag | |
int add_tag(char *tag, char *taglist, int maxcc) | int add_tag(ch *tag, ch *taglist, int maxcc) | |
{ | { | |
char *pp1, *pp2, tag1[tagcc]; | ch *pp1, *pp2, tag1[tagXcc]; | |
int cc, cc1, cc2; | int cc, cc1, cc2; | |
if (! tag) return 4; | if (! tag) return 4; | |
strncpy0(tag1,tag,tagcc); // remove leading and trailing blanks | strncpy0(tag1,tag,tagXcc); // remove leading and trailing blanks | |
cc = strTrim2(tag1); | cc = strTrim2(tag1); | |
if (! cc) return 4; | if (! cc) return 4; | |
if (utf8_check(tag1)) return 3; // look for bad characters | if (utf8_check(tag1)) return 3; // look for bad characters | |
if (strpbrk(tag1,",;:")) return 3; | if (strpbrk(tag1,",;:")) return 3; | |
strcpy(tag,tag1); | strcpy(tag,tag1); | |
pp1 = taglist; | pp1 = taglist; | |
cc1 = strlen(tag); | cc1 = strlen(tag); | |
while (true) // check if already in tag list | while (true) // check if already in tag list | |
skipping to change at line 6982 | skipping to change at line 7113 | |
void set_meta_wwhh(int ww, int hh) | void set_meta_wwhh(int ww, int hh) | |
{ | { | |
snprintf(meta_wwhh,16,"%dx%d",ww,hh); | snprintf(meta_wwhh,16,"%dx%d",ww,hh); | |
return; | return; | |
} | } | |
// remove tag from taglist, if present | // remove tag from taglist, if present | |
// returns: 0 if found and deleted, otherwise 1 | // returns: 0 if found and deleted, otherwise 1 | |
int del_tag(char *tag, char *taglist) | int del_tag(ch *tag, ch *taglist) | |
{ | { | |
int ii, ftcc, atcc, found; | int ii, ftcc, atcc, found; | |
char *temptags; | ch *temptags; | |
cchar *pp; | ch *pp; | |
temptags = zstrdup(taglist,"delete-tag"); | temptags = zstrdup(taglist,"delete-tag"); | |
*taglist = 0; | *taglist = 0; | |
ftcc = found = 0; | ftcc = found = 0; | |
for (ii = 1; ; ii++) | for (ii = 1; ; ii++) | |
{ | { | |
pp = substring(temptags,",;",ii); // next tag | pp = substring(temptags,",;",ii); // next tag | |
if (! pp) { | if (! pp) { | |
skipping to change at line 7019 | skipping to change at line 7150 | |
strcpy(taglist + ftcc, pp); | strcpy(taglist + ftcc, pp); | |
ftcc += atcc; | ftcc += atcc; | |
strcpy(taglist + ftcc, ", "); // + delim + blank | strcpy(taglist + ftcc, ", "); // + delim + blank | |
ftcc += 2; | ftcc += 2; | |
} | } | |
} | } | |
// add new tag to recent tags, if not already. | // add new tag to recent tags, if not already. | |
// remove oldest to make space if needed. | // remove oldest to make space if needed. | |
int add_recentag(char *tag) | int add_recentag(ch *tag) | |
{ | { | |
int err; | int err; | |
char *pp, temptags[tagRcc]; | ch *pp, temptags[recenttagsXcc]; | |
err = add_tag(tag,tags_recentags,tagRcc); // add tag to recent tags | err = add_tag(tag,tags_recentags,recenttagsXcc); // add tag to recent tags | |
while (err == 2) // overflow | while (err == 2) // overflow | |
{ | { | |
strncpy0(temptags,tags_recentags,tagRcc); // remove oldest to make room | strncpy0(temptags,tags_recentags,recenttagsXcc); // remove oldest to make room | |
pp = strpbrk(temptags,",;"); | pp = strpbrk(temptags,",;"); | |
if (! pp) return 0; | if (! pp) return 0; | |
strcpy(tags_recentags,pp+2); // delimiter + blank before tag | strcpy(tags_recentags,pp+2); // delimiter + blank before tag | |
err = add_tag(tag,tags_recentags,tagRcc); | err = add_tag(tag,tags_recentags,recenttagsXcc); | |
} | } | |
return 0; | return 0; | |
} | } | |
/******************************************************************************* */ | /******************************************************************************* */ | |
// Load tags_defined file into tags_deftags[ii] => category: tag1, tag2, ... | // Load tags_defined file into tags_deftags[ii] => category: tag1, tag2, ... | |
// Read image_index recs. and add unmatched tags: => nocatg: tag1, tag2, ... | // Read image_index recs. and add unmatched tags: => nocatg: tag1, tag2, ... | |
// force: read image index and build deftags list | // force: read image index and build deftags list | |
void load_deftags(int force) | void load_deftags(int force) | |
{ | { | |
int tags_Ucomp(cchar *tag1, cchar *tag2); | ||
static int Floaded = 0; | static int Floaded = 0; | |
FILE * fid; | FILE * fid; | |
xxrec_t *xxrec; | xxrec_t *xxrec; | |
int ii, jj, ntags, err, cc, tcc; | int ii, jj, ntags, err, cc, tcc; | |
int ncats, catoverflow; | int ncats, catoverflow; | |
int nocat, nocatcc; | int nocat, nocatcc; | |
char tag[tagcc], catg[tagcc]; | ch tag[tagXcc], catg[tagXcc]; | |
char tagsbuff[tagGcc]; | ch tagsbuff[catgXcc]; | |
char *pp1, *pp2; | ch *pp1, *pp2; | |
char ptags[maxtags][tagcc]; | ch ptags[maxtags][tagXcc]; | |
// 10000 * 50 = 0.5 MB | // 10000 * 50 = 0.5 MB | |
if (Floaded && ! force) return; // use memory tags if already there | if (Floaded && ! force) return; // use memory tags if already there | |
Floaded++; | Floaded++; | |
for (ii = 0; ii < maxtagcats; ii++) { // clean memory | for (ii = 0; ii < maxtagcats; ii++) { // clean memory | |
if (tags_deftags[ii]) zfree(tags_deftags[ii]); | if (tags_deftags[ii]) zfree(tags_deftags[ii]); | |
tags_deftags[ii] = 0; | tags_deftags[ii] = 0; | |
} | } | |
ncats = catoverflow = 0; | ncats = catoverflow = 0; | |
fid = fopen(tags_defined_file,"r"); // read tags_defined file | fid = fopen(tags_defined_file,"r"); // read tags_defined file | |
if (fid) { | if (fid) { | |
while (true) { | while (true) { | |
pp1 = fgets_trim(tagsbuff,tagGcc,fid); | pp1 = fgets_trim(tagsbuff,catgXcc,fid); | |
if (! pp1) break; | if (! pp1) break; | |
pp2 = strchr(pp1,':'); // isolate "category:" | pp2 = strchr(pp1,':'); // isolate "category:" | |
if (! pp2) continue; // no colon | if (! pp2) continue; // no colon | |
cc = pp2 - pp1 + 1; | cc = pp2 - pp1 + 1; | |
if (cc > tagcc-1) continue; // category name too long | if (cc > tagXcc-1) continue; // category name too long | |
strncpy0(catg,pp1,cc); // (for error message) | strncpy0(catg,pp1,cc); // (for error message) | |
if (strlen(pp1) > tagGcc-2) goto cattoobig; // all category tags too long | if (strlen(pp1) > catgXcc-2) goto cattoobig; // all category tags too long | |
pp2++; | pp2++; | |
while (*pp2 == ' ') pp2++; | while (*pp2 == ' ') pp2++; | |
/// if (strlen(pp2) < 3) continue; // no remove empty category 22.40 | ||
while (*pp2) { | while (*pp2) { | |
if (*pp2 == ';') *pp2 = ','; // replace ';' with ',' for Fotoxx | if (*pp2 == ';') *pp2 = ','; // replace ';' with ',' for Fotoxx | |
pp2++; | pp2++; | |
} | } | |
tags_deftags[ncats] = zstrdup(pp1,"load-deftags"); // tags_deftags[ii] | tags_deftags[ncats] = zstrdup(pp1,"load-deftags"); // tags_deftags[ii] | |
ncats++; // = category: tag1, tag2, ... tagN, | ncats++; // = category: tag1, tag2, ... tagN, | |
if (ncats == maxtagcats) goto toomanycats; | if (ncats == maxtagcats) goto toomanycats; | |
} | } | |
err = fclose(fid); | err = fclose(fid); | |
fid = 0; | fid = 0; | |
skipping to change at line 7129 | skipping to change at line 7257 | |
} | } | |
} | } | |
// if not already there, add category "nocatg" to the end of the list | // if not already there, add category "nocatg" to the end of the list | |
pp1 = 0; | pp1 = 0; | |
if (ncats > 0) pp1 = tags_deftags[ncats-1]; // last tag category | if (ncats > 0) pp1 = tags_deftags[ncats-1]; // last tag category | |
if (pp1 && strmatchN(pp1,"nocatg:",7)) { // already 'nocatg' | if (pp1 && strmatchN(pp1,"nocatg:",7)) { // already 'nocatg' | |
nocat = ncats - 1; | nocat = ncats - 1; | |
nocatcc = strlen(pp1); | nocatcc = strlen(pp1); | |
pp2 = (char *) zmalloc(tagGcc,"load-deftags"); // re-allocate max. size | pp2 = (ch *) zmalloc(catgXcc,"load-deftags"); // re-allocate max. size | |
tags_deftags[nocat] = pp2; // for following phase | tags_deftags[nocat] = pp2; // for following phase | |
strcpy(pp2,pp1); | strcpy(pp2,pp1); | |
zfree(pp1); | zfree(pp1); | |
} | } | |
else { | else { | |
nocat = ncats; // add to end of list | nocat = ncats; // add to end of list | |
ncats++; | ncats++; | |
tags_deftags[nocat] = (char *) zmalloc(tagGcc,"load-deftags"); // allocate max. size | tags_deftags[nocat] = (ch *) zmalloc(catgXcc,"load-deftags"); // allocate max. size | |
strcpy(tags_deftags[nocat],"nocatg: "); | strcpy(tags_deftags[nocat],"nocatg: "); | |
nocatcc = 8; | nocatcc = 8; | |
} | } | |
// search image index recs for all tags in all images | // search image index recs for all tags in all images | |
// for tags not found in defined tags list, add to 'nocatg' list | // for tags not found in defined tags list, add to 'nocatg' list | |
for (ii = 0; ii < Nxxrec; ii++) // loop all index recs | for (ii = 0; ii < Nxxrec; ii++) // loop all index recs | |
{ | { | |
zmainloop(100); // keep GTK alive | zmainloop(100); // keep GTK alive | |
skipping to change at line 7160 | skipping to change at line 7288 | |
pp1 = xxrec->tags; // should never be null | pp1 = xxrec->tags; // should never be null | |
while (pp1) // was: while (true) | while (pp1) // was: while (true) | |
{ | { | |
while (*pp1 && strchr(",; ",*pp1)) pp1++; // next image tag start | while (*pp1 && strchr(",; ",*pp1)) pp1++; // next image tag start | |
if (! *pp1) break; | if (! *pp1) break; | |
pp2 = strpbrk(pp1,",;"); // end | pp2 = strpbrk(pp1,",;"); // end | |
if (! pp2) pp2 = pp1 + strlen(pp1); | if (! pp2) pp2 = pp1 + strlen(pp1); | |
cc = pp2 - pp1; | cc = pp2 - pp1; | |
if (cc > tagcc-1) { | if (cc > tagXcc-1) { | |
pp1 = pp2; | pp1 = pp2; | |
continue; // ignore huge tag | continue; // ignore huge tag | |
} | } | |
strncpy0(tag,pp1,cc+1); // look for tag in defined tags | strncpy0(tag,pp1,cc+1); // look for tag in defined tags | |
if (find_deftag(tag)) { | if (find_deftag(tag)) { | |
pp1 = pp2; // found | pp1 = pp2; // found | |
continue; | continue; | |
} | } | |
if (nocatcc + cc + 2 > tagGcc-2) { | if (nocatcc + cc + 2 > catgXcc-2) { | |
catoverflow = 1; // nocatg: length limit reached | catoverflow = 1; // nocatg: length limit reached | |
break; | break; | |
} | } | |
else { | else { | |
strcpy(tags_deftags[nocat] + nocatcc, tag); // append tag to list | strcpy(tags_deftags[nocat] + nocatcc, tag); // append tag to list | |
nocatcc += cc; | nocatcc += cc; | |
strcpy(tags_deftags[nocat] + nocatcc, ", "); // + delim + blank | strcpy(tags_deftags[nocat] + nocatcc, ", "); // + delim + blank | |
nocatcc += 2; | nocatcc += 2; | |
} | } | |
skipping to change at line 7210 | skipping to change at line 7338 | |
pp1 = pp2 + 1; | pp1 = pp2 + 1; | |
while (*pp1 == ' ') pp1++; | while (*pp1 == ' ') pp1++; | |
tcc = 0; | tcc = 0; | |
for (jj = 0; jj < maxtags; jj++) | for (jj = 0; jj < maxtags; jj++) | |
{ | { | |
if (! *pp1) break; | if (! *pp1) break; | |
pp2 = strchr(pp1,','); | pp2 = strchr(pp1,','); | |
if (pp2) cc = pp2 - pp1; | if (pp2) cc = pp2 - pp1; | |
else cc = strlen(pp1); | else cc = strlen(pp1); | |
if (cc > tagcc-1) cc = tagcc-1; | if (cc > tagXcc-1) cc = tagXcc-1; | |
strncpy0(ptags[jj],pp1,cc+1); | strncpy0(ptags[jj],pp1,cc+1); | |
pp1 += cc + 1; | pp1 += cc + 1; | |
tcc += cc; | tcc += cc; | |
while (*pp1 == ' ') pp1++; | while (*pp1 == ' ') pp1++; | |
} | } | |
ntags = jj; | ntags = jj; | |
if (ntags == maxtags) goto toomanytags; | if (ntags == maxtags) goto toomanytags; | |
HeapSort((char *) ptags,tagcc,ntags,tags_Ucomp); | HeapSort((ch *) ptags,tagXcc,ntags,zstrcasecmp); | |
pp1 = tags_deftags[ii]; | pp1 = tags_deftags[ii]; | |
tcc += strlen(catg) + 2 + 2 * ntags + 2; // category, all tags, delimiters | tcc += strlen(catg) + 2 + 2 * ntags + 2; // category, all tags, delimiters | |
pp2 = (char *) zmalloc(tcc,"load-deftags"); | pp2 = (ch *) zmalloc(tcc,"load-deftags"); | |
tags_deftags[ii] = pp2; // swap memory | tags_deftags[ii] = pp2; // swap memory | |
zfree(pp1); | zfree(pp1); | |
strcpy(pp2,catg); | strcpy(pp2,catg); | |
pp2 += strlen(catg); | pp2 += strlen(catg); | |
strcpy(pp2,": "); // pp2 = "category: " | strcpy(pp2,": "); // pp2 = "category: " | |
pp2 += 2; | pp2 += 2; | |
for (jj = 0; jj < ntags; jj++) // add the sorted tags | for (jj = 0; jj < ntags; jj++) // add the sorted tags | |
skipping to change at line 7266 | skipping to change at line 7394 | |
toomanytags: | toomanytags: | |
zmessageACK(Mwin,"category %s has too many tags",catg); | zmessageACK(Mwin,"category %s has too many tags",catg); | |
if (fid) fclose(fid); | if (fid) fclose(fid); | |
return; | return; | |
deftagsfilerr: | deftagsfilerr: | |
zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); | zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); | |
return; | return; | |
} | } | |
// compare function for tag sorting | ||
int tags_Ucomp(cchar *tag1, cchar *tag2) | ||
{ | ||
return strcasecmp(tag1,tag2); | ||
} | ||
// write tags_deftags[] memory data to the defined tags file if any changes wer e made | // write tags_deftags[] memory data to the defined tags file if any changes wer e made | |
void save_deftags() | void save_deftags() | |
{ | { | |
int ii, err; | int ii, err; | |
FILE *fid; | FILE *fid; | |
fid = fopen(tags_defined_file,"w"); // write tags_defined file | fid = fopen(tags_defined_file,"w"); // write tags_defined file | |
if (! fid) goto deftagserr; | if (! fid) goto deftagserr; | |
skipping to change at line 7302 | skipping to change at line 7423 | |
return; | return; | |
deftagserr: | deftagserr: | |
zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); | zmessageACK(Mwin,"tags_defined file error: %s",strerror(errno)); | |
return; | return; | |
} | } | |
// find a given tag in tags_deftags[] | // find a given tag in tags_deftags[] | |
// return: 1 = found, 0 = not found | // return: 1 = found, 0 = not found | |
int find_deftag(char *tag) | int find_deftag(ch *tag) | |
{ | { | |
int ii, cc; | int ii, cc; | |
char tag2[tagcc+4]; | ch tag2[tagXcc+4]; | |
char *pp; | ch *pp; | |
strncpy0(tag2,tag,tagcc); // construct tag + delim + blank | strncpy0(tag2,tag,tagXcc); // construct tag + delim + blank | |
cc = strlen(tag2); | cc = strlen(tag2); | |
strcpy(tag2+cc,", "); | strcpy(tag2+cc,", "); | |
cc += 2; | cc += 2; | |
for (ii = 0; ii < maxtagcats; ii++) | for (ii = 0; ii < maxtagcats; ii++) | |
{ | { | |
pp = tags_deftags[ii]; // category: tag1, tag2, ... tagN, | pp = tags_deftags[ii]; // category: tag1, tag2, ... tagN, | |
if (! pp) return 0; // not found | if (! pp) return 0; // not found | |
while (pp) | while (pp) | |
skipping to change at line 7335 | skipping to change at line 7456 | |
} | } | |
return 1; | return 1; | |
} | } | |
// add new tag to tags_deftags[] >> category: tag1, tag2, ... newtag, | // add new tag to tags_deftags[] >> category: tag1, tag2, ... newtag, | |
// returns: 0 = added OK 1 = not unique (case ignored) | // returns: 0 = added OK 1 = not unique (case ignored) | |
// 2 = overflow 3 = bad name 4 = null/blank tag | // 2 = overflow 3 = bad name 4 = null/blank tag | |
// if tag present under another category, it is moved to new category | // if tag present under another category, it is moved to new category | |
int add_deftag(char *catg, char *tag) | int add_deftag(ch *catg, ch *tag) | |
{ | { | |
int ii, cc, cc1, cc2; | int ii, cc, cc1, cc2; | |
char catg1[tagcc], tag1[tagcc]; | ch catg1[tagXcc], tag1[tagXcc]; | |
char *pp1, *pp2; | ch *pp1, *pp2; | |
if (! catg) strcpy(catg1,"nocatg"); | if (! catg) strcpy(catg1,"nocatg"); | |
else strncpy0(catg1,catg,tagcc); | else strncpy0(catg1,catg,tagXcc); | |
cc = strTrim2(catg1); // remove leading and trailing blanks | cc = strTrim2(catg1); // remove leading and trailing blanks | |
if (! cc) strcpy(catg1,"nocatg"); | if (! cc) strcpy(catg1,"nocatg"); | |
if (utf8_check(catg1)) goto badcatname; // look for bad characters | if (utf8_check(catg1)) goto badcatname; // look for bad characters | |
if (strpbrk(catg1,",;:\"")) goto badcatname; | if (strpbrk(catg1,",;:\"")) goto badcatname; | |
if (! tag) return 4; | if (! tag) return 4; | |
strncpy0(tag1,tag,tagcc); // remove leading and trailing blanks | strncpy0(tag1,tag,tagXcc); // remove leading and trailing blanks | |
cc = strTrim2(tag1); | cc = strTrim2(tag1); | |
if (! cc) return 4; | if (! cc) return 4; | |
if (utf8_check(tag1)) goto badtagname; // look for bad characters | if (utf8_check(tag1)) goto badtagname; // look for bad characters | |
if (strpbrk(tag1,",;:\"")) goto badtagname; | if (strpbrk(tag1,",;:\"")) goto badtagname; | |
del_deftag(tag1); // delete tag if already there | del_deftag(tag1); // delete tag if already there | |
cc1 = strlen(catg1); | cc1 = strlen(catg1); | |
for (ii = 0; ii < maxtagcats; ii++) // look for given category | for (ii = 0; ii < maxtagcats; ii++) // look for given category | |
{ | { | |
pp1 = tags_deftags[ii]; | pp1 = tags_deftags[ii]; | |
if (! pp1) goto newcatg; | if (! pp1) goto newcatg; | |
if (! strmatchN(catg1,pp1,cc1)) continue; // match on "catname:" | if (! strmatchN(catg1,pp1,cc1)) continue; // match on "catname:" | |
if (pp1[cc1] == ':') goto oldcatg; | if (pp1[cc1] == ':') goto oldcatg; | |
} | } | |
newcatg: | newcatg: | |
if (ii == maxtagcats) goto toomanycats; | if (ii == maxtagcats) goto toomanycats; | |
cc1 = strlen(catg1) + strlen(tag1) + 6; | cc1 = strlen(catg1) + strlen(tag1) + 6; | |
pp1 = (char *) zmalloc(cc1,"add-deftag"); | pp1 = (ch *) zmalloc(cc1,"add-deftag"); | |
*pp1 = 0; | *pp1 = 0; | |
strncatv(pp1,cc1,catg1,": ",tag1,", ",null); // category: + tag + delim + blank | strncatv(pp1,cc1,catg1,": ",tag1,", ",null); // category: + tag + delim + blank | |
tags_deftags[ii] = tags_deftags[ii-1]; // move "nocatg" record to next slot | tags_deftags[ii] = tags_deftags[ii-1]; // move "nocatg" record to next slot | |
tags_deftags[ii-1] = pp1; // insert new record before | tags_deftags[ii-1] = pp1; // insert new record before | |
save_deftags(); | save_deftags(); | |
return 0; | return 0; | |
oldcatg: // logic simplified | oldcatg: // logic simplified | |
pp2 = pp1 + cc1 + 2; // char following "catname: " | pp2 = pp1 + cc1 + 2; // ch following "catname: " | |
cc1 = strlen(tag1); | cc1 = strlen(tag1); | |
cc2 = strlen(pp1); // add new tag to old record | cc2 = strlen(pp1); // add new tag to old record | |
if (cc1 + cc2 + 4 > tagGcc) goto cattoobig; | if (cc1 + cc2 + 4 > catgXcc) goto cattoobig; | |
pp2 = zstrdup(pp1,"add-deftag",cc1+cc2+4); // expand string | pp2 = zstrdup(pp1,"add-deftag",cc1+cc2+4); // expand string | |
zfree(pp1); | zfree(pp1); | |
tags_deftags[ii] = pp2; | tags_deftags[ii] = pp2; | |
strcpy(pp2+cc2,tag1); // old record + tag + delim + blank | strcpy(pp2+cc2,tag1); // old record + tag + delim + blank | |
strcpy(pp2+cc2+cc1,", "); | strcpy(pp2+cc2+cc1,", "); | |
save_deftags(); | save_deftags(); | |
return 0; | return 0; | |
badcatname: | badcatname: | |
zmessageACK(Mwin,"bad category name"); | zmessageACK(Mwin,"bad category name"); | |
skipping to change at line 7411 | skipping to change at line 7532 | |
return 2; | return 2; | |
cattoobig: | cattoobig: | |
zmessageACK(Mwin,"too many tags in a category"); | zmessageACK(Mwin,"too many tags in a category"); | |
return 2; | return 2; | |
} | } | |
// delete tag from defined tags list, tags_deftags[] | // delete tag from defined tags list, tags_deftags[] | |
// return: 0 = found and deleted, 1 = not found | // return: 0 = found and deleted, 1 = not found | |
int del_deftag(char *tag) | int del_deftag(ch *tag) | |
{ | { | |
int ii, cc; | int ii, cc; | |
char tag2[tagcc+4]; | ch tag2[tagXcc+4]; | |
char *pp, *pp1, *pp2; | ch *pp, *pp1, *pp2; | |
if (! tag || ! *tag || *tag == ' ') return 1; // bad tag (utf8 can be < ' ') | if (! tag || ! *tag || *tag == ' ') return 1; // bad tag (utf8 can be < ' ') | |
strncpy0(tag2,tag,tagcc); // construct tag + delim + blank | strncpy0(tag2,tag,tagXcc); // construct tag + delim + blank | |
cc = strlen(tag2); | cc = strlen(tag2); | |
strcpy(tag2+cc,", "); | strcpy(tag2+cc,", "); | |
cc += 2; | cc += 2; | |
for (ii = 0; ii < maxtagcats; ii++) | for (ii = 0; ii < maxtagcats; ii++) | |
{ | { | |
pp = tags_deftags[ii]; | pp = tags_deftags[ii]; | |
if (! pp) return 1; // not found | if (! pp) return 1; // not found | |
while (pp) | while (pp) | |
skipping to change at line 7450 | skipping to change at line 7571 | |
*pp1 = *pp2; | *pp1 = *pp2; | |
*pp1 = 0; | *pp1 = 0; | |
return 0; | return 0; | |
} | } | |
// delete category from defined tags list, tags_deftags[] | // delete category from defined tags list, tags_deftags[] | |
// return: 0 = found and deleted, 1 = not found | // return: 0 = found and deleted, 1 = not found | |
// 2 = not deleted because category has tags assigned | // 2 = not deleted because category has tags assigned | |
int del_defcatg(char *catg) // 22.40 | int del_defcatg(ch *catg) // 22.40 | |
{ | { | |
int ii, jj, cc; | int ii, jj, cc; | |
char catg2[tagcc+2]; | ch catg2[tagXcc+2]; | |
char *pp; | ch *pp; | |
if (! catg || ! *catg || *catg == ' ') return 1; // bad catg (utf8 can be < ' ') | if (! catg || ! *catg || *catg == ' ') return 1; // bad catg (utf8 can be < ' ') | |
strncpy0(catg2,catg,tagcc); // construct "catgname:" | strncpy0(catg2,catg,tagXcc); // construct "catgname:" | |
cc = strlen(catg2); | cc = strlen(catg2); | |
strcpy(catg2+cc,":"); | strcpy(catg2+cc,":"); | |
cc += 1; | cc += 1; | |
for (ii = 0; ii < maxtagcats; ii++) | for (ii = 0; ii < maxtagcats; ii++) | |
{ | { | |
pp = tags_deftags[ii]; | pp = tags_deftags[ii]; | |
if (! pp) return 1; // catg not found | if (! pp) return 1; // catg not found | |
if (strmatchN(pp,catg2,cc)) break; | if (strmatchN(pp,catg2,cc)) break; | |
} | } | |
skipping to change at line 7484 | skipping to change at line 7605 | |
for (jj = ii; jj < maxtagcats-1; jj++) // close hole in table | for (jj = ii; jj < maxtagcats-1; jj++) // close hole in table | |
tags_deftags[jj] = tags_deftags[jj+1]; | tags_deftags[jj] = tags_deftags[jj+1]; | |
return 0; // found and deleted | return 0; // found and deleted | |
} | } | |
// Stuff text widget "deftags" with all tags in the given category. | // Stuff text widget "deftags" with all tags in the given category. | |
// If category "ALL", stuff all tags and format by category. | // If category "ALL", stuff all tags and format by category. | |
void deftags_stuff(zdialog *zd, cchar *acatg) | void deftags_stuff(zdialog *zd, ch *acatg) | |
{ | { | |
GtkWidget *widget; | GtkWidget *widget; | |
int ii, ff, cc; | int ii, ff, cc; | |
char catgname[tagcc+4]; | ch catgname[tagXcc+4]; | |
char *pp1, *pp2; | ch *pp1, *pp2; | |
widget = zdialog_gtkwidget(zd,"deftags"); | widget = zdialog_gtkwidget(zd,"deftags"); | |
textwidget_clear(widget); | textwidget_clear(widget); | |
for (ii = 0; ii < maxtagcats; ii++) | for (ii = 0; ii < maxtagcats; ii++) | |
{ | { | |
pp1 = tags_deftags[ii]; | pp1 = tags_deftags[ii]; | |
if (! pp1) break; | if (! pp1) break; | |
pp2 = strchr(pp1,':'); | pp2 = strchr(pp1,':'); | |
if (! pp2) continue; | if (! pp2) continue; | |
cc = pp2 - pp1; | cc = pp2 - pp1; | |
if (cc < 1) continue; | if (cc < 1) continue; | |
if (cc > tagcc) continue; | if (cc > tagXcc) continue; | |
strncpy0(catgname,pp1,cc+1); | strncpy0(catgname,pp1,cc+1); | |
if (! strmatch(acatg,"ALL")) { | if (! strmatch(acatg,"ALL")) { | |
ff = strmatch(catgname,acatg); | ff = strmatch(catgname,acatg); | |
if (! ff) continue; | if (! ff) continue; | |
} | } | |
strcat(catgname,": "); | strcat(catgname,": "); | |
textwidget_append(widget,1,catgname); // "category: " in bold text | textwidget_append(widget,1,catgname); // "category: " in bold text | |
skipping to change at line 7526 | skipping to change at line 7647 | |
textwidget_append(widget,0,"\n"); | textwidget_append(widget,0,"\n"); | |
} | } | |
return; | return; | |
} | } | |
// Stuff combo box "defcats" with "ALL" + all defined categories | // Stuff combo box "defcats" with "ALL" + all defined categories | |
void defcats_stuff(zdialog *zd) | void defcats_stuff(zdialog *zd) | |
{ | { | |
char catgname[tagcc+2]; | ch catgname[tagXcc+2]; | |
int ii, cc; | int ii, cc; | |
char *pp1, *pp2; | ch *pp1, *pp2; | |
zdialog_combo_clear(zd,"defcats"); | zdialog_combo_clear(zd,"defcats"); | |
zdialog_stuff(zd,"defcats","ALL"); | zdialog_stuff(zd,"defcats","ALL"); | |
for (ii = 0; ii < maxtagcats; ii++) | for (ii = 0; ii < maxtagcats; ii++) | |
{ | { | |
pp1 = tags_deftags[ii]; | pp1 = tags_deftags[ii]; | |
if (! pp1) break; | if (! pp1) break; | |
pp2 = strchr(pp1,':'); | pp2 = strchr(pp1,':'); | |
if (! pp2) continue; | if (! pp2) continue; | |
cc = pp2 - pp1; | cc = pp2 - pp1; | |
if (cc < 1) continue; | if (cc < 1) continue; | |
if (cc > tagcc) continue; | if (cc > tagXcc) continue; | |
strncpy0(catgname,pp1,cc+1); | strncpy0(catgname,pp1,cc+1); | |
zdialog_stuff(zd,"defcats",catgname); | zdialog_stuff(zd,"defcats",catgname); | |
} | } | |
zdialog_stuff(zd,"defcats","ALL"); // default selection | zdialog_stuff(zd,"defcats","ALL"); // default selection | |
return; | return; | |
} | } | |
// report tags defined and not used in any image file | ||
void tag_orphans(GtkWidget *parent) | ||
{ | ||
FILE *fid; | ||
xxrec_t *xxrec; | ||
int ii, jj, cc; | ||
int Ndeftags; | ||
char **deftags; | ||
char usedtag[tagcc], tagsbuff[tagGcc]; | ||
char *pp1, *pp2; | ||
zdialog *zd2; | ||
if (! Findexvalid) { | ||
// should not happen | ||
Plog(0,"*** tag_orphan() no index \n"); | ||
return; | ||
} | ||
deftags = (char **) zmalloc(maxtags * sizeof(char *),"tag-orphans"); | ||
// allocate memory | ||
Ndeftags = 0; | ||
fid = fopen(tags_defined_file,"r"); | ||
// read tags_defined file | ||
if (fid) { | ||
while (true) { | ||
pp1 = fgets_trim(tagsbuff,tagGcc,fid); | ||
if (! pp1) break; | ||
pp1 = strchr(pp1,':'); | ||
// skip over "category:" | ||
if (! pp1) continue; | ||
cc = pp1 - tagsbuff; | ||
if (cc > tagcc) continue; | ||
// reject bad data (manual edit?) | ||
pp1++; | ||
for (ii = 1; ; ii++) { | ||
// get tags: tag1, tag2, ... | ||
pp2 = (char *) substring(pp1,",;",ii); | ||
if (! pp2) break; | ||
if (strlen(pp2) < 3) continue; | ||
// reject bad data | ||
if (strlen(pp2) > tagcc) continue; | ||
deftags[Ndeftags] = zstrdup(pp2,"tag-orphans"); | ||
Ndeftags++; | ||
} | ||
} | ||
fclose(fid); | ||
} | ||
for (ii = 0; ii < Nxxrec; ii++) | ||
// loop all index recs | ||
{ | ||
zmainloop(100); | ||
// keep GTK alive | ||
xxrec = xxrec_tab[ii]; | ||
pp1 = xxrec->tags; | ||
// image tags | ||
if (! pp1) continue; | ||
// should not happen | ||
while (true) | ||
{ | ||
while (*pp1 && strchr(",; ",*pp1)) pp1++; | ||
// next image tag start | ||
if (! *pp1) break; | ||
pp2 = strpbrk(pp1,",;"); | ||
// end | ||
if (! pp2) pp2 = pp1 + strlen(pp1); | ||
cc = pp2 - pp1; | ||
if (cc > tagcc-1) { | ||
pp1 = pp2; | ||
continue; | ||
// ignore huge tag | ||
} | ||
strncpy0(usedtag,pp1,cc+1); | ||
// used tag, without delimiter | ||
for (jj = 0; jj < Ndeftags; jj++) | ||
// find in defined tags | ||
if (strmatch(usedtag,deftags[jj])) break; | ||
if (jj < Ndeftags) { | ||
// found | ||
zfree(deftags[jj]); | ||
Ndeftags--; | ||
while (jj < Ndeftags) { | ||
// defined tag is in use | ||
deftags[jj] = deftags[jj+1]; | ||
// remove from list and pack down | ||
jj++; | ||
} | ||
} | ||
pp1 = pp2; | ||
} | ||
} | ||
zd2 = popup_report_open("unused tags",parent,200,200,0,0,"OK",0); | ||
// 22.15 | ||
for (ii = 0; ii < Ndeftags; ii++) | ||
popup_report_write2(zd2,0,"%s \n",deftags[ii]); | ||
popup_report_write2(zd2,0,"%d unused tags \n",Ndeftags); | ||
for (ii = 0; ii < Ndeftags; ii++) | ||
zfree(deftags[ii]); | ||
zfree(deftags); | ||
return; | ||
} | ||
/******************************************************************************* */ | /******************************************************************************* */ | |
// image file EXIF/IPTC data >> memory data: | // image file metadata >> memory metadata: | |
// meta_pdate, meta_rating, meta_tags, meta_title, meta_description, | // meta_pdate, meta_rating, meta_tags, meta_title, meta_description, | |
// meta_location, meta_country, meta_lati, meta_longi | // meta_location, meta_country, meta_lati, meta_longi | |
void load_filemeta(cchar *file) | void load_filemeta(ch *file) | |
{ | { | |
int ii, jj, cc, nkey; | int ii, jj, cc, nkey; | |
int ww, hh; | int ww, hh; | |
char *pp; | ch *pp; | |
cchar *exifkeys[100] = { exif_date_key, iptc_keywords_key, | ch *mkeys[100] = { meta_date_key, meta_tags_key, | |
iptc_rating_key, exif_ww_key, exif_hh_key, | meta_rating_key, meta_ww_key, meta_hh_key, | |
// replace exif_wwhh_key | // replace meta_wwhh_key | |
iptc_title_key, iptc_description_key, | meta_title_key, meta_description_key, | |
exif_location_key, exif_country_key, | meta_location_key, meta_country_key, | |
exif_lati_key, exif_longi_key }; | meta_lati_key, meta_longi_key }; | |
char *ppv[100], *imagedate, *imagetags, *imagerating; | ch *ppv[100], *imagedate, *imagetags, *imagerating; | |
char *imageww, *imagehh, *imagetitle, *imagedesc; | ch *imageww, *imagehh, *imagetitle, *imagedesc; | |
char *imageloc, *imagecountry, *imagelati, *imagelongi; | ch *imageloc, *imagecountry, *imagelati, *imagelongi; | |
strncpy0(p_meta_pdate,meta_pdate,15); // save prior metadata for use by | strncpy0(p_meta_pdate,meta_pdate,15); // save prior metadata for use by | |
strncpy0(p_meta_rating,meta_rating,4); // edit_metadata [Prev] button | strncpy0(p_meta_rating,meta_rating,4); // edit_metadata [Prev] button | |
strncpy0(p_meta_tags,meta_tags,tagFcc); | strncpy0(p_meta_tags,meta_tags,filetagsXcc); | |
strncpy0(p_meta_title,meta_title,exif_maxcc); | strncpy0(p_meta_title,meta_title,metadataXcc); | |
strncpy0(p_meta_description,meta_description,exif_maxcc); | strncpy0(p_meta_description,meta_description,metadataXcc); | |
strncpy0(p_meta_location,meta_location,100); | strncpy0(p_meta_location,meta_location,100); | |
strncpy0(p_meta_country,meta_country,100); | strncpy0(p_meta_country,meta_country,100); | |
strncpy0(p_meta_lati,meta_lati,20); | strncpy0(p_meta_lati,meta_lati,20); | |
strncpy0(p_meta_longi,meta_longi,20); | strncpy0(p_meta_longi,meta_longi,20); | |
*meta_tags = *meta_pdate = *meta_description = *meta_title = 0; | *meta_tags = *meta_pdate = *meta_description = *meta_title = 0; | |
strcpy(meta_rating,"0"); | strcpy(meta_rating,"0"); | |
*meta_location = *meta_country = *meta_lati = *meta_longi = 0; | *meta_location = *meta_country = *meta_lati = *meta_longi = 0; | |
nkey = 11; // add keys for indexed metadata | nkey = 11; // add keys for indexed metadata | |
for (ii = 0; ii < MXmax; ii++) { // from exifkeys[11]; | for (ii = 0; ii < xmetamaxkeys; ii++) { // from mkeys[11]; | |
if (! xmeta_keys[ii]) break; | if (! xmeta_keys[ii]) break; | |
exifkeys[nkey] = xmeta_keys[ii]; | mkeys[nkey] = xmeta_keys[ii]; | |
nkey++; | nkey++; | |
} | } | |
exif_get(file,exifkeys,ppv,nkey); | for (ii = 0; ii < xmetamaxkeys; ii++) | |
// get metadata from image file | // get indexed metadata if any | |
if (! xmeta_keys[ii]) break; | ||
meta_get1(file,mkeys,ppv,nkey); | ||
// get metadata from image file | ||
imagedate = ppv[0]; | imagedate = ppv[0]; | |
imagetags = ppv[1]; | imagetags = ppv[1]; | |
imagerating = ppv[2]; | imagerating = ppv[2]; | |
imageww = ppv[3]; | imageww = ppv[3]; | |
imagehh = ppv[4]; | imagehh = ppv[4]; | |
imagetitle = ppv[5]; | imagetitle = ppv[5]; | |
imagedesc = ppv[6]; | imagedesc = ppv[6]; | |
imageloc = ppv[7]; | imageloc = ppv[7]; | |
imagecountry = ppv[8]; | imagecountry = ppv[8]; | |
imagelati = ppv[9]; | imagelati = ppv[9]; | |
imagelongi = ppv[10]; | imagelongi = ppv[10]; | |
if (imagedate) { | if (imagedate) | |
exif_tagdate(imagedate,meta_pdate); | meta_tagdate(imagedate,meta_pdate); | |
// EXIF date/time >> yyyymmddhhmmss | // metadata date/time >> yyyymmddhhmmss | |
zfree(imagedate); | ||
} | ||
if (imagetags) | if (imagetags) | |
{ | { | |
for (ii = 1; ; ii++) | for (ii = 1; ; ii++) | |
{ | { | |
pp = (char *) substring(imagetags,",;",ii); | pp = (ch *) substring(imagetags,",;",ii); | |
if (! pp) break; | if (! pp) break; | |
if (*pp == 0) continue; | if (*pp == 0) continue; | |
cc = strlen(pp); | cc = strlen(pp); | |
if (cc >= tagcc) continue; // reject tags too big | if (cc >= tagXcc) continue; // reject tags too big | |
for (jj = 0; jj < cc; jj++) | for (jj = 0; jj < cc; jj++) | |
if (pp[jj] > 0 && pp[jj] < ' ') break; // reject tags with control characters | if (pp[jj] > 0 && pp[jj] < ' ') break; // reject tags with control characters | |
if (jj < cc) continue; | if (jj < cc) continue; | |
add_tag(pp,meta_tags,tagFcc); // add to file tags if unique | add_tag(pp,meta_tags,filetagsXcc); // add to file tags if unique | |
} | } | |
zfree(imagetags); | ||
} | } | |
if (imagerating) { | if (imagerating) { | |
meta_rating[0] = *imagerating; | meta_rating[0] = *imagerating; | |
if (meta_rating[0] < '0' || meta_rating[0] > '5') meta_rating[0] = '0'; | if (meta_rating[0] < '0' || meta_rating[0] > '5') meta_rating[0] = '0'; | |
meta_rating[1] = 0; | meta_rating[1] = 0; | |
zfree(imagerating); | ||
} | } | |
strcpy(meta_wwhh,"no data"); | strcpy(meta_wwhh,"no data"); | |
if (imageww && imagehh) { | if (imageww && imagehh) { | |
convSI(imageww,ww); | convSI(imageww,ww); | |
convSI(imagehh,hh); | convSI(imagehh,hh); | |
if (ww > 0 && hh > 0) | if (ww > 0 && hh > 0) | |
snprintf(meta_wwhh,15,"%dx%d",ww,hh); | snprintf(meta_wwhh,15,"%dx%d",ww,hh); | |
} | } | |
if (imageww) zfree(imageww); | if (imagetitle) | |
if (imagehh) zfree(imagehh); | strncpy0(meta_title,imagetitle,metadataXcc); | |
if (imagetitle) { | ||
strncpy0(meta_title,imagetitle,exif_maxcc); | ||
zfree(imagetitle); | ||
} | ||
if (imagedesc) { | if (imagedesc) | |
strncpy0(meta_description,imagedesc,exif_maxcc); | strncpy0(meta_description,imagedesc,metadataXcc); | |
zfree(imagedesc); | ||
} | ||
if (imageloc) { // geotags | if (imageloc) // geotags | |
strncpy0(meta_location,imageloc,100); | strncpy0(meta_location,imageloc,100); | |
zfree(imageloc); | ||
} | ||
else strcpy(meta_location,""); // replace missing data with "" | else strcpy(meta_location,""); // replace missing data with "" | |
if (imagecountry) { | if (imagecountry) | |
strncpy0(meta_country,imagecountry,100); | strncpy0(meta_country,imagecountry,100); | |
zfree(imagecountry); | ||
} | ||
else strcpy(meta_country,""); | else strcpy(meta_country,""); | |
if (imagelati) { | if (imagelati) | |
strncpy0(meta_lati,imagelati,12); | strncpy0(meta_lati,imagelati,12); | |
zfree(imagelati); | ||
} | ||
else strcpy(meta_lati,""); | else strcpy(meta_lati,""); | |
if (imagelongi) { | if (imagelongi) | |
strncpy0(meta_longi,imagelongi,12); | strncpy0(meta_longi,imagelongi,12); | |
zfree(imagelongi); | ||
} | ||
else strcpy(meta_longi,""); | else strcpy(meta_longi,""); | |
for (ii = 0; ii < MXmax; ii++) { // get indexed metadata if any | for (ii = 0; ii < xmetamaxkeys; ii++) { // get indexed metadata if any | |
if (! xmeta_keys[ii]) break; | if (! xmeta_keys[ii]) break; | |
if (xmeta_data[ii]) zfree(xmeta_data[ii]); | if (xmeta_data[ii]) zfree(xmeta_data[ii]); | |
if (ppv[ii+11]) xmeta_data[ii] = ppv[ii+11]; | if (ppv[ii+11]) xmeta_data[ii] = zstrdup(ppv[ii+11],"load-filemeta"); | |
else xmeta_data[ii] = zstrdup("null","load-filemeta"); // use "null" for missing data | else xmeta_data[ii] = zstrdup("null","load-filemeta"); // use "null" for missing data | |
} | } | |
for (ii = 0; ii < nkey; ii++) | ||
// free memory 23.1 | ||
if (ppv[ii]) zfree(ppv[ii]); | ||
Fmetamod = 0; // no pending changes | Fmetamod = 0; // no pending changes | |
return; | return; | |
} | } | |
// add metadata in memory to image file EXIF/IPTC data and image_index recs. | // add metadata in memory to image file metadata and image_index recs. | |
void save_filemeta(cchar *file) | void save_filemeta(ch *file) | |
{ | { | |
cchar *exifkeys[100] = { exif_date_key, iptc_keywords_key, iptc_rating_key | ch *mkeys[100] = { meta_date_key, meta_tags_key, meta_rating_key, | |
, | meta_title_key, meta_description_key, | |
iptc_title_key, iptc_description_key, | meta_location_key, meta_country_key, | |
exif_location_key, exif_country_key, | meta_lati_key, meta_longi_key }; | |
exif_lati_key, exif_longi_key }; | ||
int nkey, ii, err; | int nkey, ii, err; | |
cchar *exifdata[100]; | ch *metadata[100]; | |
char imagedate[24]; | ch imagedate[24]; | |
err = access(file,W_OK); // test file can be written by me | err = access(file,W_OK); // test file can be written by me | |
if (err) { | if (err) { | |
zmessageACK(Mwin,"no write permission: %s",file); | zmessageACK(Mwin,"no write permission: %s",file); | |
return; | return; | |
} | } | |
*imagedate = 0; | *imagedate = 0; | |
if (*meta_pdate) tag_exifdate(meta_pdate,imagedate); // yyyymmddhhmmss >> EXIF date/time | if (*meta_pdate) tag_metadate(meta_pdate,imagedate); // yyyymmddhhmmss >> metadata date/time | |
exifdata[0] = imagedate; | metadata[0] = imagedate; | |
// update file EXIF/IPTC data | // update file metadata | |
exifdata[1] = meta_tags; | metadata[1] = meta_tags; | |
exifdata[2] = meta_rating; | metadata[2] = meta_rating; | |
exifdata[3] = meta_title; | metadata[3] = meta_title; | |
exifdata[4] = meta_description; | metadata[4] = meta_description; | |
exifdata[5] = meta_location; | metadata[5] = meta_location; | |
// if "" erase EXIF | // if "" erase metadata | |
exifdata[6] = meta_country; | metadata[6] = meta_country; | |
if (strmatch(meta_lati,"") || strmatch(meta_longi,"")) | if (strmatch(meta_lati,"") || strmatch(meta_longi,"")) | |
exifdata[7] = exifdata[8] = ""; | metadata[7] = metadata[8] = 0; | |
else { | else { | |
exifdata[7] = meta_lati; | metadata[7] = meta_lati; | |
exifdata[8] = meta_longi; | metadata[8] = meta_longi; | |
} | } | |
nkey = 9; // add keys for indexed metadata | nkey = 9; // add keys for indexed metadata | |
for (ii = 0; ii < MXmax; ii++) { // from exifkeys[9]; | for (ii = 0; ii < xmetamaxkeys; ii++) { // from mkeys[9]; | |
if (! xmeta_keys[ii]) break; | if (! xmeta_keys[ii]) break; | |
exifkeys[nkey] = xmeta_keys[ii]; | mkeys[nkey] = xmeta_keys[ii]; | |
// missing data ("") will be erased | // missing data ("") will be erased | |
exifdata[nkey] = xmeta_data[ii]; | metadata[nkey] = xmeta_data[ii]; | |
nkey++; | nkey++; | |
} | } | |
err = exif_put(file,exifkeys,exifdata,nkey); | err = meta_put(file,(ch **) mkeys,metadata,nkey); | |
// write EXIF | // write metadata | |
if (err) zmessageACK(Mwin,"exif update error: %s",file); | if (err) zmessageACK(Mwin,"metadata update error: %s",file); | |
// 23.0 | // 23.0 | |
update_image_index(file); // update image index file | update_image_index(file); // update image index file | |
if (zd_metaview) meta_view(0); // live EXIF/IPTC update | if (zd_metaview) meta_view(0); // live meta update | |
Fmetamod = 0; // no pending changes | Fmetamod = 0; // no pending changes | |
return; | return; | |
} | } | |
// update image index record (replace updated file data) | // update image index record (replace updated file data) | |
// meta_xxxx data in memory >> image index record | // meta_xxxx data in memory >> image index record | |
void update_image_index(cchar *file) | void update_image_index(ch *file) | |
{ | { | |
int ii, xcc; | int ii, xcc; | |
int nn, ww, hh; | int nn, ww, hh; | |
char xmetarec[MXmaxcc]; | ch xmetarec[xmetaXcc]; | |
xxrec_t xxrec; | xxrec_t xxrec; | |
STATB statB; | STATB statB; | |