Source code: plugins/Messenger/ChatFrame.java
1 /*
2 This file is part of DeXter - Java Internet Communication Solution
3 Copyright (c) 2002 Tobias Riemer
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 package plugins.Messenger;
21 /*
22 * ChatFrame.java
23 *
24 * Created on October 18, 2002, 3:26 PM
25 */
26
27 import java.util.*;
28 import javax.swing.event.*;
29 import java.awt.event.*;
30 import javax.swing.*;
31 import javax.swing.text.*;
32 import plugins.Messenger.event.*;
33
34 /**
35 *
36 * @author Christoph Walcher
37 */
38 public class ChatFrame extends javax.swing.JFrame implements ConversationListener {
39
40 private Conversation conversation;
41
42 private dexter.swingExtensions.ChatDocument chatDocument;
43 private Map statusPanels = new java.util.HashMap(4);
44
45 private Action sendAction;
46
47 private TypingForwarder typingForwarder;
48
49 private java.text.DateFormat format = java.text.DateFormat.getTimeInstance();
50
51 /** Creates new form ChatFrame */
52 public ChatFrame() {
53 chatDocument = createDefaultChatDocument();
54
55 createDefaultActions();
56
57 initComponents();
58
59 jInputPanel.setBackground(jInputTextPane.getBackground());
60
61 registerKeyStrokes();
62
63 jInputTextPane.addKeyListener(new TypingForwarder());
64
65 enableChat(false);
66 }
67
68 protected void registerKeyStrokes() {
69 // Register Enter KEY for Message Send
70 KeyStroke enterKey = KeyStroke.getKeyStroke("ENTER");
71
72 // Cut the Enter-Key Action and paste it to ctrl-Enter
73 KeyStroke ctrlEnterKey = KeyStroke.getKeyStroke("ctrl ENTER");
74 Object actionMapKey = jInputTextPane.getInputMap(jInputTextPane.WHEN_FOCUSED).get(enterKey);
75 jInputTextPane.getInputMap(jInputTextPane.WHEN_FOCUSED).put(ctrlEnterKey, actionMapKey);
76
77 jInputTextPane.getInputMap(jInputTextPane.WHEN_FOCUSED).put(enterKey, "sendMessage");
78 jInputTextPane.getActionMap().put("sendMessage", sendAction);
79 }
80
81 protected void createDefaultActions() {
82 sendAction = new SendAction();
83 }
84
85 protected dexter.swingExtensions.ChatDocument createDefaultChatDocument() {
86 return new dexter.swingExtensions.ChatDocument();
87 }
88
89 /** This method is called from within the constructor to
90 * initialize the form.
91 * WARNING: Do NOT modify this code. The content of this method is
92 * always regenerated by the Form Editor.
93 */
94 private void initComponents() {//GEN-BEGIN:initComponents
95 java.awt.GridBagConstraints gridBagConstraints;
96
97 jEditPopupMenu = new javax.swing.JPopupMenu();
98 jCopyItem = new javax.swing.JMenuItem();
99 jPasteItem = new javax.swing.JMenuItem();
100 jCutItem = new javax.swing.JMenuItem();
101 jCopyPopupMenu = new javax.swing.JPopupMenu();
102 jOutputCopyItem = new javax.swing.JMenuItem();
103 jOutputSaveItem = new javax.swing.JMenuItem();
104 jStatusPanel = new javax.swing.JPanel();
105 jTextSplitPane = new javax.swing.JSplitPane();
106 jOutputScrollPane = new javax.swing.JScrollPane();
107 jOutputTextPane = new javax.swing.JTextPane();
108 jInteractionPanel = new javax.swing.JPanel();
109 jInputPanel = new javax.swing.JPanel();
110 jInputScrollPane = new javax.swing.JScrollPane();
111 jInputTextPane = new javax.swing.JTextPane();
112 jSendButton = new javax.swing.JButton();
113 jToolBox = new javax.swing.JToolBar();
114 jSmileyButton = new javax.swing.JButton();
115 jMenuBar = new javax.swing.JMenuBar();
116
117 jCopyItem.setAction(new CopyAction());
118 jEditPopupMenu.add(jCopyItem);
119 jPasteItem.setAction(new PasteAction());
120 jEditPopupMenu.add(jPasteItem);
121 jCutItem.setAction(new CutAction());
122 jEditPopupMenu.add(jCutItem);
123 jOutputCopyItem.setAction(new CopyAction());
124 jCopyPopupMenu.add(jOutputCopyItem);
125 jOutputSaveItem.setText("Item");
126 jOutputSaveItem.setAction(new SaveOutputAction());
127 jCopyPopupMenu.add(jOutputSaveItem);
128
129 setTitle("Connecting...");
130 setName("ChatFrame");
131 addWindowListener(new java.awt.event.WindowAdapter() {
132 public void windowClosing(java.awt.event.WindowEvent evt) {
133 exitForm(evt);
134 }
135 });
136
137 jStatusPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 1, 1));
138
139 jStatusPanel.setBorder(new javax.swing.border.MatteBorder(new java.awt.Insets(1, 0, 0, 0), java.awt.Color.black));
140 jStatusPanel.setFont(new java.awt.Font("Dialog", 0, 10));
141 jStatusPanel.setMinimumSize(new java.awt.Dimension(14, 20));
142 jStatusPanel.setPreferredSize(new java.awt.Dimension(14, 17));
143 getContentPane().add(jStatusPanel, java.awt.BorderLayout.SOUTH);
144
145 jTextSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
146 jOutputScrollPane.setMinimumSize(new java.awt.Dimension(7, 200));
147 jOutputTextPane.setDocument(chatDocument);
148 jOutputTextPane.setEditable(false);
149 jOutputTextPane.setMinimumSize(new java.awt.Dimension(7, 200));
150 jOutputTextPane.setPreferredSize(new java.awt.Dimension(7, 230));
151 jOutputTextPane.addMouseListener(new java.awt.event.MouseAdapter() {
152 public void mousePressed(java.awt.event.MouseEvent evt) {
153 jOutputTextPaneMousePressed(evt);
154 }
155 public void mouseReleased(java.awt.event.MouseEvent evt) {
156 jOutputTextPaneMouseReleased(evt);
157 }
158 });
159
160 jOutputScrollPane.setViewportView(jOutputTextPane);
161
162 jTextSplitPane.setTopComponent(jOutputScrollPane);
163
164 jInteractionPanel.setLayout(new java.awt.BorderLayout());
165
166 jInputPanel.setLayout(new java.awt.GridBagLayout());
167
168 jInputPanel.setBackground(new java.awt.Color(255, 255, 255));
169 jInputPanel.setForeground(new java.awt.Color(0, 0, 0));
170 jInputScrollPane.setBorder(null);
171 jInputScrollPane.setMinimumSize(new java.awt.Dimension(100, 30));
172 jInputTextPane.setMinimumSize(new java.awt.Dimension(100, 30));
173 jInputTextPane.setPreferredSize(new java.awt.Dimension(100, 50));
174 jInputTextPane.addMouseListener(new java.awt.event.MouseAdapter() {
175 public void mousePressed(java.awt.event.MouseEvent evt) {
176 jInputTextPaneMousePressed(evt);
177 }
178 public void mouseReleased(java.awt.event.MouseEvent evt) {
179 jInputTextPaneMouseReleased(evt);
180 }
181 });
182
183 jInputScrollPane.setViewportView(jInputTextPane);
184
185 gridBagConstraints = new java.awt.GridBagConstraints();
186 gridBagConstraints.gridx = 0;
187 gridBagConstraints.gridy = 0;
188 gridBagConstraints.gridheight = 2;
189 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
190 gridBagConstraints.weightx = 1.0;
191 gridBagConstraints.weighty = 1.0;
192 jInputPanel.add(jInputScrollPane, gridBagConstraints);
193
194 jSendButton.setAction(sendAction);
195 gridBagConstraints = new java.awt.GridBagConstraints();
196 gridBagConstraints.gridx = 1;
197 gridBagConstraints.gridy = 0;
198 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
199 gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
200 jInputPanel.add(jSendButton, gridBagConstraints);
201
202 jInteractionPanel.add(jInputPanel, java.awt.BorderLayout.CENTER);
203
204 jToolBox.setRollover(true);
205 jSmileyButton.setBorder(new javax.swing.border.EtchedBorder());
206 jSmileyButton.setIconTextGap(0);
207 jSmileyButton.setOpaque(false);
208 jToolBox.add(jSmileyButton);
209
210 jInteractionPanel.add(jToolBox, java.awt.BorderLayout.NORTH);
211
212 jTextSplitPane.setBottomComponent(jInteractionPanel);
213
214 getContentPane().add(jTextSplitPane, java.awt.BorderLayout.CENTER);
215
216 setJMenuBar(jMenuBar);
217
218 pack();
219 java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
220 setSize(new java.awt.Dimension(300, 400));
221 setLocation((screenSize.width-300)/2,(screenSize.height-400)/2);
222 }//GEN-END:initComponents
223
224 private void jInputTextPaneMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jInputTextPaneMousePressed
225 maybePopupEditMenu(evt);
226 }//GEN-LAST:event_jInputTextPaneMousePressed
227
228 private void jInputTextPaneMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jInputTextPaneMouseReleased
229 maybePopupEditMenu(evt);
230 }//GEN-LAST:event_jInputTextPaneMouseReleased
231
232 private void jOutputTextPaneMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jOutputTextPaneMouseReleased
233 maybePopupCopyMenu(evt);
234 }//GEN-LAST:event_jOutputTextPaneMouseReleased
235
236 private void jOutputTextPaneMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jOutputTextPaneMousePressed
237 maybePopupCopyMenu(evt);
238 }//GEN-LAST:event_jOutputTextPaneMousePressed
239
240 private void maybePopupEditMenu(java.awt.event.MouseEvent evt) {
241 if (evt.isPopupTrigger()) {
242 if (evt.getSource() instanceof JTextComponent) {
243 JTextComponent ownerComponent = (JTextComponent)evt.getSource();
244 jEditPopupMenu.show(ownerComponent, evt.getX(), evt.getY());
245 }
246 }
247 }
248
249 private void maybePopupCopyMenu(java.awt.event.MouseEvent evt) {
250 if (evt.isPopupTrigger()) {
251 if (evt.getSource() instanceof JTextComponent) {
252 JTextComponent ownerComponent = (JTextComponent)evt.getSource();
253 jCopyPopupMenu.show(ownerComponent, evt.getX(), evt.getY());
254 }
255 }
256 }
257 /** Exit the Application */
258 private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
259 // @todo Release Resources
260
261 if (conversation != null) {
262 // @todo - better system
263 conversation.stopConversation();
264 }
265 }//GEN-LAST:event_exitForm
266
267 /**
268 * @param args the command line arguments
269 */
270 public static void main(String args[]) {
271 new ChatFrame().show();
272 }
273
274 /**
275 * @todo Refactor
276 */
277 public void sendMessage() {
278 try {
279 String s = jInputTextPane.getText();
280
281 send(s);
282
283 String nick = conversation.getMessengerEngine().getNick();
284
285 chatDocument.appendString(nick + " says:\n", dexter.swingExtensions.ChatDocument.USER);
286 chatDocument.appendString(s + "\n");
287 scrollToBottom();
288
289 jInputTextPane.getDocument().remove(0, jInputTextPane.getDocument().getLength());
290 } catch (Exception ex) {
291 ex.printStackTrace();
292 }
293 }
294
295 protected void send(String s) {
296 if (conversation != null) conversation.send(s);
297 }
298
299 protected void scrollToBottom() {
300 final JScrollBar scrollBar = jOutputScrollPane.getVerticalScrollBar();
301
302 SwingUtilities.invokeLater(new Thread() {
303 public void run() {
304 jOutputTextPane.setCaretPosition(chatDocument.getLength());
305
306 try{
307 jOutputTextPane.scrollRectToVisible(jOutputTextPane.modelToView(
308 chatDocument.getLength()));
309
310 } catch (javax.swing.text.BadLocationException be) {
311 be.printStackTrace();
312 }
313
314 }
315 });
316 }
317
318 protected void enableChat(boolean enable) {
319 sendAction.setEnabled(enable);
320 }
321
322 public void messageReceived(String account, String nick, String msg) {
323
324 JUserStatusPanel userStatusPanel = getUserStatusPanel(account);
325 userStatusPanel.setStatus("");
326
327 if (msg == null) return;
328
329 try {
330 if (!nick.equals("")) {
331 chatDocument.appendString(nick + " says:\n",dexter.swingExtensions.ChatDocument.USER);
332 }
333
334 if (msg.charAt(0) != '\n') {
335 msg = msg + "\n";
336 }
337
338 chatDocument.appendString(msg);
339 scrollToBottom();
340
341 } catch (Exception e) {}
342 }
343
344 public void userJoined(String account, String nick) {
345 try {
346 chatDocument.appendString(format.format(new Date()) + ":", dexter.swingExtensions.ChatDocument.SMALL);
347 chatDocument.appendString( "\n" + nick + " has joined the Conversation!\n", dexter.swingExtensions.ChatDocument.SMALL);
348
349 setTitle("Talking to " + nick);
350
351 JUserStatusPanel userStatusPanel = getUserStatusPanel(account);
352 jStatusPanel.add(userStatusPanel);
353
354 enableChat(true);
355
356 } catch (Exception ex) {
357 ex.printStackTrace();
358 }
359 }
360
361 protected JUserStatusPanel getUserStatusPanel(String account) {
362 if (!statusPanels.containsKey(account)) {
363 JUserStatusPanel userStatusPanel = new ChatFrame.JUserStatusPanel("connected...");
364 statusPanels.put(account, userStatusPanel);
365 }
366
367 return (JUserStatusPanel)statusPanels.get(account);
368 }
369
370 public void userTyping(String account, String nick) {
371 JUserStatusPanel userStatusPanel = getUserStatusPanel(account);
372 userStatusPanel.setStatus(nick + " is typing a message");
373 }
374
375 /**
376 * If chatting with more than just one Partner a message will be printed out
377 * that a Partner has left the chat. If you chat with just one Person - nothing
378 * will happen.
379 */
380 public void userLeft(String account) {
381 // @todo i dont think this is absolute correct!
382 Map chatPartners = conversation.getPartners();
383
384 if (chatPartners != null && chatPartners.size() > 1) {
385 chatDocument.appendString("User " + account + " has left the chat\n");
386 }
387 }
388
389 /** Setter for property toolBox.
390 * @param toolBox New value of property toolBox.
391 */
392 public void setToolBox(javax.swing.JToolBar toolBox) {
393 this.jInteractionPanel.add(toolBox, java.awt.BorderLayout.NORTH);
394 }
395
396 public void setToolMenu(javax.swing.JMenu jPluginMenu) {
397 jMenuBar.add(jPluginMenu);
398 }
399
400 /** Getter for property jToolBox.
401 * @return Value of property jToolBox.
402 */
403 public javax.swing.JToolBar getJToolBox() {
404 return jToolBox;
405 }
406
407 /** Setter for property jToolBox.
408 * @param jToolBox New value of property jToolBox.
409 */
410 public void setJToolBox(javax.swing.JToolBar jToolBox) {
411 this.jToolBox = jToolBox;
412 }
413
414 public void setSmileyButtonAction(Action action) {
415 this.jSmileyButton.setAction(action);
416 }
417
418 public javax.swing.text.Document getInputDocument() {
419 return jInputTextPane.getDocument();
420 }
421
422 /** Getter for property conversation.
423 * @return Value of property conversation.
424 */
425 public plugins.Messenger.Conversation getConversation() {
426 return conversation;
427 }
428
429 /** Setter for property conversation.
430 * @param conversation New value of property conversation.
431 */
432 public void setConversation(plugins.Messenger.Conversation conversation) {
433 if (conversation == null) {
434 throw new IllegalArgumentException("Conversation must not be null");
435 }
436 this.conversation = conversation;
437 }
438
439 private void saveText(String text) {
440 javax.swing.JFileChooser chooser = new JFileChooser();
441 int answer = chooser.showSaveDialog(this);
442
443 if (answer == chooser.APPROVE_OPTION) {
444 java.io.File file = chooser.getSelectedFile();
445
446 boolean save = true;
447 if (file != null) {
448 if (file.exists()) {
449
450 JOptionPane pane = new JOptionPane();
451 answer = pane.showConfirmDialog(this, "Overwrite File " + file.getName() + " ?", "Save", JOptionPane.YES_NO_OPTION);
452
453 if (answer == JOptionPane.NO_OPTION) {
454 save = false;
455 }
456 }
457
458 if (save) {
459 try {
460 java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.FileWriter(file));
461
462 writer.print(text);
463 writer.flush();
464 writer.close();
465
466 } catch (java.io.IOException ex) {
467 ex.printStackTrace();
468 JOptionPane pane = new JOptionPane();
469 pane.showMessageDialog(this, "Errors occured during Save operation", "Save", JOptionPane.ERROR_MESSAGE);
470
471 }
472 }
473 }
474 }
475 }
476
477
478 // *********************** Inner Action Classes ********************************* //
479
480 public class SendAction extends javax.swing.AbstractAction {
481
482 /**
483 * @todo Provide an Icon for Send
484 */
485 public SendAction() {
486 super("Send");
487 }
488
489 /** Invoked when an action occurs.
490 */
491 public void actionPerformed(ActionEvent e) {
492 sendMessage();
493 }
494 }
495
496 public static class JUserStatusPanel extends javax.swing.JPanel {
497
498 private JLabel nickLabel;
499 private JLabel statusLabel;
500
501 private static java.awt.Font font = new java.awt.Font("Dialog", 0, 10);
502
503 public JUserStatusPanel(String initialStatus) {
504 super();
505
506 nickLabel = new JLabel("");
507 nickLabel.setFont(font);
508
509 statusLabel = new JLabel("connected...");
510 statusLabel.setFont(font);
511
512 add(nickLabel);
513 add(statusLabel);
514
515 setBorder(BorderFactory.createEmptyBorder());
516 setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 0, 0));
517 }
518
519 /** Getter for property nickLabel.
520 * @return Value of property nickLabel.
521 */
522 public String getNick() {
523 return nickLabel.getText();
524 }
525
526 /** Setter for property nickLabel.
527 * @param nickLabel New value of property nickLabel.
528 */
529 public void setNick(String nick) {
530 nickLabel.setText(nick);
531 }
532
533 /** Getter for property statusLabel.
534 * @return Value of property statusLabel.
535 */
536 public String getStatus() {
537 return statusLabel.getText();
538 }
539
540 /** Setter for property statusLabel.
541 * @param statusLabel New value of property statusLabel.
542 */
543 public void setStatus(String status) {
544 this.statusLabel.setText(status);
545 }
546 }
547
548 public class TypingForwarder extends java.awt.event.KeyAdapter {
549
550 public static final long waitTime = 1000;
551
552 private long currentMillis = System.currentTimeMillis();
553
554 /** Invoked when a key has been pressed.
555 * See the class description for {@link KeyEvent} for a definition of
556 * a key pressed event.
557 */
558 public void keyPressed(KeyEvent e) {
559 // Do not send a keyTyping message on Enter keys
560 if (e.getKeyCode() != e.VK_ENTER) {
561 if ((System.currentTimeMillis() - currentMillis) > waitTime) {
562 conversation.userTyping();
563 currentMillis = System.currentTimeMillis();
564 }
565 }
566 }
567 }
568
569 public class CutAction extends TextAction {
570
571 public CutAction() {
572 super("Cut");
573
574 this.putValue(Action.MNEMONIC_KEY, new Integer('t'));
575 this.putValue(Action.SMALL_ICON, new javax.swing.ImageIcon(ChatFrame.class.getResource("/plugins/Messenger/images/Cut16.gif")));
576 }
577
578 /** Invoked when an action occurs.
579 */
580 public void actionPerformed(ActionEvent e) {
581 JTextComponent target = getTextComponent(e);
582
583 if (target != null) {
584 target.cut();
585 }
586 }
587 }
588
589 public class CopyAction extends TextAction {
590
591 public CopyAction() {
592 super("Copy");
593
594 this.putValue(Action.MNEMONIC_KEY, new Integer('C'));
595 this.putValue(Action.SMALL_ICON, new javax.swing.ImageIcon(ChatFrame.class.getResource("/plugins/Messenger/images/Copy16.gif")));
596 }
597
598 /** Invoked when an action occurs.
599 */
600 public void actionPerformed(ActionEvent e) {
601 JTextComponent target = getTextComponent(e);
602
603 if (target != null) {
604 target.copy();
605 }
606 }
607 }
608
609 public class PasteAction extends TextAction {
610
611 public PasteAction() {
612 super("Paste");
613
614 this.putValue(Action.MNEMONIC_KEY, new Integer('P'));
615 this.putValue(Action.SMALL_ICON, new javax.swing.ImageIcon(ChatFrame.class.getResource("/plugins/Messenger/images/Paste16.gif")));
616 }
617
618 /** Invoked when an action occurs.
619 */
620 public void actionPerformed(ActionEvent e) {
621 JTextComponent target = getTextComponent(e);
622
623 if (target != null) {
624 target.paste();
625 }
626 }
627 }
628
629 public class SaveOutputAction extends AbstractAction {
630
631 public SaveOutputAction() {
632 super("Save Text");
633
634 this.putValue(Action.MNEMONIC_KEY, new Integer('S'));
635 this.putValue(Action.SMALL_ICON, new javax.swing.ImageIcon(ChatFrame.class.getResource("/plugins/Messenger/images/Save16.gif")));
636 }
637
638 /** Invoked when an action occurs.
639 */
640 public void actionPerformed(ActionEvent e) {
641 if (e != null && e.getSource() != null) {
642 Object o = e.getSource();
643 if (o instanceof JTextComponent) {
644 saveText(((JTextComponent)o).getText());
645 }
646 }
647 }
648 }
649
650 // Variables declaration - do not modify//GEN-BEGIN:variables
651 private javax.swing.JMenuItem jOutputSaveItem;
652 private javax.swing.JMenuItem jCutItem;
653 private javax.swing.JPanel jInteractionPanel;
654 private javax.swing.JToolBar jToolBox;
655 private javax.swing.JScrollPane jInputScrollPane;
656 private javax.swing.JSplitPane jTextSplitPane;
657 private javax.swing.JPopupMenu jCopyPopupMenu;
658 private javax.swing.JScrollPane jOutputScrollPane;
659 private javax.swing.JMenuItem jCopyItem;
660 private javax.swing.JTextPane jInputTextPane;
661 private javax.swing.JPanel jStatusPanel;
662 private javax.swing.JButton jSmileyButton;
663 private javax.swing.JPanel jInputPanel;
664 private javax.swing.JMenuItem jPasteItem;
665 private javax.swing.JMenuItem jOutputCopyItem;
666 private javax.swing.JTextPane jOutputTextPane;
667 private javax.swing.JMenuBar jMenuBar;
668 private javax.swing.JButton jSendButton;
669 private javax.swing.JPopupMenu jEditPopupMenu;
670 // End of variables declaration//GEN-END:variables
671
672 }