2 * Copyright (C) 2015 Roland Haeder
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 package org.mxchange.addressbook.client.gui;
19 import java.awt.BorderLayout;
20 import java.awt.GridLayout;
21 import java.awt.event.ActionEvent;
22 import java.awt.event.ActionListener;
23 import java.awt.event.MouseAdapter;
24 import java.awt.event.MouseEvent;
25 import java.awt.event.WindowAdapter;
26 import java.awt.event.WindowEvent;
27 import java.text.MessageFormat;
28 import javax.swing.BorderFactory;
29 import javax.swing.BoxLayout;
30 import javax.swing.DefaultComboBoxModel;
31 import javax.swing.JButton;
32 import javax.swing.JComboBox;
33 import javax.swing.JDialog;
34 import javax.swing.JFormattedTextField;
35 import javax.swing.JFrame;
36 import javax.swing.JLabel;
37 import javax.swing.JMenu;
38 import javax.swing.JMenuBar;
39 import javax.swing.JMenuItem;
40 import javax.swing.JPanel;
41 import javax.swing.JScrollPane;
42 import javax.swing.JTable;
43 import javax.swing.JTextArea;
44 import javax.swing.JTextField;
45 import javax.swing.border.TitledBorder;
46 import javax.swing.table.TableModel;
47 import org.mxchange.addressbook.BaseAddressbookSystem;
48 import org.mxchange.addressbook.application.AddressbookApplication;
49 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
50 import org.mxchange.addressbook.manager.contact.ManageableAddressbookContact;
51 import org.mxchange.addressbook.model.contact.ContactTableModel;
52 import org.mxchange.jcore.client.Client;
53 import org.mxchange.jcore.contact.Contact;
54 import org.mxchange.jcore.contact.Gender;
55 import org.mxchange.jcore.exceptions.FrameAlreadyInitializedException;
59 * @author Roland Haeder
61 public class AddressbookFrame extends BaseAddressbookSystem implements ClientFrame {
66 private static ClientFrame self;
69 * Singelton getter for this frame instance.
71 * @param client Client instance
72 * @return Returns a singelton instance of this frame
74 public static final ClientFrame getSelfInstance (final Client client) {
76 if (!(self instanceof ClientFrame)) {
77 // Create new instance
78 self = new AddressbookFrame(client);
86 * Dialog box "add contact"
88 private JDialog addContact;
91 * Frame instance for "add own data"
93 private JMenuItem addOwnItem;
96 * Instance to table model
98 private TableModel dataModel;
103 private JTable dataTable;
106 * Frame instance for "edit own data"
108 private JMenuItem editOwnItem;
113 private final JFrame frame;
116 * Whether this frame has been initialized
118 private boolean initialized;
121 * Status label needs to be updated
123 private JLabel statusLabel;
126 * Creates an instance of this frame with a client instance
130 private AddressbookFrame (final Client client) {
132 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
134 // Set frame instance
135 this.frame = new JFrame();
136 this.frame.setTitle(this.generateFrameTitle("main")); //NOI18N
139 this.setClient(client);
142 this.getLogger().trace("EXIT!"); //NOI18N
146 public Contact doEnterOwnData () {
148 this.getLogger().trace("CALLED!"); //NOI18N
150 // Is the "add contact" window visible?
151 if (this.addContact.isVisible()) {
152 // Something bad happened
153 throw new IllegalStateException("Window addContact is already visible."); //NOI18N
156 // Disable main window
157 this.frame.setEnabled(false);
159 // Make other window visible
160 this.addContact.setVisible(true);
163 this.getLogger().trace("Returning null : EXIT!"); //NOI18N
165 // Return value is not supported
170 * Shutdown this frame
173 public void doShutdown () {
175 this.getLogger().trace("CALLED!"); //NOI18N
177 // First only show shutdown status
178 this.updateStatus("shutdown"); //NOI18N
181 this.getLogger().trace("EXIT!"); //NOI18N
186 * Enables main window (frame)
189 public void enableMainWindow () {
191 this.getLogger().trace("CALLED!"); //NOI18N
194 this.frame.setEnabled(true);
196 // Request focus for this window
197 this.frame.requestFocus();
200 this.getLogger().trace("EXIT!"); //NOI18N
204 * Setups the frame, do not set isInitialized here
206 * @param client Client instance
209 public void setupFrame (final Client client) {
211 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
213 // Get and cast manager instance
214 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getClient().getManager();
216 // Has the user entered own data?
217 if (manager.isOwnContactAdded()) {
219 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false"); //NOI18N
221 // Not entered yet, so disable "add" menu
222 this.addOwnItem.setEnabled(false);
225 this.editOwnItem.setEnabled(false);
228 // Make the frame visible
229 this.frame.setVisible(true);
232 this.updateStatus("done"); //NOI18N
235 this.getLogger().trace("EXIT!"); //NOI18N
239 * Initalizes this frame. Having initComponents() exposed (publicly
240 * accessible) means that any other object can initialize components which
244 * org.mxchange.jcore.exceptions.FrameAlreadyInitializedException If
245 * this method has been called twice
248 public void init () throws FrameAlreadyInitializedException {
250 this.getLogger().trace("CALLED!"); //NOI18N
252 // Has this frame been initialized?
253 if (this.isInitialized()) {
255 throw new FrameAlreadyInitializedException();
259 this.initComponents();
262 this.initialized = true;
265 this.getLogger().trace("EXIT!"); //NOI18N
269 * Returns field isInitialized. This flag indicates whether this frame has
270 * been initialized or not.
272 * @return Field isInitialized
275 public final boolean isInitialized () {
276 return this.initialized;
280 * Shuts down the application.
283 public void shutdownApplication () {
285 this.getLogger().trace("CALLED!"); //NOI18N
287 // To do this, the frame must be initialized
288 if (!this.isInitialized()) {
289 // Not initalized, so bad call
290 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this."); //NOI18N
294 // Call shutdown method
295 this.getClient().getApplication().doShutdown();
298 this.getLogger().trace("EXIT!"); //NOI18N
302 * Adds a new menu item with given key to menu instance
304 * @param menu Menu instance to add item to
305 * @param key Message key part
306 * @param listener Listener instance
308 private void addMenuItem (final JMenu menu, final String key, final ActionListener listener) {
310 this.getLogger().trace(MessageFormat.format("menu={0},key={1},listener={2} - CALLED!", menu, key, listener)); //NOI18N
313 JMenuItem item = this.initMenuItemWithTooltip(key);
316 item.addActionListener(listener);
322 this.getLogger().trace("EXIT!"); //NOI18N
326 * Adds a JTextField with label and tool tip to given panel
328 * @param panel Panel instance to add text field
329 * @param key Part of message key from resource bundle
330 * @param fieldLength Length of text field
332 private void addTextFieldWithLabelToPanel (final JPanel panel, final String key, final int fieldLength) {
334 this.getLogger().trace(MessageFormat.format("panel={0},key={1},fieldLength={2} - CALLED!", panel, key, fieldLength)); //NOI18N
336 // Init label for given key
337 JLabel label = new JLabel(this.getBundle().getString(String.format("AddressbookFrame.%s.text", key))); //NOI18N
339 // And input box wih tool tip
340 JTextField field = new JTextField(fieldLength);
341 field.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.%s.toolTipText", key))); //NOI18N
348 this.getLogger().trace("EXIT!"); //NOI18N
352 * Generates a title for borders
354 * @param key Key part to look for
355 * @return Human-readable title
357 private String generateBorderTitle (final String key) {
358 // Call bundle instance
359 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key)); //NOI18N
363 * Generates a title for all frames based on given sub title key. If null is
364 * given, the sub title is not generated.
366 * @param subKey Key for sub title resource
367 * @return A full application title
369 private String generateFrameTitle (final String subKey) {
371 String title = AddressbookApplication.printableTitle();
374 if (subKey != null) {
376 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey))); //NOI18N
384 * Initializes "add" and "cancel" buttons
386 private void initAddCancelButtons () {
388 this.getLogger().trace("CALLED!"); //NOI18N
391 JPanel panel = new JPanel();
392 panel.setLayout(new GridLayout(1, 2, 10, 10));
394 // Generate "add" button
395 JButton addButton = new JButton(this.getBundle().getString("AddressbookFrame.button.addAddress.text"));
398 addButton.addActionListener(new AddActionListener(this.addContact, this));
400 // Add button to panel
401 panel.add(addButton);
403 // Generate "cancel" button
404 JButton cancelButton = new JButton(this.getBundle().getString("AddressbookFrame.button.cancel.text"));
407 cancelButton.addActionListener(new CancelActionListener(this.addContact, this));
409 // Add button to panel
410 panel.add(cancelButton);
412 // Add panel to main panel
413 this.addContact.add(panel);
416 this.getLogger().trace("EXIT!"); //NOI18N
420 * Initializes "add contact" dialog
422 private void initAddContactDialog () {
424 this.getLogger().trace("CALLED!"); //NOI18N
426 // Instance dialog and set title
427 this.addContact = new JDialog();
428 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact")); //NOI18N
431 this.addContact.setLayout(new GridLayout(0, 1, 2, 2));
433 // Only hide it on close and make it appear in middle of screen
434 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
435 this.addContact.setLocationRelativeTo(this.frame);
437 // Set always on top and auto-focus
438 this.addContact.setAlwaysOnTop(true);
439 this.addContact.setAutoRequestFocus(true);
442 this.addContact.setSize(500, 500);
444 // And it is not resizeable
445 this.addContact.setResizable(false);
448 * Add listener which asks for confirmation, if data has been entered
449 * but not saved yet. The user may appriciate this ... ;-)
453 this.addContact.addWindowListener(new WindowAdapter() {
455 * Invoked when a window has been closed.
458 public void windowClosed (final WindowEvent e) {
459 // Enable main window again
460 AddressbookFrame.getSelfInstance(null).enableMainWindow();
464 * Invoked when a window is in the process of being closed. The
465 * close operation can be overridden at this point.
468 public void windowClosing (final WindowEvent e) {
469 e.getWindow().dispose();
475 initNameDataPanel(this.addContact);
477 // 2) "address" panel
478 initAddressDataPanel(this.addContact);
481 initOtherDataPanel(this.addContact);
483 // 4) "Add" and "Cancel" buttons, combined they are unique for this dialog
484 initAddCancelButtons();
486 // x)Only for developing:
487 /* DEBUG: */ this.addContact.setVisible(true);
490 this.getLogger().trace("EXIT!"); //NOI18N
494 * Initializes address panel
496 * @param dialog A JDialog instance to this components to
498 private void initAddressDataPanel (final JDialog dialog) {
500 this.getLogger().trace("CALLED!"); //NOI18N
502 // Panel "address" input boxes
503 JPanel addressPanel = new JPanel();
504 addressPanel.setLayout(new GridLayout(0, 4, 3, 3));
506 // Set border to titled version
507 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address"))); //NOI18N
509 // Add text field for street
510 this.addTextFieldWithLabelToPanel(addressPanel, "street", 20); //NOI18N
513 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
515 // And text field, but only accept numbers
516 JFormattedTextField number = new JFormattedTextField();
517 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.toolTipText"));
519 // Add both to street panel
520 addressPanel.add(numberLabel);
521 addressPanel.add(number);
523 // Label for ZIP code, again numbers only
524 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
526 // Init text field with label
527 JFormattedTextField zip = new JFormattedTextField();
528 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.toolTipText"));
530 // Add both to street panel
531 addressPanel.add(zipLabel);
532 addressPanel.add(zip);
534 // Add text field for city name
535 this.addTextFieldWithLabelToPanel(addressPanel, "city", 20); //NOI18N
537 // Add panel to dialog
538 dialog.add(addressPanel);
541 this.getLogger().trace("EXIT!"); //NOI18N
545 * Initialize components
547 private void initComponents () {
549 this.getLogger().trace("CALLED!"); //NOI18N
551 // Set default close operation
552 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
554 // Register shutdown listener
555 this.frame.addWindowListener(new WindowAdapter() {
557 * Invoked when a window has been closed.
560 public void windowClosed (final WindowEvent e) {
561 // Shutdown application cleanly
562 self.shutdownApplication();
566 * Invoked when a window is in the process of being closed. The
567 * close operation can be overridden at this point.
570 public void windowClosing (final WindowEvent e) {
571 // Also shutdown cleanly here
572 self.shutdownApplication();
576 // Setup layout manager
577 this.frame.setLayout(new BorderLayout(2, 2));
580 this.frame.setSize(700, 400);
582 // Center window in middle of screen, instead of top-left corner
583 this.frame.setLocationRelativeTo(null);
594 // Init other windows
598 this.getLogger().trace("EXIT!"); //NOI18N
602 * Initializes a menu item instance with tool tip
604 * @param key Message key part
605 * @return A finished JMenuItem instance
607 private JMenuItem initMenuItemWithTooltip (final String key) {
609 this.getLogger().trace(MessageFormat.format("key={0} - CALLED!", key)); //NOI18N
611 JMenuItem item = new JMenuItem(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.text", key))); //NOI18N
612 item.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.toolTipText", key))); //NOI18N
615 this.getLogger().trace(MessageFormat.format("item={0} - EXIT!", item)); //NOI18N
622 * Initializes the menu system
624 private void initMenuSystem () {
626 this.getLogger().trace("CALLED!"); //NOI18N
628 // Init menu bar, menu and item instances
629 JMenuBar menuBar = new JMenuBar();
635 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
638 // 1.x) Exit program (should be last)
639 this.addMenuItem(menu, "exitProgram", new ActionListener() { //NOI18N
641 * If the user has performed this action
643 * @param e An instance of an ActionEvent class
646 public void actionPerformed (final ActionEvent e) {
647 self.shutdownApplication();
651 // Add menu -> menu bar
655 // 2) Addressbook menu
656 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
659 this.addOwnItem = this.initMenuItemWithTooltip("addOwnData"); //NOI18N
661 // Add listener to exit menu
662 this.addOwnItem.addActionListener(new ActionListener() {
664 * If the user has performed this action
666 * @param e An instance of an ActionEvent class
669 public void actionPerformed (final ActionEvent e) {
671 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
672 manager.doEnterOwnData();
673 } catch (final ContactAlreadyAddedException ex) {
674 // Already added, log away
675 // @TODO maybe output message here?
676 self.logException(ex);
682 menu.add(this.addOwnItem);
684 // 2.2) Edit own data
685 this.editOwnItem = this.initMenuItemWithTooltip("editOwnData"); //NOI18N
687 // Add listener to exit menu
688 this.editOwnItem.addActionListener(new ActionListener() {
690 * If the user has performed this action
692 * @param e An instance of an ActionEvent class
695 public void actionPerformed (final ActionEvent e) {
696 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
697 manager.doChangeOwnData();
702 menu.add(this.editOwnItem);
705 // 1) Add new contact
706 this.addMenuItem(menu, "addNewContact", new ActionListener() { //NOI18N
708 * If the user has performed this action
710 * @param e An instance of an ActionEvent class
713 public void actionPerformed (final ActionEvent e) {
714 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
715 manager.doAddOtherAddress();
719 // Add menu -> menu bar
722 // Add menu bar -> frame
723 this.frame.add(menuBar, BorderLayout.NORTH);
726 this.getLogger().trace("EXIT!"); //NOI18N
730 * Initializes name panel
732 * @param dialog A JDialog instance to this components to
734 private void initNameDataPanel (final JDialog dialog) {
736 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
738 // Panel "name" input boxes
739 JPanel namePanel = new JPanel();
740 namePanel.setLayout(new GridLayout(0, 2, 3, 3));
742 // Set border to titled version
743 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name"))); //NOI18N
746 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
749 Gender[] genders = Gender.values();
751 // Init gender combo box with tool tip
752 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
753 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.toolTipText"));
755 // Add both to gender panel
756 namePanel.add(gLabel);
757 namePanel.add(gender);
759 // Add text field for surname
760 this.addTextFieldWithLabelToPanel(namePanel, "surname", 20); //NOI18N
762 // Add text field for family name
763 this.addTextFieldWithLabelToPanel(namePanel, "familyName", 20); //NOI18N
765 // Finally add panel to dialog
766 dialog.add(namePanel);
769 this.getLogger().trace("EXIT!"); //NOI18N
773 * Initializes "other" data panel
775 * @param dialog A JDialog instance to this components to
776 * @todo Fill this with life
778 private void initOtherDataPanel (final JDialog dialog) {
780 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
782 // Panel "other" input boxes
783 JPanel otherPanel = new JPanel();
784 otherPanel.setLayout(new GridLayout(0, 2, 3, 3));
785 otherPanel.setBorder(new TitledBorder(this.generateBorderTitle("other"))); //NOI18N
787 // Add text field for email address
788 this.addTextFieldWithLabelToPanel(otherPanel, "emailAddress", 20); //NOI18N
790 // Add text field for phone number
791 this.addTextFieldWithLabelToPanel(otherPanel, "phoneNumber", 20); //NOI18N
793 // Add text field for cellphone number
794 this.addTextFieldWithLabelToPanel(otherPanel, "cellphoneNumber", 20); //NOI18N
796 // Add text field for fax number
797 this.addTextFieldWithLabelToPanel(otherPanel, "faxNumber", 20); //NOI18N
800 JLabel commentLabel = new JLabel(this.getBundle().getString("AddressbookFrame.comment.text"));
802 // Init text area with tool tip
803 JTextArea comment = new JTextArea(5, 20);
804 comment.setToolTipText(this.getBundle().getString("AddressbookFrame.comment.toolTipText"));
807 otherPanel.add(commentLabel);
808 otherPanel.add(comment);
810 // Finally add panel to dialog
811 dialog.add(otherPanel);
814 this.getLogger().trace("EXIT!"); //NOI18N
818 * Initialize other dialogs (e.g. "Add contact")
820 private void initOtherDialogs () {
822 this.getLogger().trace("CALLED!"); //NOI18N
824 // Init other windows:
826 initAddContactDialog();
829 this.getLogger().trace("EXIT!"); //NOI18N
833 * Initializes status panel
835 private void initStatusPanel () {
837 this.getLogger().trace("CALLED!"); //NOI18N
839 // Init status label (which needs to be updated
840 this.statusLabel = new JLabel();
841 this.updateStatus("initializing"); //NOI18N
843 // Init status bar in south
844 JPanel panel = new JPanel();
845 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
846 panel.add(this.statusLabel);
847 panel.setBorder(BorderFactory.createEtchedBorder());
849 // Add panel to frame
850 this.frame.add(panel, BorderLayout.SOUTH);
853 this.getLogger().trace("EXIT!"); //NOI18N
857 * Initializes the table which will show all contacts
859 private void initTable () {
861 this.getLogger().trace("CALLED!"); //NOI18N
863 // Instance table model
864 this.dataModel = new ContactTableModel(this.getClient());
867 this.dataTable = new JTable(this.dataModel);
869 // Add mouse listener
870 this.dataTable.addMouseListener(new MouseAdapter() {
872 * If the user peformed a click on a cell
874 * @param e Mouse event instance
877 public void mouseClicked (final MouseEvent e) {
878 throw new UnsupportedOperationException("Unfinished."); //NOI18N
882 // Instance scroll pane
883 JScrollPane scroller = new JScrollPane();
885 // Add table to scroll pane
886 scroller.setViewportView(this.dataTable);
887 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
888 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
891 this.frame.add(scroller, BorderLayout.CENTER);
894 this.getLogger().trace("EXIT!"); //NOI18N
898 * Updates status to given type
900 * @param type Status type
902 private void updateStatus (final String type) {
904 this.getLogger().trace(MessageFormat.format("type={0} - CALLED!", type)); //NOI18N
906 // Set status message
907 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type))); //NOI18N
910 this.getLogger().trace("EXIT!"); //NOI18N
914 * Class for "add address" button
916 private static class AddActionListener extends BaseAddressbookSystem implements ActionListener {
920 private final JDialog dialog;
923 * Frame (not JFrame) instance
925 private final ClientFrame frame;
928 * Constructor for action listener
930 * @param dialog Dialog instance to call back
931 * @param frame Frame instance (not JFrame)
933 protected AddActionListener (final JDialog dialog, final ClientFrame frame) {
934 // Set dialog and frame here
935 this.dialog = dialog;
940 * If the action has been performed
942 * @param e The performed action event
945 public void actionPerformed (final ActionEvent e) {
946 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
951 * Class for "cancel address" button
953 private static class CancelActionListener extends BaseAddressbookSystem implements ActionListener {
957 private final JDialog dialog;
960 * Frame (not JFrame) instance
962 private final ClientFrame frame;
965 * Constructor for action listener
967 * @param dialog Dialog instance to call back
968 * @param frame Frame instance (not JFrame)
970 protected CancelActionListener (final JDialog dialog, final ClientFrame frame) {
971 // Set dialog and frame here
972 this.dialog = dialog;
977 * If the action has been performed
979 * @param e The performed action event
982 public void actionPerformed (final ActionEvent e) {
983 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.