001    /* 
002     * Copyright (c) Holger Pfaff - http://pfaff.ws
003     *
004     * This software maybe used for any purpose provided the
005     * above copyright notice is retained. It is supplied as is.
006     * No warranty expressed or implied - Use at your own risk.
007     */
008    
009    import java.applet.*;
010    import java.awt.*;
011    import java.awt.event.*;
012    import java.net.*;
013    import java.util.*;
014    
015    /**
016      * (#)Main.java
017      * @author   Holger Pfaff
018      * @version  3.2 19-Mar-2004<br><br> 
019      *
020      * Common class used as base for GUI programs. Clients should implement <code>Base</code>.
021      * Main offers: <br>
022      * - run as applet or standalone program<br>
023      * - transparent handling of program parameter/options<br>
024      * - language/locale handling
025      * - resource/property files handling
026      * - variable depot
027      * - floating applet
028      *
029      * Property files are searched in a up to four level hierachy. 
030      * 
031      * (0) variables (see put())
032      * (1) <module>.properties
033      * (2) <basename>.properties
034      * (3) Main.properties
035      * 
036      * For each hierachy up to three files are loaded:
037      * <name>.properties
038      * <name>_en.properties
039      * <name>_<lang>.properties
040      *
041      * Possible cmdline switches/applet args <br>
042      *
043      * -bg <color>  specify background color @see Awt.getColor()<br>
044      * -fg <color>  specify foreground color @see Awt.getColor()<br>
045      * -bgimage <imgfile>  specify background image<br>
046      * -font <font>  specify font to use e.g. sans-14<br>
047      * -lang <lang> specify language to use e.g. de<br>
048      * -debug <debuglevel> specifi debuglevel @see Debug.setArgs()<br>
049      *
050      *
051      */
052     
053    public class Main extends Applet implements WindowListener,ParserListener {
054      
055      /**
056       * global language - initialized from system default
057       */
058      protected String     lang      = Locale.getDefault().getLanguage();
059      
060      /**
061       * our instance of the client program
062       */
063      protected Base       base      = null;
064      
065      /**
066       * usually the name of the client class
067       */
068      protected String     basename  = null;
069      
070      /**
071       * vectorized cmdline/applet args
072       */
073      protected Vector     baseargs  = null;
074      
075      /**
076       * used for standalone progs or for floating applets
077       */
078      protected Frame      baseframe = new Frame();
079    
080      /**
081       * the current mode (applet or normal prog)
082       */
083      protected boolean    isApplet  = false;
084    
085      /**
086       * the current mode (applet or normal prog)
087       */
088      protected boolean    inFrame   = false;
089    
090      /**
091       * cache for already opened resources
092       */
093      protected Hashtable   rescache = new Hashtable();
094    
095      /**
096       * a four level resource hierachy
097       */
098      protected Hashtable[] restable = new Hashtable[4];
099    
100      /**
101       * Put an object in the var bucket. null keys are ignored -
102       * null valued cause the key to be deleted from bucket
103       *
104       * @param   key   object to use as key.
105       * @param   val   object to use as value.
106       */
107      public void put(String key, Object val) {
108        Debug.debug(this, "key="+key+" val="+val);
109        if(key == null) return;
110        if(val == null) {
111          restable[0].remove(key);
112        } else {
113          restable[0].put(key, val);
114        }
115      }
116      
117      /**
118       * set the module name to use as second level in the 
119       * resource lookup hierachy
120       *
121       * @param   mod   module name to use
122       */
123      public void setModule(String mod) {
124        restable[1] = mod == null ? restable[2] : getHash(mod);
125      }
126      
127      /**
128       * get an Object from our resource hierachy. Return null for error 
129       *
130       * @param   key   the key to use
131       */
132      public Object get(String key) {
133        return get(key, 0);
134      }
135      
136      /**
137       * get an Object from our resource hierachy. Return null for error 
138       *
139       * @param   key   the key to use
140       * @param     s   start at hierachy level s
141       */
142      public Object get(String key, int s) {
143        Object r = null;
144        while(r == null && s < restable.length) {
145          r = restable[s++].get(key);
146        }
147        Debug.debug(this,"key="+key+" r="+r);
148        return r instanceof String ? Parser.replace((String) r, this) : r;
149      }
150        
151      /**
152       * get a String from our resource hierachy. Return null for error 
153       *
154       * @param   key   the key to use
155       */
156      public String getString(String key) {
157        Object result = get(key);
158        return result == null ? null : result.toString();
159      }
160      
161      /**
162       * implement ParserListener 
163       *
164       * @param   error   the error String
165       */
166      public void parseError(String error) {
167                    Awt.error(error, baseframe);
168      }
169      
170      /**
171       * get a hashtable from a resource file 
172       *
173       * @param   name   the basename of the property file
174       */
175            public Hashtable getHash(String name) {
176        if(rescache.containsKey(name) == false) {
177          rescache.put(name, Util.getResourceHash(name, lang));
178        }
179        return (Hashtable) rescache.get(name);
180            }
181      
182      /**
183       * set the default font 
184       *
185       * @param   font   the default font
186       */
187      public void setFont(Font font) {
188        if(font == null) {
189          return;
190        }
191        super.setFont(font);
192        baseframe.setFont(font);
193      }
194      
195      /**
196       * set the default foreground color
197       *
198       * @param   color   the default foreground color
199       */
200      public void setForeground(Color color) {
201        if(color == null) {
202          return;
203        }
204        super.setForeground(color);
205        baseframe.setForeground(color);
206      }
207    
208      /**
209       * set the default background color
210       *
211       * @param   color   the default background color
212       */
213      public void setBackground(Color color) {
214        if(color == null) {
215          return;
216        }
217        super.setBackground(color);
218        baseframe.setBackground(color);
219      }
220      
221      /**
222       * get the current language
223       */
224      public String getLang() {
225        return lang;
226      }
227      
228      /**
229       * are we an applet?
230       */
231      public boolean isApplet() {
232              return isApplet;
233            }
234      
235      /**
236       * are we an applet? Issue msg on failure
237       */
238      protected boolean isAppletCheck() {
239        if(isApplet == false) {
240          Awt.error("Only supported in applet mode", baseframe);
241        }
242              return isApplet;
243            }
244    
245      /**
246       * get our origin hostname (only in applet mode)
247       */
248      public String getHost() {
249        try {
250          return getDocumentBase().getHost();
251        } catch (Exception x) {
252          return null;
253        }
254      }
255      
256      /**
257       * make our applet floating (only in applet mode)
258       */
259      public void transform() {
260        if(isAppletCheck()) {
261          inFrame = !inFrame;
262          removeAll();
263          baseframe.removeAll();
264          baseframe.setVisible(inFrame);
265          if(inFrame) {
266            baseframe.add(BorderLayout.CENTER, base);
267          } else {
268            add(BorderLayout.CENTER, base);
269                            }
270          baseframe.pack();
271                      base.refreshAll();
272                    } 
273      }
274        
275      /**
276       * duplicate our applet by opening a new browser window (only in applet mode)
277       */
278            public void duplicate() {
279              if(isAppletCheck()) {
280                showDocument(getDocumentBase().toString(), "_blank");
281                    }
282            }
283      
284      /**
285       * show an url in a browser window (only in applet mode)
286       *
287       * @param   url   the url
288       * @param   name  the location to use; null = "_self"
289       */
290            public void showDocument(String url, String name) {
291              if(isAppletCheck()) {
292                    try {
293                            getAppletContext().showDocument(new URL(url), name == null ? "_self" : name);
294                            } catch(Exception x) {
295            Debug.debug(this, x);
296                            }
297                    } 
298            }
299    
300      /**
301       * the common part of the init routine
302       */
303      protected void init_() throws Exception {
304    
305        Debug.setArgs(getArg("-debug", ""));   
306        lang = getArg("-lang", lang);
307        
308        restable[0] = getHash("VAR");
309        restable[1] = new Hashtable();
310        restable[2] = getHash(basename);
311        restable[3] = getHash(getClass().getName());
312        
313        if(getString("Font") != null) {
314          setFont(Font.decode(getString("Font")));
315        }
316        setFont(Awt.getFont(getArg("-font", getFont())));
317        
318        setBackground(SystemColor.text);
319        setForeground(SystemColor.textText);
320        setBackground(Awt.getColor(get("Background")));
321        setForeground(Awt.getColor(get("Foreground")));
322        setBackground(Awt.getColor(getArg("-bg", getBackground())));
323        setForeground(Awt.getColor(getArg("-fg", getForeground())));
324                    
325                    // our widgets need some contrast to look good
326        if(getBackground().getRGB() == Color.white.getRGB()) {
327          setBackground(Color.lightGray);
328        }
329    
330        put("HOST", getHost());
331        put("LANG", lang);
332        put("BASENAME", basename);
333        put("TIMEZONE", TimeZone.getDefault().getID());
334        put("TOTALMEM", "" + Runtime.getRuntime().totalMemory() / 1024);
335        put("BACKGROUND", Awt.Color2Esc(getBackground()));
336        put("FOREGROUND", Awt.Color2Esc(getForeground()));
337        
338        String props[] = {"java.vendor", "java.version", "os.name", "os.arch", "os.version"};
339        
340        for(int i = 0; i < props.length; ++i) {
341          put(props[i], System.getProperty(props[i]));
342        }
343        
344        Class  argt[] = {Main.class};
345        Object args[] = {this};
346        
347        base = (Base) Class.forName(basename).getConstructor(argt).newInstance(args);
348        
349        setLayout(new BorderLayout());
350        add(BorderLayout.CENTER, base);
351        
352                    baseframe.setTitle(basename);
353        baseframe.addWindowListener(this);
354        
355        base.setBG(getArg("-bgimage", null));
356                    base.refreshAll();
357      }
358      
359      /**
360       * the applet part of the init routine
361       */
362      public void init() {
363        try {
364          isApplet = true;
365                            baseargs = new StringSplitter(getParameter("args"), " ").toVector();
366          basename = getParameter("app");
367          if(basename == null) {
368            throw new Exception("Parameter 'app' missing");
369          }
370          init_();
371        } catch(Exception x) {
372                      Awt.error(x, null);
373          showStatus(x.toString());
374        }
375      }
376      
377      /**
378       * applet has been stopped -> tell base
379       */
380      public void stop() {
381        if(inFrame) transform();
382        if(base != null) base.stop();
383      }
384      
385      /**
386       * applet has been started -> tell base
387       */
388      public void start() {
389        if(base != null) base.start();
390      }
391      
392      /**
393       * satisfy curious browser
394       */
395      public String getAppletInfo() {
396        return "j-awt(" + basename + ") (c) 1999-2004 H. Pfaff";
397      }
398      
399      /**
400       * satisfy curious browser
401       */
402      public String[][] getParameterInfo() {
403        String pinfo[][] = {
404          {"app",  "String", "name of app"},
405          {"args", "String", "args for app"}};
406        return pinfo;
407      }
408                         
409      /**
410       * main() for standalone programms
411       *
412       * @param   args   commandline args
413       */
414      public static void main(String[] args) {
415        if(args.length > 0) {
416          try { 
417            Main main = new Main();
418            main.basename = args[0];
419                                    main.baseargs = Util.array2Vector(args, 1);
420                                    main.init_();
421            main.start();
422                                    main.baseframe.add(BorderLayout.CENTER, main);
423                                    main.baseframe.setVisible(true);
424            main.baseframe.pack();
425          } catch (Exception x) {
426            Awt.error(x, null);
427            System.exit(1);
428          }
429        } else {
430          Awt.error("Usage: Main <app> [args]", null);
431          System.exit(1);
432        }
433      }
434            
435      /**
436       * fetch arguments from args vector
437       *
438       * @param   name   name of argument
439       * @param   def    value to return on failure
440       */
441      public Object getArg(String name, Object def) {
442        int i = baseargs.indexOf(name) + 1;
443        return i == 0 || i == baseargs.size() ? def : baseargs.elementAt(i);
444      }
445      
446      /**
447       * fetch String arguments from args vector
448       *
449       * @param   name   name of argument
450       * @param   def    value to return on failure
451       */
452      public String getArg(String name, String def) {
453        return (String) getArg(name , (Object) def);
454      }
455      
456      /**
457       * fetch int arguments from args vector
458       *
459       * @param   name   name of argument
460       * @param   def    value to return on failure
461       */
462      public int getArg(String name, int def) {
463              try {
464          return Integer.parseInt(getArg(name, "" + def));
465                    } catch(Exception x) {
466                      return def;
467                    }
468      }
469      
470      /**
471       * fetch boolean arguments from args vector
472       *
473       * @param   name   name of argument
474       */
475      public boolean getArg(String name) {
476        return baseargs.contains(name);
477      }
478      
479      /**
480       * implement java.awt.WindowListener to capture closing events
481       * and close window on request
482       *
483       * @param   e   WindowEvent
484       */
485      public void windowClosing(WindowEvent e) {
486                    if(isApplet == false)   {
487          System.exit(0);
488        }
489        if(inFrame) {
490          transform();
491        }
492      }
493      
494      /**
495       * implement java.awt.WindowListener to capture iconify events
496       * to retransform window on request
497       *
498       * @param   e   WindowEvent
499       */
500      public void windowIconified(WindowEvent e) {
501        if(inFrame) {
502          transform();
503        }
504      }
505      
506      /**
507       * implement java.awt.WindowListener - not used
508       *
509       * @param   e   WindowEvent
510       */
511      public void windowDeiconified(WindowEvent e) {
512      }
513    
514      /**
515       * implement java.awt.WindowListener - not used
516       *
517       * @param   e   WindowEvent
518       */
519      public void windowOpened(WindowEvent e) {
520      }
521    
522      /**
523       * implement java.awt.WindowListener - not used
524       *
525       * @param   e   WindowEvent
526       */
527      public void windowActivated(WindowEvent e) {
528      }
529    
530      /**
531       * implement java.awt.WindowListener - not used
532       *
533       * @param   e   WindowEvent
534       */
535      public void windowDeactivated(WindowEvent e) {
536      }
537    
538      /**
539       * implement java.awt.WindowListener - not used
540       *
541       * @param   e   WindowEvent
542       */
543      public void windowClosed(WindowEvent e) {
544      }
545    };