KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jga > swing > spreadsheet > Controller


1 // ============================================================================
2
// $Id: Controller.java,v 1.7 2005/12/17 04:45:03 davidahall Exp $
3
// Copyright (c) 2004-2005 David A. Hall
4
// ============================================================================
5
// The contents of this file are subject to the Common Development and
6
// Distribution License (CDDL), Version 1.0 (the License); you may not use this
7
// file except in compliance with the License. You should have received a copy
8
// of the the License along with this file: if not, a copy of the License is
9
// available from Sun Microsystems, Inc.
10
//
11
// http://www.sun.com/cddl/cddl.html
12
//
13
// From time to time, the license steward (initially Sun Microsystems, Inc.) may
14
// publish revised and/or new versions of the License. You may not use,
15
// distribute, or otherwise make this file available under subsequent versions
16
// of the License.
17
//
18
// Alternatively, the contents of this file may be used under the terms of the
19
// GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which
20
// case the provisions of the LGPL are applicable instead of those above. If you
21
// wish to allow use of your version of this file only under the terms of the
22
// LGPL, and not to allow others to use your version of this file under the
23
// terms of the CDDL, indicate your decision by deleting the provisions above
24
// and replace them with the notice and other provisions required by the LGPL.
25
// If you do not delete the provisions above, a recipient may use your version
26
// of this file under the terms of either the CDDL or the LGPL.
27
//
28
// This library is distributed in the hope that it will be useful,
29
// but WITHOUT ANY WARRANTY; without even the implied warranty of
30
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
31
// ============================================================================
32

33 package net.sf.jga.swing.spreadsheet;
34
35 import java.awt.Component JavaDoc;
36 import java.awt.Point JavaDoc;
37 import java.awt.event.ActionEvent JavaDoc;
38 import java.awt.event.MouseAdapter JavaDoc;
39 import java.awt.event.MouseEvent JavaDoc;
40 import java.io.File JavaDoc;
41 import java.io.FileOutputStream JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.io.InputStream JavaDoc;
44 import java.io.OutputStream JavaDoc;
45 import java.net.MalformedURLException JavaDoc;
46 import java.net.URL JavaDoc;
47 import java.text.MessageFormat JavaDoc;
48 import javax.swing.Action JavaDoc;
49 import javax.swing.JFileChooser JavaDoc;
50 import javax.swing.JMenuItem JavaDoc;
51 import javax.swing.JOptionPane JavaDoc;
52 import javax.swing.JPopupMenu JavaDoc;
53 import net.sf.jga.fn.BinaryFunctor;
54 import net.sf.jga.fn.Generator;
55 import net.sf.jga.fn.UnaryFunctor;
56 import net.sf.jga.fn.adaptor.Constant;
57 import net.sf.jga.fn.adaptor.ConstantBinary;
58 import net.sf.jga.fn.adaptor.ConstantUnary;
59 import net.sf.jga.fn.adaptor.Identity;
60 import net.sf.jga.fn.adaptor.Project2nd;
61 import net.sf.jga.fn.property.InvokeNoArgMethod;
62 import net.sf.jga.parser.FunctorParser;
63 import net.sf.jga.parser.ParseException;
64 import net.sf.jga.swing.GenericAction;
65
66 /**
67  * Provides for generally common operations on a spreadsheet when it is in
68  * an application.
69  * <p>
70  * Copyright &copy; 2004-2005 David A. Hall
71  * @author <a HREF="mailto:davidahall@users.sf.net">David A. Hall</a>
72  */

73
74 public class Controller{
75
76     // The spreadsheet being controlled
77
private Spreadsheet _sheet;
78
79     // A parser for constructing various functors for the sheet
80
private FunctorParser _parser;
81
82     // The functor executed when the user must be prompted for simple information
83
private BinaryFunctor<String JavaDoc,String JavaDoc,String JavaDoc> _promptFn =
84         new ConstantBinary<String JavaDoc,String JavaDoc,String JavaDoc>("");
85
86     // The functor executed when the user is asked a yes/no/cancel question
87
private BinaryFunctor<String JavaDoc,String JavaDoc,Integer JavaDoc> _confirmFn =
88         new ConstantBinary<String JavaDoc,String JavaDoc,Integer JavaDoc>(null);
89     
90     // The functor executed to report errors to the user
91
private BinaryFunctor<String JavaDoc, String JavaDoc, ?> _errorFn =
92         new ConstantBinary<String JavaDoc,String JavaDoc,String JavaDoc>("");
93
94     // The functor executed to load a spreadsheet
95
private UnaryFunctor<Spreadsheet,Integer JavaDoc> _loadFn =
96         new ConstantUnary<Spreadsheet,Integer JavaDoc>(null);
97
98     // The functor executed to save a spreadsheet
99
private BinaryFunctor<Spreadsheet,Boolean JavaDoc,Integer JavaDoc> _saveFn =
100         new ConstantBinary<Spreadsheet,Boolean JavaDoc,Integer JavaDoc>(null);
101
102     public static final int YES_OPTION = JOptionPane.YES_OPTION;
103     public static final int NO_OPTION = JOptionPane.NO_OPTION;
104     public static final int CANCEL_OPTION = JOptionPane.CANCEL_OPTION;
105
106     
107     /**
108      * Builds a Controller to control the given sheet widget.
109      */

110     public Controller(Spreadsheet sheet) {
111         _sheet = sheet;
112         _parser = new FunctorParser();
113         _parser.bindThis(this);
114     }
115
116
117     /**
118      * Prompts the user for a string value.
119      * @param msg the description of the string to be shown to the user
120      * @param val the default or existing value of the string
121      */

122     public String JavaDoc prompt(String JavaDoc msg, String JavaDoc val) { return _promptFn.fn(msg, val); }
123
124     
125     /**
126      * Sets the functor used to prompt the user for simple information.
127      */

128     public void setPromptFunctor(BinaryFunctor<String JavaDoc,String JavaDoc,String JavaDoc> fn) {
129         _promptFn = fn;
130     }
131
132     
133     /**
134      * Asks the user a Yes/No/Cancel question.
135      * @return a value taken fro
136      */

137
138     public int confirm(String JavaDoc msg, String JavaDoc title) { return _confirmFn.fn(msg, title); }
139
140     
141     /**
142      * Sets the functor used to ask the user a yes/no/cancel question
143      */

144     
145     public void setConfirmFunctor(BinaryFunctor<String JavaDoc,String JavaDoc,Integer JavaDoc> fn) { _confirmFn = fn; }
146
147     
148     /**
149      * Displays an error message to the user
150      */

151
152     public void notify(String JavaDoc msg, String JavaDoc title) { _errorFn.fn(msg, title); }
153
154
155     /**
156      * Sets the functor used to display an error message.
157      */

158     public void setErrorFunctor(BinaryFunctor<String JavaDoc,String JavaDoc,?> fn) { _errorFn = fn; }
159
160     
161     /**
162      * Loads a spreadsheet
163      */

164
165     public int loadSheet(Spreadsheet sheet) {
166         return _loadFn.fn(sheet);
167     }
168
169     /**
170      * Sets the functor used to get an output stream
171      */

172
173     public void setLoadFunctor(UnaryFunctor<Spreadsheet,Integer JavaDoc> fn) { _loadFn = fn; }
174                                            
175    
176     /**
177      * Saves a spreadsheet
178      */

179
180     public int saveSheet(Spreadsheet sheet, boolean prompt) {
181         return _saveFn.fn(sheet,prompt);
182     }
183
184     
185     /**
186      * Sets the functor used to get an output stream
187      */

188
189     public void setSaveFunctor(BinaryFunctor<Spreadsheet,Boolean JavaDoc,Integer JavaDoc> fn) { _saveFn = fn; }
190                                            
191     // ----------------------
192
// Spreadsheet UI Methods
193
// ----------------------
194

195     /**
196      * Returns an Action that resets the spreadsheet to default state.
197      */

198     public Action JavaDoc getFileNewCmd() {
199         return new GenericAction(parseAction("this.newWorksheet()"), "New");
200     }
201
202     
203     /**
204      * Returns an Action that sets the spreadsheet to the contents of a file.
205      */

206     public Action JavaDoc getFileOpenCmd() {
207         return new GenericAction(parseAction("this.openFile()"), "Open");
208     }
209
210
211     // TODO: saveFile should only be enabled when the file is dirty
212

213     /**
214      * Returns an Action that saves the spreadsheet to the file from whence it came.
215      */

216     public Action JavaDoc getFileSaveCmd() {
217         return new GenericAction(parseAction("this.saveFile()"), "Save");
218     }
219
220
221     // TODO: saveFileAs should only be enabled when the file is dirty
222

223     /**
224      * Returns an Action that saves the spreadsheet to a file.
225      */

226     public Action JavaDoc getFileSaveAsCmd() {
227         return new GenericAction(parseAction("this.saveFileAs()"), "Save As...");
228     }
229
230     
231     /**
232      * Returns an Action that changes the default cell format.
233      */

234     public Action JavaDoc getDefaultEditableCmd() {
235         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Editable");
236         cmd.setEnabled(false);
237         return cmd;
238     }
239
240     /**
241      * Returns an Action that changes the default cell type.
242      */

243     public Action JavaDoc getDefaultTypeCmd() {
244         return new GenericAction(parseAction("this.setDefaultType()"), "Cell Type");
245     }
246
247     /**
248      * Returns an Action that changes the default cell type.
249      */

250     public Action JavaDoc getDefaultValueCmd() {
251         return new GenericAction(parseAction("this.setDefaultValue()"), "Cell Value");
252     }
253
254     /**
255      * Returns an Action that changes the default cell format.
256      */

257     public Action JavaDoc getDefaultFormatCmd() {
258         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Format");
259         cmd.setEnabled(false);
260         return cmd;
261     }
262
263     /**
264      * Returns an Action that changes the default cell renderer.
265      */

266     public Action JavaDoc getDefaultRendererCmd() {
267         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Renderer");
268         cmd.setEnabled(false);
269         return cmd;
270     }
271
272     /**
273      * Returns an Action that changes the default cell editor.
274      */

275     public Action JavaDoc getDefaultEditorCmd() {
276         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Editor");
277         cmd.setEnabled(false);
278         return cmd;
279     }
280
281     /**
282      * Returns an Action that resizes the worksheet.
283      */

284     public Action JavaDoc getSheetColumnsCmd() {
285         return new GenericAction(parseAction("this.setColumnCount()"), "Set Column Count");
286     }
287
288     /**
289      * Returns an Action that resizes the worksheet.
290      */

291     public Action JavaDoc getSheetRowsCmd() {
292         return new GenericAction(parseAction("this.setRowCount()"), "Set Row Count");
293     }
294
295     /**
296      * Returns an Action that allows the user to import a class.
297      */

298     public Action JavaDoc getImportClassCmd() {
299         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Import Class");
300         cmd.setEnabled(false);
301         return cmd;
302     }
303
304     /**
305      * Returns an Action that renames the current cell.
306      */

307     public Action JavaDoc getCellRenameCmd() {
308         String JavaDoc cellNameExp = "this.setCellName(x.getSelectedRow(),x.getSelectedColumn())";
309         try {
310             UnaryFunctor<ActionEvent JavaDoc,Object JavaDoc> cellNameFn =
311                 new Project2nd<ActionEvent JavaDoc,Object JavaDoc>()
312                     .generate2nd(_parser.parseUnary(cellNameExp, Spreadsheet.class).bind(_sheet));
313                              
314             return new GenericAction(cellNameFn, "Set Name");
315         }
316         catch (ParseException x) {
317             x.printStackTrace();
318             GenericAction cmd =
319                 new GenericAction(new ConstantUnary<ActionEvent JavaDoc,Object JavaDoc>(null), "Set Name");
320             cmd.setEnabled(false);
321             return cmd;
322         }
323     }
324
325     /**
326      * Returns an Action that reformats the current cell.
327      */

328     public Action JavaDoc getCellFormatCmd() {
329         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Set Format");
330         cmd.setEnabled(false);
331         return cmd;
332     }
333
334     /**
335      * Returns an Action that changes the type of the current cell.
336      */

337     public Action JavaDoc getCellTypeCmd() {
338         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Set Type");
339         cmd.setEnabled(false);
340         return cmd;
341     }
342     
343
344     /**
345      * Returns an Action that changes the type of the current cell.
346      */

347     public Action JavaDoc getCellRendererCmd() {
348         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Set Renderer");
349         cmd.setEnabled(false);
350         return cmd;
351     }
352     
353
354     /**
355      * Returns an Action that changes the type of the current cell.
356      */

357     public Action JavaDoc getCellEditorCmd() {
358         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Set Editor");
359         cmd.setEnabled(false);
360         return cmd;
361     }
362     
363
364     /**
365      * Returns an Action that changes the type of the current cell.
366      */

367     public Action JavaDoc getCellValidatorCmd() {
368         GenericAction cmd = new GenericAction(new Identity<ActionEvent JavaDoc>(), "Set Validator");
369         cmd.setEnabled(false);
370         return cmd;
371     }
372     
373     // ----------------------
374
// Worksheet Methods
375
// ----------------------
376

377     /**
378      * Prompts the user for the cell type.
379      */

380     public void setDefaultType() {
381         String JavaDoc prompt = "Enter default type for uninitialized cells";
382         String JavaDoc name = _promptFn.fn(prompt, _sheet.getDefaultCellType().getName());
383         if (name == null)
384             return;
385
386         try {
387             _sheet.setDefaultCellType(Class.forName(name));
388         }
389         catch (ClassNotFoundException JavaDoc x) {
390             String JavaDoc fmt = "Class {0} not found";
391             String JavaDoc msg = MessageFormat.format(fmt, new Object JavaDoc[]{ x.getMessage() });
392             _errorFn.fn(msg, "ClassNotFoundException");
393         }
394     }
395                                      
396     /**
397      * Prompts the user for the number of columns
398      */

399     public void setColumnCount() {
400         String JavaDoc prompt = "Enter the number of columns";
401         String JavaDoc countstr = _promptFn.fn(prompt, String.valueOf(_sheet.getColumnCount()));
402         if (countstr == null)
403             return;
404
405         try {
406             _sheet.setColumnCount(Integer.parseInt(countstr));
407         }
408         catch (NumberFormatException JavaDoc x) {
409             String JavaDoc fmt = "{0} isn't a number";
410             String JavaDoc msg = MessageFormat.format(fmt, new Object JavaDoc[]{ countstr });
411             _errorFn.fn(msg, "NumberFormatException");
412         }
413     }
414                                      
415     /**
416      * Prompts the user for the number of rows
417      */

418     public void setRowCount() {
419         String JavaDoc prompt = "Enter the number of rows";
420         String JavaDoc countstr = _promptFn.fn(prompt, String.valueOf(_sheet.getRowCount()));
421         if (countstr == null)
422             return;
423
424         try {
425             _sheet.setRowCount(Integer.parseInt(countstr));
426         }
427         catch (NumberFormatException JavaDoc x) {
428             String JavaDoc fmt = "{0} isn't a number";
429             String JavaDoc msg = MessageFormat.format(fmt, new Object JavaDoc[]{ countstr });
430             _errorFn.fn(msg, "NumberFormatException");
431         }
432     }
433                                      
434     // TODO: need to save the formula and use it in the prompt, instead of the current
435
// default value represented as a string. If the user, for example, sets the default
436
// value to "new java.util.Date()", on the next prompt, he'll get the point in time
437
// in which the expression was evaluated, which itself is not parsable.
438

439     /**
440      * Prompts the user for the default cell value.
441      */

442     public void setDefaultValue() {
443         String JavaDoc prompt = "Enter default value for uninitialized cells";
444         String JavaDoc exp = _promptFn.fn(prompt, ""+_sheet.getDefaultCellValue());
445         if (exp == null)
446             return;
447
448         try {
449             Generator gen = _sheet.getParser().parseGenerator(exp);
450             _sheet.setDefaultCellValue(gen.gen());
451         }
452         catch (ParseException x) {
453             _errorFn.fn(x.getMessage(), getExceptionName(getRootCause(x)));
454         }
455     }
456
457     // ----------------------
458
// Cell Methods
459
// ----------------------
460
/**
461      * Prompts the user for the name of the designated cell.
462      */

463     public void setCellName(int row, int col) {
464         String JavaDoc prompt = "Enter name for cell("+row+","+col+")";
465         Cell cell = _sheet.getCellIfPresent(row, col);
466         String JavaDoc oldName = (cell != null) ? cell.getName() : "";
467         String JavaDoc name = _promptFn.fn(prompt, oldName);
468         if (name != null && !name.equals(oldName))
469             _sheet.setCellName(name, row, col);
470     }
471                                      
472
473     // ----------------------
474
// Spreadsheet IO Methods
475
// ----------------------
476

477     /**
478      * Creates a new, empty spreadsheet with default settings.
479      */

480     public void newWorksheet() {
481         int ans = JOptionPane.YES_OPTION;
482         if (isSheetDirty()) {
483             ans = promptAndSave();
484         }
485      
486         if (ans != JOptionPane.CANCEL_OPTION) {
487             _sheet.clear();
488             setSheetSource(null);
489             setSheetDirty(false);
490         }
491     }
492
493     
494     /**
495      * Prompts the user for a file to open, and sets the spreadsheet to the file's contents.
496      */

497     public int openFile() { return loadSheet(_sheet); }
498
499
500     /**
501      */

502     public int saveFile() { return saveSheet(_sheet, false); }
503
504                                      
505     /**
506      */

507     public int saveFileAs() { return saveSheet(_sheet, true); }
508
509  
510     /**
511      * Prompts the user using a supplied functor, and returns one of YES, NO, or CANCEL
512      */

513     public int promptAndSave() {
514         String JavaDoc format = "save {0}?";
515         Object JavaDoc name = getSheetSource();
516         if (name == null)
517             name = "worksheet";
518                 
519         String JavaDoc msg = MessageFormat.format(format, new Object JavaDoc[]{ name });
520         int ans = confirm(msg,msg);
521         if (ans == Controller.YES_OPTION) {
522             int choice = saveFile();
523             if (choice == Controller.CANCEL_OPTION || choice == JFileChooser.ERROR_OPTION) {
524                 ans = Controller.CANCEL_OPTION;
525             }
526         }
527         return ans;
528     }
529
530     // ------------------------
531
// Implementation Details
532
// ------------------------
533

534     private final String JavaDoc DIRTY_PROP = "dirty";
535     
536     public boolean isSheetDirty() {
537         Object JavaDoc dirtyProp = _sheet.getClientProperty(DIRTY_PROP);
538         return Boolean.TRUE.equals(dirtyProp);
539     }
540
541     public void setSheetDirty(boolean flag) {
542         _sheet.putClientProperty(DIRTY_PROP, Boolean.valueOf(flag));
543     }
544
545     
546     private final String JavaDoc SOURCE_PROP = "source";
547
548     public URL JavaDoc getSheetSource() {
549         Object JavaDoc sourceURL = _sheet.getClientProperty(SOURCE_PROP);
550         if (sourceURL == null)
551             return null;
552
553         if (sourceURL instanceof URL JavaDoc)
554             return (URL JavaDoc) sourceURL;
555
556         try {
557             return new File JavaDoc(new File JavaDoc("."), "worksheet1.hwks").toURL();
558         }
559         catch (MalformedURLException JavaDoc x) {
560             x.printStackTrace();
561             return null;
562         }
563     }
564
565     public void setSheetSource(URL JavaDoc url) {
566 // System.out.println("setSheetSource(" + url + ")");
567
_sheet.putClientProperty(SOURCE_PROP, url);
568     }
569     
570     private UnaryFunctor<ActionEvent JavaDoc,?> parseAction(String JavaDoc exp) {
571         try {
572             return new Project2nd<ActionEvent JavaDoc,Object JavaDoc>()
573                 .generate2nd(_parser.parseGenerator(exp));
574         }
575         catch (ParseException x) {
576             x.printStackTrace();
577             // TODO: return some default that won't kill us
578
return null;
579         }
580     }
581
582     // ===============================================================
583

584     static Throwable JavaDoc getRootCause(Throwable JavaDoc t) {
585         for (Throwable JavaDoc t1 = t.getCause(); t1 != null; t1 = t.getCause())
586             t = t1;
587
588         return t;
589     }
590
591     static String JavaDoc getExceptionName(Throwable JavaDoc t) {
592         String JavaDoc fqcn = t.getClass().getName();
593         return fqcn.substring(fqcn.lastIndexOf(".") + 1);
594     }
595 }
596
Popular Tags