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