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: WorkspaceManager.java,v $ 023 Revision 1.11 2004/05/12 19:15:12 markl 024 comment block updates 025 026 Revision 1.10 2004/03/16 06:43:39 markl 027 LocaleManager method change 028 029 Revision 1.9 2003/01/19 09:50:54 markl 030 Javadoc & comment header updates. 031 032 Revision 1.8 2001/03/20 00:54:54 markl 033 Fixed deprecated calls. 034 035 Revision 1.7 2001/03/12 09:28:03 markl 036 Source code and Javadoc cleanup. 037 038 Revision 1.6 2000/03/24 10:24:36 markl 039 Fixed compile errors. 040 041 Revision 1.5 2000/03/24 10:17:54 markl 042 Internationalization changes. 043 044 Revision 1.4 1999/06/28 09:51:28 markl 045 Added getAllEditors() method. 046 047 Revision 1.3 1999/01/10 02:57:18 markl 048 minor fixes 049 050 Revision 1.2 1999/01/10 00:48:36 markl 051 Fixed to call setUnsavedChanges(false) after the call to save(). 052 ---------------------------------------------------------------------------- 053*/ 054 055package kiwi.ui; 056 057import java.util.*; 058import java.awt.Component; 059import java.awt.event.*; 060import java.beans.*; 061import javax.swing.*; 062import javax.swing.event.*; 063 064import kiwi.event.*; 065import kiwi.ui.dialog.DialogSet; 066import kiwi.util.*; 067 068/** This class represents a workspace manager. The manager controls a set of 069 * <code>WorkspaceEditor</code>s and their relationship to a 070 * <code>JDesktopPane</code>. Each <code>WorkspaceEditor</code> is associated 071 * with an arbitrary user object (the object that it is editing) and is 072 * represented by a <code>JInternalFrame</code> in the desktop. The manager's 073 * duties include saving unsaved changes in editors when their windows are 074 * closed and providing a high-level interface for retrieving information 075 * about the editors in the desktop. 076 * 077 * @see kiwi.ui.WorkspaceEditor 078 * @see javax.swing.JDesktopPane 079 * 080 * @author Mark Lindner 081 */ 082 083public class WorkspaceManager 084 { 085 private Vector editors, listeners; 086 private JDesktopPane desktop; 087 private WorkspaceEditor activeEditor = null; 088 private _FrameListener frameListener; 089 private DialogSet dialogs; 090 private Hashtable menus; 091 private LocaleData loc; 092 093 /** Construct a new <code>WorkspaceManager</code>. 094 * 095 * @param desktop The <code>JDesktopPane</code> that is associated with this 096 * workspace. This <code>JDesktopPane</code> must have already been added to 097 * the application's component hierarchy before this constructor is called. 098 */ 099 100 public WorkspaceManager(JDesktopPane desktop) 101 { 102 editors = new Vector(); 103 listeners = new Vector(); 104 menus = new Hashtable(); 105 this.desktop = desktop; 106 frameListener = new _FrameListener(); 107 dialogs = new DialogSet(KiwiUtils.getFrameForComponent(desktop), 108 DialogSet.CENTER_PLACEMENT); 109 110 loc = LocaleManager.getDefault().getLocaleData("KiwiDialogs"); 111 112 } 113 114 /** Get an enumeration of all editors existing in this workspace. 115 * 116 * @return An <code>Enumeration</code> of the editors. 117 */ 118 119 public Enumeration getAllEditors() 120 { 121 return(editors.elements()); 122 } 123 124 /** Get the currently active editor. 125 * 126 * @return The currently active editor, or <code>null</code> if no editor is 127 * currently active. 128 */ 129 130 public WorkspaceEditor getActiveEditor() 131 { 132 JInternalFrame f[] = desktop.getAllFramesInLayer(0); 133 if(f.length > 0) 134 return((WorkspaceEditor)f[0]); 135 else 136 return(null); 137 } 138 139 /** Add an editor to the workspace. The editor's window becomes visible in 140 * the workspace. 141 * 142 * @param editor The <code>WorkspaceEditor</code> to add to the workspace. 143 * @see #removeEditor 144 * @see #closeEditor 145 */ 146 147 public void addEditor(WorkspaceEditor editor) 148 { 149 editors.addElement(editor); 150 editor.setWorkspaceManager(this); 151 desktop.add(editor); 152 editor.addInternalFrameListener(frameListener); 153 activateEditor(editor); 154 } 155 156 /** Activate a specific editor. The editor is brought to the foreground in 157 * the desktop, deiconified if necessary, and given mouse focus. This method 158 * results in a call to the editor's <code>startEditing()</code> method. 159 * 160 * @param editor The editor to activate. 161 * @see kiwi.ui.WorkspaceEditor#startEditing 162 */ 163 164 public void activateEditor(WorkspaceEditor editor) 165 { 166 editor.moveToFront(); 167 168 try 169 { 170 editor.setSelected(true); 171 172 if(editor.isIcon()) 173 editor.setIcon(false); 174 } 175 catch(PropertyVetoException ex) {} 176 177 editor.beginFocus(); 178 startEditing(editor); 179 } 180 181 /** Remove an editor from the workspace. The editor's window becomes 182 * invisible. This method does <i>not</i> attempt a save on the editor. If 183 * the editor is currently active, its <code>stopEditing()</code> method is 184 * called before it is removed. 185 * 186 * @param editor The <code>WorkspaceEditor</code> to remove from the 187 * workspace. 188 * @see #addEditor 189 * @see #closeEditor 190 * @see #removeAllEditors 191 * @see kiwi.ui.WorkspaceEditor#stopEditing 192 */ 193 194 public void removeEditor(WorkspaceEditor editor) 195 { 196 stopEditing(editor); 197 editors.removeElement(editor); 198 desktop.remove(editor); 199 editor.removeInternalFrameListener(frameListener); 200 desktop.repaint(); 201 fireEditorClosed(editor); 202 } 203 204 /** Determine if there are any editors in the workspace that have unsaved 205 * changes. 206 * 207 * @return <code>true</code> if there is at least one editor with unsaved 208 * changes, <code>false</code> otherwise. 209 */ 210 211 public boolean areUnsavedEditors() 212 { 213 Enumeration e = editors.elements(); 214 while(e.hasMoreElements()) 215 { 216 WorkspaceEditor editor = (WorkspaceEditor)e.nextElement(); 217 if(editor.hasUnsavedChanges()) 218 return(true); 219 } 220 221 return(false); 222 } 223 224 /** Get the editor for a specific object. Each editor can be associated with 225 * an arbitrary user object (the object that it is editing). This method 226 * searches for an editor that is associated with the given object. Objects 227 * are compared by calling <code>Object.equals()</code>, not by comparing 228 * references. 229 * 230 * @param object The object whose editor is desired. 231 * @return The editor associated with <code>object</code>, or 232 * <code>null</code> if there is no editor for this object in the workspace. 233 * @see java.lang.Object#equals 234 */ 235 236 public WorkspaceEditor getEditorForObject(Object object) 237 { 238 Enumeration e = editors.elements(); 239 while(e.hasMoreElements()) 240 { 241 WorkspaceEditor editor = (WorkspaceEditor)e.nextElement(); 242 Object o = editor.getObject(); 243 if(o != null) 244 if(o.equals(object)) 245 return(editor); 246 } 247 248 return(null); 249 } 250 251 /** Register a <code>WorkspaceListener</code> with this 252 * <code>WorkspaceManager</code>. Listeners are notified about events 253 * relating to the editors currently being managed by this manager. 254 * 255 * @param listener The listener to register. 256 * @see #removeWorkspaceListener 257 */ 258 259 public void addWorkspaceListener(WorkspaceListener listener) 260 { 261 listeners.addElement(listener); 262 } 263 264 /** Unregister a <code>WorkspaceListener</code> from this 265 * <code>WorkspaceManager</code>. 266 * 267 * @param listener The listener to unregister. 268 * @see #addWorkspaceListener 269 */ 270 271 public void removeWorkspaceListener(WorkspaceListener listener) 272 { 273 listeners.removeElement(listener); 274 } 275 276 /** Notify listeners that an editor has been selected in the workspace. 277 * 278 * @param editor The editor that was selected. 279 */ 280 281 protected void fireEditorSelected(WorkspaceEditor editor) 282 { 283 WorkspaceEvent evt = null; 284 Enumeration e = listeners.elements(); 285 286 while(e.hasMoreElements()) 287 { 288 WorkspaceListener l = (WorkspaceListener)e.nextElement(); 289 if(evt == null) 290 evt = new WorkspaceEvent(this, editor); 291 l.editorSelected(evt); 292 } 293 } 294 295 /** Notify listeners that an editor has been deselected in the workspace. 296 * 297 * @param editor The editor that was deselected. 298 */ 299 300 protected void fireEditorDeselected(WorkspaceEditor editor) 301 { 302 WorkspaceEvent evt = null; 303 Enumeration e = listeners.elements(); 304 305 while(e.hasMoreElements()) 306 { 307 WorkspaceListener l = (WorkspaceListener)e.nextElement(); 308 if(evt == null) 309 evt = new WorkspaceEvent(this, editor); 310 l.editorDeselected(evt); 311 } 312 } 313 314 /** Notify listeners that an editor has been maximized in the workspace. 315 * 316 * @param editor The editor that was maximized. 317 */ 318 319 protected void fireEditorRestored(WorkspaceEditor editor) 320 { 321 WorkspaceEvent evt = null; 322 Enumeration e = listeners.elements(); 323 324 while(e.hasMoreElements()) 325 { 326 WorkspaceListener l = (WorkspaceListener)e.nextElement(); 327 if(evt == null) 328 evt = new WorkspaceEvent(this, editor); 329 l.editorRestored(evt); 330 } 331 } 332 333 /** Notify listeners that an editor has been minimized (iconified) in the 334 * workspace. 335 * 336 * @param editor The editor that was minimized. 337 */ 338 339 protected void fireEditorIconified(WorkspaceEditor editor) 340 { 341 WorkspaceEvent evt = null; 342 Enumeration e = listeners.elements(); 343 344 while(e.hasMoreElements()) 345 { 346 WorkspaceListener l = (WorkspaceListener)e.nextElement(); 347 if(evt == null) 348 evt = new WorkspaceEvent(this, editor); 349 l.editorIconified(evt); 350 } 351 } 352 353 /** Notify listeners that an editor has been closed in the workspace. 354 * 355 * @param editor The editor that was closed. 356 */ 357 358 protected void fireEditorClosed(WorkspaceEditor editor) 359 { 360 WorkspaceEvent evt = null; 361 Enumeration e = listeners.elements(); 362 363 while(e.hasMoreElements()) 364 { 365 WorkspaceListener l = (WorkspaceListener)e.nextElement(); 366 if(evt == null) 367 evt = new WorkspaceEvent(this, editor); 368 l.editorClosed(evt); 369 } 370 } 371 372 /** Notify listeners that an editor's state has changed. 373 * 374 * @param editor The editor whose state has changed. 375 */ 376 377 protected void fireEditorStateChanged(WorkspaceEditor editor) 378 { 379 WorkspaceEvent evt = null; 380 Enumeration e = listeners.elements(); 381 382 while(e.hasMoreElements()) 383 { 384 WorkspaceListener l = (WorkspaceListener)e.nextElement(); 385 if(evt == null) 386 evt = new WorkspaceEvent(this, editor); 387 l.editorStateChanged(evt); 388 } 389 } 390 391 /** Count the editors in the workspace. 392 * 393 * @return The number of editors currently in the workspace (and being 394 * managed by this <code>WorkspaceManager</code>). 395 */ 396 397 public int getEditorCount() 398 { 399 return(editors.size()); 400 } 401 402 /** Close an editor. If the editor has unsaved changes, the user will be 403 * prompted with a dialog to that effect. If this editor is currently 404 * active, its <code>stopEditing()</code> method is called before it is 405 * closed. 406 * 407 * @param editor The editor to close. 408 * @see #removeEditor 409 * @see #closeAllEditors 410 * @see kiwi.ui.WorkspaceEditor#stopEditing 411 */ 412 413 public boolean closeEditor(WorkspaceEditor editor) 414 { 415 if(editor.hasUnsavedChanges()) 416 { 417 activateEditor(editor); 418 Object o = editor.getObject(); 419 420 String msg = loc.getMessage("kiwi.dialog.message.save_to", 421 ((o != null) ? o.toString() : "(Untitled)")); 422 423 if(dialogs.showQuestionDialog(msg)) 424 { 425 if(!editor.save()) 426 { 427 dialogs.showMessageDialog( 428 loc.getMessage("kiwi.dialog.message.save_failed")); 429 return(false); 430 } 431 else 432 { 433 editor.setChangesMade(false); 434 removeEditor(editor); 435 return(true); 436 } 437 } 438 else 439 { 440 removeEditor(editor); 441 return(true); 442 } 443 } 444 else 445 removeEditor(editor); 446 447 return(true); 448 } 449 450 /** Remove all editors from the workspace. Unconditionally removes each 451 * editor from the workspace, without attempting to save unsaved changes. 452 * 453 * @see #closeAllEditors 454 */ 455 456 public void removeAllEditors() 457 { 458 for(int i = editors.size() - 1; i >= 0; i--) 459 { 460 WorkspaceEditor editor = (WorkspaceEditor)editors.elementAt(i); 461 removeEditor(editor); 462 } 463 } 464 465 /** Close all of the editors in the workspace. For each editor with unsaved 466 * changes, the user will be prompted with a dialog asking whether that 467 * editor should save its changes. 468 * 469 * @return <code>true</code> if all editors were closed successfully, 470 * <code>false</code> otherwise. 471 * @see #removeAllEditors 472 */ 473 474 public boolean closeAllEditors() 475 { 476 for(int i = editors.size() - 1; i >= 0; i--) 477 { 478 WorkspaceEditor editor = (WorkspaceEditor)editors.elementAt(i); 479 480 // save changes 481 482 if(!closeEditor(editor)) return(false); 483 } 484 485 return(true); 486 } 487 488 /* frame event listener */ 489 490 private class _FrameListener extends InternalFrameAdapter 491 { 492 public void internalFrameActivated(InternalFrameEvent evt) 493 { 494 WorkspaceEditor e = (WorkspaceEditor)evt.getSource(); 495 496 if(activeEditor != null) 497 fireEditorDeselected(activeEditor); 498 499 fireEditorSelected(e); 500 activeEditor = e; 501 startEditing(e); 502 } 503 504 public void internalFrameIconified(InternalFrameEvent evt) 505 { 506 WorkspaceEditor e = (WorkspaceEditor)evt.getSource(); 507 508 stopEditing(e); 509 fireEditorIconified(e); 510 } 511 512 public void internalFrameDeiconified(InternalFrameEvent evt) 513 { 514 WorkspaceEditor e = (WorkspaceEditor)evt.getSource(); 515 516 fireEditorRestored(e); 517 } 518 519 public void internalFrameClosed(InternalFrameEvent evt) 520 { 521 } 522 523 public void internalFrameClosing(InternalFrameEvent evt) 524 { 525 WorkspaceEditor e = (WorkspaceEditor)evt.getSource(); 526 527 if(e.hasUnsavedChanges()) 528 { 529 Object o = e.getObject(); 530 531 String msg = loc.getMessage("kiwi.dialog.message.save_to", 532 ((o != null) ? o.toString() 533 : "(Untitled)")); 534 535 if(dialogs.showQuestionDialog(msg)) 536 { 537 if(!e.save()) 538 { 539 dialogs.showMessageDialog( 540 loc.getMessage("kiwi.dialog.message.save_failed")); 541 } 542 else 543 { 544 e.setChangesMade(false); 545 removeEditor(e); 546 } 547 } 548 } 549 else 550 { 551 removeEditor(e); 552 } 553 } 554 } 555 556 /** Notify the manager that an editor's state has changed. A 557 * <code>WorkspaceEditor</code> uses this method to broadcast a change event 558 * to all <code>WorkspaceListener</code>s. 559 * 560 * @param editor The editor whose state has changed. 561 */ 562 563 public void notifyStateChanged(WorkspaceEditor editor) 564 { 565 fireEditorStateChanged(editor); 566 } 567 568 /** Update the look and feel of all of the components being managed by this 569 * <code>WorkspaceManager</code>. 570 */ 571 572 public synchronized void updateLookAndFeel() 573 { 574 Enumeration e = editors.elements(); 575 while(e.hasMoreElements()) 576 { 577 WorkspaceEditor ed = (WorkspaceEditor)e.nextElement(); 578 SwingUtilities.updateComponentTreeUI((Component)ed); 579 SwingUtilities.updateComponentTreeUI((Component)ed.getDesktopIcon()); 580 } 581 } 582 583 /** Register a menu with this <code>WorkspaceManager</code>. This convenience 584 * method provides a means for a <code>WorkspaceEditor</code> to manipulate 585 * one or more menus in an external menubar (such as the main application's 586 * menubar). 587 * 588 * @param name A symbolic name for the menu. 589 * @param menu The <code>JMenu</code> to register. 590 * @see #unregisterMenu 591 * @see #getMenu 592 */ 593 594 public void registerMenu(String name, JMenu menu) 595 { 596 menus.put(name, menu); 597 } 598 599 /** Unregister a menu from this <code>WorkspaceManager</code>. 600 * 601 * @param name The name of the menu to unregister. 602 * @see #registerMenu 603 */ 604 605 public void unregisterMenu(String name) 606 { 607 menus.remove(name); 608 } 609 610 /** Get a reference to a <code>JMenu</code> associated with this 611 * <code>WorkspaceManager</code>. 612 * 613 * @param name The name under which the menu was registered. 614 * @return The <code>JMenu</code> reference, or <code>null</code> if there is 615 * no menu registered under the specified name. 616 * @see #registerMenu 617 * @see #unregisterMenu 618 */ 619 620 public JMenu getMenu(String name) 621 { 622 return((JMenu)(menus.get(name))); 623 } 624 625 /* Attach (or detach) an editor as a listener of all menu item action 626 * events. 627 */ 628 629 private void updateMenuHooks(WorkspaceEditor editor, boolean unhook) 630 { 631 Enumeration e = menus.keys(); 632 while(e.hasMoreElements()) 633 { 634 String m = (String)e.nextElement(); 635 JMenu menu = (JMenu)menus.get(m); 636 int c = menu.getItemCount(); 637 for(int i = 0; i < c; i++) 638 { 639 JMenuItem item = menu.getItem(i); 640 if(unhook) 641 item.removeActionListener(editor); 642 else 643 item.addActionListener(editor); 644 } 645 } 646 } 647 648 /* Stop editing in a given editor. 649 * 650 * @param editor The editor to stop editing in. The editor is detached 651 * as a listener for action events on all menu items of all registered 652 * manus, and then the editor's <code>stopEditing()</code> method is 653 * called. 654 */ 655 656 private void stopEditing(WorkspaceEditor editor) 657 { 658 editor.stopEditing(); 659 updateMenuHooks(editor, true); 660 } 661 662 /* Start editing for a given editor. 663 * 664 * @param editor The editor to start editing for. The editor is attached 665 * as a listener for action events on all menu items of all registered 666 * manus, and then the editor's <code>startEditing()</code> method is 667 * called. 668 */ 669 670 private void startEditing(WorkspaceEditor editor) 671 { 672 updateMenuHooks(editor, false); 673 editor.startEditing(); 674 } 675 676 } 677 678/* end of source file */