AnnoManager.java :  » Development » java-djvu-0.8.09 » com » lizardtech » djvubean » anno » Java Open Source

Java Open Source » Development » java djvu 0.8.09 
java djvu 0.8.09 » com » lizardtech » djvubean » anno » AnnoManager.java
//C- -------------------------------------------------------------------
//C- Java DjVu (r) (v. 0.8)
//C- Copyright (c) 2004-2005 LizardTech, Inc.  All Rights Reserved.
//C- Java DjVu is protected by U.S. Pat. No.C- 6,058,214 and patents
//C- pending.
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- The computer code originally released by LizardTech under this
//C- license and unmodified by other parties is deemed "the LIZARDTECH
//C- ORIGINAL CODE."  Subject to any third party intellectual property
//C- claims, LizardTech grants recipient a worldwide, royalty-free,
//C- non-exclusive license to make, use, sell, or otherwise dispose of
//C- the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- General Public License.   This grant only confers the right to
//C- infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- the extent such infringement is reasonably necessary to enable
//C- recipient to make, have made, practice, sell, or otherwise dispose
//C- of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- any greater extent that may be necessary to utilize further
//C- modifications or combinations.
//C-
//C- The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C-
//C- In addition, as a special exception, LizardTech Inc. gives permission
//C- to link the code of this program with the proprietary Java
//C- implementation provided by Sun (or other vendors as well), and
//C- distribute linked combinations including the two. You must obey the
//C- GNU General Public License in all respects for all of the code used
//C- other than the proprietary Java implementation. If you modify this
//C- file, you may extend this exception to your version of the file, but
//C- you are not obligated to do so. If you do not wish to do so, delete
//C- this exception statement from your version.
//C- -------------------------------------------------------------------
//C- Developed by Bill C. Riemers, Foxtrot Technologies Inc. as work for
//C- hire under US copyright laws.
//C- -------------------------------------------------------------------
//
package com.lizardtech.djvubean.anno;

import com.lizardtech.djvubean.*;
import com.lizardtech.djvu.*;
import com.lizardtech.djvu.anno.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;


/**
 *
 * @author docbill
 */
public class AnnoManager 
  extends MouseAdapter
  implements MouseMotionListener
{
  //~ Static fields/initializers ---------------------------------------------

  //~ Instance fields --------------------------------------------------------

  // position used for text searches

  /** DjVuBean to use. */
  protected final DjVuBean djvuBean;

  private Vector [] map_area = null;
  
  private DjVuImage image = null;
  
  private Rect mapRect = null;
  private int mapRectIndex=-1;
  
  //~ Constructors -----------------------------------------------------------

  /**
   * Creates a new AnnoManager object.
   * 
   * @param bean DjVuBean to use.
   */
  public AnnoManager(final DjVuBean bean)
  {
    djvuBean = bean;
    bean.properties.put(
      "addOn.anno",
      AnnoManager.class.getName());
    djvuBean.addMouseListener(this);
    djvuBean.addMouseMotionListener(this);
  }
 
  //~ Methods ----------------------------------------------------------------
 
  /**
   * Set the MapArea which contains the specified coordinates.
   *
   * @param x position along the X axis.
   * @param y position along the Y axis.
   */
  private void setMapRect(
    int x,
    int y)
  {
    Vector map_area=getMapArea(0);
    final DjVuImage image=this.image;
    if(image != null)
    {
      final Dimension borderNW=image.getBorderNW();
      final int jmax=image.getIndexMax();
      final double sx=1D/image.getHorizontalScale();
      final double sy=1D/image.getVerticalScale();
      for(int j=0;;)
      {
        final Rectangle bounds=image.getPageBounds(j);
        if((bounds != null)&&(map_area != null) && (map_area.size() > 0))
        {
          final int x0=borderNW.width+bounds.x;
          final int y0=borderNW.height+bounds.y;
          final int x1=x0+bounds.width;
          final int y1=y0+bounds.height;
          if((x >= x0)&&(x < x1)&&(y >= y0)&&(y < y1))
          {
            final DjVuInfo info=image.getDjVuInfo(j);
            x   = (int)(((double)(x - x0) * (double)info.dpi)*sx);
            y   = info.height
              - (int)(((double)(y - y0) * (double)info.dpi)*sy);

            for(int i = map_area.size(); i-- > 0;)
            {
              final Rect rect = (Rect)map_area.elementAt(i);
              if(rect.contains(x, y))
              {
                setMapRect(j, rect);
                return;
              }
            }
          }
        }
        if(++j >= jmax)
        {
          break;
        }
        map_area = getMapArea(j);           
      }
    }

    setMapRect(-1, null);
  }

  /**
   * Query the vector of GRectMap objects.
   *
   * @return vector of GRectMap objects.
   */
  public Vector getMapArea(final int index)
  {
    DjVuImage image=djvuBean.getImage();
    if(image != this.image)
    {
      this.image=image;
      map_area=null;
      mapRect=null;
    }
    Vector [] retval = map_area;
    if((retval == null)&&(image != null))
    {
      final int jmax=image.getIndexMax();
      retval=new Vector[jmax];
      for(int j=0;j<jmax;j++)
      {
        retval[j]=null;
        final Rectangle bounds=image.getPageBounds(j);
        if(bounds != null)
        {
          final DjVuInfo info=image.getDjVuInfo(j);
          final DjVuPage page=image.getDjVuPage(j);
          final DjVuAnno anno = (DjVuAnno)page.waitForCodec(page.annoLock, 0L);
          if(anno != null)
          {
            final Vector v=anno.getMapArea();
            for(int i = v.size(); i-- > 0;)
            {
              final Rect rect = (Rect)v.elementAt(i);
              rect.setPageSize(
                info.width,
                info.height,
                bounds.width,
                bounds.height);
            }
            retval[j]=v;
          }
        }
        map_area = retval;
      }
    }
    return retval[index];
  }

  // This routine switches the current color to the specified value.  Returns true if
  // successfull.
  private boolean switchColor(Graphics g,int color)
  {
    boolean retval=false;
    if(g!=null)
    {
        switch(color)
        {
          case Rect.NO_HILITE:
          {
            g.setColor(DjVuImage.BLACK);
            g.setPaintMode();
            break;
          }
          case Rect.XOR_HILITE:
          {
            g.setColor(DjVuImage.BLACK);
            if(DjVuImage.BROKEN_XOR)
            {
              g.setPaintMode();
            }
            else
            {
              g.setXORMode(DjVuImage.WHITE);
            }
            retval=true;
            break;
          }
          default:
          {
            g.setColor(new Color(color));
            g.setPaintMode();
            retval=true;
            break;
          }
        }
    }
    return retval;
  }
  
  // This routine drows a line with the specified thickness an an optional arrow head.
  //
  private void drawLine(Graphics g,int x0,int y0,int x1,int y1,int weight,boolean arrow)
  {
    double dx=x1-x0;
    double dy=y1-y0;
    final double ds=Math.sqrt(dx*dx+dy*dy);
    if(weight <= 1 || (ds < 4D))
    {
      g.drawLine(x0,y0,x1,y1);
      if(arrow)
      {
        if(ds > 4D)
        {
          dx*=4D/ds;
          dy*=4D/ds;
        }
        g.drawLine(x1, y1, x1-(int)(dx+dy), y1-(int)(dy-dx));
        g.drawLine(x1, y1, x1-(int)(dx-dy), y1-(int)(dy+dx));
      }
    }
    else
    {
      final double r=((double)weight)/(2D*ds);
      final double xx0=x0-r*dy;
      final double yy0=y0+r*dx;
      final double xx1=x1-r*dy;
      final double yy1=y1+r*dx;
      final double xx2=x1+r*dy;
      final double yy2=y1-r*dx;
      final double xx3=x0+r*dy;
      final double yy3=y0-r*dx;
      if(arrow)
      {
        final double xx01=xx1-4D*r*dx;
        final double yy01=yy1-4D*r*dy;
        final double xx02=xx01-4D*(dx+dy)/ds;
        final double yy02=yy01-4D*(dy-dx)/ds;
        double dxx=xx02-xx01;
        double dyy=yy02-yy01;
        double dss=Math.sqrt(dxx*dxx+dyy*dyy);
        double rr=((double)weight)/dss;
        final double xx03=xx02+rr*dyy;
        final double yy03=yy02-rr*dxx;
        final double xx07=xx2-4D*r*dx;
        final double yy07=yy2-4D*r*dy;
        final double xx06=xx07-4D*(dx-dy)/ds;
        final double yy06=yy07-4D*(dy+dx)/ds;
        dxx=xx06-xx07;
        dyy=yy06-yy07;
        dss=Math.sqrt(dxx*dxx+dyy*dyy);
        rr=((double)weight)/dss;
        final double xx05=xx06-rr*dyy;
        final double yy05=yy06+rr*dxx;
        final int xx[]={(int)xx0,(int)xx01,(int)xx02,(int)xx03,x1,(int)xx05,(int)xx06,(int)xx07,(int)xx3};
        final int yy[]={(int)yy0,(int)yy01,(int)yy02,(int)yy03,y1,(int)yy05,(int)yy06,(int)yy07,(int)yy3};
        g.fillPolygon(xx,yy,xx.length);
      }
      else
      {
        final int xx[]={(int)xx0,(int)xx1,(int)xx2,(int)xx3};
        final int yy[]={(int)yy0,(int)yy1,(int)yy2,(int)yy3};
        g.fillPolygon(xx,yy,xx.length);
      }
    }
  }
  
  /**
   * Called to draw the Rects.
   *
   * @param g graphics area to draw with.
   */
  public void draw(Graphics g)
  {
    Vector map_area = getMapArea(0);
    final DjVuImage image=this.image;
    if(image != null)
    {
      final int jmax=image.getIndexMax();
      for(int j=0;;)
      {
        final Rectangle bounds=image.getPageBounds(j);
        if((map_area != null)&&(bounds!= null))
        {
          final Graphics xg = g.create();
          final Dimension borderNW=image.getBorderNW();
          xg.translate(borderNW.width+bounds.x, borderNW.height+bounds.y);
          xg.setColor(DjVuImage.BLUE);

          for(int i = map_area.size(); i-- > 0;)
          {
            final Rect rect = (Rect)map_area.elementAt(i);

            final int[] xx = rect.getXCoordinates();
            final int[] yy = rect.getYCoordinates();
            int xmin=0,ymin=0,xmax=0,ymax=0;
            if(xx.length == 2)
            {
              xmin = (xx[0] < xx[1])?xx[0]:xx[1];
              xmax = (xx[0] > xx[1])?xx[0]:xx[1];
              ymin = (yy[0] < yy[1])?yy[0]:yy[1];
              ymax = (yy[0] > yy[1])?yy[0]:yy[1];
//              drawRect(xg,rect.getHiliteColor(),rect.getOpacity(),xmin, ymin, xmax, ymax);
              image.fillRect(xg,rect.getHiliteColor(),rect.getOpacity(),xmin, ymin, xmax, ymax);
            }
      
            if(rect.isVisible())
            {
              if(xx.length == 2)
              {
//              drawRect(xg, rect.getBgColor(),100,xmin,ymin,xmax,ymax);
                image.fillRect(xg,rect.getBgColor(),100,xmin, ymin, xmax, ymax);
              }
              final int border_type = rect.getBorderType();
              if(border_type != Rect.NO_BORDER)
              {
                switch(border_type)
                {
                  case Rect.SOLID_BORDER :
                  {
                    switchColor(xg,rect.getBorderColor());
                    break;
                  }
                  case Rect.XOR_BORDER :default :
                  {
                    switchColor(xg,Rect.XOR_HILITE);
                    break;
                  }
                }

                if(rect.getMapType() == Rect.MAP_POLY)
                {
                  if(((Poly)rect).isOpen())
                  {
                    xg.drawPolyline(xx, yy, xx.length);                                
                  }
                  else
                  {
                    xg.drawPolygon(xx, yy, xx.length);                
                  }
                }
                else if(rect.getMapType() == Rect.MAP_OVAL)
                {
                  xg.drawOval(xmin, ymin, xmax - xmin, ymax - ymin);
                }
                else
                {
                  xg.drawRect(xmin, ymin, xmax - xmin, ymax - ymin);
                }
              }
              if((xx.length == 2)&& rect.isPushpin())
              {
                xg.setColor(DjVuImage.BLACK);
                xg.setPaintMode();
                drawLine(xg,xmin+1, ymin+1, xmin+8, ymin+8,3,false);
                drawLine(xg,xmin, ymin+1, xmin+6, ymin+1,3,false);
                drawLine(xg,xmin+1, ymin+1, xmin+1, ymin+6,3,false);
              }
              if(rect.getMapType() == Rect.MAP_TEXT)
              {
                String text=rect.getComment();
                if((text != null)&&switchColor(xg,rect.getTextColor()))
                {
                  final java.awt.FontMetrics fm=xg.getFontMetrics();
                  int y0=ymin+fm.getHeight()+(rect.isPushpin()?10:0);
                  final char [] ch=text.toCharArray();
                  for(int off=0;(off < ch.length)&&(y0<ymax);)
                  {
                    int x0=xmin+4;
                    int x1=xmin+fm.charWidth(ch[off]);
                    int end=off+1;
                    int lastSpace=off;
                    for(;end < ch.length;end++)
                    {
                      char c=ch[end];
                      x1+=fm.charWidth(c);
                      if(Character.isWhitespace(c))
                      {
                        lastSpace=end;
                      }
                      if(x1 > xmax-4)
                      {
                        break;
                      }
                    }
                    if((end < ch.length)&&(lastSpace > off))
                    {
                      end=lastSpace+1;
                    }
                    xg.drawChars(ch, off, end-off, x0, y0);
                    y0+=fm.getHeight();
                    off=end;
                  }
                }
              }
            }
            else if(xx.length == 2 && rect.isPushpin())
            {
              xg.setColor(DjVuImage.BLACK);
              xg.setPaintMode();
              drawLine(xg,xmin+8, ymin+8, xmin+1, ymin+1,3,false);
              drawLine(xg,xmin+9, ymin+8, xmin+3, ymin+8,3,false);
              drawLine(xg,xmin+8, ymin+9, xmin+8, ymin+3,3,false);
            }
            if((rect.getMapType() == Rect.MAP_LINE)&&switchColor(xg,rect.getLineColor()))
            {
              drawLine(xg,xx[0], yy[0], xx[1], yy[1],rect.getLineWidth(),rect.isArrow());
            }
          }
          xg.dispose();             
        }
        if(++j >= jmax)
        {
          break;
        }
        map_area=getMapArea(j);
      }
    }
  }
  
  /**
   * Obtain the current Rect, normally set by the mouse listener as the
   * hyperlink the mouse is currently over.
   *
   * @return the current Rect.
   */
  public Rect getMapRect()
  {
    return mapRect;
  }

  /**
   * Set the current GMapRect, normally set by the mouse listener as the
   * hyperlink the mouse is currently over.
   *
   * @param mapRect the current Rect.
   */
  public void setMapRect(final int index,final Rect mapRect)
  {
    final Vector map_area = getMapArea(0);
    final DjVuImage image=this.image;
    if((image == null)||(map_area == null))
    {
      this.mapRect=null;
      return;
    }
    if(mapRect != this.mapRect)
    {
//      final long lockTime=System.currentTimeMillis();
      synchronized(this)
      {
        if(mapRect != this.mapRect)
        {
          Rectangle repaintBounds = null;

          if((this.mapRect != null) && (this.mapRect.isVisible())  && (!this.mapRect.isPushpin()))
          {
            this.mapRect.setVisible(false);
            repaintBounds = new Rectangle();
            image.transformRectangle(
              mapRectIndex,
              this.mapRect.getBounds(),
              repaintBounds);
          }

          this.mapRect = mapRect;
          mapRectIndex=index;
          
          if((mapRect != null) && (!mapRect.isPushpin())&&(!mapRect.isVisible()))
          {
            mapRect.setVisible(true);

            final Rectangle rect = new Rectangle();
            image.transformRectangle(
              index,
              mapRect.getBounds(),
              rect);

            if(repaintBounds != null)
            {
              repaintBounds.union(rect);
            }
            else
            {
              repaintBounds = rect;
            }
          }

          if(repaintBounds != null)
          {
            repaintBounds.setBounds(
              repaintBounds.x - 2,
              repaintBounds.y - 2,
              repaintBounds.width + 4,
              repaintBounds.height + 4);
            djvuBean.repaintImageCoordinates(repaintBounds);
//            djvuBean.repaint();
          }
        }
//        DjVuObject.checkLockTime(lockTime,10000);
      }
    }
  }

  /**
   * Submit the Rect if right clicked.
   *
   * @param event describing the mouse action.
   */
  public void mouseClicked(final MouseEvent event)
  {
    try
    {
      if((event.getModifiers() & ~InputEvent.BUTTON1_MASK) == 0)
      {
        final Rect rect = getMapRect();
        if(rect != null)
        {
          if(rect.isPushpin())
          {
            rect.setVisible(!rect.isVisible());
            final Rectangle repaintBounds = new Rectangle();
            image.transformRectangle(
              mapRectIndex,
              rect.getBounds(),
              repaintBounds);              
            repaintBounds.setBounds(
              repaintBounds.x - 2,
              repaintBounds.y - 2,
              repaintBounds.width + 4,
              repaintBounds.height + 4);
            djvuBean.repaintImageCoordinates(repaintBounds);
          }
          else
          {
            final String url = rect.getURL();

            if((url != null) && (url.length() > 0))
            {
              final int pageno = djvuBean.getDocument().getPageno(url);
              if(pageno >= 0)
              {
                djvuBean.setPage(pageno + 1);
              }
              else
              {
                djvuBean.setSubmit(getMapRect());
              }
            }
          }
        }
      }
    }
    catch(final Throwable exp)
    {
      exp.printStackTrace(DjVuOptions.err);
      System.gc();
    }
  }

  /**
   * Event indicating the mouse was dragged.  Performs no operation.
   *
   * @param event describing the mouse action.
   */
  public void mouseDragged(MouseEvent event) {}

  /**
   * Event indicating the mouse was moved.  Used to display Rects
   * that require the mouse to be over the Rect to display.
   *
   * @param event describing the mouse action.
   */
  public void mouseMoved(final MouseEvent event)
  {
    try
    {
      if(!djvuBean.isDecoding())
      {
        setMapRect(
            event.getX(),
            event.getY());
      }
    }
    catch(final Throwable ignored) {}
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.