// Copyright (c) 1995, 1996 Regents of the University of California. // All rights reserved. // // This software was developed by the Arcadia project // at the University of California, Irvine. // // Redistribution and use in source and binary forms are permitted // provided that the above copyright notice and this paragraph are // duplicated in all such forms and that any documentation, // advertising materials, and other materials related to such // distribution and use acknowledge that the software was developed // by the University of California, Irvine. The name of the // University may not be used to endorse or promote products derived // from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED // WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. // File: RedrawManager.java // Classes: RedrawManager // Original Author: Jason Robbins // $Id: RedrawManager.java,v 1.1.1.1 1997/02/27 20:52:47 chandra Exp $ package uci.graphedit; import java.awt.Rectangle; import java.awt.Graphics; import java.util.Date; import java.awt.Image; /** Stores a list of rectangles (and sometimes merges them) for use in determing the invalid region of a Editor. When a DiagramElement changes state, it notifies all Editor's that are viewing it, and the Editor adds bbox of that DiagramElement to its RedrawManager. Eventually, the RedrawManager will ask the editor to repaint damaged regions. It is important that the redraw not happen too soon after the damage, because we would like to use a single repaint to repair multple damages to the same part of the screen. */ public class RedrawManager implements Runnable { /** The list of damaged rectangles. */ private final int MAX_NUM_RECTS = 10; private Rectangle[] _rects = new Rectangle[MAX_NUM_RECTS]; private int _numRects = 0; /** The editor that controls this RedrawManager. */ private Document _ed; /** The thread spawned to periodically request repaints */ private Thread _repairThread; /** Time to request next repaint */ private long deadline = 0; /** Milliseconds between most recent addition of damage and next * redraw */ public long timeDelay = 150; /** Construct a new RedrawManager */ public RedrawManager(Document ed) { for (int i = 0; i < MAX_NUM_RECTS; ++i) { _rects[i] = new Rectangle(); } _ed = ed; _repairThread = new Thread(this, "RepairThread"); // _repairThread.setDaemon(true); // _repairThread.setPriority(Thread.MAX_PRIORITY); // this causes a security violation in Netscape _repairThread.start(); } /** If screen repainting is fast enough, then try do it more * often. This can be called from the Editor to try to optimize the * tradeoff between screen updates and latency in event * processing. */ public void moreRepairs() { timeDelay -= 30; if (timeDelay < 100) timeDelay = 100; } /** If screen repainting is getting really slow and the application * cannot process events, then make redraws less frequent */ public void fewerRepairs() { timeDelay += 90; if (timeDelay > 2000) timeDelay = 2000; } private void removeAllElements() { _numRects = 0; } /** Add a new rectangle of damage */ public void add(Rectangle r) { if (r.isEmpty()) return; if (!merge(r)) _rects[_numRects++].reshape(r.x, r.y, r.width, r.height); if (_numRects == MAX_NUM_RECTS) forceMerge(); if (deadline == 0) deadline = (new Date()).getTime() + timeDelay; } /** Try to merge the given rect into one of my existing damaged * rects. Rects can be merged if they are overlapping. In general, * they shuold be merged when the area added by the merger is small * enough that one large repaint is faster than two smaller * repaints. Reply true on success. */ private boolean merge(Rectangle r) { for (int i = 0; i < _numRects; ++i) { if (r.intersects(_rects[i])) { _rects[i].add(r); return true; } } return false; } /** Merge all the rectangles together, even if that means that a lot * more area is redrawn than needs to be. */ public void forceMerge() { for (int i = 1; i < _numRects; ++i) _rects[0].add(_rects[i]); _numRects = 1; deadline = 1; } /** The main method of the _repairThread, basically it just keeps * checking if there is damage that has not been repaired, and that * damage is old enough. */ public synchronized void run() { while (true) { try { wait(timeDelay * 2); } catch (InterruptedException ignore) { } repairDamage(); } } /** ask the Editor to repaint all damaged regions */ public synchronized void repairDamage() { paint(_ed, _ed.getGraphics()); } /** ask the Editor to repaint all damaged regions */ public synchronized void paint(Document ed, Graphics g) { long startTime = (new Date()).getTime(); if (startTime > deadline) { if (Globals.prefs().shouldPaintOffScreen()) paintOffscreen(ed, g); else paintOnscreen(ed, g); Globals.prefs().lastRedrawTime((new Date()).getTime() - startTime); deadline = 0; } } /** Ask the Editor to repaint damaged Rectangle's on the Graphics for * the drawing window. This allows the user to see some flicker, but * it gives more feedback that something is happening if the * computer is slow or the diagram is complex. */ public void paintOnscreen(Document ed, Graphics g) { int F = 16; // fudgefactor, extra redraw area; if (ed == null || g == null) return; for (int i = 0; i < _numRects; ++i) { Rectangle r = _rects[i]; Graphics offG = g.create(); offG.clearRect(r.x-F, r.y-F, r.width+F*2, r.height+F*2); offG.clipRect(r.x-F-1, r.y-F-1, r.width+F*2+2, r.height+F*2+2); ed.paintRect(r, offG); offG.dispose(); } removeAllElements(); } /** Ask the Editor to repaint damaged Rectangle's on an off screen * Image, and then bitblt that Image to the Graphics used by the * drawing window. This takes more time, but the user will never * see any flicker.

* * Needs-More-Work: there is a bug here, probably a hard one to fix: * sometimes dragging an object around fast will leave dirt and the * grid will appear misaligned... I think it is a synchronization * bug... */ public synchronized void paintOffscreen(Document ed, Graphics g) { int F = 16; // fudgefactor, extra redraw area; Image offscreen; if (ed == null || g == null) return; for (int i = 0; i < _numRects; ++i) { Rectangle r = _rects[i]; r.reshape(r.x-F, r.y-F, r.width+F*2, r.height+F*2); offscreen = ed.createImage(r.width, r.height); /* needs-more-work: should recycle */ if (offscreen == null) { paintOnscreen(ed, g); return; } Graphics offG = offscreen.getGraphics(); offG.translate(-r.x, -r.y); offG.setColor(ed.getBackground()); offG.fillRect(r.x, r.y, r.width, r.height); offG.clipRect(r.x-1, r.y-1, r.width+2, r.height+2); ed.paintRect(r, offG); // It is important that paintRect and the various Fig draw() // routines do not attempt to call add to register damage. There // should not be any reason to do that. It would cause deadlock... g.drawImage(offscreen, r.x, r.y, null); offG.dispose(); offscreen.flush(); } removeAllElements(); } } /* end class RedrawManager */