9fe7195bfb2836a8d0d13abb70c8e6e24e01aa2d
[jalview.git] / src / jalview / gui / Console.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Dimension;
26 import java.awt.GraphicsEnvironment;
27 import java.awt.GridBagConstraints;
28 import java.awt.GridBagLayout;
29 import java.awt.Rectangle;
30 import java.awt.Toolkit;
31 import java.awt.datatransfer.Clipboard;
32 import java.awt.datatransfer.StringSelection;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.MouseAdapter;
36 import java.awt.event.MouseEvent;
37 import java.awt.event.WindowAdapter;
38 import java.awt.event.WindowEvent;
39 import java.awt.event.WindowListener;
40 import java.io.IOException;
41 import java.io.PipedInputStream;
42 import java.io.PipedOutputStream;
43 import java.io.PrintStream;
44
45 import javax.swing.BorderFactory;
46 import javax.swing.JButton;
47 import javax.swing.JComboBox;
48 import javax.swing.JFrame;
49 import javax.swing.JLabel;
50 import javax.swing.JPanel;
51 import javax.swing.JScrollPane;
52 import javax.swing.JTextArea;
53 import javax.swing.border.Border;
54 import javax.swing.text.DefaultCaret;
55
56 import jalview.log.JLoggerI.LogLevel;
57 import jalview.log.JLoggerLog4j;
58 import jalview.log.JalviewAppender;
59 import jalview.util.ChannelProperties;
60 import jalview.util.MessageManager;
61 import jalview.util.Platform;
62
63 /**
64  * Simple Jalview Java Console. Version 1 - allows viewing of console output
65  * after desktop is created. Acquired with thanks from RJHM's site
66  * http://www.comweb.nl/java/Console/Console.html A simple Java Console for your
67  * application (Swing version) Requires Java 1.1.5 or higher Disclaimer the use
68  * of this source is at your own risk. Permision to use and distribute into your
69  * own applications RJHM van den Bergh , rvdb@comweb.nl
70  */
71
72 public class Console extends WindowAdapter
73         implements WindowListener, ActionListener, Runnable
74 {
75   private JFrame frame;
76
77   private JTextArea textArea;
78
79   /*
80    * unused - tally and limit for lines in console window int lines = 0;
81    * 
82    * int lim = 1000;
83    */
84   int byteslim = 102400, bytescut = 76800; // 100k and 75k cut point.
85
86   private Thread reader, reader2, textAppender;
87
88   private boolean quit;
89
90   private final PrintStream stdout = System.out, stderr = System.err;
91
92   private PipedInputStream pin = new PipedInputStream();
93
94   private PipedInputStream pin2 = new PipedInputStream();
95
96   private StringBuffer displayPipe = new StringBuffer();
97
98   Thread errorThrower; // just for testing (Throws an Exception at this Console
99
100   // are we attached to some parent Desktop
101   Desktop parent = null;
102
103   private int MIN_WIDTH = 300;
104
105   private int MIN_HEIGHT = 250;
106
107   private JComboBox<LogLevel> logLevelCombo = new JComboBox<LogLevel>();
108
109   protected LogLevel startingLogLevel = LogLevel.INFO;
110
111   public Console()
112   {
113     // create all components and add them
114     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
115     frame = initFrame("Java Console", screenSize.width / 2,
116             screenSize.height / 2, -1, -1);
117     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
118     initConsole(true);
119   }
120
121   private void initConsole(boolean visible)
122   {
123     initConsole(visible, true);
124   }
125
126   /**
127    * 
128    * @param visible
129    *          - open the window
130    * @param redirect
131    *          - redirect std*
132    */
133   private void initConsole(boolean visible, boolean redirect)
134   {
135     // CutAndPasteTransfer cpt = new CutAndPasteTransfer();
136     // textArea = cpt.getTextArea();
137     textArea = new JTextArea();
138     textArea.setEditable(false);
139     // autoscroll
140     DefaultCaret caret = (DefaultCaret) textArea.getCaret();
141     caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
142     // toggle autoscroll by clicking on the text area
143     Border pausedBorder = BorderFactory.createMatteBorder(2, 2, 2, 2,
144             textArea.getForeground());
145     Border noBorder = BorderFactory.createEmptyBorder(2, 2, 2, 2);
146     JScrollPane scrollPane = new JScrollPane(textArea);
147     scrollPane.setBorder(noBorder);
148     textArea.addMouseListener(new MouseAdapter()
149     {
150       public void mouseClicked(MouseEvent e)
151       {
152         if (e.getButton() == MouseEvent.BUTTON1)
153         {
154           if (caret.getUpdatePolicy() == DefaultCaret.ALWAYS_UPDATE)
155           {
156             caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
157             scrollPane.setBorder(pausedBorder);
158           }
159           else
160           {
161             caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
162             textArea.setCaretPosition(textArea.getDocument().getLength());
163             scrollPane.setBorder(noBorder);
164           }
165         }
166       }
167     });
168
169     JButton clearButton = new JButton(
170             MessageManager.getString("action.clear"));
171     JButton copyToClipboardButton = new JButton(
172             MessageManager.getString("label.copy_to_clipboard"));
173     copyToClipboardButton.addActionListener(new ActionListener()
174     {
175       public void actionPerformed(ActionEvent e)
176       {
177         copyConsoleTextToClipboard();
178       }
179     });
180     copyToClipboardButton.addMouseListener(new MouseAdapter()
181     {
182       private Color bg = textArea.getBackground();
183
184       private Color fg = textArea.getForeground();
185
186       public void mousePressed(MouseEvent e)
187       {
188         textArea.setBackground(textArea.getSelectionColor());
189         textArea.setForeground(textArea.getSelectedTextColor());
190       }
191
192       public void mouseReleased(MouseEvent e)
193       {
194         textArea.setBackground(bg);
195         textArea.setForeground(fg);
196       }
197
198     });
199     copyToClipboardButton.setToolTipText(
200             MessageManager.getString("label.copy_to_clipboard_tooltip"));
201
202     JLabel logLevelLabel = new JLabel(
203             MessageManager.getString("label.log_level") + ":");
204
205     // logLevelCombo.addItem(LogLevel.ALL);
206     logLevelCombo.addItem(LogLevel.TRACE);
207     logLevelCombo.addItem(LogLevel.DEBUG);
208     logLevelCombo.addItem(LogLevel.INFO);
209     logLevelCombo.addItem(LogLevel.WARN);
210     // logLevelCombo.addItem(LogLevel.ERROR);
211     // logLevelCombo.addItem(LogLevel.FATAL);
212     // logLevelCombo.addItem(LogLevel.ERROR);
213     // logLevelCombo.addItem(LogLevel.OFF);
214     // set startingLogLevel
215     startingLogLevel = jalview.bin.Console.log == null ? LogLevel.INFO
216             : jalview.bin.Console.log.getLevel();
217     setChosenLogLevelCombo();
218     logLevelCombo.addActionListener(new ActionListener()
219     {
220       public void actionPerformed(ActionEvent e)
221       {
222         if (jalview.bin.Console.log != null)
223         {
224           jalview.bin.Console.log
225                   .setLevel((LogLevel) logLevelCombo.getSelectedItem());
226         }
227       }
228
229     });
230
231     // frame = cpt;
232     frame.getContentPane().setLayout(new BorderLayout());
233     frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
234     JPanel southPanel = new JPanel();
235     southPanel.setLayout(new GridBagLayout());
236
237     JPanel logLevelPanel = new JPanel();
238     logLevelPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT);
239     logLevelPanel.add(logLevelLabel);
240     logLevelPanel.add(logLevelCombo);
241     String logLevelTooltip = MessageManager.formatMessage(
242             "label.log_level_tooltip", startingLogLevel.toString());
243     logLevelLabel.setToolTipText(logLevelTooltip);
244     logLevelCombo.setToolTipText(logLevelTooltip);
245
246     GridBagConstraints gbc = new GridBagConstraints();
247     gbc.gridx = 0;
248     gbc.gridy = 0;
249     gbc.gridwidth = 1;
250     gbc.gridheight = 1;
251     gbc.weightx = 0.1;
252     southPanel.add(logLevelPanel, gbc);
253
254     gbc.gridx++;
255     gbc.weightx = 0.8;
256     gbc.fill = GridBagConstraints.HORIZONTAL;
257     southPanel.add(clearButton, gbc);
258
259     gbc.gridx++;
260     gbc.weightx = 0.1;
261     gbc.fill = GridBagConstraints.NONE;
262     southPanel.add(copyToClipboardButton, gbc);
263
264     southPanel.setVisible(true);
265     frame.getContentPane().add(southPanel, BorderLayout.SOUTH);
266     frame.setVisible(visible);
267     updateConsole = visible;
268     frame.addWindowListener(this);
269     clearButton.addActionListener(this);
270
271     if (redirect)
272     {
273       redirectStreams();
274     }
275     else
276     {
277       unredirectStreams();
278     }
279     quit = false; // signals the Threads that they should exit
280
281     // Starting two seperate threads to read from the PipedInputStreams
282     //
283     reader = new Thread(this);
284     reader.setDaemon(true);
285     reader.start();
286     //
287     reader2 = new Thread(this);
288     reader2.setDaemon(true);
289     reader2.start();
290     // and a thread to append text to the textarea
291     textAppender = new Thread(this);
292     textAppender.setDaemon(true);
293     textAppender.start();
294
295     // set icons
296     frame.setIconImages(ChannelProperties.getIconList());
297   }
298
299   private void setChosenLogLevelCombo()
300   {
301     setChosenLogLevelCombo(startingLogLevel);
302   }
303
304   private void setChosenLogLevelCombo(LogLevel setLogLevel)
305   {
306     logLevelCombo.setSelectedItem(setLogLevel);
307     if (!logLevelCombo.getSelectedItem().equals(setLogLevel))
308     {
309       // setLogLevel not (yet) in list
310       if (setLogLevel != null && setLogLevel instanceof LogLevel)
311       {
312         // add new item to list (might be set via .jalview_properties)
313         boolean added = false;
314         for (int i = 0; i < logLevelCombo.getItemCount(); i++)
315         {
316           LogLevel l = (LogLevel) logLevelCombo.getItemAt(i);
317           if (l.compareTo(setLogLevel) >= 0)
318           {
319             logLevelCombo.insertItemAt(setLogLevel, i);
320             added = true;
321             break;
322           }
323         }
324         if (!added) // lower priority than others or some confusion -- add to
325                     // end of list
326         {
327           logLevelCombo.addItem(setLogLevel);
328         }
329         logLevelCombo.setSelectedItem(setLogLevel);
330       }
331       else
332       {
333         logLevelCombo.setSelectedItem(LogLevel.INFO);
334       }
335     }
336   }
337
338   private void copyConsoleTextToClipboard()
339   {
340     String consoleText = textArea.getText();
341     StringSelection consoleTextSelection = new StringSelection(consoleText);
342     Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
343     cb.setContents(consoleTextSelection, null);
344   }
345
346   PipedOutputStream pout = null, perr = null;
347
348   public void redirectStreams()
349   {
350     if (pout == null)
351     {
352       try
353       {
354         pout = new PipedOutputStream(this.pin);
355         System.setOut(new PrintStream(pout, true));
356       } catch (java.io.IOException io)
357       {
358         textArea.append("Couldn't redirect STDOUT to this console\n"
359                 + io.getMessage());
360         io.printStackTrace(stderr);
361       } catch (SecurityException se)
362       {
363         textArea.append("Couldn't redirect STDOUT to this console\n"
364                 + se.getMessage());
365         se.printStackTrace(stderr);
366       }
367
368       try
369       {
370         perr = new PipedOutputStream(this.pin2);
371         System.setErr(new PrintStream(perr, true));
372       } catch (java.io.IOException io)
373       {
374         textArea.append("Couldn't redirect STDERR to this console\n"
375                 + io.getMessage());
376         io.printStackTrace(stderr);
377       } catch (SecurityException se)
378       {
379         textArea.append("Couldn't redirect STDERR to this console\n"
380                 + se.getMessage());
381         se.printStackTrace(stderr);
382       }
383     }
384   }
385
386   public void unredirectStreams()
387   {
388     if (pout != null)
389     {
390       try
391       {
392         System.setOut(stdout);
393         pout.flush();
394         pout.close();
395         pin = new PipedInputStream();
396         pout = null;
397       } catch (java.io.IOException io)
398       {
399         textArea.append("Couldn't unredirect STDOUT to this console\n"
400                 + io.getMessage());
401         io.printStackTrace(stderr);
402       } catch (SecurityException se)
403       {
404         textArea.append("Couldn't unredirect STDOUT to this console\n"
405                 + se.getMessage());
406         se.printStackTrace(stderr);
407       }
408
409       try
410       {
411         System.setErr(stderr);
412         perr.flush();
413         perr.close();
414         pin2 = new PipedInputStream();
415         perr = null;
416       } catch (java.io.IOException io)
417       {
418         textArea.append("Couldn't unredirect STDERR to this console\n"
419                 + io.getMessage());
420         io.printStackTrace(stderr);
421       } catch (SecurityException se)
422       {
423         textArea.append("Couldn't unredirect STDERR to this console\n"
424                 + se.getMessage());
425         se.printStackTrace(stderr);
426       }
427     }
428   }
429
430   public void test()
431   {
432     // testing part
433     // you may omit this part for your application
434     //
435
436     jalview.bin.Console.outPrintln("Hello World 2");
437     jalview.bin.Console.outPrintln("All fonts available to Graphic2D:\n");
438     GraphicsEnvironment ge = GraphicsEnvironment
439             .getLocalGraphicsEnvironment();
440     String[] fontNames = ge.getAvailableFontFamilyNames();
441     for (int n = 0; n < fontNames.length; n++)
442     {
443       jalview.bin.Console.outPrintln(fontNames[n]);
444     }
445     // Testing part: simple an error thrown anywhere in this JVM will be printed
446     // on the Console
447     // We do it with a seperate Thread becasue we don't wan't to break a Thread
448     // used by the Console.
449     jalview.bin.Console.outPrintln("\nLets throw an error on this console");
450     errorThrower = new Thread(this);
451     errorThrower.setDaemon(true);
452     errorThrower.start();
453   }
454
455   private JFrame initFrame(String string, int i, int j, int x, int y)
456   {
457     JFrame frame = new JFrame(string);
458     frame.setName(string);
459     if (x == -1)
460     {
461       x = i / 2;
462     }
463     if (y == -1)
464     {
465       y = j / 2;
466     }
467     frame.setBounds(x, y, i, j);
468     return frame;
469   }
470
471   /**
472    * attach a console to the desktop - the desktop will open it if requested.
473    * 
474    * @param desktop
475    */
476   public Console(Desktop desktop)
477   {
478     this(desktop, true);
479   }
480
481   /**
482    * attach a console to the desktop - the desktop will open it if requested.
483    * 
484    * @param desktop
485    * @param showjconsole
486    *          - if true, then redirect stdout immediately
487    */
488   public Console(Desktop desktop, boolean showjconsole)
489   {
490     parent = desktop;
491     // window name - get x,y,width, height possibly scaled
492     Rectangle bounds = desktop.getLastKnownDimensions("JAVA_CONSOLE_");
493     if (bounds == null)
494     {
495       frame = initFrame(
496               ChannelProperties.getProperty("app_name") + " Java Console",
497               desktop.getWidth() / 2, desktop.getHeight() / 4,
498               desktop.getX(), desktop.getY());
499     }
500     else
501     {
502       frame = initFrame(
503               ChannelProperties.getProperty("app_name") + " Java Console",
504               bounds.width, bounds.height, bounds.x, bounds.y);
505     }
506     frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
507     // desktop.add(frame);
508     initConsole(false);
509     LogLevel level = (LogLevel) logLevelCombo.getSelectedItem();
510     if (!Platform.isJS())
511     {
512       JalviewAppender jappender = new JalviewAppender(level);
513       JalviewAppender.setTextArea(textArea);
514       jappender.start();
515       if (jalview.bin.Console.log != null
516               && jalview.bin.Console.log instanceof JLoggerLog4j)
517       {
518         JLoggerLog4j.addAppender(jalview.bin.Console.log, jappender);
519       }
520     }
521   }
522
523   public synchronized void stopConsole()
524   {
525     quit = true;
526     this.notifyAll();
527     /*
528      * reader.notify(); reader2.notify(); if (errorThrower!=null)
529      * errorThrower.notify(); // stop all threads if (textAppender!=null)
530      * textAppender.notify();
531      */
532     if (pout != null)
533     {
534       try
535       {
536         reader.join(10);
537         pin.close();
538       } catch (Exception e)
539       {
540       }
541       try
542       {
543         reader2.join(10);
544         pin2.close();
545       } catch (Exception e)
546       {
547       }
548       try
549       {
550         textAppender.join(10);
551       } catch (Exception e)
552       {
553       }
554     }
555     /*
556     if (!frame.isVisible())
557     {
558       frame.dispose();
559     }
560     */
561     // System.exit(0);
562   }
563
564   @Override
565   public synchronized void windowClosed(WindowEvent evt)
566   {
567     frame.setVisible(false);
568     closeConsoleGui();
569   }
570
571   private void closeConsoleGui()
572   {
573     updateConsole = false;
574     if (parent == null)
575     {
576
577       stopConsole();
578     }
579     else
580     {
581       parent.showConsole(false);
582     }
583   }
584
585   @Override
586   public synchronized void windowClosing(WindowEvent evt)
587   {
588     frame.setVisible(false); // default behaviour of JFrame
589     closeConsoleGui();
590
591     // frame.dispose();
592   }
593
594   @Override
595   public synchronized void actionPerformed(ActionEvent evt)
596   {
597     trimBuffer(true);
598     // textArea.setText("");
599   }
600
601   @Override
602   public synchronized void run()
603   {
604     try
605     {
606       while (Thread.currentThread() == reader)
607       {
608         if (pin == null || pin.available() == 0)
609         {
610           try
611           {
612             this.wait(100);
613             if (pin.available() == 0)
614             {
615               trimBuffer(false);
616             }
617           } catch (InterruptedException ie)
618           {
619           }
620         }
621
622         while (pin.available() != 0)
623         {
624           String input = this.readLine(pin);
625           stdout.print(input);
626           long time = System.nanoTime();
627           appendToTextArea(input);
628           // stderr.println("Time taken to stdout append:\t"
629           // + (System.nanoTime() - time) + " ns");
630           // lines++;
631         }
632         if (quit)
633         {
634           return;
635         }
636       }
637
638       while (Thread.currentThread() == reader2)
639       {
640         if (pin2.available() == 0)
641         {
642           try
643           {
644             this.wait(100);
645             if (pin2.available() == 0)
646             {
647               trimBuffer(false);
648             }
649           } catch (InterruptedException ie)
650           {
651           }
652         }
653         while (pin2.available() != 0)
654         {
655           String input = this.readLine(pin2);
656           stderr.print(input);
657           long time = System.nanoTime();
658           appendToTextArea(input);
659           // stderr.println("Time taken to stderr append:\t"
660           // + (System.nanoTime() - time) + " ns");
661           // lines++;
662         }
663         if (quit)
664         {
665           return;
666         }
667       }
668       while (Thread.currentThread() == textAppender)
669       {
670         if (updateConsole)
671         {
672           // check string buffer - if greater than console, clear console and
673           // replace with last segment of content, otherwise, append all to
674           // content.
675           long count;
676           while (displayPipe.length() > 0)
677           {
678             count = 0;
679             StringBuffer tmp = new StringBuffer(), replace;
680             synchronized (displayPipe)
681             {
682               replace = displayPipe;
683               displayPipe = tmp;
684             }
685             // simply append whole buffer
686             synchronized (textArea.getDocument())
687             {
688               textArea.append(replace.toString());
689               count += replace.length();
690               if (count > byteslim)
691               {
692                 trimBuffer(false);
693               }
694             }
695             
696           }
697           if (displayPipe.length() == 0)
698           {
699             try
700             {
701               this.wait(100);
702               if (displayPipe.length() == 0)
703               {
704                 trimBuffer(false);
705               }
706             } catch (InterruptedException e)
707             {
708             }
709           }
710         }
711         else
712         {
713           try
714           {
715             this.wait(100);
716           } catch (InterruptedException e)
717           {
718
719           }
720         }
721         if (quit)
722         {
723           return;
724         }
725
726       }
727     } catch (Exception e)
728     {
729       textArea.append("\nConsole reports an Internal error.");
730       textArea.append("The error is: " + e.getMessage());
731       // Need to uncomment this to ensure that line tally is synched.
732       // lines += 2;
733       stderr.println(
734               "Console reports an Internal error.\nThe error is: " + e);
735     }
736
737     // just for testing (Throw a Nullpointer after 1 second)
738     if (Thread.currentThread() == errorThrower)
739     {
740       try
741       {
742         this.wait(1000);
743       } catch (InterruptedException ie)
744       {
745       }
746       throw new NullPointerException(
747               MessageManager.getString("exception.application_test_npe"));
748     }
749   }
750
751   private void appendToTextArea(final String input)
752   {
753     if (updateConsole == false)
754     {
755       // do nothing;
756       return;
757     }
758     long time = System.nanoTime();
759     javax.swing.SwingUtilities.invokeLater(new Runnable()
760     {
761       @Override
762       public void run()
763       {
764         displayPipe.append(input); // change to stringBuffer
765         // displayPipe.flush();
766
767       }
768     });
769     // stderr.println("Time taken to Spawnappend:\t" + (System.nanoTime() -
770     // time)
771     // + " ns");
772   }
773
774   private String header = null;
775
776   private boolean updateConsole = false;
777
778   private synchronized void trimBuffer(boolean clear)
779   {
780     if (header == null && textArea.getLineCount() > 5)
781     {
782       try
783       {
784         header = textArea.getText(0, textArea.getLineStartOffset(5))
785                 + "\nTruncated...\n";
786       } catch (Exception e)
787       {
788         e.printStackTrace();
789       }
790     }
791     // trim the buffer
792     int tlength = textArea.getDocument().getLength();
793     if (header != null)
794     {
795       if (clear || (tlength > byteslim))
796       {
797         try
798         {
799           if (!clear)
800           {
801             long time = System.nanoTime();
802             textArea.replaceRange(header, 0, tlength - bytescut);
803             // stderr.println("Time taken to cut:\t"
804             // + (System.nanoTime() - time) + " ns");
805           }
806           else
807           {
808             textArea.setText(header);
809           }
810         } catch (Exception e)
811         {
812           e.printStackTrace();
813         }
814         // lines = textArea.getLineCount();
815       }
816     }
817
818   }
819
820   public synchronized String readLine(PipedInputStream in)
821           throws IOException
822   {
823     String input = "";
824     int lp = -1;
825     do
826     {
827       int available = in.available();
828       if (available == 0)
829       {
830         break;
831       }
832       byte b[] = new byte[available];
833       in.read(b);
834       input = input + new String(b, 0, b.length);
835       // counts lines - we don't do this for speed.
836       // while ((lp = input.indexOf("\n", lp + 1)) > -1)
837       // {
838       // lines++;
839       // }
840     } while (!input.endsWith("\n") && !input.endsWith("\r\n") && !quit);
841     return input;
842   }
843
844   /**
845    * @j2sIgnore
846    * @param arg
847    */
848   public static void main(String[] arg)
849   {
850     new Console().test(); // create console with not reference
851
852   }
853
854   public void setVisible(boolean selected)
855   {
856     frame.setVisible(selected);
857     if (selected == true)
858     {
859       setChosenLogLevelCombo();
860       redirectStreams();
861       updateConsole = true;
862       frame.toFront();
863     }
864     else
865     {
866       // reset log level to what it was before
867       if (jalview.bin.Console.log != null)
868       {
869         jalview.bin.Console.log.setLevel(startingLogLevel);
870       }
871
872       unredirectStreams();
873       updateConsole = false;
874     }
875   }
876
877   public Rectangle getBounds()
878   {
879     if (frame != null)
880     {
881       return frame.getBounds();
882     }
883     return null;
884   }
885
886   /**
887    * set the banner that appears at the top of the console output
888    * 
889    * @param string
890    */
891   public void setHeader(String string)
892   {
893     header = string;
894     if (header.charAt(header.length() - 1) != '\n')
895     {
896       header += "\n";
897     }
898     textArea.insert(header, 0);
899   }
900
901   /**
902    * get the banner
903    * 
904    * @return
905    */
906   public String getHeader()
907   {
908     return header;
909   }
910 }