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.BaseFrameworkSystem;
48 import org.mxchange.addressbook.application.AddressbookApplication;
49 import org.mxchange.addressbook.client.Client;
50 import org.mxchange.addressbook.contact.Contact;
51 import org.mxchange.addressbook.contact.Gender;
52 import org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException;
53 import org.mxchange.addressbook.model.contact.ContactTableModel;
57 * @author Roland Haeder
59 public class AddressbookFrame extends BaseFrameworkSystem implements ClientFrame {
64 private static ClientFrame self;
67 * Singelton getter for this frame instance.
69 * @param client Client instance
70 * @return Returns a singelton instance of this frame
72 public static final ClientFrame getSelfInstance (final Client client) {
74 if (!(self instanceof ClientFrame)) {
75 // Create new instance
76 self = new AddressbookFrame(client);
84 * Dialog box "add contact"
86 private JDialog addContact;
89 * Frame instance for "add own data"
91 private JMenuItem addOwnItem;
94 * Instance to table model
96 private TableModel dataModel;
101 private JTable dataTable;
104 * Frame instance for "edit own data"
106 private JMenuItem editOwnItem;
111 private final JFrame frame;
114 * Whether this frame has been initialized
116 private boolean initialized;
119 * Status label needs to be updated
121 private JLabel statusLabel;
124 * Creates an instance of this frame with a client instance
128 private AddressbookFrame (final Client client) {
130 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
132 // Set frame instance
133 this.frame = new JFrame();
134 this.frame.setTitle(this.generateFrameTitle("main")); //NOI18N
137 this.setClient(client);
140 this.getLogger().trace("EXIT!"); //NOI18N
144 public Contact doEnterOwnData () {
146 this.getLogger().trace("CALLED!"); //NOI18N
148 // Is the "add contact" window visible?
149 if (this.addContact.isVisible()) {
150 // Something bad happened
151 throw new IllegalStateException("Window addContact is already visible."); //NOI18N
154 // Disable main window
155 this.frame.setEnabled(false);
157 // Make other window visible
158 this.addContact.setVisible(true);
161 this.getLogger().trace("Returning null : EXIT!"); //NOI18N
163 // Return value is not supported
168 * Shutdown this frame
171 public void doShutdown () {
173 this.getLogger().trace("CALLED!"); //NOI18N
175 // First only show shutdown status
176 this.updateStatus("shutdown"); //NOI18N
179 this.getLogger().trace("EXIT!"); //NOI18N
184 * Enables main window (frame)
187 public void enableMainWindow () {
189 this.getLogger().trace("CALLED!"); //NOI18N
192 this.frame.setEnabled(true);
194 // Request focus for this window
195 this.frame.requestFocus();
198 this.getLogger().trace("EXIT!"); //NOI18N
202 * Setups the frame, do not set isInitialized here
204 * @param client Client instance
207 public void setupFrame (final Client client) {
209 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
211 // Has the user entered own data?
212 if (this.getClient().getContactManager().isOwnContactAdded()) {
214 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false"); //NOI18N
216 // Not entered yet, so disable "add" menu
217 this.addOwnItem.setEnabled(false);
220 this.editOwnItem.setEnabled(false);
223 // Make the frame visible
224 this.frame.setVisible(true);
227 this.updateStatus("done"); //NOI18N
230 this.getLogger().trace("EXIT!"); //NOI18N
234 * Initalizes this frame. Having initComponents() exposed (publicly
235 * accessible) means that any other object can initialize components which
239 * org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException If
240 * this method has been called twice
243 public void init () throws FrameAlreadyInitializedException {
245 this.getLogger().trace("CALLED!"); //NOI18N
247 // Has this frame been initialized?
248 if (this.isInitialized()) {
250 throw new FrameAlreadyInitializedException();
254 this.initComponents();
257 this.initialized = true;
260 this.getLogger().trace("EXIT!"); //NOI18N
264 * Returns field isInitialized. This flag indicates whether this frame has
265 * been initialized or not.
267 * @return Field isInitialized
270 public final boolean isInitialized () {
271 return this.initialized;
275 * Shuts down the application.
278 public void shutdownApplication () {
280 this.getLogger().trace("CALLED!"); //NOI18N
282 // To do this, the frame must be initialized
283 if (!this.isInitialized()) {
284 // Not initalized, so bad call
285 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this."); //NOI18N
289 // Call shutdown method
290 this.getClient().getApplication().doShutdown();
293 this.getLogger().trace("EXIT!"); //NOI18N
297 * Adds a JTextField with label and tool tip to given panel
299 * @param panel Panel instance to add text field
300 * @param key Part of message key from resource bundle
301 * @param fieldLength Length of text field
303 private void addTextFieldWithLabelToPanel (final JPanel panel, final String key, final int fieldLength) {
305 this.getLogger().trace(MessageFormat.format("panel={0},key={1},fieldLength={2} - CALLED!", panel, key, fieldLength)); //NOI18N
307 // Init label for given key
308 JLabel label = new JLabel(this.getBundle().getString(String.format("AddressbookFrame.%s.text", key))); //NOI18N
310 // And input box wih tool tip
311 JTextField field = new JTextField(fieldLength);
312 field.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.%s.toolTipText", key))); //NOI18N
319 this.getLogger().trace("EXIT!"); //NOI18N
323 * Generates a title for borders
325 * @param key Key part to look for
326 * @return Human-readable title
328 private String generateBorderTitle (final String key) {
329 // Call bundle instance
330 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key)); //NOI18N
334 * Generates a title for all frames based on given sub title key. If null is
335 * given, the sub title is not generated.
337 * @param subKey Key for sub title resource
338 * @return A full application title
340 private String generateFrameTitle (final String subKey) {
342 String title = AddressbookApplication.printableTitle();
345 if (subKey != null) {
347 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey))); //NOI18N
355 * Initializes "add" and "cancel" buttons
357 private void initAddCancelButtons () {
359 this.getLogger().trace("CALLED!"); //NOI18N
362 JPanel panel = new JPanel();
363 panel.setLayout(new GridLayout(1, 2, 10, 10));
365 // Generate "add" button
366 JButton addButton = new JButton(this.getBundle().getString("AddressbookFrame.button.addAddress.text"));
369 addButton.addActionListener(new AddActionListener(this.addContact, this));
371 // Add button to panel
372 panel.add(addButton);
374 // Generate "cancel" button
375 JButton cancelButton = new JButton(this.getBundle().getString("AddressbookFrame.button.cancel.text"));
378 cancelButton.addActionListener(new CancelActionListener(this.addContact, this));
380 // Add button to panel
381 panel.add(cancelButton);
383 // Add panel to main panel
384 this.addContact.add(panel);
387 this.getLogger().trace("EXIT!"); //NOI18N
391 * Initializes "add contact" dialog
393 private void initAddContactDialog () {
395 this.getLogger().trace("CALLED!"); //NOI18N
397 // Instance dialog and set title
398 this.addContact = new JDialog();
399 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact")); //NOI18N
402 this.addContact.setLayout(new GridLayout(0, 1, 2, 2));
404 // Only hide it on close and make it appear in middle of screen
405 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
406 this.addContact.setLocationRelativeTo(null);
408 // Set always on top and auto-focus
409 this.addContact.setAlwaysOnTop(true);
410 this.addContact.setAutoRequestFocus(true);
413 this.addContact.setSize(500, 500);
415 // And it is not resizeable
416 this.addContact.setResizable(false);
419 * Add listener which asks for confirmation, if data has been entered
420 * but not saved yet. The user may appriciate this ... ;-)
424 this.addContact.addWindowListener(new WindowAdapter() {
426 * Invoked when a window has been closed.
429 public void windowClosed (final WindowEvent e) {
430 // Enable main window again
431 AddressbookFrame.getSelfInstance(null).enableMainWindow();
435 * Invoked when a window is in the process of being closed. The
436 * close operation can be overridden at this point.
439 public void windowClosing (final WindowEvent e) {
440 e.getWindow().dispose();
446 initNameDataPanel(this.addContact);
448 // 2) "address" panel
449 initAddressDataPanel(this.addContact);
452 initOtherDataPanel(this.addContact);
454 // 4) "Add" and "Cancel" buttons, combined they are unique for this dialog
455 initAddCancelButtons();
457 // x)Only for developing:
458 /* DEBUG: */ this.addContact.setVisible(true);
461 this.getLogger().trace("EXIT!"); //NOI18N
465 * Initializes address panel
467 * @param dialog A JDialog instance to this components to
469 private void initAddressDataPanel (final JDialog dialog) {
471 this.getLogger().trace("CALLED!"); //NOI18N
473 // Panel "address" input boxes
474 JPanel addressPanel = new JPanel();
475 addressPanel.setLayout(new GridLayout(0, 4, 3, 3));
477 // Set border to titled version
478 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address"))); //NOI18N
480 // Add text field for street
481 this.addTextFieldWithLabelToPanel(addressPanel, "street", 20); //NOI18N
484 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
486 // And text field, but only accept numbers
487 JFormattedTextField number = new JFormattedTextField();
488 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.toolTipText"));
490 // Add both to street panel
491 addressPanel.add(numberLabel);
492 addressPanel.add(number);
494 // Label for ZIP code, again numbers only
495 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
497 // Init text field with label
498 JFormattedTextField zip = new JFormattedTextField();
499 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.toolTipText"));
501 // Add both to street panel
502 addressPanel.add(zipLabel);
503 addressPanel.add(zip);
505 // Add text field for city name
506 this.addTextFieldWithLabelToPanel(addressPanel, "city", 20); //NOI18N
508 // Add panel to dialog
509 dialog.add(addressPanel);
512 this.getLogger().trace("EXIT!"); //NOI18N
516 * Initialize components
518 private void initComponents () {
520 this.getLogger().trace("CALLED!"); //NOI18N
522 // Set default close operation
523 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
525 // Register shutdown listener
526 this.frame.addWindowListener(new WindowAdapter() {
528 * Invoked when a window has been closed.
531 public void windowClosed (final WindowEvent e) {
532 // Shutdown application cleanly
533 self.shutdownApplication();
537 * Invoked when a window is in the process of being closed. The
538 * close operation can be overridden at this point.
541 public void windowClosing (final WindowEvent e) {
542 // Also shutdown cleanly here
543 self.shutdownApplication();
547 // Setup layout manager
548 this.frame.setLayout(new BorderLayout(2, 2));
551 this.frame.setSize(700, 400);
553 // Center window in middle of screen, instead of top-left corner
554 this.frame.setLocationRelativeTo(null);
565 // Init other windows
569 this.getLogger().trace("EXIT!"); //NOI18N
573 * Initializes a menu item instance with tool tip
575 * @param key Message key part
576 * @return A finished JMenuItem instance
578 private JMenuItem initMenuItemWithTooltip (final String key) {
580 this.getLogger().trace(MessageFormat.format("key={0} - CALLED!", key)); //NOI18N
582 JMenuItem item = new JMenuItem(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.text", key))); //NOI18N
583 item.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.toolTipText", key))); //NOI18N
586 this.getLogger().trace(MessageFormat.format("item={0} - EXIT!", item)); //NOI18N
593 * Initializes the menu system
595 private void initMenuSystem () {
597 this.getLogger().trace("CALLED!"); //NOI18N
599 // Init menu bar, menu and item instances
600 JMenuBar menuBar = new JMenuBar();
606 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
609 // 1.x) Exit program (should be last)
610 this.addMenuItem(menu, "exitProgram", new ActionListener() { //NOI18N
612 * If the user has performed this action
614 * @param e An instance of an ActionEvent class
617 public void actionPerformed (final ActionEvent e) {
618 self.shutdownApplication();
622 // Add menu -> menu bar
626 // 2) Addressbook menu
627 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
630 this.addOwnItem = this.initMenuItemWithTooltip("addOwnData"); //NOI18N
632 // Add listener to exit menu
633 this.addOwnItem.addActionListener(new ActionListener() {
635 * If the user has performed this action
637 * @param e An instance of an ActionEvent class
640 public void actionPerformed (final ActionEvent e) {
641 self.getClient().getContactManager().doEnterOwnData();
646 menu.add(this.addOwnItem);
648 // 2.2) Edit own data
649 this.editOwnItem = this.initMenuItemWithTooltip("editOwnData"); //NOI18N
651 // Add listener to exit menu
652 this.editOwnItem.addActionListener(new ActionListener() {
654 * If the user has performed this action
656 * @param e An instance of an ActionEvent class
659 public void actionPerformed (final ActionEvent e) {
660 self.getClient().getContactManager().doChangeOwnData();
665 menu.add(this.editOwnItem);
668 // 1) Add new contact
669 this.addMenuItem(menu, "addNewContact", new ActionListener() { //NOI18N
671 * If the user has performed this action
673 * @param e An instance of an ActionEvent class
676 public void actionPerformed (final ActionEvent e) {
677 self.getClient().getContactManager().doAddOtherAddress();
681 // Add menu -> menu bar
684 // Add menu bar -> frame
685 this.frame.add(menuBar, BorderLayout.NORTH);
688 this.getLogger().trace("EXIT!"); //NOI18N
692 * Adds a new menu item with given key to menu instance
694 * @param menu Menu instance to add item to
695 * @param key Message key part
696 * @param listener Listener instance
698 private void addMenuItem (final JMenu menu, final String key, final ActionListener listener) {
700 this.getLogger().trace(MessageFormat.format("menu={0},key={1},listener={2} - CALLED!", menu, key, listener)); //NOI18N
703 JMenuItem item = this.initMenuItemWithTooltip(key);
706 item.addActionListener(listener);
712 this.getLogger().trace("EXIT!"); //NOI18N
716 * Initializes name panel
718 * @param dialog A JDialog instance to this components to
720 private void initNameDataPanel (final JDialog dialog) {
722 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
724 // Panel "name" input boxes
725 JPanel namePanel = new JPanel();
726 namePanel.setLayout(new GridLayout(0, 2, 3, 3));
728 // Set border to titled version
729 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name"))); //NOI18N
732 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
735 Gender[] genders = Gender.values();
737 // Init gender combo box with tool tip
738 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
739 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.toolTipText"));
741 // Add both to gender panel
742 namePanel.add(gLabel);
743 namePanel.add(gender);
745 // Add text field for surname
746 this.addTextFieldWithLabelToPanel(namePanel, "surname", 20); //NOI18N
748 // Add text field for family name
749 this.addTextFieldWithLabelToPanel(namePanel, "familyName", 20); //NOI18N
751 // Finally add panel to dialog
752 dialog.add(namePanel);
755 this.getLogger().trace("EXIT!"); //NOI18N
759 * Initializes "other" data panel
761 * @param dialog A JDialog instance to this components to
762 * @todo Fill this with life
764 private void initOtherDataPanel (final JDialog dialog) {
766 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
768 // Panel "other" input boxes
769 JPanel otherPanel = new JPanel();
770 otherPanel.setLayout(new GridLayout(0, 2, 3, 3));
771 otherPanel.setBorder(new TitledBorder(this.generateBorderTitle("other"))); //NOI18N
773 // Add text field for email address
774 this.addTextFieldWithLabelToPanel(otherPanel, "emailAddress", 20); //NOI18N
776 // Add text field for phone number
777 this.addTextFieldWithLabelToPanel(otherPanel, "phoneNumber", 20); //NOI18N
779 // Add text field for cellphone number
780 this.addTextFieldWithLabelToPanel(otherPanel, "cellphoneNumber", 20); //NOI18N
782 // Add text field for fax number
783 this.addTextFieldWithLabelToPanel(otherPanel, "faxNumber", 20); //NOI18N
786 JLabel commentLabel = new JLabel(this.getBundle().getString("AddressbookFrame.comment.text"));
788 // Init text area with tool tip
789 JTextArea comment = new JTextArea(5, 20);
790 comment.setToolTipText(this.getBundle().getString("AddressbookFrame.comment.toolTipText"));
793 otherPanel.add(commentLabel);
794 otherPanel.add(comment);
796 // Finally add panel to dialog
797 dialog.add(otherPanel);
800 this.getLogger().trace("EXIT!"); //NOI18N
804 * Initialize other dialogs (e.g. "Add contact")
806 private void initOtherDialogs () {
808 this.getLogger().trace("CALLED!"); //NOI18N
810 // Init other windows:
812 initAddContactDialog();
815 this.getLogger().trace("EXIT!"); //NOI18N
819 * Initializes status panel
821 private void initStatusPanel () {
823 this.getLogger().trace("CALLED!"); //NOI18N
825 // Init status label (which needs to be updated
826 this.statusLabel = new JLabel();
827 this.updateStatus("initializing"); //NOI18N
829 // Init status bar in south
830 JPanel panel = new JPanel();
831 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
832 panel.add(this.statusLabel);
833 panel.setBorder(BorderFactory.createEtchedBorder());
835 // Add panel to frame
836 this.frame.add(panel, BorderLayout.SOUTH);
839 this.getLogger().trace("EXIT!"); //NOI18N
843 * Initializes the table which will show all contacts
845 private void initTable () {
847 this.getLogger().trace("CALLED!"); //NOI18N
849 // Instance table model
850 this.dataModel = new ContactTableModel(this.getClient());
853 this.dataTable = new JTable(this.dataModel);
855 // Add mouse listener
856 this.dataTable.addMouseListener(new MouseAdapter() {
858 * If the user peformed a click on a cell
860 * @param e Mouse event instance
863 public void mouseClicked (final MouseEvent e) {
864 throw new UnsupportedOperationException("Unfinished."); //NOI18N
868 // Instance scroll pane
869 JScrollPane scroller = new JScrollPane();
871 // Add table to scroll pane
872 scroller.setViewportView(this.dataTable);
873 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
874 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
877 this.frame.add(scroller, BorderLayout.CENTER);
880 this.getLogger().trace("EXIT!"); //NOI18N
884 * Updates status to given type
886 * @param type Status type
888 private void updateStatus (final String type) {
890 this.getLogger().trace(MessageFormat.format("type={0} - CALLED!", type)); //NOI18N
892 // Set status message
893 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type))); //NOI18N
896 this.getLogger().trace("EXIT!"); //NOI18N
900 * Class for "add address" button
902 private static class AddActionListener extends BaseFrameworkSystem implements ActionListener {
906 private final JDialog dialog;
909 * Frame (not JFrame) instance
911 private final ClientFrame frame;
914 * Constructor for action listener
916 * @param dialog Dialog instance to call back
917 * @param frame Frame instance (not JFrame)
919 protected AddActionListener (final JDialog dialog, final ClientFrame frame) {
920 // Set dialog and frame here
921 this.dialog = dialog;
926 * If the action has been performed
928 * @param e The performed action event
931 public void actionPerformed (final ActionEvent e) {
932 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
937 * Class for "cancel address" button
939 private static class CancelActionListener extends BaseFrameworkSystem implements ActionListener {
943 private final JDialog dialog;
946 * Frame (not JFrame) instance
948 private final ClientFrame frame;
951 * Constructor for action listener
953 * @param dialog Dialog instance to call back
954 * @param frame Frame instance (not JFrame)
956 protected CancelActionListener (final JDialog dialog, final ClientFrame frame) {
957 // Set dialog and frame here
958 this.dialog = dialog;
963 * If the action has been performed
965 * @param e The performed action event
968 public void actionPerformed (final ActionEvent e) {
969 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.