2023-06-18 15:10:08 +00:00
|
|
|
/*
|
|
|
|
* 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 help_gui.cpp GUI to access manuals and related. */
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "gui.h"
|
|
|
|
#include "window_gui.h"
|
|
|
|
#include "textfile_gui.h"
|
|
|
|
#include "fileio_func.h"
|
|
|
|
#include "table/control_codes.h"
|
|
|
|
#include "string_func.h"
|
|
|
|
#include "openttd.h"
|
|
|
|
|
|
|
|
#include "help_gui.h"
|
|
|
|
#include "widgets/help_widget.h"
|
|
|
|
#include "widgets/misc_widget.h"
|
|
|
|
|
|
|
|
#include "safeguards.h"
|
|
|
|
|
|
|
|
static const std::string README_FILENAME = "README.md";
|
|
|
|
static const std::string CHANGELOG_FILENAME = "changelog.txt";
|
|
|
|
static const std::string KNOWN_BUGS_FILENAME = "known-bugs.txt";
|
|
|
|
static const std::string LICENSE_FILENAME = "COPYING.md";
|
|
|
|
|
|
|
|
static const std::string WEBSITE_LINK = "https://www.openttd.org/";
|
|
|
|
static const std::string WIKI_LINK = "https://wiki.openttd.org/";
|
|
|
|
static const std::string BUGTRACKER_LINK = "https://bugs.openttd.org/";
|
|
|
|
static const std::string COMMUNITY_LINK = "https://community.openttd.org/";
|
|
|
|
|
|
|
|
/** Only show the first 20 changelog versions in the textfile viewer. */
|
|
|
|
static constexpr size_t CHANGELOG_VERSIONS_LIMIT = 20;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the path to the game manual file.
|
|
|
|
*
|
|
|
|
* @param filename The filename to find.
|
|
|
|
* @return std::string The path to the filename if found.
|
|
|
|
*/
|
|
|
|
static std::optional<std::string> FindGameManualFilePath(std::string_view filename)
|
|
|
|
{
|
|
|
|
static const Searchpath searchpaths[] = {
|
|
|
|
SP_APPLICATION_BUNDLE_DIR, SP_INSTALLATION_DIR, SP_SHARED_DIR, SP_BINARY_DIR, SP_WORKING_DIR
|
|
|
|
};
|
|
|
|
|
|
|
|
for (Searchpath sp : searchpaths) {
|
|
|
|
auto file_path = FioGetDirectory(sp, BASE_DIR) + filename.data();
|
|
|
|
if (FioCheckFileExists(file_path, NO_DIRECTORY)) return file_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Window class displaying the game manual textfile viewer. */
|
|
|
|
struct GameManualTextfileWindow : public TextfileWindow {
|
|
|
|
GameManualTextfileWindow(std::string_view filename) : TextfileWindow(TFT_GAME_MANUAL)
|
|
|
|
{
|
|
|
|
/* Mark the content of these files as trusted. */
|
|
|
|
this->trusted = true;
|
|
|
|
|
|
|
|
auto filepath = FindGameManualFilePath(filename);
|
|
|
|
/* The user could, in theory, have moved the file. So just show an empty window if that is the case. */
|
|
|
|
if (!filepath.has_value()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->filepath = filepath.value();
|
|
|
|
this->LoadTextfile(this->filepath, NO_DIRECTORY);
|
|
|
|
this->OnClick({ 0, 0 }, WID_TF_WRAPTEXT, 1);
|
|
|
|
}
|
|
|
|
|
2023-12-29 19:11:59 +00:00
|
|
|
void SetStringParameters(WidgetID widget) const override
|
2023-06-18 15:10:08 +00:00
|
|
|
{
|
|
|
|
if (widget == WID_TF_CAPTION) {
|
|
|
|
SetDParamStr(0, this->filename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AfterLoadText() override
|
|
|
|
{
|
|
|
|
if (this->filename == CHANGELOG_FILENAME) {
|
|
|
|
this->link_anchors.clear();
|
|
|
|
this->AfterLoadChangelog();
|
2023-12-30 00:19:27 +00:00
|
|
|
if (this->GetWidget<NWidgetStacked>(WID_TF_SEL_JUMPLIST)->SetDisplayedPlane(this->jumplist.empty() ? SZSP_HORIZONTAL : 0)) this->ReInit();
|
2023-06-18 15:10:08 +00:00
|
|
|
} else {
|
|
|
|
this->TextfileWindow::AfterLoadText();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For changelog files, add a jumplist entry for each version.
|
|
|
|
*
|
|
|
|
* This is hardcoded and assumes "---" are used to separate versions.
|
|
|
|
*/
|
|
|
|
void AfterLoadChangelog()
|
|
|
|
{
|
|
|
|
/* Look for lines beginning with ---, they indicate that the previous line was a release name. */
|
|
|
|
for (size_t line_index = 0; line_index < this->lines.size(); ++line_index) {
|
|
|
|
const Line &line = this->lines[line_index];
|
|
|
|
if (line.text.find("---", 0) != 0) continue;
|
|
|
|
|
|
|
|
if (this->jumplist.size() >= CHANGELOG_VERSIONS_LIMIT) {
|
|
|
|
this->lines.resize(line_index - 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark the version header with a colour, and add it to the jumplist. */
|
|
|
|
this->lines[line_index - 1].colour = TC_GOLD;
|
|
|
|
this->lines[line_index].colour = TC_GOLD;
|
|
|
|
this->jumplist.push_back(line_index - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Window class displaying the help window. */
|
|
|
|
struct HelpWindow : public Window {
|
|
|
|
|
|
|
|
HelpWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
|
|
|
|
{
|
|
|
|
this->InitNested(number);
|
|
|
|
|
|
|
|
this->EnableTextfileButton(README_FILENAME, WID_HW_README);
|
|
|
|
this->EnableTextfileButton(CHANGELOG_FILENAME, WID_HW_CHANGELOG);
|
|
|
|
this->EnableTextfileButton(KNOWN_BUGS_FILENAME, WID_HW_KNOWN_BUGS);
|
|
|
|
this->EnableTextfileButton(LICENSE_FILENAME, WID_HW_LICENSE);
|
|
|
|
}
|
|
|
|
|
2023-12-29 19:11:59 +00:00
|
|
|
void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
|
2023-06-18 15:10:08 +00:00
|
|
|
{
|
|
|
|
switch (widget) {
|
|
|
|
case WID_HW_README:
|
|
|
|
new GameManualTextfileWindow(README_FILENAME);
|
|
|
|
break;
|
|
|
|
case WID_HW_CHANGELOG:
|
|
|
|
new GameManualTextfileWindow(CHANGELOG_FILENAME);
|
|
|
|
break;
|
|
|
|
case WID_HW_KNOWN_BUGS:
|
|
|
|
new GameManualTextfileWindow(KNOWN_BUGS_FILENAME);
|
|
|
|
break;
|
|
|
|
case WID_HW_LICENSE:
|
|
|
|
new GameManualTextfileWindow(LICENSE_FILENAME);
|
|
|
|
break;
|
|
|
|
case WID_HW_WEBSITE:
|
2023-11-29 00:17:05 +00:00
|
|
|
OpenBrowser(WEBSITE_LINK);
|
2023-06-18 15:10:08 +00:00
|
|
|
break;
|
|
|
|
case WID_HW_WIKI:
|
2023-11-29 00:17:05 +00:00
|
|
|
OpenBrowser(WIKI_LINK);
|
2023-06-18 15:10:08 +00:00
|
|
|
break;
|
|
|
|
case WID_HW_BUGTRACKER:
|
2023-11-29 00:17:05 +00:00
|
|
|
OpenBrowser(BUGTRACKER_LINK);
|
2023-06-18 15:10:08 +00:00
|
|
|
break;
|
|
|
|
case WID_HW_COMMUNITY:
|
2023-11-29 00:17:05 +00:00
|
|
|
OpenBrowser(COMMUNITY_LINK);
|
2023-06-18 15:10:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2024-01-14 16:47:10 +00:00
|
|
|
void EnableTextfileButton(std::string_view filename, WidgetID button_widget)
|
2023-06-18 15:10:08 +00:00
|
|
|
{
|
|
|
|
this->GetWidget<NWidgetLeaf>(button_widget)->SetDisabled(!FindGameManualFilePath(filename).has_value());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-01-15 22:49:24 +00:00
|
|
|
static constexpr NWidgetPart _nested_helpwin_widgets[] = {
|
2023-06-18 15:10:08 +00:00
|
|
|
NWidget(NWID_HORIZONTAL),
|
|
|
|
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
|
|
|
NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_HELP_WINDOW_CAPTION, STR_NULL),
|
|
|
|
EndContainer(),
|
|
|
|
|
|
|
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
|
2023-10-25 00:07:12 +00:00
|
|
|
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0), SetPadding(WidgetDimensions::unscaled.sparse),
|
|
|
|
NWidget(WWT_FRAME, COLOUR_DARK_GREEN), SetDataTip(STR_HELP_WINDOW_WEBSITES, STR_NULL),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_WEBSITE), SetDataTip(STR_HELP_WINDOW_MAIN_WEBSITE, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_WIKI), SetDataTip(STR_HELP_WINDOW_MANUAL_WIKI, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_BUGTRACKER), SetDataTip(STR_HELP_WINDOW_BUGTRACKER, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_COMMUNITY), SetDataTip(STR_HELP_WINDOW_COMMUNITY, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
2023-06-18 15:10:08 +00:00
|
|
|
EndContainer(),
|
|
|
|
|
2023-10-25 00:07:12 +00:00
|
|
|
NWidget(WWT_FRAME, COLOUR_DARK_GREEN), SetDataTip(STR_HELP_WINDOW_DOCUMENTS, STR_NULL),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_README), SetDataTip(STR_HELP_WINDOW_README, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_CHANGELOG), SetDataTip(STR_HELP_WINDOW_CHANGELOG, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_KNOWN_BUGS),SetDataTip(STR_HELP_WINDOW_KNOWN_BUGS, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
|
|
|
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_HW_LICENSE), SetDataTip(STR_HELP_WINDOW_LICENSE, STR_NULL), SetMinimalSize(128, 12), SetFill(1, 0),
|
2023-06-18 15:10:08 +00:00
|
|
|
EndContainer(),
|
|
|
|
EndContainer(),
|
|
|
|
EndContainer(),
|
|
|
|
};
|
|
|
|
|
2023-11-02 19:33:01 +00:00
|
|
|
static WindowDesc _helpwin_desc(__FILE__, __LINE__,
|
2023-06-18 15:10:08 +00:00
|
|
|
WDP_CENTER, nullptr, 0, 0,
|
|
|
|
WC_HELPWIN, WC_NONE,
|
|
|
|
0,
|
|
|
|
std::begin(_nested_helpwin_widgets), std::end(_nested_helpwin_widgets)
|
|
|
|
);
|
|
|
|
|
|
|
|
void ShowHelpWindow()
|
|
|
|
{
|
|
|
|
AllocateWindowDescFront<HelpWindow>(&_helpwin_desc, 0);
|
|
|
|
}
|