1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-05-02 09:09:46 +03:00
inform7/inform7/multimedia-module/Chapter 2/Figures.w

287 lines
9.4 KiB
OpenEdge ABL

[Figures::] Figures.
To register the names associated with picture resource numbers, which are defined
to allow the final story file to show illustrations.
@ The following is called to activate the feature:
=
void Figures::start(void) {
PluginCalls::plug(MAKE_SPECIAL_MEANINGS_PLUG, Figures::make_special_meanings);
PluginCalls::plug(NEW_BASE_KIND_NOTIFY_PLUG, Figures::figures_new_base_kind_notify);
PluginCalls::plug(NEW_INSTANCE_NOTIFY_PLUG, Figures::figures_new_named_instance_notify);
PluginCalls::plug(PRODUCTION_LINE_PLUG, Figures::production_line);
}
int Figures::production_line(int stage, int debugging, stopwatch_timer *sequence_timer) {
if (stage == INTER1_CSEQ) {
BENCH(RTMultimedia::compile_figures);
}
return FALSE;
}
@h One special meaning.
We add one special meaning for assertions, to catch sentences with the shape
"Figure... is the file...".
=
int Figures::make_special_meanings(void) {
SpecialMeanings::declare(Figures::new_figure_SMF, I"new-figure", 2);
return FALSE;
}
int Figures::new_figure_SMF(int task, parse_node *V, wording *NPs) {
wording SW = (NPs)?(NPs[0]):EMPTY_WORDING;
wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
switch (task) { /* "Figure... is the file..." */
case ACCEPT_SMFT:
if ((<nounphrase-figure>(SW)) && (<new-figure-sentence-object>(OW))) {
parse_node *O = <<rp>>;
<np-unparsed>(SW);
V->next = <<rp>>;
V->next->next = O;
return TRUE;
}
break;
case PASS_1_SMFT:
Figures::register_figure(Node::get_text(V->next),
Node::get_text(V->next->next));
break;
}
return FALSE;
}
@ And this is the Preform grammar needed:
=
<new-figure-sentence-object> ::=
<definite-article> <new-figure-sentence-object-unarticled> | ==> { pass 2 }
<new-figure-sentence-object-unarticled> ==> { pass 1 }
<new-figure-sentence-object-unarticled> ::=
file <np-unparsed> ==> { TRUE, RP[1] }
<nounphrase-figure> ::=
figure ... ==> { 0, Diagrams::new_UNPARSED_NOUN(W) }
<figure-sentence-object> ::=
<figure-source> ( <quoted-text> ) | ==> { R[1], -, <<alttext>> = R[2] }
<figure-source> ==> { pass 1 }
<figure-source> ::=
of cover art | ==> { -1, - }
<quoted-text> | ==> { pass 1 }
... ==> @<Issue PM_PictureNotTextual problem@>;
@<Issue PM_PictureNotTextual problem@> =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_PictureNotTextual),
"a figure can only be declared as a quoted file name",
"which should be the name of a JPEG or PNG image inside the "
"project's .materials folder. For instance, 'Figure 2 is the "
"file \"Crossed Swords.png\".'");
==> { 0, - };
@ This is a figure name which Inform provides special support for; it recognises
the English name when it is defined by the Standard Rules. (So there is no need
to translate this to other languages.)
=
<notable-figures> ::=
of cover art
@ In assertion pass 1, then, the following is called on any sentence which
has been found to create a figure:
=
void Figures::register_figure(wording W, wording FN) {
<<alttext>> = -1;
if (<figure-sentence-object>(FN)) {
int wn = <<r>>;
if (wn > 0) Word::dequote(wn);
if (<<alttext>> > 0) Word::dequote(<<alttext>>);
@<Make sure W is acceptable as a new figure name@>;
int id = 1; /* the ID of the cover art */
if (wn >= 0) id = Task::get_next_free_blorb_resource_ID();
TEMPORARY_TEXT(leaf)
WRITE_TO(leaf, "%N", wn);
if ((wn >= 0) && (Str::is_whitespace(leaf))) {
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_FigureWhiteSpace),
"this is not a filename I can use",
"because it is either empty or contains only spaces.");
return;
}
filename *figure_file = NULL;
if (wn >= 0) {
figure_file = ResourceFinder::find_resource(Task::figures_department(), leaf, FN);
}
DISCARD_TEXT(leaf)
if ((wn < 0) || (figure_file)) {
Figures::figures_create(W, id, figure_file, <<alttext>>);
LOGIF(MULTIMEDIA_CREATIONS, "Created figure <%W> = filename '%f' = resource ID %d\n",
W, figure_file, id);
}
}
}
@<Make sure W is acceptable as a new figure name@> =
Assertions::Creator::vet_name_for_noun(W);
if ((<s-value>(W)) &&
(Rvalues::is_CONSTANT_of_kind(<<rp>>, K_figure_name))) {
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_PictureDuplicate),
"this is already the name of a Figure",
"so there must be some duplication somewhere.");
return;
}
@h One significant kind.
= (early code)
kind *K_figure_name = NULL;
@ This is created by an Inter kit early in Inform's run; the function below
detects that this has happened, and sets |K_figure_name| to point to it.
=
int Figures::figures_new_base_kind_notify(kind *new_base, text_stream *name, wording W) {
if (Str::eq_wide_string(name, L"FIGURE_NAME_TY")) {
K_figure_name = new_base; return TRUE;
}
return FALSE;
}
@h Significant new instances.
This structure of additional data is attached to each figure instance:
=
typedef struct figures_data {
struct wording name; /* text of name */
struct filename *filename_of_image_file;
int figure_number; /* resource number of this picture inside Blorb */
int alt_description; /* word number of double-quoted description */
struct instance *as_instance;
CLASS_DEFINITION
} figures_data;
figures_data *F_cover_art = NULL;
@ We allow instances of "figure name" to be created only through the above
code calling //Figures::figures_create//. If any other proposition somehow
manages to make a figure, a problem message is thrown.
=
int allow_figure_creations = FALSE;
instance *Figures::figures_create(wording W, int id, filename *figure_file, int alt) {
allow_figure_creations = TRUE;
Assert::true(Propositions::Abstract::to_create_something(K_figure_name, W), CERTAIN_CE);
allow_figure_creations = FALSE;
instance *I = Instances::latest();
figures_data *figd = FEATURE_DATA_ON_INSTANCE(figures, I);
figd->filename_of_image_file = figure_file;
figd->name = W;
figd->figure_number = id;
figd->alt_description = alt;
figd->as_instance = I;
if (id == 1) F_cover_art = figd;
return I;
}
@ =
int Figures::figures_new_named_instance_notify(instance *I) {
if ((K_figure_name) && (Kinds::eq(Instances::to_kind(I), K_figure_name))) {
if (allow_figure_creations == FALSE)
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(PM_BackdoorFigureCreation),
"this is not the way to create a new figure name",
"which should be done with a special 'Figure ... is the file ...' "
"sentence.");
ATTACH_FEATURE_DATA_TO_SUBJECT(figures, I->as_subject, CREATE(figures_data));
return TRUE;
}
return FALSE;
}
@ The cover art is special, in that it always has ID number 1, and its
description appears in the iFiction metadata for a project.
=
wchar_t *Figures::description_of_cover_art(void) {
if ((F_cover_art == NULL) || (F_cover_art->alt_description == -1)) return L"";
return Lexer::word_text(F_cover_art->alt_description);
}
@h Blurb and manifest.
The picture manifest is used by the implementation of Glulx within the
Inform application to connect picture ID numbers with filenames relative
to the Materials folder for its project.
=
void Figures::write_picture_manifest(OUTPUT_STREAM, int include_cover,
char *cover_art_format) {
if (FEATURE_INACTIVE(figures)) return;
figures_data *figd;
WRITE("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
WRITE("<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
WRITE("<plist version=\"1.0\">\n"); INDENT;
WRITE("<dict>\n"); INDENT;
WRITE("<key>Graphics</key>\n");
WRITE("<dict>\n"); INDENT;
if (include_cover) {
WRITE("<key>1</key>\n");
filename *large = NULL;
if (strcmp(cover_art_format, "jpg") == 0)
large = Task::large_cover_art_file(TRUE);
else
large = Task::large_cover_art_file(FALSE);
WRITE("<string>%f</string>\n", large);
}
LOOP_OVER(figd, figures_data)
if (figd->figure_number > 1) {
WRITE("<key>%d</key>\n", figd->figure_number);
TEMPORARY_TEXT(rel)
Filenames::to_text_relative(rel, figd->filename_of_image_file,
Projects::materials_path(Task::project()));
WRITE("<string>%S</string>\n", rel);
DISCARD_TEXT(rel)
}
OUTDENT; WRITE("</dict>\n");
Sounds::write_sounds_manifest(OUT);
InternalFiles::write_files_manifest(OUT);
OUTDENT; WRITE("</dict>\n");
OUTDENT; WRITE("</plist>\n");
}
@ The following writes Blurb commands for all of the figures, but not for
the cover art, which is handled by Bibliographic Data.
=
void Figures::write_blurb_commands(OUTPUT_STREAM) {
if (FEATURE_INACTIVE(figures)) return;
figures_data *figd;
LOOP_OVER(figd, figures_data)
if (figd->figure_number > 1) {
wchar_t *desc = L"";
if (figd->alt_description >= 0)
desc = Lexer::word_text(figd->alt_description);
if (Wide::len(desc) > 0)
WRITE("picture %d \"%f\" \"%N\"\n", figd->figure_number,
figd->filename_of_image_file, figd->alt_description);
else
WRITE("picture %d \"%f\"\n",
figd->figure_number, figd->filename_of_image_file);
}
}
@ The following is used only with the "separate figures" release option.
=
void Figures::write_copy_commands(release_instructions *rel) {
if (FEATURE_INACTIVE(figures)) return;
figures_data *figd;
LOOP_OVER(figd, figures_data)
if (figd->figure_number > 1)
ReleaseInstructions::add_aux_file(rel, figd->filename_of_image_file,
Task::released_figures_path(), L"--", SEPARATE_FIGURES_PAYLOAD);
}