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 org.mxchange.addressbook.model.gender.GenderComboBoxModel;
20 import java.awt.BorderLayout;
21 import java.awt.GridLayout;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.awt.event.MouseAdapter;
25 import java.awt.event.MouseEvent;
26 import java.awt.event.WindowAdapter;
27 import java.awt.event.WindowEvent;
28 import java.text.MessageFormat;
29 import javax.swing.BorderFactory;
30 import javax.swing.BoxLayout;
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.border.TitledBorder;
42 import javax.swing.table.TableModel;
43 import org.mxchange.addressbook.BaseFrameworkSystem;
44 import org.mxchange.addressbook.application.AddressbookApplication;
45 import org.mxchange.addressbook.client.Client;
46 import org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException;
47 import org.mxchange.addressbook.model.contact.ContactTableModel;
51 * @author Roland Haeder
53 public class AddressbookFrame extends BaseFrameworkSystem implements ClientFrame {
58 private static ClientFrame self;
61 * Singelton getter for this frame instance.
63 * @param client Client instance
64 * @return Returns a singelton instance of this frame
66 public static final ClientFrame getSelfInstance (final Client client) {
68 if (!(self instanceof ClientFrame)) {
69 // Create new instance
70 self = new AddressbookFrame(client);
78 * Dialog box "add contact"
80 private JDialog addContact;
83 * Frame instance for "add own data"
85 private JMenuItem addOwnItem;
88 * Instance to table model
90 private TableModel dataModel;
95 private JTable dataTable;
98 * Frame instance for "edit own data"
100 private JMenuItem editOwnItem;
105 private final JFrame frame;
108 * Whether this frame has been initialized
110 private boolean isInitialized;
113 * Status label needs to be updated
115 private JLabel statusLabel;
118 * Creates an instance of this frame with a client instance
122 private AddressbookFrame (final Client client) {
124 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client));
126 // Set frame instance
127 this.frame = new JFrame();
128 this.frame.setTitle(this.generateFrameTitle("main"));
131 this.setClient(client);
135 * Shutdown this frame
138 public void doShutdown () {
139 // First only show shutdown status
140 this.updateStatus("shutdown");
144 * Setups the frame, do not set isInitialized here
146 * @param client Client instance
149 public void setupFrame (final Client client) {
151 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client));
153 // Has the user entered own data?
154 if (this.getClient().getContactManager().isOwnContactAdded()) {
156 this.getLogger().debug("Disabling menus: isOwnContactAdded()=false");
158 // Not entered yet, so disable "add" menu
159 this.addOwnItem.setEnabled(false);
162 this.editOwnItem.setEnabled(false);
165 // Make the frame visible
166 this.frame.setVisible(true);
169 this.updateStatus("done");
173 * Initalizes this frame. Having initComponents() exposed (publicly
174 * accessible) means that any other object can initialize components which
178 * org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException If
179 * this method has been called twice
182 public void init () throws FrameAlreadyInitializedException {
184 this.getLogger().trace("CALLED!");
186 // Has this frame been initialized?
187 if (this.isInitialized()) {
189 throw new FrameAlreadyInitializedException();
193 this.initComponents();
196 this.isInitialized = true;
200 * Returns field isInitialized. This flag indicates whether this frame has
201 * been initialized or not.
203 * @return Field isInitialized
206 public final boolean isInitialized () {
207 return this.isInitialized;
211 * Shuts down the application.
214 public void shutdownApplication () {
215 // To do this, the frame must be initialized
216 if (!this.isInitialized()) {
217 // Not initalized, so bad call
218 this.getLogger().fatal("Bad call of shutdownApplication(). Please report this.");
221 this.getClient().getApplication().doShutdown();
225 * Generates a title for borders
226 * @param key Key part to look for
227 * @return Human-readable title
229 private String generateBorderTitle (final String key) {
230 // Call bundle instance
231 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key));
235 * Generates a title for all frames based on given sub title key. If null is
236 * given, the sub title is not generated.
238 * @param subKey Key for sub title resource
239 * @return A full application title
241 private String generateFrameTitle (final String subKey) {
243 String title = AddressbookApplication.printableTitle();
246 if (subKey != null) {
248 title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey)));
256 * Initializes "add contact" dialog
258 private void initAddContactDialog () {
259 // Instance dialog and set title
260 this.addContact = new JDialog();
261 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact"));
262 this.addContact.setLayout(new GridLayout(4, 1));
264 // Only hide it on close and make it appear in middle of screen
265 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
266 this.addContact.setLocationRelativeTo(null);
268 // Set always on top and auto-focus
269 this.addContact.setAlwaysOnTop(true);
270 this.addContact.setAutoRequestFocus(true);
273 this.addContact.setSize(400, 300);
276 * Add listener which asks for confirmation, if data has been entered
277 * but not saved yet. The user may appriciate this ... ;-)
283 // 1) Panel "name" input boxes
284 JPanel panel = new JPanel();
285 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
287 // Set border to titled version
288 panel.setBorder(new TitledBorder(this.generateBorderTitle("name")));
290 // Add some input boxes for "name" panel
291 JComboBox<String> gender = new JComboBox<>(new GenderComboBoxModel(this.getClient()));
293 // Finally add panel to dialog
294 this.addContact.add(panel);
296 // Only for developing:
297 /* DEBUG: */ this.addContact.setVisible(true);
301 * Initialize components
303 private void initComponents () {
305 this.getLogger().trace("CALLED!");
307 // Set default close operation
308 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
310 // Register shutdown listener
311 this.frame.addWindowListener(new WindowAdapter() {
313 * Invoked when a window has been closed.
316 public void windowClosed (final WindowEvent e) {
317 // Shutdown application cleanly
318 self.shutdownApplication();
322 * Invoked when a window is in the process of being closed. The
323 * close operation can be overridden at this point.
326 public void windowClosing (final WindowEvent e) {
327 // Also shutdown cleanly here
328 self.shutdownApplication();
332 // Setup layout manager
333 this.frame.setLayout(new BorderLayout(2, 2));
336 this.frame.setSize(700, 400);
338 // Center window in middle of screen, instead of top-left corner
339 this.frame.setLocationRelativeTo(null);
350 // Init other windows
355 * Initializes the menu system
357 private void initMenuSystem () {
358 // Init menu bar, menu and item instances
359 JMenuBar menuBar = new JMenuBar();
365 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
368 // 1.x) Exit program (should be last)
369 item = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.text"));
370 item.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.toolTipText"));
372 // Add listener to exit menu
373 item.addActionListener(new ActionListener() {
375 * If the user has performed this action
377 * @param e An instance of an ActionEvent class
380 public void actionPerformed (final ActionEvent e) {
381 self.shutdownApplication();
388 // Add menu -> menu bar
392 // 2) Addressbook menu
393 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
396 this.addOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.text"));
397 this.addOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.toolTipText"));
399 // Add listener to exit menu
400 this.addOwnItem.addActionListener(new ActionListener() {
402 * If the user has performed this action
404 * @param e An instance of an ActionEvent class
407 public void actionPerformed (final ActionEvent e) {
408 self.getClient().getContactManager().doEnterOwnData();
413 menu.add(this.addOwnItem);
415 // 2.2) Edit own data
416 this.editOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.text"));
417 this.editOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.toolTipText"));
419 // Add listener to exit menu
420 this.editOwnItem.addActionListener(new ActionListener() {
422 * If the user has performed this action
424 * @param e An instance of an ActionEvent class
427 public void actionPerformed (final ActionEvent e) {
428 self.getClient().getContactManager().doChangeOwnData();
433 menu.add(this.editOwnItem);
435 // Add menu -> menu bar
438 // Add menu bar -> frame
439 this.frame.add(menuBar, BorderLayout.NORTH);
443 * Initialize other dialogs (e.g. "Add contact")
445 private void initOtherDialogs () {
446 // Init other windows:
448 initAddContactDialog();
452 * Initializes status panel
454 private void initStatusPanel () {
455 // Init status label (which needs to be updated
456 this.statusLabel = new JLabel();
457 this.updateStatus("initializing");
459 // Init status bar in south
460 JPanel panel = new JPanel();
461 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
462 panel.add(this.statusLabel);
463 panel.setBorder(BorderFactory.createEtchedBorder());
465 // Add panel to frame
466 this.frame.add(panel, BorderLayout.SOUTH);
470 * Initializes the table which will show all contacts
472 private void initTable () {
473 // Instance table model
474 this.dataModel = new ContactTableModel(this.getClient());
477 this.dataTable = new JTable(this.dataModel);
479 // Add mouse listener
480 this.dataTable.addMouseListener(new MouseAdapter() {
482 * If the user peformed a click on a cell
484 * @param e Mouse event instance
487 public void mouseClicked (final MouseEvent e) {
488 throw new UnsupportedOperationException("Unfinished.");
492 // Instance scroll pane
493 JScrollPane scroller = new JScrollPane();
495 // Add table to scroll pane
496 scroller.setViewportView(this.dataTable);
497 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
498 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
501 this.frame.add(scroller, BorderLayout.CENTER);
505 * Updates status to given type
507 * @param type Status type
509 private void updateStatus (final String type) {
510 // Set status message
511 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type)));