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