1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the FIFE team                              *
3  *   http://www.fifengine.net                                              *
4  *   This file is part of FIFE.                                            *
5  *                                                                         *
6  *   FIFE is free software; you can redistribute it and/or                 *
7  *   modify it under the terms of the GNU Lesser General Public            *
8  *   License as published by the Free Software Foundation; either          *
9  *   version 2.1 of the License, or (at your option) any later version.    *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the                 *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 #include <cassert>
24 
25 // 3rd party library includes
26 #include <boost/tokenizer.hpp>
27 
28 // FIFE includes
29 // These includes are split up in two parts, separated by one empty line
30 // First block: files included from the FIFE root src directory
31 // Second block: files included from the same folder
32 #include "video/renderbackend.h"
33 #include "util/time/timemanager.h"
34 #include "util/log/logger.h"
35 #include "util/base/exception.h"
36 #include "gui/fifechan/fifechanmanager.h"
37 #include "gui/fifechan/base/gui_font.h"
38 
39 #include "commandline.h"
40 #include "console.h"
41 
42 namespace FIFE {
43 	const unsigned  Console::m_maxOutputRows = 50;
44 	static Logger _log(LM_CONSOLE);
45 
Console()46 	Console::Console()
47 		: fcn::Container(),
48 		m_consoleexec(0),
49 		m_input(new CommandLine()),
50 		m_output(new fcn::TextBox()),
51 		m_outputscrollarea(new fcn::ScrollArea(m_output)),
52 		m_status(new fcn::Label()),
53 		m_toolsbutton(new fcn::Button("Tools"))
54 		{
55 		reLayout();
56 
57 		add(m_outputscrollarea);
58 		add(m_input);
59 		add(m_status);
60 		add(m_toolsbutton);
61 
62 		setOpaque(true);
63 
64 		m_input->setCallback(std::bind1st( std::mem_fun(&Console::execute), this) );
65 		m_prompt = "-- ";
66 
67 		m_isAttached = false;
68 
69 		m_fpsTimer.setInterval(500);
70 		m_fpsTimer.setCallback(std::bind(&Console::updateCaption, this) );
71 
72 		m_hiding = true;
73 
74 		m_animationTimer.setInterval(20);
75 		m_animationTimer.setCallback(std::bind(&Console::updateAnimation, this) );
76 
77 		m_toolsbutton->addActionListener(this);
78 		m_toolsbutton->setFocusable(false);
79 		m_input->addFocusListener(this);
80 
81 		GuiFont* font = FifechanManager::instance()->createFont();
82 		font->setColor(255,255,255);
83 		setIOFont(font);
84 	}
85 
reLayout()86 	void Console::reLayout() {
87 		int32_t w, h, b, input_h, bbar_h, button_w;
88 		w = RenderBackend::instance()->getScreenWidth() * 4/5;
89 		h = RenderBackend::instance()->getScreenHeight() * 4/5;
90 		b = 0;
91 		input_h = getFont()->getHeight();
92 		bbar_h = input_h;
93 		button_w = 80;
94 
95 		fcn::Color black(0x00,0,0,0xff);
96 		fcn::Color white(0xff,0xff,0xff,0xff);
97 		fcn::Color dark(50,60,50,0xff);
98 
99 		setSize(w, h);
100 		setPosition((RenderBackend::instance()->getScreenWidth() - w) / 2,-h);
101 		setBorderSize(0);
102 
103 		setForegroundColor(white);
104 		setBackgroundColor(black);
105 		setBaseColor(dark);
106 
107 		setSize(w, h);
108 
109 		m_outputscrollarea->setSize(w - 2*b, h - input_h - 3*b - bbar_h);
110 		m_outputscrollarea->setPosition(b,0);
111 
112 		m_input->setPosition(b, h - input_h - b - bbar_h);
113 		m_input->setSize(w - 2*b, input_h);
114 
115 		m_status->setPosition(b, h - b - bbar_h);
116 		m_status->setSize(w - 2*b, bbar_h);
117 
118 		m_toolsbutton->setPosition(w - button_w, h - b - bbar_h);
119 		m_toolsbutton->setSize(button_w, bbar_h);
120 
121 		m_output->setBackgroundColor(black);
122 		m_output->setFocusable(false);
123 
124 		m_outputscrollarea->setBackgroundColor(black);
125 		m_outputscrollarea->setBaseColor(dark);
126 
127 		m_input->setForegroundColor(white);
128 		m_input->setBackgroundColor(black);
129 
130 		m_status->setForegroundColor(white);
131 		m_status->setBackgroundColor(black);
132 
133 		m_toolsbutton->setForegroundColor(white);
134 		m_toolsbutton->setBackgroundColor(black);
135 		m_toolsbutton->setBaseColor(dark);
136 
137 		m_hiddenPos = -h;
138 		m_animationDelta = h/6;
139 	}
140 
~Console()141 	Console::~Console() {
142 		doHide();
143 
144 		remove(m_input);
145 		remove(m_outputscrollarea);
146 		remove(m_status);
147 
148 		delete m_output;
149 		delete m_input;
150 		delete m_outputscrollarea;
151 		delete m_status;
152 		delete m_toolsbutton;
153 	}
154 
updateCaption()155 	void Console::updateCaption() {
156 		std::string caption = "FIFE Console - FPS: ";
157 		double fps = 1e3/TimeManager::instance()->getAverageFrameTime();
158 		caption += std::to_string(fps);
159 		m_status->setCaption( caption );
160 	}
161 
updateAnimation()162 	void Console::updateAnimation() {
163 	    if (m_hiding){
164 		setPosition(getX(), getY() - m_animationDelta);
165 		if (getY() <= m_hiddenPos){
166 		    doHide();
167 			m_animationTimer.stop();
168 		}
169 	    }else{
170 		setPosition(getX(), getY() + m_animationDelta);
171 		if (getY() >= 0){
172 		    setPosition(getX(), 0);
173 			m_animationTimer.stop();
174 		}
175 	}
176 	}
177 
clear()178 	void Console::clear() {
179 		m_output->setText("");
180 	}
181 
doShow()182 	void Console::doShow() {
183 		if (m_isAttached)
184 			return;
185 		m_isAttached = true;
186 		FifechanManager::instance()->add(this);
187 		FifechanManager::instance()->getTopContainer()->moveToTop(this);
188 		// Assure the input field is focused when shown.
189 		m_input->requestFocus();
190 
191 		m_fpsTimer.start();
192 	}
193 
doHide()194 	void Console::doHide() {
195 		if (!m_isAttached)
196 			return;
197 		m_isAttached = false;
198 		FifechanManager::instance()->remove(this);
199 		m_fpsTimer.stop();
200 	}
201 
show()202 	void Console::show() {
203 		if(m_hiding) {
204 			m_hiding = false;
205 			doShow();
206 			m_animationTimer.start();
207 		}
208 	}
209 
hide()210 	void Console::hide() {
211 		if(!m_hiding) {
212 			m_hiding = true;
213 			m_animationTimer.start();
214 		}
215 	}
216 
toggleShowHide()217 	void Console::toggleShowHide() {
218 		m_hiding = !m_hiding;
219 		if(!m_hiding)
220 			doShow();
221 		m_animationTimer.start();
222 	}
223 
execute(std::string cmd)224 	void Console::execute(std::string cmd) {
225  		FL_DBG(_log, LMsg("in execute with command ") << cmd);
226 		if (cmd.empty())
227 			return;
228 
229 		// copy input to output
230 		println(m_prompt + cmd);
231 
232 		// run the command
233 		try {
234 			if (m_consoleexec) {
235 				std::string resp = m_consoleexec->onConsoleCommand(cmd);
236 				println(resp);
237 			} else {
238 				FL_WARN(_log, LMsg("ConsoleExecuter not bind, but command received: ") << cmd.c_str());
239 			}
240 		}
241 		catch (const FIFE::Exception & e) {
242 			FL_WARN(_log, LMsg("Console caught exception: ") << e.what());
243 			println(e.what());
244 		}
245 	}
246 
println(const std::string & s)247 	void Console::println(const std::string & s) {
248 		assert(m_output);
249 
250 		// Add the text in rows
251 		boost::char_separator<char> separator("\n");
252 		typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
253 		tokenizer tokens(s,separator);
254 		for(tokenizer::iterator i = tokens.begin(); i != tokens.end(); ++i) {
255 			m_output->addRow(*i);
256 		}
257 
258 		// Assure the maximum number of rows
259 		if( m_output->getNumberOfRows() > m_maxOutputRows ) {
260 			unsigned rows = m_output->getNumberOfRows();
261 			int32_t delta_rows = rows - m_maxOutputRows;
262 			std::vector<std::string> rows_text;
263 			for(size_t i=delta_rows; i != rows; ++i) {
264 				rows_text.push_back(m_output->getTextRow(i));
265 			}
266 			m_output->setText("");
267 			for(size_t i=0; i != rows_text.size(); ++i) {
268 				m_output->addRow(rows_text[i]);
269 			}
270 		}
271 
272 		// Assure the new text is visible
273 		fcn::Rectangle rect(0,m_output->getHeight(),0,0);
274 		m_outputscrollarea->showWidgetPart(m_output,rect);
275 	}
276 
action(const fcn::ActionEvent & event)277 	void Console::action(const fcn::ActionEvent & event) {
278 		if (m_consoleexec) {
279 			m_consoleexec->onToolsClick();
280 		} else {
281 			FL_WARN(_log, "ConsoleExecuter not bind, but tools button clicked");
282 		}
283 	}
284 
setConsoleExecuter(ConsoleExecuter * const consoleexec)285 	void Console::setConsoleExecuter(ConsoleExecuter* const consoleexec) {
286 		m_consoleexec = consoleexec;
287 	}
288 
removeConsoleExecuter()289 	void Console::removeConsoleExecuter() {
290 		m_consoleexec = NULL;
291 	}
292 
setIOFont(GuiFont * font)293 	void Console::setIOFont(GuiFont* font) {
294 		m_input->setFont(font);
295 		m_output->setFont(font);
296 	}
297 
focusLost(const fcn::Event &)298 	void Console::focusLost(const fcn::Event& ) {
299 		hide();
300 	}
301 }
302