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