"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "f.meta.cc" between
fotoxx-23.0.tar.gz and fotoxx-23.1.tar.gz

About: fotoxx is a program for photo editing and collection management.

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;