Compare commits

...

12 Commits

Author SHA1 Message Date
Matt Mascarenhas 14dafa4abe cinera.c: Fix stack-use-after-return segfault
The ClashResolver, or a memory_book variable therein, triggered a
stack-use-after-return segfault. Fixed by initialising the ClashResolver
in main() and passing it to InitClashResolver() by pointer, rather than
initialising it in InitClashResolver().
2024-03-12 13:03:08 +00:00
Matt Mascarenhas 213bb2f882 cinera.c: Fix colouring of topic dots
The colour computed for topic dots and put into cinera_topics.css in HSL
format has its lightness value modified depending on whether it's on a
dark or light background. Web browsers do not tell us an element's
computed colour in HSL format, but in RGB, so we would need to convert
it from RGB to HSL when setting the lightness. Previously, this
conversion was busted, calculating too small a value for the saturation.

This commit obviates the need for any RGB→HSL conversion by writing the
hue and saturation values as attributes to the elements, which the
function responsible for setting the lightness may use directly.
2024-02-21 20:52:34 +00:00
Matt Mascarenhas 77aec74483 cinera_player_post.js: Fix fullscreen handling
It is possible to bring our player out of fullscreen mode without using
the provided views menu or keyboard shortcut, e.g. by pressing Escape.
Doing so leaves our state in the SUPERTHEATRE view, which omits
SUPERtheatre mode itself from the views menu. This commit fixes this by
switching our state to the THEATRE view.

Thanks to Aske Bisgaard Vammen for the report.
2024-01-30 15:41:30 +00:00
Matt Mascarenhas 6852d06e04 cinera_player_pre.js: Unset focused menu
When hiding any menu, set this.MenusFocused.MenuID = menu_id.UNSET, to
let hovering over the markers focus them after hiding the link menu.
2023-03-25 01:51:40 +00:00
Matt Mascarenhas 52d6d989f8 cinera_player_pre.js: Fix scoping of "this" 2023-03-25 01:31:13 +00:00
Matt Mascarenhas df93674bf7 cinera: Increase precision to milliseconds
This commit simply adds millisecond precision of timecodes. It includes
small changes to hmmlib.h, cinera.c and the frontend JS files.
2023-03-25 00:04:34 +00:00
Matt Mascarenhas 026585e50b cinera: Fix mobile scrolling and centring
This commit fixes spurious scrolling in DeriveReliableWindowDimensions()
on mobile. It also fixes centring of Vimeo videos on mobile.

In addition, it replaces some deprecated JavaScript:
•   window.orientation → screen.orientation.type
•   window.onorientationchange → screen.orientation.onchange
2023-03-21 19:34:24 +00:00
Matt Mascarenhas 9d5f0f9146 cinera_player_pre.js: Fix scoping of "this" 2023-03-18 01:54:42 +00:00
Matt Mascarenhas a7694d4c3b cinera: Improve keyboard controls and scrolling
This commit adds keyboard navigation of the indices, documented in the
"Help" text box. It also improves scrolling of menus to follow progress
through the video, with the ability to centre the scrolling around a
range of references. Finally in this UI work, it enables the keyboard
and mouse to work more cooperatively.

Other changes:

• Added a "Clear" so the player's initial sizing happens invisibly.
• Fixed getBackgroundColourRGB() to handle both rgb() and rgba().
• Deduplicated code, including spurious querySelectorAll() calls.
• Moved global variables into the Player object.
2023-03-18 01:27:05 +00:00
Matt Mascarenhas c9bf96c7aa cinera_player_pre.js: Improve sizing and scrolling
This commit fixes the sizing of the player's menus to not protrude below
the player.

It also adds scrolling of the references menu to the first menu item
cited in the newly-current timestamp, and fixes scrolling of the markers
container when switching to a timestamp after the user has scrolled the
container away from its default position.
2023-02-20 20:29:16 +00:00
Matt Mascarenhas 44a5008aa7 cinera: Resolve output location clashes
This commit adds support for tracking and resolving clashes of output
locations. Previously open-ended clash dependency chains could passively
resolve across multiple Cinera invocations (undesirable, but possible);
whereas closed-loop dependency chains could not resolve without the user
manually resolving them by moving one clash to a temporary location (not
acceptable). This new support handles such clashes in one fell swoop.

Other smaller changes in this commit:

•   Change message_control to use memory_book
•   Fix deletion of old .index files when reorganising projects
•   Fix deletion of moved entries when subdividing projects
2023-02-10 23:12:21 +00:00
Matt Mascarenhas 6576a91d11 cinera: Support message deduplication
This commit adds support for deduplication of messages emitted by
PrintEdit(). It's a first pass, only supporting single-line messages and
ones no larger than 512 bytes.

It also fixes StringContains() to skip strings shorter than the search
substring, and emits a more sensible error on empty "output" value.
2023-01-13 20:31:44 +00:00
8 changed files with 3689 additions and 2313 deletions

File diff suppressed because it is too large Load Diff

View File

@ -540,6 +540,16 @@ ul.cineraNavPlain li.current > a {
display: block; display: block;
} }
.cineraHelp .help_container .help_custom_index {
background-color: #159 !important;
}
.cineraHelp .help_container h2 .help_title_key {
padding: .2em;
border: 1px solid;
border-radius: 4px;
}
.cineraHelp .help_container .help_key { .cineraHelp .help_container .help_key {
box-sizing: content-box; box-sizing: content-box;
font-family: Inconsolata; font-family: Inconsolata;
@ -614,12 +624,11 @@ ul.cineraNavPlain li.current > a {
.cineraMenus > .menu .credits_container { .cineraMenus > .menu .credits_container {
border: 1px solid; border: 1px solid;
border-top: none; border-top: none;
display: none; z-index: -1; /* NOTE(matt): Using "display: none" to hide them proved problematic for scrolling non-visible menus */
overflow-y: auto; overflow-y: auto;
position: absolute; position: absolute;
right: 0; right: 0;
top: 100%; top: 100%;
z-index: 8;
} }
.cineraMenus > .menu .refs, .cineraMenus > .menu .refs,
@ -639,7 +648,7 @@ ul.cineraNavPlain li.current > a {
} }
.cineraMenus > .menu .visible { .cineraMenus > .menu .visible {
display: block; z-index: 8;
} }
.cineraMenus > .menu > .refs .ref { .cineraMenus > .menu > .refs .ref {
@ -737,6 +746,11 @@ ul.cineraNavPlain li.current > a {
margin-right: 4px; margin-right: 4px;
} }
.cineraMenus > .menu > .filter_container .filter_header {
position: sticky;
top: 0;
}
.cineraMenus > .menu > .filter_container .filter_mode, .cineraMenus > .menu > .filter_container .filter_mode,
.cineraMenus > .menu > .link_container #cineraLinkMode { .cineraMenus > .menu > .link_container #cineraLinkMode {
cursor: pointer; cursor: pointer;
@ -756,11 +770,13 @@ ul.cineraNavPlain li.current > a {
text-align: center; text-align: center;
} }
.cineraMenus > .menu > .filter_container .filter_header .filter_titles,
.cineraMenus > .menu > .filter_container .filters { .cineraMenus > .menu > .filter_container .filters {
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
} }
.cineraMenus > .menu > .filter_container .filter_header .filter_titles > *,
.cineraMenus > .menu > .filter_container .filters > * { .cineraMenus > .menu > .filter_container .filters > * {
width: 50%; width: 50%;
flex-grow: 1; flex-grow: 1;

View File

@ -113,20 +113,20 @@ MakeString_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
int FormatLength = StringLength(Format); int FormatLength = StringLength(Format);
if(FormatLength != ArgCount) if(FormatLength != ArgCount)
{ {
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d MakeString()", Filename, LineNumber); Colourise(CS_END); Colourise(CS_FAILURE); Print(stderr, "%s:%d MakeString()", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr, Print(stderr,
"\n" "\n"
" MakeString() has been passed a format string containing "); " MakeString() has been passed a format string containing ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END); Colourise(CS_BLUE_BOLD); Print(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr, Print(stderr,
",\n" ",\n"
" and "); " and ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END); Colourise(CS_BLUE_BOLD); Print(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr, ", but these numbers should be equal.\n"); Print(stderr, ", but these numbers should be equal.\n");
__asm__("int3"); __asm__("int3");
} }
@ -161,8 +161,8 @@ MakeString_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
} break; } break;
default: default:
{ {
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END); Colourise(CS_FAILURE); Print(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr, "\n" Print(stderr, "\n"
" MakeString() has been passed an unsupported format specifier: %c\n", Format[i]); " MakeString() has been passed an unsupported format specifier: %c\n", Format[i]);
__asm__("int3"); __asm__("int3");
} break; } break;
@ -197,20 +197,20 @@ MakeString0_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
int FormatLength = StringLength(Format); int FormatLength = StringLength(Format);
if(FormatLength != ArgCount) if(FormatLength != ArgCount)
{ {
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d MakeString0()", Filename, LineNumber); Colourise(CS_END); Colourise(CS_FAILURE); Print(stderr, "%s:%d MakeString0()", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr, Print(stderr,
"\n" "\n"
" MakeString0() has been passed a format string containing "); " MakeString0() has been passed a format string containing ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END); Colourise(CS_BLUE_BOLD); Print(stderr, "%d specifier%s", FormatLength, FormatLength == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr, Print(stderr,
",\n" ",\n"
" and "); " and ");
Colourise(CS_BLUE_BOLD); fprintf(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END); Colourise(CS_BLUE_BOLD); Print(stderr, "%d argument%s", ArgCount, ArgCount == 1 ? "" : "s"); Colourise(CS_END);
fprintf(stderr, ", but these numbers should be equal.\n"); Print(stderr, ", but these numbers should be equal.\n");
__asm__("int3"); __asm__("int3");
} }
@ -264,8 +264,8 @@ MakeString0_(char *Filename, int LineNumber, char *Format, int ArgCount, ...)
} break; } break;
default: default:
{ {
Colourise(CS_FAILURE); fprintf(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END); Colourise(CS_FAILURE); Print(stderr, "%s:%d", Filename, LineNumber); Colourise(CS_END);
fprintf(stderr, "\n" Print(stderr, "\n"
" MakeString0() has been passed an unsupported format specifier: %c\n", Format[i]); " MakeString0() has been passed an unsupported format specifier: %c\n", Format[i]);
__asm__("int3"); __asm__("int3");
} break; } break;
@ -304,11 +304,11 @@ PushToken(tokens *Set, token *Token)
void void
PrintToken(token *T, uint64_t Index) PrintToken(token *T, uint64_t Index)
{ {
fprintf(stderr, "[%lu, %s] ", Index, TokenTypeStrings[T->Type]); Print(stderr, "[%lu, %s] ", Index, TokenTypeStrings[T->Type]);
if(T->Type == TOKEN_NUMBER) if(T->Type == TOKEN_NUMBER)
{ {
Colourise(CS_BLUE_BOLD); Colourise(CS_BLUE_BOLD);
fprintf(stderr, "%li", T->int64_t); Print(stderr, "%li", T->int64_t);
Colourise(CS_END); Colourise(CS_END);
} }
@ -320,7 +320,7 @@ PrintToken(token *T, uint64_t Index)
{ {
PrintString(T->Content); PrintString(T->Content);
} }
fprintf(stderr, "\n"); Print(stderr, "\n");
} }
void void
@ -1094,7 +1094,7 @@ PrintTypeField(config_type_field *F, config_identifier_id ParentScopeID, int Ind
} }
else{ else{
Colourise(CS_GREEN_BOLD); Colourise(CS_GREEN_BOLD);
fprintf(stderr, "\"%s\"", FieldTypeNames[F->Type]); Print(stderr, "\"%s\"", FieldTypeNames[F->Type]);
Colourise(CS_END); Colourise(CS_END);
} }
PrintC(CS_BLACK_BOLD, ""); PrintC(CS_BLACK_BOLD, "");
@ -1190,7 +1190,7 @@ PrintTypeSpec(config_type_spec *S, int IndentationLevel)
void void
PrintTypeSpecs(memory_book *TypeSpecs, int IndentationLevel) PrintTypeSpecs(memory_book *TypeSpecs, int IndentationLevel)
{ {
fprintf(stderr, "\n"); Print(stderr, "\n");
// TODO(matt): Document the fact that scopes within scopes are fully configurable, even though // TODO(matt): Document the fact that scopes within scopes are fully configurable, even though
// we only print out the "id" of scopes, omitting their containing fields // we only print out the "id" of scopes, omitting their containing fields
for(int i = 0; i < TypeSpecs->ItemCount; ++i) for(int i = 0; i < TypeSpecs->ItemCount; ++i)
@ -1453,28 +1453,28 @@ FreeScopeTree(scope_tree *T)
void void
PrintPair(config_pair *P, char *Delimiter, bool FillSyntax, uint64_t Indentation, bool PrependNewline, bool AppendNewline) PrintPair(config_pair *P, char *Delimiter, bool FillSyntax, uint64_t Indentation, bool PrependNewline, bool AppendNewline)
{ {
if(PrependNewline) { fprintf(stderr, "\n"); } if(PrependNewline) { Print(stderr, "\n"); }
Indent(Indentation); Indent(Indentation);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[P->Key].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[P->Key].String);
fprintf(stderr, "%s", Delimiter); Print(stderr, "%s", Delimiter);
if(P->Type == PT_BOOL) if(P->Type == PT_BOOL)
{ {
if(P->bool == TRUE) if(P->bool == TRUE)
{ {
Colourise(CS_GREEN); Colourise(CS_GREEN);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
fprintf(stderr, "true"); Print(stderr, "true");
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
} }
else else
{ {
Colourise(CS_RED); Colourise(CS_RED);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
fprintf(stderr, "false"); Print(stderr, "false");
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
} }
Colourise(CS_END); Colourise(CS_END);
if(FillSyntax) { fprintf(stderr, ";"); } if(FillSyntax) { Print(stderr, ";"); }
} }
else if(P->Type == PT_INT64) else if(P->Type == PT_INT64)
{ {
@ -1483,68 +1483,68 @@ PrintPair(config_pair *P, char *Delimiter, bool FillSyntax, uint64_t Indentation
case IDENT_GENRE: case IDENT_GENRE:
{ {
Colourise(CS_GREEN_BOLD); Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
fprintf(stderr, "%s", GenreStrings[P->int64_t]); Print(stderr, "%s", GenreStrings[P->int64_t]);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
} break; } break;
case IDENT_LOG_LEVEL: case IDENT_LOG_LEVEL:
{ {
Colourise(CS_GREEN_BOLD); Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
fprintf(stderr, "%s", LogLevelStrings[P->int64_t]); Print(stderr, "%s", LogLevelStrings[P->int64_t]);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
} break; } break;
case IDENT_NUMBERING_SCHEME: case IDENT_NUMBERING_SCHEME:
{ {
Colourise(CS_GREEN_BOLD); Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
fprintf(stderr, "%s", NumberingSchemeStrings[P->int64_t]); Print(stderr, "%s", NumberingSchemeStrings[P->int64_t]);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
} break; } break;
default: default:
{ {
Colourise(CS_BLUE_BOLD); Colourise(CS_BLUE_BOLD);
fprintf(stderr, "%li", P->int64_t); Print(stderr, "%li", P->int64_t);
} break; } break;
} }
Colourise(CS_END); Colourise(CS_END);
if(FillSyntax) { fprintf(stderr, ";"); } if(FillSyntax) { Print(stderr, ";"); }
} }
else if(P->Type == PT_STRING) else if(P->Type == PT_STRING)
{ {
if(P->String.Length) if(P->String.Length)
{ {
Colourise(CS_GREEN_BOLD); Colourise(CS_GREEN_BOLD);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
PrintString(P->String); PrintString(P->String);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
Colourise(CS_END); Colourise(CS_END);
if(FillSyntax) { fprintf(stderr, ";"); } if(FillSyntax) { Print(stderr, ";"); }
} }
else else
{ {
PrintC(CS_YELLOW, "[unset]"); PrintC(CS_YELLOW, "[unset]");
} }
} }
if(AppendNewline) { fprintf(stderr, "\n"); } if(AppendNewline) { Print(stderr, "\n"); }
} }
void void
PrintScopeID(config_pair *P, char *Delimiter, uint64_t Indentation) PrintScopeID(config_pair *P, char *Delimiter, uint64_t Indentation)
{ {
fprintf(stderr, "\n"); Print(stderr, "\n");
Indent(Indentation); Indent(Indentation);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[P->Key].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[P->Key].String);
fprintf(stderr, "%s", Delimiter); Print(stderr, "%s", Delimiter);
if(P->String.Length) if(P->String.Length)
{ {
Colourise(CS_GREEN_BOLD); Colourise(CS_GREEN_BOLD);
fprintf(stderr, "\""); Print(stderr, "\"");
PrintString(P->String); PrintString(P->String);
fprintf(stderr, "\""); Print(stderr, "\"");
Colourise(CS_END); Colourise(CS_END);
IndentedCarriageReturn(Indentation); IndentedCarriageReturn(Indentation);
fprintf(stderr, "{"); Print(stderr, "{");
} }
else else
{ {
@ -1555,26 +1555,26 @@ PrintScopeID(config_pair *P, char *Delimiter, uint64_t Indentation)
void void
PrintBool(char *Title, bool Bool, char *Delimiter, bool FillSyntax, uint64_t Indentation, bool PrependNewline, bool AppendNewline) PrintBool(char *Title, bool Bool, char *Delimiter, bool FillSyntax, uint64_t Indentation, bool PrependNewline, bool AppendNewline)
{ {
if(PrependNewline) { fprintf(stderr, "\n"); } if(PrependNewline) { Print(stderr, "\n"); }
Indent(Indentation); Indent(Indentation);
PrintC(CS_YELLOW_BOLD, Title); PrintC(CS_YELLOW_BOLD, Title);
fprintf(stderr, "%s", Delimiter); Print(stderr, "%s", Delimiter);
if(Bool == TRUE) if(Bool == TRUE)
{ {
Colourise(CS_GREEN); Colourise(CS_GREEN);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
fprintf(stderr, "true"); Print(stderr, "true");
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
} }
else else
{ {
Colourise(CS_RED); Colourise(CS_RED);
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
fprintf(stderr, "false"); Print(stderr, "false");
if(FillSyntax) { fprintf(stderr, "\""); } if(FillSyntax) { Print(stderr, "\""); }
} }
Colourise(CS_END); Colourise(CS_END);
if(AppendNewline) { fprintf(stderr, "\n"); } if(AppendNewline) { Print(stderr, "\n"); }
} }
void PrintScopeTree(scope_tree *T, int IndentationLevel); void PrintScopeTree(scope_tree *T, int IndentationLevel);
@ -1602,7 +1602,7 @@ PrintScopeTree(scope_tree *T, int IndentationLevel)
{ {
--IndentationLevel; --IndentationLevel;
IndentedCarriageReturn(IndentationLevel); IndentedCarriageReturn(IndentationLevel);
fprintf(stderr, "}"); Print(stderr, "}");
} }
} }
} }
@ -1982,7 +1982,7 @@ ParseIncludeRules(memory_book *TypeSpecs, config_type_spec *ParentTypeSpec, toke
{ {
token *This = GetPlaceInBook(&T->Token, T->CurrentIndex); token *This = GetPlaceInBook(&T->Token, T->CurrentIndex);
ConfigError(&Filepath, This->LineNumber, S_WARNING, "Invalid identifier in \"include\" scope: ", &This->Content); ConfigError(&Filepath, This->LineNumber, S_WARNING, "Invalid identifier in \"include\" scope: ", &This->Content);
fprintf(stderr, Print(stderr,
" Valid identifiers:\n" " Valid identifiers:\n"
" allow\n" " allow\n"
" deny\n"); " deny\n");
@ -4013,11 +4013,11 @@ PrintAssociationClashes(config_string_associations *A)
config_pair Assoc1 = { .Key = A->ID1, .String = Association->String1, .Type = PT_STRING }; config_pair Assoc1 = { .Key = A->ID1, .String = Association->String1, .Type = PT_STRING };
PrintPair(&Assoc1, ": ", FALSE, IndentLevel, TRUE, FALSE); PrintPair(&Assoc1, ": ", FALSE, IndentLevel, TRUE, FALSE);
} }
fprintf(stderr, "\n"); Print(stderr, "\n");
for(int j = 0; j < Association->Projects.ItemCount; ++j) for(int j = 0; j < Association->Projects.ItemCount; ++j)
{ {
project **P = GetPlaceInBook(&Association->Projects, j); project **P = GetPlaceInBook(&Association->Projects, j);
fprintf(stderr, " "); Print(stderr, " ");
if(*P) if(*P)
{ {
PrintStringC(CS_CYAN, (*P)->Lineage); PrintStringC(CS_CYAN, (*P)->Lineage);
@ -4032,7 +4032,7 @@ PrintAssociationClashes(config_string_associations *A)
} }
PrintStringC(CS_RED, Wrap0("]")); PrintStringC(CS_RED, Wrap0("]"));
} }
fprintf(stderr, "\n"); Print(stderr, "\n");
} }
} }
} }
@ -4056,11 +4056,11 @@ PrintVariants(uint64_t V, colour_code ColourCode, string Margin)
int Length = StringLength(String); int Length = StringLength(String);
if(RunningAvailableColumns - Length >= 0) if(RunningAvailableColumns - Length >= 0)
{ {
fprintf(stderr, "%s", NeedsSpacing ? " " : ""); Print(stderr, "%s", NeedsSpacing ? " " : "");
} }
else else
{ {
fprintf(stderr, "\n"); Print(stderr, "\n");
PrintString(Margin); PrintString(Margin);
NeedsSpacing = FALSE; NeedsSpacing = FALSE;
RunningAvailableColumns = AvailableColumnsForValue; RunningAvailableColumns = AvailableColumnsForValue;
@ -4091,7 +4091,7 @@ PrintVariantInconsistencies(memory_book *VariantStrings)
string Margin = Wrap0(" "); string Margin = Wrap0(" ");
variant V = *(variant *)GetPlaceInBook(&ThisVariantsString->Variants, j); variant V = *(variant *)GetPlaceInBook(&ThisVariantsString->Variants, j);
PrintVariants(V, j % 2 ? CS_CYAN_BOLD : CS_YELLOW_BOLD, Margin); PrintVariants(V, j % 2 ? CS_CYAN_BOLD : CS_YELLOW_BOLD, Margin);
fprintf(stderr, "\n"); Print(stderr, "\n");
} }
} }
} }
@ -4133,9 +4133,9 @@ PrintLogLevel(char *Title, log_level Level, char *Delimiter, uint64_t Indentatio
{ {
Indent(Indentation); Indent(Indentation);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_LOG_LEVEL].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_LOG_LEVEL].String);
fprintf(stderr, "%s", Delimiter); Print(stderr, "%s", Delimiter);
PrintC(CS_GREEN_BOLD, LogLevelStrings[Level]); PrintC(CS_GREEN_BOLD, LogLevelStrings[Level]);
if(AppendNewline) { fprintf(stderr, "\n"); } if(AppendNewline) { Print(stderr, "\n"); }
} }
typedef struct typedef struct
@ -4157,14 +4157,14 @@ PrintVerticals(typography *T, int Count)
{ {
for(int i = 0; i < Count; ++i) for(int i = 0; i < Count; ++i)
{ {
fprintf(stderr, "%s", T->Vertical); Print(stderr, "%s", T->Vertical);
} }
} }
void void
CarriageReturn(typography *T, int VerticalsRequired) CarriageReturn(typography *T, int VerticalsRequired)
{ {
fprintf(stderr, "\n"); Print(stderr, "\n");
PrintVerticals(T, VerticalsRequired); PrintVerticals(T, VerticalsRequired);
} }
@ -4176,12 +4176,12 @@ TypesetIconType(typography *T, uint8_t Generation, icon_type Type, bool PrependN
// TODO(matt): Implement me, please! // TODO(matt): Implement me, please!
//CarriageReturn(T, Generation); //CarriageReturn(T, Generation);
//fprintf(stderr, "%s", T.Margin); //Print(stderr, "%s", T.Margin);
#if 0 #if 0
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_ICON_TYPE].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_ICON_TYPE].String);
fprintf(stderr, "%s", T.Delimiter); Print(stderr, "%s", T.Delimiter);
PrintC(CS_GREEN_BOLD, IconTypeStrings[Type]); PrintC(CS_GREEN_BOLD, IconTypeStrings[Type]);
fprintf(stderr, "\n"); Print(stderr, "\n");
#endif #endif
@ -4195,10 +4195,10 @@ TypesetIconType(typography *T, uint8_t Generation, icon_type Type, bool PrependN
++Result; ++Result;
} }
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
config_identifier_id Key = IDENT_ICON_TYPE; config_identifier_id Key = IDENT_ICON_TYPE;
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String);
fprintf(stderr, "%s", T->Delimiter); Print(stderr, "%s", T->Delimiter);
if(Type == IT_DEFAULT_UNSET) if(Type == IT_DEFAULT_UNSET)
{ {
PrintC(CS_YELLOW, "[unset]"); PrintC(CS_YELLOW, "[unset]");
@ -4229,17 +4229,17 @@ TypesetVariants(typography *T, uint8_t Generation, config_identifier_id Key, uin
if(RowsRequired) if(RowsRequired)
{ {
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin); Print(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin);
} }
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
int CharactersInKeyPlusDelimiter = StringLength(ConfigIdentifiers[Key].String) + StringLength(T->Delimiter); int CharactersInKeyPlusDelimiter = StringLength(ConfigIdentifiers[Key].String) + StringLength(T->Delimiter);
int AvailableColumnsForValue = AvailableColumns - CharactersInKeyPlusDelimiter; int AvailableColumnsForValue = AvailableColumns - CharactersInKeyPlusDelimiter;
int RunningAvailableColumns = AvailableColumnsForValue; int RunningAvailableColumns = AvailableColumnsForValue;
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String);
fprintf(stderr, "%s", T->Delimiter); Print(stderr, "%s", T->Delimiter);
bool NeedsSpacing = FALSE; bool NeedsSpacing = FALSE;
if(Value) if(Value)
@ -4252,7 +4252,7 @@ TypesetVariants(typography *T, uint8_t Generation, config_identifier_id Key, uin
int Length = StringLength(String); int Length = StringLength(String);
if(RunningAvailableColumns - Length >= 0) if(RunningAvailableColumns - Length >= 0)
{ {
fprintf(stderr, "%s", NeedsSpacing ? " " : ""); Print(stderr, "%s", NeedsSpacing ? " " : "");
} }
else else
{ {
@ -4260,12 +4260,12 @@ TypesetVariants(typography *T, uint8_t Generation, config_identifier_id Key, uin
if(RowsRequired) if(RowsRequired)
{ {
--*RowsRequired; --*RowsRequired;
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin); Print(stderr, "%s%s ", *RowsRequired == 1 ? T->LowerLeft : T->Vertical, T->Margin);
} }
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
for(int i = 0; i < CharactersInKeyPlusDelimiter; ++i) for(int i = 0; i < CharactersInKeyPlusDelimiter; ++i)
{ {
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
} }
NeedsSpacing = FALSE; NeedsSpacing = FALSE;
RunningAvailableColumns = AvailableColumnsForValue; RunningAvailableColumns = AvailableColumnsForValue;
@ -4295,9 +4295,9 @@ PrintMedium(typography *T, medium *M, char *Delimiter, uint64_t Indentation, boo
{ {
PrintC(CS_BLACK_BOLD, " [hidden]"); PrintC(CS_BLACK_BOLD, " [hidden]");
} }
fprintf(stderr, "%s", Delimiter); Print(stderr, "%s", Delimiter);
PrintStringC(CS_MAGENTA, M->Name); PrintStringC(CS_MAGENTA, M->Name);
fprintf(stderr, " ("); Print(stderr, " (");
if(M->Icon.Length > 0) if(M->Icon.Length > 0)
{ {
PrintStringI(M->Icon, Indentation); PrintStringI(M->Icon, Indentation);
@ -4312,10 +4312,10 @@ PrintMedium(typography *T, medium *M, char *Delimiter, uint64_t Indentation, boo
} }
TypesetIconType(T, Indentation, M->IconType, FALSE, AppendNewline); TypesetIconType(T, Indentation, M->IconType, FALSE, AppendNewline);
TypesetVariants(T, Indentation, IDENT_ICON_VARIANTS, M->IconVariants, 0, 0, FALSE, AppendNewline); TypesetVariants(T, Indentation, IDENT_ICON_VARIANTS, M->IconVariants, 0, 0, FALSE, AppendNewline);
fprintf(stderr, ")"); Print(stderr, ")");
if(AppendNewline) if(AppendNewline)
{ {
fprintf(stderr, "\n"); Print(stderr, "\n");
} }
} }
@ -4324,13 +4324,13 @@ PrintSupport(support *S, typography *Typography, int IndentationLevel, uint8_t *
{ {
bool ShouldFillSyntax = FALSE; bool ShouldFillSyntax = FALSE;
config_pair ID = { .Key = IDENT_SUPPORT, .String = S->ID, .Type = PT_STRING }; config_pair ID = { .Key = IDENT_SUPPORT, .String = S->ID, .Type = PT_STRING };
fprintf(stderr, "%s%s", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin); Print(stderr, "%s%s", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&ID, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); PrintPair(&ID, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired; --*RowsRequired;
if(S->URL.Length > 0) if(S->URL.Length > 0)
{ {
config_pair URL = { .Key = IDENT_URL, .String = S->URL, .Type = PT_STRING }; config_pair URL = { .Key = IDENT_URL, .String = S->URL, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin); Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&URL, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); PrintPair(&URL, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired; --*RowsRequired;
} }
@ -4338,28 +4338,28 @@ PrintSupport(support *S, typography *Typography, int IndentationLevel, uint8_t *
if(S->Icon.Length > 0) if(S->Icon.Length > 0)
{ {
config_pair Icon = { .Key = IDENT_ICON, .String = S->Icon, .Type = PT_STRING }; config_pair Icon = { .Key = IDENT_ICON, .String = S->Icon, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin); Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&Icon, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); PrintPair(&Icon, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired; --*RowsRequired;
} }
if(S->IconNormal.Length > 0) if(S->IconNormal.Length > 0)
{ {
config_pair IconNormal = { .Key = IDENT_ICON_NORMAL, .String = S->IconNormal, .Type = PT_STRING }; config_pair IconNormal = { .Key = IDENT_ICON_NORMAL, .String = S->IconNormal, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin); Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&IconNormal, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); PrintPair(&IconNormal, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired; --*RowsRequired;
} }
if(S->IconFocused.Length > 0) if(S->IconFocused.Length > 0)
{ {
config_pair IconFocused = { .Key = IDENT_ICON_FOCUSED, .String = S->IconFocused, .Type = PT_STRING }; config_pair IconFocused = { .Key = IDENT_ICON_FOCUSED, .String = S->IconFocused, .Type = PT_STRING };
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin); Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
PrintPair(&IconFocused, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); PrintPair(&IconFocused, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--*RowsRequired; --*RowsRequired;
} }
#if 1 #if 1
// TODO(matt): FIX ME // TODO(matt): FIX ME
fprintf(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin); Print(stderr, "%s%s ", *RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical, Typography->Margin);
*RowsRequired -= TypesetIconType(Typography, IndentationLevel, S->IconType, FALSE, TRUE); *RowsRequired -= TypesetIconType(Typography, IndentationLevel, S->IconType, FALSE, TRUE);
uint8_t StartingColumn = 4; uint8_t StartingColumn = 4;
uint64_t AvailableColumns = GetTerminalColumns() - StartingColumn; uint64_t AvailableColumns = GetTerminalColumns() - StartingColumn;
@ -4424,11 +4424,11 @@ void
PrintPerson(person *P, typography *Typography) PrintPerson(person *P, typography *Typography)
{ {
bool HaveInfo = P->Name.Length > 0 || P->Homepage.Length > 0 || P->Support.ItemCount > 0; bool HaveInfo = P->Name.Length > 0 || P->Homepage.Length > 0 || P->Support.ItemCount > 0;
fprintf(stderr, "\n" Print(stderr, "\n"
"%s%s%s%s%s ", HaveInfo ? Typography->UpperLeftCorner : Typography->UpperLeft, "%s%s%s%s%s ", HaveInfo ? Typography->UpperLeftCorner : Typography->UpperLeft,
Typography->Horizontal, Typography->Horizontal, Typography->Horizontal, Typography->UpperRight); Typography->Horizontal, Typography->Horizontal, Typography->Horizontal, Typography->UpperRight);
PrintStringC(CS_GREEN_BOLD, P->ID); PrintStringC(CS_GREEN_BOLD, P->ID);
fprintf(stderr, "\n"); Print(stderr, "\n");
uint8_t RowsRequired = GetRowsRequiredForPersonInfo(Typography, P); uint8_t RowsRequired = GetRowsRequiredForPersonInfo(Typography, P);
@ -4436,35 +4436,35 @@ PrintPerson(person *P, typography *Typography)
bool ShouldFillSyntax = FALSE; bool ShouldFillSyntax = FALSE;
if(P->Name.Length > 0) if(P->Name.Length > 0)
{ {
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Name = { .Key = IDENT_NAME, .String = P->Name, .Type = PT_STRING }; PrintPair(&Name, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair Name = { .Key = IDENT_NAME, .String = P->Name, .Type = PT_STRING }; PrintPair(&Name, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
if(P->Abbreviations[AS_INITIAL].Length > 0) if(P->Abbreviations[AS_INITIAL].Length > 0)
{ {
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair AbbrevInitial = { .Key = IDENT_ABBREV_INITIAL, .String = P->Abbreviations[AS_INITIAL], .Type = PT_STRING }; PrintPair(&AbbrevInitial, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair AbbrevInitial = { .Key = IDENT_ABBREV_INITIAL, .String = P->Abbreviations[AS_INITIAL], .Type = PT_STRING }; PrintPair(&AbbrevInitial, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
if(P->Abbreviations[AS_GIVEN_OR_NICKNAME].Length > 0) if(P->Abbreviations[AS_GIVEN_OR_NICKNAME].Length > 0)
{ {
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair AbbrevGivenOrNickname = { .Key = IDENT_ABBREV_GIVEN_OR_NICKNAME, .String = P->Abbreviations[AS_GIVEN_OR_NICKNAME], .Type = PT_STRING }; PrintPair(&AbbrevGivenOrNickname, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair AbbrevGivenOrNickname = { .Key = IDENT_ABBREV_GIVEN_OR_NICKNAME, .String = P->Abbreviations[AS_GIVEN_OR_NICKNAME], .Type = PT_STRING }; PrintPair(&AbbrevGivenOrNickname, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
if(P->Abbreviations[AS_DOTTED_INITIAL_AND_SURNAME].Length > 0) if(P->Abbreviations[AS_DOTTED_INITIAL_AND_SURNAME].Length > 0)
{ {
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair AbbrevDottedInitialAndSurname = { .Key = IDENT_ABBREV_DOTTED_INITIAL_AND_SURNAME, .String = P->Abbreviations[AS_DOTTED_INITIAL_AND_SURNAME], .Type = PT_STRING }; PrintPair(&AbbrevDottedInitialAndSurname, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair AbbrevDottedInitialAndSurname = { .Key = IDENT_ABBREV_DOTTED_INITIAL_AND_SURNAME, .String = P->Abbreviations[AS_DOTTED_INITIAL_AND_SURNAME], .Type = PT_STRING }; PrintPair(&AbbrevDottedInitialAndSurname, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
if(P->Homepage.Length > 0) if(P->Homepage.Length > 0)
{ {
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Homepage = { .Key = IDENT_HOMEPAGE, .String = P->Homepage, .Type = PT_STRING }; PrintPair(&Homepage, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair Homepage = { .Key = IDENT_HOMEPAGE, .String = P->Homepage, .Type = PT_STRING }; PrintPair(&Homepage, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
@ -4490,11 +4490,11 @@ void
PrintRole(role *R, typography *Typography) PrintRole(role *R, typography *Typography)
{ {
bool HaveInfo = R->Name.Length > 0 || R->Plural.Length > 0; bool HaveInfo = R->Name.Length > 0 || R->Plural.Length > 0;
fprintf(stderr, "\n" Print(stderr, "\n"
"%s%s%s%s%s ", HaveInfo ? Typography->UpperLeftCorner : Typography->UpperLeft, "%s%s%s%s%s ", HaveInfo ? Typography->UpperLeftCorner : Typography->UpperLeft,
Typography->Horizontal, Typography->Horizontal, Typography->Horizontal, Typography->UpperRight); Typography->Horizontal, Typography->Horizontal, Typography->Horizontal, Typography->UpperRight);
PrintStringC(CS_GREEN_BOLD, R->ID); PrintStringC(CS_GREEN_BOLD, R->ID);
fprintf(stderr, "\n"); Print(stderr, "\n");
uint8_t RowsRequired = GetRowsRequiredForRoleInfo(R); uint8_t RowsRequired = GetRowsRequiredForRoleInfo(R);
@ -4502,18 +4502,18 @@ PrintRole(role *R, typography *Typography)
bool ShouldFillSyntax = FALSE; bool ShouldFillSyntax = FALSE;
if(R->Name.Length > 0) if(R->Name.Length > 0)
{ {
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Name = { .Key = IDENT_NAME, .String = R->Name, .Type = PT_STRING }; PrintPair(&Name, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair Name = { .Key = IDENT_NAME, .String = R->Name, .Type = PT_STRING }; PrintPair(&Name, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
if(R->Plural.Length > 0) if(R->Plural.Length > 0)
{ {
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair Plural = { .Key = IDENT_PLURAL, .String = R->Plural, .Type = PT_STRING }; PrintPair(&Plural, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair Plural = { .Key = IDENT_PLURAL, .String = R->Plural, .Type = PT_STRING }; PrintPair(&Plural, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical); Print(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
config_pair NonSpeaking = { .Key = IDENT_NON_SPEAKING, .bool = R->NonSpeaking, .Type = PT_BOOL }; PrintPair(&NonSpeaking, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair NonSpeaking = { .Key = IDENT_NON_SPEAKING, .bool = R->NonSpeaking, .Type = PT_BOOL }; PrintPair(&NonSpeaking, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
--RowsRequired; --RowsRequired;
} }
@ -4529,7 +4529,21 @@ PrintLineage(string Lineage, bool AppendNewline)
} }
string Us = GetFinalComponent(Lineage); string Us = GetFinalComponent(Lineage);
PrintStringC(CS_BLUE_BOLD, Us); PrintStringC(CS_BLUE_BOLD, Us);
if(AppendNewline) { fprintf(stderr, "\n"); } if(AppendNewline) { Print(stderr, "\n"); }
}
void
ExtendLineageInBook(memory_book *M, string Lineage, bool AppendNewline)
{
string Parent = StripComponentFromPath(Lineage);
ExtendStringCInBook(M, CS_BLACK_BOLD, Parent);
if(Parent.Length > 0)
{
ExtendStringCInBook(M, CS_BLACK_BOLD, Wrap0("/"));
}
string Us = GetFinalComponent(Lineage);
ExtendStringCInBook(M, CS_BLUE_BOLD, Us);
if(AppendNewline) { ExtendStringInBook(M, Wrap0("\n")); }
} }
uint8_t uint8_t
@ -4554,20 +4568,20 @@ PrintTitle(char *Text, typography *T, uint8_t Generation, int AvailableColumns,
int InnerCharsRequired = AvailableColumns - LeftmostCharLength - StringLength(Text) - RightmostCharLength - SpacesRequired; int InnerCharsRequired = AvailableColumns - LeftmostCharLength - StringLength(Text) - RightmostCharLength - SpacesRequired;
int LeftCharsRequired = 4; int LeftCharsRequired = 4;
int RightCharsRequired = InnerCharsRequired - LeftCharsRequired; int RightCharsRequired = InnerCharsRequired - LeftCharsRequired;
fprintf(stderr, "%s", LeftmostChar); Print(stderr, "%s", LeftmostChar);
for(int i = 0; i < LeftCharsRequired; ++i) for(int i = 0; i < LeftCharsRequired; ++i)
{ {
fprintf(stderr, "%s", InnerChar); Print(stderr, "%s", InnerChar);
} }
fprintf(stderr, " %s ", Text); Print(stderr, " %s ", Text);
for(int i = 0; i < RightCharsRequired; ++i) for(int i = 0; i < RightCharsRequired; ++i)
{ {
fprintf(stderr, "%s", InnerChar); Print(stderr, "%s", InnerChar);
} }
fprintf(stderr, "%s", RightmostChar); Print(stderr, "%s", RightmostChar);
if(SectionItemCount == 0) if(SectionItemCount == 0)
{ {
//fprintf(stderr, "\n"); //Print(stderr, "\n");
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
} }
} }
@ -4577,10 +4591,10 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
{ {
int IndentationLevel = 0; int IndentationLevel = 0;
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintTitle("Media", T, Generation, AvailableColumns, P->Medium.ItemCount); PrintTitle("Media", T, Generation, AvailableColumns, P->Medium.ItemCount);
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
//AvailableColumns -= Generation + 1; //AvailableColumns -= Generation + 1;
int RunningAvailableColumns = AvailableColumns; int RunningAvailableColumns = AvailableColumns;
medium *This = GetPlaceInBook(&P->Medium, 0); medium *This = GetPlaceInBook(&P->Medium, 0);
@ -4597,19 +4611,19 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
ColumnsRequired = GetColumnsRequiredForMedium(This, T); ColumnsRequired = GetColumnsRequiredForMedium(This, T);
if(RunningAvailableColumns >= StringLengthOfSpacePlusSeparator) if(RunningAvailableColumns >= StringLengthOfSpacePlusSeparator)
{ {
fprintf(stderr, " %s", T->Separator); Print(stderr, " %s", T->Separator);
RunningAvailableColumns -= StringLengthOfSpacePlusSeparator; RunningAvailableColumns -= StringLengthOfSpacePlusSeparator;
if(ColumnsRequired + 1 <= RunningAvailableColumns) if(ColumnsRequired + 1 <= RunningAvailableColumns)
{ {
fprintf(stderr, " "); Print(stderr, " ");
--RunningAvailableColumns; --RunningAvailableColumns;
PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE); PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE);
} }
else else
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
RunningAvailableColumns = AvailableColumns; RunningAvailableColumns = AvailableColumns;
PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE); PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE);
} }
@ -4617,9 +4631,9 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
else else
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
RunningAvailableColumns = AvailableColumns; RunningAvailableColumns = AvailableColumns;
fprintf(stderr, "%s ", T->Separator); Print(stderr, "%s ", T->Separator);
RunningAvailableColumns -= StringLengthOfSpacePlusSeparator; RunningAvailableColumns -= StringLengthOfSpacePlusSeparator;
PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE); PrintMedium(T, This, T->Delimiter, IndentationLevel, FALSE);
} }
@ -4647,7 +4661,7 @@ PrintCredits(config *C, project *P, typography *T, uint8_t Generation, int Inden
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
int Alignment = 0; int Alignment = 0;
Alignment += fprintf(stderr, "%s", T->Margin); Alignment += Print(stderr, "%s", T->Margin);
if(CreditCount == 1) if(CreditCount == 1)
{ {
Alignment += PrintStringC(CS_YELLOW_BOLD, Role->Name.Length ? Role->Name : Role->ID); Alignment += PrintStringC(CS_YELLOW_BOLD, Role->Name.Length ? Role->Name : Role->ID);
@ -4664,7 +4678,7 @@ PrintCredits(config *C, project *P, typography *T, uint8_t Generation, int Inden
Alignment += PrintStringC(CS_YELLOW_BOLD, Wrap0("s")); Alignment += PrintStringC(CS_YELLOW_BOLD, Wrap0("s"));
} }
} }
Alignment += fprintf(stderr, "%s", T->Delimiter); Alignment += Print(stderr, "%s", T->Delimiter);
bool Printed = FALSE; bool Printed = FALSE;
for(int j = 0; j < P->Credit.ItemCount; ++j) for(int j = 0; j < P->Credit.ItemCount; ++j)
{ {
@ -4688,13 +4702,13 @@ void
TypesetPair(typography *T, uint8_t Generation, config_identifier_id Key, string Value, int AvailableColumns) TypesetPair(typography *T, uint8_t Generation, config_identifier_id Key, string Value, int AvailableColumns)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
int CharactersInKeyPlusDelimiter = StringLength(ConfigIdentifiers[Key].String) + StringLength(T->Delimiter); int CharactersInKeyPlusDelimiter = StringLength(ConfigIdentifiers[Key].String) + StringLength(T->Delimiter);
int AvailableColumnsForValue = AvailableColumns - CharactersInKeyPlusDelimiter; int AvailableColumnsForValue = AvailableColumns - CharactersInKeyPlusDelimiter;
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[Key].String);
fprintf(stderr, "%s", T->Delimiter); Print(stderr, "%s", T->Delimiter);
int CharactersToWrite = Value.Length; int CharactersToWrite = Value.Length;
if(Value.Length > 0) if(Value.Length > 0)
@ -4706,10 +4720,10 @@ TypesetPair(typography *T, uint8_t Generation, config_identifier_id Key, string
while(CharactersToWrite > 0) while(CharactersToWrite > 0)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
for(int i = 0; i < CharactersInKeyPlusDelimiter; ++i) for(int i = 0; i < CharactersInKeyPlusDelimiter; ++i)
{ {
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
} }
string Pen = { .Base = Value.Base + Value.Length - CharactersToWrite, string Pen = { .Base = Value.Base + Value.Length - CharactersToWrite,
@ -4728,7 +4742,7 @@ void
TypesetNumber(typography *T, uint8_t Generation, config_identifier_id Key, int64_t Value) TypesetNumber(typography *T, uint8_t Generation, config_identifier_id Key, int64_t Value)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
config_pair Pair = { .Key = Key, .int64_t = Value, .Type = PT_INT64 }; config_pair Pair = { .Key = Key, .int64_t = Value, .Type = PT_INT64 };
bool ShouldFillSyntax = FALSE; bool ShouldFillSyntax = FALSE;
PrintPair(&Pair, T->Delimiter, ShouldFillSyntax, 0, FALSE, FALSE); PrintPair(&Pair, T->Delimiter, ShouldFillSyntax, 0, FALSE, FALSE);
@ -4738,7 +4752,7 @@ void
TypesetBool(typography *T, uint8_t Generation, config_identifier_id Key, bool Value) TypesetBool(typography *T, uint8_t Generation, config_identifier_id Key, bool Value)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
config_pair Pair = { .Key = Key, .bool = Value, .Type = PT_BOOL }; config_pair Pair = { .Key = Key, .bool = Value, .Type = PT_BOOL };
bool ShouldFillSyntax = FALSE; bool ShouldFillSyntax = FALSE;
PrintPair(&Pair, T->Delimiter, ShouldFillSyntax, 0, FALSE, FALSE); PrintPair(&Pair, T->Delimiter, ShouldFillSyntax, 0, FALSE, FALSE);
@ -4748,9 +4762,9 @@ void
TypesetGenre(typography *T, uint8_t Generation, genre G) TypesetGenre(typography *T, uint8_t Generation, genre G)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_GENRE].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_GENRE].String);
fprintf(stderr, "%s", T->Delimiter); Print(stderr, "%s", T->Delimiter);
PrintC(CS_GREEN_BOLD, GenreStrings[G]); PrintC(CS_GREEN_BOLD, GenreStrings[G]);
} }
@ -4758,9 +4772,9 @@ void
TypesetVODPlatform(typography *T, uint8_t Generation, vod_platform P) TypesetVODPlatform(typography *T, uint8_t Generation, vod_platform P)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_VOD_PLATFORM].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_VOD_PLATFORM].String);
fprintf(stderr, "%s", T->Delimiter); Print(stderr, "%s", T->Delimiter);
if(IsValidVODPlatform(P)) if(IsValidVODPlatform(P))
{ {
PrintC(CS_GREEN_BOLD, VODPlatformStrings[P]); PrintC(CS_GREEN_BOLD, VODPlatformStrings[P]);
@ -4775,9 +4789,9 @@ void
TypesetNumberingMethod(typography *T, uint8_t Generation, numbering_method N) TypesetNumberingMethod(typography *T, uint8_t Generation, numbering_method N)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_NUMBERING_METHOD].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_NUMBERING_METHOD].String);
fprintf(stderr, "%s", T-> Delimiter); Print(stderr, "%s", T-> Delimiter);
PrintC(CS_GREEN_BOLD, NumberingMethodStrings[N]); PrintC(CS_GREEN_BOLD, NumberingMethodStrings[N]);
} }
@ -4785,9 +4799,9 @@ void
TypesetNumberingScheme(typography *T, uint8_t Generation, numbering_scheme N) TypesetNumberingScheme(typography *T, uint8_t Generation, numbering_scheme N)
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_NUMBERING_SCHEME].String); PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_NUMBERING_SCHEME].String);
fprintf(stderr, "%s", T-> Delimiter); Print(stderr, "%s", T-> Delimiter);
PrintC(CS_GREEN_BOLD, NumberingSchemeStrings[N]); PrintC(CS_GREEN_BOLD, NumberingSchemeStrings[N]);
} }
@ -4797,7 +4811,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
int Generation = Ancestors + 1; int Generation = Ancestors + 1;
CarriageReturn(T, Ancestors); CarriageReturn(T, Ancestors);
fprintf(stderr, "%s%s%s%s%s ", T->UpperLeftCorner, Print(stderr, "%s%s%s%s%s ", T->UpperLeftCorner,
T->Horizontal, T->Horizontal, T->Horizontal, T->UpperRight); T->Horizontal, T->Horizontal, T->Horizontal, T->UpperRight);
PrintLineage(P->Lineage, FALSE); PrintLineage(P->Lineage, FALSE);
@ -4810,7 +4824,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintTitle("Settings", T, Generation, AvailableColumns, -1); PrintTitle("Settings", T, Generation, AvailableColumns, -1);
TypesetPair(T, Generation, IDENT_TITLE, P->Title, AvailableColumns); TypesetPair(T, Generation, IDENT_TITLE, P->Title, AvailableColumns);
@ -4858,7 +4872,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintTitle("Credits", T, Generation, AvailableColumns, P->Credit.ItemCount); PrintTitle("Credits", T, Generation, AvailableColumns, P->Credit.ItemCount);
if(P->Credit.ItemCount > 0) if(P->Credit.ItemCount > 0)
{ {
@ -4866,7 +4880,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
} }
else else
{ {
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintC(CS_YELLOW, "[none]"); PrintC(CS_YELLOW, "[none]");
} }
@ -4874,7 +4888,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
{ {
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
CarriageReturn(T, Generation); CarriageReturn(T, Generation);
fprintf(stderr, "%s", T->Margin); Print(stderr, "%s", T->Margin);
PrintTitle("Children", T, Generation, AvailableColumns, -1); PrintTitle("Children", T, Generation, AvailableColumns, -1);
++Ancestors; ++Ancestors;
for(int i = 0; i < P->Child.ItemCount; ++i) for(int i = 0; i < P->Child.ItemCount; ++i)
@ -4884,7 +4898,7 @@ PrintProject(config *C, project *P, typography *T, int Ancestors, int Indentatio
--Ancestors; --Ancestors;
} }
CarriageReturn(T, Ancestors); CarriageReturn(T, Ancestors);
fprintf(stderr, "%s%s%s%s%s ", T->LowerLeftCorner, Print(stderr, "%s%s%s%s%s ", T->LowerLeftCorner,
T->Horizontal, T->Horizontal, T->Horizontal, T->UpperRight); T->Horizontal, T->Horizontal, T->Horizontal, T->UpperRight);
PrintLineage(P->Lineage, FALSE); PrintLineage(P->Lineage, FALSE);
} }
@ -4911,7 +4925,7 @@ PrintConfig(config *C, bool ShouldClearTerminal)
}; };
// separate // separate
fprintf(stderr, "\n"); Print(stderr, "\n");
PrintTitle("Global Settings", &Typography, 0, TermCols, -1); PrintTitle("Global Settings", &Typography, 0, TermCols, -1);
int IndentationLevel = 0; int IndentationLevel = 0;
@ -4920,14 +4934,14 @@ PrintConfig(config *C, bool ShouldClearTerminal)
TypesetPair(&Typography, 0, IDENT_CACHE_DIR, C->CacheDir, AvailableColumns); TypesetPair(&Typography, 0, IDENT_CACHE_DIR, C->CacheDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_INSTANCE_TITLE, C->InstanceTitle, AvailableColumns); TypesetPair(&Typography, 0, IDENT_INSTANCE_TITLE, C->InstanceTitle, AvailableColumns);
fprintf(stderr, "\n"); Print(stderr, "\n");
TypesetPair(&Typography, 0, IDENT_GLOBAL_TEMPLATES_DIR, C->GlobalTemplatesDir, AvailableColumns); TypesetPair(&Typography, 0, IDENT_GLOBAL_TEMPLATES_DIR, C->GlobalTemplatesDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_TEMPLATE, C->GlobalSearchTemplatePath, AvailableColumns); TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_TEMPLATE, C->GlobalSearchTemplatePath, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_DIR, C->GlobalSearchDir, AvailableColumns); TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_DIR, C->GlobalSearchDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_URL, C->GlobalSearchURL, AvailableColumns); TypesetPair(&Typography, 0, IDENT_GLOBAL_SEARCH_URL, C->GlobalSearchURL, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_GLOBAL_THEME, C->GlobalTheme, AvailableColumns); TypesetPair(&Typography, 0, IDENT_GLOBAL_THEME, C->GlobalTheme, AvailableColumns);
fprintf(stderr, "\n"); Print(stderr, "\n");
TypesetPair(&Typography, 0, IDENT_ASSETS_ROOT_DIR, C->AssetsRootDir, AvailableColumns); TypesetPair(&Typography, 0, IDENT_ASSETS_ROOT_DIR, C->AssetsRootDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_ASSETS_ROOT_URL, C->AssetsRootURL, AvailableColumns); TypesetPair(&Typography, 0, IDENT_ASSETS_ROOT_URL, C->AssetsRootURL, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_CSS_PATH, C->CSSDir, AvailableColumns); TypesetPair(&Typography, 0, IDENT_CSS_PATH, C->CSSDir, AvailableColumns);
@ -4935,32 +4949,32 @@ PrintConfig(config *C, bool ShouldClearTerminal)
TypesetPair(&Typography, 0, IDENT_JS_PATH, C->JSDir, AvailableColumns); TypesetPair(&Typography, 0, IDENT_JS_PATH, C->JSDir, AvailableColumns);
TypesetPair(&Typography, 0, IDENT_QUERY_STRING, C->QueryString, AvailableColumns); TypesetPair(&Typography, 0, IDENT_QUERY_STRING, C->QueryString, AvailableColumns);
fprintf(stderr, "\n" Print(stderr, "\n"
"\n" "\n"
"%s", Typography.Margin); "%s", Typography.Margin);
bool ShouldFillSyntax = FALSE; bool ShouldFillSyntax = FALSE;
PrintBool("Respecting Privacy (derived from projects)", C->RespectingPrivacy, Typography.Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); PrintBool("Respecting Privacy (derived from projects)", C->RespectingPrivacy, Typography.Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
fprintf(stderr, "%s", Typography.Margin); Print(stderr, "%s", Typography.Margin);
int SecondsPerMinute = 60; int SecondsPerMinute = 60;
config_pair PrivacyCheckInterval = { .Key = IDENT_PRIVACY_CHECK_INTERVAL, .int64_t = C->PrivacyCheckInterval / SecondsPerMinute, .Type = PT_INT64 }; PrintPair(&PrivacyCheckInterval, Typography.Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE); config_pair PrivacyCheckInterval = { .Key = IDENT_PRIVACY_CHECK_INTERVAL, .int64_t = C->PrivacyCheckInterval / SecondsPerMinute, .Type = PT_INT64 }; PrintPair(&PrivacyCheckInterval, Typography.Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
fprintf(stderr, "%s", Typography.Margin); Print(stderr, "%s", Typography.Margin);
PrintLogLevel(ConfigIdentifiers[IDENT_LOG_LEVEL].String, C->LogLevel, Typography.Delimiter, IndentationLevel, TRUE); PrintLogLevel(ConfigIdentifiers[IDENT_LOG_LEVEL].String, C->LogLevel, Typography.Delimiter, IndentationLevel, TRUE);
fprintf(stderr, "\n"); Print(stderr, "\n");
PrintTitle("People", &Typography, 0, TermCols, C->Person.ItemCount); PrintTitle("People", &Typography, 0, TermCols, C->Person.ItemCount);
for(int i = 0; i < C->Person.ItemCount; ++i) for(int i = 0; i < C->Person.ItemCount; ++i)
{ {
PrintPerson(GetPlaceInBook(&C->Person, i), &Typography); PrintPerson(GetPlaceInBook(&C->Person, i), &Typography);
} }
fprintf(stderr, "\n"); Print(stderr, "\n");
PrintTitle("Roles", &Typography, 0, TermCols, C->Role.ItemCount); PrintTitle("Roles", &Typography, 0, TermCols, C->Role.ItemCount);
for(int i = 0; i < C->Role.ItemCount; ++i) for(int i = 0; i < C->Role.ItemCount; ++i)
{ {
PrintRole(GetPlaceInBook(&C->Role, i), &Typography); PrintRole(GetPlaceInBook(&C->Role, i), &Typography);
} }
fprintf(stderr, "\n"); Print(stderr, "\n");
PrintTitle("Projects", &Typography, 0, TermCols, -1); PrintTitle("Projects", &Typography, 0, TermCols, -1);
for(int i = 0; i < C->Project.ItemCount; ++i) for(int i = 0; i < C->Project.ItemCount; ++i)
{ {
@ -5055,7 +5069,7 @@ PositionRolesInConfig(config *C, resolution_errors *E, config_verifiers *V, scop
config_pair *Position = GetPair(*Role, IDENT_POSITION); config_pair *Position = GetPair(*Role, IDENT_POSITION);
if(IsPositioned(Position)) if(IsPositioned(Position))
{ {
int TargetPos; int TargetPos = 0;
if(Position->int64_t < 0) if(Position->int64_t < 0)
{ {
TargetPos = SlotCount + Position->int64_t; TargetPos = SlotCount + Position->int64_t;
@ -5368,7 +5382,7 @@ ParseConfig(string Path, memory_book *TokensList)
int int
main(int ArgC, char **Args) main(int ArgC, char **Args)
{ {
//fprintf(stderr, "%s\n", ConfigFile.Buffer.Location); //Print(stderr, "%s\n", ConfigFile.Buffer.Location);
config_type_specs TypeSpecs = InitTypeSpecs(); config_type_specs TypeSpecs = InitTypeSpecs();
config *Config = ParseConfig(&TypeSpecs, Wrap0("cinera.conf")); config *Config = ParseConfig(&TypeSpecs, Wrap0("cinera.conf"));

View File

@ -1,40 +1,5 @@
var cinera = document.querySelector(".cinera");
var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location; var baseURL = location.hash ? (location.toString().substr(0, location.toString().length - location.hash.length)) : location;
var originalTextContent = {
TitleQuotes: null,
TitleReferences: null,
TitleCredits: null,
EpisodePrev: null,
EpisodeNext: null,
};
var menuState = [];
var titleBar = cinera.querySelector(".cineraMenus");
var quotesMenu = null;
var referencesMenu = null;
var filterMenu = null;
var viewsMenu = null;
var linkMenu = null;
var creditsMenu = null;
var sourceMenus = null;
var helpButton = null;
var helpDocumentation = null;
// NOTE(matt): One set of markers per page. There is code to support multiple, which we may want to extend everywhere
var MarkersContainer = cinera.querySelector(".markers_container");
var views = {
REGULAR: 0,
THEATRE: 1,
SUPERTHEATRE: 2,
};
var devices = {
DESKTOP: 0,
MOBILE: 1,
};
var CineraProps = { var CineraProps = {
C: null, C: null,
V: views.REGULAR, V: views.REGULAR,
@ -57,275 +22,19 @@ var CineraProps = {
}; };
CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile); CineraProps.O = GetRealOrientation(orientations.LANDSCAPE_LEFT, CineraProps.IsMobile);
if(titleBar)
{
quotesMenu = titleBar.querySelector(".quotes_container");
if(quotesMenu)
{
originalTextContent.TitleQuotes = quotesMenu.previousElementSibling.textContent;
menuState.push(quotesMenu);
var quoteItems = quotesMenu.querySelectorAll(".ref");
if(quoteItems)
{
for(var i = 0; i < quoteItems.length; ++i)
{
quoteItems[i].addEventListener("mouseenter", function(ev) {
mouseOverQuotes(this);
})
};
}
var quoteTimecodes = quotesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
for (var i = 0; i < quoteTimecodes.length; ++i) {
quoteTimecodes[i].addEventListener("click", function(ev) {
if (player) {
var time = ev.currentTarget.getAttribute("data-timestamp");
mouseSkipToTimecode(player, time, ev);
}
});
}
var lastFocusedQuote = null;
}
referencesMenu = titleBar.querySelector(".references_container");
if(referencesMenu)
{
originalTextContent.TitleReferences = referencesMenu.previousElementSibling.textContent;
menuState.push(referencesMenu);
var referenceItems = referencesMenu.querySelectorAll(".ref");
if(referenceItems)
{
for(var i = 0; i < referenceItems.length; ++i)
{
referenceItems[i].addEventListener("mouseenter", function(ev) {
mouseOverReferences(this);
})
};
var lastFocusedReference = null;
var lastFocusedIdentifier = null;
}
var refTimecodes = referencesMenu.querySelectorAll(".refs .ref .ref_indices .timecode");
for (var i = 0; i < refTimecodes.length; ++i) {
refTimecodes[i].addEventListener("click", function(ev) {
if (player) {
var time = ev.currentTarget.getAttribute("data-timestamp");
mouseSkipToTimecode(player, time, ev);
}
});
}
}
if(referencesMenu || quotesMenu)
{
var refSources = titleBar.querySelectorAll(".refs .ref"); // This is for both quotes and refs
for (var i = 0; i < refSources.length; ++i) {
refSources[i].addEventListener("click", function(ev) {
if (player) {
player.pause();
}
});
}
}
filterMenu = titleBar.querySelector(".filter_container");
if(filterMenu)
{
menuState.push(filterMenu);
var lastFocusedCategory = null;
var lastFocusedTopic = null;
var lastFocusedMedium = null;
var filter = filterMenu.parentNode;
var filterModeElement = filter.querySelector(".filter_mode");
filterModeElement.addEventListener("click", function(ev) {
ev.stopPropagation();
toggleFilterMode();
});
var filterMode = filterModeElement.classList[1];
var filterItems = filter.querySelectorAll(".filter_content");
var filterInitState = new Object();
var filterState = new Object();
for(var i = 0; i < filterItems.length; ++i)
{
filterItems[i].addEventListener("mouseenter", function(ev) {
navigateFilter(this);
})
filterItems[i].addEventListener("click", function(ev) {
ev.stopPropagation();
filterItemToggle(this);
});
var filterItemName = filterItems[i].classList.item(1);
if(filterItems[i].parentNode.classList.contains("filter_topics"))
{
filterInitState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
filterState[filterItemName] = { "type" : "topic", "off": (filterItems[i].classList.item(2) == "off") };
}
else
{
filterInitState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
filterState[filterItemName] = { "type" : "medium", "off": (filterItems[i].classList.item(2) == "off") };
}
}
}
viewsMenu = titleBar.querySelector(".views");
if(viewsMenu && !CineraProps.IsMobile)
{
menuState.push(viewsMenu);
var viewsContainer = viewsMenu.querySelector(".views_container");
viewsMenu.addEventListener("mouseenter", function(ev) {
handleMouseOverViewsMenu();
});
viewsMenu.addEventListener("mouseleave", function(ev) {
viewsContainer.style.display = "none";
});
var viewItems = viewsMenu.querySelectorAll(".view");
for(var i = 0; i < viewItems.length; ++i)
{
viewItems[i].addEventListener("click", function(ev) {
switch(this.getAttribute("data-id"))
{
case "regular":
case "theatre":
{
toggleTheatreMode();
} break;
case "super":
{
toggleSuperTheatreMode();
} break;
}
});
}
}
linkMenu = titleBar.querySelector(".link_container");
linkTimestamp = true;
if(linkMenu)
{
menuState.push(linkMenu);
var linkMode = linkMenu.querySelector("#cineraLinkMode");
var link = linkMenu.querySelector("#cineraLink");
linkMode.addEventListener("click", function(ev) {
ev.stopPropagation();
toggleLinkMode(linkMode, link);
});
link.addEventListener("click", function(ev) {
CopyToClipboard(link);
toggleMenuVisibility(linkMenu);
});
}
creditsMenu = titleBar.querySelector(".credits_container");
if(creditsMenu)
{
originalTextContent.TitleCredits = creditsMenu.previousElementSibling.textContent;
menuState.push(creditsMenu);
var lastFocusedCreditItem = null;
var creditItems = creditsMenu.querySelectorAll(".person, .support");
for(var i = 0; i < creditItems.length; ++i)
{
creditItems[i].addEventListener("mouseenter", function(ev) {
if(this != lastFocusedCreditItem)
{
lastFocusedCreditItem.classList.remove("focused");
unfocusSprite(lastFocusedCreditItem);
if(lastFocusedCreditItem.classList.contains("support"))
{
setSpriteLightness(lastFocusedCreditItem.firstChild);
}
lastFocusedCreditItem = this;
focusedElement = lastFocusedCreditItem;
focusedElement.classList.add("focused");
focusSprite(focusedElement);
if(focusedElement.classList.contains("support"))
{
setSpriteLightness(focusedElement.firstChild);
}
}
});
if(creditItems[i].tagName == "A")
{
creditItems[i].addEventListener("click", function(ev) {
if(player)
{
player.pause();
}
});
}
}
}
sourceMenus = titleBar.querySelectorAll(".menu");
helpButton = titleBar.querySelector(".cineraHelp");
helpDocumentation = helpButton.querySelector(".help_container");
BindHelp(helpButton, helpDocumentation);
}
var focusedElement = null;
var focusedIdentifier = null;
var playerContainer = cinera.querySelector(".cineraPlayerContainer")
var prevEpisode = playerContainer.querySelector(".episodeMarker.prev");
if(prevEpisode) { originalTextContent.EpisodePrev = prevEpisode.firstChild.textContent; }
var nextEpisode = playerContainer.querySelector(".episodeMarker.next");
if(nextEpisode) { originalTextContent.EpisodeNext = nextEpisode.firstChild.textContent; }
var testMarkers = playerContainer.querySelectorAll(".marker");
// NOTE(matt): All the originalTextContent values must be set by this point, because the player's construction may need them
var MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent"; var MobileCineraContentRuleSelector = ".cinera.mobile .cineraPlayerContainer .markers_container > .markers .marker .cineraContent";
var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector); var MobileCineraContentRule = GetOrSetRule(MobileCineraContentRuleSelector);
var MenuContainerRuleSelector = ".cineraMenus > .menu .quotes_container, .cineraMenus > .menu .references_container, .cineraMenus > .menu .filter_container, .cineraMenus > .menu .views_container, .cineraMenus > .menu .link_container, .cineraMenus > .menu .credits_container"; var MenuContainerRuleSelector = ".cineraMenus > .menu .quotes_container, .cineraMenus > .menu .references_container, .cineraMenus > .menu .filter_container, .cineraMenus > .menu .views_container, .cineraMenus > .menu .link_container, .cineraMenus > .menu .credits_container";
var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector); var MenuContainerRule = GetOrSetRule(MenuContainerRuleSelector);
if(CineraProps.IsMobile) var cinera = document.querySelector(".cinera");
{ var player = new Player(cinera, onRefChanged);
InitMobileStyle();
}
else
{
var MenuMaxHeight = cinera.offsetHeight - titleBar.offsetHeight - 4;
MenuContainerRule.style.maxHeight = MenuMaxHeight + "px";
}
var player = new Player(playerContainer, onRefChanged);
if(CineraProps.IsMobile)
{
ConnectMobileControls(player);
}
var cineraViewStorageItem = "cineraView";
if(viewsMenu && localStorage.getItem(cineraViewStorageItem))
{
toggleTheatreMode();
}
InitScrollEventListener(cinera);
function
DelayedUpdateSize()
{
player.updateSize();
}
window.addEventListener("resize", function() { window.addEventListener("resize", function() {
if(CineraProps.IsMobile) if(CineraProps.IsMobile)
{ {
setTimeout(DelayedUpdateSize, 512); setTimeout(DelayedUpdateSize, 512, player);
} }
else else
{ {
@ -333,10 +42,10 @@ window.addEventListener("resize", function() {
} }
}); });
window.onorientationchange = function() { screen.orientation.onchange = function() {
if(CineraProps.IsMobile) if(CineraProps.IsMobile)
{ {
setTimeout(DelayedUpdateSize, 512); setTimeout(DelayedUpdateSize, 512, player);
} }
else else
{ {
@ -351,43 +60,17 @@ document.addEventListener("keydown", function(ev) {
key = "capitalSpace"; key = "capitalSpace";
} }
if(!ev.getModifierState("Control") && handleKey(key) == true && focusedElement) if(!ev.getModifierState("Control") && player.handleKey(key) == true && player.MenusFocused.Item)
{ {
ev.preventDefault(); ev.preventDefault();
} }
}); });
for(var i = 0; i < sourceMenus.length; ++i) document.addEventListener("fullscreenchange", function() {
{ if(!document.fullscreenElement && CineraProps.V == views.SUPERTHEATRE)
sourceMenus[i].addEventListener("mouseenter", function(ev) { {
handleMenuTogglerInteraction(this, ev.type); CineraProps.V = views.THEATRE;
}) localStorage.setItem(player.cineraViewStorageItem, views.THEATRE);
sourceMenus[i].addEventListener("mouseleave", function(ev) { player.updateSize();
handleMenuTogglerInteraction(this, ev.type); }
}) });
sourceMenus[i].addEventListener("click", function(ev) {
handleMenuTogglerInteraction(this, ev.type);
})
};
var colouredItems = playerContainer.querySelectorAll(".author, .member, .project");
for(i = 0; i < colouredItems.length; ++i)
{
setTextLightness(colouredItems[i]);
}
var topicDots = cinera.querySelectorAll(".category");
for(var i = 0; i < topicDots.length; ++i)
{
setDotLightness(topicDots[i]);
}
var lastTimestampStorageItem = "cineraTimecode_" + window.location.pathname;
var lastTimestamp;
if(location.hash) {
player.setTimeThenPlay(location.hash.startsWith('#') ? location.hash.substr(1) : location.hash);
}
else if(lastTimestamp = localStorage.getItem(lastTimestampStorageItem))
{
player.setTimeThenPlay(lastTimestamp);
}

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,9 @@ DeriveReliableWindowDimensions()
Y: null, Y: null,
}; };
var ScrollPosX = window.scrollX;
var ScrollPosY = window.scrollY;
var DisplaySettings = []; var DisplaySettings = [];
for(var i = 0; i < document.body.children.length; ++i) for(var i = 0; i < document.body.children.length; ++i)
{ {
@ -40,6 +43,9 @@ DeriveReliableWindowDimensions()
Child.style.display = DisplaySettings.shift(); Child.style.display = DisplaySettings.shift();
} }
ScrollTriggeredInternally = true;
window.scroll(ScrollPosX, ScrollPosY);
return Result; return Result;
} }
@ -76,7 +82,7 @@ function IsVisible(Element, WindowDim) {
function function
GetRealOrientation(PreferredLandscape, IsMobile) GetRealOrientation(PreferredLandscape, IsMobile)
{ {
var Result = window.orientation; var Result = screen.orientation.angle;
var WindowDim = GetWindowDim(IsMobile); var WindowDim = GetWindowDim(IsMobile);
if(WindowDim.Y > WindowDim.X) if(WindowDim.Y > WindowDim.X)
{ {
@ -186,14 +192,21 @@ function enableSprite(Element)
function disableSprite(Element) function disableSprite(Element)
{ {
if(Element.classList.contains("cineraSprite")) if(Element.classList.contains("focused"))
{ {
setSpriteLightness(Element); focusSprite(Element);
Element.style.backgroundPositionY = Element.getAttribute("data-y-disabled") + "px";
} }
for(var i = 0; i < Element.childElementCount; ++i) else
{ {
disableSprite(Element.children[i]); if(Element.classList.contains("cineraSprite"))
{
setSpriteLightness(Element);
Element.style.backgroundPositionY = Element.getAttribute("data-y-disabled") + "px";
}
for(var i = 0; i < Element.childElementCount; ++i)
{
disableSprite(Element.children[i]);
}
} }
} }
@ -310,6 +323,7 @@ function IsInRangeEx(Min, N, Max)
} }
/* Auto-scrolling */ /* Auto-scrolling */
var ScrollTriggeredInternally = false;
var LastScrollYPos = 0; var LastScrollYPos = 0;
var ScrollTicking = false; var ScrollTicking = false;
var ScrollerFunction; var ScrollerFunction;
@ -376,7 +390,11 @@ function
InitScrollEventListener(Element, IsMobile, StickyObscuringElement) InitScrollEventListener(Element, IsMobile, StickyObscuringElement)
{ {
window.addEventListener('scroll', function() { window.addEventListener('scroll', function() {
if(ScrollCondition == undefined || ScrollCondition == true) if(ScrollTriggeredInternally)
{
ScrollTriggeredInternally = false;
}
else if(ScrollCondition == undefined || ScrollCondition == true)
{ {
LastScrollYPos = window.scrollY; LastScrollYPos = window.scrollY;
@ -503,17 +521,29 @@ IsOverflowed(Element)
return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth; return Element.scrollHeight > Element.clientHeight || Element.scrollWidth > Element.clientWidth;
} }
function
SetHelpUnfocused(Button)
{
Button.firstElementChild.innerText = "¿";
Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button";
}
function
SetHelpFocused(Button)
{
Button.firstElementChild.innerText = "?";
Button.firstElementChild.title = ""
}
function function
BindHelp(Button, DocumentationContainer) BindHelp(Button, DocumentationContainer)
{ {
window.addEventListener("blur", function(){ window.addEventListener("blur", function(){
Button.firstElementChild.innerText = "¿"; SetHelpUnfocused(Button);
Button.firstElementChild.title = "Keypresses will not pass through to Cinera because focus is currently elsewhere.\n\nTo regain focus, please press Tab / Shift-Tab (multiple times) or click somewhere related to Cinera other than the video, e.g. this button";
}); });
window.addEventListener("focus", function(){ window.addEventListener("focus", function(){
Button.firstElementChild.innerText = "?"; SetHelpFocused(Button);
Button.firstElementChild.title = ""
}); });
Button.addEventListener("click", function() { Button.addEventListener("click", function() {
@ -521,45 +551,16 @@ BindHelp(Button, DocumentationContainer)
}) })
} }
function RGBtoHSL(colour)
{
var rgb = colour.slice(4, -1).split(", ");
var red = rgb[0];
var green = rgb[1];
var blue = rgb[2];
var min = Math.min(red, green, blue);
var max = Math.max(red, green, blue);
var chroma = max - min;
var hue = 0;
if(max == red)
{
hue = ((green - blue) / chroma) % 6;
}
else if(max == green)
{
hue = ((blue - red) / chroma) + 2;
}
else if(max == blue)
{
hue = ((red - green) / chroma) + 4;
}
var saturation = chroma / 255 * 100;
hue = (hue * 60) < 0 ? 360 + (hue * 60) : (hue * 60);
return [hue, saturation]
}
function getBackgroundColourRGB(element) { function getBackgroundColourRGB(element) {
var Colour = getComputedStyle(element).getPropertyValue("background-color"); var Colour = getComputedStyle(element).getPropertyValue("background-color");
var depth = 0; var depth = 0;
while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && depth <= 4) while((Colour == "transparent" || Colour == "rgba(0, 0, 0, 0)") && element.parentElement && depth <= 4)
{ {
element = element.parentNode; element = element.parentElement;
Colour = getComputedStyle(element).getPropertyValue("background-color"); Colour = getComputedStyle(element).getPropertyValue("background-color");
++depth; ++depth;
} }
var Staging = Colour.slice(4, -1).split(", "); var Staging = Colour.slice(Colour.indexOf("(") + 1, -1).split(", ");
var Result = { var Result = {
R: parseInt(Staging[0]), R: parseInt(Staging[0]),
G: parseInt(Staging[1]), G: parseInt(Staging[1]),
@ -580,28 +581,34 @@ function setTextLightness(textElement)
{ {
var textHue = textElement.getAttribute("data-hue"); var textHue = textElement.getAttribute("data-hue");
var textSaturation = textElement.getAttribute("data-saturation"); var textSaturation = textElement.getAttribute("data-saturation");
if(getBackgroundBrightness(textElement.parentNode) < 127) if(textHue && textSaturation)
{ {
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)"); if(getBackgroundBrightness(textElement.parentNode) < 127)
} {
else textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
{ }
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)"); else
{
textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
}
} }
} }
function setDotLightness(topicDot) function setDotLightness(topicDot)
{ {
var Hue = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[0]; var dotHue = topicDot.getAttribute("data-hue");
var Saturation = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[1]; var dotSaturation = topicDot.getAttribute("data-saturation");
if(getBackgroundBrightness(topicDot.parentNode) < 127) if(dotHue && dotSaturation)
{ {
topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)"); if(getBackgroundBrightness(topicDot.parentNode) < 127)
topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)"); {
} topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
else topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
{ }
topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)"); else
topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)"); {
topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
}
} }
} }

View File

@ -213,11 +213,11 @@ function prepareToParseIndexFile(project)
mode = "markers"; mode = "markers";
episode.markers = []; episode.markers = [];
} else if (mode == "markers") { } else if (mode == "markers") {
var match = line.match(/"(\d+)": "(.+)"/); var match = line.match(/"(\d+.\d+)": "(.+)"/);
if (match == null) { if (match == null) {
console.log(name, line); console.log(name, line);
} else { } else {
var totalTime = parseInt(line.slice(1)); var totalTime = parseFloat(line.slice(1));
var marker = { var marker = {
totalTime: totalTime, totalTime: totalTime,
prettyTime: markerTime(totalTime), prettyTime: markerTime(totalTime),
@ -292,7 +292,7 @@ function markerTime(totalTime) {
var markTime = "("; var markTime = "(";
var hours = Math.floor(totalTime / 60 / 60); var hours = Math.floor(totalTime / 60 / 60);
var minutes = Math.floor(totalTime / 60) % 60; var minutes = Math.floor(totalTime / 60) % 60;
var seconds = totalTime % 60; var seconds = Math.floor(totalTime) % 60;
if (hours > 0) { if (hours > 0) {
markTime += padTimeComponent(hours) + ":"; markTime += padTimeComponent(hours) + ":";
} }
@ -460,7 +460,7 @@ function runSearch(refresh) {
Search.ResultsSummary.style.display = "none"; Search.ResultsSummary.style.display = "none";
} }
var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + totalSeconds%60 + "s "; var totalTime = Math.floor(totalSeconds/60/60) + "h " + Math.floor(totalSeconds/60)%60 + "m " + Math.floor(totalSeconds)%60 + "s ";
Search.ResultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total."; Search.ResultsSummary.textContent = "Found: " + numEpisodes + " episodes, " + numMarkers + " markers, " + totalTime + "total.";
} }
@ -3866,7 +3866,7 @@ InitResizeEventListener()
function function
InitOrientationChangeListener() InitOrientationChangeListener()
{ {
window.onorientationchange = function() screen.orientation.onchange = function()
{ {
if(CineraProps.IsMobile) if(CineraProps.IsMobile)
{ {

View File

@ -76,7 +76,7 @@ typedef struct {
typedef struct { typedef struct {
int line; int line;
int h, m, s; int h, m, s, ms;
char* text; char* text;
char* author; char* author;
@ -471,7 +471,7 @@ next_attr:
static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts) static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
{ {
unsigned int h = 0, m = 0, s = 0; unsigned int h = 0, m = 0, s = 0, ms = 0;
int offset = 0; int offset = 0;
int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset); int count = sscanf(p->cursor, "[%u:%u%n", &m, &s, &offset);
@ -485,7 +485,7 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
if(c == ':') { if(c == ':') {
unsigned int tmp; unsigned int tmp;
offset = 0; offset = 0;
if(sscanf(p->cursor, ":%u]%n", &tmp, &offset) != 1 || offset == 0) { if(sscanf(p->cursor, ":%u%n", &tmp, &offset) != 1 || offset == 0) {
_hmml_err(p, "Unable to parse 3-part timecode"); _hmml_err(p, "Unable to parse 3-part timecode");
} }
@ -494,6 +494,27 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
s = tmp; s = tmp;
p->cursor += offset; p->cursor += offset;
c = *p->cursor;
}
if(c == '.') {
unsigned int tmp;
offset = 0;
int non_number_chars = 2;
int digits_in_100 = 3;
int max_chars_to_parse = non_number_chars + digits_in_100;
if(sscanf(p->cursor, ".%u]%n", &tmp, &offset) != 1 || offset == 0 || offset > max_chars_to_parse) {
_hmml_err(p, "Unable to parse %u.5-part timecode", h ? 3 : 2);
}
for(int i = offset - non_number_chars; i < digits_in_100; ++i) {
tmp *= 10;
}
ms = tmp;
p->cursor += offset;
} else if(c != ']') { } else if(c != ']') {
_hmml_err(p, "Unable to parse timecode"); _hmml_err(p, "Unable to parse timecode");
@ -501,6 +522,10 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
++p->cursor; ++p->cursor;
} }
if(ms >= 1000) {
_hmml_err(p, "Milliseconds cannot exceed 999");
}
if(s >= 60) { if(s >= 60) {
_hmml_err(p, "Seconds cannot exceed 59"); _hmml_err(p, "Seconds cannot exceed 59");
} }
@ -512,6 +537,7 @@ static void _hmml_parse_timecode(struct _hmml_parser* p, HMML_Timestamp* ts)
ts->h = h; ts->h = h;
ts->m = m; ts->m = m;
ts->s = s; ts->s = s;
ts->ms = ms;
} }
static void _hmml_store_marker(struct _hmml_parser* p, HMML_Timestamp* ts, char** out, char* text_mem, size_t text_mem_size) static void _hmml_store_marker(struct _hmml_parser* p, HMML_Timestamp* ts, char** out, char* text_mem, size_t text_mem_size)
@ -824,7 +850,7 @@ void hmml_free(HMML_Output* out)
} }
const struct HMML_Version hmml_version = { const struct HMML_Version hmml_version = {
2, 0, 14 2, 0, 15
}; };
#undef HSTX #undef HSTX