001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018: package org.columba.mail.gui.frame;
019:
020: import java.awt.BorderLayout;
021: import java.awt.Point;
022: import java.awt.event.KeyEvent;
023: import java.awt.event.MouseAdapter;
024: import java.awt.event.MouseEvent;
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.IOException;
028: import java.io.InputStream;
029:
030: import javax.swing.BorderFactory;
031: import javax.swing.JPanel;
032: import javax.swing.JPopupMenu;
033: import javax.swing.JScrollPane;
034: import javax.swing.JSplitPane;
035: import javax.swing.JTable;
036: import javax.swing.KeyStroke;
037: import javax.swing.SwingUtilities;
038: import javax.swing.tree.TreePath;
039:
040: import org.columba.api.gui.frame.IContainer;
041: import org.columba.api.gui.frame.IDock;
042: import org.columba.api.gui.frame.IDockable;
043: import org.columba.api.selection.ISelectionListener;
044: import org.columba.api.selection.SelectionChangedEvent;
045: import org.columba.core.config.ViewItem;
046: import org.columba.core.gui.base.UIFSplitPane;
047: import org.columba.core.gui.menu.MenuXMLDecoder;
048: import org.columba.core.gui.tagging.TagList;
049: import org.columba.core.gui.tagging.TagPopupMenu;
050: import org.columba.core.io.DiskIO;
051: import org.columba.mail.command.IMailFolderCommandReference;
052: import org.columba.mail.config.MailConfig;
053: import org.columba.mail.folder.IMailFolder;
054: import org.columba.mail.folder.IMailbox;
055: import org.columba.mail.folder.IMailboxInfo;
056: import org.columba.mail.folder.event.IFolderEvent;
057: import org.columba.mail.folder.event.IFolderListener;
058: import org.columba.mail.gui.composer.HeaderController;
059: import org.columba.mail.gui.filtertoolbar.FilterToolbar;
060: import org.columba.mail.gui.message.action.ViewMessageAction;
061: import org.columba.mail.gui.table.ITableController;
062: import org.columba.mail.gui.table.TableController;
063: import org.columba.mail.gui.table.action.DeleteAction;
064: import org.columba.mail.gui.table.action.OpenMessageWithComposerAction;
065: import org.columba.mail.gui.table.action.OpenMessageWithMessageFrameAction;
066: import org.columba.mail.gui.table.action.ViewHeaderListAction;
067: import org.columba.mail.gui.table.model.HeaderTableModel;
068: import org.columba.mail.gui.table.model.MessageNode;
069: import org.columba.mail.gui.table.selection.TableSelectionChangedEvent;
070: import org.columba.mail.gui.table.selection.TableSelectionHandler;
071: import org.columba.mail.gui.tagging.MailTagList;
072: import org.columba.mail.gui.tree.FolderTreeModel;
073: import org.columba.mail.gui.tree.ITreeController;
074: import org.columba.mail.gui.tree.TreeController;
075: import org.columba.mail.gui.tree.action.MoveDownAction;
076: import org.columba.mail.gui.tree.action.MoveUpAction;
077: import org.columba.mail.gui.tree.action.RenameFolderAction;
078: import org.columba.mail.gui.tree.action.SortFoldersMenu;
079: import org.columba.mail.gui.tree.selection.TreeSelectionChangedEvent;
080: import org.columba.mail.gui.tree.selection.TreeSelectionHandler;
081: import org.columba.mail.util.MailResourceLoader;
082:
083: /**
084: * @author fdietz
085: */
086: public class ThreePaneMailFrameController extends
087: AbstractMailFrameController implements TreeViewOwner,
088: TableViewOwner, ISelectionListener, IFolderListener {
089:
090: public TreeController treeController;
091:
092: public TableController tableController;
093:
094: public HeaderController headerController;
095:
096: public FilterToolbar filterToolbar;
097:
098: public JSplitPane mainSplitPane;
099:
100: public JSplitPane rightSplitPane;
101:
102: private JPanel tablePanel;
103:
104: private JPanel messagePanel;
105:
106: private IMailFolder currentFolder;
107:
108: /**
109: * true, if the messagelist table selection event was triggered by a popup
110: * event. False, otherwise.
111: */
112: public boolean isTablePopupEvent;
113:
114: /**
115: * true, if the tree selection event was triggered by a popup event. False,
116: * otherwise.
117: */
118: public boolean isTreePopupEvent;
119:
120: private IDockable folderTreeDockable;
121:
122: private IDockable messageListDockable;
123:
124: private IDockable messageViewerDockable;
125:
126: private IDockable tagListDockable;
127:
128: /**
129: * @param viewItem
130: */
131: public ThreePaneMailFrameController(ViewItem viewItem) {
132: super (viewItem);
133:
134: treeController = new TreeController(this , FolderTreeModel
135: .getInstance());
136: tableController = new TableController(this );
137:
138: // create selection handlers
139: TableSelectionHandler tableHandler = new TableSelectionHandler(
140: tableController);
141: getSelectionManager().addSelectionHandler(tableHandler);
142: tableHandler.addSelectionListener(this );
143:
144: TreeSelectionHandler treeHandler = new TreeSelectionHandler(
145: treeController.getView());
146: getSelectionManager().addSelectionHandler(treeHandler);
147:
148: // double-click mouse listener
149: tableController.getView().addMouseListener(
150: new TableMouseListener());
151:
152: treeController.getView().addMouseListener(
153: new TreeMouseListener());
154:
155: // table registers interest in tree selection events
156: treeHandler.addSelectionListener(tableHandler);
157:
158: // also register interest in tree seleciton events
159: // for updating the title
160: treeHandler.addSelectionListener(this );
161:
162: filterToolbar = new FilterToolbar(this );
163:
164: RenameFolderAction renameFolderAction = new RenameFolderAction(
165: this );
166:
167: // Register F2 hotkey for renaming folder when the message panel has
168: // focus
169: tableController.getView().getActionMap().put("F2",
170: renameFolderAction);
171: tableController.getView().getInputMap().put(
172: KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "F2");
173:
174: // Register F2 hotkey for renaming folder when the folder tree itself
175: // has focus
176: treeController.getView().getActionMap().put("F2",
177: renameFolderAction);
178: treeController.getView().getInputMap().put(
179: KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "F2");
180:
181: // Register Alt-Up hotkey for moving up folder when folder tree or
182: // table have focus
183: MoveUpAction moveUpAction = new MoveUpAction(this );
184: tableController.getView().getActionMap().put("ALT_UP",
185: moveUpAction);
186: tableController.getView().getInputMap().put(
187: KeyStroke.getKeyStroke(KeyEvent.VK_UP,
188: KeyEvent.ALT_MASK), "ALT_UP");
189:
190: treeController.getView().getActionMap().put("ALT_UP",
191: moveUpAction);
192: treeController.getView().getInputMap().put(
193: KeyStroke.getKeyStroke(KeyEvent.VK_UP,
194: KeyEvent.ALT_MASK), "ALT_UP");
195:
196: // Register Alt-Down hotkey for moving up folder when folder tree or
197: // table have focus
198: MoveDownAction moveDownAction = new MoveDownAction(this );
199: tableController.getView().getActionMap().put("ALT_DOWN",
200: moveDownAction);
201: tableController.getView().getInputMap().put(
202: KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,
203: KeyEvent.ALT_MASK), "ALT_DOWN");
204:
205: treeController.getView().getActionMap().put("ALT_DOWN",
206: moveDownAction);
207: treeController.getView().getInputMap().put(
208: KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,
209: KeyEvent.ALT_MASK), "ALT_DOWN");
210:
211: DeleteAction deleteAction = new DeleteAction(this );
212: tableController.getView().getActionMap().put("DEL",
213: deleteAction);
214: tableController.getView().getInputMap().put(
215: KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "DEL");
216:
217: OpenMessageWithMessageFrameAction openAction = new OpenMessageWithMessageFrameAction(
218: this );
219: tableController.getView().getActionMap().put("ENTER",
220: openAction);
221: tableController.getView().getInputMap().put(
222: KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "ENTER");
223:
224: registerDockables();
225:
226: tableController.createPopupMenu();
227: treeController.createPopupMenu();
228: // messageController.createPopupMenu();
229:
230: }
231:
232: public void enableMessagePreview(boolean enable) {
233: getViewItem().setBoolean("header_enabled", enable);
234:
235: if (enable) {
236: rightSplitPane = new UIFSplitPane();
237: rightSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
238: rightSplitPane.add(tablePanel, JSplitPane.LEFT);
239: rightSplitPane.add(messagePanel, JSplitPane.RIGHT);
240:
241: mainSplitPane.add(rightSplitPane, JSplitPane.RIGHT);
242: } else {
243: rightSplitPane = null;
244:
245: mainSplitPane.add(tablePanel, JSplitPane.RIGHT);
246: }
247:
248: mainSplitPane.setDividerLocation(viewItem
249: .getIntegerWithDefault("splitpanes", "main", 100));
250:
251: if (enable)
252: rightSplitPane
253: .setDividerLocation(viewItem.getIntegerWithDefault(
254: "splitpanes", "header", 100));
255:
256: fireLayoutChanged();
257: }
258:
259: /**
260: * @return Returns the filterToolbar.
261: */
262: public FilterToolbar getFilterToolbar() {
263: return filterToolbar;
264: }
265:
266: /**
267: * @see org.columba.mail.gui.frame.TreeViewOwner#getTreeController()
268: */
269: public ITreeController getTreeController() {
270: return treeController;
271: }
272:
273: /**
274: * @see org.columba.mail.gui.frame.TableViewOwner#getTableController()
275: */
276: public ITableController getTableController() {
277: return tableController;
278: }
279:
280: /**
281: * @see org.columba.api.gui.frame.IFrameMediator#getContentPane()
282: */
283: // public JComponent getContentPane() {
284: // JComponent c = super.getContentPane();
285: //
286: //
287: //
288: // return c;
289: // }
290: public void showFilterToolbar() {
291: tablePanel.add(filterToolbar, BorderLayout.NORTH);
292: tablePanel.validate();
293:
294: }
295:
296: public void hideFilterToolbar() {
297: tablePanel.remove(filterToolbar);
298: tablePanel.validate();
299:
300: }
301:
302: // public void savePositions(ViewItem viewItem) {
303: // super.savePositions(viewItem);
304: //
305: // // splitpanes
306: // viewItem.setInteger("splitpanes", "main", mainSplitPane
307: // .getDividerLocation());
308: //
309: // if (rightSplitPane != null)
310: // viewItem.setInteger("splitpanes", "header", rightSplitPane
311: // .getDividerLocation());
312: // viewItem.setBoolean("splitpanes", "header_enabled",
313: // rightSplitPane != null);
314: //
315: //
316: // }
317:
318: /**
319: * @see org.columba.api.gui.frame.IFrameMediator#getString(java.lang.String,
320: * java.lang.String, java.lang.String)
321: */
322: public String getString(String sPath, String sName, String sID) {
323: return MailResourceLoader.getString(sPath, sName, sID);
324: }
325:
326: /**
327: * @see org.columba.api.gui.frame.IFrameMediator#getContentPane()
328: */
329: // public IContentPane getContentPane() {
330: // return this;
331: // }
332: /**
333: * @see org.columba.api.selection.ISelectionListener#selectionChanged(org.columba.api.selection.SelectionChangedEvent)
334: */
335: public void selectionChanged(SelectionChangedEvent e) {
336:
337: if (e instanceof TreeSelectionChangedEvent) {
338: // tree selection event
339: TreeSelectionChangedEvent event = (TreeSelectionChangedEvent) e;
340:
341: IMailFolder[] selectedFolders = event.getSelected();
342:
343: if (isTreePopupEvent == false) {
344: // view headerlist in message list viewer
345: new ViewHeaderListAction(this ).actionPerformed(null);
346:
347: // Unregister/register as Folder listener
348: if (currentFolder != null) {
349: currentFolder.removeFolderListener(this );
350: currentFolder = null;
351: }
352: if (selectedFolders.length == 1
353: && selectedFolders[0] != null) {
354: selectedFolders[0].addFolderListener(this );
355: currentFolder = selectedFolders[0];
356: }
357:
358: // update frame title
359: updateTreeDockableTitle();
360: }
361:
362: isTreePopupEvent = false;
363:
364: } else if (e instanceof TableSelectionChangedEvent) {
365: if (isTablePopupEvent == false)
366: // show message content
367: new ViewMessageAction(this ).actionPerformed(null);
368:
369: isTablePopupEvent = false;
370: } else
371: throw new IllegalArgumentException(
372: "unknown selection changed event");
373: }
374:
375: private void updateTreeDockableTitle() {
376: if (currentFolder != null) {
377: fireTitleChanged(currentFolder.getName());
378:
379: // update message list view title
380: messageListDockable.setTitle(currentFolder.getName());
381:
382: // simply demonstration of how to change the docking title
383: if (currentFolder instanceof IMailbox) {
384: IMailboxInfo info = ((IMailbox) currentFolder)
385: .getMessageFolderInfo();
386: StringBuffer buf = new StringBuffer();
387: buf.append("Total: " + info.getExists());
388: buf.append(" Recent: " + info.getRecent());
389: folderTreeDockable.setTitle(buf.toString());
390: } else
391: folderTreeDockable.setTitle(currentFolder.getName());
392: } else {
393: fireTitleChanged("");
394: }
395: }
396:
397: /**
398: * Double-click mouse listener for message list table component. <p/> If
399: * message is marked as draft, the composer will be opened to edit the
400: * message. Otherwise, the message will be viewed in the message frame.
401: *
402: * @author Frederik Dietz
403: */
404: class TableMouseListener extends MouseAdapter {
405:
406: /**
407: * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
408: */
409: public void mousePressed(MouseEvent event) {
410: if (event.isPopupTrigger()) {
411: processPopup(event);
412: }
413: }
414:
415: /**
416: * @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)
417: */
418: public void mouseReleased(MouseEvent event) {
419: if (event.isPopupTrigger()) {
420: processPopup(event);
421: }
422: }
423:
424: /**
425: * @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
426: */
427: public void mouseClicked(MouseEvent event) {
428: // if mouse button was pressed twice times
429: if (event.getClickCount() == 2) {
430: // get selected row
431: int selectedRow = tableController.getView()
432: .getSelectedRow();
433:
434: // get message node at selected row
435: MessageNode node = (MessageNode) ((HeaderTableModel) tableController
436: .getHeaderTableModel())
437: .getMessageNodeAtRow(selectedRow);
438:
439: // is the message marked as draft ?
440: boolean markedAsDraft = node.getHeader().getFlags()
441: .getDraft();
442:
443: if (markedAsDraft) {
444: // edit message in composer
445: new OpenMessageWithComposerAction(
446: ThreePaneMailFrameController.this )
447: .actionPerformed(null);
448: } else {
449: // open message in new message-frame
450: new OpenMessageWithMessageFrameAction(
451: ThreePaneMailFrameController.this )
452: .actionPerformed(null);
453: }
454: }
455: }
456:
457: protected void processPopup(final MouseEvent event) {
458:
459: isTablePopupEvent = true;
460:
461: JTable table = tableController.getView();
462:
463: int selectedRows = table.getSelectedRowCount();
464:
465: if (selectedRows <= 1) {
466: // select node
467: int row = table.rowAtPoint(new Point(event.getX(),
468: event.getY()));
469: table.setRowSelectionInterval(row, row);
470: }
471:
472: SwingUtilities.invokeLater(new Runnable() {
473:
474: public void run() {
475: tableController.getPopupMenu().show(
476: event.getComponent(), event.getX(),
477: event.getY());
478: isTablePopupEvent = false;
479: }
480: });
481: }
482: }
483:
484: class TreeMouseListener extends MouseAdapter {
485:
486: /**
487: * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
488: */
489: public void mousePressed(MouseEvent event) {
490: if (event.isPopupTrigger()) {
491: processPopup(event);
492: }
493: }
494:
495: /**
496: * @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)
497: */
498: public void mouseReleased(MouseEvent event) {
499: if (event.isPopupTrigger()) {
500: processPopup(event);
501: }
502: }
503:
504: /**
505: * @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
506: */
507: public void mouseClicked(MouseEvent event) {
508: // if mouse button was pressed twice times
509: if (event.getClickCount() == 2) {
510: // get selected row
511:
512: }
513: }
514:
515: protected void processPopup(final MouseEvent event) {
516:
517: isTreePopupEvent = true;
518:
519: Point point = event.getPoint();
520: TreePath path = treeController.getView()
521: .getClosestPathForLocation(point.x, point.y);
522: treeController.getView().setSelectionPath(path);
523:
524: SwingUtilities.invokeLater(new Runnable() {
525:
526: public void run() {
527: treeController.getPopupMenu().show(
528: event.getComponent(), event.getX(),
529: event.getY());
530: isTreePopupEvent = false;
531: }
532: });
533: }
534: }
535:
536: /**
537: * @see org.columba.core.gui.frame.DefaultFrameController#close(org.columba.api.gui.frame.IContainer)
538: */
539: public void close(IContainer container) {
540: super .close(container);
541:
542: IMailFolderCommandReference r = getTreeSelection();
543:
544: if (r != null) {
545: IMailFolder folder = (IMailFolder) r.getSourceFolder();
546:
547: // folder-based configuration
548:
549: if (folder instanceof IMailbox)
550: getFolderOptionsController().save((IMailbox) folder);
551: }
552: }
553:
554: /**
555: * @see org.columba.core.gui.frame.DockFrameController#loadDefaultPosition()
556: */
557: public void loadDefaultPosition() {
558:
559: super .dock(messageListDockable, IDock.REGION.CENTER);
560:
561: super .dock(folderTreeDockable, messageListDockable,
562: IDock.REGION.WEST, 0.3f);
563:
564: super .dock(messageViewerDockable, messageListDockable,
565: IDock.REGION.SOUTH, 0.3f);
566:
567: super .setSplitProportion(folderTreeDockable, 0.3f);
568: super .setSplitProportion(messageListDockable, 0.35f);
569: }
570:
571: private void registerDockables() {
572:
573: JPopupMenu popup = null;
574:
575: // mail folder tree
576: JScrollPane treeScrollPane = new JScrollPane(treeController
577: .getView());
578: treeScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0,
579: 0, 0));
580:
581: // JPopupMenu popup = null;
582: // try {
583: // InputStream is = DiskIO
584: // .getResourceStream("org/columba/mail/action/table_dockmenu.xml");
585: // popup = new MenuXMLDecoder(this).createPopupMenu(is);
586: // } catch (IOException e1) {
587: // e1.printStackTrace();
588: // }
589:
590: folderTreeDockable = registerDockable("mail_foldertree",
591: MailResourceLoader.getString("global",
592: "dockable_foldertree"), treeScrollPane,
593: new SortFoldersMenu(this ));
594:
595: // message list
596: JPanel p = new JPanel();
597: p.setLayout(new BorderLayout());
598: JScrollPane tableScrollPane = new JScrollPane(tableController
599: .getView());
600: tableScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0,
601: 0, 0));
602: p.add(tableScrollPane, BorderLayout.CENTER);
603: p.add(filterToolbar, BorderLayout.NORTH);
604:
605: popup = null;
606: try {
607: InputStream is = DiskIO
608: .getResourceStream("org/columba/mail/action/table_dockmenu.xml");
609: popup = new MenuXMLDecoder(this ).createPopupMenu(is);
610: } catch (IOException e1) {
611: e1.printStackTrace();
612: }
613:
614: messageListDockable = registerDockable("mail_messagelist",
615: MailResourceLoader.getString("global",
616: "dockable_messagelist"), p, popup);
617:
618: popup = null;
619: try {
620: InputStream is = DiskIO
621: .getResourceStream("org/columba/mail/action/message_dockmenu.xml");
622: popup = new MenuXMLDecoder(this ).createPopupMenu(is);
623: } catch (IOException e1) {
624: e1.printStackTrace();
625: }
626:
627: messageViewerDockable = registerDockable("mail_messageviewer",
628: MailResourceLoader.getString("global",
629: "dockable_messageviewer"), messageController,
630: popup);
631:
632: // TagList tagList = new MailTagList(this);
633: // JScrollPane tagListScrollPane = new JScrollPane(tagList);
634: // tagListScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
635: // tagListScrollPane
636: // .setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
637:
638: // tagListDockable = registerDockable("mail_taglist", MailResourceLoader
639: // .getString("global", "dockable_taglist"), tagListScrollPane,
640: // new TagPopupMenu(this, tagList));
641: // tagList.setPopupMenu(new TagPopupMenu(this, tagList));
642: }
643:
644: /**
645: * ********************** container callbacks *************
646: */
647:
648: public void extendMenu(IContainer container) {
649: try {
650: InputStream is = DiskIO
651: .getResourceStream("org/columba/mail/action/menu.xml");
652: container.extendMenu(this , is);
653: } catch (IOException e) {
654: e.printStackTrace();
655: }
656: }
657:
658: public void extendToolBar(IContainer container) {
659: try {
660: File configDirectory = MailConfig.getInstance()
661: .getConfigDirectory();
662: InputStream is2 = new FileInputStream(new File(
663: configDirectory, "main_toolbar.xml"));
664: container.extendToolbar(this , is2);
665: } catch (IOException e) {
666: e.printStackTrace();
667: }
668: }
669:
670: /**
671: * @return Returns the messageViewerPanel.
672: */
673: public IDockable getMessageViewerDockable() {
674: return messageViewerDockable;
675: }
676:
677: public void messageAdded(IFolderEvent e) {
678: }
679:
680: public void messageRemoved(IFolderEvent e) {
681: }
682:
683: public void messageFlagChanged(IFolderEvent e) {
684: }
685:
686: public void folderPropertyChanged(IFolderEvent e) {
687: // fire in EDT
688: SwingUtilities.invokeLater(new Runnable() {
689: public void run() {
690: updateTreeDockableTitle();
691: }
692: });
693: }
694:
695: public void folderAdded(IFolderEvent e) {
696: }
697:
698: public void folderRemoved(IFolderEvent e) {
699: }
700:
701: }
|