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.Gender;
50 import org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException;
51 import org.mxchange.addressbook.model.contact.ContactTableModel;
55 * @author Roland Haeder
57 public class AddressbookFrame extends BaseFrameworkSystem implements ClientFrame {
62 private static ClientFrame self;
65 * Singelton getter for this frame instance.
67 * @param client Client instance
68 * @return Returns a singelton instance of this frame
70 public static final ClientFrame getSelfInstance (final Client client) {
72 if (!(self instanceof ClientFrame)) {
73 // Create new instance
74 self = new AddressbookFrame(client);
82 * Dialog box "add contact"
84 private JDialog addContact;
87 * Frame instance for "add own data"
89 private JMenuItem addOwnItem;
92 * Instance to table model
94 private TableModel dataModel;
99 private JTable dataTable;
102 * Frame instance for "edit own data"
104 private JMenuItem editOwnItem;
109 private final JFrame frame;
112 * Whether this frame has been initialized
114 private boolean isInitialized;
119 private GridLayout layout;
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);
144 * Shutdown this frame
147 public void doShutdown () {
148 // First only show shutdown status
149 this.updateStatus("shutdown"); //NOI18N
153 * Setups the frame, do not set isInitialized here
155 * @param client Client instance
158 public void setupFrame (final Client client) {
160 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client)); //NOI18N
162 // Has the user entered own data?
163 if (this.getClient().getContactManager().isOwnContactAdded()) {
165 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false"); //NOI18N
167 // Not entered yet, so disable "add" menu
168 this.addOwnItem.setEnabled(false);
171 this.editOwnItem.setEnabled(false);
174 // Make the frame visible
175 this.frame.setVisible(true);
178 this.updateStatus("done"); //NOI18N
182 * Initalizes this frame. Having initComponents() exposed (publicly
183 * accessible) means that any other object can initialize components which
187 * org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException If
188 * this method has been called twice
191 public void init () throws FrameAlreadyInitializedException {
193 this.getLogger().trace("CALLED!"); //NOI18N
195 // Has this frame been initialized?
196 if (this.isInitialized()) {
198 throw new FrameAlreadyInitializedException();
202 this.initComponents();
205 this.isInitialized = true;
209 * Returns field isInitialized. This flag indicates whether this frame has
210 * been initialized or not.
212 * @return Field isInitialized
215 public final boolean isInitialized () {
216 return this.isInitialized;
220 * Shuts down the application.
223 public void shutdownApplication () {
224 // To do this, the frame must be initialized
225 if (!this.isInitialized()) {
226 // Not initalized, so bad call
227 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this."); //NOI18N
230 this.getClient().getApplication().doShutdown();
234 * Generates a title for borders
236 * @param key Key part to look for
237 * @return Human-readable title
239 private String generateBorderTitle (final String key) {
240 // Call bundle instance
241 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key)); //NOI18N
245 * Generates a title for all frames based on given sub title key. If null is
246 * given, the sub title is not generated.
248 * @param subKey Key for sub title resource
249 * @return A full application title
251 private String generateFrameTitle (final String subKey) {
253 String title = AddressbookApplication.printableTitle();
256 if (subKey != null) {
258 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey))); //NOI18N
266 * Initializes "add contact" dialog
268 private void initAddContactDialog () {
269 // Instance dialog and set title
270 this.addContact = new JDialog();
271 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact")); //NOI18N
274 this.addContact.setLayout(new GridLayout(0, 1, 2, 2));
276 // Only hide it on close and make it appear in middle of screen
277 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
278 this.addContact.setLocationRelativeTo(null);
280 // Set always on top and auto-focus
281 this.addContact.setAlwaysOnTop(true);
282 this.addContact.setAutoRequestFocus(true);
285 this.addContact.setSize(500, 500);
288 * Add listener which asks for confirmation, if data has been entered
289 * but not saved yet. The user may appriciate this ... ;-)
297 // 2) "address" panel
298 initAddressDataPanel();
301 initOtherDataPanel();
303 // x)Only for developing:
306 */ this.addContact.setVisible(true);
310 * Initializes address panel
312 private void initAddressDataPanel () {
313 // Panel "address" input boxes
314 JPanel addressPanel = new JPanel();
315 addressPanel.setLayout(new BoxLayout(addressPanel, BoxLayout.Y_AXIS));
317 // Set border to titled version
318 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address"))); //NOI18N
320 // Init all elements:
321 // 1) Street and number together
322 JPanel streetNumberPanel = new JPanel();
323 streetNumberPanel.setLayout(new GridLayout(1, 4, 5, 5));
326 JLabel streetLabel = new JLabel(this.getBundle().getString("AddressbookFrame.street.text"));
328 // Init text field with label
329 JTextField street = new JTextField(20);
330 street.setToolTipText(this.getBundle().getString("AddressbookFrame.street.tooltipText"));
332 // Add both to street panel
333 streetNumberPanel.add(streetLabel);
334 streetNumberPanel.add(street);
337 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
339 // And text field, but only accept numbers
340 JTextField number = new JTextField(4);
341 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.tooltipText"));
343 // Add number verifier
344 number.setInputVerifier(new InputVerifier() {
347 * Method to verify that the entered data is a number.
349 * @param input Input to verify
350 * @return Whether the data is a number
353 public boolean verify (final JComponent input) {
354 // Cast on text field
355 JTextField text = (JTextField) input;
358 boolean isValid = true;
360 // Try to convert input text to a number
362 int num = Integer.valueOf(text.getText());
363 } catch (final NumberFormatException ex) {
373 // Add both to street panel
374 streetNumberPanel.add(numberLabel);
375 streetNumberPanel.add(number);
377 // Add panel to address panel
378 addressPanel.add(streetNumberPanel);
380 // 2) ZIP code and ccity name
381 JPanel zipCityPanel = new JPanel();
382 zipCityPanel.setLayout(new GridLayout(1, 4, 5, 5));
384 // Label for ZIP code, again numbers only
385 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
387 // Init text field with label
388 JTextField zip = new JTextField(20);
389 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.tooltipText"));
391 // Add number verifier
392 zip.setInputVerifier(new InputVerifier() {
395 * Method to verify that the entered data is a number.
397 * @param input Input to verify
398 * @return Whether the data is a number
401 public boolean verify (final JComponent input) {
402 // Cast on text field
403 JTextField text = (JTextField) input;
406 boolean isValid = true;
408 // Try to convert input text to a number
410 int num = Integer.valueOf(text.getText());
411 } catch (final NumberFormatException ex) {
421 // Add both to street panel
422 zipCityPanel.add(zipLabel);
423 zipCityPanel.add(zip);
426 JLabel cityLabel = new JLabel(this.getBundle().getString("AddressbookFrame.city.text"));
428 // Init text field with label
429 JTextField city = new JTextField(20);
430 city.setToolTipText(this.getBundle().getString("AddressbookFrame.city.tooltipText"));
432 // Add both to street panel
433 zipCityPanel.add(cityLabel);
434 zipCityPanel.add(city);
436 // Add panel to address panel
437 addressPanel.add(zipCityPanel);
439 // Add panel to dialog
440 this.addContact.add(addressPanel);
444 * Initialize components
446 private void initComponents () {
448 this.getLogger().trace("CALLED!"); //NOI18N
450 // Set default close operation
451 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
453 // Register shutdown listener
454 this.frame.addWindowListener(new WindowAdapter() {
456 * Invoked when a window has been closed.
459 public void windowClosed (final WindowEvent e) {
460 // Shutdown application cleanly
461 self.shutdownApplication();
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 // Also shutdown cleanly here
471 self.shutdownApplication();
475 // Setup layout manager
476 this.frame.setLayout(new BorderLayout(2, 2));
479 this.frame.setSize(700, 400);
481 // Center window in middle of screen, instead of top-left corner
482 this.frame.setLocationRelativeTo(null);
493 // Init other windows
498 * Initializes the menu system
500 private void initMenuSystem () {
501 // Init menu bar, menu and item instances
502 JMenuBar menuBar = new JMenuBar();
508 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
511 // 1.x) Exit program (should be last)
512 item = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.text"));
513 item.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.toolTipText"));
515 // Add listener to exit menu
516 item.addActionListener(new ActionListener() {
518 * If the user has performed this action
520 * @param e An instance of an ActionEvent class
523 public void actionPerformed (final ActionEvent e) {
524 self.shutdownApplication();
531 // Add menu -> menu bar
535 // 2) Addressbook menu
536 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
539 this.addOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.text"));
540 this.addOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.toolTipText"));
542 // Add listener to exit menu
543 this.addOwnItem.addActionListener(new ActionListener() {
545 * If the user has performed this action
547 * @param e An instance of an ActionEvent class
550 public void actionPerformed (final ActionEvent e) {
551 self.getClient().getContactManager().doEnterOwnData();
556 menu.add(this.addOwnItem);
558 // 2.2) Edit own data
559 this.editOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.text"));
560 this.editOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.toolTipText"));
562 // Add listener to exit menu
563 this.editOwnItem.addActionListener(new ActionListener() {
565 * If the user has performed this action
567 * @param e An instance of an ActionEvent class
570 public void actionPerformed (final ActionEvent e) {
571 self.getClient().getContactManager().doChangeOwnData();
576 menu.add(this.editOwnItem);
578 // Add menu -> menu bar
581 // Add menu bar -> frame
582 this.frame.add(menuBar, BorderLayout.NORTH);
586 * Initializes name panel
588 private void initNameDataPanel () {
589 // Panel "name" input boxes
590 JPanel namePanel = new JPanel();
591 namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS));
593 // Set border to titled version
594 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name"))); //NOI18N
597 JPanel gPanel = new JPanel();
598 gPanel.setLayout(new GridLayout(1, 2, 5, 5));
601 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
604 Gender[] genders = Gender.values();
606 // Init gender combo box with tool tip
607 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
608 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.tooltipText"));
610 // Add both to gender panel
614 // Add gender panel to "name" panel
615 namePanel.add(gPanel);
618 JPanel sPanel = new JPanel();
619 sPanel.setLayout(new GridLayout(1, 2, 5, 5));
621 // New label for surname is not needed
622 JLabel sLabel = new JLabel(this.getBundle().getString("AddressbookFrame.surname.text"));
624 // And input box wih tool tip
625 JTextField surname = new JTextField(20);
626 surname.setToolTipText(this.getBundle().getString("AddressbookFrame.surname.tooltipText"));
628 // Add both to surname panel
632 // Add surname panel to "name" panel
633 namePanel.add(sPanel);
636 JPanel fPanel = new JPanel();
637 fPanel.setLayout(new GridLayout(1, 2));
639 // New label for surname is not needed
640 JLabel fLabel = new JLabel(this.getBundle().getString("AddressbookFrame.familyName.text"));
642 // And input box wih tool tip
643 JTextField familyName = new JTextField(20);
644 familyName.setToolTipText(this.getBundle().getString("AddressbookFrame.familyName.tooltipText"));
646 // Add both to surname panel
648 fPanel.add(familyName);
650 // Add family namepanel to "name" panel
651 namePanel.add(fPanel);
653 // Finally add panel to dialog
654 this.addContact.add(namePanel);
658 * Initializes "other" data panel
660 private void initOtherDataPanel () {
664 * Initialize other dialogs (e.g. "Add contact")
666 private void initOtherDialogs () {
667 // Init other windows:
669 initAddContactDialog();
673 * Initializes status panel
675 private void initStatusPanel () {
676 // Init status label (which needs to be updated
677 this.statusLabel = new JLabel();
678 this.updateStatus("initializing"); //NOI18N
680 // Init status bar in south
681 JPanel panel = new JPanel();
682 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
683 panel.add(this.statusLabel);
684 panel.setBorder(BorderFactory.createEtchedBorder());
686 // Add panel to frame
687 this.frame.add(panel, BorderLayout.SOUTH);
691 * Initializes the table which will show all contacts
693 private void initTable () {
694 // Instance table model
695 this.dataModel = new ContactTableModel(this.getClient());
698 this.dataTable = new JTable(this.dataModel);
700 // Add mouse listener
701 this.dataTable.addMouseListener(new MouseAdapter() {
703 * If the user peformed a click on a cell
705 * @param e Mouse event instance
708 public void mouseClicked (final MouseEvent e) {
709 throw new UnsupportedOperationException("Unfinished."); //NOI18N
713 // Instance scroll pane
714 JScrollPane scroller = new JScrollPane();
716 // Add table to scroll pane
717 scroller.setViewportView(this.dataTable);
718 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
719 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
722 this.frame.add(scroller, BorderLayout.CENTER);
726 * Updates status to given type
728 * @param type Status type
730 private void updateStatus (final String type) {
731 // Set status message
732 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type))); //NOI18N