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 }