1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.gui;
21
22 import java.awt.BorderLayout;
23 import java.awt.Toolkit;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.awt.event.MouseListener;
27 import java.beans.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.util.StringTokenizer;
30 import javax.swing.AbstractAction;
31 import javax.swing.*;
32 import javax.swing.table.*;
33 import javax.swing.BoxLayout;
34 import javax.swing.JButton;
35 import javax.swing.JCheckBox;
36 import javax.swing.JLabel;
37 import javax.swing.JMenu;
38 import javax.swing.JMenuItem;
39 import javax.swing.JOptionPane;
40 import javax.swing.JPanel;
41 import javax.swing.JPopupMenu;
42 import javax.swing.JScrollPane;
43 import javax.swing.JSplitPane;
44 import javax.swing.*;
45 import javax.swing.SwingUtilities;
46 import javax.swing.border.EmptyBorder;
47 import org.xnap.XNap;
48 import org.xnap.chat.Channel;
49 import org.xnap.chat.ChannelEvent;
50 import org.xnap.chat.ChannelListener;
51 import org.xnap.cmdl.Command;
52 import org.xnap.cmdl.Console;
53 import org.xnap.cmdl.Executer;
54 import org.xnap.cmdl.LocalExecuter;
55 import org.xnap.gui.action.MenuAction;
56 import org.xnap.gui.component.*;
57 import org.xnap.gui.component.XNapTextField;
58 import org.xnap.gui.component.XNapToggleButton;
59 import org.xnap.gui.event.PopupListener;
60 import org.xnap.gui.menu.PeerMenu;
61 import org.xnap.gui.table.ChatPeerTableModel;
62 import org.xnap.gui.util.FocusHandler;
63 import org.xnap.gui.util.GUIHelper;
64 import org.xnap.peer.Peer;
65 import org.xnap.util.Formatter;
66 import org.xnap.util.Preferences;
67
68 /***
69 * Provides a panel that handles a chat {@link Channel}. Provides a
70 * {@link ChatPane} that displays the messages and a table that
71 * contains the joined {@link Peer} objects.
72 */
73 public class ChannelPanel extends JPanel
74 implements ChannelListener, PropertyChangeListener, PeerProvider,
75 ActionProvider, Console {
76
77
78
79 private Preferences prefs = Preferences.getInstance();
80
81 private ChatPanel parent;
82
83 private ChatPane cpChat;
84 private JTable jta;
85 private XNapTextField jteInput;
86 private ChatPeerTableModel ptm;
87 private JSplitPane jspH;
88 private JLabel jlTopic;
89 private JLabel jlServer;
90 private JCheckBox jcbShowChatMsgTime;
91 private JCheckBox jcbBeepOnChatMessage;
92 private JCheckBox jcbBlinkOnChatMessage;
93 private SendAction acSend = new SendAction();
94
95 private Channel channel;
96
97 private LocalExecuter executer;
98
99
100
101 public ChannelPanel(ChatPanel parent, Channel channel)
102 {
103 this.parent = parent;
104 this.channel = channel;
105
106 initialize();
107
108
109 Peer[] peers = channel.getPeers();
110 channel.addChannelListener(this);
111 for (int i = 0; i < peers.length; i++) {
112 ptm.add(peers[i]);
113 }
114 updateStatus();
115
116 if (prefs.getPrintServerNotificationsInChatWindow()
117 && channel.getProvider() != null) {
118 println(XNap.tr("connected to {0}",
119 channel.getProvider().getName()),
120 "chatInfo");
121 }
122
123 Command[] commands = channel.getCommands();
124 if (commands != null) {
125 executer = new LocalExecuter(commands);
126 }
127
128 jspH.addPropertyChangeListener
129 (JSplitPane.DIVIDER_LOCATION_PROPERTY,
130 this);
131 }
132
133
134
135 private void initialize()
136 {
137 cpChat = new ChatPane();
138
139 Box boxTop = new Box(BoxLayout.X_AXIS);
140
141
142 boxTop.add(Box.createHorizontalStrut(5));
143 jlTopic = new SqueezedTextLabel();
144 setTopic(channel.getTopic());
145 boxTop.add(jlTopic);
146
147 boxTop.add(Box.createGlue());
148
149 jcbShowChatMsgTime = new JCheckBox
150 (XNap.tr("Timestamp Messages"), prefs.getShowChatMsgTime());
151 boxTop.add(jcbShowChatMsgTime);
152
153 jcbBeepOnChatMessage = new JCheckBox
154 (XNap.tr("Beep"), prefs.getBeepOnChatMessage());
155 boxTop.add(jcbBeepOnChatMessage);
156
157 jcbBlinkOnChatMessage = new JCheckBox(XNap.tr("Blink"), false);
158 boxTop.add(jcbBlinkOnChatMessage);
159
160
161 JPanel jpPeers = new JPanel(new BorderLayout());
162
163 JMenu jm = new ChatPeerMenu(this);
164
165 ptm = new ChatPeerTableModel(getChannel());
166 jta = ptm.createTable(prefs, "user");
167 MouseListener ml = new PopupListener(jm);
168 jta.addMouseListener(ml);
169 jta.setShowGrid(false);
170
171 JScrollPane jsp = new JScrollPane(jta);
172 jpPeers.add(jsp, BorderLayout.CENTER);
173
174
175
176 JPanel jpServer = new JPanel(new BorderLayout());
177 jpServer.setBorder(new EmptyBorder(5, 5, 5, 5));
178
179 jlServer = new JLabel(" ");
180 jpServer.add(jlServer, BorderLayout.CENTER);
181
182 jpPeers.add(jpServer, BorderLayout.SOUTH);
183
184
185 Box boxBottom = new Box(BoxLayout.X_AXIS);
186
187 Command[] commands = channel.getCommands();
188 if (commands != null) {
189 CommandMenuActionListener listener
190 = new CommandMenuActionListener();
191 JMenu commandMenu = new JMenu("Commands");
192 for (int i = 0; i < commands.length; i++) {
193 String name = commands[i].getCommand() + " "
194 + commands[i].getParameter();
195 JMenuItem item = new JMenuItem(name);
196 item.addActionListener(listener);
197 item.setActionCommand(name);
198 commandMenu.add(item);
199 }
200
201 MenuAction commandMenuAction
202 = new MenuAction(commandMenu, "run.png");
203 boxBottom.add(new XNapToggleButton(commandMenuAction));
204 boxBottom.add(Box.createHorizontalStrut(5));
205 }
206
207
208 jteInput = new XNapTextField("", 20);
209 jteInput.setPreferences("chat");
210 GUIHelper.bindEnterKey(jteInput, acSend);
211 boxBottom.add(jteInput);
212
213 boxBottom.add(Box.createHorizontalStrut(5));
214
215 boxBottom.add(new JButton(acSend));
216
217
218 jspH = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
219 jspH.add(cpChat, JSplitPane.LEFT);
220 jspH.add(jpPeers, JSplitPane.RIGHT);
221 jspH.setDividerLocation(prefs.getChatVerticalDividerLocation());
222 jspH.setResizeWeight(1);
223 jspH.setOneTouchExpandable(true);
224 jspH.setBorder(GUIHelper.createEmptyBorder());
225
226
227 setLayout(new BorderLayout());
228 add(boxTop, BorderLayout.NORTH);
229 add(jspH, BorderLayout.CENTER);
230 add(boxBottom, BorderLayout.SOUTH);
231
232 addComponentListener(new FocusHandler(jteInput));
233 }
234
235 public void channelJoined(ChannelEvent e)
236 {
237 parent.setTabTitle(this, channel.getName());
238 }
239
240 public void channelParted(ChannelEvent e)
241 {
242 parent.setTabTitle(this, "(" + channel.getName() + ")");
243 }
244
245 public Action[] getActions()
246 {
247 return getChannel().getActions();
248
249 }
250
251 public Channel getChannel()
252 {
253 return channel;
254 }
255
256 public Command getCommand(String name)
257 {
258 return (executer != null)
259 ? executer.getCommand(name)
260 : Executer.getCommand(name);
261 }
262
263 public Command[] getCommands()
264 {
265 return (executer != null)
266 ? executer.getCommands()
267 : Executer.getCommands();
268 }
269
270 public Peer[] getPeers()
271 {
272 int[] rows = jta.getSelectedRows();
273 Peer[] peers = new Peer[rows.length];
274 for (int i = 0; i < rows.length; i++) {
275 peers[i] = ptm.get(rows[i]);
276 }
277
278 return peers;
279 }
280
281 /***
282 * Returns true.
283 */
284 public boolean isEchoing()
285 {
286 return true;
287 }
288
289 public void messageReceived(ChannelEvent e)
290 {
291 Peer sender = e.getPeer();
292 if (e.getMessageType() == ChannelEvent.MESSAGE_TYPE_MESSAGE) {
293 printTimeStamp();
294 print(sender.getName(), "chatUser");
295 print("> " + e.getMessage(), "chatMessage");
296 println("", "chatMessage", !channel.isLocal(sender));
297 }
298 else if (e.getMessageType()
299 == ChannelEvent.MESSAGE_TYPE_ACTION_MESSAGE) {
300 println("\003\006* " + sender.getName() + " " + e.getMessage(),
301 "chatMessage");
302 }
303 else if (e.getMessageType() == ChannelEvent.MESSAGE_TYPE_ERROR) {
304 println(e.getMessage(), "chatError");
305 }
306 else if (e.getMessageType() == ChannelEvent.MESSAGE_TYPE_INFO) {
307 println(e.getMessage(), "chatInfo");
308 }
309
310 if (jcbBlinkOnChatMessage.isSelected()) {
311 Runnable runner = new Runnable()
312 {
313 public void run()
314 {
315 XNapFrame.getInstance().chatBlink();
316 }
317 };
318 SwingUtilities.invokeLater(runner);
319 }
320 }
321
322 public void print(String s, String style)
323 {
324 cpChat.appendLater(s, style);
325 }
326
327 public void println(String s, String style, boolean beep)
328 {
329 if (s.length() > 0) {
330 printTimeStamp();
331 }
332 print(s + "\n", style);
333 if (beep && jcbBeepOnChatMessage.isSelected()) {
334 Toolkit.getDefaultToolkit().beep();
335 }
336 }
337
338 public void println(String s, String style)
339 {
340 println(s, style, true);
341 }
342
343 /***
344 * Invoked by {@link xnap.cmdl.Command} objects.
345 *
346 * @see xnap.cmdl.Console#println(String)
347 */
348 public void println(String text)
349 {
350 println(text, "chatInfo", false);
351 }
352
353 public void printTimeStamp()
354 {
355 if (jcbShowChatMsgTime.isSelected()) {
356 cpChat.appendLater("[" + Formatter.shortDate() + "] ",
357 "chatMessage");
358 }
359 }
360
361 public String readln(String prompt)
362 {
363 return JOptionPane.showInputDialog
364 (this, prompt, XNap.tr("Console"), JOptionPane.QUESTION_MESSAGE);
365 }
366
367 public void propertyChange(PropertyChangeEvent e)
368 {
369 prefs.setChatVerticalDividerLocation(jspH.getDividerLocation());
370 }
371
372 public void updateStatus()
373 {
374 StringBuffer sb = new StringBuffer();
375
376 if (getChannel().getProvider() != null) {
377 sb.append(getChannel().getProvider().getName());
378 sb.append(" / ");
379 }
380 sb.append(ptm.getRowCount());
381 sb.append(" ");
382 sb.append(XNap.tr("users"));
383
384 final String s = sb.toString();
385 Runnable runner = new Runnable()
386 {
387 public void run()
388 {
389 jlServer.setText(s);
390 }
391 };
392 SwingUtilities.invokeLater(runner);
393 }
394
395 public void peerAdded(final ChannelEvent e)
396 {
397 Runnable runner = new Runnable()
398 {
399 public void run()
400 {
401 ptm.add(e.getPeer());
402 updateStatus();
403 }
404 };
405 SwingUtilities.invokeLater(runner);
406 }
407
408 public void peerChanged(final ChannelEvent e)
409 {
410 Runnable runner = new Runnable()
411 {
412 public void run()
413 {
414 ptm.changed(e.getPeer());
415 }
416 };
417 SwingUtilities.invokeLater(runner);
418 }
419
420 public void peerRemoved(final ChannelEvent e)
421 {
422 Runnable runner = new Runnable()
423 {
424 public void run()
425 {
426 ptm.remove(e.getPeer());
427 updateStatus();
428 }
429 };
430 SwingUtilities.invokeLater(runner);
431 }
432
433 public void setTopic(String topic)
434 {
435 jlTopic.setText(topic);
436
437 }
438
439 public void topicChanged(final ChannelEvent e)
440 {
441 Runnable runner = new Runnable()
442 {
443 public void run()
444 {
445 setTopic(e.getTopic());
446 }
447 };
448 SwingUtilities.invokeLater(runner);
449 }
450
451
452
453 /***
454 * Sends a message.
455 */
456 private class SendAction extends AbstractAction {
457
458 public SendAction()
459 {
460 putValue(Action.NAME, XNap.tr("Send"));
461 putValue(Action.SHORT_DESCRIPTION, XNap.tr("Sends message."));
462 putValue(Action.MNEMONIC_KEY, new Integer('S'));
463 }
464
465 public void actionPerformed(ActionEvent event)
466 {
467 String s = jteInput.getText();
468 if (s.length() > 0) {
469 jteInput.getCompletionModel().insert(s);
470 if (s.startsWith("/")) {
471 if (Executer.parse(s, ChannelPanel.this)) {
472 jteInput.setText("");
473 return;
474 }
475 }
476 channel.sendMessage(s);
477 jteInput.setText("");
478 }
479 }
480
481 }
482
483 /***
484 * Inserts a command into the input field.
485 */
486 private class CommandMenuActionListener
487 implements ActionListener {
488
489 public void actionPerformed(ActionEvent event)
490 {
491 String command = event.getActionCommand();
492 if (command != null) {
493 StringTokenizer t
494 = new StringTokenizer(command, " ");
495 if (t.hasMoreTokens()) {
496 String cmd = "/" + t.nextToken() + " ";
497 String parameter
498 = (t.hasMoreTokens()) ? t.nextToken("") : "";
499 jteInput.setText(cmd + parameter);
500 jteInput.setCaretPosition(cmd.length());
501 jteInput.setSelectionStart(cmd.length());
502 jteInput.setSelectionEnd
503 (cmd.length() + parameter.length());
504 jteInput.requestFocus();
505 }
506 }
507 }
508
509 }
510
511 private class ChatPeerMenu extends PeerMenu
512 {
513
514
515
516
517
518
519
520 public ChatPeerMenu(PeerProvider pp)
521 {
522 super(pp);
523 }
524
525
526
527 protected void willBecomeVisible()
528 {
529 super.willBecomeVisible();
530
531 Peer[] peers = getPeers();
532 if (peers != null && peers.length == 1) {
533 Action[] actions = channel.getPeerActions(peers[0]);
534 if (actions != null) {
535 addTemporary(new JPopupMenu.Separator(), 0);
536 for (int i = 0; i < actions.length; i++) {
537 addTemporary(new XNapMenuItem(actions[i]), i);
538 }
539 }
540 }
541 }
542 }
543 }
544
545