changeset 19078:8cb1275f2bb3 draft

(svn r23932) -Codechange: split the NewGRF text window into its own source files
author rubidium <rubidium@openttd.org>
date Sun, 12 Feb 2012 10:32:41 +0000
parents 400747be7966
children 0af594336a81
files projects/openttd_vs100.vcxproj projects/openttd_vs100.vcxproj.filters projects/openttd_vs80.vcproj projects/openttd_vs90.vcproj source.list src/newgrf_config.h src/newgrf_gui.cpp src/script/api/game/game_window.hpp.sq src/script/api/script_window.hpp src/script/api/template/template_window.hpp.sq src/textfile_gui.cpp src/textfile_gui.h src/textfile_type.h src/widgets/misc_widget.h src/widgets/newgrf_widget.h src/window.cpp src/window_type.h
diffstat 17 files changed, 352 insertions(+), 236 deletions(-) [+]
line wrap: on
line diff
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -573,6 +573,8 @@
     <ClInclude Include="..\src\terraform_gui.h" />
     <ClInclude Include="..\src\textbuf_gui.h" />
     <ClInclude Include="..\src\texteff.hpp" />
+    <ClInclude Include="..\src\textfile_gui.h" />
+    <ClInclude Include="..\src\textfile_type.h" />
     <ClInclude Include="..\src\tgp.h" />
     <ClInclude Include="..\src\tile_cmd.h" />
     <ClInclude Include="..\src\tile_type.h" />
@@ -684,6 +686,7 @@
     <ClCompile Include="..\src\statusbar_gui.cpp" />
     <ClCompile Include="..\src\subsidy_gui.cpp" />
     <ClCompile Include="..\src\terraform_gui.cpp" />
+    <ClCompile Include="..\src\textfile_gui.cpp" />
     <ClCompile Include="..\src\timetable_gui.cpp" />
     <ClCompile Include="..\src\toolbar_gui.cpp" />
     <ClCompile Include="..\src\town_gui.cpp" />
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -948,6 +948,12 @@
     <ClInclude Include="..\src\texteff.hpp">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\textfile_gui.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\textfile_type.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\tgp.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -1281,6 +1287,9 @@
     <ClCompile Include="..\src\terraform_gui.cpp">
       <Filter>GUI Source Code</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\textfile_gui.cpp">
+      <Filter>GUI Source Code</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\timetable_gui.cpp">
       <Filter>GUI Source Code</Filter>
     </ClCompile>
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -1567,6 +1567,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\textfile_gui.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\textfile_type.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\tgp.h"
 				>
 			</File>
@@ -2019,6 +2027,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\textfile_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\timetable_gui.cpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -1564,6 +1564,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\textfile_gui.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\textfile_type.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\tgp.h"
 				>
 			</File>
@@ -2016,6 +2024,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\textfile_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\timetable_gui.cpp"
 				>
 			</File>
--- a/source.list
+++ b/source.list
@@ -306,6 +306,8 @@
 terraform_gui.h
 textbuf_gui.h
 texteff.hpp
+textfile_gui.h
+textfile_type.h
 tgp.h
 tile_cmd.h
 tile_type.h
@@ -436,6 +438,7 @@
 statusbar_gui.cpp
 subsidy_gui.cpp
 terraform_gui.cpp
+textfile_gui.cpp
 timetable_gui.cpp
 toolbar_gui.cpp
 town_gui.cpp
--- a/src/newgrf_config.h
+++ b/src/newgrf_config.h
@@ -17,6 +17,7 @@
 #include "core/smallmap_type.hpp"
 #include "misc/countedptr.hpp"
 #include "fileio_type.h"
+#include "textfile_type.h"
 
 /** GRF config bit flags */
 enum GCF_Flags {
@@ -145,18 +146,6 @@
 	~GRFTextWrapper();
 };
 
-/** Additional text files accompanying NewGRFs */
-enum TextfileType {
-	TFT_BEGIN,
-
-	TFT_README = TFT_BEGIN,  ///< NewGRF readme
-	TFT_CHANGELOG,           ///< NewGRF changelog
-	TFT_LICENSE,             ///< NewGRF license
-
-	TFT_END,
-};
-DECLARE_POSTFIX_INCREMENT(TextfileType)
-
 /** Information about GRF, used in the game and (part of it) in savegames */
 struct GRFConfig : ZeroedMemoryAllocator {
 	GRFConfig(const char *filename = NULL);
--- a/src/newgrf_gui.cpp
+++ b/src/newgrf_gui.cpp
@@ -25,10 +25,10 @@
 #include "querystring_gui.h"
 #include "core/geometry_func.hpp"
 #include "newgrf_text.h"
-#include "fileio_func.h"
-#include "fontcache.h"
+#include "textfile_gui.h"
 
 #include "widgets/newgrf_widget.h"
+#include "widgets/misc_widget.h"
 
 #include "table/sprites.h"
 
@@ -465,207 +465,27 @@
 }
 
 /** Window for displaying the textfile of a NewGRF. */
-struct NewGRFTextfileWindow : public Window, MissingGlyphSearcher {
-	const GRFConfig *grf_config;         ///< View the textfile of this GRFConfig.
-	TextfileType file_type;              ///< Type of textfile to view.
-	int line_height;                     ///< Height of a line in the display widget.
-	Scrollbar *vscroll;                  ///< Vertical scrollbar.
-	Scrollbar *hscroll;                  ///< Horizontal scrollbar.
-	char *text;                          ///< Lines of text from the NewGRF's textfile.
-	SmallVector<const char *, 64> lines; ///< #text, split into lines in a table with lines.
-	uint max_length;                     ///< The longest line in the textfile (in pixels).
-
-	static const int TOP_SPACING    = WD_FRAMETEXT_TOP;    ///< Additional spacing at the top of the #WID_NT_BACKGROUND widget.
-	static const int BOTTOM_SPACING = WD_FRAMETEXT_BOTTOM; ///< Additional spacing at the bottom of the #WID_NT_BACKGROUND widget.
-
-	NewGRFTextfileWindow(const WindowDesc *desc, const GRFConfig *c, TextfileType file_type) : Window(), grf_config(c), file_type(file_type)
-	{
-		this->CreateNestedTree(desc);
-		this->GetWidget<NWidgetCore>(WID_NT_CAPTION)->SetDataTip(STR_NEWGRF_README_CAPTION + file_type, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
-		this->vscroll = this->GetScrollbar(WID_NT_VSCROLLBAR);
-		this->hscroll = this->GetScrollbar(WID_NT_HSCROLLBAR);
-		this->FinishInitNested(desc);
-
-		this->LoadTextfile();
-	}
-
-	~NewGRFTextfileWindow()
-	{
-		free(this->text);
-	}
-
-	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
-	{
-		switch (widget) {
-			case WID_NT_BACKGROUND:
-				this->line_height = FONT_HEIGHT_MONO + 2;
-				resize->height = this->line_height;
+struct NewGRFTextfileWindow : public TextfileWindow {
+	const GRFConfig *grf_config; ///< View the textfile of this GRFConfig.
 
-				size->height = 4 * resize->height + TOP_SPACING + BOTTOM_SPACING; // At least 4 lines are visible.
-				size->width = max(200u, size->width); // At least 200 pixels wide.
-				break;
-		}
-	}
-
-	virtual void SetStringParameters(int widget) const
+	NewGRFTextfileWindow(TextfileType file_type, const GRFConfig *c) : TextfileWindow(file_type), grf_config(c)
 	{
-		if (widget == WID_NT_CAPTION) SetDParamStr(0, this->grf_config->GetName());
-	}
-
-	virtual void DrawWidget(const Rect &r, int widget) const
-	{
-		if (widget != WID_NT_BACKGROUND) return;
-
-		int width = r.right - r.left + 1 - WD_BEVEL_LEFT - WD_BEVEL_RIGHT;
-		int height = r.bottom - r.top + 1 - WD_BEVEL_LEFT - WD_BEVEL_RIGHT;
+		this->GetWidget<NWidgetCore>(WID_TF_CAPTION)->SetDataTip(STR_NEWGRF_README_CAPTION + file_type, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS);
 
-		DrawPixelInfo new_dpi;
-		if (!FillDrawPixelInfo(&new_dpi, r.left + WD_BEVEL_LEFT, r.top, width, height)) return;
-		DrawPixelInfo *old_dpi = _cur_dpi;
-		_cur_dpi = &new_dpi;
-
-		int left, right;
-		if (_current_text_dir == TD_RTL) {
-			left = width + WD_BEVEL_RIGHT - WD_FRAMETEXT_RIGHT - this->hscroll->GetCount();
-			right = width + WD_BEVEL_RIGHT - WD_FRAMETEXT_RIGHT - 1 + this->hscroll->GetPosition();
-		} else {
-			left = WD_FRAMETEXT_LEFT - WD_BEVEL_LEFT - this->hscroll->GetPosition();
-			right = WD_FRAMETEXT_LEFT - WD_BEVEL_LEFT + this->hscroll->GetCount() - 1;
-		}
-		int top = TOP_SPACING;
-		for (uint i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->lines.Length(); i++) {
-			DrawString(left, right, top + i * this->line_height, this->lines[i + this->vscroll->GetPosition()], TC_WHITE, SA_LEFT, false, FS_MONO);
-		}
-
-		_cur_dpi = old_dpi;
-	}
-
-	virtual void OnResize()
-	{
-		this->vscroll->SetCapacityFromWidget(this, WID_NT_BACKGROUND, TOP_SPACING + BOTTOM_SPACING);
-		this->hscroll->SetCapacityFromWidget(this, WID_NT_BACKGROUND);
+		const char *textfile = this->grf_config->GetTextfile(file_type);
+		this->LoadTextfile(textfile, NEWGRF_DIR);
 	}
 
-private:
-	uint search_iterator; ///< Iterator for the font check search.
-
-	/* virtual */ void Reset()
-	{
-		this->search_iterator = 0;
-	}
-
-	FontSize DefaultSize()
-	{
-		return FS_MONO;
-	}
-
-	const char *NextString()
-	{
-		if (this->search_iterator >= this->lines.Length()) return NULL;
-
-		return this->lines[this->search_iterator++];
-	}
-
-	/* virtual */ bool Monospace()
-	{
-		return true;
-	}
-
-	/* virtual */ void SetFontNames(FreeTypeSettings *settings, const char *font_name)
-	{
-#ifdef WITH_FREETYPE
-		strecpy(settings->mono_font, font_name, lastof(settings->mono_font));
-#endif /* WITH_FREETYPE */
-	}
-
-	/**
-	 * Load the NewGRF's textfile text from file, and setup #lines, #max_length, and both scrollbars.
-	 */
-	void LoadTextfile()
+	/* virtual */ void SetStringParameters(int widget) const
 	{
-		this->lines.Clear();
-
-		/* Does GRF have a file of the demanded type? */
-		const char *textfile = this->grf_config->GetTextfile(file_type);
-		if (textfile == NULL) return;
-
-		/* Get text from file */
-		size_t filesize;
-		FILE *handle = FioFOpenFile(textfile, "rb", NEWGRF_DIR, &filesize);
-		if (handle == NULL) return;
-
-		this->text = ReallocT(this->text, filesize + 1);
-		size_t read = fread(this->text, 1, filesize, handle);
-		fclose(handle);
-
-		if (read != filesize) return;
-
-		this->text[filesize] = '\0';
-
-		/* Replace tabs and line feeds with a space since str_validate removes those. */
-		for (char *p = this->text; *p != '\0'; p++) {
-			if (*p == '\t' || *p == '\r') *p = ' ';
-		}
-
-		/* Check for the byte-order-mark, and skip it if needed. */
-		char *p = this->text + (strncmp("\xEF\xBB\xBF", this->text, 3) == 0 ? 3 : 0);
-
-		/* Make sure the string is a valid UTF-8 sequence. */
-		str_validate(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
-
-		/* Split the string on newlines. */
-		*this->lines.Append() = p;
-		for (; *p != '\0'; p++) {
-			if (*p == '\n') {
-				*p = '\0';
-				*this->lines.Append() = p + 1;
-			}
-		}
-
-		CheckForMissingGlyphs(true, this);
-
-		/* Initialize scrollbars */
-		this->vscroll->SetCount(this->lines.Length());
-
-		this->max_length = 0;
-		for (uint i = 0; i < this->lines.Length(); i++) {
-			this->max_length = max(this->max_length, GetStringBoundingBox(this->lines[i], FS_MONO).width);
-		}
-		this->hscroll->SetCount(this->max_length + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT);
-		this->hscroll->SetStepSize(10); // Speed up horizontal scrollbar
+		if (widget == WID_TF_CAPTION) SetDParamStr(0, this->grf_config->GetName());
 	}
 };
 
-static const NWidgetPart _nested_newgrf_textfile_widgets[] = {
-	NWidget(NWID_HORIZONTAL),
-		NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
-		NWidget(WWT_CAPTION, COLOUR_MAUVE, WID_NT_CAPTION), SetDataTip(STR_NULL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
-	EndContainer(),
-	NWidget(NWID_HORIZONTAL),
-		NWidget(WWT_PANEL, COLOUR_MAUVE, WID_NT_BACKGROUND), SetMinimalSize(200, 125), SetResize(1, 12), SetScrollbar(WID_NT_VSCROLLBAR),
-		EndContainer(),
-		NWidget(NWID_VERTICAL),
-			NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_NT_VSCROLLBAR),
-		EndContainer(),
-	EndContainer(),
-	NWidget(NWID_HORIZONTAL),
-		NWidget(NWID_HSCROLLBAR, COLOUR_MAUVE, WID_NT_HSCROLLBAR),
-		NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
-	EndContainer(),
-};
-
-/** Window definition for the grf textfile window */
-static const WindowDesc _newgrf_textfile_desc(
-	WDP_CENTER, 630, 460,
-	WC_NEWGRF_TEXTFILE, WC_NONE,
-	WDF_UNCLICK_BUTTONS,
-	_nested_newgrf_textfile_widgets, lengthof(_nested_newgrf_textfile_widgets)
-);
-
-void ShowNewGRFTextfileWindow(const GRFConfig *c, TextfileType file_type)
+void ShowNewGRFTextfileWindow(TextfileType file_type, const GRFConfig *c)
 {
-	DeleteWindowByClass(WC_NEWGRF_TEXTFILE);
-	new NewGRFTextfileWindow(&_newgrf_textfile_desc, c, file_type);
+	DeleteWindowByClass(WC_TEXTFILE);
+	new NewGRFTextfileWindow(file_type, c);
 }
 
 static GRFPresetList _grf_preset_list;
@@ -756,7 +576,7 @@
 	~NewGRFWindow()
 	{
 		DeleteWindowByClass(WC_GRF_PARAMETERS);
-		DeleteWindowByClass(WC_NEWGRF_TEXTFILE);
+		DeleteWindowByClass(WC_TEXTFILE);
 
 		if (this->editable && !this->execute) {
 			CopyGRFConfigList(this->orig_list, this->actives, true);
@@ -962,7 +782,7 @@
 		if (widget >= WID_NS_NEWGRF_TEXTFILE && widget < WID_NS_NEWGRF_TEXTFILE + TFT_END) {
 			if (this->active_sel == NULL && this->avail_sel == NULL) return;
 
-			ShowNewGRFTextfileWindow(this->active_sel != NULL ? this->active_sel : this->avail_sel, (TextfileType)(widget - WID_NS_NEWGRF_TEXTFILE));
+			ShowNewGRFTextfileWindow((TextfileType)(widget - WID_NS_NEWGRF_TEXTFILE), this->active_sel != NULL ? this->active_sel : this->avail_sel);
 			return;
 		}
 
@@ -1185,8 +1005,8 @@
 		this->avail_sel = NULL;
 		this->avail_pos = -1;
 		this->avails.ForceRebuild();
-		this->DeleteChildWindows(WC_QUERY_STRING);  // Remove the parameter query window
-		this->DeleteChildWindows(WC_NEWGRF_TEXTFILE); // Remove the view textfile window
+		this->DeleteChildWindows(WC_QUERY_STRING); // Remove the parameter query window
+		this->DeleteChildWindows(WC_TEXTFILE);     // Remove the view textfile window
 	}
 
 	virtual void OnDropdownSelect(int widget, int index)
--- a/src/script/api/game/game_window.hpp.sq
+++ b/src/script/api/game/game_window.hpp.sq
@@ -59,7 +59,7 @@
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_SET_DATE,                               "WC_SET_DATE");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_AI_SETTINGS,                            "WC_AI_SETTINGS");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_GRF_PARAMETERS,                         "WC_GRF_PARAMETERS");
-	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_NEWGRF_TEXTFILE,                        "WC_NEWGRF_TEXTFILE");
+	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_TEXTFILE,                               "WC_TEXTFILE");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_TOWN_AUTHORITY,                         "WC_TOWN_AUTHORITY");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_VEHICLE_DETAILS,                        "WC_VEHICLE_DETAILS");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WC_VEHICLE_REFIT,                          "WC_VEHICLE_REFIT");
@@ -750,10 +750,6 @@
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NP_RESET,                              "WID_NP_RESET");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NP_SHOW_DESCRIPTION,                   "WID_NP_SHOW_DESCRIPTION");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NP_DESCRIPTION,                        "WID_NP_DESCRIPTION");
-	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NT_CAPTION,                            "WID_NT_CAPTION");
-	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NT_BACKGROUND,                         "WID_NT_BACKGROUND");
-	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NT_VSCROLLBAR,                         "WID_NT_VSCROLLBAR");
-	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NT_HSCROLLBAR,                         "WID_NT_HSCROLLBAR");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NS_PRESET_LIST,                        "WID_NS_PRESET_LIST");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NS_PRESET_SAVE,                        "WID_NS_PRESET_SAVE");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NS_PRESET_DELETE,                      "WID_NS_PRESET_DELETE");
@@ -782,6 +778,10 @@
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_NS_SHOW_APPLY,                         "WID_NS_SHOW_APPLY");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SP_PROGRESS_BAR,                       "WID_SP_PROGRESS_BAR");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_SP_PROGRESS_TEXT,                      "WID_SP_PROGRESS_TEXT");
+	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TF_CAPTION,                            "WID_TF_CAPTION");
+	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TF_BACKGROUND,                         "WID_TF_BACKGROUND");
+	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TF_VSCROLLBAR,                         "WID_TF_VSCROLLBAR");
+	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TF_HSCROLLBAR,                         "WID_TF_HSCROLLBAR");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_N_PANEL,                               "WID_N_PANEL");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_N_TITLE,                               "WID_N_TITLE");
 	SQGSWindow.DefSQConst(engine, ScriptWindow::WID_N_HEADLINE,                            "WID_N_HEADLINE");
--- a/src/script/api/script_window.hpp
+++ b/src/script/api/script_window.hpp
@@ -250,7 +250,7 @@
 		 * textfile; Window numbers:
 		 *   - 0 = #NewGRFTextfileWidgets
 		 */
-		WC_NEWGRF_TEXTFILE = ::WC_NEWGRF_TEXTFILE,
+		WC_TEXTFILE = ::WC_TEXTFILE,
 
 
 		/**
@@ -1671,14 +1671,6 @@
 		WID_NP_DESCRIPTION                   = ::WID_NP_DESCRIPTION,                   ///< Multi-line description of a parameter.
 	};
 
-	/** Widgets of the #NewGRFTextfileWindow class. */
-	enum NewGRFTextfileWidgets {
-		WID_NT_CAPTION                       = ::WID_NT_CAPTION,                       ///< The caption of the window.
-		WID_NT_BACKGROUND                    = ::WID_NT_BACKGROUND,                    ///< Panel to draw the textfile on.
-		WID_NT_VSCROLLBAR                    = ::WID_NT_VSCROLLBAR,                    ///< Vertical scrollbar to scroll through the textfile up-and-down.
-		WID_NT_HSCROLLBAR                    = ::WID_NT_HSCROLLBAR,                    ///< Horizontal scrollbar to scroll through the textfile left-to-right.
-	};
-
 	/** Widgets of the #NewGRFWindow class. */
 	enum NewGRFStateWidgets {
 		WID_NS_PRESET_LIST                   = ::WID_NS_PRESET_LIST,                   ///< Active NewGRF preset.
@@ -1715,6 +1707,14 @@
 		WID_SP_PROGRESS_TEXT                 = ::WID_SP_PROGRESS_TEXT,                 ///< Text explaining what is happening.
 	};
 
+	/** Widgets of the #TextfileWindow class. */
+	enum TextfileWidgets {
+		WID_TF_CAPTION                       = ::WID_TF_CAPTION,                       ///< The caption of the window.
+		WID_TF_BACKGROUND                    = ::WID_TF_BACKGROUND,                    ///< Panel to draw the textfile on.
+		WID_TF_VSCROLLBAR                    = ::WID_TF_VSCROLLBAR,                    ///< Vertical scrollbar to scroll through the textfile up-and-down.
+		WID_TF_HSCROLLBAR                    = ::WID_TF_HSCROLLBAR,                    ///< Horizontal scrollbar to scroll through the textfile left-to-right.
+	};
+
 	/** Widgets of the #NewsWindow class. */
 	enum NewsWidgets {
 		WID_N_PANEL                          = ::WID_N_PANEL,                          ///< Panel of the window.
--- a/src/script/api/template/template_window.hpp.sq
+++ b/src/script/api/template/template_window.hpp.sq
@@ -155,12 +155,12 @@
 	template <> inline int Return<ScriptWindow::SpriteAlignerWidgets>(HSQUIRRELVM vm, ScriptWindow::SpriteAlignerWidgets res) { sq_pushinteger(vm, (int32)res); return 1; }
 	template <> inline ScriptWindow::NewGRFParametersWidgets GetParam(ForceType<ScriptWindow::NewGRFParametersWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::NewGRFParametersWidgets)tmp; }
 	template <> inline int Return<ScriptWindow::NewGRFParametersWidgets>(HSQUIRRELVM vm, ScriptWindow::NewGRFParametersWidgets res) { sq_pushinteger(vm, (int32)res); return 1; }
-	template <> inline ScriptWindow::NewGRFTextfileWidgets GetParam(ForceType<ScriptWindow::NewGRFTextfileWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::NewGRFTextfileWidgets)tmp; }
-	template <> inline int Return<ScriptWindow::NewGRFTextfileWidgets>(HSQUIRRELVM vm, ScriptWindow::NewGRFTextfileWidgets res) { sq_pushinteger(vm, (int32)res); return 1; }
 	template <> inline ScriptWindow::NewGRFStateWidgets GetParam(ForceType<ScriptWindow::NewGRFStateWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::NewGRFStateWidgets)tmp; }
 	template <> inline int Return<ScriptWindow::NewGRFStateWidgets>(HSQUIRRELVM vm, ScriptWindow::NewGRFStateWidgets res) { sq_pushinteger(vm, (int32)res); return 1; }
 	template <> inline ScriptWindow::ScanProgressWidgets GetParam(ForceType<ScriptWindow::ScanProgressWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::ScanProgressWidgets)tmp; }
 	template <> inline int Return<ScriptWindow::ScanProgressWidgets>(HSQUIRRELVM vm, ScriptWindow::ScanProgressWidgets res) { sq_pushinteger(vm, (int32)res); return 1; }
+	template <> inline ScriptWindow::TextfileWidgets GetParam(ForceType<ScriptWindow::TextfileWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::TextfileWidgets)tmp; }
+	template <> inline int Return<ScriptWindow::TextfileWidgets>(HSQUIRRELVM vm, ScriptWindow::TextfileWidgets res) { sq_pushinteger(vm, (int32)res); return 1; }
 	template <> inline ScriptWindow::NewsWidgets GetParam(ForceType<ScriptWindow::NewsWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::NewsWidgets)tmp; }
 	template <> inline int Return<ScriptWindow::NewsWidgets>(HSQUIRRELVM vm, ScriptWindow::NewsWidgets res) { sq_pushinteger(vm, (int32)res); return 1; }
 	template <> inline ScriptWindow::MessageHistoryWidgets GetParam(ForceType<ScriptWindow::MessageHistoryWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::MessageHistoryWidgets)tmp; }
new file mode 100644
--- /dev/null
+++ b/src/textfile_gui.cpp
@@ -0,0 +1,194 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file textfile_gui.cpp Implementation of textfile window. */
+
+#include "stdafx.h"
+#include "fileio_func.h"
+#include "fontcache.h"
+#include "gfx_type.h"
+#include "gfx_func.h"
+#include "string_func.h"
+#include "textfile_gui.h"
+
+#include "widgets/misc_widget.h"
+
+#include "table/strings.h"
+
+
+/** Widgets for the textfile window. */
+static const NWidgetPart _nested_textfile_widgets[] = {
+	NWidget(NWID_HORIZONTAL),
+		NWidget(WWT_CLOSEBOX, COLOUR_MAUVE),
+		NWidget(WWT_CAPTION, COLOUR_MAUVE, WID_TF_CAPTION), SetDataTip(STR_NULL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+	EndContainer(),
+	NWidget(NWID_HORIZONTAL),
+		NWidget(WWT_PANEL, COLOUR_MAUVE, WID_TF_BACKGROUND), SetMinimalSize(200, 125), SetResize(1, 12), SetScrollbar(WID_TF_VSCROLLBAR),
+		EndContainer(),
+		NWidget(NWID_VERTICAL),
+			NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_TF_VSCROLLBAR),
+		EndContainer(),
+	EndContainer(),
+	NWidget(NWID_HORIZONTAL),
+		NWidget(NWID_HSCROLLBAR, COLOUR_MAUVE, WID_TF_HSCROLLBAR),
+		NWidget(WWT_RESIZEBOX, COLOUR_MAUVE),
+	EndContainer(),
+};
+
+/** Window definition for the textfile window */
+static const WindowDesc _textfile_desc(
+	WDP_CENTER, 630, 460,
+	WC_TEXTFILE, WC_NONE,
+	WDF_UNCLICK_BUTTONS,
+	_nested_textfile_widgets, lengthof(_nested_textfile_widgets)
+);
+
+TextfileWindow::TextfileWindow(TextfileType file_type) : Window(), file_type(file_type)
+{
+	this->CreateNestedTree(&_textfile_desc);
+	this->vscroll = this->GetScrollbar(WID_TF_VSCROLLBAR);
+	this->hscroll = this->GetScrollbar(WID_TF_HSCROLLBAR);
+	this->FinishInitNested(&_textfile_desc);
+}
+
+/* virtual */ TextfileWindow::~TextfileWindow()
+{
+	free(this->text);
+}
+
+/* virtual */ void TextfileWindow::UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
+{
+	switch (widget) {
+		case WID_TF_BACKGROUND:
+			this->line_height = FONT_HEIGHT_MONO + 2;
+			resize->height = this->line_height;
+
+			size->height = 4 * resize->height + TOP_SPACING + BOTTOM_SPACING; // At least 4 lines are visible.
+			size->width = max(200u, size->width); // At least 200 pixels wide.
+			break;
+	}
+}
+
+/* virtual */ void TextfileWindow::DrawWidget(const Rect &r, int widget) const
+{
+	if (widget != WID_TF_BACKGROUND) return;
+
+	int width = r.right - r.left + 1 - WD_BEVEL_LEFT - WD_BEVEL_RIGHT;
+	int height = r.bottom - r.top + 1 - WD_BEVEL_LEFT - WD_BEVEL_RIGHT;
+
+	DrawPixelInfo new_dpi;
+	if (!FillDrawPixelInfo(&new_dpi, r.left + WD_BEVEL_LEFT, r.top, width, height)) return;
+	DrawPixelInfo *old_dpi = _cur_dpi;
+	_cur_dpi = &new_dpi;
+
+	int left, right;
+	if (_current_text_dir == TD_RTL) {
+		left = width + WD_BEVEL_RIGHT - WD_FRAMETEXT_RIGHT - this->hscroll->GetCount();
+		right = width + WD_BEVEL_RIGHT - WD_FRAMETEXT_RIGHT - 1 + this->hscroll->GetPosition();
+	} else {
+		left = WD_FRAMETEXT_LEFT - WD_BEVEL_LEFT - this->hscroll->GetPosition();
+		right = WD_FRAMETEXT_LEFT - WD_BEVEL_LEFT + this->hscroll->GetCount() - 1;
+	}
+	int top = TOP_SPACING;
+	for (uint i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->lines.Length(); i++) {
+		DrawString(left, right, top + i * this->line_height, this->lines[i + this->vscroll->GetPosition()], TC_WHITE, SA_LEFT, false, FS_MONO);
+	}
+
+	_cur_dpi = old_dpi;
+}
+
+/* virtual */ void TextfileWindow::OnResize()
+{
+	this->vscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND, TOP_SPACING + BOTTOM_SPACING);
+	this->hscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND);
+}
+
+/* virtual */ void TextfileWindow::Reset()
+{
+	this->search_iterator = 0;
+}
+
+/* virtual */ FontSize TextfileWindow::DefaultSize()
+{
+	return FS_MONO;
+}
+
+/* virtual */ const char *TextfileWindow::NextString()
+{
+	if (this->search_iterator >= this->lines.Length()) return NULL;
+
+	return this->lines[this->search_iterator++];
+}
+
+/* virtual */ bool TextfileWindow::Monospace()
+{
+	return true;
+}
+
+/* virtual */ void TextfileWindow::SetFontNames(FreeTypeSettings *settings, const char *font_name)
+{
+#ifdef WITH_FREETYPE
+	strecpy(settings->mono_font, font_name, lastof(settings->mono_font));
+#endif /* WITH_FREETYPE */
+}
+
+/**
+ * Loads the textfile text from file, and setup #lines, #max_length, and both scrollbars.
+ */
+/* virtual */ void TextfileWindow::LoadTextfile(const char *textfile, Subdirectory dir)
+{
+	if (textfile == NULL) return;
+
+	this->lines.Clear();
+
+	/* Get text from file */
+	size_t filesize;
+	FILE *handle = FioFOpenFile(textfile, "rb", dir, &filesize);
+	if (handle == NULL) return;
+
+	this->text = ReallocT(this->text, filesize + 1);
+	size_t read = fread(this->text, 1, filesize, handle);
+	fclose(handle);
+
+	if (read != filesize) return;
+
+	this->text[filesize] = '\0';
+
+	/* Replace tabs and line feeds with a space since str_validate removes those. */
+	for (char *p = this->text; *p != '\0'; p++) {
+		if (*p == '\t' || *p == '\r') *p = ' ';
+	}
+
+	/* Check for the byte-order-mark, and skip it if needed. */
+	char *p = this->text + (strncmp("\xEF\xBB\xBF", this->text, 3) == 0 ? 3 : 0);
+
+	/* Make sure the string is a valid UTF-8 sequence. */
+	str_validate(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE);
+
+	/* Split the string on newlines. */
+	*this->lines.Append() = p;
+	for (; *p != '\0'; p++) {
+		if (*p == '\n') {
+			*p = '\0';
+			*this->lines.Append() = p + 1;
+		}
+	}
+
+	CheckForMissingGlyphs(true, this);
+
+	/* Initialize scrollbars */
+	this->vscroll->SetCount(this->lines.Length());
+
+	this->max_length = 0;
+	for (uint i = 0; i < this->lines.Length(); i++) {
+		this->max_length = max(this->max_length, GetStringBoundingBox(this->lines[i], FS_MONO).width);
+	}
+	this->hscroll->SetCount(this->max_length + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT);
+	this->hscroll->SetStepSize(10); // Speed up horizontal scrollbar
+}
new file mode 100644
--- /dev/null
+++ b/src/textfile_gui.h
@@ -0,0 +1,46 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file textfile_gui.h GUI functions related to textfiles. */
+
+#ifndef TEXTFILE_GUI_H
+#define TEXTFILE_GUI_H
+
+#include "strings_func.h"
+#include "textfile_type.h"
+#include "window_gui.h"
+
+/** Window for displaying a textfile */
+struct TextfileWindow : public Window, MissingGlyphSearcher {
+	TextfileType file_type;              ///< Type of textfile to view.
+	int line_height;                     ///< Height of a line in the display widget.
+	Scrollbar *vscroll;                  ///< Vertical scrollbar.
+	Scrollbar *hscroll;                  ///< Horizontal scrollbar.
+	char *text;                          ///< Lines of text from the NewGRF's textfile.
+	SmallVector<const char *, 64> lines; ///< #text, split into lines in a table with lines.
+	uint max_length;                     ///< The longest line in the textfile (in pixels).
+	uint search_iterator;                ///< Iterator for the font check search.
+
+	static const int TOP_SPACING    = WD_FRAMETEXT_TOP;    ///< Additional spacing at the top of the #WID_TF_BACKGROUND widget.
+	static const int BOTTOM_SPACING = WD_FRAMETEXT_BOTTOM; ///< Additional spacing at the bottom of the #WID_TF_BACKGROUND widget.
+
+	TextfileWindow(TextfileType file_type);
+	virtual ~TextfileWindow();
+	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize);
+	virtual void DrawWidget(const Rect &r, int widget) const;
+	virtual void OnResize();
+	virtual void Reset();
+	virtual FontSize DefaultSize();
+	virtual const char *NextString();
+	virtual bool Monospace();
+	virtual void SetFontNames(FreeTypeSettings *settings, const char *font_name);
+	virtual void LoadTextfile(const char *textfile, Subdirectory dir);
+};
+
+#endif /* TEXTFILE_GUI_H */
new file mode 100644
--- /dev/null
+++ b/src/textfile_type.h
@@ -0,0 +1,27 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file textfile_type.h Types related to textfiles. */
+
+#ifndef TEXTFILE_TYPE_H
+#define TEXTFILE_TYPE_H
+
+/** Additional text files accompanying Tar archives */
+enum TextfileType {
+	TFT_BEGIN,
+
+	TFT_README = TFT_BEGIN, ///< NewGRF readme
+	TFT_CHANGELOG,          ///< NewGRF changelog
+	TFT_LICENSE,            ///< NewGRF license
+
+	TFT_END,
+};
+DECLARE_POSTFIX_INCREMENT(TextfileType)
+
+#endif /* TEXTFILE_TYPE_H */
--- a/src/widgets/misc_widget.h
+++ b/src/widgets/misc_widget.h
@@ -45,4 +45,12 @@
 	WID_Q_YES,     ///< No button.
 };
 
+/** Widgets of the #TextfileWindow class. */
+enum TextfileWidgets {
+	WID_TF_CAPTION,    ///< The caption of the window.
+	WID_TF_BACKGROUND, ///< Panel to draw the textfile on.
+	WID_TF_VSCROLLBAR, ///< Vertical scrollbar to scroll through the textfile up-and-down.
+	WID_TF_HSCROLLBAR, ///< Horizontal scrollbar to scroll through the textfile left-to-right.
+};
+
 #endif /* WIDGETS_MISC_WIDGET_H */
--- a/src/widgets/newgrf_widget.h
+++ b/src/widgets/newgrf_widget.h
@@ -13,6 +13,7 @@
 #define WIDGETS_NEWGRF_WIDGET_H
 
 #include "../newgrf_config.h"
+#include "../textfile_type.h"
 
 /** Widgets of the #NewGRFParametersWindow class. */
 enum NewGRFParametersWidgets {
@@ -29,14 +30,6 @@
 	WID_NP_DESCRIPTION,      ///< Multi-line description of a parameter.
 };
 
-/** Widgets of the #NewGRFTextfileWindow class. */
-enum NewGRFTextfileWidgets {
-	WID_NT_CAPTION,    ///< The caption of the window.
-	WID_NT_BACKGROUND, ///< Panel to draw the textfile on.
-	WID_NT_VSCROLLBAR, ///< Vertical scrollbar to scroll through the textfile up-and-down.
-	WID_NT_HSCROLLBAR, ///< Horizontal scrollbar to scroll through the textfile left-to-right.
-};
-
 /** Widgets of the #NewGRFWindow class. */
 enum NewGRFStateWidgets {
 	WID_NS_PRESET_LIST,       ///< Active NewGRF preset.
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -999,9 +999,9 @@
 		case WC_CUSTOM_CURRENCY:
 		case WC_NETWORK_WINDOW:
 		case WC_GRF_PARAMETERS:
-		case WC_NEWGRF_TEXTFILE:
 		case WC_AI_LIST:
 		case WC_AI_SETTINGS:
+		case WC_TEXTFILE:
 			++z_priority;
 
 		case WC_CONSOLE:
--- a/src/window_type.h
+++ b/src/window_type.h
@@ -179,9 +179,9 @@
 
 	/**
 	 * textfile; %Window numbers:
-	 *   - 0 = #NewGRFTextfileWidgets
+	 *   - 0 = #TextfileWidgets
 	 */
-	WC_NEWGRF_TEXTFILE,
+	WC_TEXTFILE,
 
 
 	/**