jsugar

Window.java

1
/**
2
 * Window.java - Module to create a new window with JSugar.
3
 * Copyright © 2016 Maarten "Vngngdn" Vangeneugden
4
 * 
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * 
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 * 
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
/*
20
 * TODO list:
21
 * - JSlider (It's the same as the JSpinner, only longer. So an extra.)
22
 * - JTable (And a JScrollBar to accompany it) (extra, because of JList)
23
 * - JFileChooser (?)
24
 * DONE list:
25
 * - JLabel
26
 * - JText
27
 * - JButton
28
 * - JDialogBoxes (you know, everything dialog related)
29
 * - JCheckbox
30
 * - JRadioButton (properly grouping them has been taken care of as well)
31
 * - JSpinner
32
 * - JComboBox
33
 * - JList
34
 */
35
36
import javax.swing.*; // FIXME: Maybe namespacing it to "javax.swing;" is a better idea.
37
import java.util.NoSuchElementException;
38
import java.lang.reflect.Method;
39
import java.io.File;
40
/**
41
 * Window class for the program.
42
 *
43
 * Window contains the necessary data and methods to present the user with what
44
 * he's familiar with as being a "window". To make it functional, the developer
45
 * can make use of a series of methods to add components to said window, remove
46
 * components, and so on.
47
 * Currently, Window also contains methods to show dialogs. This will be cleaned
48
 * in the near future.
49
 * @author Maarten Vangeneugden
50
 */
51
public class Window { // Must be public, in order to generate Javadoc.
52
	private JPanel panel; // The panel that contains all the components.
53
	private JFrame frame; // The "window" being presented to the user.
54
55
	/**
56
	 * Constructor of Window.
57
	 * By creating a new Window instance, this constructor will automatically
58
	 * start the initialization of the GUI. After doing so, the caller can
59
	 * start adding components to the window as pleased.
60
	 * @param title The title to be shown in the window's title bar.
61
	 */
62
	public Window(String title) {
63
		// Setting the UI style to the platform's UI style. Fuck Swing's,
64
		// really.
65
		try {
66
		UIManager.setLookAndFeel(
67
				UIManager.getSystemLookAndFeelClassName());
68
		} catch(Exception e) {
69
			e.printStackTrace();
70
		}
71
72
		if(title == null || title.equals("")) { // If the title was omitted:
73
			title = "JSugar";
74
		}
75
		this.panel = new JPanel();
76
		// TODO: The current title is "Hello world!" but that will become caller
77
		// defined soon.
78
		JFrame frame = new JFrame(title);
79
		// Makes it so that if the user clicks the X in the titlebar, the window
80
		// closes:
81
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
82
		//frame.getContentPane().add(lblHelloWorld); // So you use a get() in order to set() data? #JavaWTF
83
		frame.setContentPane(this.panel); // Connecting the component panel to the window.
84
		// Makes the window fit to the necessary width and height, so it can show all "subcomponents".
85
		frame.pack(); 	
86
		frame.setVisible(true); // Makes the window visible to the user.
87
		this.frame = frame;
88
	}
89
90
	/**
91
	 * Resizes the window to fit all components.
92
	 * By calling this method, the window will evaluate the currently visible
93
	 * components, and resize itself so that all components become properly
94
	 * visible.
95
	 */
96
	private void updateWindow() {
97
		this.frame.pack();
98
	}
99
100
	/**
101
	 * A series of tests for method and class handling.
102
	 * When a caller presents certain methods with data concerning reflection,
103
	 * the Java classes you need to use for that are quite opaque, and don't
104
	 * offer much safety in any way.
105
	 * The solution therefore, is run some validation checks, but these take up
106
	 * a decent amount of space in terms of LoC.
107
	 * This method takes care of all that. Call this function whenever data
108
	 * needs to be validated.
109
	 * @param methodName The name of the method, as it is declared in object.
110
	 * @param object The class instance in where this method will be called.
111
	 * @return The method that could be derived from the supplied data, or null
112
	 * if that wasn't possible.
113
	 * @throws NullPointerException if either methodName or object are null
114
	 * pointers.
115
	 * @throws IllegalArgumentException if methodName is empty, or the method
116
	 * does not appear to be declared in the given object, or object is not a
117
	 * class.
118
	 */
119
	// All unchecked typecasts are safe, and the use of raw types is taken care
120
	// of.
121
	@SuppressWarnings({"unchecked","rawtypes"})
122
	private Method handleReflectionData(String methodName, Object object) {
123
		// Null pointer checking:
124
		if (methodName == null || object == null) {
125
			throw new NullPointerException("One or more of the given parameters are null pointers.");
126
		}
127
128
		// XXX: Some might say the next line should be in an else{} block. But
129
		// Scoping rules require that I'd then have to wrap the rest of the
130
		// method in the same else to use it.
131
		Class methodClass = object.getClass(); 
132
		if (methodName.equals("")) {
133
			throw new IllegalArgumentException("The given methodName was empty.");
134
		}
135
		Method method;
136
		try { // First: Look if there's a method without parameters.
137
			method = methodClass.getMethod(methodName, null);
138
		}
139
		catch (NoSuchMethodException exception) {
140
			try {
141
				// It's possible that the method requires an event parameter, so
142
				// check for that as well:
143
				Class<?>[] parameters = {java.awt.event.ActionEvent.class};
144
				method = methodClass.getMethod(methodName, parameters);
145
			}
146
			catch (NoSuchMethodException e) {
147
				throw new IllegalArgumentException("The given method does not appear in the given class. Be aware that the given method mustn't have any parameters, or only 1 parameter, which has to be of type java.awt.event.ActionEvent.");
148
			}
149
		}
150
		// At this stage, the given data has been validated, and we've been able
151
		// to retrieve the method itself.
152
		return method;
153
	}
154
155
	/**
156
	 * Creates a button in the GUI for interaction.
157
	 * This function offers a convenient way to create a button, that can be
158
	 * directly interacted with by the user. After creation, the button itself
159
	 * is returned to the caller, if he wishes to do something else with it.
160
	 * @param text The text that will be displayed in the button.
161
	 * @param action The action that will be returned to the action listener.
162
	 * @param methodName The name of the method that will be called when an
163
	 * action is triggered.
164
	 * @param triggerObject The object instance that contains the given method.
165
	 * This may only be a null pointer if triggerMethod is not an instance
166
	 * method.
167
	 * performed. This method may accept an ActionEvent parameter as its only
168
	 * parameter, or no parameters at all.
169
	 * @throws NullPointerException if triggerMethod is a null pointer, or
170
	 * the empty String was given.
171
	 * @throws IllegalArgumentException if triggerMethod has more than 1
172
	 * parameter, or the 1 required parameter is not of type ActionEvent.
173
	 * @return The button that was created.
174
	 * @see java.awt.event.ActionEvent
175
	 * @see java.lang.reflect.Method#invoke
176
	 */
177
	public JButton createButton(String text, String action, String methodName, Object triggerObject) {
178
		Method triggerMethod = this.handleReflectionData(methodName, triggerObject);
179
180
		// For starters, we first assert that the given parameters are valid:
181
		if (text == null) {
182
			text = "";
183
		}
184
		if (action == null) {
185
			action = "";
186
		}
187
		
188
		// When the method gets here, everything's been validated correctly.
189
		JButton button = new JButton(text);
190
		button.setActionCommand(action);
191
		button.addActionListener(
192
				new java.awt.event.ActionListener() {
193
					public void actionPerformed(java.awt.event.ActionEvent event) {
194
						try {
195
							triggerMethod.setAccessible(true);
196
							if (triggerMethod.getParameterTypes().length == 0) {
197
								// FIXME: Next line throws a warning?
198
								triggerMethod.invoke(triggerObject, null);
199
							}
200
							else {
201
								triggerMethod.invoke(triggerObject, new Object[]{event});
202
							}
203
						}
204
						catch (Exception useless) {
205
							/*
206
							 * XXX: Some info on why I don't just throw said
207
							 * Exception to the caller:
208
							 * Java has this awful language constraint, which
209
							 * forces every damn exception that isn't a subclass
210
							 * of RuntimeException, to be declared in the method
211
							 * declaration. This tends to infect all underlying
212
							 * methods as well, and all that for reasons I can't
213
							 * comprehend. In order to keep JSugar a simple and
214
							 * clean library, I'll rather just handle it here,
215
							 * and throw a RuntimeException with appropriate
216
							 * details.
217
							 */
218
							throw new IllegalArgumentException("triggerMethod is not accessible from this context.");
219
						}
220
					}
221
				});
222
		this.addComponent(button);
223
		return button;
224
	}
225
226
	/**
227
	 * Ask the user for input through a dialog box.
228
	 * This method presents the user with an input field, that can accept
229
	 * textual input. The method will return the given input after the user's
230
	 * clicked a button to send.
231
	 * @param text The text/question to be asked to the user.
232
	 * @return A String, equal to what the user entered.
233
	 * @throws NullPointerException if text is a null pointer.
234
	 */
235
	public String inputDialog(String text) {
236
		if (text == null) {
237
			throw new NullPointerException("The given text/question was a null pointer.");
238
		}
239
		return JOptionPane.showInputDialog(text);
240
	}
241
242
	/**
243
	 * Give the user a dialog box.
244
	 * This method can be used to provide a simple dialog to the user.
245
	 * This will show the user the given question, after which a boolean value
246
	 * is returned, holding the choice.
247
	 * @param text The text/question to be asked to the user.
248
	 * @return True if the user confirms, False if he denies.
249
	 * @throws NullPointerException if text is a null pointer.
250
	 */
251
	public boolean confirmDialog(String text) {
252
		if (text == null) {
253
			throw new NullPointerException("The given text/question was a null pointer.");
254
		}
255
		final int ACCEPTED = 0;
256
		//final int DENIED = 1; // Not used in the current context.
257
		int result = this.choiceDialog(text, new String[]{"Confirm", "Deny"});
258
		if (result == ACCEPTED) {
259
			return true;
260
		}
261
		else {
262
			return false;
263
		}
264
	}
265
266
	/**
267
	 * Give the user a choice dialog box.
268
	 * This method gives the user a simple dialog with predefined choices.
269
	 * These choices are to be provided by the caller in a simple array.
270
	 *
271
	 * Tip: This method works extremely well with arbitrary created choices.
272
	 * That is: if the outcome of the dialog is trivial (e.g. Only 1 choice),
273
	 * then that value is immediately returned.
274
	 * @param text The text/question to be asked to the user.
275
	 * @param choices An array of Strings, containing the choices the user can
276
	 * pick.
277
	 * @return The index value of the picked choice, or -1 if no choices were
278
	 * given.
279
	 * @throws NullPointerException if text is a null pointer.
280
	 */
281
	public int choiceDialog(String text, String[] choices) {
282
		if (text == null) {
283
			throw new NullPointerException("The given text/question was a null pointer.");
284
		}
285
		// First: handling the trivial cases:
286
		if (choices.length == 0) {
287
			return -1;
288
		}
289
		else if (choices.length == 1) {
290
			return 0;
291
		}
292
		int answer = JOptionPane.CLOSED_OPTION;
293
		// The dialog needs to be shown again until the user has made a possible
294
		// choice, i.e. Chickening out using the close button is not possible
295
		// (Because that returns CLOSED_OPTION).
296
		while (answer == JOptionPane.CLOSED_OPTION) {
297
				JOptionPane.showOptionDialog(
298
					null, // The parent component. May become the panel?
299
					text, // The text/question to describe the goal
300
					"Dialog", // The text in the title bar
301
					JOptionPane.DEFAULT_OPTION, // The kind of available options
302
					JOptionPane.QUESTION_MESSAGE, // The type of message
303
					null, // The icon to show
304
					choices, // The possible choices
305
					choices[0] // The standard choice
306
					);
307
		}
308
		return answer;
309
	}
310
		
311
312
	/**
313
	 * Creates a label in the GUI for interaction.
314
	 * This function offers a convenient way to create a label, that can be
315
	 * directly interacted with by the user. After creation, the label itself
316
	 * is returned to the caller, if he wishes to do something else with it.
317
	 * @param text The text that will be displayed in the label.
318
	 * @return The label that was created.
319
	 */
320
	public JLabel createLabel(String text) {
321
		JLabel label = new JLabel(text);
322
		this.addComponent(label);
323
		return label;
324
	}
325
326
	/**
327
	 * Adds a checkbox to the window.
328
	 * By providing a String, you can use this method to easily
329
	 * create a checkbox, and add it to the window. 
330
	 * @param text The text to put next to the checkbox.
331
	 * @return The checkbox that was created and added to the GUI.
332
	 */
333
	public JCheckBox createCheckbox(String text) {
334
		JCheckBox checkbox = new JCheckBox(text);
335
		this.addComponent(checkbox);
336
		return checkbox;
337
	}
338
339
	/**
340
	 * Adds radio buttons to the window.
341
	 * Given a list of Strings, this method will create the same amount of radio
342
	 * buttons.
343
	 *
344
	 * The radio buttons will silently be grouped in a ButtonGroup object,
345
	 * making them automatically disable each other, so only 1 radio button can
346
	 * be enabled. This ButtonGroup is immutable.
347
	 *
348
	 * If you need a mutable ButtonGroup, create your own, and use the 
349
	 * {@link #addComponent} method to add the radio buttons manually.
350
	 * @param text An array of Strings. The length of the array will determine
351
	 * the amount of radio buttons that will be created.
352
	 * @return An array of radio buttons, in the same order as text.
353
	 */
354
	public JRadioButton[] createRadioButtons(String text[]) {
355
		JRadioButton[] radioButtons = new JRadioButton[text.length];
356
		ButtonGroup buttonGroup = new ButtonGroup();
357
		for (int i=0; i<radioButtons.length; i++) {
358
			radioButtons[i].setText(text[i]);
359
			buttonGroup.add(radioButtons[i]);
360
			this.addComponent(radioButtons[i]);
361
		}
362
363
		assert radioButtons.length == buttonGroup.getButtonCount() : "The amount of radio buttons ("+ radioButtons.length +") differs from the amount of buttons in buttonGroup ("+ buttonGroup.getButtonCount() +").";
364
		return radioButtons;
365
	}
366
367
	/**
368
	 * Adds a number spinner component to the GUI.
369
	 * This method allows the caller to create a so-called "spinner component"
370
	 * to the window. This spinner is an input box, in which only integers can
371
	 * be put.
372
	 *
373
	 * The caller can set a range, a start value, and a step size.
374
	 *
375
	 * The spinner created with this method may modify itself based on the
376
	 * parameters.
377
	 * For example: If the minimum and maximum value are equal, the spinner will
378
	 * be disabled.
379
	 *
380
	 * @param minimum The minimum value that can be selected.
381
	 * @param maximum The maximum value that can be selected.
382
	 * @param start The value that will initially be shown in the component.
383
	 * @param stepSize The step size when the user increases/decreases the
384
	 * value.
385
	 * @throws IllegalArgumentException if minimum is larger than maximum, 
386
	 * start is not in the range of the selectable values, or stepsize is not a
387
	 * natural number.
388
	 * @return The JSpinner that was added to the window.
389
	 */
390
	public JSpinner createSpinner(int minimum, int maximum, int start, int stepSize) {
391
		// As usual, we begin with checking the contract:
392
		if(minimum > maximum) {
393
			throw new IllegalArgumentException("The minimum value ("+ minimum +") was larger than the maximum value ("+ maximum +")");
394
		}
395
		// The "start ∉ [minimum, maximum]" is done by the SpinnerNumberModel
396
		// constructor, which will be constructed later.
397
		if(stepSize <= 0) { // stepSize ∉ ℕ¹ (In Belgium: ℕ₀)
398
			throw new IllegalArgumentException("The stepSize ("+ stepSize +") is not a natural number (excluding 0).");
399
		}
400
		// If the contract is valid, we can begin:
401
		/*
402
		 * I'd like to interject here, because this is a nice example of why
403
		 * JSugar was a good idea:
404
		 * If you want a spinner, you'll typically want an integer spinner. But
405
		 * in Swing, when you create a JSpinner, it creates a JSpinner, with a
406
		 * predefined 'SpinnerNumberModel' attached to it.
407
		 * It's this model you then have to extract from the created spinner, on
408
		 * which you need to apply the configuration.
409
		 * What you actually have to do, is create a SpinnerNumberModel
410
		 * yourself, put your settings on that, and then, create a JSpinner to
411
		 * which you give that SpinnerNumberModel.
412
		 * In essence: The entire Java framework is shit.
413
		 */
414
		SpinnerNumberModel spinnerSettings = new SpinnerNumberModel(
415
				start,
416
				minimum,
417
				maximum,
418
				stepSize
419
				);
420
		JSpinner spinner = new JSpinner(spinnerSettings);
421
		if(minimum == maximum) { // Trivial value is set already, --> disable.
422
			spinner.setEnabled(false);
423
		}
424
		this.addComponent(spinner);
425
		return spinner;
426
	}
427
428
	/**
429
	 * Adds a number spinner component to the GUI.
430
	 * This method allows the caller to create a so-called "spinner component"
431
	 * to the window. This spinner is an input box, in which only integers can
432
	 * be put.
433
	 * 
434
	 * Tip: This method is a convenience method, and works extremely well with
435
	 * arbitrary data.
436
	 * For example: The start value is automatically set to the minimal possible
437
	 * value, and the step size defaults to 1.
438
	 * If the minimum and maximum are equal, the component will be disabled, and
439
	 * thus, be locked on the only (trivially) possible value.
440
	 * If minimum is larger than maximum, the method will notify you of this,
441
	 * but also swap the values. So you can rest assured that the spinner will
442
	 * handle errors, but also, inform you about it.
443
	 * @param minimum The minimum value that can be selected.
444
	 * @param maximum The maximum value that can be selected.
445
	 * @return The JSpinner component that was added to the window.
446
	 */
447
	public JSpinner createSpinner(int minimum, int maximum) {
448
		// The disabling of equal values is done in the full createSpinner(), so
449
		// this is merely switching values if they need to be swapped.
450
		if(minimum > maximum) {
451
			System.err.println("minimum ("+ minimum +") was larger than maximum ("+ maximum +").");
452
			// FIXME: Consider whether it's appropriate to print a stacktrace
453
			// here, which may be convenient for debugging.
454
			
455
			// XXX: I know you don't need the help variable when swapping
456
			// integers, because you can also do basic arithmetics. Change it if
457
			// it causes too much eye burn for you.
458
			int swapValue = minimum;
459
			minimum = maximum;
460
			maximum = swapValue;
461
		}
462
463
		// Yeah, these 2 variables make you cringe huh, performance addicts?
464
		// Drown me in the tears of your useless performance-related opinions.
465
		int startValue = minimum;
466
		int stepSize = 1;
467
		return this.createSpinner(minimum, maximum, startValue, stepSize);
468
	}
469
470
	/**
471
	 * Adds a combobox to the GUI.
472
	 * Allows the caller to create a combobox by providing the values that
473
	 * should be put in it.
474
	 *
475
	 * This method can only be used for String values. If that is not what you
476
	 * need, consider creating your own combobox and adding it manually. Or, if
477
	 * you need a combobox for integers, consider {@link #createSpinner}.
478
	 *
479
	 * WARNING: {@link JComboBox#getSelectedItem} returns an object, not a
480
	 * String. You need to manually typecast this. This is a constraint of the
481
	 * Swing framework.
482
	 * @param items An array of Strings that will be put in the combobox.
483
	 * @throws NullPointerException if one of the values in items is a null
484
	 * pointer.
485
	 * @throws IllegalArgumentException if items is empty.
486
	 * @return The JCombobox that was added to the window.
487
	 */
488
	public JComboBox<String> addComboBox(String[] items) {
489
		// Contract validation:
490
		if(items.length == 0) {
491
			throw new IllegalArgumentException("The given array of items was empty.");
492
		}
493
		for(String item : items) {
494
			if(item == null) {
495
				throw new NullPointerException("One of the given Strings is a null pointer.");
496
			}
497
		}
498
		// Contract validated, create the component:
499
		JComboBox<String> comboBox = new JComboBox<String>(items);
500
		comboBox.setSelectedIndex(0);
501
		if(comboBox.getItemCount() == 1) { // Trivial selection
502
			comboBox.setEnabled(false);
503
		}
504
		this.addComponent(comboBox);
505
		return comboBox;
506
	}
507
508
	/**
509
	 * Creates a list of the given data, and adds it to the GUI.
510
	 * This will create a JList component, containing the given data. 
511
	 * To jar up your memory: A list in this context, is a component in which
512
	 * data of the same type is printed out. The user of said list, can then
513
	 * select a subset of these items.
514
	 *
515
	 * @see JList for a collection of possible operations.
516
	 * @param items The String items that will be put in the list.
517
	 * @throws NullPointerException if one of the values in items is a null
518
	 * pointer.
519
	 * @throws IllegalArgumentException if items is empty.
520
	 * @return A JList component, that was a added to the GUI.
521
	 */
522
	public JList createList(String[] items) {
523
		// Contract validation:
524
		if(items.length == 0) {
525
			throw new IllegalArgumentException("The given array of items was empty.");
526
		}
527
		for(String item : items) {
528
			if(item == null) {
529
				throw new NullPointerException("One of the given Strings is a null pointer.");
530
			}
531
		}
532
		// Contract validated, create the component:
533
		JList list = new JList(items);
534
		this.addComponent(list);
535
		return list;
536
	}
537
538
	/**
539
	 * Creates a table of the given data, and adds it to the GUI.
540
	 * This method allows you to create a table with sorting functionality in
541
	 * the GUI.
542
	 * This method relies on implications, deducted from the given data. That
543
	 * is, the length of the rows and columns is calculated by the longest
544
	 * length it can find in the nested array.
545
	 * To change the data, take a look at the JTable documentation.
546
	 *
547
	 * @see JTable for a collection of possible operations.
548
	 * @param items The String items that will be put in the list.
549
	 * @throws NullPointerException if one of the values in items is a null
550
	 * pointer.
551
	 * @throws IllegalArgumentException if items is empty, or the amount of
552
	 * column names does not correspond with the given amount of items.
553
	 * @return A JTable component, that was a added to the GUI.
554
	 */
555
	public JTable createTable(String[] columns, String[][] items) {
556
		// Contract validation:
557
		if(items.length == 0) {
558
			throw new IllegalArgumentException("The given array of items was empty.");
559
		}
560
		if(columns.length != items[0].length) {
561
			throw new IllegalArgumentException("The amount of columns does not correspond to the given amount of items.");
562
		}
563
		for(int i=0; i<items.length; i++) {
564
			if(items[i] == null) {
565
				throw new NullPointerException("One of the given Strings is a null pointer.");
566
			}
567
		}
568
		// Contract validated, create the component:
569
		// Deducting max length:
570
		int columnCount = columns.length;
571
		int rowCount = items.length;
572
		
573
		JTable table = new JTable(items, columns);
574
		this.addComponent(table);
575
		return table;
576
	}
577
578
	/**
579
	 * Adds the given component to the GUI.
580
	 * This method allows its caller to give a pre-made component, so that it
581
	 * can be added to the GUI. Even though its main use is for the Window class
582
	 * itself, the user of JSugar can also use it to create components himself,
583
	 * and then add them. As such, this method doesn't provide parameters for
584
	 * reflection/action triggering purposes.
585
	 * @param component The component to be added to the window.
586
	 * @throws NullPointerException if the given component is a null pointer.
587
	 */
588
	public void addComponent(JComponent component) {
589
		int originalSize = this.panel.getComponentCount();
590
		this.panel.add(component); // Throws the exception if null.
591
		this.updateWindow();
592
593
		assert originalSize == this.panel.getComponentCount()-1 : "A component was supposed to be added to the window, but the total amount of components was unchanged after the addition.";
594
	}
595
596
	/**
597
	 * Removes the given component from the GUI.
598
	 * This method allows its caller to remove a component from the GUI.
599
	 * @param component The component to be removed.
600
	 * @throws NoSuchElementException if the given component does not exist in
601
	 * the GUI.
602
	 * @throws NullPointerException if the given component is a null pointer.
603
	 */
604
	public void removeComponent(JComponent component) {
605
		int originalSize = this.panel.getComponentCount();
606
		this.panel.remove(component);
607
		int newSize = this.panel.getComponentCount();
608
		if (originalSize != newSize+1) {
609
			throw new NoSuchElementException("The given component does not exist in the GUI.");
610
		}
611
		this.updateWindow();
612
	}
613
	/**
614
	 * Prompts the user with a file chooser dialog.
615
	 * By calling this method, the user will be presented with a file chooser
616
	 * dialog, out of which a single file can be selected. If the selected file
617
	 * exists, a File object is returned, a null pointer if the user cancelled.
618
	 * @return A File object representing the file the user selected, or null
619
	 * otherwise.
620
	 */
621
	public File openFileChooserDialog() {
622
		JFileChooser fileDialog = new JFileChooser();
623
		fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
624
625
		int userResponse = fileDialog.showOpenDialog(this.panel);
626
		if(userResponse == JFileChooser.APPROVE_OPTION) {
627
			return fileDialog.getSelectedFile();
628
		}
629
		else {
630
			return null;
631
		}
632
	}
633
}
634