001package com.hfg.bio.phylogeny;
002
003
004import java.awt.*;
005
006import com.hfg.exception.InvalidValueException;
007import com.hfg.exception.ProgrammingException;
008import com.hfg.graphics.units.GfxSize;
009import com.hfg.graphics.units.GfxUnits;
010import com.hfg.graphics.units.Pixels;
011import com.hfg.setting.BooleanSetting;
012import com.hfg.setting.ComplexSetting;
013import com.hfg.setting.FloatSetting;
014import com.hfg.setting.IntSetting;
015import com.hfg.setting.Settings;
016import com.hfg.setting.StringSetting;
017import com.hfg.util.StringBuilderPlus;
018import com.hfg.util.StringUtil;
019
020//------------------------------------------------------------------------------
021/**
022 * Phylogenetic tree display options for use with NewickTree.
023 *
024 * @author J. Alex Taylor, hairyfatguy.com
025 */
026//------------------------------------------------------------------------------
027// com.hfg XML/HTML Coding Library
028//
029// This library is free software; you can redistribute it and/or
030// modify it under the terms of the GNU Lesser General Public
031// License as published by the Free Software Foundation; either
032// version 2.1 of the License, or (at your option) any later version.
033//
034// This library is distributed in the hope that it will be useful,
035// but WITHOUT ANY WARRANTY; without even the implied warranty of
036// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
037// Lesser General Public License for more details.
038//
039// You should have received a copy of the GNU Lesser General Public
040// License along with this library; if not, write to the Free Software
041// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
042//
043// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
044// jataylor@hairyfatguy.com
045//------------------------------------------------------------------------------
046
047public class TreeDisplaySettings extends Settings
048{
049
050   private static final String CLADOGRAM_STYLE                    = "CladogramStyle";
051   private static final String TREE_WIDTH                         = "TreeWidth";
052   private static final String ASSIGN_GROUPS_TO_SINGLETONS        = "AssignGroupsToSingletons";
053   private static final String ALIGN_LEAF_NODE_LABELS             = "AlignLeaftNodeLabels";
054   private static final String SHOW_SCALE                         = "ShowScale";
055   private static final String ENABLE_DYNAMIC_GROUPING            = "EnableDynamicGrouping";
056   private static final String BRANCH_LENGTH_TRAVERSAL_LIMIT      = "BranchLengthTraversalLimit";
057   private static final String SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT = "ShowBranchLengthTraversalLimit";
058   private static final String BLTL_ONCHANGE_CALLBACK             = "BLTL_OnChangeCallback";
059   private static final String BLTL_SELECTION_COMPLETE_CALLBACK   = "BLTL_SelectionCompleteCallback";
060   private static final String FONT                               = "Font";
061   private static final String LEAF_LABEL_LEFT_PADDING            = "LeafLabelLeftPadding";
062   private static final String SCALE_TICK_HEIGHT                  = "ScaleTickHeight";
063   private static final String SCALE_PADDING_TOP                  = "ScalePaddingTop";
064
065   private static CladogramStyle sDefaultCladogramStyle      = CladogramStyle.Rectangular;
066   private static int     sDefaultTreeWidth                  = 500;
067   private static boolean sDefaultAssignGroupsToSingletons   = false;
068   private static boolean sDefaultAlignLeaftNodeLabels       = true;
069   private static boolean sDefaultShowScale                  = true;
070   private static boolean sDefaultShowBranchLengthTraversalLimit = false;
071   private static boolean sDefaultEnableDynamicGrouping      = false;
072   private static Font    sDefaultFont                       = Font.decode("Arial-PLAIN-10");
073   private static int     sDefaultScaleTickHeight            = 10;
074   private static GfxSize sDefaultLeafLabelLeftPadding       = new Pixels(10);
075   private static GfxSize sDefaultScalePaddingTop            = new Pixels(40);
076
077   //**************************************************************************
078   // CONSTRUCTORS
079   //**************************************************************************
080
081   //---------------------------------------------------------------------------
082   public TreeDisplaySettings()
083   {
084
085   }
086
087   //---------------------------------------------------------------------------
088   @Override
089   protected void init()
090   {
091      super.init();
092
093      add(new ComplexSetting(CLADOGRAM_STYLE, sDefaultCladogramStyle));
094      add(new IntSetting(TREE_WIDTH, sDefaultTreeWidth));
095      add(new BooleanSetting(ASSIGN_GROUPS_TO_SINGLETONS, sDefaultAssignGroupsToSingletons));
096      add(new BooleanSetting(ALIGN_LEAF_NODE_LABELS, sDefaultAlignLeaftNodeLabels));
097      add(new BooleanSetting(SHOW_SCALE, sDefaultShowScale));
098      add(new BooleanSetting(ENABLE_DYNAMIC_GROUPING, sDefaultEnableDynamicGrouping));
099      add(new FloatSetting(BRANCH_LENGTH_TRAVERSAL_LIMIT));
100      add(new BooleanSetting(SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT, sDefaultShowBranchLengthTraversalLimit));
101      add(new StringSetting(BLTL_ONCHANGE_CALLBACK));
102      add(new StringSetting(BLTL_SELECTION_COMPLETE_CALLBACK));
103      add(new StringSetting(FONT));
104      add(new IntSetting(LEAF_LABEL_LEFT_PADDING));
105      add(new IntSetting(SCALE_TICK_HEIGHT, sDefaultScaleTickHeight));
106      add(new IntSetting(SCALE_PADDING_TOP));
107
108      setFont(sDefaultFont);
109      setLeafLabelLeftPadding(sDefaultLeafLabelLeftPadding);
110      setScalePaddingTop(sDefaultScalePaddingTop);
111   }
112
113   //###########################################################################
114   // PUBLIC METHODS
115   //###########################################################################
116
117   //---------------------------------------------------------------------------
118   public TreeDisplaySettings setCladogramStyle(CladogramStyle inValue)
119   {
120      get(CLADOGRAM_STYLE).setValue(inValue);
121      return this;
122   }
123
124   //---------------------------------------------------------------------------
125   public CladogramStyle getCladogramStyle()
126   {
127      return (CladogramStyle) get(CLADOGRAM_STYLE).getValue();
128   }
129
130
131   //---------------------------------------------------------------------------
132   public TreeDisplaySettings setTreeWidth(int inValue)
133   {
134      get(TREE_WIDTH).setValue(inValue);
135      return this;
136   }
137
138   //---------------------------------------------------------------------------
139   public Integer getTreeWidth()
140   {
141      return (Integer) get(TREE_WIDTH).getValue();
142   }
143
144
145   //---------------------------------------------------------------------------
146   public TreeDisplaySettings setAssignGroupsToSingletons(boolean inValue)
147   {
148      get(ASSIGN_GROUPS_TO_SINGLETONS).setValue(inValue);
149      return this;
150   }
151
152   //---------------------------------------------------------------------------
153   public Boolean getAssignGroupsToSingletons()
154   {
155      return (Boolean) get(ASSIGN_GROUPS_TO_SINGLETONS).getValue();
156   }
157
158
159   //---------------------------------------------------------------------------
160   public TreeDisplaySettings setAlignLeaftNodeLabels(boolean inValue)
161   {
162      get(ALIGN_LEAF_NODE_LABELS).setValue(inValue);
163      return this;
164   }
165
166   //---------------------------------------------------------------------------
167   public Boolean getAlignLeaftNodeLabels()
168   {
169      return (Boolean) get(ALIGN_LEAF_NODE_LABELS).getValue();
170   }
171
172
173   //---------------------------------------------------------------------------
174   public TreeDisplaySettings setShowScale(boolean inValue)
175   {
176      get(SHOW_SCALE).setValue(inValue);
177      return this;
178   }
179
180   //---------------------------------------------------------------------------
181   public Boolean getShowScale()
182   {
183      return (Boolean) get(SHOW_SCALE).getValue();
184   }
185
186
187   //---------------------------------------------------------------------------
188   /**
189    When outputting the tree in SVG, this option allows for dynamically adjusting
190    the branch length traversal limit to group closely related leaf nodes.
191    * @param inValue whether or not to enable the dynamic grouping option
192    * @return this Settings object to allow method chaining
193    */
194   public TreeDisplaySettings setEnableDynamicGrouping(boolean inValue)
195   {
196      get(ENABLE_DYNAMIC_GROUPING).setValue(inValue);
197      return this;
198   }
199
200   //---------------------------------------------------------------------------
201   public Boolean getEnableDynamicGrouping()
202   {
203      return (Boolean) get(ENABLE_DYNAMIC_GROUPING).getValue();
204   }
205
206
207   //---------------------------------------------------------------------------
208   public TreeDisplaySettings setBranchLengthTraversalLimit(Float inValue)
209   {
210      get(BRANCH_LENGTH_TRAVERSAL_LIMIT).setValue(inValue);
211      return this;
212   }
213
214   //---------------------------------------------------------------------------
215   public Float getBranchLengthTraversalLimit()
216   {
217      return (Float) get(BRANCH_LENGTH_TRAVERSAL_LIMIT).getValue();
218   }
219
220   //--------------------------------------------------------------------------
221   public void boundsCheckBranchLengthTraversalLimit(PhylogeneticTree inTree)
222   {
223      Float branchLengthTraversalLimit = getBranchLengthTraversalLimit();
224      if (branchLengthTraversalLimit != null)
225      {
226         // Bounds check
227         if (branchLengthTraversalLimit < 0)
228         {
229            branchLengthTraversalLimit = 0f;
230         }
231         else if (branchLengthTraversalLimit > 2 * inTree.getRootedTreeDistance())
232         {
233            branchLengthTraversalLimit = 2 * inTree.getRootedTreeDistance();
234         }
235
236         setBranchLengthTraversalLimit(branchLengthTraversalLimit);
237      }
238   }
239
240
241   //---------------------------------------------------------------------------
242   public TreeDisplaySettings setShowBranchLengthTraversalLimit(boolean inValue)
243   {
244      get(SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT).setValue(inValue);
245      return this;
246   }
247
248   //---------------------------------------------------------------------------
249   public Boolean getShowBranchLengthTraversalLimit()
250   {
251      return (Boolean) get(SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT).getValue();
252   }
253
254
255   //---------------------------------------------------------------------------
256   /**
257    While dynamically adjusting the branch length traversal limit to group closely
258    related leaf nodes, this callback, if defined, is invoked so the changing BLTL
259    value can be displayed.
260    * @param inValue the javascript method to invoke (with the new BLTL value, and
261    *                the drag handle object as arguments)
262    * @return this Settings object to allow method chaining
263    */
264   public TreeDisplaySettings setBLTL_OnChangeCallback(String inValue)
265   {
266      get(BLTL_ONCHANGE_CALLBACK).setValue(inValue);
267      return this;
268   }
269
270   //---------------------------------------------------------------------------
271   public String getBLTL_OnChangeCallback()
272   {
273      return (String) get(BLTL_ONCHANGE_CALLBACK).getValue();
274   }
275
276
277   //---------------------------------------------------------------------------
278   /**
279    When the dynamic adjustment of the branch length traversal limit to group closely
280    related leaf nodes is completed (end of drag event), this callback, if defined, is invoked.
281    * @param inValue the javascript method to invoke (with the new BLTL value, and
282    *                the drag handle object as arguments)
283    * @return this Settings object to allow method chaining
284    */
285   public TreeDisplaySettings setBLTL_SelectionCompleteCallback(String inValue)
286   {
287      get(BLTL_SELECTION_COMPLETE_CALLBACK).setValue(inValue);
288      return this;
289   }
290
291   //---------------------------------------------------------------------------
292   public String getBLTL_SelectionCompleteCallback()
293   {
294      return (String) get(BLTL_SELECTION_COMPLETE_CALLBACK).getValue();
295   }
296
297
298   //---------------------------------------------------------------------------
299   public TreeDisplaySettings setFont(Font inValue)
300   {
301      CharSequence value = null;
302
303      if (inValue != null)
304      {
305         // TODO: This logic should be put in some utility function like FontUtil.encode()
306         String style;
307         switch (inValue.getStyle())
308         {
309            case Font.PLAIN:
310               style = "PLAIN";
311               break;
312            case Font.BOLD:
313               style = "BOLD";
314               break;
315            case Font.ITALIC:
316               style = "ITALIC";
317               break;
318            case 3:
319               style = "BOLDITALIC";
320               break;
321            default:
322               throw new ProgrammingException("Unexpected Font style: " + inValue.getStyle() + "!");
323         }
324
325         StringBuilderPlus stringValue = new StringBuilderPlus().setDelimiter("-");
326
327         stringValue.append(inValue.getFontName())
328               .delimitedAppend(style)
329               .delimitedAppend(inValue.getSize());
330         value = stringValue;
331      }
332
333      get(FONT).setValue(value != null ? value.toString() : null);
334      return this;
335   }
336
337   //---------------------------------------------------------------------------
338   public Font getFont()
339   {
340      Font value = null;
341
342      String stringValue = (String) get(FONT).getValue();
343      if (StringUtil.isSet(stringValue))
344      {
345         value = Font.decode(stringValue);
346      }
347
348      return value;
349   }
350
351
352   //---------------------------------------------------------------------------
353   public TreeDisplaySettings setLeafLabelLeftPadding(GfxSize inValue)
354   {
355      if (null == inValue)
356      {
357         throw new InvalidValueException("The leaf label left padding cannot be set to null!");
358      }
359
360      get(LEAF_LABEL_LEFT_PADDING).setValue(inValue != null ? inValue.toInt(GfxUnits.pixels) : null);
361      return this;
362   }
363
364   //---------------------------------------------------------------------------
365   public GfxSize getLeafLabelLeftPadding()
366   {
367      GfxSize padding = null;
368
369      Integer pixelValue = (Integer) get(LEAF_LABEL_LEFT_PADDING).getValue();
370      if (pixelValue != null)
371      {
372         padding = GfxSize.allocate(pixelValue, GfxUnits.pixels);
373      }
374
375      return padding;
376   }
377
378
379   //---------------------------------------------------------------------------
380   public TreeDisplaySettings setScaleTickHeight(int inValue)
381   {
382      get(SCALE_TICK_HEIGHT).setValue(inValue);
383      return this;
384   }
385
386   //---------------------------------------------------------------------------
387   public Integer getScaleTickHeight()
388   {
389      return (Integer) get(SCALE_TICK_HEIGHT).getValue();
390   }
391
392
393   //---------------------------------------------------------------------------
394   public TreeDisplaySettings setScalePaddingTop(GfxSize inValue)
395   {
396      if (null == inValue)
397      {
398         throw new InvalidValueException("The scale padding top cannot be set to null!");
399      }
400
401      get(SCALE_PADDING_TOP).setValue(inValue != null ? inValue.toInt(GfxUnits.pixels) : null);
402      return this;
403   }
404
405   //---------------------------------------------------------------------------
406   public GfxSize getScalePaddingTop()
407   {
408      GfxSize padding = null;
409
410      Integer pixelValue = (Integer) get(SCALE_PADDING_TOP).getValue();
411      if (pixelValue != null)
412      {
413         padding = GfxSize.allocate(pixelValue, GfxUnits.pixels);
414      }
415
416      return padding;
417   }
418
419}