/** * HF Receiver Controller Applet 1.0.0 * @author Robert J Morton YE572246C * @version 25 November 2001 revanped 16 November 2007 * @copyright Robert J Morton (all rights reserved) */ /* This applet conforms to API 1.1 It does the following: INITIALISATION SEQUENCE 1. Loads a file full of the names of lots of HF broadcasting services and stations from a file located on the server, and places them into a stations Choice list. 2. Selects the first one in the list and loads the list of frequencies on which this station broadcasts into a frequency Choice list. 3. Sets up the standard set of squelch levels S0 to S9+ in a squelch Choice list and selects S5 as the default. 4. Sends the default frequency and squelch level back to the server to be POSTed to a receiver whose command interface is connected to the server PC's AUX port. 5. Awaits user input. USER-INITIATED ACTIONS 1. User can select another station or broadcasting service from the list. The new station's list of frequencies is then loaded from the server into the frequencies Choice list. New station's first frequency is then sent automatically to the receiver as a re-tuning command. 2. User can select a frequency from the frequency Choice list. This frequ- ency is then sent automatically to the receiver as a re-tuning command. 3. User can set the receiver scanning through the current station's frequencies at the rate of one per second. It stops scanning when the receiver detects a signal of a strength equal to or greater than the currently selected squelch level. The user can press the scan Start button again to resume scanning round the frequencies list. 4. User can step up and down the frequency list manually by pressing the Higher and Lower buttons. Each new frequency is sent as a tuning comm- and to the receiver. 5. User can enter a word or phrase and search for it within all station names in the list of stations or braodcasting services. If found, the first station's name is selected and its broadcasting frequencies loaded. Pressing the Find/Next button again proceeds with the search onwards down the list of stations. Appropriate code has to be inserted into this applet where indicated to construct and POST receiver command messages of the type required for your receiver and its control interface software. Your server needs to know where to POST these messages. */ import java.awt.*; import java.io.*; import java.applet.*; import java.awt.event.*; // for the new-fangled 1.1 event handling import java.net.*; // for downloading data from the remote server public class hfbrx extends Applet implements Runnable { private boolean // false = not yet added to applet panel: stationsAdded = false, // staions Choice men frequenciesAdded = false, // frequencies Choice menu StationNamesLoaded = false, // station names not yet loaded loadingFreqs = false, // loading stations, true = loading frequencies ScanFlag = false, // automatic scanning not yet in progress NewFreq = false, // new frequency must be displayed when true NewStn = false, // new station must be displayed when true NewSquelch = false, // new squelch level must be displayed when true showMsg = false; // true to update() progress and error messages // The selected station's frequencies have not yet been loaded FrequenciesLoaded = false, // To stop panel flicker as items are added to station names PanelCompleted = false, private byte B[]; // gigantic byte array to hold the downloaded index data private int MaxFreq = 0, // max list number for frequs this station transmits on FreqNum = 0, // list number of the current listening frequency // 0=idle 1=connecting 2=loading 3=connecting error 4=loading error lp = 0, LP = 1, L = 0, // length of the remote item being loaded l = 0, // number of bytes of the above successfully downloaded SearchIndex = 0, // list number of station name we have searched up to fh, Fh, // font heights and extended font heights (leading) m, // index of the progress and error messages array M[] Aw = 560, Ah = 300, Am = 12, // applet width and height, plus margin Bw = 80, Bh = 30, // button width and height Mh = 35, // menu height for Choice menus and Text Field W1 = 100, // width of Station Search field W2 = 245, // width of Station Name Selector W3 = 80, // width of frequency selector W4 = 60, // width of Squelch selector W5 = Aw - W1 - Am - Am, // width of button label annotation field X1 = Am, // first column (Station Search) X2 = X1 + W1 + Am, // second column (Station Name Selector) X3 = X2 + W2 + Am, // third column (Frequency) X4 = X3 + W3 + Am, // fourth column (Squelch) X5 = X2 + Bw + 5, // column 2a (button annotations) X7 = X2, // x-coord of start of message field Y1 = Am, // top row (menu annotations) Y2 = Y1 + Am + Am, // Menus row Y3 = Y2 + Mh + Am, // Start button Y4 = Y3 + Bh + Am, // Higher button Y5 = Y4 + Bh + Am, // Lower button Y6 = Y5 + Bh + Am, // Find/Next button Y7 = Y6 + Bh + Am, // y-coord of start of message field W7 = Aw - X2 - Am, // width of the message field y1, // y-origin of the of the text field and choice box titles y3, y4, y5, // y-origins of the button annotation texts y6, // y-origin of names and values for Station, Frequency and Squelch y7; // y-coord of font base line of message field private long si = 1000, // frequency scan interval T; // time at which to step to the next frequency private String loadFile = "stations.txt", // name of the station names file SearchString = "", // search string for finding a station name E, // for Exception during downloading + method where it occurred cb, // code base URL - where this applet's class file came from /* Progress and error messages: The error conditions have been tested. The station names file and a frequencies file were renamed to have false names. The correct error messages was displayed. The files were then re- named back to their correct names and the Retry button was then pressed. The station names or frequencies were then loaded correctly. */ M[] = { "", "Connecting to server...", "Loading station names...", "Loading this station's frequencies...", "Couldn't find station names.", "Couldn't find this station's frequencies.", "Couldn't load station names.", "Couldn't load this station's frequencies." }, A[] = { "Find station", "whose name", "contains the", "entered word", "or phrase." }; // reference for a text field for station search engine private TextField FindStn; private Choice // Reference for a choice menu for ... stations, // radio station names frequencies, // a station's frequencies squelch; // the squelch levels private Label // Reference for the ... ScanLab, // 'frequency-scan' button's label NextLab, // 'Next' button's label PrevLab, // 'Prev' (previous) button's label FindLab; // 'Find' button's label private Button // Button to ... ScanBut, // start/stop frequency scanning for current station NextBut, // move to next frequency in a stations frequency list PrevBut, // move to prev frequency in a stations frequency list FindBut, // go to the next frequency in the list RtryBut; // retry a station names or frequency list load // for reading in station and frequency lists from files private BufferedReader r; // input stream for downloading index of current HTML file private InputStream I; /* references for large and small fonts used in this applet for their typeface dimensions and leading. */ private Font font, font2; private FontMetrics fm, fm2; private Thread TH; // a thread reference variable for running this applet // background colour that shows up both black and white lettering private Color bg = new Color(215,215,220); public void init() { // URL (less file name) from where this applet came cb = getCodeBase().toString(); /* Workaround Hotjava & Microsoft intranet machine names where getCodeBase() wrongly returns the document base (cira Nov 1999). */ if(!cb.endsWith("/")) { int x = cb.lastIndexOf('/'); if(x != -1) cb = cb.substring(0, x + 1); } setBackground(bg); // set background colour of applet /* Create a type font for labels lettering, get its dimensions and set it for use in the Text Field, Choice Menus and Buttons. */ font = new Font("Serif",Font.BOLD,12); fm = getFontMetrics(font); setFont(font); /* Get the font's leading, to increase the line spacing slightly and compute its vertical base line offset. */ fh = fm.getHeight(); Fh = fh + 2; int y = fm.getAscent() + (Bh - fh)/2; y1 = Y1 + y; // y-origin of the text field and choice menu titles y3 = Y3 + y; // y-origins of the auto scan button's annotation text y4 = Y4 + y; // y-origin of the 'next' button's annotation text y5 = Y5 + y; // y-origin of the 'prev' button's annotation text /* Create a second font for use on the applet canvas. Get its letter dimensions etc., compute its vertical base line offset */ font2 = new Font("Serif",Font.BOLD,14); fm2 = getFontMetrics(font); y = fm2.getAscent() + (Bh - fm2.getHeight()) / 2; y6 = Y6 + y; // y origin of the data display text y7 = Y7 + y; // y origin of the data display text setLayout(null); // to allow the control panels to be laid out manually /* CREATE THE TEXT FIELD, MENU AND BUTTON OBJECTS, SIZE THEM AND INSTALL THEM ON APPLET PANEL */ FindStn = new TextField(); add(FindStn); FindStn.setBounds(X1,Y2,W1,Mh); stations = new Choice(); frequencies = new Choice(); squelch = new Choice(); // create a new Choice Menu for squelch levels // Set up the 10 menu items. Must be done before adding menu to applet. for(int i = 0; i < 10; i++) squelch.addItem("S" + i); /* Set default squench level to 'S5' signal strength, add its choice menu to the applet panel, set the size of the area occuped by the squelch choice menu and set the flag to indicate that a new squelch setting has been made. */ squelch.select(5); add(squelch); squelch.setBounds(X4,Y2,W4,Mh); NewSquelch = true; // CREATE THE BUTTON OBJECTS, SIZE THEM AND INSTALL THEM ON APPLET PANEL ScanBut = new Button("Start"); NextBut = new Button("Higher"); PrevBut = new Button("Lower"); FindBut = new Button("Find/Next"); RtryBut = new Button(""); add(ScanBut); ScanBut.setBounds(X2, Y3, Bw, Bh); add(NextBut); NextBut.setBounds(X2, Y4, Bw, Bh); add(PrevBut); PrevBut.setBounds(X2, Y5, Bw, Bh); add(FindBut); FindBut.setBounds(X1, Y6, W1, Bh); add(RtryBut); RtryBut.setBounds(X1, Y7, W1, Bh); //CREATE AND REGISTER EVENT LISTENERS FOR EACH OF THE ABOVE OBJECTS FindStn.addActionListener(new btlisten(btlisten.FINDSTN, this)); stations.addItemListener(new chlisten(chlisten.STATION, this)); frequencies.addItemListener(new chlisten(chlisten.FREQUENCY, this)); squelch.addItemListener(new chlisten(chlisten.SQUELCH, this)); ScanBut.addActionListener(new btlisten(btlisten.SCANBUT, this)); NextBut.addActionListener(new btlisten(btlisten.NEXTBUT, this)); PrevBut.addActionListener(new btlisten(btlisten.PREVBUT, this)); FindBut.addActionListener(new btlisten(btlisten.FINDBUT, this)); RtryBut.addActionListener(new btlisten(btlisten.RTRYBUT, this)); lp = 1; // start the station names loading process PanelCompleted = true; // means that applet initialization is finished } // DISPLAY ON INITIALIZATION OR WHENEVER THE APPLET BECOMES UNECLIPSED public void paint(Graphics g) { g.setFont(font); // small font for general annotations g.setColor(Color.black); // print the following on black g.drawString("Search",X1,y1); g.drawString("Broadcasting Service",X2,y1); g.drawString("Freq (kHz)",X3,y1); g.drawString("Squelch",X4,y1); g.drawString("Automatic Frequency Scan",X5,y3); g.drawString("Listen on next higher frequency.",X5,y4); g.drawString("Listen on next lower frequency.",X5,y5); // display the word/phrase search instructions for(int i = 0,h = 3;i < 5;i++) { g.drawString(A[i],X1,Y3 + h); h += fh; } // Re-display: showStn(g); // station name when an overlaid window removed showFreq(g); // frequency when an overlaid window removed showSquelch(g); // the squelch setting showMessage(g); // any progress or error message } // UPDATE THE CONFIRMATION FIELDS SENT BY THE RECEIVER public void update(Graphics g) { if(NewFreq) { // If a new frequency has just been selected, showFreq(g); // re-display the frequency field and NewFreq = false; // set flag to indicate that this has been done. } if(NewStn) { // If a new station has just been selected, showStn(g); // re-display the station name and NewStn = false; // set flag to indicate that this has been done. } if(NewSquelch) { // If a new squelch level has just been selected, showSquelch(g); // re-display the squelch level and NewSquelch = false; // set flag to indicate that this has been done. } if(showMsg) { // If a new progress or error condition has occurred, showMessage(g); // re-display the progress/error message field and showMsg = false; // set flag to indicate that this has been done and } // doesn't need doing again. } // (RE)DISPLAY THE STATION NAME CONFIRMATION FIELD private void showStn(Graphics g) { /* Provided station names download is complete, clear the field by filling it with the background colour then use the big bold font to display confirmation field in black lettering. */ if(StationNamesLoaded) { g.setColor(getBackground()); g.fillRect(X2,Y6,W2,Bh); g.setFont(font2); g.setColor(Color.black); g.drawString(stations.getSelectedItem(),X2,y6); } } // (RE)DISPLAY THE FREQUENCY CONFIRMATION FIELD private void showFreq(Graphics g) { /* Provided frequencies download is complete, clear the field by filling it with background colour then use the big bold font to display the confirmation field in black lettering. */ if(FrequenciesLoaded) { g.setColor(getBackground()); g.fillRect(X3,Y6,W3,Bh); g.setFont(font2); g.setColor(Color.black); g.drawString(frequencies.getSelectedItem(),X3,y6); } } // (RE)DISPLAY THE SQUELCH-LEVEL CONFORMATION FIELD private void showSquelch(Graphics g) { /* Clear the field by filling it with background colour, then, use the big bold font display the confirmation field in black lettering. */ g.setColor(getBackground()); g.fillRect(X4,Y6,W4,Bh); g.setFont(font2); g.setColor(Color.black); g.drawString(squelch.getSelectedItem(),X4,y6); } // (RE)DISPLAY THE PROGRESS/ERROR MESSAGE FIELD private void showMessage(Graphics g) { g.setColor(getBackground()); // Clear the field by filling it g.fillRect(X7,Y7,W7,Bh); // with the background colour. g.setFont(font2); // use the big bold font if(m == 0) // If loading has finished completely, RtryBut.setLabel(""); // kill the label on the 'retry' button, else if(m < 4) { // else if still loading, g.setColor(Color.black); // display the progress message g.drawString(M[m],X7,y7); // in black lettering. } else { // Otherwise it must be an error condition, RtryBut.setLabel("Retry"); // so change the label on the retry button g.setColor(Color.red); // to "Retry" and display error g.drawString(M[m],X7,y7); // message in red lettering. } } void selectStation() { // broadcast selected event handler // provided all the station names have been loaded if(StationNamesLoaded) { frequencies.removeAll(); // clear the frequencies list // number of the selected station within the choice list int x = stations.getSelectedIndex(); loadFile = "" + x; // form string version of station number if(x < 10) // If it is less than 10, loadFile = "00" + loadFile; // pad it out with 2 leading zeros else if(x < 100) // else if it is less than 100, loadFile = "0" + loadFile; // pad it with just 1 leading zero. // form the name of the appropriate frequencies file loadFile = "freqs" + loadFile + ".txt"; //indicate that the selected station's frequencies are not yet loaded FrequenciesLoaded = false; loadingFreqs = true; // state type of file being loaded lp = 1; // start the loading process NewStn = true; // trigger display of new station name } } void selectFrequency() { //frequency selection event handler // note the list number of the frequency just selected FreqNum = frequencies.getSelectedIndex(); NewFreq = true; // trigger display of new selected frequency repaint(); // display new frequency /* Here is where you construct and send an HTTP 'POST' message and send it back to your HTTPD server, telling it to send a 'tune to this frequ- ency' command to your physical receiver. Your HTTPD server will have to know where to POST this type of message on to. I wrote my web server in Java, so I simply added a class file to construct and send receiver commands via my PC's AUX port. */ } void selectSquelch() { //SQUELCH SELECTION EVENT HANDLER // get the selected squelch level S0, S1, S2, ... String SquelchLevel = squelch.getSelectedItem(); NewSquelch = true; // trigger display of new squelch level repaint(); // display new squelch level /* Here is where you construct and send an HTTP 'POST' message and send it back to your HTTPD server, telling it to send a 'set this squelch level' command to your physical receiver. Your HTTPD server will have to know where to POST this type of message on to. I wrote my web server in Java, so I simply added a class file to construct and send receiver commands via my PC's AUX port. */ } void pressScanBut() { // WHEN THE SCAN BUTTON IS PRESSED if(ScanFlag) { // If scanning is in progress, FindBut.setLabel("Find/Next"); // re-display all the button labels, NextBut.setLabel("Higher"); PrevBut.setLabel("Lower"); ScanBut.setLabel("Start"); ScanFlag = false; // kill the scanning process } else { // else, scanning is not in progress FindBut.setLabel(""); // so clear all the button labels NextBut.setLabel(""); revBut.setLabel(""); ScanBut.setLabel("Stop"); // re-label START button as STOP button ScanFlag = true; // start the scanning process } } void pressNextBut() { // NEXT BUTTON PRESSED: if(!ScanFlag) // provided automatic scanning is not on, nextFreq(); // step to next frequency in station's frequency list. } void pressPrevBut() { // PREV BUTTON PRESSED: if(!ScanFlag) { // Provided automatic scanning is not on, if(--FreqNum < 0) // decrement frequency number FreqNum = MaxFreq; // and loop back to max if overshot. frequencies.select(FreqNum); // select the new listening frequency repaint(); // sets up a call to update() NewFreq = true; // new frequency must be displayed } } void pressRtryBut() { // RE-TRY BUTTON TO ATTEMPT A RELOAD AFTER AN ERROR if(LP > 2) { // if there was a connecting or loading error RtryBut.setLabel(""); // kill the label of the retry button m = 0; showMsg = true; // and wipe the error message. repaint(); lp = 1; LP = 1; // re-initiate the loading sequence } } // error conditions tested and found to be working correctly // STEP TO THE NEXT FREQUENCY IN THE STATION'S FREQUENCY LIST private void nextFreq() { if(++FreqNum > MaxFreq) // increment frequency number FreqNum = 0; // loop back to zero if overshot frequencies.select(FreqNum); // select the new listening frequency repaint(); // sets up a call to update() NewFreq = true; // new frequency must be displayed } // START/RESUME SEARCH FROM CURRENT SearchIndex VALUE void findStation() { if(!ScanFlag) { //get THE search string THAT IS currently in the text field String s = FindStn.getText().trim().toLowerCase(); // If it is different from last time's search string, if(!SearchString.equals(s)) { SearchString = s; // update the search string and SearchIndex = 0; // reset search index to start of list. } int I = stations.getItemCount(), // number of station names in list i = SearchIndex; // item after where we got to last time // for as many stations as there are in the list for(int k = 0; k < I; k++) { s = stations.getItem(i).toLowerCase(); // get [next] station name // If search string found in this station name,/ if(s.indexOf(SearchString) != -1) { stations.select(i); // select station name, mark from SearchIndex = i + 1; // where to start searching next time, showStatus(""); // clear possible 'not found' message from selectStation(); // last time, then select the station. return; } else if(++i >= I) // otherwise go try the next name i = 0; } showStatus("Search string not found in any station name."); } } // CONNECT TO THE APPROPRIATE DATA FILE ON THE SERVER private void fileConnect() { try { // set to capture any exceptions locally /* Form the URL of the 'station data' file then use it to open a connection to the remote file. */ URL url = new URL(cb + loadFile); URLConnection u = url.openConnection(); /* Get the length of the remote file (in bytes), set the number of bytes so far successfully downloaded to zero and create an input stream object through which to access the file. */ L = u.getContentLength(); l = 0; I = u.getInputStream(); /* Create a gigantic byte array in which to put the received data and advance to the index loading phase. */ B = new byte[L]; lp = 2; } /* Catch any exception that may occur during the above 'try' and display a mesage showing the type of exception and where it occurred. Then set the loader to State 3: unrecoverable connect- ing error. */ catch(Exception e) { E = "fileConnect() " + e; showStatus(E); lp = 3; } } private void fileLoad() { // DOWNLOAD THE APPROPRIATE DATA FILE int k; //current byte being read() try { /* While read() hasn't hit the current end of the input stream and the entire file has not yet been downloaded add its new byte to the array big byte array. */ while(l < L && (k = I.read()) != -1) B[l++] = (byte)k; if(l >= L) { // If the whole of the index has now been downloaded, I.close(); // close the URL Connection's input stream, lp = 0; // put this loader program into its 'idle' state then unravel(); // go transfer data from the giant byte array into the } // appropriate Choice menu. } // Catch any exception that may occur during the above 'try' catch(Exception e) { E = "fileLoad() " // display a mesage + e // showing the type of exception + L + " " // length of item being loaded + l; // and where the error occurred lp = 4; // set the loader to State 4: showStatus(E); // unrecoverable loading error. } } private void unravel() { // TRANSFER LOADED DATA FROM BYTE ARRAY try { // allow WHILE loop to be interrupted by external events /* Set up a means of reading station data from the giant byte array line-by-line. */ r = new BufferedReader( new InputStreamReader( new ByteArrayInputStream(B) ) ); String s; // string to accommodate [next] line of text /* While there are still lines of text in the giant byte array, read the next line. If loading a station's 'list of frequencies', add the frequency to current station's frequencies list; otherwise, the list of station names is being loaded, so add station name to the stations choice list. */ while((s = r.readLine()) != null) { if(loadingFreqs) frequencies.addItem(s.trim()); else stations.addItem(s.trim()); } r.close(); // close the byte array reader } catch(Exception e) { } // ignore any errors; it's local B = null; // garbage the byte array to release memory /* If loading a station's list of frequencies, get the total num- ber of frequencies for this station, reset to first frequency in the list and signal that this station's frequencies are loaded. */ if(loadingFreqs) { MaxFreq = frequencies.getItemCount() - 1; FreqNum = 0; FrequenciesLoaded = true; /* If the frequencies Choice Menu has not yet been added, add it to the applet panel, then set its position and dimensions. */ if(!frequenciesAdded) { add(frequencies); frequencies.setBounds(X3,Y2,W3,Mh); /* Set flag to signal that the frequencies Choice Menu has now been added so that it does not get added again when loading the frequency lists of subsequently selected stations.*/ frequenciesAdded = true; } NewFreq = true; // display the new frequency in white repaint(); // at bottom of the applet panel } else { // Else the list of station names is being loaded. /* If the stations Choice menu has not yet been added, add it to the applet panel then set its position and dimensions. Set flag to indicate that the stations Choice menu has now been added to the applet panel. */ if(!stationsAdded) { add(stations); stations.setBounds(X2,Y2,W2,Mh); stationsAdded = true; } /* NOTE: the call to selectStation() [which pre-selects the default station name] uses the state of StationNamesLoaded flag. */ StationNamesLoaded = true; selectStation(); } } public void run() { // RUN THE AUXILIARY THREAD // get the reference of the current thread Thread thisThread = Thread.currentThread(); /* While the current thread is our thread, remain in the while-loop for as long as this thread is alive. */ while(TH == thisThread){ // If all the GUI components have been built and added to the applet if(PanelCompleted) { /* If the applet is in FREQUENCY SCAN mode and this station's frequencies are loaded, then step to the next frequency in the frequencies list of the currently selected station. */ if(ScanFlag && FrequenciesLoaded) nextFreq(); /* If the loader is currently in its connecting phase or down- loading phase, go process the respective phase. Note: the program will drop straight through the switch if the loader is idle or in an error state.*/ switch(lp) { case 1: fileConnect(); break; case 2: fileLoad(); } /* If the loading of all station data is complete, make the initial setting for the message index 'm' the current loader phase 'lp' then adjust the message index 'm' according to whether we are loading station names or frequencies. The message index 'm' is used in repaint() [see below]. */ if(lp != LP) { m = lp; // initial setting for the message index switch(lp) { case 2: if(loadingFreqs) m++; break; case 3: m++; if(loadingFreqs) m++; break; case 4: m += 2; if(loadingFreqs) m++; } LP = lp; // latch the current load state showMsg = true; // display the appropriate message repaint(); } } /* Get the amount of time remaing of the present time frame 'T'. If it is less than 5 milliseconds, make it 5 milliseconds. Then put the thread to sleep for this remaining time. */ long s = T - System.currentTimeMillis(); if (s < 5) s = 5; try { Thread.currentThread().sleep(s); } /* Catch any exceptions, such as browser interruptions, but do nothing about then since they are expected normal events. Then set up the finishing time 'T' of the next time frame for testing next time through. */ catch (InterruptedException e) { } T = System.currentTimeMillis() + si; } // end of the while-loop } // THE START AND STOP METHODS FOR THE AUXILIARY THREAD public void start() { TH = new Thread(this); TH.start(); } public void stop() { TH = null; } } /* The following classes create instances of ActionListener. They used by this applet to create and service action events from the Text Field, the Choice Menus and each of the Control Buttons. The underlying system calls the actionPerformed() method below every time a button is pushed. The method is invoked upon the instance of the following class corresponding to that particular button. The value of the variable id at the time is therefore that which corresponds to the button concerned. This causes the appropriate case statement to be executed, thus passing control to the appropriate handling method above. */ // LISTENS FOR EVENTS FROM THE CHOICE MENU SELECTORS class chlisten implements ItemListener { static final int STATION = 0, // an event from the 'Stations' Choice Menu FREQUENCY = 1, // an event from the 'Frequency' Choice Menu SQUELCH = 2; // an event from the 'Squelch' Choice Menu int id; // one of the above events hfbrx ap; // application that called: always the above applet! /* constructor for a new Choice selection event: set the id number for this instance of 'chlisten' and the reference to the instance of the 'hfbrx' applet from which it came. (there will only be one instance). */ public chlisten(int id, hfbrx ap) { this.id = id; this.ap = ap; } // a Choice selection has occurred from checkbox 'id' public void itemStateChanged(ItemEvent e) { switch(id) { /* Execute method in above applet which deals with the Choice selection that invoked this instance of chlisten. */ case STATION: ap.selectStation(); break; case FREQUENCY: ap.selectFrequency(); break; case SQUELCH: ap.selectSquelch(); break; } } } // LISTENS FOR EVENTS FROM THE BUTTONS class btlisten implements ActionListener { static final int SCANBUT = 0, // 'scan button pressed' event NEXTBUT = 1, // 'next button pressed' event PREVBUT = 2, // 'prev button pressed' event FINDSTN = 3, // 'station search text field' event FINDBUT = 4, // 'station search button pressed' event RTRYBUT = 5; // 'station search button pressed' event int id; // one of the above hfbrx ap; // the application that called: always the above applet! // constructor for a new command public btlisten(int id, hfbrx ap) { this.id = id; this.ap = ap; } // one of the buttons has been clicked public void actionPerformed(ActionEvent e) { switch(id) { // id of this instance of ActionEvent: it was the: case SCANBUT: ap.pressScanBut(); break; // 'scan' button case NEXTBUT: ap.pressNextBut(); break; // 'next' button case PREVBUT: ap.pressPrevBut(); break; // 'prev' button case FINDSTN: ap.findStation(); break; // 'find' field C/R case FINDBUT: ap.findStation(); break; // 'find' button case RTRYBUT: ap.pressRtryBut(); break; // 'retry' button } } }