add the Java Client from M0AZM
[spider.git] / SpiderConsole / src / SpiderCluster.java
1 /**
2  * SpiderCluster - SpiderCluster console plugin.
3  * @author Ian Norton
4  * @verison 1.0 - 20010418.
5  * @see JPanel
6  *
7  * Copyright (C) 2001 Ian Norton.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public Licence as published by
11  * the Free Software Foundation; either version 2 of the Licence, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public Licence for more details.
18  *
19  * You should have received a copy of the GNU General Public Licence
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * Contacting the author :
24  * Ian Norton
25  * i.norton@lancaster.ac.uk
26  * http://www.lancs.ac.uk/~norton/
27  **/
28
29 import javax.swing.*;
30 import javax.swing.text.*;
31 import javax.swing.event.*;
32 import java.awt.*;
33 import java.io.*;
34 import java.awt.event.*;
35 import java.util.Hashtable ;
36 import java.util.Enumeration ;
37
38 // public class Cluster extends Plugin implements Runnable
39 class SpiderCluster extends Plugin implements Runnable
40     {
41     // Name and tip used when creating the tabbed pane.
42     public static final String NAME = "SpiderCluster" ;   
43     public static final String TIP = "Spider DX Cluster Console" ;   
44     
45     // Number of commands to buffer.
46     public static final int CMDBUFFERLINES = 30 ;
47
48     // Number of lines of scrollback to buffer.
49     public static final int SCROLLBUFFERLINES = 100 ;
50
51     public static final boolean DEBUG = false ;
52
53     // Input and output streams for the plugin.
54     private BufferedReader bir ;
55     private PipedOutputStream pos ;
56
57     // User input field.
58     private JTextField tf ;
59
60     private JTextPane jtp ;
61     private Thread t ;
62     private SimpleAttributeSet attr ;
63     private LimitedStyledDocument doc ;
64
65     // Input line scrollback buffer.
66     private CommandBuffer cbuf ;
67
68     // Callsign of the connecting user.
69     private String call ;
70
71     private static final String encoding = "latin1"; // "ISO8859_1";
72
73     /**
74      * Class initialiser.
75      **/
76     public SpiderCluster()
77         {
78         super() ;
79         }   
80
81     /**
82      * Plugin initialiser.
83      * @param PipedInputStream i - Stream to read data from
84      * @param PipedOutputStream o - Stream to write data to
85      **/
86     public void init(PipedInputStream i, PipedOutputStream o)
87         {
88         // Initialise the plugin IO.
89         bir = new BufferedReader(new InputStreamReader(i)) ;
90         pos = o ;
91
92         // Initialise the Scrolling output area.
93         doc = new LimitedStyledDocument(SCROLLBUFFERLINES) ;
94         jtp = new JTextPane(doc) ;
95         jtp.setEditable(false) ;
96         attr = new SimpleAttributeSet() ;
97         StyleConstants.setFontFamily(attr, "Monospaced") ;
98         StyleConstants.setFontSize(attr, 10) ;
99         jtp.setBackground(Color.black) ;
100
101         doc.addDocumentListener(new DocumentListener() {
102             public void insertUpdate(DocumentEvent e) {
103                 jtp.setCaretPosition(doc.getLength()) ;
104                 }
105             public void removeUpdate(DocumentEvent e) { }
106             public void changedUpdate(DocumentEvent e) { }
107             });
108
109         // Initialise the TextField for user input.
110         tf = new JTextField() ;
111         tf.setFont(new Font("Courier", Font.PLAIN, 10)) ;
112         Insets inset = tf.getMargin() ;
113         inset.top = inset.top + 1 ;
114         inset.bottom = inset.bottom + 1 ;
115         tf.setMargin(inset) ;
116         tf.setForeground(Color.white) ;
117         tf.setBackground(Color.black) ;
118         tf.setCaretColor(Color.white) ;
119     
120         // Set the layout manager.
121         this.setLayout(new BorderLayout()) ;
122
123         // Scrollbars for scrolling text area.
124         JScrollPane scrollpane = new JScrollPane(jtp);
125
126         // Add the bits to the panel.
127         this.add(scrollpane, BorderLayout.CENTER);
128         this.add(tf, BorderLayout.SOUTH);
129
130         // Initialise the command buffer.
131         cbuf = new CommandBuffer(CMDBUFFERLINES) ;
132
133         // Action listener stuff.
134         tf.addKeyListener(new KeyAdapter()
135             {
136             public void keyTyped(KeyEvent e)
137                 {
138                 // Enter key
139                 if((e.getID() == KeyEvent.KEY_TYPED) && (e.getKeyChar() == KeyEvent.VK_ENTER))
140                     {
141                     if(DEBUG) System.out.println("Enter Event") ;
142                     send(tf.getText() + '\n') ;
143                     cbuf.addCommand(tf.getText()) ;
144                     tf.setText("") ;
145                     }
146                 }
147             public void keyPressed(KeyEvent e)
148                 {
149                 // UP Arrow
150                 if((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_UP))
151                     {
152                     if(DEBUG) System.out.println("UP Event") ;
153                     tf.setText(cbuf.getPreviousCommand()) ;
154                     tf.setCaretPosition(tf.getText().length()) ;
155                     }
156                 // DOWN Arrow
157                 if((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_DOWN))
158                     {
159                     if(DEBUG) System.out.println("DOWN Event") ;
160                     tf.setText(cbuf.getNextCommand()) ;
161                     tf.setCaretPosition(tf.getText().length()) ;
162                     }
163                 // Escape key
164                 if((e.getID() == KeyEvent.KEY_PRESSED) && (e.getKeyCode() == KeyEvent.VK_ESCAPE))
165                     {
166                     if(DEBUG) System.out.println("ESCAPE Event") ;
167                     tf.setText("") ;                                                                }
168                 }
169             }) ;
170  
171         // Add component listener to focus text field.
172         this.addComponentListener(new ComponentAdapter() {
173             public void componentShown(ComponentEvent e) {
174                 tf.setVisible(true) ;
175                 tf.requestFocus() ;
176                 }
177             });
178         
179         // Init the scrolling thread.
180         t = new Thread(this, "Scrolling thread") ;
181         t.start() ;
182
183         // Prompt for callsign to connect with.
184         while(call == null || call.indexOf(" ") > -1)
185              {
186              call = JOptionPane.showInputDialog("Enter your callsign") ;
187              }
188
189         call = call.toUpperCase() ;
190         } // End of init
191  
192     /**
193      * getTabName - Get the name that this component should show on it's tab
194      * @returns String s - Tab name
195      **/
196  
197     public String getTabName()
198         {                                                                               return NAME ;
199         }
200  
201     /**
202      * getTabTip - Get the tip that this component should show on it's tab
203      * @returns String s - Tab tip
204      **/
205     public String getTabTip()
206         {
207         return TIP ;
208         }
209  
210     /**
211      * getMenu - get the menu to add to the main menu bar.
212      * @returns JMenu
213      **/
214     public JMenu getMenu()
215         {
216         return null ;
217         }                                                                        
218     /**
219      * send - Helper function to send data out to the PipedOutputMUX
220      * @param String s - data to send.
221      **/
222     private void send(String s)
223         {
224         if(DEBUG) System.out.println("Cluster: send got : " + s) ;
225  
226         // If the input has no | in it, prefix I<CALLSIGN>| and send it.
227         if(s.indexOf("|") == -1)
228             {
229             s = "I" + call + "|" + s ;
230             }                                                                    
231         try
232             {
233             // Write the data to the stream.
234             for(int i=0;i<s.length();i++)
235                 {
236                 pos.write(s.charAt(i)) ;
237                 }
238             }
239         catch(IOException ex)
240             {
241             System.out.println("Cluster: IOException on destination stream.") ;
242             System.out.println(ex) ;
243             }
244         }
245  
246     /**
247      * Loop continually checking to see if anything has been written to the
248      * file that is being monitored.
249      */
250     public void run()
251         {
252         String output = new String() ;
253         // Loop continually reading from the input stream
254         while(true)                                                          
255             {
256             
257             try
258                 {
259                 // Read in a line of data (This screws up prompts with no /n)
260                 output = bir.readLine() ;
261                 if(output != null) display(output) ;
262
263                 if(DEBUG) System.out.println("After reading a line.") ;
264                 }
265             catch(IOException ex)
266                 {
267                 System.out.println("SpiderCluster: IOException trying to read.") ;
268                 }
269             } // End of infinate loop.
270         } // End of run.                                                        
271
272     private void display(String s)
273         {
274         // Automatic login - when we see "Conneted to" send the login string.
275         if(s.startsWith("Connected to")) { send("A" + call + "|local\n") ; }
276         
277         // s = s.replace('\a', ' ') ;
278
279         // Get rid of Ctrl-G's in UNICODE.
280         while(s.indexOf("%07") > -1)
281             {
282             StringBuffer sb = new StringBuffer(s) ;
283             sb.delete(s.indexOf("%07"), s.indexOf("%07") + 3) ;
284
285             s = sb.toString() ;
286             }
287
288         // If the line has a | and starts with D, strip off upto and inc the |.
289         if(s.indexOf("|") != -1 && s.charAt(0) == 'D')
290             {
291             s = s.substring(s.indexOf("|") + 1, s.length()) ;
292             }
293         
294         // Find out what colour this needs to be.
295         attr = getAttributes(s) ;
296
297         // Display it in the doc.
298         doc.append(s + "\n", attr) ;
299         }
300
301     /**
302      * getAttributes(String s) - get attributes (i.e. colour) given a string.
303      * @param String s
304      **/
305     private SimpleAttributeSet getAttributes(String s)
306         {
307         SimpleAttributeSet sas = attr ;
308
309         /**
310          # 0 - $foreground, $background
311          # 1 - RED, $background
312          # 2 - BROWN, $background
313          # 3 - GREEN, $background
314          # 4 - CYAN, $background
315          # 5 - BLUE, $background
316          # 6 - MAGENTA, $background
317
318         VHF DX SPOT
319          [ '^DX de [\-A-Z0-9]+:\s+([57][01]\d\d\d\.|\d\d\d\d\d\d+.)', COLOR_PAIR(1) ],
320         PROMPT
321          [ '^G0VGS de GB7MBC', COLOR_PAIR(6) ],
322         DUNNO!
323          [ '^G0VGS de', A_BOLD|COLOR_PAIR(2) ],
324         HF DX SPOT
325          [ '^DX', COLOR_PAIR(5) ],
326         ANNOUNCE
327          [ '^To', COLOR_PAIR(3) ],
328         WWV SPOT
329          [ '^WWV', COLOR_PAIR(4) ],
330         DUNNO! 
331          [ '^[-A-Z0-9]+ de [-A-Z0-9]+ \d\d-\w\w\w-\d\d\d\d \d\d\d\dZ', COLOR_PAIR(0) ],
332         DUNNO! - PROBABLY A TALK
333          [ '^[-A-Z0-9]+ de [-A-Z0-9]+ ', COLOR_PAIR(6) ],
334         WX SPOT
335          [ '^WX', COLOR_PAIR(3) ],
336         NEW MAIL
337          [ '^New mail', A_BOLD|COLOR_PAIR(4) ],
338         USER LOGIN?
339          [ '^User', COLOR_PAIR(2) ],
340         NODE LOGIN?
341          [ '^Node', COLOR_PAIR(2) ],                                  
342          **/
343
344         Hashtable h = new Hashtable() ;
345         h.put("DX de", Color.red) ;  // HF DX
346         h.put(call + " de ", Color.magenta) ;
347         // h.put("DX", Color.blue) ; // VHF/UHF DX
348         h.put("To", Color.green) ;
349         h.put("WWV", Color.cyan) ;
350         h.put("WCY", Color.cyan) ;
351         // h.put("", Color.) ;
352         // h.put("", Color.) ;
353         h.put("WX", Color.green) ;
354         h.put("New mail", Color.cyan) ;
355         //h.put("User", Color.brown) ;
356         //h.put("Node", Color.brown) ;
357         h.put("User", Color.yellow) ;
358         h.put("Node", Color.orange) ;
359         
360         Enumeration e = h.keys() ;
361         
362         while(e.hasMoreElements())
363             {
364             String prefix = (String)e.nextElement() ;
365             if(s.startsWith(prefix))
366                 {
367                 StyleConstants.setForeground(sas, (Color)h.get(prefix)) ;
368                 return sas ;
369                 }
370             }
371
372         StyleConstants.setForeground(sas, Color.white) ;
373         return sas ;
374         }
375     } // End of class.