]> git.mxchange.org Git - addressbook-lib.git/blob - Addressbook/src/org/mxchange/addressbook/client/gui/AddressbookFrame.java
fdc59e1e990e50d7e1e4b377e3e83552f1011e7a
[addressbook-lib.git] / Addressbook / src / org / mxchange / addressbook / client / gui / AddressbookFrame.java
1 /*
2  * Copyright (C) 2015 Roland Haeder
3  *
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.
8  *
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.
13  *
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/>.
16  */
17 package org.mxchange.addressbook.client.gui;
18
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;
52
53 /**
54  *
55  * @author Roland Haeder
56  */
57 public class AddressbookFrame extends BaseFrameworkSystem implements ClientFrame {
58
59         /**
60          * Own instance
61          */
62         private static ClientFrame self;
63
64         /**
65          * Singelton getter for this frame instance.
66          *
67          * @param client Client instance
68          * @return Returns a singelton instance of this frame
69          */
70         public static final ClientFrame getSelfInstance (final Client client) {
71                 // Is it set?
72                 if (!(self instanceof ClientFrame)) {
73                         // Create new instance
74                         self = new AddressbookFrame(client);
75                 }
76
77                 // Return instance
78                 return self;
79         }
80
81         /**
82          * Dialog box "add contact"
83          */
84         private JDialog addContact;
85
86         /**
87          * Frame instance for "add own data"
88          */
89         private JMenuItem addOwnItem;
90
91         /**
92          * Instance to table model
93          */
94         private TableModel dataModel;
95
96         /**
97          * Table instance
98          */
99         private JTable dataTable;
100
101         /**
102          * Frame instance for "edit own data"
103          */
104         private JMenuItem editOwnItem;
105
106         /**
107          * Frame instance
108          */
109         private final JFrame frame;
110
111         /**
112          * Whether this frame has been initialized
113          */
114         private boolean isInitialized;
115
116         /**
117          * Status label needs to be updated
118          */
119         private JLabel statusLabel;
120
121         /**
122          * Creates an instance of this frame with a client instance
123          *
124          * @param client
125          */
126         private AddressbookFrame (final Client client) {
127                 // Debug line
128                 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client));
129
130                 // Set frame instance
131                 this.frame = new JFrame();
132                 this.frame.setTitle(this.generateFrameTitle("main"));
133
134                 // Set client here
135                 this.setClient(client);
136         }
137
138         /**
139          * Shutdown this frame
140          */
141         @Override
142         public void doShutdown () {
143                 // First only show shutdown status
144                 this.updateStatus("shutdown");
145         }
146
147         /**
148          * Setups the frame, do not set isInitialized here
149          *
150          * @param client Client instance
151          */
152         @Override
153         public void setupFrame (final Client client) {
154                 // Debug line
155                 this.getLogger().trace(MessageFormat.format("client={0}: CALLED!", client));
156
157                 // Has the user entered own data?
158                 if (this.getClient().getContactManager().isOwnContactAdded()) {
159                         // Debug message
160                         this.getLogger().debug("Disabling menus: isOwnContactAdded()=false");
161
162                         // Not entered yet, so disable "add" menu
163                         this.addOwnItem.setEnabled(false);
164                 } else {
165                         // Disable "edit"
166                         this.editOwnItem.setEnabled(false);
167                 }
168
169                 // Make the frame visible
170                 this.frame.setVisible(true);
171
172                 // All done here
173                 this.updateStatus("done");
174         }
175
176         /**
177          * Initalizes this frame. Having initComponents() exposed (publicly
178          * accessible) means that any other object can initialize components which
179          * you may not want.
180          *
181          * @throws
182          * org.mxchange.addressbook.exceptions.FrameAlreadyInitializedException If
183          * this method has been called twice
184          */
185         @Override
186         public void init () throws FrameAlreadyInitializedException {
187                 // Debug line
188                 this.getLogger().trace("CALLED!");
189
190                 // Has this frame been initialized?
191                 if (this.isInitialized()) {
192                         // Throw exception
193                         throw new FrameAlreadyInitializedException();
194                 }
195
196                 // Init components
197                 this.initComponents();
198
199                 // Set flag
200                 this.isInitialized = true;
201         }
202
203         /**
204          * Returns field isInitialized. This flag indicates whether this frame has
205          * been initialized or not.
206          *
207          * @return Field isInitialized
208          */
209         @Override
210         public final boolean isInitialized () {
211                 return this.isInitialized;
212         }
213
214         /**
215          * Shuts down the application.
216          */
217         @Override
218         public void shutdownApplication () {
219                 // To do this, the frame must be initialized
220                 if (!this.isInitialized()) {
221                         // Not initalized, so bad call
222                         this.getLogger().fatal("Bad call of shutdownApplication(). Please report this.");
223                         return;
224                 }
225                 this.getClient().getApplication().doShutdown();
226         }
227
228         /**
229          * Generates a title for borders
230          * @param key Key part to look for
231          * @return Human-readable title
232          */
233         private String generateBorderTitle (final String key) {
234                 // Call bundle instance
235                 return this.getBundle().getString(String.format("AddressbookFrame.border.%s.title.text", key));
236         }
237
238         /**
239          * Generates a title for all frames based on given sub title key. If null is
240          * given, the sub title is not generated.
241          * 
242          * @param subKey Key for sub title resource
243          * @return A full application title
244          */
245         private String generateFrameTitle (final String subKey) {
246                 // Base title
247                 String title = AddressbookApplication.printableTitle();
248
249                 // Is key given?
250                 if (subKey != null) {
251                         // Add sub title
252                         title = String.format("%s - %s", title, this.getBundle().getString(String.format("AddressbookFrame.%s.title.text", subKey)));
253                 }
254
255                 // Return it
256                 return title;
257         }
258
259         /**
260          * Initializes "add contact" dialog
261          */
262         private void initAddContactDialog () {
263                 // Instance dialog and set title
264                 this.addContact = new JDialog();
265                 this.addContact.setTitle(this.generateFrameTitle("dialog.addContact"));
266                 this.addContact.setLayout(new GridLayout(4, 1));
267
268                 // Only hide it on close and make it appear in middle of screen
269                 this.addContact.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
270                 this.addContact.setLocationRelativeTo(null);
271
272                 // Set always on top and auto-focus
273                 this.addContact.setAlwaysOnTop(true);
274                 this.addContact.setAutoRequestFocus(true);
275
276                 // Initial dimension
277                 this.addContact.setSize(500, 400);
278
279                 /*
280                  * Add listener which asks for confirmation, if data has been entered
281                  * but not saved yet. The user may appriciate this ... ;-)
282                  *
283                  * @TODO Unfinished
284                  */
285
286                 // Init 3 panels:
287                 // 1) "name" panel
288                 initNamePanel();
289
290                 // 2) "address" panel
291                 initAddressPanel();
292
293                 // x)Only for developing:
294                 /* DEBUG: */ this.addContact.setVisible(true);
295         }
296
297         /**
298          * Initializes address panel
299          */
300         private void initAddressPanel () {
301                 // Panel "address" input boxes
302                 JPanel addressPanel = new JPanel();
303                 addressPanel.setLayout(new BoxLayout(addressPanel, BoxLayout.Y_AXIS));
304                 
305                 // Set border to titled version
306                 addressPanel.setBorder(new TitledBorder(this.generateBorderTitle("address")));
307
308                 // Init all elements:
309                 // 1) Street and number together
310                 JPanel streetNumberPanel = new JPanel();
311                 streetNumberPanel.setLayout(new GridLayout(1, 4));
312
313                 // Label for street
314                 JLabel streetLabel = new JLabel(this.getBundle().getString("AddressbookFrame.street.text"));
315
316                 // Init text field with label
317                 JTextField street = new JTextField(20);
318                 street.setToolTipText(this.getBundle().getString("AddressbookFrame.street.tooltipText"));
319
320                 // Add both to street panel
321                 streetNumberPanel.add(streetLabel);
322                 streetNumberPanel.add(street);
323
324                 // Number label
325                 JLabel numberLabel = new JLabel(this.getBundle().getString("AddressbookFrame.number.text"));
326
327                 // And text field, but only accept numbers
328                 JTextField number = new JTextField(4);
329                 number.setToolTipText(this.getBundle().getString("AddressbookFrame.number.tooltipText"));
330
331                 // Add number verifier
332                 number.setInputVerifier(new InputVerifier() {
333
334                         /**
335                          * Method to verify that the entered data is a number.
336                          * 
337                          * @param input Input to verify
338                          * @return Whether the data is a number
339                          */
340                         @Override
341                         public boolean verify (final JComponent input) {
342                                 // Cast on text field
343                                 JTextField text = (JTextField) input;
344
345                                 // Default is passed
346                                 boolean isValid = true;
347
348                                 // Try to convert input text to a number
349                                 try {
350                                         int num = Integer.valueOf(text.getText());
351                                 } catch (final NumberFormatException ex) {
352                                         // Didn't work
353                                         isValid = false;
354                                 }
355
356                                 // Return status
357                                 return isValid;
358                         }
359                 });
360
361                 // Add both to street panel
362                 streetNumberPanel.add(numberLabel);
363                 streetNumberPanel.add(number);
364
365                 // Add panel to address panel
366                 addressPanel.add(streetNumberPanel);
367
368                 // 2) ZIP code and ccity name
369                 JPanel zipCityPanel = new JPanel();
370                 zipCityPanel.setLayout(new GridLayout(1, 4));
371
372                 // Label for ZIP code, again numbers only
373                 JLabel zipLabel = new JLabel(this.getBundle().getString("AddressbookFrame.zip.text"));
374
375                 // Init text field with label
376                 JTextField zip = new JTextField(20);
377                 zip.setToolTipText(this.getBundle().getString("AddressbookFrame.zip.tooltipText"));
378
379                 // Add number verifier
380                 zip.setInputVerifier(new InputVerifier() {
381
382                         /**
383                          * Method to verify that the entered data is a number.
384                          * 
385                          * @param input Input to verify
386                          * @return Whether the data is a number
387                          */
388                         @Override
389                         public boolean verify (final JComponent input) {
390                                 // Cast on text field
391                                 JTextField text = (JTextField) input;
392
393                                 // Default is passed
394                                 boolean isValid = true;
395
396                                 // Try to convert input text to a number
397                                 try {
398                                         int num = Integer.valueOf(text.getText());
399                                 } catch (final NumberFormatException ex) {
400                                         // Didn't work
401                                         isValid = false;
402                                 }
403
404                                 // Return status
405                                 return isValid;
406                         }
407                 });
408
409                 // Add both to street panel
410                 zipCityPanel.add(zipLabel);
411                 zipCityPanel.add(zip);
412
413                 // Label for street
414                 JLabel cityLabel = new JLabel(this.getBundle().getString("AddressbookFrame.city.text"));
415
416                 // Init text field with label
417                 JTextField city = new JTextField(20);
418                 city.setToolTipText(this.getBundle().getString("AddressbookFrame.city.tooltipText"));
419
420                 // Add both to street panel
421                 zipCityPanel.add(cityLabel);
422                 zipCityPanel.add(city);
423
424                 // Add panel to address panel
425                 addressPanel.add(zipCityPanel);
426
427                 // Add panel to dialog
428                 this.addContact.add(addressPanel);
429         }
430
431         /**
432          * Initialize components
433          */
434         private void initComponents () {
435                 // Debug line
436                 this.getLogger().trace("CALLED!");
437
438                 // Set default close operation
439                 this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
440
441                 // Register shutdown listener
442                 this.frame.addWindowListener(new WindowAdapter() {
443                         /**
444                          * Invoked when a window has been closed.
445                          */
446                         @Override
447                         public void windowClosed (final WindowEvent e) {
448                                 // Shutdown application cleanly
449                                 self.shutdownApplication();
450                         }
451
452                         /**
453                          * Invoked when a window is in the process of being closed. The
454                          * close operation can be overridden at this point.
455                          */
456                         @Override
457                         public void windowClosing (final WindowEvent e) {
458                                 // Also shutdown cleanly here
459                                 self.shutdownApplication();
460                         }
461                 });
462
463                 // Setup layout manager
464                 this.frame.setLayout(new BorderLayout(2, 2));
465
466                 // Set window size
467                 this.frame.setSize(700, 400);
468
469                 // Center window in middle of screen, instead of top-left corner
470                 this.frame.setLocationRelativeTo(null);
471
472                 // Init menu system
473                 initMenuSystem();
474
475                 // Init table
476                 initTable();
477
478                 // Init status panel
479                 initStatusPanel();
480
481                 // Init other windows
482                 initOtherDialogs();
483         }
484
485         /**
486          * Initializes the menu system
487          */
488         private void initMenuSystem () {
489                 // Init menu bar, menu and item instances
490                 JMenuBar menuBar = new JMenuBar();
491                 JMenu menu;
492                 JMenuItem item;
493                 
494                 // Init some menus:
495                 // 1) File menu
496                 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.file.text"));
497                 
498                 // Add menu items:
499                 // 1.x) Exit program (should be last)
500                 item = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.text"));
501                 item.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.exitProgram.toolTipText"));
502
503                 // Add listener to exit menu
504                 item.addActionListener(new ActionListener() {
505                         /**
506                          * If the user has performed this action
507                          *
508                          * @param e An instance of an ActionEvent class
509                          */
510                         @Override
511                         public void actionPerformed (final ActionEvent e) {
512                                 self.shutdownApplication();
513                         }
514                 });
515
516                 // Add item -> menu
517                 menu.add(item);
518
519                 // Add menu -> menu bar
520                 menuBar.add(menu);
521                 
522                 // Init some menus:
523                 // 2) Addressbook menu
524                 menu = new JMenu(this.getBundle().getString("AddressbookFrame.menu.addressbook.text"));
525
526                 // 2.1) Add own data
527                 this.addOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.text"));
528                 this.addOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.addOwnData.toolTipText"));
529
530                 // Add listener to exit menu
531                 this.addOwnItem.addActionListener(new ActionListener() {
532                         /**
533                          * If the user has performed this action
534                          *
535                          * @param e An instance of an ActionEvent class
536                          */
537                         @Override
538                         public void actionPerformed (final ActionEvent e) {
539                                 self.getClient().getContactManager().doEnterOwnData();
540                         }
541                 });
542
543                 // Add item -> menu
544                 menu.add(this.addOwnItem);
545
546                 // 2.2) Edit own data
547                 this.editOwnItem = new JMenuItem(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.text"));
548                 this.editOwnItem.setToolTipText(this.getBundle().getString("AddressbookFrame.menuItem.editOwnData.toolTipText"));
549
550                 // Add listener to exit menu
551                 this.editOwnItem.addActionListener(new ActionListener() {
552                         /**
553                          * If the user has performed this action
554                          *
555                          * @param e An instance of an ActionEvent class
556                          */
557                         @Override
558                         public void actionPerformed (final ActionEvent e) {
559                                 self.getClient().getContactManager().doChangeOwnData();
560                         }
561                 });
562
563                 // Add item -> menu
564                 menu.add(this.editOwnItem);
565
566                 // Add menu -> menu bar
567                 menuBar.add(menu);
568
569                 // Add menu bar -> frame
570                 this.frame.add(menuBar, BorderLayout.NORTH);
571         }
572
573         /**
574          * Initializes name panel
575          */
576         private void initNamePanel () {
577                 // Panel "name" input boxes
578                 JPanel namePanel = new JPanel();
579                 namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS));
580
581                 // Set border to titled version
582                 namePanel.setBorder(new TitledBorder(this.generateBorderTitle("name")));
583
584                 // Panel for gender
585                 JPanel gPanel = new JPanel();
586                 gPanel.setLayout(new GridLayout(1, 2));
587                 
588                 // Gender text field
589                 JLabel gLabel = new JLabel(this.getBundle().getString("AddressbookFrame.gender.text"));
590                 
591                 // Get all genders
592                 Gender[] genders = Gender.values();
593                 
594                 // Init gender combo box with tool tip
595                 JComboBox<Gender> gender = new JComboBox<>(new DefaultComboBoxModel<>(genders));
596                 gender.setToolTipText(this.getBundle().getString("AddressbookFrame.gender.tooltipText"));
597
598                 // Add both to gender panel
599                 gPanel.add(gLabel);
600                 gPanel.add(gender);
601
602                 // Add gender panel to "name" panel
603                 namePanel.add(gPanel);
604
605                 // Panel for surname
606                 JPanel sPanel = new JPanel();
607                 sPanel.setLayout(new GridLayout(1, 2));
608
609                 // New label for surname is not needed
610                 JLabel sLabel = new JLabel(this.getBundle().getString("AddressbookFrame.surname.text"));
611
612                 // And input box wih tool tip
613                 JTextField surname = new JTextField(20);
614                 surname.setToolTipText(this.getBundle().getString("AddressbookFrame.surname.tooltipText"));
615
616                 // Add both to surname panel
617                 sPanel.add(sLabel);
618                 sPanel.add(surname);
619
620                 // Add surname panel to "name" panel
621                 namePanel.add(sPanel);
622
623                 // Panel for surname
624                 JPanel fPanel = new JPanel();
625                 fPanel.setLayout(new GridLayout(1, 2));
626
627                 // New label for surname is not needed
628                 JLabel fLabel = new JLabel(this.getBundle().getString("AddressbookFrame.familyName.text"));
629
630                 // And input box wih tool tip
631                 JTextField familyName = new JTextField(20);
632                 familyName.setToolTipText(this.getBundle().getString("AddressbookFrame.familyName.tooltipText"));
633
634                 // Add both to surname panel
635                 fPanel.add(fLabel);
636                 fPanel.add(familyName);
637
638                 // Add family namepanel to "name" panel
639                 namePanel.add(fPanel);
640
641                 // Finally add panel to dialog
642                 this.addContact.add(namePanel);
643         }
644
645         /**
646          * Initialize other dialogs (e.g. "Add contact")
647          */
648         private void initOtherDialogs () {
649                 // Init other windows:
650                 // 1) Add contact
651                 initAddContactDialog();
652         }
653
654         /**
655          * Initializes status panel
656          */
657         private void initStatusPanel () {
658                 // Init status label (which needs to be updated
659                 this.statusLabel = new JLabel();
660                 this.updateStatus("initializing");
661
662                 // Init status bar in south
663                 JPanel panel = new JPanel();
664                 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
665                 panel.add(this.statusLabel);
666                 panel.setBorder(BorderFactory.createEtchedBorder());
667
668                 // Add panel to frame
669                 this.frame.add(panel, BorderLayout.SOUTH);
670         }
671
672         /**
673          * Initializes the table which will show all contacts
674          */
675         private void initTable () {
676                 // Instance table model
677                 this.dataModel = new ContactTableModel(this.getClient());
678
679                 // Instance table
680                 this.dataTable = new JTable(this.dataModel);
681
682                 // Add mouse listener
683                 this.dataTable.addMouseListener(new MouseAdapter() {
684                         /**
685                          * If the user peformed a click on a cell
686                          *
687                          * @param e Mouse event instance
688                          */
689                         @Override
690                         public void mouseClicked (final MouseEvent e) {
691                                 throw new UnsupportedOperationException("Unfinished.");
692                         }
693                 });
694
695                 // Instance scroll pane
696                 JScrollPane scroller = new JScrollPane();
697
698                 // Add table to scroll pane
699                 scroller.setViewportView(this.dataTable);
700                 scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
701                 scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
702
703                 // Add pane to frame
704                 this.frame.add(scroller, BorderLayout.CENTER);
705         }
706
707         /**
708          * Updates status to given type
709          *
710          * @param type Status type
711          */
712         private void updateStatus (final String type) {
713                 // Set status message
714                 this.statusLabel.setText(this.getBundle().getString(String.format("AddressbookFrame.statusLabel.%s.text", type)));
715         }
716 }