/* Copyright 1996 Matthew Freedman and University of Washington mattf@cac.washington.edu This program may be freely used, modified, or redistributed for any non-commercial purpose as long as credit is given. This file contains the Controller, CountController, CountSMController, and DelayController classes. */ import java.awt.*; /* This is the parent class for the three types of controllers. */ abstract class Controller extends Panel { /* All controllers share these widgets */ Panel first_row, second_row, third_row; Label main_label; TextField val_text; Scrollbar scroll; String prev_text; int max; GenericController god; /* Our creator */ /* Shadow variable for the val_text widget */ int val; /* Constructor -- set up a single controller box. There are three types. Use a single colum three row grid layout, where each row contains another panel using default centered flow layout. Sub-classes will fill up the rows */ Controller() { setLayout(new GridLayout(3, 1, 0, 0)); add(first_row = new Panel()); add(second_row = new Panel()); add(third_row = new Panel()); /* All sub-classes have a scrollbar in third Row. Create a two-row grid so scrollbar does not take up entire cell. This is really gross. And it is not centered vertically, maybe I should try the GridBagLayout */ third_row.setLayout(new GridLayout(2, 1)); scroll = new Scrollbar(Scrollbar.HORIZONTAL, 1, 10, 0, 100); max = 100; third_row.add(scroll); } /* Return main value */ int value() { return(val); } /* The most generic event handling happens here. Called from subclass's handleEvent. Return true if this event causes a change in main value */ boolean checkEvent(Event e) { String new_text; int new_val; if (e.target == scroll) { val = scroll.getValue(); val_text.setText(String.valueOf(val)); return(true); } /* Handle typing into text */ if (e.target == val_text) { if (e.id == e.KEY_PRESS) { prev_text = val_text.getText(); return(false); } if (e.id == e.KEY_RELEASE) { new_text = val_text.getText(); try { new_val = Integer.parseInt(new_text); } catch (NumberFormatException err) { /* We must allow empty strings for backspace case */ if (new_text.length() > 0) val_text.setText(prev_text); return(false); } if (new_val > max) { val_text.setText(prev_text); return(false); } /* New value checks out ok, so make scrollbar match */ val = new_val; scroll.setValue(val); return(true); } } return(false); } } /* A sub-class for controllers that count, but have a fixed max value */ class CountController extends Controller { Choice mode_choice; Label max_label; /* Shadow variables to track values of widgets */ boolean random; CountController(GenericController god, String label, String init_val) { this.god = god; main_label = new Label(label, Label.RIGHT); main_label.setFont(new Font("Helvetica", Font.BOLD, 14)); first_row.add(main_label); val_text = new TextField(init_val); first_row.add(val_text); mode_choice = new Choice(); mode_choice.addItem("Random"); mode_choice.addItem("Selected"); second_row.add(mode_choice); second_row.add(new Label("max =", Label.RIGHT)); max_label = new Label("2000", Label.LEFT); second_row.add(max_label); } /* Set the values for this controller */ void setValues(boolean force, int val, boolean random, int max) { boolean adjust_scroll = false; if (val > max) val = max; if (force || (val != this.val)) { this.val = val; val_text.setText(String.valueOf(val)); adjust_scroll = true; } if (force || (random != this.random)) { this.random = random; /* Due to netscape bug, most hide and show all choice menus when changing their values or they disappear */ mode_choice.show(false); if (random) mode_choice.select("Random"); else mode_choice.select("Selected"); mode_choice.show(true); } if (force || (max != this.max)) { this.max = max; max_label.setText(String.valueOf(max)); adjust_scroll = true; } if (adjust_scroll) scroll.setValues(val, Math.max(max/10, 1), 1, max); } /* Return whether we are random */ boolean is_ran() { return(random); } /* Event handler, called by system */ public boolean handleEvent(Event e) { /* Handle change of random/selected state by self */ if ((e.target == mode_choice) && (e.id == e.ACTION_EVENT)) { random = ((String) e.arg).equals("Random"); return(false); } /* otherwise see if superclass can do anything with it */ else if (super.checkEvent(e)) { /* Our main value has been changed by user. Force switch into Selected mode */ if (random) { random = false; mode_choice.show(false); mode_choice.select("Selected"); mode_choice.show(true); } /* this change could be important, so notify our creator */ god.interfaceChanged(this); } return(false); } } /* A sub-class for controllers that count, but the user can select the max value. Pass in array of choices for max-val */ class CountSMController extends CountController { Choice upper_choice; CountSMController(GenericController god, String label, String[] max_list, String init_val) { /* Call regular CountController constructor first */ super(god, label, init_val); this.god = god; /* Get rid of the max label, replace with Choice menu */ second_row.remove(max_label); upper_choice = new Choice(); for (int i = 0; i < max_list.length; i++) upper_choice.addItem(max_list[i]); second_row.add(upper_choice); } /* Set the values for this controller. Superclass takes care of everything but max choice widget */ void setValues(boolean force, int val, boolean random, int max) { if (force || (max != this.max)) { upper_choice.show(false); upper_choice.select(String.valueOf(max)); upper_choice.show(true); } super.setValues(force, val, random, max); } /* Return the current max value */ int max() { return(max); } /* Event handler, called by system. We only handle change of max Choice here. Superclasses do everything else */ public boolean handleEvent(Event e) { if ((e.target == upper_choice) && (e.id == e.ACTION_EVENT)) { max = Integer.parseInt((String) e.arg); if (val > max) { val = max; val_text.setText(String.valueOf(val)); } scroll.setValues(val, Math.max(max/10, 1), 1, max); /* this change could be important, so notify our creator */ god.interfaceChanged(this); return(false); } else return(super.handleEvent(e)); } } /* A sub-class for controllers that set a delay value */ class DelayController extends Controller { Checkbox main_toggle; Label delay_label; /* Shadow variables for widgets */ boolean is_set; DelayController(GenericController god, String label, String sub_label, String init_val) { this.god = god; main_toggle = new Checkbox(label, null, true); main_toggle.setFont(new Font("Helvetica", Font.BOLD, 14)); first_row.add(main_toggle); delay_label = new Label(sub_label, Label.RIGHT); second_row.add(delay_label); val_text = new TextField(init_val); second_row.add(val_text); } /* Set the values for this controller */ void setValues(boolean force, int val, boolean is_set) { if (force || (is_set != this.is_set)) { this.is_set = is_set; main_toggle.setState(is_set); /* Hide/show inapplicable widgets if necessary */ second_row.show(is_set); third_row.show(is_set); } if (force || (val != this.val)){ this.val = val; val_text.setText(String.valueOf(val)); scroll.setValue(val); } } /* Return whether toggle is set */ boolean is_set() { return(is_set); } /* Event handler, called by system */ public boolean handleEvent(Event e) { /* see if superclass can do anything with it */ if ((e.target == main_toggle) && (e.id == e.ACTION_EVENT)){ is_set = ((Boolean) e.arg).booleanValue(); /* Hide/show inapplicable widgets if necessary */ second_row.show(is_set); third_row.show(is_set); /* this change could be important, so notify our creator */ god.interfaceChanged(this); } if (super.checkEvent(e)) { /* Our main value has been changed by user */ god.interfaceChanged(this); } return(false); } }