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