Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5106 was 5106, checked in by bensch, 20 years ago

orxonox/trunk: top-down shell, as proposed by patrick. This makes sense, becuase the general people knows only shells comming top down

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