001    /*
002     *  Copyright (C) 2003 Adam Olsen
003     *  This program is free software; you can redistribute it and/or modify
004     *  it under the terms of the GNU General Public License as published by
005     *  the Free Software Foundation; either version 1, or (at your option)
006     *  any later version.
007     *  This program is distributed in the hope that it will be useful,
008     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
009     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
010     *  GNU General Public License for more details.
011     *  You should have received a copy of the GNU General Public License
012     *  along with this program; if not, write to the Free Software
013     *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
014     */
015    package com.valhalla.jbother;
016    
017    import java.awt.Font;
018    import java.awt.event.*;
019    import java.io.File;
020    import java.text.SimpleDateFormat;
021    import java.util.Calendar;
022    import java.util.Date;
023    import java.util.Locale;
024    import java.util.ResourceBundle;
025    import java.util.TimeZone;
026    
027    import javax.swing.*;
028    
029    import com.valhalla.gui.*;
030    import com.valhalla.jbother.jabber.BuddyStatus;
031    import com.valhalla.jbother.plugins.events.MessageReceivedEvent;
032    import com.valhalla.settings.Settings;
033    import net.infonode.tabbedpanel.*;
034    import net.infonode.tabbedpanel.titledtab.*;
035    import net.infonode.util.*;
036    import org.jivesoftware.smack.packet.*;
037    
038    
039    /**
040     * Provides common tools for conversation windows (such as logging). Must be
041     * extended.
042     *
043     * @author Adam Olsen
044     * @author Yury Soldak (tail)
045     * @author Andrey Zakirov
046     * @created April 10, 2005
047     * @version 1.1
048     * @see com.valhalla.jbother.jabber.BuddyStatus
049     */
050    public abstract class ConversationPanel extends JPanel implements
051            LogViewerCaller, TabFramePanel {
052        private ResourceBundle resources = ResourceBundle.getBundle(
053                "JBotherBundle", Locale.getDefault());
054    
055        protected BuddyStatus buddy;
056        private TitledTab tab;
057        protected Message lastReceived = null;
058        MJTextArea textEntryArea = new MJTextArea(true);
059    
060        public void ConversationPanel(BuddyStatus buddy) {this.buddy = buddy;}
061    
062        /**
063         * the conversation area
064         */
065        protected ConversationArea conversationArea = new ConversationArea();
066    
067        private int oldMaximum = 0;
068    
069        /**
070         * the close timer
071         */
072        protected javax.swing.Timer timer = new javax.swing.Timer(600000,
073                new CloseListener());
074    
075        public JFrame getFrame() { return frame; }
076    
077        /**
078         * the containing frame
079         */
080        protected JFrame frame = null;
081    
082        /**
083         * if the listeners have been added
084         */
085        protected boolean listenersAdded = false;
086        public void setTab( TitledTab tab ) { this.tab = tab; }
087        public TitledTab getTab() { return tab; }
088    
089        /**
090         * Sets up the defaults in the ConversationPanel
091         *
092         * @param buddy
093         *            the buddy that this window corresponds to
094         */
095        public ConversationPanel(BuddyStatus buddy) {
096            this.buddy = buddy;
097            startLog();
098        }
099    
100    
101    
102        /**
103         * Listens for a right click - and displays a menu if it's caught
104         *
105         * @author Adam Olsen
106         * @created October 26, 2004
107         * @version 1.0
108         */
109        class RightClickListener extends MouseAdapter {
110            private JPopupMenu menu;
111    
112            private JMenuItem item = new JMenuItem(resources
113                    .getString("closeConversation"));
114    
115            private boolean containsCloseItem = false;
116    
117            /**
118             * Constructor for the RightClickListener object
119             *
120             * @param menu
121             *            the menu
122             */
123            public RightClickListener(JPopupMenu menu) {
124                this.menu = menu;
125            }
126    
127            /**
128             * Description of the Method
129             *
130             * @param e
131             *            the event
132             */
133            public void mousePressed(MouseEvent e) {
134                checkPop(e);
135            }
136    
137            /**
138             * @param e
139             *            the mouse event
140             */
141            public void mouseReleased(MouseEvent e) {
142                checkPop(e);
143            }
144    
145            /**
146             * @param e
147             *            the mouse event
148             */
149            public void mouseClicked(MouseEvent e) {
150                checkPop(e);
151            }
152    
153            /**
154             * @param e
155             *            the mouse event
156             */
157            public void checkPop(MouseEvent e) {
158                if (e.isPopupTrigger()) {
159                    if (conversationArea.getSelectedText() != null)
160                        return;
161                    if (Settings.getInstance().getBoolean("useTabbedWindow")
162                            && !containsCloseItem) {
163                        containsCloseItem = true;
164                        menu.add(item);
165                        item.addActionListener(new ActionListener() {
166                            public void actionPerformed(ActionEvent e) {
167                                checkCloseHandler();
168                            }
169                        });
170                    } else if (!Settings.getInstance()
171                            .getBoolean("useTabbedWindow")
172                            && containsCloseItem) {
173                        containsCloseItem = false;
174                        menu.remove(item);
175                    }
176    
177                    // if a right click is detected, show the popup menu
178                    menu.show(e.getComponent(), e.getX(), e.getY());
179                }
180            }
181        }
182    
183        /**
184         * @return true if the TabFrame panel listeners have already been added to
185         *         this panel
186         */
187        public boolean listenersAdded() {
188            return listenersAdded;
189        }
190    
191        /**
192         * Sets whether or not the TabFrame panel listeners have been added
193         *
194         * @param added
195         *            true if they have been added
196         */
197        public void setListenersAdded(boolean added) {
198            this.listenersAdded = added;
199        }
200    
201        /**
202         * @return the input area of this panel
203         */
204        public JComponent getInputComponent() {
205            return conversationArea;
206        }
207       
208        /**
209         * @return A string containing the tooltip that should be displayed for
210         * this tab
211         */
212        public String getPanelToolTip()
213        {
214            return getWindowTitle();
215        }
216    
217        /**
218         * @return the name of the tab in the TabFrame
219         */
220        public String getPanelName() {
221            if (buddy != null) {
222                String n = buddy.getName();
223                if (n == null)
224                    n = buddy.getUser();
225                int i = n.indexOf("@");
226                if (i > -1)
227                    n = n.substring(0, i);
228                n = n.replaceAll("%.*", "");
229                if (n.length() >= 10 )
230                {
231                    n = n.substring(0, 7) + "...";
232                }
233                return n;
234            } else {
235                return "blank";
236            }
237        }
238    
239        /**
240         * @return the title of the window in the TabFrame
241         */
242        public String getWindowTitle() {
243            if (buddy != null) {
244                if( buddy.getName() == null )
245                {
246                    return buddy.getUser();
247                }
248    
249                if ((buddy.getName().toLowerCase ()).matches(buddy.getUser()))
250                {
251                    return buddy.getName();
252                }
253                else
254                {
255                    return buddy.getName() +  " (" + buddy.getUser() + ")";
256                }
257            }
258            else {
259                return "Blank Message";
260            }
261        }
262    
263        /**
264         * @return the tooltip for this tab in the TabFrame
265         */
266        public String getTooltip() {
267            if (buddy != null) {
268                return buddy.getUser();
269            } else {
270                return "Blank Message";
271            }
272        }
273    
274        /**
275         * @return the containing frame for this panel
276         */
277        public JFrame getContainingFrame() {
278            return frame;
279        }
280    
281        /**
282         * @param frame
283         *            the new containing frame
284         */
285        public void setContainingFrame(JFrame frame) {
286            this.frame = frame;
287        }
288    
289        /**
290         * Returns the buddy status for this dialog
291         *
292         * @return the buddy passed in to the constructor
293         */
294        public BuddyStatus getBuddy() {
295            return buddy;
296        }
297    
298        /**
299         * Listens for a close event, and either makes the dialog hidden or removes
300         * it from the dialog tracker
301         *
302         * @author Adam Olsen
303         * @created October 26, 2004
304         * @version 1.0
305         */
306        class CloseListener implements ActionListener {
307            /**
308             * @param e
309             *            the event
310             */
311            public void actionPerformed(ActionEvent e) {
312                timer.stop();
313                closeHandler();
314                timer = null;
315            }
316        }
317    
318        /**
319         * Checks to see if we are preserving messages. If so, it starts the timer,
320         * otherwise it just closes the panel
321         */
322        public void checkCloseHandler() {
323            if (Settings.getInstance().getProperty("preserveMessages") == null) {
324                closeHandler();
325            } else {
326                startTimer();
327                if (!Settings.getInstance().getBoolean("useTabbedWindow")) {
328                    frame.setVisible(false);
329                } else {
330                    BuddyList.getInstance().removeTabPanel(this);
331                }
332            }
333        }
334    
335        /**
336         * Destroys the dialog, disposes the containing frame if there is one and
337         * removes the panel from the TabFrame if required.
338         */
339        public void closeHandler() {
340            if(this instanceof ChatPanel) ((ChatPanel)this).removeScroll();
341            textEntryArea.getInputMap().clear();
342            textEntryArea = null;
343            closeLog();
344            if (frame != null) {
345                frame.setVisible(false);
346                frame.dispose();
347            }
348    
349            if (buddy != null) {
350                com.valhalla.Logger.debug("Closing ConversationPanel for "
351                        + buddy.getUser());
352                buddy.setConversation(null);
353            }
354    
355            if (Settings.getInstance().getBoolean("useTabbedWindow")) {
356                BuddyList.getInstance().removeTabPanel(this);
357            }
358    
359            timer.stop();
360            com.valhalla.Logger.debug("timer is now null");
361            timer = null;
362    
363            MessageDelegator.getInstance().removePanel(this);
364        }
365    
366        /**
367         * Opens a <code>com.valhalla.jbother.LogViewerDialog</code>
368         */
369        public void openLogWindow() {
370            new LogViewerDialog(this, buddy.getUser());
371        }
372    
373        /**
374         * Opens a log and starts it.
375         */
376        public void startLog() {
377            if (buddy == null)
378                return;
379            // for loggingg
380            if (Settings.getInstance().getBoolean("keepLogs")) {
381                String logFileName = LogViewerDialog.getDateName() + ".log";
382                String logFileDir = JBother.profileDir
383                        + File.separatorChar
384                        + "logs"
385                        + File.separatorChar
386                        + this.buddy.getUser().replaceAll("@", "_at_").replaceAll(
387                                "\\/", "-");
388                File logDir = new File(logFileDir);
389    
390                if (!logDir.isDirectory() && !logDir.mkdirs()) {
391                    Standard.warningMessage(this, resources.getString("log"),
392                            resources.getString("couldNotCreateLogDir"));
393                }
394    
395                String logEnc =
396                    Settings.getInstance().getProperty("keepLogsEncoding");
397                conversationArea.setLogFile(new File(logDir, logFileName), logEnc);
398    
399            }
400        }
401    
402        /**
403         * Sets a message to offline (displays "this message is offline")
404         */
405        public void setOfflineMessage() {
406        }
407    
408        /**
409         * @return a String representing the current time the format:
410         *         [Hour:Minute:Second]
411         * @param d
412         *            the date stamp to use
413         */
414        public static String getDate(Date d) {
415            if (d != null) {
416                // calculation of the offset is no longer needed
417                // perhaps a smack change?
418                //d = new Date(d.getTime()
419                  //      + TimeZone.getDefault().getOffset(d.getTime()));
420            } else {
421                d = new Date();
422            }
423    
424            Calendar mtime = Calendar.getInstance();
425            mtime.setTime(d);
426    
427            Calendar today = Calendar.getInstance();
428            today.setTime(new Date());
429    
430            SimpleDateFormat formatter = new SimpleDateFormat("[HH:mm:ss]");
431    
432            if (mtime.get(Calendar.MONTH) != today.get(Calendar.MONTH)
433                    || mtime.get(Calendar.DAY_OF_MONTH) != today
434                            .get(Calendar.DAY_OF_MONTH)
435                    || mtime.get(Calendar.YEAR) != today.get(Calendar.YEAR)) {
436                formatter = new SimpleDateFormat("[MM/dd@HH:mm]");
437            }
438    
439            String date = formatter.format(d);
440            return date;
441        }
442    
443        /**
444         * Receives a message
445         *
446         * @param sbj
447         *            the subject of the message
448         * @param body
449         *            the message body
450         * @param resource
451         *            the message resource
452         * @param date
453         *            the timestamp when the message was received
454         */
455        public void receiveMessage(String sbj, String delayInfo, String body, String resource,
456                Date date, boolean decryptedFlag, boolean verifiedFlag) {
457            receiveMessage();
458        }
459    
460        /**
461         * Calls the received message events
462         */
463        public void receiveMessage() {
464            MessageDelegator.getInstance().showPanel(this);
465    
466    
467            stopTimer();
468    
469            JFrame f = frame;
470            boolean isFocused = true;
471            if (Settings.getInstance().getBoolean("useTabbedWindow")) {
472                f = BuddyList.getInstance().getTabFrame();
473                BuddyList.getInstance().getTabFrame().markTab(this, false);
474                isFocused = f.isFocused();
475            } else {
476                if (!frame.isVisible()) {
477                    frame.setVisible(true);
478                } else if (Settings.getInstance().getBoolean("focusWindow")) {
479                    isFocused = frame.isFocused();
480                    frame.toFront();
481                } else
482                    isFocused = frame.isFocused();
483            }
484    
485    
486            if (Settings.getInstance().getBoolean("usePopup")
487                // && !isFocused
488                    && f != null) {
489                if (buddy != null && buddy.getName() != null) {
490                    NotificationPopup.showSingleton(f, resources
491                            .getString("messageReceived"), "<b>"
492                            + resources.getString("from") + ":</b>&nbsp;&nbsp;"
493                            + buddy.getName(),this);
494                } else {
495                    NotificationPopup.showSingleton(f, resources
496                            .getString("messageReceived"), "Message Received",this);
497                }
498            }
499    
500            com.valhalla.jbother.sound.SoundPlayer.play("receivedSound");
501    
502        }
503    
504        public void setLastReceivedMessage(Message message) {
505            this.lastReceived = message;
506    
507            MessageReceivedEvent event = new MessageReceivedEvent(this);
508    
509            Message newMessage = new Message( message.getTo(), message.getType() );
510            newMessage.setSubject(message.getSubject());
511            newMessage.setBody(message.getBody());
512            newMessage.setThread(message.getThread());
513            event.setMessage(newMessage);
514            com.valhalla.pluginmanager.PluginChain.fireEvent(event);
515        }
516    
517        /**
518         * Abstract createFrame - creates the containing frame of this panel
519         */
520        public abstract void createFrame();
521    
522        /**
523         * Stops the close timer
524         */
525        public void stopTimer() {
526            timer.stop();
527        }
528    
529        /**
530         * Starts the closet imer
531         */
532        public void startTimer() {
533            timer.start();
534        }
535    
536        /**
537         * Displays a "disconnected" message"
538         */
539        public void disconnected() {
540            conversationArea.append(getDate(null) + " **** " + resources.getString("disconnected"), ConversationArea.BLACK, true);
541        }
542    
543    
544        public void updateStyle(Font font){}
545    
546        /**
547         * Closes the log file
548         */
549        public void closeLog() {
550            conversationArea.closeLog();
551        }
552    
553        public void finailize()
554        {
555            String user = "";
556            if(buddy != null) user = buddy.getUser();
557            com.valhalla.Logger.debug("Finalize called for conversationpanel " + user);
558        }
559    
560    }
561