Package org.gudy.azureus2.ui.swt.shells

Source Code of org.gudy.azureus2.ui.swt.shells.GCStringPrinter$URLInfo

/*
* File    : GCStringPrinter.java
* Created : 16 mars 2004
* By      : Olivier
*
* Azureus - a Java Bittorrent client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.gudy.azureus2.ui.swt.shells;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.core3.util.Debug;

import com.aelitis.azureus.ui.swt.imageloader.ImageLoader;

/**
* @author Olivier Chalouhi
* @author TuxPaper (rewrite)
*/
public class GCStringPrinter
{
  private static final boolean DEBUG = false;

  private static final String GOOD_STRING = "(/|,jI~`gy";

  public static final int FLAG_SKIPCLIP = 1;

  public static final int FLAG_FULLLINESONLY = 2;

  public static final int FLAG_NODRAW = 4;

  public static final int FLAG_KEEP_URL_INFO = 8;

  private static final Pattern patHREF = Pattern.compile(
      "<\\s*?a\\s.*?href\\s*?=\\s*?\"(.+?)\".*?>(.*?)<\\s*?/a\\s*?>",
      Pattern.CASE_INSENSITIVE);

  private static final Pattern patAHREF_TITLE = Pattern.compile(
      "title=\\\"([^\\\"]+)", Pattern.CASE_INSENSITIVE);

  private static final Pattern patAHREF_TARGET = Pattern.compile(
      "target=\\\"([^\\\"]+)", Pattern.CASE_INSENSITIVE);

  //private static final Pattern patOver1000 = Pattern.compile("[^\n]{1010,}");

  // Limit word/line length as OSX crashes on stringExtent on very very long words
  private static final int MAX_LINE_LEN = 4000;

  // max Word length can be same as line length since words are auto-split
  // across lines
  private static final int MAX_WORD_LEN = 4000;

  private boolean cutoff;

  private GC gc;

  private String string;

  private Rectangle printArea;

  private int swtFlags;

  private int printFlags;

  private Point size;

  private Color urlColor;

  private List<URLInfo> listUrlInfo;

  private Image[] images;
 
  private float[] imageScales;

  private int iCurrentHeight;

  private boolean wrap;

  public static class URLInfo
  {
    public String url;

    public String text;

    public Color urlColor;
   
    public Color dropShadowColor;

    int relStartPos;

    // We could use a region, but that uses a resource that requires disposal
    public List<Rectangle> hitAreas = null;

    int titleLength;

    public String fullString;

    public String title;

    public String target;

    public boolean urlUnderline;

    // @see java.lang.Object#toString()
    public String toString() {
      return super.toString() + ": relStart=" + relStartPos + ";url=" + url
          + ";title=" + text + ";hit="
          + (hitAreas == null ? 0 : hitAreas.size());
    }
  }

  private class LineInfo
  {
    public int width;

    String originalLine;

    String lineOutputed;

    int excessPos;

    public int relStartPos;
   
    public int height;
   
    public int imageIndexes[];

    public LineInfo(String originalLine, int relStartPos) {
      this.originalLine = originalLine;
      this.relStartPos = relStartPos;
    }

    // @see java.lang.Object#toString()
    public String toString() {
      return super.toString() + ": relStart=" + relStartPos + ";xcess="
          + excessPos + ";orig=" + originalLine + ";output=" + lineOutputed;
    }
  }
 
  public static boolean printString(GC gc, String string, Rectangle printArea) {
    return printString(gc, string, printArea, false, false);
  }

  public static boolean printString(GC gc, String string, Rectangle printArea,
      boolean skipClip, boolean fullLinesOnly) {
    return printString(gc, string, printArea, skipClip, fullLinesOnly, SWT.WRAP
        | SWT.TOP);
  }

  /**
   *
   * @param gc GC to print on
   * @param string Text to print
   * @param printArea Area of GC to print text to
   * @param skipClip Don't set any clipping on the GC.  Text may overhang
   *                 printArea when this is true
   * @param fullLinesOnly If bottom of a line will be chopped off, do not display it
   * @param swtFlags SWT flags.  SWT.CENTER, SWT.BOTTOM, SWT.TOP, SWT.WRAP
   * @return whether it fit
   */
  public static boolean printString(GC gc, String string, Rectangle printArea,
      boolean skipClip, boolean fullLinesOnly, int swtFlags) {
    try {
      GCStringPrinter sp = new GCStringPrinter(gc, string, printArea, skipClip,
          fullLinesOnly, swtFlags);
      return sp.printString();
    } catch (Exception e) {
      e.printStackTrace();
    }

    return false;
  }

  private boolean _printString() {
    if (Constants.isWindows) {
      return swt_printString_NoAdvanced();
    }
    return swt_printString();
  }
 
  private boolean swt_printString_NoAdvanced() {
    boolean b = false;
    try {
      boolean wasAdvanced = gc.getAdvanced();
      Rectangle clipping = null;
      // With Advanced on text antialias in SWT.DEFAULT is not the system's
      // default (Try flipping the "Turn on ClearType" checkbox on
      // the ClearType Text Tuner", and you'll see the text redraw correctly
      // when advanced is off, but not when it's on)
      // Other problems with text and GDIP, see http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/362ab21b-1dc4-4140-a39a-a366beea9e40
     
      // Turn off Advanced while drawing text so it antialiases based on
      // system prefs.
      // NOTE: This messes up any Transforms :(
      if (gc.getAdvanced() && gc.getTextAntialias() == SWT.DEFAULT
          && gc.getAlpha() == 255) {
        clipping = gc.getClipping();
        gc.setAdvanced(false);
        gc.setClipping(clipping);
      }
      b = __printString();
      if (wasAdvanced) {
        gc.setAdvanced(true);
        gc.setClipping(clipping);
      }
    } catch (Throwable t) {
      Debug.out(t);
    }

    if (DEBUG) {
      System.out.println("");
    }

    return b;
  }

  private boolean swt_printString() {
    boolean b = false;
    try {
      b = __printString();
    } catch (Throwable t) {
      Debug.out(t);
    }

    if (DEBUG) {
      System.out.println("");
    }

    return b;
  }

  /**
   * @param gc
   * @param string
   * @param printArea
   * @param printFlags
   * @param swtFlags
   * @return
   *
   * @since 3.0.4.3
   */
  private boolean __printString() {
    size = new Point(0, 0);

    if (string == null) {
      return false;
    }

    if (printArea == null || printArea.isEmpty()) {
      return false;
    }

    ArrayList<LineInfo> lines = new ArrayList<LineInfo>(1);
   
    while (string.indexOf('\t') >= 0) {
      string = string.replace('\t', ' ');
    }
   
    if (string.indexOf("  ") > 0) {
      string = string.replaceAll("  +", " ");
    }
   
    boolean hasSlashR = string.indexOf('\r') > 0;

    boolean fullLinesOnly = (printFlags & FLAG_FULLLINESONLY) > 0;
    boolean skipClip = (printFlags & FLAG_SKIPCLIP) > 0;
    boolean noDraw = (printFlags & FLAG_NODRAW) > 0;
    wrap = (swtFlags & SWT.WRAP) > 0;

    if (string.indexOf('<') >= 0) {
      if ((printFlags & FLAG_KEEP_URL_INFO) == 0) {
        Matcher htmlMatcher = patHREF.matcher(string);
        boolean hasURL = htmlMatcher.find();
        if (hasURL) {
          listUrlInfo = new ArrayList<URLInfo>(1);
 
          while (hasURL) {
            URLInfo urlInfo = new URLInfo();
 
            // Store the full ahref string once, then use substring which doesn't
            // create real strings :)
            urlInfo.fullString = htmlMatcher.group();
            urlInfo.relStartPos = htmlMatcher.start(0);
 
            urlInfo.url = string.substring(htmlMatcher.start(1),
                htmlMatcher.end(1));
            urlInfo.text = string.substring(htmlMatcher.start(2),
                htmlMatcher.end(2));
            urlInfo.titleLength = urlInfo.text.length();
 
            Matcher matcherTitle = patAHREF_TITLE.matcher(urlInfo.fullString);
            if (matcherTitle.find()) {
              urlInfo.title = string.substring(urlInfo.relStartPos
                  + matcherTitle.start(1), urlInfo.relStartPos
                  + matcherTitle.end(1));
            }
 
            Matcher matcherTarget = patAHREF_TARGET.matcher(urlInfo.fullString);
            if (matcherTarget.find()) {
              urlInfo.target = string.substring(urlInfo.relStartPos
                  + matcherTarget.start(1), urlInfo.relStartPos
                  + matcherTarget.end(1));
            }
 
            //System.out.println("URLINFO! " + urlInfo.fullString
            //    + "\ntarget="
            //    + urlInfo.target + "\ntt=" + urlInfo.title + "\nurl="
            //    + urlInfo.url + "\ntext=" + urlInfo.text + "\n\n");
 
            string = htmlMatcher.replaceFirst(urlInfo.text.replaceAll("\\$",
                "\\\\\\$"));
           
            listUrlInfo.add(urlInfo);
            htmlMatcher = patHREF.matcher(string);
            hasURL = htmlMatcher.find(urlInfo.relStartPos);
          }
        }
      } else {
        Matcher htmlMatcher = patHREF.matcher(string);
        string = htmlMatcher.replaceAll("$2");
      }
    }

    Rectangle rectDraw = new Rectangle(printArea.x, printArea.y,
        printArea.width, printArea.height);

    Rectangle oldClipping = null;
    try {
      if (!skipClip && !noDraw) {
        oldClipping = gc.getClipping();

        // Protect the GC from drawing outside the drawing area
        gc.setClipping(printArea);
      }

      // Process string line by line
      iCurrentHeight = 0;
      int currentCharPos = 0;
     
      int posNewLine = string.indexOf('\n');
      if (hasSlashR) {
        int posR = string.indexOf('\r');
        if (posR == -1) {
          posR = posNewLine;
        }
        posNewLine = Math.min(posNewLine, posR);
      }
      if (posNewLine < 0) {
        posNewLine = string.length();
      }
      int posLastNewLine = 0;
      while (posNewLine >= 0 && posLastNewLine < string.length()) {
        String sLine = string.substring(posLastNewLine, posNewLine);

        do {
          LineInfo lineInfo = new LineInfo(sLine, currentCharPos);
          lineInfo = processLine(gc, lineInfo, printArea,  fullLinesOnly,
              false);
          String sProcessedLine = (String) lineInfo.lineOutputed;

          if (sProcessedLine != null && sProcessedLine.length() > 0) {
            if (lineInfo.width == 0 || lineInfo.height == 0) {
              Point gcExtent = gc.stringExtent(sProcessedLine);
              if (lineInfo.width == 0) {
                lineInfo.width = gcExtent.x;
              }
              if (lineInfo.height == 0) {
                lineInfo.height = gcExtent.y;
              }
            }
            Point extent = new Point(lineInfo.width, lineInfo.height);
            iCurrentHeight += extent.y;
            boolean isOverY = iCurrentHeight > printArea.height;

            if (DEBUG) {
              System.out.println("Adding Line: [" + sProcessedLine + "]"
                  + sProcessedLine.length() + "; h=" + iCurrentHeight + "("
                  + printArea.height + "). fullOnly?" + fullLinesOnly
                  + ". Excess: " + lineInfo.excessPos);
            }

            if (isOverY && !fullLinesOnly) {
              //fullLinesOnly = true; // <-- don't know why we needed this
              lines.add(lineInfo);
            } else if (isOverY && fullLinesOnly && lines.size() > 0) {
              String excess = lineInfo.excessPos >= 0
                  ? sLine.substring(lineInfo.excessPos) : null;
              if (excess != null) {
                if (fullLinesOnly) {
                  if (lines.size() > 0) {
                    lineInfo = lines.remove(lines.size() - 1);
                    sProcessedLine = lineInfo.originalLine.length() > MAX_LINE_LEN
                        ? lineInfo.originalLine.substring(0, MAX_LINE_LEN)
                        : lineInfo.originalLine;
                    //sProcessedLine = ((LineInfo) lines.remove(lines.size() - 1)).originalLine;
                    extent = gc.stringExtent(sProcessedLine);
                  } else {
                    if (DEBUG) {
                      System.out.println("No PREV!?");
                    }
                    return false;
                  }
                } else {
                  sProcessedLine = sProcessedLine.length() > MAX_LINE_LEN
                      ? sProcessedLine.substring(0, MAX_LINE_LEN)
                      : sProcessedLine;
                }

                if (excess.length() > MAX_LINE_LEN) {
                  excess = excess.substring(0, MAX_LINE_LEN);
                }

                StringBuffer outputLine = new StringBuffer(sProcessedLine);
                lineInfo.width = extent.x;
                wrap = false;
                int newExcessPos = processWord(gc, sProcessedLine,
                    " " + excess, printArea, lineInfo, outputLine,
                    new StringBuffer());
                if (DEBUG) {
                  System.out.println("  with word [" + excess + "] len is "
                      + lineInfo.width + "(" + printArea.width + ") w/excess "
                      + newExcessPos);
                }

                lineInfo.lineOutputed = outputLine.toString();
                lines.add(lineInfo);
                if (DEBUG) {
                  System.out.println("replace prev line with: "
                      + outputLine.toString());
                }
              } else {
                if (DEBUG) {
                  System.out.println("No Excess");
                }
              }
              cutoff = true;
              return false;
            } else {
              lines.add(lineInfo);
            }
            sLine = lineInfo.excessPos >= 0 && wrap
                ? sLine.substring(lineInfo.excessPos) : null;
          } else {
            if (DEBUG) {
              System.out.println("Line process resulted in no text: " + sLine);
            }
            lines.add(lineInfo);
            currentCharPos++;
            break;
            //return false;
          }

          currentCharPos += lineInfo.excessPos >= 0 ? lineInfo.excessPos
              : lineInfo.lineOutputed.length();
          //System.out.println("output: " + lineInfo.lineOutputed.length() + ";"
          //    + lineInfo.lineOutputed + ";xc=" + lineInfo.excessPos + ";ccp=" + currentCharPos);
          //System.out.println("lineo=" + lineInfo.lineOutputed.length() + ";" + sLine.length() );
        } while (sLine != null);

        if (string.length() > posNewLine && string.charAt(posNewLine) == '\r'
            && string.charAt(posNewLine + 1) == '\n') {
          posNewLine++;
        }
        posLastNewLine = posNewLine + 1;
        currentCharPos = posLastNewLine;

        posNewLine = string.indexOf('\n', posLastNewLine);
        if (hasSlashR) {
          int posR = string.indexOf('\r', posLastNewLine);
          if (posR == -1) {
            posR = posNewLine;
          }
          posNewLine = Math.min(posNewLine, posR);
        }
        if (posNewLine < 0) {
          posNewLine = string.length();
        }
      }
    } finally {
      if (lines.size() > 0) {
        // rebuild full text to get the exact y-extent of the output
        // this may be different (but shouldn't be!) than the height of each
        // line
        /*
        StringBuffer fullText = new StringBuffer(string.length() + 10);
        for (LineInfo lineInfo : lines) {
          if (fullText.length() > 0) {
            fullText.append('\n');
          }
          fullText.append(lineInfo.lineOutputed);
        }

        //size = gc.textExtent(fullText.toString());
         */

        for (LineInfo lineInfo : lines) {
          size.x = Math.max(lineInfo.width, size.x);
          size.y += lineInfo.height;
        }
       
        if ((swtFlags & (SWT.BOTTOM)) != 0) {
          rectDraw.y = rectDraw.y + rectDraw.height - size.y;
        } else if ((swtFlags & SWT.TOP) == 0) {
          // center vert
          rectDraw.y = rectDraw.y + (rectDraw.height - size.y) / 2;
        }

        if (!noDraw || listUrlInfo != null) {
          for (LineInfo lineInfo : lines) {
            try {
              drawLine(gc, lineInfo, swtFlags, rectDraw, noDraw);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
        }
      }

      if (!skipClip && !noDraw) {
        gc.setClipping(oldClipping);
      }

    }
   
    cutoff |= size.y > printArea.height;
    return !cutoff;
  }

  /**
   * @param hasMoreElements
   * @param line
   *
   * @since 3.0.0.7
   */
  private LineInfo processLine(final GC gc, final LineInfo lineInfo,
      final Rectangle printArea, final boolean fullLinesOnly,
      boolean hasMoreElements) {

    if (lineInfo.originalLine.length() == 0) {
      lineInfo.lineOutputed = "";
      lineInfo.height = gc.stringExtent(GOOD_STRING).y;
      return lineInfo;
    }
   
    StringBuffer outputLine = null;
    int excessPos = -1;

    if (images != null || lineInfo.originalLine.length() > MAX_LINE_LEN
        || gc.stringExtent(lineInfo.originalLine).x > printArea.width) {
      outputLine = new StringBuffer();
      if (DEBUG) {
        System.out.println("Line to process: " + lineInfo.originalLine);
      }
      StringBuffer space = new StringBuffer(1);

      if (!wrap && images == null) {
        if (DEBUG) {
          System.out.println("No Wrap.. doing all in one line");
        }

        String sProcessedLine = lineInfo.originalLine.length() > MAX_LINE_LEN
            ? lineInfo.originalLine.substring(0, MAX_LINE_LEN)
            : lineInfo.originalLine;

        // if it weren't for the elipses, we could do:
        // outputLine.append(sProcessedLine);

        excessPos = processWord(gc, lineInfo.originalLine, sProcessedLine,
            printArea, lineInfo, outputLine, space);
      } else {
        int posLastWordStart = 0;
        int posWordStart = lineInfo.originalLine.indexOf(' ');
        while (posWordStart == 0) {
          posWordStart = lineInfo.originalLine.indexOf(' ', posWordStart + 1);
        }
        if (posWordStart < 0) {
          posWordStart = lineInfo.originalLine.length();
        }
        // Process line word by word
        int curPos = 0;
        while (posWordStart >= 0 && posLastWordStart < lineInfo.originalLine.length()) {
          String word = lineInfo.originalLine.substring(posLastWordStart, posWordStart);
          if (word.length() == 0) {
            excessPos = -1;
            outputLine.append(' ');
          }

          for (int i = 0; i < word.length(); i += MAX_WORD_LEN) {
            String subWord;
            int endPos = i + MAX_WORD_LEN;
            if (endPos > word.length()) {
              subWord = word.substring(i);
            } else {
              subWord = word.substring(i, endPos);
            }

            excessPos = processWord(gc, lineInfo.originalLine, subWord,
                printArea, lineInfo, outputLine, space);
            if (DEBUG) {
              System.out.println("  with word [" + subWord + "] len is "
                  + lineInfo.width + "(" + printArea.width + ") w/excess "
                  + excessPos);
            }
            if (excessPos >= 0) {
              excessPos += curPos;
              break;
            }
            if (endPos <= word.length()) {
              space.setLength(0);
            }
            curPos += subWord.length() + 1;
          }
          if (excessPos >= 0) {
            break;
          }
         
          posLastWordStart = posWordStart + 1;
          posWordStart = lineInfo.originalLine.indexOf(' ', posLastWordStart);
          if (posWordStart < 0) {
            posWordStart = lineInfo.originalLine.length();
          }
        }
      }
    }

    if (!wrap && hasMoreElements && excessPos >= 0) {
      if (outputLine == null) {
        outputLine = new StringBuffer(lineInfo.originalLine);
      }
      int len = outputLine.length();
      if (len > 2) {
        len -= 2;
      }
      outputLine.setLength(len);
      outputLine.append("\u2026");
      cutoff = true;
    }
    //drawLine(gc, outputLine, swtFlags, rectDraw);
    //    if (!wrap) {
    //      return hasMoreElements;
    //    }
    lineInfo.excessPos = excessPos;
    lineInfo.lineOutputed = outputLine == null ? lineInfo.originalLine : outputLine.toString();
    return lineInfo;
  }

  /**
   * @param int Position of part of word that didn't fit
   *
   * @since 3.0.0.7
   */
  private int processWord(final GC gc, final String sLine, String word,
      final Rectangle printArea, final LineInfo lineInfo,
      StringBuffer outputLine, final StringBuffer space) {

    if (word.length() == 0) {
      space.append(' ');
      return -1;
    }
   
    //System.out.println("PW: " + word);
    if (images != null && word.length() >= 2 && word.charAt(0) == '%') {
      int imgIdx = word.charAt(1) - '0';
      if (images.length > imgIdx && imgIdx >= 0 && images[imgIdx] != null) {
        Image img = images[imgIdx];
        Rectangle bounds = img.getBounds();
        if (imageScales != null && imageScales.length > imgIdx) {
          bounds.width = (int) (bounds.width * imageScales[imgIdx]);
          bounds.height = (int) (bounds.height * imageScales[imgIdx]);
        }
       
        Point spaceExtent = gc.stringExtent(space.toString());
        int newWidth = lineInfo.width + bounds.width + spaceExtent.x;


        if (newWidth > printArea.width) {
          if (bounds.width + spaceExtent.x < printArea.width || lineInfo.width > 0) {
            //outputLine.append(space);
            //outputLine.append(word, 0, 2);
            //System.out.println("w1 = " + lineInfo.width + ";h=" + lineInfo.height);
            return 0;
          }
        }
       
        if (lineInfo.imageIndexes == null) {
          lineInfo.imageIndexes = new int[] { imgIdx };
        }
       
       
        //int targetWidth = lineInfo.width + newWidth;
       
        lineInfo.width = newWidth;
        lineInfo.height = Math.max(bounds.height, lineInfo.height);

        Point ptWordSize = gc.stringExtent(word.substring(2) + " ");
        if (lineInfo.width + ptWordSize.x > printArea.width) {
          outputLine.append(space);
          outputLine.append(word.substring(0,2));
          //System.out.println("w8 = " + lineInfo.width + ";h=" + lineInfo.height);
          return 2;
        }
       
        outputLine.append(space);
        space.setLength(0);
        outputLine.append(word.substring(0, 2));
        word = word.substring(2);
        //outputLine.append(word);
        //if (space.length() > 0) {
        //  space.delete(0, space.length());
        //}
        //space.append(' ');
       
        //System.out.println("w2 = " + lineInfo.width + ";h=" + lineInfo.height);
        //return -1;
      }
    }

    Point ptLineAndWordSize = gc.stringExtent(outputLine + word + " ");
    //System.out.println(ptLineAndWordSize + ";" + outputLine  + "::WordComp " + (ptLineAndWordSize.x - lineInfo.width));
    if (ptLineAndWordSize.x > printArea.width) {
      // word is longer than space avail, split

      Point ptWordSize2 = gc.stringExtent(word + " ");
      boolean bWordLargerThanWidth = ptWordSize2.x > printArea.width;
      // This will split put a word that is longer than a full line onto a new
      // line (when the existing line has text).
      if (bWordLargerThanWidth && lineInfo.width > 0) {
        //System.out.println("w3 = " + lineInfo.width + ";h=" + lineInfo.height);
        return 0;
      }

      int endIndex = word.length();
      long diff = endIndex;

      while (ptLineAndWordSize.x != printArea.width) {
        diff = (diff >> 1) + (diff % 2);

        if (diff <= 0) {
          diff = 1;
        }

        //System.out.println("diff=" + diff + ";e=" + endIndex + ";tw=" + targetWidth + ";paw= " + printArea.width);
        if (ptLineAndWordSize.x > printArea.width) {
          endIndex -= diff;
          if (endIndex < 1) {
            endIndex = 1;
          }
        } else {
          endIndex += diff;
          if (endIndex > word.length()) {
            endIndex = word.length();
          }
        }

        ptLineAndWordSize = gc.stringExtent(outputLine + word.substring(0, endIndex) + " ");

        if (diff <= 1) {
          break;
        }
      }
      boolean nothingFit = endIndex == 0;
      if (nothingFit) {
        endIndex = 1;
      }
      if (ptLineAndWordSize.x > printArea.width && endIndex > 1) {
        endIndex--;
        ptLineAndWordSize = gc.stringExtent(outputLine + word.substring(0, endIndex) + " ");
      }

      if (DEBUG) {
        System.out.println("excess starts at " + endIndex + " of "
            + word.length() + ". "
            + "wrap?" + wrap);
      }
      if (wrap && (printFlags & FLAG_FULLLINESONLY) > 0) {
        int nextLineHeight = gc.stringExtent(GOOD_STRING).y;
        if (iCurrentHeight + ptLineAndWordSize.y + nextLineHeight > printArea.height) {
          if (DEBUG) {
            System.out.println("turn off wrap");
          }
          wrap = false;
        }
      }

      if (endIndex > 0 && outputLine.length() > 0 && !nothingFit) {
        outputLine.append(space);
      }

      //int w = ptLineAndWordSize.x - lineInfo.width;
      if (wrap && !nothingFit && !bWordLargerThanWidth) {
        // whole word is excess
        return 0;
      }

      outputLine.append(word.substring(0, endIndex));
      if (!wrap) {
        int len = outputLine.length();
        if (len == 0) {
          if (word.length() > 0) {
            outputLine.append(word.charAt(0));
          } else if (sLine.length() > 0) {
            outputLine.append(sLine.charAt(0));
          }
        } else {
          if (len > 2) {
            len -= 2;
          }
          outputLine.setLength(len);
          outputLine.append("\u2026");
          cutoff = true;
        }
      }
      //drawLine(gc, outputLine, swtFlags, rectDraw);
      if (DEBUG) {
        System.out.println("excess " + word.substring(endIndex));
      }
      //System.out.println("w9 = " + lineInfo.width + ";h=" + lineInfo.height);
      return endIndex;
    }

    lineInfo.width = ptLineAndWordSize.x;
    if (lineInfo.width > printArea.width) {
      if (space.length() > 0) {
        space.delete(0, space.length());
      }

      if (!wrap) {
        int len = outputLine.length();
        if (len == 0) {
          if (word.length() > 0) {
            outputLine.append(word.charAt(0));
          } else if (sLine.length() > 0) {
            outputLine.append(sLine.charAt(0));
          }
        } else {
          if (len > 2) {
            len -= 2;
          }
          outputLine.setLength(len);
          outputLine.append("\u2026");
          cutoff = true;
        }
        //System.out.println("w5 = " + lineInfo.width + ";h=" + lineInfo.height);
        return -1;
      } else {
        //System.out.println("w6 = " + lineInfo.width + ";h=" + lineInfo.height);
        return 0;
      }
      //drawLine(gc, outputLine, swtFlags, rectDraw);
    }

    if (outputLine.length() > 0) {
      outputLine.append(space);
    }
    outputLine.append(word);
    if (space.length() > 0) {
      space.delete(0, space.length());
    }
    space.append(' ');

    //System.out.println("w4 = " + lineInfo.width + ";h=" + lineInfo.height);
    return -1;
  }

  /**
   * printArea is updated to the position of the next row
   *
   * @param gc
   * @param outputLine
   * @param swtFlags
   * @param printArea
   * @param noDraw
   */
  private void drawLine(GC gc, LineInfo lineInfo, int swtFlags,
      Rectangle printArea, boolean noDraw) {
    String text = lineInfo.lineOutputed;
    // TODO: ensure width and height have values
    if (lineInfo.width == 0 || lineInfo.height == 0) {
      Point gcExtent = gc.stringExtent(text);;
      if (lineInfo.width == 0) {
        lineInfo.width = gcExtent.x;
      }
      if (lineInfo.height == 0) {
        lineInfo.height = gcExtent.y;
      }
    }
    Point drawSize = new Point(lineInfo.width, lineInfo.height);
   
    int x0;
    if ((swtFlags & SWT.RIGHT) > 0) {
      x0 = printArea.x + printArea.width - drawSize.x;
    } else if ((swtFlags & SWT.CENTER) > 0) {
      x0 = printArea.x + (printArea.width - drawSize.x) / 2;
    } else {
      x0 = printArea.x;
    }

    int y0 = printArea.y;

    int lineInfoRelEndPos = lineInfo.relStartPos
        + lineInfo.lineOutputed.length();
    int relStartPos = lineInfo.relStartPos;
    int lineStartPos = 0;

    URLInfo urlInfo = null;
    boolean drawURL = hasHitUrl();

    if (drawURL) {
      URLInfo[] hitUrlInfo = getHitUrlInfo();
      int nextHitUrlInfoPos = 0;

      while (drawURL) {
        drawURL = false;
        for (int i = nextHitUrlInfoPos; i < hitUrlInfo.length; i++) {
          urlInfo = hitUrlInfo[i];

          drawURL = (urlInfo.relStartPos < lineInfoRelEndPos)
              && (urlInfo.relStartPos + urlInfo.titleLength > relStartPos)
              && (relStartPos >= lineInfo.relStartPos)
              && (relStartPos < lineInfoRelEndPos);
          if (drawURL) {
            nextHitUrlInfoPos = i + 1;
            break;
          }
        }

        if (!drawURL) {
          break;
        }

        //int numHitUrlsAlready = urlInfo.hitAreas == null ? 0 : urlInfo.hitAreas.size();

        // draw text before url
        int i = lineStartPos + urlInfo.relStartPos - relStartPos;
        //System.out.println("numHitUrlsAlready = " + numHitUrlsAlready + ";i=" + i);
        if (i > 0 && i > lineStartPos && i <= text.length()) {
          String s = text.substring(lineStartPos, i);
          //gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_RED));
          x0 += drawText(gc, s, x0, y0, lineInfo.height, null, noDraw, true).x;

          relStartPos += (i - lineStartPos);
          lineStartPos += (i - lineStartPos);
          //System.out.println("|" + s + "|" + textExtent.x);
        }

        // draw url text
        int end = i + urlInfo.titleLength;
        if (i < 0) {
          i = 0;
        }
        //System.out.println("end=" + end + ";" + text.length() + ";titlelen=" + urlInfo.titleLength);
        if (end > text.length()) {
          end = text.length();
        }
        String s = text.substring(i, end);
        relStartPos += (end - i);
        lineStartPos += (end - i);
        Point pt = null;
        //System.out.println("|" + s + "|");
        Color fgColor = null;
        if (!noDraw) {
          fgColor = gc.getForeground();

          if (urlInfo.dropShadowColor != null) {
            gc.setForeground(urlInfo.dropShadowColor);
            drawText(gc, s, x0 + 1, y0 + 1, lineInfo.height, null, noDraw,
                false);
          }

          if (urlInfo.urlColor != null) {
            gc.setForeground(urlInfo.urlColor);
          } else if (urlColor != null) {
            gc.setForeground(urlColor);
          }
        }
        if (urlInfo.hitAreas == null) {
          urlInfo.hitAreas = new ArrayList<Rectangle>(1);
        }
        pt = drawText(gc, s, x0, y0, lineInfo.height, urlInfo.hitAreas, noDraw,
            true);
        if (!noDraw) {
          if (urlInfo.urlUnderline) {
            gc.drawLine(x0, y0 + pt.y - 1, x0 + pt.x - 1, y0 + pt.y - 1);
          }
          gc.setForeground(fgColor);
        }

        if (urlInfo.hitAreas == null) {
          urlInfo.hitAreas = new ArrayList<Rectangle>(1);
        }
        //gc.drawRectangle(new Rectangle(x0, y0, pt.x, lineInfo.height));

        x0 += pt.x;
      }
    }

    // draw text after url
    if (lineStartPos < text.length()) {
      String s = text.substring(lineStartPos);
      if (!noDraw) {
        drawText(gc, s, x0, y0, lineInfo.height, null, noDraw, false);
      }
    }
    printArea.y += drawSize.y;
  }
 
  private Point drawText(GC gc, String s, int x, int y, int height,
      List<Rectangle> hitAreas, boolean nodraw, boolean calcExtent) {
    Point textExtent;

    if (images != null) {
      int pctPos = s.indexOf('%');
      int lastPos = 0;
      int w = 0;
      int h = 0;
      while (pctPos >= 0) {
        if (pctPos >= 0 && s.length() > pctPos + 1) {
          int imgIdx = s.charAt(pctPos + 1) - '0';
         
          if (imgIdx >= images.length || imgIdx < 0 || images[imgIdx] == null) {
            String sStart = s.substring(lastPos, pctPos + 1);
            textExtent = gc.textExtent(sStart);
            int centerY = y + (height / 2 - textExtent.y / 2);
            if (hitAreas != null) {
              hitAreas.add(new Rectangle(x, centerY, textExtent.x, textExtent.y));
            }
            if (!nodraw) {
              gc.drawText(sStart, x, centerY, true);
            }
            x += textExtent.x;
            w += textExtent.x;
            h = Math.max(h, textExtent.y);

            lastPos = pctPos + 1;
            pctPos = s.indexOf('%', pctPos + 1);
            continue;
          }
         
          String sStart = s.substring(lastPos, pctPos);
          textExtent = gc.textExtent(sStart);
          int centerY = y + (height / 2 - textExtent.y / 2);
          if (!nodraw) {
            gc.drawText(sStart, x, centerY, true);
          }
          x += textExtent.x;
          w += textExtent.x;
          h = Math.max(h, textExtent.y);
          if (hitAreas != null) {
            hitAreas.add(new Rectangle(x, centerY, textExtent.x, textExtent.y));
          }

          //System.out.println("drawimage: " + x + "x" + y + ";idx=" + imgIdx);
          Rectangle imgBounds = images[imgIdx].getBounds();
          float scale = 1.0f;
          if (imageScales != null && imageScales.length > imgIdx) {
            scale = imageScales[imgIdx];
          }
          int scaleImageWidth = (int) (imgBounds.width * scale);
          int scaleImageHeight = (int) (imgBounds.height * scale);

         
          centerY = y + (height / 2 - scaleImageHeight / 2);
          if (hitAreas != null) {
            hitAreas.add(new Rectangle(x, centerY, scaleImageWidth, scaleImageHeight));
          }
          if (!nodraw) {
            //gc.drawImage(images[imgIdx], x, centerY);
            gc.drawImage(images[imgIdx], 0, 0, imgBounds.width,
                imgBounds.height, x, centerY, scaleImageWidth, scaleImageHeight);
          }
          x += scaleImageWidth;
          w += scaleImageWidth;
         
          h = Math.max(h, scaleImageHeight);
        }
        lastPos = pctPos + 2;
        pctPos = s.indexOf('%', lastPos);
      }

      if (s.length() >= lastPos) {
        String sEnd = s.substring(lastPos);
        textExtent = gc.textExtent(sEnd);
        int centerY = y + (height / 2 - textExtent.y / 2);
        if (hitAreas != null) {
          hitAreas.add(new Rectangle(x, centerY, textExtent.x, textExtent.y));
        }
        if (!nodraw) {
          gc.drawText(sEnd, x, centerY, true);
        }
        x += textExtent.x;
        w += textExtent.x;
        h = Math.max(h, textExtent.y);
      }
      return new Point(w, h);
    }


    if (!nodraw) {
      gc.drawText(s, x, y, true);
    }
    if (!calcExtent && hitAreas == null) {
      return null;
    }
    textExtent = gc.textExtent(s);
    if (hitAreas != null) {
      hitAreas.add(new Rectangle(x, y, textExtent.x, textExtent.y));
    }
    return textExtent;
  }

  public static void main(String[] args) {

    //String s = "this is $1.00";
    //String s2 = "$1";
    //String s3 = s2.replaceAll("\\$", "\\\\\\$");
    //System.out.println(s3);
    //s.replaceAll("h", s3);
    //System.out.println(s);
    //if (true) {
    //  return;
    //}
   
    final Display display = Display.getDefault();
    final Shell shell = new Shell(display, SWT.SHELL_TRIM);

    ImageLoader imageLoader = ImageLoader.getInstance();
   
    final Image[] images = {
      imageLoader.getImage("azureus32"),
      imageLoader.getImage("azureus64"),
      imageLoader.getImage("azureus"),
      imageLoader.getImage("azureus128"),
    };

    //final String text = "Opil Wrir, Na Poys Iysk, Yann Only. test of the string printer averlongwordthisisyesindeed";
    final String text = "Apple <A HREF=\"aa\">Banana</a>, Cow <A HREF=\"ss\">Dug Ergo</a>, Flip Only. test of the string printer averlongwordthisisyesindeed " + Constants.INFINITY_STRING;
    //final String text = "Apple, Cow sfjkhsd %1 f, Flip Only. test of %0 the string printer averlongwordthisisyesindeed";

    shell.setSize(500, 500);

    GridLayout gridLayout = new GridLayout(2, false);
    shell.setLayout(gridLayout);

    Composite cButtons = new Composite(shell, SWT.NONE);
    GridData gridData = new GridData(SWT.NONE, SWT.FILL, false, true);
    cButtons.setLayoutData(gridData);
    final Canvas cPaint = new Canvas(shell, SWT.DOUBLE_BUFFERED);
    gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
    gridData.heightHint = 40;
    cPaint.setLayoutData(gridData);

    cButtons.setLayout(new RowLayout(SWT.VERTICAL));

    Listener l = new Listener() {
      public void handleEvent(Event event) {
        cPaint.redraw();
      }
    };

    final Text txtText = new Text(cButtons, SWT.WRAP | SWT.MULTI | SWT.BORDER);
    txtText.setText(text);
    txtText.addListener(SWT.Modify, l);
    txtText.setLayoutData(new RowData(100, 200));
    txtText.addKeyListener(new KeyListener() {
      public void keyReleased(KeyEvent e) {
      }

      public void keyPressed(KeyEvent e) {
        if (e.keyCode == 'a' && e.stateMask == SWT.CONTROL) {
          txtText.selectAll();
        }
      }
    });

    final Button btnSkipClip = new Button(cButtons, SWT.CHECK);
    btnSkipClip.setText("Skip Clip");
    btnSkipClip.setSelection(true);
    btnSkipClip.addListener(SWT.Selection, l);

    final Button btnFullOnly = new Button(cButtons, SWT.CHECK);
    btnFullOnly.setText("Full Lines Only");
    btnFullOnly.setSelection(true);
    btnFullOnly.addListener(SWT.Selection, l);

    final Combo cboVAlign = new Combo(cButtons, SWT.READ_ONLY);
    cboVAlign.add("Top");
    cboVAlign.add("Bottom");
    cboVAlign.add("None");
    cboVAlign.addListener(SWT.Selection, l);
    cboVAlign.select(0);

    final Combo cboHAlign = new Combo(cButtons, SWT.READ_ONLY);
    cboHAlign.add("Left");
    cboHAlign.add("Center");
    cboHAlign.add("Right");
    cboHAlign.add("None");
    cboHAlign.addListener(SWT.Selection, l);
    cboHAlign.select(0);

    final Button btnWrap = new Button(cButtons, SWT.CHECK);
    btnWrap.setText("Wrap");
    btnWrap.setSelection(true);
    btnWrap.addListener(SWT.Selection, l);
   
    final Button btnGCAdvanced = new Button(cButtons, SWT.CHECK);
    btnGCAdvanced.setText("gc.Advanced");
    btnGCAdvanced.setSelection(true);
    btnGCAdvanced.addListener(SWT.Selection, l);
   
    final Label lblInfo = new Label(shell, SWT.WRAP);
    lblInfo.setText("Welcome");
   

    Listener l2 = new Listener() {
      URLInfo lastHitInfo = null;

      public void handleEvent(Event event) {
        GC gc = event.gc;
        //System.out.println("HE" + event.type);
        boolean ourGC = gc == null;
        if (ourGC) {
          gc = new GC(cPaint);
        }
        try {
          gc.setAdvanced(btnGCAdvanced.getSelection());
          GCStringPrinter sp = buildSP(gc);
          Color colorURL = gc.getDevice().getSystemColor(SWT.COLOR_RED);
          Color colorURL2 = gc.getDevice().getSystemColor(
              SWT.COLOR_DARK_MAGENTA);

          if (event.type == SWT.MouseMove) {
            Point pt = cPaint.toControl(display.getCursorLocation());
            URLInfo hitUrl = sp.getHitUrl(pt.x, pt.y);
            String url1 = hitUrl == null || hitUrl.url == null ? ""
                : hitUrl.url;
            String url2 = lastHitInfo == null || lastHitInfo.url == null ? ""
                : lastHitInfo.url;

            if (url1.equals(url2)) {
              return;
            }
            cPaint.redraw();
            lastHitInfo = hitUrl;
            return;
          }

          Rectangle bounds = cPaint.getClientArea();

          Color colorBox = gc.getDevice().getSystemColor(SWT.COLOR_YELLOW);
          Color colorText = gc.getDevice().getSystemColor(SWT.COLOR_BLACK);

          gc.setForeground(colorText);

          Point pt = cPaint.toControl(display.getCursorLocation());
          sp.setUrlColor(colorURL);
          URLInfo hitUrl = sp.getHitUrl(pt.x, pt.y);
          if (hitUrl != null) {
            shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_HAND));
            hitUrl.urlColor = colorURL2;
          } else {
            shell.setCursor(null);
          }
          boolean fit = sp.printString();
         
          lblInfo.setText(fit ? "fit" : "no fit");

          bounds.width--;
          bounds.height--;

          gc.setForeground(colorBox);
          gc.drawRectangle(bounds);

          //System.out.println("-         " + System.currentTimeMillis());

        } catch (Throwable t) {
          t.printStackTrace();

        } finally {
          if (ourGC) {
            gc.dispose();
          }
        }
      }

      private GCStringPrinter buildSP(GC gc) {
        //gc.setFont(Utils.getFontWithHeight(shell.getFont(), gc, 15));
        //gc.setTextAntialias(SWT.ON);
        Rectangle bounds = cPaint.getClientArea();

        int style = btnWrap.getSelection() ? SWT.WRAP : 0;
        if (cboVAlign.getSelectionIndex() == 0) {
          style |= SWT.TOP;
        } else if (cboVAlign.getSelectionIndex() == 1) {
          style |= SWT.BOTTOM;
        }

        if (cboHAlign.getSelectionIndex() == 0) {
          style |= SWT.LEFT;
        } else if (cboHAlign.getSelectionIndex() == 1) {
          style |= SWT.CENTER;
        } else if (cboHAlign.getSelectionIndex() == 2) {
          style |= SWT.RIGHT;
        }

        String text = txtText.getText();
        text = text.replaceAll("\r\n", "\n");
        GCStringPrinter sp = new GCStringPrinter(gc, text, bounds,
            btnSkipClip.getSelection(), btnFullOnly.getSelection(), style);
        sp.setImages(images);
        sp.calculateMetrics();

        return sp;
      }
    };
    cPaint.addListener(SWT.Paint, l2);
    cPaint.addListener(SWT.MouseMove, l2);

    shell.open();

    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
  }

  /**
   *
   */
  public GCStringPrinter(GC gc, String string, Rectangle printArea,
      boolean skipClip, boolean fullLinesOnly, int swtFlags) {
    this.gc = gc;
    this.string = string;
    this.printArea = printArea;
    this.swtFlags = swtFlags;

    printFlags = 0;
    if (skipClip) {
      printFlags |= FLAG_SKIPCLIP;
    }
    if (fullLinesOnly) {
      printFlags |= FLAG_FULLLINESONLY;
    }
  }

  public GCStringPrinter(GC gc, String string, Rectangle printArea,
      int printFlags, int swtFlags) {
    this.gc = gc;
    this.string = string;
    this.printArea = printArea;
    this.swtFlags = swtFlags;
    this.printFlags = printFlags;
  }

  public boolean printString() {
    return _printString();
  }

  public boolean printString(int printFlags) {
    int oldPrintFlags = this.printFlags;
    printFlags |= printFlags;
    boolean b = _printString();
    this.printFlags = oldPrintFlags;
    return b;
  }

  public void calculateMetrics() {
    int oldPrintFlags = printFlags;
    printFlags |= FLAG_NODRAW;
    _printString();
    printFlags = oldPrintFlags;
  }

  /**
   * @param rectangle
   *
   * @since 3.0.4.3
   */
  public void printString(GC gc, Rectangle rectangle, int swtFlags) {
    this.gc = gc;
    int printFlags = this.printFlags;
    if (printArea.width == rectangle.width) {
      printFlags |= FLAG_KEEP_URL_INFO;
    }
    printArea = rectangle;
    this.swtFlags = swtFlags;
    printString(printFlags);
  }

  public Point getCalculatedSize() {
    return size;
  }

  public Color getUrlColor() {
    return urlColor;
  }

  public void setUrlColor(Color urlColor) {
    this.urlColor = urlColor;
  }

  public URLInfo getHitUrl(int x, int y) {
    if (listUrlInfo == null || listUrlInfo.size() == 0) {
      return null;
    }
    for (URLInfo urlInfo : listUrlInfo) {
      if (urlInfo.hitAreas != null) {
        for (Rectangle r : urlInfo.hitAreas) {
          if (r.contains(x, y)) {
            return urlInfo;
          }
        }
      }
    }
    return null;
  }

  public URLInfo[] getHitUrlInfo() {
    if (listUrlInfo == null) {
      return new URLInfo[0];
    }
    return (URLInfo[]) listUrlInfo.toArray(new URLInfo[0]);
  }

  public boolean hasHitUrl() {
    return listUrlInfo != null && listUrlInfo.size() > 0;
  }

  public boolean isCutoff() {
    return cutoff;
  }
 
  public void setImages(Image[] images) {
    this.images = images;
  }

  public float[] getImageScales() {
    return imageScales;
  }

  public void setImageScales(float[] imageScales) {
    this.imageScales = imageScales;
  }

  /**
   * @return
   *
   * @since 4.0.0.1
   */
  public String getText() {
    return string;
  }

  /*
  private Point stringExtent(GC gc, String s) {
    Matcher m = patOver1000.matcher(s);
    //if (s.length() > MAX_LINE_LEN) {
    if (m.find()) {
      System.out.println(s.length() + "\n" + Debug.getStackTrace(false, false));
      System.out.println(s);
    }
    return gc.stringExtent(s);
  }

  private Point textExtent(GC gc, String s) {
    Matcher m = patOver1000.matcher(s);
    //if (s.length() > MAX_LINE_LEN) {
    if (m.find()) {
      System.out.println(s.length() + "\n" + Debug.getStackTrace(false, false));
      System.out.println(s);
    }
    return gc.textExtent(s);
  }
  */
TOP

Related Classes of org.gudy.azureus2.ui.swt.shells.GCStringPrinter$URLInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
y>