0001: package jimm.datavision.gui;
0002:
0003: import jimm.datavision.*;
0004: import jimm.datavision.field.Field;
0005: import jimm.datavision.source.DataSource;
0006: import jimm.datavision.layout.swing.SwingLE;
0007: import jimm.datavision.gui.sql.*;
0008: import jimm.datavision.gui.cmd.*;
0009: import jimm.util.I18N;
0010: import java.awt.*;
0011: import java.awt.event.*;
0012: import java.io.File;
0013: import java.util.*;
0014: import java.util.prefs.Preferences;
0015: import javax.swing.*;
0016:
0017: /**
0018: * The abstract superclass of {@link Report} designer windows and applets.
0019: *
0020: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
0021: */
0022: public abstract class Designer implements ActionListener, Observer {
0023:
0024: public static final int GRID_SIZE = 8;
0025:
0026: public static final int ALIGN_TOP = 0;
0027: public static final int ALIGN_MIDDLE = 1;
0028: public static final int ALIGN_BOTTOM = 2;
0029: public static final int ALIGN_LEFT = 3;
0030: public static final int ALIGN_CENTER = 4;
0031: public static final int ALIGN_RIGHT = 5;
0032: public static final int ALIGN_SNAP_TO_GRID = 6;
0033:
0034: public static final int SIZE_SAME_WIDTH = 0;
0035: public static final int SIZE_SAME_HEIGHT = 1;
0036: public static final int SIZE_SAME_SIZE = 2;
0037:
0038: protected static ArrayList designWindows = new ArrayList();
0039: protected static boolean exitWhenLastWindowClosed = true;
0040: protected static JFileChooser chooser;
0041:
0042: protected Report report;
0043: /** The frame may be null; it will be the parent of dialog boxes. */
0044: protected JFrame frame;
0045: /** The container of the root pane---the highest level widget. */
0046: protected RootPaneContainer rootPaneContainer; // Set by subclasses
0047: protected CommandHistory commandHistory;
0048: protected ArrayList sectionWidgets;
0049: protected String reportFilePath;
0050: protected JLayeredPane sectionContainer;
0051: protected ArrayList selectedFields;
0052: protected JScrollPane scroller;
0053: protected JMenuItem undoItem, redoItem, cutItem, copyItem,
0054: pasteItem, delSelectionItem, delGroupItem, delSectionItem,
0055: aggrItem, groupItem, sectItem, formatItem, borderItem,
0056: boundsItem, defaultFormatItem, tableJoinItem,
0057: sqlQueryTextItem, connectionItem, selectRecordsItem,
0058: sortByItem, groupByItem, runItem, exportItem,
0059: subreportItem;
0060: protected JMenu alignSubmenu, sizeSubmenu, paperSizeSubmenu;
0061: protected boolean placingNewTextField;
0062: protected boolean ignoreKeys;
0063:
0064: /**
0065: * Adds a window to the list of open design windows. Also notifies the
0066: * error handler class that we it should use the GUI to display error
0067: * messages.
0068: *
0069: * @param win a design window
0070: */
0071: public static void addWindow(Designer win) {
0072: designWindows.add(win);
0073: }
0074:
0075: /**
0076: * Returns the design window associated with the specified report, or
0077: * <code>null</code> if one is not found.
0078: *
0079: * @param r a report
0080: * @return a design window or <code>null</code> if one is not found
0081: */
0082: public static Designer findWindowFor(Report r) {
0083: for (Iterator iter = designWindows.iterator(); iter.hasNext();) {
0084: Designer win = (Designer) iter.next();
0085: if (win.report == r)
0086: return win;
0087: }
0088: return null;
0089: }
0090:
0091: /**
0092: * Sets value of flag that determines if the application should exit
0093: * when the last design window is closed. The default value is
0094: * <code>true</code>.
0095: */
0096: public static void setExitWhenLastWinClosed(boolean exit) {
0097: exitWhenLastWindowClosed = exit;
0098: }
0099:
0100: /**
0101: * Deletes a window from the list of windows. If there are no more design
0102: * windows left and <var>exitWhenLastWindowClosed</var> is
0103: * <code>true</code> (which it is by default), exits the application.
0104: *
0105: * @param win a design window
0106: */
0107: protected static void deleteWindow(Designer win) {
0108: designWindows.remove(win);
0109: if (designWindows.isEmpty() && exitWhenLastWindowClosed)
0110: System.exit(0);
0111: }
0112:
0113: /**
0114: * Closes each open design window (unless user cancels). If all are closed,
0115: * quits the application.
0116: */
0117: protected static void maybeQuit() {
0118: // Can't use iterator over original list 'cause we're deleting items
0119: // from the list.
0120: ArrayList copy = (ArrayList) designWindows.clone();
0121: for (Iterator iter = copy.iterator(); iter.hasNext();)
0122: ((Designer) iter.next()).maybeClose();
0123: }
0124:
0125: public static JFileChooser getChooser() {
0126: if (chooser == null)
0127: chooser = new JFileChooser();
0128: return chooser;
0129: }
0130:
0131: /**
0132: * Sets the current directory for the chooser based on
0133: * the directory stored in the java preferences for
0134: * value 'key'. Default key is reportDir
0135: */
0136: public static void setPrefsDir(JFileChooser jfc, String key) {
0137: // Set the starting report directory (null is default home directory)
0138: // Store the report directory in the preferences for this package
0139: if (jfc != null) {
0140: if (key == null || key.trim().equals(""))
0141: key = "reportDir";
0142: Preferences prefs = Preferences.userRoot().node(
0143: "/jimm/datavision");
0144: String dir = prefs.get(key, null);
0145: if (dir != null)
0146: jfc.setCurrentDirectory(new File(dir));
0147: }
0148: }
0149:
0150: /**
0151: * Store the JFileChoosers current directory in the preferences
0152: * this 'remembers' the last directory used so the user isn't
0153: * having to navigate to their report directory each time.
0154: */
0155: public static void savePrefsDir(JFileChooser jfc, String key) {
0156: if (jfc != null) {
0157: if (key == null || key.trim().equals(""))
0158: key = "reportDir";
0159: String selectedFile = jfc.getSelectedFile().getPath();
0160: if (selectedFile != null) {
0161: Preferences prefs = Preferences.userRoot().node(
0162: "/jimm/datavision");
0163: String dir = prefs.get(key, null);
0164: // If the file path has changed then store it in the
0165: // preferences
0166: String jfcap = jfc.getSelectedFile().getAbsolutePath();
0167: if (jfcap != null) {
0168: File f = new File(jfcap);
0169: String newdir = f.getParent();
0170: if (newdir == null)
0171: newdir = dir;
0172: if (newdir != null) {
0173: boolean changed = true;
0174: if (dir != null && newdir.compareTo(dir) == 0)
0175: changed = false;
0176: if (changed)
0177: prefs.put(key, newdir);
0178: }
0179: }
0180: }
0181: }
0182: }
0183:
0184: /**
0185: * Constructor. Reads the named report file or, if it's <code>null</code>,
0186: * creates a new, empty report.
0187: *
0188: * @param f an XML report file; may be <code>null</code>
0189: * @param databasePassword string to give to report; OK if it's
0190: * <code>null</code>
0191: * @param rpc the root pane container (the <code>JFrame</code> or
0192: * <code>JApplet</code>)
0193: * @param jframe the design window; may be <code>null</code> (for example,
0194: * when the designer is an applet)
0195: */
0196: public Designer(File f, String databasePassword,
0197: RootPaneContainer rpc, JFrame jframe) {
0198: // In subclasses, calling super(fileName, password, rpc, frame)
0199: // is a pain when frame == rpc because it is difficult to create
0200: // the frame, assign it to a temp, and pass the temp into both frame
0201: // and rpc. Therefore, when you pass in a frame but no rpc we assume
0202: // frame == rpc (because a JFrame is a RootPaneContainer).
0203: frame = jframe;
0204: rootPaneContainer = rpc;
0205: if (frame != null && rootPaneContainer == null)
0206: rootPaneContainer = frame;
0207:
0208: ErrorHandler.useGUI(true); // Must set before opening report
0209: selectedFields = new ArrayList();
0210:
0211: // Must create command history before asking for database information
0212: // because that can cause a command to be created.
0213: commandHistory = new CommandHistory();
0214:
0215: StatusDialog statusDialog = new StatusDialog(frame, I18N
0216: .get("DesignWin.status_title"), false, f == null ? I18N
0217: .get("DesignWin.creating_empty") : I18N
0218: .get("DesignWin.reading_xml"));
0219:
0220: // readReport() sets our report ivar as a side-effect.
0221: boolean askForDbConnInfo = readReport(f, databasePassword);
0222:
0223: if (report == null
0224: || (report.getDataSource() == null && !askForDbConnInfo)) {
0225: // User cancelled when asked for password. Close this window and
0226: // quit if this is the only report open.
0227: statusDialog.dispose();
0228: closeMe();
0229: Designer.deleteWindow(this ); // Will quit if no other windows
0230: return;
0231: }
0232:
0233: if (askForDbConnInfo) {
0234: openDbConnWin(true); // Modally ask for database connection info
0235: if (report.getDataSource() == null) {
0236: // User cancelled. Close this window and quit if this is the
0237: // only report open.
0238: statusDialog.dispose();
0239: closeMe();
0240: Designer.deleteWindow(this ); // Will quit if no other windows
0241: return;
0242: }
0243: }
0244:
0245: statusDialog.update(I18N.get("DesignWin.building_win"));
0246:
0247: buildWindow();
0248: commandHistory.setMenuItems(undoItem, redoItem);
0249: enableMenuItems();
0250:
0251: // Might be using this code for dragging to select multiple fields.
0252: // SelectionHandler h = new SelectionHandler();
0253: // frame.addMouseListener(h);
0254: // frame.addMouseMotionListener(h);
0255:
0256: Designer.addWindow(this );
0257:
0258: // Let the format window start a thread to load font names. It will
0259: // only load them the first time this is called, but that's not our
0260: // problem, now is it?
0261: FormatWin.loadFontChoices();
0262:
0263: statusDialog.dispose();
0264: }
0265:
0266: protected void closeMe() {
0267: if (frame != null)
0268: frame.dispose();
0269: }
0270:
0271: public void update(Observable o, Object arg) {
0272: enableMenuItems();
0273: }
0274:
0275: /**
0276: * Performs a command.
0277: *
0278: * @param cmd a command
0279: */
0280: public void performCommand(Command cmd) {
0281: commandHistory.perform(cmd);
0282: }
0283:
0284: /**
0285: * Adds to the command history a command that has already been performed.
0286: *
0287: * @param cmd a command
0288: */
0289: public void addCommand(Command cmd) {
0290: commandHistory.add(cmd);
0291: }
0292:
0293: /**
0294: * Reads the named report file or, if it's <code>null</code>, creates
0295: * a new, empty report. Returns <code>true</code> if we need to ask
0296: * the user for connection info because this is a new report.
0297: *
0298: * @param f report XML file
0299: * @param databasePassword string to give to report; OK if it's
0300: * <code>null</code>
0301: * @return <code>true</code> if we need to ask the user for connection info
0302: */
0303: protected boolean readReport(File f, String databasePassword) {
0304: if (f != null) {
0305: reportFilePath = f.getPath();
0306: report = new Report();
0307: report.setDatabasePassword(databasePassword);
0308: try {
0309: report.read(f);
0310: return false;
0311: } catch (UserCancellationException uce) {
0312: report = null; // Signal report open cancelled
0313: return true; // Doesn't matter what we return
0314: } catch (Exception e) {
0315: ErrorHandler.error(e);
0316: report = new Report();
0317: return true;
0318: }
0319: } else {
0320: report = new Report();
0321: reportFilePath = null;
0322: return true;
0323: }
0324: }
0325:
0326: public void setIgnoreKeys(boolean ignore) {
0327: ignoreKeys = ignore;
0328: }
0329:
0330: /**
0331: * Builds the window components.
0332: */
0333: protected void buildWindow() {
0334: rootPaneContainer.getRootPane().setJMenuBar(buildMenuBar());
0335: buildSections();
0336: }
0337:
0338: /**
0339: * Builds the window menu bar.
0340: */
0341: protected JMenuBar buildMenuBar() {
0342: JMenuBar bar = new JMenuBar();
0343: bar.add(buildFileMenu());
0344: bar.add(buildEditMenu());
0345: bar.add(buildInsertMenu());
0346: bar.add(buildFormatMenu());
0347: bar.add(buildDatabaseMenu());
0348: bar.add(buildReportMenu());
0349: bar.add(Box.createHorizontalGlue());
0350: bar.add(buildHelpMenu());
0351:
0352: return bar;
0353: }
0354:
0355: /**
0356: * Builds and returns the "File" menu.
0357: *
0358: * @return a menu
0359: */
0360: protected JMenu buildFileMenu() {
0361: JMenu menu = MenuUtils.readMenu("DesignWin.menu_file");
0362:
0363: MenuUtils.addToMenu(this , menu, "DesignWin.menu_file_new");
0364: MenuUtils.addToMenu(this , menu, "DesignWin.menu_file_open");
0365: menu.addSeparator();
0366: MenuUtils.addToMenu(this , menu, "DesignWin.menu_file_save");
0367: MenuUtils.addToMenu(this , menu, "DesignWin.menu_file_save_as");
0368:
0369: menu.addSeparator();
0370:
0371: ButtonGroup orientationGroup = new ButtonGroup();
0372: ButtonGroup nameGroup = new ButtonGroup();
0373: menu.add(paperSizeSubmenu = MenuUtils.buildPaperSizeMenu(this ,
0374: report.getPaperFormat(), orientationGroup, nameGroup));
0375: menu.addSeparator();
0376: MenuUtils.addToMenu(this , menu, "DesignWin.menu_file_close");
0377: MenuUtils.addToMenu(this , menu, "DesignWin.menu_file_quit");
0378:
0379: return menu;
0380: }
0381:
0382: /**
0383: * Builds and returns the "Edit" menu.
0384: *
0385: * @return a menu
0386: */
0387: protected JMenu buildEditMenu() {
0388: JMenu menu = MenuUtils.readMenu("DesignWin.menu_edit");
0389:
0390: undoItem = MenuUtils.addToMenu(this , menu,
0391: "DesignWin.menu_edit_undo");
0392: redoItem = MenuUtils.addToMenu(this , menu,
0393: "DesignWin.menu_edit_redo");
0394: menu.addSeparator();
0395: cutItem = MenuUtils.addToMenu(this , menu,
0396: "DesignWin.menu_edit_cut");
0397: copyItem = MenuUtils.addToMenu(this , menu,
0398: "DesignWin.menu_edit_copy");
0399: pasteItem = MenuUtils.addToMenu(this , menu,
0400: "DesignWin.menu_edit_paste");
0401: menu.addSeparator();
0402: delSelectionItem = MenuUtils.addToMenu(this , menu,
0403: "DesignWin.menu_edit_del_fields");
0404: menu.addSeparator();
0405: delGroupItem = MenuUtils.addToMenu(this , menu,
0406: "DesignWin.menu_edit_del_group");
0407: delSectionItem = MenuUtils.addToMenu(this , menu,
0408: "DesignWin.menu_edit_del_section");
0409:
0410: return menu;
0411: }
0412:
0413: /**
0414: * Builds and returns the "Insert" menu.
0415: *
0416: * @return a menu
0417: */
0418: protected JMenu buildInsertMenu() {
0419: JMenu menu = MenuUtils.readMenu("DesignWin.menu_insert");
0420:
0421: MenuUtils.addToMenu(this , menu, "DesignWin.menu_insert_column");
0422: MenuUtils.addToMenu(this , menu, "DesignWin.menu_insert_text");
0423: MenuUtils
0424: .addToMenu(this , menu, "DesignWin.menu_insert_formula");
0425: MenuUtils.addToMenu(this , menu, "DesignWin.menu_insert_param");
0426: MenuUtils
0427: .addToMenu(this , menu, "DesignWin.menu_insert_usercol");
0428: MenuUtils
0429: .addToMenu(this , menu, "DesignWin.menu_insert_special");
0430: aggrItem = MenuUtils.addToMenu(this , menu,
0431: "DesignWin.menu_insert_aggr");
0432: menu.addSeparator();
0433: MenuUtils.addToMenu(this , menu, "DesignWin.menu_insert_image");
0434: MenuUtils.addToMenu(this , menu, "DesignWin.menu_insert_line");
0435: menu.addSeparator();
0436: groupItem = MenuUtils.addToMenu(this , menu,
0437: "DesignWin.menu_insert_group");
0438: sectItem = MenuUtils.addToMenu(this , menu,
0439: "DesignWin.menu_insert_section");
0440: subreportItem = MenuUtils.addToMenu(this , menu,
0441: "DesignWin.menu_insert_subreport");
0442:
0443: return menu;
0444: }
0445:
0446: /**
0447: * Builds and returns the "Format" menu.
0448: *
0449: * @return a menu
0450: */
0451: protected JMenu buildFormatMenu() {
0452: JMenu menu = MenuUtils.readMenu("DesignWin.menu_format");
0453:
0454: formatItem = MenuUtils.addToMenu(this , menu,
0455: "DesignWin.menu_format_format");
0456: borderItem = MenuUtils.addToMenu(this , menu,
0457: "DesignWin.menu_format_border");
0458: menu.addSeparator();
0459: boundsItem = MenuUtils.addToMenu(this , menu,
0460: "DesignWin.menu_format_bounds");
0461: menu.add(alignSubmenu = MenuUtils.buildAlignMenu(this , null));
0462: menu.add(sizeSubmenu = MenuUtils.buildSizeMenu(this , null));
0463: menu.addSeparator();
0464: defaultFormatItem = MenuUtils.addToMenu(this , menu,
0465: "DesignWin.menu_format_default");
0466:
0467: return menu;
0468: }
0469:
0470: /**
0471: * Builds and returns the "Database" menu.
0472: *
0473: * @return a menu
0474: */
0475: protected JMenu buildDatabaseMenu() {
0476: JMenu menu = MenuUtils.readMenu("DesignWin.menu_database");
0477:
0478: tableJoinItem = MenuUtils.addToMenu(this , menu,
0479: "DesignWin.menu_database_linker");
0480: menu.addSeparator();
0481: // MenuUtils.addToMenu(this, menu, "DesignWin.menu_database_join");
0482: // menu.addSeparator();
0483: sqlQueryTextItem = MenuUtils.addToMenu(this , menu,
0484: "DesignWin.menu_database_sql");
0485: menu.addSeparator();
0486: connectionItem = MenuUtils.addToMenu(this , menu,
0487: "DesignWin.menu_connection");
0488:
0489: return menu;
0490: }
0491:
0492: /**
0493: * Builds and returns the "Report" menu.
0494: *
0495: * @return a menu
0496: */
0497: protected JMenu buildReportMenu() {
0498: JMenu menu = MenuUtils.readMenu("DesignWin.menu_report");
0499:
0500: runItem = MenuUtils.addToMenu(this , menu,
0501: "DesignWin.menu_report_run");
0502: exportItem = MenuUtils.addToMenu(this , menu,
0503: "DesignWin.menu_report_export");
0504: menu.addSeparator();
0505: selectRecordsItem = MenuUtils.addToMenu(this , menu,
0506: "DesignWin.menu_report_select");
0507: sortByItem = MenuUtils.addToMenu(this , menu,
0508: "DesignWin.menu_report_sort");
0509: groupByItem = MenuUtils.addToMenu(this , menu,
0510: "DesignWin.menu_report_group");
0511: MenuUtils.addToMenu(this , menu,
0512: "DesignWin.menu_report_start_formula");
0513: menu.addSeparator();
0514: MenuUtils.addToMenu(this , menu,
0515: "DesignWin.menu_report_scripting_langs");
0516: MenuUtils
0517: .addToMenu(this , menu, "DesignWin.menu_report_summary");
0518:
0519: return menu;
0520: }
0521:
0522: /**
0523: * Builds and returns the "Help" menu.
0524: *
0525: * @return a menu
0526: */
0527: protected JMenu buildHelpMenu() {
0528: JMenu menu = MenuUtils.readMenu("DesignWin.menu_help");
0529:
0530: MenuUtils.addToMenu(this , menu, "DesignWin.menu_help_help");
0531: menu.addSeparator();
0532: MenuUtils.addToMenu(this , menu, "DesignWin.menu_help_about");
0533:
0534: return menu;
0535: }
0536:
0537: /**
0538: * Builds the sections and adds them to the section container.
0539: */
0540: protected void buildSections() {
0541: rootPaneContainer.getContentPane()
0542: .setLayout(new BorderLayout());
0543:
0544: sectionWidgets = new ArrayList();
0545: sectionContainer = new JLayeredPane();
0546: sectionContainer.setLayout(new DesignWinLayout());
0547:
0548: // Can't just call report.each_section because we want to append
0549: // '(a)', '(b)', etc. if there is more than one section in the group.
0550: buildSectionsInArea(report.headers());
0551: buildSectionsInArea(report.pageHeaders());
0552:
0553: // Add headers for each group.
0554: for (Iterator iter = report.groups(); iter.hasNext();) {
0555: Group g = (Group) iter.next();
0556: buildSectionsInArea(g.headers());
0557: }
0558:
0559: buildSectionsInArea(report.details());
0560:
0561: // Add footers for each group, where the groups are in reverse order.
0562: for (Iterator iter = report.groupsReversed(); iter.hasNext();) {
0563: Group g = (Group) iter.next();
0564: buildSectionsInArea(g.footers());
0565: }
0566:
0567: buildSectionsInArea(report.footers());
0568: buildSectionsInArea(report.pageFooters());
0569:
0570: renameSectionWidgets(); // Muck with section names (e.g., add " (a)")
0571:
0572: // Put sectionContainer within scroller and add scroller to content
0573: // pane.
0574: int width = SectionWidget.LHS_WIDTH
0575: + (int) report.getPaperFormat().getWidth();
0576: scroller = new JScrollPane(sectionContainer);
0577: scroller.setPreferredSize(new Dimension(width + 4, 400));
0578: rootPaneContainer.getContentPane().add(scroller,
0579: BorderLayout.CENTER);
0580: }
0581:
0582: /**
0583: * Builds the section widgets for the sections in an area and adds them to the
0584: * section container. We name the widgets with a separate call to {@link
0585: * #renameSectionWidgets}.
0586: * <p>
0587: * We also start observing each section. Though our caller is called more than
0588: * once, it is OK to call addObserver() on an Observable multiple times.
0589: *
0590: * @param area contains sections
0591: */
0592: protected void buildSectionsInArea(SectionArea area) {
0593: for (Iterator iter = area.iterator(); iter.hasNext();) {
0594: Section sect = (Section) iter.next();
0595: sect.addObserver(this ); // Start observing changes
0596:
0597: SectionWidget sw = new SectionWidget(this , sect, "");
0598: sectionWidgets.add(sw);
0599:
0600: // Add to palette layer (one above default layer) because we want
0601: // to move field widgets below section widgets temporarily at the
0602: // end of a drag (see putDown()).
0603: sectionContainer.add(sw, JLayeredPane.PALETTE_LAYER);
0604: }
0605: }
0606:
0607: /**
0608: * Recalculates section names for all sections in the report. Calls {@link
0609: * #renameSectionWidgetsIn} for each group of sections.
0610: */
0611: protected void renameSectionWidgets() {
0612: renameSectionWidgetsIn(report.headers(), I18N
0613: .get("Report.report_header"), null);
0614: renameSectionWidgetsIn(report.pageHeaders(), I18N
0615: .get("Report.page_header"), null);
0616: int i = 1;
0617: for (Iterator iter = report.groups(); iter.hasNext(); ++i) {
0618: Group g = (Group) iter.next();
0619: renameSectionWidgetsIn(g.headers(), I18N
0620: .get("DesignWin.group")
0621: + " #" + i + ' ' + I18N.get("DesignWin.header"), g);
0622: }
0623: renameSectionWidgetsIn(report.details(), I18N
0624: .get("Report.detail"), null);
0625: i = 1;
0626: for (Iterator iter = report.groups(); iter.hasNext(); ++i) {
0627: Group g = (Group) iter.next();
0628: renameSectionWidgetsIn(g.footers(), I18N
0629: .get("DesignWin.group")
0630: + " #" + i + ' ' + I18N.get("DesignWin.footer"), g);
0631: }
0632: renameSectionWidgetsIn(report.footers(), I18N
0633: .get("Report.report_footer"), null);
0634: renameSectionWidgetsIn(report.pageFooters(), I18N
0635: .get("Report.page_footer"), null);
0636: }
0637:
0638: /**
0639: * Recalculates section names for a collection of sections. Called from {@link
0640: * #renameSectionWidgets}. Also sets the section's popup menu's first item
0641: * text.
0642: *
0643: * @param area a section area
0644: * @param prefix prepended to all section names in the collection
0645: * @param group the group containing this section list; may be
0646: * <code>null</code>
0647: */
0648: protected void renameSectionWidgetsIn(SectionArea area,
0649: String prefix, Group group) {
0650: SectionWidget firstSectionWidget = null;
0651: boolean firstSectionFixed = false;
0652: int i = 0;
0653: for (Iterator iter = area.iterator(); iter.hasNext(); ++i) {
0654: Section s = (Section) iter.next();
0655: SectionWidget sw = findSectionWidgetFor(s);
0656: if (firstSectionWidget == null) {
0657: sw.setDisplayName(prefix);
0658: firstSectionWidget = sw;
0659: } else {
0660: if (!firstSectionFixed) {
0661: firstSectionWidget.setDisplayName(prefix + " (a)");
0662: firstSectionFixed = true;
0663: }
0664: sw.setDisplayName(prefix + " (" + (char) ('a' + i)
0665: + ")");
0666: }
0667: sw.setPopupName(group == null ? prefix : group
0668: .getSelectableName());
0669: }
0670: }
0671:
0672: /**
0673: * Enables or disables menu items based on field and window state.
0674: */
0675: public void enableMenuItems() {
0676: int numSelected = countSelectedFields();
0677: boolean someFieldSelected = numSelected > 0;
0678: boolean multipleFieldsSelected = numSelected > 1;
0679: FieldWidget first = someFieldSelected ? (FieldWidget) selectedFields
0680: .get(0)
0681: : null;
0682:
0683: DataSource ds = report.getDataSource();
0684:
0685: // Edit menu
0686: Section s = (first == null) ? null : first.getSectionWidget()
0687: .getSection();
0688:
0689: cutItem.setEnabled(someFieldSelected);
0690: copyItem.setEnabled(someFieldSelected);
0691: pasteItem.setEnabled(!Clipboard.instance().isEmpty());
0692: delSelectionItem.setEnabled(someFieldSelected);
0693: delGroupItem.setEnabled(someFieldSelected
0694: && report.isInsideGroup(s));
0695: delSectionItem.setEnabled(someFieldSelected
0696: && !report.isOneOfAKind(s));
0697:
0698: // Insert menu
0699: if (numSelected == 1) { // One field is selected
0700: // Only enable aggregates if the selected field is a field
0701: // for which aggregates make sense.
0702: aggrItem.setEnabled(first.getField().canBeAggregated());
0703: } else
0704: aggrItem.setEnabled(false);
0705: sectItem.setEnabled(someFieldSelected);
0706: subreportItem.setEnabled(ds.canJoinTables());
0707:
0708: // Format menu
0709: if (someFieldSelected) {
0710: // Only enable if some field is formattable
0711: boolean enable = someSelectedFieldUsesFormat();
0712: formatItem.setEnabled(enable);
0713: } else
0714: formatItem.setEnabled(false);
0715: borderItem.setEnabled(someFieldSelected);
0716: boundsItem.setEnabled(someFieldSelected);
0717: alignSubmenu.setEnabled(multipleFieldsSelected);
0718: sizeSubmenu.setEnabled(multipleFieldsSelected);
0719:
0720: // Database menu
0721: tableJoinItem.setEnabled(ds.canJoinTables());
0722: sqlQueryTextItem.setEnabled(ds.isSQLGenerated());
0723: connectionItem.setEnabled(ds.isConnectionEditable());
0724:
0725: // Report menu
0726: runItem.setEnabled(ds.canRunReports());
0727: exportItem.setEnabled(ds.canRunReports());
0728: selectRecordsItem.setEnabled(ds.areRecordsSelectable());
0729: sortByItem.setEnabled(ds.areRecordsSortable());
0730: groupByItem.setEnabled(ds.canGroupRecords());
0731: }
0732:
0733: /**
0734: * Returns <code>true</code> if there is some selected field that can
0735: * be formatted.
0736: *
0737: * @return <code>true</code> if there is some selected field that can
0738: * be formatted
0739: */
0740: public boolean someSelectedFieldUsesFormat() {
0741: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
0742: if (((FieldWidget) iter.next()).usesFormat())
0743: return true;
0744: return false;
0745: }
0746:
0747: String action(String name) {
0748: return I18N.get(I18N.MENU_FILE_PREFIX, "DesignWin.menu_" + name
0749: + ".action");
0750: }
0751:
0752: String action(String menu, String name) {
0753: return I18N.get(I18N.MENU_FILE_PREFIX, menu + ".menu_" + name
0754: + ".action");
0755: }
0756:
0757: /**
0758: * Handles user actions.
0759: */
0760: public void actionPerformed(ActionEvent e) {
0761: String cmd = e.getActionCommand();
0762: if (cmd == null || cmd.length() == 0)
0763: return;
0764:
0765: // File menu
0766: if (cmd.equals(action("file_new")))
0767: newReport();
0768: else if (cmd.equals(action("file_open")))
0769: openReport();
0770: else if (cmd.equals(action("file_save")))
0771: saveReport();
0772: else if (cmd.equals(action("file_save_as")))
0773: saveReportAs();
0774: else if (cmd.equals(action("file_close")))
0775: maybeClose();
0776: else if (cmd.equals(action("file_quit")))
0777: maybeQuit();
0778:
0779: // Edit menu
0780: else if (cmd.equals(action("edit_undo")))
0781: commandHistory.undo();
0782: else if (cmd.equals(action("edit_redo")))
0783: commandHistory.redo();
0784: else if (cmd.equals(action("edit_cut")))
0785: commandHistory
0786: .perform(new CutCommand(this , selectedFields));
0787: else if (cmd.equals(action("edit_copy")))
0788: copySelectedFields();
0789: else if (cmd.equals(action("edit_paste")))
0790: paste();
0791: else if (cmd.equals(action("edit_del_fields")))
0792: deleteSelectedFields();
0793: else if (cmd.equals(action("edit_del_group"))) {
0794: FieldWidget fw = (FieldWidget) selectedFields.get(0);
0795: deleteGroupContaining(fw.getSectionWidget().getSection());
0796: } else if (cmd.equals(action("edit_del_section"))) {
0797: FieldWidget fw = (FieldWidget) selectedFields.get(0);
0798: deleteSection(fw.getSectionWidget().getSection());
0799: }
0800:
0801: // Insert menu
0802: else if (cmd.equals(action("insert_column")))
0803: openFieldPickerWin(FieldPickerWin.REPORT_DATABASE_FIELDS);
0804: else if (cmd.equals(action("insert_text")))
0805: placeNewTextField();
0806: else if (cmd.equals(action("insert_formula")))
0807: openFieldPickerWin(FieldPickerWin.FORMULAS);
0808: else if (cmd.equals(action("insert_usercol")))
0809: openFieldPickerWin(FieldPickerWin.USERCOLS);
0810: else if (cmd.equals(action("insert_param")))
0811: openFieldPickerWin(FieldPickerWin.PARAMETERS);
0812: else if (cmd.equals(action("insert_aggr")))
0813: openAggregateWin();
0814: else if (cmd.equals(action("insert_special")))
0815: openFieldPickerWin(FieldPickerWin.SPECIAL_FIELDS);
0816: else if (cmd.equals(action("insert_image")))
0817: createImageField();
0818: // else if (cmd.equals(action("insert_line"))) placeNewLine();
0819: else if (cmd.equals(action("insert_group")))
0820: openNewGroupWin();
0821: else if (cmd.equals(action("insert_section")))
0822: insertSection();
0823: else if (cmd.equals(action("insert_subreport")))
0824: insertSubreport();
0825:
0826: // Format menu
0827: else if (cmd.equals(action("format_format")))
0828: openFormatWin(0);
0829: else if (cmd.equals(action("format_border")))
0830: openFormatWin(1);
0831: else if (cmd.equals(action("format_bounds")))
0832: openBoundsWin();
0833: else if (cmd.equals(action("format_default")))
0834: openDefaultFormatWin();
0835:
0836: // Format/Align submenu
0837: else if (cmd.equals(action("Align", "tops")))
0838: align(ALIGN_TOP);
0839: else if (cmd.equals(action("Align", "middles")))
0840: align(ALIGN_MIDDLE);
0841: else if (cmd.equals(action("Align", "bottoms")))
0842: align(ALIGN_BOTTOM);
0843: else if (cmd.equals(action("Align", "lefts")))
0844: align(ALIGN_LEFT);
0845: else if (cmd.equals(action("Align", "centers")))
0846: align(ALIGN_CENTER);
0847: else if (cmd.equals(action("Align", "rights")))
0848: align(ALIGN_RIGHT);
0849: else if (cmd.equals(action("Align", "snap")))
0850: align(ALIGN_SNAP_TO_GRID);
0851:
0852: // Format/Size submenu
0853: else if (cmd.equals(action("Size", "same_width")))
0854: size(SIZE_SAME_WIDTH);
0855: else if (cmd.equals(action("Size", "same_height")))
0856: size(SIZE_SAME_HEIGHT);
0857: else if (cmd.equals(action("Size", "same_size")))
0858: size(SIZE_SAME_SIZE);
0859:
0860: // Database menu
0861: else if (cmd.equals(action("database_linker")))
0862: openVisTableWin();
0863: else if (cmd.equals(action("database_sql")))
0864: showSQL();
0865: else if (cmd.equals(action("connection")))
0866: openDbConnWin(false);
0867:
0868: // Report menu
0869: else if (cmd.equals(action("report_run")))
0870: runReport();
0871: else if (cmd.equals(action("report_export")))
0872: exportReport();
0873: else if (cmd.equals(action("report_select")))
0874: openWhereClauseEditor();
0875: else if (cmd.equals(action("report_sort")))
0876: openSortWin();
0877: else if (cmd.equals(action("report_group")))
0878: openGroupWin();
0879: else if (cmd.equals(action("report_start_formula")))
0880: openStartupScriptEditor();
0881: else if (cmd.equals(action("report_scripting_langs")))
0882: openScriptingWin();
0883: else if (cmd.equals(action("report_summary")))
0884: openDescripWin();
0885:
0886: // Help menu
0887: else if (cmd.equals(action("help_help")))
0888: help();
0889: else if (cmd.equals(action("help_about")))
0890: about();
0891:
0892: // Paper size menu
0893: else if (cmd.equals(action("MenuUtils", "paper_size_portrait")))
0894: changePaperOrientation(PaperFormat.PORTRAIT);
0895: else if (cmd
0896: .equals(action("MenuUtils", "paper_size_landscape")))
0897: changePaperOrientation(PaperFormat.LANDSCAPE);
0898: else {
0899: PaperFormat p = PaperFormat.get(report.getPaperFormat()
0900: .getOrientation(), cmd);
0901: changePaperSize(p);
0902: }
0903: }
0904:
0905: protected void changePaperOrientation(int orientation) {
0906: changePaperSize(PaperFormat.get(orientation, report
0907: .getPaperFormat().getName()));
0908: }
0909:
0910: protected void changePaperSize(PaperFormat p) {
0911: if (p != null) {
0912: performCommand(new PaperSizeCommand(report, this , p));
0913: // The paper size menu gets updated as a side effect of the
0914: // command's call to paperSizeChanged().
0915: }
0916: }
0917:
0918: /**
0919: * Update paper orientation and size menus.
0920: */
0921: public void updatePaperSizeMenu(PaperFormat p) {
0922: paperSizeSubmenu.getItem(
0923: p.getOrientation() == PaperFormat.PORTRAIT ? 0 : 1)
0924: .setSelected(true);
0925: int i = 3;
0926: for (Iterator iter = PaperFormat.names(); iter.hasNext(); ++i) {
0927: String name = (String) iter.next();
0928: if (name.equals(p.getName()))
0929: paperSizeSubmenu.getItem(i).setSelected(true);
0930: }
0931: }
0932:
0933: public void paperSizeChanged(PaperFormat p) {
0934: for (Iterator iter = sectionWidgets.iterator(); iter.hasNext();)
0935: ((SectionWidget) iter.next()).paperSizeChanged();
0936:
0937: Dimension d = new Dimension(SectionWidget.LHS_WIDTH
0938: + (int) report.getPaperFormat().getWidth(),
0939: sectionContainer.getHeight());
0940: sectionContainer.setPreferredSize(d);
0941: sectionContainer.invalidate();
0942: rootPaneContainer.getRootPane().validate();
0943:
0944: updatePaperSizeMenu(p);
0945: }
0946:
0947: public Frame getFrame() {
0948: return frame;
0949: }
0950:
0951: public void invalidate() {
0952: rootPaneContainer.getRootPane().invalidate();
0953: }
0954:
0955: public Report getReport() {
0956: return report;
0957: }
0958:
0959: /**
0960: * Creates a new report in a new design window.
0961: */
0962: protected void newReport() {
0963: new DesignWin(null); // Open a new window
0964: }
0965:
0966: /**
0967: * Opens an existing report in a new design window.
0968: */
0969: protected void openReport() {
0970: JFileChooser jfc = getChooser();
0971: setPrefsDir(jfc, null);
0972: int returnVal = jfc.showOpenDialog(frame);
0973: if (returnVal == JFileChooser.APPROVE_OPTION) {
0974: savePrefsDir(jfc, null); // save report directory
0975: new DesignWin(getChooser().getSelectedFile());
0976: }
0977: }
0978:
0979: /**
0980: * Saves the current report.
0981: */
0982: protected void saveReport() {
0983: if (reportFilePath == null)
0984: saveReportAs();
0985: else
0986: writeReportFile(reportFilePath);
0987: }
0988:
0989: /**
0990: * Saves the current report in a different file.
0991: */
0992: protected void saveReportAs() {
0993: JFileChooser jfc = getChooser();
0994: setPrefsDir(jfc, null);
0995: int returnVal = jfc.showSaveDialog(frame);
0996: if (returnVal == JFileChooser.APPROVE_OPTION) {
0997: savePrefsDir(jfc, null); // save report directory
0998: reportFilePath = getChooser().getSelectedFile().getPath();
0999: writeReportFile(reportFilePath);
1000: }
1001: }
1002:
1003: /**
1004: * Writes the current report to the specified file. Also tells the
1005: * command history the report has been saved so it knows how to report
1006: * if any changes have been made from this point on.
1007: *
1008: * @param fileName a file name
1009: */
1010: protected void writeReportFile(String fileName) {
1011: report.writeFile(fileName);
1012: commandHistory.setBaseline();
1013: }
1014:
1015: /**
1016: * Exports the report output using one of the layout engines.
1017: */
1018: protected void exportReport() {
1019: new ExportWin(getFrame(), report);
1020: }
1021:
1022: /**
1023: * Saves the report if it is changed (some command has been performed) and
1024: * closes the current design window. If there are no more open design
1025: * windows, exist the application.
1026: */
1027: protected void maybeClose() {
1028: if (commandHistory.isChanged()) {
1029: String str = I18N.get("DesignWin.save_question");
1030: switch (JOptionPane.showConfirmDialog(frame, str)) {
1031: case JOptionPane.YES_OPTION:
1032: saveReport();
1033: case JOptionPane.NO_OPTION:
1034: break;
1035: case JOptionPane.CANCEL_OPTION:
1036: return; // Don't close window
1037: }
1038: }
1039: closeMe();
1040: Designer.deleteWindow(this );
1041: }
1042:
1043: /**
1044: * Runs and displays the report.
1045: */
1046: protected void runReport() {
1047: report.setLayoutEngine(new SwingLE());
1048: report.run();
1049: }
1050:
1051: /**
1052: * Returns the number of selected fields.
1053: *
1054: * @return the number of selected fields
1055: */
1056: int countSelectedFields() {
1057: return selectedFields.size();
1058: }
1059:
1060: /**
1061: * Hands each field widget to the specified {@link FieldWidgetWalker}.
1062: *
1063: * @param perambulator a field widget walker
1064: */
1065: void withWidgetsDo(FieldWidgetWalker perambulator) {
1066: for (Iterator iter = sectionWidgets.iterator(); iter.hasNext();) {
1067: SectionWidget sw = (SectionWidget) iter.next();
1068: Object[] kids = sw.fieldPanel.getComponents();
1069: for (int i = 0; i < kids.length; ++i) {
1070: FieldWidget fw = FieldWidget
1071: .findFieldWidgetOwning(kids[i]);
1072: if (fw != null)
1073: perambulator.step(fw);
1074: }
1075: }
1076: }
1077:
1078: /**
1079: * Hands each selected field widget's field to the specified {@link
1080: * FieldWalker}.
1081: *
1082: * @param perambulator a field walker
1083: */
1084: void withSelectedFieldsDo(FieldWalker perambulator) {
1085: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1086: perambulator.step(((FieldWidget) iter.next()).getField());
1087: }
1088:
1089: /**
1090: * Deletes the group that contains the specified section.
1091: *
1092: * @param section a report section
1093: */
1094: void deleteGroupContaining(Section section) {
1095: Group group = report.findGroup(section);
1096: if (group != null)
1097: commandHistory.perform(new DeleteGroupCommand(this , report,
1098: group));
1099: }
1100:
1101: /**
1102: * Inserts a new section below the section containing the first selected
1103: * field widget. Only called when at least one widget is selected.
1104: */
1105: void insertSection() {
1106: Field f = firstSelectedFieldWidget().getField();
1107: if (f != null)
1108: insertSectionBelow(f.getSection());
1109: }
1110:
1111: /**
1112: * Inserts a new section below the specified section.
1113: *
1114: * @param s a report section
1115: */
1116: void insertSectionBelow(Section s) {
1117: commandHistory.perform(new NewSectionCommand(this , report, s));
1118: }
1119:
1120: /**
1121: * Inserts a section widget at the specified position in the list. Also starts
1122: * observing the section and renames all section widgets (adding "(a)", for
1123: * example. Called from {@link NewSectionCommand#perform}.
1124: *
1125: * @param sw the section widget to insert
1126: * @param putAfter <var>sw</var> goes after this widget; may be
1127: * <code>null</code>
1128: */
1129: public void insertSectionWidgetAfter(SectionWidget sw,
1130: SectionWidget putAfter) {
1131: int insertIndex = sectionWidgets.indexOf(putAfter);
1132: sectionWidgets.add(insertIndex + 1, sw);
1133:
1134: // Add to palette layer (one above default layer) because
1135: // we want to move field widgets below section widgets
1136: // temporarily at the end of a drag (see putDown()).
1137: sectionContainer.add(sw, JLayeredPane.PALETTE_LAYER,
1138: insertIndex + 1);
1139:
1140: sw.getSection().addObserver(this );
1141: renameSectionWidgets(); // Muck with names (e.g., add " (a)")
1142: }
1143:
1144: /**
1145: * Deletes the specified section. Does nothing if this section is the only
1146: * one of its kind. (This method should only be called if the section is
1147: * not one-of-a-kind. No harm done if you do, though.)
1148: *
1149: * @param s a report section
1150: */
1151: public void deleteSection(Section s) {
1152: if (s != null && !report.isOneOfAKind(s))
1153: commandHistory.perform(new DeleteSectionCommand(this ,
1154: report, s));
1155: }
1156:
1157: /**
1158: * Deletes a section from the report and the design window and returns the
1159: * section widget <em>above</em> the section's. If <var>s</var> is the first
1160: * section in the report, returns <code>null</code>. Called from commands;
1161: * don't call this yourself. Insetad call {@link #deleteSection}.
1162: *
1163: * @param s the section to delete
1164: * @return the section widget above the section's, or <code>null</code>
1165: * if there is none
1166: */
1167: public SectionWidget doDeleteSection(Section s) {
1168: SectionWidget sw = findSectionWidgetFor(s);
1169: int index = sectionWidgets.indexOf(sw);
1170: SectionWidget widgetBefore = (index == 0) ? null
1171: : (SectionWidget) sectionWidgets.get(index - 1);
1172:
1173: s.deleteObserver(this );
1174: report.removeSection(s);
1175: sectionContainer.remove(sw);
1176: sectionWidgets.remove(sw);
1177: renameSectionWidgets();
1178:
1179: return widgetBefore;
1180: }
1181:
1182: /**
1183: * Opens the dialog that starts the process of inserting a sub-report.
1184: */
1185: protected void insertSubreport() {
1186: new SubreportWin(this , report);
1187: }
1188:
1189: /**
1190: * Rebuilds the group sections and redisplays the report.
1191: */
1192: public void rebuildGroups() {
1193: deselectAll();
1194: rootPaneContainer.getContentPane().remove(scroller);
1195: buildSections();
1196: if (frame != null)
1197: frame.pack();
1198: }
1199:
1200: /**
1201: * Creates and adds a new text field to the first section of the page header.
1202: *
1203: * @param x where to place the title
1204: * @param width how wide it should be
1205: * @param title the string to display
1206: * @return the newly-created widget
1207: */
1208: public FieldWidget addTitleField(int x, int width, String title) {
1209: Section s = report
1210: .getFirstSectionByArea(SectionArea.PAGE_HEADER);
1211:
1212: // Create the field.
1213: Field f = Field.create(null, report, s, "text", title, true);
1214: jimm.datavision.field.Rectangle b = f.getBounds();
1215: b.setBounds(x, 0, width, Field.DEFAULT_HEIGHT);
1216:
1217: // Make the field underlined and bold.
1218: f.getFormat().setBold(true);
1219: f.getFormat().setUnderline(true);
1220:
1221: // Add the field to the report section.
1222: s.addField(f);
1223:
1224: // Create widget and add to section widget.
1225: TextFieldWidget tfw = new TextFieldWidget(null, f);
1226: tfw.moveToSection(findSectionWidgetFor(s));
1227: return tfw;
1228: }
1229:
1230: /**
1231: * Aligns the selected fields to the first selected field (chronologically
1232: * speaking).
1233: *
1234: * @param which alignment constant.
1235: */
1236: protected void align(int which) {
1237: if (selectedFields.isEmpty())
1238: return;
1239:
1240: // Align the widgets based on the position of the first selected field.
1241: CompoundCommand cmd = new CompoundCommand(I18N
1242: .get("FieldAlignCommand.name"));
1243: Field first = firstSelectedFieldWidget().getField();
1244: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1245: cmd.add(new FieldAlignCommand((FieldWidget) iter.next(),
1246: which, first));
1247:
1248: commandHistory.perform(cmd);
1249: }
1250:
1251: /**
1252: * Resize the selected fields based on the first selected field
1253: * (chronologically speaking) and the specified resize (width, height, both).
1254: *
1255: * @param which size constant
1256: */
1257: protected void size(int which) {
1258: if (selectedFields.isEmpty())
1259: return;
1260:
1261: // Resize the widgets based on the position of the first selected field.
1262: CompoundCommand cmd = new CompoundCommand(I18N
1263: .get("FieldResizeCommand.name"));
1264: Field first = firstSelectedFieldWidget().getField();
1265: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1266: cmd.add(new FieldResizeCommand((FieldWidget) iter.next(),
1267: which, first));
1268:
1269: commandHistory.perform(cmd);
1270: }
1271:
1272: /**
1273: * Returns first selected field widget (chronologically speaking). May
1274: * return <code>null</code> if no fields are selected
1275: *
1276: * @return field widget selected the earliest, or <code>null</code> if
1277: * no fields are selected
1278: */
1279: FieldWidget firstSelectedFieldWidget() {
1280: return selectedFields.isEmpty() ? null
1281: : (FieldWidget) selectedFields.get(0);
1282: }
1283:
1284: /**
1285: * Selects a field widget, possibly deselecting all others everywhere.
1286: * Called from section widget.
1287: *
1288: * @param fieldWidget a field widget
1289: * @param makeSelected new selection state
1290: * @param deselectOthers if <code>true</code>, all other fields in all
1291: * sections are deselected first
1292: */
1293: public void select(FieldWidget fieldWidget, boolean makeSelected,
1294: boolean deselectOthers) {
1295: if (deselectOthers)
1296: deselectAll();
1297:
1298: fieldWidget.doSelect(makeSelected);
1299: if (makeSelected) {
1300: if (!selectedFields.contains(fieldWidget)) // Don't add it twice
1301: selectedFields.add(fieldWidget);
1302: } else {
1303: selectedFields.remove(fieldWidget);
1304: }
1305:
1306: enableMenuItems();
1307: }
1308:
1309: /**
1310: * Deselect all fields. Called from {@link SectionWidget#deselectAll}.
1311: */
1312: public void deselectAll() {
1313: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1314: ((FieldWidget) iter.next()).doSelect(false);
1315: selectedFields.clear();
1316:
1317: enableMenuItems();
1318: }
1319:
1320: /**
1321: * Copies the selected fields to the clipboard. We need to create {@link
1322: * jimm.datavision.gui.cmd.Pasteable} objects.
1323: */
1324: protected void copySelectedFields() {
1325: ArrayList pasteables = new ArrayList();
1326: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1327: pasteables
1328: .add(new FieldClipping((FieldWidget) iter.next()));
1329: Clipboard.instance().setContents(pasteables);
1330:
1331: pasteItem.setEnabled(true);
1332: }
1333:
1334: protected void paste() {
1335: CompoundCommand cmd = new CompoundCommand(I18N
1336: .get("PasteCommand.name"));
1337: if (selectedFields.size() > 0)
1338: cmd.add(new DeleteCommand(this , selectedFields));
1339: cmd.add(new PasteCommand(this ));
1340: commandHistory.perform(cmd);
1341: }
1342:
1343: /**
1344: * Delete selected fields.
1345: */
1346: protected void deleteSelectedFields() {
1347: deleteSelectedFieldsAnd(null);
1348: }
1349:
1350: /**
1351: * Delete specified field and all selected fields.
1352: *
1353: * @param oneMore an additional field to delete; may be <code>null</code>
1354: */
1355: protected void deleteSelectedFieldsAnd(FieldWidget oneMore) {
1356: ArrayList fields = new ArrayList(selectedFields);
1357: if (oneMore != null && !fields.contains(oneMore))
1358: fields.add(oneMore);
1359: commandHistory.perform(new DeleteCommand(this , fields));
1360: }
1361:
1362: /**
1363: * Toggles the visibility of all selected fields plus the one passed in.
1364: * Called by a section widget.
1365: *
1366: * @see FieldWidget#doSetVisibility
1367: */
1368: void setFieldVisibility(boolean newVisiblity, FieldWidget fw) {
1369: if (selectedFields.isEmpty() && fw == null)
1370: return;
1371:
1372: String nameKey = newVisiblity ? "FieldShowCommand.name"
1373: : "FieldHideCommand.name";
1374:
1375: CompoundCommand cmd = new CompoundCommand(I18N.get(nameKey));
1376: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1377: cmd.add(new FieldShowHideCommand((FieldWidget) iter.next(),
1378: nameKey, newVisiblity));
1379: if (fw != null && !selectedFields.contains(fw))
1380: cmd
1381: .add(new FieldShowHideCommand(fw, nameKey,
1382: newVisiblity));
1383:
1384: commandHistory.perform(cmd);
1385: }
1386:
1387: /**
1388: * Asks design window to create and accepts a new text field. Called from
1389: * {@link SectionWidget#createNewTextField}.
1390: *
1391: * @see Designer#createNewTextField
1392: */
1393: void createNewTextField(SectionWidget sw, MouseEvent e) {
1394: commandHistory.perform(new NewTextFieldCommand(sw, e));
1395:
1396: // Accept the drop
1397: acceptNewTextField();
1398: }
1399:
1400: /**
1401: * Picks up the field widget because field dragging is starting.
1402: * Called from SectionWidget#pickUp.
1403: *
1404: * @param mouseScreenPos the location of the mouse in screen coordinates
1405: */
1406: void pickUp(java.awt.Point mouseScreenPos) {
1407: Dimension size = sectionContainer.getBounds().getSize();
1408: sectionContainer.setPreferredSize(size);
1409: sectionContainer.setMinimumSize(size);
1410:
1411: for (Iterator iter = selectedFields.iterator(); iter.hasNext();) {
1412: FieldWidget fw = (FieldWidget) iter.next();
1413: fw.pickUp(mouseScreenPos);
1414:
1415: // Add to the drag layer of our section container.
1416: sectionContainer.add(fw.getComponent(),
1417: JLayeredPane.DRAG_LAYER);
1418:
1419: // Recalculate bounds relative to the section container.
1420: jimm.datavision.field.Rectangle b = fw.getField()
1421: .getBounds();
1422: b.setBounds(b.x + SectionWidget.LHS_WIDTH, b.y
1423: + fw.getSectionWidget().getBounds().y, b.width,
1424: b.height);
1425: }
1426: }
1427:
1428: /**
1429: * Puts the dragged field widgets down inside the sections they are floating
1430: * above. Called from {@link SectionWidget#putDown}.
1431: *
1432: * @param f the field widget being dragged; all other selected fields
1433: * have been dragged along with it
1434: * @param origScreenPos the original location of the field in screen
1435: * coordinates
1436: * @param mouseScreenPos the current mouse position in screen coordinates;
1437: * note
1438: */
1439: void putDown(FieldWidget f, java.awt.Point origScreenPos,
1440: java.awt.Point mouseScreenPos) {
1441: // Move all dragged fields under everything else so getComponentAt()
1442: // will not return this field.
1443: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1444: sectionContainer.setLayer(((FieldWidget) iter.next())
1445: .getComponent(), JLayeredPane.DEFAULT_LAYER
1446: .intValue());
1447:
1448: CompoundCommand cmd = new CompoundCommand(I18N
1449: .get("FieldMoveCommand.name"));
1450:
1451: // Move to new section. Each field may be dropped into a different
1452: // section.
1453: for (Iterator iter = selectedFields.iterator(); iter.hasNext();) {
1454: FieldWidget fw = (FieldWidget) iter.next();
1455: SectionWidget sw = getSectionWidgetUnder(fw.getComponent()
1456: .getLocationOnScreen());
1457: if (sw == null) // Field snaps back to orig pos if sw is null
1458: fw.snapBack();
1459: else
1460: cmd.add(new FieldMoveCommand(fw, sw));
1461: }
1462:
1463: if (cmd.numCommands() > 0)
1464: commandHistory.perform(cmd);
1465: }
1466:
1467: /**
1468: * Starts stretching all selected fields. Called from {@link
1469: * SectionWidget#startStretching}.
1470: *
1471: * @param mouseScreenPos the location of the mouse in screen coordinates
1472: */
1473: void startStretching(java.awt.Point mouseScreenPos) {
1474: for (Iterator iter = selectedFields.iterator(); iter.hasNext();) {
1475: FieldWidget fw = (FieldWidget) iter.next();
1476: fw.startStretching(mouseScreenPos);
1477: }
1478: }
1479:
1480: /**
1481: * Tells each field to stop stretching and creates a command that will undo
1482: * all that stretching. Called from {@link SectionWidget#stopStretching}.
1483: *
1484: * @param f the field widget being dragged; all other selected fields
1485: * have been dragged along with it
1486: * @param origBounds the field's original bounds
1487: */
1488: void stopStretching(FieldWidget f,
1489: jimm.datavision.field.Rectangle origBounds) {
1490: CompoundCommand cmd = new CompoundCommand(I18N
1491: .get("FieldStretchCommand.name"));
1492:
1493: for (Iterator iter = selectedFields.iterator(); iter.hasNext();) {
1494: FieldWidget fw = (FieldWidget) iter.next();
1495: cmd.add(new FieldStretchCommand(fw, origBounds));
1496: fw.stopStretching();
1497: }
1498:
1499: if (cmd.numCommands() > 0)
1500: commandHistory.perform(cmd);
1501: }
1502:
1503: /**
1504: * Returns the section widget under the mouse. Returns <code>null</code>
1505: * unless the mouse is over a section field panel (the white area on
1506: * which fields belong).
1507: *
1508: * @param screenPos a position in screen coordinates
1509: * @return the SectionWidget under the mouse
1510: */
1511: protected SectionWidget getSectionWidgetUnder(
1512: java.awt.Point screenPos) {
1513: // Translate screenPos to sectionContainer coords
1514: java.awt.Point scScreenPos = sectionContainer
1515: .getLocationOnScreen();
1516: java.awt.Point scPos = new java.awt.Point(screenPos.x
1517: - scScreenPos.x, screenPos.y - scScreenPos.y);
1518:
1519: if (scPos.x < SectionWidget.LHS_WIDTH) // Reject if in LHS name
1520: return null;
1521:
1522: // We've landed on a section field panel, a field in the section, or a
1523: // section name label. Crawl up the parent hierarchy until we find
1524: // first the section field panel (insuring we are in the right place)
1525: // and then the section.
1526: Component c = sectionContainer.getComponentAt(scPos);
1527: while (c != null && !(c instanceof SectionWidget))
1528: c = c.getParent();
1529: return (SectionWidget) c;
1530: }
1531:
1532: /**
1533: * Drags the selected field widgets a few pixels. Called from section that
1534: * contains field being dragged.
1535: *
1536: * @param action a {@link FieldWidget}<code>.ACTION_*</code> constant
1537: * @param mouseScreenPos mouse screen position
1538: */
1539: protected void dragSelectedWidgets(int action,
1540: java.awt.Point mouseScreenPos) {
1541: for (Iterator iter = selectedFields.iterator(); iter.hasNext();)
1542: ((FieldWidget) iter.next()).doDrag(action, mouseScreenPos);
1543: }
1544:
1545: /**
1546: * Opens a new or existing field picker window.
1547: *
1548: * @param startingType the index of the starting type to display
1549: */
1550: protected void openFieldPickerWin(int startingType) {
1551: new FieldPickerWin(this , report, startingType);
1552: }
1553:
1554: /**
1555: * Opens a new or existing field aggregate window. We should
1556: * only get here if there is exactly one selected field.
1557: */
1558: protected void openAggregateWin() {
1559: new AggregatesWin(this , (FieldWidget) selectedFields.get(0));
1560: }
1561:
1562: /**
1563: * Opens a dialog that asks the user to select an image file. Creates an
1564: * image in the report header.
1565: */
1566: protected void createImageField() {
1567: String url = new AskStringDialog(frame, I18N
1568: .get("DesignWin.image_url_title"), I18N
1569: .get("DesignWin.image_url_label")).getString();
1570: if (url != null) {
1571: Section s = report
1572: .getFirstSectionByArea(SectionArea.REPORT_HEADER);
1573: NewImageFieldCommand cmd = new NewImageFieldCommand(
1574: findSectionWidgetFor(s), url);
1575: commandHistory.perform(cmd);
1576: }
1577: }
1578:
1579: /**
1580: * Opens a new or existing new group window.
1581: */
1582: protected void openNewGroupWin() {
1583: new NewGroupWin(this , report);
1584: }
1585:
1586: /**
1587: * Sets the flag that tells everyone else that the user wants to place
1588: * a new text field.
1589: */
1590: protected void placeNewTextField() {
1591: placingNewTextField = true;
1592: rootPaneContainer.getRootPane().setCursor(
1593: Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
1594: }
1595:
1596: /**
1597: * Returns <code>true</code> if the user is trying to place a new text field.
1598: *
1599: * @return <code>true</code> if the user is trying to place a new text field
1600: */
1601: boolean isPlacingNewTextField() {
1602: return placingNewTextField;
1603: }
1604:
1605: /**
1606: * The caller has accepted the new text field.
1607: */
1608: void acceptNewTextField() {
1609: placingNewTextField = false;
1610: rootPaneContainer.getRootPane().setCursor(null);
1611: }
1612:
1613: /**
1614: * The caller has rejected the new text field.
1615: */
1616: void rejectNewTextField() {
1617: acceptNewTextField();
1618: }
1619:
1620: /**
1621: * Opens a new or existing field format window.
1622: *
1623: * @param whichTab the index of the tab to display when opened
1624: */
1625: protected void openFormatWin(int whichTab) {
1626: FieldWidget fw = firstSelectedFieldWidget();
1627: if (fw != null)
1628: new FormatWin(this , fw.getField(), whichTab);
1629: }
1630:
1631: /**
1632: * Opens a new or existing field format window that lets the user edit the
1633: * report's default format and border.
1634: */
1635: protected void openDefaultFormatWin() {
1636: new FormatWin(this , report.getDefaultField(), 0);
1637: }
1638:
1639: /**
1640: * Opens a new or existing bounds editor window.
1641: */
1642: protected void openBoundsWin() {
1643: FieldWidget fw = firstSelectedFieldWidget();
1644: if (fw != null)
1645: new BoundsWin(this , fw.getField());
1646: }
1647:
1648: /**
1649: * Opens a new or existing visible table joiner window.
1650: */
1651: protected void openVisTableWin() {
1652: new VisTableWin(this , report);
1653: }
1654:
1655: /**
1656: * Opens a new or existing where clause editor.
1657: */
1658: protected void openWhereClauseEditor() {
1659: new WhereClauseWin(this , report);
1660: }
1661:
1662: /**
1663: * Opens a new or existing sort order window.
1664: */
1665: protected void openSortWin() {
1666: new SortWin(this , report);
1667: }
1668:
1669: /**
1670: * Opens a new or existing group order window.
1671: */
1672: protected void openGroupWin() {
1673: new GroupWin(this , report);
1674: }
1675:
1676: /**
1677: * Opens a starutp script editor.
1678: */
1679: protected void openStartupScriptEditor() {
1680: new StartupScriptEditor(this , report);
1681: }
1682:
1683: /**
1684: * Opens a new or existing report formula language window.
1685: */
1686: protected void openScriptingWin() {
1687: new ScriptingWin(this , report.getScripting());
1688: }
1689:
1690: /**
1691: * Opens a new or existing report description (name, title, etc.) window.
1692: */
1693: protected void openDescripWin() {
1694: new DescripWin(this , report);
1695: }
1696:
1697: /**
1698: * Opens the help window.
1699: */
1700: protected void help() {
1701: HelpWin helpWin = HelpWin.instance();
1702: helpWin.setState(Frame.NORMAL); // De-iconify. Why is this necessary?
1703: helpWin.setVisible(true);
1704: helpWin.toFront();
1705: }
1706:
1707: /**
1708: * Opens the about box.
1709: */
1710: protected void about() {
1711: String msg = I18N.get("DesignWin.about_1") + info.Version
1712: + "\n" + I18N.get("DesignWin.about_2") + "\n"
1713: + info.URL + "\n\n" + info.Copyright + ".\n\n"
1714: + I18N.get("DesignWin.about_3");
1715: JOptionPane.showMessageDialog(null, msg, I18N
1716: .get("DesignWin.about_title"),
1717: JOptionPane.PLAIN_MESSAGE);
1718: }
1719:
1720: /**
1721: * Opens a new or existing database connection info window.
1722: *
1723: * @param modal passed on to dialog constructor
1724: */
1725: protected void openDbConnWin(boolean modal) {
1726: new DbConnWin(this , report, modal);
1727: }
1728:
1729: /**
1730: * Opens a new window containing the SQL query text.
1731: */
1732: protected void showSQL() {
1733: new SQLQueryWin(frame, report.getDataSource().getQuery()
1734: .toString());
1735: }
1736:
1737: /**
1738: * Returns the section widget containing the specified section.
1739: *
1740: * @param s section
1741: * @return the section widget containing the section
1742: */
1743: public SectionWidget findSectionWidgetFor(Section s) {
1744: for (Iterator iter = sectionWidgets.iterator(); iter.hasNext();) {
1745: SectionWidget sw = (SectionWidget) iter.next();
1746: if (sw.section == s)
1747: return sw;
1748: }
1749: return null;
1750: }
1751:
1752: /**
1753: * Snaps the rectangle to the grid.
1754: *
1755: * @param r a rectangle
1756: */
1757: public void snapToGrid(jimm.datavision.field.Rectangle r) {
1758: int coord = (int) r.x;
1759: int mod = coord % GRID_SIZE;
1760: if (mod != 0) {
1761: if (mod <= GRID_SIZE / 2)
1762: r.setX(coord - mod);
1763: else
1764: r.setX(coord + GRID_SIZE - mod);
1765: }
1766:
1767: coord = (int) r.y;
1768: mod = coord % GRID_SIZE;
1769: if (mod != 0) {
1770: if (mod < GRID_SIZE / 2)
1771: r.setY(coord - mod);
1772: else
1773: r.setY(coord + GRID_SIZE - mod);
1774: }
1775: }
1776:
1777: // Might be using this code for dragging to select multiple fields. It
1778: // should eventually live in a separate file.
1779:
1780: // class SelectionHandler extends MouseInputAdapter {
1781:
1782: // protected java.awt.Rectangle rect;
1783: // protected HashSet fields;
1784: // protected JPanel selectionOutline;
1785:
1786: // public void mousePressed(MouseEvent e) {
1787: // int x = e.getX() - SectionWidget.LHS_WIDTH;
1788: // if (x < 0)
1789: // return;
1790:
1791: // deselectAll();
1792:
1793: // int y = e.getY();
1794: // rect = new java.awt.Rectangle(x, y, 0, 0);
1795: // selectionOutline = new JPanel();
1796: // selectionOutline.setBounds(rect);
1797: // rootPaneContainer.getContentPane().add(selectionOutline, 0); // Add to top of visual stack
1798: // }
1799:
1800: // public void mouseDragged(MouseEvent e) {
1801: // updateSize(e);
1802: // }
1803:
1804: // public void mouseReleased(MouseEvent e) {
1805: // updateSize(e);
1806: // remove(selectionOutline);
1807: // }
1808:
1809: // /*
1810: // * Updates the size of the current rectangle, changes selection list, and
1811: // * calls {@link #repaint}. Because rect always has the same origin,
1812: // * translate it if the width or height is negative.
1813: // *
1814: // * For efficiency, specify the painting region using arguments to the
1815: // * {@link #repaint} call.
1816: // */
1817: // void updateSize(MouseEvent e) {
1818: // if (selectionOutline == null)
1819: // return;
1820:
1821: // int x = e.getX();
1822: // int y = e.getY();
1823: // rect.setSize(x - rect.x, y - rect.y);
1824: // selectionOutline.setSize(x - rect.x, y - rect.y);
1825:
1826: // // selectFieldsWithin(rect);
1827:
1828: // // java.awt.Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
1829: // // repaint(totalRepaint.x, totalRepaint.y,
1830: // // totalRepaint.width, totalRepaint.height);
1831: // }
1832:
1833: // }
1834:
1835: }
|