package thaw.plugins.miniFrost; import java.awt.BorderLayout; import java.awt.GridLayout; import java.util.Observer; import java.util.Observable; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.JScrollPane; import javax.swing.JButton; import java.awt.Color; import javax.swing.event.TableModelEvent; import java.util.Vector; import java.util.Iterator; import java.util.Enumeration; import java.awt.Component; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JComboBox; import javax.swing.JTextField; import java.awt.event.MouseListener; import java.awt.event.MouseEvent; import javax.swing.JCheckBox; import java.awt.Font; import java.awt.event.KeyEvent; import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeNode; import java.util.Hashtable; import thaw.gui.Table; import thaw.gui.CheckBox; import thaw.gui.IconBox; import thaw.core.I18n; import thaw.core.Logger; import thaw.plugins.MiniFrost; import thaw.plugins.signatures.Identity; import thaw.plugins.miniFrost.interfaces.Author; import thaw.plugins.miniFrost.interfaces.Board; import thaw.plugins.miniFrost.interfaces.BoardFactory; import thaw.plugins.miniFrost.interfaces.Message; import thaw.plugins.miniFrost.interfaces.Draft; public class MessageTreeTable implements Observer, MouseListener, ActionListener { /** * Just here to avoid an infinite recursion */ public final static int MAX_DEPTH = 30; public final static String[] COLUMNS = { "", /* checkboxes */ I18n.getMessage("thaw.plugin.miniFrost.subject"), I18n.getMessage("thaw.plugin.miniFrost.author"), I18n.getMessage("thaw.plugin.miniFrost.status"), /* author status */ I18n.getMessage("thaw.plugin.miniFrost.date"), }; public final static String[] GMAIL_ACTIONS = new String[] { I18n.getMessage("thaw.plugin.miniFrost.actions"), I18n.getMessage("thaw.plugin.miniFrost.selectAll"), I18n.getMessage("thaw.plugin.miniFrost.selectNone"), I18n.getMessage("thaw.plugin.miniFrost.markAsRead"), I18n.getMessage("thaw.plugin.miniFrost.markAsNonRead"), I18n.getMessage("thaw.plugin.miniFrost.newMessage"), I18n.getMessage("thaw.plugin.miniFrost.archivate"), I18n.getMessage("thaw.plugin.miniFrost.unarchivate") }; public final static String[] OUTLOOK_ACTIONS = new String[] { I18n.getMessage("thaw.plugin.miniFrost.actions"), I18n.getMessage("thaw.plugin.miniFrost.reply"), I18n.getMessage("thaw.plugin.miniFrost.selectAll"), I18n.getMessage("thaw.plugin.miniFrost.selectNone"), I18n.getMessage("thaw.plugin.miniFrost.markAsRead"), I18n.getMessage("thaw.plugin.miniFrost.markAsNonRead"), I18n.getMessage("thaw.plugin.miniFrost.newMessage"), I18n.getMessage("thaw.plugin.miniFrost.archivate"), I18n.getMessage("thaw.plugin.miniFrost.unarchivate") }; public final static int FIRST_COLUMN_SIZE = 25; public final static int DEFAULT_ROW_HEIGHT = 20; private MiniFrostPanel mainPanel; private JPanel panel; private Board targetBoard; private MessageTableModel model; private Table table; private JTextField searchField; private JCheckBox everywhereBox; private JButton searchButton; private JButton nextUnread; private CheckBox seeRead; private CheckBox seeArchived; private JComboBox actions; private JScrollPane tableScrollPane; private String[] keywords; private int orderBy; private boolean desc; private boolean tree; private CheckBox seeUnsigned; private JComboBox minTrustLevel; private int minTrustLevelInt; private boolean advancedMode; private boolean gmailView; /** for the thread tree **/ private MessageNodeTree messageNodeTree; private final static String trustLevelNoneStr = I18n.getMessage("thaw.plugin.signature.trustLevel.none"); public MessageTreeTable(MiniFrostPanel mainPanel) { this.mainPanel = mainPanel; this.gmailView = mainPanel.isInGmailView(); orderBy = Board.ORDER_DATE; desc = true; panel = new JPanel(new BorderLayout(5, 5)); advancedMode = Boolean.valueOf(mainPanel.getConfig().getValue("advancedMode")).booleanValue(); /* Actions */ JPanel northPanel = new JPanel(new BorderLayout(20, 20)); searchField = new JTextField(""); everywhereBox = new JCheckBox(I18n.getMessage("thaw.plugin.miniFrost.onAllBoards")); searchButton = new JButton(IconBox.minSearch); searchButton.setToolTipText(I18n.getMessage("thaw.common.search")); searchButton.addActionListener(this); searchField.addActionListener(this); nextUnread = new JButton("", IconBox.minNextUnread); nextUnread.setToolTipText(I18n.getMessage("thaw.plugin.miniFrost.nextUnread")); nextUnread.addActionListener(this); JPanel searchPanel = new JPanel(new BorderLayout(5, 5)); searchPanel.add(searchField, BorderLayout.CENTER); JPanel boxAndButtonPanel = new JPanel(new BorderLayout(5, 5)); boxAndButtonPanel.add(everywhereBox, BorderLayout.CENTER); boxAndButtonPanel.add(searchButton, BorderLayout.EAST); searchPanel.add(nextUnread, BorderLayout.WEST); searchPanel.add(boxAndButtonPanel, BorderLayout.EAST); northPanel.add(searchPanel, BorderLayout.CENTER); if (gmailView) actions = new JComboBox(GMAIL_ACTIONS); else actions = new JComboBox(OUTLOOK_ACTIONS); actions.addActionListener(this); northPanel.add(actions, BorderLayout.EAST); /* Table */ model = new MessageTableModel(); table = new Table(mainPanel.getConfig(), "table_minifrost_message_table", model); table.setDefaultRenderer(table.getColumnClass(0), new MessageTableRenderer()); //table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); table.getColumnModel().getColumn(0).setPreferredWidth(FIRST_COLUMN_SIZE); table.getColumnModel().getColumn(0).setResizable(false); table.getColumnModel().getColumn(0).setPreferredWidth(FIRST_COLUMN_SIZE); table.getColumnModel().getColumn(0).setMinWidth(FIRST_COLUMN_SIZE); table.getColumnModel().getColumn(0).setMaxWidth(FIRST_COLUMN_SIZE); table.setRowHeight(DEFAULT_ROW_HEIGHT); table.addMouseListener(this); setBoard(null); tableScrollPane = new JScrollPane(table); panel.add(northPanel, BorderLayout.NORTH); panel.add(tableScrollPane, BorderLayout.CENTER); /** some filters **/ /* read */ seeRead = new CheckBox(mainPanel.getConfig(), "miniFrost_seeRead", I18n.getMessage("thaw.plugin.miniFrost.seeRead"), true); seeRead.addActionListener(this); /* archived */ seeArchived = new CheckBox(mainPanel.getConfig(), "miniFrost_seeArchived", I18n.getMessage("thaw.plugin.miniFrost.seeArchived"), false); seeArchived.addActionListener(this); /* trust level */ String minTrustLvlStr = mainPanel.getConfig().getValue("minTrustLevel"); minTrustLevelInt = thaw.plugins.Signatures.DEFAULT_MIN_TRUST_LEVEL; if (minTrustLvlStr != null) minTrustLevelInt = Integer.parseInt(minTrustLvlStr); JPanel minTrustLevelPanel = new JPanel(new BorderLayout(5, 5)); minTrustLevelPanel.add(new JLabel(I18n.getMessage("thaw.plugin.miniFrost.hideStatusBelow")), BorderLayout.WEST); minTrustLevel = new JComboBox(Identity.trustLevelUserStr); minTrustLevel.setSelectedItem(Identity.getTrustLevelStr(minTrustLevelInt)); minTrustLevel.addActionListener(this); minTrustLevelPanel.add(minTrustLevel, BorderLayout.CENTER); seeUnsigned = new CheckBox(mainPanel.getConfig(), "miniFrost_seeUnsigned", I18n.getMessage("thaw.plugin.miniFrost.seeUnsigned"), true); seeUnsigned.addActionListener(this); tree = MiniFrost.DISPLAY_AS_TREE; if (mainPanel.getConfig().getValue("checkbox_miniFrost_seeTree") != null) { tree = Boolean.valueOf(mainPanel.getConfig().getValue("checkbox_miniFrost_seeTree")).booleanValue(); } JPanel southWestPanel = new JPanel(new GridLayout(1, 3, 5, 5)); //southEastPanelTop.add(new JLabel(I18n.getMessage("thaw.plugin.miniFrost.seeMessages"))); southWestPanel.add(seeUnsigned); southWestPanel.add(seeArchived); southWestPanel.add(seeRead); JPanel southPanel = new JPanel(new BorderLayout(5, 5)); southPanel.add(southWestPanel, BorderLayout.WEST); southPanel.add(new JLabel(""), BorderLayout.CENTER); southPanel.add(minTrustLevelPanel, BorderLayout.EAST); panel.add(southPanel, BorderLayout.SOUTH); refresh(); } public void hided() { if (gmailView) nextUnread.setMnemonic(KeyEvent.VK_Z); } /** * will revalidate */ public void redisplayed() { /** * due to a swing bug ? */ table.getColumnModel().getColumn(0).setPreferredWidth(FIRST_COLUMN_SIZE); table.getColumnModel().getColumn(0).setResizable(false); table.getColumnModel().getColumn(0).setPreferredWidth(FIRST_COLUMN_SIZE); table.getColumnModel().getColumn(0).setMinWidth(FIRST_COLUMN_SIZE); table.getColumnModel().getColumn(0).setMaxWidth(FIRST_COLUMN_SIZE); nextUnread.setMnemonic(KeyEvent.VK_N); nextUnread.requestFocus(); } public JPanel getPanel() { return panel; } protected class MessageNodeTree extends JTree { /** * */ private static final long serialVersionUID = 6110558336513763649L; private DefaultTreeCellRenderer cellRenderer; public MessageNodeTree(TreeNode root) { super(root); cellRenderer = (DefaultTreeCellRenderer)getCellRenderer(); cellRenderer.setOpenIcon(cellRenderer.getDefaultLeafIcon()); cellRenderer.setClosedIcon(cellRenderer.getDefaultLeafIcon()); } protected int visibleRow; protected int rowHeight; public void setBounds(int x, int y, int w, int h) { super.setBounds(x, 0, w, table.getHeight()); } public void paint(java.awt.Graphics g) { g.translate(0, (-(visibleRow+1) * rowHeight)); super.paint(g); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) setSelectionRow(row+1); /* don't forget the root :) */ Color background = thaw.gui.Table.DefaultRenderer.setBackground(this, row, isSelected); setRowHeight(table.getRowHeight()); rowHeight = table.getRowHeight(); Message msg = ((MessageNode)value).getMessage(); int mod = Font.PLAIN; if (msg != null) { if (!msg.isRead()) { mod = Font.BOLD; } if (msg.isArchived()) { if (mod == Font.BOLD) mod = Font.ITALIC | Font.BOLD; else mod = Font.ITALIC; } } else mod = Font.ITALIC; setFont(getFont().deriveFont(mod)); if (msg != null && msg.getSender().getIdentity() != null) { Color foreground = msg.getSender().getIdentity().getTrustLevelColor(); cellRenderer.setTextNonSelectionColor(foreground); cellRenderer.setTextSelectionColor(foreground); } else { cellRenderer.setTextNonSelectionColor(Color.BLACK); cellRenderer.setTextSelectionColor(Color.BLACK); } if (background != null) { cellRenderer.setBackground(background); cellRenderer.setBackgroundNonSelectionColor(background); } if (isSelected) this.setBackground(cellRenderer.getBackgroundSelectionColor()); else this.setBackground(cellRenderer.getBackgroundNonSelectionColor()); visibleRow = row; return this; } } protected static class RootMessageNode implements TreeNode { private Vector children; public RootMessageNode(Vector nodes) { this.children = nodes; } public Enumeration children() { synchronized(children) { /* yep, quite useless */ return children.elements(); } } public boolean getAllowsChildren() { return true; } public TreeNode getChildAt(int childIndex) { synchronized(children) { return (MessageNode)children.get(childIndex); } } public int getChildCount() { synchronized(children) { return children.size(); } } public int getIndex(TreeNode node) { synchronized(children) { return children.indexOf(node); } } public TreeNode getParent() { return null; } public boolean isLeaf() { synchronized(children) { return (children.size() == 0); } } } protected static class MessageNode implements TreeNode { private Vector children; private boolean hasParent; private TreeNode parent; private Message msg; public MessageNode(Message msg) { this.parent = null; this.msg = msg; children = new Vector(0); } public Message getMessage() { return msg; } public void setParent(TreeNode node) { parent = node; } /** * will register */ public void setParent(Hashtable messageNodes) { String inReplyTo; if (msg != null && (inReplyTo = msg.getInReplyToId()) != null) { hasParent = true; MessageNode node = (MessageNode)messageNodes.get(inReplyTo); if (node != null) { setParent(node); node.registerChild(this); } } } public boolean hasParent() { return hasParent; } public void registerChild(MessageNode node) { synchronized(children) { children.insertElementAt(node, 0); } } public Enumeration children() { synchronized(children) { return children.elements(); } } public boolean getAllowsChildren() { return true; } public TreeNode getChildAt(int childIndex) { synchronized(children) { return (MessageNode)children.get(childIndex); } } public int getChildCount() { synchronized(children) { return children.size(); } } public int getIndex(TreeNode node) { synchronized(children) { return children.indexOf(node); } } public TreeNode getParent() { return parent; } public boolean isLeaf() { synchronized(children) { return (children.size() == 0); } } public String toString() { if (msg != null) return msg.getSubject(); else return "(?)"; } } protected class MessageTableRenderer extends Table.DefaultRenderer { /** * */ private static final long serialVersionUID = -2689945423474352988L; private JCheckBox checkBoxRenderer; public MessageTableRenderer() { super(); this.checkBoxRenderer = new JCheckBox(); } public Component getTableCellRendererComponent(final JTable table, Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) { Component c; Message msg = model.getMsg(row); Author author = (msg != null ? msg.getSender() : null); if (value instanceof Boolean) { checkBoxRenderer.setEnabled(msg != null); if (msg != null) checkBoxRenderer.setSelected(((Boolean)value).booleanValue()); else checkBoxRenderer.setSelected(false); return checkBoxRenderer; } if (value instanceof MessageNode && tree) { return messageNodeTree.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } Color color = Color.BLACK; if (column == 2) { value = ((author != null) ? author.toString() : "(?)"); } else if (column == 3) { if (author != null && author.getIdentity() != null) value = author.getIdentity().getTrustLevelStr(); else value = trustLevelNoneStr; } if (value instanceof java.util.Date) { value = java.text.DateFormat.getDateTimeInstance().format((java.util.Date)value); if (advancedMode) { int rev = msg.getRev(); if (rev >= 0) { value = ("("+Integer.toString(rev)+") "+ ((String)value)); } } } if (author != null && author.getIdentity() != null) color = author.getIdentity().getTrustLevelColor(); c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); c.setForeground(color); int mod = Font.PLAIN; if (msg != null) { if (!msg.isRead()) { mod = Font.BOLD; } if (msg.isArchived()) { if (mod == Font.BOLD) mod = Font.ITALIC | Font.BOLD; else mod = Font.ITALIC; } } else mod = Font.ITALIC; c.setFont(c.getFont().deriveFont(mod)); return c; } } protected class MessageTableModel extends javax.swing.table.AbstractTableModel { /** * */ private static final long serialVersionUID = 6796753954357075844L; private Vector msgs; private boolean[] selection; public MessageTableModel() { super(); this.msgs = null; } public int getRowCount() { if (msgs == null) return 0; synchronized(msgs) { return msgs.size(); } } public int getColumnCount() { return COLUMNS.length; } public String getColumnName(int column) { return COLUMNS[column]; } public Message getMsg(int row) { synchronized(msgs) { return ((MessageNode)msgs.get(row)).getMessage(); } } public Object getValueAt(int row, int column) { if (column == 0) { return new Boolean(selection[row]); } if (column == 1) { synchronized(msgs) { return (MessageNode)msgs.get(row); } } Message msg; synchronized(msgs) { msg = ((MessageNode)msgs.get(row)).getMessage(); } if (msg == null) return "(?)"; if (column == 2) { return msg.getSender(); } if (column == 3) { return msg.getSender(); } if (column == 4) { return msg.getDate(); } if (column == 5) { return msg; //.getMessage(); } return null; } public void setMessages(Vector msgs) { this.msgs = msgs; int lng = 0; synchronized(msgs) { lng = msgs.size(); } selection = new boolean[lng]; for (int i = 0 ; i < lng ; i++) selection[i] = false; } public MessageNode getNode(Message msg) { int i; if ( (i = getRow(msg)) < 0 ) return null; return (MessageNode)msgs.get(i); } public int getRow(Message msg) { boolean found = false; int i = 0; synchronized(msgs) { for (Iterator it = msgs.iterator(); it.hasNext(); i++) { MessageNode sNode = (MessageNode)it.next(); if (sNode.getMessage() != null && sNode.getMessage().equals(msg)) { found = true; break; } } } if (!found) { Logger.notice(this, "Node not found"); return -1; } return i; } public void setSelectedAll(boolean s) { synchronized(msgs) { for (int i = 0 ; i < selection.length ; i++) { Message msg = ((MessageNode)msgs.get(i)).getMessage(); if (msg != null) selection[i] = s; } } } public boolean[] getSelection() { return selection; } public void switchSelection(int row) { Message msg = null; synchronized(msgs) { msg = ((MessageNode)msgs.get(row)).getMessage(); } if (msg != null) selection[row] = !selection[row]; else selection[row] = false; refresh(row); } public void switchSelection(int row, boolean val) { Message msg = null; synchronized(msgs) { msg = ((MessageNode)msgs.get(row)).getMessage(); } if (msg != null) selection[row] = val; else selection[row] = false; refresh(row); } public Vector getMessages(Vector msgs) { return msgs; } public void refresh(Message msg) { /* quick and dirty */ refresh(); } public void refresh(MessageNode msgNode) { //refresh(msgs.indexOf(msgNode)); refresh(); } public void refresh(int row) { if (row == -1) { Logger.error(this, "Message not found in the list ?!"); return; } fireTableChanged(new TableModelEvent(this, row)); } public void refresh() { fireTableChanged(new TableModelEvent(this)); } } public int getRow(Message msg) { if (msg == null) return -1; return model.getRow(msg); } public void setBoard(Board board) { this.targetBoard = board; searchField.setText(""); everywhereBox.setSelected(false); keywords = null; } public Board getBoard() { return targetBoard; } public void refresh() { refresh(keywords, orderBy, desc, everywhereBox.isSelected()); } private boolean rebuildMsgList(Vector msgs, TreeNode node, int depth) { if (node instanceof MessageNode) msgs.add(node); if (depth >= MAX_DEPTH) { Logger.notice(this, "Too much depths, sorry"); return false; } for(Enumeration e = node.children(); e.hasMoreElements();) { TreeNode sub = (TreeNode)e.nextElement(); if (!rebuildMsgList(msgs, sub, depth+1)) return false; } return true; } public void refresh(String[] keywords, int orderBy, boolean desc, boolean allBoards) { Vector msgs = null; if ((!allBoards) && targetBoard != null) { Vector rawMsgs = targetBoard.getMessages(keywords, orderBy, desc, seeArchived.isSelected(), seeRead.isSelected(), seeUnsigned.isSelected(), minTrustLevelInt); msgs = new Vector(rawMsgs.size()); for(Iterator it = rawMsgs.iterator(); it.hasNext();) { msgs.add(new MessageNode((Message)it.next())); } } if (allBoards) { msgs = new Vector(); BoardFactory[] factories = mainPanel.getPluginCore().getFactories(); for (int i = 0 ; i < factories.length ; i++) { Vector boardMsgs = factories[i].getAllMessages(keywords, orderBy, desc, seeArchived.isSelected(), seeRead.isSelected(), seeUnsigned.isSelected(), minTrustLevelInt); for (Iterator it = boardMsgs.iterator(); it.hasNext();) { msgs.add(new MessageNode((Message)it.next())); } } } if (msgs == null) { msgs = new Vector(); } Logger.info(this, "Nmb msgs in the tree (before) : "+Integer.toString(msgs.size())); Vector rootNodes; if (tree) { /** Filling in messageNodeHashtable **/ Hashtable messageNodeHashtable = new Hashtable(msgs.size()); synchronized(messageNodeHashtable) { for (Iterator it = msgs.iterator(); it.hasNext();) { MessageNode node = (MessageNode)it.next(); messageNodeHashtable.put(node.getMessage().getMsgId(), node); } /** Building the tree **/ for (Iterator it = msgs.iterator(); it.hasNext();) { ((MessageNode)it.next()).setParent(messageNodeHashtable); } } /** we search the nodes who should have a parent but haven't **/ /* we don't use an iterator to avoid the collisions */ for (int i = 0 ; i < msgs.size(); i++) { MessageNode node = (MessageNode)msgs.get(i); if (node.getParent() == null && node.hasParent()) { MessageNode newEmptyNode = new MessageNode(null); node.setParent(newEmptyNode); newEmptyNode.registerChild(node); /* we replace */ msgs.set(i, newEmptyNode); /* and readd the other node at the end of the vector */ msgs.add(node); } } rootNodes = new Vector(); /* Building the root tree */ for (Iterator it = msgs.iterator(); it.hasNext();) { MessageNode node = (MessageNode)it.next(); if (node.getParent() == null) rootNodes.add(node); } } else rootNodes = msgs; /* we add all the message without any parent to the root */ RootMessageNode rootNode = new RootMessageNode(rootNodes); for (Iterator it = rootNodes.iterator(); it.hasNext();) { ((MessageNode)it.next()).setParent(rootNode); } /** and to finish, the tree itself **/ messageNodeTree = new MessageNodeTree(rootNode); for (int i = 0 ; i < messageNodeTree.getRowCount() ; i++) { messageNodeTree.expandRow(i); } /** next we check we have the same order in the tree and in the table **/ msgs = new Vector(); rebuildMsgList(msgs, rootNode, 0); Logger.info(this, "Nmb msgs in the tree (after) : "+Integer.toString(msgs.size())); model.setMessages(msgs); model.refresh(); } public void refresh(Message msg) { model.refresh(msg); } public void refresh(int row) { model.refresh(row); } public void update(Observable o, Object param) { if (o == mainPanel.getBoardTree()) { setBoard((Board)param); refresh(); } } public class LineSelecter implements Runnable { private int line; public LineSelecter(int line) { this.line = line; } public void run() { table.setRowSelectionInterval(line, line); table.setColumnSelectionInterval(0, COLUMNS.length-1); model.refresh(line); int max = tableScrollPane.getVerticalScrollBar().getMaximum(); int min = tableScrollPane.getVerticalScrollBar().getMinimum(); int value = (((max-min) * (line-5)) / model.getRowCount()) + min; if (value < min) value = min; if (value > max) value = max; tableScrollPane.getVerticalScrollBar().setValue(value); model.setSelectedAll(false); model.switchSelection(line, true); } } public Message getNextMessageInThread(Message currentMsg) { MessageNode node = model.getNode(currentMsg); if (node == null) { Logger.notice(this, "Can't find back the message in the tree ?!"); return null; } /* we first check if it has an unread child */ for (Enumeration e = node.children(); e.hasMoreElements();) { MessageNode child = (MessageNode)e.nextElement(); if (!child.getMessage().isRead()) return child.getMessage(); } /* if it has no child, we check if it has a brother (or if its parents * have brothers, etc) */ for (; node != null; node = (MessageNode)node.getParent()) { TreeNode treeParent = (TreeNode)node.getParent(); /* if it has no parent, we return null to let the calling function * search a new thread in the database (ORDER BY date, etc) */ if (treeParent == null || treeParent instanceof RootMessageNode) return null; MessageNode parent = (MessageNode)treeParent; int i = parent.getIndex(node); for (i++ ; i < parent.getChildCount() ; i++) { MessageNode child = (MessageNode)parent.getChildAt(i); if (!child.getMessage().isRead()) return child.getMessage(); } } return null; } public boolean nextUnread() { if (targetBoard == null) { Logger.warning(this, "No board selected atm ; can't get the next unread message"); return false; } Message newMsg = null; if (mainPanel.getMessagePanel().getMessage() != null) newMsg = getNextMessageInThread(mainPanel.getMessagePanel().getMessage()); if (newMsg == null) newMsg = targetBoard.getNextUnreadMessage(seeUnsigned.isSelected(), seeArchived.isSelected(), minTrustLevelInt); if (newMsg != null) { int line = getRow(newMsg); if (line >= 0) { Logger.info(this, "Line: "+Integer.toString(line)); model.setSelectedAll(false); model.switchSelection(line, true); javax.swing.SwingUtilities.invokeLater(new LineSelecter(line)); } if (line >= 0) { model.getMsg(line).setRead(true); model.refresh(line); } else { newMsg.setRead(true); refresh(); } mainPanel.getMessagePanel().setMessage(newMsg); /* will do all the refresh display required */ mainPanel.displayMessage(); return true; } return false; } public int getMinTrustLevel() { return minTrustLevelInt; } public boolean seeUnsigned() { return seeUnsigned.isSelected(); } public boolean seeArchived() { return seeArchived.isSelected(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == seeUnsigned || e.getSource() == minTrustLevel || e.getSource() == seeArchived || e.getSource() == seeRead) { minTrustLevelInt = Identity.getTrustLevel((String)(minTrustLevel.getSelectedItem())); refresh(); mainPanel.getBoardTree().refresh(); } else if (e.getSource() == searchButton || e.getSource() == searchField) { keywords = searchField.getText().split(" "); Logger.info(this, "Searching ..."); refresh(keywords, orderBy, desc, everywhereBox.isSelected()); } else if (e.getSource() == nextUnread) { nextUnread(); } else if (e.getSource() == actions) { int sel = actions.getSelectedIndex(); boolean[] selected = model.getSelection(); Logger.info(this, "Applying action : "+Integer.toString(sel)); if (sel <= 0) return; if (gmailView) sel += 1; if (sel == 1) { /* reply */ mainPanel.getMessagePanel().reply(); } else if (sel == 4 || sel == 5) { /* mark as (non-)read */ boolean markAsRead = (sel == 4); for (int i = 0 ; i < selected.length ; i++) { if (selected[i]) { model.getMsg(i).setRead(markAsRead); model.refresh(i); } } mainPanel.getBoardTree().refresh(targetBoard); } else if (sel == 7 || sel == 8) { /* (un)archive */ boolean archive = (sel == 7); for (int i = 0 ; i < selected.length ; i++) { if (selected[i]) model.getMsg(i).setArchived(archive); } refresh(); mainPanel.getBoardTree().refresh(targetBoard); } else if (sel == 2 || sel == 3) { /* (un)select all */ boolean select = (sel == 2); model.setSelectedAll(select); model.refresh(); } else if (sel == 6) { /* new message */ if (targetBoard != null) { Draft draft = targetBoard.getDraft(null); mainPanel.getDraftPanel().setDraft(draft); mainPanel.displayDraftPanel(); } } actions.setSelectedIndex(0); } } public int startRow = -1; public int endRow = -1; public void mouseClicked(MouseEvent e) { int row = table.rowAtPoint(e.getPoint()); int column = table.columnAtPoint(e.getPoint()); Logger.info(this, "Mouse clicked"); if (column == 0) { model.switchSelection(row); refresh(row); } else { if (endRow < 0) endRow = row; if ( (startRow < 0 || endRow >= 0 || startRow == endRow) ) { /* only one selection */ Message msg = model.getMsg(row); if (msg != null) { model.setSelectedAll(false); model.switchSelection(row, true); mainPanel.getMessagePanel().setMessage(msg); mainPanel.displayMessage(); } } } startRow = -1; endRow = -1; } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { int column = table.columnAtPoint(e.getPoint()); Logger.info(this, "mouse pressed"); startRow = -1; endRow = -1; if (column == 0) { return; } startRow = table.rowAtPoint(e.getPoint()); } public void mouseReleased(MouseEvent e) { Logger.info(this, "mouse released"); endRow = table.rowAtPoint(e.getPoint()); if (startRow >= 0 && endRow >= 0 && startRow != endRow) { /* many selections */ for (int i = startRow ; i <= endRow ; i++) { model.switchSelection(i); } startRow = -1; endRow = -1; } } }