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 }