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> "
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