/* * ConsoleStore.java * ============== * Copyright (C) 1999, Ron Poet, Dept. Comp.Sci. Uni Glasgow Scotland * $Author: ron $ * $Date: 1999/09/03 10:51:30 $ * $Revision: 1.1 $ * * Java awt uses god knows how many threads. The Java synchronization * mechanism is woefully inadequate. We just have to make sure we * define atomic operations for anything that might involve more than * one thread. All drawing routines go via this class. */ package FormatIO; import java.io.*; import java.awt.*; import java.util.*; /*=================*/ class ConsoleStore /*=================*/ { private Canvas canvas; private Scrollbar sbar; private int char_width, char_height; private Color[] type_colour = { Color.blue, Color.black, Color.red, new Color(0.0F, 0.5F, 0.0F) }; private int prompt_char = '*'; private boolean prompt = false; // entries keep (char, type) pairs for each char private Vector entries = new Vector(100, 100); private int first_element = 0; // non-zero after clear // current state private int nrows, ncols; private int row = 0, col = 0; // position of next char private int row0 = 0; // first row on screen private int last_row; // as seen by scrollbar // permanant workspace for optimizations private char[] by = new char[1]; /*------------------------------------------------*/ ConsoleStore(Canvas c, Scrollbar s, int cw, int ch) /*------------------------------------------------*/ { canvas = c; sbar = s; char_width = cw; char_height = ch; } /***********************/ /* Atomic Operations */ /***********************/ /*------------------------------------------*/ synchronized void set_prompt(boolean p) /*------------------------------------------*/ { if (prompt == p) return; // no change if (prompt) undraw_char(prompt_char); prompt = p; if (prompt) draw_char(prompt_char, Console.T_PROMPT); } /*---------------------------------------------------*/ synchronized ConsoleEntry get_char_at(int index) /*---------------------------------------------------*/ { return (ConsoleEntry) entries.elementAt(index); } /*---------------------------------------------------*/ synchronized void add_string(String s, int type) /*---------------------------------------------------*/ { check_scrolling(); if (prompt) undraw_char(prompt_char); for (int i = 0; i < s.length(); i++) { int c = (int) s.charAt(i); entries.addElement(new ConsoleEntry(c, type)); draw_char(c, type); } if (prompt) draw_char(prompt_char, Console.T_PROMPT); } /*----------------------------------------------*/ synchronized int add_char(int c, int type) /*----------------------------------------------*/ { check_scrolling(); entries.addElement(new ConsoleEntry(c, type)); if (prompt) undraw_char(prompt_char); draw_char(c, type); if (prompt) draw_char(prompt_char, Console.T_PROMPT); return entries.size() - 1; } /* * Can only remove the last char if its type is T_USER or T_PROMPT * and the character is not a newline. Returns true if successful. */ /*------------------------------------*/ synchronized boolean sub_last_char() /*------------------------------------*/ { int i = entries.size() - 1; // avoid concurrent updates to entries ConsoleEntry ce = (ConsoleEntry) entries.elementAt(i); if (ce.c == '\n' || ce.t == Console.T_PROG || ce.t == Console.T_ERROR) return false; entries.removeElementAt(i); if (prompt) undraw_char(prompt_char); undraw_char(ce.c); if (prompt) draw_char(prompt_char, Console.T_PROMPT); return true; } /*---------------------------------*/ synchronized void redraw_all() // called by paint, don't clear /*---------------------------------*/ { draw_all(false); } /*---------------------------------------*/ synchronized void redraw_all(int r0) // called by us, clear /*---------------------------------------*/ { row0 = r0; draw_all(true); } /*-----------------------------------------------*/ synchronized void repaginate(int nr, int nc) /*-----------------------------------------------*/ { nrows = nr; /* * If column width changes then we have to repaginate, which will change * row, col, row0. Try and keep the top line the same, which means * recalculating row0. First find index of first character on line row0. * Then find new line with that character, which is new row0. */ if (ncols != nc) { // repeat pagination with old values int index0 = 0; row = 0; col = 0; int len = entries.size(); for (int i = first_element; i < len; i++) { if (row == row0) { index0 = i; break; } ConsoleEntry ce = (ConsoleEntry) entries.elementAt(i); if (ce.c == '\n' || col >= ncols) { row++; col = 0; } else col++; } // now paginate with new values ncols = nc; row = 0; col = 0; for (int i = first_element; i < len; i++) { if (index0 == i) { row0 = row; index0 = i - 1; // test never true again } ConsoleEntry ce = (ConsoleEntry) entries.elementAt(i); if (ce.c == '\n' || col >= ncols) { row++; col = 0; } else col++; } } /* * We may have many more rows visible, and last_row should * reflect that. last_row = max(row0 + nrows, row) * last_row can get larger or smaller, but cannot be less than row. */ last_row = max(row0 + nrows, row); sbar.setValues(row0, nrows, 0, last_row); sbar.setBlockIncrement(nrows); sbar.setVisibleAmount(nrows); // paint is called automatically } /*------------------------------*/ private int max(int a, int b) /*------------------------------*/ { if (a > b) return a; else return b; } /*----------------------------*/ synchronized void clear() /*----------------------------*/ { entries.addElement(new ConsoleEntry('\n', Console.T_PROG)); entries.addElement(new ConsoleEntry('\n', Console.T_PROG)); entries.addElement(new ConsoleEntry('-', Console.T_PROG)); entries.addElement(new ConsoleEntry('-', Console.T_PROG)); entries.addElement(new ConsoleEntry('-', Console.T_PROG)); entries.addElement(new ConsoleEntry('C', Console.T_PROG)); entries.addElement(new ConsoleEntry('L', Console.T_PROG)); entries.addElement(new ConsoleEntry('E', Console.T_PROG)); entries.addElement(new ConsoleEntry('A', Console.T_PROG)); entries.addElement(new ConsoleEntry('R', Console.T_PROG)); entries.addElement(new ConsoleEntry('-', Console.T_PROG)); entries.addElement(new ConsoleEntry('-', Console.T_PROG)); entries.addElement(new ConsoleEntry('-', Console.T_PROG)); entries.addElement(new ConsoleEntry('\n', Console.T_PROG)); entries.addElement(new ConsoleEntry('\n', Console.T_PROG)); row0 = 0; last_row = nrows - 1; sbar.setValues(0, nrows, 0, last_row); first_element = entries.size(); draw_all(true); } /*------------------------------------------------------*/ synchronized void write_file(FileOutputStream dest) throws IOException /*------------------------------------------------------*/ { int c = 0; int len = entries.size(); for (int i = 0; i < len; i++) { ConsoleEntry ce = (ConsoleEntry) entries.elementAt(i); if (ce.c == '\n' || c >= ncols) { dest.write('\n'); c = 0; } else { dest.write(ce.c); c++; } } } /*********************/ /* Private Methods */ /*********************/ /* * There are two reasons for adjusting the scroll position when drawing * a character. Firstly, the number of rows in the console has increased * and we would be writing past the bottom of the window. Secondly, the * user may have scrolled back, and we need to go forward again. */ /*----------------------------------------------*/ private synchronized void check_scrolling() /*----------------------------------------------*/ { if (row >= last_row) // First reason { last_row = row + nrows / 2; row0 = last_row - nrows + 1; sbar.setValues(row0, nrows, 0, last_row); draw_all(true); } else if (row - row0 > nrows) // Second reason { row0 = last_row - nrows + 1; sbar.setValue(row0); draw_all(true); } } /*------------------------------------------*/ private void draw_all(boolean clear_first) /*------------------------------------------*/ { if (clear_first) { Graphics g = canvas.getGraphics(); Dimension d = canvas.getSize(); g.clearRect(0, 0, d.width, d.height); } row = 0; col = 0; int len = entries.size(); for (int i = first_element; i < len; i++) { ConsoleEntry ce = (ConsoleEntry) entries.elementAt(i); draw_char(ce.c, ce.t); } if (prompt) draw_char(prompt_char, Console.T_PROMPT); } /*---------------------------------------*/ private void draw_char(int c, int type) /*---------------------------------------*/ { // a newline character if (c == '\n') { row++; col = 0; return; } // check line wrap if (col >= ncols) { row++; col = 0; } just_draw_char(c, type_colour[type]); col++; } /*-------------------------------*/ private void undraw_char(int c) /*-------------------------------*/ { col--; // reverse line wrap if necessary if (col < 0) { col = ncols - 1; row--; } just_draw_char(c, canvas.getBackground()); } /*------------------------------------------------*/ private void just_draw_char(int c, Color colour) /*------------------------------------------------*/ { // draw the character int x = (col + 1) * char_width; int y = (row - row0 + 1) * char_height; by[0] = (char) c; Graphics g = canvas.getGraphics(); g.setColor(colour); g.drawChars(by, 0, 1, x, y); } }