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.JComboBox;
32 import javax.swing.JDialog;
33 import javax.swing.JFrame;
34 import javax.swing.JLabel;
35 import javax.swing.JMenu;
36 import javax.swing.JMenuBar;
37 import javax.swing.JMenuItem;
38 import javax.swing.JPanel;
39 import javax.swing.JScrollPane;
40 import javax.swing.JTable;
41 import javax.swing.JTextField;
42 import javax.swing.border.TitledBorder;
43 import javax.swing.table.TableModel;
44 import org.mxchange.addressbook.BaseFrameworkSystem;
45 import org.mxchange.addressbook.application.AddressbookApplication;
46 import org.mxchange.addressbook.client.Client;
47 import org.mxchange.addressbook.contact.Gender;
48 import org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException;
49 import org.mxchange.addressbook.model.contact.ContactTableModel;
53 * @author Roland Haeder
55 public class AddressbookFrame extends BaseFrameworkSystem implements ClientFrame {
60 private static ClientFrame self;
63 * Singelton getter for this frame instance.
65 * @param client Client instance
66 * @return Returns a singelton instance of this frame
68 public static final ClientFrame getSelfInstance (final Client client) {
70 if (!(self instanceof ClientFrame)) {
71 // Create new instance
72 self = new AddressbookFrame(client);
80 * Dialog box "add contact"
82 private JDialog addContact;
85 * Frame instance for "add own data"
87 private JMenuItem addOwnItem;
90 * Instance to table model
92 private TableModel dataModel;
97 private JTable dataTable;
100 * Frame instance for "edit own data"
102 private JMenuItem editOwnItem;
107 private final JFrame frame;
110 * Whether this frame has been initialized
112 private boolean isInitialized;
115 * Status label needs to be updated
117 private JLabel statusLabel;
120 * Creates an instance of this frame with a client instance
124 private AddressbookFrame (final Client client) {
126 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client));
128 // Set frame instance
129 this.frame = new JFrame();
130 this.frame.setTitle(this.generateFrameTitle("main"));
133 this.setClient(client);
137 * Shutdown this frame
140 public void doShutdown () {
141 // First only show shutdown status
142 this.updateStatus("shutdown");
146 * Setups the frame, do not set isInitialized here
148 * @param client Client instance
151 public void setupFrame (final Client client) {
153 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client));
155 // Has the user entered own data?
156 if (this.getClient().getContactManager().isOwnContactAdded()) {
158 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false");
160 // Not entered yet, so disable "add" menu
161 this.addOwnItem.setEnabled(false);
164 this.editOwnItem.setEnabled(false);
167 // Make the frame visible
168 this.frame.setVisible(true);
171 this.updateStatus("done");
175 * Initalizes this frame. Having initComponents() exposed (publicly
176 * accessible) means that any other object can initialize components which
180 * org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException If
181 * this method has been called twice
184 public void init () throws FrameAlreadyInitializedException {
186 this.getLogger().trace("CALLED!");
188 // Has this frame been initialized?
189 if (this.isInitialized()) {
191 throw new FrameAlreadyInitializedException();
195 this.initComponents();
198 this.isInitialized = true;
202 * Returns field isInitialized. This flag indicates whether this frame has
203 * been initialized or not.
205 * @return Field isInitialized
208 public final boolean isInitialized () {
209 return this.isInitialized;
213 * Shuts down the application.
216 public void shutdownApplication () {
217 // To do this, the frame must be initialized
218 if (!this.isInitialized()) {
219 // Not initalized, so bad call
220 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this.");
223 this.getClient().getApplication().doShutdown();
227 * Generates a title for borders
228 * @param key Key part to look for
229 * @return Human-readable title
231 private String generateBorderTitle (final String key) {
232 // Call bundle instance
233 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key));
237 * Generates a title for all frames based on given sub title key. If null is
238 * given, the sub title is not generated.
240 * @param subKey Key for sub title resource
241 * @return A full application title
243 private String generateFrameTitle (final String subKey) {
245 String title = AddressbookApplication.printableTitle();
248 if (subKey != null) {
250 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey)));
258 * Initializes "add contact" dialog
260 private void initAddContactDialog () {
261 // Instance dialog and set title
262 this.addContact = new JDialog();
263 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact"));
264 this.addContact.setLayout(new GridLayout(4, 1));
266 // Only hide it on close and make it appear in middle of screen
267 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
268 this.addContact.setLocationRelativeTo(null);
270 // Set always on top and auto-focus
271 this.addContact.setAlwaysOnTop(true);
272 this.addContact.setAutoRequestFocus(true);
275 this.addContact.setSize(500, 400);
278 * Add listener which asks for confirmation, if data has been entered
279 * but not saved yet. The user may appriciate this ... ;-)
290 * Initialize components
292 private void initComponents () {
294 this.getLogger().trace("CALLED!");
296 // Set default close operation
297 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
299 // Register shutdown listener
300 this.frame.addWindowListener(new WindowAdapter() {
302 * Invoked when a window has been closed.
305 public void windowClosed (final WindowEvent e) {
306 // Shutdown application cleanly
307 self.shutdownApplication();
311 * Invoked when a window is in the process of being closed. The
312 * close operation can be overridden at this point.
315 public void windowClosing (final WindowEvent e) {
316 // Also shutdown cleanly here
317 self.shutdownApplication();
321 // Setup layout manager
322 this.frame.setLayout(new BorderLayout(2, 2));
325 this.frame.setSize(700, 400);
327 // Center window in middle of screen, instead of top-left corner
328 this.frame.setLocationRelativeTo(null);
339 // Init other windows
344 * Initializes the menu system
346 private void initMenuSystem () {
347 // Init menu bar, menu and item instances
348 JMenuBar menuBar = new JMenuBar();
354 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
357 // 1.x) Exit program (should be last)
358 item = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.text"));
359 item.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.toolTipText"));
361 // Add listener to exit menu
362 item.addActionListener(new ActionListener() {
364 * If the user has performed this action
366 * @param e An instance of an ActionEvent class
369 public void actionPerformed (final ActionEvent e) {
370 self.shutdownApplication();
377 // Add menu -> menu bar
381 // 2) Addressbook menu
382 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
385 this.addOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.text"));
386 this.addOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.toolTipText"));
388 // Add listener to exit menu
389 this.addOwnItem.addActionListener(new ActionListener() {
391 * If the user has performed this action
393 * @param e An instance of an ActionEvent class
396 public void actionPerformed (final ActionEvent e) {
397 self.getClient().getContactManager().doEnterOwnData();
402 menu.add(this.addOwnItem);
404 // 2.2) Edit own data
405 this.editOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.text"));
406 this.editOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.toolTipText"));
408 // Add listener to exit menu
409 this.editOwnItem.addActionListener(new ActionListener() {
411 * If the user has performed this action
413 * @param e An instance of an ActionEvent class
416 public void actionPerformed (final ActionEvent e) {
417 self.getClient().getContactManager().doChangeOwnData();
422 menu.add(this.editOwnItem);
424 // Add menu -> menu bar
427 // Add menu bar -> frame
428 this.frame.add(menuBar, BorderLayout.NORTH);
432 * Initializes name panel
434 private void initNamePanel () {
435 // 1) Panel "name" input boxes
436 JPanel namePanel = new JPanel();
437 namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS));
439 // Set border to titled version
440 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name")));
443 Gender[] genders = Gender.values();
445 // Init gender combo box
446 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
449 JPanel gPanel = new JPanel();
450 gPanel.setLayout(new GridLayout(1, 2));
453 gPanel.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.tooltipText"));
456 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
458 // Add both to gender panel
462 // Add gender panel to "name" panel
463 namePanel.add(gPanel);
466 JPanel sPanel = new JPanel();
467 sPanel.setLayout(new GridLayout(1, 2));
470 sPanel.setToolTipText(this.getBundle().getString("AddressbookFrame.surname.tooltipText"));
472 // New label for surname is not needed
473 JLabel sLabel = new JLabel(this.getBundle().getString("AddressbookFrame.surname.text"));
476 JTextField surname = new JTextField(20);
478 // Add both to surname panel
482 // Add surname panel to "name" panel
483 namePanel.add(sPanel);
486 JPanel fPanel = new JPanel();
487 fPanel.setLayout(new GridLayout(1, 2));
490 fPanel.setToolTipText(this.getBundle().getString("AddressbookFrame.familyName.tooltipText"));
492 // New label for surname is not needed
493 JLabel fLabel = new JLabel(this.getBundle().getString("AddressbookFrame.familyName.text"));
496 JTextField familyName = new JTextField(20);
498 // Add both to surname panel
500 fPanel.add(familyName);
502 // Add family namepanel to "name" panel
503 namePanel.add(fPanel);
505 // Finally add panel to dialog
506 this.addContact.add(namePanel);
508 // Only for developing:
509 /* DEBUG: */ this.addContact.setVisible(true);
513 * Initialize other dialogs (e.g. "Add contact")
515 private void initOtherDialogs () {
516 // Init other windows:
518 initAddContactDialog();
522 * Initializes status panel
524 private void initStatusPanel () {
525 // Init status label (which needs to be updated
526 this.statusLabel = new JLabel();
527 this.updateStatus("initializing");
529 // Init status bar in south
530 JPanel panel = new JPanel();
531 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
532 panel.add(this.statusLabel);
533 panel.setBorder(BorderFactory.createEtchedBorder());
535 // Add panel to frame
536 this.frame.add(panel, BorderLayout.SOUTH);
540 * Initializes the table which will show all contacts
542 private void initTable () {
543 // Instance table model
544 this.dataModel = new ContactTableModel(this.getClient());
547 this.dataTable = new JTable(this.dataModel);
549 // Add mouse listener
550 this.dataTable.addMouseListener(new MouseAdapter() {
552 * If the user peformed a click on a cell
554 * @param e Mouse event instance
557 public void mouseClicked (final MouseEvent e) {
558 throw new UnsupportedOperationException("Unfinished.");
562 // Instance scroll pane
563 JScrollPane scroller = new JScrollPane();
565 // Add table to scroll pane
566 scroller.setViewportView(this.dataTable);
567 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
568 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
571 this.frame.add(scroller, BorderLayout.CENTER);
575 * Updates status to given type
577 * @param type Status type
579 private void updateStatus (final String type) {
580 // Set status message
581 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type)));