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.txt";
27 static const std::string KNOWN_BUGS_FILENAME
= "known-bugs.txt";
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();
90 if (this->GetWidget
<NWidgetStacked
>(WID_TF_SEL_JUMPLIST
)->SetDisplayedPlane(this->jumplist
.empty() ? SZSP_HORIZONTAL
: 0)) this->ReInit();
92 this->TextfileWindow::AfterLoadText();
97 * For changelog files, add a jumplist entry for each version.
99 * This is hardcoded and assumes "---" are used to separate versions.
101 void AfterLoadChangelog()
103 /* Look for lines beginning with ---, they indicate that the previous line was 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
.find("---", 0) != 0) continue;
108 if (this->jumplist
.size() >= CHANGELOG_VERSIONS_LIMIT
) {
109 this->lines
.resize(line_index
- 2);
113 /* Mark the version header with a colour, and add it to the jumplist. */
114 this->lines
[line_index
- 1].colour
= TC_GOLD
;
115 this->lines
[line_index
].colour
= TC_GOLD
;
116 this->jumplist
.push_back(line_index
- 1);
121 /** Window class displaying the help window. */
122 struct HelpWindow
: public Window
{
124 HelpWindow(WindowDesc
&desc
, WindowNumber number
) : Window(desc
)
126 this->InitNested(number
);
128 this->EnableTextfileButton(README_FILENAME
, WID_HW_README
);
129 this->EnableTextfileButton(CHANGELOG_FILENAME
, WID_HW_CHANGELOG
);
130 this->EnableTextfileButton(KNOWN_BUGS_FILENAME
, WID_HW_KNOWN_BUGS
);
131 this->EnableTextfileButton(LICENSE_FILENAME
, WID_HW_LICENSE
);
134 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
138 new GameManualTextfileWindow(README_FILENAME
);
140 case WID_HW_CHANGELOG
:
141 new GameManualTextfileWindow(CHANGELOG_FILENAME
);
143 case WID_HW_KNOWN_BUGS
:
144 new GameManualTextfileWindow(KNOWN_BUGS_FILENAME
);
147 new GameManualTextfileWindow(LICENSE_FILENAME
);
150 OpenBrowser(WEBSITE_LINK
);
153 OpenBrowser(WIKI_LINK
);
155 case WID_HW_BUGTRACKER
:
156 OpenBrowser(BUGTRACKER_LINK
);
158 case WID_HW_COMMUNITY
:
159 OpenBrowser(COMMUNITY_LINK
);
165 void EnableTextfileButton(std::string_view filename
, WidgetID button_widget
)
167 this->GetWidget
<NWidgetLeaf
>(button_widget
)->SetDisabled(!FindGameManualFilePath(filename
).has_value());
171 static constexpr NWidgetPart _nested_helpwin_widgets
[] = {
172 NWidget(NWID_HORIZONTAL
),
173 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
174 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
), SetDataTip(STR_HELP_WINDOW_CAPTION
, STR_NULL
),
177 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
),
178 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_wide
, 0), SetPadding(WidgetDimensions::unscaled
.sparse
),
179 NWidget(WWT_FRAME
, COLOUR_DARK_GREEN
), SetDataTip(STR_HELP_WINDOW_WEBSITES
, STR_NULL
),
180 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_WEBSITE
), SetDataTip(STR_HELP_WINDOW_MAIN_WEBSITE
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
181 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_WIKI
), SetDataTip(STR_HELP_WINDOW_MANUAL_WIKI
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
182 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_BUGTRACKER
), SetDataTip(STR_HELP_WINDOW_BUGTRACKER
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
183 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_COMMUNITY
), SetDataTip(STR_HELP_WINDOW_COMMUNITY
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
186 NWidget(WWT_FRAME
, COLOUR_DARK_GREEN
), SetDataTip(STR_HELP_WINDOW_DOCUMENTS
, STR_NULL
),
187 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_README
), SetDataTip(STR_HELP_WINDOW_README
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
188 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_CHANGELOG
), SetDataTip(STR_HELP_WINDOW_CHANGELOG
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
189 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_KNOWN_BUGS
),SetDataTip(STR_HELP_WINDOW_KNOWN_BUGS
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
190 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREEN
, WID_HW_LICENSE
), SetDataTip(STR_HELP_WINDOW_LICENSE
, STR_NULL
), SetMinimalSize(128, 12), SetFill(1, 0),
196 static WindowDesc
_helpwin_desc(
197 WDP_CENTER
, nullptr, 0, 0,
200 _nested_helpwin_widgets
203 void ShowHelpWindow()
205 AllocateWindowDescFront
<HelpWindow
>(_helpwin_desc
, 0);