001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   License, or (at your option) any later version.
009
010   This library is distributed in the hope that it will be useful,
011   but WITHOUT ANY WARRANTY; without even the implied warranty of
012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: WorkspaceEditor.java,v $
023   Revision 1.9  2004/05/12 19:16:38  markl
024   comment block updates
025
026   Revision 1.8  2004/03/16 06:43:39  markl
027   LocaleManager method change
028
029   Revision 1.7  2003/01/19 09:50:54  markl
030   Javadoc & comment header updates.
031
032   Revision 1.6  2001/03/20 00:54:53  markl
033   Fixed deprecated calls.
034
035   Revision 1.5  2001/03/12 09:56:55  markl
036   KLabel/KLabelArea changes.
037
038   Revision 1.4  2001/03/12 09:28:02  markl
039   Source code and Javadoc cleanup.
040
041   Revision 1.3  2000/03/24 10:17:54  markl
042   Internationalization changes.
043
044   Revision 1.2  1999/01/10 02:57:18  markl
045   minor fixes
046   ----------------------------------------------------------------------------
047*/
048
049package kiwi.ui;
050
051import java.awt.*;
052import java.awt.event.*;
053import java.beans.*;
054import javax.swing.*;
055import javax.swing.text.*;
056import javax.swing.border.*;
057
058import kiwi.util.*;
059
060/** An abstract class that defines common behavior for all workspace editors.
061  * This class should be extended to provide the appropriate user interface and
062  * behavior for editing a specific type of problem domain object. Editors
063  * are managed by a <code>WorkspaceManager</code>, which provides some
064  * rudimentary inter-editor coordination, persistence support,  and other
065  * useful facilities. 
066  *
067  * @see kiwi.ui.WorkspaceManager
068  *
069  * @author Mark Lindner
070  */
071
072public abstract class WorkspaceEditor extends JInternalFrame
073  implements ActionListener
074  {
075  /** The problem domain object associated with this editor. */
076  protected Object object = null;
077
078  private KButton b_save, b_cancel;
079  private boolean changesMade = false;
080  private _KeyListener keyAdapter;
081  private _MouseListener mouseAdapter;
082  private _FocusListener focusAdapter;
083  private _ButtonListener actionListener;
084  private Component firstComponent = null;
085  private boolean editable;
086  private JTextComponent curFocus = null;
087  private WorkspaceManager manager = null;
088  private KLabel l_comment;
089  private KPanel pane;
090  
091  /** Construct a new <code>WorkspaceEditor</code> with a default window title.
092    * It is created as editable.
093    */
094  
095  public WorkspaceEditor()
096    {
097    this("");
098    }
099
100  /** Construct a new editable <code>WorkspaceEditor</code> with the given
101    * window title.
102    *
103    * @param title The title for the editor's window.
104    */
105
106  public WorkspaceEditor(String title)
107    {
108    this(title, true);
109    }
110
111  /** Construct a new <code>WorkspaceEditor</code> with the given window
112    * title and editable mode.
113    *
114    * @param title The title for the editor's window.
115    * @param editable A flag specifying whether the editor will be editable.
116    */
117
118  public WorkspaceEditor(String title, boolean editable)
119    {
120    super(title, true, true, true, true);
121
122    this.object = object;
123    this.editable = editable;
124
125    keyAdapter = new _KeyListener();
126    mouseAdapter = new _MouseListener();
127    actionListener = new _ButtonListener();
128    focusAdapter = new _FocusListener();
129
130    LocaleData loc = LocaleManager.getDefault().getLocaleData("KiwiDialogs");
131
132    setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
133
134    Container main = getContentPane();
135
136    pane = new KPanel(UIChangeManager.getInstance()
137                      .getDefaultTexture());
138    pane.setBorder(KiwiUtils.defaultBorder);
139    pane.setLayout(new BorderLayout(0, 0));
140
141    main.setLayout(new GridLayout(1, 0));
142    main.add(pane);
143
144    l_comment = new KLabel();
145    l_comment.setBorder(KiwiUtils.defaultBorder);
146    
147    pane.add("Center", buildEditingUI());
148
149    ButtonPanel buttons = new ButtonPanel();
150        
151    if(isEditable())
152      {
153      b_save = new KButton(loc.getMessage("kiwi.button.save"));
154      b_save.addActionListener(actionListener);
155      b_save.setEnabled(false);
156      buttons.addButton(b_save);
157
158      b_cancel = new KButton(loc.getMessage("kiwi.button.cancel"));
159      }
160    else
161      {
162      b_cancel = new KButton(loc.getMessage("kiwi.button.close"));
163      }
164
165    b_cancel.addActionListener(actionListener);
166    buttons.addButton(b_cancel);
167
168    pane.add("South", buttons);
169
170    JMenuBar mb = buildMenuBar();
171    if(mb != null)
172      setJMenuBar(mb);
173    }
174
175  /* Set the workspace manager for this editor. */
176
177  final void setWorkspaceManager(WorkspaceManager manager)
178    {
179    this.manager = manager;
180    }
181
182  /** Get the <code>WorkspaceManager</code> for this editor. The method will
183    * return <code>null</code> if the editor has not yet been added to a
184    * workspace (that is, a <code>JDesktopPane</code>).
185    */
186
187  protected final WorkspaceManager getWorkspaceManager()
188    {
189    return(manager);
190    }
191
192  /** Build the editing UI. Subclasses must implement this method to build the
193    * user interface for the editor.
194    */
195
196  abstract protected Component buildEditingUI();
197
198  /** Update the editing UI. Subclasses must implement this method to update
199   * the state of the user interface when a new problem domain object is
200   * associated with this editor.
201   */
202
203  abstract protected void updateEditingUI();
204
205  /** Set the comment that appears in the top portion of the editor. */
206
207  protected void setComment(String comment)
208    {
209    if(comment != null)
210      {
211      l_comment.setText(comment);
212      pane.add("North", l_comment);
213      }
214    else
215      {
216      if(l_comment.getText() != null)
217        pane.remove(l_comment);
218      l_comment.setText(null);
219      }
220    }
221
222  /** Construct the menu bar for this editor. The default implementation
223    * returns <code>null</code>, which signifies that no menubar is needed.
224    */
225
226  protected JMenuBar buildMenuBar()
227    {
228    return(null);
229    }
230
231  /** Give keyboard focus to the first text input component in this editor.
232    * These components are registered via
233    * <code>registerTextInputComponent()</code>.
234    */
235
236  public final void beginFocus()
237    {
238    if((firstComponent != null) && isEditable())
239      firstComponent.requestFocus();
240    }
241
242  /** Handle events. The default implementation of this method does nothing;
243    * subclassers may override it to handle specific user interface events.
244    */
245
246  public void actionPerformed(ActionEvent evt)
247    {
248    }
249
250  /** Get the current problem domain object associated with this editor, or
251    * <code>null</code> if there is no object associated with the editor.
252    *
253    * @return The object currently associated with this editor.
254    * @see #setObject
255    */
256
257  public final Object getObject()
258    {
259    return(object);
260    }
261
262  /** Set the problem domain object to be associated with this editor. This
263    * method ultimately results in a call to <code>updateEditingUI()</code>.
264    *
265    * @param object The new object to be associated with this editor.
266    * @see #getObject
267    */
268
269  public final void setObject(Object object)
270    {
271    this.object = object;
272    updateEditingUI();
273    }
274
275  /** Determine if this editor is displaying unsaved changes.
276    *
277    * @return <code>true</code> if there are unsaved changes, and
278    * <cod>false</code> otherwise.
279    * @see #setChangesMade
280    */
281
282  public final boolean hasUnsavedChanges()
283    {
284    return(changesMade);
285    }
286
287  /** Set the <i>changes made</i> flag on this editor. The editor checks this
288    * flag at close time to determine if changes need to be saved.
289    *
290    * @param flag The new value for the flag.
291    * @see #hasUnsavedChanges
292    */
293
294  protected final void setChangesMade(boolean flag)
295    {
296    if(isEditable())
297      {
298      changesMade = flag;
299      b_save.setEnabled(flag);
300      }
301    }
302
303  /** Persist the edits made in this editor.
304    *
305    * @return <code>true</code> if the save was successful and
306    * <code>false</code> otherwise.
307    */
308
309  public abstract boolean save();
310
311  /** Determine if the editor is editable.
312    *
313    * @return <code>true</code> if the editor is editable, and
314    * <code>false</code> otherwise.
315    */
316
317  public final boolean isEditable()
318    {
319    return(editable);
320    }
321
322  /** Register a text input component. This method is used by subclassers to
323    * notify the editor of input fields in the user interface. The editor uses
324    * this information when requesting focus, and also listens for keypress
325    * events on all of these components, setting the <i>changes made</i> flag
326    * to <code>true</code> when one occurs.
327    *
328    * @param c The component to register.
329    * @see #registerMouseInputComponent
330    */
331
332  protected final void registerTextInputComponent(JTextComponent c)
333    {
334    if(firstComponent == null) firstComponent = c;
335
336    c.addKeyListener(keyAdapter);
337    c.addFocusListener(focusAdapter);
338    }
339
340  /** Register a mouse input component. This method is used by subclassers to
341    * notify the editor of components that change the state of the data being
342    * edited when they are clicked. The editor listens for mouse events on all
343    * of these components, setting the <i>changes</i> made flag to
344    * <code>true</code> when one occurs.
345    *
346    * @param c The component to register.
347    * @see #registerTextInputComponent
348    */
349
350  protected final void registerMouseInputComponent(Component c)
351    {
352    c.addMouseListener(mouseAdapter);
353    }
354
355
356  /** Start editing in this editor. This method is called whenever the editor
357    * is activated in the workspace. The default implementation does nothing.
358    *
359    * @see #stopEditing
360    */
361
362  protected void startEditing()
363    {
364    }
365
366  /** Start editing in this editor. This method is called whenever the editor
367    * is deactivated in the workspace. The default implementation does nothing.
368    *
369    * @see #startEditing
370    */
371  
372  protected void stopEditing()
373    {
374    }
375
376  /* key listener */
377
378  private class _KeyListener extends KeyAdapter
379    {
380    public void keyTyped(KeyEvent evt)
381      {
382      setChangesMade(true);
383      }
384    }
385
386  /* focus listener */
387
388  private class _FocusListener extends FocusAdapter
389    {
390    public void focusGained(FocusEvent evt)
391      {
392      Component c = (Component)evt.getSource();
393      if(c instanceof JTextComponent)
394        curFocus = (JTextComponent)c;
395      }
396
397    public void focusLost(FocusEvent evt)
398      {
399      Component c = (Component)evt.getSource();
400      if(c == curFocus)
401        curFocus = null;
402      }
403    }
404
405  /* mouse listener */
406
407  private class _MouseListener extends MouseAdapter
408    {
409    public void mouseClicked(MouseEvent evt)
410      {
411      setChangesMade(true);
412      }
413
414    public void mouseReleased(MouseEvent evt)
415      {
416      setChangesMade(true);
417      }
418    }
419
420  /* button listener */
421
422  private class _ButtonListener implements ActionListener
423    {
424    public void actionPerformed(ActionEvent evt)
425      {
426      Object o = evt.getSource();
427
428      if(o == b_save)
429        {
430        boolean ok = save();
431        if(ok)
432          _hide();
433        }
434      else if(o == b_cancel)
435        {
436        setChangesMade(false);
437        _hide();
438        }
439      }
440    }
441
442  /** Invoke a <i>copy</i> action on this editor. The text in the text input
443    * component that currently has focus (if any) is copied to the system
444    * clipboard.
445    */
446
447  public final void copy()
448    {
449    if(curFocus != null)
450      curFocus.copy();
451    }
452
453  /** Invoke a <i>cut</i> action on this editor. The text in the text input
454    * component that currently has focus (if any) is moved to the system
455    * clipboard.
456    */
457
458  public final void cut()
459    {
460    if(curFocus != null)
461      curFocus.cut();
462    }
463
464  /** Invoke a <i>paste</i> action on this editor. The text in the system
465    * clipboard is copied to the text input component that currently has focus
466    * (if any).
467    */
468
469  public final void paste()
470    {
471    if(curFocus != null)
472      curFocus.paste();
473    }
474
475  /** Fire an <i>editor state changed</i> event. Notifies listeners that the
476    * internal state of this editor has changed in some way.
477    */
478  
479  protected final void fireStateChanged()
480    {
481    if(manager != null)
482      manager.fireEditorStateChanged(this);
483    }
484  
485  /* hide the editor */
486
487  private void _hide()
488    {
489    try
490      {
491      setClosed(true);
492      }
493    catch(PropertyVetoException ex)
494      {
495      }
496    }
497
498  }
499
500/* end of source file */