001    /*
002            Copyright (C) 2003 Adam Olsen
003    
004            This program is free software; you can redistribute it and/or modify
005            it under the terms of the GNU General Public License as published by
006            the Free Software Foundation; either version 1, or (at your option)
007            any later version.
008    
009            This program is distributed in the hope that it will be useful,
010            but WITHOUT ANY WARRANTY; without even the implied warranty of
011            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012            GNU General Public License for more details.
013    
014            You should have received a copy of the GNU General Public License
015            along with this program; if not, write to the Free Software
016            Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
017    */
018    package com.valhalla.jbother;
019    
020    import java.awt.*;
021    import java.awt.event.*;
022    import java.util.*;
023    import java.util.regex.Pattern;
024    
025    import javax.swing.*;
026    import javax.swing.event.HyperlinkEvent;
027    import javax.swing.event.HyperlinkListener;
028    import javax.swing.text.html.HTMLDocument;
029    import javax.swing.text.html.HTMLEditorKit;
030    import javax.swing.text.html.StyleSheet;
031    
032    import com.valhalla.gui.*;
033    import com.valhalla.jbother.*;
034    import com.valhalla.settings.Settings;
035    
036    /**
037     * Represents an appending JEditorPane.
038     * Text appended to this pane will cause it's scrollbar to scroll to the bottom.
039     * Also handles hyperlink events
040     *
041     * @author Adam Olsen
042     * @version 1.0
043    */
044    public class ConversationArea extends JEditorPane implements HyperlinkListener
045    {
046            private ResourceBundle resources = ResourceBundle.getBundle( "JBotherBundle", Locale.getDefault() );
047            private JScrollPane scroll;
048            private HTMLEditorKit kit = (HTMLEditorKit)getEditorKit();
049            private HTMLDocument doc = (HTMLDocument)getDocument();
050            private String lastHTML;
051            private javax.swing.Timer timer = new javax.swing.Timer( 100, new ShowHandler() );
052            private StringBuffer buffer = new StringBuffer();
053            private StyleSheet myStyleSheet = new StyleSheet();
054    
055            /**
056             * Default constructor - sets up the JEditorPane
057             */
058            public ConversationArea()
059            {
060                    super( "text/html; enctype='UTF-8'", "" );
061    
062                    String fontString = Settings.getInstance().getProperty( "messageWindowFont" );
063                    Font windowFont;
064                    if( fontString == null ) windowFont = new Font( "Sans", Font.PLAIN, 12 );
065                    else windowFont = Font.decode( fontString );
066    
067                    loadStyleSheet( windowFont );
068    
069                    kit.setStyleSheet( myStyleSheet );
070                    doc = new HTMLDocument( myStyleSheet );
071                    setDocument( doc );
072                    setEditable( false );
073    
074                    addHyperlinkListener( this );
075                    setBackground( Color.WHITE );
076            }
077    
078            /**
079             * Loads a new font for the JEditorPane
080             * @param windowFont the font to load
081            */
082            public void loadStyleSheet( Font windowFont )
083            {
084                    String ruleString = "font-size: " + windowFont.getSize() + "; font-family: " + windowFont.getName() + "; ";
085                    if( windowFont.isItalic() ) ruleString += "font-style: italic; ";
086                    else ruleString += "font-style: normal; ";
087    
088                    if( windowFont.isBold() ) ruleString += "font-weight: bold; ";
089                    else ruleString += "font-weight: normal; ";
090    
091                    myStyleSheet.addRule( "body { " + ruleString + " }" );
092                    myStyleSheet.addRule( "body a { color: #5B62BC; text-decoration: underline }" );
093            }
094    
095            /**
096             * Sets this window's scrollbar
097             * @param scroll the scrollbar
098            */
099            public void setScroll( JScrollPane scroll )
100            {
101                    this.scroll = scroll;
102            }
103    
104            /**
105             * Appends text to the ConversationArea
106             * Scrolls down if the scrollbar is already at the bottom, or
107             * does nothing if the person has scrolled up any
108             * @param html the text to append
109            */
110            public void append( String html )
111            {
112                    html = html.replaceAll( "\\n", "<br>" );
113                    lastHTML = html;
114                    buffer.append( html );
115                    if( !timer.isRunning() ) timer.start();
116                    else timer.restart();
117            }
118    
119            /**
120             * Actually appends the buffered data to the JEditorPane
121             * @author Adam Olsen
122             * @version 2.0
123            */
124            class ShowHandler implements ActionListener
125            {
126                    public synchronized void actionPerformed( ActionEvent ae )
127                    {
128                            JScrollBar bar = scroll.getVerticalScrollBar();
129    
130                            boolean end = bar.getValue() - ( bar.getMaximum() - bar.getModel().getExtent() ) >= -16;
131                            Point p = scroll.getViewport().getViewPosition();
132    
133                            boolean scrFlag = bar.isVisible();
134                            p.y += 50;  // just so it's not the first line (might scroll a bit)
135                            int pos = viewToModel( p );
136    
137                            if( pos < 0 ) pos = 0;
138    
139                            try {
140    
141                                    kit.insertHTML( doc, doc.getLength(), buffer.toString().replaceAll( "\\<br\\>$", "" ), 0, 0, null );
142    
143                                    if( !end && scrFlag ) setCaretPosition( pos );
144                                    else setCaretPosition( doc.getLength() );
145                                    scroll.repaint();
146                            }
147                            catch( Exception e )
148                            {
149                                    com.valhalla.Logger.debug( "ShowHandler.class Exception: " + e.toString() );
150                            }
151    
152                            buffer = new StringBuffer();
153                            timer.stop();
154                    }
155            }
156    
157            /**
158             * @return the last appended HTML
159            */
160            public String getLastHTML() { return this.lastHTML; }
161    
162            /**
163             * Moves the cursor to the end of the editorpane
164             */
165            public void moveToEnd()
166            {
167                    try {
168                            setCaretPosition( doc.getLength() );
169                    }
170                    catch( Exception e ) { }
171            }
172    
173            /**
174             * Is called when someone clicks on a hyperlink in the window
175            **/
176            public void hyperlinkUpdate( HyperlinkEvent e )
177            {
178                    if( e.getEventType() == HyperlinkEvent.EventType.ACTIVATED )
179                    {
180                            try {
181                                    String app = Settings.getInstance().getProperty( "browserApplication" );
182                                    if( Pattern.matches( "^mailto\\:.*", e.getURL().toString() ) ) app = Settings.getInstance().getProperty( "emailApplication" );
183    
184                                    if( app != null && !app.equals( "" ) ) {
185                                            /*tail*/
186                        String command = app.replaceAll( "%s", e.getURL().toString() );
187                        command = command.replaceAll( "\\%l", e.getURL().toString() ); // for backwards compatability
188    
189                        Runtime.getRuntime().exec( command );
190                                            /*tail*/
191                    }
192                                    else Standard.warningMessage( this, resources.getString( "hyperlink" ),
193                                            resources.getString( "noApplication" ) );
194                            }
195                            catch( java.io.IOException ex ) { Standard.warningMessage( this, resources.getString( "hyperlink" ),
196                                    resources.getString( "errorExecutingApplication" ) ); }
197                    }
198            }
199    }