1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2004-2009  The Mana World Development Team
4  *  Copyright (C) 2009-2010  The Mana Developers
5  *  Copyright (C) 2011-2019  The ManaPlus Developers
6  *  Copyright (C) 2019-2021  Andrei Karas
7  *
8  *  This file is part of The ManaPlus Client.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "gui/windows/helpwindow.h"
25 
26 #include "configuration.h"
27 
28 #include "enums/gui/layouttype.h"
29 
30 #include "fs/paths.h"
31 
32 #include "fs/virtfs/tools.h"
33 
34 #include "gui/gui.h"
35 
36 #include "gui/windows/setupwindow.h"
37 
38 #include "gui/widgets/button.h"
39 #include "gui/widgets/staticbrowserbox.h"
40 #include "gui/widgets/layout.h"
41 #include "gui/widgets/scrollarea.h"
42 
43 #include "input/inputmanager.h"
44 
45 #include "utils/foreach.h"
46 #include "utils/gettext.h"
47 #include "utils/process.h"
48 
49 #include "utils/translation/podict.h"
50 #include "utils/translation/translationmanager.h"
51 
52 #include "debug.h"
53 
54 HelpWindow *helpWindow = nullptr;
55 
HelpWindow()56 HelpWindow::HelpWindow() :
57     // TRANSLATORS: help window name
58     Window(_("Help"), Modal_false, nullptr, "help.xml"),
59     LinkHandler(),
60     ActionListener(),
61     // TRANSLATORS: help window. button.
62     mDYKButton(new Button(this, _("Did you know..."), "DYK",
63         BUTTON_SKIN, this)),
64     mBrowserBox(new StaticBrowserBox(this, Opaque_true,
65         "browserbox.xml")),
66     mScrollArea(new ScrollArea(this, mBrowserBox,
67         Opaque_true, "help_background.xml")),
68     mTagFileMap()
69 {
70     setMinWidth(300);
71     setMinHeight(220);
72     setContentSize(455, 350);
73     setWindowName("Help");
74     setCloseButton(true);
75     setResizable(true);
76     setStickyButtonLock(true);
77 
78     if (setupWindow != nullptr)
79         setupWindow->registerWindowForReset(this);
80 
81     setDefaultSize(500, 400, ImagePosition::CENTER, 0, 0);
82 
83     mBrowserBox->setOpaque(Opaque_false);
84 
85     mBrowserBox->setLinkHandler(this);
86     if (gui != nullptr)
87         mBrowserBox->setFont(gui->getHelpFont());
88     mBrowserBox->setProcessVars(true);
89     mBrowserBox->setEnableImages(true);
90     mBrowserBox->setEnableKeys(true);
91     mBrowserBox->setEnableTabs(true);
92 
93     place(4, 3, mDYKButton, 1, 1);
94     place(0, 0, mScrollArea, 5, 3).setPadding(3);
95 
96     Layout &layout = getLayout();
97     layout.setRowHeight(0, LayoutType::SET);
98 
99     loadWindowState();
100     loadTags();
101     enableVisibleSound(true);
102     widgetResized(Event(nullptr));
103 }
104 
action(const ActionEvent & event)105 void HelpWindow::action(const ActionEvent &event)
106 {
107     if (event.getId() == "DYK")
108         inputManager.executeAction(InputAction::WINDOW_DIDYOUKNOW);
109 }
110 
handleLink(const std::string & link,MouseEvent * const event A_UNUSED)111 void HelpWindow::handleLink(const std::string &link,
112                             MouseEvent *const event A_UNUSED)
113 {
114     if (!strStartWith(link, "http://") && !strStartWith(link, "https://"))
115     {
116         // need use separate variable
117         const std::string helpFile = link;  // NOLINT
118         loadHelp(helpFile);
119     }
120     else
121     {
122         openBrowser(link);
123     }
124 }
125 
loadHelp(const std::string & helpFile)126 void HelpWindow::loadHelp(const std::string &helpFile)
127 {
128     if (!checkPath(helpFile))
129         return;
130     mBrowserBox->clearRows();
131     loadFile("header");
132     loadFile(helpFile);
133     loadFile("footer");
134     mScrollArea->setVerticalScrollAmount(0);
135     mBrowserBox->updateHeight();
136     setVisible(Visible_true);
137 }
138 
loadHelpSimple(const std::string & helpFile)139 void HelpWindow::loadHelpSimple(const std::string &helpFile)
140 {
141     if (!checkPath(helpFile))
142         return;
143     mBrowserBox->clearRows();
144     loadFile(helpFile);
145     mScrollArea->setVerticalScrollAmount(0);
146     mBrowserBox->updateHeight();
147     setVisible(Visible_true);
148 }
149 
loadFile(std::string file)150 void HelpWindow::loadFile(std::string file)
151 {
152     trim(file);
153     std::string helpPath = branding.getStringValue("helpPath");
154     if (helpPath.empty())
155         helpPath = paths.getStringValue("help");
156 
157     StringVect lines;
158     TranslationManager::translateFile(pathJoin(helpPath, file).append(".txt"),
159         translator, lines);
160 
161     for (size_t i = 0, sz = lines.size(); i < sz; ++i)
162         mBrowserBox->addRow(lines[i], false);
163 }
164 
loadTags()165 void HelpWindow::loadTags()
166 {
167     std::string helpPath = branding.getStringValue("tagsPath");
168     if (helpPath.empty())
169         helpPath = paths.getStringValue("tags");
170 
171     StringVect filesVect;
172     VirtFs::getFilesInDir(helpPath, filesVect, ".idx");
173     FOR_EACH (StringVectCIter, itVect, filesVect)
174     {
175         StringVect lines;
176         VirtFs::loadTextFile(*itVect, lines);
177         FOR_EACH (StringVectCIter, it, lines)
178         {
179             const std::string &str = *it;
180             const size_t idx = str.find('|');
181             if (idx != std::string::npos)
182                 mTagFileMap[str.substr(idx + 1)].insert(str.substr(0, idx));
183         }
184     }
185 }
186 
search(const std::string & text0)187 void HelpWindow::search(const std::string &text0)
188 {
189     std::string text = text0;
190     trim(text);
191     toLower(text);
192     if (mTagFileMap.find(text) == mTagFileMap.end())
193     {
194         loadHelp("searchnotfound");
195     }
196     else
197     {
198         const HelpNames &names = mTagFileMap[text];
199         if (names.size() == 1)
200         {
201             loadHelp(*names.begin());
202         }
203         else
204         {
205             if (translator == nullptr)
206                 return;
207             mBrowserBox->clearRows();
208             loadFile("header");
209             loadFile("searchmany");
210             FOR_EACH (HelpNamesCIter, it, names)
211             {
212                 const char *const str = (*it).c_str();
213                 mBrowserBox->addRow(strprintf(" -> @@%s|%s@@", str,
214                     translator->getChar(str)),
215                     false);
216             }
217             loadFile("footer");
218             mScrollArea->setVerticalScrollAmount(0);
219             mBrowserBox->updateHeight();
220             setVisible(Visible_true);
221         }
222     }
223 }
224