001 /*
002 Copyright (C) 2003 Adam Olsen
003
004 This program is free software; you can redistribute it and/or modify
005 it under the terms of the GNU General Public License as published by
006 the Free Software Foundation; either version 1, or (at your option)
007 any later version.
008
009 This program is distributed in the hope that it will be useful,
010 but WITHOUT ANY WARRANTY; without even the implied warranty of
011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012 GNU General Public License for more details.
013
014 You should have received a copy of the GNU General Public License
015 along with this program; if not, write to the Free Software
016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
017 */
018
019 package com.valhalla.jbother;
020
021 import java.awt.BorderLayout;
022 import java.awt.Dimension;
023 import java.awt.GridBagConstraints;
024 import java.awt.GridBagLayout;
025 import java.awt.event.ActionEvent;
026 import java.awt.event.ActionListener;
027 import java.util.Locale;
028 import java.util.ResourceBundle;
029 import java.util.Vector;
030
031 import javax.swing.BorderFactory;
032 import javax.swing.Box;
033 import javax.swing.BoxLayout;
034 import javax.swing.JButton;
035 import javax.swing.JDialog;
036 import javax.swing.JLabel;
037 import javax.swing.JPanel;
038 import javax.swing.JScrollPane;
039 import javax.swing.JTabbedPane;
040 import javax.swing.JTextField;
041 import javax.swing.SwingUtilities;
042 import javax.swing.text.JTextComponent;
043
044 import org.jivesoftware.smack.PacketCollector;
045 import org.jivesoftware.smack.SmackConfiguration;
046 import org.jivesoftware.smack.XMPPConnection;
047 import org.jivesoftware.smack.XMPPException;
048 import org.jivesoftware.smack.filter.PacketIDFilter;
049 import org.jivesoftware.smack.packet.IQ;
050 import org.jivesoftware.smackx.packet.*;
051 import org.jivesoftware.smackx.packet.*;
052 import com.valhalla.jbother.jabber.smack.*;
053
054 import com.valhalla.gui.*;
055
056
057 import com.valhalla.jbother.jabber.smack.LastActivity;
058
059 /**
060 * A dialog that collects and shows information about a Jabber user Shows a
061 * dialog with several fields and starts an information collecting field for
062 * each one. As each piece of information is found it fills out the fields
063 *
064 * @author Adam Olsen
065 * @author Andrey Zakirov
066 * @version 1.0
067 */
068 public class InformationViewerDialog extends JDialog implements WaitDialogListener {
069 private ResourceBundle resources = ResourceBundle.getBundle(
070 "JBotherBundle", Locale.getDefault());
071
072 private JButton okButton = new JButton(resources.getString("okButton"));
073 private JButton retrieve = new JButton(resources.getString("retrieve"));
074 private JButton save = new JButton(resources.getString("saveButton"));
075 private String user;
076 private JPanel mainPanel;
077 private JTabbedPane pane = new JTabbedPane();
078 private Vector fields = new Vector();
079 private GridBagConstraints c = new GridBagConstraints();
080 private MJTextField name = new MJTextField();
081 private MJTextField last = new MJTextField();
082 private MJTextField birthday = new MJTextField();
083 private MJTextField nickname = new MJTextField();
084 private MJTextField email = new MJTextField();
085 private MJTextField homepage = new MJTextField();
086 private MJTextField phone = new MJTextField();
087
088 // location
089 private MJTextField street1 = new MJTextField();
090 private MJTextField street2 = new MJTextField();
091 private MJTextField city = new MJTextField();
092 private MJTextField state = new MJTextField();
093 private MJTextField zip = new MJTextField();
094 private MJTextField country = new MJTextField();
095
096 // work
097 private MJTextField company = new MJTextField();
098 private MJTextField department = new MJTextField();
099 private MJTextField position = new MJTextField();
100 private MJTextField role = new MJTextField();
101 private MJTextArea about = new MJTextArea();
102 private WaitDialog wait = new WaitDialog(this, this, resources
103 .getString("pleaseWait"));
104 private MJTextField clientField = new MJTextField();
105 private MJTextField timeField = new MJTextField();
106 private MJTextField lastField = new MJTextField();
107 private boolean personal;
108 protected boolean cancelled = false;
109
110 /**
111 * Default constructor
112 *
113 * @param user
114 * the user you want to collect information about
115 */
116 public InformationViewerDialog(String user, boolean personal) {
117 super(BuddyList.getInstance().getContainerFrame());
118 this.personal = personal;
119 this.user = user;
120 if (!personal)
121 setTitle(resources.getString("information") + " " + user);
122 else
123 setTitle(resources.getString("editInformation"));
124
125 if (!BuddyList.getInstance().checkConnection()) {
126 BuddyList.getInstance().connectionError();
127 return;
128 }
129
130 about.setWrapStyleWord(true);
131 mainPanel = (JPanel) getContentPane();
132
133 mainPanel.setLayout(new BorderLayout());
134 mainPanel.add(pane, BorderLayout.CENTER);
135
136 JPanel buttonPanel = new JPanel();
137 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
138 buttonPanel.add(Box.createHorizontalGlue());
139 buttonPanel.add(retrieve);
140 if (personal)
141 buttonPanel.add(save);
142 buttonPanel.add(okButton);
143 //buttonPanel.add( Box.createHorizontalGlue() );
144 buttonPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
145
146 mainPanel.add(buttonPanel, BorderLayout.SOUTH);
147
148 addGeneralItems();
149 addLocationItems();
150 addWorkItems();
151 addAboutItems();
152
153 if (!personal) {
154 addClientItems();
155 }
156
157 pack();
158 Dimension dim = getSize();
159 setSize(new Dimension(450, (int) dim.getHeight()));
160
161 setLocationRelativeTo(null);
162 DialogTracker.addDialog(this, false, true);
163 collectInformation();
164
165 okButton.addActionListener(new ActionListener() {
166 public void actionPerformed(ActionEvent e) {
167 DialogTracker.removeDialog(InformationViewerDialog.this);
168 }
169 });
170
171 retrieve.addActionListener(new ActionListener() {
172 public void actionPerformed(ActionEvent e) {
173 collectInformation();
174 }
175 });
176
177 save.addActionListener(new ActionListener() {
178 public void actionPerformed(ActionEvent e) {
179 wait.setVisible(true);
180 cancelled = false;
181 disableAll();
182 new Thread(new SaveVCard()).start();
183 }
184 });
185 }
186
187 public void cancel()
188 {
189 cancelled = true;
190 }
191
192 private void collectInformation() {
193 wait.setVisible(true);
194 disableAll();
195 new Thread(new VCardCollector()).start();
196
197 if (!personal) {
198 new Thread(new VersionCollector(clientField, this)).start();
199 new Thread(new TimeCollector(timeField, this)).start();
200 new Thread(new LastCollector(lastField, this)).start();
201 }
202 }
203
204 public InformationViewerDialog(String user) {
205 this(user, false);
206 }
207
208 /**
209 * Saves a the VCard
210 */
211 private class SaveVCard implements Runnable {
212 public void run() {
213 if( cancelled ) return;
214 VCard card = new VCard();
215 card.setFirstName(name.getText());
216 card.setLastName(last.getText());
217 card.setNickName(nickname.getText());
218
219 card.setEmailHome(email.getText());
220 card.setPhoneHome("VOICE", phone.getText());
221 card.setField("BDAY", birthday.getText());
222
223 card.setAddressFieldHome("STREET", street1.getText());
224 card.setAddressFieldHome("LOCALITY", city.getText());
225 card.setAddressFieldHome("REGION", state.getText());
226 card.setAddressFieldHome("PCODE", zip.getText());
227 card.setAddressFieldHome("CTRY", country.getText());
228
229 card.setOrganization(company.getText());
230 card.setOrganizationUnit(department.getText());
231 card.setField("TITLE", position.getText());
232 card.setField("ROLE", role.getText());
233 card.setField("DESC", about.getText());
234
235 try {
236 card.save(BuddyList.getInstance().getConnection());
237 } catch (NullPointerException npe) {
238 npe.printStackTrace();
239 }
240 catch( XMPPException ex ) { }
241
242 if( cancelled ) return;
243
244 SwingUtilities.invokeLater(new Runnable() {
245 public void run() {
246 wait.setVisible(false);
247 enableAll();
248 }
249 });
250 }
251 }
252
253 /**
254 * Collects a VCard for a user and fills in the InformationViewer form
255 * fields
256 */
257 private class VCardCollector implements Runnable {
258 /**
259 * Cancels this thread
260 */
261 public void cancel()
262 {
263 cancelled = true;
264 }
265
266 /**
267 * Called by Thread.start()
268 */
269 public void run() {
270 VCard temp = new VCard();
271 if( cancelled ) return;
272
273 try {
274 if (!personal) {
275 temp.load(BuddyList.getInstance().getConnection(),
276 user);
277 } else {
278 temp.load(BuddyList.getInstance().getConnection());
279 }
280 } catch (NullPointerException npe) {
281 } catch (XMPPException ex) {
282 }
283
284 final VCard card = temp;
285 if( cancelled ) return;
286
287 SwingUtilities.invokeLater(new Runnable() {
288 public void run() {
289 if (card != null) {
290 name.setText(card.getFirstName());
291 last.setText(card.getLastName());
292 nickname.setText(card.getNickName());
293 birthday.setText(card.getField("BDAY"));
294 email.setText(card.getEmailHome());
295 phone.setText(card.getPhoneHome("VOICE"));
296
297 street1.setText(card.getAddressFieldHome("STREET"));
298 city.setText(card.getAddressFieldHome("LOCALITY"));
299 state.setText(card.getAddressFieldHome("REGION"));
300 zip.setText(card.getAddressFieldHome("PCODE"));
301 country.setText(card.getAddressFieldHome("CTRY"));
302
303 company.setText(card.getOrganization());
304 department.setText(card.getOrganizationUnit());
305 position.setText(card.getField("TITLE"));
306 role.setText(card.getField("ROLE"));
307 about.setText(card.getField("DESC"));
308 about.setCaretPosition(0);
309 }
310
311 if (personal)
312 enableAll();
313 wait.setVisible(false);
314 validate();
315 setVisible(true);
316 }
317 });
318 }
319 }
320
321 /**
322 * Creates the "About" tab
323 */
324 private void addAboutItems() {
325 JPanel panel = new JPanel(new BorderLayout());
326 panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
327 panel.add(new JScrollPane(about), BorderLayout.CENTER);
328 fields.add(about);
329
330 pane.add(panel, resources.getString("aboutUser"));
331 }
332
333 /**
334 * Creates the "work" tab
335 */
336 private void addWorkItems() {
337 GridBagLayout grid = new GridBagLayout();
338 JPanel work = createNewPanel(grid);
339
340 addItem(work, resources.getString("company"), company, grid);
341 addItem(work, resources.getString("department"), department, grid);
342 addItem(work, resources.getString("position"), position, grid);
343 addItem(work, resources.getString("role"), role, grid);
344
345 pane.add(work, resources.getString("work"));
346 padEnd(work, grid);
347 }
348
349 /**
350 * Creates the "general" tab
351 */
352 private void addGeneralItems() {
353 GridBagLayout grid = new GridBagLayout();
354 JPanel general = createNewPanel(grid);
355
356 addItem(general, resources.getString("name"), name, grid);
357 addItem(general, resources.getString("last"), last, grid);
358 addItem(general, resources.getString("nickname"), nickname, grid);
359 addItem(general, resources.getString("birthday"), birthday, grid);
360 addItem(general, resources.getString("email"), email, grid);
361 addItem(general, resources.getString("phone"), phone, grid);
362
363 pane.add(general, resources.getString("general"));
364 padEnd(general, grid);
365 }
366
367 /**
368 * Adds the "location" tab items
369 */
370 private void addLocationItems() {
371 GridBagLayout grid = new GridBagLayout();
372 JPanel location = createNewPanel(grid);
373
374 addItem(location, resources.getString("street"), street1, grid);
375 addItem(location, resources.getString("city"), city, grid);
376 addItem(location, resources.getString("state"), state, grid);
377 addItem(location, resources.getString("zip"), zip, grid);
378 addItem(location, resources.getString("country"), country, grid);
379
380 pane.add(location, resources.getString("location"));
381 padEnd(location, grid);
382 }
383
384 /**
385 * Adds the different fields to the displayed form
386 */
387 private void addClientItems() {
388 GridBagLayout grid = new GridBagLayout();
389 JPanel client = createNewPanel(grid);
390
391 /* gets jabber:iq:version information */
392 addItem(client, resources.getString("clientInformation"), clientField,
393 grid);
394 addItem(client, resources.getString("timeInformation"), timeField, grid);
395 addItem(client, "Idle for", lastField, grid);
396 pane.add(client, resources.getString("misc"));
397 padEnd(client, grid);
398 }
399
400 /**
401 * Creates a new panel suitable for the Information Viewer
402 * @param grid The gridbag to use
403 * @return a new JPanel with appropriate constraints
404 */
405 private JPanel createNewPanel(GridBagLayout grid) {
406 JPanel panel = new JPanel(grid);
407 panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
408
409 c.gridx = 0;
410 c.gridy = 0;
411 c.gridwidth = 1;
412 c.weighty = 0;
413 c.anchor = GridBagConstraints.NORTHWEST;
414 return panel;
415 }
416
417 /**
418 * Disables all the fields in the dialog
419 * Appropriate for when you are viewing another users
420 * information (that you can't change)
421 */
422 private void disableAll() {
423 for (int i = 0; i < fields.size(); i++) {
424 JTextComponent field = (JTextComponent) fields.get(i);
425 field.setEditable(false);
426 }
427 validate();
428 }
429
430 /**
431 * Enables all the fields in the dialog
432 * Used for when you are changing your own information.
433 */
434 private void enableAll() {
435 for (int i = 0; i < fields.size(); i++) {
436 JTextComponent field = (JTextComponent) fields.get(i);
437 field.setEditable(true);
438 }
439 validate();
440 }
441
442 /**
443 * Adds a blank panel that takes all excess space on the panel
444 * @param panel The panel to add this spacer to
445 * @param grid The grid that's associated with this panel
446 */
447 private void padEnd(JPanel panel, GridBagLayout grid) {
448 JLabel blank = new JLabel("");
449 c.gridwidth = 2;
450 c.weighty = 1;
451 grid.setConstraints(blank, c);
452 panel.add(blank);
453 }
454
455 /**
456 * Adds a field and starts the thread that will collect it's information
457 *
458 * @param key the name of the field
459 * @param informationThread the thread that will collect the information
460 */
461 private void addItem(JPanel panel, String l, MJTextField field,
462 GridBagLayout grid) {
463 c.fill = GridBagConstraints.HORIZONTAL;
464
465 if (!l.equals(""))
466 l = l + ": ";
467 JLabel label = new JLabel(l);
468 label.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 5));
469 c.ipadx = 2;
470 c.ipady = 2;
471 c.weightx = .1;
472 c.gridx = 0;
473 grid.setConstraints(label, c);
474 panel.add(label);
475 c.gridx++;
476 c.weightx = 1;
477 grid.setConstraints(field, c);
478 panel.add(field);
479 fields.add(field);
480 c.gridy++;
481 }
482
483 public String getUser() {
484 return user;
485 }
486 }
487
488 /**
489 * An <code>InformationCollector</code> that collects jabbber:iq:version
490 * information
491 *
492 * @author Adam Olsen
493 * @version 1.0
494 */
495
496 class VersionCollector implements Runnable {
497 InformationViewerDialog dialog;
498
499 JTextField field;
500
501 /**
502 * The default constructor
503 *
504 * @param dialog
505 * the InformationViewerDialog that called this thread
506 * @param id
507 * the field id
508 */
509 public VersionCollector(MJTextField field, InformationViewerDialog dialog) {
510 this.field = field;
511 this.dialog = dialog;
512 }
513
514 /**
515 * Called by the enclosing <code>java.util.Thread</code> The run() method
516 * is responsible for collecting the requested information and setting the
517 * field in the InformationViewerDialog
518 */
519 public void run() {
520 XMPPConnection con = BuddyList.getInstance().getConnection();
521 if( dialog.cancelled ) return;
522
523 Version request = new Version();
524 request.setType(IQ.Type.GET);
525 request.setTo(dialog.getUser());
526
527 // Create a packet collector to listen for a response.
528 PacketCollector collector = con
529 .createPacketCollector(new PacketIDFilter(request.getPacketID()));
530
531 con.sendPacket(request);
532
533 // Wait up to 5 seconds for a result.
534 IQ result = (IQ) collector.nextResult(SmackConfiguration
535 .getPacketReplyTimeout());
536 if( dialog.cancelled ) return;
537
538 if (result != null && result.getType() == IQ.Type.RESULT) {
539 Version v = (Version) result;
540
541 field.setText(v.getName() + " " + v.getVersion() + " / "
542 + v.getOs());
543 } else
544 field.setText("N/A");
545 field.validate();
546 }
547 }
548
549 /**
550 * @author Adam Olsen
551 * @version 1.0
552 */
553
554 class TimeCollector implements Runnable {
555 InformationViewerDialog dialog;
556
557 JTextField field;
558
559 /**
560 * The default constructor
561 *
562 * @param dialog
563 * the InformationViewerDialog that called this thread
564 * @param id
565 * the field id
566 */
567 public TimeCollector(MJTextField field, InformationViewerDialog dialog) {
568 this.field = field;
569 this.dialog = dialog;
570 }
571
572 /**
573 * Called by the enclosing <code>java.util.Thread</code> The run() method
574 * is responsible for collecting the requested information and setting the
575 * field in the InformationViewerDialog
576 */
577 public void run() {
578 if( dialog.cancelled ) return;
579
580 XMPPConnection con = BuddyList.getInstance().getConnection();
581
582 Time request = new Time();
583 request.setType(IQ.Type.GET);
584 request.setTo(dialog.getUser());
585
586 // Create a packet collector to listen for a response.
587 PacketCollector collector = con
588 .createPacketCollector(new PacketIDFilter(request.getPacketID()));
589
590 con.sendPacket(request);
591
592
593 // Wait up to 5 seconds for a result.
594 IQ result = (IQ) collector.nextResult(SmackConfiguration
595 .getPacketReplyTimeout());
596 collector.cancel();
597 if( dialog.cancelled ) return;
598
599 if (result != null && result.getType() == IQ.Type.RESULT) {
600 Time t = (Time) result;
601
602 field.setText(t.getDisplay());
603 } else
604 field.setText("N/A");
605 field.validate();
606 }
607 }
608
609 /**
610 * Collects information in order to show the idle time of a user
611 */
612 class LastCollector implements Runnable {
613 InformationViewerDialog dialog;
614
615 JTextField field;
616
617 /**
618 * The default constructor
619 *
620 * @param dialog
621 * the InformationViewerDialog that called this thread
622 * @param id
623 * the field id
624 */
625 public LastCollector(MJTextField field, InformationViewerDialog dialog) {
626 this.field = field;
627 this.dialog = dialog;
628 }
629
630 /**
631 * Called by the enclosing <code>java.util.Thread</code> The run() method
632 * is responsible for collecting the requested information and setting the
633 * field in the InformationViewerDialog
634 */
635 public void run() {
636 if( dialog.cancelled ) return;
637
638 XMPPConnection con = BuddyList.getInstance().getConnection();
639
640 LastActivity request = new LastActivity();
641 request.setType(IQ.Type.GET);
642 request.setTo(dialog.getUser());
643
644 // Create a packet collector to listen for a response.
645 PacketCollector collector = con
646 .createPacketCollector(new PacketIDFilter(request.getPacketID()));
647
648 con.sendPacket(request);
649
650 // Wait up to 5 seconds for a result.
651 IQ result = (IQ) collector.nextResult(SmackConfiguration
652 .getPacketReplyTimeout());
653 if( dialog.cancelled ) return;
654
655 if (result != null && result.getType() == IQ.Type.RESULT) {
656 LastActivity t = (LastActivity) result;
657
658 field.setText(t.showTime());
659 } else
660 field.setText("N/A");
661 field.validate();
662 }
663 }