001 package edu.harvard.deas.hyperenc.gui; 002 003 import java.util.ArrayList; 004 import java.util.Date; 005 import java.util.List; 006 007 import javax.swing.table.AbstractTableModel; 008 009 import edu.harvard.deas.hyperenc.Contact; 010 import edu.harvard.deas.hyperenc.HyperMessageType; 011 012 013 /** 014 * A TableModel backed by a MessageStorage object. Displays a list of messages 015 * stored in a MessageStorage, one message per row, with selected fields of the 016 * message (for example: subject, sender, date) displayed in each of several 017 * columns. 018 * <p> 019 * TODO document how to use this -- when are update events fired, etc. 020 * TODO store all messages and use a filter instead of the ridiculous acrobatics we do now 021 * TODO this whole class sucks -- replace with a simpler version (use RowFilter) 022 */ 023 public class MessageTableModel extends AbstractTableModel { 024 private static final long serialVersionUID = 1L; 025 026 private MessageStorage msgStorage; 027 private Contact selectedSender; 028 private List<GuiMessage> senderMessages = new ArrayList<GuiMessage>(); // XXX not backed by the MessageStorage... 029 030 private List<String> columnNames; 031 032 /** 033 * TODO document constructor 034 * @param msgStorage 035 */ 036 public MessageTableModel(MessageStorage msgStorage) { 037 columnNames = new ArrayList<String>(); 038 // XXX hard-coded columns... 039 columnNames.add("Subject"); 040 columnNames.add("Date"); 041 columnNames.add("Decrypted?"); 042 columnNames.add("Unread?"); 043 044 this.msgStorage = msgStorage; 045 } 046 047 @Override 048 public String getColumnName(int column) { 049 return columnNames.get(column); 050 } 051 052 // XXX hard-coded, this sucks 053 @Override 054 public Class<?> getColumnClass(int column) { 055 switch (column) { 056 case 0: 057 return String.class; 058 case 1: 059 return Date.class; 060 case 2: 061 return Boolean.class; 062 case 3: 063 return Boolean.class; 064 default: 065 throw new IllegalArgumentException("Illegal column number!"); 066 } 067 } 068 069 public int getColumnCount() { 070 return columnNames.size(); 071 } 072 073 public int getRowCount() { 074 if (senderMessages == null) 075 return 0; 076 return senderMessages.size(); 077 } 078 079 public Object getValueAt(int rowIndex, int columnIndex) { 080 GuiMessage gm; 081 try { 082 gm = senderMessages.get(rowIndex); 083 } catch (IndexOutOfBoundsException e) { 084 throw new IllegalArgumentException("illegal row index"); 085 } 086 087 switch (columnIndex) { 088 case 0: 089 return gm.getSubject(); 090 case 1: 091 return gm.getDate(); 092 case 2: 093 return gm.getHyperMessage().getType() == HyperMessageType.UNENCRYPTED; 094 case 3: 095 return gm.isRead() == ReadStatus.UNREAD; 096 default: 097 throw new IllegalArgumentException("illegal column index"); 098 } 099 } 100 101 /** 102 * Always returns <code>false</code>; no cells are editable. 103 * @return <code>false</code> 104 */ 105 @Override 106 public boolean isCellEditable(int row, int col) { 107 return false; 108 } 109 110 /** 111 * Sets this model to show only those messages in the HyperStorage from the 112 * given sender. Fires an event to indicate that the table data has changed. 113 * 114 * @param contact 115 * e-mail contact of the desired sender 116 */ 117 public void selectSender(Contact contact) { 118 senderMessages = new ArrayList<GuiMessage>(msgStorage.getMessagesBySender(contact)); 119 120 fireTableDataChanged(); 121 } 122 123 /** 124 * Returns the sender for which this model is set to show messages. 125 * @see #selectSender(Contact) 126 */ 127 public Contact getSelectedSender() { 128 return selectedSender; 129 } 130 131 /** 132 * Adds a new GuiMessage to the underlying MessageStorage. Fires an event to 133 * indicate that the table has been updated. 134 * 135 * @param gm 136 * the message to add 137 */ 138 // TODO use a row-inserted event instead 139 public void addMessage(GuiMessage gm) { 140 msgStorage.addMessage(gm); 141 senderMessages = new ArrayList<GuiMessage>(msgStorage.getMessagesBySender(selectedSender)); 142 fireTableDataChanged(); 143 } 144 145 /** 146 * Returns the GuiMessage represented by the specified row. 147 * @param rowNum a row number 148 * @return the GuiMessage in the given row 149 */ 150 public GuiMessage getMessageAtRow(int rowNum) { 151 return senderMessages.get(rowNum); 152 } 153 154 /** 155 * Replaces the GuiMessage in row <code>rowNum</code> with <code>gm</code>. 156 * 157 * @param rowNum 158 * the row to place the message in 159 * @param gm 160 * the new message to insert 161 * @return the message that was replaced (the message that was originally in 162 * the specified row) 163 */ 164 public GuiMessage replaceMessageAtRow(int rowNum, GuiMessage gm) { 165 GuiMessage oldgm = senderMessages.set(rowNum, gm); 166 msgStorage.removeMessage(oldgm.getID()); 167 msgStorage.addMessage(gm); 168 fireTableRowsUpdated(rowNum, rowNum); 169 return oldgm; 170 } 171 172 /** 173 * Marks the GuiMessage in the specified row as read. 174 * @param rowNum the row to mark read 175 */ 176 public void markRead(int rowNum) { 177 GuiMessage gm = senderMessages.get(rowNum); 178 GuiMessage newgm = GuiMessage.markRead(gm); 179 replaceMessageAtRow(rowNum, newgm); 180 } 181 182 /** 183 * Marks the GuiMessage in the specified row as unread. 184 * @param rowNum the row to mark unread 185 */ 186 public void markUnread(int rowNum) { 187 GuiMessage gm = senderMessages.get(rowNum); 188 GuiMessage newgm = GuiMessage.markUnread(gm); 189 replaceMessageAtRow(rowNum, newgm); 190 } 191 192 /** 193 * Removes the GuiMessage with the given ID from the underlying 194 * MessageStorage. Fires an event to indicate that the table has been updated. 195 * 196 * @param id 197 * the message ID to remove 198 */ 199 // TODO use a row-deleted event instead 200 public void removeMessage(int id) { 201 msgStorage.removeMessage(id); 202 senderMessages = new ArrayList<GuiMessage>(msgStorage.getMessagesBySender(selectedSender)); 203 fireTableDataChanged(); 204 } 205 }