From d99b1ff360bc9f5f14a82e875c092cf9e2e02545 Mon Sep 17 00:00:00 2001
From: David Rose
Date: Thu, 21 Nov 2002 06:09:12 +0000
Subject: [PATCH] handle larger, nested indexes
---
pandaapp/src/indexify/fontSamples.cxx | 3 +-
pandaapp/src/indexify/indexImage.cxx | 91 ++-----
pandaapp/src/indexify/indexImage.h | 3 -
pandaapp/src/indexify/indexParameters.cxx | 114 ++++++++
pandaapp/src/indexify/indexParameters.h | 8 +
pandaapp/src/indexify/indexify.cxx | 129 +++++++--
pandaapp/src/indexify/rollDirectory.cxx | 302 ++++++++++++++++++++--
pandaapp/src/indexify/rollDirectory.h | 17 +-
8 files changed, 560 insertions(+), 107 deletions(-)
diff --git a/pandaapp/src/indexify/fontSamples.cxx b/pandaapp/src/indexify/fontSamples.cxx
index f6bfc09b49..52d77c1268 100644
--- a/pandaapp/src/indexify/fontSamples.cxx
+++ b/pandaapp/src/indexify/fontSamples.cxx
@@ -20,9 +20,10 @@
#include "pnmTextMaker.h"
#include "default_font.h"
#include "pnmImage.h"
-
#include "notify.h"
+#include
+
////////////////////////////////////////////////////////////////////
// Function: FontSamples::Constructor
// Access: Public
diff --git a/pandaapp/src/indexify/indexImage.cxx b/pandaapp/src/indexify/indexImage.cxx
index a13e7bbbfe..7b8a673c77 100644
--- a/pandaapp/src/indexify/indexImage.cxx
+++ b/pandaapp/src/indexify/indexImage.cxx
@@ -96,8 +96,8 @@ generate_images(const Filename &archive_dir, PNMTextMaker *text_maker) {
PNMImage index_image;
Filename reduced_dir(archive_dir, "reduced/" + _dir->get_basename());
-
- Filename output_filename(archive_dir, _name);
+ Filename thumbnail_dir(archive_dir, "thumbs");
+ Filename output_filename(thumbnail_dir, _name);
output_filename.set_extension("jpg");
Photos::const_iterator pi;
@@ -161,9 +161,11 @@ generate_images(const Filename &archive_dir, PNMTextMaker *text_maker) {
Photo *photo = _dir->get_photo(pinfo._photo_index);
Filename photo_filename(_dir->get_dir(), photo->get_basename());
Filename reduced_filename(reduced_dir, photo->get_basename());
+ photo_filename.standardize();
+ reduced_filename.standardize();
PNMImage reduced_image;
- if (!dummy_mode &&
+ if (!dummy_mode && photo_filename != reduced_filename &&
(force_regenerate ||
reduced_filename.compare_timestamps(photo_filename) < 0)) {
// If the reduced filename does not exist or is older than the
@@ -182,6 +184,10 @@ generate_images(const Filename &archive_dir, PNMTextMaker *text_maker) {
// Generate a reduced image for the photo.
compute_reduction(photo_image, reduced_image, reduced_width, reduced_height);
+
+ photo->_reduced_x_size = reduced_image.get_x_size();
+ photo->_reduced_y_size = reduced_image.get_y_size();
+
reduced_image.quick_filter_from(photo_image);
reduced_filename.make_dir();
nout << "Writing " << reduced_filename << "\n";
@@ -190,9 +196,6 @@ generate_images(const Filename &archive_dir, PNMTextMaker *text_maker) {
return false;
}
- photo->_reduced_x_size = reduced_image.get_x_size();
- photo->_reduced_y_size = reduced_image.get_y_size();
-
} else {
// If the reduced image already exists and is newer than the
// source image, use it.
@@ -262,7 +265,8 @@ generate_images(const Filename &archive_dir, PNMTextMaker *text_maker) {
pinfo._x_place + x_center, pinfo._y_place + y_center,
thumbnail_image.get_x_size(), thumbnail_image.get_y_size());
}
-
+
+ thumbnail_image.set_color_type(index_image.get_color_type());
index_image.copy_sub_image(thumbnail_image,
pinfo._x_place + x_center,
pinfo._y_place + y_center);
@@ -277,6 +281,7 @@ generate_images(const Filename &archive_dir, PNMTextMaker *text_maker) {
if (generate_index_image) {
nout << "Writing " << output_filename << "\n";
+ output_filename.make_dir();
if (!index_image.write(output_filename)) {
nout << "Unable to write.\n";
return false;
@@ -304,21 +309,22 @@ generate_html(ostream &root_html, const Filename &archive_dir,
const Filename &roll_dir_root) {
root_html
<< "\n"
- << "
\n"
<< "
\n";
- if (!omit_full_links) {
+ if (!omit_full_links &&
+ (photo->_full_x_size != photo->_reduced_x_size ||
+ photo->_full_y_size != photo->_reduced_y_size)) {
html
<< "View full size image ("
<< photo->_full_x_size << " x " << photo->_full_y_size << ")
";
@@ -503,7 +514,7 @@ generate_reduced_html(ostream &html, Photo *photo, int photo_index, int pi,
////////////////////////////////////////////////////////////////////
// Function: IndexImage::generate_nav_buttons
-// Access: Private, Static
+// Access: Private
// Description: Outputs the HTML code to generate the next, prev,
// up buttons when viewing each reduced image.
////////////////////////////////////////////////////////////////////
@@ -570,54 +581,6 @@ generate_nav_buttons(ostream &html, const Filename &prev_photo_filename,
html << "\n";
}
-////////////////////////////////////////////////////////////////////
-// Function: IndexImage::compose_href
-// Access: Private
-// Description: Combines a user-supplied prefix with a relative
-// directory to generate the appropriate href to the
-// file.
-//
-// rel_dir is the relative path to archive_dir.
-// user_prefix is the string the user indicated as the
-// relative or absolute path to the file's parent
-// directory from archive_dir. basename is the name
-// of the file, or empty if the filename is part of
-// user_prefix.
-////////////////////////////////////////////////////////////////////
-Filename IndexImage::
-compose_href(const Filename &rel_dir, const Filename &user_prefix,
- const Filename &basename) {
- Filename result;
-
- if (user_prefix.empty()) {
- result = rel_dir;
-
- } else {
- // Check to see if the user prefix begins with a URL designator,
- // like http:// or ftp://.
- size_t ui = 0;
- while (ui < user_prefix.length() && isalpha(user_prefix[ui])) {
- ui++;
- }
- bool is_url = (user_prefix.get_fullpath().substr(ui, 3) == "://");
-
- if (!is_url && user_prefix.is_local()) {
- Filename rel_user_dir(rel_dir, user_prefix);
- result = rel_user_dir;
- result.standardize();
-
- } else {
- result = user_prefix;
- }
- }
-
- if (basename.empty()) {
- return result;
- } else {
- return Filename(result, basename);
- }
-}
-
////////////////////////////////////////////////////////////////////
// Function: IndexImage::compute_reduction
// Access: Private, Static
diff --git a/pandaapp/src/indexify/indexImage.h b/pandaapp/src/indexify/indexImage.h
index 21ff193ac0..0cb8ee2c28 100644
--- a/pandaapp/src/indexify/indexImage.h
+++ b/pandaapp/src/indexify/indexImage.h
@@ -56,9 +56,6 @@ private:
const Filename &next_photo_filename,
const string &up_href);
- Filename compose_href(const Filename &rel_dir, const Filename &user_prefix,
- const Filename &basename = Filename());
-
static void compute_reduction(const PNMImageHeader &source_image,
PNMImage &dest_image,
int x_size, int y_size);
diff --git a/pandaapp/src/indexify/indexParameters.cxx b/pandaapp/src/indexify/indexParameters.cxx
index 12f3a4c24d..f53e85cee1 100644
--- a/pandaapp/src/indexify/indexParameters.cxx
+++ b/pandaapp/src/indexify/indexParameters.cxx
@@ -44,6 +44,7 @@ Filename up_icon;
bool force_regenerate = false;
bool format_rose = false;
+bool sort_date = false;
bool dummy_mode = false;
bool draw_frames = false;
bool omit_roll_headers = false;
@@ -87,3 +88,116 @@ finalize_parameters() {
thumb_interior_height = thumb_height;
}
}
+
+////////////////////////////////////////////////////////////////////
+// Function: compose_href
+// Description: Combines a user-supplied prefix with a relative
+// directory to generate the appropriate href to the
+// file.
+//
+// rel_dir is the relative path to archive_dir.
+// user_prefix is the string the user indicated as the
+// relative or absolute path to the file's parent
+// directory from archive_dir. basename is the name
+// of the file, or empty if the filename is part of
+// user_prefix.
+////////////////////////////////////////////////////////////////////
+Filename
+compose_href(const Filename &rel_dir, const Filename &user_prefix,
+ const Filename &basename) {
+ Filename result;
+
+ if (user_prefix.empty()) {
+ result = rel_dir;
+
+ } else {
+ // Check to see if the user prefix begins with a URL designator,
+ // like http:// or ftp://.
+ size_t ui = 0;
+ while (ui < user_prefix.length() && isalpha(user_prefix[ui])) {
+ ui++;
+ }
+ bool is_url = (user_prefix.get_fullpath().substr(ui, 3) == "://");
+
+ if (!is_url && user_prefix.is_local()) {
+ Filename rel_user_dir(rel_dir, user_prefix);
+ result = rel_user_dir;
+ result.standardize();
+
+ } else {
+ result = user_prefix;
+ }
+ }
+
+ if (basename.empty()) {
+ return result;
+ } else {
+ return Filename(result, basename);
+ }
+}
+
+////////////////////////////////////////////////////////////////////
+// Function: escape_html
+// Description: Returns the input string with all invalid characters
+// for HTML code replaced by their HTML equivalents.
+////////////////////////////////////////////////////////////////////
+string
+escape_html(const string &input) {
+ static const struct {
+ const char *name;
+ int code;
+ } tokens[] = {
+ { "amp", '&' }, { "lt", '<' }, { "gt", '>' }, { "quot", '"' },
+ { "nbsp", 160 },
+
+ { "iexcl", 161 }, { "cent", 162 }, { "pound", 163 }, { "curren", 164 },
+ { "yen", 165 }, { "brvbar", 166 }, { "brkbar", 166 }, { "sect", 167 },
+ { "uml", 168 }, { "die", 168 }, { "copy", 169 }, { "ordf", 170 },
+ { "laquo", 171 }, { "not", 172 }, { "shy", 173 }, { "reg", 174 },
+ { "macr", 175 }, { "hibar", 175 }, { "deg", 176 }, { "plusmn", 177 },
+ { "sup2", 178 }, { "sup3", 179 }, { "acute", 180 }, { "micro", 181 },
+ { "para", 182 }, { "middot", 183 }, { "cedil", 184 }, { "sup1", 185 },
+ { "ordm", 186 }, { "raquo", 187 }, { "frac14", 188 }, { "frac12", 189 },
+ { "frac34", 190 }, { "iquest", 191 }, { "Agrave", 192 }, { "Aacute", 193 },
+ { "Acirc", 194 }, { "Atilde", 195 }, { "Auml", 196 }, { "Aring", 197 },
+ { "AElig", 198 }, { "Ccedil", 199 }, { "Egrave", 200 }, { "Eacute", 201 },
+ { "Ecirc", 202 }, { "Euml", 203 }, { "Igrave", 204 }, { "Iacute", 205 },
+ { "Icirc", 206 }, { "Iuml", 207 }, { "ETH", 208 }, { "Dstrok", 208 },
+ { "Ntilde", 209 }, { "Ograve", 210 }, { "Oacute", 211 }, { "Ocirc", 212 },
+ { "Otilde", 213 }, { "Ouml", 214 }, { "times", 215 }, { "Oslash", 216 },
+ { "Ugrave", 217 }, { "Uacute", 218 }, { "Ucirc", 219 }, { "Uuml", 220 },
+ { "Yacute", 221 }, { "THORN", 222 }, { "szlig", 223 }, { "agrave", 224 },
+ { "aacute", 225 }, { "acirc", 226 }, { "atilde", 227 }, { "auml", 228 },
+ { "aring", 229 }, { "aelig", 230 }, { "ccedil", 231 }, { "egrave", 232 },
+ { "eacute", 233 }, { "ecirc", 234 }, { "euml", 235 }, { "igrave", 236 },
+ { "iacute", 237 }, { "icirc", 238 }, { "iuml", 239 }, { "eth", 240 },
+ { "ntilde", 241 }, { "ograve", 242 }, { "oacute", 243 }, { "ocirc", 244 },
+ { "otilde", 245 }, { "ouml", 246 }, { "divide", 247 }, { "oslash", 248 },
+ { "ugrave", 249 }, { "uacute", 250 }, { "ucirc", 251 }, { "uuml", 252 },
+ { "yacute", 253 }, { "thorn", 254 }, { "yuml", 255 },
+
+ { NULL, 0 },
+ };
+
+ string result;
+ for (string::const_iterator ii = input.begin();
+ ii != input.end();
+ ++ii) {
+ int code = (unsigned char)(*ii);
+ bool found_match = false;
+ for (int i = 0; !found_match && tokens[i].name != NULL; i++) {
+ if (code == tokens[i].code) {
+ result += '&';
+ result += tokens[i].name;
+ result += ';';
+ found_match = true;
+ }
+ }
+
+ if (!found_match) {
+ result += (*ii);
+ }
+ }
+
+ return result;
+}
diff --git a/pandaapp/src/indexify/indexParameters.h b/pandaapp/src/indexify/indexParameters.h
index 3bc8e0ab9d..74c95693df 100644
--- a/pandaapp/src/indexify/indexParameters.h
+++ b/pandaapp/src/indexify/indexParameters.h
@@ -83,6 +83,9 @@ extern bool force_regenerate;
// True to use the Rose formatting convention for roll directory names.
extern bool format_rose;
+// True to sort roll directory names by date. Useful only with -r.
+extern bool sort_date;
+
// True to place dummy thumbnails instead of loading actual images.
extern bool dummy_mode;
@@ -127,6 +130,11 @@ extern int actual_index_width;
extern int thumb_interior_width;
extern int thumb_interior_height;
+Filename compose_href(const Filename &rel_dir, const Filename &user_prefix,
+ const Filename &basename = Filename());
+
+string escape_html(const string &input);
+
#endif
diff --git a/pandaapp/src/indexify/indexify.cxx b/pandaapp/src/indexify/indexify.cxx
index 631aa0ebc0..bf862bedbf 100644
--- a/pandaapp/src/indexify/indexify.cxx
+++ b/pandaapp/src/indexify/indexify.cxx
@@ -25,6 +25,8 @@
#include "indexParameters.h"
#include "string_utils.h"
+#include
+
////////////////////////////////////////////////////////////////////
// Function: Indexify::Constructor
// Access: Public
@@ -34,6 +36,7 @@ Indexify::
Indexify() {
clear_runlines();
add_runline("[opts] roll1-dir roll2-dir [roll3-dir ...]");
+ add_runline("[opts] full/*");
set_program_description
("This program reads a collection of directories containing photo "
@@ -47,18 +50,24 @@ Indexify() {
"should be within the same parent directory. Each directory is "
"considered a \"roll\", which may or may not correspond to a physical "
"roll of film, and the photos within each directory are grouped "
- "correspondingly on the generated HTML pages.\n\n"
+ "correspondingly on the generated HTML pages. One common special case "
+ "is in which all the roll directories are found in the subdirectory "
+ "named \"full\" (e.g., the second example above) or \"reduced\". This "
+ "keeps the root directory nice and clean.\n\n"
"If a file exists by the same name as an image file but with the "
"extension \"cm\", that file is taken to be a HTML comment about that "
"particular image and is inserted the HTML page for that image. "
"Similarly, if there is a file within a roll directory with the same "
"name as the directory itself (but with the extension \"cm\"), that file "
- "is inserted into the front page to introduce that particular roll.\n\n"
+ "is inserted into the front page to introduce that particular roll. "
+ "Finally, a file with the name of the directory, but with the extension "
+ "\"ds\" may contain a brief one-line description of the directory, for "
+ "the toplevel index page.\n\n"
"Normally, all image files with the specified extension (normally "
"\"jpg\") within a roll directory are included in the index, and sorted "
- "into alphabetical (or numeric) order. If you wish to specify a "
+ "into alphabetical (or numerical) order. If you wish to specify a "
"different order, or use only a subset of the images in a directory, "
"create a file in the roll directory with the same name as the "
"directory itself, and the extension \"ls\". This file should "
@@ -81,8 +90,8 @@ Indexify() {
add_option
("r", "relative-dir", 0,
- "When -a is specifies to place the generate html files in a directory "
- "other than the one above the actual roll directories, you may need "
+ "When -a is specified to place the generated html files in a directory "
+ "other than the default, you may need "
"to specify how the html files will address the roll directories. This "
"parameter specifies the relative path to the directory above the roll "
"directories, from the directory named by -a.",
@@ -103,6 +112,12 @@ Indexify() {
"name will be reformatted to m-yy/s for output.",
&Indexify::dispatch_none, &format_rose);
+ add_option
+ ("s", "", 0,
+ "When used in conjunction with -r, requests sorting of the roll "
+ "directory names by date.",
+ &Indexify::dispatch_none, &sort_date);
+
add_option
("d", "", 0,
"Run in \"dummy\" mode; don't load any images, but instead just "
@@ -261,7 +276,8 @@ handle_args(ProgramBase::Args &args) {
filename.standardize();
if (filename.is_directory()) {
string basename = filename.get_basename();
- if (basename == "icons" || basename == "html" || basename == "reduced") {
+ if (basename == "icons" || basename == "html" ||
+ basename == "reduced" || basename == "thumbs") {
nout << "Ignoring " << filename << "; indexify-generated directory.\n";
} else {
@@ -287,6 +303,13 @@ handle_args(ProgramBase::Args &args) {
return true;
}
+class SortRollDirs {
+public:
+ bool operator () (const RollDirectory *a, const RollDirectory *b) const {
+ return a->sort_date_before(*b);
+ }
+};
+
////////////////////////////////////////////////////////////////////
// Function: Indexify::post_command_line
// Access: Protected, Virtual
@@ -307,6 +330,12 @@ post_command_line() {
if (_archive_dir.empty()) {
// Choose a default archive directory, above the first roll directory.
_archive_dir = _roll_dirs.front()->get_dir().get_dirname();
+ string parent_dirname = _archive_dir.get_basename();
+ if (parent_dirname == "full" || parent_dirname == "reduced") {
+ // As a special case, if the subdirectory name is "full" or
+ // "reduced", use the directory above that.
+ _archive_dir = _archive_dir.get_dirname();
+ }
if (_archive_dir.empty()) {
_archive_dir = ".";
}
@@ -317,12 +346,17 @@ post_command_line() {
_roll_dir_root.standardize();
}
+ // Sort the roll directories, if specified.
+ if (format_rose && sort_date) {
+ sort(_roll_dirs.begin(), _roll_dirs.end(), SortRollDirs());
+ }
+
if (_front_title.empty()) {
// Supply a default title.
if (_roll_dirs.size() == 1) {
_front_title = _roll_dirs.front()->get_name();
} else {
- _front_title = _roll_dirs.front()->get_name() + " to " + _roll_dirs.back()->get_name();
+ _front_title = _roll_dirs.front()->get_name() + " through " + _roll_dirs.back()->get_name();
}
}
@@ -479,17 +513,26 @@ run() {
}
// Then go back and generate the HTML.
+ for (di = _roll_dirs.begin(); di != _roll_dirs.end(); ++di) {
+ RollDirectory *roll_dir = (*di);
+ if (!roll_dir->generate_html(_archive_dir, _roll_dir_root)) {
+ nout << "Failure.\n";
+ exit(1);
+ }
+ }
- Filename html_filename(_archive_dir, "index.htm");
- nout << "Generating " << html_filename << "\n";
- html_filename.set_text();
- ofstream root_html;
- if (!html_filename.open_write(root_html)) {
- nout << "Unable to write to " << html_filename << "\n";
+ // Generate the complete index that browses all the roll directories
+ // at once.
+ Filename complete_filename(_archive_dir, "html/complete.htm");
+ nout << "Generating " << complete_filename << "\n";
+ complete_filename.set_text();
+ ofstream complete_html;
+ if (!complete_filename.open_write(complete_html)) {
+ nout << "Unable to write to " << complete_filename << "\n";
exit(1);
}
- root_html
+ complete_html
<< "\n"
<< "\n"
<< "" << _front_title << "\n"
@@ -499,13 +542,63 @@ run() {
for (di = _roll_dirs.begin(); di != _roll_dirs.end(); ++di) {
RollDirectory *roll_dir = (*di);
- if (!roll_dir->generate_html(root_html, _archive_dir, _roll_dir_root)) {
- nout << "Failure.\n";
- exit(1);
+ complete_html
+ << roll_dir->get_comment_html()
+ << roll_dir->get_index_html();
+ }
+
+ complete_html << "\n";
+ if (!up_icon.empty()) {
+ // Use an icon to go up.
+ Filename up_icon_href = compose_href("..", up_icon);
+ complete_html
+ << "
\n";
+ } else {
+ // No up icon; use text to go up.
+ complete_html
+ << "
Return to index\n";
+ }
+ complete_html << "
\n";
+
+ complete_html
+ << "\n"
+ << "\n";
+
+ // And finally, generate the index HTML file that sits on the top of
+ // all of this.
+ Filename index_filename(_archive_dir, "index.htm");
+ nout << "Generating " << index_filename << "\n";
+ index_filename.set_text();
+ ofstream index_html;
+ if (!index_filename.open_write(index_html)) {
+ nout << "Unable to write to " << index_filename << "\n";
+ exit(1);
+ }
+
+ index_html
+ << "\n"
+ << "\n"
+ << "" << _front_title << "\n"
+ << "\n"
+ << "\n"
+ << "" << _front_title << "
\n"
+ << "\n"
+ << "(complete archive)\n"
<< "\n"
<< "\n";
}
diff --git a/pandaapp/src/indexify/rollDirectory.cxx b/pandaapp/src/indexify/rollDirectory.cxx
index 3220502e03..910e004262 100644
--- a/pandaapp/src/indexify/rollDirectory.cxx
+++ b/pandaapp/src/indexify/rollDirectory.cxx
@@ -101,6 +101,16 @@ get_name() const {
return _name;
}
+////////////////////////////////////////////////////////////////////
+// Function: RollDirectory::get_desc
+// Access: Public
+// Description: Returns the one-line description for the directory.
+////////////////////////////////////////////////////////////////////
+const string &RollDirectory::
+get_desc() const {
+ return _desc;
+}
+
////////////////////////////////////////////////////////////////////
// Function: RollDirectory::scan
// Access: Public
@@ -111,9 +121,47 @@ scan(const string &extension) {
bool reverse_order = false;
bool explicit_list = false;
+ // Check for a .ds file, which contains a one-line description of
+ // the contents of the directory.
+ Filename ds_filename(_basename);
+ ds_filename.set_extension("ds");
+ if (cm_search.is_empty() || !ds_filename.resolve_filename(cm_search)) {
+ // If the ds file isn't found along the search path specified
+ // via -cmdir on the command line, then look for it in the
+ // appropriate source directory.
+ ds_filename = Filename(_dir, ds_filename);
+ }
+ if (ds_filename.exists()) {
+ ds_filename.set_text();
+ ifstream ds;
+ if (!ds_filename.open_read(ds)) {
+ nout << "Could not read " << ds_filename << "\n";
+ } else {
+ // Get the words out one at a time and put just one space
+ // between them.
+ string word;
+ ds >> word;
+ while (!ds.eof() && !ds.fail()) {
+ if (!_desc.empty()) {
+ _desc += ' ';
+ }
+ _desc += word;
+ word = string();
+ ds >> word;
+ }
+ if (!word.empty()) {
+ if (!_desc.empty()) {
+ _desc += ' ';
+ }
+ _desc += word;
+ }
+ }
+ }
+
// Check for an .ls file in the roll directory, which may give an
// explicit ordering, or if empty, it specifies reverse ordering.
- Filename ls_filename(_dir, _basename + ".ls");
+ Filename ls_filename(_dir, _basename);
+ ls_filename.set_extension("ls");
if (ls_filename.exists()) {
add_contributing_filename(ls_filename);
ls_filename.set_text();
@@ -213,6 +261,51 @@ collect_index_images() {
}
}
+////////////////////////////////////////////////////////////////////
+// Function: RollDirectory::sort_date_before
+// Access: Public
+// Description: Returns true if the given directory name should sort
+// before the other one, assuming the Rose naming
+// convention of mmyyss is in place.
+////////////////////////////////////////////////////////////////////
+bool RollDirectory::
+sort_date_before(const RollDirectory &other) const {
+ if (_name == _basename && other._name == other._basename) {
+ // If Rose naming convention is not in place in either case, sort
+ // alphabetically.
+ return _basename < other._basename;
+
+ } else if (_name == _basename) {
+ // If Rose naming convention is in place on this one and not the
+ // other, it sorts first.
+ return true;
+
+ } else if (other._name == other._basename) {
+ // And vice-versa.
+ return false;
+
+ } else {
+ // Rose naming convention holds. Sort based on year first. Years
+ // above 90 are deemed to belong to the previous century.
+ string yy = _basename.substr(2, 2);
+ string other_yy = other._basename.substr(2, 2);
+ int year = atoi(yy.c_str());
+ int other_year = atoi(other_yy.c_str());
+ if (year < 90) {
+ year += 100;
+ }
+ if (other_year < 90) {
+ other_year += 100;
+ }
+ if (year != other_year) {
+ return year < other_year;
+ }
+
+ // After year, sort alphabetically.
+ return _basename < other._basename;
+ }
+}
+
////////////////////////////////////////////////////////////////////
// Function: RollDirectory::get_newest_contributing_filename
// Access: Public
@@ -310,17 +403,18 @@ generate_images(const Filename &archive_dir, PNMTextMaker *text_maker) {
// Access: Public
// Description: Generates all appropriate HTML files for this
// directory, and generate the appropriate HTML code
-// into the root_html file.
+// into the html strings (retrieved by
+// get_comment_html() and get_index_html()).
////////////////////////////////////////////////////////////////////
bool RollDirectory::
-generate_html(ostream &root_html, const Filename &archive_dir,
- const Filename &roll_dir_root) {
+generate_html(const Filename &archive_dir, const Filename &roll_dir_root) {
if (is_empty()) {
return true;
}
nassertr(!_index_images.empty(), false);
- root_html
+ ostringstream comment_strm;
+ comment_strm
<< "\n";
if (!omit_roll_headers) {
@@ -336,32 +430,122 @@ generate_html(ostream &root_html, const Filename &archive_dir,
if (cm_filename.exists()) {
// If the comment file for the roll exists, insert its contents
// here instead of the generic header.
- if (!insert_html_comment(root_html, cm_filename)) {
+ if (!insert_html_comment(comment_strm, cm_filename)) {
return false;
}
} else {
- root_html
+ comment_strm
<< "" << _name << "
\n";
+ if (!_desc.empty()) {
+ comment_strm << "" << escape_html(_desc) << ".
\n";
+ }
}
}
+ _comment_html = comment_strm.str();
- nout << "Generating " << Filename(archive_dir, "html/")
- << _basename << "/*\n";
+ Filename html_dir(archive_dir, "html");
+ nout << "Generating " << Filename(html_dir, _basename) << "/*\n";
- root_html << "\n";
+ ostringstream index_strm;
+ index_strm << "
\n";
IndexImages::iterator ii;
for (ii = _index_images.begin(); ii != _index_images.end(); ++ii) {
IndexImage *index_image = (*ii);
- if (!index_image->generate_html(root_html, archive_dir, roll_dir_root)) {
+ if (!index_image->generate_html(index_strm, archive_dir, roll_dir_root)) {
return false;
}
}
- root_html << "
\n";
+ index_strm << "\n";
+ _index_html = index_strm.str();
+ // Also generate the index html for this directory.
+ Filename html_filename(html_dir, _basename);
+ html_filename.set_extension("htm");
+ nout << "Generating " << html_filename << "\n";
+ html_filename.set_text();
+ ofstream index_html;
+ if (!html_filename.open_write(index_html)) {
+ nout << "Unable to write to " << html_filename << "\n";
+ exit(1);
+ }
+
+ string up_href = "../index.htm#" + _basename;
+
+ Filename prev_roll_filename;
+ Filename next_roll_filename;
+
+ if (_prev != (RollDirectory *)NULL) {
+ prev_roll_filename = _prev->_basename;
+ prev_roll_filename.set_extension("htm");
+ }
+ if (_next != (RollDirectory *)NULL) {
+ next_roll_filename = _next->_basename;
+ next_roll_filename.set_extension("htm");
+ }
+
+ index_html
+ << "\n"
+ << "\n";
+ if (_desc.empty()) {
+ index_html
+ << "" << _name << "\n";
+ } else {
+ index_html
+ << "" << _name << " " << escape_html(_desc) << "\n";
+ }
+ index_html
+ << "\n"
+ << "\n"
+ << get_comment_html();
+
+ generate_nav_buttons(index_html, prev_roll_filename, next_roll_filename,
+ up_href);
+ index_html << get_index_html();
+ generate_nav_buttons(index_html, prev_roll_filename, next_roll_filename,
+ up_href);
+
+ index_html
+ << "(complete archive)\n"
+ << "\n"
+ << "\n";
+
+
return true;
}
+////////////////////////////////////////////////////////////////////
+// Function: RollDirectory::get_comment_html
+// Access: Public
+// Description: Returns the HTML text that describes this directory's
+// index. This is set when generate_html() returns
+// true.
+//
+// This text may be inserted into the middle of a HTML
+// page to include the imagemap that references each of
+// the images in this directory.
+////////////////////////////////////////////////////////////////////
+const string &RollDirectory::
+get_comment_html() const {
+ return _comment_html;
+}
+
+////////////////////////////////////////////////////////////////////
+// Function: RollDirectory::get_index_html
+// Access: Public
+// Description: Returns the HTML text that describes this directory's
+// index. This is set when generate_html() returns
+// true.
+//
+// This text may be inserted into the middle of a HTML
+// page to include the imagemap that references each of
+// the images in this directory.
+////////////////////////////////////////////////////////////////////
+const string &RollDirectory::
+get_index_html() const {
+ return _index_html;
+}
+
////////////////////////////////////////////////////////////////////
// Function: RollDirectory::output
// Access: Public
@@ -492,23 +676,103 @@ format_basename(const string &basename) {
return basename;
}
- // The first four characters must be digits.
- for (size_t i = 0; i < 4; i++) {
- if (!isdigit(basename[i])) {
- return basename;
- }
+ // The first two characters must be alphanumeric.
+ if (!isalnum(basename[0]) || !isalnum(basename[1])) {
+ return basename;
}
+ // The next two characters must be digits.
+ if (!isdigit(basename[2]) || !isdigit(basename[3])) {
+ return basename;
+ }
+
+ // If the first two were digits as well as being alphanumeric, then
+ // we have mm-yy/sequence. Otherwise, we just have xxyy/sequence.
+ bool mm_is_month = (isdigit(basename[0]) && isdigit(basename[1]));
+
string mm = basename.substr(0, 2);
string yy = basename.substr(2, 2);
string ss = basename.substr(4);
- if (mm[0] == '0') {
+ if (mm_is_month && mm[0] == '0') {
mm = mm[1];
}
while (ss.length() > 1 && ss[0] == '0') {
ss = ss.substr(1);
}
- return mm + "-" + yy + "/" + ss;
+ if (mm_is_month) {
+ return mm + "-" + yy + "/" + ss;
+ } else {
+ return mm + yy + "/" + ss;
+ }
+}
+
+////////////////////////////////////////////////////////////////////
+// Function: RollDirectory::generate_nav_buttons
+// Access: Private, Static
+// Description: Outputs the HTML code to generate the next, prev,
+// up buttons when viewing each reduced image.
+////////////////////////////////////////////////////////////////////
+void RollDirectory::
+generate_nav_buttons(ostream &html, const Filename &prev_roll_filename,
+ const Filename &next_roll_filename,
+ const string &up_href) {
+ html << "\n";
+
+ bool first_icons = false;
+ if (!prev_icon.empty() && !next_icon.empty()) {
+ first_icons = true;
+ // Use icons to go forward and back.
+ Filename prev_icon_href = compose_href("..", prev_icon);
+ if (prev_roll_filename.empty()) {
+ html << "
\n";
+ } else {
+ html << "
\n";
+ }
+
+ Filename next_icon_href = compose_href("..", next_icon);
+ if (next_roll_filename.empty()) {
+ html << "
\n";
+ } else {
+ html << "
\n";
+ }
+
+ } else {
+ // No prev/next icons; use text to go forward and back.
+ if (prev_roll_filename.empty()) {
+ html << "(This is the first roll.)\n";
+ } else {
+ html << "Back to previous roll\n";
+ }
+
+ if (next_roll_filename.empty()) {
+ html << "
(This is the last roll.)\n";
+ } else {
+ html << "
On to next roll\n";
+ }
+ }
+
+ if (!up_href.empty()) {
+ if (!up_icon.empty()) {
+ // Use an icon to go up.
+ if (!first_icons) {
+ html << "
";
+ } else {
+ html << " ";
+ }
+ Filename up_icon_href = compose_href("..", up_icon);
+ html << "
\n";
+ } else {
+ // No up icon; use text to go up.
+ html << "
Return to index\n";
+ }
+ }
+ html << "
\n";
}
diff --git a/pandaapp/src/indexify/rollDirectory.h b/pandaapp/src/indexify/rollDirectory.h
index 9cb589a59b..55a78f63bc 100644
--- a/pandaapp/src/indexify/rollDirectory.h
+++ b/pandaapp/src/indexify/rollDirectory.h
@@ -41,9 +41,12 @@ public:
const Filename &get_dir() const;
const string &get_basename() const;
const string &get_name() const;
+ const string &get_desc() const;
bool scan(const string &extension);
void collect_index_images();
+ bool sort_date_before(const RollDirectory &other) const;
+
const Filename &get_newest_contributing_filename() const;
bool is_empty() const;
@@ -54,8 +57,10 @@ public:
IndexImage *get_index_image(int n) const;
bool generate_images(const Filename &archive_dir, PNMTextMaker *text_maker);
- bool generate_html(ostream &root_html, const Filename &archive_dir,
- const Filename &roll_dir_root);
+ bool generate_html(const Filename &archive_dir,
+ const Filename &roll_dir_root);
+ const string &get_comment_html() const;
+ const string &get_index_html() const;
void output(ostream &out) const;
void write(ostream &out, int indent_level) const;
@@ -66,6 +71,10 @@ private:
void add_contributing_filename(const Filename &filename);
static bool insert_html_comment_body(ostream &html, istream &cm);
static string format_basename(const string &basename);
+ void generate_nav_buttons(ostream &html,
+ const Filename &prev_roll_filename,
+ const Filename &next_roll_filename,
+ const string &up_href);
public:
RollDirectory *_prev;
@@ -75,6 +84,7 @@ private:
Filename _dir;
string _basename;
string _name;
+ string _desc;
typedef pvector Photos;
Photos _photos;
@@ -82,6 +92,9 @@ private:
typedef pvector IndexImages;
IndexImages _index_images;
+
+ string _comment_html;
+ string _index_html;
};
INLINE ostream &operator << (ostream &out, const RollDirectory &d) {