Source for javax.swing.text.DefaultHighlighter

   1: /* DefaultHighlighter.java --
   2:    Copyright (C) 2004, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath 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: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.text;
  40: 
  41: import gnu.classpath.NotImplementedException;
  42: 
  43: import java.awt.Color;
  44: import java.awt.Graphics;
  45: import java.awt.Insets;
  46: import java.awt.Rectangle;
  47: import java.awt.Shape;
  48: import java.util.ArrayList;
  49: 
  50: import javax.swing.SwingUtilities;
  51: import javax.swing.plaf.TextUI;
  52: 
  53: public class DefaultHighlighter extends LayeredHighlighter
  54: {
  55:   public static class DefaultHighlightPainter
  56:     extends LayerPainter
  57:   {
  58:     private Color color;
  59:     
  60:     public DefaultHighlightPainter(Color c)
  61:     {
  62:       super();
  63:       color = c;
  64:     }
  65: 
  66:     public Color getColor()
  67:     {
  68:       return color;
  69:     }
  70: 
  71:     private void paintHighlight(Graphics g, Rectangle rect)
  72:     {
  73:       g.fillRect(rect.x, rect.y, rect.width, rect.height);
  74:     }
  75:     
  76:     public void paint(Graphics g, int p0, int p1, Shape bounds,
  77:               JTextComponent t)
  78:     {
  79:       if (p0 == p1)
  80:         return;
  81:       
  82:       Rectangle rect = bounds.getBounds();
  83: 
  84:       if (color == null)
  85:         g.setColor(t.getSelectionColor());
  86:       else
  87:         g.setColor(color);
  88: 
  89:       TextUI ui = t.getUI();
  90:       
  91:       try
  92:       {
  93:         
  94:         Rectangle l0 = ui.modelToView(t, p0, null);
  95:         Rectangle l1 = ui.modelToView(t, p1, null);
  96:         
  97:         // Note: The computed locations may lie outside of the allocation
  98:         // area if the text is scrolled.
  99:         
 100:         if (l0.y == l1.y)
 101:           {
 102:             SwingUtilities.computeUnion(l0.x, l0.y, l0.width, l0.height, l1);
 103: 
 104:             // Paint only inside the allocation area.
 105:             SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height, l1);
 106:         
 107:             paintHighlight(g, l1);
 108:           }
 109:         else
 110:           {
 111:             // 1. The line of p0 is painted from the position of p0
 112:             // to the right border.
 113:             // 2. All lines between the ones where p0 and p1 lie on
 114:             // are completely highlighted. The allocation area is used to find
 115:             // out the bounds.
 116:             // 3. The final line is painted from the left border to the
 117:             // position of p1.
 118:             
 119:             // Highlight first line until the end.
 120:             // If rect.x is non-zero the calculation will properly adjust the
 121:             // area to be painted.
 122:             l0.x -= rect.x;
 123:             l0.width = rect.width - l0.x - rect.x;
 124:             
 125:             paintHighlight(g, l0);
 126:             
 127:             int posBelow = Utilities.getPositionBelow(t, p0, l0.x);
 128:             int p1RowStart = Utilities.getRowStart(t, p1);
 129:             if (posBelow != -1
 130:                 && posBelow != p0
 131:                 && Utilities.getRowStart(t, posBelow)
 132:                    != p1RowStart)
 133:               {
 134:                 Rectangle grow = ui.modelToView(t, posBelow);
 135:                 grow.x = rect.x;
 136:                 grow.width = rect.width;
 137:                 
 138:                 // Find further lines which have to be highlighted completely.
 139:                 int nextPosBelow = posBelow;
 140:                 while (nextPosBelow != -1
 141:                        && Utilities.getRowStart(t, nextPosBelow) != p1RowStart)
 142:                   {
 143:                     posBelow = nextPosBelow;
 144:                     nextPosBelow = Utilities.getPositionBelow(t, posBelow, l0.x);
 145:                     
 146:                     if (nextPosBelow == posBelow)
 147:                       break;
 148:                   }
 149:                 // Now posBelow is an offset on the last line which has to be painted
 150:                 // completely. (newPosBelow is on the same line as p1)
 151:                  
 152:                 // Retrieve the rectangle of posBelow and use its y and height
 153:                 // value to calculate the final height of the multiple line
 154:                 // spanning rectangle.
 155:                 Rectangle end = ui.modelToView(t, posBelow);
 156:                 grow.height = end.y + end.height - grow.y;
 157:                 
 158:                 paintHighlight(g, grow);
 159:               }
 160:             
 161:             // Paint last line from its beginning to the position of p1.
 162:             l1.width = l1.x + l1.width - rect.x;
 163:             l1.x = rect.x;
 164:             paintHighlight(g, l1);
 165:           }
 166:       }
 167:     catch (BadLocationException ex)
 168:       {
 169:         AssertionError err = new AssertionError("Unexpected bad location exception");
 170:         err.initCause(ex);
 171:         throw err;
 172:       }
 173:     }
 174: 
 175:     public Shape paintLayer(Graphics g, int p0, int p1, Shape bounds,
 176:                 JTextComponent c, View view)
 177:     {
 178:       throw new InternalError();
 179:     }
 180:   }
 181:   
 182:   private class HighlightEntry implements Highlighter.Highlight
 183:   {
 184:     int p0;
 185:     int p1;
 186:     Highlighter.HighlightPainter painter;
 187: 
 188:     public HighlightEntry(int p0, int p1, Highlighter.HighlightPainter painter)
 189:     {
 190:       this.p0 = p0;
 191:       this.p1 = p1;
 192:       this.painter = painter;
 193:     }
 194: 
 195:     public int getStartOffset()
 196:     {
 197:       return p0;
 198:     }
 199: 
 200:     public int getEndOffset()
 201:     {
 202:       return p1;
 203:     }
 204: 
 205:     public Highlighter.HighlightPainter getPainter()
 206:     {
 207:       return painter;
 208:     }
 209:   }
 210: 
 211:   /**
 212:    * @specnote final as of 1.4
 213:    */
 214:   public static final LayeredHighlighter.LayerPainter DefaultPainter =
 215:     new DefaultHighlightPainter(null);
 216:   
 217:   private JTextComponent textComponent;
 218:   private ArrayList highlights = new ArrayList();
 219:   private boolean drawsLayeredHighlights = true;
 220:   
 221:   public DefaultHighlighter()
 222:   {
 223:     // Nothing to do here.
 224:   }
 225: 
 226:   public boolean getDrawsLayeredHighlights()
 227:   {
 228:     return drawsLayeredHighlights;
 229:   }
 230: 
 231:   public void setDrawsLayeredHighlights(boolean newValue)
 232:   {
 233:     drawsLayeredHighlights = newValue;
 234:   }
 235:   
 236:   private void checkPositions(int p0, int p1)
 237:     throws BadLocationException
 238:   {
 239:     if (p0 < 0)
 240:       throw new BadLocationException("DefaultHighlighter", p0);
 241:     
 242:     if (p1 < p0)
 243:       throw new BadLocationException("DefaultHighlighter", p1);
 244:   }
 245: 
 246:   public void install(JTextComponent c)
 247:   {
 248:     textComponent = c;
 249:     removeAllHighlights();
 250:   }
 251: 
 252:   public void deinstall(JTextComponent c)
 253:   {
 254:     textComponent = null;
 255:   }
 256: 
 257:   public Object addHighlight(int p0, int p1, Highlighter.HighlightPainter painter)
 258:     throws BadLocationException
 259:   {
 260:     checkPositions(p0, p1);
 261:     HighlightEntry entry = new HighlightEntry(p0, p1, painter);
 262:     highlights.add(entry);
 263:     
 264:     textComponent.getUI().damageRange(textComponent, p0, p1);
 265:     
 266:     return entry;
 267:   }
 268: 
 269:   public void removeHighlight(Object tag)
 270:   {
 271:     highlights.remove(tag);
 272: 
 273:     HighlightEntry entry = (HighlightEntry) tag;
 274:     textComponent.getUI().damageRange(textComponent,
 275:                                       entry.p0,
 276:                                       entry.p1);
 277:   }
 278: 
 279:   public void removeAllHighlights()
 280:   {
 281:     highlights.clear();
 282:   }
 283: 
 284:   public Highlighter.Highlight[] getHighlights()
 285:   {
 286:     return (Highlighter.Highlight[]) 
 287:       highlights.toArray(new Highlighter.Highlight[highlights.size()]);
 288:   }
 289: 
 290:   public void changeHighlight(Object tag, int n0, int n1)
 291:     throws BadLocationException
 292:   {
 293:     int o0, o1;
 294:     
 295:     checkPositions(n0, n1);
 296:     HighlightEntry entry = (HighlightEntry) tag;
 297:     o0 = entry.p0;
 298:     o1 = entry.p1;
 299:     
 300:     // Prevent useless write & repaint operations.
 301:     if (o0 == n0 && o1 == n1)
 302:       return;
 303:     
 304:     entry.p0 = n0;
 305:     entry.p1 = n1;
 306:     
 307:     TextUI ui = textComponent.getUI();
 308:     
 309:     // Special situation where the old area has to be cleared simply.
 310:     if (n0 == n1)
 311:       ui.damageRange(textComponent, o0, o1);
 312:     // Calculates the areas where a change is really neccessary
 313:     else if ((o1 > n0 && o1 <= n1)
 314:         || (n1 > o0 && n1 <= o1))
 315:       {
 316:         // [fds, fde) - the first damage region
 317:         // [sds, sde] - the second damage region
 318:         int fds, sds;
 319:         int fde, sde;
 320: 
 321:         // Calculate first damaged region.
 322:         if(o0 < n0)
 323:           {
 324:             // Damaged region will be cleared as
 325:             // the old highlight region starts first.
 326:             fds = o0;
 327:             fde = n0;
 328:           }
 329:         else
 330:           {
 331:             // Damaged region will be painted as
 332:             // the new highlight region starts first.
 333:             fds = n0;
 334:             fde = o0;
 335:           }
 336: 
 337:         if (o1 < n1)
 338:           {
 339:             // Final region will be painted as the
 340:             // old highlight region finishes first
 341:             sds = o1;
 342:             sde = n1;
 343:           }
 344:         else
 345:           {
 346:             // Final region will be cleared as the
 347:             // new highlight region finishes first.
 348:             sds = n1;
 349:             sde = o1;
 350:           }
 351:         
 352:         // If there is no undamaged region in between
 353:         // call damageRange only once.
 354:         if (fde == sds)
 355:           ui.damageRange(textComponent, fds, sde);
 356:         else
 357:           {
 358:             if (fds != fde)
 359:               ui.damageRange(textComponent, fds, fde);
 360:         
 361:             if (sds != sde)
 362:               ui.damageRange(textComponent, sds, sde);
 363:           }
 364:       }
 365:     else
 366:       {
 367:         // The two regions do not overlap. So mark
 368:         // both areas as damaged.
 369:         ui.damageRange(textComponent, o0, o1);
 370:         ui.damageRange(textComponent, n0, n1);
 371:       }
 372:     
 373:   }
 374: 
 375:   public void paintLayeredHighlights(Graphics g, int p0, int p1,
 376:                                      Shape viewBounds, JTextComponent editor,
 377:                                      View view)
 378:   throws NotImplementedException
 379:   {
 380:     // TODO: Implement this properly.
 381:   }
 382: 
 383:   public void paint(Graphics g)
 384:   {
 385:     int size = highlights.size();
 386:     
 387:     // Check if there are any highlights.
 388:     if (size == 0)
 389:       return;
 390: 
 391:     // Prepares the rectangle of the inner drawing area.
 392:     Insets insets = textComponent.getInsets();
 393:     Shape bounds =
 394:       new Rectangle(insets.left,
 395:                     insets.top,
 396:                     textComponent.getWidth() - insets.left - insets.right,
 397:                     textComponent.getHeight() - insets.top - insets.bottom);
 398:     
 399:     for (int index = 0; index < size; ++index)
 400:       {
 401:     HighlightEntry entry = (HighlightEntry) highlights.get(index);
 402:     entry.painter.paint(g, entry.p0, entry.p1, bounds, textComponent);
 403:       }
 404:   }
 405: }