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,v 1.4 2007/04/01 18:49:31 taqua Exp $
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 public ZoomSelectAction(KeyedComboBoxModel source,
093 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(ActionCategory cat)
127 {
128 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(JMenu menu,
145 ActionPlugin[] actions,
146 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 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(JMenu zoom, PreviewPane pane)
199 {
200 final double[] zoomFactors = pane.getZoomFactors();
201 for (int i = 0; i < zoomFactors.length; i++)
202 {
203 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 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(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(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 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 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 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 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(Configuration config)
398 {
399 String themeClass = config.getConfigProperty(ICON_THEME_CONFIG_KEY);
400 Object maybeTheme = ObjectUtilities.loadAndInstantiate(themeClass, PreviewPane.class, IconTheme.class);
401 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(Configuration config)
415 {
416 final String factoryClass = config.getConfigProperty(ACTION_FACTORY_CONFIG_KEY);
417 Object maybeFactory = ObjectUtilities.loadAndInstantiate
418 (factoryClass, PreviewPane.class, ActionFactory.class);
419 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 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 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(SwingGuiContext swingGuiContext)
483 {
484 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(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 }