JAL-2738 update spike branch with latest
[jalview.git] / src / jalview / appletgui / FontChooser.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.appletgui;
22
23 import jalview.util.MessageManager;
24
25 import java.awt.BorderLayout;
26 import java.awt.Button;
27 import java.awt.Checkbox;
28 import java.awt.Choice;
29 import java.awt.Color;
30 import java.awt.FlowLayout;
31 import java.awt.Font;
32 import java.awt.FontMetrics;
33 import java.awt.Frame;
34 import java.awt.Label;
35 import java.awt.Panel;
36 import java.awt.Toolkit;
37 import java.awt.event.ActionEvent;
38 import java.awt.event.ActionListener;
39 import java.awt.event.ItemEvent;
40 import java.awt.event.ItemListener;
41
42 /**
43  * This dialog allows the user to try different font settings and related
44  * options. Changes are immediately visible on the alignment or tree. The user
45  * can dismiss the dialog by confirming changes with 'OK', or reverting to
46  * previous settings with 'Cancel'.
47  */
48 @SuppressWarnings("serial")
49 public class FontChooser extends Panel implements ItemListener
50 {
51   private static final Font VERDANA_11PT = new Font("Verdana", 0, 11);
52
53   private Choice fontSize = new Choice();
54
55   private Choice fontStyle = new Choice();
56
57   private Choice fontName = new Choice();
58
59   private Checkbox scaleAsCdna = new Checkbox();
60
61   private Checkbox fontAsCdna = new Checkbox();
62
63   private Button ok = new Button();
64
65   private Button cancel = new Button();
66
67   private AlignmentPanel ap;
68
69   private TreePanel tp;
70
71   private Font oldFont;
72
73   private Font oldComplementFont;
74
75   private int oldCharWidth = 0;
76
77   /*
78    * the state of 'scale protein to cDNA' on opening the dialog
79    */
80   private boolean oldScaleProtein = false;
81
82   /*
83    * the state of 'same font for protein and cDNA' on opening the dialog
84    */
85   boolean oldMirrorFont;
86
87   private Font lastSelected = null;
88
89   private int lastSelStyle = 0;
90
91   private int lastSelSize = 0;
92
93   private boolean init = true;
94
95   private Frame frame;
96
97   boolean inSplitFrame = false;
98
99   /**
100    * Constructor for a TreePanel font chooser
101    * 
102    * @param tp
103    */
104   public FontChooser(TreePanel tp)
105   {
106     try
107     {
108       jbInit();
109     } catch (Exception e)
110     {
111       e.printStackTrace();
112     }
113
114     this.tp = tp;
115     oldFont = tp.getTreeFont();
116     init();
117   }
118
119   /**
120    * Constructor for an AlignmentPanel font chooser
121    * 
122    * @param ap
123    */
124   public FontChooser(AlignmentPanel ap)
125   {
126     this.ap = ap;
127     oldFont = ap.av.getFont();
128     oldCharWidth = ap.av.getCharWidth();
129     oldScaleProtein = ap.av.isScaleProteinAsCdna();
130     oldMirrorFont = ap.av.isProteinFontAsCdna();
131
132     try
133     {
134       jbInit();
135     } catch (Exception e)
136     {
137       e.printStackTrace();
138     }
139     init();
140   }
141
142   /**
143    * Populate choice lists and open this dialog
144    */
145   void init()
146   {
147     String fonts[] = Toolkit.getDefaultToolkit().getFontList();
148     for (int i = 0; i < fonts.length; i++)
149     {
150       fontName.addItem(fonts[i]);
151     }
152
153     for (int i = 1; i < 31; i++)
154     {
155       fontSize.addItem(i + "");
156     }
157
158     fontStyle.addItem("plain");
159     fontStyle.addItem("bold");
160     fontStyle.addItem("italic");
161
162     fontName.select(oldFont.getName());
163     fontSize.select(oldFont.getSize() + "");
164     fontStyle.select(oldFont.getStyle());
165
166     this.frame = new Frame();
167     frame.add(this);
168     jalview.bin.JalviewLite.addFrame(frame,
169             MessageManager.getString("action.change_font"), 440, 145);
170
171     init = false;
172   }
173
174   /**
175    * Actions on change of font name, size or style.
176    */
177   @Override
178   public void itemStateChanged(ItemEvent evt)
179   {
180     final Object source = evt.getSource();
181     if (source == fontName)
182     {
183       fontName_actionPerformed();
184     }
185     else if (source == fontSize)
186     {
187       fontSize_actionPerformed();
188     }
189     else if (source == fontStyle)
190     {
191       fontStyle_actionPerformed();
192     }
193     else if (source == scaleAsCdna)
194     {
195       scaleAsCdna_actionPerformed();
196     }
197     else if (source == fontAsCdna)
198     {
199       mirrorFont_actionPerformed();
200     }
201   }
202
203   /**
204    * Action on checking or unchecking 'use same font across split screen'
205    * option. When checked, the font settings are copied to the other half of the
206    * split screen. When unchecked, the other half is restored to its initial
207    * settings.
208    */
209   protected void mirrorFont_actionPerformed()
210   {
211     boolean selected = fontAsCdna.getState();
212     ap.av.setProteinFontAsCdna(selected);
213     ap.av.getCodingComplement().setProteinFontAsCdna(selected);
214
215     if (!selected)
216     {
217       ap.av.getCodingComplement().setFont(oldComplementFont, true);
218     }
219     changeFont();
220   }
221
222   /**
223    * Close this dialog on OK to confirm any changes. Also updates the overview
224    * window if displayed.
225    */
226   protected void ok_actionPerformed()
227   {
228     frame.setVisible(false);
229     if (ap != null)
230     {
231       if (ap.getOverviewPanel() != null)
232       {
233         ap.getOverviewPanel().updateOverviewImage();
234       }
235     }
236   }
237
238   /**
239    * Close this dialog on Cancel, reverting to previous font settings.
240    */
241   protected void cancel_actionPerformed()
242   {
243     if (ap != null)
244     {
245       ap.av.setScaleProteinAsCdna(oldScaleProtein);
246       ap.av.setProteinFontAsCdna(oldMirrorFont);
247
248       if (ap.av.getCodingComplement() != null)
249       {
250         ap.av.getCodingComplement().setScaleProteinAsCdna(oldScaleProtein);
251         ap.av.getCodingComplement().setProteinFontAsCdna(oldMirrorFont);
252         ap.av.getCodingComplement().setFont(oldComplementFont, true);
253         SplitFrame splitFrame = ap.alignFrame.getSplitFrame();
254         splitFrame.adjustLayout();
255         splitFrame.getComplement(ap.alignFrame).alignPanel.fontChanged();
256         splitFrame.repaint();
257       }
258
259       ap.av.setFont(oldFont, true);
260       if (ap.av.getCharWidth() != oldCharWidth)
261       {
262         ap.av.setCharWidth(oldCharWidth);
263       }
264       ap.paintAlignment(true, false);
265     }
266     else if (tp != null)
267     {
268       tp.setTreeFont(oldFont);
269       tp.treeCanvas.repaint();
270     }
271
272     fontName.select(oldFont.getName());
273     fontSize.select(oldFont.getSize() + "");
274     fontStyle.select(oldFont.getStyle());
275
276     frame.setVisible(false);
277   }
278
279   /**
280    * DOCUMENT ME!
281    */
282   void changeFont()
283   {
284     if (lastSelected == null)
285     {
286       // initialise with original font
287       lastSelected = oldFont;
288       lastSelSize = oldFont.getSize();
289       lastSelStyle = oldFont.getStyle();
290     }
291
292     Font newFont = new Font(fontName.getSelectedItem().toString(),
293             fontStyle.getSelectedIndex(),
294             Integer.parseInt(fontSize.getSelectedItem().toString()));
295     FontMetrics fm = getGraphics().getFontMetrics(newFont);
296     double mw = fm.getStringBounds("M", getGraphics()).getWidth(),
297             iw = fm.getStringBounds("I", getGraphics()).getWidth();
298     if (mw < 1 || iw < 1)
299     {
300       // TODO: JAL-1100
301       fontName.select(lastSelected.getName());
302       fontStyle.select(lastSelStyle);
303       fontSize.select("" + lastSelSize);
304       JVDialog d = new JVDialog(this.frame,
305               MessageManager.getString("label.invalid_font"), true, 350,
306               200);
307       Panel mp = new Panel();
308       d.cancel.setVisible(false);
309       mp.setLayout(new FlowLayout());
310       mp.add(new Label(
311               "Font doesn't have letters defined\nso cannot be used\nwith alignment data."));
312       d.setMainPanel(mp);
313       d.setVisible(true);
314       return;
315     }
316     if (tp != null)
317     {
318       tp.setTreeFont(newFont);
319     }
320     else if (ap != null)
321     {
322       ap.av.setFont(newFont, true);
323       ap.fontChanged();
324
325       /*
326        * and change font in other half of split frame if any
327        */
328       if (inSplitFrame)
329       {
330         if (fontAsCdna.getState())
331         {
332           ap.av.getCodingComplement().setFont(newFont, true);
333         }
334         SplitFrame splitFrame = ap.alignFrame.getSplitFrame();
335         splitFrame.adjustLayout();
336         splitFrame.getComplement(ap.alignFrame).alignPanel.fontChanged();
337         splitFrame.repaint();
338       }
339     }
340     // remember last selected
341     lastSelected = newFont;
342   }
343
344   protected void fontName_actionPerformed()
345   {
346     if (init)
347     {
348       return;
349     }
350     changeFont();
351   }
352
353   protected void fontSize_actionPerformed()
354   {
355     if (init)
356     {
357       return;
358     }
359     changeFont();
360   }
361
362   protected void fontStyle_actionPerformed()
363   {
364     if (init)
365     {
366       return;
367     }
368     changeFont();
369   }
370
371   /**
372    * Construct this panel's contents
373    * 
374    * @throws Exception
375    */
376   private void jbInit() throws Exception
377   {
378     this.setLayout(new BorderLayout());
379     this.setBackground(Color.white);
380
381     Label fontLabel = new Label(MessageManager.getString("label.font"));
382     fontLabel.setFont(VERDANA_11PT);
383     fontLabel.setAlignment(Label.RIGHT);
384     fontSize.setFont(VERDANA_11PT);
385     fontSize.addItemListener(this);
386     fontStyle.setFont(VERDANA_11PT);
387     fontStyle.addItemListener(this);
388
389     Label sizeLabel = new Label(MessageManager.getString("label.size"));
390     sizeLabel.setAlignment(Label.RIGHT);
391     sizeLabel.setFont(VERDANA_11PT);
392
393     Label styleLabel = new Label(MessageManager.getString("label.style"));
394     styleLabel.setAlignment(Label.RIGHT);
395     styleLabel.setFont(VERDANA_11PT);
396
397     fontName.setFont(VERDANA_11PT);
398     fontName.addItemListener(this);
399
400     scaleAsCdna.setLabel(MessageManager.getString("label.scale_as_cdna"));
401     scaleAsCdna.setFont(VERDANA_11PT);
402     scaleAsCdna.addItemListener(this);
403     scaleAsCdna.setState(ap.av.isScaleProteinAsCdna());
404
405     fontAsCdna.setLabel(MessageManager.getString("label.font_as_cdna"));
406     fontAsCdna.setFont(VERDANA_11PT);
407     fontAsCdna.addItemListener(this);
408     fontAsCdna.setState(ap.av.isProteinFontAsCdna());
409
410     ok.setFont(VERDANA_11PT);
411     ok.setLabel(MessageManager.getString("action.ok"));
412     ok.addActionListener(new ActionListener()
413     {
414       @Override
415       public void actionPerformed(ActionEvent e)
416       {
417         ok_actionPerformed();
418       }
419     });
420     cancel.setFont(VERDANA_11PT);
421     cancel.setLabel(MessageManager.getString("action.cancel"));
422     cancel.addActionListener(new ActionListener()
423     {
424       @Override
425       public void actionPerformed(ActionEvent e)
426       {
427         cancel_actionPerformed();
428       }
429     });
430
431     Panel fontPanel = new Panel();
432     fontPanel.setLayout(new BorderLayout());
433     Panel stylePanel = new Panel();
434     stylePanel.setLayout(new BorderLayout());
435     Panel sizePanel = new Panel();
436     sizePanel.setLayout(new BorderLayout());
437     Panel scalePanel = new Panel();
438     scalePanel.setLayout(new BorderLayout());
439     Panel okCancelPanel = new Panel();
440     Panel optionsPanel = new Panel();
441
442     fontPanel.setBackground(Color.white);
443     stylePanel.setBackground(Color.white);
444     sizePanel.setBackground(Color.white);
445     okCancelPanel.setBackground(Color.white);
446     optionsPanel.setBackground(Color.white);
447
448     fontPanel.add(fontLabel, BorderLayout.WEST);
449     fontPanel.add(fontName, BorderLayout.CENTER);
450     stylePanel.add(styleLabel, BorderLayout.WEST);
451     stylePanel.add(fontStyle, BorderLayout.CENTER);
452     sizePanel.add(sizeLabel, BorderLayout.WEST);
453     sizePanel.add(fontSize, BorderLayout.CENTER);
454     scalePanel.add(scaleAsCdna, BorderLayout.NORTH);
455     scalePanel.add(fontAsCdna, BorderLayout.SOUTH);
456     okCancelPanel.add(ok, null);
457     okCancelPanel.add(cancel, null);
458
459     optionsPanel.add(fontPanel, null);
460     optionsPanel.add(sizePanel, null);
461     optionsPanel.add(stylePanel, null);
462
463     /*
464      * Only show 'scale protein as cDNA' in a SplitFrame
465      */
466     this.add(optionsPanel, BorderLayout.NORTH);
467     if (ap.alignFrame.getSplitFrame() != null)
468     {
469       inSplitFrame = true;
470       oldComplementFont = ((AlignViewport) ap.av.getCodingComplement())
471               .getFont();
472       this.add(scalePanel, BorderLayout.CENTER);
473     }
474     this.add(okCancelPanel, BorderLayout.SOUTH);
475   }
476
477   /**
478    * Turn on/off scaling of protein characters to 3 times the width of cDNA
479    * characters
480    */
481   protected void scaleAsCdna_actionPerformed()
482   {
483     ap.av.setScaleProteinAsCdna(scaleAsCdna.getState());
484     ap.av.getCodingComplement()
485             .setScaleProteinAsCdna(scaleAsCdna.getState());
486     changeFont();
487   }
488
489 }