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 org.mxchange.jcore.client.gui.ClientFrame;
20 import java.awt.BorderLayout;
21 import java.awt.GridLayout;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.awt.event.MouseAdapter;
25 import java.awt.event.MouseEvent;
26 import java.awt.event.WindowAdapter;
27 import java.awt.event.WindowEvent;
28 import java.text.MessageFormat;
29 import javax.swing.BorderFactory;
30 import javax.swing.BoxLayout;
31 import javax.swing.DefaultComboBoxModel;
32 import javax.swing.JButton;
33 import javax.swing.JComboBox;
34 import javax.swing.JDialog;
35 import javax.swing.JFormattedTextField;
36 import javax.swing.JFrame;
37 import javax.swing.JLabel;
38 import javax.swing.JMenu;
39 import javax.swing.JMenuBar;
40 import javax.swing.JMenuItem;
41 import javax.swing.JPanel;
42 import javax.swing.JScrollPane;
43 import javax.swing.JTable;
44 import javax.swing.JTextArea;
45 import javax.swing.JTextField;
46 import javax.swing.border.TitledBorder;
47 import javax.swing.table.TableModel;
48 import org.mxchange.addressbook.BaseAddressbookSystem;
49 import org.mxchange.addressbook.application.AddressbookApplication;
50 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
51 import org.mxchange.addressbook.manager.contact.ManageableAddressbookContact;
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;
56 import org.mxchange.jcore.model.swing.contact.ContactTableModel;
60 * @author Roland Haeder
62 public class AddressbookFrame extends BaseAddressbookSystem implements ClientFrame {
67 private static ClientFrame self;
70 * Singelton getter for this frame instance.
72 * @param client Client instance
73 * @return Returns a singelton instance of this frame
75 public static final ClientFrame getSelfInstance (final Client client) {
77 if (!(self instanceof ClientFrame)) {
78 // Create new instance
79 self = new AddressbookFrame(client);
87 * Dialog box "add contact"
89 private JDialog addContact;
92 * Frame instance for "add own data"
94 private JMenuItem addOwnItem;
97 * Instance to table model
99 private TableModel dataModel;
104 private JTable dataTable;
107 * Frame instance for "edit own data"
109 private JMenuItem editOwnItem;
114 private final JFrame frame;
117 * Whether this frame has been initialized
119 private boolean initialized;
122 * Status label needs to be updated
124 private JLabel statusLabel;
127 * Creates an instance of this frame with a client instance
131 private AddressbookFrame (final Client client) {
133 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
135 // Set frame instance
136 this.frame = new JFrame();
137 this.frame.setTitle(this.generateFrameTitle("main")); //NOI18N
140 this.setClient(client);
143 this.getLogger().trace("EXIT!"); //NOI18N
147 public Contact doEnterOwnData () {
149 this.getLogger().trace("CALLED!"); //NOI18N
151 // Is the "add contact" window visible?
152 if (this.addContact.isVisible()) {
153 // Something bad happened
154 throw new IllegalStateException("Window addContact is already visible."); //NOI18N
157 // Disable main window
158 this.frame.setEnabled(false);
160 // Make other window visible
161 this.addContact.setVisible(true);
164 this.getLogger().trace("Returning null : EXIT!"); //NOI18N
166 // Return value is not supported
171 * Shutdown this frame
174 public void doShutdown () {
176 this.getLogger().trace("CALLED!"); //NOI18N
178 // First only show shutdown status
179 this.updateStatus("shutdown"); //NOI18N
182 this.getLogger().trace("EXIT!"); //NOI18N
187 * Enables main window (frame)
190 public void enableMainWindow () {
192 this.getLogger().trace("CALLED!"); //NOI18N
195 this.frame.setEnabled(true);
197 // Request focus for this window
198 this.frame.requestFocus();
201 this.getLogger().trace("EXIT!"); //NOI18N
205 * Setups the frame, do not set isInitialized here
207 * @param client Client instance
210 public void setupFrame (final Client client) {
212 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
214 // Get and cast manager instance
215 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getClient().getManager();
217 // Has the user entered own data?
218 if (manager.isOwnContactAdded()) {
220 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false"); //NOI18N
222 // Not entered yet, so disable "add" menu
223 this.addOwnItem.setEnabled(false);
226 this.editOwnItem.setEnabled(false);
229 // Make the frame visible
230 this.frame.setVisible(true);
233 this.updateStatus("done"); //NOI18N
236 this.getLogger().trace("EXIT!"); //NOI18N
240 * Initalizes this frame. Having initComponents() exposed (publicly
241 * accessible) means that any other object can initialize components which
245 * org.mxchange.jcore.exceptions.FrameAlreadyInitializedException If
246 * this method has been called twice
249 public void init () throws FrameAlreadyInitializedException {
251 this.getLogger().trace("CALLED!"); //NOI18N
253 // Has this frame been initialized?
254 if (this.isInitialized()) {
256 throw new FrameAlreadyInitializedException();
260 this.initComponents();
263 this.initialized = true;
266 this.getLogger().trace("EXIT!"); //NOI18N
270 * Returns field isInitialized. This flag indicates whether this frame has
271 * been initialized or not.
273 * @return Field isInitialized
276 public final boolean isInitialized () {
277 return this.initialized;
281 * Shuts down the application.
284 public void shutdownApplication () {
286 this.getLogger().trace("CALLED!"); //NOI18N
288 // To do this, the frame must be initialized
289 if (!this.isInitialized()) {
290 // Not initalized, so bad call
291 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this."); //NOI18N
295 // Call shutdown method
296 this.getClient().getApplication().doShutdown();
299 this.getLogger().trace("EXIT!"); //NOI18N
303 * Adds a new menu item with given key to menu instance
305 * @param menu Menu instance to add item to
306 * @param key Message key part
307 * @param listener Listener instance
309 private void addMenuItem (final JMenu menu, final String key, final ActionListener listener) {
311 this.getLogger().trace(MessageFormat.format("menu={0},key={1},listener={2} - CALLED!", menu, key, listener)); //NOI18N
314 JMenuItem item = this.initMenuItemWithTooltip(key);
317 item.addActionListener(listener);
323 this.getLogger().trace("EXIT!"); //NOI18N
327 * Adds a JTextField with label and tool tip to given panel
329 * @param panel Panel instance to add text field
330 * @param key Part of message key from resource bundle
331 * @param fieldLength Length of text field
333 private void addTextFieldWithLabelToPanel (final JPanel panel, final String key, final int fieldLength) {
335 this.getLogger().trace(MessageFormat.format("panel={0},key={1},fieldLength={2} - CALLED!", panel, key, fieldLength)); //NOI18N
337 // Init label for given key
338 JLabel label = new JLabel(this.getBundle().getString(String.format("AddressbookFrame.%s.text", key))); //NOI18N
340 // And input box wih tool tip
341 JTextField field = new JTextField(fieldLength);
342 field.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.%s.toolTipText", key))); //NOI18N
349 this.getLogger().trace("EXIT!"); //NOI18N
353 * Generates a title for borders
355 * @param key Key part to look for
356 * @return Human-readable title
358 private String generateBorderTitle (final String key) {
359 // Call bundle instance
360 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key)); //NOI18N
364 * Generates a title for all frames based on given sub title key. If null is
365 * given, the sub title is not generated.
367 * @param subKey Key for sub title resource
368 * @return A full application title
370 private String generateFrameTitle (final String subKey) {
372 String title = AddressbookApplication.printableTitle();
375 if (subKey != null) {
377 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey))); //NOI18N
385 * Initializes "add" and "cancel" buttons
387 private void initAddCancelButtons () {
389 this.getLogger().trace("CALLED!"); //NOI18N
392 JPanel panel = new JPanel();
393 panel.setLayout(new GridLayout(1, 2, 10, 10));
395 // Generate "add" button
396 JButton addButton = new JButton(this.getBundle().getString("AddressbookFrame.button.addAddress.text"));
399 addButton.addActionListener(new AddActionListener(this.addContact, this));
401 // Add button to panel
402 panel.add(addButton);
404 // Generate "cancel" button
405 JButton cancelButton = new JButton(this.getBundle().getString("AddressbookFrame.button.cancel.text"));
408 cancelButton.addActionListener(new CancelActionListener(this.addContact, this));
410 // Add button to panel
411 panel.add(cancelButton);
413 // Add panel to main panel
414 this.addContact.add(panel);
417 this.getLogger().trace("EXIT!"); //NOI18N
421 * Initializes "add contact" dialog
423 private void initAddContactDialog () {
425 this.getLogger().trace("CALLED!"); //NOI18N
427 // Instance dialog and set title
428 this.addContact = new JDialog();
429 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact")); //NOI18N
432 this.addContact.setLayout(new GridLayout(0, 1, 2, 2));
434 // Only hide it on close and make it appear in middle of screen
435 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
436 this.addContact.setLocationRelativeTo(this.frame);
438 // Set always on top and auto-focus
439 this.addContact.setAlwaysOnTop(true);
440 this.addContact.setAutoRequestFocus(true);
443 this.addContact.setSize(500, 500);
445 // And it is not resizeable
446 this.addContact.setResizable(false);
449 * Add listener which asks for confirmation, if data has been entered
450 * but not saved yet. The user may appriciate this ... ;-)
454 this.addContact.addWindowListener(new WindowAdapter() {
456 * Invoked when a window has been closed.
459 public void windowClosed (final WindowEvent e) {
460 // Enable main window again
461 AddressbookFrame.getSelfInstance(null).enableMainWindow();
465 * Invoked when a window is in the process of being closed. The
466 * close operation can be overridden at this point.
469 public void windowClosing (final WindowEvent e) {
470 e.getWindow().dispose();
476 initNameDataPanel(this.addContact);
478 // 2) "address" panel
479 initAddressDataPanel(this.addContact);
482 initOtherDataPanel(this.addContact);
484 // 4) "Add" and "Cancel" buttons, combined they are unique for this dialog
485 initAddCancelButtons();
487 // x)Only for developing:
488 /* DEBUG: */ this.addContact.setVisible(true);
491 this.getLogger().trace("EXIT!"); //NOI18N
495 * Initializes address panel
497 * @param dialog A JDialog instance to this components to
499 private void initAddressDataPanel (final JDialog dialog) {
501 this.getLogger().trace("CALLED!"); //NOI18N
503 // Panel "address" input boxes
504 JPanel addressPanel = new JPanel();
505 addressPanel.setLayout(new GridLayout(0, 4, 3, 3));
507 // Set border to titled version
508 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address"))); //NOI18N
510 // Add text field for street
511 this.addTextFieldWithLabelToPanel(addressPanel, "street", 20); //NOI18N
514 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
516 // And text field, but only accept numbers
517 JFormattedTextField number = new JFormattedTextField();
518 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.toolTipText"));
520 // Add both to street panel
521 addressPanel.add(numberLabel);
522 addressPanel.add(number);
524 // Label for ZIP code, again numbers only
525 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
527 // Init text field with label
528 JFormattedTextField zip = new JFormattedTextField();
529 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.toolTipText"));
531 // Add both to street panel
532 addressPanel.add(zipLabel);
533 addressPanel.add(zip);
535 // Add text field for city name
536 this.addTextFieldWithLabelToPanel(addressPanel, "city", 20); //NOI18N
538 // Add panel to dialog
539 dialog.add(addressPanel);
542 this.getLogger().trace("EXIT!"); //NOI18N
546 * Initialize components
548 private void initComponents () {
550 this.getLogger().trace("CALLED!"); //NOI18N
552 // Set default close operation
553 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
555 // Register shutdown listener
556 this.frame.addWindowListener(new WindowAdapter() {
558 * Invoked when a window has been closed.
561 public void windowClosed (final WindowEvent e) {
562 // Shutdown application cleanly
563 self.shutdownApplication();
567 * Invoked when a window is in the process of being closed. The
568 * close operation can be overridden at this point.
571 public void windowClosing (final WindowEvent e) {
572 // Also shutdown cleanly here
573 self.shutdownApplication();
577 // Setup layout manager
578 this.frame.setLayout(new BorderLayout(2, 2));
581 this.frame.setSize(700, 400);
583 // Center window in middle of screen, instead of top-left corner
584 this.frame.setLocationRelativeTo(null);
595 // Init other windows
599 this.getLogger().trace("EXIT!"); //NOI18N
603 * Initializes a menu item instance with tool tip
605 * @param key Message key part
606 * @return A finished JMenuItem instance
608 private JMenuItem initMenuItemWithTooltip (final String key) {
610 this.getLogger().trace(MessageFormat.format("key={0} - CALLED!", key)); //NOI18N
612 JMenuItem item = new JMenuItem(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.text", key))); //NOI18N
613 item.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.toolTipText", key))); //NOI18N
616 this.getLogger().trace(MessageFormat.format("item={0} - EXIT!", item)); //NOI18N
623 * Initializes the menu system
625 private void initMenuSystem () {
627 this.getLogger().trace("CALLED!"); //NOI18N
629 // Init menu bar, menu and item instances
630 JMenuBar menuBar = new JMenuBar();
636 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
639 // 1.x) Exit program (should be last)
640 this.addMenuItem(menu, "exitProgram", new ActionListener() { //NOI18N
642 * If the user has performed this action
644 * @param e An instance of an ActionEvent class
647 public void actionPerformed (final ActionEvent e) {
648 self.shutdownApplication();
652 // Add menu -> menu bar
656 // 2) Addressbook menu
657 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
660 this.addOwnItem = this.initMenuItemWithTooltip("addOwnData"); //NOI18N
662 // Add listener to exit menu
663 this.addOwnItem.addActionListener(new ActionListener() {
665 * If the user has performed this action
667 * @param e An instance of an ActionEvent class
670 public void actionPerformed (final ActionEvent e) {
672 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
673 manager.doEnterOwnData();
674 } catch (final ContactAlreadyAddedException ex) {
675 // Already added, log away
676 // @TODO maybe output message here?
677 self.logException(ex);
683 menu.add(this.addOwnItem);
685 // 2.2) Edit own data
686 this.editOwnItem = this.initMenuItemWithTooltip("editOwnData"); //NOI18N
688 // Add listener to exit menu
689 this.editOwnItem.addActionListener(new ActionListener() {
691 * If the user has performed this action
693 * @param e An instance of an ActionEvent class
696 public void actionPerformed (final ActionEvent e) {
697 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
698 manager.doChangeOwnData();
703 menu.add(this.editOwnItem);
706 // 1) Add new contact
707 this.addMenuItem(menu, "addNewContact", new ActionListener() { //NOI18N
709 * If the user has performed this action
711 * @param e An instance of an ActionEvent class
714 public void actionPerformed (final ActionEvent e) {
715 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
716 manager.doAddOtherAddress();
720 // Add menu -> menu bar
723 // Add menu bar -> frame
724 this.frame.add(menuBar, BorderLayout.NORTH);
727 this.getLogger().trace("EXIT!"); //NOI18N
731 * Initializes name panel
733 * @param dialog A JDialog instance to this components to
735 private void initNameDataPanel (final JDialog dialog) {
737 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
739 // Panel "name" input boxes
740 JPanel namePanel = new JPanel();
741 namePanel.setLayout(new GridLayout(0, 2, 3, 3));
743 // Set border to titled version
744 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name"))); //NOI18N
747 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
750 Gender[] genders = Gender.values();
752 // Init gender combo box with tool tip
753 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
754 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.toolTipText"));
756 // Add both to gender panel
757 namePanel.add(gLabel);
758 namePanel.add(gender);
760 // Add text field for surname
761 this.addTextFieldWithLabelToPanel(namePanel, "surname", 20); //NOI18N
763 // Add text field for family name
764 this.addTextFieldWithLabelToPanel(namePanel, "familyName", 20); //NOI18N
766 // Finally add panel to dialog
767 dialog.add(namePanel);
770 this.getLogger().trace("EXIT!"); //NOI18N
774 * Initializes "other" data panel
776 * @param dialog A JDialog instance to this components to
777 * @todo Fill this with life
779 private void initOtherDataPanel (final JDialog dialog) {
781 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
783 // Panel "other" input boxes
784 JPanel otherPanel = new JPanel();
785 otherPanel.setLayout(new GridLayout(0, 2, 3, 3));
786 otherPanel.setBorder(new TitledBorder(this.generateBorderTitle("other"))); //NOI18N
788 // Add text field for email address
789 this.addTextFieldWithLabelToPanel(otherPanel, "emailAddress", 20); //NOI18N
791 // Add text field for phone number
792 this.addTextFieldWithLabelToPanel(otherPanel, "phoneNumber", 20); //NOI18N
794 // Add text field for cellphone number
795 this.addTextFieldWithLabelToPanel(otherPanel, "cellphoneNumber", 20); //NOI18N
797 // Add text field for fax number
798 this.addTextFieldWithLabelToPanel(otherPanel, "faxNumber", 20); //NOI18N
801 JLabel commentLabel = new JLabel(this.getBundle().getString("AddressbookFrame.comment.text"));
803 // Init text area with tool tip
804 JTextArea comment = new JTextArea(5, 20);
805 comment.setToolTipText(this.getBundle().getString("AddressbookFrame.comment.toolTipText"));
808 otherPanel.add(commentLabel);
809 otherPanel.add(comment);
811 // Finally add panel to dialog
812 dialog.add(otherPanel);
815 this.getLogger().trace("EXIT!"); //NOI18N
819 * Initialize other dialogs (e.g. "Add contact")
821 private void initOtherDialogs () {
823 this.getLogger().trace("CALLED!"); //NOI18N
825 // Init other windows:
827 initAddContactDialog();
830 this.getLogger().trace("EXIT!"); //NOI18N
834 * Initializes status panel
836 private void initStatusPanel () {
838 this.getLogger().trace("CALLED!"); //NOI18N
840 // Init status label (which needs to be updated
841 this.statusLabel = new JLabel();
842 this.updateStatus("initializing"); //NOI18N
844 // Init status bar in south
845 JPanel panel = new JPanel();
846 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
847 panel.add(this.statusLabel);
848 panel.setBorder(BorderFactory.createEtchedBorder());
850 // Add panel to frame
851 this.frame.add(panel, BorderLayout.SOUTH);
854 this.getLogger().trace("EXIT!"); //NOI18N
858 * Initializes the table which will show all contacts
860 private void initTable () {
862 this.getLogger().trace("CALLED!"); //NOI18N
864 // Instance table model
865 this.dataModel = new ContactTableModel(this.getClient());
868 this.dataTable = new JTable(this.dataModel);
870 // Add mouse listener
871 this.dataTable.addMouseListener(new MouseAdapter() {
873 * If the user peformed a click on a cell
875 * @param e Mouse event instance
878 public void mouseClicked (final MouseEvent e) {
879 throw new UnsupportedOperationException("Unfinished."); //NOI18N
883 // Instance scroll pane
884 JScrollPane scroller = new JScrollPane();
886 // Add table to scroll pane
887 scroller.setViewportView(this.dataTable);
888 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
889 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
892 this.frame.add(scroller, BorderLayout.CENTER);
895 this.getLogger().trace("EXIT!"); //NOI18N
899 * Updates status to given type
901 * @param type Status type
903 private void updateStatus (final String type) {
905 this.getLogger().trace(MessageFormat.format("type={0} - CALLED!", type)); //NOI18N
907 // Set status message
908 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type))); //NOI18N
911 this.getLogger().trace("EXIT!"); //NOI18N
915 * Class for "add address" button
917 private static class AddActionListener extends BaseAddressbookSystem implements ActionListener {
921 private final JDialog dialog;
924 * Frame (not JFrame) instance
926 private final ClientFrame frame;
929 * Constructor for action listener
931 * @param dialog Dialog instance to call back
932 * @param frame Frame instance (not JFrame)
934 protected AddActionListener (final JDialog dialog, final ClientFrame frame) {
935 // Set dialog and frame here
936 this.dialog = dialog;
941 * If the action has been performed
943 * @param e The performed action event
946 public void actionPerformed (final ActionEvent e) {
947 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
952 * Class for "cancel address" button
954 private static class CancelActionListener extends BaseAddressbookSystem implements ActionListener {
958 private final JDialog dialog;
961 * Frame (not JFrame) instance
963 private final ClientFrame frame;
966 * Constructor for action listener
968 * @param dialog Dialog instance to call back
969 * @param frame Frame instance (not JFrame)
971 protected CancelActionListener (final JDialog dialog, final ClientFrame frame) {
972 // Set dialog and frame here
973 this.dialog = dialog;
978 * If the action has been performed
980 * @param e The performed action event
983 public void actionPerformed (final ActionEvent e) {
984 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.