0001: package net.suberic.pooka.gui;
0002:
0003: import net.suberic.pooka.*;
0004: import javax.mail.*;
0005: import javax.mail.internet.MimeMessage;
0006: import javax.mail.event.*;
0007: import java.awt.*;
0008: import java.awt.event.*;
0009: import javax.swing.*;
0010: import javax.swing.table.*;
0011: import javax.swing.text.TextAction;
0012: import java.util.*;
0013: import net.suberic.util.gui.*;
0014: import net.suberic.util.event.*;
0015: import net.suberic.util.thread.*;
0016: import net.suberic.util.swing.*;
0017: import net.suberic.pooka.gui.dnd.FolderTransferHandler;
0018:
0019: /**
0020: * This is a JPanel which contains a JTable which displays the messages in
0021: * the table.
0022: *
0023: * Note that this class does not actually do any real work. It does have
0024: * Actions, but those are just passed on from the MessageProxy object in
0025: * the table. You will need to have another component which implements
0026: * FolderDisplayUI to use as the actual UI object for the Folder. That
0027: * component can then use the FolderDisplayPanel to display the messages.
0028: *
0029: */
0030:
0031: public class FolderDisplayPanel extends JPanel {
0032: JTable messageTable = null;
0033: JScrollPane scrollPane = null;
0034: FolderInfo folderInfo = null;
0035: boolean enabled = true;
0036:
0037: boolean validated = false;
0038:
0039: /**
0040: * Creates an empty FolderDisplayPanel.
0041: */
0042: public FolderDisplayPanel() {
0043: initWindow();
0044: enabled = false;
0045: }
0046:
0047: /**
0048: * Creates a FolderDisplayPanel for the given FolderInfo.
0049: */
0050: public FolderDisplayPanel(FolderInfo newFolderInfo) {
0051: initWindow();
0052: setFolderInfo(newFolderInfo);
0053: addMessageTable();
0054: }
0055:
0056: /**
0057: * Initializes the window.
0058: */
0059:
0060: public void initWindow() {
0061: scrollPane = new JScrollPane();
0062: this .setLayout(new BorderLayout());
0063: this .add("Center", scrollPane);
0064:
0065: this .setPreferredSize(new Dimension(Integer.parseInt(Pooka
0066: .getProperty("folderWindow.height", "570")), Integer
0067: .parseInt(Pooka
0068: .getProperty("folderWindow.width", "380"))));
0069:
0070: // if the FolderDisplayPanel itself gets the focus, pass it on to
0071: // the messageTable
0072: this .addFocusListener(new FocusAdapter() {
0073: public void focusGained(FocusEvent e) {
0074: java.util.logging.Logger.getLogger(
0075: "Pooka.debug.gui.focus").fine(
0076: "folder display panel: gained focus.");
0077:
0078: if (messageTable != null) {
0079: messageTable.requestFocusInWindow();
0080: }
0081: Pooka.getMainPanel().refreshActiveMenus();
0082: if (getFolderInfo() != null
0083: && getFolderInfo().hasNewMessages()) {
0084: getFolderInfo().setNewMessages(false);
0085: FolderNode fn = getFolderInfo().getFolderNode();
0086: if (fn != null)
0087: fn.getParentContainer().repaint();
0088: }
0089: }
0090: });
0091:
0092: JScrollBar jsb = scrollPane.getVerticalScrollBar();
0093: if (jsb != null) {
0094: jsb.addAdjustmentListener(new AdjustmentListener() {
0095: public void adjustmentValueChanged(AdjustmentEvent e) {
0096: if (getFolderInfo() != null
0097: && getFolderInfo().hasNewMessages()) {
0098: getFolderInfo().setNewMessages(false);
0099: FolderNode fn = getFolderInfo().getFolderNode();
0100: if (fn != null)
0101: fn.getParentContainer().repaint();
0102: }
0103: }
0104:
0105: });
0106: }
0107:
0108: Pooka.getHelpBroker().enableHelpKey(this , "ui.folderWindow",
0109: Pooka.getHelpBroker().getHelpSet());
0110:
0111: setTransferHandler(new FolderTransferHandler());
0112:
0113: }
0114:
0115: /**
0116: * Creates the JTable for the FolderInfo and adds it to the component.
0117: */
0118: public void addMessageTable() {
0119: if (folderInfo != null) {
0120: createMessageTable();
0121: scrollPane.getViewport().add(messageTable);
0122: }
0123:
0124: }
0125:
0126: /**
0127: * This creates the messageTable.
0128: */
0129: public void createMessageTable() {
0130: messageTable = new JTable(getFolderInfo().getFolderTableModel()) {
0131: public String getToolTipText(MouseEvent event) {
0132: int rowIndex = rowAtPoint(event.getPoint());
0133: int columnIndex = columnAtPoint(event.getPoint());
0134: Object value = getValueAt(rowIndex, columnIndex);
0135: if (value != null) {
0136: return value.toString();
0137: } else {
0138: return null;
0139: }
0140: }
0141: };
0142:
0143: if (!Pooka.getProperty("FolderTable.showLines", "true").equals(
0144: "true")) {
0145: messageTable.setShowVerticalLines(false);
0146: messageTable.setShowHorizontalLines(false);
0147: }
0148:
0149: for (int i = 0; i < messageTable.getColumnCount(); i++) {
0150: messageTable.getColumnModel().getColumn(i)
0151: .setPreferredWidth(
0152: getFolderInfo().getFolderTableModel()
0153: .getColumnSize(i));
0154: }
0155:
0156: messageTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
0157:
0158: messageTable.setDefaultRenderer(Object.class,
0159: new FilterFolderCellRenderer());
0160: messageTable.setDefaultRenderer(Number.class,
0161: new FilterFolderCellRenderer());
0162:
0163: messageTable.setCellSelectionEnabled(false);
0164: messageTable.setColumnSelectionAllowed(false);
0165: messageTable.setRowSelectionAllowed(true);
0166: addListeners();
0167:
0168: messageTable.setTransferHandler(new FolderTransferHandler());
0169:
0170: messageTable.setDragEnabled(true);
0171:
0172: }
0173:
0174: /**
0175: * This removes the current message table.
0176: */
0177: public void removeMessageTable() {
0178: if (messageTable != null) {
0179: scrollPane.getViewport().remove(messageTable);
0180: if (getFolderInfo() != null)
0181: getFolderInfo().getFolderTableModel()
0182: .removeTableModelListener(messageTable);
0183: messageTable = null;
0184: }
0185: }
0186:
0187: /**
0188: * This removes rows from the FolderTableModel. This is the preferred
0189: * way to remove rows from the FolderTableModel.
0190: *
0191: * Called from within the FolderThread.
0192: */
0193: public void removeRows(Vector removedProxies) {
0194: /*
0195: This is here so that we can select the next row and remove the
0196: removed rows together in one call to the AWTEventThread.
0197: */
0198: final Vector removedProxiesTmp = removedProxies;
0199:
0200: try {
0201: SwingUtilities.invokeAndWait(new Runnable() {
0202: public void run() {
0203: moveSelectionOnRemoval(removedProxiesTmp);
0204:
0205: getFolderTableModel().removeRows(removedProxiesTmp);
0206: }
0207: });
0208: } catch (Exception e) {
0209: }
0210: }
0211:
0212: /**
0213: * This checks to see if the message which has been removed is
0214: * currently selected. If so, we unselect it and select the next
0215: * row.
0216: */
0217: public void moveSelectionOnRemoval(MessageChangedEvent e) {
0218: try {
0219: // don't bother if we're just going to autoexpunge it...
0220: if ((!Pooka.getProperty("Pooka.autoExpunge", "true")
0221: .equalsIgnoreCase("true"))
0222: && e.getMessageChangeType() == MessageChangedEvent.FLAGS_CHANGED
0223: && (e.getMessage().isExpunged() || e.getMessage()
0224: .getFlags().contains(Flags.Flag.DELETED))) {
0225: final Message changedMessage = e.getMessage();
0226: SwingUtilities.invokeLater(new Runnable() {
0227: public void run() {
0228: MessageProxy selectedProxy = getSelectedMessage();
0229:
0230: if (selectedProxy != null
0231: && (!(selectedProxy instanceof MultiMessageProxy))
0232: && selectedProxy.getMessageInfo()
0233: .getMessage().equals(
0234: changedMessage)) {
0235: selectNextMessage();
0236: }
0237: }
0238: });
0239: }
0240: } catch (MessagingException me) {
0241: }
0242: }
0243:
0244: /**
0245: * This checks to see if the message which has been removed is
0246: * currently selected. If so, we unselect it and select the next
0247: * row.
0248: */
0249: public void moveSelectionOnRemoval(MessageCountEvent e) {
0250: final Message[] removedMsgs = e.getMessages();
0251:
0252: SwingUtilities.invokeLater(new Runnable() {
0253: public void run() {
0254: MessageProxy selectedProxy = getSelectedMessage();
0255: if (selectedProxy != null) {
0256: boolean found = false;
0257: Message currentMsg = selectedProxy.getMessageInfo()
0258: .getMessage();
0259: for (int i = 0; (currentMsg != null
0260: && found == false && i < removedMsgs.length); i++) {
0261: if (currentMsg.equals(removedMsgs[i])) {
0262: found = true;
0263: }
0264: }
0265:
0266: if (found) {
0267: selectNextMessage();
0268: }
0269: }
0270: }
0271: });
0272:
0273: }
0274:
0275: /**
0276: * This checks to see if the message which has been removed is
0277: * currently selected. If so, we unselect it and select the next
0278: * row.
0279: *
0280: * Should be called on the AWTEventThread while the FolderThread
0281: * is locked.
0282: */
0283: void moveSelectionOnRemoval(Vector removedProxies) {
0284: MessageProxy selectedProxy = getSelectedMessage();
0285: if (selectedProxy != null) {
0286: boolean selectNextMessage = false;
0287: if (selectedProxy instanceof MultiMessageProxy) {
0288: MultiMessageInfo mmi = (MultiMessageInfo) selectedProxy
0289: .getMessageInfo();
0290: int messageCount = mmi.getMessageCount();
0291: selectNextMessage = true;
0292: for (int i = 0; selectNextMessage && i < messageCount; i++) {
0293: MessageProxy currentProxy = mmi.getMessageInfo(i)
0294: .getMessageProxy();
0295: if (!removedProxies.contains(currentProxy))
0296: selectNextMessage = false;
0297: }
0298:
0299: } else {
0300: if (removedProxies.contains(selectedProxy)) {
0301: selectNextMessage = true;
0302: }
0303:
0304: }
0305:
0306: if (selectNextMessage) {
0307: int currentlySelected = messageTable.getSelectedRow();
0308: int nextValue = getNextSelectableMessage(
0309: currentlySelected, removedProxies);
0310: if (nextValue >= messageTable.getRowCount()) {
0311: // in that case, check for a selectable message before this one.
0312: nextValue = getPreviousSelectableMessage(nextValue,
0313: removedProxies);
0314: }
0315:
0316: if (nextValue < 0) {
0317: // if we're removing all of the messages, then we should just
0318: // be able to unselect everything.
0319: int[] rowSelection = messageTable.getSelectedRows();
0320: messageTable.removeRowSelectionInterval(
0321: rowSelection[0],
0322: rowSelection[rowSelection.length - 1]);
0323: } else {
0324: selectMessage(nextValue);
0325: }
0326: }
0327: }
0328: }
0329:
0330: /**
0331: * This recreates the message table with a new FolderTableModel.
0332: */
0333: public void resetFolderTableModel(FolderTableModel newModel) {
0334: if (messageTable != null) {
0335: FolderTableModel oldFtm = (FolderTableModel) messageTable
0336: .getModel();
0337: oldFtm.removeTableModelListener(messageTable);
0338: //newModel.addTableModelListener(messageTable);
0339: messageTable.setModel(newModel);
0340: }
0341: }
0342:
0343: /**
0344: * This adds all the listeners to the current FolderDisplayPanel.
0345: */
0346:
0347: public void addListeners() {
0348: // add a mouse listener
0349:
0350: messageTable.addMouseListener(new MouseAdapter() {
0351: public void mouseClicked(MouseEvent e) {
0352: if (e.getClickCount() == 2) {
0353: int rowIndex = getMessageTable().rowAtPoint(
0354: e.getPoint());
0355: if (rowIndex != -1) {
0356: getMessageTable().setRowSelectionInterval(
0357: rowIndex, rowIndex);
0358: MessageProxy selectedMessage = getSelectedMessage();
0359: String actionCommand = Pooka.getProperty(
0360: "MessagePanel.2xClickAction",
0361: "file-open");
0362: if (selectedMessage != null) {
0363: Action clickAction = selectedMessage
0364: .getAction(actionCommand);
0365: if (clickAction != null && isEnabled()) {
0366: clickAction
0367: .actionPerformed(new ActionEvent(
0368: this ,
0369: ActionEvent.ACTION_PERFORMED,
0370: actionCommand));
0371:
0372: }
0373: }
0374: }
0375: }
0376: }
0377:
0378: public void mousePressed(MouseEvent e) {
0379: if (e.isPopupTrigger()) {
0380: // see if anything is selected
0381: int rowIndex = getMessageTable().rowAtPoint(
0382: e.getPoint());
0383: int columnIndex = getMessageTable().columnAtPoint(
0384: e.getPoint());
0385: if (rowIndex == -1
0386: || !getMessageTable().isRowSelected(
0387: rowIndex)) {
0388: getMessageTable().setRowSelectionInterval(
0389: rowIndex, rowIndex);
0390: }
0391:
0392: MessageProxy selectedMessage = getSelectedMessage();
0393: if (selectedMessage != null && isEnabled()) {
0394: Object o = getMessageTable().getValueAt(
0395: rowIndex, columnIndex);
0396: if (o != null && o instanceof BooleanIcon) {
0397: BooleanIcon bi = (BooleanIcon) o;
0398: if (bi.getIconId().equalsIgnoreCase(
0399: "attachments")
0400: && bi.iconValue()) {
0401: selectedMessage
0402: .showAttachmentPopupMenu(
0403: getMessageTable(), e);
0404: } else {
0405: selectedMessage.showPopupMenu(
0406: getMessageTable(), e);
0407:
0408: }
0409: } else {
0410: selectedMessage.showPopupMenu(
0411: getMessageTable(), e);
0412: }
0413: }
0414: }
0415: }
0416:
0417: public void mouseReleased(MouseEvent e) {
0418: if (e.isPopupTrigger()) {
0419: // see if anything is selected
0420: int rowIndex = getMessageTable().rowAtPoint(
0421: e.getPoint());
0422: int columnIndex = getMessageTable().columnAtPoint(
0423: e.getPoint());
0424: if (rowIndex == -1
0425: || !getMessageTable().isRowSelected(
0426: rowIndex)) {
0427: getMessageTable().setRowSelectionInterval(
0428: rowIndex, rowIndex);
0429: }
0430:
0431: MessageProxy selectedMessage = getSelectedMessage();
0432: if (selectedMessage != null && isEnabled())
0433: if (columnIndex == 2)
0434: selectedMessage.showAttachmentPopupMenu(
0435: getMessageTable(), e);
0436: else
0437: selectedMessage.showPopupMenu(
0438: getMessageTable(), e);
0439: }
0440: }
0441: });
0442:
0443: messageTable.getSelectionModel().addListSelectionListener(
0444: new SelectionListener());
0445:
0446: // add sorting by header.
0447:
0448: messageTable.getTableHeader().addMouseListener(
0449: new MouseAdapter() {
0450: public void mousePressed(MouseEvent e) {
0451: // show a wait cursor if we're not done loading the messages yet.
0452: boolean allLoaded = true;
0453: java.util.List data = ((FolderTableModel) messageTable
0454: .getModel()).getAllProxies();
0455: java.util.Iterator it = data.iterator();
0456: while (allLoaded && it.hasNext()) {
0457: MessageProxy current = (MessageProxy) it
0458: .next();
0459: if (!current.isLoaded())
0460: allLoaded = false;
0461: }
0462:
0463: if (!allLoaded) {
0464: messageTable
0465: .getTableHeader()
0466: .setCursor(
0467: Cursor
0468: .getPredefinedCursor(Cursor.WAIT_CURSOR));
0469: }
0470:
0471: }
0472:
0473: public void mouseReleased(MouseEvent e) {
0474: // clear the wait cursor, if any.
0475: messageTable
0476: .getTableHeader()
0477: .setCursor(
0478: Cursor
0479: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
0480: }
0481:
0482: public void mouseClicked(MouseEvent e) {
0483: TableColumnModel columnModel = messageTable
0484: .getColumnModel();
0485: int viewColumn = columnModel
0486: .getColumnIndexAtX(e.getX());
0487: int column = messageTable
0488: .convertColumnIndexToModel(viewColumn);
0489: if (e.getClickCount() == 1 && column != -1) {
0490: // check to make sure that all messages are loaded.
0491: boolean allLoaded = true;
0492: java.util.List data = ((FolderTableModel) messageTable
0493: .getModel()).getAllProxies();
0494: java.util.Iterator it = data.iterator();
0495: while (allLoaded && it.hasNext()) {
0496: MessageProxy current = (MessageProxy) it
0497: .next();
0498: if (!current.isLoaded())
0499: allLoaded = false;
0500: }
0501:
0502: if (allLoaded) {
0503: java.util.logging.Logger.getLogger(
0504: "Pooka.debug").fine(
0505: "Sorting ...");
0506:
0507: int shiftPressed = e.getModifiers()
0508: & InputEvent.SHIFT_MASK;
0509: boolean ascending = (shiftPressed == 0);
0510:
0511: MessageProxy selectedMessage = null;
0512:
0513: int rowsSelected = messageTable
0514: .getSelectedRowCount();
0515:
0516: if (rowsSelected == 1)
0517: selectedMessage = getFolderInfo()
0518: .getMessageProxy(
0519: messageTable
0520: .getSelectedRow());
0521: else if (rowsSelected > 1)
0522: selectedMessage = getFolderInfo()
0523: .getMessageProxy(
0524: messageTable
0525: .getSelectedRows()[0]);
0526:
0527: if (!ascending) {
0528: ((FolderTableModel) messageTable
0529: .getModel()).sortByColumn(
0530: column, ascending);
0531: } else {
0532: ((FolderTableModel) messageTable
0533: .getModel())
0534: .sortByColumn(column);
0535: }
0536:
0537: if (selectedMessage != null) {
0538: int selectedIndex = ((FolderTableModel) messageTable
0539: .getModel())
0540: .getRowForMessage(selectedMessage);
0541: messageTable
0542: .setRowSelectionInterval(
0543: selectedIndex,
0544: selectedIndex);
0545: makeSelectionVisible(selectedIndex);
0546: }
0547: }
0548: }
0549: }
0550: });
0551:
0552: messageTable.registerKeyboardAction(new ActionListener() {
0553: public void actionPerformed(ActionEvent e) {
0554: FolderDisplayUI fdui = getFolderInfo()
0555: .getFolderDisplayUI();
0556: if (fdui != null) {
0557: fdui.selectNextMessage();
0558: }
0559: }
0560: }, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DOWN, 0),
0561: JComponent.WHEN_FOCUSED);
0562:
0563: messageTable.registerKeyboardAction(new ActionListener() {
0564: public void actionPerformed(ActionEvent e) {
0565: FolderDisplayUI fdui = getFolderInfo()
0566: .getFolderDisplayUI();
0567: if (fdui != null) {
0568: fdui.selectPreviousMessage();
0569: }
0570: }
0571: }, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_UP, 0),
0572: JComponent.WHEN_FOCUSED);
0573:
0574: messageTable.registerKeyboardAction(
0575: new ActionListener() {
0576: public void actionPerformed(ActionEvent e) {
0577: MessageProxy selectedMessage = getSelectedMessage();
0578: if (selectedMessage != null) {
0579: Pooka.getUIFactory().doDefaultOpen(
0580: selectedMessage);
0581: }
0582: }
0583: }, KeyStroke.getKeyStroke(
0584: java.awt.event.KeyEvent.VK_ENTER, 0),
0585: JComponent.WHEN_FOCUSED);
0586:
0587: messageTable.registerKeyboardAction(
0588: new ActionListener() {
0589: public void actionPerformed(ActionEvent e) {
0590: MessageProxy selectedMessage = getSelectedMessage();
0591: if (selectedMessage != null) {
0592: Pooka.getUIFactory().doDefaultOpen(
0593: selectedMessage);
0594: }
0595: }
0596: }, KeyStroke.getKeyStroke(
0597: java.awt.event.KeyEvent.VK_SPACE, 0),
0598: JComponent.WHEN_FOCUSED);
0599:
0600: messageTable.registerKeyboardAction(new ActionListener() {
0601: public void actionPerformed(ActionEvent e) {
0602: selectNextUnreadMessage();
0603: }
0604: }, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_TAB, 0),
0605: JComponent.WHEN_FOCUSED);
0606:
0607: messageTable.registerKeyboardAction(new ActionListener() {
0608: public void actionPerformed(ActionEvent e) {
0609: selectPreviousUnreadMessage();
0610: }
0611: }, KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_TAB,
0612: java.awt.Event.SHIFT_MASK), JComponent.WHEN_FOCUSED);
0613:
0614: }
0615:
0616: /**
0617: * This finds the first unread message (if any) and sets that message
0618: * to selected, and returns that index.
0619: */
0620: public void selectFirstUnread() {
0621:
0622: // sigh.
0623: getFolderInfo().getFolderThread().addToQueue(
0624: new javax.swing.AbstractAction() {
0625: public void actionPerformed(
0626: java.awt.event.ActionEvent ae) {
0627: final int firstUnread = getFolderInfo()
0628: .getFirstUnreadMessage();
0629: SwingUtilities.invokeLater(new Runnable() {
0630: public void run() {
0631: int useFirstUnread = firstUnread;
0632: if (useFirstUnread < 0
0633: || useFirstUnread > messageTable
0634: .getRowCount()) {
0635: useFirstUnread = messageTable
0636: .getRowCount() - 1;
0637: } else {
0638: messageTable
0639: .setRowSelectionInterval(
0640: useFirstUnread,
0641: useFirstUnread);
0642: }
0643: makeSelectionVisible(useFirstUnread);
0644: }
0645: });
0646: }
0647: },
0648: new java.awt.event.ActionEvent(this , 0,
0649: "folder-select-first-unread"));
0650:
0651: }
0652:
0653: /**
0654: * This scrolls the given row number to visible.
0655: */
0656: public void makeSelectionVisible(int rowNumber) {
0657: messageTable.scrollRectToVisible(messageTable.getCellRect(
0658: rowNumber, 1, true));
0659:
0660: }
0661:
0662: /**
0663: * This selects the next message. If no message is selected, then
0664: * the first message is selected.
0665: */
0666: public int selectNextMessage() {
0667: int selectedRow = messageTable.getSelectedRow();
0668: int nextSelectable = getNextSelectableMessage(selectedRow, null);
0669: return selectMessage(nextSelectable);
0670: }
0671:
0672: /**
0673: * This selects the next unread message. If no message is selected, then
0674: * the first unread message is selected. If no unread messages exist, this
0675: * does nothing.
0676: */
0677: public int selectNextUnreadMessage() {
0678: int selectedRow = messageTable.getSelectedRow();
0679: int nextSelectable = getNextSelectableMessage(selectedRow,
0680: null, true);
0681: return selectMessage(nextSelectable);
0682: }
0683:
0684: /**
0685: * Determines which message is the next selectable message. If no
0686: * messages past this one are selectable (i.e. not deleted or about to
0687: * be deleted), returns messageTable.getRowCount() (i.e. an unused
0688: * row.
0689: *
0690: * Since we're polling flags on the Messages, this probably should be
0691: * called on the FolderThread. The change of selection itself, of course,
0692: * should be done on the AWTEventThread.
0693: */
0694: public int getNextSelectableMessage(int selectedRow,
0695: Vector removedProxies) {
0696: return getNextSelectableMessage(selectedRow, removedProxies,
0697: false);
0698: }
0699:
0700: /**
0701: * Determines which message is the next selectable message. If no
0702: * messages past this one are selectable (i.e. not deleted or about to
0703: * be deleted), returns messageTable.getRowCount() (i.e. an unused
0704: * row.
0705: *
0706: * Since we're polling flags on the Messages, this probably should be
0707: * called on the FolderThread. The change of selection itself, of course,
0708: * should be done on the AWTEventThread.
0709: */
0710: public int getNextSelectableMessage(int selectedRow,
0711: Vector removedProxies, boolean unread) {
0712: int newRow = selectedRow + 1;
0713: boolean done = false;
0714: while (!done && newRow < messageTable.getRowCount()) {
0715: MessageProxy mp = getFolderInfo().getMessageProxy(newRow);
0716: try {
0717: //if ((removedProxies != null && removedProxies.contains(mp)) || mp.getMessageInfo().getFlags().contains(Flags.Flag.DELETED) || (unread && mp.getMessageInfo().getFlags().contains(Flags.Flag.SEEN))) {
0718: if ((removedProxies != null && removedProxies
0719: .contains(mp))
0720: || mp.isDeleted() || (unread && mp.isSeen())) {
0721: newRow++;
0722: } else {
0723: done = true;
0724: }
0725: } catch (MessagingException me) {
0726: newRow++;
0727: }
0728: }
0729:
0730: return newRow;
0731: }
0732:
0733: /**
0734: * This selects the previous message. If no message is selected, then
0735: * the last message is selected.
0736: */
0737: public int selectPreviousMessage() {
0738: int[] rowsSelected = messageTable.getSelectedRows();
0739: int selectedRow = 0;
0740: if (rowsSelected.length > 0)
0741: selectedRow = rowsSelected[0];
0742: else
0743: selectedRow = messageTable.getRowCount();
0744:
0745: int previousSelectable = getPreviousSelectableMessage(
0746: selectedRow, null, false);
0747: return selectMessage(previousSelectable);
0748: }
0749:
0750: /**
0751: * This selects the previous unread message. If no message is selected, then
0752: * the first message is selected.
0753: */
0754: public int selectPreviousUnreadMessage() {
0755: int[] rowsSelected = messageTable.getSelectedRows();
0756: int selectedRow = 0;
0757: if (rowsSelected.length > 0)
0758: selectedRow = rowsSelected[0];
0759: else
0760: selectedRow = messageTable.getRowCount();
0761:
0762: int previousSelectable = getPreviousSelectableMessage(
0763: selectedRow, null, true);
0764: return selectMessage(previousSelectable);
0765: }
0766:
0767: /**
0768: * Determines which message is the previous selectable message. If no
0769: * messages before this one are selectable (i.e. not deleted or about to
0770: * be deleted), returns -1.
0771: *
0772: * Since we're polling flags on the Messages, this probably should be
0773: * called on the FolderThread. The change of selection itself, of course,
0774: * should be done on the AWTEventThread.
0775: */
0776: public int getPreviousSelectableMessage(int selectedRow,
0777: Vector removedProxies) {
0778: return getPreviousSelectableMessage(selectedRow,
0779: removedProxies, false);
0780: }
0781:
0782: /**
0783: * Determines which message is the previous selectable message. If no
0784: * messages before this one are selectable (i.e. not deleted or about to
0785: * be deleted), returns -1.
0786: *
0787: * Since we're polling flags on the Messages, this probably should be
0788: * called on the FolderThread. The change of selection itself, of course,
0789: * should be done on the AWTEventThread.
0790: */
0791: public int getPreviousSelectableMessage(int selectedRow,
0792: Vector removedProxies, boolean unread) {
0793: int newRow = selectedRow - 1;
0794: boolean done = false;
0795: while (!done && newRow >= 0) {
0796: MessageProxy mp = getFolderInfo().getMessageProxy(newRow);
0797: try {
0798: //if ((removedProxies != null && removedProxies.contains(mp)) || mp.getMessageInfo().getFlags().contains(Flags.Flag.DELETED) || (unread && mp.getMessageInfo().getFlags().contains(Flags.Flag.SEEN))) {
0799: if ((removedProxies != null && removedProxies
0800: .contains(mp))
0801: || mp.isDeleted() || (unread && mp.isSeen())) {
0802: newRow--;
0803: } else {
0804: done = true;
0805: }
0806: } catch (MessagingException me) {
0807: newRow--;
0808: }
0809: }
0810:
0811: return newRow;
0812:
0813: }
0814:
0815: /**
0816: * Selects all of the messages in the FolderTable.
0817: */
0818: public void selectAll() {
0819: messageTable.selectAll();
0820: }
0821:
0822: /**
0823: * This selects the message at the given row, and also scrolls the
0824: * MessageTable to make the given row visible.
0825: *
0826: * If the number entered is below the range of available messages, then
0827: * the first message is selected. If the number entered is above that
0828: * range, then the last message is selected. If the MessageTable
0829: * contains no messages, nothing happens.
0830: *
0831: * @return the index of the newly selected row.
0832: */
0833: public int selectMessage(int messageNumber) {
0834: int rowCount = messageTable.getRowCount();
0835:
0836: if (rowCount > 0) {
0837: int numberToSet = messageNumber;
0838:
0839: if (messageNumber < 0) {
0840: numberToSet = 0;
0841: } else if (messageNumber >= rowCount) {
0842: numberToSet = rowCount - 1;
0843: }
0844: messageTable.setRowSelectionInterval(numberToSet,
0845: numberToSet);
0846: makeSelectionVisible(numberToSet);
0847: return numberToSet;
0848: } else {
0849: return -1;
0850: }
0851: }
0852:
0853: /**
0854: * This method takes the currently selected row(s) and returns the
0855: * appropriate MessageProxy object.
0856: *
0857: * If no rows are selected, null is returned.
0858: */
0859: public MessageProxy getSelectedMessage() {
0860: if (messageTable != null) {
0861: int rowsSelected = messageTable.getSelectedRowCount();
0862:
0863: if (rowsSelected == 1)
0864: return getFolderInfo().getMessageProxy(
0865: messageTable.getSelectedRow());
0866: else if (rowsSelected < 1)
0867: return null;
0868: else {
0869: int[] selectedRows = messageTable.getSelectedRows();
0870: MessageProxy[] msgSelected = new MessageProxy[selectedRows.length];
0871: for (int i = 0; i < selectedRows.length; i++)
0872: msgSelected[i] = getFolderInfo().getMessageProxy(
0873: selectedRows[i]);
0874: return new MultiMessageProxy(selectedRows, msgSelected,
0875: this .getFolderInfo());
0876: }
0877: } else {
0878: return null;
0879: }
0880: }
0881:
0882: /**
0883: * This updates the entry for the given message, if that message is
0884: * visible.
0885: */
0886: public void repaintMessage(MessageProxy mp) {
0887: int row = getFolderTableModel().getRowForMessage(mp);
0888: if (row >= 0) {
0889: getFolderTableModel().fireTableRowsUpdated(row, row);
0890: }
0891: }
0892:
0893: /**
0894: * This resets the size to that of the parent component.
0895: */
0896: public void resize() {
0897: this .setSize(getParent().getSize());
0898: }
0899:
0900: // Accessor methods.
0901:
0902: public JTable getMessageTable() {
0903: return messageTable;
0904: }
0905:
0906: /**
0907: * This sets the FolderInfo.
0908: */
0909: public void setFolderInfo(FolderInfo newValue) {
0910: folderInfo = newValue;
0911: }
0912:
0913: public FolderInfo getFolderInfo() {
0914: return folderInfo;
0915: }
0916:
0917: /**
0918: * Returns the FolderTableModel for this FolderDisplayPanel.
0919: */
0920: public FolderTableModel getFolderTableModel() {
0921: if (getFolderInfo() != null)
0922: return getFolderInfo().getFolderTableModel();
0923: else
0924: return null;
0925: }
0926:
0927: /**
0928: * gets the actions handled both by the FolderDisplayPanel and the
0929: * selected Message(s).
0930: */
0931:
0932: public class SelectionListener implements
0933: javax.swing.event.ListSelectionListener {
0934: SelectionListener() {
0935: }
0936:
0937: public void valueChanged(javax.swing.event.ListSelectionEvent e) {
0938: Pooka.getMainPanel().refreshActiveMenus();
0939: getFolderInfo().setNewMessages(false);
0940: FolderNode fn = getFolderInfo().getFolderNode();
0941: if (fn != null)
0942: fn.getParentContainer().repaint();
0943: }
0944: }
0945:
0946: /**
0947: * This registers the Keyboard action not only for the FolderDisplayPanel
0948: * itself, but also for pretty much all of its children, also. This
0949: * is to work around something which I think is a bug in jdk 1.2.
0950: * (this is not really necessary in jdk 1.3.)
0951: *
0952: * Overrides JComponent.registerKeyboardAction(ActionListener anAction,
0953: * String aCommand, KeyStroke aKeyStroke, int aCondition)
0954: */
0955:
0956: public void registerKeyboardAction(ActionListener anAction,
0957: String aCommand, KeyStroke aKeyStroke, int aCondition) {
0958: super .registerKeyboardAction(anAction, aCommand, aKeyStroke,
0959: aCondition);
0960:
0961: if (messageTable != null)
0962: messageTable.registerKeyboardAction(anAction, aCommand,
0963: aKeyStroke, aCondition);
0964: }
0965:
0966: /**
0967: * This unregisters the Keyboard action not only for the FolderDisplayPanel
0968: * itself, but also for pretty much all of its children, also. This
0969: * is to work around something which I think is a bug in jdk 1.2.
0970: * (this is not really necessary in jdk 1.3.)
0971: *
0972: * Overrides JComponent.unregisterKeyboardAction(KeyStroke aKeyStroke)
0973: */
0974:
0975: public void unregisterKeyboardAction(KeyStroke aKeyStroke) {
0976: super .unregisterKeyboardAction(aKeyStroke);
0977:
0978: messageTable.unregisterKeyboardAction(aKeyStroke);
0979: }
0980:
0981: /**
0982: * Returns whether or not this window is enabled. This should be true
0983: * just about all of the time. The only time it won't be true is if
0984: * the Folder is closed or disconnected, and the mail store isn't set
0985: * up to work in disconnected mode.
0986: */
0987: public boolean isEnabled() {
0988: return enabled;
0989: }
0990:
0991: /**
0992: * This sets whether or not the window is enabled. This should only
0993: * be set to false when the Folder is no longer available.
0994: */
0995: public void setEnabled(boolean newValue) {
0996: enabled = newValue;
0997: }
0998:
0999: public Action[] getActions() {
1000: if (isEnabled()) {
1001: Action[] returnValue = null;
1002: MessageProxy m = getSelectedMessage();
1003:
1004: if (m != null)
1005: returnValue = m.getActions();
1006:
1007: if (folderInfo.getActions() != null) {
1008: if (returnValue != null) {
1009: returnValue = TextAction.augmentList(folderInfo
1010: .getActions(), returnValue);
1011: } else {
1012: returnValue = folderInfo.getActions();
1013: }
1014: }
1015:
1016: if (messageTable != null) {
1017: Action[] defaultActions = new Action[] {
1018: FolderTransferHandler
1019: .getCutAction(messageTable),
1020: FolderTransferHandler
1021: .getCopyAction(messageTable),
1022: FolderTransferHandler
1023: .getPasteAction(messageTable) };
1024: if (returnValue != null) {
1025: returnValue = TextAction.augmentList(
1026: defaultActions, returnValue);
1027: } else {
1028: returnValue = defaultActions;
1029: }
1030: }
1031:
1032: return returnValue;
1033: } else {
1034: return null;
1035: }
1036: }
1037: }
|