/** * HF Receiver Controller Applet 1.0.0 * @author Robert J Morton YE572246C * @version 25 November 2001, revanped 16 November 2007, Swing Version 31 January 2012 * @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 command 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 javax.swing.*; // the Swing GUI system import java.awt.*; // abstract windowing toolkit import java.io.*; // to handle file input/output operations 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_swing extends JApplet implements Runnable { final hfbrx_swing ap = this; //self reference to this applet static Image IMG1; // for applet background world map picture private imgldr il; // instance reference to the image loader private Color bg = new Color(238,238,238); // background colour off-white boolean JarredImages = true, //true = get the image files from the jar file JarredSearch = true; //true = get search data files from the jar file private boolean ScanFlag = false, // true = automatic scanning in progress loadingFreqs = false, // false = loading stations, // true = loading frequencies StationNamesLoaded = false, // true when all station namesloaded, and FrequenciesLoaded = false, // selected station's frequencies loaded NewSquelch = false, // true = a new squelch level must be displayed /* These flags are set true when the second phase of the build- ing of the GUI has been respectively started and finished. */ Init2started = false, Init2finished = false; private byte B[]; // gigantic byte array to hold the downloaded index data private int MaxFreq = 0, // number for frequencies this station transmits on FreqNum = 0, // list number of the current listening frequency /* Loader's current phase or state: 0=idle 1=connecting 2=loading 3=connecting error 4=loading error */ lp = 0, LP = 1, // a latch variable for the above 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) Aw = 545, Ah = 295, // applet width and height Am = 12, // applet's panel margin or border Bw = 80, Bh = 30, // button width and height Mh = 35, Lh = 25, // 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 + W1 + 8, // 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 Y8 = Y2 + Mh, // y-coord of text under search box W7 = Aw - X2 - Am, // width of the message field y1, // y-origin of the of the text field and choice box titles y3, // y-origins of the three button annotation texts y4, y5, y6, // y-origin of names and values for Station, Frequency and Squelch y7, // y-coord of font base line of message field ns = 0, // total number of stations nf = 0; // total number of frequencies private long si = 1000, // frequency scan interval T, // time at which to step to the next frequency Pn = 505949278; // Pn bias constant private String E, // for Exception during downloading + method where it occurred cb, // code base URL - where this applet's class file came from loadFile = "stations.txt", // name of the station names file SearchString = "", // search string for finding a station name RNE = "V429810-2", // staion data access code on server S[] = new String[1000], // allow up to 1000 stations F[] = new String[1000]; // allow up to 1000 frequencies per station Container cp; // instance reference of the JApplet's pane private worldmap wm; // panel for background image // reference for a text field for station search engine private JTextField FindStn; JComboBox // Reference for a Choice Menu for... stations, // radio station names frequencies, // a station's frequencies squelch; // the squelch levels private JLabel // References for the... ScanLab, // 'frequency-scan' button's label NextLab, // 'Next' button's label PrevLab, // 'Prev' (previous) button's label FindLab, // 'Find' button's label SrchLab, // 'Search' label SvceLab, // 'Broadcasting Service'label FreqLab, // 'Frequencies' label SqchLab, // 'Squelch' label Station; // 'broadcaster or station.' label private JButton // 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 JButton RtryBut; // retry a station names or frequency list load // White panel at bottom ... private stationnamepanel sn; // where station name is shown private frequencypanel sf; // where frequency is shown private squelchpanel sq; // where squelch level is shown private messagespanel sm; // for progress and error messages // 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 and their typeface dimensions. */ private Font font, font2; private FontMetrics fm, fm2; private Thread TH; // a thread reference variable for running this applet public void init() { // URL (less file name) from where this applet came cb = getCodeBase().toString(); /* Workaround for 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); } try { // Attempt Phase 1 of the GUI build javax.swing.SwingUtilities.invokeAndWait( new Runnable() { public void run() { init1(); } } ); } catch(Exception e) { showStatus("couldn't create Swing GUI"); } il = new imgldr(this, sm); // create and start the image loader // set timeout for first cycle of run() thread T = System.currentTimeMillis() + si; } /* THE FIRST PHASE OF INITIALISATION Since this involves the creation and set-up of Swing widgets, it must be done on the Event Dispatching thread. It is invoked from the applet's init() method above. */ private void init1() { // Set a small plain font for Text Field, Choice Menus and Buttons setFont(new Font("Serif",Font.PLAIN,12)); /* Create a large bold font for labels and lettering, get letter dim- ensions and leading for the above font, increase its line spacing slightly and compute its vertical baseline off-set. */ font = new Font("Serif",Font.BOLD,13); fm = getFontMetrics(font); 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-origin 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 an even larger bold font for the applet canvas, get its letter dimensions and compute its vertical baseline off-set. */ 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 cp = getContentPane(); // get the content pane of the JApplet cp.setLayout(null); // allow control panels to be laid out manually /* CREATE TEXT FIELD, MENU AND BUTTON OBJECTS, SIZE THEM AND INSTALL THEM ON APPLET PANEL */ FindStn = new JTextField(); stations = new JComboBox(); frequencies = new JComboBox(); cp.add(FindStn); FindStn.setBounds(X1,Y2,W1,Mh); cp.add(stations); stations.setBounds(X2,Y2,W2,Mh); cp.add(frequencies); frequencies.setBounds(X3,Y2,W3,Mh); FindStn.setBackground(bg); // set background colour of search field // create a Choice Menu for squelch levels squelch = new JComboBox(); /* Set up the 10 menu items for the Squelch Menu. This 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 squelch.setSelectedIndex(5); // Add Squelch Menu to applet pane then set its position and size. cp.add(squelch); squelch.setBounds(X4,Y2,W4,Mh); NewSquelch = true; // indicate that a new squelch setting has been made /* Stop the drop-down lists from eclipsing the message fields. NOTE: frequencies access code 016.815.226-67 is implicit. */ stations.setMaximumRowCount(12); frequencies.setMaximumRowCount(12); squelch.setMaximumRowCount(12); // CREATE LABEL OBJECTS, SIZE THEM AND INSTALL THEM ON APPLET PANEL ScanLab = new JLabel("Automatic Scanning"); NextLab = new JLabel("Listen on next higher frequency."); PrevLab = new JLabel("Listen on next lower frequency."); SrchLab = new JLabel("Search for..."); SvceLab = new JLabel("Station or Broadcasting Service"); FreqLab = new JLabel("Freq (kHz)"); SqchLab = new JLabel("Squelch"); Station = new JLabel("broadcaster
or station."); cp.add(ScanLab); ScanLab.setBounds(X5,Y3,400,Bh); cp.add(NextLab); NextLab.setBounds(X5,Y4,400,Bh); cp.add(PrevLab); PrevLab.setBounds(X5,Y5,400,Bh); cp.add(SrchLab); SrchLab.setBounds(X1,Y1,W1,Lh); cp.add(SvceLab); SvceLab.setBounds(X2,Y1,W2,Lh); cp.add(FreqLab); FreqLab.setBounds(X3,Y1,W3+5,Lh); cp.add(SqchLab); SqchLab.setBounds(X4,Y1,W4,Lh); cp.add(Station); Station.setBounds(X1,Y8,W1,Mh); // CREATE BUTTON OBJECTS, SIZE THEM AND INSTALL THEM ON APPLET PANEL ScanBut = new JButton("Start"); NextBut = new JButton("Higher"); PrevBut = new JButton("Lower"); FindBut = new JButton("Find/Next"); RtryBut = new JButton(""); cp.add(ScanBut); ScanBut.setBounds(X2,Y3,W1,Bh); cp.add(NextBut); NextBut.setBounds(X2,Y4,W1,Bh); cp.add(PrevBut); PrevBut.setBounds(X2,Y5,W1,Bh); cp.add(FindBut); FindBut.setBounds(X1,Y6,W1,Bh); cp.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,ap)); stations.addItemListener(new chlisten(chlisten.STATION,ap)); frequencies.addItemListener(new chlisten(chlisten.FREQUENCY,ap)); squelch.addItemListener(new chlisten(chlisten.SQUELCH,ap)); ScanBut.addActionListener(new btlisten(btlisten.SCANBUT,ap)); NextBut.addActionListener(new btlisten(btlisten.NEXTBUT,ap)); PrevBut.addActionListener(new btlisten(btlisten.PREVBUT,ap)); FindBut.addActionListener(new btlisten(btlisten.FINDBUT,ap)); RtryBut.addActionListener(new btlisten(btlisten.RTRYBUT,ap)); // CREATE PANELS ON WHICH TO DISPLAY DATA AND MESSAGES sn = new stationnamepanel(W2,Bh,y,font2,this,bg); sf = new frequencypanel(W3,Bh,y,font2,this,bg); sq = new squelchpanel(W4,Bh,y,font2,this,bg); sm = new messagespanel(W7,Bh,y,font2,this,bg); cp.add(sn); sn.setBounds(X2,Y6,W2,Bh); cp.add(sf); sf.setBounds(X3,Y6,W3,Bh); cp.add(sq); sq.setBounds(X4,Y6,W4,Bh); cp.add(sm); sm.setBounds(X7,Y7,W7,Bh); } /* SECOND PHASE OF INITIALISATION This can only be done after the background image for the applet has finished downloading from the server. Since it involves the creation of a Swing JPanel, it must be executed by the Event Dispatching thread and not the local run() thread from where the process must be invoked.*/ private void scheduleInit2() { Init2started = true; // show Phase 2 initialisation has been scheduled try { // Attempt to build the map & nav information panels javax.swing.SwingUtilities.invokeAndWait( new Runnable() { public void run() { init2(); } } ); } catch(Exception e) { showStatus("createGUI phase 2 didn't successfully complete"); } } private void init2() { /* Create a panel for the applet' background image, add it to the applet's content pane then set its size to the full size of the applet window. */ wm = new worldmap(this); cp.add(wm); wm.setBounds(0, 0, Aw, Ah); /* This call to repaint() is to make sure that everything else is displayed ON TOP OF the background image; not underneath it. */ repaint(); lp = 1; // start the station names loading process Init2finished = true; // phase 2 of the initialization has finished } /* THE RUN SECTION The local thread is started and entered. The local thread TH runs around the while() loop. Initially, it runs without sleeping long enough to complete the loading of the applet's background image, the list of station names and the first station's frequency list. Once it has completed these tasks it adopts a cycle of si ( = 1000 ) milliseconds during which it does whatever is necessary (in this case scanning the selected station's next frequency) and sleeping for the rest of the cycle. The thread is terminated by the stop() method, which is called by the host environment when the applet is terminated.*/ public void start() {TH = new Thread(this); TH.start();} public void run() { // RUN THE AUXILIARY THREAD if(il.awaitingImages()) return; // if still waiting for images to load // Get the reference of the current thread Thread thisThread = Thread.currentThread(); // if it's our thread, loop while as long as it is alive while(TH == thisThread){ runLoop(); /* Get the amount of time remaining in this scan interval, making sure it is at least 5 milliseconds, then make the thread sleep for this remaining time. */ long s = T - System.currentTimeMillis(); if (s < 50) s = 50; try { Thread.currentThread().sleep(s); } /* Catch any events or exceptions here but do nothing with them, since they are expected. Then set the finishing time of the next scan interval ready for the next pass of the program. */ catch (InterruptedException e) { } T = System.currentTimeMillis() + si; } } private void runLoop() { // re-entered every 'si' milliseconds if(!il.imagesLoaded()) return; // media tracker not yot terminated /* If second phase of initialization not yet finished, then {if second phase of initialisation has not yet started, re- quest for it to be done on event-dispatching thread} return. */ if(!Init2finished) { if(!Init2started) scheduleInit2(); return; } /* if applet is in FREQUENCY SCAN mode and this station's frequencies have all been loaded, go to the next higher frequency in the station's frequencies list. */ if(ScanFlag && FrequenciesLoaded) nextFreq(); /* If a new data download request has been initiated, initiate a conn- ection to the appropriate index resource on server. Else, if we are in a file downloading phase, manage the downloading of its content.*/ if(lp == 1) fileConnect(); else if(lp == 2) fileLoad(); /* If the loader state is not the same as the latch state, then if the loader has now reached its idle state, clear the 'loading' message then latch the current load state so we don't keep on clearing the message each pass. */ if(lp != LP) { if(lp == 0) sm.showMsg("", false); LP = lp; } } public void stop() {TH = null;} // Stop the local run-thread void imgReload() { // Try to reload images. il.killTracker(); il = new imgldr(this,sm); } /* THE DOWNLOADING OF STATION NAMES AND FREQUENCIES Station names are loaded automatically after phase 1 and pahse 2 of the ap- plet's initialisation have completed and the applet's background image has been installed. The frequency list for the first station in the stations list are then downloaded. The frequency list another station is downloaded as and when that other station is selected by the user from the station names Choice menu. The station names loading process comprises 3 phases. fileConnect() estab- lishes an HTTP connection between the applet and the station names file on the server. The fileLoad() then loads the byte stream of the file into a byte array B[]. Once all the bytes of the file are in B[], they are the unravel() method parses the byte stream into the list of station names and puts them in the String array S[]. The installStations() method then copies the station names from S[] into the items list of the station selector JComboBox. This has to be done through an invokation call to the Event Dis- patching thread to avoid upsetting the JComboBox. Essentially the same phases occur in downloading and installing the frequ- ency list for a given station. The only difference is that in this case the first phase is preluded by the setFreqFileName() method, which sets up the file name of the frequency list for the particular station selected. */ // form the file name of the selected station's freq file private void setFreqFileName(int x) { loadFile = "" + x; // form string version of station number /* If it is less than 10, pad it out with 2 leading zeros; else if it is less than 100, pad it with just 1 leading zero. */ if(x < 10) loadFile = "00" + loadFile; else if(x < 100) loadFile = "0" + loadFile; // form the name of the appropriate frequencies file loadFile = "freqs" + loadFile + ".txt"; /* Set the flag to indicate that we are loading the frequencies of the selected station as opposed to the names of all the stations. Show that these frequencies have not yet finished loading and dis- play a message to this effect on the frequencies display panel. */ loadingFreqs = true; FrequenciesLoaded = false; sf.setState(false); sn.setStn(x); // send station name to station display panel lp = 1; // initiate the loading process } // CONNECT TO THE APPROPRIATE DATA FILE ON THE SERVER private void fileConnect() { sm.showMsg("Connecting to server...", false); try { // set to capture any exceptions locally /* If loading from the applet's ".jar" file, create a Class Resource Stream for loading the file from the applet's ".jar" file. Use the known length of the content because this is not easy to find from a ".jar" file. */ if(JarredSearch) { I = getClass().getResourceAsStream(loadFile); L = 4096; } /* Else we must be loading from the remote server, so form the URL of the station data or frequencies file, open a connection to this file, get the length of the remote content (in bytes) and create an input stream object through which to access it. */ else { URL url = new URL(cb + loadFile); URLConnection u = url.openConnection(); L = u.getContentLength(); I = u.getInputStream(); } B = new byte[L]; // create the gigantic buffer for the data l = 0; // number of bytes so far successfully downloaded lp = 2; // advance to the index loading phase } catch(Exception e) { // if any exception at all occurs /* Note the type of exception and where it occurred and display this in an error message in the message field at the bottom of the applet panel. */ E = "fileConnect() " + e; showStatus(E); lp = 3; // set the loader to its unrecoverable error state String s; if(loadingFreqs) s = "Couldn't find this station's frequencies."; else s = "Couldn't find the station names."; sm.showMsg(s,true); } } private void fileLoad() { // DOWNLOAD THE APPROPRIATE DATA FILE if(loadingFreqs) sm.showMsg("Loading frequencies...", false); else sm.showMsg("Loading station names...", false); int k; // current byte being read() try { // set to catch any data transmission error conditions if(JarredSearch) /* While read() hasn't yet hit the current end of the input stream and the entire file has not yet been down- loaded, add its new byte to the big byte array. */ while((k = I.read()) != -1) B[l++] = (byte)k; else /* While we have not yet reached the end of the file AND read() hasn't yet hit the current end of the input stream, add its new byte to the big byte array. */ while(l < L && (k = I.read()) != -1) B[l++] = (byte)k; /* If we are loading from a ".jar" file AND the whole of the file has now been downloaded, close the URL Connection's input stream, set the loader to its 'idle' state and go transfer data from the big byte array into the appropriate Choice menu. */ if(JarredSearch || l >= L) { I.close(); lp = 0; unravel(); } } catch(Exception e) { // catch any data transmission error conditions lp = 4; // set the loader to its loading error state String s; if(loadingFreqs) s = "Couldn't load this station's frequencies."; else s = "Couldn't load station names."; sm.showMsg(s,true); /* Note the type of exception and where it occurred and dis- play this as an error message on browser's status line. */ E = "fileLoad() " + e + " " + L + " " + l; showStatus(E); } } private void unravel() { // TRANSFER LOADED DATA FROM BYTE ARRAY /* If loading frequencies, clear the old frequencies list, otherwise, we are loading station names, so clear the old station names list. */ if(loadingFreqs) nf = 0; else ns = 0; try { // allow the 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; other- wise, the list of station names is being loaded, so add station name to the stations choice list. */ while((s = r.readLine()) != null) { if(loadingFreqs) F[nf++] = s.trim(); else S[ns++] = s.trim(); } /* If just finished loading frequencies, set the number of frequencies for this station. */ if(loadingFreqs) MaxFreq = nf - 1; 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, install the station's frequencies when loaded. Then inform the frequen- cies panel that the station's frequencies are loaded and display the default its frequency on the frequency panel. */ if(loadingFreqs) { invokeInstallFrequencies(); FrequenciesLoaded = true; sf.setState(true); sf.setFreq(0); } /* Otherwise we were loading the list of station names, so install the station names in the stations JComboBox, set the flag to say that the station names have been loaded OK [NOTE: the following call uses the state of this flag.], inform the station name panel that all the station names are loaded and pre-select the default station name. */ else { invokeInstallStations(); StationNamesLoaded = true; sn.setStationNamesLoaded(); setFreqFileName(0); } } /* The following methods copy the station names and frequencies from the arrays S[] and F[] respectively into the Items Lists of the corres- ponding stations/frequencies JComboBoxes. These tasks have to be done on the Event Dispatching thread and not the local run() thread because Swing widgets get upset when they receive action requests on any other than the Event Dispatching thread. */ //Install the staions into their JComboBox via the event-dispatching thread void invokeInstallStations() { try { SwingUtilities.invokeAndWait( new Runnable() { public void run() { installStations(); } } ); } catch (Exception e) { } // can't deal with swing widget errors here } private void installStations() { stations.removeAllItems(); // remove all station names from JComboBox for(int i = 0; i < ns; i++) // install the name of each station stations.addItem(S[i]); // as an item in the JComboBox stations.setSelectedIndex(0); // pre-select first station in the list } /* Install a staion's frequencies into their JComboBox via the event-dispatching thread. */ void invokeInstallFrequencies() { try { SwingUtilities.invokeAndWait( new Runnable() { public void run() { installFrequencies(); } } ); } catch (Exception e) { } // can't deal with swing widget errors here } private void installFrequencies() { frequencies.removeAllItems(); // remove all frequencies from JComboBox for(int i = 0; i < nf; i++) // install each of the station's frequ- frequencies.addItem(F[i]); // encies as an item in the JComboBox frequencies.setSelectedIndex(0); // pre-select first frequency in list } /* USER-INITIATED EVENT HANDLING The following methods handle the various kinds of ewvents that can occur as a result of the user pressing buttons or selecting an item from a Choice menu. */ void selectStation() { // selected broadcast frequency event handler: /* provided all the station names have been loaded, set the number of the selected station within the choice list. */ if(StationNamesLoaded) setFreqFileName(stations.getSelectedIndex()); } void selectFrequency() { // frequency selection event handler if(FrequenciesLoaded) { /* Get the list number of the frequency just sel- ected and send it to the freqency display panel. */ FreqNum = frequencies.getSelectedIndex(); sf.setFreq(FreqNum); /* 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 frequency' 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 = (String)squelch.getSelectedItem(); NewSquelch = true; // trigger display of new squelch level sq.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(!FrequenciesLoaded) // can't do if frequencies return; // are not yet loaded if(ScanFlag) { // If scanning is in progress, FindBut.setText("Find/Next"); // re-display all the button labels NextBut.setText("Higher"); // except for the scanning button PrevBut.setText("Lower"); // and re-display buttons, ScanBut.setText("Start"); // which must be greyed out. ScanFlag = false; // stop the scanning process } else { // otherwise, FindBut.setText(""); // grey out all the button labels NextBut.setText(""); // except for the PrevBut.setText(""); // START/STOP button. ScanBut.setText("Stop"); ScanFlag = true; // set the scan flag to start scanning. } } /* NEXT BUTTON: provided automatic scanning is not on, step to next frequency in the station's frequency list. */ void pressNextBut() { if(!ScanFlag) nextFreq(); } void pressPrevBut() { // PREV BUTTON if(!FrequenciesLoaded) // can't do if loader return; // has not yet finished if(!ScanFlag) { // do nothing if automatic scanning is on if(--FreqNum < 0) // Decrement the frequency and FreqNum = MaxFreq; // loop back to maximum if undershoot. // Select the new listening frequency and frequencies.setSelectedIndex(FreqNum); sf.setFreq(FreqNum); // send it to the freqency display panel. } } // RE-TRY BUTTON TO ATTEMPT A RELOAD AFTER AN ERROR void pressRtryBut() { if(LP > 2) { // If there was a connecting or loading error, RtryBut.setText(""); // kill the label of the retry button lp = 1; // re-initiate the loading sequence LP = 1; } } // [error conditions tested and found to be working correctly] // STEP TO THE NEXT FREQUENCY IN THE STATION'S FREQUENCY LIST private void nextFreq() { if(!FrequenciesLoaded) // can't do if frequencies return; // not yet loaded if(++FreqNum > MaxFreq) // increment frequency list number, FreqNum = 0; // loop back to zero if overshot sf.setFreq(FreqNum); // send frequency to freqency display panel } void findStation() { // start/resume search from SearchIndex value if(!ScanFlag) { // provided we're not in Scan Mode // Get the search string that is currently in the text field. String s = FindStn.getText().trim().toLowerCase(); if(!SearchString.equals(s)) { // If different from last time's search SearchString = s; // string, update search string and SearchIndex = 0; // reset search index to start of list. } int I = stations.getItemCount(), // number of station names in the list i = SearchIndex; // item after where we got to last time /* for as many stations as there are in the list, get the next station name and convert it to all lower case. */ for(int k = 0; k < I; k++) { s = (String)stations.getItemAt(i); s = s.toLowerCase(); // If the search string is found in this station name: if(s.indexOf(SearchString) != -1) { stations.setSelectedIndex(i); // select the station name, mark SearchIndex = i + 1; // from where to start search next showStatus(""); // time, clear possible 'not found' selectStation(); // message from last time, select return; // the station [see above] and exit. } else if(++i >= I) // Else, advance station names index. i = 0; // loop back to beginning if over- } // shot end of station names index. showStatus("Search string not found in any station name."); } } } /* 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_swing ap; // application that called: always the above applet! /* constructor for a new Choice selection event: set id number for this instance of 'chlisten' set reference to the instance of 'hfbrx' applet from which it came. (there will only be one instance). */ public chlisten(int id, hfbrx_swing ap) { this.id = id; this.ap = ap; } // a Choice selection has occurred from checkbox 'id' public void itemStateChanged(ItemEvent e) { switch(id) { // one of the following was selected: case STATION: ap.selectStation(); break; // a station case FREQUENCY: ap.selectFrequency(); break; // a frequency case SQUELCH: ap.selectSquelch(); break; // a squelch level } } } // 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 protected by YE572246C hfbrx_swing ap; // the application that called: always the above applet! // constructor for a new command public btlisten(int id, hfbrx_swing ap) { this.id = id; this.ap = ap; } public void actionPerformed(ActionEvent e) { switch(id) { // id of the instance of ActionEvent pertaining to 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 } } }