Merge branch 'releases/Release_2_11_3_Branch'
[jalview.git] / src / jalview / gui / 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.gui;
22
23 import java.awt.Font;
24 import java.awt.FontMetrics;
25 import java.awt.geom.Rectangle2D;
26
27 import javax.swing.JInternalFrame;
28 import javax.swing.JLayeredPane;
29
30 import jalview.bin.Cache;
31 import jalview.jbgui.GFontChooser;
32 import jalview.util.MessageManager;
33
34 /**
35  * DOCUMENT ME!
36  * 
37  * @author $author$
38  * @version $Revision$
39  */
40 public class FontChooser extends GFontChooser
41 {
42   AlignmentPanel ap;
43
44   TreePanel tp;
45
46   /*
47    * The font on opening the dialog (to be restored on Cancel)
48    */
49   Font oldFont;
50
51   /*
52    * The font on opening the dialog (to be restored on Cancel)
53    * on the other half of a split frame (if applicable)
54    */
55   Font oldComplementFont;
56
57   /*
58    * the state of 'scale protein as cDNA' on opening the dialog
59    */
60   boolean oldProteinScale;
61
62   /*
63    * the state of 'same font for protein and cDNA' on opening the dialog
64    */
65   boolean oldMirrorFont;
66
67   boolean init = true;
68
69   JInternalFrame frame;
70
71   /*
72    * The last font settings selected in the dialog
73    */
74   private Font lastSelected = null;
75
76   private boolean lastSelMono = false;
77
78   private boolean oldSmoothFont;
79
80   private boolean oldComplementSmooth;
81
82   /**
83    * Creates a new FontChooser for a tree panel
84    * 
85    * @param treePanel
86    */
87   public FontChooser(TreePanel treePanel)
88   {
89     this.tp = treePanel;
90     ap = treePanel.getTreeCanvas().getAssociatedPanel();
91     oldFont = treePanel.getTreeFont();
92     defaultButton.setVisible(false);
93     smoothFont.setEnabled(false);
94     init();
95   }
96
97   /**
98    * Creates a new FontChooser for an alignment panel
99    * 
100    * @param alignPanel
101    */
102   public FontChooser(AlignmentPanel alignPanel)
103   {
104     oldFont = alignPanel.av.getFont();
105     oldProteinScale = alignPanel.av.isScaleProteinAsCdna();
106     oldMirrorFont = alignPanel.av.isProteinFontAsCdna();
107     oldSmoothFont = alignPanel.av.antiAlias;
108     this.ap = alignPanel;
109     init();
110   }
111
112   void init()
113   {
114     frame = new JInternalFrame();
115     frame.setFrameIcon(null);
116     frame.setContentPane(this);
117
118     smoothFont.setSelected(ap.av.antiAlias);
119
120     /*
121      * Enable 'scale protein as cDNA' in a SplitFrame view. The selection is
122      * stored in the ViewStyle of both dna and protein Viewport. Also enable
123      * checkbox for copy font changes to other half of split frame.
124      */
125     boolean inSplitFrame = ap.av.getCodingComplement() != null;
126     if (inSplitFrame)
127     {
128       oldComplementFont = ((AlignViewport) ap.av.getCodingComplement())
129               .getFont();
130       oldComplementSmooth = ((AlignViewport) ap.av
131               .getCodingComplement()).antiAlias;
132       scaleAsCdna.setVisible(true);
133       scaleAsCdna.setSelected(ap.av.isScaleProteinAsCdna());
134       fontAsCdna.setVisible(true);
135       fontAsCdna.setSelected(ap.av.isProteinFontAsCdna());
136     }
137
138     if (isTreeFont())
139     {
140       Desktop.addInternalFrame(frame,
141               MessageManager.getString("action.change_font_tree_panel"),
142               400, 200, false);
143     }
144     else
145     {
146       Desktop.addInternalFrame(frame,
147               MessageManager.getString("action.change_font"), 380, 220,
148               false);
149     }
150
151     frame.setLayer(JLayeredPane.PALETTE_LAYER);
152
153     String[] fonts = java.awt.GraphicsEnvironment
154             .getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
155
156     for (int i = 0; i < fonts.length; i++)
157     {
158       fontName.addItem(fonts[i]);
159     }
160
161     for (int i = 1; i < 51; i++)
162     {
163       fontSize.addItem(i);
164     }
165
166     fontStyle.addItem("plain");
167     fontStyle.addItem("bold");
168     fontStyle.addItem("italic");
169
170     fontName.setSelectedItem(oldFont.getName());
171     fontSize.setSelectedItem(oldFont.getSize());
172     fontStyle.setSelectedIndex(oldFont.getStyle());
173
174     FontMetrics fm = getGraphics().getFontMetrics(oldFont);
175     monospaced.setSelected(
176             fm.getStringBounds("M", getGraphics()).getWidth() == fm
177                     .getStringBounds("|", getGraphics()).getWidth());
178
179     init = false;
180   }
181
182   @Override
183   protected void smoothFont_actionPerformed()
184   {
185     ap.av.antiAlias = smoothFont.isSelected();
186     ap.getAnnotationPanel().image = null;
187     ap.paintAlignment(true, false);
188     if (ap.av.getCodingComplement() != null && ap.av.isProteinFontAsCdna())
189     {
190       ((AlignViewport) ap.av
191               .getCodingComplement()).antiAlias = ap.av.antiAlias;
192       SplitFrame sv = (SplitFrame) ap.alignFrame.getSplitViewContainer();
193       sv.adjustLayout();
194       sv.repaint();
195     }
196
197   }
198
199   /**
200    * DOCUMENT ME!
201    * 
202    * @param e
203    *          DOCUMENT ME!
204    */
205   @Override
206   protected void ok_actionPerformed()
207   {
208     try
209     {
210       frame.setClosed(true);
211     } catch (Exception ex)
212     {
213     }
214
215     if (ap != null)
216     {
217       if (ap.getOverviewPanel() != null)
218       {
219         ap.getOverviewPanel().updateOverviewImage();
220       }
221     }
222   }
223
224   /**
225    * DOCUMENT ME!
226    * 
227    * @param e
228    *          DOCUMENT ME!
229    */
230   @Override
231   protected void cancel_actionPerformed()
232   {
233     if (isTreeFont())
234     {
235       tp.setTreeFont(oldFont);
236     }
237     else if (ap != null)
238     {
239       ap.av.setFont(oldFont, true);
240       ap.av.setScaleProteinAsCdna(oldProteinScale);
241       ap.av.setProteinFontAsCdna(oldMirrorFont);
242       ap.av.antiAlias = oldSmoothFont;
243       ap.fontChanged();
244
245       if (scaleAsCdna.isVisible() && scaleAsCdna.isEnabled())
246       {
247         ap.av.getCodingComplement().setScaleProteinAsCdna(oldProteinScale);
248         ap.av.getCodingComplement().setProteinFontAsCdna(oldMirrorFont);
249         ((AlignViewport) ap.av
250                 .getCodingComplement()).antiAlias = oldComplementSmooth;
251         ap.av.getCodingComplement().setFont(oldComplementFont, true);
252         SplitFrame splitFrame = (SplitFrame) ap.alignFrame
253                 .getSplitViewContainer();
254         splitFrame.adjustLayout();
255         splitFrame.repaint();
256       }
257     }
258
259     try
260     {
261       frame.setClosed(true);
262     } catch (Exception ex)
263     {
264     }
265   }
266
267   private boolean isTreeFont()
268   {
269     return tp != null;
270   }
271
272   /**
273    * DOCUMENT ME!
274    */
275   void changeFont()
276   {
277     if (lastSelected == null)
278     {
279       // initialise with original font
280       lastSelected = oldFont;
281       FontMetrics fm = getGraphics().getFontMetrics(oldFont);
282       double mw = fm.getStringBounds("M", getGraphics()).getWidth();
283       double iw = fm.getStringBounds("I", getGraphics()).getWidth();
284       lastSelMono = (mw == iw); // == on double - flaky?
285     }
286
287     Font newFont = new Font(fontName.getSelectedItem().toString(),
288             fontStyle.getSelectedIndex(),
289             (Integer) fontSize.getSelectedItem());
290     FontMetrics fm = getGraphics().getFontMetrics(newFont);
291     double mw = fm.getStringBounds("M", getGraphics()).getWidth();
292     final Rectangle2D iBounds = fm.getStringBounds("I", getGraphics());
293     double iw = iBounds.getWidth();
294     if (mw < 1 || iw < 1)
295     {
296       String message = iBounds.getHeight() < 1
297               ? MessageManager
298                       .getString("label.font_doesnt_have_letters_defined")
299               : MessageManager.getString("label.font_too_small");
300       JvOptionPane.showInternalMessageDialog(this, message,
301               MessageManager.getString("label.invalid_font"),
302               JvOptionPane.WARNING_MESSAGE);
303       /*
304        * Restore the changed value - note this will reinvoke this method via the
305        * ActionListener, but now validation should pass
306        */
307       if (lastSelected.getSize() != (Integer) fontSize.getSelectedItem()) // autoboxing
308       {
309         fontSize.setSelectedItem(lastSelected.getSize());
310       }
311       if (!lastSelected.getName()
312               .equals(fontName.getSelectedItem().toString()))
313       {
314         fontName.setSelectedItem(lastSelected.getName());
315       }
316       if (lastSelected.getStyle() != fontStyle.getSelectedIndex())
317       {
318         fontStyle.setSelectedIndex(lastSelected.getStyle());
319       }
320       if (lastSelMono != monospaced.isSelected())
321       {
322         monospaced.setSelected(lastSelMono);
323       }
324       return;
325     }
326     if (isTreeFont())
327     {
328       tp.setTreeFont(newFont);
329     }
330     else if (ap != null)
331     {
332       ap.av.setFont(newFont, true);
333       ap.fontChanged();
334
335       /*
336        * adjust other half of split frame if present, whether or not same font or
337        * scale to cDNA is selected, because a font change may affect character
338        * width, and this is kept the same in both panels
339        */
340       if (fontAsCdna.isVisible())
341       {
342         if (fontAsCdna.isSelected())
343         {
344           ap.av.getCodingComplement().setFont(newFont, true);
345         }
346
347         SplitFrame splitFrame = (SplitFrame) ap.alignFrame
348                 .getSplitViewContainer();
349         splitFrame.adjustLayout();
350         splitFrame.repaint();
351       }
352     }
353
354     monospaced.setSelected(mw == iw);
355
356     /*
357      * Remember latest valid selection, so it can be restored if followed by an
358      * invalid one
359      */
360     lastSelected = newFont;
361   }
362
363   /**
364    * Updates on change of selected font name
365    */
366   @Override
367   protected void fontName_actionPerformed()
368   {
369     if (init)
370     {
371       return;
372     }
373
374     changeFont();
375   }
376
377   /**
378    * Updates on change of selected font size
379    */
380   @Override
381   protected void fontSize_actionPerformed()
382   {
383     if (init)
384     {
385       return;
386     }
387
388     changeFont();
389   }
390
391   /**
392    * Updates on change of selected font style
393    */
394   @Override
395   protected void fontStyle_actionPerformed()
396   {
397     if (init)
398     {
399       return;
400     }
401
402     changeFont();
403   }
404
405   /**
406    * Make selected settings the defaults by storing them (via Cache class) in
407    * the .jalview_properties file (the file is only written when Jalview exits)
408    */
409   @Override
410   public void defaultButton_actionPerformed()
411   {
412     Cache.setProperty("FONT_NAME", fontName.getSelectedItem().toString());
413     Cache.setProperty("FONT_STYLE", fontStyle.getSelectedIndex() + "");
414     Cache.setProperty("FONT_SIZE", fontSize.getSelectedItem().toString());
415     Cache.setProperty("ANTI_ALIAS",
416             Boolean.toString(smoothFont.isSelected()));
417     Cache.setProperty(Preferences.SCALE_PROTEIN_TO_CDNA,
418             Boolean.toString(scaleAsCdna.isSelected()));
419   }
420
421   /**
422    * Turn on/off scaling of protein characters to 3 times the width of cDNA
423    * characters
424    */
425   @Override
426   protected void scaleAsCdna_actionPerformed()
427   {
428     ap.av.setScaleProteinAsCdna(scaleAsCdna.isSelected());
429     ap.av.getCodingComplement()
430             .setScaleProteinAsCdna(scaleAsCdna.isSelected());
431     final SplitFrame splitFrame = (SplitFrame) ap.alignFrame
432             .getSplitViewContainer();
433     splitFrame.adjustLayout();
434     splitFrame.repaint();
435   }
436
437   /**
438    * Turn on/off mirroring of font across split frame. If turning on, also
439    * copies the current font across the split frame. If turning off, restores
440    * the other half of the split frame to its initial font.
441    */
442   @Override
443   protected void mirrorFonts_actionPerformed()
444   {
445     boolean selected = fontAsCdna.isSelected();
446     ap.av.setProteinFontAsCdna(selected);
447     ap.av.getCodingComplement().setProteinFontAsCdna(selected);
448
449     /*
450      * reset other half of split frame if turning option off
451      */
452     if (!selected)
453     {
454       ap.av.getCodingComplement().setFont(oldComplementFont, true);
455     }
456
457     changeFont();
458   }
459 }