-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathOSPControl.java
More file actions
691 lines (638 loc) · 23.1 KB
/
OSPControl.java
File metadata and controls
691 lines (638 loc) · 23.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
package org.opensourcephysics.controls;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.opensourcephysics.display.Hidable;
import org.opensourcephysics.display.OSPFrame;
import org.opensourcephysics.display.OSPRuntime;
import org.opensourcephysics.numerics.DoubleArray;
import org.opensourcephysics.numerics.IntegerArray;
import org.opensourcephysics.tools.ToolsRes;
/**
* A Control that shows its parameters in a JTable.
* Custom buttons can be added.
*
* @author Wolfgang Christian
* @version 1.0
*/
public class OSPControl extends ControlFrame implements PropertyChangeListener, MainFrame {
OSPControlTable table = new OSPControlTable(new XMLControlElement());
JScrollPane controlScrollPane = new JScrollPane(table);
JTextArea messageTextArea;
JLabel clearLabel, messageLabel, inputLabel;
JSplitPane splitPane;
JMenuItem translateItem;
static final Color PANEL_BACKGROUND = javax.swing.UIManager.getColor("Panel.background"); //$NON-NLS-1$
/**
* Constructs an OSPControl.
*
* @param _model
*/
public OSPControl(Object _model) {
super(ControlsRes.getString("OSPControl.Default_Title")); //$NON-NLS-1$
model = _model;
if(model!=null) {
// added by D Brown 2006-09-10
// modified by D Brown 2007-10-17
if(OSPRuntime.getTranslator()!=null) {
OSPRuntime.getTranslator().associate(this, model.getClass());
}
String name = model.getClass().getName();
setTitle(name.substring(1+name.lastIndexOf("."))+ControlsRes.getString("OSPControl.Controller")); //$NON-NLS-1$ //$NON-NLS-2$
}
Font labelFont = new Font("Dialog", Font.BOLD, 12); //$NON-NLS-1$
inputLabel = new JLabel(ControlsRes.getString("OSPControl.Input_Parameters"), SwingConstants.CENTER); //$NON-NLS-1$
inputLabel.setFont(labelFont);
messageTextArea = new JTextArea(5, 5) {
public void paintComponent(Graphics g) {
if(OSPRuntime.antiAliasText) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints rh = g2.getRenderingHints();
rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
super.paintComponent(g);
}
};
JScrollPane messageScrollPane = new JScrollPane(messageTextArea);
// contains a view of the control
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.add(inputLabel, BorderLayout.NORTH);
topPanel.add(controlScrollPane, BorderLayout.CENTER);
buttonPanel.setVisible(true);
topPanel.add(buttonPanel, BorderLayout.SOUTH); // buttons are added using addButton method.
// clear panel acts like a button to clear the message area
JPanel clearPanel = new JPanel(new BorderLayout());
clearPanel.addMouseListener(new ClearMouseAdapter());
clearLabel = new JLabel(ControlsRes.getString("OSPControl.Clear")); //$NON-NLS-1$
clearLabel.setFont(new Font(clearLabel.getFont().getFamily(), Font.PLAIN, 9));
clearLabel.setForeground(Color.black);
clearPanel.add(clearLabel, BorderLayout.WEST);
// contains the messages
JPanel bottomPanel = new JPanel(new BorderLayout());
messageLabel = new JLabel(ControlsRes.getString("OSPControl.Messages"), SwingConstants.CENTER); //$NON-NLS-1$
messageLabel.setFont(labelFont);
bottomPanel.add(messageLabel, BorderLayout.NORTH);
bottomPanel.add(messageScrollPane, BorderLayout.CENTER);
bottomPanel.add(clearPanel, BorderLayout.SOUTH);
Container cp = getContentPane();
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topPanel, bottomPanel);
splitPane.setOneTouchExpandable(true);
cp.add(splitPane, BorderLayout.CENTER);
messageTextArea.setEditable(false);
controlScrollPane.setPreferredSize(new Dimension(350, 200));
controlScrollPane.setMinimumSize(new Dimension(0, 50));
messageScrollPane.setPreferredSize(new Dimension(350, 75));
if((OSPRuntime.getTranslator()!=null)&&(model!=null)) {
OSPRuntime.getTranslator().associate(table, model.getClass());
}
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
setLocation((d.width-getSize().width)/2, (d.height-getSize().height)/2); // center the frame
init();
ToolsRes.addPropertyChangeListener("locale", this); //$NON-NLS-1$
}
/**
* Gets this frame. Implementation of MainFrame interface.
* @return OSPFrame
*/
public OSPFrame getMainFrame() {
return this;
}
/**
* Adds a Display menu to the menu bar. Overrides OSPFrame method.
*
* @return the display menu
*/
protected JMenu loadDisplayMenu() {
JMenuBar menuBar = getJMenuBar();
if(menuBar==null) {
return null;
}
JMenu menu = super.loadDisplayMenu();
translateItem = new JMenuItem();
translateItem.setText(ControlsRes.getString("OSPControl.Translate")); //$NON-NLS-1$
// changed by D Brown 2007-10-17
if(OSPRuntime.getTranslator()!=null) {
translateItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
OSPRuntime.getTranslator().showProperties(model.getClass());
if(OSPRuntime.getTranslator() instanceof Hidable) {
((Hidable) OSPRuntime.getTranslator()).setKeepHidden(false);
}
OSPRuntime.getTranslator().setVisible(true);
}
});
translateItem.setEnabled(OSPRuntime.isAuthorMode());
languageMenu.add(translateItem, 0);
}
// changed by D Brown 2006-09-10
if(languageMenu.getItemCount()>1) {
languageMenu.insertSeparator(1);
}
return menu;
}
/**
* Refreshes the user interface in response to display changes such as Language.
*/
protected void refreshGUI() {
if(messageLabel==null) {
return;
}
super.refreshGUI();
messageLabel.setText(ControlsRes.getString("OSPControl.Messages")); //$NON-NLS-1$
clearLabel.setText(ControlsRes.getString("OSPControl.Clear")); //$NON-NLS-1$
inputLabel.setText(ControlsRes.getString("OSPControl.Input_Parameters")); //$NON-NLS-1$
table.refresh();
}
/**
* Listens for property change events.
*
* @param e the property change event
*/
public void propertyChange(PropertyChangeEvent e) {
String name = e.getPropertyName();
if(name.equals("translation")||name.equals("locale")) {//$NON-NLS-1$ //$NON-NLS-2$
refreshGUI();
// forward event to other listeners
} else {
firePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue());
}
}
/**
* Initializes this control after all objects have been created.
*
* Override this method and change the default close operation if this control is used with an applet.
*/
protected void init() {
splitPane.setDividerLocation(-1);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public Object getModel() {
return model;
}
/**
* Sets the location of the divider between the control table and the message panel.
* @param loc int
*/
public void setDividerLocation(int loc) {
splitPane.setDividerLocation(loc);
}
/**
* Sets the editable property of the given parameter so that it can not be changed from within the GUI.
*
* @param parameter String
* @param editable boolean
*/
public void setEditable(String parameter, boolean editable) {
table.setEditable(parameter, editable);
}
/**
* Locks the control's interface. Values sent to the control will not
* update the display until the control is unlocked.
*
* @param lock boolean
*/
public void setLockValues(boolean lock) {
table.setLockValues(true);
}
/**
* Creates a string representation of the control parameters.
*
* @return the control parameters
*/
public String toString() {
return table.toString();
}
/**
* Adds a parameter to the input display.
*
* @param par the parameter name
* @param val the initial parameter value
*/
public void setValue(String par, Object val) {
table.setValue(par, val);
}
/**
* Adds an initial boolean value of a parameter to the input display.
*
* @param par the parameter name
* @param val the initial parameter value
*/
public void setValue(String par, boolean val) {
table.setValue(par, val);
}
/**
* Adds an initial value of a parameter to the input display.
*
* @param par the parameter name
* @param val the initial parameter value
*/
public void setValue(String par, double val) {
table.setValue(par, Double.toString(val));
}
/**
* Adds an initial value of a parameter to the input display.
*
* @param par the parameter name
* @param val the initial parameter value
*/
public void setValue(String par, int val) {
table.setValue(par, Integer.toString(val));
}
/**
* Removes a parameter from the table.
*
* @param par the parameter name
*/
public void removeParameter(String par) {
table.setValue(par, null);
}
/**
* Reads a parameter value from the input display.
*
* @param par
* @return double the value of of the parameter
*/
public double getDouble(String par) {
return table.getDouble(par);
}
/**
* Reads a parameter value from the input display.
*
* @param par
* @return int the value of of the parameter
*/
public int getInt(String par) {
return table.getInt(par);
}
/**
* Gets the object with the specified property name.
* Throws an UnsupportedOperationException if the named object has not been stored.
*
* @param par
* @return the object
*/
public Object getObject(String par) throws UnsupportedOperationException {
return table.getObject(par);
}
/**
* Reads a parameter value from the input display.
*
* @param par the parameter name
* @return String the value of of the parameter
*/
public String getString(String par) {
return table.getString(par);
}
/**
* Reads a parameter value from the input display.
*
* @param par the parameter name
* @return the value of of the parameter
*/
public boolean getBoolean(String par) {
return table.getBoolean(par);
}
/**
* Reads the current property names.
*
* @return the property names
*/
public Collection<String> getPropertyNames() {
return table.getPropertyNames();
}
/**
* Adds a custom button to the control's frame.
*
* @param methodName the name of the method; the method has no parameters
* @param text the button's text label
* @return the custom button
*/
public JButton addButton(String methodName, String text) {
return addButton(methodName, text, null, model);
}
/**
* Adds a custom button to the control's frame.
*
* @param methodName the name of the method; the method has no parameters
* @param text the button's text label
* @param toolTipText the button's tool tip text
* @return the custom button
*/
public JButton addButton(String methodName, String text, String toolTipText) {
return addButton(methodName, text, toolTipText, model);
}
/**
* Adds a ControlTableListener that invokes method in the control's model.
* The method in the model is invoked with the table's variable name passed as a
* parameter.
*
* @param methodName the name of the method; the method has no parameters
*/
public void addControlListener(String methodName) {
addControlListener(methodName, model);
}
/**
* Adds a ControlTableListener that invokes method in the given object.
* The method in the target is invoked with the table's variable name passed as a
* parameter.
*
* @param methodName the name of the method; the method has no parameters
* @param target the target for the method
*/
public void addControlListener(String methodName, final Object target) {
Class<?>[] parameters = new Class[] {String.class};
try {
final java.lang.reflect.Method m = target.getClass().getMethod(methodName, parameters);
table.tableModel.addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
if((e.getType()!=TableModelEvent.UPDATE)||(e.getColumn()!=1)||(e.getFirstRow()<0)) {
return;
}
String name = table.getValueAt(e.getFirstRow(), 0).toString();
Object[] args = {name};
try {
m.invoke(target, args);
} catch(IllegalAccessException iae) {}
catch(java.lang.reflect.InvocationTargetException ite) {}
}
});
} catch(NoSuchMethodException nsme) {
System.err.println("The method "+methodName+"() does not exist."); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* Prints a line of text in the message area.
*
* @param s
*/
public void println(String s) {
print(s+"\n"); //$NON-NLS-1$
}
/**
* Prints a blank line in the message area.
*/
public void println() {
print("\n"); //$NON-NLS-1$
}
/**
* Prints text in the message area.
*
* @param s
*/
public void print(final String s) {
if(SwingUtilities.isEventDispatchThread()||Thread.currentThread().getName().equals("main")) { //$NON-NLS-1$
messageTextArea.append(s);
return;
}
Runnable doLater = new Runnable() {
public void run() {
messageTextArea.append(s);
}
};
// Update from the event queue.
java.awt.EventQueue.invokeLater(doLater);
}
/**
* Remove all text from the message area.
*/
public void clearMessages() {
if(SwingUtilities.isEventDispatchThread()||Thread.currentThread().getName().equals("main")) { //$NON-NLS-1$
messageTextArea.setText(""); //$NON-NLS-1$
return;
}
Runnable doLater = new Runnable() {
public void run() {
messageTextArea.setText(""); //$NON-NLS-1$
}
};
// Update from the event queue.
java.awt.EventQueue.invokeLater(doLater);
}
/**
* Remove all text from the data input area.
*/
public void clearValues() {
table.clearValues();
}
/**
* A signal that a method has completed. A message is printed in the message area.
*
* @param message
*/
public void calculationDone(String message) {
// not implemented
if(message!=null) {
println(message);
}
}
class ClearMouseAdapter extends java.awt.event.MouseAdapter {
/**
* Method mousePressed
*
* @param evt
*/
public void mousePressed(java.awt.event.MouseEvent evt) {
clearMessages();
}
/**
* Method mouseEntered
*
* @param evt
*/
public void mouseEntered(java.awt.event.MouseEvent evt) {
clearLabel.setFont(new Font(clearLabel.getFont().getFamily(), Font.BOLD, 10));
clearLabel.setText(ControlsRes.getString("OSPControl.Click_to_clear_message")); //$NON-NLS-1$
}
/**
* Method mouseExited
*
* @param evt
*/
public void mouseExited(java.awt.event.MouseEvent evt) {
clearLabel.setFont(new Font(clearLabel.getFont().getFamily(), Font.PLAIN, 9));
clearLabel.setText(ControlsRes.getString("OSPControl.Clear")); //$NON-NLS-1$
}
}
/**
* Returns an XML.ObjectLoader to save and load data for this object.
*
* @return the object loader
*/
public static XML.ObjectLoader getLoader() {
return new OSPControlLoader();
}
/**
* A class to save and load data for OSPControls.
*/
static class OSPControlLoader implements XML.ObjectLoader {
/**
* Saves object data to an XMLControl.
*
* @param prefsXMLControl the control to save to
* @param obj the object to save
*/
public void saveObject(XMLControl xmlControl, Object obj) {
OSPControl ospControl = (OSPControl) obj;
saveControlProperites(xmlControl, ospControl);
// save the model if the control is the top level
if(xmlControl.getLevel()==0) {
xmlControl.setValue("model", ospControl.model); //$NON-NLS-1$
}
}
protected void saveControlProperites(XMLControl xmlControl, OSPControl ospControl) {
// save the parameters
Iterator<String> it = ospControl.getPropertyNames().iterator();
while(it.hasNext()) {
String name = it.next();
Object val = ospControl.getObject(name);
if(val.getClass()==DoubleArray.class) {
xmlControl.setValue(name, ((DoubleArray) val).getArray());
} else if(val.getClass()==IntegerArray.class) {
xmlControl.setValue(name, ((IntegerArray) val).getArray());
} else if(val.getClass()==Boolean.class) {
xmlControl.setValue(name, ((Boolean) val).booleanValue());
} else if(val.getClass()==Double.class) {
xmlControl.setValue(name, ((Double) val).doubleValue());
} else if(val.getClass()==Integer.class) {
xmlControl.setValue(name, ((Integer) val).intValue());
} else if(val.getClass()==Character.class) {
xmlControl.setValue(name, ((Character) val).toString());
} else if(val.getClass().isArray()) {
xmlControl.setValue(name, val);
} else {
xmlControl.setValue(name, val);
}
// xmlControl.setValue(name, val.toString());
}
}
/**
* Creates an object using data from an XMLControl.
*
* @param control the control
* @return the newly created object
*/
public Object createObject(XMLControl control) {
return new OSPControl(null);
}
/**
* Loads an object with data from an XMLControl.
*
* @param control the control
* @param obj the object
* @return the loaded object
*/
public Object loadObject(XMLControl control, Object obj) {
OSPControl cf = (OSPControl) obj;
// iterate over properties and add them to table model
Iterator<String> it = control.getPropertyNames().iterator();
cf.table.setLockValues(true);
while(it.hasNext()) {
String name = it.next();
// skip "model" object properties
if(name.equals("model")&&control.getPropertyType(name).equals("object")) { //$NON-NLS-1$ //$NON-NLS-2$
XMLControl child = control.getChildControl("model"); //$NON-NLS-1$
cf.model = child.loadObject(cf.model);
continue;
}
if((cf.getObject(name) instanceof OSPCombo)&&control.getPropertyType(name).equals("string")) { //$NON-NLS-1$
OSPCombo combo = (OSPCombo) cf.getObject(name); // keep the combo but select the correct item
String itemName = control.getString(name);
String[] items = combo.items;
for(int i = 0, n = items.length; i<n; i++) {
if(itemName.equals(items[i])) {
combo.selected = i;
break;
}
}
cf.setValue(name, combo);
} else if(control.getPropertyType(name).equals("string")) { //$NON-NLS-1$
cf.setValue(name, control.getString(name));
} else if(control.getPropertyType(name).equals("int")) { //$NON-NLS-1$
cf.setValue(name, control.getInt(name));
} else if(control.getPropertyType(name).equals("double")) { //$NON-NLS-1$
cf.setValue(name, control.getDouble(name));
} else if(control.getPropertyType(name).equals("boolean")) { //$NON-NLS-1$
cf.setValue(name, control.getBoolean(name));
} else {
cf.setValue(name, control.getObject(name));
}
}
cf.table.setLockValues(false);
// XMLControl child = control.getChildControl("model"); //$NON-NLS-1$
// if(child!=null) {
// cf.model = child.loadObject(cf.model);
// }
return obj;
}
}
/**
* Creates an OSP control and establishes communication between the control and the model.
*
* Custom buttons are usually added to this control to invoke actions in the model.
*
* @param model Object
* @return AnimationControl
*/
public static OSPControl createApp(Object model) {
OSPControl control = new OSPControl(model);
control.setSize(300, 300);
control.setVisible(true);
return control;
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/