jsugar

Window.java: Added an addComponent() method, so that a user can add components himself, but also an easy way to add components made by Window itself, as it automatically updates the GUI.

Author
Vngngdn
Date
July 17, 2016, 10:32 a.m.
Hash
44274335ba7708f829b41feed4164af7077170dd
Parent
807d67f05369fda64ade9860e2e51368e71a6f7b
Modified file
Window.java

Window.java

17 additions and 3 deletions.

View changes Hide changes
1
1
 * Window.java - Module to create a new window with JSugar.
2
2
 * Copyright © 2016 Maarten "Vngngdn" Vangeneugden
3
3
 * 
4
4
 * This program is free software: you can redistribute it and/or modify
5
5
 * it under the terms of the GNU General Public License as published by
6
6
 * the Free Software Foundation, either version 3 of the License, or
7
7
 * (at your option) any later version.
8
8
 * 
9
9
 * This program is distributed in the hope that it will be useful,
10
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
12
 * GNU General Public License for more details.
13
13
 * 
14
14
 * You should have received a copy of the GNU General Public License
15
15
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
16
 */
17
17
18
18
/**
19
19
 * Window class for the program.
20
20
 *
21
21
 * Window contains the necessary data and methods to present the user with what
22
22
 * he's familiar with as being a "window". To make it functional, the developer
23
23
 * can make use of a series of methods to add components to said window, remove
24
24
 * components, and so on.
25
25
 * Currently, Window also contains methods to show dialogs. This will be cleaned
26
26
 * in the near future.
27
27
 * @author Maarten Vangeneugden
28
28
 */
29
29
import javax.swing.*; // FIXME: Maybe namespacing it to "javax.swing;" is a better idea.
30
30
import java.util.NoSuchElementException;
31
31
import java.lang.reflect.Method;
32
32
33
33
class Window {
34
34
	private JPanel panel; // The panel that contains all the components.
35
35
	private JFrame frame; // The "window" being presented to the user.
36
36
37
37
	/**
38
38
	 * Constructor of Window.
39
39
	 * By creating a new Window instance, this constructor will automatically
40
40
	 * start the initialization of the GUI. After doing so, the caller can
41
41
	 * start adding components to the window as pleased.
42
42
	 * @param title The title to be shown in the window's title bar.
43
43
	 */
44
44
	public Window() {
45
45
		this.panel = new JPanel();
46
46
		// TODO: The current title is "Hello world!" but that will become caller
47
47
		// defined soon.
48
48
		JFrame frame = new JFrame("Hello world!");
49
49
		// Makes it so that if the user clicks the X in the titlebar, the window
50
50
		// closes:
51
51
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
52
52
		//frame.getContentPane().add(lblHelloWorld); // So you use a get() in order to set() data? #JavaWTF
53
53
		frame.setContentPane(this.panel); // Connecting the component panel to the window.
54
54
		// Makes the window fit to the necessary width and height, so it can show all "subcomponents".
55
55
		frame.pack(); 	
56
56
		frame.setVisible(true); // Makes the window visible to the user.
57
57
		this.frame = frame;
58
58
	}
59
59
60
60
	/**
61
61
	 * Resizes the window to fit all components.
62
62
	 * By calling this method, the window will evaluate the currently visible
63
63
	 * components, and resize itself so that all components become properly
64
64
	 * visible.
65
65
	 */
66
66
	private void updateWindow() {
67
67
		this.frame.pack();
68
68
	}
69
69
70
70
	/**
71
71
	 * A series of tests for method and class handling.
72
72
	 * When a caller presents certain methods with data concerning reflection,
73
73
	 * the Java classes you need to use for that are quite opaque, and don't
74
74
	 * offer much safety in any way.
75
75
	 * The solution therefore, is run some validation checks, but these take up
76
76
	 * a decent amount of space in terms of LoC.
77
77
	 * This method takes care of all that. Call this function whenever data
78
78
	 * needs to be validated.
79
79
	 * @param methodName The name of the method, as it is declared in object.
80
80
	 * @param object The class instance in where this method will be called.
81
81
	 * @return The method that could be derived from the supplied data, or null
82
82
	 * if that wasn't possible.
83
83
	 * @throws NullPointerException if either methodName or object are null
84
84
	 * pointers.
85
85
	 * @throws IllegalArgumentException if methodName is empty, or the method
86
86
	 * does not appear to be declared in the given object, or object is not a
87
87
	 * class.
88
88
	 */
89
89
	// All unchecked typecasts are safe, and the use of raw types is taken care
90
90
	// of.
91
91
	@SuppressWarnings({"unchecked","rawtypes"})
92
92
	private Method handleReflectionData(String methodName, Object object) {
93
93
		// Null pointer checking:
94
94
		if (methodName == null || object == null) {
95
95
			throw new NullPointerException("One or more of the given parameters are null pointers.");
96
96
		}
97
97
98
98
		// XXX: Some might say the next line should be in an else{} block. But
99
99
		// Scoping rules require that I'd then have to wrap the rest of the
100
100
		// method in the same else to use it.
101
101
		Class methodClass = object.getClass(); 
102
102
		if (methodName.equals("")) {
103
103
			throw new IllegalArgumentException("The given methodName was empty.");
104
104
		}
105
105
		Method method;
106
106
		try { // First: Look if there's a method without parameters.
107
107
			method = methodClass.getMethod(methodName, null);
108
108
		}
109
109
		catch (NoSuchMethodException exception) {
110
110
			try {
111
111
				// It's possible that the method requires an event parameter, so
112
112
				// check for that as well:
113
113
				Class<?>[] parameters = {java.awt.event.ActionEvent.class};
114
114
				method = methodClass.getMethod(methodName, parameters);
115
115
			}
116
116
			catch (NoSuchMethodException e) {
117
117
				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.");
118
118
			}
119
119
		}
120
120
		// At this stage, the given data has been validated, and we've been able
121
121
		// to retrieve the method itself.
122
122
		return method;
123
123
	}
124
124
125
125
	/**
126
126
	 * Creates a button in the GUI for interaction.
127
127
	 * This function offers a convenient way to create a button, that can be
128
128
	 * directly interacted with by the user. After creation, the button itself
129
129
	 * is returned to the caller, if he wishes to do something else with it.
130
130
	 * @param text The text that will be displayed in the button.
131
131
	 * @param action The action that will be returned to the action listener.
132
132
	 * @param methodName The name of the method that will be called when an
133
133
	 * action is triggered.
134
134
	 * @param objectInstance The object instance that contains the given method.
135
135
	 * This may only be a null pointer if triggerMethod is not an instance
136
136
	 * method.
137
137
	 * performed. This method may accept an ActionEvent parameter as its only
138
138
	 * parameter, or no parameters at all.
139
139
	 * @throws NullPointerException if triggerMethod is a null pointer, or
140
140
	 * the empty String was given.
141
141
	 * @throws IllegalArgumentException if triggerMethod has more than 1
142
142
	 * parameter, or the 1 required parameter is not of type ActionEvent.
143
143
	 * @return The button that was created.
144
144
	 * @see java.awt.event.ActionEvent
145
145
	 * @see java.lang.reflect.Method.invoke()
146
146
	 */
147
147
	public JButton createButton(String text, String action, String methodName, Object triggerObject) {
148
148
		Method triggerMethod = this.handleReflectionData(methodName, triggerObject);
149
149
150
150
		// For starters, we first assert that the given parameters are valid:
151
151
		if (text == null) {
152
152
			text = "";
153
153
		}
154
154
		if (action == null) {
155
155
			action = "";
156
156
		}
157
157
		
158
158
		// When the method gets here, everything's been validated correctly.
159
159
		JButton button = new JButton(text);
160
160
		button.setActionCommand(action);
161
161
		button.addActionListener(
162
162
				new java.awt.event.ActionListener() {
163
163
					public void actionPerformed(java.awt.event.ActionEvent event) {
164
164
						try {
165
165
							triggerMethod.setAccessible(true);
166
166
							if (triggerMethod.getParameterTypes().length == 0) {
167
167
								// FIXME: Next line throws a warning?
168
168
								triggerMethod.invoke(triggerObject, null);
169
169
							}
170
170
							else {
171
171
								triggerMethod.invoke(triggerObject, new Object[]{event});
172
172
							}
173
173
						}
174
174
						catch (Exception useless) {
175
175
							/*
176
176
							 * XXX: Some info on why I don't just throw said
177
177
							 * Exception to the caller:
178
178
							 * Java has this awful language constraint, which
179
179
							 * forces every damn exception that isn't a subclass
180
180
							 * of RuntimeException, to be declared in the method
181
181
							 * declaration. This tends to infect all underlying
182
182
							 * methods as well, and all that for reasons I can't
183
183
							 * comprehend. In order to keep JSugar a simple and
184
184
							 * clean library, I'll rather just handle it here,
185
185
							 * and throw a RuntimeException with appropriate
186
186
							 * details.
187
187
							 */
188
188
							throw new IllegalArgumentException("triggerMethod is not accessible from this context.");
189
189
						}
190
190
					}
191
191
				});
192
192
		this.panel.add(button);
193
-
		this.frame.pack();
194
-
		return button;
+
193
		return button;
195
194
	}
196
195
197
196
	/**
198
197
	 * Ask the user for input through a dialog box.
199
198
	 * This method presents the user with an input field, that can accept
200
199
	 * textual input. The method will return the given input after the user's
201
200
	 * clicked a button to send.
202
201
	 * @param text The text/question to be asked to the user.
203
202
	 * @return A String, equal to what the user entered.
204
203
	 * @throws NullPointerException if text is a null pointer.
205
204
	 */
206
205
	public String inputDialog(String text) {
207
206
		if (text == null) {
208
207
			throw new NullPointerException("The given text/question was a null pointer.");
209
208
		}
210
209
		return JOptionPane.showInputDialog(text);
211
210
	}
212
211
213
212
	/**
214
213
	 * Give the user a dialog box.
215
214
	 * This method can be used to provide a simple dialog to the user.
216
215
	 * This will show the user the given question, after which a boolean value
217
216
	 * is returned, holding the choice.
218
217
	 * @param text The text/question to be asked to the user.
219
218
	 * @return True if the user confirms, False if he denies.
220
219
	 * @throws NullPointerException if text is a null pointer.
221
220
	 */
222
221
	public boolean confirmDialog(String text) {
223
222
		if (text == null) {
224
223
			throw new NullPointerException("The given text/question was a null pointer.");
225
224
		}
226
225
		final int ACCEPTED = 0;
227
226
		final int DENIED = 1;
228
227
		int result = this.choiceDialog(text, new String[]{"Confirm", "Deny"});
229
228
		if (result == ACCEPTED) {
230
229
			return true;
231
230
		}
232
231
		else {
233
232
			return false;
234
233
		}
235
234
	}
236
235
237
236
	/**
238
237
	 * Give the user a choice dialog box.
239
238
	 * This method gives the user a simple dialog with predefined choices.
240
239
	 * These choices are to be provided by the caller in a simple array.
241
240
	 * Tip: This method works extremely well with arbitrary created choices.
242
241
	 * That is: if the outcome of the dialog is trivial (e.g. Only 1 choice),
243
242
	 * then that value is immediately returned.
244
243
	 * @param text The text/question to be asked to the user.
245
244
	 * @param choices An array of Strings, containing the choices the user can
246
245
	 * pick.
247
246
	 * @return The index value of the picked choice, or -1 if no choices were
248
247
	 * given.
249
248
	 * @throws NullPointerException if text is a null pointer.
250
249
	 */
251
250
	public int choiceDialog(String text, String[] choices) {
252
251
		if (text == null) {
253
252
			throw new NullPointerException("The given text/question was a null pointer.");
254
253
		}
255
254
		// First: handling the trivial cases:
256
255
		if (choices.length == 0) {
257
256
			return -1;
258
257
		}
259
258
		else if (choices.length == 1) {
260
259
			return 0;
261
260
		}
262
261
		int answer = JOptionPane.CLOSED_OPTION;
263
262
		// The dialog needs to be shown again until the user has made a possible
264
263
		// choice, i.e. Chickening out using the close button is not possible
265
264
		// (Because that returns CLOSED_OPTION).
266
265
		while (answer == JOptionPane.CLOSED_OPTION) {
267
266
				JOptionPane.showOptionDialog(
268
267
					null, // The parent component. May become the panel?
269
268
					text, // The text/question to describe the goal
270
269
					"Dialog", // The text in the title bar
271
270
					JOptionPane.DEFAULT_OPTION, // The kind of available options
272
271
					JOptionPane.QUESTION_MESSAGE, // The type of message
273
272
					null, // The icon to show
274
273
					choices, // The possible choices
275
274
					choices[0] // The standard choice
276
275
					);
277
276
		}
278
277
		return answer;
279
278
	}
280
279
		
281
280
282
281
	/**
283
282
	 * Creates a label in the GUI for interaction.
284
283
	 * This function offers a convenient way to create a label, that can be
285
284
	 * directly interacted with by the user. After creation, the label itself
286
285
	 * is returned to the caller, if he wishes to do something else with it.
287
286
	 * @param text The text that will be displayed in the label.
288
287
	 * @return The label that was created.
289
288
	 */
290
289
	public JLabel createLabel(String text) {
291
290
		JLabel label = new JLabel(text);
292
291
		this.panel.add(label);
293
-
		return label;
+
292
		return label;
294
293
	}
295
294
296
295
	/**
+
296
	 * Adds the given component to the GUI.
+
297
	 * This method allows its caller to give a pre-made component, so that it
+
298
	 * can be added to the GUI. Even though its main use is for the Window class
+
299
	 * itself, the user of JSugar can also use it to create components himself,
+
300
	 * and then add them. As such, this method doesn't provide parameters for
+
301
	 * reflection/action triggering purposes.
+
302
	 * @param component The component to be added to the window.
+
303
	 * @throws NullPointerException if the given component is a null pointer.
+
304
	 */
+
305
	public void addComponent(JComponent component) {
+
306
		this.panel.add(component); // Throws the exception if null.
+
307
		this.updateWindow();
+
308
	}
+
309
+
310
	/**
297
311
	 * Removes the given component from the GUI.
298
312
	 * This method allows its caller to remove a component from the GUI.
299
313
	 * @param component The component to be removed.
300
314
	 * @throws NoSuchElementException if the given component does not exist in
301
315
	 * the GUI.
302
316
	 * @throws NullPointerException if the given component is a null pointer.
303
317
	 */
304
318
	public void removeComponent(JComponent component) {
305
319
		int originalSize = this.panel.getComponentCount();
306
320
		this.panel.remove(component);
307
321
		int newSize = this.panel.getComponentCount();
308
322
		if (originalSize != newSize+1) {
309
323
			throw new NoSuchElementException("The given component does not exist in the GUI.");
310
324
		}
311
325
		this.updateWindow();
312
326
	}
313
327
}
314
328