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.ContactAlreadyAddedException;
53 import org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException;
54 import org.mxchange.addressbook.model.contact.ContactTableModel;
58 * @author Roland Haeder
60 public class AddressbookFrame extends BaseFrameworkSystem implements ClientFrame {
65 private static ClientFrame self;
68 * Singelton getter for this frame instance.
70 * @param client Client instance
71 * @return Returns a singelton instance of this frame
73 public static final ClientFrame getSelfInstance (final Client client) {
75 if (!(self instanceof ClientFrame)) {
76 // Create new instance
77 self = new AddressbookFrame(client);
85 * Dialog box "add contact"
87 private JDialog addContact;
90 * Frame instance for "add own data"
92 private JMenuItem addOwnItem;
95 * Instance to table model
97 private TableModel dataModel;
102 private JTable dataTable;
105 * Frame instance for "edit own data"
107 private JMenuItem editOwnItem;
112 private final JFrame frame;
115 * Whether this frame has been initialized
117 private boolean initialized;
120 * Status label needs to be updated
122 private JLabel statusLabel;
125 * Creates an instance of this frame with a client instance
129 private AddressbookFrame (final Client client) {
131 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
133 // Set frame instance
134 this.frame = new JFrame();
135 this.frame.setTitle(this.generateFrameTitle("main")); //NOI18N
138 this.setClient(client);
141 this.getLogger().trace("EXIT!"); //NOI18N
145 public Contact doEnterOwnData () {
147 this.getLogger().trace("CALLED!"); //NOI18N
149 // Is the "add contact" window visible?
150 if (this.addContact.isVisible()) {
151 // Something bad happened
152 throw new IllegalStateException("Window addContact is already visible."); //NOI18N
155 // Disable main window
156 this.frame.setEnabled(false);
158 // Make other window visible
159 this.addContact.setVisible(true);
162 this.getLogger().trace("Returning null : EXIT!"); //NOI18N
164 // Return value is not supported
169 * Shutdown this frame
172 public void doShutdown () {
174 this.getLogger().trace("CALLED!"); //NOI18N
176 // First only show shutdown status
177 this.updateStatus("shutdown"); //NOI18N
180 this.getLogger().trace("EXIT!"); //NOI18N
185 * Enables main window (frame)
188 public void enableMainWindow () {
190 this.getLogger().trace("CALLED!"); //NOI18N
193 this.frame.setEnabled(true);
195 // Request focus for this window
196 this.frame.requestFocus();
199 this.getLogger().trace("EXIT!"); //NOI18N
203 * Setups the frame, do not set isInitialized here
205 * @param client Client instance
208 public void setupFrame (final Client client) {
210 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
212 // Has the user entered own data?
213 if (this.getClient().getContactManager().isOwnContactAdded()) {
215 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false"); //NOI18N
217 // Not entered yet, so disable "add" menu
218 this.addOwnItem.setEnabled(false);
221 this.editOwnItem.setEnabled(false);
224 // Make the frame visible
225 this.frame.setVisible(true);
228 this.updateStatus("done"); //NOI18N
231 this.getLogger().trace("EXIT!"); //NOI18N
235 * Initalizes this frame. Having initComponents() exposed (publicly
236 * accessible) means that any other object can initialize components which
240 * org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException If
241 * this method has been called twice
244 public void init () throws FrameAlreadyInitializedException {
246 this.getLogger().trace("CALLED!"); //NOI18N
248 // Has this frame been initialized?
249 if (this.isInitialized()) {
251 throw new FrameAlreadyInitializedException();
255 this.initComponents();
258 this.initialized = true;
261 this.getLogger().trace("EXIT!"); //NOI18N
265 * Returns field isInitialized. This flag indicates whether this frame has
266 * been initialized or not.
268 * @return Field isInitialized
271 public final boolean isInitialized () {
272 return this.initialized;
276 * Shuts down the application.
279 public void shutdownApplication () {
281 this.getLogger().trace("CALLED!"); //NOI18N
283 // To do this, the frame must be initialized
284 if (!this.isInitialized()) {
285 // Not initalized, so bad call
286 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this."); //NOI18N
290 // Call shutdown method
291 this.getClient().getApplication().doShutdown();
294 this.getLogger().trace("EXIT!"); //NOI18N
298 * Adds a JTextField with label and tool tip to given panel
300 * @param panel Panel instance to add text field
301 * @param key Part of message key from resource bundle
302 * @param fieldLength Length of text field
304 private void addTextFieldWithLabelToPanel (final JPanel panel, final String key, final int fieldLength) {
306 this.getLogger().trace(MessageFormat.format("panel={0},key={1},fieldLength={2} - CALLED!", panel, key, fieldLength)); //NOI18N
308 // Init label for given key
309 JLabel label = new JLabel(this.getBundle().getString(String.format("AddressbookFrame.%s.text", key))); //NOI18N
311 // And input box wih tool tip
312 JTextField field = new JTextField(fieldLength);
313 field.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.%s.toolTipText", key))); //NOI18N
320 this.getLogger().trace("EXIT!"); //NOI18N
324 * Generates a title for borders
326 * @param key Key part to look for
327 * @return Human-readable title
329 private String generateBorderTitle (final String key) {
330 // Call bundle instance
331 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key)); //NOI18N
335 * Generates a title for all frames based on given sub title key. If null is
336 * given, the sub title is not generated.
338 * @param subKey Key for sub title resource
339 * @return A full application title
341 private String generateFrameTitle (final String subKey) {
343 String title = AddressbookApplication.printableTitle();
346 if (subKey != null) {
348 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey))); //NOI18N
356 * Initializes "add" and "cancel" buttons
358 private void initAddCancelButtons () {
360 this.getLogger().trace("CALLED!"); //NOI18N
363 JPanel panel = new JPanel();
364 panel.setLayout(new GridLayout(1, 2, 10, 10));
366 // Generate "add" button
367 JButton addButton = new JButton(this.getBundle().getString("AddressbookFrame.button.addAddress.text"));
370 addButton.addActionListener(new AddActionListener(this.addContact, this));
372 // Add button to panel
373 panel.add(addButton);
375 // Generate "cancel" button
376 JButton cancelButton = new JButton(this.getBundle().getString("AddressbookFrame.button.cancel.text"));
379 cancelButton.addActionListener(new CancelActionListener(this.addContact, this));
381 // Add button to panel
382 panel.add(cancelButton);
384 // Add panel to main panel
385 this.addContact.add(panel);
388 this.getLogger().trace("EXIT!"); //NOI18N
392 * Initializes "add contact" dialog
394 private void initAddContactDialog () {
396 this.getLogger().trace("CALLED!"); //NOI18N
398 // Instance dialog and set title
399 this.addContact = new JDialog();
400 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact")); //NOI18N
403 this.addContact.setLayout(new GridLayout(0, 1, 2, 2));
405 // Only hide it on close and make it appear in middle of screen
406 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
407 this.addContact.setLocationRelativeTo(null);
409 // Set always on top and auto-focus
410 this.addContact.setAlwaysOnTop(true);
411 this.addContact.setAutoRequestFocus(true);
414 this.addContact.setSize(500, 500);
416 // And it is not resizeable
417 this.addContact.setResizable(false);
420 * Add listener which asks for confirmation, if data has been entered
421 * but not saved yet. The user may appriciate this ... ;-)
425 this.addContact.addWindowListener(new WindowAdapter() {
427 * Invoked when a window has been closed.
430 public void windowClosed (final WindowEvent e) {
431 // Enable main window again
432 AddressbookFrame.getSelfInstance(null).enableMainWindow();
436 * Invoked when a window is in the process of being closed. The
437 * close operation can be overridden at this point.
440 public void windowClosing (final WindowEvent e) {
441 e.getWindow().dispose();
447 initNameDataPanel(this.addContact);
449 // 2) "address" panel
450 initAddressDataPanel(this.addContact);
453 initOtherDataPanel(this.addContact);
455 // 4) "Add" and "Cancel" buttons, combined they are unique for this dialog
456 initAddCancelButtons();
458 // x)Only for developing:
459 /* DEBUG: */ this.addContact.setVisible(true);
462 this.getLogger().trace("EXIT!"); //NOI18N
466 * Initializes address panel
468 * @param dialog A JDialog instance to this components to
470 private void initAddressDataPanel (final JDialog dialog) {
472 this.getLogger().trace("CALLED!"); //NOI18N
474 // Panel "address" input boxes
475 JPanel addressPanel = new JPanel();
476 addressPanel.setLayout(new GridLayout(0, 4, 3, 3));
478 // Set border to titled version
479 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address"))); //NOI18N
481 // Add text field for street
482 this.addTextFieldWithLabelToPanel(addressPanel, "street", 20); //NOI18N
485 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
487 // And text field, but only accept numbers
488 JFormattedTextField number = new JFormattedTextField();
489 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.toolTipText"));
491 // Add both to street panel
492 addressPanel.add(numberLabel);
493 addressPanel.add(number);
495 // Label for ZIP code, again numbers only
496 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
498 // Init text field with label
499 JFormattedTextField zip = new JFormattedTextField();
500 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.toolTipText"));
502 // Add both to street panel
503 addressPanel.add(zipLabel);
504 addressPanel.add(zip);
506 // Add text field for city name
507 this.addTextFieldWithLabelToPanel(addressPanel, "city", 20); //NOI18N
509 // Add panel to dialog
510 dialog.add(addressPanel);
513 this.getLogger().trace("EXIT!"); //NOI18N
517 * Initialize components
519 private void initComponents () {
521 this.getLogger().trace("CALLED!"); //NOI18N
523 // Set default close operation
524 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
526 // Register shutdown listener
527 this.frame.addWindowListener(new WindowAdapter() {
529 * Invoked when a window has been closed.
532 public void windowClosed (final WindowEvent e) {
533 // Shutdown application cleanly
534 self.shutdownApplication();
538 * Invoked when a window is in the process of being closed. The
539 * close operation can be overridden at this point.
542 public void windowClosing (final WindowEvent e) {
543 // Also shutdown cleanly here
544 self.shutdownApplication();
548 // Setup layout manager
549 this.frame.setLayout(new BorderLayout(2, 2));
552 this.frame.setSize(700, 400);
554 // Center window in middle of screen, instead of top-left corner
555 this.frame.setLocationRelativeTo(null);
566 // Init other windows
570 this.getLogger().trace("EXIT!"); //NOI18N
574 * Initializes a menu item instance with tool tip
576 * @param key Message key part
577 * @return A finished JMenuItem instance
579 private JMenuItem initMenuItemWithTooltip (final String key) {
581 this.getLogger().trace(MessageFormat.format("key={0} - CALLED!", key)); //NOI18N
583 JMenuItem item = new JMenuItem(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.text", key))); //NOI18N
584 item.setToolTipText(this.getBundle().getString(String.format("AddressbookFrame.menuItem.%s.toolTipText", key))); //NOI18N
587 this.getLogger().trace(MessageFormat.format("item={0} - EXIT!", item)); //NOI18N
594 * Initializes the menu system
596 private void initMenuSystem () {
598 this.getLogger().trace("CALLED!"); //NOI18N
600 // Init menu bar, menu and item instances
601 JMenuBar menuBar = new JMenuBar();
607 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
610 // 1.x) Exit program (should be last)
611 this.addMenuItem(menu, "exitProgram", new ActionListener() { //NOI18N
613 * If the user has performed this action
615 * @param e An instance of an ActionEvent class
618 public void actionPerformed (final ActionEvent e) {
619 self.shutdownApplication();
623 // Add menu -> menu bar
627 // 2) Addressbook menu
628 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
631 this.addOwnItem = this.initMenuItemWithTooltip("addOwnData"); //NOI18N
633 // Add listener to exit menu
634 this.addOwnItem.addActionListener(new ActionListener() {
636 * If the user has performed this action
638 * @param e An instance of an ActionEvent class
641 public void actionPerformed (final ActionEvent e) {
643 self.getClient().getContactManager().doEnterOwnData();
644 } catch (final ContactAlreadyAddedException ex) {
645 // Already added, log away
646 // @TODO maybe output message here?
647 self.logException(ex);
653 menu.add(this.addOwnItem);
655 // 2.2) Edit own data
656 this.editOwnItem = this.initMenuItemWithTooltip("editOwnData"); //NOI18N
658 // Add listener to exit menu
659 this.editOwnItem.addActionListener(new ActionListener() {
661 * If the user has performed this action
663 * @param e An instance of an ActionEvent class
666 public void actionPerformed (final ActionEvent e) {
667 self.getClient().getContactManager().doChangeOwnData();
672 menu.add(this.editOwnItem);
675 // 1) Add new contact
676 this.addMenuItem(menu, "addNewContact", new ActionListener() { //NOI18N
678 * If the user has performed this action
680 * @param e An instance of an ActionEvent class
683 public void actionPerformed (final ActionEvent e) {
684 self.getClient().getContactManager().doAddOtherAddress();
688 // Add menu -> menu bar
691 // Add menu bar -> frame
692 this.frame.add(menuBar, BorderLayout.NORTH);
695 this.getLogger().trace("EXIT!"); //NOI18N
699 * Adds a new menu item with given key to menu instance
701 * @param menu Menu instance to add item to
702 * @param key Message key part
703 * @param listener Listener instance
705 private void addMenuItem (final JMenu menu, final String key, final ActionListener listener) {
707 this.getLogger().trace(MessageFormat.format("menu={0},key={1},listener={2} - CALLED!", menu, key, listener)); //NOI18N
710 JMenuItem item = this.initMenuItemWithTooltip(key);
713 item.addActionListener(listener);
719 this.getLogger().trace("EXIT!"); //NOI18N
723 * Initializes name panel
725 * @param dialog A JDialog instance to this components to
727 private void initNameDataPanel (final JDialog dialog) {
729 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
731 // Panel "name" input boxes
732 JPanel namePanel = new JPanel();
733 namePanel.setLayout(new GridLayout(0, 2, 3, 3));
735 // Set border to titled version
736 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name"))); //NOI18N
739 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
742 Gender[] genders = Gender.values();
744 // Init gender combo box with tool tip
745 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
746 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.toolTipText"));
748 // Add both to gender panel
749 namePanel.add(gLabel);
750 namePanel.add(gender);
752 // Add text field for surname
753 this.addTextFieldWithLabelToPanel(namePanel, "surname", 20); //NOI18N
755 // Add text field for family name
756 this.addTextFieldWithLabelToPanel(namePanel, "familyName", 20); //NOI18N
758 // Finally add panel to dialog
759 dialog.add(namePanel);
762 this.getLogger().trace("EXIT!"); //NOI18N
766 * Initializes "other" data panel
768 * @param dialog A JDialog instance to this components to
769 * @todo Fill this with life
771 private void initOtherDataPanel (final JDialog dialog) {
773 this.getLogger().trace(MessageFormat.format("dialog={0} - CALLED!", dialog)); //NOI18N
775 // Panel "other" input boxes
776 JPanel otherPanel = new JPanel();
777 otherPanel.setLayout(new GridLayout(0, 2, 3, 3));
778 otherPanel.setBorder(new TitledBorder(this.generateBorderTitle("other"))); //NOI18N
780 // Add text field for email address
781 this.addTextFieldWithLabelToPanel(otherPanel, "emailAddress", 20); //NOI18N
783 // Add text field for phone number
784 this.addTextFieldWithLabelToPanel(otherPanel, "phoneNumber", 20); //NOI18N
786 // Add text field for cellphone number
787 this.addTextFieldWithLabelToPanel(otherPanel, "cellphoneNumber", 20); //NOI18N
789 // Add text field for fax number
790 this.addTextFieldWithLabelToPanel(otherPanel, "faxNumber", 20); //NOI18N
793 JLabel commentLabel = new JLabel(this.getBundle().getString("AddressbookFrame.comment.text"));
795 // Init text area with tool tip
796 JTextArea comment = new JTextArea(5, 20);
797 comment.setToolTipText(this.getBundle().getString("AddressbookFrame.comment.toolTipText"));
800 otherPanel.add(commentLabel);
801 otherPanel.add(comment);
803 // Finally add panel to dialog
804 dialog.add(otherPanel);
807 this.getLogger().trace("EXIT!"); //NOI18N
811 * Initialize other dialogs (e.g. "Add contact")
813 private void initOtherDialogs () {
815 this.getLogger().trace("CALLED!"); //NOI18N
817 // Init other windows:
819 initAddContactDialog();
822 this.getLogger().trace("EXIT!"); //NOI18N
826 * Initializes status panel
828 private void initStatusPanel () {
830 this.getLogger().trace("CALLED!"); //NOI18N
832 // Init status label (which needs to be updated
833 this.statusLabel = new JLabel();
834 this.updateStatus("initializing"); //NOI18N
836 // Init status bar in south
837 JPanel panel = new JPanel();
838 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
839 panel.add(this.statusLabel);
840 panel.setBorder(BorderFactory.createEtchedBorder());
842 // Add panel to frame
843 this.frame.add(panel, BorderLayout.SOUTH);
846 this.getLogger().trace("EXIT!"); //NOI18N
850 * Initializes the table which will show all contacts
852 private void initTable () {
854 this.getLogger().trace("CALLED!"); //NOI18N
856 // Instance table model
857 this.dataModel = new ContactTableModel(this.getClient());
860 this.dataTable = new JTable(this.dataModel);
862 // Add mouse listener
863 this.dataTable.addMouseListener(new MouseAdapter() {
865 * If the user peformed a click on a cell
867 * @param e Mouse event instance
870 public void mouseClicked (final MouseEvent e) {
871 throw new UnsupportedOperationException("Unfinished."); //NOI18N
875 // Instance scroll pane
876 JScrollPane scroller = new JScrollPane();
878 // Add table to scroll pane
879 scroller.setViewportView(this.dataTable);
880 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
881 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
884 this.frame.add(scroller, BorderLayout.CENTER);
887 this.getLogger().trace("EXIT!"); //NOI18N
891 * Updates status to given type
893 * @param type Status type
895 private void updateStatus (final String type) {
897 this.getLogger().trace(MessageFormat.format("type={0} - CALLED!", type)); //NOI18N
899 // Set status message
900 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type))); //NOI18N
903 this.getLogger().trace("EXIT!"); //NOI18N
907 * Class for "add address" button
909 private static class AddActionListener extends BaseFrameworkSystem implements ActionListener {
913 private final JDialog dialog;
916 * Frame (not JFrame) instance
918 private final ClientFrame frame;
921 * Constructor for action listener
923 * @param dialog Dialog instance to call back
924 * @param frame Frame instance (not JFrame)
926 protected AddActionListener (final JDialog dialog, final ClientFrame frame) {
927 // Set dialog and frame here
928 this.dialog = dialog;
933 * If the action has been performed
935 * @param e The performed action event
938 public void actionPerformed (final ActionEvent e) {
939 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
944 * Class for "cancel address" button
946 private static class CancelActionListener extends BaseFrameworkSystem implements ActionListener {
950 private final JDialog dialog;
953 * Frame (not JFrame) instance
955 private final ClientFrame frame;
958 * Constructor for action listener
960 * @param dialog Dialog instance to call back
961 * @param frame Frame instance (not JFrame)
963 protected CancelActionListener (final JDialog dialog, final ClientFrame frame) {
964 // Set dialog and frame here
965 this.dialog = dialog;
970 * If the action has been performed
972 * @param e The performed action event
975 public void actionPerformed (final ActionEvent e) {
976 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.