Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: semi-active buffers

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