To generate the index page for the extension mini-website, which is the home page displayed in the Extensions tab for the Inform GUI apps.
void ExtensionIndex::write(inform_project *proj) { if (proj == NULL) internal_error("no project"); filename *F = ExtensionWebsite::cut_way_for_index_page(proj); if (F == NULL) return; linked_list *L = NEW_LINKED_LIST(inbuild_search_result); linked_list *U = NEW_LINKED_LIST(inbuild_copy); linked_list *R = NEW_LINKED_LIST(inbuild_requirement); int internals_used = 0, materials_used = 0, externals_used = 0; int internals_installed = 0, materials_installed = 0; See what we have installed and used1.1; text_stream HOMEPAGE_struct; text_stream *OUT = &HOMEPAGE_struct; if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE) return; InformPages::header(OUT, I"Extensions", JAVASCRIPT_FOR_ONE_EXTENSION_IRES, NULL); Write the body of the HTML1.2; InformPages::footer(OUT); STREAM_CLOSE(OUT); }
§1.1. See what we have installed and used1.1 =
linked_list *search_list = NEW_LINKED_LIST(inbuild_nest); inbuild_nest *materials = Projects::materials_nest(proj); if (materials) ADD_TO_LINKED_LIST(materials, inbuild_nest, search_list); inbuild_nest *internal = Supervisor::internal(); if (internal) ADD_TO_LINKED_LIST(internal, inbuild_nest, search_list); inbuild_requirement *req = Requirements::anything_of_genre(extension_bundle_genre); if (LinkedLists::len(search_list) > 0) Nests::search_for(req, search_list, L); ExtensionIndex::find_used_extensions(proj, U, R); inbuild_search_result *res; LOOP_OVER_LINKED_LIST(res, inbuild_search_result, L) { if (Nests::get_tag(res->nest) == INTERNAL_NEST_TAG) internals_installed++; else if (Nests::get_tag(res->nest) == MATERIALS_NEST_TAG) materials_installed++; } inbuild_copy *C; LOOP_OVER_LINKED_LIST(C, inbuild_copy, U) if (C->nest_of_origin) { switch (Nests::get_tag(C->nest_of_origin)) { case INTERNAL_NEST_TAG: internals_used++; break; case MATERIALS_NEST_TAG: materials_used++; break; default: externals_used++; Nests::add_search_result(L, C->nest_of_origin, C, req); break; } }
- This code is used in §1.
§1.2. Write the body of the HTML1.2 =
ExtensionWebsite::add_home_breadcrumb(I"Extensions in this Project"); ExtensionWebsite::titling_and_navigation(OUT, I"Those installed and those used"); HTML::begin_html_table(OUT, NULL, TRUE, 0, 4, 0, 0, 0); HTML::first_html_column(OUT, 0); HTML_TAG_WITH("img", "src='inform:/doc_images/extensions@2x.png' border=0 width=150 height=150"); HTML::next_html_column(OUT, 0); Display the location of installed extensions1.2.1; HTML::end_html_row(OUT); HTML::end_html_table(OUT); HTML_TAG("hr"); HTML_OPEN("p"); HTML_OPEN("b"); WRITE("The project '%S' currently uses...", proj->as_copy->edition->work->title); HTML_CLOSE("b"); HTML_CLOSE("p"); int usage_state = TRUE; Display an alphabetised directory1.2.2; HTML_TAG("hr"); if ((internals_used < internals_installed) || (materials_used < materials_installed)) { HTML_OPEN("p"); HTML_OPEN("b"); WRITE("These are available, but are not used by '%S'...", proj->as_copy->edition->work->title); HTML_CLOSE("b"); HTML_CLOSE("p"); usage_state = FALSE; Display an alphabetised directory1.2.2; }
- This code is used in §1.
§1.2.1. Display the location of installed extensions1.2.1 =
HTML_OPEN("p"); pathname *P = Nests::get_location(Projects::materials_nest(proj)); P = Pathnames::down(P, I"Extensions"); PasteButtons::open_file(OUT, P, NULL, PROJECT_SPECIFIC_SYMBOL); WRITE(" "); if (materials_installed > 0) { WRITE("%d extension%s installed in the .materials folder for the " "project '%S'", materials_installed, (materials_installed==1)?" is":"s are", proj->as_copy->edition->work->title); int i = materials_installed, u = materials_used; Say how many of those installed are used1.2.1.1; WRITE(" (Click the icon to show the location.)"); } else { WRITE("No extensions are installed in the .materials folder for the " "project '%S'. (Click the icon to show the location. " "Extensions should be put in the 'Extensions' subfolder of this: you " "can put them there yourself, or use the Inform app to install them " "for you.)", proj->as_copy->edition->work->title); } HTML_CLOSE("p"); HTML_OPEN("p"); HTML_TAG_WITH("img", BUILT_IN_SYMBOL); WRITE(" "); WRITE("The Inform app comes with a small number of built-in extensions, which " "you need not install, and which are automatically included if necessary. " "'%S' has access to %d", proj->as_copy->edition->work->title, internals_installed); int i = internals_installed, u = internals_used; Say how many of those installed are used1.2.1.1; HTML_CLOSE("p"); if (externals_used > 0) { HTML_OPEN("p"); HTML_TAG_WITH("img", LEGACY_AREA_SYMBOL); WRITE(" "); WRITE("And '%S' still uses %d extension%s from the legacy extensions area. Best " "practice is to install %s into .materials instead, which the Inform app " "can do for you.", proj->as_copy->edition->work->title, externals_used, (externals_used==1)?"":"s", (externals_used==1)?"it":"them"); HTML_CLOSE("p"); }
- This code is used in §1.2.
§1.2.1.1. Say how many of those installed are used1.2.1.1 =
if (u == 0) { if (i == 1) { WRITE(", but it doesn't use it."); } else if (i == 2) { WRITE(", but it doesn't use either of them."); } else { WRITE(", but it doesn't use any of them."); } } else if (u < i) { WRITE(", but it uses only %d.", u); } else if (u == 1) { WRITE(", and it uses it."); } else if (u == 2) { WRITE(", and it uses both of them."); } else { WRITE(", and it uses all of them."); }
- This code is used in §1.2.1 (twice).
§1.2.2. The following is an alphabetised directory of extensions by author and then title, along with some useful information about them, and then a list of any oddities found in the external extensions area.
Display an alphabetised directory1.2.2 =
linked_list *key_list = NEW_LINKED_LIST(extensions_key_item); int no_entries = LinkedLists::len(L); inbuild_search_result **sorted_census_results = Memory::calloc(no_entries, sizeof(inbuild_search_result *), RESULTS_SORTING_MREASON); int d = 3; int no_entries_in_set = 0; Sort the census into the appropriate order1.2.2.3; Display the sorted version of the census1.2.2.4; Print the key to any symbols used in the census lines1.2.2.1; Transcribe any census errors1.2.2.2; Memory::I7_array_free(sorted_census_results, RESULTS_SORTING_MREASON, no_entries, sizeof(inbuild_search_result *));
- This code is used in §1.2 (twice).
§1.2.2.1. Print the key to any symbols used in the census lines1.2.2.1 =
if (LinkedLists::len(key_list) > 0) ExtensionIndex::render_key(OUT, key_list);
- This code is used in §1.2.2.
§1.2.2.2. Census errors are nothing more than copy errors arising on the copies of extensions found by the census:
Transcribe any census errors1.2.2.2 =
int no_census_errors = 0; for (int i=0; i<no_entries_in_set; i++) { inbuild_search_result *res = sorted_census_results[i]; no_census_errors += LinkedLists::len(res->copy->errors_reading_source_text); } if (no_census_errors > 0) { Include the headnote explaining what census errors are1.2.2.2.1; for (int i=0; i<no_entries_in_set; i++) { inbuild_search_result *res = sorted_census_results[i]; if (LinkedLists::len(res->copy->errors_reading_source_text) > 0) { copy_error *CE; LOOP_OVER_LINKED_LIST(CE, copy_error, res->copy->errors_reading_source_text) { #ifdef INDEX_MODULE HTML::open_indented_p(OUT, 2, "hanging"); #endif #ifndef INDEX_MODULE HTML_OPEN("p"); #endif WRITE("<b>%X</b> - ", res->copy->edition->work); CopyErrors::write(OUT, CE); HTML_CLOSE("p"); } } } }
- This code is used in §1.2.2.
§1.2.2.2.1. We only want to warn people here: not to stop them from using Inform until they put matters right.
Include the headnote explaining what census errors are1.2.2.2.1 =
HTML_TAG("hr"); HTML_OPEN("p"); HTML_TAG_WITH("img", "border=0 align=\"left\" src=inform:/doc_images/census_problem.png"); WRITE("<b>Warning</b>. Inform checks the folder of user-installed extensions " "each time it translates the source text, in order to keep this directory " "page up to date. Each file must be a properly labelled extension (with " "its titling line correctly identifying itself), and must be in the right " "place - e.g. 'Marbles by Daphne Quilt' must have the filename 'Marbles.i7x' " "(or just 'Marbles' with no file extension) and be stored in the folder " "'Daphne Quilt'. The title should be at most %d characters long; the " "author name, %d. At the last check, these rules were not being followed:", MAX_EXTENSION_TITLE_LENGTH, MAX_EXTENSION_AUTHOR_LENGTH); HTML_CLOSE("p");
- This code is used in §1.2.2.2.
define SORT_CE_BY_TITLE 1
define SORT_CE_BY_AUTHOR 2
define SORT_CE_BY_LOCATION 3
define SORT_CE_BY_USAGE 4
Sort the census into the appropriate order1.2.2.3 =
int i = 0; inbuild_search_result *res; LOOP_OVER_LINKED_LIST(res, inbuild_search_result, L) { int found = FALSE; inbuild_copy *C; LOOP_OVER_LINKED_LIST(C, inbuild_copy, U) if (C == res->copy) { found = TRUE; break; } if (found == usage_state) sorted_census_results[i++] = res; } no_entries_in_set = i; int (*criterion)(const void *, const void *) = NULL; switch (d) { case SORT_CE_BY_TITLE: criterion = ExtensionIndex::compare_res_by_title; break; case SORT_CE_BY_AUTHOR: criterion = ExtensionIndex::compare_res_by_author; break; case SORT_CE_BY_LOCATION: criterion = ExtensionIndex::compare_res_by_location; break; case SORT_CE_BY_USAGE: criterion = ExtensionIndex::compare_res_by_title; break; default: internal_error("no such sorting criterion"); } qsort(sorted_census_results, (size_t) no_entries_in_set, sizeof(inbuild_search_result *), criterion);
- This code is used in §1.2.2.
§1.2.2.4. Standard rows have black text on striped background colours, these being the usual ones seen in Mac OS X applications such as iTunes.
Display the sorted version of the census1.2.2.4 =
HTML::begin_html_table(OUT, I"stripeone", TRUE, 0, 0, 2, 0, 0); Show a titling row explaining the census sorting, if necessary1.2.2.4.1; int stripe = 0; TEMPORARY_TEXT(current_author_name) for (int i=0; i<no_entries_in_set; i++) { inbuild_search_result *res = sorted_census_results[i]; Insert a subtitling row in the census sorting, if necessary1.2.2.4.2; stripe = 1 - stripe; if (stripe == 0) HTML::first_html_column_coloured(OUT, 0, I"stripetwo", 0); else HTML::first_html_column_coloured(OUT, 0, I"stripeone", 0); Print the census line for this extension1.2.2.4.4; HTML::end_html_row(OUT); if (stripe == 0) ExtensionIndex::first_html_column_wrapping(OUT, 0, I"stripetwo", 4, 48, 4); else ExtensionIndex::first_html_column_wrapping(OUT, 0, I"stripeone", 4, 48, 4); Print the rubric line for this extension1.2.2.4.5; HTML::end_html_row(OUT); } DISCARD_TEXT(current_author_name) Show a final titling row closing the census sorting1.2.2.4.3; HTML::end_html_table(OUT);
- This code is used in §1.2.2.
§1.2.2.4.1. Show a titling row explaining the census sorting, if necessary1.2.2.4.1 =
switch (d) { case SORT_CE_BY_TITLE: case SORT_CE_BY_USAGE: Begin a tinted census line1.2.2.4.1.1; WRITE("Extensions in alphabetical order"); End a tinted census line1.2.2.4.1.2; break; case SORT_CE_BY_LOCATION: Begin a tinted census line1.2.2.4.1.1; WRITE("Extensions grouped by location"); End a tinted census line1.2.2.4.1.2; break; }
- This code is used in §1.2.2.4.
§1.2.2.4.2. Insert a subtitling row in the census sorting, if necessary1.2.2.4.2 =
if ((d == SORT_CE_BY_AUTHOR) && (Str::ne(current_author_name, res->copy->edition->work->author_name))) { Str::copy(current_author_name, res->copy->edition->work->author_name); Begin a tinted census line1.2.2.4.1.1; Print the author's line in the extension census table1.2.2.4.2.1; End a tinted census line1.2.2.4.1.2; stripe = 0; }
- This code is used in §1.2.2.4.
§1.2.2.4.3. Show a final titling row closing the census sorting1.2.2.4.3 =
Begin a tinted census line1.2.2.4.1.1; HTML::begin_span(OUT, I"smaller"); WRITE("%d extension%s in total", no_entries_in_set, (no_entries_in_set == 1)?"":"s"); HTML::end_span(OUT); End a tinted census line1.2.2.4.1.2;
- This code is used in §1.2.2.4.
Begin a tinted census line1.2.2.4.1.1 =
HTML::first_html_column_coloured(OUT, 0, I"tintedrow", 4); HTML::begin_span(OUT, I"extensioncensusentry"); WRITE(" ");
- This code is used in §1.2.2.4.1 (twice), §1.2.2.4.2 and §1.2.2.4.3.
§1.2.2.4.1.2. End a tinted census line1.2.2.4.1.2 =
HTML::end_span(OUT); HTML::end_html_row(OUT);
- This code is used in §1.2.2.4.1 (twice), §1.2.2.4.2 and §1.2.2.4.3.
Print the author's line in the extension census table1.2.2.4.2.1 =
WRITE("%S", res->copy->edition->work->raw_author_name);
- This code is used in §1.2.2.4.2.
§1.2.2.4.4. Print the census line for this extension1.2.2.4.4 =
Print column 1 of the census line1.2.2.4.4.1; HTML::next_html_column_nw(OUT, 0); Print column 2 of the census line1.2.2.4.4.2; HTML::next_html_column_nw(OUT, 0); Print column 3 of the census line1.2.2.4.4.3; HTML::next_html_column_w(OUT, 0); Print column 4 of the census line1.2.2.4.4.4;
- This code is used in §1.2.2.4.
§1.2.2.4.4.1. Print column 1 of the census line1.2.2.4.4.1 =
inform_extension *E = Extensions::from_copy(res->copy); HTML::begin_span(OUT, I"extensionindexentry"); if (LinkedLists::len(res->copy->errors_reading_source_text) == 0) { source_location sl = Extensions::top_line_location(E); if (sl.file_of_origin) { ExtensionIndex::add_to_key(key_list, REVEAL_SYMBOL, I"See source text"); SourceLinks::link(OUT, sl, FALSE); WRITE(" "); } } TEMPORARY_TEXT(link) TEMPORARY_TEXT(URL) filename *F = ExtensionWebsite::page_filename(proj, res->copy->edition, -1); if (TextFiles::exists(F)) { WRITE_TO(URL, "%f", ExtensionWebsite::page_filename_relative_to_materials(res->copy->edition, -1)); WRITE_TO(link, "href='inform:/"); Works::escape_apostrophes(link, URL); WRITE_TO(link, "' style=\"text-decoration: none\""); } if (Str::len(link) > 0) { HTML_OPEN_WITH("a", "%S class=\"registrycontentslink\"", link); } if (d != SORT_CE_BY_AUTHOR) { WRITE("%S", res->copy->edition->work->raw_title); if (Nests::get_tag(res->nest) != INTERNAL_NEST_TAG) WRITE(" by %S", res->copy->edition->work->raw_author_name); } else { WRITE("%S", res->copy->edition->work->raw_title); } if (Str::len(link) > 0) { HTML_CLOSE("a"); } HTML::end_span(OUT); if (Str::len(link) > 0) { WRITE(" "); HTML_OPEN_WITH("a", "%S", link); HTML_TAG_WITH("img", "%s", HELP_SYMBOL); ExtensionIndex::add_to_key(key_list, HELP_SYMBOL, I"Documentation (click to read)"); HTML_CLOSE("a"); } parse_node *at = Extensions::get_inclusion_sentence(E); if (at) { wording W = Node::get_text(at); source_location sl = Lexer::word_location(Wordings::first_wn(W)); if (sl.file_of_origin) { WRITE(" "); HTML_OPEN("em"); WRITE("— included here: "); HTML_CLOSE("em"); WRITE(" "); SourceLinks::link(OUT, sl, TRUE); ExtensionIndex::add_to_key(key_list, REVEAL_SYMBOL, I"Open source (left of title: the whole extension; right: where it is Included"); } } if (LinkedLists::len(res->copy->errors_reading_source_text) > 0) { WRITE(" "); HTML_TAG_WITH("img", "%s", PROBLEM_SYMBOL); ExtensionIndex::add_to_key(key_list, PROBLEM_SYMBOL, I"Has errors (see below)"); } else { if (usage_state == FALSE) { WRITE(" "); TEMPORARY_TEXT(inclusion_text) WRITE_TO(inclusion_text, "Include %X.\n\n\n", res->copy->edition->work); PasteButtons::paste_text_new_style(OUT, inclusion_text); DISCARD_TEXT(inclusion_text) ExtensionIndex::add_to_key(key_list, PASTE_SYMBOL, I"Source text to Include this (click to paste in)"); if (Nests::get_tag(res->nest) == MATERIALS_NEST_TAG) { WRITE(" "); ExtensionInstaller::uninstall_button(OUT, proj, res->copy); ExtensionIndex::add_to_key(key_list, UNINSTALL_SYMBOL, I"Remove from this project"); WRITE(" "); } } else { if (Nests::get_tag(res->nest) == EXTERNAL_NEST_TAG) { WRITE(" "); ExtensionInstaller::install_button(OUT, proj, res->copy); WRITE(" "); ExtensionIndex::add_to_key(key_list, INSTALL_SYMBOL, I"Install to this project"); } } if (res->copy->location_if_file) { if (Nests::get_tag(res->nest) == MATERIALS_NEST_TAG) { WRITE(" "); ExtensionInstaller::modernise_button(OUT, proj, res->copy); WRITE(" "); ExtensionIndex::add_to_key(key_list, MODERNISE_SYMBOL, I"Convert this extension to the more modern directory format"); } } } DISCARD_TEXT(link) DISCARD_TEXT(URL)
- This code is used in §1.2.2.4.4.
§1.2.2.4.4.2. Print column 2 of the census line1.2.2.4.4.2 =
HTML::begin_span(OUT, I"smaller"); if (VersionNumbers::is_null(res->copy->edition->version) == FALSE) WRITE("v %v", &(res->copy->edition->version)); else WRITE("--"); HTML::end_span(OUT);
- This code is used in §1.2.2.4.4.
§1.2.2.4.4.3. Print column 3 of the census line1.2.2.4.4.3 =
char *opener = NULL; if (Nests::get_tag(res->nest) == INTERNAL_NEST_TAG) { opener = BUILT_IN_SYMBOL; ExtensionIndex::add_to_key(key_list, BUILT_IN_SYMBOL, I"Built in"); } else if (Nests::get_tag(res->nest) == MATERIALS_NEST_TAG) { opener = PROJECT_SPECIFIC_SYMBOL; ExtensionIndex::add_to_key(key_list, PROJECT_SPECIFIC_SYMBOL, I"Installed in .materials"); } else { opener = LEGACY_AREA_SYMBOL; ExtensionIndex::add_to_key(key_list, LEGACY_AREA_SYMBOL, I"Used from legacy extensions area"); } if (Nests::get_tag(res->nest) == INTERNAL_NEST_TAG) HTML_TAG_WITH("img", "%s", opener) else { #ifdef INDEX_MODULE pathname *area = ExtensionManager::path_within_nest(res->nest); PasteButtons::open_file(OUT, area, res->copy->edition->work->raw_author_name, opener); #endif }
- This code is used in §1.2.2.4.4.
§1.2.2.4.4.4. Print column 4 of the census line1.2.2.4.4.4 =
HTML::begin_span(OUT, I"smaller"); if (d == SORT_CE_BY_LOCATION) { if (Nests::get_tag(res->nest) == INTERNAL_NEST_TAG) WRITE("Built in to Inform"); else if (Nests::get_tag(res->nest) == EXTERNAL_NEST_TAG) WRITE("Uninstalled"); else WRITE("Installed in this project"); } else { text_stream *R = Extensions::get_rubric(Extensions::from_copy(res->copy)); if (Str::len(R) > 0) WRITE("%S", R); else WRITE("--"); } HTML::end_span(OUT);
- This code is used in §1.2.2.4.4.
§1.2.2.4.5. Print the rubric line for this extension1.2.2.4.5 =
HTML::begin_span(OUT, I"smaller"); text_stream *R = Extensions::get_rubric(Extensions::from_copy(res->copy)); if (Str::len(R) > 0) InformFlavouredMarkdown::render_text(OUT, R); compatibility_specification *C = res->copy->edition->compatibility; if (Str::len(C->parsed_from) > 0) { if (Str::len(R) > 0) WRITE(" "); HTML_OPEN("b"); TEMPORARY_TEXT(proviso) WRITE_TO(proviso, "%S", C->parsed_from); if ((Str::get_first_char(proviso) == '(') && (Str::get_last_char(proviso) == ')')) { Str::delete_first_character(proviso); Str::delete_last_character(proviso); } InformFlavouredMarkdown::render_text(OUT, proviso); DISCARD_TEXT(proviso) HTML_CLOSE("b"); } HTML::end_span(OUT);
- This code is used in §1.2.2.4.
void ExtensionIndex::first_html_column_wrapping(OUTPUT_STREAM, int width, text_stream *classname, int cs, int left_padding, int bottom_padding) { if (Str::len(classname) > 0) HTML_OPEN_WITH("tr", "class=\"%S\"", classname) else HTML_OPEN("tr"); TEMPORARY_TEXT(col) WRITE_TO(col, "align=\"left\" valign=\"top\""); if (width > 0) WRITE_TO(col, " width=\"%d\"", width); if (cs > 0) WRITE_TO(col, " colspan=\"%d\"", cs); if ((left_padding > 0) || (bottom_padding > 0)) { WRITE_TO(col, " style=\""); if (left_padding > 0) WRITE_TO(col, "padding-left: %dpx;", left_padding); if (bottom_padding > 0) WRITE_TO(col, "padding-bottom: %dpx;", bottom_padding); WRITE_TO(col, "\""); } HTML_OPEN_WITH("td", "%S", col); DISCARD_TEXT(col) }
define PROBLEM_SYMBOL "border=\"0\" height=\"12\" src=\"inform:/doc_images/census_problem.png\""
define REVEAL_SYMBOL "border=\"0\" src=\"inform:/doc_images/Reveal.png\""
define HELP_SYMBOL "border=\"0\" src=\"inform:/doc_images/help.png\""
define PASTE_SYMBOL "paste"
define INSTALL_SYMBOL "install"
define UNINSTALL_SYMBOL "uninstall"
define MODERNISE_SYMBOL "modernise"
define BUILT_IN_SYMBOL "border=\"0\" src=\"inform:/doc_images/builtin_ext.png\""
define PROJECT_SPECIFIC_SYMBOL "border=\"0\" src=\"inform:/doc_images/folder4.png\""
define LEGACY_AREA_SYMBOL "border=\"0\" src=\"inform:/doc_images/pspec_ext.png\""
classdef extensions_key_item { struct text_stream *image_URL; struct text_stream *gloss; int displayed; struct text_stream *ideograph; } void ExtensionIndex::add_to_key(linked_list *L, char *URL, text_stream *gloss) { TEMPORARY_TEXT(as_text) WRITE_TO(as_text, "%s", URL); int found = FALSE; extensions_key_item *eki; LOOP_OVER_LINKED_LIST(eki, extensions_key_item, L) if (Str::eq(eki->image_URL, as_text)) found = TRUE; if (found == FALSE) { eki = CREATE(extensions_key_item); eki->image_URL = Str::duplicate(as_text); eki->gloss = Str::duplicate(gloss); eki->displayed = FALSE; eki->ideograph = Str::new(); text_stream *OUT = eki->ideograph; if (Str::eq(as_text, I"paste")) { HTML_OPEN_WITH("span", "class=\"deadactionbutton\""); PasteButtons::paste_ideograph(OUT); HTML_CLOSE("span"); } if (Str::eq(as_text, I"install")) { HTML_OPEN_WITH("span", "class=\"deadactionbutton\""); ExtensionInstaller::install_icon(OUT); HTML_CLOSE("span"); } if (Str::eq(as_text, I"uninstall")) { HTML_OPEN_WITH("span", "class=\"deadactionbutton\""); ExtensionInstaller::uninstall_icon(OUT); HTML_CLOSE("span"); } if (Str::eq(as_text, I"modernise")) { HTML_OPEN_WITH("span", "class=\"deadactionbutton\""); ExtensionInstaller::modernise_icon(OUT); HTML_CLOSE("span"); } ADD_TO_LINKED_LIST(eki, extensions_key_item, L); } DISCARD_TEXT(as_text) } void ExtensionIndex::render_key(OUTPUT_STREAM, linked_list *L) { HTML_OPEN("p"); WRITE("Key: "); char *sequence[] = { PROJECT_SPECIFIC_SYMBOL, BUILT_IN_SYMBOL, LEGACY_AREA_SYMBOL, HELP_SYMBOL, REVEAL_SYMBOL, PASTE_SYMBOL, PROBLEM_SYMBOL, NULL }; for (int i=0; sequence[i] != NULL; i++) { TEMPORARY_TEXT(as_text) WRITE_TO(as_text, "%s", sequence[i]); extensions_key_item *eki; LOOP_OVER_LINKED_LIST(eki, extensions_key_item, L) { if (Str::eq(eki->image_URL, as_text)) { ExtensionIndex::render_icon(OUT, eki); WRITE(" %S ", eki->gloss); eki->displayed = TRUE; } } DISCARD_TEXT(as_text) } extensions_key_item *eki; LOOP_OVER_LINKED_LIST(eki, extensions_key_item, L) { if (eki->displayed == FALSE) { ExtensionIndex::render_icon(OUT, eki); WRITE(" %S ", eki->gloss); eki->displayed = TRUE; } } HTML_CLOSE("p"); } void ExtensionIndex::render_icon(OUTPUT_STREAM, extensions_key_item *eki) { if (Str::len(eki->ideograph) > 0) { WRITE("%S", eki->ideograph); } else { HTML_TAG_WITH("img", "%S", eki->image_URL); } }
- The structure extensions_key_item is private to this section.
void ExtensionIndex::find_used_extensions(inform_project *proj, linked_list *U, linked_list *R) { build_vertex *V = Copies::construct_project_graph(proj->as_copy); ExtensionIndex::find_used_extensions_r(V, Graphs::get_unique_graph_scan_count(), U, R); } void ExtensionIndex::find_used_extensions_r(build_vertex *V, int scan_count, linked_list *U, linked_list *R) { if (V->type == COPY_VERTEX) { inbuild_copy *C = V->as_copy; if ((C->edition->work->genre == extension_genre) || (C->edition->work->genre == extension_bundle_genre)) { if (C->last_scanned != scan_count) { ADD_TO_LINKED_LIST(C, inbuild_copy, U); C->last_scanned = scan_count; } } } if (V->type == REQUIREMENT_VERTEX) { if ((V->as_requirement->work->genre == extension_genre) || (V->as_requirement->work->genre == extension_bundle_genre)) { ADD_TO_LINKED_LIST(V->as_requirement, inbuild_requirement, R); } } build_vertex *W; LOOP_OVER_LINKED_LIST(W, build_vertex, V->build_edges) ExtensionIndex::find_used_extensions_r(W, scan_count, U, R); LOOP_OVER_LINKED_LIST(W, build_vertex, V->use_edges) ExtensionIndex::find_used_extensions_r(W, scan_count, U, R); }
§5. Sorting criteria. The following give some sorting criteria, and are functions fit to be
handed to qsort.
int ExtensionIndex::compare_res_by_title(const void *res1, const void *res2) { inbuild_search_result *e1 = *((inbuild_search_result **) res1); inbuild_search_result *e2 = *((inbuild_search_result **) res2); inform_extension *E1 = Extensions::from_copy(e1->copy); inform_extension *E2 = Extensions::from_copy(e2->copy); return Extensions::compare_by_title(E2, E1); } int ExtensionIndex::compare_res_by_author(const void *res1, const void *res2) { inbuild_search_result *e1 = *((inbuild_search_result **) res1); inbuild_search_result *e2 = *((inbuild_search_result **) res2); inform_extension *E1 = Extensions::from_copy(e1->copy); inform_extension *E2 = Extensions::from_copy(e2->copy); return Extensions::compare_by_author(E2, E1); } int ExtensionIndex::compare_res_by_location(const void *res1, const void *res2) { inbuild_search_result *e1 = *((inbuild_search_result **) res1); inbuild_search_result *e2 = *((inbuild_search_result **) res2); int d = Nests::get_tag(e1->nest) - Nests::get_tag(e2->nest); if (d != 0) return d; return ExtensionIndex::compare_res_by_title(res1, res2); }