001    /*
002     * ConversationArea.java
003     *
004     * Created on September 9, 2005, 6:26 PM
005     *
006     * To change this template, choose Tools | Options and locate the template under
007     * the Source Creation and Management node. Right-click the template and choose
008     * Open. You can then make changes to the template in the Source Editor.
009     */
010    
011    package com.valhalla.jbother;
012    
013    import javax.swing.*;
014    import javax.swing.text.*;
015    import java.awt.*;
016    import javax.swing.event.*;
017    import java.awt.event.*;
018    import java.io.*;
019    import java.util.*;
020    import java.util.regex.*;
021    
022    import com.valhalla.settings.*;
023    import com.valhalla.gui.*;
024    
025    /**
026     *
027     * @author synic
028     */
029    public class ConversationArea extends JScrollPane {
030        private ResourceBundle resources = ResourceBundle.getBundle(
031                "JBotherBundle", Locale.getDefault());
032    
033        JTextPane pane = new JTextPane();
034    
035        public static Color SENDER = new Color(128, 0, 0);
036        public static Color RECEIVER = new Color(22, 86, 158);
037        public static Color SERVER = new Color(0, 128, 0);
038        public static Color BLACK = Color.BLACK;
039        public static Color HL = new Color(248,192,192);
040        public static Color GRAY = Color.GRAY;
041        Document doc = pane.getDocument();
042        File file;
043        PrintWriter out = null;
044        OutputStreamWriter fw = null;
045        LinkedList textBuffer = new LinkedList();
046        LinkedList sasBuffer = new LinkedList();
047        javax.swing.Timer timer = new javax.swing.Timer(50, new ShowHandler());
048        boolean emoticons = true;
049    
050        /** Creates a new instance of ConversationArea */
051        public ConversationArea() {
052            pane.setBackground(Color.WHITE);
053            setViewportView(pane);
054            pane.setEditable(false);
055    
056            Color c = UIManager.getColor("TextPane.foreground");
057            pane.setForeground(c);
058    
059            setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
060    
061            addAdapter(pane);
062        }
063    
064        public void setEmoticons(boolean e) { this.emoticons = e; }
065    
066        /**
067         * Sets the log file
068         *
069         * @param logFile
070         *            the file to use for logging
071         * @param encoding
072         *            the encoding of the log file
073         *            (if null, empty, or invalid, the platform default encoding will
074         *            be used)
075         */
076        public void setLogFile(File logFile, String encoding) {
077            try {
078                fw = null;
079                try {
080                    fw = new OutputStreamWriter(
081                        new FileOutputStream(logFile, true), encoding);
082                } catch (UnsupportedEncodingException ex) {
083                } catch (NullPointerException ex) {
084                }
085                if (fw == null) {
086                    fw = new OutputStreamWriter(
087                        new FileOutputStream(logFile, true));
088                }
089                out = new PrintWriter(fw, true);
090            } catch (Exception ex) {
091                fw = null;
092            }
093        }
094    
095        public void closeLog()
096        {
097            try {
098                if( fw != null ) fw.close();
099            }
100            catch( Exception ex){}
101        }
102    
103        public void setText(String text) { pane.setText(text);}
104    
105        public JTextPane getTextPane() { return pane; }
106    
107        public void append( String text, Color color )
108        {
109            this.append(text, color, false, Color.WHITE);
110        }
111    
112        public void append( String text, Color color, boolean bold )
113        {
114            this.append(text, color, bold, Color.WHITE);
115        }
116    
117        public String getSelectedText() { return pane.getSelectedText(); }
118    
119        public void append( String text )
120        {
121            this.append(text, Color.BLACK);
122        }
123    
124        public void appendIcon(ImageIcon icon)
125        {
126            pane.insertIcon(icon);
127        }
128    
129        public void append( String text, final Color color,
130                final boolean bold, final Color background )
131        {
132            text = text.replaceAll("\n", " \n");
133            if( out != null )
134            {
135                out.print(text);
136                out.flush();
137            }
138    
139            SimpleAttributeSet sas = new SimpleAttributeSet();
140            StyleConstants.setForeground(sas, color);
141            StyleConstants.setBackground(sas, background);
142            StyleConstants.setBold(sas, bold);
143    
144            synchronized(textBuffer)
145            {
146                textBuffer.add(text);
147                sasBuffer.add(sas);
148                if(!timer.isRunning()) timer.start();
149                else timer.restart();
150            }
151        }
152    
153        private synchronized void showItem(final String text, final SimpleAttributeSet sas)
154        {
155            SwingUtilities.invokeLater(new Runnable()
156            {
157                public void run()
158                {
159                    final JScrollBar bar = getVerticalScrollBar();
160                    final int barPos = bar.getValue();
161    
162                    boolean end = bar.getValue() - ( bar.getMaximum() - bar.getModel().getExtent() ) >= -16;
163                    Point p = getViewport().getViewPosition();
164    
165                    boolean scrFlag = bar.isVisible();
166                    p.y += 50;  // just so it's not the first line (might scroll a bit)
167                    int pos = pane.viewToModel( p );
168    
169                    try {
170    
171                        ConversationFormatter.getInstance().replaceIcons(text, (StyledDocument)doc, sas, pane, emoticons);
172                        //doc.insertString(doc.getLength(), text , sas);
173    
174                        if( !end && scrFlag ) pane.setCaretPosition( pos );
175                        else pane.setCaretPosition(doc.getLength());
176    
177                    }
178                    catch( Exception blx )
179                    {
180                        //com.valhalla.Logger.logException( blx );
181                    }
182                }
183            });
184    
185            notify();
186    
187        }
188    
189        class ShowHandler implements ActionListener
190        {
191            public void actionPerformed(ActionEvent e)
192            {
193                synchronized(textBuffer)
194                {
195                    while(textBuffer.size() > 0)
196                    {
197                        String text = (String)textBuffer.removeFirst();
198                        SimpleAttributeSet sas = (SimpleAttributeSet)sasBuffer.removeFirst();
199                        showItem(text, sas);
200                    }
201    
202                    timer.stop();
203                }
204            }
205        }
206    
207        private void addAdapter(final JTextPane pane)
208        {
209            pane.addMouseListener(new MouseAdapter()
210            {
211                public void mouseReleased(MouseEvent ev)
212                {
213                    if(ev.getButton() != MouseEvent.BUTTON1) return;
214                    ev.consume();
215    
216                    if( pane.getSelectedText() != null ) return;
217                    int c = pane.viewToModel(ev.getPoint());
218                    String text = pane.getText().replaceAll("\n", " ");
219    
220                    if(c == -1) return;
221                    int b = text.lastIndexOf(" ", c);
222                    if( b == -1 ) b = 0;
223                    int e = text.indexOf(" ", c);
224                    if( e == -1 ) e = text.length();
225    
226                    String word = text.substring(b, e).trim();
227                    if(text.indexOf(" ") == -1) word = text;
228    
229                    String app = null;
230                    boolean match = false;
231    
232                    if( Pattern.matches("^.*(\\s|^)((?!(ftp|https?)://)[^\\s\"'\\/]+?@[^\\s\"'\\/]+?)(\\s|$).*$", word))
233                    {
234                        app = Settings.getInstance().getProperty("emailApplication");
235                        match = true;
236                    }
237                    else if( Pattern.matches("^.*(^|\\s)((ftp|http|https)://[^\\s\"']+?)(\\s|$).*$", word))
238                    {
239                        app = Settings.getInstance().getProperty("browserApplication");
240                        match = true;
241                    }
242    
243                    if( match )
244                    {
245                        try {
246                            if (app != null && !app.equals("")) {
247                                /* tail */
248                                String command = app
249                                        .replaceAll("%s", word);
250                                command = command.replaceAll("\\%l", word);
251    
252                                Runtime.getRuntime().exec(command);
253                            /* tail */
254                            } else
255                                Standard.warningMessage(BuddyList.getInstance().getContainerFrame(), resources
256                                        .getString("hyperlink"), resources
257                                        .getString("noApplication"));
258                        } catch (java.io.IOException ex) {
259                            Standard.warningMessage(BuddyList.getInstance().getContainerFrame(), resources.getString("hyperlink"),
260                                    resources.getString("errorExecutingApplication"));
261                        }
262                    }
263                }
264            });
265        }
266    }