Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/shell/shell.cc @ 5175

Last change on this file since 5175 was 5175, checked in by bensch, 19 years ago

orxonox/trunk: cleaner outtakes of the ShellBuffer

File size: 20.1 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14*/
15
16//#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_
17
18#include "shell.h"
19#include "shell_command.h"
20#include "shell_buffer.h"
21
22
23#include "text_engine.h"
24#include "list.h"
25#include "graphics_engine.h"
26#include "event_handler.h"
27#include "debug.h"
28#include "class_list.h"
29
30#include "key_names.h"
31#include <stdarg.h>
32#include <stdio.h>
33
34using namespace std;
35
36SHELL_COMMAND(clear, Shell, clear)->describe("Clears the shell from unwanted lines (empties all buffers)");
37SHELL_COMMAND(deactivate, Shell, deactivate)->describe("Deactivates the Shell. (moves it into background)");
38
39/**
40 * standard constructor
41 */
42Shell::Shell ()
43{
44  this->setClassID(CL_SHELL, "Shell");
45  this->setName("Shell");
46
47  this->setAbsCoor2D(3, -400);
48  this->textSize = 15;
49  this->lineSpacing = 5;
50  this->bActive = false;
51
52
53  // BUFFER
54  this->bufferText = NULL;
55  this->setBufferDisplaySize(10);
56
57  // INPUT LINE
58  this->delayed = 0;
59  this->setRepeatDelay(.3, .05);
60  this->pressedKey = SDLK_FIRST;
61
62  this->inputLineText = NULL;
63  this->inputLine = new char[1];
64  this->inputLine[0] = '\0';
65  this->inputHistory = new tList<char>;
66  //this->commandList = new tList<ShellCommand>;
67
68  this->rebuildText();
69  this->completionList = NULL;
70
71  // EVENT-Handler subscription of '`' to all States, and all other keyboard commands to ES_SEHLL
72  EventHandler* evh = EventHandler::getInstance();
73  evh->subscribe(this, ES_ALL, SDLK_BACKQUOTE);
74  for (int i = 1; i < SDLK_LAST; i++)
75    evh->subscribe(this, ES_SHELL, i);
76}
77
78Shell* Shell::singletonRef = NULL;
79
80/**
81 * standard deconstructor
82 */
83Shell::~Shell ()
84{
85  // delete the displayable Buffers
86  for (int i = 0; i < this->bufferDisplaySize; i++)
87    delete this->bufferText[i];
88  delete[] this->bufferText;
89
90  // delete the inputLine
91  delete this->inputLineText;
92  delete this->inputLine;
93
94  Shell::singletonRef = NULL;
95}
96
97/**
98 * activates the shell
99 *
100 * This also feeds the Last few lines from the main buffers into the displayBuffer
101 */
102void Shell::activate()
103{
104  if (this->bActive == true)
105    PRINTF(3)("The shell is already active\n");
106  this->bActive = true;
107
108  EventHandler::getInstance()->setState(ES_SHELL);
109  this->setRelCoorSoft2D(0, 0, 1, 5);
110
111  ShellBuffer::getInstance()->getBufferIterator()->lastElement();
112  for (int i = 0; i < this->bufferDisplaySize; i++)
113    this->bufferText[i]->setText(ShellBuffer::getInstance()->getBufferIterator()->prevElement(), true);
114}
115
116/**
117 * deactiveates the Shell.
118 */
119void Shell::deactivate()
120{
121  if (this->bActive == false)
122    PRINTF(3)("The shell is already inactive\n");
123  this->bActive = false;
124
125  EventHandler::getInstance()->setState(ES_GAME);
126  this->setRelCoorSoft2D(0, -400, 1, 5);
127
128  ShellBuffer::getInstance()->getBufferIterator()->lastElement();
129  for (int i = 0; i < this->bufferDisplaySize; i++)
130    this->bufferText[i]->setText(ShellBuffer::getInstance()->getBufferIterator()->prevElement(), false);
131}
132
133
134/**
135 * sets the size of the text and spacing
136 * @param textSize the size of the Text in Pixels
137 * @param lineSpacing the size of the Spacing between two lines in pixels
138 *
139 * this also rebuilds the entire Text, inputLine and displayBuffer,
140 * to be accurate again.
141 */
142void Shell::setTextSize(unsigned int textSize, unsigned int lineSpacing)
143{
144  this->textSize = textSize;
145  this->lineSpacing = lineSpacing;
146
147  this->rebuildText();
148}
149
150/**
151 * rebuilds the Text's
152 *
153 * use this function, if you changed the Font/Size or something else.
154 */
155void Shell::rebuildText()
156{
157  if (this->inputLineText == NULL)
158    delete this->inputLineText;
159  this->inputLineText = TextEngine::getInstance()->createText("fonts/Aniron_Bold.ttf", this->textSize, TEXT_RENDER_DYNAMIC);
160  this->inputLineText->setColor(1, 0, 0);
161  this->inputLineText->setAlignment(TEXT_ALIGN_LEFT);
162  this->inputLineText->setText(NULL);
163  this->inputLineText->setParent2D(this);
164  this->inputLineText->setRelCoor2D(5, (this->textSize + this->lineSpacing)*this->bufferDisplaySize + this->textSize);
165
166  this->setBufferDisplaySize(this->bufferDisplaySize);
167}
168
169/**
170 * sets The count of Lines to display in the buffer.
171 * @param bufferDisplaySize the count of lines to display in the Shell-Buffer.
172 */
173void Shell::setBufferDisplaySize(unsigned int bufferDisplaySize)
174{
175  if (this->bufferText != NULL)
176  {
177    for (unsigned int i = 0; i < this->bufferDisplaySize; i++)
178      delete this->bufferText[i];
179    delete[] this->bufferText;
180  }
181
182  this->bufferText = new Text*[bufferDisplaySize];
183  for (unsigned int i = 0; i < bufferDisplaySize; i++)
184  {
185    this->bufferText[i] = TextEngine::getInstance()->createText("fonts/Aniron_Bold.ttf", this->textSize, TEXT_RENDER_DYNAMIC);
186    this->bufferText[i]->setColor(1, 0, 0);
187    this->bufferText[i]->setAlignment(TEXT_ALIGN_LEFT);
188    this->bufferText[i]->setRelCoor2D(calculateLinePosition(i));
189    this->bufferText[i]->setText(NULL);
190    this->bufferText[i]->setParent2D(this);
191  }
192  this->bufferDisplaySize = bufferDisplaySize;
193
194  this->shellHeight = (this->textSize + this->lineSpacing) * (bufferDisplaySize+1);
195}
196
197/**
198 * deletes all the Buffers
199 */
200void Shell::flush()
201{
202  // remove all chars from the BufferTexts.
203  if (this->bufferText)
204    for (int i = 0; i < this->bufferDisplaySize; i++)
205    {
206      this->bufferText[i]->setText(NULL, true);
207    }
208
209
210    // BUFFER FLUSHING
211}
212
213/**
214 * prints out some text to the input-buffers
215 * @param text the text to output.
216 */
217void Shell::printToDisplayBuffer(const char* text)
218{
219  if(likely(bufferText != NULL))
220  {
221    Text* lastText = this->bufferText[this->bufferDisplaySize-1];
222
223    Text* swapText;
224    Text* moveText = this->bufferText[0];
225    this->bufferText[0]->setRelCoorSoft2D(this->calculateLinePosition(1),10);
226    for (unsigned int i = 1; i < this->bufferDisplaySize; i++)
227    {
228      if ( i < this->bufferDisplaySize-1)
229        this->bufferText[i]->setRelCoorSoft2D(this->calculateLinePosition(i+1),5);
230      swapText = this->bufferText[i];
231      this  ->bufferText[i] = moveText;
232      moveText = swapText;
233    }
234    lastText->setRelCoor2D(this->calculateLinePosition(0));
235    this->bufferText[0] = lastText;
236
237    this->bufferText[0]->setText(text, true);
238  }
239}
240
241/**
242 * deletes the InputLine
243 */
244void Shell::flushInputLine()
245{
246  if (likely(this->inputLine != NULL))
247  {
248    delete[] this->inputLine;
249  }
250  this->inputLine = new char[1];
251  *this->inputLine = '\0';
252  this->inputLineText->setText(this->inputLine, true);
253}
254
255/**
256 * adds one character to the inputLine
257 * @param character the character to add to the inputLine
258 */
259void Shell::addCharacter(char character)
260{
261  char* addCharLine = new char[strlen(inputLine)+2];
262
263  sprintf(addCharLine, "%s%c", this->inputLine, character);
264  delete this->inputLine;
265  this->inputLine = addCharLine;
266  this->inputLineText->setText(inputLine, true);
267}
268
269/**
270 * adds multiple Characters to thr inputLine
271 * @param characters a \\0 terminated char-array to add to the InputLine
272 */
273void Shell::addCharacters(const char* characters)
274{
275  char* addCharLine = new char[strlen(inputLine)+strlen(characters)+1];
276
277  sprintf(addCharLine, "%s%s", this->inputLine, characters);
278  delete this->inputLine;
279  this->inputLine = addCharLine;
280  this->inputLineText->setText(inputLine, true);
281}
282
283/**
284 * removes characterCount characters from the InputLine
285 * @param characterCount the count of Characters to remove from the input Line
286 */
287void Shell::removeCharacters(unsigned int characterCount)
288{
289  if (strlen(this->inputLine) == 0)
290    return;
291
292  if (characterCount > strlen(this->inputLine))
293    characterCount = strlen(this->inputLine);
294
295  char* removeCharLine = new char[strlen(inputLine)-characterCount+1];
296
297  strncpy(removeCharLine, this->inputLine, strlen(inputLine)-characterCount);
298  removeCharLine[strlen(inputLine)-characterCount] = '\0';
299  delete this->inputLine;
300  this->inputLine = removeCharLine;
301  this->inputLineText->setText(inputLine, true);
302}
303
304/**
305 * executes the command stored in the inputLine
306 * @return true if the command was commited successfully, false otherwise
307 */
308bool Shell::executeCommand()
309{
310  ShellBuffer::addBufferLineStatic("Execute Command: %s\n", this->inputLine);
311
312  char* newCommand = new char[strlen(this->inputLine)+1];
313  strcpy(newCommand, this->inputLine);
314  this->inputHistory->add(newCommand);
315
316  ShellCommandBase::execute(this->inputLine);
317
318  this->flushInputLine();
319
320  return false;
321}
322
323/**
324 * clears the Shell (empties all buffers)
325 */
326void Shell::clear()
327{
328  this->flush();
329  ShellBuffer::addBufferLineStatic("orxonox - shell\n ==================== \n", NULL);
330}
331
332/**
333 * sets the Repeate-delay and rate
334 * @param repeatDelay the Delay it takes, to repeate a key
335 * @param repeatRate the rate to repeate a pressed key
336 */
337void Shell::setRepeatDelay(float repeatDelay, float repeatRate)
338{
339  this->repeatDelay = repeatDelay;
340  this->repeatRate = repeatRate;
341
342}
343
344/**
345 * listens for some event
346 * @param event the Event happened
347 */
348void Shell::process(const Event &event)
349{
350  if (event.bPressed)
351  {
352    PRINTF(5)("Shell received command %s\n", SDLKToKeyname(event.type));
353    if (event.type == SDLK_BACKQUOTE)
354    {
355      if (EventHandler::getInstance()->getState() == ES_GAME)
356        this->activate();
357      else
358        this->deactivate();
359    }
360    else if (event.type == SDLK_F1)
361      this->help();
362    else if (event.type == SDLK_F2)
363      this->debug();
364    else if (event.type == SDLK_TAB)
365      this->autoComplete();
366    else if (event.type == SDLK_BACKSPACE)
367    {
368      this->delayed = this->repeatDelay;
369      this->pressedKey = SDLK_BACKSPACE;
370      this->removeCharacters(1);
371    }
372    else if (event.type == SDLK_RETURN)
373      this->executeCommand();
374    /*
375    else if (event.type == SDLK_UP)
376    {
377//      this->flushInputLine();
378      tIterator<char>* iterator = this->commandList->getIterator();
379      char* command = iterator->lastElement();
380      while (command)
381      {
382        if (!strcmp (command, inputLine))
383        {
384          inputLine = iterator->prevElement();
385          return;
386        }
387        command = iterator->prevElement();
388      }
389      inputLine = iterator->lastElement();
390    }
391    */
392    else if (likely(event.type < 127))
393    {
394      Uint8 *keystate = SDL_GetKeyState(NULL);
395      this->delayed = this->repeatDelay;
396      if (unlikely( keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT] ))
397      {
398        this->pressedKey = event.type-32;
399        this->addCharacter(event.type-32);
400      }
401      else
402      {
403        this->pressedKey = event.type;
404        this->addCharacter(event.type);
405      }
406    }
407  }
408  else // if(!event.bPressed)
409  {
410    if (this->pressedKey == event.type || (this->pressedKey == event.type - 32))
411    {
412      this->pressedKey = SDLK_FIRST;
413      this->delayed = 0.0;
414    }
415  }
416}
417
418/**
419 * ticks the Shell for dt Seconds
420 * @param dt the elapsed time since the last tick();
421 */
422void Shell::tick(float dt)
423{
424  if (this->delayed > 0.0)
425    this->delayed -= dt;
426  else if (this->pressedKey != SDLK_FIRST )
427  {
428    this->delayed = this->repeatRate;
429    if (this->pressedKey == SDLK_BACKSPACE)
430      this->removeCharacters(1);
431    else if (pressedKey < 127)
432      this->addCharacter(this->pressedKey);
433  }
434}
435
436/**
437 * displays the Shell
438 */
439void Shell::draw() const
440{
441  glPushMatrix();
442  // transform for alignment.
443  // setting the Blending effects
444
445  glColor4f(0.0f, 0.0f, 0.8f, .4);
446  glEnable(GL_BLEND);
447  glDisable(GL_TEXTURE_2D);
448  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
449
450  glBindTexture(GL_TEXTURE_2D, 0);
451  glBegin(GL_TRIANGLE_STRIP);
452
453  glTexCoord2f(0, 0);
454  glVertex2f(this->getAbsCoor2D().x,   this->getAbsCoor2D().);
455
456  glTexCoord2f(1, 0);
457  glVertex2f(GraphicsEngine::getInstance()->getResolutionX() - this->getAbsCoor2D().x, this->getAbsCoor2D().);
458
459  glTexCoord2f(0, 1);
460  glVertex2f(this->getAbsCoor2D().x, this->getAbsCoor2D().y + this->shellHeight);
461
462  glTexCoord2f(1, 1);
463  glVertex2f(GraphicsEngine::getInstance()->getResolutionX() - this->getAbsCoor2D().x, this->getAbsCoor2D().y + this->shellHeight);
464
465  glEnd();
466}
467
468
469/**
470 * autocompletes the Shell's inputLine
471 * @returns true, if a result was found, false otherwise
472 *
473 * @todo implement it!!
474 */
475bool Shell::autoComplete()
476{
477  //PRINTF(3)("AutoCompletion not implemented yet\n");
478
479  char* completionLine = new char[strlen(inputLine)+1];
480  strcpy(completionLine, this->inputLine);
481
482  char* commandBegin = strrchr(completionLine, ' ');
483  if (commandBegin == NULL)
484    commandBegin = completionLine;
485  else
486  {
487    if(commandBegin >= completionLine + strlen(completionLine))
488      commandBegin = completionLine + strlen(completionLine);
489    else
490      commandBegin++;
491  }
492
493  char* objectStart;
494  if (objectStart = strstr(commandBegin, "::"))
495  {
496    char* classIdentity = new char[objectStart - commandBegin +1];
497    strncpy(classIdentity, commandBegin, objectStart - commandBegin);
498    classIdentity[objectStart - commandBegin] = '\0';
499    this->objectComplete(objectStart+2, ClassList::StringToID(classIdentity));
500    delete[] classIdentity;
501  }
502  else
503    this->classComplete(commandBegin);
504
505  delete[] completionLine;
506}
507
508/**
509 * autocompletes a className
510 * @param classBegin the Beginning of a String to autoComplete
511 * @return true on success, false otherwise
512 */
513bool Shell::classComplete(const char* classBegin)
514{
515  if (unlikely(classBegin == NULL))
516    return false;
517  const tList<const char>* clList = ClassList::getClassList();
518  if (clList != NULL)
519  {
520    const tList<const char>* classList = this->createCompleteList(clList, classBegin);
521    if (classList != NULL)
522      this->generalComplete(classList, classBegin, "%s::", "::");
523    else
524      return false;
525  }
526  else
527    return false;
528  return true;
529}
530
531/**
532 * autocompletes an ObjectName
533 * @param objectBegin the beginning string of a Object
534 * @param classID the ID of the Class to search for.
535 * @return true on success, false otherwise
536 */
537bool Shell::objectComplete(const char* objectBegin, long classID)
538{
539  printf("%s\n", objectBegin);
540
541  if (unlikely(objectBegin == NULL))
542    return false;
543  tList<BaseObject>* boList = ClassList::getList(classID);
544  if (boList != NULL)
545  {
546    printf("\n", boList->firstElement()->getName());
547    const tList<const char>* objectList = this->createCompleteList(boList, objectBegin);
548    if (objectList != NULL)
549      this->generalComplete(objectList, objectBegin, "%s");
550    else
551      return false;
552  }
553  else
554    return false;
555  return true;
556}
557
558/**
559 * completes a Function
560 * @param functionBegin the beginning of the function String
561 */
562bool Shell::functionComplete(const char* functionBegin)
563{
564}
565
566/**
567 * completes the inputline on grounds of an inputList
568 * @param stringList the List to parse through
569 * @param begin the String to search in the inputList, and to extend with it.
570 * @param displayAs how to display the found value to the user, printf-style, !!with only one %s!! ex.: "::%s::"
571 * @param addBack what should be added at the end of the completion
572 * @param addFront what should be added to the front of one finished completion
573 * @return true if ok, false otherwise
574 */
575bool Shell::generalComplete(const tList<const char>* stringList, const char* begin, const char* displayAs, const char* addBack, const char* addFront)
576{
577  if (stringList->getSize() == 0)
578    return false;
579
580  const char* addString = stringList->firstElement();
581  unsigned int addLength = 0;
582  unsigned int inputLenght = strlen(begin);
583
584  if (addString != NULL)
585    addLength = strlen(addString);
586  tIterator<const char>* charIterator = stringList->getIterator();
587  const char* charElem = charIterator->firstElement();
588  while (charElem != NULL)
589  {
590    PRINTF(0)(displayAs, charElem);
591    for (unsigned int i = inputLenght; i < addLength; i++)
592      if (addString[i] != charElem[i])
593    {
594      addLength = i;
595      break;
596    }
597    charElem = charIterator->nextElement();
598  }
599  delete charIterator;
600
601  if (addLength >= inputLenght)
602  {
603    char* adder = new char[addLength+1];
604    strncpy(adder, addString, addLength);
605    adder[addLength] = '\0';
606    this->removeCharacters(inputLenght);
607    this->addCharacters(adder);
608    if (addBack != NULL && stringList->getSize() == 1)
609      this->addCharacters("::");
610    delete[] adder;
611  }
612  return true;
613}
614
615/**
616 * searches for classes, which beginn with classNameBegin
617 * @param inputList the List to parse through
618 * @param classNameBegin the beginning string
619 * @return a NEW char-array with ClassNames. The LIST should be deleted afterwards,
620 * !! The strings MUST NOT be deleted !!
621 */
622const tList<const char>* Shell::createCompleteList(const tList<const char>* inputList, const char* classNameBegin)
623{
624  if (inputList == NULL || classNameBegin == NULL)
625    return NULL;
626  unsigned int searchLength = strlen(classNameBegin);
627  if (this->completionList != NULL)
628    delete this->completionList;
629  this->completionList = new tList<const char>;
630
631//  tList<const char>* classList = ClassList::getClassList();
632
633  tIterator<const char>* iterator = inputList->getIterator();
634  const char* enumString = iterator->firstElement();
635  while (enumString != NULL)
636  {
637    if (strlen(enumString)>searchLength+1 &&
638        !strncasecmp(enumString, classNameBegin, searchLength))
639    {
640      this->completionList->add(enumString);
641    }
642    enumString = iterator->nextElement();
643  }
644  delete iterator;
645
646  return this->completionList;
647}
648
649/**
650 * searches for classes, which beginn with classNameBegin
651 * @param inputList the List to parse through
652 * @param classNameBegin the beginning string
653 * @return a NEW char-array with ClassNames. The LIST should be deleted afterwards,
654 * !! The strings MUST NOT be deleted !!
655 */
656const tList<const char>* Shell::createCompleteList(const tList<BaseObject>* inputList, const char* classNameBegin)
657{
658  if (inputList == NULL || classNameBegin == NULL)
659    return NULL;
660  unsigned int searchLength = strlen(classNameBegin);
661  if (this->completionList != NULL)
662    delete this->completionList;
663  this->completionList = new tList<const char>;
664
665  tIterator<BaseObject>* iterator = inputList->getIterator();
666  BaseObject* enumBO = iterator->firstElement();
667  while (enumBO != NULL)
668  {
669    if (enumBO->getName() != NULL &&
670        strlen(enumBO->getName())>searchLength+1 &&
671        !strncasecmp(enumBO->getName(), classNameBegin, searchLength))
672    {
673      this->completionList->add(enumBO->getName());
674    }
675    enumBO = iterator->nextElement();
676  }
677  delete iterator;
678
679  return this->completionList;
680}
681
682/**
683 * prints out some nice help about the Shell
684 */
685void Shell::help() const
686{
687  PRINT(0)("Help for the most important Shell-commands\n");
688  PRINT(0)("F1 - HELP; F2 - DEBUG; ` - open/close shell\n");
689  PRINT(0)("input order:\n");
690  PRINT(0)("ClassName::objectName function [parameter1, [parameter2 ...]]  or\n");
691  PRINT(0)("Command [parameter]\n");
692}
693
694
695///////////////////////
696// HELPER FUNCTIONS  //
697///////////////////////
698
699/**
700 * calculates the position of a Buffer-Display Line
701 * @param lineNumber the lineNumber from the bottom to calculate the position from
702 * @returns the Position of the Line.
703 */
704Vector Shell::calculateLinePosition(unsigned int lineNumber)
705{
706  return Vector(5, (this->textSize + this->lineSpacing)*(this->bufferDisplaySize - lineNumber -1) + this->textSize, 0);
707}
708
709
710
711/**
712 * displays some nice output from the Shell
713 */
714void Shell::debug() const
715{
716  PRINT(3)("Debugging output to console (not this shell)\n");
717
718  if (this->pressedKey != SDLK_FIRST)
719    printf("%s::%f %f\n", SDLKToKeyname(this->pressedKey), this->delayed, this->repeatDelay);
720
721
722  char* tmpChar = ShellBuffer::getInstance()->getBufferIterator()->firstElement();
723  while(tmpChar != NULL)
724  {
725    printf(tmpChar);
726    tmpChar = ShellBuffer::getInstance()->getBufferIterator()->nextElement();
727  }
728}
729
730
731
732// void Shell::testI (int i)
733// {
734//   PRINTF(3)("This is the Test for one Int '%d'\n", i);
735// }
736//
737// void Shell::testS (const char* s)
738// {
739//   PRINTF(3)("This is the Test for one String '%s'\n", s);
740// }
741//
742// void Shell::testB (bool b)
743// {
744//   PRINTF(3)("This is the Test for one Bool: ");
745//   if (b)
746//     PRINTF(3)("true\n");
747//   else
748//     PRINTF(3)("false\n");
749// }
750//
751// void Shell::testF (float f)
752// {
753//   PRINTF(3)("This is the Test for one Float '%f'\n", f);
754// }
755//
756// void Shell::testSF (const char* s, float f)
757// {
758//   PRINTF(3)("This is the Test for one String '%s' and one Float '%f'\n",s , f);
759// }
Note: See TracBrowser for help on using the repository browser.