1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.jcurl.demo.smack;
21
22 import java.awt.BorderLayout;
23 import java.awt.Frame;
24 import java.awt.GridBagConstraints;
25 import java.awt.GridBagLayout;
26 import java.awt.Insets;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.util.Properties;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 import javax.swing.Box;
35 import javax.swing.JButton;
36 import javax.swing.JCheckBoxMenuItem;
37 import javax.swing.JComponent;
38 import javax.swing.JDialog;
39 import javax.swing.JFileChooser;
40 import javax.swing.JLabel;
41 import javax.swing.JMenu;
42 import javax.swing.JMenuBar;
43 import javax.swing.JMenuItem;
44 import javax.swing.JPanel;
45 import javax.swing.JScrollPane;
46 import javax.swing.JSeparator;
47 import javax.swing.JSplitPane;
48 import javax.swing.JTextField;
49 import javax.swing.ScrollPaneConstants;
50 import javax.swing.SwingUtilities;
51 import javax.swing.border.EmptyBorder;
52 import javax.swing.filechooser.FileFilter;
53
54 import org.apache.commons.logging.Log;
55 import org.jcurl.core.api.MutableObject;
56 import org.jcurl.core.log.JCLoggerFactory;
57 import org.jcurl.core.ui.FileNameExtensionFilter;
58 import org.jcurl.demo.tactics.JCurlShotPlanner;
59 import org.jdesktop.application.Action;
60 import org.jdesktop.application.ApplicationContext;
61 import org.jdesktop.application.ResourceMap;
62 import org.jdesktop.application.SingleFrameApplication;
63 import org.jdesktop.application.Task.BlockingScope;
64 import org.jivesoftware.smack.ChatManager;
65 import org.jivesoftware.smack.XMPPConnection;
66 import org.jivesoftware.smack.XMPPException;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public class JCurlSmackClient extends SingleFrameApplication {
85 static class GuiUtil {
86
87 private static final Pattern check = Pattern.compile("\\[\\](.+)");
88 private static final Insets zeroInsets = new Insets(0, 0, 0, 0);
89 private final ApplicationContext act;
90
91 public GuiUtil(final ApplicationContext act) {
92 this.act = act;
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107 private JDialog createAboutBox(final Frame owner) {
108 final JPanel panel = new JPanel(new GridBagLayout());
109 panel.setBorder(new EmptyBorder(0, 28, 16, 28));
110
111 final JLabel titleLabel = new JLabel();
112 titleLabel.setName("aboutTitleLabel");
113 final GridBagConstraints c = new GridBagConstraints();
114 initGridBagConstraints(c);
115 c.anchor = GridBagConstraints.WEST;
116 c.gridwidth = GridBagConstraints.REMAINDER;
117 c.fill = GridBagConstraints.HORIZONTAL;
118 c.ipady = 32;
119 c.weightx = 1.0;
120 panel.add(titleLabel, c);
121 final String[] fields = { "description", "version", "vendor",
122 "home" };
123 for (final String field : fields) {
124 final JLabel label = new JLabel();
125 label.setName(field + "Label");
126 initGridBagConstraints(c);
127
128 c.anchor = GridBagConstraints.EAST;
129 panel.add(label, c);
130 initGridBagConstraints(c);
131 c.weightx = 1.0;
132 c.gridwidth = GridBagConstraints.REMAINDER;
133 c.fill = GridBagConstraints.HORIZONTAL;
134 final JTextField textField = new JTextField();
135 textField.setName(field + "TextField");
136 textField.setEditable(false);
137 textField.setBorder(null);
138 panel.add(textField, c);
139 }
140 final JButton closeAboutButton = new JButton();
141 closeAboutButton.setAction(findAction("closeAboutBox"));
142 initGridBagConstraints(c);
143 c.anchor = GridBagConstraints.EAST;
144 c.gridx = 1;
145 panel.add(closeAboutButton, c);
146 final JDialog dialog = new JDialog(owner);
147 dialog.setName("aboutDialog");
148 dialog.add(panel, BorderLayout.CENTER);
149 return dialog;
150 }
151
152 private JFileChooser createFileChooser(final File base,
153 final String resourceName, final FileFilter filter) {
154 final JFileChooser fc = new JFileChooser(base);
155 fc.setName(resourceName);
156 fc.setMultiSelectionEnabled(false);
157 fc.setAcceptAllFileFilterUsed(true);
158 fc.setFileFilter(filter);
159 getContext().getResourceMap().injectComponents(fc);
160 return fc;
161 }
162
163 private FileNameExtensionFilter createFileFilter(
164 final String resourceName, final String... extensions) {
165 final ResourceMap appResourceMap = getContext().getResourceMap();
166 final String key = resourceName + ".description";
167 final String desc = appResourceMap.getString(key);
168 return new FileNameExtensionFilter(desc == null ? key : desc,
169 extensions);
170 }
171
172 private JMenu createMenu(final String menuName,
173 final String[] actionNames) {
174 return createMenu(menuName, actionNames, null);
175 }
176
177 private JMenu createMenu(final String menuName,
178 final String[] actionNames, JMenu menu) {
179 if (menu == null)
180 menu = new JMenu();
181 menu.setName(menuName);
182 for (String actionName : actionNames)
183 if (actionName.equals("---"))
184 menu.add(new JSeparator());
185 else {
186 final JMenuItem menuItem;
187 {
188 final Matcher m = check.matcher(actionName);
189 if (m.matches()) {
190 actionName = m.group(1);
191 menuItem = new JCheckBoxMenuItem();
192 } else
193 menuItem = new JMenuItem();
194 }
195 menuItem.setAction(findAction(actionName));
196 menuItem.setIcon(null);
197 menu.add(menuItem);
198 }
199 return menu;
200 }
201
202 private File ensureSuffix(final File dst,
203 final FileNameExtensionFilter pat) {
204 if (pat.accept(dst))
205 return dst;
206 return new File(dst.getAbsoluteFile() + "."
207 + pat.getExtensions()[0]);
208 }
209
210 private javax.swing.Action findAction(final String actionName) {
211 return getContext().getActionMap().get(actionName);
212 };
213
214 public ApplicationContext getContext() {
215 return act;
216 };
217
218 private void initGridBagConstraints(final GridBagConstraints c) {
219 c.anchor = GridBagConstraints.CENTER;
220 c.fill = GridBagConstraints.NONE;
221 c.gridwidth = 1;
222 c.gridheight = 1;
223 c.gridx = GridBagConstraints.RELATIVE;
224 c.gridy = GridBagConstraints.RELATIVE;
225 c.insets = zeroInsets;
226 c.ipadx = 4;
227 c.ipady = 4;
228 c.weightx = 0.0;
229 c.weighty = 0.0;
230 };
231 }
232
233 static class XmppAccount extends MutableObject {
234 private static final Log lo = JCLoggerFactory
235 .getLogger(XmppAccount.class);
236 private transient XMPPConnection conn;
237 private CharSequence pwd;
238 private XmppAddress uid;
239
240 public XMPPConnection getConn() {
241 return conn;
242 }
243
244 public CharSequence getPwd() {
245 return pwd;
246 }
247
248 public XmppAddress getUid() {
249 return uid;
250 }
251
252 public XMPPConnection login(final String resource) throws XMPPException {
253 logout();
254 final XMPPConnection conn = new XMPPConnection(uid.getHost());
255 conn.connect();
256 lo.info("connected: " + conn.isConnected() + ", secure: "
257 + conn.isSecureConnection() + ", TLS: " + conn.isUsingTLS()
258 + ", compressed: " + conn.isUsingCompression());
259 conn.login(uid.getAccount(), pwd.toString(), resource);
260 setConn(conn);
261 return getConn();
262 }
263
264 public void logout() {
265 if (conn != null && conn.isConnected())
266 conn.disconnect();
267 setConn(null);
268 }
269
270 private void setConn(final XMPPConnection conn) {
271 final XMPPConnection old = this.conn;
272 firePropertyChange("conn", old, this.conn = conn);
273 }
274
275 public void setPwd(final CharSequence pwd) {
276 this.pwd = pwd;
277 }
278
279 public void setUid(final XmppAddress uid) {
280 this.uid = uid;
281 }
282 }
283
284 private static final Log log = JCLoggerFactory
285 .getLogger(JCurlSmackClient.class);
286
287 private static Properties loadClassProps(final Class<?> c, Properties p)
288 throws IOException {
289 if (p == null)
290 p = new Properties();
291 final String r = "/" + c.getName().replace('.', '/') + ".properties";
292 final InputStream i = c.getResourceAsStream(r);
293 if (i == null) {
294 System.err.println("Add a file '." + r
295 + "' to the classpath containing the properties:");
296 System.err
297 .println("\nacc_uid = foo@foo.org # a jabber account name");
298 System.err.println("\nacc_pwd = .. # a jabber account password");
299 }
300 p.load(i);
301 return p;
302 }
303
304 public static void main(final String[] args) {
305 launch(JCurlSmackClient.class, args);
306 }
307
308 private JDialog aboutBox = null;
309 private final XmppAccount acc = new XmppAccount();
310 private final GuiUtil gui = new GuiUtil(getContext());
311 private JCheckBoxMenuItem miRoster;
312 private final String resource;
313 private final ChatSimpleSwingBean sca = new ChatSimpleSwingBean();
314 private final ChatLogSimpleSwingBean slo = new ChatLogSimpleSwingBean();
315 private final RosterSimpleSwingBean sro = new RosterSimpleSwingBean();
316
317 public JCurlSmackClient() {
318 resource = this.getClass().getSimpleName();
319 sro.setStar(Pattern.compile("[^/]+/" + resource));
320 }
321
322 @Action
323 public void closeAboutBox() {
324 if (aboutBox == null)
325 return;
326 aboutBox.setVisible(false);
327 aboutBox = null;
328 }
329
330 private JMenuBar createMenuBar() {
331 final JMenuBar menuBar = new JMenuBar();
332
333 final String[] xmppMenuActionNames = { "xmppAccount", "[]xmppSession",
334 "[]xmppChat", "xmppPreferences", "---", "[]xmppRoster", "---",
335 "quit" };
336 final JMenu menu = gui.createMenu("xmppMenu", xmppMenuActionNames);
337 miRoster = (JCheckBoxMenuItem) menu.getMenuComponent(5);
338 menuBar.add(menu);
339
340 final String[] helpMenuActionNames = { "showAboutBox" };
341 menuBar.add(gui.createMenu("helpMenu", helpMenuActionNames));
342
343 return menuBar;
344 }
345
346 public boolean isAlwaysFalse() {
347 return false;
348 }
349
350
351 @Action(block = BlockingScope.COMPONENT)
352 public void showAboutBox() {
353 if (aboutBox == null)
354 aboutBox = gui.createAboutBox(getMainFrame());
355 show(aboutBox);
356 }
357
358 @Override
359 protected void startup() {
360 getMainFrame().setJMenuBar(createMenuBar());
361 miRoster.setSelected(true);
362
363 final JComponent pv = new JPanel();
364 pv.setLayout(new BorderLayout());
365 final Box conversation = Box.createVerticalBox();
366 pv.add(new JScrollPane(conversation,
367 ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
368 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED),
369 BorderLayout.CENTER);
370 pv.add(new JScrollPane(sca,
371 ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
372 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS),
373 BorderLayout.SOUTH);
374
375 final JSplitPane ph = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
376 ph.setResizeWeight(0.8);
377 ph.add(pv, JSplitPane.LEFT);
378 ph.add(new JScrollPane(sro), JSplitPane.RIGHT);
379
380 xmppRoster();
381 show(ph);
382
383
384 new Thread(new Runnable() {
385 public void run() {
386 try {
387
388 final Properties p = loadClassProps(JCurlSmackClient.class,
389 null);
390 acc.setUid(XmppAddress.parse(p.getProperty("acc_uid")));
391 acc.setPwd(p.getProperty("acc_pwd"));
392
393 acc.login(resource);
394 SwingUtilities.invokeLater(new Runnable() {
395 public void run() {
396 sro.setConn(acc.getConn());
397
398 final ChatManager cm = sro.getChatManager();
399 cm.addChatListener(sca);
400 cm.addChatListener(slo);
401 }
402 });
403 } catch (final IOException e) {
404 throw new RuntimeException("Unhandled", e);
405 } catch (final XMPPException e) {
406 throw new RuntimeException("Unhandled", e);
407 }
408 }
409 }).start();
410 }
411
412
413 @Action
414 public void xmppAccount() {
415 log.info("");
416 }
417
418
419 @Action(enabledProperty = "alwaysFalse")
420 public void xmppChat() {
421 log.info("");
422 }
423
424
425 @Action(enabledProperty = "alwaysFalse")
426 public void xmppPreferences() {
427 log.info("");
428 }
429
430
431 @Action
432 public void xmppRoster() {
433 sro.setEnabled(miRoster.isSelected());
434
435 }
436
437
438 @Action
439 public void xmppSession() {
440 log.info("");
441 }
442 }