Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/util/shell.cc @ 5119

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

orxonox/trunk: cleanup/some display stuff

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