|
OpenTTD Source
14.0-beta1
|
Go to the documentation of this file.
25 #include "table/strings.h"
28 #if defined(WITH_ZLIB)
32 #if defined(WITH_LIBLZMA)
90 void TextfileWindow::ConstructWindow()
95 this->GetWidget<NWidgetCore>(
WID_TF_CAPTION)->SetDataTip(STR_TEXTFILE_README_CAPTION + this->
file_type, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
112 for (
auto &line : this->
lines) {
119 for (
auto &line : this->
lines) {
129 uint TextfileWindow::GetContentHeight()
131 if (this->
lines.empty())
return 0;
132 return this->
lines.back().bottom;
142 size->width = std::max(200u, size->width);
166 static const std::regex
_markdown_link_regex{
"\\[(.+?)\\]\\((.+?)\\)", std::regex_constants::ECMAScript | std::regex_constants::optimize};
207 for (
char c : line) {
210 if (c ==
'#')
continue;
211 if (c ==
' ')
continue;
217 if (c ==
' ' || c ==
'-')
continue;
228 }
else if (c ==
' ' || c ==
'-') {
245 std::string::const_iterator last_match_end = line.
text.cbegin();
246 std::string fixed_line;
250 while (matcher != std::sregex_iterator()) {
251 std::smatch match = *matcher;
254 link.
line = line_index;
256 this->
links.push_back(link);
262 link_colour = SCC_GREEN;
265 link_colour = SCC_LTBLUE;
268 link_colour = SCC_LTBROWN;
272 link_colour = SCC_CONTROL_END;
276 if (link_colour != SCC_CONTROL_END) {
278 fixed_line += std::string(last_match_end, match[0].first);
279 this->
links.back().begin = fixed_line.length();
280 fixed_line += std::string(ccbuf,
Utf8Encode(ccbuf, SCC_PUSH_COLOUR));
281 fixed_line += std::string(ccbuf,
Utf8Encode(ccbuf, link_colour));
282 fixed_line += match[1].str();
283 this->
links.back().end = fixed_line.length();
284 fixed_line += std::string(ccbuf,
Utf8Encode(ccbuf, SCC_POP_COLOUR));
285 last_match_end = match[0].second;
291 if (last_match_end == line.
text.cbegin())
return;
294 fixed_line += std::string(last_match_end, line.
text.cend());
297 line.
text = fixed_line;
307 if (this->
links.empty())
return;
314 auto it = std::find_if(std::begin(this->
lines), std::end(this->
lines), [clicked_row](
const Line &l) {
return l.
top <= clicked_row && l.
bottom > clicked_row; });
315 if (it == this->
lines.cend())
return;
316 line_index = it - this->
lines.cbegin();
317 subline = clicked_row - it->top;
318 Debug(misc, 4,
"TextfileWindow check hyperlink: clicked_row={}, line_index={}, line.top={}, subline={}", clicked_row, line_index, it->top, subline);
325 std::vector<Hyperlink> found_links;
326 for (
const auto &link : this->
links) {
327 if (link.line == line_index) found_links.push_back(link);
329 if (found_links.empty())
return;
334 assert(subline < layout.size());
336 if (char_index < 0)
return;
337 Debug(misc, 4,
"TextfileWindow check hyperlink click: line={}, subline={}, char_index={}", line_index, subline, (
int)char_index);
340 for (
const auto &link : found_links) {
341 Debug(misc, 4,
"Checking link from char {} to {}", link.begin, link.end);
342 if ((
size_t)char_index >= link.begin && (
size_t)char_index < link.end) {
343 Debug(misc, 4,
"Activating link with destination: {}", link.destination);
357 this->
history.erase(this->
history.begin() + this->history_pos + 1, this->history.end());
380 if (delta == 0)
return;
381 if (delta < 0 &&
static_cast<int>(this->
history_pos) < -delta)
return;
405 auto it = std::find_if(this->
link_anchors.cbegin(), this->link_anchors.cend(), [&](
const Hyperlink &other) { return link.destination == other.destination; });
408 this->ScrollToLine(it->line);
437 if (!newfile.starts_with(
"./"))
return;
440 std::string newpath = this->
filepath;
441 size_t pos = newpath.find_last_of(PATHSEPCHAR);
442 if (pos == std::string::npos) {
445 newpath.erase(pos + 1);
449 size_t anchor_pos = newfile.find_first_of(
'#');
451 if (anchor_pos != std::string::npos) {
452 anchor = newfile.substr(anchor_pos);
453 newfile.erase(anchor_pos);
460 newfile = newfile.substr(2);
461 if (PATHSEPCHAR !=
'/') {
462 for (
char &c : newfile) {
463 if (c ==
'/') c = PATHSEPCHAR;
468 newpath = newpath + newfile;
476 this->
filename = newpath.substr(newpath.find_last_of(PATHSEP) + 1);
483 if (anchor.empty() || line != 0) {
484 this->ScrollToLine(line);
486 auto anchor_dest = std::find_if(this->
link_anchors.cbegin(), this->link_anchors.cend(), [&](
const Hyperlink &other) { return anchor == other.destination; });
488 this->ScrollToLine(anchor_dest->line);
491 this->ScrollToLine(0);
510 for (
size_t line_index = 0; line_index < this->
lines.size(); ++line_index) {
517 if (!line.
text.empty() && line.
text[0] ==
'#') {
518 this->
jumplist.push_back(line_index);
519 this->
lines[line_index].colour = TC_GOLD;
525 void TextfileWindow::OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
535 for (
size_t line : this->
jumplist) {
537 list.push_back(std::make_unique<DropDownListStringItem>(STR_TEXTFILE_JUMPLIST_ITEM, (
int)line,
false));
557 void TextfileWindow::DrawWidget(
const Rect &r,
WidgetID widget)
const
573 for (
auto &line : this->
lines) {
574 if (line.bottom < pos)
continue;
575 if (line.top > pos + cap)
break;
577 int y_offset = (line.top - pos) * line_height;
594 void TextfileWindow::OnInvalidateData([[maybe_unused]]
int data, [[maybe_unused]]
bool gui_scope)
596 if (!gui_scope)
return;
601 void TextfileWindow::OnDropdownSelect(
WidgetID widget,
int index)
605 this->ScrollToLine(index);
608 void TextfileWindow::ScrollToLine(
size_t line)
613 newpos = this->lines[line].top;
615 newpos =
static_cast<int>(line);
645 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
651 #if defined(WITH_ZLIB)
667 static void Gunzip(
byte **bufp,
size_t *sizep)
669 static const int BLOCKSIZE = 8192;
671 size_t alloc_size = 0;
675 memset(&z, 0,
sizeof(z));
677 z.avail_in = (uInt)*sizep;
680 res = inflateInit2(&z, 15 + 32);
682 while (res == Z_OK || (res == Z_BUF_ERROR && z.avail_out == 0)) {
685 alloc_size += BLOCKSIZE;
686 z.avail_out += BLOCKSIZE;
688 z.next_out = buf + alloc_size - z.avail_out;
689 res = inflate(&z, Z_FINISH);
695 if (res == Z_STREAM_END) {
697 *sizep = alloc_size - z.avail_out;
707 #if defined(WITH_LIBLZMA)
723 static void Xunzip(
byte **bufp,
size_t *sizep)
725 static const int BLOCKSIZE = 8192;
727 size_t alloc_size = 0;
728 lzma_stream z = LZMA_STREAM_INIT;
734 res = lzma_auto_decoder(&z, UINT64_MAX, LZMA_CONCATENATED);
736 while (res == LZMA_OK || (res == LZMA_BUF_ERROR && z.avail_out == 0)) {
739 alloc_size += BLOCKSIZE;
740 z.avail_out += BLOCKSIZE;
742 z.next_out = buf + alloc_size - z.avail_out;
743 res = lzma_code(&z, LZMA_FINISH);
749 if (res == LZMA_STREAM_END) {
751 *sizep = alloc_size - z.avail_out;
768 this->jumplist.clear();
772 if (textfile.empty())
return;
776 FILE *handle =
FioFOpenFile(textfile,
"rb", dir, &filesize);
777 if (handle ==
nullptr)
return;
779 if (filesize == 0)
return;
781 char *buf = MallocT<char>(filesize);
782 size_t read = fread(buf, 1, filesize, handle);
785 if (read != filesize) {
790 #if defined(WITH_ZLIB)
792 if (textfile.ends_with(
".gz"))
Gunzip((
byte**)&buf, &filesize);
795 #if defined(WITH_LIBLZMA)
797 if (textfile.ends_with(
".xz"))
Xunzip((
byte**)&buf, &filesize);
800 if (buf ==
nullptr)
return;
802 std::string_view sv_buf(buf, filesize);
805 if (sv_buf.starts_with(
"\ufeff")) sv_buf.remove_prefix(3);
831 std::string_view p(text);
833 auto next = p.find_first_of(
'\n');
834 while (next != std::string_view::npos) {
835 this->lines.emplace_back(row, p.substr(0, next));
836 p.remove_prefix(next + 1);
839 next = p.find_first_of(
'\n');
841 this->lines.emplace_back(row, p);
845 for (
auto &line : this->lines) {
864 static const char *
const prefixes[] = {
869 static_assert(
lengthof(prefixes) == TFT_CONTENT_END);
872 if (type >= TFT_CONTENT_END)
return std::nullopt;
874 std::string_view prefix = prefixes[type];
876 if (filename.empty())
return std::nullopt;
878 auto slash = filename.find_last_of(PATHSEPCHAR);
879 if (slash == std::string::npos)
return std::nullopt;
881 std::string_view base_path(filename.data(), slash + 1);
883 static const std::initializer_list<std::string_view> extensions{
886 #if defined(WITH_ZLIB)
890 #if defined(WITH_LIBLZMA)
896 for (
auto &extension : extensions) {
903 file_path = fmt::format(
"{}{}.{}", base_path, prefix, extension);
FontSize DefaultSize() override
Get the default (font) size of the string.
virtual void OnHyperlinkClick(const Hyperlink &link)
Handle the clicking on a hyperlink.
std::vector< HistoryEntry > history
Browsing history in this window.
@ SVS_ALLOW_NEWLINE
Allow newlines; replaces '\r ' with ' ' during processing.
const char * GetCurrentLanguageIsoCode()
Get the ISO language code of the currently loaded language.
void AfterLoadMarkdown()
Post-processing of markdown files.
Dimensions (a width and height) of a rectangle in 2D.
@ Internal
Internal link, or "anchor" in HTML language.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
static void Xunzip(byte **bufp, size_t *sizep)
Do an in-memory xunzip operation.
int bottom
Bottom scroll position in visual lines.
static void Gunzip(byte **bufp, size_t *sizep)
Do an in-memory gunzip operation.
uint ReflowContent()
Get the total height of the content displayed in this window, if wrapping is disabled.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
HyperlinkType
Types of link we support in markdown files.
void OnResize() override
Called after the window got resized.
void EnableWidget(WidgetID widget_index)
Sets a widget to Enabled.
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
bool StrEndsWithIgnoreCase(std::string_view str, const std::string_view suffix)
Check whether the given string ends with the given suffix, ignoring case.
static std::string MakeAnchorSlug(const std::string &line)
Create a valid slug for the anchor.
int top
Top scroll position in visual lines.
void SetFontNames(FontCacheSettings *settings, const char *font_name, const void *os_data) override
Set the right font names.
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
TextfileType file_type
Type of textfile to view.
@ File
Link to a local file.
void CheckHyperlinkClick(Point pt)
Check if the user clicked on a hyperlink, and handle it if so.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
T * ReallocT(T *t_ptr, size_t num_elements)
Simplified reallocation function that allocates the specified number of elements of the given type.
void NavigateHistory(int delta)
Navigate through the history, either forward or backward.
High level window description.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
std::optional< std::string_view > NextString() override
Get the next string to search through.
size_t history_pos
Position in browsing history (for forward movement).
Settings for the four different fonts.
ResizeInfo resize
Resize information.
@ SA_TOP
Top align the text.
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
virtual void AfterLoadText()
Post-processing after the text is loaded.
int height
Height of the window (number of pixels down in y direction)
void free(const void *ptr)
Version of the standard free that accepts const pointers.
FILE * FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
std::optional< std::string > GetTextfile(TextfileType type, Subdirectory dir, const std::string &filename)
Search a textfile file next to the given content.
std::vector< Hyperlink > links
Clickable links in lines.
The layouter performs all the layout work.
std::string destination
Destination for the link.
void LoadText(std::string_view buf)
Load a text into the textfile viewer.
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
virtual void LoadTextfile(const std::string &textfile, Subdirectory dir)
Loads the textfile text from file and setup lines.
void SetupScrollbars(bool force_reflow)
Set scrollbars to the right lengths.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
fluid_settings_t * settings
FluidSynth settings handle.
static HyperlinkType ClassifyHyperlink(const std::string &destination, bool trusted)
Classify the type of hyperlink the destination describes.
Coordinates of a point in 2D.
static WindowDesc _textfile_desc(__FILE__, __LINE__, WDP_CENTER, "textfile", 630, 460, WC_TEXTFILE, WC_NONE, 0, std::begin(_nested_textfile_widgets), std::end(_nested_textfile_widgets))
Window definition for the textfile window.
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
static constexpr NWidgetPart _nested_textfile_widgets[]
Widgets for the textfile window.
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
void FindHyperlinksInMarkdown(Line &line, size_t line_index)
Find any hyperlinks in a given line.
std::vector< size_t > jumplist
Table of contents list, line numbers.
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Scrollbar * hscroll
Horizontal scrollbar.
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
uint max_length
Maximum length of unwrapped text line.
void Reset() override
Reset the search, i.e.
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
uint search_iterator
Iterator for the font check search.
void UpdateHistoryScrollpos()
Update the scroll position to the current, so we can restore there if we go back.
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close)
Show a drop down list.
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
Check whether the given file exists.
std::string filepath
Full path to the filename.
void DisableWidget(WidgetID widget_index)
Sets a widget to disabled.
@ NO_DIRECTORY
A path without any base directory.
StringControlCode
List of string control codes used for string formatting, displaying, and by strgen to generate the la...
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
bool Monospace() override
Whether to search for a monospace font or not.
std::vector< Line > lines
#text, split into lines in a table with lines.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
void AppendHistory(const std::string &filepath)
Append the new location to the history, so the user can go back.
@ SA_LEFT
Left align the text.
void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
Check whether the currently loaded language pack uses characters that the currently loaded font does ...
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
std::vector< Hyperlink > link_anchors
Anchor names of headings that can be linked to.
#define lengthof(x)
Return the length of an fixed size array.
size_t line
Which line the link is on.
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
@ FS_MONO
Index of the monospaced font in the font tables.
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
std::string text
Contents of the line.
FontSize
Available font sizes.
size_t Utf8Encode(T buf, char32_t c)
Encode a unicode character and place it in the buffer.
Data structure for an opened window.
TextfileType
Additional text files accompanying Tar archives.
@ SVS_REPLACE_TAB_CR_NL_WITH_SPACE
Replace tabs ('\t'), carriage returns ('\r') and newlines (' ') with spaces.
void NavigateToFile(std::string newfile, size_t line)
Navigate to the requested file.
std::string filename
Filename of the textfile.
static const std::regex _markdown_link_regex
Regular expression that searches for Markdown links.
Specification of a rectangle with absolute coordinates of all edges.
void ToggleWidgetLoweredState(WidgetID widget_index)
Invert the lowered/raised status of a widget.
@ WC_TEXTFILE
textfile; Window numbers:
@ SVS_REPLACE_WITH_QUESTION_MARK
Replace the unknown/bad bits with question marks.
@ WDP_CENTER
Center the window.
@ Web
Link to an external website.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Scrollbar * vscroll
Vertical scrollbar.
Data about how and where to blit pixels.
ptrdiff_t GetCharAtPosition(int x, size_t line_index) const
Get the character that is at a pixel position in the first line of the layouted text.