~ubuntu-branches/debian/squeeze/stella/squeeze

« back to all changes in this revision

Viewing changes to src/debugger/gui/PromptWidget.cxx

  • Committer: Bazaar Package Importer
  • Author(s): Mario Iseli
  • Date: 2006-04-08 18:38:25 UTC
  • mfrom: (1.1.2 upstream) (2.1.1 etch)
  • Revision ID: james.westby@ubuntu.com-20060408183825-vu1jk57rk929derx
* New Maintainer (Closes: #361345)
* New upstream release (Closes: #349725)
* Build-Depend now on libslang2-dev (Closes: #325577)
* Complete rebuild of debian/, upgraded to policy-standards
  3.6.2 and compat-level 5.
* Removed stellarc since stella only reads ~/.stellarc and even
  works without a first config.
* New debian/watch file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//============================================================================
 
2
//
 
3
//   SSSS    tt          lll  lll       
 
4
//  SS  SS   tt           ll   ll        
 
5
//  SS     tttttt  eeee   ll   ll   aaaa 
 
6
//   SSSS    tt   ee  ee  ll   ll      aa
 
7
//      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
 
8
//  SS  SS   tt   ee      ll   ll  aa  aa
 
9
//   SSSS     ttt  eeeee llll llll  aaaaa
 
10
//
 
11
// Copyright (c) 1995-2005 by Bradford W. Mott and the Stella team
 
12
//
 
13
// See the file "license" for information on usage and redistribution of
 
14
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
15
//
 
16
// $Id: PromptWidget.cxx,v 1.8 2006/03/25 00:34:17 stephena Exp $
 
17
//
 
18
//   Based on code from ScummVM - Scumm Interpreter
 
19
//   Copyright (C) 2002-2004 The ScummVM project
 
20
//============================================================================
 
21
 
 
22
#include <iostream>
 
23
#include <fstream>
 
24
 
 
25
#include "ScrollBarWidget.hxx"
 
26
#include "FrameBuffer.hxx"
 
27
#include "EventHandler.hxx"
 
28
#include "Version.hxx"
 
29
#include "Debugger.hxx"
 
30
#include "DebuggerDialog.hxx"
 
31
#include "DebuggerParser.hxx"
 
32
 
 
33
#include "PromptWidget.hxx"
 
34
#include "EquateList.hxx"
 
35
 
 
36
#define PROMPT  "> "
 
37
 
 
38
/* TODO:
 
39
 * - it is very inefficient to redraw the full thingy when just one char is added/removed.
 
40
 *   Instead, we could just copy the GFX of the blank console (i.e. after the transparent
 
41
 *   background is drawn, before any text is drawn). Then using that, it becomes trivial
 
42
 *   to erase a single character, do scrolling etc.
 
43
 * - a *lot* of others things, this code is in no way complete and heavily under progress
 
44
 */
 
45
 
 
46
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
47
PromptWidget::PromptWidget(GuiObject* boss, const GUI::Font& font,
 
48
                           int x, int y, int w, int h)
 
49
  : Widget(boss, font, x, y, w - kScrollBarWidth, h),
 
50
    CommandSender(boss),
 
51
    _makeDirty(false),
 
52
    _firstTime(true)
 
53
{
 
54
  _flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS |
 
55
           WIDGET_WANTS_TAB;
 
56
  _type = kPromptWidget;
 
57
 
 
58
  _kConsoleCharWidth  = font.getMaxCharWidth();
 
59
  _kConsoleCharHeight = font.getFontHeight();
 
60
  _kConsoleLineHeight = _kConsoleCharHeight + 2;
 
61
 
 
62
  // Calculate depending values
 
63
  _lineWidth = (_w - kScrollBarWidth - 2) / _kConsoleCharWidth;
 
64
  _linesPerPage = (_h - 2) / _kConsoleLineHeight;
 
65
 
 
66
  memset(_buffer, 0, kBufferSize * sizeof(int));
 
67
  _linesInBuffer = kBufferSize / _lineWidth;
 
68
 
 
69
  _currentPos = 0;
 
70
  _scrollLine = _linesPerPage - 1;
 
71
  _firstLineInBuffer = 0;
 
72
 
 
73
  // Add scrollbar
 
74
  _scrollBar = new ScrollBarWidget(boss, font, _x + _w, _y, kScrollBarWidth, _h);
 
75
  _scrollBar->setTarget(this);
 
76
 
 
77
  // Init colors
 
78
  defaultTextColor = kTextColor;
 
79
  defaultBGColor = kBGColor;
 
80
  textColor = defaultTextColor;
 
81
  bgColor = defaultBGColor;
 
82
  _inverse = false;
 
83
 
 
84
  // Init History
 
85
  _historyIndex = 0;
 
86
  _historyLine = 0;
 
87
  _historySize = 0;
 
88
  for (int i = 0; i < kHistorySize; i++)
 
89
    _history[i][0] = '\0';
 
90
 
 
91
  _promptStartPos = _promptEndPos = -1;
 
92
 
 
93
  addFocusWidget(this);
 
94
}
 
95
 
 
96
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
97
PromptWidget::~PromptWidget()
 
98
{
 
99
}
 
100
 
 
101
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
102
void PromptWidget::drawWidget(bool hilite)
 
103
{
 
104
//cerr << "PromptWidget::drawWidget\n";
 
105
  int fgcolor, bgcolor;
 
106
 
 
107
  FrameBuffer& fb = _boss->instance()->frameBuffer();
 
108
 
 
109
  // Draw text
 
110
  int start = _scrollLine - _linesPerPage + 1;
 
111
  int y = _y + 2;
 
112
 
 
113
  for (int line = 0; line < _linesPerPage; line++)
 
114
  {
 
115
    int x = _x + 1;
 
116
    for (int column = 0; column < _lineWidth; column++) {
 
117
      int c = buffer((start + line) * _lineWidth + column);
 
118
 
 
119
      if(c & (1 << 17)) { // inverse video flag
 
120
        fgcolor = bgColor;
 
121
        bgcolor = (c & 0x1ffff) >> 8;
 
122
        fb.fillRect(x, y, _kConsoleCharWidth, _kConsoleCharHeight, bgcolor);
 
123
      } else {
 
124
        fgcolor = c >> 8;
 
125
        bgcolor = bgColor;
 
126
      }
 
127
      fb.drawChar(&instance()->consoleFont(), c & 0x7f, x, y, fgcolor);
 
128
      x += _kConsoleCharWidth;
 
129
    }
 
130
    y += _kConsoleLineHeight;
 
131
  }
 
132
 
 
133
  // Draw the caret
 
134
  drawCaret();
 
135
 
 
136
  // Draw the scrollbar
 
137
  _scrollBar->draw();
 
138
}
 
139
 
 
140
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
141
void PromptWidget::handleMouseDown(int x, int y, int button, int clickCount)
 
142
{
 
143
//  cerr << "PromptWidget::handleMouseDown\n";
 
144
}
 
145
 
 
146
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
147
void PromptWidget::handleMouseWheel(int x, int y, int direction)
 
148
{
 
149
  _scrollBar->handleMouseWheel(x, y, direction);
 
150
}
 
151
 
 
152
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
153
void PromptWidget::printPrompt()
 
154
{
 
155
  string watches = instance()->debugger().showWatches();
 
156
  if(watches.length() > 0)
 
157
    print(watches);
 
158
 
 
159
  print(PROMPT);
 
160
  _promptStartPos = _promptEndPos = _currentPos;
 
161
}
 
162
 
 
163
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
164
bool PromptWidget::handleKeyDown(int ascii, int keycode, int modifiers)
 
165
{
 
166
  int i;
 
167
  bool handled = true;
 
168
  bool dirty = false;
 
169
        
 
170
  switch(ascii)
 
171
  {
 
172
    case '\n': // enter/return
 
173
    case '\r':
 
174
    {
 
175
      nextLine();
 
176
 
 
177
      assert(_promptEndPos >= _promptStartPos);
 
178
      int len = _promptEndPos - _promptStartPos;
 
179
 
 
180
      if (len > 0)
 
181
      {
 
182
        // We have to allocate the string buffer with new, since VC++ sadly does not
 
183
        // comply to the C++ standard, so we can't use a dynamic sized stack array.
 
184
        char *str = new char[len + 1];
 
185
 
 
186
        // Copy the user input to str
 
187
        for (i = 0; i < len; i++)
 
188
          str[i] = buffer(_promptStartPos + i) & 0x7f;
 
189
        str[len] = '\0';
 
190
 
 
191
        // Add the input to the history
 
192
        addToHistory(str);
 
193
 
 
194
        // Pass the command to the debugger, and print the result
 
195
        print( instance()->debugger().run(str) + "\n");
 
196
 
 
197
        // Get rid of the string buffer
 
198
        delete [] str;
 
199
      }
 
200
 
 
201
      printPrompt();
 
202
      dirty = true;
 
203
      break;
 
204
    }
 
205
 
 
206
    case '\t':   // tab
 
207
    {
 
208
      // Tab completion: we complete either commands or labels, but not
 
209
      // both at once.
 
210
 
 
211
      if(_currentPos <= _promptStartPos)
 
212
        break;
 
213
 
 
214
      scrollToCurrent();
 
215
      int len = _promptEndPos - _promptStartPos;
 
216
 
 
217
      int lastDelimPos = -1;
 
218
      char delimiter = '\0';
 
219
 
 
220
      char *str = new char[len + 1];
 
221
      for (i = 0; i < len; i++) {
 
222
        str[i] = buffer(_promptStartPos + i) & 0x7f;
 
223
        if(strchr("*@<> ", str[i]) != NULL ) {
 
224
          lastDelimPos = i;
 
225
          delimiter = str[i];
 
226
        }
 
227
      }
 
228
      str[len] = '\0';
 
229
 
 
230
      const char *completionList;
 
231
      const char *prefix;
 
232
      int possibilities;
 
233
 
 
234
      if(lastDelimPos < 0) {
 
235
        // no delimiters, do command completion:
 
236
        DebuggerParser *parser = instance()->debugger().parser();
 
237
        possibilities = parser->countCompletions(str);
 
238
 
 
239
        if(possibilities < 1) {
 
240
          delete[] str;
 
241
          break;
 
242
        }
 
243
 
 
244
        completionList = parser->getCompletions();
 
245
        prefix = parser->getCompletionPrefix();
 
246
            } else {
 
247
        // we got a delimiter, so this must be a label:
 
248
        EquateList *equates = instance()->debugger().equates();
 
249
        possibilities = equates->countCompletions(str + lastDelimPos + 1);
 
250
 
 
251
        if(possibilities < 1) {
 
252
          delete[] str;
 
253
          break;
 
254
        }
 
255
 
 
256
                completionList = equates->getCompletions();
 
257
                prefix = equates->getCompletionPrefix();
 
258
            }
 
259
 
 
260
      if(possibilities == 1) {
 
261
        // add to buffer as though user typed it (plus a space)
 
262
        _currentPos = _promptStartPos + lastDelimPos + 1;
 
263
        while(*completionList != '\0') {
 
264
          putcharIntern(*completionList++);
 
265
        }
 
266
        putcharIntern(' ');
 
267
        _promptEndPos = _currentPos;
 
268
      } else {
 
269
        nextLine();
 
270
        // add to buffer as-is, then add PROMPT plus whatever we have so far
 
271
        _currentPos = _promptStartPos + lastDelimPos + 1;
 
272
 
 
273
        print("\n");
 
274
        print(completionList);
 
275
        print("\n");
 
276
        print(PROMPT);
 
277
 
 
278
        _promptStartPos = _currentPos;
 
279
 
 
280
        for(i=0; i<lastDelimPos; i++)
 
281
          putcharIntern(str[i]);
 
282
 
 
283
        if(lastDelimPos > 0)
 
284
          putcharIntern(delimiter);
 
285
 
 
286
        print(prefix);
 
287
        _promptEndPos = _currentPos;
 
288
      }
 
289
      delete[] str;
 
290
      dirty = true;
 
291
      break;
 
292
    }
 
293
 
 
294
    case 8:  // backspace
 
295
      if (_currentPos > _promptStartPos)
 
296
        killChar(-1);
 
297
 
 
298
      scrollToCurrent();
 
299
      dirty = true;
 
300
      break;
 
301
 
 
302
    case 127:
 
303
      killChar(+1);
 
304
      dirty = true;
 
305
      break;
 
306
 
 
307
    case 256 + 24:  // pageup
 
308
      if (instance()->eventHandler().kbdShift(modifiers))
 
309
      {
 
310
        // Don't scroll up when at top of buffer
 
311
        if(_scrollLine < _linesPerPage)
 
312
          break;
 
313
 
 
314
        _scrollLine -= _linesPerPage - 1;
 
315
        if (_scrollLine < _firstLineInBuffer + _linesPerPage - 1)
 
316
          _scrollLine = _firstLineInBuffer + _linesPerPage - 1;
 
317
        updateScrollBuffer();
 
318
 
 
319
        dirty = true;
 
320
      }
 
321
      break;
 
322
 
 
323
    case 256 + 25:  // pagedown
 
324
      if (instance()->eventHandler().kbdShift(modifiers))
 
325
      {
 
326
        // Don't scroll down when at bottom of buffer
 
327
        if(_scrollLine >= _promptEndPos / _lineWidth)
 
328
          break;
 
329
 
 
330
        _scrollLine += _linesPerPage - 1;
 
331
        if (_scrollLine > _promptEndPos / _lineWidth)
 
332
          _scrollLine = _promptEndPos / _lineWidth;
 
333
        updateScrollBuffer();
 
334
 
 
335
        dirty = true;
 
336
      }
 
337
      break;
 
338
 
 
339
    case 256 + 22:  // home
 
340
      if (instance()->eventHandler().kbdShift(modifiers))
 
341
      {
 
342
        _scrollLine = _firstLineInBuffer + _linesPerPage - 1;
 
343
        updateScrollBuffer();
 
344
      }
 
345
      else
 
346
        _currentPos = _promptStartPos;
 
347
 
 
348
      dirty = true;
 
349
      break;
 
350
 
 
351
    case 256 + 23:  // end
 
352
      if (instance()->eventHandler().kbdShift(modifiers))
 
353
      {
 
354
        _scrollLine = _promptEndPos / _lineWidth;
 
355
        if (_scrollLine < _linesPerPage - 1)
 
356
          _scrollLine = _linesPerPage - 1;
 
357
        updateScrollBuffer();
 
358
      }
 
359
      else
 
360
        _currentPos = _promptEndPos;
 
361
 
 
362
      dirty = true;
 
363
      break;
 
364
 
 
365
    case 273:  // cursor up
 
366
      if (instance()->eventHandler().kbdShift(modifiers))
 
367
      {
 
368
        if(_scrollLine <= _firstLineInBuffer + _linesPerPage - 1)
 
369
          break;
 
370
 
 
371
        _scrollLine -= 1;
 
372
        updateScrollBuffer();
 
373
 
 
374
        dirty = true;
 
375
      }
 
376
      else
 
377
        historyScroll(+1);
 
378
      break;
 
379
 
 
380
    case 274:  // cursor down
 
381
      if (instance()->eventHandler().kbdShift(modifiers))
 
382
      {
 
383
        // Don't scroll down when at bottom of buffer
 
384
        if(_scrollLine >= _promptEndPos / _lineWidth)
 
385
          break;
 
386
 
 
387
        _scrollLine += 1;
 
388
        updateScrollBuffer();
 
389
 
 
390
        dirty = true;
 
391
      }
 
392
      else
 
393
        historyScroll(-1);
 
394
      break;
 
395
 
 
396
    case 275:  // cursor right
 
397
      if (_currentPos < _promptEndPos)
 
398
        _currentPos++;
 
399
 
 
400
      dirty = true;
 
401
      break;
 
402
 
 
403
    case 276:  // cursor left
 
404
      if (_currentPos > _promptStartPos)
 
405
        _currentPos--;
 
406
 
 
407
      dirty = true;
 
408
      break;
 
409
 
 
410
    default:
 
411
      if (instance()->eventHandler().kbdControl(modifiers))
 
412
      {
 
413
        specialKeys(keycode);
 
414
      }
 
415
      else if (instance()->eventHandler().kbdAlt(modifiers))
 
416
      {
 
417
      }
 
418
      else if (isprint(ascii))
 
419
      {
 
420
        for (i = _promptEndPos - 1; i >= _currentPos; i--)
 
421
          buffer(i + 1) = buffer(i);
 
422
        _promptEndPos++;
 
423
        putchar(ascii);
 
424
        scrollToCurrent();
 
425
      }
 
426
      else
 
427
        handled = false;
 
428
      break;
 
429
  }
 
430
 
 
431
  // Take care of changes made above
 
432
  if(dirty)
 
433
  {
 
434
    setDirty(); draw();
 
435
  }
 
436
 
 
437
  // There are times when we want the prompt and scrollbar to be marked
 
438
  // as dirty *after* they've been drawn above.  One such occurrence is
 
439
  // when we issue a command that indirectly redraws the entire parent
 
440
  // dialog (such as 'scanline' or 'frame').
 
441
  // In those cases, the return code of the command must be shown, but the
 
442
  // entire dialog contents are redrawn at a later time.  So the prompt and
 
443
  // scrollbar won't be redrawn unless they're dirty again.
 
444
  if(_makeDirty)
 
445
  {
 
446
    setDirty();
 
447
    _scrollBar->setDirty();
 
448
    _makeDirty = false;
 
449
  }
 
450
 
 
451
  return handled;
 
452
}
 
453
 
 
454
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
455
void PromptWidget::insertIntoPrompt(const char* str)
 
456
{
 
457
  unsigned int l = strlen(str);
 
458
 
 
459
  for (int i = _promptEndPos - 1; i >= _currentPos; i--)
 
460
    buffer(i + l) = buffer(i);
 
461
 
 
462
  for (unsigned int j = 0; j < l; ++j)
 
463
  {
 
464
    _promptEndPos++;
 
465
    putcharIntern(str[j]);
 
466
  }
 
467
}
 
468
 
 
469
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
470
void PromptWidget::handleCommand(CommandSender* sender, int cmd,
 
471
                                 int data, int id)
 
472
{
 
473
  switch (cmd)
 
474
  {
 
475
    case kSetPositionCmd:
 
476
      int newPos = (int)data + _linesPerPage - 1 + _firstLineInBuffer;
 
477
      if (newPos != _scrollLine)
 
478
      {
 
479
        _scrollLine = newPos;
 
480
        setDirty(); draw();
 
481
      }
 
482
      break;
 
483
  }
 
484
}
 
485
 
 
486
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
487
GUI::Rect PromptWidget::getRect() const
 
488
{
 
489
  // Account for attached scrollbar when calculating width
 
490
  int x = getAbsX() - 1,  y = getAbsY() - 1,
 
491
      w = getWidth() + kScrollBarWidth + 2, h = getHeight() + 2;
 
492
 
 
493
  GUI::Rect r(x, y, x+w, y+h);
 
494
  return r;
 
495
}
 
496
 
 
497
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
498
void PromptWidget::loadConfig()
 
499
{
 
500
  // See logic at the end of handleKeyDown for an explanation of this
 
501
  _makeDirty = true;
 
502
 
 
503
  // Show the prompt the first time we draw this widget
 
504
  if(_firstTime)
 
505
  {
 
506
    // Display greetings & prompt
 
507
    string version = string("Stella ") + STELLA_VERSION + "\n";
 
508
    print(version.c_str());
 
509
    print("Debugger is ready\n");
 
510
    print(PROMPT);
 
511
    _promptStartPos = _promptEndPos = _currentPos;
 
512
 
 
513
    _firstTime = false;
 
514
 
 
515
    // Take care of one-time debugger stuff
 
516
    instance()->debugger().autoExec();
 
517
  }
 
518
}
 
519
 
 
520
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
521
void PromptWidget::specialKeys(int keycode)
 
522
{
 
523
  bool handled = false;
 
524
 
 
525
  switch (keycode)
 
526
  {
 
527
    case 'a':
 
528
      _currentPos = _promptStartPos;
 
529
      handled = true;
 
530
      break;
 
531
 
 
532
    case 'd':
 
533
      killChar(+1);
 
534
      handled = true;
 
535
      break;
 
536
 
 
537
    case 'e':
 
538
      _currentPos = _promptEndPos;
 
539
      handled = true;
 
540
      break;
 
541
 
 
542
    case 'k':
 
543
      killLine(+1);
 
544
      handled = true;
 
545
      break;
 
546
 
 
547
    case 'u':
 
548
      killLine(-1);
 
549
      handled = true;
 
550
      break;
 
551
 
 
552
    case 'w':
 
553
      killLastWord();
 
554
      handled = true;
 
555
      break;
 
556
  }
 
557
 
 
558
  if(handled)
 
559
  {
 
560
    setDirty(); draw();
 
561
  }
 
562
}
 
563
 
 
564
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
565
void PromptWidget::killChar(int direction)
 
566
{
 
567
  if(direction == -1)    // Delete previous character (backspace)
 
568
  {
 
569
    if(_currentPos <= _promptStartPos)
 
570
      return;
 
571
 
 
572
    _currentPos--;
 
573
    for (int i = _currentPos; i < _promptEndPos; i++)
 
574
      buffer(i) = buffer(i + 1);
 
575
 
 
576
    buffer(_promptEndPos) = ' ';
 
577
    _promptEndPos--;
 
578
  }
 
579
  else if(direction == 1)    // Delete next character (delete)
 
580
  {
 
581
    if(_currentPos >= _promptEndPos)
 
582
      return;
 
583
 
 
584
    // There are further characters to the right of cursor
 
585
    if(_currentPos + 1 <= _promptEndPos)
 
586
    {
 
587
      for (int i = _currentPos; i < _promptEndPos; i++)
 
588
        buffer(i) = buffer(i + 1);
 
589
 
 
590
      buffer(_promptEndPos) = ' ';
 
591
      _promptEndPos--;
 
592
    }
 
593
  }
 
594
}
 
595
 
 
596
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
597
void PromptWidget::killLine(int direction)
 
598
{
 
599
  if(direction == -1)  // erase from current position to beginning of line
 
600
  {
 
601
    int count = _currentPos - _promptStartPos;
 
602
    if(count > 0)
 
603
      for (int i = 0; i < count; i++)
 
604
       killChar(-1);
 
605
  }
 
606
  else if(direction == 1)  // erase from current position to end of line
 
607
  {
 
608
    for (int i = _currentPos; i < _promptEndPos; i++)
 
609
      buffer(i) = ' ';
 
610
 
 
611
    _promptEndPos = _currentPos;
 
612
  }
 
613
}
 
614
 
 
615
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
616
void PromptWidget::killLastWord()
 
617
{
 
618
  int cnt = 0;
 
619
  bool space = true;
 
620
  while (_currentPos > _promptStartPos)
 
621
  {
 
622
    if ((buffer(_currentPos - 1) & 0xff) == ' ')
 
623
    {
 
624
      if (!space)
 
625
        break;
 
626
    }
 
627
    else
 
628
      space = false;
 
629
 
 
630
    _currentPos--;
 
631
    cnt++;
 
632
  }
 
633
 
 
634
  for (int i = _currentPos; i < _promptEndPos; i++)
 
635
    buffer(i) = buffer(i + cnt);
 
636
 
 
637
  buffer(_promptEndPos) = ' ';
 
638
  _promptEndPos -= cnt;
 
639
}
 
640
 
 
641
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
642
void PromptWidget::addToHistory(const char *str)
 
643
{
 
644
  strcpy(_history[_historyIndex], str);
 
645
  _historyIndex = (_historyIndex + 1) % kHistorySize;
 
646
  _historyLine = 0;
 
647
 
 
648
  if (_historySize < kHistorySize)
 
649
    _historySize++;
 
650
}
 
651
 
 
652
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
653
int PromptWidget::compareHistory(const char *histLine) {
 
654
        return 1;
 
655
}
 
656
 
 
657
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
658
void PromptWidget::historyScroll(int direction)
 
659
{
 
660
  if (_historySize == 0)
 
661
    return;
 
662
 
 
663
  if (_historyLine == 0 && direction > 0)
 
664
  {
 
665
    int i;
 
666
    for (i = 0; i < _promptEndPos - _promptStartPos; i++)
 
667
      _history[_historyIndex][i] = buffer(_promptStartPos + i);
 
668
 
 
669
    _history[_historyIndex][i] = '\0';
 
670
  }
 
671
 
 
672
  // Advance to the next line in the history
 
673
  int line = _historyLine + direction;
 
674
  if ((direction < 0 && line < 0) || (direction > 0 && line > _historySize))
 
675
    return;
 
676
 
 
677
  // If they press arrow-up with anything in the buffer, search backwards
 
678
  // in the history.
 
679
  /*
 
680
  if(direction < 0 && _currentPos > _promptStartPos) {
 
681
    for(;line > 0; line--) {
 
682
      if(compareHistory(_history[line]) == 0)
 
683
        break;
 
684
    }
 
685
  }
 
686
  */
 
687
 
 
688
  _historyLine = line;
 
689
 
 
690
  // Remove the current user text
 
691
  _currentPos = _promptStartPos;
 
692
  killLine(1);  // to end of line
 
693
 
 
694
  // ... and ensure the prompt is visible
 
695
  scrollToCurrent();
 
696
 
 
697
  // Print the text from the history
 
698
  int idx;
 
699
  if (_historyLine > 0)
 
700
    idx = (_historyIndex - _historyLine + _historySize) % _historySize;
 
701
  else
 
702
    idx = _historyIndex;
 
703
 
 
704
  for (int i = 0; i < kLineBufferSize && _history[idx][i] != '\0'; i++)
 
705
    putcharIntern(_history[idx][i]);
 
706
 
 
707
  _promptEndPos = _currentPos;
 
708
 
 
709
  // Ensure once more the caret is visible (in case of very long history entries)
 
710
  scrollToCurrent();
 
711
 
 
712
  setDirty(); draw();
 
713
}
 
714
 
 
715
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
716
void PromptWidget::nextLine()
 
717
{
 
718
  // reset colors every line, so I don't have to remember to do it myself
 
719
  textColor = defaultTextColor;
 
720
  bgColor = defaultBGColor;
 
721
  _inverse = false;
 
722
 
 
723
  int line = _currentPos / _lineWidth;
 
724
  if (line == _scrollLine)
 
725
    _scrollLine++;
 
726
 
 
727
  _currentPos = (line + 1) * _lineWidth;
 
728
 
 
729
  updateScrollBuffer();
 
730
}
 
731
 
 
732
 
 
733
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
734
// Call this (at least) when the current line changes or when a new line is added
 
735
void PromptWidget::updateScrollBuffer()
 
736
{
 
737
  int lastchar = MAX(_promptEndPos, _currentPos);
 
738
  int line = lastchar / _lineWidth;
 
739
  int numlines = (line < _linesInBuffer) ? line + 1 : _linesInBuffer;
 
740
  int firstline = line - numlines + 1;
 
741
 
 
742
  if (firstline > _firstLineInBuffer)
 
743
  {
 
744
    // clear old line from buffer
 
745
    for (int i = lastchar; i < (line+1) * _lineWidth; ++i)
 
746
      buffer(i) = ' ';
 
747
 
 
748
    _firstLineInBuffer = firstline;
 
749
  }
 
750
 
 
751
  _scrollBar->_numEntries = numlines;
 
752
  _scrollBar->_currentPos = _scrollBar->_numEntries - (line - _scrollLine + _linesPerPage);
 
753
  _scrollBar->_entriesPerPage = _linesPerPage;
 
754
  _scrollBar->recalc();
 
755
}
 
756
 
 
757
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
758
int PromptWidget::printf(const char *format, ...)
 
759
{
 
760
  va_list argptr;
 
761
 
 
762
  va_start(argptr, format);
 
763
  int count = this->vprintf(format, argptr);
 
764
  va_end (argptr);
 
765
  return count;
 
766
}
 
767
 
 
768
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
769
int PromptWidget::vprintf(const char *format, va_list argptr)
 
770
{
 
771
  char buf[2048];
 
772
  int count = VSNPRINTF(buf, sizeof(buf), format, argptr);
 
773
 
 
774
  print(buf);
 
775
  return count;
 
776
}
 
777
 
 
778
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
779
void PromptWidget::putchar(int c)
 
780
{
 
781
  putcharIntern(c);
 
782
 
 
783
  setDirty(); draw();  // FIXME - not nice to redraw the full console just for one char!
 
784
}
 
785
 
 
786
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
787
void PromptWidget::putcharIntern(int c)
 
788
{
 
789
  if (c == '\n')
 
790
    nextLine();
 
791
  else if(c & 0x80) { // set foreground color to TIA color
 
792
                      // don't print or advance cursor
 
793
                      // there are only 128 TIA colors, but
 
794
                      // OverlayColor contains 256 of them
 
795
    textColor = (c & 0x7f) << 1;
 
796
  }
 
797
  else if(c < ' ') { // More colors (the regular GUI ones)
 
798
    textColor = c + 0x100;
 
799
  }
 
800
  else if(c == 0x7f) { // toggle inverse video (DEL char)
 
801
    _inverse = !_inverse;
 
802
  }
 
803
  else
 
804
  {
 
805
    buffer(_currentPos) = c | (textColor << 8) | (_inverse << 17);
 
806
    _currentPos++;
 
807
    if ((_scrollLine + 1) * _lineWidth == _currentPos)
 
808
    {
 
809
      _scrollLine++;
 
810
      updateScrollBuffer();
 
811
    }
 
812
  }
 
813
}
 
814
 
 
815
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
816
void PromptWidget::print(const string& str)
 
817
{
 
818
  const char* c = str.c_str();
 
819
  while(*c)
 
820
    putcharIntern(*c++);
 
821
}
 
822
 
 
823
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
824
void PromptWidget::drawCaret()
 
825
{
 
826
//cerr << "PromptWidget::drawCaret()\n";
 
827
  FrameBuffer& fb = _boss->instance()->frameBuffer();
 
828
 
 
829
  int line = _currentPos / _lineWidth;
 
830
 
 
831
  // Don't draw the cursor if it's not in the current view
 
832
  if(_scrollLine < line)
 
833
    return;
 
834
 
 
835
  int displayLine = line - _scrollLine + _linesPerPage - 1;
 
836
  int x = _x + 1 + (_currentPos % _lineWidth) * _kConsoleCharWidth;
 
837
  int y = _y + displayLine * _kConsoleLineHeight;
 
838
 
 
839
  char c = buffer(_currentPos);
 
840
  fb.fillRect(x, y, _kConsoleCharWidth, _kConsoleLineHeight, kTextColor);
 
841
  fb.drawChar(&_boss->instance()->consoleFont(), c, x, y + 2, kBGColor);
 
842
}
 
843
 
 
844
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
845
void PromptWidget::scrollToCurrent()
 
846
{
 
847
  int line = _promptEndPos / _lineWidth;
 
848
 
 
849
  if (line + _linesPerPage <= _scrollLine)
 
850
  {
 
851
    // TODO - this should only occur for loong edit lines, though
 
852
  }
 
853
  else if (line > _scrollLine)
 
854
  {
 
855
    _scrollLine = line;
 
856
    updateScrollBuffer();
 
857
  }
 
858
}
 
859
 
 
860
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
861
bool PromptWidget::saveBuffer(string& filename)
 
862
{
 
863
  ofstream out(filename.c_str());
 
864
  if(!out.is_open())
 
865
    return false;
 
866
 
 
867
  for(int start=0; start<_promptStartPos; start+=_lineWidth) {
 
868
    int end = start+_lineWidth-1;
 
869
 
 
870
    // look for first non-space, printing char from end of line
 
871
    while( char(_buffer[end] & 0xff) <= ' ' && end >= start)
 
872
      end--;
 
873
 
 
874
    // spit out the line minus its trailing junk.
 
875
    // Strip off any color/inverse bits
 
876
    for(int j=start; j<=end; j++)
 
877
      out << char(_buffer[j] & 0xff);
 
878
 
 
879
    // add a \n
 
880
    out << endl;
 
881
  }
 
882
 
 
883
  out.close();
 
884
  return true;
 
885
}