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.io.IOException;
28 import java.sql.SQLException;
29 import java.text.MessageFormat;
30 import javax.swing.BorderFactory;
31 import javax.swing.BoxLayout;
32 import javax.swing.DefaultComboBoxModel;
33 import javax.swing.JButton;
34 import javax.swing.JComboBox;
35 import javax.swing.JDialog;
36 import javax.swing.JFormattedTextField;
37 import javax.swing.JFrame;
38 import javax.swing.JLabel;
39 import javax.swing.JMenu;
40 import javax.swing.JMenuBar;
41 import javax.swing.JMenuItem;
42 import javax.swing.JPanel;
43 import javax.swing.JScrollPane;
44 import javax.swing.JTable;
45 import javax.swing.JTextArea;
46 import javax.swing.JTextField;
47 import javax.swing.border.TitledBorder;
48 import javax.swing.table.TableModel;
49 import org.mxchange.addressbook.BaseAddressbookSystem;
50 import org.mxchange.addressbook.application.AddressbookApplication;
51 import org.mxchange.addressbook.exceptions.ContactAlreadyAddedException;
52 import org.mxchange.addressbook.manager.contact.ManageableAddressbookContact;
53 import org.mxchange.jcore.client.Client;
54 import org.mxchange.jcore.client.gui.ClientFrame;
55 import org.mxchange.jcore.contact.Contact;
56 import org.mxchange.jcore.contact.Gender;
57 import org.mxchange.jcore.exceptions.FrameAlreadyInitializedException;
58 import org.mxchange.jcore.model.swing.contact.ContactTableModel;
62 * @author Roland Haeder
64 public class AddressbookFrame extends BaseAddressbookSystem implements ClientFrame {
69 private static ClientFrame self;
72 * Singelton getter for this frame instance.
74 * @param client Client instance
75 * @return Returns a singelton instance of this frame
77 public static final ClientFrame getSelfInstance (final Client client) {
79 if (!(self instanceof ClientFrame)) {
80 // Create new instance
81 self = new AddressbookFrame(client);
89 * Dialog box "add contact"
91 private JDialog addContact;
94 * Frame instance for "add own data"
96 private JMenuItem addOwnItem;
99 * Instance to table model
101 private TableModel dataModel;
106 private JTable dataTable;
109 * Frame instance for "edit own data"
111 private JMenuItem editOwnItem;
116 private final JFrame frame;
119 * Whether this frame has been initialized
121 private boolean initialized;
124 * Status label needs to be updated
126 private JLabel statusLabel;
129 * Creates an instance of this frame with a client instance
133 private AddressbookFrame (final Client client) {
135 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
137 // Set frame instance
138 this.frame = new JFrame();
139 this.frame.setTitle(this.generateFrameTitle("main")); //NOI18N
142 this.setClient(client);
145 this.getLogger().trace("EXIT!"); //NOI18N
149 public Contact doEnterOwnData () {
151 this.getLogger().trace("CALLED!"); //NOI18N
153 // Is the "add contact" window visible?
154 if (this.addContact.isVisible()) {
155 // Something bad happened
156 throw new IllegalStateException("Window addContact is already visible."); //NOI18N
159 // Disable main window
160 this.frame.setEnabled(false);
162 // Make other window visible
163 this.addContact.setVisible(true);
166 this.getLogger().trace("Returning null : EXIT!"); //NOI18N
168 // Return value is not supported
173 * Shutdown this frame
176 public void doShutdown () {
178 this.getLogger().trace("CALLED!"); //NOI18N
180 // First only show shutdown status
181 this.updateStatus("shutdown"); //NOI18N
184 this.getLogger().trace("EXIT!"); //NOI18N
189 * Enables main window (frame)
192 public void enableMainWindow () {
194 this.getLogger().trace("CALLED!"); //NOI18N
197 this.frame.setEnabled(true);
199 // Request focus for this window
200 this.frame.requestFocus();
203 this.getLogger().trace("EXIT!"); //NOI18N
207 * Setups the frame, do not set isInitialized here
209 * @param client Client instance
212 public void setupFrame (final Client client) {
214 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
216 // Get and cast manager instance
217 ManageableAddressbookContact manager = (ManageableAddressbookContact) this.getClient().getManager();
219 // Has the user entered own data?
220 if (manager.isOwnContactAdded()) {
222 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false"); //NOI18N
224 // Not entered yet, so disable "add" menu
225 this.addOwnItem.setEnabled(false);
228 this.editOwnItem.setEnabled(false);
231 // Make the frame visible
232 this.frame.setVisible(true);
235 this.updateStatus("done"); //NOI18N
238 this.getLogger().trace("EXIT!"); //NOI18N
242 * Initalizes this frame. Having initComponents() exposed (publicly
243 * accessible) means that any other object can initialize components which
247 * org.mxchange.jcore.exceptions.FrameAlreadyInitializedException If
248 * this method has been called twice
251 public void init () throws FrameAlreadyInitializedException {
253 this.getLogger().trace("CALLED!"); //NOI18N
255 // Has this frame been initialized?
256 if (this.isInitialized()) {
258 throw new FrameAlreadyInitializedException();
262 this.initComponents();
265 this.initialized = true;
268 this.getLogger().trace("EXIT!"); //NOI18N
272 * Returns field isInitialized. This flag indicates whether this frame has
273 * been initialized or not.
275 * @return Field isInitialized
278 public final boolean isInitialized () {
279 return this.initialized;
283 * Shuts down the application.
286 public void shutdownApplication () {
288 this.getLogger().trace("CALLED!"); //NOI18N
290 // To do this, the frame must be initialized
291 if (!this.isInitialized()) {
292 // Not initalized, so bad call
293 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this."); //NOI18N
297 // Call shutdown method
299 this.getClient().getApplication().doShutdown();
300 } catch (final SQLException | IOException ex) {
302 this.abortProgramWithException(ex);
306 this.getLogger().trace("EXIT!"); //NOI18N
310 * Adds a new menu item with given key to menu instance
312 * @param menu Menu instance to add item to
313 * @param key Message key part
314 * @param listener Listener instance
316 private void addMenuItem (final JMenu menu, final String key, final ActionListener listener) {
318 this.getLogger().trace(MessageFormat.format("menu={0},key={1},listener={2} - CALLED!", menu, key, listener)); //NOI18N
321 JMenuItem item = this.initMenuItemWithTooltip(key);
324 item.addActionListener(listener);
330 this.getLogger().trace("EXIT!"); //NOI18N
334 * Adds a JTextField with label and tool tip to given panel
336 * @param panel Panel instance to add text field
337 * @param key Part of message key from resource bundle
338 * @param fieldLength Length of text field
340 private void addTextFieldWithLabelToPanel (final JPanel panel, final String key, final int fieldLength) {
342 this.getLogger().trace(MessageFormat.format("panel={0},key={1},fieldLength={2} - CALLED!", panel, key, fieldLength)); //NOI18N
344 // Init label for given key
345 JLabel label = new JLabel(this.getBundle().getString(String.format("AddressbookFrame.%s.text", key))); //NOI18N
347 // And input box wih tool tip
348 JTextField field = new JTextField(fieldLength);
349 field.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.%s.toolTipText", key))); //NOI18N
356 this.getLogger().trace("EXIT!"); //NOI18N
360 * Generates a title for borders
362 * @param key Key part to look for
363 * @return Human-readable title
365 private String generateBorderTitle (final String key) {
366 // Call bundle instance
367 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key)); //NOI18N
371 * Generates a title for all frames based on given sub title key. If null is
372 * given, the sub title is not generated.
374 * @param subKey Key for sub title resource
375 * @return A full application title
377 private String generateFrameTitle (final String subKey) {
379 String title = AddressbookApplication.printableTitle();
382 if (subKey != null) {
384 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey))); //NOI18N
392 * Initializes "add" and "cancel" buttons
394 private void initAddCancelButtons () {
396 this.getLogger().trace("CALLED!"); //NOI18N
399 JPanel panel = new JPanel();
400 panel.setLayout(new GridLayout(1, 2, 10, 10));
402 // Generate "add" button
403 JButton addButton = new JButton(this.getBundle().getString("AddressbookFrame.button.addAddress.text"));
406 addButton.addActionListener(new AddActionListener(this.addContact, this));
408 // Add button to panel
409 panel.add(addButton);
411 // Generate "cancel" button
412 JButton cancelButton = new JButton(this.getBundle().getString("AddressbookFrame.button.cancel.text"));
415 cancelButton.addActionListener(new CancelActionListener(this.addContact, this));
417 // Add button to panel
418 panel.add(cancelButton);
420 // Add panel to main panel
421 this.addContact.add(panel);
424 this.getLogger().trace("EXIT!"); //NOI18N
428 * Initializes "add contact" dialog
430 private void initAddContactDialog () {
432 this.getLogger().trace("CALLED!"); //NOI18N
434 // Instance dialog and set title
435 this.addContact = new JDialog();
436 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact")); //NOI18N
439 this.addContact.setLayout(new GridLayout(0, 1, 2, 2));
441 // Only hide it on close and make it appear in middle of screen
442 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
443 this.addContact.setLocationRelativeTo(this.frame);
445 // Set always on top and auto-focus
446 this.addContact.setAlwaysOnTop(true);
447 this.addContact.setAutoRequestFocus(true);
450 this.addContact.setSize(500, 500);
452 // And it is not resizeable
453 this.addContact.setResizable(false);
456 * Add listener which asks for confirmation, if data has been entered
457 * but not saved yet. The user may appriciate this ... ;-)
461 this.addContact.addWindowListener(new WindowAdapter() {
463 * Invoked when a window has been closed.
466 public void windowClosed (final WindowEvent e) {
467 // Enable main window again
468 AddressbookFrame.getSelfInstance(null).enableMainWindow();
472 * Invoked when a window is in the process of being closed. The
473 * close operation can be overridden at this point.
476 public void windowClosing (final WindowEvent e) {
477 e.getWindow().dispose();
483 initNameDataPanel(this.addContact);
485 // 2) "address" panel
486 initAddressDataPanel(this.addContact);
489 initOtherDataPanel(this.addContact);
491 // 4) "Add" and "Cancel" buttons, combined they are unique for this dialog
492 initAddCancelButtons();
494 // x)Only for developing:
495 /* DEBUG: */ this.addContact.setVisible(true);
498 this.getLogger().trace("EXIT!"); //NOI18N
502 * Initializes address panel
504 * @param dialog A JDialog instance to this components to
506 private void initAddressDataPanel (final JDialog dialog) {
508 this.getLogger().trace("CALLED!"); //NOI18N
510 // Panel "address" input boxes
511 JPanel addressPanel = new JPanel();
512 addressPanel.setLayout(new GridLayout(0, 4, 3, 3));
514 // Set border to titled version
515 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address"))); //NOI18N
517 // Add text field for street
518 this.addTextFieldWithLabelToPanel(addressPanel, "street", 20); //NOI18N
521 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
523 // And text field, but only accept numbers
524 JFormattedTextField number = new JFormattedTextField();
525 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.toolTipText"));
527 // Add both to street panel
528 addressPanel.add(numberLabel);
529 addressPanel.add(number);
531 // Label for ZIP code, again numbers only
532 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
534 // Init text field with label
535 JFormattedTextField zip = new JFormattedTextField();
536 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.toolTipText"));
538 // Add both to street panel
539 addressPanel.add(zipLabel);
540 addressPanel.add(zip);
542 // Add text field for city name
543 this.addTextFieldWithLabelToPanel(addressPanel, "city", 20); //NOI18N
545 // Add panel to dialog
546 dialog.add(addressPanel);
549 this.getLogger().trace("EXIT!"); //NOI18N
553 * Initialize components
555 private void initComponents () {
557 this.getLogger().trace("CALLED!"); //NOI18N
559 // Set default close operation
560 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
562 // Register shutdown listener
563 this.frame.addWindowListener(new WindowAdapter() {
565 * Invoked when a window has been closed.
568 public void windowClosed (final WindowEvent e) {
569 // Shutdown application cleanly
570 self.shutdownApplication();
574 * Invoked when a window is in the process of being closed. The
575 * close operation can be overridden at this point.
578 public void windowClosing (final WindowEvent e) {
579 // Also shutdown cleanly here
580 self.shutdownApplication();
584 // Setup layout manager
585 this.frame.setLayout(new BorderLayout(2, 2));
588 this.frame.setSize(700, 400);
590 // Center window in middle of screen, instead of top-left corner
591 this.frame.setLocationRelativeTo(null);
602 // Init other windows
606 this.getLogger().trace("EXIT!"); //NOI18N
610 * Initializes a menu item instance with tool tip
612 * @param key Message key part
613 * @return A finished JMenuItem instance
615 private JMenuItem initMenuItemWithTooltip (final String key) {
617 this.getLogger().trace(MessageFormat.format("key={0} - CALLED!", key)); //NOI18N
619 JMenuItem item = new JMenuItem(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.text", key))); //NOI18N
620 item.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.toolTipText", key))); //NOI18N
623 this.getLogger().trace(MessageFormat.format("item={0} - EXIT!", item)); //NOI18N
630 * Initializes the menu system
632 private void initMenuSystem () {
634 this.getLogger().trace("CALLED!"); //NOI18N
636 // Init menu bar, menu and item instances
637 JMenuBar menuBar = new JMenuBar();
643 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
646 // 1.x) Exit program (should be last)
647 this.addMenuItem(menu, "exitProgram", new ActionListener() { //NOI18N
649 * If the user has performed this action
651 * @param e An instance of an ActionEvent class
654 public void actionPerformed (final ActionEvent e) {
655 self.shutdownApplication();
659 // Add menu -> menu bar
663 // 2) Addressbook menu
664 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
667 this.addOwnItem = this.initMenuItemWithTooltip("addOwnData"); //NOI18N
669 // Add listener to exit menu
670 this.addOwnItem.addActionListener(new ActionListener() {
672 * If the user has performed this action
674 * @param e An instance of an ActionEvent class
677 public void actionPerformed (final ActionEvent e) {
679 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
680 manager.doEnterOwnData();
681 } catch (final ContactAlreadyAddedException ex) {
682 // Already added, log away
683 // @TODO maybe output message here?
684 self.logException(ex);
690 menu.add(this.addOwnItem);
692 // 2.2) Edit own data
693 this.editOwnItem = this.initMenuItemWithTooltip("editOwnData"); //NOI18N
695 // Add listener to exit menu
696 this.editOwnItem.addActionListener(new ActionListener() {
698 * If the user has performed this action
700 * @param e An instance of an ActionEvent class
703 public void actionPerformed (final ActionEvent e) {
704 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
705 manager.doChangeOwnData();
710 menu.add(this.editOwnItem);
713 // 1) Add new contact
714 this.addMenuItem(menu, "addNewContact", new ActionListener() { //NOI18N
716 * If the user has performed this action
718 * @param e An instance of an ActionEvent class
721 public void actionPerformed (final ActionEvent e) {
722 ManageableAddressbookContact manager = (ManageableAddressbookContact) self.getClient().getManager();
723 manager.doAddOtherAddress();
727 // Add menu -> menu bar
730 // Add menu bar -> frame
731 this.frame.add(menuBar, BorderLayout.NORTH);
734 this.getLogger().trace("EXIT!"); //NOI18N
738 * Initializes name panel
740 * @param dialog A JDialog instance to this components to
742 private void initNameDataPanel (final JDialog dialog) {
744 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
746 // Panel "name" input boxes
747 JPanel namePanel = new JPanel();
748 namePanel.setLayout(new GridLayout(0, 2, 3, 3));
750 // Set border to titled version
751 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name"))); //NOI18N
754 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
757 Gender[] genders = Gender.values();
759 // Init gender combo box with tool tip
760 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
761 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.toolTipText"));
763 // Add both to gender panel
764 namePanel.add(gLabel);
765 namePanel.add(gender);
767 // Add text field for surname
768 this.addTextFieldWithLabelToPanel(namePanel, "surname", 20); //NOI18N
770 // Add text field for family name
771 this.addTextFieldWithLabelToPanel(namePanel, "familyName", 20); //NOI18N
773 // Finally add panel to dialog
774 dialog.add(namePanel);
777 this.getLogger().trace("EXIT!"); //NOI18N
781 * Initializes "other" data panel
783 * @param dialog A JDialog instance to this components to
784 * @todo Fill this with life
786 private void initOtherDataPanel (final JDialog dialog) {
788 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
790 // Panel "other" input boxes
791 JPanel otherPanel = new JPanel();
792 otherPanel.setLayout(new GridLayout(0, 2, 3, 3));
793 otherPanel.setBorder(new TitledBorder(this.generateBorderTitle("other"))); //NOI18N
795 // Add text field for email address
796 this.addTextFieldWithLabelToPanel(otherPanel, "emailAddress", 20); //NOI18N
798 // Add text field for phone number
799 this.addTextFieldWithLabelToPanel(otherPanel, "phoneNumber", 20); //NOI18N
801 // Add text field for cellphone number
802 this.addTextFieldWithLabelToPanel(otherPanel, "cellphoneNumber", 20); //NOI18N
804 // Add text field for fax number
805 this.addTextFieldWithLabelToPanel(otherPanel, "faxNumber", 20); //NOI18N
808 JLabel commentLabel = new JLabel(this.getBundle().getString("AddressbookFrame.comment.text"));
810 // Init text area with tool tip
811 JTextArea comment = new JTextArea(5, 20);
812 comment.setToolTipText(this.getBundle().getString("AddressbookFrame.comment.toolTipText"));
815 otherPanel.add(commentLabel);
816 otherPanel.add(comment);
818 // Finally add panel to dialog
819 dialog.add(otherPanel);
822 this.getLogger().trace("EXIT!"); //NOI18N
826 * Initialize other dialogs (e.g. "Add contact")
828 private void initOtherDialogs () {
830 this.getLogger().trace("CALLED!"); //NOI18N
832 // Init other windows:
834 initAddContactDialog();
837 this.getLogger().trace("EXIT!"); //NOI18N
841 * Initializes status panel
843 private void initStatusPanel () {
845 this.getLogger().trace("CALLED!"); //NOI18N
847 // Init status label (which needs to be updated
848 this.statusLabel = new JLabel();
849 this.updateStatus("initializing"); //NOI18N
851 // Init status bar in south
852 JPanel panel = new JPanel();
853 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
854 panel.add(this.statusLabel);
855 panel.setBorder(BorderFactory.createEtchedBorder());
857 // Add panel to frame
858 this.frame.add(panel, BorderLayout.SOUTH);
861 this.getLogger().trace("EXIT!"); //NOI18N
865 * Initializes the table which will show all contacts
867 private void initTable () {
869 this.getLogger().trace("CALLED!"); //NOI18N
871 // Instance table model
872 this.dataModel = new ContactTableModel(this.getClient());
875 this.dataTable = new JTable(this.dataModel);
877 // Add mouse listener
878 this.dataTable.addMouseListener(new MouseAdapter() {
880 * If the user peformed a click on a cell
882 * @param e Mouse event instance
885 public void mouseClicked (final MouseEvent e) {
886 throw new UnsupportedOperationException("Unfinished."); //NOI18N
890 // Instance scroll pane
891 JScrollPane scroller = new JScrollPane();
893 // Add table to scroll pane
894 scroller.setViewportView(this.dataTable);
895 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
896 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
899 this.frame.add(scroller, BorderLayout.CENTER);
902 this.getLogger().trace("EXIT!"); //NOI18N
906 * Updates status to given type
908 * @param type Status type
910 private void updateStatus (final String type) {
912 this.getLogger().trace(MessageFormat.format("type={0} - CALLED!", type)); //NOI18N
914 // Set status message
915 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type))); //NOI18N
918 this.getLogger().trace("EXIT!"); //NOI18N
922 * Class for "add address" button
924 private static class AddActionListener extends BaseAddressbookSystem implements ActionListener {
928 private final JDialog dialog;
931 * Frame (not JFrame) instance
933 private final ClientFrame frame;
936 * Constructor for action listener
938 * @param dialog Dialog instance to call back
939 * @param frame Frame instance (not JFrame)
941 protected AddActionListener (final JDialog dialog, final ClientFrame frame) {
942 // Set dialog and frame here
943 this.dialog = dialog;
948 * If the action has been performed
950 * @param e The performed action event
953 public void actionPerformed (final ActionEvent e) {
954 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
959 * Class for "cancel address" button
961 private static class CancelActionListener extends BaseAddressbookSystem implements ActionListener {
965 private final JDialog dialog;
968 * Frame (not JFrame) instance
970 private final ClientFrame frame;
973 * Constructor for action listener
975 * @param dialog Dialog instance to call back
976 * @param frame Frame instance (not JFrame)
978 protected CancelActionListener (final JDialog dialog, final ClientFrame frame) {
979 // Set dialog and frame here
980 this.dialog = dialog;
985 * If the action has been performed
987 * @param e The performed action event
990 public void actionPerformed (final ActionEvent e) {
991 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.