001    /**
002     * ========================================
003     * JFreeReport : a free Java report library
004     * ========================================
005     *
006     * Project Info:  http://reporting.pentaho.org/
007     *
008     * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009     *
010     * This library is free software; you can redistribute it and/or modify it under the terms
011     * of the GNU Lesser General Public License as published by the Free Software Foundation;
012     * either version 2.1 of the License, or (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016     * See the GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public License along with this
019     * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020     * Boston, MA 02111-1307, USA.
021     *
022     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023     * in the United States and other countries.]
024     *
025     * ------------
026     * $Id: PreviewPaneUtilities.java 3525 2007-10-16 11:43:48Z tmorgner $
027     * ------------
028     * (C) Copyright 2000-2005, by Object Refinery Limited.
029     * (C) Copyright 2005-2007, by Pentaho Corporation.
030     */
031    
032    package org.jfree.report.modules.gui.swing.preview;
033    
034    import java.awt.Component;
035    import java.awt.FlowLayout;
036    import java.awt.Insets;
037    import java.awt.event.ActionEvent;
038    import java.util.ArrayList;
039    import java.util.Arrays;
040    import java.util.HashMap;
041    import java.util.Iterator;
042    import javax.swing.AbstractAction;
043    import javax.swing.Action;
044    import javax.swing.Icon;
045    import javax.swing.JButton;
046    import javax.swing.JComboBox;
047    import javax.swing.JMenu;
048    import javax.swing.JPanel;
049    import javax.swing.JToolBar;
050    
051    import org.jfree.report.modules.gui.common.DefaultIconTheme;
052    import org.jfree.report.modules.gui.common.IconTheme;
053    import org.jfree.report.modules.gui.swing.common.ActionFactory;
054    import org.jfree.report.modules.gui.swing.common.ActionPlugin;
055    import org.jfree.report.modules.gui.swing.common.ActionPluginMenuComparator;
056    import org.jfree.report.modules.gui.swing.common.DefaultActionFactory;
057    import org.jfree.report.modules.gui.swing.common.ExportActionPlugin;
058    import org.jfree.report.modules.gui.swing.common.SwingCommonModule;
059    import org.jfree.report.modules.gui.swing.common.SwingGuiContext;
060    import org.jfree.report.modules.gui.swing.preview.actions.ControlAction;
061    import org.jfree.report.modules.gui.swing.preview.actions.ControlActionPlugin;
062    import org.jfree.report.modules.gui.swing.preview.actions.ExportAction;
063    import org.jfree.report.modules.gui.swing.preview.actions.ZoomAction;
064    import org.jfree.report.modules.gui.swing.preview.actions.ZoomListActionPlugin;
065    import org.jfree.report.util.TextUtilities;
066    import org.jfree.ui.FloatingButtonEnabler;
067    import org.jfree.ui.KeyedComboBoxModel;
068    import org.jfree.ui.action.ActionButton;
069    import org.jfree.ui.action.ActionMenuItem;
070    import org.jfree.util.Configuration;
071    import org.jfree.util.Log;
072    import org.jfree.util.ObjectUtilities;
073    
074    /**
075     * Creation-Date: 17.11.2006, 15:06:51
076     *
077     * @author Thomas Morgner
078     */
079    public class PreviewPaneUtilities
080    {
081      /**
082       * A zoom select action.
083       */
084      private static class ZoomSelectAction extends AbstractAction
085      {
086        private KeyedComboBoxModel source;
087        private PreviewPane pane;
088    
089        /**
090         * Creates a new action.
091         */
092        private ZoomSelectAction(final KeyedComboBoxModel source,
093                                final PreviewPane pane)
094        {
095          this.source = source;
096          this.pane = pane;
097        }
098    
099        /**
100         * Invoked when an action occurs.
101         *
102         * @param e the event.
103         */
104        public void actionPerformed(final ActionEvent e)
105        {
106          final Double selected = (Double) source.getSelectedKey();
107          if (selected != null)
108          {
109            pane.setZoom(selected.doubleValue());
110          }
111          else
112          {
113            Log.warn ("No selected key! : " + pane.getZoom());
114          }
115        }
116      }
117    
118      private static final String ICON_THEME_CONFIG_KEY = "org.jfree.report.modules.gui.common.IconTheme";
119      private static final String ACTION_FACTORY_CONFIG_KEY = "org.jfree.report.modules.gui.swing.preview.ActionFactory";
120      private static final String CATEGORY_PREFIX = "org.jfree.report.modules.gui.swing.category.";
121    
122      private PreviewPaneUtilities()
123      {
124      }
125    
126      public static JMenu createMenu(final ActionCategory cat)
127      {
128        final JMenu menu = new JMenu();
129        menu.setText(cat.getDisplayName());
130        final Integer mnemonicKey = cat.getMnemonicKey();
131        if (mnemonicKey != null)
132        {
133          menu.setMnemonic(mnemonicKey.intValue());
134        }
135        final String toolTip = cat.getShortDescription();
136        if (toolTip != null && toolTip.length() > 0)
137        {
138          menu.setToolTipText(toolTip);
139        }
140        return menu;
141      }
142    
143    
144      public static int buildMenu(final JMenu menu,
145                                  final ActionPlugin[] actions,
146                                  final PreviewPane pane)
147      {
148        if (actions.length == 0)
149        {
150          return 0;
151        }
152    
153        Arrays.sort(actions, new ActionPluginMenuComparator());
154        boolean separatorPending = false;
155        int count = 0;
156        for (int i = 0; i < actions.length; i++)
157        {
158          final ActionPlugin actionPlugin = actions[i];
159          if (actionPlugin.isAddToMenu() == false)
160          {
161            continue;
162          }
163    
164          if (count > 0 && separatorPending)
165          {
166            menu.addSeparator();
167            separatorPending = false;
168          }
169    
170          if (actionPlugin instanceof ExportActionPlugin)
171          {
172            final ExportActionPlugin exportPlugin = (ExportActionPlugin) actionPlugin;
173            final ExportAction action = new ExportAction(exportPlugin, pane);
174            menu.add(new ActionMenuItem(action));
175            count += 1;
176          }
177          else if (actionPlugin instanceof ControlActionPlugin)
178          {
179            final ControlActionPlugin controlPlugin = (ControlActionPlugin) actionPlugin;
180            final ControlAction action = new ControlAction(controlPlugin, pane);
181            menu.add(new ActionMenuItem(action));
182            count += 1;
183          }
184          else if (actionPlugin instanceof ZoomListActionPlugin)
185          {
186            buildViewMenu(menu, pane);
187          }
188    
189          if (actionPlugin.isSeparated())
190          {
191            separatorPending = true;
192          }
193    
194        }
195        return count;
196      }
197    
198      private static void buildViewMenu(final JMenu zoom, final PreviewPane pane)
199      {
200        final double[] zoomFactors = pane.getZoomFactors();
201        for (int i = 0; i < zoomFactors.length; i++)
202        {
203          final double factor = zoomFactors[i];
204          zoom.add(new ActionMenuItem(new ZoomAction(factor, pane)));
205        }
206      }
207    
208      public static void addActionsToToolBar(final JToolBar toolBar,
209                                             final ActionPlugin[] reportActions,
210                                             final PreviewPane pane)
211      {
212        if (reportActions == null)
213        {
214          return;
215        }
216    
217        boolean separatorPending = false;
218        int count = 0;
219    
220        for (int i = 0; i < reportActions.length; i++)
221        {
222          final ActionPlugin actionPlugin = reportActions[i];
223          if (actionPlugin.isAddToToolbar() == false)
224          {
225            continue;
226          }
227    
228          if (count > 0 && separatorPending)
229          {
230            toolBar.addSeparator();
231            separatorPending = false;
232          }
233    
234          if (actionPlugin instanceof ExportActionPlugin)
235          {
236            final ExportActionPlugin exportPlugin = (ExportActionPlugin) actionPlugin;
237            final ExportAction action = new ExportAction(exportPlugin, pane);
238            toolBar.add(createButton(action, pane.getSwingGuiContext()));
239            count += 1;
240          }
241          else if (actionPlugin instanceof ControlActionPlugin)
242          {
243            final ControlActionPlugin controlPlugin = (ControlActionPlugin) actionPlugin;
244            final ControlAction action = new ControlAction(controlPlugin, pane);
245            toolBar.add(createButton(action, pane.getSwingGuiContext()));
246            count += 1;
247          }
248          else if (actionPlugin instanceof ZoomListActionPlugin)
249          {
250            final JPanel zoomPane = new JPanel();
251            zoomPane.setLayout(new FlowLayout(FlowLayout.LEFT));
252            zoomPane.add(createZoomSelector(pane));
253            toolBar.add(zoomPane);
254            count += 1;
255          }
256    
257          if (actionPlugin.isSeparated())
258          {
259            separatorPending = true;
260          }
261        }
262      }
263    
264    
265      private static JComboBox createZoomSelector(final PreviewPane pane)
266      {
267        final JComboBox zoomSelect = new JComboBox(pane.getZoomModel());
268        zoomSelect.addActionListener(new ZoomSelectAction(pane.getZoomModel(), pane));
269        zoomSelect.setAlignmentX(Component.RIGHT_ALIGNMENT);
270        return zoomSelect;
271      }
272    
273      /**
274       * Creates a button using the given action properties for the button's
275       * initialisation.
276       *
277       * @param action the action used to set up the button.
278       * @return a button based on the supplied action.
279       */
280      private static JButton createButton(final Action action,
281                                          final SwingGuiContext swingGuiContext)
282      {
283        final JButton button = new ActionButton(action);
284        boolean needText = true;
285        if (isLargeButtonsEnabled(swingGuiContext))
286        {
287          final Icon icon = (Icon) action.getValue(SwingCommonModule.LARGE_ICON_PROPERTY);
288          if (icon != null && (icon.getIconHeight() > 1 && icon.getIconHeight() > 1))
289          {
290            button.setIcon(icon);
291            needText = false;
292          }
293        }
294        else
295        {
296          final Icon icon = (Icon) action.getValue(Action.SMALL_ICON);
297          if (icon != null && (icon.getIconHeight() > 1 && icon.getIconHeight() > 1))
298          {
299            button.setIcon(icon);
300            needText = false;
301          }
302        }
303    
304        if (needText)
305        {
306          final Object value = action.getValue(Action.NAME);
307          if (value != null)
308          {
309            button.setText(String.valueOf(value));
310          }
311        }
312        else
313        {
314          button.setText(null);
315          button.setMargin(new Insets(0, 0, 0, 0));
316        }
317    
318        FloatingButtonEnabler.getInstance().addButton(button);
319        return button;
320      }
321    
322      private static boolean isLargeButtonsEnabled(final SwingGuiContext swingGuiContext)
323      {
324        final Configuration configuration = swingGuiContext.getConfiguration();
325        if ("true".equals(configuration.getConfigProperty
326            ("org.jfree.report.modules.gui.swing.preview.LargeIcons")))
327        {
328          return true;
329        }
330        return false;
331      }
332    
333    
334      public static double getNextZoomOut(final double zoom,
335                                          final double[] zoomFactors)
336      {
337        if (zoom <= zoomFactors[0])
338        {
339          return (zoom * 2.0) / 3.0;
340        }
341    
342        final double largestZoom = zoomFactors[zoomFactors.length - 1];
343        if (zoom > largestZoom)
344        {
345          final double linear = (zoom * 2.0) / 3.0;
346          if (linear < largestZoom)
347          {
348            return largestZoom;
349          }
350          return linear;
351        }
352    
353        for (int i = zoomFactors.length - 1; i >= 0; i--)
354        {
355          final double factor = zoomFactors[i];
356          if (factor < zoom)
357          {
358            return factor;
359          }
360        }
361    
362        return (zoom * 2.0) / 3.0;
363      }
364    
365      public static double getNextZoomIn(final double zoom,
366                                         final double[] zoomFactors)
367      {
368        final double largestZoom = zoomFactors[zoomFactors.length - 1];
369        if (zoom >= largestZoom)
370        {
371          return (zoom * 1.5);
372        }
373    
374        final double smallestZoom = zoomFactors[0];
375        if (zoom < smallestZoom)
376        {
377          final double linear = (zoom * 1.5);
378          if (linear > smallestZoom)
379          {
380            return smallestZoom;
381          }
382          return linear;
383        }
384    
385        for (int i = 0; i < zoomFactors.length; i++)
386        {
387          final double factor = zoomFactors[i];
388          if (factor > zoom)
389          {
390            return factor;
391          }
392        }
393        return (zoom * 1.5);
394      }
395    
396    
397      public static IconTheme createIconTheme(final Configuration config)
398      {
399        final String themeClass = config.getConfigProperty(ICON_THEME_CONFIG_KEY);
400        final Object maybeTheme = ObjectUtilities.loadAndInstantiate(themeClass, PreviewPane.class, IconTheme.class);
401        final IconTheme iconTheme;
402        if (maybeTheme != null)
403        {
404          iconTheme = (IconTheme) maybeTheme;
405        }
406        else
407        {
408          iconTheme = new DefaultIconTheme();
409        }
410        iconTheme.initialize(config);
411        return iconTheme;
412      }
413    
414      public static ActionFactory createActionFactory(final Configuration config)
415      {
416        final String factoryClass = config.getConfigProperty(ACTION_FACTORY_CONFIG_KEY);
417        final Object maybeFactory = ObjectUtilities.loadAndInstantiate
418            (factoryClass, PreviewPane.class, ActionFactory.class);
419        final ActionFactory actionFactory;
420        if (maybeFactory != null)
421        {
422          actionFactory = (ActionFactory) maybeFactory;
423        }
424        else
425        {
426          actionFactory = new DefaultActionFactory();
427        }
428        return actionFactory;
429      }
430    
431      public static CategoryTreeItem[] buildMenuTree(final ActionCategory[] categories)
432      {
433        final CategoryTreeItem[] tree = new CategoryTreeItem[categories.length];
434        for (int i = 0; i < categories.length; i++)
435        {
436          final ActionCategory category = categories[i];
437          tree[i] = new CategoryTreeItem(category);
438        }
439    
440        for (int j = 0; j < tree.length; j++)
441        {
442          final CategoryTreeItem item = tree[j];
443          final String itemName = item.getName();
444          int parentWeight = 0;
445          CategoryTreeItem parent = null;
446          // now for each item, find the best parent item.
447          for (int k = 0; k < tree.length; k++)
448          {
449            if (k == j)
450            {
451              // never add yourself ..
452              continue;
453            }
454            final CategoryTreeItem treeItem = tree[k];
455            final String parentName = treeItem.getName();
456            if (itemName.startsWith(parentName) == false)
457            {
458              continue;
459            }
460            if (parentName.length() > parentWeight)
461            {
462              parent = treeItem;
463              parentWeight = parentName.length();
464            }
465          }
466    
467          item.setParent(parent);
468        }
469    
470        for (int j = 0; j < tree.length; j++)
471        {
472          final CategoryTreeItem item = tree[j];
473          final CategoryTreeItem parent = item.getParent();
474          if (parent != null)
475          {
476            parent.add(item);
477          }
478        }
479        return tree;
480      }
481    
482      public static HashMap loadActions(final SwingGuiContext swingGuiContext)
483      {
484        final HashMap actions = new HashMap();
485    
486        final Configuration configuration = swingGuiContext.getConfiguration();
487        final ActionCategory[] categories = loadCategories(swingGuiContext);
488        final ActionFactory factory = PreviewPaneUtilities.createActionFactory(configuration);
489    
490        for (int i = 0; i < categories.length; i++)
491        {
492          final ActionCategory category = categories[i];
493          actions.put(category,
494              factory.getActions(swingGuiContext, category.getName()));
495        }
496        return actions;
497      }
498    
499    
500      public static ActionCategory[] loadCategories(final SwingGuiContext swingGuiContext)
501      {
502        final ArrayList categories = new ArrayList();
503        final Configuration configuration = swingGuiContext.getConfiguration();
504        final Iterator keys = configuration.findPropertyKeys(CATEGORY_PREFIX);
505        while (keys.hasNext())
506        {
507          final String enableKey = (String) keys.next();
508          if (enableKey.endsWith(".enabled") == false)
509          {
510            continue;
511          }
512    
513          if ("true".equals(configuration.getConfigProperty(enableKey)) == false)
514          {
515            continue;
516          }
517    
518          final String base = enableKey.substring(0, enableKey.length() - ".enabled".length());
519          if (base.length() == 0)
520          {
521            continue;
522          }
523    
524          final String categoryKey = base.substring(CATEGORY_PREFIX.length());
525          final String className = configuration.getConfigProperty(base + ".class");
526          ActionCategory actionCategory;
527          if (className == null)
528          {
529            actionCategory = new ActionCategory();
530          }
531          else
532          {
533            actionCategory = (ActionCategory) ObjectUtilities.loadAndInstantiate
534                (className, PreviewPane.class, ActionCategory.class);
535            if (actionCategory == null)
536            {
537              actionCategory = new ActionCategory();
538            }
539          }
540    
541          final String positionText = configuration.getConfigProperty(base + ".position");
542          actionCategory.setPosition(TextUtilities.parseInt(positionText, 0));
543          actionCategory.setName(categoryKey);
544          actionCategory.setResourceBase(configuration.getConfigProperty(base + ".resource-base"));
545          actionCategory.setResourcePrefix(configuration.getConfigProperty(base + ".resource-prefix"));
546          actionCategory.initialize(swingGuiContext);
547          categories.add(actionCategory);
548        }
549    
550        return (ActionCategory[]) categories.toArray
551            (new ActionCategory[categories.size()]);
552      }
553    }