added applet mode, terminal window
[fleet.git] / src / de / mud / telnet / display / CharDisplay.java
1 package de.mud.telnet.display;
2 /* "In case you would like to use the packages as libraries please
3  *  apply the GNU Library General Public License as documented in the
4  *  file COPYING.LIB." (from Telnet/Documentation/index.html)
5  */
6 /**
7  * CharDisplay -- a simple character display
8  * --
9  * $Id: CharDisplay.java,v 1.27 1998/03/18 12:43:33 leo Exp $
10  * $timestamp: Thu Jul 24 15:19:18 1997 by Matthias L. Jugel :$
11  *
12  * This file is part of "The Java Telnet Applet".
13  *
14  * This is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2, or (at your option)
17  * any later version.
18  *
19  * "The Java Telnet Applet" is distributed in the hope that it will be 
20  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with this software; see the file COPYING.  If not, write to the
26  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27  * Boston, MA 02111-1307, USA.
28  */
29
30 import java.awt.Graphics;
31 import java.awt.Panel;
32 import java.awt.Color;
33 import java.awt.Dimension;
34 import java.awt.Font;
35 import java.awt.FontMetrics;
36 import java.awt.Point;
37 import java.awt.Insets;
38 import java.awt.Event;
39 import java.awt.TextArea;
40 import java.awt.Label;
41 import java.awt.Frame;
42 import java.awt.Scrollbar;
43 import java.awt.Rectangle;
44
45 /**
46  * A simple character display.
47  * @version $Id: CharDisplay.java,v 1.27 1998/03/18 12:43:33 leo Exp $
48  * @author  Matthias L. Jugel, Marcus Meißner
49  */
50 public class CharDisplay extends Panel
51 {
52   /**
53    * If you need the runtime version, just ask this variable.
54    */
55   public String version = "$Revision: 1.27 $ $Date: 1998/03/18 12:43:33 $";
56   /**
57    * Enable debug messages. This is final static to prevent unused
58    * code to be compiled.
59    */
60   public final static int debug = 0;
61   
62   private Dimension size;      /* rows and columns */
63   private Insets insets;      /* size of the border */
64   private boolean raised;      /* indicator if the border is raised */
65
66   private char charArray[][];      /* contains the characters */
67   private int charAttributes[][];    /* contains character attrs */
68   private int bufSize, maxBufSize;    /* buffer sizes */
69
70   private int windowBase;      /* where the start displaying */
71   private int screenBase;      /* the actual screen start */
72   private int topMargin;      /* top scroll margon */
73   private int bottomMargin;      /* bottom scroll margon */
74   private Scrollbar scrollBar;  /* the scroll bar */
75   private String scrBarPos;      /* the scroll bar position */
76
77   private Font normalFont;      /* normal font */
78   private FontMetrics fm;      /* current font metrics */
79   private int charWidth;      /* current width of a char */
80   private int charHeight;      /* current height of a char */
81   private int charDescent;      /* base line descent */
82   private int resizeStrategy;      /* current resizing strategy */
83
84   private int cursorX, cursorY;      /* current cursor position */
85   private Point selectBegin, selectEnd;  /* selection coordinates */
86   private TextArea selection;
87   private Frame selectFrame;
88
89   private de.mud.telnet.display.SoftFont  sf = new de.mud.telnet.display.SoftFont();
90
91   private boolean screenLocked = false;    /* screen needs to be locked */
92                                                 /* because of paint requests */
93                                                 /* during other operations */
94   private boolean update[];
95   
96   private Color color[] = { Color.black.brighter(),
97                             Color.red.brighter(),
98                             Color.green.brighter(),
99                             Color.yellow.brighter(),
100                             Color.blue.brighter(),
101                             Color.magenta.brighter(),
102                             Color.cyan.brighter(),
103                             Color.white.brighter(),
104   };
105   private final static int COLOR_FG_STD = 7;
106   private final static int COLOR_BG_STD = 0;
107   private final static int COLOR        = 0x7f8;
108   private final static int COLOR_FG     = 0x78;
109   private final static int COLOR_BG     = 0x780;
110
111   /**
112    * Scroll up when inserting a line.
113    */
114   public final static boolean SCROLL_UP   = false;
115   /**
116    * Scroll down when inserting a line.
117    */
118   public final static boolean SCROLL_DOWN = true;
119
120   /**
121    * Do nothing when the container is resized.
122    */
123   public final static int RESIZE_NONE  = 0;
124   /**
125    * Resize the width and height of the characterscreen.
126    */
127   public final static int RESIZE_SCREEN  = 1;
128   /**
129    * Resize the font to the new screensize.
130    */
131   public final static int RESIZE_FONT  = 2;
132   
133   /**
134    * Make character normal.
135    */ 
136   public final static int NORMAL  = 0x00;
137   /**
138    * Make character bold.
139    */ 
140   public final static int BOLD    = 0x01;
141   /**
142    * Underline character.
143    */ 
144   public final static int UNDERLINE  = 0x02;
145   /**
146    * Invert character.
147    */ 
148   public final static int INVERT  = 0x04;
149
150   private void InitializeCharDisplay(int width, int height, 
151                                      String fontname, int fsize)
152   {
153     System.err.println("CharDisplay: screen size: ["+width+","+height+"]");
154     normalFont = new Font(fontname, Font.BOLD, fsize);
155     setFont(normalFont);
156     fm = getFontMetrics(normalFont);
157     if(fm != null)
158     {
159       charWidth = fm.charWidth('@');
160       charHeight = fm.getHeight();
161       charDescent = fm.getDescent();
162     }
163
164     resizeStrategy = RESIZE_FONT;
165     size = new Dimension(width, height);
166     charArray = new char[size.height][size.width];
167     charAttributes = new int[size.height][size.width];
168     bufSize = size.height;
169     maxBufSize = 2 * size.height;
170
171     windowBase = 0;
172     screenBase = 0;
173     topMargin = 0;
174     bottomMargin = size.height - 1;
175
176     update = new boolean[size.height + 1];
177     for(int i = 1; i <= size.height; i++) update[i] = true;
178
179     selectBegin = new Point(0,0);
180     selectEnd = new Point(0,0);
181
182     setLayout(null);
183   }
184   
185   /**
186    * Create a character display with size 80x24 and Font "Courier", size 12.
187    */
188   public CharDisplay()
189   {
190     InitializeCharDisplay(80, 24, "Courier", 12);
191   }
192
193   /**
194    * Create a character display with specific size, Font is "Courier", size 12.
195    */
196   public CharDisplay(int width, int height)
197   {
198     InitializeCharDisplay(width, height, "Courier", 12);
199   }
200
201   /**
202    * Create a character display with 80x24 and specific font and font size.
203    */
204   public CharDisplay(String fname, int fsize)
205   {
206     InitializeCharDisplay(80, 24, fname, fsize);
207   }
208
209   /**
210    * Create a character display with specific size, font and font size.
211    */
212   public CharDisplay(int width, int height, String fname, int fsize)
213   {
214     InitializeCharDisplay(width, height, fname, fsize);
215   }
216   
217   /**
218    * Put a character on the screen with normal font and outline.
219    * The character previously on that position will be overwritten.
220    * You need to call redraw() to update the screen.
221    * @param c x-coordinate (column)
222    * @param l y-coordinate (line)
223    * @param ch the character to show on the screen
224    * @see #insertChar
225    * @see #deleteChar
226    * @see #redraw
227    */
228   public void putChar(int c, int l, char ch)
229   {
230     putChar(c, l, ch, NORMAL);
231   }
232
233   /**
234    * Put a character on the screen with specific font and outline.
235    * The character previously on that position will be overwritten.
236    * You need to call redraw() to update the screen.
237    * @param c x-coordinate (column)
238    * @param l y-coordinate (line)
239    * @param ch the character to show on the screen
240    * @param attributes the character attributes
241    * @see #BOLD
242    * @see #UNDERLINE
243    * @see #INVERT
244    * @see #NORMAL
245    * @see #insertChar
246    * @see #deleteChar
247    * @see #redraw
248    */  
249
250   public void putChar(int c, int l, char ch, int attributes)
251   {
252     c = checkBounds(c, 0, size.width - 1);
253     l = checkBounds(l, 0, size.height - 1);
254     charArray[screenBase + l][c] = ch;
255     charAttributes[screenBase + l][c] = attributes;
256     markLine(l, 1);
257   }
258
259   /**
260    * Get the character at the specified position.
261    * @param c x-coordinate (column)
262    * @param l y-coordinate (line)
263    * @see #putChar
264    */
265   public char getChar(int c, int l)
266   {
267     c = checkBounds(c, 0, size.width - 1);
268     l = checkBounds(l, 0, size.height - 1);
269     return charArray[l][c];
270   }
271
272   /**
273    * Get the attributes for the specified position.
274    * @param c x-coordinate (column)
275    * @param l y-coordinate (line)
276    * @see #putChar
277    */
278   public int getAttributes(int c, int l)
279   {
280     c = checkBounds(c, 0, size.width - 1);
281     l = checkBounds(l, 0, size.height - 1);
282     return charAttributes[l][c];
283   }
284
285   /**
286    * Insert a character at a specific position on the screen.
287    * All character right to from this position will be moved one to the right.
288    * You need to call redraw() to update the screen.
289    * @param c x-coordinate (column)
290    * @param l y-coordinate (line)
291    * @param ch the character to insert
292    * @param attributes the character attributes
293    * @see #BOLD
294    * @see #UNDERLINE
295    * @see #INVERT
296    * @see #NORMAL
297    * @see #putChar
298    * @see #deleteChar
299    * @see #redraw
300    */
301   public void insertChar(int c, int l, char ch, int attributes)
302   {
303     c = checkBounds(c, 0, size.width - 1);
304     l = checkBounds(l, 0, size.height - 1);
305     System.arraycopy(charArray[screenBase + l], c, 
306          charArray[screenBase + l], c + 1, size.width - c - 1);
307     System.arraycopy(charAttributes[screenBase + l], c, 
308          charAttributes[screenBase + l], c + 1, size.width - c - 1);
309     putChar(c, l, ch, attributes);
310   }
311
312   /**
313    * Delete a character at a given position on the screen.
314    * All characters right to the position will be moved one to the left.
315    * You need to call redraw() to update the screen.
316    * @param c x-coordinate (column)
317    * @param l y-coordinate (line)
318    * @see #putChar
319    * @see #insertChar
320    * @see #redraw
321    */
322   public void deleteChar(int c, int l)
323   {
324     c = checkBounds(c, 0, size.width - 1);
325     l = checkBounds(l, 0, size.height - 1);
326     if(c < size.width - 1)
327     {
328       System.arraycopy(charArray[screenBase + l], c + 1,
329            charArray[screenBase + l], c, size.width - c - 1);
330       System.arraycopy(charAttributes[screenBase + l], c + 1,
331            charAttributes[screenBase + l], c, size.width - c - 1);
332     }
333     putChar(size.width - 1, l, (char)0);
334   }
335
336   /**
337    * Put a String at a specific position. Any characters previously on that 
338    * position will be overwritten. You need to call redraw() for screen update.
339    * @param c x-coordinate (column)
340    * @param l y-coordinate (line)
341    * @param s the string to be shown on the screen
342    * @see #BOLD
343    * @see #UNDERLINE
344    * @see #INVERT
345    * @see #NORMAL
346    * @see #putChar
347    * @see #insertLine
348    * @see #deleteLine
349    * @see #redraw
350    */  
351   public void putString(int c, int l, String s)
352   {
353     putString(c, l, s, NORMAL);
354   }
355   
356   /**
357    * Put a String at a specific position giving all characters the same
358    * attributes. Any characters previously on that position will be 
359    * overwritten. You need to call redraw() to update the screen.
360    * @param c x-coordinate (column)
361    * @param l y-coordinate (line)
362    * @param s the string to be shown on the screen
363    * @param attributes character attributes
364    * @see #BOLD
365    * @see #UNDERLINE
366    * @see #INVERT
367    * @see #NORMAL
368    * @see #putChar
369    * @see #insertLine
370    * @see #deleteLine
371    * @see #redraw
372    */
373   public void putString(int c, int l, String s, int attributes)
374   {
375     for(int i = 0; i < s.length() && c + i < size.width; i++)
376       putChar(c + i, l, s.charAt(i), attributes);
377   }
378
379   /**
380    * Insert a blank line at a specific position.
381    * The current line and all previous lines are scrolled one line up. The
382    * top line is lost. You need to call redraw() to update the screen.
383    * @param l the y-coordinate to insert the line
384    * @see #deleteLine
385    * @see #redraw
386    */
387   public void insertLine(int l)
388   {
389     insertLine(l, 1, SCROLL_UP);
390   }
391
392   /**
393    * Insert blank lines at a specific position.
394    * You need to call redraw() to update the screen
395    * @param l the y-coordinate to insert the line
396    * @param n amount of lines to be inserted
397    * @see #deleteLine
398    * @see #redraw
399    */
400   public void insertLine(int l, int n)
401   {
402     insertLine(l, n, SCROLL_UP);
403   }  
404
405   /**
406    * Insert a blank line at a specific position. Scroll text according to
407    * the argument.
408    * You need to call redraw() to update the screen
409    * @param l the y-coordinate to insert the line
410    * @param scrollDown scroll down
411    * @see #deleteLine
412    * @see #SCROLL_UP
413    * @see #SCROLL_DOWN
414    * @see #redraw
415    */
416   public void insertLine(int l, boolean scrollDown)
417   {
418     insertLine(l, 1, scrollDown);
419   }  
420
421   /**
422    * Insert blank lines at a specific position.
423    * The current line and all previous lines are scrolled one line up. The
424    * top line is lost. You need to call redraw() to update the screen.
425    * @param l the y-coordinate to insert the line
426    * @param n number of lines to be inserted
427    * @param scrollDown scroll down
428    * @see #deleteLine
429    * @see #SCROLL_UP
430    * @see #SCROLL_DOWN
431    * @see #redraw
432    */
433   public synchronized void insertLine(int l, int n, boolean scrollDown)
434   {
435     screenLocked = true;
436
437     l = checkBounds(l, 0, size.height - 1);
438
439     char cbuf[][] = null;
440     int abuf[][] = null;
441     int offset = 0;
442     int oldBase = screenBase;
443     int top = (l < topMargin ? 
444                0 : (l > bottomMargin ?
445                     (bottomMargin + 1 < size.height ?
446                      bottomMargin + 1 : size.height - 1) : topMargin));
447     int bottom = (l > bottomMargin ?
448                   size.height - 1 : (l < topMargin ? 
449                                      (topMargin > 0 ?
450                                       topMargin - 1 : 0) : bottomMargin));
451     
452     
453     if(scrollDown) {
454       if(n > (bottom - top)) n = (bottom - top);
455       cbuf = new char[bottom - l - (n - 1)][size.width];
456       abuf = new int[bottom - l - (n - 1)][size.width];
457       
458       System.arraycopy(charArray, oldBase + l, cbuf, 0, bottom - l - (n - 1));
459       System.arraycopy(charAttributes, oldBase + l, 
460                        abuf, 0, bottom - l - (n - 1));
461       System.arraycopy(cbuf, 0, charArray, oldBase + l + n, 
462                        bottom - l - (n - 1));
463       System.arraycopy(abuf, 0, charAttributes, oldBase + l + n, 
464                        bottom - l - (n - 1));
465       cbuf = charArray;
466       abuf = charAttributes;
467     } else try {
468       if(n > (bottom - top) + 1) n = (bottom - top) + 1;
469       if(bufSize < maxBufSize) {
470         if(bufSize + n > maxBufSize) {
471           offset = n - (maxBufSize - bufSize);
472           bufSize = maxBufSize;
473           screenBase = maxBufSize - size.height - 1;
474           windowBase = screenBase;
475         } else {
476           screenBase += n;
477           windowBase += n;
478           bufSize += n;
479         }
480         cbuf = new char[bufSize][size.width];
481         abuf = new int[bufSize][size.width];
482       } else {
483         offset = n;
484         cbuf = charArray;
485         abuf = charAttributes;
486       }
487       /*
488        * copy anything from the top of the buffer (+offset) to the new top
489        * up to the screenBase.
490        */
491       if(oldBase > 0)
492       {
493         System.arraycopy(charArray, offset, 
494                          cbuf, 0, 
495                          oldBase - offset);
496         System.arraycopy(charAttributes, offset, 
497                          abuf, 0, 
498                          oldBase - offset);
499       }
500       /*
501        * copy anything from the top of the screen (screenBase) up to the
502        * topMargin to the new screen
503        */
504       if(top > 0)
505       {
506         System.arraycopy(charArray, oldBase, 
507                          cbuf, screenBase, 
508                          top);
509         System.arraycopy(charAttributes, oldBase, 
510                          abuf, screenBase, 
511                          top);
512       }
513       /* 
514        * copy anything from the topMargin up to the amount of lines inserted
515        * to the gap left over between scrollback buffer and screenBase
516        */
517       if(oldBase > 0) {
518         System.arraycopy(charArray, oldBase + top, 
519                          cbuf, oldBase - offset,
520                          n);
521         System.arraycopy(charAttributes, oldBase + top, 
522                          abuf, oldBase - offset,
523                          n);
524       }
525       /*
526        * copy anything from topMargin + n up to the line linserted to the
527        * topMargin
528        */
529       System.arraycopy(charArray, oldBase + top + n,
530                        cbuf, screenBase + top,
531                        l - top - (n - 1));
532       System.arraycopy(charAttributes, oldBase + top + n,
533                        abuf, screenBase + top,
534                        l - top - (n - 1));
535       /*
536        * copy the all lines next to the inserted to the new buffer
537        */
538       if(l < size.height - 1)
539       {
540         System.arraycopy(charArray, oldBase + l + 1,
541                          cbuf, screenBase + l + 1,
542                          (size.height - 1) - l);
543         System.arraycopy(charAttributes, oldBase + l + 1,
544                          abuf, screenBase + l + 1,
545                          (size.height - 1) - l);
546       }
547     } catch(ArrayIndexOutOfBoundsException e) {
548       System.err.println("*** Error while scrolling up:");
549       System.err.println("--- BEGIN STACKTRACE ---");
550       e.printStackTrace();
551       System.err.println("--- END STACKTRACE ---");
552       System.err.println("bufSize="+bufSize+", maxBufSize="+maxBufSize);
553       System.err.println("top="+top+", bottom="+bottom);
554       System.err.println("n="+n+", l="+l);
555       System.err.println("screenBase="+screenBase+", windowBase="+windowBase);
556       System.err.println("oldBase="+oldBase);
557       System.err.println("size.width="+size.width+", size.height="+size.height);
558       System.err.println("abuf.length="+abuf.length+", cbuf.length="+cbuf.length);
559       System.err.println("*** done dumping debug information");
560     }
561     
562     for(int i = 0; i < n; i++)
563     {
564       cbuf[(screenBase + l) + (scrollDown ? i : -i) ] = new char[size.width];
565       abuf[(screenBase + l) + (scrollDown ? i : -i) ] = new int[size.width];
566     }
567
568     charArray = cbuf;
569     charAttributes = abuf;
570     
571     if(scrollDown)
572       markLine(l, bottom - l + 1);
573     else
574       markLine(top, l - top + 1);
575
576     if(scrollBar != null)
577       scrollBar.setValues(windowBase, size.height, 0, bufSize);
578     
579     screenLocked = false;
580   }
581   
582   /**
583    * Delete a line at a specific position. Subsequent lines will be scrolled 
584    * up to fill the space and a blank line is inserted at the end of the 
585    * screen.
586    * @param l the y-coordinate to insert the line
587    * @see #deleteLine
588    */
589   public void deleteLine(int l)
590   {
591     l = checkBounds(l, 0, size.height - 1);
592
593     int bottom = (l>bottomMargin?size.height-1:
594                   (l<topMargin?topMargin:bottomMargin+1));
595     System.arraycopy(charArray, screenBase + l + 1,
596                      charArray, screenBase + l, bottom - l -1 );
597     System.arraycopy(charAttributes, screenBase + l + 1,
598                      charAttributes, screenBase + l, bottom - l -1);
599     charArray[screenBase + bottom - 1] = new char[size.width];
600     charAttributes[screenBase + bottom - 1] = new int[size.width];
601     markLine(l, bottom - l);
602   }
603
604
605   /**
606    * Delete a rectangular portion of the screen.
607    * You need to call redraw() to update the screen.
608    * @param c x-coordinate (column)
609    * @param l y-coordinate (row)
610    * @param w with of the area in characters
611    * @param h height of the area in characters
612    * @see #deleteChar
613    * @see #deleteLine
614    * @see redraw
615    */
616   public void deleteArea(int c, int l, int w, int h)
617   {
618     c = checkBounds(c, 0, size.width - 1);
619     l = checkBounds(l, 0, size.height - 1);
620
621     char cbuf[] = new char[w];
622     int abuf[] = new int[w];
623     
624     for(int i = 0; i < h && l + i < size.height; i++)
625     {
626       System.arraycopy(cbuf, 0, charArray[screenBase + l + i], c, w);
627       System.arraycopy(abuf, 0, charAttributes[screenBase + l + i], c, w);
628     }
629     markLine(l, h);
630   }
631
632   /**
633    * Puts the cursor at the specified position.
634    * @param c column
635    * @param l line
636    */
637   public void setCursorPos(int c, int l)
638   {
639     c = checkBounds(c, 0, size.width - 1);
640     l = checkBounds(l, 0, size.height - 1);
641     markLine(cursorY, 1);
642     cursorX = (c < size.width ? c : size.width);
643     cursorY = (l < size.height ? l : size.height);
644     markLine(l, 1);
645   }
646
647   /**
648    * Get the current cursor position.
649    * @see java.awt.Dimension
650    */
651   public Dimension getCursorPos()
652   {
653     return new Dimension(cursorX, cursorY);
654   }
655
656   /**
657    * Set the top scroll margin for the screen. If the current bottom margin
658    * is smaller it will become the top margin and the line will become the
659    * bottom margin.
660    * @param l line that is the margin
661    */
662   public void setTopMargin(int l)
663   {
664     if(l > bottomMargin) 
665     {
666       topMargin = bottomMargin;
667       bottomMargin = l;
668     }
669     else
670       topMargin = l;
671     if(topMargin < 0) topMargin = 0;
672     if(bottomMargin > size.height - 1) bottomMargin = size.height - 1;
673   }
674
675   /**
676    * Get the top scroll margin.
677    */
678   public int getTopMargin()
679   {
680     return topMargin;
681   }
682
683   /**
684    * Set the bottom scroll margin for the screen. If the current top margin
685    * is bigger it will become the bottom margin and the line will become the
686    * top margin.
687    * @param l line that is the margin
688    */
689   public void setBottomMargin(int l)
690   {
691     if(l < topMargin) 
692     {
693       bottomMargin = topMargin;
694       topMargin = l;
695     }
696     else
697       bottomMargin = l;
698     if(topMargin < 0) topMargin = 0;
699     if(bottomMargin > size.height - 1) bottomMargin = size.height - 1;
700   }
701
702   /**
703    * Get the bottom scroll margin.
704    */
705   public int getBottomMargin()
706   {
707     return bottomMargin;
708   }
709     
710   /**
711    * Set scrollback buffer size.
712    * @param amount new size of the buffer
713    */
714   public void setBufferSize(int amount)
715   {
716     screenLocked = true;
717
718     if(amount < size.height) amount = size.height;
719     if(amount < maxBufSize)
720     {
721       char cbuf[][] = new char[amount][size.width];
722       int abuf[][] = new int[amount][size.width];
723       System.arraycopy(charArray, bufSize - amount, cbuf, 0, amount);
724       System.arraycopy(charAttributes, bufSize - amount, abuf, 0, amount);
725       charArray = cbuf;
726       charAttributes = abuf;
727     }
728     maxBufSize = amount;
729  
730     screenLocked = false;
731
732     repaint();
733   }
734
735   /**
736    * Retrieve current scrollback buffer size.
737    * @see #setBufferSize
738    */
739   public int getBufferSize()
740   {
741     return bufSize;
742   }
743
744   /**
745    * Retrieve maximum buffer Size.
746    * @see #getBufferSize
747    */
748   public int getMaxBufferSize()
749   {
750     return maxBufSize;
751   }
752
753   /**
754    * Set the current window base. This allows to view the scrollback buffer.
755    * @param line the line where the screen window starts
756    * @see setBufferSize
757    * @see getBufferSize
758    */
759   public void setWindowBase(int line)
760   {
761     if(line > screenBase) line = screenBase;
762     else if(line < 0) line = 0;
763     windowBase = line;
764     repaint();
765   }
766
767   /**
768    * Get the current window base.
769    * @see setWindowBase
770    */
771   public int getWindowBase()
772   {
773     return windowBase;
774   }
775
776   /**
777    * Change the size of the screen. This will include adjustment of the 
778    * scrollback buffer.
779    * @param columns width of the screen
780    * @param columns height of the screen
781    */
782   public void setWindowSize(int width, int height)
783   {
784     char cbuf[][];
785     int abuf[][];
786     int bsize = bufSize;
787
788     if(width < 1 || height < 1) return;
789
790     screenLocked = true;
791     
792     super.update(getGraphics());
793     
794     if(height > maxBufSize) 
795       maxBufSize = height;
796     if(height > bufSize)
797     {
798       bufSize = height;
799       screenBase = 0;
800       windowBase = 0;
801     }
802
803     cbuf = new char[bufSize][width];
804     abuf = new int[bufSize][width];
805     
806     for(int i = 0; i < bsize && i < bufSize; i++)
807     {
808       System.arraycopy(charArray[i], 0, cbuf[i], 0, 
809            width < size.width ? width : size.width);
810       System.arraycopy(charAttributes[i], 0, abuf[i], 0, 
811            width < size.width ? width : size.width);
812     }
813     charArray = cbuf;
814     charAttributes = abuf;
815     size = new Dimension(width, height);
816     topMargin = 0;
817     bottomMargin = height - 1;
818     update = new boolean[height + 1];
819     for(int i = 0; i <= height; i++) update[i] = true;
820     screenLocked = false;
821   }
822
823   /**
824    * Set the strategy when window is resized.
825    * RESIZE_FONT is default.
826    * @param strategy the strategy
827    * @see #RESIZE_NONE
828    * @see #RESIZE_FONT
829    * @see #RESIZE_SCREEN
830    */
831   public void setResizeStrategy(int strategy)
832   {
833     resizeStrategy = strategy;
834   }
835   
836   /**
837    * Get amount of rows on the screen.
838    */
839   public int getRows() { return size.height; }
840
841   /**
842    * Get amount of columns on the screen.
843    */
844   public int getColumns() { return size.width; }
845
846   /**
847    * Set the border thickness and the border type.
848    * @param thickness border thickness in pixels, zero means no border
849    * @param raised a boolean indicating a raised or embossed border
850    */
851   public void setBorder(int thickness, boolean raised)
852   {
853     if(thickness == 0) insets = null;
854     else insets = new Insets(thickness+1, thickness+1, 
855                              thickness+1, thickness+1);
856     this.raised = raised;
857   }
858
859   /**
860    * Set the scrollbar position. valid values are "East" or "West".
861    * @param position the position of the scrollbar
862    */
863   public void setScrollbar(String position)
864   {
865     add(scrollBar = new Scrollbar());
866     scrollBar.setValues(windowBase, size.height, 0, bufSize - size.height);
867     scrBarPos = position;
868   }
869   
870   /**
871    * Mark lines to be updated with redraw().
872    * @param l starting line
873    * @param n amount of lines to be updated
874    * @see #redraw
875    */
876   public void markLine(int l, int n)
877   {
878     l = checkBounds(l, 0, size.height - 1);
879     for(int i = 0; i < n && l + i < size.height; i++) 
880       update[l + i + 1] = true;
881   }
882   
883   /**
884    * Redraw marked lines.
885    * @see #markLine
886    */
887   public void redraw()
888   {
889     update[0] = true;
890     repaint();
891   }
892
893   /**
894    * Update the display. to reduce flashing we have overridden this method.
895    */
896   public void update(Graphics g)
897   {
898     paint(g);
899   }
900   
901   /**
902    * Paint the current screen. All painting is done here. Only lines that have
903    * changed will be redrawn!
904    */
905   public synchronized void paint(Graphics g)
906   {
907     if(screenLocked) return;
908     int xoffset = (super.size().width - size.width * charWidth - 
909                    (scrollBar != null ? 15 : 0)) / 2;
910     int yoffset = (super.size().height - size.height * charHeight) / 2;
911
912     Color fg = color[COLOR_FG_STD];
913     Color bg = color[COLOR_BG_STD];
914
915     if(scrollBar != null && scrBarPos.equals("West")) xoffset += 15;
916     
917     g.setFont(normalFont);
918
919     for(int l = 0; l < size.height; l++)
920     {
921       if(update[0] && !update[l + 1]) continue;
922       update[l + 1] = false;
923       for(int c = 0; c < size.width; c++)
924       {
925         int addr = 0;
926         int currAttr = charAttributes[windowBase + l][c];
927
928         fg = color[COLOR_FG_STD];
929         bg = color[COLOR_BG_STD];
930         if ((currAttr & COLOR_FG) != 0) {
931           fg = color[((currAttr & COLOR_FG) >> 3)-1];
932         }
933         if ((currAttr & COLOR_BG) != 0) {
934           bg = color[((currAttr & COLOR_BG) >> 7)-1];
935         }
936
937         if((currAttr & BOLD) != 0) {
938           if(fg.equals(Color.black))
939             fg = Color.gray;
940           else fg = fg.darker();
941         }
942         if((currAttr & INVERT) != 0) { Color swapc = bg; bg=fg;fg=swapc; }
943
944         if (sf.inSoftFont(charArray[windowBase + l][c])) {
945           g.setColor(bg);       
946           g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset, 
947                      charWidth, charHeight);
948           g.setColor(fg);       
949           sf.drawChar(g,charArray[windowBase + l][c],xoffset+c*charWidth,
950                       l*charHeight+yoffset, charWidth, charHeight);
951           continue;
952         }
953         
954         // determine the maximum of characters we can print in one go
955         while(c + addr < size.width && 
956               charAttributes[windowBase + l][c + addr] == currAttr &&
957               !sf.inSoftFont(charArray[windowBase + l ][c+addr])
958         ) {
959           if(charArray[windowBase + l][c + addr] < ' ')
960             charArray[windowBase + l][c + addr] = ' ';
961           addr++;
962         }
963         
964         // clear the part of the screen we want to change (fill rectangle)
965         g.setColor(bg);
966         g.fillRect(c * charWidth + xoffset, l * charHeight + yoffset,
967                    addr * charWidth, charHeight);
968
969         g.setColor(fg);
970         
971         // draw the characters
972         g.drawChars(charArray[windowBase + l], c, addr, 
973                     c * charWidth + xoffset, 
974                     (l+1) * charHeight - charDescent + yoffset);
975
976         if((currAttr & UNDERLINE) != 0)
977           g.drawLine(c * charWidth + 1 + xoffset,
978                      (l+1) * charHeight - charDescent / 2 + yoffset,
979                      c * charWidth + addr * charWidth + xoffset, 
980                      (l+1) * charHeight - charDescent / 2 + yoffset);
981         
982         c += addr - 1;
983       }
984     }
985
986     // draw cursor
987     if(screenBase + cursorY >= windowBase && 
988        screenBase + cursorY < windowBase + size.height)
989     {
990       g.setColor(color[COLOR_FG_STD]);
991       g.setXORMode(color[COLOR_BG_STD]);
992       g.fillRect( cursorX * charWidth + xoffset, 
993                  (cursorY + screenBase - windowBase) * charHeight + yoffset,
994                  charWidth, charHeight);
995       g.setPaintMode();
996     }
997
998     if(windowBase <= selectBegin.y || windowBase <= selectEnd.y) {
999       int beginLine = selectBegin.y - windowBase;
1000       int endLine = selectEnd.y - selectBegin.y;
1001       if(beginLine < 0) {
1002         endLine += beginLine;
1003         beginLine = 0;
1004       }
1005       if(endLine > size.height) endLine = size.height - beginLine;
1006        
1007       g.setXORMode(color[COLOR_BG_STD]);
1008       g.fillRect(selectBegin.x * charWidth + xoffset,
1009                  beginLine * charHeight + yoffset,
1010                  (endLine == 0 ? (selectEnd.x - selectBegin.x) : 
1011                   (size.width - selectBegin.x)) 
1012                  * charWidth,
1013                  charHeight);
1014       if(endLine > 1)
1015         g.fillRect(0 + xoffset, 
1016                    (beginLine + 1) * charHeight + yoffset, 
1017                    size.width * charWidth, 
1018                    (endLine - 1) * charHeight);
1019       if(endLine > 0)
1020         g.fillRect(0 + xoffset, 
1021                    (beginLine + endLine) * charHeight + yoffset,
1022                    selectEnd.x * charWidth, 
1023                    charHeight);
1024       g.setPaintMode();
1025     }
1026
1027     if(insets != null) {
1028       g.setColor(getBackground());
1029       xoffset--; yoffset--;
1030       for(int i = insets.top - 1; i >= 0; i--)
1031         g.draw3DRect(xoffset - i, yoffset - i,
1032                      charWidth * size.width + 1 + i * 2, 
1033                      charHeight * size.height + 1 + i * 2,
1034                      raised);
1035     }
1036
1037     update[0] = false;
1038   }
1039
1040   private int checkBounds(int value, int lower, int upper)
1041   {
1042     if(value < lower) return lower;
1043     if(value > upper) return upper;
1044     return value;
1045   }
1046
1047   /**
1048    * Reshape character display according to resize strategy.
1049    * @see #setResizeStrategy
1050    */
1051   public void reshape(int x, int y, int w, int h)
1052   {
1053     if(debug > 0)
1054       System.err.println("CharDisplay: reshape("+x+","+y+","+w+","+h+")");
1055
1056     int xborder = 0, yborder = 0;
1057     
1058     if(insets != null) {
1059       w -= (xborder = insets.left + insets.right);
1060       h -= (yborder = insets.top + insets.bottom);
1061     }
1062     if(scrollBar != null) { w -= 15;}
1063
1064     Font tmpFont = normalFont;
1065     String fontName = normalFont.getName();
1066     fm = getFontMetrics(normalFont);
1067     if(fm != null)
1068     {
1069       charWidth = fm.charWidth('@');
1070       charHeight = fm.getHeight();
1071     }
1072     
1073     switch(resizeStrategy)
1074     {
1075     case RESIZE_SCREEN:
1076       setWindowSize(w / charWidth, size.height = h / charHeight);
1077       break;
1078     case RESIZE_FONT:
1079       int height = h / size.height;
1080       int width = w / size.width;
1081       
1082       fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
1083                                                 charHeight));
1084       
1085       // adapt current font size (from small up to best fit)
1086       if(fm.getHeight() < height || fm.charWidth('@') < width)
1087         do {
1088           fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
1089                                                     ++charHeight));
1090         } while(fm.getHeight() < height || 
1091                 fm.charWidth('@') < width); 
1092       
1093       // now check if we got a font that is too large
1094       if(fm.getHeight() > height || fm.charWidth('@') > width)
1095         do {
1096           fm = getFontMetrics(normalFont = new Font(fontName, Font.PLAIN, 
1097                                                     --charHeight));
1098         } while(charHeight > 1 && 
1099                 (fm.getHeight() > height || 
1100                  fm.charWidth('@') > width));
1101       
1102       if(charHeight <= 1) 
1103       {
1104         System.err.println("CharDisplay: error during resize, resetting");
1105         normalFont = tmpFont;
1106         System.err.println("CharDisplay: disabling font/screen resize");
1107         resizeStrategy = RESIZE_NONE;
1108       }
1109
1110       setFont(normalFont);
1111       fm = getFontMetrics(normalFont);
1112       charWidth = fm.charWidth('@');
1113       charHeight = fm.getHeight();
1114       charDescent = fm.getDescent();
1115       break;
1116     case RESIZE_NONE:
1117     default:
1118       break;
1119     }
1120     if(debug > 0)
1121     {
1122       System.err.println("CharDisplay: charWidth="+charWidth+", "+
1123                          "charHeight="+charHeight+", "+
1124                          "charDescent="+charDescent);
1125       System.err.println("CharDisplay: "+normalFont+", "+fm);
1126     }
1127     super.reshape(x, y, 
1128                   w + xborder + (scrollBar != null ? 15 : 0), 
1129                   h + yborder);
1130     
1131     if(scrollBar != null) {
1132       int xoffset = (super.size().width - size.width * charWidth - 15) / 2;
1133       int yoffset = (super.size().height - size.height * charHeight) / 2;
1134       if(scrBarPos.equals("West"))
1135         scrollBar.reshape(xoffset - (xborder / 2), yoffset - yborder / 2,
1136                           15, size.height * charHeight + yborder);
1137       else
1138         scrollBar.reshape(xoffset + (xborder / 2) + size.width * charWidth, 
1139                           yoffset - yborder / 2, 15, 
1140                           size.height * charHeight + yborder);
1141     }
1142   }
1143
1144   /**
1145    * Return the real size in points of the character display.
1146    * @return Dimension the dimension of the display
1147    * @see java.awt.Dimension
1148    */
1149   public Dimension size()
1150   {
1151     int xborder = 0, yborder = 0;
1152     if(insets != null) {
1153       xborder = insets.left + insets.right;
1154       yborder = insets.top + insets.bottom;
1155     }
1156     if(scrollBar != null) xborder += 15;
1157     
1158     return new Dimension(size.width * charWidth + xborder, 
1159                          size.height * charHeight + yborder);
1160   }
1161
1162   /**
1163    * Return the preferred Size of the character display.
1164    * This turns out to be the actual size.
1165    * @return Dimension dimension of the display
1166    * @see size
1167    */
1168   public Dimension preferredSize() 
1169   {
1170     return size();
1171   }
1172
1173   /**
1174    * The insets of the character display define the border.
1175    * @return Insets border thickness in pixels
1176    */
1177   public Insets insets() 
1178   {
1179     return insets == null ? super.insets() : insets;
1180   }
1181
1182   /**
1183    * Handle mouse events for copy & paste
1184    * @param evt the event that occured
1185    * @return boolean true if action was taken
1186    * @see java.awt.Event
1187    */
1188   public boolean handleEvent(Event evt) 
1189   {
1190     // handle scrollbar events
1191     if(evt != null && evt.target == scrollBar && evt.arg != null) {
1192       int val = ((Integer)evt.arg).intValue();
1193       setWindowBase(val);
1194       return true;
1195     }
1196
1197     if(evt.id == Event.MOUSE_DOWN || evt.id == Event.MOUSE_UP ||
1198        evt.id == Event.MOUSE_DRAG) {
1199       int xoffset = (super.size().width - size.width * charWidth) / 2;
1200       int yoffset = (super.size().height - size.height * charHeight) / 2;
1201       switch(evt.id) {
1202       case Event.MOUSE_DOWN:
1203         selectBegin.x = (evt.x - xoffset) / charWidth;
1204         selectBegin.y = (evt.y - yoffset) / charHeight + windowBase;
1205         selectEnd.x = selectBegin.x;
1206         selectEnd.y = selectBegin.y;
1207         if(selectFrame != null) selectFrame.hide();
1208         break;
1209       case Event.MOUSE_UP:
1210       case Event.MOUSE_DRAG:
1211         int x = (evt.x - xoffset) / charWidth;
1212         int y = (evt.y - yoffset) / charHeight + windowBase;
1213         int oldx = selectEnd.x, oldy = selectEnd.y;
1214
1215         if((x < selectBegin.x && y < selectBegin.y) &&
1216            (x < selectEnd.x && y < selectEnd.y)) {
1217           selectBegin.x = x;
1218           selectBegin.y = y;
1219         } else {
1220           selectEnd.x = x;
1221           selectEnd.y = y;
1222         }
1223
1224         if(evt.id == Event.MOUSE_UP) {
1225           if(selectBegin.x == selectEnd.x &&
1226              selectBegin.y == selectEnd.y) {
1227             repaint();
1228             return true;
1229           }
1230           String tmp = "";
1231           // fix end.x and end.y, they can get over the border
1232           if (selectEnd.x < 0) selectEnd.x = 0;
1233           if (selectEnd.y < 0) selectEnd.y = 0;
1234           if (selectEnd.y >= charArray.length) {
1235                 selectEnd.y = charArray.length-1;
1236           }
1237           if (selectEnd.x >= charArray[0].length) {
1238                 selectEnd.x = charArray[0].length-1;
1239           }
1240           for(int l = selectBegin.y; l <= selectEnd.y; l++)
1241             if(l == selectBegin.y) 
1242               tmp = (new String(charArray[l])).substring(selectBegin.x) + "\n";
1243             else if(l == selectEnd.y) 
1244               tmp += (new String(charArray[l])).substring(0, selectEnd.x);
1245             else tmp += new String(charArray[l]) + "\n";
1246           if(selectFrame == null) {
1247             /* for jdk-1.1
1248             String s=(String) ((StringSelection)this.getToolkit().
1249                                getSystemClipboard().
1250                                getContents(this)).
1251               getTransferData(DataFlavor.stringFlavor);
1252             System.out.println(s);
1253             */
1254
1255             selectFrame = new Frame("Pasteboard");
1256             selection = new TextArea();
1257             selection.setFont(normalFont);
1258             selectFrame.add("Center", selection);
1259             selectFrame.add("South", new Label("Click on the terminal window"+
1260                                                " to hide!"));
1261             selectFrame.pack();
1262           }
1263           selection.setText(tmp);
1264           //selectFrame.show();
1265           selection.selectAll();
1266           repaint();
1267         } else
1268           if(oldx != x || oldy != y) repaint();
1269         break;
1270       }
1271       return true;
1272     }
1273     return false;
1274   }
1275 }