Clover coverage report -
Coverage timestamp: Sun Apr 18 2004 21:32:30 EDT
file stats: LOC: 2,396   Methods: 75
NCLOC: 1,503   Classes: 6
 
 Source file Conditionals Statements Methods TOTAL
SHTMLEditorPane.java 16.7% 33.7% 44% 29.8%
coverage coverage
 1   
 /*
 2   
  * SimplyHTML, a word processor based on Java, HTML and CSS
 3   
  * Copyright (C) 2002 Ulrich Hilger
 4   
  *
 5   
  * This program is free software; you can redistribute it and/or
 6   
  * modify it under the terms of the GNU General Public License
 7   
  * as published by the Free Software Foundation; either version 2
 8   
  * of the License, or (at your option) any later version.
 9   
  *
 10   
  * This program is distributed in the hope that it will be useful,
 11   
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12   
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13   
  * GNU General Public License for more details.
 14   
  *
 15   
  * You should have received a copy of the GNU General Public License
 16   
  * along with this program; if not, write to the Free Software
 17   
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 18   
  */
 19   
 
 20   
 
 21   
 import java.awt.*;
 22   
 import javax.swing.SwingUtilities;
 23   
 import javax.swing.JComponent;
 24   
 import javax.swing.ActionMap;
 25   
 import javax.swing.InputMap;
 26   
 import javax.swing.JFrame;
 27   
 import java.awt.Graphics;
 28   
 import java.awt.Graphics2D;
 29   
 import java.awt.RenderingHints;
 30   
 import javax.swing.JEditorPane;
 31   
 import java.awt.Color;
 32   
 import java.awt.Component;
 33   
 import java.awt.Cursor;
 34   
 import java.awt.Dimension;
 35   
 import java.awt.datatransfer.Clipboard;
 36   
 import java.awt.datatransfer.ClipboardOwner;
 37   
 
 38   
 import java.awt.datatransfer.DataFlavor;
 39   
 import java.awt.datatransfer.Transferable;
 40   
 
 41   
 import java.awt.datatransfer.ClipboardOwner;
 42   
 import javax.swing.text.Document;
 43   
 import javax.swing.text.Caret;
 44   
 import javax.swing.text.*;
 45   
 
 46   
 import java.awt.dnd.DnDConstants;
 47   
 import java.awt.dnd.DragGestureEvent;
 48   
 import java.awt.dnd.DragGestureListener;
 49   
 
 50   
 import java.awt.dnd.DragSource;
 51   
 import java.awt.dnd.DragSourceDragEvent;
 52   
 import java.awt.dnd.DragSourceDropEvent;
 53   
 import java.awt.dnd.DragSourceEvent;
 54   
 import java.awt.dnd.DragSourceListener;
 55   
 import java.awt.dnd.DropTarget;
 56   
 import java.awt.dnd.DropTargetDragEvent;
 57   
 import java.awt.dnd.DropTargetDropEvent;
 58   
 import java.awt.dnd.DropTargetEvent;
 59   
 import java.awt.dnd.DropTargetListener;
 60   
 import java.awt.event.ActionEvent;
 61   
 import java.awt.event.InputEvent;
 62   
 import java.awt.event.KeyEvent;
 63   
 import java.awt.event.MouseAdapter;
 64   
 import java.awt.event.MouseEvent;
 65   
 import java.awt.image.BufferedImage;
 66   
 import java.io.File;
 67   
 import java.io.FileOutputStream;
 68   
 import java.io.IOException;
 69   
 import java.io.OutputStream;
 70   
 import java.io.StringReader;
 71   
 import java.io.StringWriter;
 72   
 import java.util.Vector;
 73   
 
 74   
 import javax.swing.AbstractAction;
 75   
 import javax.swing.ActionMap;
 76   
 import javax.swing.InputMap;
 77   
 import javax.swing.JComponent;
 78   
 import javax.swing.JEditorPane;
 79   
 import javax.swing.JTextPane;
 80   
 import javax.swing.KeyStroke;
 81   
 import javax.swing.text.AttributeSet;
 82   
 import javax.swing.text.BadLocationException;
 83   
 import javax.swing.text.Caret;
 84   
 import javax.swing.text.Element;
 85   
 import javax.swing.text.ElementIterator;
 86   
 
 87   
 
 88   
 import javax.swing.text.MutableAttributeSet;
 89   
 import javax.swing.text.SimpleAttributeSet;
 90   
 import javax.swing.text.html.CSS;
 91   
 import javax.swing.text.html.HTML;
 92   
 import javax.swing.plaf.basic.BasicTextUI;
 93   
 
 94   
 import com.sun.image.codec.jpeg.JPEGCodec;
 95   
 import com.sun.image.codec.jpeg.JPEGImageEncoder;
 96   
 
 97   
 import java.awt.print.*;
 98   
 
 99   
 
 100   
 /**
 101   
  * An editor pane for application SimplyHTML.
 102   
  *
 103   
  * <p>This is extending <code>JEditorPane</code> by cut and paste
 104   
  * and drag and drop for HTML text.
 105   
  * <code>JEditorPane</code> inherits cut and paste from <code>
 106   
  * JTextComponent</code> where handling for plain text is implemented only.
 107   
  * <code>JEditorPane</code> has no additional functionality to add cut
 108   
  * and paste for the various content types it supports
 109   
  * (such as 'text/html').</p>
 110   
  *
 111   
  * <p>In stage 4 support for caret movement inside tables and
 112   
  * table manipulation methods are added.</p>
 113   
  *
 114   
  * <p>In stage 6 support for list manipulation was added.</p>
 115   
  *
 116   
  * @author Ulrich Hilger
 117   
  * @author Light Development
 118   
  * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
 119   
  * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
 120   
  * @author published under the terms and conditions of the
 121   
  *      GNU General Public License,
 122   
  *      for details see file gpl.txt in the distribution
 123   
  *      package of this software
 124   
  *
 125   
  * @version stage 11, April 27, 2003
 126   
  *
 127   
  * @see com.lightdev.app.shtm.HTMLText
 128   
  * @see com.lightdev.app.shtm.HTMLTextSelection
 129   
  */
 130   
 
 131   
 
 132   
 public class SHTMLEditorPane extends JEditorPane implements
 133   
     DropTargetListener, DragSourceListener, DragGestureListener, Printable
 134   
 
 135   
 {
 136   
     
 137   
     public int currentPrintJobPages;
 138   
     boolean pagesSet = false;
 139   
     protected PrintView m_printView;
 140  2
   public int print(Graphics pg, PageFormat pageFormat,
 141   
    int pageIndex) throws PrinterException {
 142  2
     pg.translate((int)pageFormat.getImageableX(), 
 143   
       (int)pageFormat.getImageableY());
 144  2
     int wPage = (int)pageFormat.getImageableWidth();
 145  2
     int hPage = (int)pageFormat.getImageableHeight();
 146  2
     pg.setClip(0, 0, wPage, hPage);
 147   
 
 148   
     // Only do this once per print
 149  2
     if (m_printView == null) {
 150  1
       BasicTextUI btui = (BasicTextUI)this.getUI();
 151  1
       View root = btui.getRootView(this);
 152  1
       m_printView = new PrintView(
 153   
         this.getDocument().getDefaultRootElement(), 
 154   
         root, wPage, hPage);
 155   
     }
 156   
         
 157  2
     boolean bContinue = m_printView.paintPage(pg, 
 158   
       hPage, pageIndex);
 159  2
     System.gc();
 160   
         
 161  2
     if (bContinue)
 162  1
       return Printable.PAGE_EXISTS;
 163   
     else {
 164  1
       m_printView = null;
 165  1
       return Printable.NO_SUCH_PAGE;
 166   
     }
 167   
   }
 168   
   class PrintView extends BoxView
 169   
   {
 170   
     protected int m_firstOnPage = 0;
 171   
     protected int m_lastOnPage = 0;
 172   
     protected int m_pageIndex = 0;
 173   
 
 174  1
     public PrintView(Element elem, View root, int w, int h) {
 175  1
       super(elem, Y_AXIS);
 176  1
       setParent(root);
 177  1
       setSize(w, h);
 178  1
       layout(w, h);
 179   
     }
 180   
 
 181  2
     public boolean paintPage(Graphics g, int hPage, 
 182   
      int pageIndex) {
 183  2
       if (pageIndex > m_pageIndex) {
 184  1
         m_firstOnPage = m_lastOnPage + 1;
 185  1
         if (m_firstOnPage >= getViewCount())
 186  1
           return false;
 187  0
         m_pageIndex = pageIndex;
 188   
       }
 189  1
       int yMin = getOffset(Y_AXIS, m_firstOnPage);
 190  1
       int yMax = yMin + hPage;
 191  1
       Rectangle rc = new Rectangle();
 192   
 
 193  1
       for (int k = m_firstOnPage; k < getViewCount(); k++) {
 194  2
         rc.x = getOffset(X_AXIS, k);
 195  2
         rc.y = getOffset(Y_AXIS, k);
 196  2
         rc.width = getSpan(X_AXIS, k);
 197  2
         rc.height = getSpan(Y_AXIS, k);
 198  2
         if (rc.y+rc.height > yMax)
 199  0
           break;
 200  2
         m_lastOnPage = k;
 201  2
         rc.y -= yMin;
 202  2
         paintChild(g, rc, k);
 203   
       }
 204  1
       return true;
 205   
     }
 206   
   }
 207   
 
 208   
     
 209   
     DocumentPane dp;
 210   
     
 211   
   /**
 212   
    * construct a new <code>SHTMLEditorPane</code>
 213   
    */
 214  158
   public SHTMLEditorPane(DocumentPane dp) {
 215  158
     super();
 216  158
     this.dp = dp;
 217  158
     setCaretColor(Color.black);
 218   
 
 219   
 
 220   
     /**
 221   
      * set the cursor by adding
 222   
      * a MouseListener that allows to display a text cursor when the
 223   
      * mouse pointer enters the editor. For some reason
 224   
      * (probably someone knows why and could let me know...) the method
 225   
      * setCursor does not have the same effect.
 226   
      */
 227   
 
 228  158
     addMouseListener( 
 229   
       new MouseAdapter() {
 230  89
         public void mouseEntered(MouseEvent e) {
 231  89
           Component gp = getRootPane().getGlassPane();
 232  88
           gp.setCursor(textCursor);
 233  88
           gp.setVisible(true);
 234   
         }
 235  76
         public void mouseExited(MouseEvent e) {
 236  76
           Component gp = getRootPane().getGlassPane();
 237  76
           gp.setCursor(defaultCursor);
 238  76
           gp.setVisible(false);
 239   
         }
 240   
       }
 241   
     );
 242   
 
 243   
     /** implement customized caret movement */
 244  158
     adjustKeyBindings();
 245   
 
 246   
     /** init drag and drop */
 247  158
     initDnd();
 248   
   }
 249   
 
 250   
   
 251   
 
 252   
 
 253   
   /**
 254   
    * adjust the key bindings of the key map existing for this
 255   
    * editor pane to our needs (i.e. add actions to certain keys
 256   
    * such as tab/shift tab for caret movement inside tables, etc.)
 257   
    *
 258   
    * This method had to be redone for using InputMap / ActionMap
 259   
    * instead of Keymap.
 260   
    */
 261  158
   private void adjustKeyBindings() {
 262  158
     ActionMap myActionMap = new ActionMap();
 263  158
     InputMap myInputMap = new InputMap();
 264   
 
 265  158
     KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
 266  158
     myActionMap.put(FrmMain.nextTableCellAction, new NextTableCellAction(FrmMain.nextTableCellAction));
 267  158
     myInputMap.put(tab, FrmMain.nextTableCellAction);
 268   
 
 269  158
     KeyStroke shiftTab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK);
 270  158
     myActionMap.put(FrmMain.prevTableCellAction, new PrevTableCellAction(FrmMain.prevTableCellAction));
 271  158
     myInputMap.put(shiftTab,FrmMain.prevTableCellAction);
 272   
 
 273  158
     KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
 274  158
     myActionMap.put(newListItemAction, new NewListItemAction());
 275  158
     myInputMap.put(enter, newListItemAction);
 276   
 
 277  158
     myActionMap.setParent(getActionMap());
 278  158
     myInputMap.setParent(getInputMap());
 279  158
     setActionMap(myActionMap);
 280  158
     setInputMap(JComponent.WHEN_FOCUSED, myInputMap);
 281   
 
 282   
     /*
 283   
      implementation before 1.4.1
 284   
      -------------------------------------
 285   
 
 286   
     Keymap map = getKeymap();
 287   
     KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
 288   
     map.addActionForKeyStroke(tab, new NextTableCellAction(map.getAction(tab)));
 289   
     KeyStroke shiftTab = KeyStroke.getKeyStroke(
 290   
                             KeyEvent.VK_TAB, InputEvent.SHIFT_MASK);
 291   
     map.addActionForKeyStroke(shiftTab,
 292   
                     new PrevTableCellAction(map.getAction(shiftTab)));
 293   
     KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
 294   
     map.addActionForKeyStroke(enter,
 295   
                               new NewListItemAction(map.getAction(enter)));
 296   
     setKeymap(map);
 297   
     */
 298   
   }
 299   
 
 300   
   /* ------- list manipulation start ------------------- */
 301   
 
 302   
   /** start of block to remove */
 303   
   private int removeStart;
 304   
 
 305   
   /** end of block to remove */
 306   
   private int removeEnd;
 307   
 
 308   
   /**
 309   
    * apply a set of attributes to the list the caret is
 310   
    * currently in (if any)
 311   
    *
 312   
    * @param a  the set of attributes to apply
 313   
    */
 314  0
   public void applyListAttributes(AttributeSet a) {
 315  0
     SHTMLDocument doc = (SHTMLDocument) getDocument();
 316  0
     Element first = doc.getParagraphElement(getSelectionStart());
 317  0
     Element list = getListElement(first);
 318  0
     if(list != null) {
 319  0
       if(a.getAttributeCount() > 0) {
 320  0
         doc.addAttributes(list, a);
 321   
         /**
 322   
          * for some reason above code does not show the changed attributes
 323   
          * of the table, although the element really has them (maybe somebody
 324   
          * could let me know why...). Therefore we update the editor pane
 325   
          * contents comparably rude (any other more elegant alternatives
 326   
          * welcome!)
 327   
          *
 328   
          * --> found out why: the swing package does not render short hand
 329   
          *                    properties such as MARGIN or PADDING. When
 330   
          *                    contained in a document inside an AttributeSet
 331   
          *                    they already have to be split into MARGIN-TOP,
 332   
          *                    MARGIN-LEFT, etc.
 333   
          *                    adjusted AttributeComponents accordingly so
 334   
          *                    we don't need refresh anymore
 335   
          */
 336   
         //refresh();
 337   
       }
 338   
     }
 339   
   }
 340   
 
 341   
   /**
 342   
    * <code>Action</code> to create a new list item.
 343   
    */
 344   
   public class NewListItemAction extends AbstractAction {
 345   
 
 346   
     /** action to use when not inside a table */
 347   
     /* removed for changes in J2SE 1.4.1
 348   
     Action alternateAction;
 349   
     */
 350   
 
 351   
     /** construct a <code>NewListItemAction</code> */
 352  158
     public NewListItemAction() {
 353   
     }
 354   
 
 355   
     /**
 356   
      * construct a <code>NewListItemAction</code>
 357   
      *
 358   
      * @param altAction  the action to use when the caret
 359   
      *      is not inside a list
 360   
      */
 361   
     /* removed for changes in J2SE 1.4.1
 362   
     public NewListItemAction(Action altAction) {
 363   
       alternateAction = altAction;
 364   
     }
 365   
     */
 366   
 
 367   
     /**
 368   
      * create a new list item, when the caret is inside a list
 369   
      *
 370   
      * <p>The new item is created after the item at the caret position</p>
 371   
      */
 372  0
     public void actionPerformed(ActionEvent ae) {
 373  0
       try {
 374  0
         SHTMLDocument doc = (SHTMLDocument) getDocument();
 375  0
         Element elem = doc.getParagraphElement(getSelectionStart());
 376   
         // if we are in a list, create a new item
 377  0
         if(getListElement(elem) != null) {
 378  0
           String li = HTML.Tag.LI.toString();
 379  0
           String p = HTML.Tag.P.toString();
 380   
           // get LI having the caret
 381  0
           while(elem != null &&
 382   
                 !elem.getName().equalsIgnoreCase(li))
 383   
           {
 384  0
             elem = elem.getParentElement();
 385   
           }
 386   
           // if LI found, append new LI after the found one
 387  0
           if(elem != null) {
 388  0
             StringWriter sw = new StringWriter();
 389  0
             SHTMLWriter w = new SHTMLWriter(sw);
 390  0
             w.startTag(li, null);
 391  0
             w.startTag(p, null);
 392  0
             w.endTag(p);
 393  0
             w.endTag(li);
 394  0
             doc.insertAfterEnd(elem, sw.getBuffer().toString());
 395  0
             int newPos = elem.getEndOffset();
 396  0
             select(newPos, newPos);
 397   
           }
 398   
         }
 399   
         // we are not in a list, call alternate action
 400   
         else {
 401  0
           KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
 402  0
           Object key = getInputMap().getParent().get(enter);
 403  0
           if(key != null) {
 404  0
             getActionMap().getParent().get(key).actionPerformed(ae);
 405   
           }
 406   
 
 407   
           /* removed for changes in J2SE 1.4.1
 408   
           if(alternateAction != null) {
 409   
             alternateAction.actionPerformed(ae);
 410   
           }
 411   
           */
 412   
         }
 413   
       }
 414   
       catch(Exception e) {
 415  0
         Util.errMsg(null, e.getMessage(), e);
 416   
       }
 417   
     }
 418   
   }
 419   
 
 420   
   /**
 421   
    * toggle list formatting on or off for the currently
 422   
    * selected text portion.
 423   
    *
 424   
    * <p>Switches list display on for the given type, if the selection
 425   
    * contains parts not formatted as list or parts formatted as list
 426   
    * of another type.</p>
 427   
    *
 428   
    * <p>Switches list formatting off, if the selection contains
 429   
    * only parts formatted as list of the given type.</p>
 430   
    *
 431   
    * @param listTag  the list tag type to toggle on or off (UL or OL)
 432   
    * @param a  the attributes to use for the list to toggle to
 433   
    * @param forceOff  indicator for toggle operation. If true, possibly
 434   
    * exisiting list formatting inside the selected parts always is switched
 435   
    * off. If false, the method decides, if list formatting for the parts
 436   
    * inside the selection needs to be switched on or off.
 437   
    */
 438  2
   public void toggleList(String listTag, AttributeSet a, boolean forceOff) {
 439  2
     try {
 440  2
       boolean listOn = false;
 441  2
       Element parent;
 442  2
       Element elem;
 443  2
       SHTMLDocument doc = (SHTMLDocument) getDocument();
 444  2
       Element first = doc.getParagraphElement(getSelectionStart());
 445  2
       int oStart = getSelectionStart();
 446  2
       int oEnd = getSelectionEnd();
 447  2
       int start = first.getStartOffset();
 448  2
       int end = doc.getParagraphElement(oEnd).getEndOffset();
 449  2
       removeStart = start;
 450  2
       removeEnd = end;
 451  2
       Element list = getListElement(first);
 452  2
       if(list == null) {
 453  2
         parent = first.getParentElement();
 454   
       }
 455   
       else {
 456  0
         parent = list.getParentElement();
 457   
       }
 458  2
       StringWriter sw = new StringWriter();
 459  2
       if(forceOff) {
 460  0
         listOff(new SHTMLWriter(sw, doc), listTag, parent, start, end, first);
 461   
       }
 462   
       else {
 463  2
         if(switchOn(listTag, parent, start, end)) {
 464  2
           listOn(new SHTMLWriter(sw, doc), listTag, parent,
 465   
                  start, end, first, a);
 466   
         }
 467   
         else {
 468  0
           listOff(new SHTMLWriter(sw, doc), listTag, parent,
 469   
                   start, end, first);
 470   
         }
 471   
       }
 472  2
       StringBuffer newHTML = sw.getBuffer();
 473  2
       if(newHTML.length() > 0) {
 474   
         //System.out.println("newHTML=\r\n\r\n" + newHTML.toString());
 475  2
         if(parent.getName() != HTML.Tag.TD.toString()) {
 476  2
           select(removeStart, removeEnd);
 477  2
           replaceSelection("");
 478  2
           SHTMLEditorKit kit = (SHTMLEditorKit) getEditorKit();
 479  2
           kit.read(new StringReader(newHTML.toString()),
 480   
                    doc, getCaretPosition());
 481   
         }
 482   
         else {
 483  0
           doc.setInnerHTML(parent, newHTML.toString());
 484   
         }
 485  2
         if(oStart == oEnd) {
 486  2
           setCaretPosition(oStart);
 487   
         }
 488   
         else {
 489  0
           select(oStart, oEnd);
 490   
         }
 491  2
         requestFocus();
 492   
       }
 493   
     }
 494   
     catch(Exception e) {
 495  0
       Util.errMsg(null, e.getMessage(), e);
 496   
     }
 497   
   }
 498   
 
 499   
   /**
 500   
    * decide to switch on or off list formatting
 501   
    *
 502   
    * @param listTag  the tag name to sitch on or off
 503   
    * @param parent  the parent element of the selection
 504   
    * @param start  the start of the selection
 505   
    * @param end the end of the selection
 506   
    *
 507   
    * @return  true, if list formatting is to be switched on, false if not
 508   
    */
 509  2
   private boolean switchOn(String listTag, Element parent,
 510   
                               int start, int end)
 511   
   {
 512  2
     boolean listOn = false;
 513  2
     int i = 0;
 514  2
     int count = parent.getElementCount();
 515  2
     Element elem = parent.getElement(i);
 516  2
     int eStart;
 517  2
     int eEnd;
 518  2
     while(i < count && !listOn) {
 519  2
       eStart = elem.getStartOffset();
 520  2
       eEnd = elem.getEndOffset();
 521  2
       if(!elem.getName().equalsIgnoreCase(listTag)) {
 522  2
         if(((eStart > start) && (eStart < end)) ||
 523   
            ((eEnd > start) && (eEnd < end)) ||
 524   
            ((start >= eStart) && (end <= eEnd)))
 525   
         {
 526  2
           listOn = true;
 527   
         }
 528   
       }
 529  2
       i++;
 530  2
       if(i < count) {
 531  0
         elem = parent.getElement(i);
 532   
       }
 533   
     }
 534  2
     return listOn;
 535   
   }
 536   
 
 537   
   /**
 538   
    * switch OFF list formatting for a given block of elements.
 539   
    *
 540   
    * <p>switches off all list formatting inside the block for the
 541   
    * given tag.</p>
 542   
    *
 543   
    * <p>Splits lists if the selection covers only part of a list.</p>
 544   
    *
 545   
    * @param w  the SHTMLWriter to write to
 546   
    * @param tag  the list tag to switch list formitting off for (UL or OL)
 547   
    * @param parent  the parent element having the list
 548   
    * @param start  the start of the selected block
 549   
    * @param end  the end of the selected block
 550   
    * @param doc  the document containing the list
 551   
    * @param first  the first element in the block of elements to turn
 552   
    * list formatting off for
 553   
    */
 554  0
   private void listOff(SHTMLWriter w, String tag, Element parent,
 555   
                        int start, int end, Element first)
 556   
   {
 557  0
     int elemIndex;
 558  0
     int count;
 559  0
     Element elem;
 560  0
     String elemName;
 561  0
     String listName = null;
 562  0
     boolean inTag = false;
 563  0
     try {
 564  0
       removeStart = start;
 565  0
       removeEnd = end;
 566  0
       Element list = getListElement(first);
 567  0
       if(list == null) {
 568  0
         parent = first.getParentElement();
 569   
       }
 570   
       else {
 571  0
         parent = list.getParentElement();
 572   
       }
 573  0
       for(int i = 0; i < parent.getElementCount(); i++) {
 574  0
         elem = parent.getElement(i);
 575  0
         elemName = elem.getName();
 576  0
         if((elemName.equalsIgnoreCase(HTML.Tag.UL.toString()) ||
 577   
            elemName.equalsIgnoreCase(HTML.Tag.OL.toString())) &&
 578   
            (elem.getStartOffset() <= end) && (elem.getEndOffset() >= start))
 579   
         {
 580  0
           list = elem;
 581  0
           listName = elem.getName();
 582  0
           count = list.getElementCount();
 583  0
           if(removeStart > elem.getStartOffset()) {
 584  0
             removeStart = elem.getStartOffset();
 585   
           }
 586  0
           if(removeEnd < elem.getEndOffset()) {
 587  0
             removeEnd = elem.getEndOffset();
 588   
           }
 589   
 
 590  0
           elemIndex = 0;
 591  0
           elem = list.getElement(elemIndex);
 592  0
           while(elemIndex < count && elem.getStartOffset() < start) {
 593  0
             if(!inTag) {
 594  0
               inTag = true;
 595  0
               w.startTag(listName, null);
 596   
             }
 597  0
             w.write(elem);
 598  0
             elemIndex++;
 599  0
             if(elemIndex < count) {
 600  0
               elem = list.getElement(elemIndex);
 601   
             }
 602   
           }
 603  0
           if(inTag) {
 604  0
             inTag = false;
 605  0
             w.endTag(listName);
 606   
           }
 607   
 
 608  0
           while(elemIndex < count &&
 609   
                 elem.getStartOffset() >= start &&
 610   
                 elem.getEndOffset() <= end)
 611   
           {
 612  0
             w.writeChildElements(elem);
 613  0
             elemIndex++;
 614  0
             if(elemIndex < count) {
 615  0
               elem = list.getElement(elemIndex);
 616   
             }
 617   
           }
 618   
 
 619  0
           while(elemIndex < count && elem.getEndOffset() > end) {
 620  0
             if(!inTag) {
 621  0
               inTag = true;
 622  0
               w.startTag(listName, null);
 623   
             }
 624  0
             w.write(elem);
 625  0
             elemIndex++;
 626  0
             if(elemIndex < count) {
 627  0
               elem = list.getElement(elemIndex);
 628   
             }
 629   
           }
 630  0
           if(elemIndex >= count && inTag) {
 631  0
             inTag = false;
 632  0
             w.endTag(listName);
 633   
           }
 634   
         }
 635   
       }
 636   
     }
 637   
     catch(Exception e) {
 638  0
       Util.errMsg(null, e.getMessage(), e);
 639   
     }
 640   
   }
 641   
 
 642   
   /**
 643   
    * switch ON list formatting for a given block of elements.
 644   
    *
 645   
    * <p>Takes care of merging existing lists before, after and inside
 646   
    * respective element block.</p>
 647   
    *
 648   
    * <p>Working but ugly code, optimization welcome!</p>
 649   
    *
 650   
    * @param w  the SHTMLWriter to write to
 651   
    * @param tag  the list tag to switch the selection to (UL or OL)
 652   
    * @param parent  the parent element having the selection
 653   
    * @param start  the start of the selected block
 654   
    * @param end  the end of the selected block
 655   
    * @param doc  the document containing the selection
 656   
    * @param first  the first element of the block to switch
 657   
    * list formatting on for
 658   
    */
 659  2
   private void listOn(SHTMLWriter w, String tag, Element parent,
 660   
                       int start, int end, Element first, AttributeSet la)
 661   
   {
 662  2
     int k;
 663  2
     int count;
 664  2
     Element elem;
 665  2
     String elemName;
 666  2
     String listName = null;
 667  2
     int eStart;
 668  2
     int eEnd;
 669  2
     boolean bStarted = false;
 670  2
     boolean iStarted = false;
 671  2
     boolean aStarted = false;
 672  2
     boolean mergeList = false;
 673  2
     try {
 674  2
       removeStart = start;
 675  2
       removeEnd = end;
 676  2
       Element list = getListElement(first);
 677  2
       if(list == null) {
 678  2
         parent = first.getParentElement();
 679   
       }
 680   
       else {
 681  0
         parent = list.getParentElement();
 682   
       }
 683  2
       for(int i = 0; i < parent.getElementCount(); i++) {
 684   
         // for every element of the parent
 685  2
         elem = parent.getElement(i);
 686  2
         elemName = elem.getName();
 687  2
         eStart = elem.getStartOffset();
 688  2
         eEnd = elem.getEndOffset();
 689  2
         if(elemName.equalsIgnoreCase(HTML.Tag.UL.toString()) ||
 690   
            elemName.equalsIgnoreCase(HTML.Tag.OL.toString()))
 691   
         {
 692   
           // elem is a list
 693  0
           list = elem;
 694  0
           listName = elem.getName();
 695  0
           count = list.getElementCount();
 696  0
           if(eEnd == start && listName.equalsIgnoreCase(tag))
 697   
           {
 698   
             // a list of the same name directly before the selection
 699  0
             mergeList = true;
 700  0
             if(removeStart > eStart) {
 701  0
               removeStart = eStart;
 702   
             }
 703  0
             if(!bStarted) {
 704  0
               bStarted = true;
 705  0
               w.startTag(listName, list.getAttributes());
 706   
             }
 707  0
             k = 0;
 708  0
             elem = list.getElement(k);
 709  0
             while(k < count) {
 710  0
               w.write(elem);
 711  0
               k++;
 712  0
               if(k < count) {
 713  0
                 elem = list.getElement(k);
 714   
               }
 715   
             }
 716   
           }
 717  0
           else if(eStart < start && eEnd >= start) {
 718   
             // list starts outside the selection before
 719  0
             if(removeStart > eStart) {
 720  0
               removeStart = eStart;
 721   
             }
 722  0
             if(removeEnd < eEnd) {
 723  0
               removeEnd = eEnd;
 724   
             }
 725  0
             if(!bStarted) {
 726  0
               bStarted = true;
 727  0
               w.startTag(listName, list.getAttributes());
 728   
             }
 729  0
             k = 0;
 730  0
             elem = list.getElement(k);
 731  0
             while(k < count && elem.getStartOffset() < start) {
 732  0
               w.write(elem);
 733  0
               k++;
 734  0
               if(k < count) {
 735  0
                 elem = list.getElement(k);
 736   
               }
 737   
             }
 738  0
             while(k < count &&
 739   
                   elem.getStartOffset() >= start &&
 740   
                   elem.getEndOffset() <= end) {
 741  0
               if(bStarted) {
 742  0
                 bStarted = false;
 743  0
                 if(!listName.equalsIgnoreCase(tag)) {
 744  0
                   w.endTag(listName);
 745   
                 }
 746   
               }
 747  0
               if(!iStarted) {
 748  0
                 iStarted = true;
 749  0
                 if(!listName.equalsIgnoreCase(tag)) {
 750  0
                   w.startTag(tag, la);
 751   
                 }
 752   
               }
 753  0
               w.write(elem);
 754  0
               k++;
 755  0
               if(k < count) {
 756  0
                 elem = list.getElement(k);
 757   
               }
 758   
             }
 759  0
             while(k < count && elem.getEndOffset() > end) {
 760  0
               if(iStarted) {
 761  0
                 iStarted = false;
 762  0
                 if(!listName.equalsIgnoreCase(tag)) {
 763  0
                   w.endTag(tag);
 764   
                 }
 765   
               }
 766  0
               if(!aStarted) {
 767  0
                 aStarted = true;
 768  0
                 if(!listName.equalsIgnoreCase(tag)) {
 769  0
                   w.startTag(listName, list.getAttributes());
 770   
                 }
 771   
               }
 772  0
               w.write(elem);
 773  0
               k++;
 774  0
               if(k < count) {
 775  0
                 elem = list.getElement(k);
 776   
               }
 777   
             }
 778   
           }
 779  0
           else if(eStart >= start && eStart < end) {
 780   
             // list starts inside the selection
 781  0
             if(eEnd > removeEnd) {
 782  0
               removeEnd = eEnd;
 783   
             }
 784  0
             k = 0;
 785  0
             elem = list.getElement(k);
 786  0
             while(k < count &&
 787   
                   elem.getStartOffset() >= start &&
 788   
                   elem.getEndOffset() <= end) {
 789  0
               if(bStarted) {
 790  0
                 bStarted = false;
 791  0
                 if((!mergeList) && (!listName.equalsIgnoreCase(tag))) {
 792  0
                   w.endTag(listName);
 793   
                 }
 794   
               }
 795  0
               if(!iStarted) {
 796  0
                 iStarted = true;
 797  0
                 if((!mergeList) && (!listName.equalsIgnoreCase(tag))) {
 798  0
                   w.startTag(tag, la);
 799   
                 }
 800   
               }
 801  0
               w.write(elem);
 802  0
               k++;
 803  0
               if(k < count) {
 804  0
                 elem = list.getElement(k);
 805   
               }
 806   
             }
 807  0
             while(k < count && elem.getEndOffset() > end) {
 808  0
               if(iStarted) {
 809  0
                 iStarted = false;
 810  0
                 if(!listName.equalsIgnoreCase(tag)) {
 811  0
                   w.endTag(tag);
 812   
                 }
 813   
               }
 814  0
               if(!aStarted) {
 815  0
                 aStarted = true;
 816  0
                 if(!listName.equalsIgnoreCase(tag)) {
 817  0
                   w.startTag(listName, list.getAttributes());
 818   
                 }
 819   
               }
 820  0
               w.write(elem);
 821  0
               k++;
 822  0
               if(k < count) {
 823  0
                 elem = list.getElement(k);
 824   
               }
 825   
             }
 826   
           }
 827  0
           else if(eStart == end && listName.equalsIgnoreCase(tag)) {
 828   
             // list with same name directly after selection
 829  0
             if(removeEnd < eEnd) {
 830  0
               removeEnd = eEnd;
 831   
             }
 832  0
             k = 0;
 833  0
             elem = list.getElement(k);
 834  0
             while(k < count) {
 835  0
               w.write(elem);
 836  0
               k++;
 837  0
               if(k < count) {
 838  0
                 elem = list.getElement(k);
 839   
               }
 840   
             }
 841  0
             w.endTag(tag);
 842  0
             aStarted = false;
 843  0
             iStarted = false;
 844   
           }
 845   
         }
 846   
         else {
 847   
           // elem is not a list
 848  2
           list = null;
 849  2
           if(eStart >= start && eEnd <= end) {
 850   
             // element is inside the selection
 851  2
             if(!iStarted) {
 852  2
               iStarted = true;
 853  2
               if(!bStarted) {
 854  2
                 w.startTag(tag, la);
 855   
               }
 856   
               else {
 857  0
                 if(!tag.equalsIgnoreCase(listName)) {
 858  0
                   w.endTag(listName);
 859  0
                   bStarted = false;
 860  0
                   w.startTag(tag, la);
 861   
                 }
 862   
               }
 863   
             }
 864  2
             w.startTag(HTML.Tag.LI.toString(), null);
 865  2
             w.write(elem);
 866  2
             w.endTag(HTML.Tag.LI.toString());
 867   
           }
 868   
         }
 869   
       }
 870  2
       if(iStarted) {
 871  2
         iStarted = false;
 872  2
         if(list == null) {
 873  2
           w.endTag(tag);
 874   
         }
 875   
         else {
 876  0
           if(!aStarted) {
 877  0
             w.endTag(listName);
 878   
           }
 879   
         }
 880   
       }
 881  2
       if(aStarted) {
 882  0
         aStarted = false;
 883  0
         w.endTag(listName);
 884   
       }
 885   
     }
 886   
     catch(Exception e) {
 887  0
       Util.errMsg(null, e.getMessage(), e);
 888   
     }
 889   
   }
 890   
 
 891   
   /**
 892   
    * get the list element a given element is inside (if any).
 893   
    *
 894   
    * @param elem  the element to get the list element for
 895   
    *
 896   
    * @return the list element the given element is inside, or null, if
 897   
    * the given element is not inside a list
 898   
    */
 899  4
   private Element getListElement(Element elem) {
 900  4
     Element list = Util.findElementUp(HTML.Tag.UL.toString(), elem);
 901  4
     if(list == null) {
 902  4
       list = Util.findElementUp(HTML.Tag.OL.toString(), elem);
 903   
     }
 904  4
     return list;
 905   
   }
 906   
 
 907   
   /* ------- list manipulation end ------------------- */
 908   
 
 909   
   /* ------- table manipulation start ------------------ */
 910   
 
 911   
   /** range indicator for applying attributes to the current cell only */
 912   
   public static final int THIS_CELL = 0;
 913   
 
 914   
   /** range indicator for applying attributes to cells of the current column only */
 915   
   public static final int THIS_COLUMN = 1;
 916   
 
 917   
   /** range indicator for applying attributes to cells of the current row only */
 918   
   public static final int THIS_ROW = 2;
 919   
 
 920   
   /** range indicator for applying attributes to all cells */
 921   
   public static final int ALL_CELLS = 3;
 922   
 
 923   
   /** default table width */
 924   
   public static final String DEFAULT_TABLE_WIDTH = "80%";
 925   
 
 926   
   /** default vertical alignment */
 927   
   public static final String DEFAULT_VERTICAL_ALIGN = "top";
 928   
 
 929   
   /**
 930   
    * insert a table
 931   
    *
 932   
    * @param colCount the number of columns the new table shall have
 933   
    */
 934  2
   public void insertTable(int colCount) {
 935  2
     int start = getSelectionStart();
 936  2
     StringWriter sw = new StringWriter();
 937  2
     SHTMLWriter w = new SHTMLWriter(sw);
 938   
     // some needed constants
 939  2
     String table = HTML.Tag.TABLE.toString();
 940  2
     String tr = HTML.Tag.TR.toString();
 941  2
     String td = HTML.Tag.TD.toString();
 942  2
     String p = HTML.Tag.P.toString();
 943   
     // the attribute set to use for applying attributes to tags
 944  2
     SimpleAttributeSet set = new SimpleAttributeSet();
 945   
     // build table attribute
 946  2
     Util.styleSheet().addCSSAttribute(set, CSS.Attribute.WIDTH, DEFAULT_TABLE_WIDTH);
 947  2
     Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_STYLE, "solid");
 948  2
     Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_TOP_WIDTH, "0");
 949  2
     Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_RIGHT_WIDTH, "0");
 950  2
     Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_BOTTOM_WIDTH, "0");
 951  2
     Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_LEFT_WIDTH, "0");
 952  2
     set.addAttribute(HTML.Attribute.BORDER, "0");
 953  2
     try {
 954  2
       w.startTag(table, set);
 955   
       // start row tag
 956  2
       w.startTag(tr, null);
 957   
       // get width of each cell according to column count
 958  2
       String tdWidth = Integer.toString(100 / colCount);
 959   
       // build cell width attribute
 960  2
       Util.styleSheet().addCSSAttribute(set,
 961   
                         CSS.Attribute.WIDTH,
 962   
                         Integer.toString(100 / colCount) + Util.pct);
 963  2
       set.addAttribute(HTML.Attribute.VALIGN, DEFAULT_VERTICAL_ALIGN);
 964  2
       Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_TOP_WIDTH, "1");
 965  2
       Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_RIGHT_WIDTH, "1");
 966  2
       Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_BOTTOM_WIDTH, "1");
 967  2
       Util.styleSheet().addCSSAttribute(set, CSS.Attribute.BORDER_LEFT_WIDTH, "1");
 968  2
       SimpleAttributeSet pSet = new SimpleAttributeSet();
 969  2
       Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_TOP, "1");
 970  2
       Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_RIGHT, "1");
 971  2
       Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_BOTTOM, "1");
 972  2
       Util.styleSheet().addCSSAttribute(pSet, CSS.Attribute.MARGIN_LEFT, "1");
 973  2
       set.removeAttribute(HTML.Attribute.BORDER);
 974   
       // add cells
 975  2
       for(int i=0; i<colCount; i++) {
 976  6
         w.startTag(td, set);
 977  6
         w.startTag(p, pSet);
 978  6
         w.endTag(p);
 979  6
         w.endTag(td);
 980   
       }
 981   
       // end row and table tags
 982  2
       w.endTag(tr);
 983  2
       w.endTag(table);
 984   
       // read table html into document
 985  2
       SHTMLDocument doc = (SHTMLDocument) getDocument();
 986  2
       Element para = doc.getParagraphElement(getSelectionStart());
 987  2
       if(para != null) {
 988  2
         doc.insertAfterEnd(para, sw.getBuffer().toString());
 989   
       }
 990   
     }
 991   
     catch(Exception ex) {
 992  0
       Util.errMsg(null, ex.getMessage(), ex);
 993   
     }
 994  2
     select(start, start);
 995   
   }
 996   
 
 997   
   /**
 998   
    * apply a new anchor to the currently selected text
 999   
    *
 1000   
    * <p>If nothing is selected, this method does nothing</p>
 1001   
    *
 1002   
    * @param anchorName  the name of the new anchor
 1003   
    */
 1004  1
   public void insertAnchor(String anchorName) {
 1005  1
     if(getSelectionStart() != getSelectionEnd()) {
 1006  1
       String a = HTML.Tag.A.toString();
 1007  1
       String selectedText = getSelectedText();
 1008  1
       SimpleAttributeSet aSet = new SimpleAttributeSet();
 1009  1
       aSet.addAttribute(HTML.Attribute.NAME, anchorName);
 1010  1
       SimpleAttributeSet set = new SimpleAttributeSet();
 1011  1
       set.addAttribute(HTML.Tag.A, aSet);
 1012  1
       applyAttributes(set, false);
 1013   
     }
 1014   
   }
 1015   
 
 1016   
   /**
 1017   
    * insert a line break (i.e. a break for which paragraph
 1018   
    * spacing is not applied)
 1019   
    */
 1020  2
   public void insertBreak() {
 1021  2
     int caretPos = getCaretPosition();
 1022  2
     SHTMLDocument doc = (SHTMLDocument) getDocument();
 1023  2
     try {
 1024  2
       ((SHTMLEditorKit) getEditorKit()).insertHTML(
 1025   
           doc, caretPos, "<BR>", 0, 0, HTML.Tag.BR);
 1026   
     }
 1027   
     catch(Exception e) {}
 1028  2
     setCaretPosition(caretPos + 1);
 1029   
   }
 1030   
 
 1031   
   /**
 1032   
    * set a text link at the current selection replacing the selection
 1033   
    * with a given text.
 1034   
    *
 1035   
    * <p>If nothing is selected, but the caret is inside a link, this will
 1036   
    * replace the existing link. If nothing is selected and the caret
 1037   
    * is not inside a link, this method does nothing.</p>
 1038   
    *
 1039   
    * @param linkText  the text that shall appear as link at the current selection
 1040   
    * @param href  the target this link shall refer to
 1041   
    * @param className  the style class to be used
 1042   
    */
 1043  0
   public void setLink(String linkText, String href, String className) {
 1044  0
     setLink(linkText, href, className, null, null);
 1045   
   }
 1046   
 
 1047   
   /**
 1048   
    * set a link at the current selection replacing the selection
 1049   
    * with the given text or image.
 1050   
    *
 1051   
    * @param linkText  the text to show as link (or null, if an image shall appear instead)
 1052   
    * @param href  the link reference
 1053   
    * @param className  the style name to be used for the link
 1054   
    * @param linkImage  the file name of the image be used for the link (or null, if a text link is to be set instead)
 1055   
    * @param size  the size of the image or null
 1056   
    */
 1057  1
   public void setLink(String linkText, String href, String className, String linkImage, Dimension size) {
 1058  1
     SHTMLDocument doc = (SHTMLDocument) getDocument();
 1059  1
     Element e = Util.findLinkElementUp(doc.getCharacterElement(getSelectionStart()));
 1060  1
     if(linkImage == null) {
 1061  1
       setTextLink(e, href, className, linkText, doc);
 1062   
     }
 1063   
     else {
 1064  0
       setImageLink(doc, e, href, className, linkImage, size);
 1065   
     }
 1066   
   }
 1067   
 
 1068   
   /**
 1069   
    * set an image link replacing the current selection
 1070   
    *
 1071   
    * @param doc  the document to apply the link to
 1072   
    * @param e  the link element found at the selection, or null if none was found
 1073   
    * @param href  the link reference
 1074   
    * @param className  the style name to be used for the link
 1075   
    * @param linkImage  the file name of the image be used for the link
 1076   
    * @param size  the size of the image
 1077   
    */
 1078  0
   private void setImageLink(SHTMLDocument doc, Element e, String href, String className, String linkImage, Dimension size) {
 1079  0
     String a = HTML.Tag.A.toString();
 1080  0
     SimpleAttributeSet set = new SimpleAttributeSet();
 1081  0
     set.addAttribute(HTML.Attribute.HREF, href);
 1082  0
     set.addAttribute(HTML.Attribute.CLASS, className);
 1083  0
     StringWriter sw = new StringWriter();
 1084  0
     SHTMLWriter w = new SHTMLWriter(sw);
 1085  0
     try {
 1086  0
       w.startTag(a, set);
 1087  0
       set = new SimpleAttributeSet();
 1088  0
       set.addAttribute(HTML.Attribute.SRC,
 1089   
                        Util.getRelativePath(new File(doc.getBase().getFile()), new File(linkImage)));
 1090  0
       set.addAttribute(HTML.Attribute.BORDER, "0");
 1091  0
       if(size != null) {
 1092  0
         set.addAttribute(HTML.Attribute.WIDTH, Integer.toString(new Double(size.getWidth()).intValue()));
 1093  0
         set.addAttribute(HTML.Attribute.HEIGHT, Integer.toString(new Double(size.getHeight()).intValue()));
 1094   
       }
 1095  0
       w.startTag(HTML.Tag.IMG.toString(), set);
 1096  0
       w.endTag(a);
 1097  0
       if(e != null) {
 1098  0
         System.out.println("SHTMLEditorPane.setImageLink setOuterHTML html='" + sw.getBuffer() + "'");
 1099  0
         doc.setOuterHTML(e, sw.getBuffer().toString());
 1100   
       }
 1101   
       else {
 1102  0
         int start = getSelectionStart();
 1103  0
         if(start < getSelectionEnd()) {
 1104  0
           replaceSelection("");
 1105  0
           System.out.println("SHTMLEditorPane.setImageLink insertAfterEnd html='" + sw.getBuffer() + "'");
 1106  0
           doc.insertAfterEnd(doc.getCharacterElement(start), sw.getBuffer().toString());
 1107   
         }
 1108   
       }
 1109   
     }
 1110   
     catch(Exception ex) {
 1111  0
       Util.errMsg(this, ex.getMessage(), ex);
 1112   
     }
 1113   
   }
 1114   
 
 1115   
   /**
 1116   
    * set a text link replacing the current selection
 1117   
    *
 1118   
    * @param e  the link element found at the selection, or null if none was found
 1119   
    * @param href  the link reference
 1120   
    * @param className  the style name to be used for the link
 1121   
    * @param linkText  the text to show as link
 1122   
    * @param doc  the document to apply the link to
 1123   
    */
 1124  1
   private void setTextLink(Element e, String href, String className, String linkText, SHTMLDocument doc) {
 1125  1
     SimpleAttributeSet aSet = new SimpleAttributeSet();
 1126  1
     aSet.addAttribute(HTML.Attribute.HREF, href);
 1127  1
     String sStyleName = FrmMain.dynRes.getResourceString(FrmMain.resources, "standardStyleName");
 1128  1
     if(className != null && !className.equalsIgnoreCase(sStyleName)) {
 1129  0
       aSet.addAttribute(HTML.Attribute.CLASS, className);
 1130   
     }
 1131  1
     SimpleAttributeSet set = new SimpleAttributeSet();
 1132  1
     if(e != null) {
 1133   
       // replace existing link
 1134  0
       set.addAttributes(e.getAttributes());
 1135  0
       set.addAttribute(HTML.Tag.A, aSet);
 1136  0
       int start = e.getStartOffset();
 1137  0
       try {
 1138  0
         doc.replace(start, e.getEndOffset() - start, linkText, set);
 1139   
       }
 1140   
       catch(BadLocationException ex) {
 1141  0
         Util.errMsg(this, ex.getMessage(), ex);
 1142   
       }
 1143   
     }
 1144   
     else {
 1145   
       // create new link for text selection
 1146  1
       int start = getSelectionStart();
 1147  1
       if(start < getSelectionEnd()) {
 1148  1
         set.addAttribute(HTML.Tag.A, aSet);
 1149  1
         replaceSelection(linkText);
 1150  1
         doc.setCharacterAttributes(start, linkText.length(), set, false);
 1151   
       }
 1152   
     }
 1153   
   }
 1154   
 
 1155   
   /**
 1156   
    * remove an anchor with a given name
 1157   
    *
 1158   
    * @param anchorName  the name of the anchor to remove
 1159   
    */
 1160  0
   public void removeAnchor(String anchorName) {
 1161   
     //System.out.println("SHTMLEditorPane removeAnchor");
 1162  0
     String aTag = HTML.Tag.A.toString();
 1163  0
     AttributeSet attrs;
 1164  0
     Object nameAttr;
 1165  0
     Object link;
 1166  0
     ElementIterator eli = new ElementIterator(getDocument());
 1167  0
     Element elem = eli.first();
 1168  0
     while(elem != null) {
 1169  0
       attrs = elem.getAttributes();
 1170  0
       link = attrs.getAttribute(HTML.Tag.A);
 1171  0
       if(link != null /*&& link.toString().equalsIgnoreCase(HTML.Tag.A.toString())*/) {
 1172   
         //System.out.println("found anchor attribute");
 1173  0
         nameAttr = ((AttributeSet) link).getAttribute(HTML.Attribute.NAME);
 1174  0
         if(nameAttr != null && nameAttr.toString().equalsIgnoreCase(anchorName)) {
 1175   
           // remove anchor here
 1176   
           //System.out.println("removing anchor name=" + nameAttr);
 1177  0
           SimpleAttributeSet newSet = new SimpleAttributeSet(attrs);
 1178  0
           newSet.removeAttribute(HTML.Tag.A);
 1179  0
           SHTMLDocument doc = (SHTMLDocument) getDocument();
 1180  0
           int start = elem.getStartOffset();
 1181  0
           doc.setCharacterAttributes(elem.getStartOffset(), elem.getEndOffset() - start, newSet, true);
 1182   
         }
 1183   
       }
 1184  0
       elem = eli.next();
 1185   
     }
 1186   
   }
 1187   
 
 1188   
   /**
 1189   
    * insert a table column before the current column
 1190   
    * (if any)
 1191   
    */
 1192  1
   public void insertTableColumn() {
 1193  1
     Element cell = getCurTableCell();
 1194  1
     if(cell != null) {
 1195  1
       createTableColumn(cell, Util.getElementIndex(cell)/*getColNumber(cell)*/, true);
 1196   
     }
 1197   
   }
 1198   
 
 1199   
   /**
 1200   
    * append a table column after the last column
 1201   
    * (if any)
 1202   
    */
 1203  0
   public void appendTableColumn() {
 1204  0
     Element cell = getCurTableCell();
 1205  0
     if(cell != null) {
 1206  0
       Element lastCell = getLastTableCell(cell);
 1207  0
       createTableColumn(lastCell, Util.getElementIndex(cell)/*getColNumber(lastCell)*/, false);
 1208   
     }
 1209   
   }
 1210   
 
 1211   
   /**
 1212   
    * create a table column before or after a given column
 1213   
    *
 1214   
    * the width of the first cell in the column
 1215   
    * (if there is a width attribute) is split into
 1216   
    * half so that the new column and the column
 1217   
    * inserted before are sharing the space originally
 1218   
    * taken by the column inserted before.
 1219   
    *
 1220   
    * @param cell  the cell to copy from
 1221   
    * @param cIndex  the number of the column 'cell' is in
 1222   
    * @param before  true indicates insert before, false append after
 1223   
    */
 1224  1
   private void createTableColumn(Element cell, int cIndex, boolean before) {
 1225   
 
 1226   
     // get the new width setting for this column and the new column
 1227  1
     SHTMLDocument doc = (SHTMLDocument) getDocument();
 1228  1
     Element table = cell.getParentElement().getParentElement();
 1229  1
     Element srcCell = table.getElement(0).getElement(cIndex);
 1230  1
     SimpleAttributeSet set = new SimpleAttributeSet(srcCell.getAttributes());
 1231  1
     Object attr = set.getAttribute(CSS.Attribute.WIDTH);
 1232  1
     if(attr != null) {
 1233   
       //LengthValue lv = new LengthValue(attr);
 1234   
       //String unit = lv.getUnit();
 1235   
       //int width = (int) lv.getAttrValue(attr.toString(), unit);
 1236  1
       int width = (int) Util.getAbsoluteAttrVal(attr);  // Util.getAttrValue(attr);
 1237   
       //System.out.println("SHTMLEditorPane.createTableColumn width=" + width);
 1238  1
       String unit = Util.getLastAttrUnit();
 1239   
       //System.out.println("SHTMLEditorPane.createTableColumn unit=" + unit);
 1240  1
       String widthString = Integer.toString(width / 2) + unit;
 1241   
       //System.out.println("SHTMLEditorPane.createTableColumn widthString=" + widthString);
 1242  1
       Util.styleSheet().addCSSAttribute(set, CSS.Attribute.WIDTH,
 1243   
           widthString);
 1244   
     }
 1245   
 
 1246   
     // adjust width and insert new column
 1247   
     //SimpleAttributeSet a = new SimpleAttributeSet(set.copyAttributes());
 1248   
     //int cellIndex = getCellIndex(srcCell);
 1249   
     //boolean insertFirst = (before && (cellIndex == 0));
 1250  1
     for(int rIndex = 0; rIndex < table.getElementCount(); rIndex++) {
 1251  2
       srcCell = table.getElement(rIndex).getElement(cIndex);
 1252   
       /*
 1253   
       if(rIndex > 0) {
 1254   
         adjustBorder(a, a, a, CombinedAttribute.ATTR_TOP);
 1255   
       }
 1256   
       adjustBorder(a, a, a, CombinedAttribute.ATTR_LEFT);
 1257   
       */
 1258  2
       doc.addAttributes(srcCell, set);
 1259  2
       try {
 1260  2
         if(before) {
 1261  2
           doc.insertBeforeStart(srcCell, getTableCellHTML(srcCell));
 1262   
         }
 1263   
         else {
 1264  0
           doc.insertAfterEnd(srcCell, getTableCellHTML(srcCell));
 1265   
         }
 1266   
       }
 1267   
       catch(IOException ioe) {
 1268  0
         Util.errMsg(null, ioe.getMessage(), ioe);
 1269   
       }
 1270   
       catch(BadLocationException ble) {
 1271  0
         Util.errMsg(null, ble.getMessage(), ble);
 1272   
       }
 1273   
     }
 1274   
     //adjustColumnBorders(table.getElement(0).getElement(cIndex));
 1275   
     //adjustColumnBorders(table.getElement(0).getElement(++cIndex));
 1276   
   }
 1277   
 
 1278   
   /**
 1279   
    * append a row to a table assuming the caret currently
 1280   
    * is inside a table
 1281   
    */
 1282  0
   public void appendTableRow() {
 1283  0
     Element cell = getCurTableCell();
 1284  0
     if(cell != null) {
 1285  0
       Element table = cell.getParentElement().getParentElement();
 1286  0
       Element lastRow = table.getElement(table.getElementCount()-1);
 1287  0
       createTableRow(lastRow, Util.getRowIndex(lastRow.getElement(0)), false);
 1288   
     }
 1289   
   }
 1290   
 
 1291   
   /**
 1292   
    * insert a row to a table assuming the caret currently
 1293   
    * is inside a table
 1294   
    */
 1295  1
   public void insertTableRow() {
 1296  1
     Element cell = getCurTableCell();
 1297  1
     if(cell != null) {
 1298  1
       createTableRow(cell.getParentElement(), Util.getRowIndex(cell), true);
 1299   
     }
 1300   
   }
 1301   
 
 1302   
   /**
 1303   
    * create a new table row by either inserting it before a
 1304   
    * given row or appending it after a given row
 1305   
    *
 1306   
    * this method is shared by appendRow and insertRow
 1307   
    *
 1308   
    * @param srcRow  the row element to copy from
 1309   
    * @param before  true indicates insert before, false append after
 1310   
    */
 1311  1
   private void createTableRow(Element srcRow, int rowIndex, boolean before) {
 1312  1
     try {
 1313  1
       if(before) {
 1314  1
         ((SHTMLDocument) getDocument()).insertBeforeStart(srcRow,
 1315   
             getTableRowHTML(srcRow));
 1316  1
         if(rowIndex == 0) {
 1317  1
           rowIndex++;
 1318   
         }
 1319   
       }
 1320   
       else {
 1321  0
         ((SHTMLDocument) getDocument()).insertAfterEnd(srcRow,
 1322   
             getTableRowHTML(srcRow));
 1323  0
         rowIndex++;
 1324   
       }
 1325   
     }
 1326   
     catch(IOException ioe) {
 1327  0
       Util.errMsg(null, ioe.getMessage(), ioe);
 1328   
     }
 1329   
     catch(BadLocationException ble) {
 1330  0
       Util.errMsg(null, ble.getMessage(), ble);
 1331   
     }
 1332   
   }
 1333   
 
 1334   
   /**
 1335   
    * build an HTML string copying from an existing table row.
 1336   
    *
 1337   
    * For each table column found in srcRow a start and end
 1338   
    * tag TD is created with the same attributes as in the
 1339   
    * column found in srcRow. The attributes of srcRow
 1340   
    * are applied to the newly created row HTML string as well.
 1341   
    *
 1342   
    * @param srcRow  the table row Element to copy from
 1343   
    * @param insert  indicates if a row is inserted before another row
 1344   
    *
 1345   
    * @return an HTML string representing the new table row
 1346   
    * (without cell contents)
 1347   
    */
 1348  1
   public String getTableRowHTML(Element srcRow) {
 1349  1
     String tr = HTML.Tag.TR.toString();
 1350  1
     String td = HTML.Tag.TD.toString();
 1351  1
     String p = HTML.Tag.P.toString();
 1352  1
     StringWriter sw = new StringWriter();
 1353  1
     SHTMLWriter w = new SHTMLWriter(sw);
 1354  1
     try {
 1355  1
       w.startTag(tr, srcRow.getAttributes());
 1356  1
       for(int i = 0; i < srcRow.getElementCount(); i++) {
 1357  3
         w.startTag(td, srcRow.getElement(i).getAttributes());
 1358  3
         w.startTag(p, srcRow.getElement(i).getElement(0).getAttributes());
 1359  3
         w.endTag(p);
 1360  3
         w.endTag(td);
 1361   
       }
 1362  1
       w.endTag(tr);
 1363   
     }
 1364   
     catch(IOException ex) {
 1365  0
       Util.errMsg(null, ex.getMessage(), ex);
 1366   
     }
 1367  1
     return sw.getBuffer().toString();
 1368   
   }
 1369   
 
 1370   
   /**
 1371   
    * build an HTML string copying from an existing table cell
 1372   
    *
 1373   
    * @param srcCell the cell to get the HTML for
 1374   
    * @param a  set of attributes to copy if we are inserting first table column
 1375   
    * @param insertFirst  indicates if we are inserting first table column
 1376   
    * @param rNum  number of row a cell is to be inserted to
 1377   
    *     (can be any value if insertFirst is false)
 1378   
    *
 1379   
    * @return the HTML string for the given cell (without cell contents)
 1380   
    */
 1381  2
   public String getTableCellHTML(Element srcCell)
 1382   
   {
 1383  2
     StringWriter sw = new StringWriter();
 1384  2
     SHTMLWriter w = new SHTMLWriter(sw);
 1385  2
     String td = HTML.Tag.TD.toString();
 1386  2
     String p = HTML.Tag.P.toString();
 1387  2
     try {
 1388  2
       w.startTag(td, srcCell.getAttributes());
 1389  2
       w.startTag(p, null);
 1390  2
       w.endTag(p);
 1391  2
       w.endTag(td);
 1392   
     }
 1393   
     catch(IOException e) {
 1394  0
       Util.errMsg(null, e.getMessage(), e);
 1395   
     }
 1396   
     //System.out.println("getTableCellHTML buffer='" + sw.getBuffer().toString() + "'");
 1397  2
     return sw.getBuffer().toString();
 1398   
   }
 1399   
 
 1400   
   /**
 1401   
    * delete the row of the table the caret is currently in (if any)
 1402   
    */
 1403  0
   public void deleteTableRow() {
 1404  0
     Element cell = getCurTableCell();
 1405  0
     if(cell != null) {
 1406  0
       removeElement(cell.getParentElement());
 1407   
     }
 1408   
   }
 1409   
 
 1410   
   /**
 1411   
    * delete the column of the table the caret is currently in (if any)
 1412   
    *
 1413   
    * <p>width of adjacent column is adjusted, if there is more than one
 1414   
    * column in the table. Width adjustment only works, if width
 1415   
    * attributes of both the column to remove and its adjacent column
 1416   
    * have the same unit (pt or %).</p>
 1417   
    *
 1418   
    * <p>If there is only one cell or if the caret is not in a table,
 1419   
    * this method does nothing</p>
 1420   
    *
 1421   
    * <p>Smart border handling automatically sets the left border of a cell
 1422   
    * to zero, if the cell on the left of that cell has a right border and
 1423   
    * both cells have no margin. In that case removing the first column
 1424   
    * will cause all cells of the new first column to have no left border.</p>
 1425   
    */
 1426  0
   public void deleteTableCol() {
 1427  0
     Element cell = getCurTableCell();
 1428  0
     if(cell != null) {
 1429   
 
 1430  0
       Element row = cell.getParentElement();
 1431  0
       int lastColIndex = row.getElementCount() - 1;
 1432   
 
 1433  0
       if(lastColIndex > 0) {
 1434  0
         int cIndex = Util.getElementIndex(cell); //getColNumber(cell);
 1435  0
         int offset = -1; // adjacent cell is left of current cell
 1436  0
         if(cIndex == 0) { // if current cell is in first column...
 1437  0
           offset *= -1; // ...adjacent cell is right of current cell
 1438   
         }
 1439   
 
 1440  0
         Object attrC = cell.getAttributes().getAttribute(CSS.Attribute.WIDTH);
 1441  0
         Object attrA = row.getElement(cIndex + offset).getAttributes().
 1442   
                        getAttribute(CSS.Attribute.WIDTH);
 1443  0
         SimpleAttributeSet set = null;
 1444   
 
 1445  0
         if(attrC != null && attrA != null) {
 1446   
           //LengthValue lvC = new LengthValue(attrC);
 1447   
           //LengthValue lvA = new LengthValue(attrA);
 1448  0
           int widthC = (int) Util.getAbsoluteAttrVal(attrC);  // Util.getAttrValue(attrC);
 1449  0
           String cUnit = Util.getLastAttrUnit();
 1450   
           //String cUnit = lvC.getUnit();
 1451  0
           int widthA = (int)  Util.getAbsoluteAttrVal(attrA); // Util.getAttrValue(attrA);
 1452  0
           String aUnit = Util.getLastAttrUnit();
 1453  0
           if(aUnit.equalsIgnoreCase(cUnit)) {
 1454  0
             int width = 0;
 1455  0
             width += widthC;
 1456  0
             width += widthA;
 1457  0
             if(width > 0)
 1458   
             {
 1459  0
               String widthString = Integer.toString(width) + cUnit;
 1460  0
               set = new SimpleAttributeSet(
 1461   
                   row.getElement(cIndex + offset).getAttributes());
 1462  0
               Util.styleSheet().addCSSAttribute(set, CSS.Attribute.WIDTH,
 1463   
                   widthString);
 1464   
             }
 1465   
           }
 1466   
         }
 1467   
 
 1468  0
         Element table = row.getParentElement();
 1469  0
         SHTMLDocument doc = (SHTMLDocument) getDocument();
 1470   
 
 1471  0
         if(cIndex < lastColIndex) {
 1472  0
           offset = 0;
 1473   
         }
 1474   
 
 1475  0
         for(int rIndex = table.getElementCount() - 1; rIndex >= 0; rIndex--) {
 1476  0
           row = table.getElement(rIndex);
 1477  0
           try {
 1478  0
             doc.removeElements(row, cIndex, 1);
 1479   
             /*
 1480   
             the following line does not work for the last column in a table
 1481   
             so we use above code instead
 1482   
 
 1483   
             removeElement(row.getElement(cIndex));
 1484   
             */
 1485   
           }
 1486   
           catch(BadLocationException ble) {
 1487  0
             Util.errMsg(null, ble.getMessage(), ble);
 1488   
           }
 1489  0
            if(set != null) {
 1490  0
             doc.addAttributes(row.getElement(cIndex + offset), set);
 1491   
           }
 1492   
           //adjustColumnBorders(table.getElement(0).getElement(cIndex + offset));
 1493   
         }
 1494   
       }
 1495   
     }
 1496   
   }
 1497   
 
 1498   
   /**
 1499   
    * remove an element from the document of this editor
 1500   
    * (shared by deleteTableRow and deleteTableCol)
 1501   
    *
 1502   
    * @param e  the element to remove
 1503   
    */
 1504  0
   private void removeElement(Element e) {
 1505  0
     int start = e.getStartOffset();
 1506  0
     try {
 1507  0
       ((SHTMLDocument) getDocument()).remove(start, e.getEndOffset() - start);
 1508   
     }
 1509   
     catch(BadLocationException ble) {
 1510  0
       Util.errMsg(null, ble.getMessage(), ble);
 1511   
     }
 1512   
 
 1513   
   }
 1514   
 
 1515   
   /**
 1516   
    * apply a set of attributes to the table the caret is
 1517   
    * currently in (if any)
 1518   
    *
 1519   
    * @param a  the set of attributes to apply
 1520   
    */
 1521  2
   public void applyTableAttributes(AttributeSet a) {
 1522  2
     Element cell = getCurTableCell();
 1523  2
     if(cell != null) {
 1524  2
       Element table = cell.getParentElement().getParentElement();
 1525  2
       if(a.getAttributeCount() > 0) {
 1526   
         //System.out.println("applyTableAttributes count=" + a.getAttributeCount() + " a=" + a);
 1527  2
         ((SHTMLDocument) getDocument()).addAttributes(table, a);
 1528   
         /**
 1529   
          * for some reason above code does not show the changed attributes
 1530   
          * of the table, although the element really has them (maybe somebody
 1531   
          * could let me know why...). Therefore we update the editor pane
 1532   
          * contents comparably rude (any other more elegant alternatives
 1533   
          * welcome!)
 1534   
          *
 1535   
          * --> found out why: the swing package does not render short hand
 1536   
          *                    properties such as MARGIN or PADDING. When
 1537   
          *                    contained in a document inside an AttributeSet
 1538   
          *                    they already have to be split into MARGIN-TOP,
 1539   
          *                    MARGIN-LEFT, etc.
 1540   
          *                    adjusted AttributeComponents accordingly so
 1541   
          *                    we don't need refresh anymore
 1542   
          */
 1543   
         //refresh();
 1544   
       }
 1545   
     }
 1546   
   }
 1547   
 
 1548   
   /**
 1549   
    * refresh the whole contents of this editor pane with brute force
 1550   
    */
 1551  0
   public void refresh() {
 1552  0
     int pos = getCaretPosition();
 1553  0
     String data = getText();
 1554  0
     setText(""); 
 1555  0
     setText(data);
 1556  0
     setCaretPosition(pos);
 1557   
   }
 1558   
 
 1559   
 
 1560   
   /**
 1561   
    * apply a set of attributes to a given range of cells
 1562   
    * of the table the caret is currently in (if any)
 1563   
    *
 1564   
    * @param a  the set of attributes to apply
 1565   
    * @param range  the range of cells to apply attributes to
 1566   
    *
 1567   
    * @see adjustColWidths
 1568   
    */
 1569  0
   public void applyCellAttributes(AttributeSet a, int range) {
 1570   
     //System.out.println("SHTMLEditorPane applyCellAttributes a=" + a);
 1571  0
     Element cell = getCurTableCell();
 1572  0
     int cIndex = 0;
 1573  0
     int rIndex = 0;
 1574  0
     SHTMLDocument doc = (SHTMLDocument) getDocument();
 1575  0
     if(cell != null) {
 1576  0
       Element row = cell.getParentElement();
 1577  0
       Element table = row.getParentElement();
 1578  0
       Element aCell;
 1579  0
       switch(range) {
 1580  0
         case THIS_CELL:
 1581  0
           doc.addAttributes(cell, a);
 1582  0
           break;
 1583  0
         case THIS_ROW:
 1584  0
           for(cIndex = 0; cIndex < row.getElementCount(); cIndex++) {
 1585  0
             aCell = row.getElement(cIndex);
 1586  0
             doc.addAttributes(aCell, a);
 1587   
           }
 1588  0
           break;
 1589  0
         case THIS_COLUMN:
 1590  0
           cIndex = Util.getElementIndex(cell); //getColNumber(cell);
 1591  0
           for(rIndex = 0; rIndex < table.getElementCount(); rIndex++) {
 1592  0
             aCell = table.getElement(rIndex).getElement(cIndex);
 1593  0
             doc.addAttributes(aCell, a);
 1594   
           }
 1595  0
           break;
 1596  0
         case ALL_CELLS:
 1597  0
           while(rIndex < table.getElementCount()) {
 1598  0
             row = table.getElement(rIndex);
 1599  0
             cIndex = 0;
 1600  0
             while(cIndex < row.getElementCount()) {
 1601  0
               aCell = row.getElement(cIndex);
 1602   
               //System.out.println("applyCellAttributes ALL_CELLS adjusted a=" + adjustCellBorders(aCell, a));
 1603  0
               doc.addAttributes(aCell, a);
 1604  0
               cIndex++;
 1605   
             }
 1606  0
             rIndex++;
 1607   
           }
 1608  0
           break;
 1609   
       }
 1610   
     }
 1611   
   }
 1612   
 
 1613   
   /**
 1614   
    * get the number of the table column a given cell is in
 1615   
    *
 1616   
    * @param cell  the cell to get the column number for
 1617   
    * @return the column number of the given cell
 1618   
    */
 1619  0
   private int getColNumber(Element cell) {
 1620  0
     int i = 0;
 1621  0
     Element thisRow = cell.getParentElement();
 1622  0
     int last = thisRow.getElementCount() - 1;
 1623  0
     Element aCell = thisRow.getElement(i);
 1624  0
     if(aCell != cell) {
 1625  0
       while((i < last) && (aCell != cell)) {
 1626  0
         aCell = thisRow.getElement(++i);
 1627   
       }
 1628   
     }
 1629  0
     return i;
 1630   
   }
 1631   
 
 1632   
   /* ------- table manipulation end -------------------- */
 1633   
 
 1634   
   /* ------- table cell navigation start --------------- */
 1635   
 
 1636   
   /**
 1637   
    * <code>Action</code> to move the caret from the current table cell
 1638   
    * to the next table cell.
 1639   
    */
 1640   
   public class NextTableCellAction extends AbstractAction {
 1641   
 
 1642   
     /** action to use when not inside a table */
 1643   
     /* removed for changes in J2SE 1.4.1
 1644   
     private Action alternateAction;
 1645   
     */
 1646   
 
 1647   
     /** construct a <code>NextTableCellAction</code> */
 1648  158
     public NextTableCellAction(String actionName) {
 1649  158
       super(actionName);
 1650   
     }
 1651   
 
 1652   
     /**
 1653   
      * construct a <code>NextTableCellAction</code>
 1654   
      *
 1655   
      * @param altAction  the action to use when the caret
 1656   
      *      is not inside a table
 1657   
      */
 1658   
     /* removed for changes in J2SE 1.4.1
 1659   
     public NextTableCellAction(Action altAction) {
 1660   
       alternateAction = altAction;
 1661   
     }
 1662   
     */
 1663   
 
 1664   
     /**
 1665   
      * move to the previous cell or invoke an alternate action if the
 1666   
      * caret is not inside a table
 1667   
      *
 1668   
      * this will append a new table row when the caret
 1669   
      * is inside the last table cell
 1670   
      */
 1671  0
     public void actionPerformed(ActionEvent ae) {
 1672  0
       Element cell = getCurTableCell();
 1673  0
       if(cell != null) {
 1674  0
         goNextCell(cell);
 1675   
       }
 1676   
       else {
 1677  0
         KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
 1678  0
         Object key = getInputMap().getParent().get(tab);
 1679  0
         if(key != null) {
 1680  0
           getActionMap().getParent().get(key).actionPerformed(ae);
 1681   
         }
 1682   
 
 1683   
         /* removed for changes in J2SE 1.4.1
 1684   
         if(alternateAction != null) {
 1685   
           alternateAction.actionPerformed(ae);
 1686   
         }
 1687   
         */
 1688   
       }
 1689   
     }
 1690   
 
 1691   
   }
 1692   
 
 1693   
   /**
 1694   
    * <code>Action</code> to move the caret from the current table cell
 1695   
    * to the previous table cell.
 1696   
    */
 1697   
   public class PrevTableCellAction extends AbstractAction {
 1698   
 
 1699   
     /** action to use when not inside a table */
 1700   
     /* removed for changes in J2SE 1.4.1
 1701   
     private Action alternateAction;
 1702   
     */
 1703   
 
 1704   
     /** construct a <code>PrevTableCellAction</code> */
 1705  158
     public PrevTableCellAction(String actionName) {
 1706  158
       super(actionName);
 1707   
     }
 1708   
 
 1709   
     /**
 1710   
      * construct a <code>PrevTableCellAction</code>
 1711   
      *
 1712   
      * @param altAction  the action to use when the caret
 1713   
      *      is not inside a table
 1714   
      */
 1715   
     /* removed for changes in J2SE 1.4.1
 1716   
     public PrevTableCellAction(Action altAction) {
 1717   
       alternateAction = altAction;
 1718   
     }
 1719   
     */
 1720   
 
 1721   
     /**
 1722   
      * move to the previous cell or invoke an alternate action if the
 1723   
      * caret is not inside a table
 1724   
      */
 1725  0
     public void actionPerformed(ActionEvent ae) {
 1726  0
       Element cell = getCurTableCell();
 1727  0
       if(cell != null) {
 1728  0
         goPrevCell(cell);
 1729   
       }
 1730   
       else {
 1731  0
         KeyStroke shiftTab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK);
 1732  0
         Object key = getInputMap().getParent().get(shiftTab);
 1733  0
         if(key != null) {
 1734  0
           getActionMap().getParent().get(key).actionPerformed(ae);
 1735   
         }
 1736   
 
 1737   
         /* removed for changes in J2SE 1.4.1
 1738   
         if(alternateAction != null) {
 1739   
           alternateAction.actionPerformed(ae);
 1740   
         }
 1741   
         */
 1742   
       }
 1743   
     }
 1744   
   }
 1745   
 
 1746  0
   public void goNextCell(Element cell) {
 1747  0
     if(cell == getLastTableCell(cell)) {
 1748  0
       appendTableRow();
 1749  0
       cell = getCurTableCell();
 1750   
     }
 1751  0
     int pos = getNextCell(cell).getStartOffset();
 1752  0
     select(pos, pos);
 1753   
   }
 1754   
 
 1755  0
   public void goPrevCell(Element cell) {
 1756  0
     int newPos;
 1757  0
     if(cell != getFirstTableCell(cell)) {
 1758  0
       cell = getPrevCell(cell);
 1759  0
       newPos = cell.getStartOffset();
 1760  0
       select(newPos, newPos);
 1761   
     }
 1762   
   }
 1763   
 
 1764   
   /**
 1765   
    * get the table cell following a given table cell
 1766   
    *
 1767   
    * @param cell  the cell whose following cell shall be found
 1768   
    * @return the Element having the cell following the given cell or null
 1769   
    *    if the given cell is the last cell in the table
 1770   
    */
 1771  0
   private Element getNextCell(Element cell) {
 1772  0
     Element nextCell = null;
 1773  0
     Element thisRow = cell.getParentElement();
 1774  0
     Element nextRow = null;
 1775  0
     Element table = thisRow.getParentElement();
 1776  0
     int i = thisRow.getElementCount()-1;
 1777  0
     Element aCell = thisRow.getElement(i);
 1778  0
     if(aCell != cell) {
 1779  0
       while((i > 0) && (aCell != cell)) {
 1780  0
         nextCell = aCell;
 1781  0
         aCell = thisRow.getElement(--i);
 1782   
       }
 1783   
     }
 1784   
     else {
 1785  0
       i = table.getElementCount()-1;
 1786  0
       Element aRow = table.getElement(i);
 1787  0
       while((i > 0) && (aRow != thisRow)) {
 1788  0
         nextRow = aRow;
 1789  0
         aRow = table.getElement(--i);
 1790   
       }
 1791  0
       nextCell = nextRow.getElement(0);
 1792   
     }
 1793  0
     return nextCell;
 1794   
   }
 1795   
 
 1796   
   /**
 1797   
    * get the table cell preceding a given table cell
 1798   
    *
 1799   
    * @param cell  the cell whose preceding cell shall be found
 1800   
    * @return the Element having the cell preceding the given cell or null
 1801   
    *    if the given cell is the first cell in the table
 1802   
    */
 1803  0
   private Element getPrevCell(Element cell) {
 1804  0
     Element thisRow = cell.getParentElement();
 1805  0
     Element table = thisRow.getParentElement();
 1806  0
     Element prevCell = null;
 1807  0
     int i = 0;
 1808  0
     Element aCell = thisRow.getElement(i);
 1809  0
     if(aCell != cell) {
 1810  0
       while(aCell != cell) {
 1811  0
         prevCell = aCell;
 1812  0
         aCell = thisRow.getElement(i++);
 1813   
       }
 1814   
     }
 1815   
     else {
 1816  0
       Element prevRow = null;
 1817  0
       Element aRow = table.getElement(i);
 1818  0
       while(aRow != thisRow) {
 1819  0
         prevRow = aRow;
 1820  0
         aRow = table.getElement(i++);
 1821   
       }
 1822  0
       prevCell = prevRow.getElement(prevRow.getElementCount()-1);
 1823   
     }
 1824  0
     return prevCell;
 1825   
   }
 1826   
 
 1827   
   /**
 1828   
    * get the last cell of the table a given table cell belongs to
 1829   
    *
 1830   
    * @param cell  a cell of the table to get the last cell of
 1831   
    * @return the Element having the last table cell
 1832   
    */
 1833  0
   private Element getLastTableCell(Element cell) {
 1834  0
     Element table = cell.getParentElement().getParentElement();
 1835  0
     Element lastRow = table.getElement(table.getElementCount()-1);
 1836  0
     Element lastCell = lastRow.getElement(lastRow.getElementCount()-1);
 1837  0
     return lastCell;
 1838   
   }
 1839   
 
 1840   
   /**
 1841   
    * get the first cell of the table a given table cell belongs to
 1842   
    *
 1843   
    * @param cell  a cell of the table to get the first cell of
 1844   
    * @return the Element having the first table cell
 1845   
    */
 1846  0
   private Element getFirstTableCell(Element cell) {
 1847  0
     Element table = cell.getParentElement().getParentElement();
 1848  0
     Element firstCell = table.getElement(0).getElement(0);
 1849  0
     return firstCell;
 1850   
   }
 1851   
 
 1852   
   /**
 1853   
    * get the table cell at the current caret position
 1854   
    *
 1855   
    * @return the Element having the current table cell or null if
 1856   
    *    the caret is not inside a table cell
 1857   
    */
 1858  1372
   public Element getCurTableCell() {
 1859  1372
     return Util.findElementUp(HTML.Tag.TD.toString(),
 1860   
       ((SHTMLDocument)getDocument()).getCharacterElement(getSelectionStart()));
 1861   
   }
 1862   
 
 1863   
   /* ---------- table cell navigation end --------------*/
 1864   
 
 1865   
   /**
 1866   
    * Replaces the currently selected content with new content
 1867   
    * represented by the given <code>HTMLText</code>. If there is no selection
 1868   
    * this amounts to an insert of the given text.  If there
 1869   
    * is no replacement text this amounts to a removal of the
 1870   
    * current selection.
 1871   
    *
 1872   
    * @overrides replaceSelection in <code>JEditorPane</code> for usage of
 1873   
    *        our own HTMLText object
 1874   
    *
 1875   
    * @param content  the content to replace the selection with
 1876   
    */
 1877  0
   public void replaceSelection(HTMLText content) {
 1878  0
     SHTMLDocument doc = (SHTMLDocument) getDocument();
 1879  0
     String text;
 1880  0
     Caret caret = getCaret();
 1881  0
     int insertPos = 0;
 1882  0
     int i;
 1883  0
     int contentSize;
 1884  0
     if (doc != null) {
 1885  0
       try {
 1886  0
         int p0 = Math.min(caret.getDot(), caret.getMark());
 1887  0
         int p1 = Math.max(caret.getDot(), caret.getMark());
 1888  0
         if (p0 != p1) {
 1889  0
           doc.remove(p0, p1 - p0);
 1890   
         }
 1891  0
         if (content != null) {
 1892  0
           content.pasteHTML(doc, p0);
 1893   
         }
 1894   
       }
 1895   
       catch (Exception e) {
 1896  0
         getToolkit().beep();
 1897   
       }
 1898   
     }
 1899   
   }
 1900   
 
 1901   
   /* ------ start of drag and drop implementation -------------------------
 1902   
               (see also constructor of this class) */
 1903   
 
 1904   
   /** enables this component to be a Drop Target */
 1905   
   DropTarget dropTarget = null;
 1906   
 
 1907   
   /** enables this component to be a Drag Source */
 1908   
   DragSource dragSource = null;
 1909   
 
 1910   
   /** the last selection start */
 1911   
   private int lastSelStart = 0;
 1912   
 
 1913   
   /** the last selection end */
 1914   
   private int lastSelEnd = 0;
 1915   
 
 1916   
   /** the location of the last event in the text component */
 1917   
   private int dndEventLocation = 0;
 1918   
 
 1919   
   /**
 1920   
    * <p>This flag is set by this objects dragGestureRecognizer to indicate that
 1921   
    * a drag operation has been started from this object. It is cleared once
 1922   
    * dragDropEnd is captured by this object.</p>
 1923   
    *
 1924   
    * <p>If a drop occurs in this object and this object started the drag
 1925   
    * operation, then the element to be dropped comes from this object and thus
 1926   
    * has to be removed somewhere else in this object.</p>
 1927   
    *
 1928   
    * <p>To the contrary if a drop occurs in this object and the drag operation
 1929   
    * was not started in this object, then the element to be dropped does not
 1930   
    * come from this object and has not to be removed here.</p>
 1931   
    */
 1932   
   private boolean dragStartedHere = false;
 1933   
 
 1934   
   /**
 1935   
    * Initialize the drag and drop implementation for this component.
 1936   
    *
 1937   
    * <p>DropTarget, DragSource and DragGestureRecognizer are instantiated
 1938   
    * and a MouseListener is established to track the selection in drag
 1939   
    * operations</p>
 1940   
    *
 1941   
    * <p>this ideally is called in the constructor of a class which
 1942   
    * would like to implement drag and drop</p>
 1943   
    */
 1944  158
   public void initDnd() {
 1945  158
     dropTarget = new DropTarget (this, this);
 1946  158
     dragSource = new DragSource();
 1947  158
     dragSource.createDefaultDragGestureRecognizer(
 1948   
                           this, DnDConstants.ACTION_MOVE, this);
 1949  158
     this.addMouseListener(new MouseAdapter() {
 1950  1
       public void mouseReleased(MouseEvent e) {
 1951  1
         this_mouseReleased(e);
 1952   
       }
 1953  0
       public void mouseClicked(MouseEvent e) {
 1954  0
         this_mouseClicked(e);
 1955   
       }
 1956   
     });
 1957   
   }
 1958   
 
 1959   
   /** a drag gesture has been initiated */
 1960  1
   public void dragGestureRecognized(DragGestureEvent event) {
 1961  1
     int selStart = getSelectionStart();
 1962  1
     int selEnd = getSelectionEnd();
 1963  1
     try {
 1964  1
       if( (lastSelEnd > lastSelStart) &&
 1965   
           (selStart >= lastSelStart) &&
 1966   
           (selStart < lastSelEnd) )
 1967   
       {
 1968  0
         dragStartedHere = true;
 1969  0
         select(lastSelStart, lastSelEnd);
 1970  0
         HTMLText text = new HTMLText();
 1971  0
         int start = getSelectionStart();
 1972  0
         text.copyHTML(this, start, getSelectionEnd() - start);
 1973  0
         HTMLTextSelection trans = new HTMLTextSelection(text);
 1974  0
         dragSource.startDrag(event, DragSource.DefaultMoveDrop, trans, this);
 1975   
       }
 1976   
     }
 1977   
     catch(Exception e) {
 1978   
       //getToolkit().beep();
 1979   
     }
 1980   
   }
 1981   
 
 1982   
   /**
 1983   
    * this message goes to DragSourceListener, informing it that the dragging
 1984   
    * has ended
 1985   
    */
 1986  0
   public void dragDropEnd (DragSourceDropEvent event) {
 1987  0
     dragStartedHere = false;
 1988   
   }
 1989   
 
 1990   
   /** is invoked when a drag operation is going on */
 1991  0
   public void dragOver (DropTargetDragEvent event) {
 1992  0
     dndEventLocation = viewToModel(event.getLocation());
 1993  0
     try {
 1994  0
       setCaretPosition(dndEventLocation);
 1995   
     }
 1996   
     catch(Exception e) {
 1997   
       //getToolkit().beep();
 1998   
     }
 1999   
   }
 2000   
 
 2001   
   /**
 2002   
    * a drop has occurred. If the dragged element has a suitable
 2003   
    * <code>DataFlavor</code>, do the drop.
 2004   
    *
 2005   
    * @param event - the event specifiying the drop operation
 2006   
    * @see java.awt.datatransfer.DataFlavor
 2007   
    * @see de.calcom.cclib.text.StyledText
 2008   
    * @see de.calcom.cclib.text.StyledTextSelection
 2009   
    */
 2010  0
   public void drop(DropTargetDropEvent event) {
 2011  0
     dndEventLocation = viewToModel(event.getLocation());
 2012  0
     if( (dndEventLocation >= lastSelStart) &&
 2013   
         (dndEventLocation <= lastSelEnd) )
 2014   
     {
 2015  0
       event.rejectDrop();
 2016  0
       select(lastSelStart, lastSelEnd);
 2017   
     }
 2018   
     else {
 2019  0
       try {
 2020  0
         Transferable transferable = event.getTransferable();
 2021  0
         if(transferable.isDataFlavorSupported(df)){
 2022  0
           HTMLText s = (HTMLText) transferable.getTransferData(df);
 2023  0
           doDrop(event, s);
 2024   
         }
 2025  0
         else if(transferable.isDataFlavorSupported(DataFlavor.stringFlavor)){
 2026  0
           String s = (String) transferable.getTransferData(
 2027   
                                           DataFlavor.stringFlavor);
 2028  0
           doDrop(event, s);
 2029   
         }
 2030   
         else {
 2031  0
           event.rejectDrop();
 2032   
         }
 2033   
       }
 2034   
       catch (Exception exception) {
 2035  0
         exception.printStackTrace();
 2036  0
         event.rejectDrop();
 2037   
       }
 2038   
     }
 2039   
   }
 2040   
 
 2041   
   /**
 2042   
    * do the drop operation consisting of adding the dragged element and
 2043   
    * necessarily removing the dragged element from the original position
 2044   
    */
 2045  0
   private void doDrop(DropTargetDropEvent event, Object s) {
 2046  0
     int removeOffset = 0;
 2047  0
     int moveOffset = 0;
 2048  0
     int newSelStart;
 2049  0
     int newSelEnd;
 2050  0
     event.acceptDrop(DnDConstants.ACTION_MOVE);
 2051  0
     setCaretPosition(dndEventLocation);
 2052  0
     if(s instanceof HTMLText) {
 2053  0
       replaceSelection((HTMLText) s);
 2054   
     }
 2055  0
     else if(s instanceof String) {
 2056  0
       replaceSelection((String) s);
 2057   
     }
 2058  0
     if(dndEventLocation < lastSelStart) {
 2059  0
       removeOffset = s.toString().length();
 2060   
     }
 2061   
     else {
 2062  0
       moveOffset = s.toString().length();
 2063   
     }
 2064  0
     newSelEnd = dndEventLocation + (lastSelEnd - lastSelStart) - moveOffset;
 2065  0
     newSelStart = dndEventLocation - moveOffset;
 2066  0
     if(dragStartedHere) {
 2067  0
       lastSelStart += removeOffset;
 2068  0
       lastSelEnd += removeOffset;
 2069  0
       select(lastSelStart, lastSelEnd);
 2070  0
       replaceSelection("");
 2071   
     }
 2072  0
     lastSelEnd = newSelEnd;
 2073  0
     lastSelStart = newSelStart;
 2074  0
     select(lastSelStart, lastSelEnd);
 2075  0
     event.getDropTargetContext().dropComplete(true);
 2076   
   }
 2077   
 
 2078   
   /** remember current selection when mouse button is released */
 2079  1
   void this_mouseReleased(MouseEvent e) {
 2080  1
     lastSelStart = getSelectionStart();
 2081  1
     lastSelEnd = getSelectionEnd();
 2082   
   }
 2083   
 
 2084   
   /** remember current selection when mouse button is double clicked */
 2085  0
   void this_mouseClicked(MouseEvent e) {
 2086  0
     if(e.getClickCount() > 1) {
 2087  0
       lastSelStart = getSelectionStart();
 2088  0
       lastSelEnd = getSelectionEnd();
 2089   
     }
 2090   
   }
 2091   
 
 2092   
   /** is invoked if the user modifies the current drop gesture */
 2093  0
   public void dropActionChanged ( DropTargetDragEvent event ) {
 2094   
   }
 2095   
 
 2096   
   /** is invoked when the user changes the dropAction */
 2097  0
   public void dropActionChanged ( DragSourceDragEvent event) {
 2098   
   }
 2099   
 
 2100   
   /** is invoked when you are dragging over the DropSite */
 2101  0
   public void dragEnter (DropTargetDragEvent event) {
 2102   
   }
 2103   
 
 2104   
   /** is invoked when you are exit the DropSite without dropping */
 2105  0
   public void dragExit (DropTargetEvent event) {
 2106   
   }
 2107   
 
 2108   
   /**
 2109   
    * this message goes to DragSourceListener, informing it that the dragging
 2110   
    * has entered the DropSite
 2111   
    */
 2112  0
   public void dragEnter (DragSourceDragEvent event) {
 2113   
   }
 2114   
 
 2115   
   /**
 2116   
    * this message goes to DragSourceListener, informing it that the dragging
 2117   
    * has exited the DropSite
 2118   
    */
 2119  0
   public void dragExit (DragSourceEvent event) {
 2120   
   }
 2121   
 
 2122   
   /**
 2123   
    * this message goes to DragSourceListener, informing it that
 2124   
    * the dragging is currently ocurring over the DropSite
 2125   
    */
 2126  0
   public void dragOver (DragSourceDragEvent event) {
 2127   
   }
 2128   
 
 2129   
   // ------ end of drag and drop implementation ----------------------------
 2130   
 
 2131   
   /* ------ start of cut, copy and paste implementation ------------------- */
 2132   
 
 2133   
   /**
 2134   
    * Transfers the currently selected range in the associated
 2135   
    * text model to the system clipboard, removing the contents
 2136   
    * from the model. The current selection is reset.
 2137   
    *
 2138   
    * @see #replaceSelection
 2139   
    */
 2140   
 
 2141  0
   public void cut() {
 2142  0
     if (isEditable() && isEnabled()) {
 2143  0
       copy();
 2144  0
       replaceSelection("");
 2145   
     }
 2146   
   }
 2147   
 
 2148   
 
 2149   
   /**
 2150   
    * Transfers the currently selected range in the associated
 2151   
    * text model to the system clipboard, leaving the contents
 2152   
    * in the text model.  The current selection remains intact.
 2153   
    */
 2154   
 
 2155  0
   public void copy() {
 2156  0
     try
 2157   
     {
 2158  0
       HTMLText st = new HTMLText();
 2159  0
       int start = getSelectionStart();
 2160  0
       st.copyHTML(this, start, getSelectionEnd() - start);
 2161  0
       HTMLTextSelection contents = new HTMLTextSelection(st);
 2162  0
       Clipboard clipboard = getToolkit().getSystemClipboard();
 2163  0
       clipboard.setContents(contents, defaultClipboardOwner);
 2164   
     }
 2165   
     catch(Exception e) {
 2166  0
       getToolkit().beep();
 2167   
     }
 2168   
   }
 2169   
 
 2170   
 
 2171  0
   public File saveAsJPEG(BufferedImage bi, String fileName) {
 2172  0
      BufferedImage myImage = bi;
 2173  0
      Graphics2D g2 = myImage.createGraphics();
 2174  0
      File f = null;
 2175  0
      try {
 2176  0
        OutputStream out = new FileOutputStream(fileName);
 2177  0
        f= new File(fileName);
 2178  0
        Debug.print(f.getAbsolutePath());
 2179   
        
 2180  0
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
 2181  0
        encoder.encode(myImage);
 2182  0
        out.close();
 2183  0
        Debug.print("in saveASJPEG");
 2184   
      } catch (Exception e) {
 2185  0
        System.out.println(e.toString());
 2186   
      }
 2187  0
      return f;
 2188   
    }
 2189   
 
 2190   
   /**
 2191   
    * Transfers the contents of the system clipboard into the
 2192   
    * associated text model. If there is a selection in the
 2193   
    * associated view, it is replaced with the contents of the
 2194   
    * clipboard. If there is no selection, the clipboard contents
 2195   
    * are inserted in front of the current insert position in
 2196   
    * the associated view. If the clipboard is empty, does nothing.
 2197   
    *
 2198   
    * @see #replaceSelection
 2199   
    */
 2200   
 
 2201  0
   public void paste() {
 2202  0
       Debug.print("IN OUR PASTE");
 2203  0
       Clipboard clipboard = getToolkit().getSystemClipboard();
 2204  0
       Transferable content = clipboard.getContents(this);
 2205  0
       FrmMain.pastingImage=false;
 2206  0
       if (content != null) {
 2207  0
         try {
 2208  0
           if(content.isDataFlavorSupported(df)) {
 2209  0
             HTMLText st = (HTMLText) content.getTransferData(df);
 2210  0
             replaceSelection(st);
 2211  0
             Debug.print("HTMLText");
 2212   
           }
 2213  0
           else if(content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
 2214  0
             String text = (String) content.getTransferData(DataFlavor.stringFlavor);
 2215  0
             replaceSelection(text);
 2216  0
             Debug.print("stringFlavor");
 2217   
           }
 2218   
           else {
 2219  0
               if ( content.isDataFlavorSupported(DataFlavor.imageFlavor) ) {
 2220  0
                 FrmMain.pastingImage=true;
 2221  0
                   Debug.print(content.getTransferData(DataFlavor.imageFlavor).getClass().getName());
 2222  0
                   BufferedImage bi = (BufferedImage) content.getTransferData(DataFlavor.imageFlavor);
 2223  0
                 File saveloc = new File(dp.docTempDir.toString());
 2224  0
                  System.out.println(saveloc.toString());
 2225   
                  // File f = saveAsJPEG(bi, saveloc.toString()+"/pastefile.jpg");
 2226   
                 //saveAsJPEG(bi, "/pastefile.jpg");
 2227   
                   //Util.copyFile(saveloc, )
 2228  0
                   Debug.print("image?");
 2229  0
                   File imgDir = dp.getImageDir();
 2230  0
                   Debug.print("IMGDIR IS "+imgDir.toString()+ "and exists?"+imgDir.exists());
 2231  0
                 imgDir.mkdirs();
 2232  0
                 imgDir.mkdirs();
 2233  0
                 imgDir.mkdirs();
 2234   
                   
 2235  0
                 if(!imgDir.exists()) {
 2236  0
                          Debug.print("Made Image directory is "+(imgDir.mkdirs()==true));
 2237   
                         }
 2238  0
                         String imgDirName = imgDir.getAbsolutePath();
 2239  0
                 File f = saveAsJPEG(bi, imgDir.toString()+"/pastefile.jpg");
 2240   
                         //for(int i = 0; i < sFiles.length; i++)
 2241   
                          {
 2242   
                           //System.out.println("file selected: " + sFiles[i] + " new name= " + imgDirName + File.separator + sFiles[i].getName());
 2243   
                         //File f2 = new File (imgDir.toString());
 2244   
                     //    Debug.print(f2.toString());
 2245   
                     //      Util.copyFile(f,f2);
 2246   
                         }
 2247   
               }
 2248   
           }
 2249   
         }
 2250   
         catch (Exception e) {
 2251   
           //e.printStackTrace();
 2252  0
           getToolkit().beep();
 2253   
         }
 2254   
       }
 2255   
     }
 2256   
 
 2257   
 
 2258   
   /* ------ end of cut, copy and paste implementation --------------- */
 2259   
 
 2260   
   /* ------ start of Clipboard implementation --------------- */
 2261   
 
 2262   
   private static ClipboardOwner defaultClipboardOwner = new ClipboardObserver();
 2263   
 
 2264   
   static class ClipboardObserver implements ClipboardOwner {
 2265  0
     public void lostOwnership(Clipboard clipboard, Transferable contents) {
 2266   
     }
 2267   
   }
 2268   
 
 2269   
   /* ------ end of Clipboard implementation --------------- */
 2270   
 
 2271   
   /* ------ start of font/paragraph manipulation --------------- */
 2272   
 
 2273  5
   public void applyAttributes(AttributeSet a, boolean para) {
 2274  5
     applyAttributes(a, para, false);
 2275   
   }
 2276   
 
 2277   
   /**
 2278   
    * set the attributes for a given part of this editor. If a range of
 2279   
    * text is selected, the attributes are applied to the selection.
 2280   
    * If nothing is selected, the input attributes of the given
 2281   
    * editor are set thus applying the given attributes to future
 2282   
    * inputs.
 2283   
    *
 2284   
    * @param a  the set of attributes to apply
 2285   
    * @param para  true, if the attributes shall be applied to the whole
 2286   
    *     paragraph, false, if only the selected range of characters shall have them
 2287   
    * @param replace  true, if existing attribtes are to be replaced, false if not
 2288   
    */
 2289  5
   public void applyAttributes(AttributeSet a, boolean para, boolean replace) {
 2290  5
     SHTMLDocument doc = (SHTMLDocument) getDocument();
 2291  5
     requestFocus();
 2292  5
     int start = getSelectionStart();
 2293  5
     int end = getSelectionEnd();
 2294  5
     if(para) {
 2295  1
       doc.setParagraphAttributes(start, end - start, a, replace);
 2296   
     }
 2297   
     else {
 2298  4
       if(end != start) {
 2299  2
         doc.setCharacterAttributes(start, end - start, a, replace);
 2300   
       }
 2301   
       else {
 2302  2
         MutableAttributeSet inputAttributes =
 2303   
             ((SHTMLEditorKit) getEditorKit()).getInputAttributes();
 2304  2
         inputAttributes.addAttributes(a);
 2305   
       }
 2306   
     }
 2307   
   }
 2308   
 
 2309   
   /**
 2310   
    * switch elements in the current selection to a given tag
 2311   
    *
 2312   
    * @param tag  the tag name to switche elements to
 2313   
    * @param allowedTags  tags that may be switched
 2314   
    */
 2315  0
   public void applyTag(String tag, Vector allowedTags) {
 2316  0
     try {
 2317  0
       int start = getSelectionStart();
 2318  0
       int end = getSelectionEnd();
 2319   
       //System.out.println("SHTMLEditorPane applyTag start=" + start + ", end=" + end);
 2320  0
       StringWriter sw = new StringWriter();
 2321  0
       SHTMLDocument doc = (SHTMLDocument) getDocument();
 2322  0
       SHTMLWriter w = new SHTMLWriter(sw, doc);
 2323  0
       Element elem = doc.getParagraphElement(start);
 2324   
       //System.out.println("SHTMLEditorPane applyTag elemName=" + elem.getName());
 2325  0
       start = elem.getStartOffset();
 2326  0
       if(end < elem.getEndOffset()) {
 2327  0
         end = elem.getEndOffset();
 2328   
       }
 2329   
       //System.out.println("SHTMLEditorPane applyTag start=" + start + ", end=" + end);
 2330  0
       elem = elem.getParentElement();
 2331  0
       int replaceStart = -1;
 2332  0
       int replaceEnd = -1;
 2333  0
       int index = -1;
 2334  0
       int removeCount = 0;
 2335  0
       int eCount = elem.getElementCount();
 2336   
       //System.out.println("SHTMLEditorPane applyTag parent elem=" + elem.getName() + ", eCount=" + eCount);
 2337  0
       for(int i = 0; i < eCount; i++) {
 2338  0
         Element child = elem.getElement(i);
 2339   
         //System.out.println("SHTMLEditorPane applyTag child elem=" + child.getName() + ", eCount=" + eCount);
 2340  0
         int eStart = child.getStartOffset();
 2341  0
         int eEnd = child.getEndOffset();
 2342   
         //System.out.println("SHTMLEditorPane applyTag eStart=" + eStart + ", eEnd=" + eEnd);
 2343  0
         if((eStart >= start && eStart < end) ||
 2344   
            (eEnd > start && eEnd <= end))
 2345   
         {
 2346  0
           ++removeCount;
 2347  0
           if(allowedTags.contains(child.getName())) {
 2348   
             //System.out.println("SHTMLEditorPane applyTag element is in selection");
 2349  0
             w.startTag(tag.toString(), child.getAttributes());
 2350  0
             w.writeChildElements(child);
 2351  0
             w.endTag(tag.toString());
 2352  0
             if(index < 0) {
 2353  0
               index = i;
 2354   
             }
 2355  0
             if(replaceStart < 0 || replaceStart > eStart) {
 2356  0
               replaceStart = eStart;
 2357   
             }
 2358  0
             if(replaceEnd < 0 || replaceEnd < eEnd) {
 2359  0
               replaceEnd = eEnd;
 2360   
             }
 2361   
           }
 2362   
           else {
 2363  0
             w.write(child);
 2364   
           }
 2365   
         }
 2366   
       }
 2367   
       //System.out.println("SHTMLEditorPane applyTag remove index=" + index + ", removeCount=" + removeCount);
 2368  0
       doc.insertAfterEnd(elem.getElement(index), sw.getBuffer().toString());
 2369  0
       doc.removeElements(elem, index, removeCount);
 2370   
       //SHTMLEditorKit kit = (SHTMLEditorKit) getEditorKit();
 2371   
       //System.out.println("SHTMLEditorPane applyTag new HTML=\r\n" + sw.getBuffer().toString() );
 2372   
       //kit.read(new StringReader(sw.getBuffer().toString()), doc, getCaretPosition());
 2373   
     }
 2374   
     catch(Exception e) {
 2375  0
       Util.errMsg(this, e.getMessage(), e);
 2376   
     }
 2377   
   }
 2378   
 
 2379   
   /* ------ end of font/paragraph manipulation --------------- */
 2380   
 
 2381   
   /* ---------- class fields start -------------- */
 2382   
 
 2383   
   public static final String newListItemAction = "newListItem";
 2384   
 
 2385   
   /** a data flavor for transferables processed by this component */
 2386   
   private DataFlavor df =
 2387   
         new DataFlavor(HTMLText.class, "HTMLText");
 2388   
 
 2389   
   /* Cursors for mouseovers in the editor */
 2390   
   private Cursor textCursor = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
 2391   
   private Cursor defaultCursor =
 2392   
                   Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
 2393   
 
 2394   
   /* ---------- class fields end -------------- */
 2395   
 
 2396   
 }