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.InputVerifier;
32 import javax.swing.JComboBox;
33 import javax.swing.JComponent;
34 import javax.swing.JDialog;
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.JTextField;
44 import javax.swing.border.TitledBorder;
45 import javax.swing.table.TableModel;
46 import org.mxchange.addressbook.BaseFrameworkSystem;
47 import org.mxchange.addressbook.application.AddressbookApplication;
48 import org.mxchange.addressbook.client.Client;
49 import org.mxchange.addressbook.contact.Contact;
50 import org.mxchange.addressbook.contact.Gender;
51 import org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException;
52 import org.mxchange.addressbook.model.contact.ContactTableModel;
56 * @author Roland Haeder
58 public class AddressbookFrame extends BaseFrameworkSystem implements ClientFrame {
63 private static ClientFrame self;
66 * Singelton getter for this frame instance.
68 * @param client Client instance
69 * @return Returns a singelton instance of this frame
71 public static final ClientFrame getSelfInstance (final Client client) {
73 if (!(self instanceof ClientFrame)) {
74 // Create new instance
75 self = new AddressbookFrame(client);
83 * Dialog box "add contact"
85 private JDialog addContact;
88 * Frame instance for "add own data"
90 private JMenuItem addOwnItem;
93 * Instance to table model
95 private TableModel dataModel;
100 private JTable dataTable;
103 * Frame instance for "edit own data"
105 private JMenuItem editOwnItem;
110 private final JFrame frame;
113 * Whether this frame has been initialized
115 private boolean isInitialized;
120 private GridLayout layout;
123 * Status label needs to be updated
125 private JLabel statusLabel;
128 * Creates an instance of this frame with a client instance
132 private AddressbookFrame (final Client client) {
134 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
136 // Set frame instance
137 this.frame = new JFrame();
138 this.frame.setTitle(this.generateFrameTitle("main")); //NOI18N
141 this.setClient(client);
144 this.getLogger().trace("EXIT!"); //NOI18N
148 public Contact doEnterOwnData () {
150 this.getLogger().trace("CALLED!"); //NOI18N
152 // Is the "add contact" window visible?
153 if (this.addContact.isVisible()) {
154 // Something bad happened
155 throw new IllegalStateException("Window addContact is already visible.");
158 // Disable main window
159 this.frame.setEnabled(false);
161 // Make other window visible
162 this.addContact.setVisible(true);
165 this.getLogger().trace("Returning null : EXIT!"); //NOI18N
167 // Return value is not supported
172 * Shutdown this frame
175 public void doShutdown () {
177 this.getLogger().trace("CALLED!"); //NOI18N
179 // First only show shutdown status
180 this.updateStatus("shutdown"); //NOI18N
183 this.getLogger().trace("EXIT!"); //NOI18N
188 * Enables main window (frame)
191 public void enableMainWindow () {
193 this.getLogger().trace("CALLED!"); //NOI18N
196 this.frame.setEnabled(true);
198 // Request focus for this window
199 this.frame.requestFocus();
202 this.getLogger().trace("EXIT!"); //NOI18N
206 * Setups the frame, do not set isInitialized here
208 * @param client Client instance
211 public void setupFrame (final Client client) {
213 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
215 // Has the user entered own data?
216 if (this.getClient().getContactManager().isOwnContactAdded()) {
218 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false"); //NOI18N
220 // Not entered yet, so disable "add" menu
221 this.addOwnItem.setEnabled(false);
224 this.editOwnItem.setEnabled(false);
227 // Make the frame visible
228 this.frame.setVisible(true);
231 this.updateStatus("done"); //NOI18N
234 this.getLogger().trace("EXIT!"); //NOI18N
238 * Initalizes this frame. Having initComponents() exposed (publicly
239 * accessible) means that any other object can initialize components which
243 * org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException If
244 * this method has been called twice
247 public void init () throws FrameAlreadyInitializedException {
249 this.getLogger().trace("CALLED!"); //NOI18N
251 // Has this frame been initialized?
252 if (this.isInitialized()) {
254 throw new FrameAlreadyInitializedException();
258 this.initComponents();
261 this.isInitialized = true;
264 this.getLogger().trace("EXIT!"); //NOI18N
268 * Returns field isInitialized. This flag indicates whether this frame has
269 * been initialized or not.
271 * @return Field isInitialized
274 public final boolean isInitialized () {
275 return this.isInitialized;
279 * Shuts down the application.
282 public void shutdownApplication () {
284 this.getLogger().trace("CALLED!"); //NOI18N
286 // To do this, the frame must be initialized
287 if (!this.isInitialized()) {
288 // Not initalized, so bad call
289 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this."); //NOI18N
293 // Call shutdown method
294 this.getClient().getApplication().doShutdown();
297 this.getLogger().trace("EXIT!"); //NOI18N
301 * Generates a title for borders
303 * @param key Key part to look for
304 * @return Human-readable title
306 private String generateBorderTitle (final String key) {
307 // Call bundle instance
308 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key)); //NOI18N
312 * Generates a title for all frames based on given sub title key. If null is
313 * given, the sub title is not generated.
315 * @param subKey Key for sub title resource
316 * @return A full application title
318 private String generateFrameTitle (final String subKey) {
320 String title = AddressbookApplication.printableTitle();
323 if (subKey != null) {
325 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey))); //NOI18N
333 * Initializes "add" and "cancel" buttons
335 private void initAddCancelButtons () {
339 * Initializes "add contact" dialog
341 private void initAddContactDialog () {
343 this.getLogger().trace("EXIT!"); //NOI18N
345 // Instance dialog and set title
346 this.addContact = new JDialog();
347 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact")); //NOI18N
350 this.addContact.setLayout(new GridLayout(0, 1, 2, 2));
352 // Only hide it on close and make it appear in middle of screen
353 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
354 this.addContact.setLocationRelativeTo(null);
356 // Set always on top and auto-focus
357 this.addContact.setAlwaysOnTop(true);
358 this.addContact.setAutoRequestFocus(true);
361 this.addContact.setSize(500, 500);
363 // And it is not resizeable
364 this.addContact.setResizable(false);
367 * Add listener which asks for confirmation, if data has been entered
368 * but not saved yet. The user may appriciate this ... ;-)
372 this.addContact.addWindowListener(new WindowAdapter() {
374 * Invoked when a window has been closed.
377 public void windowClosed (final WindowEvent e) {
378 // Enable main window again
379 AddressbookFrame.getSelfInstance(null).enableMainWindow();
383 * Invoked when a window is in the process of being closed. The
384 * close operation can be overridden at this point.
387 public void windowClosing (final WindowEvent e) {
388 e.getWindow().dispose();
394 initNameDataPanel(this.addContact);
396 // 2) "address" panel
397 initAddressDataPanel(this.addContact);
400 initOtherDataPanel(this.addContact);
402 // 4) "Add" and "Cancel" buttons
403 initAddCancelButtons();
405 // x)Only for developing:
406 /* DEBUG: */ this.addContact.setVisible(true);
409 this.getLogger().trace("EXIT!"); //NOI18N
413 * Initializes address panel
415 * @param dialog A JDialog instance to this components to
417 private void initAddressDataPanel (final JDialog dialog) {
419 this.getLogger().trace("CALLED!"); //NOI18N
421 // Panel "address" input boxes
422 JPanel addressPanel = new JPanel();
423 addressPanel.setLayout(new BoxLayout(addressPanel, BoxLayout.Y_AXIS));
425 // Set border to titled version
426 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address"))); //NOI18N
428 // Init all elements:
429 // 1) Street and number together
430 JPanel streetNumberPanel = new JPanel();
431 streetNumberPanel.setLayout(new GridLayout(1, 4, 5, 5));
434 JLabel streetLabel = new JLabel(this.getBundle().getString("AddressbookFrame.street.text"));
436 // Init text field with label
437 JTextField street = new JTextField(20);
438 street.setToolTipText(this.getBundle().getString("AddressbookFrame.street.tooltipText"));
440 // Add both to street panel
441 streetNumberPanel.add(streetLabel);
442 streetNumberPanel.add(street);
445 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
447 // And text field, but only accept numbers
448 JTextField number = new JTextField(4);
449 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.tooltipText"));
451 // Add number verifier
452 number.setInputVerifier(new InputVerifier() {
455 * Method to verify that the entered data is a number.
457 * @param input Input to verify
458 * @return Whether the data is a number
461 public boolean verify (final JComponent input) {
462 // Cast on text field
463 JTextField text = (JTextField) input;
466 boolean isValid = true;
468 // Try to convert input text to a number
470 int num = Integer.valueOf(text.getText());
471 } catch (final NumberFormatException ex) {
481 // Add both to street panel
482 streetNumberPanel.add(numberLabel);
483 streetNumberPanel.add(number);
485 // Add panel to address panel
486 addressPanel.add(streetNumberPanel);
488 // 2) ZIP code and ccity name
489 JPanel zipCityPanel = new JPanel();
490 zipCityPanel.setLayout(new GridLayout(1, 4, 5, 5));
492 // Label for ZIP code, again numbers only
493 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
495 // Init text field with label
496 JTextField zip = new JTextField(20);
497 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.tooltipText"));
499 // Add number verifier
500 zip.setInputVerifier(new InputVerifier() {
503 * Method to verify that the entered data is a number.
505 * @param input Input to verify
506 * @return Whether the data is a number
509 public boolean verify (final JComponent input) {
510 // Cast on text field
511 JTextField text = (JTextField) input;
514 boolean isValid = true;
516 // Try to convert input text to a number
518 int num = Integer.valueOf(text.getText());
519 } catch (final NumberFormatException ex) {
529 // Add both to street panel
530 zipCityPanel.add(zipLabel);
531 zipCityPanel.add(zip);
534 JLabel cityLabel = new JLabel(this.getBundle().getString("AddressbookFrame.city.text"));
536 // Init text field with label
537 JTextField city = new JTextField(20);
538 city.setToolTipText(this.getBundle().getString("AddressbookFrame.city.tooltipText"));
540 // Add both to street panel
541 zipCityPanel.add(cityLabel);
542 zipCityPanel.add(city);
544 // Add panel to address panel
545 addressPanel.add(zipCityPanel);
547 // Add panel to dialog
548 dialog.add(addressPanel);
551 this.getLogger().trace("EXIT!"); //NOI18N
555 * Initialize components
557 private void initComponents () {
559 this.getLogger().trace("CALLED!"); //NOI18N
561 // Set default close operation
562 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
564 // Register shutdown listener
565 this.frame.addWindowListener(new WindowAdapter() {
567 * Invoked when a window has been closed.
570 public void windowClosed (final WindowEvent e) {
571 // Shutdown application cleanly
572 self.shutdownApplication();
576 * Invoked when a window is in the process of being closed. The
577 * close operation can be overridden at this point.
580 public void windowClosing (final WindowEvent e) {
581 // Also shutdown cleanly here
582 self.shutdownApplication();
586 // Setup layout manager
587 this.frame.setLayout(new BorderLayout(2, 2));
590 this.frame.setSize(700, 400);
592 // Center window in middle of screen, instead of top-left corner
593 this.frame.setLocationRelativeTo(null);
604 // Init other windows
608 this.getLogger().trace("EXIT!"); //NOI18N
612 * Initializes the menu system
614 private void initMenuSystem () {
616 this.getLogger().trace("CALLED!"); //NOI18N
618 // Init menu bar, menu and item instances
619 JMenuBar menuBar = new JMenuBar();
625 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
628 // 1.x) Exit program (should be last)
629 item = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.text"));
630 item.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.toolTipText"));
632 // Add listener to exit menu
633 item.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.shutdownApplication();
648 // Add menu -> menu bar
652 // 2) Addressbook menu
653 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
656 this.addOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.text"));
657 this.addOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.toolTipText"));
659 // Add listener to exit menu
660 this.addOwnItem.addActionListener(new ActionListener() {
662 * If the user has performed this action
664 * @param e An instance of an ActionEvent class
667 public void actionPerformed (final ActionEvent e) {
668 self.getClient().getContactManager().doEnterOwnData();
673 menu.add(this.addOwnItem);
675 // 2.2) Edit own data
676 this.editOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.text"));
677 this.editOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.toolTipText"));
679 // Add listener to exit menu
680 this.editOwnItem.addActionListener(new ActionListener() {
682 * If the user has performed this action
684 * @param e An instance of an ActionEvent class
687 public void actionPerformed (final ActionEvent e) {
688 self.getClient().getContactManager().doChangeOwnData();
693 menu.add(this.editOwnItem);
695 // Add menu -> menu bar
698 // Add menu bar -> frame
699 this.frame.add(menuBar, BorderLayout.NORTH);
702 this.getLogger().trace("EXIT!"); //NOI18N
706 * Initializes name panel
708 * @param dialog A JDialog instance to this components to
710 private void initNameDataPanel (final JDialog dialog) {
712 this.getLogger().trace("CALLED!"); //NOI18N
714 // Panel "name" input boxes
715 JPanel namePanel = new JPanel();
716 namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS));
718 // Set border to titled version
719 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name"))); //NOI18N
722 JPanel gPanel = new JPanel();
723 gPanel.setLayout(new GridLayout(1, 2, 5, 5));
726 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
729 Gender[] genders = Gender.values();
731 // Init gender combo box with tool tip
732 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
733 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.tooltipText"));
735 // Add both to gender panel
739 // Add gender panel to "name" panel
740 namePanel.add(gPanel);
743 JPanel sPanel = new JPanel();
744 sPanel.setLayout(new GridLayout(1, 2, 5, 5));
746 // New label for surname is not needed
747 JLabel sLabel = new JLabel(this.getBundle().getString("AddressbookFrame.surname.text"));
749 // And input box wih tool tip
750 JTextField surname = new JTextField(20);
751 surname.setToolTipText(this.getBundle().getString("AddressbookFrame.surname.tooltipText"));
753 // Add both to surname panel
757 // Add surname panel to "name" panel
758 namePanel.add(sPanel);
761 JPanel fPanel = new JPanel();
762 fPanel.setLayout(new GridLayout(1, 2));
764 // New label for surname is not needed
765 JLabel fLabel = new JLabel(this.getBundle().getString("AddressbookFrame.familyName.text"));
767 // And input box wih tool tip
768 JTextField familyName = new JTextField(20);
769 familyName.setToolTipText(this.getBundle().getString("AddressbookFrame.familyName.tooltipText"));
771 // Add both to surname panel
773 fPanel.add(familyName);
775 // Add family namepanel to "name" panel
776 namePanel.add(fPanel);
778 // Finally add panel to dialog
779 dialog.add(namePanel);
782 this.getLogger().trace("EXIT!"); //NOI18N
786 * Initializes "other" data panel
788 * @param dialog A JDialog instance to this components to
789 * @todo Fill this with life
791 private void initOtherDataPanel (final JDialog dialog) {
793 this.getLogger().trace("CALLED!"); //NOI18N
795 // Panel "other" input boxes
796 JPanel otherPanel = new JPanel();
797 otherPanel.setLayout(new BoxLayout(otherPanel, BoxLayout.Y_AXIS));
799 // Set border to titled version
800 otherPanel.setBorder(new TitledBorder(this.generateBorderTitle("other"))); //NOI18N
802 // Finally add panel to dialog
803 dialog.add(otherPanel);
806 this.getLogger().trace("EXIT!"); //NOI18N
810 * Initialize other dialogs (e.g. "Add contact")
812 private void initOtherDialogs () {
814 this.getLogger().trace("CALLED!"); //NOI18N
816 // Init other windows:
818 initAddContactDialog();
821 this.getLogger().trace("EXIT!"); //NOI18N
825 * Initializes status panel
827 private void initStatusPanel () {
829 this.getLogger().trace("CALLED!"); //NOI18N
831 // Init status label (which needs to be updated
832 this.statusLabel = new JLabel();
833 this.updateStatus("initializing"); //NOI18N
835 // Init status bar in south
836 JPanel panel = new JPanel();
837 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
838 panel.add(this.statusLabel);
839 panel.setBorder(BorderFactory.createEtchedBorder());
841 // Add panel to frame
842 this.frame.add(panel, BorderLayout.SOUTH);
845 this.getLogger().trace("EXIT!"); //NOI18N
849 * Initializes the table which will show all contacts
851 private void initTable () {
853 this.getLogger().trace("CALLED!"); //NOI18N
855 // Instance table model
856 this.dataModel = new ContactTableModel(this.getClient());
859 this.dataTable = new JTable(this.dataModel);
861 // Add mouse listener
862 this.dataTable.addMouseListener(new MouseAdapter() {
864 * If the user peformed a click on a cell
866 * @param e Mouse event instance
869 public void mouseClicked (final MouseEvent e) {
870 throw new UnsupportedOperationException("Unfinished."); //NOI18N
874 // Instance scroll pane
875 JScrollPane scroller = new JScrollPane();
877 // Add table to scroll pane
878 scroller.setViewportView(this.dataTable);
879 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
880 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
883 this.frame.add(scroller, BorderLayout.CENTER);
886 this.getLogger().trace("EXIT!"); //NOI18N
890 * Updates status to given type
892 * @param type Status type
894 private void updateStatus (final String type) {
896 this.getLogger().trace(MessageFormat.format("type={0} - CALLED!", type)); //NOI18N
898 // Set status message
899 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type))); //NOI18N
902 this.getLogger().trace("EXIT!"); //NOI18N