1 11 package org.eclipse.ui.internal.forms.widgets; 12 13 import java.util.ArrayList ; 14 import java.util.Hashtable ; 15 import java.util.Vector ; 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.graphics.Color; 19 import org.eclipse.swt.graphics.Font; 20 import org.eclipse.swt.graphics.FontMetrics; 21 import org.eclipse.swt.graphics.GC; 22 import org.eclipse.swt.graphics.Point; 23 import org.eclipse.swt.graphics.Rectangle; 24 25 import com.ibm.icu.text.BreakIterator; 26 27 31 public class TextSegment extends ParagraphSegment { 32 private String colorId; 33 34 private String fontId; 35 36 private String text; 37 38 protected boolean underline; 39 40 private boolean wrapAllowed = true; 41 42 protected Vector areaRectangles = new Vector (); 43 44 private TextFragment[] textFragments; 45 46 class AreaRectangle { 47 Rectangle rect; 48 49 int from, to; 50 51 public AreaRectangle(Rectangle rect, int from, int to) { 52 this.rect = rect; 53 this.from = from; 54 this.to = to; 55 } 56 57 public boolean contains(int x, int y) { 58 return rect.contains(x, y); 59 } 60 61 public boolean intersects(Rectangle region) { 62 return rect.intersects(region); 63 } 64 65 public String getText() { 66 if (from == 0 && to == -1) 67 return TextSegment.this.getText(); 68 if (from > 0 && to == -1) 69 return TextSegment.this.getText().substring(from); 70 return TextSegment.this.getText().substring(from, to); 71 } 72 } 73 74 static class SelectionRange { 75 public int start; 76 77 public int stop; 78 79 public SelectionRange() { 80 reset(); 81 } 82 83 public void reset() { 84 start = -1; 85 stop = -1; 86 } 87 } 88 89 static class TextFragment { 90 short index; 91 92 short length; 93 94 public TextFragment(short index, short length) { 95 this.index = index; 96 this.length = length; 97 } 98 } 99 100 public TextSegment(String text, String fontId) { 101 this(text, fontId, null, true); 102 } 103 104 public TextSegment(String text, String fontId, String colorId) { 105 this(text, fontId, colorId, true); 106 } 107 108 public TextSegment(String text, String fontId, String colorId, boolean wrapAllowed) { 109 this.text = cleanup(text); 110 this.fontId = fontId; 111 this.colorId = colorId; 112 this.wrapAllowed = wrapAllowed; 113 } 114 115 private String cleanup(String text) { 116 StringBuffer buf = new StringBuffer (); 117 for (int i = 0; i < text.length(); i++) { 118 char c = text.charAt(i); 119 if (c == '\n' || c == '\r' || c == '\f') { 120 if (i > 0) 121 buf.append(' '); 122 } else 123 buf.append(c); 124 } 125 return buf.toString(); 126 } 127 128 public void setWordWrapAllowed(boolean value) { 129 wrapAllowed = value; 130 } 131 132 public boolean isWordWrapAllowed() { 133 return wrapAllowed; 134 } 135 136 public boolean isSelectable() { 137 return false; 138 } 139 140 public String getColorId() { 141 return colorId; 142 } 143 144 public String getText() { 145 return text; 146 } 147 148 void setText(String text) { 149 this.text = cleanup(text); 150 textFragments = null; 151 } 152 153 void setColorId(String colorId) { 154 this.colorId = colorId; 155 } 156 157 void setFontId(String fontId) { 158 this.fontId = fontId; 159 textFragments = null; 160 } 161 162 public boolean contains(int x, int y) { 163 for (int i = 0; i < areaRectangles.size(); i++) { 164 AreaRectangle ar = (AreaRectangle) areaRectangles.get(i); 165 if (ar.contains(x, y)) 166 return true; 167 if (i<areaRectangles.size()-1) { 168 Rectangle top = ar.rect; 170 Rectangle bot = ((AreaRectangle)areaRectangles.get(i+1)).rect; 171 if (y >= top.y+top.height && y < bot.y) { 172 int left = Math.max(top.x, bot.x); 174 int right = Math.min(top.x+top.width, bot.x+bot.width); 175 if (x>=left && x<=right) { 176 return true; 177 } 178 } 179 } 180 } 181 return false; 182 } 183 184 public boolean intersects(Rectangle rect) { 185 for (int i = 0; i < areaRectangles.size(); i++) { 186 AreaRectangle ar = (AreaRectangle) areaRectangles.get(i); 187 if (ar.intersects(rect)) 188 return true; 189 if (i<areaRectangles.size()-1) { 190 Rectangle top = ar.rect; 192 Rectangle bot = ((AreaRectangle)areaRectangles.get(i+1)).rect; 193 if (top.y+top.height < bot.y) { 194 int y = top.y+top.height; 195 int height = bot.y-y; 196 int left = Math.max(top.x, bot.x); 197 int right = Math.min(top.x+top.width, bot.x+bot.width); 198 Rectangle gap = new Rectangle(left, y, right-left, height); 199 if (gap.intersects(rect)) 200 return true; 201 } 202 } 203 } 204 return false; 205 } 206 207 public Rectangle getBounds() { 208 int x = 0, y = 0; 209 int width = 0, height = 0; 210 211 for (int i = 0; i < areaRectangles.size(); i++) { 212 AreaRectangle ar = (AreaRectangle) areaRectangles.get(i); 213 if (i == 0) { 214 x = ar.rect.x; 215 y = ar.rect.y; 216 } else 217 x = Math.min(ar.rect.x, x); 218 width = Math.max(ar.rect.width, width); 219 height += ar.rect.height; 220 } 221 return new Rectangle(x, y, width, height); 222 } 223 224 public boolean advanceLocator(GC gc, int wHint, Locator locator, 225 Hashtable objectTable, boolean computeHeightOnly) { 226 Font oldFont = null; 227 if (fontId != null) { 228 oldFont = gc.getFont(); 229 Font newFont = (Font) objectTable.get(fontId); 230 if (newFont != null) 231 gc.setFont(newFont); 232 } 233 FontMetrics fm = gc.getFontMetrics(); 234 int lineHeight = fm.getHeight(); 235 boolean newLine = false; 236 237 if (wHint == SWT.DEFAULT || !wrapAllowed) { 238 Point extent = gc.textExtent(text); 239 int totalExtent = locator.x+extent.x; 240 if (isSelectable()) 241 totalExtent+=1; 242 243 if (wHint != SWT.DEFAULT && totalExtent > wHint) { 244 locator.x = locator.indent; 246 locator.y += locator.rowHeight; 247 if (computeHeightOnly) 248 locator.collectHeights(); 249 locator.rowHeight = 0; 250 locator.leading = 0; 251 newLine = true; 252 } 253 int width = extent.x; 254 if (isSelectable()) 255 width += 1; 256 locator.x += width; 257 locator.width = locator.indent + width; 258 locator.rowHeight = Math.max(locator.rowHeight, extent.y); 259 locator.leading = Math.max(locator.leading, fm.getLeading()); 260 return newLine; 261 } 262 263 computeTextFragments(gc); 264 265 int width = 0; 266 Point lineExtent = new Point(0, 0); 267 268 for (int i = 0; i < textFragments.length; i++) { 269 TextFragment textFragment = textFragments[i]; 270 int currentExtent = locator.x + lineExtent.x; 271 272 if (isSelectable()) 273 currentExtent += 1; 274 275 if (currentExtent + textFragment.length > wHint) { 276 int lineWidth = currentExtent; 278 locator.rowHeight = Math.max(locator.rowHeight, lineExtent.y); 279 locator.leading = Math.max(locator.leading, fm.getLeading()); 280 if (computeHeightOnly) 281 locator.collectHeights(); 282 locator.x = locator.indent; 283 locator.y += locator.rowHeight; 284 locator.rowHeight = 0; 285 locator.leading = 0; 286 lineExtent.x = 0; 287 lineExtent.y = 0; 288 width = Math.max(width, lineWidth); 289 newLine = true; 290 } 291 lineExtent.x += textFragment.length; 292 lineExtent.y = Math.max(lineHeight, lineExtent.y); 293 } 294 int lineWidth = lineExtent.x; 295 if (isSelectable()) 296 lineWidth += 1; 297 locator.x += lineWidth; 298 locator.width = width; 299 locator.rowHeight = Math.max(locator.rowHeight, lineExtent.y); 300 locator.leading = Math.max(locator.leading, fm.getLeading()); 301 if (oldFont != null) { 302 gc.setFont(oldFont); 303 } 304 return newLine; 305 } 306 307 318 private void layoutWithoutWrapping(GC gc, int width, Locator locator, 319 boolean selected, FontMetrics fm, int lineHeight, int descent) { 320 Point extent = gc.textExtent(text); 321 int ewidth = extent.x; 322 if (isSelectable()) 323 ewidth += 1; 324 if (locator.x + ewidth > width-locator.marginWidth) { 325 locator.resetCaret(); 327 locator.y += locator.rowHeight; 328 locator.rowHeight = 0; 329 locator.rowCounter++; 330 } 331 int ly = locator.getBaseline(fm.getHeight() - fm.getLeading()); 332 Rectangle br = new Rectangle(locator.x, ly, ewidth, 334 lineHeight - descent + 3); 335 areaRectangles.add(new AreaRectangle(br, 0, -1)); 336 locator.x += ewidth; 337 locator.width = ewidth; 338 locator.rowHeight = Math.max(locator.rowHeight, extent.y); 339 } 340 341 protected int convertOffsetToStringIndex(GC gc, String s, int x, 342 int swidth, int selOffset) { 343 int index = s.length(); 344 while (index > 0 && x + swidth > selOffset) { 345 index--; 346 String ss = s.substring(0, index); 347 swidth = gc.textExtent(ss).x; 348 } 349 return index; 350 } 351 352 public void paintFocus(GC gc, Color bg, Color fg, boolean selected, 353 Rectangle repaintRegion) { 354 if (areaRectangles == null) 355 return; 356 for (int i = 0; i < areaRectangles.size(); i++) { 357 AreaRectangle areaRectangle = (AreaRectangle) areaRectangles.get(i); 358 Rectangle br = areaRectangle.rect; 359 int bx = br.x; 360 int by = br.y; 361 if (repaintRegion != null) { 362 bx -= repaintRegion.x; 363 by -= repaintRegion.y; 364 } 365 if (selected) { 366 gc.setBackground(bg); 367 gc.setForeground(fg); 368 gc.drawFocus(bx, by, br.width, br.height); 369 } else { 370 gc.setForeground(bg); 371 gc.drawRectangle(bx, by, br.width - 1, br.height - 1); 372 } 373 } 374 } 375 376 public void paint(GC gc, boolean hover, Hashtable resourceTable, 377 boolean selected, SelectionData selData, Rectangle repaintRegion) { 378 this.paint(gc, hover, resourceTable, selected, false, selData, 379 repaintRegion); 380 } 381 382 protected void paint(GC gc, boolean hover, Hashtable resourceTable, 383 boolean selected, boolean rollover, SelectionData selData, 384 Rectangle repaintRegion) { 385 Font oldFont = null; 386 Color oldColor = null; 387 Color oldBg = null; 388 389 if (fontId != null) { 391 oldFont = gc.getFont(); 392 Font newFont = (Font) resourceTable.get(fontId); 393 if (newFont != null) 394 gc.setFont(newFont); 395 } 396 if (!hover && colorId != null) { 397 oldColor = gc.getForeground(); 398 Color newColor = (Color) resourceTable.get(colorId); 399 if (newColor != null) 400 gc.setForeground(newColor); 401 } 402 oldBg = gc.getBackground(); 403 404 FontMetrics fm = gc.getFontMetrics(); 405 int lineHeight = fm.getHeight(); 406 int descent = fm.getDescent(); 407 408 for (int i = 0; i < areaRectangles.size(); i++) { 410 AreaRectangle areaRectangle = (AreaRectangle) areaRectangles.get(i); 411 Rectangle rect = areaRectangle.rect; 412 String text = areaRectangle.getText(); 413 Point extent = gc.textExtent(text); 414 int textX = rect.x + (isSelectable()?1:0); 415 int lineY = rect.y + lineHeight - descent + 1; 416 paintString(gc, text, extent.x, textX, rect.y, lineY, selData, 417 rect, hover, rollover, repaintRegion); 418 if (selected) { 419 int fx = rect.x; 420 int fy = rect.y; 421 if (repaintRegion != null) { 422 fx -= repaintRegion.x; 423 fy -= repaintRegion.y; 424 } 425 Color fg = gc.getForeground(); 428 gc.setForeground(oldBg); 429 gc.drawRectangle(fx, fy, rect.width - 1, rect.height - 1); 430 gc.setForeground(fg); 431 gc.drawFocus(fx, fy, rect.width, rect.height); 432 } 433 } 434 if (oldFont != null) { 436 gc.setFont(oldFont); 437 } 438 if (oldColor != null) { 439 gc.setForeground(oldColor); 440 } 441 if (oldBg != null) { 442 gc.setBackground(oldBg); 443 } 444 } 445 446 public void computeSelection(GC gc, Hashtable resourceTable, SelectionData selData) { 447 Font oldFont = null; 448 449 if (fontId != null) { 450 oldFont = gc.getFont(); 451 Font newFont = (Font) resourceTable.get(fontId); 452 if (newFont != null) 453 gc.setFont(newFont); 454 } 455 456 for (int i = 0; i < areaRectangles.size(); i++) { 457 AreaRectangle areaRectangle = (AreaRectangle) areaRectangles.get(i); 458 Rectangle rect = areaRectangle.rect; 459 String text = areaRectangle.getText(); 460 Point extent = gc.textExtent(text); 461 computeSelection(gc, text, extent.x, selData, 462 rect); 463 } 464 if (oldFont != null) { 466 gc.setFont(oldFont); 467 } 468 } 469 470 private void paintString(GC gc, String s, int swidth, int x, int y, 471 int lineY, SelectionData selData, Rectangle bounds, boolean hover, 472 boolean rolloverMode, Rectangle repaintRegion) { 473 if (selData != null && selData.isEnclosed()) { 475 Color savedBg = gc.getBackground(); 476 Color savedFg = gc.getForeground(); 477 int leftOffset = selData.getLeftOffset(bounds.height); 478 int rightOffset = selData.getRightOffset(bounds.height); 479 boolean firstRow = selData.isFirstSelectionRow(bounds.y, 480 bounds.height); 481 boolean lastRow = selData.isLastSelectionRow(bounds.y, 482 bounds.height); 483 boolean selectedRow = selData 484 .isSelectedRow(bounds.y, bounds.height); 485 486 int sstart = -1; 487 int sstop = -1; 488 489 if ((firstRow && x + swidth < leftOffset) 490 || (lastRow && x > rightOffset)) { 491 paintStringSegment(gc, s, gc.textExtent(s).x, x, y, lineY, 492 hover, rolloverMode, repaintRegion); 493 return; 494 } 495 496 if (firstRow && bounds.x + swidth > leftOffset) { 497 sstart = convertOffsetToStringIndex(gc, s, bounds.x, swidth, 498 leftOffset); 499 } 500 if (lastRow && bounds.x + swidth > rightOffset) { 501 sstop = convertOffsetToStringIndex(gc, s, bounds.x, swidth, 502 rightOffset); 503 } 504 505 if (firstRow && sstart != -1) { 506 String left = s.substring(0, sstart); 507 int width = gc.textExtent(left).x; 508 paintStringSegment(gc, left, width, x, y, lineY, hover, 509 rolloverMode, repaintRegion); 510 x += width; 511 } 512 if (selectedRow) { 513 int lindex = sstart != -1 ? sstart : 0; 514 int rindex = sstop != -1 ? sstop : s.length(); 515 String mid = s.substring(lindex, rindex); 516 Point extent = gc.textExtent(mid); 517 gc.setForeground(selData.fg); 518 gc.setBackground(selData.bg); 519 gc.fillRectangle(x, y, extent.x, extent.y); 520 paintStringSegment(gc, mid, extent.x, x, y, lineY, hover, 521 rolloverMode, repaintRegion); 522 x += extent.x; 523 gc.setForeground(savedFg); 524 gc.setBackground(savedBg); 525 } else { 526 paintStringSegment(gc, s, gc.textExtent(s).x, x, y, lineY, 527 hover, rolloverMode, repaintRegion); 528 } 529 if (lastRow && sstop != -1) { 530 String right = s.substring(sstop); 531 paintStringSegment(gc, right, gc.textExtent(right).x, x, y, 532 lineY, hover, rolloverMode, repaintRegion); 533 } 534 } else { 535 paintStringSegment(gc, s, gc.textExtent(s).x, x, y, lineY, hover, 536 rolloverMode, repaintRegion); 537 } 538 } 539 540 private void computeSelection(GC gc, String s, int swidth, SelectionData selData, Rectangle bounds) { 541 int leftOffset = selData.getLeftOffset(bounds.height); 542 int rightOffset = selData.getRightOffset(bounds.height); 543 boolean firstRow = selData.isFirstSelectionRow(bounds.y, bounds.height); 544 boolean lastRow = selData.isLastSelectionRow(bounds.y, bounds.height); 545 boolean selectedRow = selData.isSelectedRow(bounds.y, bounds.height); 546 547 int sstart = -1; 548 int sstop = -1; 549 550 if (firstRow && bounds.x + swidth > leftOffset) { 551 sstart = convertOffsetToStringIndex(gc, s, bounds.x, swidth, 552 leftOffset); 553 } 554 if (lastRow && bounds.x + swidth > rightOffset) { 555 sstop = convertOffsetToStringIndex(gc, s, bounds.x, swidth, 556 rightOffset); 557 } 558 559 if (selectedRow) { 560 int lindex = sstart != -1 ? sstart : 0; 561 int rindex = sstop != -1 ? sstop : s.length(); 562 String mid = s.substring(lindex, rindex); 563 selData.addSegment(mid); 564 } 565 } 566 567 576 private void paintStringSegment(GC gc, String s, int swidth, int x, int y, 577 int lineY, boolean hover, boolean rolloverMode, 578 Rectangle repaintRegion) { 579 boolean reverse = false; 580 int clipX = x; 581 int clipY = y; 582 int clipLineY = lineY; 583 if (repaintRegion != null) { 584 clipX -= repaintRegion.x; 585 clipY -= repaintRegion.y; 586 clipLineY -= repaintRegion.y; 587 } 588 if (underline || hover || rolloverMode) { 589 if (rolloverMode && !hover) 590 reverse = true; 591 } 592 if (reverse) { 593 drawUnderline(gc, swidth, clipX, clipLineY, hover, rolloverMode); 594 gc.drawString(s, clipX, clipY, false); 595 } else { 596 gc.drawString(s, clipX, clipY, false); 597 drawUnderline(gc, swidth, clipX, clipLineY, hover, rolloverMode); 598 } 599 } 600 601 private void drawUnderline(GC gc, int swidth, int x, int y, boolean hover, 602 boolean rolloverMode) { 603 if (underline || hover || rolloverMode) { 604 Color saved = null; 605 if (rolloverMode && !hover) { 606 saved = gc.getForeground(); 607 gc.setForeground(gc.getBackground()); 608 } 609 gc.drawLine(x, y, x + swidth-1, y); 610 if (saved != null) 611 gc.setForeground(saved); 612 } 613 } 614 615 623 public void layout(GC gc, int width, Locator locator, 624 Hashtable resourceTable, boolean selected) { 625 Font oldFont = null; 626 627 areaRectangles.clear(); 628 629 if (fontId != null) { 630 oldFont = gc.getFont(); 631 Font newFont = (Font) resourceTable.get(fontId); 632 if (newFont != null) 633 gc.setFont(newFont); 634 } 635 FontMetrics fm = gc.getFontMetrics(); 636 int lineHeight = fm.getHeight(); 637 int descent = fm.getDescent(); 638 639 if (!wrapAllowed) { 640 layoutWithoutWrapping(gc, width, locator, selected, fm, lineHeight, 641 descent); 642 } else { 643 int lineStart = 0; 644 int lastLoc = 0; 645 Point lineExtent = new Point(0, 0); 646 computeTextFragments(gc); 647 int rightEdge = width-locator.marginWidth; 648 for (int i = 0; i < textFragments.length; i++) { 649 TextFragment fragment = textFragments[i]; 650 int breakLoc = fragment.index; 651 if (breakLoc == 0) 652 continue; 653 if (locator.x + lineExtent.x + fragment.length > rightEdge) { 654 int lineWidth = locator.x + lineExtent.x; 656 if (isSelectable()) 657 lineWidth += 1; 658 int ly = locator.getBaseline(lineHeight - fm.getLeading()); 659 Rectangle br = new Rectangle(isSelectable()? 660 locator.x - 1:locator.x, ly, 661 isSelectable()?lineExtent.x + 1:lineExtent.x, lineHeight - descent + 3); 662 areaRectangles 663 .add(new AreaRectangle(br, lineStart, lastLoc)); 664 665 locator.rowHeight = Math.max(locator.rowHeight, 666 lineExtent.y); 667 locator.resetCaret(); 668 if (isSelectable()) 669 locator.x += 1; 670 locator.y += locator.rowHeight; 671 locator.rowCounter++; 672 locator.rowHeight = 0; 673 lineStart = lastLoc; 674 lineExtent.x = 0; 675 lineExtent.y = 0; 676 } 677 lastLoc = breakLoc; 678 lineExtent.x += fragment.length; 679 lineExtent.y = Math.max(lineHeight, lineExtent.y); 680 } 681 int ly = locator.getBaseline(lineHeight - fm.getLeading()); 683 int lastWidth = lineExtent.x; 684 if (isSelectable()) 685 lastWidth += 1; 686 Rectangle br = new Rectangle(isSelectable()?locator.x - 1:locator.x, ly, 687 isSelectable()?lineExtent.x + 1:lineExtent.x, 688 lineHeight - descent + 3); 689 areaRectangles.add(new AreaRectangle(br, lineStart, lastLoc)); 691 locator.x += lastWidth; 692 locator.rowHeight = Math.max(locator.rowHeight, lineExtent.y); 693 } 694 if (oldFont != null) { 695 gc.setFont(oldFont); 696 } 697 } 698 699 private void computeTextFragments(GC gc) { 700 if (textFragments != null) 701 return; 702 ArrayList list = new ArrayList (); 703 BreakIterator wb = BreakIterator.getLineInstance(); 704 wb.setText(getText()); 705 int cursor = 0; 706 for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) { 707 if (loc == 0) 708 continue; 709 String word = text.substring(cursor, loc); 710 Point extent = gc.textExtent(word); 711 list.add(new TextFragment((short) loc, (short) extent.x)); 712 cursor = loc; 713 } 714 textFragments = (TextFragment[]) list.toArray(new TextFragment[list 715 .size()]); 716 } 717 718 public void clearCache(String fontId) { 719 if (fontId==null && (this.fontId==null||this.fontId.equals(FormTextModel.BOLD_FONT_ID))) 720 textFragments = null; 721 else if (fontId!=null && this.fontId!=null && fontId.equals(this.fontId)) 722 textFragments = null; 723 } 724 } 725 | Popular Tags |