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