Package net.cakenet.jsaton.ui.tools

Source Code of net.cakenet.jsaton.ui.tools.LogPanel

/*
* Created by JFormDesigner on Tue Feb 12 15:26:54 GMT 2013
*/

package net.cakenet.jsaton.ui.tools;

import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.HTML;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

/**
* @author James Lawrence
*/
public class LogPanel extends JPanel {
    private static final int LOG_LENGTH = 3000;

    private final Object docLock = new Object(); // lock on modifying the document
    private AttributeSet inAttrs; // attributes used for user input
    private LogInputStream in = new LogInputStream(); // an input stream for the user input (updated on newline)
    private StyledDocument doc; // our document
    private String input; // the input string (buffer)
    private int outOff; // our offset into the document for output
    private transient boolean output; // whether the current write/delete operation is for output

    // hyperlink shit... (bad to put it here, I know)
    private int hyperlinkOff = -1;
    private int hyperlinkEndCount;
    private boolean hyperlinkStart, inHyperlink;
    private String hyperlink;
    private java.util.List<Character> validChars = Arrays.asList('/', '%', '.', '\\', '_', '&', '#', ';', '+', '=',
            '-', ':', ' ');

    public LogPanel() {
        initComponents();
        this.doc = (StyledDocument) messages.getDocument();

        // This filter makes sure that input text is styled and that the output isn't modifiable
        ((AbstractDocument) doc).setDocumentFilter(new DocumentFilter() {
            private void updateInput() throws BadLocationException {
                input = doc.getText(outOff, doc.getLength() - outOff);
                if (input.isEmpty())
                    input = null;
            }

            public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
                final boolean in = !output;
                if (in) {
                    if (offset < outOff) {
                        length -= outOff - offset;
                        offset = outOff;
                    }
                }
                super.remove(fb, offset, length);
                if (in && length != 0)
                    updateInput();
            }

            public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
                final boolean in = !output;
                if (in) {
                    if (offset < outOff) {
                        length -= outOff - offset;
                        offset = outOff;
                    }
                }
                super.replace(fb, offset, length, text, inAttrs);
                if (in) {
                    updateInput();
                    if (text.contains("\n")) {
                        synchronized (docLock) {
                            // New line, we need to push the input!
                            int idx = input.lastIndexOf('\n');
                            String toPush = input.substring(0, idx + 1); // inlclude the newline
                            outOff += toPush.length();
                            updateInput();
                            synchronized (LogPanel.this.in.buffer) {
                                for (char c : toPush.toCharArray())
                                    LogPanel.this.in.buffer.add(c);
                            }
                        }
                    }
                }

            }
        });
        SimpleAttributeSet sas = new SimpleAttributeSet();
        StyleConstants.setForeground(sas, Color.GREEN);
        inAttrs = sas;
        doc.setCharacterAttributes(0, 1, inAttrs, false);
        messages.setEditable(true);
    }

    private void _write(String s, AttributeSet attrs, boolean in) {
        synchronized (docLock) {
            output = !in;
            int startOff = -1;
            try {
                startOff = outOff;
                int caretPos = messages.getCaretPosition();
                doc.insertString(outOff, s, attrs);
                if (output)
                    outOff += s.length();
                // If the element before the one we're inserting has the same attributes as us, change the range...
                if (startOff != 0) {
                    Element prev = doc.getCharacterElement(startOff);
                    AttributeSet prevAttrs = prev.getAttributes();
                    if (prevAttrs.getAttributeCount() == attrs.getAttributeCount() && prevAttrs.containsAttributes(attrs)) {
                        doc.setCharacterAttributes(startOff, outOff - startOff, attrs, true); // merge...
                    }
                }
                if (caretPos >= startOff)
                    messages.setCaretPosition(outOff);
            } catch (BadLocationException e) {
                e.printStackTrace();
            }

            // Nasty hyperlink stuff...
            char[] charArray = s.toCharArray();
            for (int i1 = 0; i1 < charArray.length; i1++) {
                int off = startOff + i1 + 1;
                char b = charArray[i1];
                if (inHyperlink) {
                    boolean valid = Character.isLetterOrDigit(b);
                    valid |= validChars.contains(b);
                    if (valid) {
                        hyperlink += b;
                    } else {
                        inHyperlink = false;
                        SimpleAttributeSet hyperlinkAttrs = new SimpleAttributeSet();
                        StyleConstants.setForeground(hyperlinkAttrs, Color.BLUE);
                        StyleConstants.setUnderline(hyperlinkAttrs, true);
                        hyperlinkAttrs.addAttribute(HTML.Attribute.HREF, hyperlink);
                        doc.setCharacterAttributes(off - (hyperlink.length() + 1), hyperlink.length(), hyperlinkAttrs, true);
                    }
                }
                if (b == ':') {
                    hyperlinkStart = true;
                    hyperlinkOff = off - 1;
                } else if (hyperlinkStart && b == '/') {
                    hyperlinkEndCount++;
                    if (hyperlinkEndCount == 2) {
                        try {
                            String text = doc.getText(0, hyperlinkOff);
                            String proto = "";
                            for (int i = text.length() - 1; i >= 0; i--) {
                                char c = text.charAt(i);
                                if (!Character.isLetter(c))
                                    break;
                                proto = c + proto;
                                hyperlinkOff = i;
                            }
                            hyperlink = proto + "://";
                            inHyperlink = true;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        hyperlinkEndCount = 0;
                        hyperlinkOff = off - 1;
                        hyperlinkStart = false;
                    }
                } else {
                    hyperlinkOff = -1;
                    hyperlinkEndCount = 0;
                    hyperlinkStart = false;
                }
            }
        }
    }

    public synchronized void write(String s, AttributeSet attrs) {
        _write(s, attrs, false);
        synchronized (docLock) {
            final int inputLen = input == null ? 0 : input.length();
            final int toDelete = (outOff - inputLen) - LOG_LENGTH;
            if (toDelete > 0) {
                try {
                    int idx = doc.getText(0, toDelete).indexOf('\n') + 1;
                    if (idx != 0) {
                        doc.remove(0, idx);
                        outOff -= idx;
                    }
                } catch (BadLocationException e) {
                /**/
                }
            }
        }
        output = false;
    }

    public LogOutputStream createOutputStream(OutputStream delegate, AttributeSet attrs) {
        return new LogOutputStream(delegate, attrs);
    }

    public LogInputStream getInputStream() {
        return in;
    }

    public void setInputStreamAttributes(AttributeSet attrs) {
        inAttrs = attrs;
    }

    private void messagesMouseMoved(MouseEvent e) {
        getLink(e.getPoint());
    }

    private void messagesMouseReleased(MouseEvent e) {
    }

    private void messagesMouseClicked(MouseEvent e) {
        String link = getLink(e.getPoint());
        if (link == null)
            return;
        try {
            if (!Desktop.isDesktopSupported()) {
                System.err.println("Unable to open link, your OS doesn't support java desktop interaction");
                return;
            }
            if (link.startsWith("http://") || link.startsWith("https://")) {
                URL url = new URL(link);
                Desktop.getDesktop().browse(url.toURI());
            } else {
                int protIdx = link.indexOf(":");
                String protocol = link.substring(0, protIdx);
                String path = link.substring(protIdx + 3);
                if (protocol.equals("file")) {
                    File f = new File(path);
                    Desktop.getDesktop().open(f);
                } else {
                    System.out.println("Unhandled protocol: " + protocol);
                }
            }
        } catch (IOException | URISyntaxException e1) {
            e1.printStackTrace();
        }
    }

    Cursor prevCursor;

    private String getLink(Point p) {
        int pos = messages.viewToModel(p);
        Element at = doc.getCharacterElement(pos);
        AttributeSet attrs = at.getAttributes();
        Object href = attrs.getAttribute(HTML.Attribute.HREF);
        if (href != null && prevCursor == null) {
            prevCursor = messages.getCursor();
            messages.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        } else if (href == null && prevCursor != null) {
            messages.setCursor(prevCursor);
            prevCursor = null;
        }
        if (href != null) {
            return (String) href;
        }
        return null;
    }

    public class LogOutputStream extends OutputStream {
        private OutputStream delegate;
        private AttributeSet attributes;

        public LogOutputStream(OutputStream delegate, AttributeSet attrs) {
            this.delegate = delegate;
            this.attributes = attrs;
        }

        public void write(byte[] b, int off, int len) throws IOException {
            LogPanel.this.write(new String(b, off, len), attributes);
            if (delegate != null)
                delegate.write(b, off, len);
        }

        public void write(int b) throws IOException {
            LogPanel.this.write(new String(new byte[]{(byte) b}), attributes);
            if (delegate != null)
                delegate.write(b);
        }
    }

    public class LogInputStream extends InputStream {
        private final Queue<Character> buffer = new LinkedList<>();

        public int read(byte[] b, int off, int len) throws IOException {
            int toRead = Math.min(available(), len);
            for (int i = 0; i < toRead; i++)
                b[off + i] = (byte) read();
            return toRead;
        }

        public int read() throws IOException {
            boolean empty;
            synchronized (buffer) {
                empty = buffer.isEmpty();
                if (empty) {
                    try {
                        buffer.wait();
                    } catch (InterruptedException e) {
                /**/
                    }
                }
                return buffer.poll() & 0xff;
            }
        }

        public int available() throws IOException {
            return buffer.size();
        }
    }

    private void initComponents() {
        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
        scrollPanel = new JScrollPane();
        noWrapPanel = new JPanel();
        messages = new JTextPane();
        commands = new JPanel();
        button1 = new JButton();
        button2 = new JButton();
        wrapToggle = new JToggleButton();
        clearAction = new ClearAction();
        toggleWordwrapAction = new ToggleWordWrapAction();
        scrollToBottomAction = new ScrollToBottomAction();

        //======== this ========
        setLayout(new BorderLayout());

        //======== scrollPanel ========
        {

            //======== noWrapPanel ========
            {
                noWrapPanel.setLayout(new BorderLayout());

                //---- messages ----
                messages.setEditable(false);
                messages.setContentType("text/rtf");
                messages.addMouseMotionListener(new MouseMotionAdapter() {
                    @Override
                    public void mouseMoved(MouseEvent e) {
                        messagesMouseMoved(e);
                    }
                });
                messages.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        messagesMouseClicked(e);
                    }

                    @Override
                    public void mouseReleased(MouseEvent e) {
                        messagesMouseReleased(e);
                    }
                });
                noWrapPanel.add(messages, BorderLayout.CENTER);
            }
            scrollPanel.setViewportView(noWrapPanel);
        }
        add(scrollPanel, BorderLayout.CENTER);

        //======== commands ========
        {
            commands.setLayout(new BoxLayout(commands, BoxLayout.Y_AXIS));

            //---- button1 ----
            button1.setAction(clearAction);
            button1.setHideActionText(true);
            button1.setMargin(new Insets(2, 2, 2, 2));
            commands.add(button1);

            //---- button2 ----
            button2.setMargin(new Insets(2, 2, 2, 2));
            button2.setAction(scrollToBottomAction);
            button2.setHideActionText(true);
            commands.add(button2);

            //---- wrapToggle ----
            wrapToggle.setAction(toggleWordwrapAction);
            wrapToggle.setHideActionText(true);
            wrapToggle.setMargin(new Insets(2, 2, 2, 2));
            commands.add(wrapToggle);
        }
        add(commands, BorderLayout.WEST);
        // JFormDesigner - End of component initialization  //GEN-END:initComponents
    }

    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
    private JScrollPane scrollPanel;
    private JPanel noWrapPanel;
    private JTextPane messages;
    private JPanel commands;
    private JButton button1;
    private JButton button2;
    private JToggleButton wrapToggle;
    private ClearAction clearAction;
    private ToggleWordWrapAction toggleWordwrapAction;
    private ScrollToBottomAction scrollToBottomAction;
    // JFormDesigner - End of variables declaration  //GEN-END:variables

    private class ClearAction extends AbstractAction {
        private ClearAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            putValue(NAME, "Clear");
            putValue(SHORT_DESCRIPTION, "Clear the log");
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/icons/delete.png")));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            synchronized (docLock) {
                output = true; // Only way this will work...
                try {
                    doc.remove(0, outOff);
                } catch (BadLocationException e1) {
                    /**/
                }
                output = false;
                outOff = 0;
            }
        }
    }

    private class ToggleWordWrapAction extends AbstractAction {
        private ToggleWordWrapAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            putValue(NAME, "Toggle wordwrap");
            putValue(SHORT_DESCRIPTION, "Toggle word wrap in the log");
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/icons/doc_resize_actual.png")));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            if (wrapToggle.isSelected()) {
                noWrapPanel.remove(messages);
                scrollPanel.setViewportView(messages);
            } else {
                noWrapPanel.add(messages);
                scrollPanel.setViewportView(noWrapPanel);
            }
        }
    }

    private class ScrollToBottomAction extends AbstractAction {
        private ScrollToBottomAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            putValue(NAME, "Scroll to bottom");
            putValue(SHORT_DESCRIPTION, "Scroll to the bottom");
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/icons/application_put.png")));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            messages.setCaretPosition(doc.getLength());
        }
    }
}
TOP

Related Classes of net.cakenet.jsaton.ui.tools.LogPanel

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.