chiark / gitweb /
Forbid undo of new-game if it would change the params.
[sgt-puzzles.git] / PuzzleApplet.java
1 /*
2  * PuzzleApplet.java: NestedVM applet for the puzzle collection
3  */
4 import java.awt.*;
5 import java.awt.event.*;
6 import java.awt.image.BufferedImage;
7 import java.util.*;
8 import javax.swing.*;
9 import javax.swing.border.BevelBorder;
10 import javax.swing.Timer;
11 import java.util.List;
12
13 import org.ibex.nestedvm.Runtime;
14
15 public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
16
17     private static final long serialVersionUID = 1L;
18
19     private static final int CFG_SETTINGS = 0, CFG_SEED = 1, CFG_DESC = 2,
20             LEFT_BUTTON = 0x0200, MIDDLE_BUTTON = 0x201, RIGHT_BUTTON = 0x202,
21             LEFT_DRAG = 0x203, MIDDLE_DRAG = 0x204, RIGHT_DRAG = 0x205,
22             LEFT_RELEASE = 0x206, CURSOR_UP = 0x209, CURSOR_DOWN = 0x20a,
23             CURSOR_LEFT = 0x20b, CURSOR_RIGHT = 0x20c, MOD_CTRL = 0x1000,
24             MOD_SHFT = 0x2000, MOD_NUM_KEYPAD = 0x4000, ALIGN_VCENTRE = 0x100,
25             ALIGN_HCENTRE = 0x001, ALIGN_HRIGHT = 0x002, C_STRING = 0,
26             C_CHOICES = 1, C_BOOLEAN = 2;
27
28     private JFrame mainWindow;
29
30     private JMenu typeMenu;
31     private JMenuItem[] typeMenuItems;
32     private int customMenuItemIndex;
33
34     private JMenuItem solveCommand;
35     private Color[] colors;
36     private JLabel statusBar;
37     private PuzzlePanel pp;
38     private Runtime runtime;
39     private String[] puzzle_args;
40     private Graphics2D  gg;
41     private Timer timer;
42     private int xarg1, xarg2, xarg3;
43     private int[] xPoints, yPoints;
44     private BufferedImage[] blitters = new BufferedImage[512];
45     private ConfigDialog dlg;
46
47     static {
48         try {
49             UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
50         } catch (Exception ex) {
51             ex.printStackTrace();
52         }
53     }
54
55     public void init() {
56         try {
57             Container cp = getContentPane();
58             cp.setLayout(new BorderLayout());
59             runtime = (Runtime) Class.forName("PuzzleEngine").newInstance();
60             runtime.setCallJavaCB(this);
61             JMenuBar menubar = new JMenuBar();
62             JMenu jm;
63             menubar.add(jm = new JMenu("Game"));
64             addMenuItemCallback(jm, "New", "jcallback_newgame_event");
65             addMenuItemCallback(jm, "Restart", "jcallback_restart_event");
66             addMenuItemCallback(jm, "Specific...", "jcallback_config_event", CFG_DESC);
67             addMenuItemCallback(jm, "Random Seed...", "jcallback_config_event", CFG_SEED);
68             jm.addSeparator();
69             addMenuItemCallback(jm, "Undo", "jcallback_undo_event");
70             addMenuItemCallback(jm, "Redo", "jcallback_redo_event");
71             jm.addSeparator();
72             solveCommand = addMenuItemCallback(jm, "Solve", "jcallback_solve_event");
73             solveCommand.setEnabled(false);
74             if (mainWindow != null) {
75                 jm.addSeparator();
76                 addMenuItemCallback(jm, "Exit", "jcallback_quit_event");
77             }
78             menubar.add(typeMenu = new JMenu("Type"));
79             typeMenu.setVisible(false);
80             menubar.add(jm = new JMenu("Help"));
81             addMenuItemCallback(jm, "About", "jcallback_about_event");
82             setJMenuBar(menubar);
83             cp.add(pp = new PuzzlePanel(), BorderLayout.CENTER);
84             pp.addKeyListener(new KeyAdapter() {
85                 public void keyPressed(KeyEvent e) {
86                     int key = -1;
87                     int shift = e.isShiftDown() ? MOD_SHFT : 0;
88                     int ctrl = e.isControlDown() ? MOD_CTRL : 0;
89                     switch (e.getKeyCode()) {
90                     case KeyEvent.VK_LEFT:
91                     case KeyEvent.VK_KP_LEFT:
92                         key = shift | ctrl | CURSOR_LEFT;
93                         break;
94                     case KeyEvent.VK_RIGHT:
95                     case KeyEvent.VK_KP_RIGHT:
96                         key = shift | ctrl | CURSOR_RIGHT;
97                         break;
98                     case KeyEvent.VK_UP:
99                     case KeyEvent.VK_KP_UP:
100                         key = shift | ctrl | CURSOR_UP;
101                         break;
102                     case KeyEvent.VK_DOWN:
103                     case KeyEvent.VK_KP_DOWN:
104                         key = shift | ctrl | CURSOR_DOWN;
105                         break;
106                     case KeyEvent.VK_PAGE_UP:
107                         key = shift | ctrl | MOD_NUM_KEYPAD | '9';
108                         break;
109                     case KeyEvent.VK_PAGE_DOWN:
110                         key = shift | ctrl | MOD_NUM_KEYPAD | '3';
111                         break;
112                     case KeyEvent.VK_HOME:
113                         key = shift | ctrl | MOD_NUM_KEYPAD | '7';
114                         break;
115                     case KeyEvent.VK_END:
116                         key = shift | ctrl | MOD_NUM_KEYPAD | '1';
117                         break;
118                     default:
119                         if (e.getKeyCode() >= KeyEvent.VK_NUMPAD0 && e.getKeyCode() <=KeyEvent.VK_NUMPAD9) {
120                             key = MOD_NUM_KEYPAD | (e.getKeyCode() - KeyEvent.VK_NUMPAD0+'0');
121                         }
122                     break;
123                     }
124                     if (key != -1) {
125                         runtimeCall("jcallback_key_event", new int[] {0, 0, key});
126                     }
127                 }
128                 public void keyTyped(KeyEvent e) {
129                     int key = e.getKeyChar();
130                     if (key == 26 && e.isShiftDown() && e.isControlDown()) {
131                         runtimeCall("jcallback_redo_event", new int[0]);
132                         return;
133                     }
134                     runtimeCall("jcallback_key_event", new int[] {0, 0, key});
135                 }
136             });
137             pp.addMouseListener(new MouseAdapter() {
138                 public void mouseReleased(MouseEvent e) {
139                     mousePressedReleased(e, true);
140                 }
141                 public void mousePressed(MouseEvent e) {
142                     pp.requestFocus();
143                     mousePressedReleased(e, false);
144                 }
145                 private void mousePressedReleased(MouseEvent e, boolean released) {
146                     int button;
147                     if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
148                         button = MIDDLE_BUTTON;
149                     else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
150                         button = RIGHT_BUTTON;
151                     else if ((e.getModifiers() & (InputEvent.BUTTON1_MASK)) != 0)
152                         button = LEFT_BUTTON;
153                     else
154                         return;
155                     if (released)
156                         button += LEFT_RELEASE - LEFT_BUTTON;
157                     runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
158                 }
159             });
160             pp.addMouseMotionListener(new MouseMotionAdapter() {
161                 public void mouseDragged(MouseEvent e) {
162                     int button;
163                     if ((e.getModifiers() & (InputEvent.BUTTON2_MASK | InputEvent.SHIFT_MASK)) != 0)
164                         button = MIDDLE_DRAG;
165                     else if ((e.getModifiers() & (InputEvent.BUTTON3_MASK | InputEvent.ALT_MASK)) != 0)
166                         button = RIGHT_DRAG;
167                     else
168                         button = LEFT_DRAG;
169                     runtimeCall("jcallback_key_event", new int[] {e.getX(), e.getY(), button});
170                 }
171             });
172             pp.addComponentListener(new ComponentAdapter() {
173                 public void componentResized(ComponentEvent e) {
174                     handleResized();
175                 }
176             });
177             pp.setFocusable(true);
178             pp.requestFocus();
179             timer = new Timer(20, new ActionListener() {
180                 public void actionPerformed(ActionEvent e) {
181                     runtimeCall("jcallback_timer_func", new int[0]);
182                 }
183             });
184             String gameid;
185             try {
186                 gameid = getParameter("game_id");
187             } catch (java.lang.NullPointerException ex) {
188                 gameid = null;
189             }
190             if (gameid == null) {
191                 puzzle_args = null;
192             } else {
193                 puzzle_args = new String[2];
194                 puzzle_args[0] = "puzzle";
195                 puzzle_args[1] = gameid;
196             }
197             SwingUtilities.invokeLater(new Runnable() {
198                 public void run() {
199                     runtime.start(puzzle_args);
200                     runtime.execute();
201                 }
202             });
203         } catch (Exception ex) {
204             ex.printStackTrace();
205         }
206     }
207
208     public void destroy() {
209         SwingUtilities.invokeLater(new Runnable() {
210             public void run() {
211                 runtime.execute();
212                 if (mainWindow != null) {
213                     mainWindow.dispose();
214                     System.exit(0);
215                 }
216             }
217         });
218     }
219
220     protected void handleResized() {
221         pp.createBackBuffer(pp.getWidth(), pp.getHeight(), colors[0]);
222         runtimeCall("jcallback_resize", new int[] {pp.getWidth(), pp.getHeight()});
223     }
224
225     private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int arg) {
226         return addMenuItemCallback(jm, name, callback, new int[] {arg}, false);
227     }
228
229     private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback) {
230         return addMenuItemCallback(jm, name, callback, new int[0], false);
231     }
232
233     private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int[] args, boolean checkbox) {
234         JMenuItem jmi;
235         if (checkbox)
236             jm.add(jmi = new JCheckBoxMenuItem(name));
237         else
238         jm.add(jmi = new JMenuItem(name));
239         jmi.addActionListener(new ActionListener() {
240             public void actionPerformed(ActionEvent e) {
241                 runtimeCall(callback, args);
242             }
243         });
244         return jmi;
245     }
246
247     protected void runtimeCall(String func, int[] args) {
248         if (runtimeCallWithResult(func, args) == 42 && mainWindow != null) {
249             destroy();
250         }
251     }
252
253     protected int runtimeCallWithResult(String func, int[] args) {
254         try {
255             return runtime.call(func, args);
256         } catch (Runtime.CallException ex) {
257             ex.printStackTrace();
258             return 42;
259         }
260     }
261
262     private void buildConfigureMenuItem() {
263         if (typeMenu.isVisible()) {
264             typeMenu.addSeparator();
265         } else {
266             typeMenu.setVisible(true);
267         }
268         typeMenuItems[customMenuItemIndex] =
269             addMenuItemCallback(typeMenu, "Custom...",
270                                 "jcallback_config_event",
271                                 new int[] {CFG_SETTINGS}, true);
272     }
273
274     private void addTypeItem
275         (JMenu targetMenu, String name, int newId, final int ptrGameParams) {
276
277         typeMenu.setVisible(true);
278         typeMenuItems[newId] =
279             addMenuItemCallback(targetMenu, name,
280                                 "jcallback_preset_event",
281                                 new int[] {ptrGameParams}, true);
282     }
283
284     private void addTypeSubmenu
285         (JMenu targetMenu, String name, int newId) {
286
287         JMenu newMenu = new JMenu(name);
288         newMenu.setVisible(true);
289         typeMenuItems[newId] = newMenu;
290         targetMenu.add(newMenu);
291     }
292
293     public int call(int cmd, int arg1, int arg2, int arg3) {
294         try {
295             switch(cmd) {
296             case 0: // initialize
297                 if (mainWindow != null) mainWindow.setTitle(runtime.cstring(arg1));
298                 if ((arg2 & 1) != 0) buildConfigureMenuItem();
299                 if ((arg2 & 2) != 0) addStatusBar();
300                 if ((arg2 & 4) != 0) solveCommand.setEnabled(true);
301                 colors = new Color[arg3];
302                 return 0;
303             case 1: // configure Type menu
304                 if (arg1 == 0) {
305                     // preliminary setup
306                     typeMenuItems = new JMenuItem[arg2 + 2];
307                     typeMenuItems[arg2] = typeMenu;
308                     customMenuItemIndex = arg2 + 1;
309                     return arg2;
310                 } else if (xarg1 != 0) {
311                     addTypeItem((JMenu)typeMenuItems[arg2],
312                                 runtime.cstring(arg1), arg3, xarg1);
313                 } else {
314                     addTypeSubmenu((JMenu)typeMenuItems[arg2],
315                                    runtime.cstring(arg1), arg3);
316                 }
317                 return 0;
318             case 2: // MessageBox
319                 JOptionPane.showMessageDialog(this, runtime.cstring(arg2), runtime.cstring(arg1), arg3 == 0 ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE);
320                 return 0;
321             case 3: // Resize
322                 pp.setPreferredSize(new Dimension(arg1, arg2));
323                 if (mainWindow != null) mainWindow.pack();
324                 handleResized();
325                 if (mainWindow != null) mainWindow.setVisible(true);
326                 return 0;
327             case 4: // drawing tasks
328                 switch(arg1) {
329                 case 0:
330                     String text = runtime.cstring(arg2);
331                     if (text.equals("")) text = " ";
332                     statusBar.setText(text);
333                     break;
334                 case 1:
335                     gg = pp.backBuffer.createGraphics();
336                     if (arg2 != 0 || arg3 != 0 ||
337                         arg2 + xarg2 != getWidth() ||
338                         arg3 + xarg3 != getHeight()) {
339                         int left = arg2, right = arg2 + xarg2;
340                         int top = arg3, bottom = arg3 + xarg3;
341                         int width = getWidth(), height = getHeight();
342                         gg.setColor(colors != null ? colors[0] : Color.black);
343                         gg.fillRect(0, 0, left, height);
344                         gg.fillRect(right, 0, width-right, height);
345                         gg.fillRect(0, 0, width, top);
346                         gg.fillRect(0, bottom, width, height-bottom);
347                         gg.setClip(left, top, right-left, bottom-top);
348                     }
349                     break;
350                 case 2: gg.dispose(); pp.repaint(); break;
351                 case 3: gg.setClip(arg2, arg3, xarg1, xarg2); break;
352                 case 4:
353                     if (arg2 == 0 && arg3 == 0) {
354                         gg.setClip(0, 0, getWidth(), getHeight());
355                     } else {
356                         gg.setClip(arg2, arg3, getWidth()-2*arg2, getHeight()-2*arg3);
357                     }
358                     break;
359                 case 5:
360                     gg.setColor(colors[xarg3]);
361                     gg.fillRect(arg2, arg3, xarg1, xarg2);
362                     break;
363                 case 6:
364                     gg.setColor(colors[xarg3]);
365                     gg.drawLine(arg2, arg3, xarg1, xarg2);
366                     break;
367                 case 7:
368                     xPoints = new int[arg2];
369                     yPoints = new int[arg2];
370                     break;
371                 case 8:
372                     if (arg3 != -1) {
373                         gg.setColor(colors[arg3]);
374                         gg.fillPolygon(xPoints, yPoints, xPoints.length);
375                     }
376                     gg.setColor(colors[arg2]);
377                     gg.drawPolygon(xPoints, yPoints, xPoints.length);
378                     break;
379                 case 9:
380                     if (arg3 != -1) {
381                         gg.setColor(colors[arg3]);
382                         gg.fillOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
383                     }
384                     gg.setColor(colors[arg2]);
385                     gg.drawOval(xarg1-xarg3, xarg2-xarg3, xarg3*2, xarg3*2);
386                     break;
387                 case 10:
388                     for(int i=0; i<blitters.length; i++) {
389                         if (blitters[i] == null) {
390                             blitters[i] = new BufferedImage(arg2, arg3, BufferedImage.TYPE_3BYTE_BGR);
391                             return i;
392                         }
393                     }
394                     throw new RuntimeException("No free blitter found!");
395                 case 11: blitters[arg2] = null; break;
396                 case 12:
397                     timer.start(); break;
398                 case 13:
399                     timer.stop(); break;
400                 }
401                 return 0;
402             case 5: // more arguments
403                 xarg1 = arg1;
404                 xarg2 = arg2;
405                 xarg3 = arg3;
406                 return 0;
407             case 6: // polygon vertex
408                 xPoints[arg1]=arg2;
409                 yPoints[arg1]=arg3;
410                 return 0;
411             case 7: // string
412                 gg.setColor(colors[arg2]);
413                 {
414                     String text = runtime.utfstring(arg3);
415                     Font ft = new Font((xarg3 & 0x10) != 0 ? "Monospaced" : "Dialog",
416                             Font.PLAIN, 100);
417                     int height100 = this.getFontMetrics(ft).getHeight();
418                     ft = ft.deriveFont(arg1 * 100 / (float)height100);
419                     FontMetrics fm = this.getFontMetrics(ft);
420                     int asc = fm.getAscent(), desc = fm.getDescent();
421                     if ((xarg3 & ALIGN_VCENTRE) != 0)
422                         xarg2 += asc - (asc+desc)/2;
423                     int wid = fm.stringWidth(text);
424                     if ((xarg3 & ALIGN_HCENTRE) != 0)
425                         xarg1 -= wid / 2;
426                     else if ((xarg3 & ALIGN_HRIGHT) != 0)
427                         xarg1 -= wid;
428                     gg.setFont(ft);
429                     gg.drawString(text, xarg1, xarg2);
430                 }
431                 return 0;
432             case 8: // blitter_save
433                 Graphics g2 = blitters[arg1].createGraphics();
434                 g2.drawImage(pp.backBuffer, 0, 0, blitters[arg1].getWidth(), blitters[arg1].getHeight(),
435                         arg2, arg3, arg2 + blitters[arg1].getWidth(), arg3 + blitters[arg1].getHeight(), this);
436                 g2.dispose();
437                 return 0;
438             case 9: // blitter_load
439                 gg.drawImage(blitters[arg1], arg2, arg3, this);
440                 return 0;
441             case 10: // dialog_init
442                 dlg= new ConfigDialog(this, runtime.cstring(arg1));
443                 return 0;
444             case 11: // dialog_add_control
445                 {
446                     int sval_ptr = arg1;
447                     int ival = arg2;
448                     int ptr = xarg1;
449                     int type=xarg2;
450                     String name = runtime.cstring(xarg3);
451                     switch(type) {
452                     case C_STRING:
453                         dlg.addTextBox(ptr, name, runtime.cstring(sval_ptr));
454                         break;
455                     case C_BOOLEAN:
456                         dlg.addCheckBox(ptr, name, ival != 0);
457                         break;
458                     case C_CHOICES:
459                         dlg.addComboBox(ptr, name, runtime.cstring(sval_ptr), ival);
460                     }
461                 }
462                 return 0;
463             case 12:
464                 dlg.finish();
465                 dlg = null;
466                 return 0;
467             case 13: // tick a menu item
468                 if (arg1 < 0) arg1 = customMenuItemIndex;
469                 for (int i = 0; i < typeMenuItems.length; i++) {
470                     if (typeMenuItems[i] instanceof JCheckBoxMenuItem) {
471                         ((JCheckBoxMenuItem)typeMenuItems[i]).setSelected
472                             (arg1 == i);
473                     }
474                 }
475                 return 0;
476             default:
477                 if (cmd >= 1024 && cmd < 2048) { // palette
478                     colors[cmd-1024] = new Color(arg1, arg2, arg3);
479                 }
480             if (cmd == 1024) {
481                 pp.setBackground(colors[0]);
482                 if (statusBar != null) statusBar.setBackground(colors[0]);
483                 this.setBackground(colors[0]);
484             }
485             return 0;
486             }
487         } catch (Throwable ex) {
488             ex.printStackTrace();
489             System.exit(-1);
490             return 0;
491         }
492     }
493
494     private void addStatusBar() {
495         statusBar = new JLabel("test");
496         statusBar.setBorder(new BevelBorder(BevelBorder.LOWERED));
497         getContentPane().add(BorderLayout.SOUTH,statusBar);
498     }
499
500     // Standalone runner
501     public static void main(String[] args) {
502         final PuzzleApplet a = new PuzzleApplet();
503         JFrame jf = new JFrame("Loading...");
504         jf.getContentPane().setLayout(new BorderLayout());
505         jf.getContentPane().add(a, BorderLayout.CENTER);
506         a.mainWindow=jf;
507         a.init();
508         a.start();
509         jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
510         jf.addWindowListener(new WindowAdapter() {
511             public void windowClosing(WindowEvent e) {
512                 a.stop();
513                 a.destroy();
514             }
515         });
516         jf.setVisible(true);
517     }
518
519     public static class PuzzlePanel extends JPanel {
520
521         private static final long serialVersionUID = 1L;
522         protected BufferedImage backBuffer;
523
524         public PuzzlePanel() {
525             setPreferredSize(new Dimension(100,100));
526             createBackBuffer(100,100, Color.black);
527         }
528
529         public void createBackBuffer(int w, int h, Color bg) {
530             if (w > 0 && h > 0) {
531                 backBuffer = new BufferedImage(w,h, BufferedImage.TYPE_3BYTE_BGR);
532                 Graphics g = backBuffer.createGraphics();
533                 g.setColor(bg);
534                 g.fillRect(0, 0, w, h);
535                 g.dispose();
536             }
537         }
538
539         protected void paintComponent(Graphics g) {
540             g.drawImage(backBuffer, 0, 0, this);
541         }
542     }
543
544     public static class ConfigComponent {
545         public int type;
546         public int configItemPointer;
547         public JComponent component;
548
549         public ConfigComponent(int type, int configItemPointer, JComponent component) {
550             this.type = type;
551             this.configItemPointer = configItemPointer;
552             this.component = component;
553         }
554     }
555
556     public class ConfigDialog extends JDialog {
557
558         private GridBagConstraints gbcLeft = new GridBagConstraints(
559                 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE, 1, 1,
560                 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
561                 new Insets(0, 0, 0, 0), 0, 0);
562         private GridBagConstraints gbcRight = new GridBagConstraints(
563                 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
564                 GridBagConstraints.REMAINDER, 1, 1.0, 0,
565                 GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
566                 new Insets(5, 5, 5, 5), 0, 0);
567         private GridBagConstraints gbcBottom = new GridBagConstraints(
568                 GridBagConstraints.RELATIVE, GridBagConstraints.RELATIVE,
569                 GridBagConstraints.REMAINDER, GridBagConstraints.REMAINDER,
570                 1.0, 1.0, GridBagConstraints.CENTER,
571                 GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0);
572
573         private static final long serialVersionUID = 1L;
574         private List components = new ArrayList();
575
576         public ConfigDialog(JApplet parent, String title) {
577             super(JOptionPane.getFrameForComponent(parent), title, true);
578             getContentPane().setLayout(new GridBagLayout());
579         }
580
581         public void addTextBox(int ptr, String name, String value) {
582             getContentPane().add(new JLabel(name), gbcLeft);
583             JComponent c = new JTextField(value, 25);
584             getContentPane().add(c, gbcRight);
585             components.add(new ConfigComponent(C_STRING, ptr, c));
586         }
587
588
589         public void addCheckBox(int ptr, String name, boolean selected) {
590             JComponent c = new JCheckBox(name, selected);
591             getContentPane().add(c, gbcRight);
592             components.add(new ConfigComponent(C_BOOLEAN, ptr, c));
593         }
594
595         public void addComboBox(int ptr, String name, String values, int selected) {
596             getContentPane().add(new JLabel(name), gbcLeft);
597             StringTokenizer st = new StringTokenizer(values.substring(1), values.substring(0,1));
598             JComboBox c = new JComboBox();
599             c.setEditable(false);
600             while(st.hasMoreTokens())
601                 c.addItem(st.nextToken());
602             c.setSelectedIndex(selected);
603             getContentPane().add(c, gbcRight);
604             components.add(new ConfigComponent(C_CHOICES, ptr, c));
605         }
606
607         public void finish() {
608             JPanel buttons = new JPanel(new GridLayout(1, 2, 5, 5));
609             getContentPane().add(buttons, gbcBottom);
610             JButton b;
611             buttons.add(b=new JButton("OK"));
612             b.addActionListener(new ActionListener() {
613                 public void actionPerformed(ActionEvent e) {
614                     save();
615                     dispose();
616                 }
617             });
618             getRootPane().setDefaultButton(b);
619             buttons.add(b=new JButton("Cancel"));
620             b.addActionListener(new ActionListener() {
621                 public void actionPerformed(ActionEvent e) {
622                     dispose();
623                 }
624             });
625             setDefaultCloseOperation(DISPOSE_ON_CLOSE);
626             pack();
627             setLocationRelativeTo(null);
628             setVisible(true);
629         }
630         private void save() {
631             for (int i = 0; i < components.size(); i++) {
632                 ConfigComponent cc = (ConfigComponent) components.get(i);
633                 switch(cc.type) {
634                 case C_STRING:
635                     JTextField jtf = (JTextField)cc.component;
636                     runtimeCall("jcallback_config_set_string", new int[] {cc.configItemPointer, runtime.strdup(jtf.getText())});
637                     break;
638                 case C_BOOLEAN:
639                     JCheckBox jcb = (JCheckBox)cc.component;
640                     runtimeCall("jcallback_config_set_boolean", new int[] {cc.configItemPointer, jcb.isSelected()?1:0});
641                     break;
642                 case C_CHOICES:
643                     JComboBox jcm = (JComboBox)cc.component;
644                     runtimeCall("jcallback_config_set_choice", new int[] {cc.configItemPointer, jcm.getSelectedIndex()});
645                     break;
646                 }
647             }
648             runtimeCall("jcallback_config_ok", new int[0]);
649         }
650     }
651 }