1 /*--License:
2 	Kyra Sprite Engine
3 	Copyright Lee Thomason (Grinning Lizard Software) 2001-2005
4 	www.grinninglizard.com/kyra
5 	www.sourceforge.net/projects/kyra
6 
7 	Kyra is provided under the LGPL.
8 
9 	I kindly request you display a splash screen (provided in the HTML documentation)
10 	to promote Kyra and acknowledge the software and everyone who has contributed to it,
11 	but it is not required by the license.
12 
13 --- LGPL License --
14 
15     This library is free software; you can redistribute it and/or
16     modify it under the terms of the GNU Lesser General Public
17     License as published by the Free Software Foundation; either
18     version 2.1 of the License, or (at your option) any later version.
19 
20     This library is distributed in the hope that it will be useful,
21     but WITHOUT ANY WARRANTY; without even the implied warranty of
22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23     Lesser General Public License for more details.
24 
25     You should have received a copy of the GNU Lesser General Public
26     License along with this library; if not, write to the Free Software
27     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 
29 	The full text of the license can be found in lgpl.txt
30 */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include "console.h"
36 #include "textwidget.h"
37 #include "../engine/engine.h"
38 #include "../engine/textbox.h"
39 #include "../engine/canvas.h"
40 #include "../engine/imagetree.h"
41 #include "../engine/boxresource.h"
42 #include "../engine/box.h"
43 #include "../../grinliz/glutil.h"
44 
45 using namespace grinliz;
46 
KrConsole(int _width,int _height,int _lineSpacing,const KrScheme & scheme)47 KrConsole::KrConsole(	int _width, int _height,
48 						int _lineSpacing,
49 						const KrScheme& scheme ) : KrWidget( scheme )
50 {
51 	commandBufSize = 0;
52 	commandBufNode = 0;
53 	background = 0;
54 	backgroundRes = 0;
55 	font = scheme.font;
56 	GLASSERT( font );
57 	width = _width;
58 	height = _height;
59 	lineSpacing = _lineSpacing;
60 	commandLine = 0;
61 
62 }
63 
64 
~KrConsole()65 KrConsole::~KrConsole()
66 {
67 	// Our children are deleted first, so this is safe.
68 	delete backgroundRes;
69 	backgroundRes = 0;
70 }
71 
72 
AddedtoTree()73 void KrConsole::AddedtoTree()
74 {
75 	KrWidget::AddedtoTree();
76 
77 	// Create the textbox
78 	textBox = new KrTextBox(	font,
79 								width,
80 								height - lineSpacing - font->FontHeight(),	// leave space for command line
81 								lineSpacing );
82 	GLASSERT( textBox );
83 	if ( !textBox )
84 		return;
85 
86 	textBox->SetZDepth( DEPTH_TEXT );
87 	Engine()->Tree()->AddNode( this, textBox );
88 
89 	commandLine = new KrTextWidget( width,
90 									font->FontHeight(),
91 									false,
92 									false,
93 									true,
94 									scheme );
95 
96 	commandLine->SetPos( 0, height + lineSpacing - font->FontHeight() );
97 	commandLine->SetZDepth( DEPTH_TEXT );
98 
99 	Engine()->Tree()->AddNode( this, commandLine );
100 
101 	commandLine->widgetPublish.AddListener( this );
102 	//commandLine->AddListener( this );
103 	//commandLine->EnterSignal.connect( this, &KrConsole::ProcessEnterKey );
104 }
105 
106 
SetBackgroundColor(const KrRGBA & color)107 void KrConsole::SetBackgroundColor( const KrRGBA& color )
108 {
109 	if ( Engine() )
110 	{
111 		if ( background )
112 		{
113 			Engine()->Tree()->DeleteNode( background );
114 			background = 0;
115 			GLASSERT( backgroundRes );
116 
117 			delete backgroundRes;
118 			backgroundRes = 0;
119 		}
120 
121 		backgroundRes = new KrBoxResource(	"background",
122 											textBox->Width(), textBox->Height() + commandLine->Height(),
123 											&color,
124 											1,
125 											KrBoxResource::FILL );
126 		GLASSERT( backgroundRes );
127 		background = new KrBox( backgroundRes );
128 		background->SetZDepth( DEPTH_BACKGROUND );
129 		Engine()->Tree()->AddNode( this, background );
130 	}
131 }
132 
133 
HandleWidgetEvent(KrWidget * source,const KrWidgetEvent & event)134 bool KrConsole::HandleWidgetEvent( KrWidget* source, const KrWidgetEvent& event )
135 {
136 	GLASSERT( source == commandLine );
137 
138 	if ( event.type == KrWidgetEvent::ACTIVATED )
139 	{
140 		ProcessEnterKey();
141 	}
142 	return true;			// A console handles all keys.
143 }
144 
145 
KeyEvent(const SDL_Event & key)146 bool KrConsole::KeyEvent( const SDL_Event& key )
147 {
148 	if ( key.key.keysym.sym == SDLK_UP )
149 	{
150 		if ( !commandBufNode  )
151 		{
152 			commandBufNode = commandBuf.FrontNode();
153 		}
154 		else
155 		{
156 			commandBufNode = commandBufNode->next;
157 		}
158 		commandLine->SetTextChar( commandBufNode->data.c_str() );
159 	}
160 	else if ( key.key.keysym.sym == SDLK_DOWN )
161 	{
162 		if ( !commandBufNode  )
163 		{
164 			commandBufNode = commandBuf.BackNode();
165 		}
166 		else
167 		{
168 			commandBufNode = commandBufNode->prev;
169 		}
170 		commandLine->SetTextChar( commandBufNode->data.c_str() );
171 	}
172 	else if ( key.key.keysym.sym == SDLK_ESCAPE )
173 	{
174 		commandBufNode = 0;
175 		commandLine->SetTextChar( "" );
176 	}
177 	else if ( key.key.keysym.sym == SDLK_TAB )
178 	{
179 		TabCompletion();
180 	}
181 	return true;
182 }
183 
184 
ProcessEnterKey()185 void KrConsole::ProcessEnterKey()
186 {
187 	std::string buf;
188 	commandLine->GetTextChar( &buf );
189 
190 	// Push the command to the command buffer. Don't add duplicates.
191 	if ( buf == commandBuf.Front() )
192 	{
193 		if ( commandBufSize >= COMMAND_BUF_SIZE )
194 		{
195 			commandBuf.PopBack();
196 		}
197 		else
198 		{
199 			++commandBufSize;
200 		}
201 		commandBuf.PushFront( buf );
202 	}
203 
204 	// Scroll up!
205 	//textBox->SetText16( 0, textBox->NumLines() - 1 );
206 	PushText( buf.c_str() );
207 	commandBufNode = 0;
208 	commandLine->SetTextChar( "" );
209 
210 	// Scan the command list, if we have a hit, set the 'recognized' flag.
211 	GlSListNode<std::string>* node = 0;
212 
213 	std::string compareBuf = buf;
214 	std::string arg;
215 
216 	int spaceAt = compareBuf.find( ' ' );
217 	if ( spaceAt > 0 )
218 	{
219 		arg = buf.substr( spaceAt + 1, buf.length() );
220 		compareBuf.resize( spaceAt );
221 	}
222 
223 	KrWidgetEvent event;
224 	event.type = KrWidgetEvent::COMMAND;
225 	event.command.command = compareBuf.c_str();
226 	event.command.arg = arg.c_str();
227 	event.command.recognized = false;
228 
229 	for( node = commandList.FrontNode(); node; node = node->next )
230 	{
231 		if ( node->data == compareBuf )
232 		{
233 			event.command.recognized = true;
234 			break;
235 		}
236 	}
237 
238 	//PublishTaggedEvent( ACTIVATED, 0, compareBuf.c_str(), arg.c_str(), node->data.handler );
239 	//CommandSignal.emit( compareBuf.c_str(), arg.c_str() );
240 	for(	Publisher< IKrWidgetListener >::const_iterator it = widgetPublish.begin();
241 			it != widgetPublish.end();
242 			++it )
243 	{
244 		(*it)->HandleWidgetEvent( this, event );
245 	}
246 }
247 
248 
249 //void KrConsole::AddCommandHandler( const char* command, IKrCommand* handler )
250 //{
251 //	Command c;
252 //	c.command = command;
253 //	c.handler = handler;
254 //
255 //	commandList.PushFront( c );
256 //}
257 
258 
PushText(const char * text)259 void KrConsole::PushText( const char* text )
260 {
261 	int i;
262 	const U16* p;
263 
264 	for( i=1; i<textBox->NumLines(); i++ )
265 	{
266 		p = textBox->GetText16( i );
267 		textBox->SetText16( p, i-1 );
268 	}
269  	textBox->SetTextChar( text, textBox->NumLines()-1 );
270 }
271 
272 
TabCompletion()273 void KrConsole::TabCompletion()
274 {
275 	// Go through the command list and figure out all the commands
276 	// this could complete to. If only one, make the completion,
277 	// else push the options to the console.
278 	std::string buf;
279 	GlSListNode<std::string>* node;
280 	GlSList<std::string> matches;
281 
282 	commandLine->GetTextChar( &buf );
283 
284 	for ( node = commandList.FrontNode(); node; node = node->next )
285 	{
286 		if ( buf.compare( node->data.substr( 0, buf.length() ) ) == 0 )
287 		{
288 			matches.PushFront( node->data );
289 		}
290 	}
291 
292 	if ( matches.Size() > 1 )
293 	{
294 		GlSListNode<std::string>* mNode;
295 		for( mNode = matches.FrontNode(); mNode; mNode = mNode->next )
296 		{
297 			PushText( mNode->data.c_str() );
298 		}
299 	}
300 	else if ( matches.Size() == 1 )
301 	{
302 		//strcpy( buf, matches.FrontNode()->data->c_str() );
303 		//strcat( buf, " " );
304 		buf = matches.FrontNode()->data;
305 		buf += " ";
306 		commandLine->SetTextChar( buf );
307 	}
308 }
309 
310 
Print(const char * format,...)311 void KrConsole::Print( const char* format, ... )
312 {
313     va_list     va;
314     char		buffer[1024];
315 
316     //
317     //  format and output the message..
318     //
319     va_start( va, format );
320     vsprintf( buffer, format, va );
321     va_end( va );
322 
323 	char* start;
324 	char* end;
325 	char* next;
326 
327 	start = buffer;
328 
329 	while ( start && *start )
330 	{
331 		end = strchr( start, '\n' );
332 		if ( end )
333 		{
334 			next  = end + 1;
335 			*end = 0;
336 			PushText( start );
337 			start = next;
338 		}
339 		else
340 		{
341 			PushText( start );
342 			start = 0;
343 		}
344 	}
345 }
346 
347 
AddCommand(const std::string & command)348 void KrConsole::AddCommand( const std::string& command )	//, IKrWidgetListener* handler )
349 {
350 	//AddListener( handler );
351 
352 //	Command c;
353 //	c.command = command;
354 	//c.handler = handler;
355 
356 	commandList.PushFront( command );
357 }
358 
GetEntryTextChar(std::string * buffer)359 void KrConsole::GetEntryTextChar( std::string* buffer )
360 {
361 	*buffer = "";
362 	if ( commandLine )
363 		commandLine->GetTextChar( buffer );
364 }
365 
366