Package org.jriaffe.blog.panels

Source Code of org.jriaffe.blog.panels.TextTransferHandler

/*
*
* Copyright 2012 Chad Preisler
*
* This file is part of jriaffeBlog.
*
*    jriaffeBlog is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    jriaffeBlog is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with jriaffeBlog.  If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.jriaffe.blog.panels;

import jriaffe.client.NotificationCenter;
import org.jriaffe.blog.domain.Blog;
import org.jriaffe.blog.service.ImageImporterService;
import org.jriaffe.blog.service.ImportImageInfo;

import javax.swing.*;
import javax.swing.text.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.im.InputContext;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;


/**
* This class was mostly copied from the JDK. I added the ability to drop image files
* into the JEditPane.  This class needs some cleaning up.  It assumes that the JTextComponent
* is has an HTMLEditorKit.
*
* I'm not happy about copying the code from the JDK but I didn't see that I had a choice because
* of the way Swing implemented the TextTransferHandler.
*/
class TextTransferHandler extends TransferHandler  {

    private JTextComponent exportComp;
    private Blog blog;
    private boolean shouldRemove;
    private int p0;
    private int p1;

    /**
     * Whether or not this is a drop using
     * <code>DropMode.INSERT</code>.
     */
    private boolean modeBetween = false;

    /**
     * Whether or not this is a drop.
     */
    private boolean isDrop = false;

    /**
     * The drop action.
     */
    private int dropAction = MOVE;

    /**
     * The drop bias.
     */
    private Position.Bias dropBias;

    /**
     * Try to find a flavor that can be used to import a Transferable.
     * The set of usable flavors are tried in the following order:
     * <ol>
     * <li>First, an attempt is made to find a flavor matching the content type
     * of the EditorKit for the component.
     * <li>Second, an attempt to find a text/plain flavor is made.
     * <li>Third, an attempt to find a flavor representing a String reference
     * in the same VM is made.
     * <li>Lastly, DataFlavor.stringFlavor is searched for.
     * </ol>
     * @param flavors
     * @param c
     * @return
     */
    protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {
        DataFlavor plainFlavor = null;
        DataFlavor refFlavor = null;
        DataFlavor stringFlavor = null;
        DataFlavor imageFlavor = null;


            for (int i = 0; i < flavors.length; i++) {
                String mime = flavors[i].getMimeType();
                System.out.println("Flavor Mimetype is " + mime);
                if (mime.startsWith(((JEditorPane) c).getEditorKit().getContentType())) {
                    return flavors[i];
                } else if (plainFlavor == null && mime.startsWith("text/plain")) {
                    plainFlavor = flavors[i];
                } else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref")
                        && flavors[i].getRepresentationClass() == java.lang.String.class) {
                    refFlavor = flavors[i];
                } else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) {
                    stringFlavor = flavors[i];
                } else if (imageFlavor == null && mime.startsWith("application/x-java-file-list; class=java.util.List")) {
                    imageFlavor = flavors[i];
                    break;
                } else if (imageFlavor == null && mime.startsWith("text/uri-list; class=java.io.Reader")) {
                    imageFlavor = flavors[i];
                    break;
                }
            }
            if (imageFlavor != null) {
                return imageFlavor;
            } else if (plainFlavor != null) {
                return plainFlavor;
            } else if (refFlavor != null) {
                return refFlavor;
            } else if (stringFlavor != null) {
                return stringFlavor;
            }
        return null;
    }

    /**
     * Import the given stream data into the text component.
     */
    protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead)
            throws BadLocationException, IOException {
        if (useRead) {
            int startPosition = c.getSelectionStart();
            int endPosition = c.getSelectionEnd();
            int length = endPosition - startPosition;
            EditorKit kit = c.getUI().getEditorKit(c);
            Document doc = c.getDocument();
            if (length > 0) {
                doc.remove(startPosition, length);
            }
            kit.read(in, doc, startPosition);
        } else {
            char[] buff = new char[1024];
            int nch;
            boolean lastWasCR = false;
            int last;
            StringBuffer sbuff = null;

            // Read in a block at a time, mapping \r\n to \n, as well as single
            // \r to \n.
            while ((nch = in.read(buff, 0, buff.length)) != -1) {
                if (sbuff == null) {
                    sbuff = new StringBuffer(nch);
                }
                last = 0;
                for (int counter = 0; counter < nch; counter++) {
                    switch (buff[counter]) {
                        case '\r':
                            if (lastWasCR) {
                                if (counter == 0) {
                                    sbuff.append('\n');
                                } else {
                                    buff[counter - 1] = '\n';
                                }
                            } else {
                                lastWasCR = true;
                            }
                            break;
                        case '\n':
                            if (lastWasCR) {
                                if (counter > (last + 1)) {
                                    sbuff.append(buff, last, counter - last - 1);
                                }
                                // else nothing to do, can skip \r, next write will
                                // write \n
                                lastWasCR = false;
                                last = counter;
                            }
                            break;
                        default:
                            if (lastWasCR) {
                                if (counter == 0) {
                                    sbuff.append('\n');
                                } else {
                                    buff[counter - 1] = '\n';
                                }
                                lastWasCR = false;
                            }
                            break;
                    }
                }
                if (last < nch) {
                    if (lastWasCR) {
                        if (last < (nch - 1)) {
                            sbuff.append(buff, last, nch - last - 1);
                        }
                    } else {
                        sbuff.append(buff, last, nch - last);
                    }
                }
            }
            if (lastWasCR) {
                sbuff.append('\n');
            }
            c.replaceSelection(sbuff != null ? sbuff.toString() : "");
        }
    }

// --- TransferHandler methods ------------------------------------

    /**
     * This is the type of transfer actions supported by the source.  Some models are
     * not mutable, so a transfer operation of COPY only should
     * be advertised in that case.
     *
     * @param c The component holding the data to be transfered.  This
     *          argument is provided to enable sharing of TransferHandlers by
     *          multiple components.
     * @return This is implemented to return NONE if the component is a JPasswordField
     *         since exporting data via user gestures is not allowed.  If the text component is
     *         editable, COPY_OR_MOVE is returned, otherwise just COPY is allowed.
     */
    public int getSourceActions(JComponent c) {
        if (c instanceof JPasswordField &&
                c.getClientProperty("JPasswordField.cutCopyAllowed") !=
                        Boolean.TRUE) {
            return NONE;
        }

        return ((JTextComponent) c).isEditable() ? COPY_OR_MOVE : COPY;
    }

/**
* Create a Transferable to use as the source for a data transfer.
*
* @param comp  The component holding the data to be transfered.  This
*  argument is provided to enable sharing of TransferHandlers by
*  multiple components.
* @return The representation of the data to be transfered.
*
*/
    protected Transferable createTransferable(JComponent comp) {
        exportComp = (JTextComponent)comp;
        shouldRemove = true;
        p0 = exportComp.getSelectionStart();
        p1 = exportComp.getSelectionEnd();
        return (p0 != p1) ? (new EditPaneTransferable((JEditorPane)exportComp, p0, p1)) : null;
    }

    /**
     * This method is called after data has been exported.  This method should remove
     * the data that was transfered if the action was MOVE.
     *
     * @param source The component that was the source of the data.
     * @param data   The data that was transferred or possibly null
     *               if the action is <code>NONE</code>.
     * @param action The actual action that was performed.
     */
    protected void exportDone(JComponent source, Transferable data, int action) {
        // only remove the text if shouldRemove has not been set to
        // false by importData and only if the action is a move
        if (shouldRemove && action == MOVE) {
            EditPaneTransferable t = (EditPaneTransferable)data;
            t.removeText();
            t.removeText();
        }

        exportComp = null;
    }

    public boolean importData(TransferSupport support) {
        isDrop = support.isDrop();

        if (isDrop) {
            modeBetween =
                    ((JTextComponent) support.getComponent()).getDropMode() == DropMode.INSERT;

            dropBias = ((JTextComponent.DropLocation) support.getDropLocation()).getBias();

            dropAction = support.getDropAction();
        }

        try {
            return super.importData(support);
        } finally {
            isDrop = false;
            modeBetween = false;
            dropBias = null;
            dropAction = MOVE;
        }
    }

    /**
     * This method causes a transfer to a component from a clipboard or a
     * DND drop operation.  The Transferable represents the data to be
     * imported into the component.
     *
     * @param comp The component to receive the transfer.  This
     *             argument is provided to enable sharing of TransferHandlers by
     *             multiple components.
     * @param t    The data to import
     * @return true if the data was inserted into the component, false otherwise.
     */
    public boolean importData(JComponent comp, Transferable t) {
        JTextComponent c = (JTextComponent) comp;

        int pos = modeBetween
                ? (c.getDropLocation()).getIndex()
                : c.getCaretPosition();

        // if we are importing to the same component that we exported from
        // then don't actually do anything if the drop location is inside
        // the drag location and set shouldRemove to false so that exportDone
        // knows not to remove any data
        if (dropAction == MOVE && c == exportComp && pos >= p0 && pos <= p1) {
            shouldRemove = false;
            return true;
        }

        boolean imported = false;
        DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c);
        if (importFlavor != null) {
            try {
                boolean useRead = false;
                if (comp instanceof JEditorPane) {
                    JEditorPane ep = (JEditorPane) comp;
                    if (!ep.getContentType().startsWith("text/plain") &&
                            importFlavor.getMimeType().startsWith(ep.getContentType())) {
                        useRead = true;
                    }
                }
                InputContext ic = c.getInputContext();
                if (ic != null) {
                    ic.endComposition();
                }



                if (modeBetween) {
                    Caret caret = c.getCaret();
                    if (caret instanceof DefaultCaret) {
                        ((DefaultCaret) caret).setDot(pos, dropBias);
                    } else {
                        c.setCaretPosition(pos);
                    }
                }

                 if (importFlavor.getMimeType().startsWith("application/x-java-file-list; class=java.util.List")) {
                     List<File> files = (List<File>)t.getTransferData(importFlavor);
                     notifyFileImportService(files, c, blog);
                 } else if(importFlavor.getMimeType().startsWith("text/uri-list; class=java.io.Reader")) {
                     BufferedReader reader = new BufferedReader((InputStreamReader)t.getTransferData(importFlavor));
                     String line = reader.readLine();
                     List<File> files = new ArrayList<File> ();
                    
                     while (line != null) {
                         if(line.startsWith("file://"))  {
                            try {
                                files.add(new File(new URI(line)));
                            } catch (URISyntaxException e) {
                                //shouldn't really happen but if it does print the line and try the next line.
                                e.printStackTrace();
                            }
                         }
                         line = reader.readLine();
                        
                     }
                     notifyFileImportService(files, c, blog);
                 } else {
                    Reader r = importFlavor.getReaderForText(t);
                    handleReaderImport(r, c, useRead);
                 }

                if (isDrop) {
                    c.requestFocus();
                    Caret caret = c.getCaret();
                    if (caret instanceof DefaultCaret) {
                        int newPos = caret.getDot();
                        Position.Bias newBias = ((DefaultCaret) caret).getDotBias();

                        ((DefaultCaret) caret).setDot(pos, dropBias);
                        ((DefaultCaret) caret).moveDot(newPos, newBias);
                    } else {
                        c.select(pos, c.getCaretPosition());
                    }
                }

                imported = true;
            } catch (UnsupportedFlavorException ufe) {
            } catch (BadLocationException ble) {
            } catch (IOException ioe) {
            }
        }
        return imported;
    }
   
    private void notifyFileImportService(List<File>files, JTextComponent comp, Blog blog) {
        ImportImageInfo info = new ImportImageInfo();
        info.setFiles(files);
        info.setCaretPos(comp.getCaret().getDot());
        info.setComponent((JEditorPane)comp);
        info.setOutputDir(blog.getOutputDir());
        NotificationCenter.getDefaultNotificationCenter().postNotification(ImageImporterService.START_IMPORT, info);
    }

    /**
     * This method indicates if a component would accept an import of the given
     * set of data flavors prior to actually attempting to import it.
     *
     * @param comp    The component to receive the transfer.  This
     *                argument is provided to enable sharing of TransferHandlers by
     *                multiple components.
     * @param flavors The data formats available
     * @return true if the data can be inserted into the component, false otherwise.
     */
    public boolean canImport(JComponent comp, DataFlavor[] flavors) {
        JTextComponent c = (JTextComponent) comp;
        if (!(c.isEditable() && c.isEnabled())) {
            return false;
        }
        return (getImportFlavor(flavors, c) != null);
    }
}
TOP

Related Classes of org.jriaffe.blog.panels.TextTransferHandler

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