001: /*--
002:
003: Copyright (C) 2002 Anthony Eden.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: me@anthonyeden.com.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Anthony Eden (me@anthonyeden.com).
027:
028: In addition, I request (but do not require) that you include in the
029: end-user documentation provided with the redistribution and/or in the
030: software itself an acknowledgement equivalent to the following:
031: "This product includes software developed by
032: Anthony Eden (http://www.anthonyeden.com/)."
033:
034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
038: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
039: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
040: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
041: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
042: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
043: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
044: POSSIBILITY OF SUCH DAMAGE.
045:
046: For more information on OBE, please see <http://obe.sourceforge.net/>.
047:
048: */
049:
050: package org.obe.designer;
051:
052: import com.anthonyeden.lib.event.ErrorEvent;
053: import com.anthonyeden.lib.event.ErrorListener;
054: import com.anthonyeden.lib.gui.StandardAction;
055: import com.anthonyeden.lib.gui.StatusPanel;
056: import com.anthonyeden.lib.util.XArrayList;
057: import java.awt.*;
058: import java.awt.event.WindowAdapter;
059: import java.awt.event.WindowEvent;
060: import java.io.File;
061: import java.text.MessageFormat;
062: import java.util.HashMap;
063: import java.util.Iterator;
064: import java.util.Map;
065: import java.util.prefs.PreferenceChangeEvent;
066: import java.util.prefs.PreferenceChangeListener;
067: import java.util.prefs.Preferences;
068: import java.beans.PropertyVetoException;
069: import javax.swing.*;
070: import javax.swing.event.ChangeEvent;
071: import javax.swing.event.ChangeListener;
072: import org.apache.commons.logging.Log;
073: import org.apache.commons.logging.LogFactory;
074: import org.obe.designer.dialog.*;
075: import org.obe.designer.util.EditMode;
076: import org.obe.designer.util.Scale;
077: import org.obe.designer.util.UUID;
078: import org.obe.xpdl.model.pkg.XPDLPackage;
079: import org.obe.xpdl.model.workflow.WorkflowProcess;
080: import org.obe.util.ErrorHandler;
081:
082: /**
083: * This is the main entry point into the OBEDesginer. It will display the main
084: * window. In addition, this class implements the top-level actions which are
085: * tied to menus such as open, close, quit, etc.
086: *
087: * @author Anthony Eden
088: */
089: public class OBEDesigner extends JFrame implements Designer,
090: ErrorListener, PreferenceChangeListener {
091:
092: private static final String DEFAULT_DOCUMENT_NAME = "New Package";
093: // private static final String APP_PROPS_FILENAME = "obedesigner.properties";
094: private static final String APP_TITLE = "OBE Designer";
095: private static final File homeDirectory = new File(System
096: .getProperty("user.home"), ".obe");
097: private static final File tempDirectory = new File(homeDirectory,
098: "temp");
099:
100: private static File workingDirectory = new File(System
101: .getProperty("user.dir"));
102:
103: private static Log log = LogFactory.getLog(OBEDesigner.class);
104:
105: private Map actions;
106:
107: private File currentFile;
108:
109: private Preferences preferences;
110:
111: // private OBEGraph graph;
112: // private OBEGraphModel graphModel;
113: private OBETabbedPane tabbedPane;
114:
115: private XPDLPackage workflowPackage;
116:
117: private EditMode editMode = EditMode.EDIT;
118:
119: private StatusPanel statusPanel;
120: private OBEDesignerToolBar toolBar;
121: private OBEDesignerMenuBar menuBar;
122:
123: /**
124: * Construct a new OBEDesigner window.
125: */
126:
127: public OBEDesigner() {
128: preferences = Preferences.userNodeForPackage(OBEDesigner.class);
129: preferences.addPreferenceChangeListener(this );
130: preferences.node("appearance")
131: .addPreferenceChangeListener(this );
132:
133: init();
134: }
135:
136: /**
137: * Main entry point.
138: *
139: * @param args Command line arguments
140: */
141:
142: public static void main(String[] args) {
143: try {
144: // generate the first UUID and throw it away
145: Thread t = new Thread(new Runnable() {
146: public void run() {
147: UUID.generate();
148: }
149: });
150: t.start();
151:
152: // make the home and temp directory if necessary
153: getHomeDirectory().mkdirs();
154: getTempDirectory().mkdirs();
155:
156: OBEDesigner app = new OBEDesigner();
157: app.setVisible(true);
158: } catch (Exception e) {
159: e.printStackTrace();
160: }
161: }
162:
163: /**
164: * Call this method to execute common error handling behavior.
165: *
166: * @param t The error
167: */
168:
169: public static void error(Throwable t) {
170: //ThrowableDialog d = new ThrowableDialog(t);
171: //d.setVisible(true);
172: t.printStackTrace();
173: }
174:
175: /**
176: * Get the current working directory. The initial value is the value of the
177: * system property <code>user.dir</code>.
178: *
179: * @return The current working directory
180: */
181:
182: public static File getWorkingDirectory() {
183: return workingDirectory;
184: }
185:
186: /**
187: * Set the working directory. If the file argument is a directory, then the
188: * working directory is changed to that value. If the file argument is a
189: * file then the <code>file.getParentFile()</code> method is used to
190: * determine the working directory.
191: *
192: * @param file A file or directory
193: */
194:
195: public static void setWorkingDirectory(File file) {
196: if (file.isDirectory()) {
197: file = file.getParentFile();
198: }
199: workingDirectory = file;
200: }
201:
202: /**
203: * Get the OBEDesigner home directory. This directory is found by combining
204: * the system property <code>user.home</code> and the <code>.obe</code>,
205: * thus creating a "hidden" directory in the user's home directory.
206: *
207: * @return The home directory
208: */
209:
210: public static File getHomeDirectory() {
211: return homeDirectory;
212: }
213:
214: /**
215: * Get the temp directory. This directory is used for storing editor JAR
216: * files which are downloaded from the OBE server.
217: *
218: * @return The temp directory
219: */
220:
221: public static File getTempDirectory() {
222: return tempDirectory;
223: }
224:
225: /**
226: * Get a Map of all supported actions.
227: *
228: * @return The action map
229: */
230:
231: public Map getActions() {
232: return actions;
233: }
234:
235: // Actions
236:
237: /**
238: * Create a new workflow package.
239: */
240:
241: public void newPackage() {
242: close();
243: workflowPackage = null;
244: setCurrentFile(null);
245: requireWorkflowPackage();
246: }
247:
248: /**
249: * Edit the workflow package.
250: */
251:
252: public void editPackage() {
253: if (workflowPackage != null) {
254: EditPackageDialog d = new EditPackageDialog();
255: d.setWorkflowPackage(workflowPackage);
256: d.showDialog();
257: }
258: }
259:
260: /**
261: * Display an open file dialog and allow a workflow definition to be
262: * selected and opened.
263: */
264:
265: public void open() {
266: JFileChooser chooser = new JFileChooser(getWorkingDirectory());
267: chooser.rescanCurrentDirectory();
268: if (chooser.showOpenDialog(this ) == JFileChooser.APPROVE_OPTION) {
269: try {
270: open(chooser.getSelectedFile());
271: } catch (Throwable t) {
272: error(t);
273: }
274: }
275: }
276:
277: /**
278: * Load the package specified in the given XPDL file.
279: *
280: * @param file The file
281: * @throws Exception
282: */
283:
284: public void open(File file) throws Exception {
285: if (file != null) {
286: XArrayList workflowProcesses = tabbedPane
287: .getWorkflowProcesses();
288: workflowProcesses.clear();
289: OpenWorkflowThread t = new OpenWorkflowThread(this );
290: t.addErrorListener(this );
291: t.loadWorkflowDefinition(file);
292: setCurrentFile(file);
293: setWorkingDirectory(file.getParentFile());
294: }
295: }
296:
297: /**
298: * Close the current workflow package.
299: */
300:
301: public void close() {
302: try {
303: tabbedPane.getWorkflowProcesses().clear();
304: currentFile = null;
305: setTitle(null, DEFAULT_DOCUMENT_NAME);
306: } catch (Throwable t) {
307: log.error("Error closing : " + t.getMessage());
308: }
309: }
310:
311: /**
312: * Revert the current workflow package to the data on file.
313: */
314:
315: public void revert() {
316: if (currentFile != null) {
317: try {
318: open(currentFile);
319: } catch (Exception e) {
320: error(e);
321: }
322: }
323: }
324:
325: /**
326: * Save the workflow package.
327: */
328:
329: public void save() {
330: if (currentFile == null) {
331: saveAs();
332: } else {
333: try {
334: save(currentFile);
335: } catch (Throwable t) {
336: error(t);
337: }
338: }
339: }
340:
341: public void saveAs() {
342: JFileChooser chooser = new JFileChooser(getWorkingDirectory());
343: chooser.rescanCurrentDirectory();
344: if (chooser.showSaveDialog(this ) == JFileChooser.APPROVE_OPTION) {
345: try {
346: save(chooser.getSelectedFile());
347: } catch (Throwable t) {
348: error(t);
349: }
350: }
351: }
352:
353: public void save(File file) throws Exception {
354: if (file != null) {
355: requireWorkflowPackage();
356:
357: if (workflowPackage == null) {
358: log.info("No workflow package: not saving");
359: return;
360: }
361:
362: SaveWorkflowThread t = new SaveWorkflowThread(this ,
363: workflowPackage, tabbedPane);
364: t.addErrorListener(this );
365: t.saveWorkflowDefinition(file);
366:
367: setCurrentFile(file);
368: setWorkingDirectory(file.getParentFile());
369: setTitle(currentFile, workflowPackage.getName());
370: }
371: }
372:
373: public void quit() {
374: saveApplicationPreferences();
375: System.exit(0);
376: }
377:
378: public void cut() {
379:
380: }
381:
382: public void copy() {
383:
384: }
385:
386: public void paste() {
387:
388: }
389:
390: public void selectAll() {
391: OBEGraph graph = tabbedPane.getSelectedGraph();
392: if (graph != null)
393: graph.selectAll();
394: }
395:
396: public void selectNone() {
397: OBEGraph graph = getSelectedGraph();
398: if (graph != null) {
399: graph.selectNone();
400: }
401: }
402:
403: public void editPreferences() {
404: PreferencesDialog d = new PreferencesDialog(this );
405: d.setVisible(true);
406: }
407:
408: public OBEGraph getSelectedGraph() {
409: return tabbedPane.getSelectedGraph();
410: }
411:
412: /**
413: * Require that a workflow Package exists. If it does not exist then create
414: * one.
415: */
416:
417: public void requireWorkflowPackage() {
418: if (workflowPackage == null) {
419: NewPackageDialog dialog = new NewPackageDialog();
420: if (dialog.showDialog() == NewPackageDialog.APPROVE_OPTION) {
421: workflowPackage = dialog.getWorkflowPackage();
422: setTitle(null, workflowPackage.getName());
423: }
424: }
425: }
426:
427: public void addWorkflowProcess() {
428: try {
429: requireWorkflowPackage();
430:
431: NewWorkflowProcessDialog dialog = new NewWorkflowProcessDialog();
432: if (dialog.showDialog() == NewWorkflowProcessDialog.APPROVE_OPTION) {
433: WorkflowProcess wp = dialog
434: .getWorkflowProcess(workflowPackage);
435: tabbedPane.getWorkflowProcesses().add(wp);
436: workflowPackage.add(wp);
437: }
438: } catch (Throwable t) {
439: t.printStackTrace();
440: }
441: }
442:
443: public void editWorkflowProcess() {
444: try {
445: OBEGraph graph = tabbedPane.getSelectedGraph();
446: if (graph != null) {
447: EditWorkflowProcessDialog d = new EditWorkflowProcessDialog(
448: graph.getWorkflowProcess());
449: d.show();
450: }
451: } catch (Throwable t) {
452: t.printStackTrace();
453: }
454: }
455:
456: public void removeWorkflowProcess() {
457: OBEGraph graph = tabbedPane.getSelectedGraph();
458: if (graph != null) {
459: WorkflowProcess wp = graph.getWorkflowProcess();
460: Object[] args = { wp.getName() };
461: if (JOptionPane
462: .showConfirmDialog(
463: this ,
464: MessageFormat
465: .format(
466: "Are you sure you want to remove the workflow process {0}?",
467: args), "Verify Delete",
468: JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
469: tabbedPane.getWorkflowProcesses().remove(wp);
470: try {
471: workflowPackage.remove(wp);
472: } catch (PropertyVetoException e) {
473: ErrorHandler.handleError(e);
474: }
475: }
476: }
477: }
478:
479: public void cleanUpWorkflowProcesses() {
480: try {
481: OBEGraph graph = tabbedPane.getSelectedGraph();
482: if (graph != null) {
483: graph.cleanUp();
484: }
485: } catch (Throwable t) {
486: t.printStackTrace();
487: }
488: }
489:
490: public EditMode getEditMode() {
491: return editMode;
492: }
493:
494: public void setEditMode(EditMode editMode) {
495: if (editMode == null) {
496: throw new IllegalArgumentException(
497: "Edit mode cannot be null");
498: }
499:
500: if (editMode == EditMode.VIEW) {
501: Iterator graphs = tabbedPane.getGraphs().iterator();
502: while (graphs.hasNext()) {
503: OBEGraph graph = (OBEGraph) graphs.next();
504: graph.setMoveable(false);
505: graph.setEditable(false);
506: }
507: } else if (editMode == EditMode.EDIT) {
508: Iterator graphs = tabbedPane.getGraphs().iterator();
509: while (graphs.hasNext()) {
510: OBEGraph graph = (OBEGraph) graphs.next();
511: graph.setMoveable(true);
512: graph.setEditable(true);
513: }
514: } else if (editMode == EditMode.CONNECT) {
515: Iterator graphs = tabbedPane.getGraphs().iterator();
516: while (graphs.hasNext()) {
517: OBEGraph graph = (OBEGraph) graphs.next();
518: graph.setMoveable(false);
519: graph.setEditable(true);
520: }
521: }
522:
523: this .editMode = editMode;
524: }
525:
526: // property methods
527:
528: public Preferences getPreferences() {
529: return preferences;
530: }
531:
532: public void preferenceChange(PreferenceChangeEvent evt) {
533: try {
534: Preferences node = evt.getNode();
535: log.debug("Preference node change: " + node.name());
536: applyPreferences();
537: } catch (Exception e) {
538: error(e);
539: }
540: }
541:
542: public void applyPreferences() {
543: int size = tabbedPane.getWorkflowProcesses().size();
544: for (int i = 0; i < size; i++) {
545: OBEGraph graph = (OBEGraph) tabbedPane.getGraphs().get(i);
546: graph.setAntiAliased(preferences.node("appearance")
547: .getBoolean("anti-alias", false));
548: }
549:
550: try {
551: UIManager.setLookAndFeel(preferences.node("appearance")
552: .get("look-and-feel",
553: UIManager.getSystemLookAndFeelClassName()));
554: SwingUtilities.updateComponentTreeUI(this );
555: } catch (Exception e) {
556: log
557: .error("Error changing look and feel: "
558: + e.getMessage());
559: }
560: }
561:
562: public void setScale(Scale scale) {
563: OBEGraph graph = tabbedPane.getSelectedGraph();
564: if (graph != null) {
565: graph.setScale(scale.getValue());
566: updateToolbar();
567: }
568: }
569:
570: public Scale getScale() {
571: OBEGraph graph = tabbedPane.getSelectedGraph();
572: if (graph != null) {
573: return new Scale(graph.getScale());
574: }
575: return null;
576: }
577:
578: public void setStatus(String status) {
579: statusPanel.setStatus(status);
580: }
581:
582: public XPDLPackage getWorkflowPackage() {
583: return workflowPackage;
584: }
585:
586: public void setWorkflowPackage(XPDLPackage workflowPackage) {
587: this .workflowPackage = workflowPackage;
588:
589: XArrayList workflowProcesses = tabbedPane
590: .getWorkflowProcesses();
591: WorkflowProcess[] processes = workflowPackage
592: .getWorkflowProcess();
593: for (int i = 0; i < processes.length; i++) {
594: WorkflowProcess wp = processes[i];
595: log.info("Adding " + wp.getName() + " to UI");
596: workflowProcesses.add(wp);
597: }
598: }
599:
600: protected void loadApplicationPreferences() {
601: Preferences appNode = preferences.node("app");
602: String lastFile = appNode.get("lastFile", null);
603: if (lastFile != null) {
604: File file = new File(lastFile);
605: if (file.exists()) {
606: try {
607: log.debug("Opening last file: " + file);
608: open(file);
609: } catch (Exception e) {
610: error(e);
611: }
612: }
613: }
614:
615: Preferences windowNode = preferences.node("window");
616: if (windowNode.getBoolean("remember", false)) {
617: setBounds(new Rectangle(windowNode.getInt("x", 20),
618: windowNode.getInt("y", 20), windowNode.getInt(
619: "width", 400), windowNode.getInt("height",
620: 300)));
621: }
622: }
623:
624: protected void saveApplicationPreferences() {
625: Preferences appNode = preferences.node("app");
626: if (currentFile != null) {
627: appNode.put("lastFile", currentFile.getAbsolutePath());
628: } else {
629: appNode.remove("lastFile");
630: }
631:
632: Preferences windowNode = preferences.node("window");
633: Rectangle bounds = getBounds();
634: windowNode.putInt("x", bounds.x);
635: windowNode.putInt("y", bounds.y);
636: windowNode.putInt("width", bounds.width);
637: windowNode.putInt("height", bounds.height);
638: windowNode.putBoolean("remember", true);
639: }
640:
641: public void error(ErrorEvent evt) {
642: error(evt.getThrowable());
643: }
644:
645: protected File getCurrentFile() {
646: return currentFile;
647: }
648:
649: protected void setCurrentFile(File currentFile) {
650: ((Action) actions.get("file.revert"))
651: .setEnabled(currentFile != null);
652: this .currentFile = currentFile;
653: }
654:
655: /**
656: * Update the toolbar to reflect the state of the currently selected tab.
657: */
658:
659: protected void updateToolbar() {
660: Scale scale = getScale();
661: if (scale != null) {
662: toolBar.setScale(scale);
663: }
664: }
665:
666: private void init() {
667: initActions();
668:
669: tabbedPane = new OBETabbedPane(this );
670: tabbedPane.addChangeListener(new ChangeListener() {
671: public void stateChanged(ChangeEvent evt) {
672: updateToolbar();
673: }
674: });
675:
676: toolBar = new OBEDesignerToolBar(this );
677:
678: statusPanel = new StatusPanel();
679:
680: getContentPane().setLayout(new BorderLayout());
681: getContentPane().add(toolBar, BorderLayout.NORTH);
682: getContentPane().add(tabbedPane, BorderLayout.CENTER);
683: getContentPane().add(statusPanel, BorderLayout.SOUTH);
684:
685: menuBar = new OBEDesignerMenuBar(this );
686: setJMenuBar(menuBar);
687:
688: addWindowListener(new WindowAdapter() {
689: public void windowClosing(WindowEvent evt) {
690: quit();
691: }
692: });
693:
694: setTitle(null, DEFAULT_DOCUMENT_NAME);
695:
696: pack();
697:
698: loadApplicationPreferences();
699:
700: applyPreferences();
701: }
702:
703: /**
704: * Set the designer window title. Values of null will be ignored.
705: *
706: * @param file The current file
707: * @param packageName The current package name
708: */
709:
710: protected void setTitle(File file, String packageName) {
711: StringBuffer buffer = new StringBuffer();
712: buffer.append(APP_TITLE);
713: buffer.append(": ");
714: if (packageName != null) {
715: buffer.append(packageName);
716: buffer.append(" (");
717: if (file != null) {
718: buffer.append(file.getAbsolutePath());
719: } else {
720: buffer.append("Not Saved");
721: }
722: buffer.append(")");
723: }
724: setTitle(buffer.toString());
725: }
726:
727: private void initActions() {
728: actions = new HashMap();
729: actions.put("file.new", new StandardAction(this , "newPackage"));
730: actions.put("file.open", new StandardAction(this , "open"));
731: actions.put("file.close", new StandardAction(this , "close"));
732: actions.put("file.revert", new StandardAction(this , "revert"));
733: actions.put("file.properties", new StandardAction(this ,
734: "editPackage"));
735: actions.put("file.save", new StandardAction(this , "save"));
736: actions.put("file.saveAs", new StandardAction(this , "saveAs"));
737: actions.put("file.quit", new StandardAction(this , "quit"));
738:
739: actions.put("edit.cut", new StandardAction(this , "cut"));
740: actions.put("edit.copy", new StandardAction(this , "copy"));
741: actions.put("edit.paste", new StandardAction(this , "paste"));
742: actions.put("edit.selectAll", new StandardAction(this ,
743: "selectAll"));
744: actions.put("edit.selectNone", new StandardAction(this ,
745: "selectNone"));
746: actions.put("edit.preferences", new StandardAction(this ,
747: "editPreferences"));
748:
749: actions.put("workflow.add", new StandardAction(this ,
750: "addWorkflowProcess"));
751: actions.put("workflow.edit", new StandardAction(this ,
752: "editWorkflowProcess"));
753: actions.put("workflow.remove", new StandardAction(this ,
754: "removeWorkflowProcess"));
755: actions.put("workflow.cleanup", new StandardAction(this ,
756: "cleanUpWorkflowProcesses"));
757: }
758:
759: }
|