2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
8 /** @file help_gui.cpp GUI to access manuals and related. */
12 #include "window_gui.h"
13 #include "textfile_gui.h"
14 #include "fileio_func.h"
15 #include "table/control_codes.h"
16 #include "string_func.h"
20 #include "widgets/help_widget.h"
21 #include "widgets/misc_widget.h"
23 #include "safeguards.h"
25 static const std::string README_FILENAME
= "README.md";
26 static const std::string CHANGELOG_FILENAME
= "changelog.md";
27 static const std::string KNOWN_BUGS_FILENAME
= "known-bugs.md";
28 static const std::string LICENSE_FILENAME
= "COPYING.md";
30 static const std::string WEBSITE_LINK
= "https://www.openttd.org/";
31 static const std::string WIKI_LINK
= "https://wiki.openttd.org/";
32 static const std::string BUGTRACKER_LINK
= "https://bugs.openttd.org/";
33 static const std::string COMMUNITY_LINK
= "https://community.openttd.org/";
35 /** Only show the first 20 changelog versions in the textfile viewer. */
36 static constexpr size_t CHANGELOG_VERSIONS_LIMIT
= 20;
39 * Find the path to the game manual file.
41 * @param filename The filename to find.
42 * @return std::string The path to the filename if found.
44 static std::optional
<std::string
> FindGameManualFilePath(std::string_view filename
)
46 static const Searchpath searchpaths
[] = {
47 SP_APPLICATION_BUNDLE_DIR
, SP_INSTALLATION_DIR
, SP_SHARED_DIR
, SP_BINARY_DIR
, SP_WORKING_DIR
50 for (Searchpath sp
: searchpaths
) {
51 auto file_path
= FioGetDirectory(sp
, BASE_DIR
) + filename
.data();
52 if (FioCheckFileExists(file_path
, NO_DIRECTORY
)) return file_path
;
58 /** Window class displaying the game manual textfile viewer. */
59 struct GameManualTextfileWindow
: public TextfileWindow
{
60 GameManualTextfileWindow(std::string_view filename
) : TextfileWindow(TFT_GAME_MANUAL
)
62 this->ConstructWindow();
64 /* Mark the content of these files as trusted. */
67 auto filepath
= FindGameManualFilePath(filename
);
68 /* The user could, in theory, have moved the file. So just show an empty window if that is the case. */
69 if (!filepath
.has_value()) {
73 this->filepath
= filepath
.value();
74 this->LoadTextfile(this->filepath
, NO_DIRECTORY
);
75 this->OnClick({ 0, 0 }, WID_TF_WRAPTEXT
, 1);
78 void SetStringParameters(WidgetID widget
) const override
80 if (widget
== WID_TF_CAPTION
) {
81 SetDParamStr(0, this->filename
);
85 void AfterLoadText() override
87 if (this->filename
== CHANGELOG_FILENAME
) {
88 this->link_anchors
.clear();
89 this->AfterLoadChangelog();
91 this->TextfileWindow::AfterLoadText();
95 * For changelog files, truncate the file after CHANGELOG_VERSIONS_LIMIT versions.
97 * This is hardcoded and assumes "###" is used to separate versions.
99 void AfterLoadChangelog()
103 /* Look for lines beginning with ###, they indicate a release name. */
104 for (size_t line_index
= 0; line_index
< this->lines
.size(); ++line_index
) {
105 const Line
&line
= this->lines
[line_index
];
106 if (!line
.text
.starts_with("###")) continue;
108 if (versions
>= CHANGELOG_VERSIONS_LIMIT
) {
109 this->lines
.resize(line_index
- 2);
118 /** Window class displaying the help window. */
119 struct HelpWindow
: public Window
{
121 HelpWindow(WindowDesc
&desc
, WindowNumber number
) : Window(desc
)
123 this->InitNested(number
);
125 this->EnableTextfileButton(README_FILENAME
, WID_HW_README
);
126 this->EnableTextfileButton(CHANGELOG_FILENAME
, WID_HW_CHANGELOG
);
127 this->EnableTextfileButton(KNOWN_BUGS_FILENAME
, WID_HW_KNOWN_BUGS
);
128 this->EnableTextfileButton(LICENSE_FILENAME
, WID_HW_LICENSE
);
131 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
135 new GameManualTextfileWindow(README_FILENAME
);
137 case WID_HW_CHANGELOG
:
138 new GameManualTextfileWindow(CHANGELOG_FILENAME
);
140 case WID_HW_KNOWN_BUGS
:
141 new GameManualTextfileWindow(KNOWN_BUGS_FILENAME
);
144 new GameManualTextfileWindow(LICENSE_FILENAME
);
147 OpenBrowser(WEBSITE_LINK
);
150 OpenBrowser(WIKI_LINK
);
152 case WID_HW_BUGTRACKER
:
153 OpenBrowser(BUGTRACKER_LINK
);
155 case WID_HW_COMMUNITY
:
156 OpenBrowser(COMMUNITY_LINK
);
162 void EnableTextfileButton(std::string_view filename
, WidgetID button_widget
)
164 this->GetWidget
<NWidgetLeaf
>(button_widget
)->SetDisabled(!FindGameManualFilePath(filename
).has_value());
168 static constexpr NWidgetPart _nested_helpwin_widgets
[] = {
169 NWidget(NWID_HORIZONTAL
),
170 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
171 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
), SetDataTip(STR_HELP_WINDOW_CAPTION
, STR_NULL
),
174 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
),
175 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_wide
, 0), SetPadding(WidgetDimensions::unscaled
.sparse
),
176 NWidget(WWT_FRAME
, COLOUR_DARK_GREEN
), SetDataTip(STR_HELP_WINDOW_WEBSITES
, STR_NULL
),
177 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_WEBSITE
), SetDataTip(STR_HELP_WINDOW_MAIN_WEBSITE
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
178 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_WIKI
), SetDataTip(STR_HELP_WINDOW_MANUAL_WIKI
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
179 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_BUGTRACKER
), SetDataTip(STR_HELP_WINDOW_BUGTRACKER
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
180 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_COMMUNITY
), SetDataTip(STR_HELP_WINDOW_COMMUNITY
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
183 NWidget(WWT_FRAME
, COLOUR_DARK_GREEN
), SetDataTip(STR_HELP_WINDOW_DOCUMENTS
, STR_NULL
),
184 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_README
), SetDataTip(STR_HELP_WINDOW_README
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
185 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_CHANGELOG
), SetDataTip(STR_HELP_WINDOW_CHANGELOG
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
186 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_KNOWN_BUGS
),SetDataTip(STR_HELP_WINDOW_KNOWN_BUGS
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
187 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_LICENSE
), SetDataTip(STR_HELP_WINDOW_LICENSE
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
193 static WindowDesc
_helpwin_desc(
194 WDP_CENTER
, nullptr, 0, 0,
197 _nested_helpwin_widgets
200 void ShowHelpWindow()
202 AllocateWindowDescFront
<HelpWindow
>(_helpwin_desc
, 0);