| OLD | NEW |
| 1 package autotest.common.table; | 1 package autotest.common.table; |
| 2 | 2 |
| 3 import autotest.common.SimpleCallback; | 3 import autotest.common.SimpleCallback; |
| 4 import autotest.common.table.DataSource.DataCallback; | 4 import autotest.common.table.DataSource.DataCallback; |
| 5 import autotest.common.table.DataSource.Query; | 5 import autotest.common.table.DataSource.Query; |
| 6 import autotest.common.table.DataSource.SortDirection; | 6 import autotest.common.table.DataSource.SortDirection; |
| 7 import autotest.common.table.DataSource.SortSpec; | 7 import autotest.common.table.DataSource.SortSpec; |
| 8 import autotest.common.ui.Paginator; | 8 import autotest.common.ui.Paginator; |
| 9 | 9 |
| 10 import com.google.gwt.json.client.JSONObject; | 10 import com.google.gwt.json.client.JSONObject; |
| 11 import com.google.gwt.user.client.ui.Composite; | 11 import com.google.gwt.user.client.ui.Composite; |
| 12 import com.google.gwt.user.client.ui.HTMLPanel; | 12 import com.google.gwt.user.client.ui.HTMLPanel; |
| 13 import com.google.gwt.user.client.ui.Image; | 13 import com.google.gwt.user.client.ui.Image; |
| 14 | 14 |
| 15 import java.util.ArrayList; | 15 import java.util.ArrayList; |
| 16 import java.util.Collections; | 16 import java.util.Collections; |
| 17 import java.util.Iterator; | 17 import java.util.Iterator; |
| 18 import java.util.List; | 18 import java.util.List; |
| 19 | 19 |
| 20 /** | 20 /** |
| 21 * Extended DataTable supporting sorting, filtering and pagination. | 21 * Extended DataTable supporting sorting, filtering and pagination. |
| 22 */ | 22 */ |
| 23 public class DynamicTable extends DataTable implements DataCallback { | 23 public class DynamicTable extends DataTable implements DataCallback { |
| 24 public static final int NO_COLUMN = -1; | 24 public static final int NO_COLUMN = -1; |
| 25 public static final String SORT_UP_IMAGE = "arrow_up.png", | 25 public static final String SORT_UP_IMAGE = "arrow_up.png", |
| 26 SORT_DOWN_IMAGE = "arrow_down.png"; | 26 SORT_DOWN_IMAGE = "arrow_down.png"; |
| 27 | 27 |
| 28 public static interface DynamicTableListener extends DataTableListener { | 28 public static interface DynamicTableListener extends DataTableListener { |
| 29 public void onTableRefreshed(); | 29 public void onTableRefreshed(); |
| 30 } | 30 } |
| 31 | 31 |
| 32 static class SortIndicator extends Composite { | 32 static class SortIndicator extends Composite { |
| 33 public int column; | 33 public int column; |
| 34 private Image image = new Image(); | 34 private Image image = new Image(); |
| 35 | 35 |
| 36 public SortIndicator(int column) { | 36 public SortIndicator(int column) { |
| 37 this.column = column; | 37 this.column = column; |
| 38 initWidget(image); | 38 initWidget(image); |
| 39 setVisible(false); | 39 setVisible(false); |
| 40 } | 40 } |
| 41 | 41 |
| 42 public void sortOn(SortDirection direction) { | 42 public void sortOn(SortDirection direction) { |
| 43 image.setUrl(direction == SortDirection.ASCENDING ? SORT_UP_IMAGE :
SORT_DOWN_IMAGE); | 43 image.setUrl(direction == SortDirection.ASCENDING ? SORT_UP_IMAGE :
SORT_DOWN_IMAGE); |
| 44 setVisible(true); | 44 setVisible(true); |
| 45 } | 45 } |
| 46 | 46 |
| 47 public void sortOff() { | 47 public void sortOff() { |
| 48 setVisible(false); | 48 setVisible(false); |
| 49 } | 49 } |
| 50 } | 50 } |
| 51 | 51 |
| 52 protected DataSource dataSource; | 52 protected DataSource dataSource; |
| 53 private Query currentQuery; | 53 private Query currentQuery; |
| 54 | 54 |
| 55 private boolean clientSortable = false; | 55 private boolean clientSortable = false; |
| 56 private SortIndicator[] sortIndicators; | 56 private SortIndicator[] sortIndicators; |
| 57 private List<SortSpec> sortColumns = new ArrayList<SortSpec>(); | 57 private List<SortSpec> sortColumns = new ArrayList<SortSpec>(); |
| 58 | 58 |
| 59 protected List<Filter> filters = new ArrayList<Filter>(); | 59 protected List<Filter> filters = new ArrayList<Filter>(); |
| 60 protected List<Paginator> paginators = new ArrayList<Paginator>(); | 60 protected List<Paginator> paginators = new ArrayList<Paginator>(); |
| 61 protected Integer rowsPerPage; | 61 protected Integer rowsPerPage; |
| 62 | 62 |
| 63 protected List<DynamicTableListener> dynamicTableListeners = | 63 protected List<DynamicTableListener> dynamicTableListeners = |
| 64 new ArrayList<DynamicTableListener>(); | 64 new ArrayList<DynamicTableListener>(); |
| 65 | 65 |
| 66 public DynamicTable(String[][] columns, DataSource dataSource) { | 66 public DynamicTable(String[][] columns, DataSource dataSource) { |
| 67 super(columns); | 67 super(columns); |
| 68 setDataSource(dataSource); | 68 setDataSource(dataSource); |
| 69 } | 69 } |
| 70 | 70 |
| 71 // SORTING | 71 // SORTING |
| 72 | 72 |
| 73 /** | 73 /** |
| 74 * Makes the table client sortable, that is, sortable by the user by | 74 * Makes the table client sortable, that is, sortable by the user by |
| 75 * clicking on column headers. | 75 * clicking on column headers. |
| 76 */ | 76 */ |
| 77 public void makeClientSortable() { | 77 public void makeClientSortable() { |
| 78 this.clientSortable = true; | 78 this.clientSortable = true; |
| 79 table.getRowFormatter().addStyleName(0, | 79 table.getRowFormatter().addStyleName(0, |
| 80 DataTable.HEADER_STYLE + "-sortable"); | 80 DataTable.HEADER_STYLE + "-sortable"); |
| 81 | 81 |
| 82 sortIndicators = new SortIndicator[columns.length]; | 82 sortIndicators = new SortIndicator[columns.length]; |
| 83 for(int i = 0; i < columns.length; i++) { | 83 for(int i = 0; i < columns.length; i++) { |
| 84 sortIndicators[i] = new SortIndicator(i); | 84 sortIndicators[i] = new SortIndicator(i); |
| 85 | 85 |
| 86 // we have to use an HTMLPanel here to preserve styles correctly and | 86 // we have to use an HTMLPanel here to preserve styles correctly and |
| 87 // not break hover | 87 // not break hover |
| 88 // we add a <span> with a unique ID to hold the sort indicator | 88 // we add a <span> with a unique ID to hold the sort indicator |
| 89 String name = columns[i][COL_TITLE]; | 89 String name = columns[i][COL_TITLE]; |
| 90 String id = HTMLPanel.createUniqueId(); | 90 String id = HTMLPanel.createUniqueId(); |
| 91 HTMLPanel panel = new HTMLPanel(name + | 91 HTMLPanel panel = new HTMLPanel(name + |
| 92 " <span id=\"" + id + "\"></span>"); | 92 " <span id=\"" + id + "\"></span>"); |
| 93 panel.add(sortIndicators[i], id); | 93 panel.add(sortIndicators[i], id); |
| 94 table.setWidget(0, i, panel); | 94 table.setWidget(0, i, panel); |
| 95 } | 95 } |
| 96 } | 96 } |
| 97 | 97 |
| 98 private void updateSortIndicators() { | 98 private void updateSortIndicators() { |
| 99 if (!clientSortable) { | 99 if (!clientSortable) { |
| 100 return; | 100 return; |
| 101 } | 101 } |
| 102 | 102 |
| 103 SortSpec firstSpec = getFirstSortSpec(); | 103 SortSpec firstSpec = getFirstSortSpec(); |
| 104 for (SortIndicator indicator : sortIndicators) { | 104 for (SortIndicator indicator : sortIndicators) { |
| 105 if (columns[indicator.column][COL_NAME].equals(firstSpec.getField())
) { | 105 if (columns[indicator.column][COL_NAME].equals(firstSpec.getField())
) { |
| 106 indicator.sortOn(firstSpec.getDirection()); | 106 indicator.sortOn(firstSpec.getDirection()); |
| 107 } else { | 107 } else { |
| 108 indicator.sortOff(); | 108 indicator.sortOff(); |
| 109 } | 109 } |
| 110 } | 110 } |
| 111 } | 111 } |
| 112 | 112 |
| 113 private SortSpec getFirstSortSpec() { | 113 private SortSpec getFirstSortSpec() { |
| 114 if (sortColumns.isEmpty()) { | 114 if (sortColumns.isEmpty()) { |
| 115 return null; | 115 return null; |
| 116 } | 116 } |
| 117 return sortColumns.get(0); | 117 return sortColumns.get(0); |
| 118 } | 118 } |
| 119 | 119 |
| 120 /** | 120 /** |
| 121 * Set column on which data is sorted. You must call <code>refresh()</code> | 121 * Set column on which data is sorted. You must call <code>refresh()</code> |
| 122 * after this to display the results. | 122 * after this to display the results. |
| 123 * @param columnField field of the column to sort on | 123 * @param columnField field of the column to sort on |
| 124 * @param sortDirection DynamicTable.ASCENDING or DynamicTable.DESCENDING | 124 * @param sortDirection DynamicTable.ASCENDING or DynamicTable.DESCENDING |
| 125 */ | 125 */ |
| 126 public void sortOnColumn(String columnField, SortDirection sortDirection) { | 126 public void sortOnColumn(String columnField, SortDirection sortDirection) { |
| 127 // remove any existing sort on this column | 127 // remove any existing sort on this column |
| 128 for (Iterator<SortSpec> i = sortColumns.iterator(); i.hasNext(); ) { | 128 for (Iterator<SortSpec> i = sortColumns.iterator(); i.hasNext(); ) { |
| 129 if (i.next().getField().equals(columnField)) { | 129 if (i.next().getField().equals(columnField)) { |
| 130 i.remove(); | 130 i.remove(); |
| 131 break; | 131 break; |
| 132 } | 132 } |
| 133 } | 133 } |
| 134 | 134 |
| 135 sortColumns.add(0, new SortSpec(columnField, sortDirection)); | 135 sortColumns.add(0, new SortSpec(columnField, sortDirection)); |
| 136 updateSortIndicators(); | 136 updateSortIndicators(); |
| 137 } | 137 } |
| 138 | 138 |
| 139 /** | 139 /** |
| 140 * Defaults to ascending order. | 140 * Defaults to ascending order. |
| 141 */ | 141 */ |
| 142 public void sortOnColumn(String columnField) { | 142 public void sortOnColumn(String columnField) { |
| 143 sortOnColumn(columnField, SortDirection.ASCENDING); | 143 sortOnColumn(columnField, SortDirection.ASCENDING); |
| 144 } | 144 } |
| 145 | 145 |
| 146 public void clearSorts() { | 146 public void clearSorts() { |
| 147 sortColumns.clear(); | 147 sortColumns.clear(); |
| 148 updateSortIndicators(); | 148 updateSortIndicators(); |
| 149 } | 149 } |
| 150 | 150 |
| 151 // PAGINATION | 151 // PAGINATION |
| 152 | 152 |
| 153 /** | 153 /** |
| 154 * Attach a new paginator to this table. | 154 * Attach a new paginator to this table. |
| 155 */ | 155 */ |
| 156 public void attachPaginator(Paginator paginator) { | 156 public void attachPaginator(Paginator paginator) { |
| 157 assert rowsPerPage != null; | 157 assert rowsPerPage != null; |
| 158 paginators.add(paginator); | 158 paginators.add(paginator); |
| 159 paginator.addCallback(new SimpleCallback() { | 159 paginator.addCallback(new SimpleCallback() { |
| 160 public void doCallback(Object source) { | 160 public void doCallback(Object source) { |
| 161 setPaginatorStart(((Paginator) source).getStart()); | 161 setPaginatorStart(((Paginator) source).getStart()); |
| 162 fetchPage(); | 162 fetchPage(); |
| 163 } | 163 } |
| 164 }); | 164 }); |
| 165 paginator.setResultsPerPage(rowsPerPage.intValue()); | 165 paginator.setResultsPerPage(rowsPerPage.intValue()); |
| 166 } | 166 } |
| 167 | 167 |
| 168 /** | 168 /** |
| 169 * Set the page size of this table (only useful if you attach paginators). | 169 * Set the page size of this table (only useful if you attach paginators). |
| 170 */ | 170 */ |
| 171 public void setRowsPerPage(int rowsPerPage) { | 171 public void setRowsPerPage(int rowsPerPage) { |
| 172 assert rowsPerPage > 0; | 172 assert rowsPerPage > 0; |
| 173 this.rowsPerPage = Integer.valueOf(rowsPerPage); | 173 this.rowsPerPage = Integer.valueOf(rowsPerPage); |
| 174 for (Paginator paginator : paginators) { | 174 for (Paginator paginator : paginators) { |
| 175 paginator.setResultsPerPage(rowsPerPage); | 175 paginator.setResultsPerPage(rowsPerPage); |
| 176 } | 176 } |
| 177 } | 177 } |
| 178 | 178 |
| 179 /** | 179 /** |
| 180 * Set start row for pagination. You must call | 180 * Set start row for pagination. You must call |
| 181 * <code>refresh()</code> after this to display the results. | 181 * <code>refresh()</code> after this to display the results. |
| 182 */ | 182 */ |
| 183 public void setPaginatorStart(int start) { | 183 public void setPaginatorStart(int start) { |
| 184 for (Paginator paginator : paginators) { | 184 for (Paginator paginator : paginators) { |
| 185 paginator.setStart(start); | 185 paginator.setStart(start); |
| 186 } | 186 } |
| 187 } | 187 } |
| 188 | 188 |
| 189 protected void refreshPaginators() { | 189 protected void refreshPaginators() { |
| 190 for (Paginator paginator : paginators) { | 190 for (Paginator paginator : paginators) { |
| 191 paginator.update(); | 191 paginator.update(); |
| 192 } | 192 } |
| 193 } | 193 } |
| 194 | 194 |
| 195 protected void updatePaginatorTotalResults(int totalResults) { | 195 protected void updatePaginatorTotalResults(int totalResults) { |
| 196 for (Paginator paginator : paginators) { | 196 for (Paginator paginator : paginators) { |
| 197 paginator.setNumTotalResults(totalResults); | 197 paginator.setNumTotalResults(totalResults); |
| 198 } | 198 } |
| 199 } | 199 } |
| 200 | 200 |
| 201 | 201 |
| 202 // FILTERING | 202 // FILTERING |
| 203 | 203 |
| 204 public void addFilter(Filter filter) { | 204 public void addFilter(Filter filter) { |
| 205 filters.add(filter); | 205 filters.add(filter); |
| 206 filter.addCallback(new SimpleCallback() { | 206 filter.addCallback(new SimpleCallback() { |
| 207 public void doCallback(Object source) { | 207 public void doCallback(Object source) { |
| 208 setPaginatorStart(0); | 208 setPaginatorStart(0); |
| 209 refresh(); | 209 refresh(); |
| 210 } | 210 } |
| 211 }); | 211 }); |
| 212 } | 212 } |
| 213 | 213 |
| 214 protected void addFilterParams(JSONObject params) { | 214 protected void addFilterParams(JSONObject params) { |
| 215 for (Filter filter : filters) { | 215 for (Filter filter : filters) { |
| 216 if (filter.isActive()) { | 216 if (filter.isActive()) { |
| 217 filter.addParams(params); | 217 filter.addParams(params); |
| 218 } | 218 } |
| 219 } | 219 } |
| 220 } | 220 } |
| 221 | 221 |
| 222 public boolean isAnyUserFilterActive() { | 222 public boolean isAnyUserFilterActive() { |
| 223 for (Filter filter : filters) { | 223 for (Filter filter : filters) { |
| 224 if (filter.isUserControlled() && filter.isActive()) { | 224 if (filter.isUserControlled() && filter.isActive()) { |
| 225 return true; | 225 return true; |
| 226 } | 226 } |
| 227 } | 227 } |
| 228 | 228 |
| 229 return false; | 229 return false; |
| 230 } | 230 } |
| 231 | 231 |
| 232 | 232 |
| 233 // DATA MANAGEMENT | 233 // DATA MANAGEMENT |
| 234 | 234 |
| 235 public void refresh() { | 235 public void refresh() { |
| 236 JSONObject params = new JSONObject(); | 236 JSONObject params = new JSONObject(); |
| 237 addFilterParams(params); | 237 addFilterParams(params); |
| 238 dataSource.query(params, this); | 238 dataSource.query(params, this); |
| 239 } | 239 } |
| 240 | 240 |
| 241 @Override | 241 @Override |
| 242 public void onQueryReady(Query query) { | 242 public void onQueryReady(Query query) { |
| 243 currentQuery = query; | 243 currentQuery = query; |
| 244 if (!paginators.isEmpty()) { | 244 if (!paginators.isEmpty()) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 271 public void handlePage(List<JSONObject> data) { | 271 public void handlePage(List<JSONObject> data) { |
| 272 clear(); | 272 clear(); |
| 273 addRows(data); | 273 addRows(data); |
| 274 refreshPaginators(); | 274 refreshPaginators(); |
| 275 notifyListenersRefreshed(); | 275 notifyListenersRefreshed(); |
| 276 } | 276 } |
| 277 | 277 |
| 278 public String[] getRowData(int row) { | 278 public String[] getRowData(int row) { |
| 279 String[] data = new String[columns.length]; | 279 String[] data = new String[columns.length]; |
| 280 for (int i = 0; i < columns.length; i++) { | 280 for (int i = 0; i < columns.length; i++) { |
| 281 if(isWidgetColumn(i)) | 281 if(isWidgetColumn(i)) { |
| 282 continue; | 282 continue; |
| 283 } |
| 283 data[i] = table.getHTML(row, i); | 284 data[i] = table.getHTML(row, i); |
| 284 } | 285 } |
| 285 return data; | 286 return data; |
| 286 } | 287 } |
| 287 | 288 |
| 288 public DataSource getDataSource() { | 289 public DataSource getDataSource() { |
| 289 return dataSource; | 290 return dataSource; |
| 290 } | 291 } |
| 291 | 292 |
| 292 public void setDataSource(DataSource dataSource) { | 293 public void setDataSource(DataSource dataSource) { |
| 293 this.dataSource = dataSource; | 294 this.dataSource = dataSource; |
| 294 } | 295 } |
| 295 | 296 |
| 296 public Query getCurrentQuery() { | 297 public Query getCurrentQuery() { |
| 297 return currentQuery; | 298 return currentQuery; |
| 298 } | 299 } |
| 299 | 300 |
| 300 | 301 |
| 301 // INPUT | 302 // INPUT |
| 302 | 303 |
| 303 @Override | 304 @Override |
| 304 protected void onCellClicked(int row, int cell, boolean isRightClick) { | 305 protected void onCellClicked(int row, int cell, boolean isRightClick) { |
| 305 if (row == headerRow) { | 306 if (row == headerRow) { |
| 306 if (isWidgetColumn(cell)) { | 307 if (isWidgetColumn(cell)) { |
| 307 // ignore sorting on widget columns | 308 // ignore sorting on widget columns |
| 308 return; | 309 return; |
| 309 } | 310 } |
| 310 String columnName = columns[cell][COL_NAME]; | 311 String columnName = columns[cell][COL_NAME]; |
| 311 SortDirection newSortDirection = SortDirection.ASCENDING; | 312 SortDirection newSortDirection = SortDirection.ASCENDING; |
| 312 SortSpec firstSortSpec = getFirstSortSpec(); | 313 SortSpec firstSortSpec = getFirstSortSpec(); |
| 313 // when clicking on the last sorted field, invert the sort | 314 // when clicking on the last sorted field, invert the sort |
| 314 if (firstSortSpec != null && columnName.equals(firstSortSpec.getFiel
d())) { | 315 if (firstSortSpec != null && columnName.equals(firstSortSpec.getFiel
d())) { |
| 315 newSortDirection = invertSortDirection(firstSortSpec.getDirectio
n()); | 316 newSortDirection = invertSortDirection(firstSortSpec.getDirectio
n()); |
| 316 } | 317 } |
| 317 | 318 |
| 318 sortOnColumn(columnName, newSortDirection); | 319 sortOnColumn(columnName, newSortDirection); |
| 319 refresh(); | 320 refresh(); |
| 320 return; | 321 return; |
| 321 } | 322 } |
| 322 | 323 |
| 323 super.onCellClicked(row, cell, isRightClick); | 324 super.onCellClicked(row, cell, isRightClick); |
| 324 } | 325 } |
| 325 | 326 |
| 326 private SortDirection invertSortDirection(SortDirection direction) { | 327 private SortDirection invertSortDirection(SortDirection direction) { |
| 327 return direction == SortDirection.ASCENDING ? | 328 return direction == SortDirection.ASCENDING ? |
| 328 SortDirection.DESCENDING : SortDirection
.ASCENDING; | 329 SortDirection.DESCENDING : SortDirection
.ASCENDING; |
| 329 } | 330 } |
| 330 | 331 |
| 331 public void addListener(DynamicTableListener listener) { | 332 public void addListener(DynamicTableListener listener) { |
| 332 super.addListener(listener); | 333 super.addListener(listener); |
| 333 dynamicTableListeners.add(listener); | 334 dynamicTableListeners.add(listener); |
| 334 } | 335 } |
| 335 | 336 |
| 336 public void removeListener(DynamicTableListener listener) { | 337 public void removeListener(DynamicTableListener listener) { |
| 337 super.removeListener(listener); | 338 super.removeListener(listener); |
| 338 dynamicTableListeners.remove(listener); | 339 dynamicTableListeners.remove(listener); |
| 339 } | 340 } |
| 340 | 341 |
| 341 protected void notifyListenersRefreshed() { | 342 protected void notifyListenersRefreshed() { |
| 342 for (DynamicTableListener listener : dynamicTableListeners) { | 343 for (DynamicTableListener listener : dynamicTableListeners) { |
| 343 listener.onTableRefreshed(); | 344 listener.onTableRefreshed(); |
| 344 } | 345 } |
| 345 } | 346 } |
| 346 | 347 |
| 347 public List<SortSpec> getSortSpecs() { | 348 public List<SortSpec> getSortSpecs() { |
| 348 return Collections.unmodifiableList(sortColumns); | 349 return Collections.unmodifiableList(sortColumns); |
| 349 } | 350 } |
| 350 | 351 |
| 351 public void onError(JSONObject errorObject) { | 352 public void onError(JSONObject errorObject) { |
| 352 // nothing to do | 353 // nothing to do |
| 353 } | 354 } |
| 354 } | 355 } |
| OLD | NEW |