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