handle larger, nested indexes

This commit is contained in:
David Rose 2002-11-21 06:09:12 +00:00
parent ae4ad65a8b
commit d99b1ff360
8 changed files with 560 additions and 107 deletions

View File

@ -20,9 +20,10 @@
#include "pnmTextMaker.h"
#include "default_font.h"
#include "pnmImage.h"
#include "notify.h"
#include <stdio.h>
////////////////////////////////////////////////////////////////////
// Function: FontSamples::Constructor
// Access: Public

View File

@ -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
<< "<a name=\"" << _name << "\">\n"
<< "<img src=\"" << _index_basename
<< "<img src=\"../thumbs/" << _index_basename
<< "\" width=" << _index_x_size << " height=" << _index_y_size
<< " ismap usemap=\"#" << _name << "\">\n"
<< "<map name=\"" << _name << "\"><br>\n";
Filename html_dir(archive_dir, "html");
Photos::const_iterator pi;
for (pi = _photos.begin(); pi != _photos.end(); ++pi) {
const PhotoInfo &pinfo = (*pi);
int photo_index = pinfo._photo_index;
Photo *photo = _dir->get_photo(pinfo._photo_index);
Filename html_relname("html/" + _dir->get_basename(),
photo->get_basename());
Filename html_relname(_dir->get_basename(), photo->get_basename());
html_relname.set_extension("htm");
Filename html_filename(archive_dir, html_relname);
Filename html_filename(html_dir, html_relname);
html_filename.make_dir();
html_filename.set_text();
ofstream reduced_html;
@ -382,15 +388,18 @@ write(ostream &out, int indent_level) const {
bool IndexImage::
generate_reduced_html(ostream &html, Photo *photo, int photo_index, int pi,
const Filename &roll_dir_root) {
Filename full_dir =
Filename full_dir;
if (roll_dir_root.empty()) {
full_dir = Filename("../..", _dir->get_dir());
} else {
compose_href("../..", roll_dir_root, _dir->get_basename());
}
Filename full(full_dir, photo->get_basename());
Filename reduced_dir("../../reduced", _dir->get_basename());
Filename reduced(reduced_dir, photo->get_basename());
Filename root_html("../..", "index.htm");
string up_href = root_html.get_fullpath() + "#" + _name;
string up_href = "../" + _dir->get_basename() + ".htm#" + _name;
Filename prev_photo_filename;
Filename next_photo_filename;
@ -485,7 +494,9 @@ generate_reduced_html(ostream &html, Photo *photo, int photo_index, int pi,
<< " height=" << photo->_reduced_y_size << " alt=\"" << photo->get_name()
<< "\"></a></p>\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
<< "<p><a href=\"" << full << "\">View full size image ("
<< photo->_full_x_size << " x " << photo->_full_y_size << ")</a></p>";
@ -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 << "</p>\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

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -25,6 +25,8 @@
#include "indexParameters.h"
#include "string_utils.h"
#include <algorithm>
////////////////////////////////////////////////////////////////////
// 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
<< "<html>\n"
<< "<head>\n"
<< "<title>" << _front_title << "</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 << "<p>\n";
if (!up_icon.empty()) {
// Use an icon to go up.
Filename up_icon_href = compose_href("..", up_icon);
complete_html
<< "<a href=\"../index.htm\"><img src=\"" << up_icon_href
<< "\" alt=\"return to index\"></a>\n";
} else {
// No up icon; use text to go up.
complete_html
<< "<br><a href=\"../index.htm\">Return to index</a>\n";
}
complete_html << "</p>\n";
complete_html
<< "</body>\n"
<< "</html>\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
<< "<html>\n"
<< "<head>\n"
<< "<title>" << _front_title << "</title>\n"
<< "</head>\n"
<< "<body>\n"
<< "<h1>" << _front_title << "</h1>\n"
<< "<ul>\n";
for (di = _roll_dirs.begin(); di != _roll_dirs.end(); ++di) {
RollDirectory *roll_dir = (*di);
if (!roll_dir->is_empty()) {
index_html
<< "<a name=\"" << roll_dir->get_basename() << "\">\n"
<< "<a href=\"html/" << roll_dir->get_basename() << ".htm\"><li>"
<< roll_dir->get_name() << " " << escape_html(roll_dir->get_desc())
<< "</li></a>\n";
}
}
root_html
index_html
<< "</ul>\n"
<< "<a href=\"html/complete.htm\">(complete archive)</a>\n"
<< "</body>\n"
<< "</html>\n";
}

View File

@ -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
<< "<a name=\"" << _basename << "\">\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
<< "<h2>" << _name << "</h2>\n";
if (!_desc.empty()) {
comment_strm << "<p>" << escape_html(_desc) << ".</p>\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 << "<p>\n";
ostringstream index_strm;
index_strm << "<p>\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 << "</p>\n";
index_strm << "</p>\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
<< "<html>\n"
<< "<head>\n";
if (_desc.empty()) {
index_html
<< "<title>" << _name << "</title>\n";
} else {
index_html
<< "<title>" << _name << " " << escape_html(_desc) << "</title>\n";
}
index_html
<< "</head>\n"
<< "<body>\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
<< "<a href=\"complete.htm#" << _basename << "\">(complete archive)</a>\n"
<< "</body>\n"
<< "</html>\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 << "<p>\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 << "<img src=\"" << prev_icon_href << "\" alt=\"No previous roll\">\n";
} else {
html << "<a href=\"" << prev_roll_filename
<< "\"><img src=\"" << prev_icon_href << "\" alt=\"previous\"></a>\n";
}
Filename next_icon_href = compose_href("..", next_icon);
if (next_roll_filename.empty()) {
html << "<img src=\"" << next_icon_href << "\" alt=\"No next roll\">\n";
} else {
html << "<a href=\"" << next_roll_filename
<< "\"><img src=\"" << next_icon_href << "\" alt=\"next\"></a>\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 << "<a href=\"" << prev_roll_filename
<< "\">Back to previous roll</a>\n";
}
if (next_roll_filename.empty()) {
html << "<br>(This is the last roll.)\n";
} else {
html << "<br><a href=\"" << next_roll_filename
<< "\">On to next roll</a>\n";
}
}
if (!up_href.empty()) {
if (!up_icon.empty()) {
// Use an icon to go up.
if (!first_icons) {
html << "<br>";
} else {
html << "&nbsp;&nbsp;&nbsp;";
}
Filename up_icon_href = compose_href("..", up_icon);
html << "<a href=\"" << up_href
<< "\"><img src=\"" << up_icon_href << "\" alt=\"return to index\"></a>\n";
} else {
// No up icon; use text to go up.
html << "<br><a href=\"" << up_href
<< "\">Return to index</a>\n";
}
}
html << "</p>\n";
}

View File

@ -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<Photo *> Photos;
Photos _photos;
@ -82,6 +92,9 @@ private:
typedef pvector<IndexImage *> IndexImages;
IndexImages _index_images;
string _comment_html;
string _index_html;
};
INLINE ostream &operator << (ostream &out, const RollDirectory &d) {