View Javadoc

1   package org.kuali.student.common.ui.client.widgets.table.scroll;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import org.kuali.student.common.ui.client.widgets.list.HasSelectionChangeHandlers;
7   import org.kuali.student.common.ui.client.widgets.list.SelectionChangeEvent;
8   import org.kuali.student.common.ui.client.widgets.list.SelectionChangeHandler;
9   import org.kuali.student.common.ui.client.widgets.notification.LoadingDiv;
10  
11  import com.google.gwt.core.client.GWT;
12  import com.google.gwt.event.dom.client.ChangeEvent;
13  import com.google.gwt.event.dom.client.ChangeHandler;
14  import com.google.gwt.event.dom.client.ClickEvent;
15  import com.google.gwt.event.dom.client.ClickHandler;
16  import com.google.gwt.event.dom.client.HasChangeHandlers;
17  import com.google.gwt.event.dom.client.HasClickHandlers;
18  import com.google.gwt.event.dom.client.KeyCodes;
19  import com.google.gwt.event.dom.client.KeyDownEvent;
20  import com.google.gwt.event.dom.client.KeyDownHandler;
21  import com.google.gwt.event.dom.client.ScrollEvent;
22  import com.google.gwt.event.dom.client.ScrollHandler;
23  import com.google.gwt.event.shared.HandlerRegistration;
24  import com.google.gwt.resources.client.CssResource;
25  import com.google.gwt.uibinder.client.UiBinder;
26  import com.google.gwt.uibinder.client.UiField;
27  import com.google.gwt.uibinder.client.UiHandler;
28  import com.google.gwt.user.client.Element;
29  import com.google.gwt.user.client.ui.CheckBox;
30  import com.google.gwt.user.client.ui.Composite;
31  import com.google.gwt.user.client.ui.FlexTable;
32  import com.google.gwt.user.client.ui.FocusPanel;
33  import com.google.gwt.user.client.ui.HTMLPanel;
34  import com.google.gwt.user.client.ui.Label;
35  import com.google.gwt.user.client.ui.ScrollPanel;
36  import com.google.gwt.user.client.ui.Widget;
37  import com.google.gwt.user.client.ui.HTMLTable.Cell;
38  import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
39  
40  /**
41   * A table with UiBinder.
42   */
43  public class Table extends Composite implements HasRetrieveAdditionalDataHandlers, HasSelectionChangeHandlers {
44  
45      private int headerSelectedCellIndex = -1;
46  
47      private FocusType focusType = FocusType.NONE;
48  
49      private static TableUiBinder uiBinder = GWT.create(TableUiBinder.class);
50      private final List<RetrieveAdditionalDataHandler> retrieveDataHandlers = new ArrayList<RetrieveAdditionalDataHandler>();
51  
52      interface TableUiBinder extends UiBinder<Widget, Table> {
53      }
54  
55      interface SelectionStyle extends CssResource {
56          String selectedRow();
57  
58          String columnAscending();
59  
60          String columnDescending();
61  
62          String selectedHeaderCell();
63      }
64  
65      private static enum FocusType {
66          HEADER,
67          BODY,
68          NONE
69      }
70  
71      @UiField
72      FlexTable header;
73      @UiField
74      MouseHoverFlexTable table;
75      @UiField
76      SelectionStyle selectionStyle;
77      @UiField
78      ScrollPanel scrollPanel;
79      @UiField
80      FocusPanel focusPanel;
81      @UiField
82      FocusPanel headerFocusPanel;
83      @UiField
84      HTMLPanel panel;
85  
86      private TableModel tableModel;
87      private final LoadingDiv loading = new LoadingDiv();
88  
89      public Table() {
90          initWidget(uiBinder.createAndBindUi(this));
91  
92          scrollPanel.addScrollHandler(new ScrollHandler() {
93              @Override
94              public void onScroll(ScrollEvent event) {
95                  int position = scrollPanel.getScrollPosition();
96                  // see 6th comment in KSLAB-1790; possibly not created yet?
97                  if (null != scrollPanel.getWidget()) {
98                      int size = scrollPanel.getWidget().getOffsetHeight();
99                      int diff = size - scrollPanel.getOffsetHeight();
100                     if (position == diff) {
101                         for (int i = 0; i < retrieveDataHandlers.size(); i++) {
102                             retrieveDataHandlers.get(i).onAdditionalDataRequest();
103                         }
104                     }
105                 }
106             }
107         });
108         addHandlers();
109     }
110     
111     public void removeAllRows(){
112     	table.removeAllRows();
113     }
114 
115     public void removeContent(){
116     	getScrollPanel().clear();
117     }
118  
119     public void addContent(){
120     	getScrollPanel().setWidget(getContent());
121     }
122     
123     private void addHandlers() {
124         focusPanel.addKeyDownHandler(new KeyDownHandler() {
125 
126             @Override
127             public void onKeyDown(KeyDownEvent event) {
128                 int code = event.getNativeKeyCode();
129                 if (code == KeyCodes.KEY_DOWN) {
130                     processKeyUpAndDownEvent(TablePredicateFactory.DOWN_RIGHT_PREDICATE);
131                 } else if (code == KeyCodes.KEY_UP) {
132                     processKeyUpAndDownEvent(TablePredicateFactory.UP_LEFT_PREDICATE);
133                 } else if (code == ' ') {
134                     processSpaceClick();
135                 }
136             }
137         });
138         headerFocusPanel.addKeyDownHandler(new KeyDownHandler() {
139             @Override
140             public void onKeyDown(KeyDownEvent event) {
141                 int code = event.getNativeKeyCode();
142                 if (code == KeyCodes.KEY_RIGHT) {
143                     processKeyLeftRight(TablePredicateFactory.DOWN_RIGHT_PREDICATE);
144                 } else if (code == KeyCodes.KEY_LEFT) {
145                     processKeyLeftRight(TablePredicateFactory.UP_LEFT_PREDICATE);
146                 } else if (code == KeyCodes.KEY_DOWN) {
147                     processKeyDownOnHeader();
148                 } else if (code == ' ') {
149                     onTableHeaderClicked(headerSelectedCellIndex, true);
150                 }
151             }
152         });
153     }
154 
155     private void processKeyDownOnHeader() {
156         changeFocus(FocusType.BODY);
157         Row currentRow = tableModel.getRow(0);
158         tableModel.setCurrentIndex(0);
159         currentRow.setHighlighted(true);
160         updateTableSelection();
161         removeHeaderSelection();
162     }
163 
164     private void removeHeaderSelection() {
165         if (headerSelectedCellIndex >= 0) {
166             header.getCellFormatter().removeStyleName(0, headerSelectedCellIndex, selectionStyle.selectedHeaderCell());
167             headerSelectedCellIndex = -1;
168         }
169     }
170 
171     private void processKeyLeftRight(TablePredicateFactory.Predicate tablePredicate) {
172         if (tablePredicate.indexCondition(headerSelectedCellIndex, tableModel.getColumnCount())) {
173             header.getCellFormatter().removeStyleName(0, headerSelectedCellIndex, selectionStyle.selectedHeaderCell());
174             headerSelectedCellIndex = tablePredicate.nextIndex(headerSelectedCellIndex);
175             header.getCellFormatter().addStyleName(0, headerSelectedCellIndex, selectionStyle.selectedHeaderCell());
176 
177         }
178     }
179 
180     private void processSpaceClick() {
181         int index = tableModel.getCurrentIndex();
182         if (index >= 0) {
183             Row currentRow = tableModel.getRow(index);
184             boolean selected = currentRow.isSelected();
185             if (selected) {
186                 currentRow.setSelected(false);
187             } else {
188                 tableModel.setSelectedRow(index);
189             }
190             updateTableSelection();
191         }
192     }
193 
194     private void processKeyUpAndDownEvent(TablePredicateFactory.Predicate tablePredicate) {
195         int currentIndex = tableModel.getCurrentIndex();
196         Row currentRow = tableModel.getRow(currentIndex);
197         if (currentIndex == 0 && tablePredicate.moveType() == TablePredicateFactory.MoveType.UP_LEFT) {
198             tableModel.getRow(currentIndex).setHighlighted(false);
199             tableModel.setCurrentIndex(-1);
200             headerSelectedCellIndex = 0;
201             changeFocus(FocusType.HEADER);
202             header.getCellFormatter().addStyleName(0, 0, selectionStyle.selectedHeaderCell());
203         } else {
204             if (currentRow.isHighlighted() && tablePredicate.indexCondition(currentIndex, tableModel.getRowCount())) {
205                 currentRow.setHighlighted(false);
206                 currentIndex = tablePredicate.nextIndex(currentIndex);
207                 tableModel.getRow(currentIndex).setHighlighted(true);
208                 tableModel.setCurrentIndex(currentIndex);
209                 scrollPanel.ensureVisible(table.getWidget(currentIndex, 0));
210             }
211         }
212         updateTableSelection();
213     }
214 
215     public FlexTable getHeader() {
216         return header;
217     }
218 
219     public FlexTable getContent() {
220         return table;
221     }
222 
223     public ScrollPanel getScrollPanel() {
224         return scrollPanel;
225     }
226 
227     public void setTableModel(TableModel m) {
228         tableModel = m;
229         table.setModel(tableModel);
230         if (m instanceof AbstractTableModel) {
231             ((AbstractTableModel) tableModel).addTableModelListener(new TableModelListener() {
232                 @Override
233                 public void tableChanged(TableModelEvent e) {
234                     updateTable(e);
235                 }
236             });
237             ((AbstractTableModel) tableModel).fireTableStructureChanged();
238         }
239     }
240     
241     public TableModel getTableModel(){
242     	return tableModel;
243     }
244 
245     @UiHandler("table")
246     void onTableClicked(ClickEvent event) {
247         removeHeaderSelection();
248         //changeFocus(FocusType.BODY);
249         Cell cell = table.getCellForEvent(event);
250 
251         if (cell == null) {
252             return;
253         }
254         int cellIndex = cell.getCellIndex();
255         int rowIndex = cell.getRowIndex();
256         tableModel.setCurrentIndex(rowIndex);
257         Row row = tableModel.getRow(rowIndex);
258 
259         if (tableModel.isMultipleSelectable() == false) {
260             for (int r = 0; r < tableModel.getRowCount(); r++) {
261                 if (r != rowIndex) {
262                     tableModel.getRow(r).setSelected(false);
263                 }
264             }
265             row.setSelected(!row.isSelected());
266             updateTableSelection();
267             for (int r = 0; r < tableModel.getRowCount(); r++) {
268                 updateTableCell(r, 0);
269             }
270         } else {
271             if (cellIndex == 0) {
272                 return;
273             }
274             row.setSelected(!row.isSelected());
275             updateTableSelection();
276             updateTableCell(rowIndex, 0);
277         }
278     }
279 
280     @UiHandler("header")
281     void onTableHeaderClicked(ClickEvent event) {
282         Cell cell = header.getCellForEvent(event);
283         if (cell != null) {
284             onTableHeaderClicked(cell.getCellIndex(), false);
285         }
286     }
287 
288     private void onTableHeaderClicked(int cellIndex, boolean propagateEventIfNotSortingColumn) {
289         if (cellIndex == 0 && tableModel.isMultipleSelectable() && propagateEventIfNotSortingColumn) {
290             Widget widget = header.getWidget(0, 0);
291             if (widget instanceof CheckBox) {
292                 CheckBox checkBox = (CheckBox) widget;
293                 boolean correctValue = !checkBox.getValue();
294                 checkBox.setValue(correctValue);
295                 for (int row = 0; row < tableModel.getRowCount(); row++) {
296                     tableModel.getRow(row).setSelected(correctValue);
297                     updateTableSelection();
298                 }
299             }
300         } else {
301             removeHeaderSelection();
302         }
303         headerSelectedCellIndex = cellIndex;
304         Column col = tableModel.getColumn(cellIndex);
305         tableModel.sort(col);
306     }
307 
308     private void onTableClicked(int row, String columnId,
309                                 TableCellWidget cellWidget) {
310         onTableCellChanged(row, columnId, cellWidget);
311     }
312 
313     private void onTableCellChanged(int rowIndex, String columnId,
314                                     TableCellWidget cellWidget) {
315         Row row = tableModel.getRow(rowIndex);
316         if ("RowHeader".equals(columnId)) {
317             row.setSelected(!row.isSelected());
318             updateTableSelection();
319         }
320         row.setCellData(columnId,
321                 cellWidget.getCellEditorValue());
322     }
323 
324     private void updateTableSelection() {
325         int count = tableModel.getRowCount();
326         for (int i = 0; i < count; i++) {
327             Element tr = table.getRowFormatter().getElement(i);
328             if (tableModel.getRow(i).isSelected()) {
329             	tr.setAttribute("class", "table-row-selected");
330             } else {
331                 tr.setAttribute("class", "table-row");
332             }
333             if (tableModel.getRow(i).isHighlighted()) {
334             	tr.setAttribute("class", "table-row-hover");
335             }
336             if (tableModel.isMultipleSelectable()) {
337                 updateTableCell(i, 0);
338             }
339         }
340         SelectionChangeEvent.fire(this);
341     }
342 
343     private void changeFocus(FocusType focusType) {
344         this.focusType = focusType;
345         if (focusType == FocusType.HEADER) {
346             headerFocusPanel.setFocus(true);
347         } else if (focusType == FocusType.BODY) {
348             focusPanel.setFocus(true);
349         }
350     }
351 
352     public void updateTable(TableModelEvent event) {
353         if (event.getType() == TableModelEvent.TableStructure) {
354             updateTableStructure();
355             updateTableData();
356         } else if (event.getType() == TableModelEvent.TableData) {
357             updateTableData();
358         } else if (event.getType() == TableModelEvent.CellUpdate) {
359             updateTableCell(event.getFirstRow(), event.getColumn());
360         }
361     }
362 
363     private void updateTableData() {
364         for (int r = 0; r < tableModel.getRowCount(); r++) {
365             int columnCount = tableModel.getColumnCount();
366             for (int c = 0; c < columnCount; c++) {
367                 updateTableCell(r, c);
368             }
369         }
370         updateTableSelection();
371     }
372 
373     private void updateTableCell(final int r, final int c) {
374         int columnCount = tableModel.getColumnCount();
375         for (int i = 0; i < columnCount - 1; i++) {
376             Column col = tableModel.getColumn(i);
377             table.getColumnFormatter().setWidth(i, col.getWidth());
378         }
379         final String columnId = tableModel.getColumn(c).getId();
380         Row row = tableModel.getRow(r);
381         Object v = null;
382         if ("RowHeader".equals(columnId)) {
383             v = row.isSelected();
384         } else {
385             v = row.getCellData(columnId);
386         }
387         if ("RowHeader".equals(columnId) == false) {
388             if (v != null) {
389                 table.setWidget(r, c, new Label(v.toString()));
390             } else {
391                 table.setHTML(r, c, "&nbsp;");
392             }
393             return;
394         }
395         final TableCellWidget widget = new TableCellWidget(v);
396         widget.setCellEditorValue(v);
397         if (widget instanceof HasClickHandlers) {
398             ((HasClickHandlers) widget)
399                     .addClickHandler(new ClickHandler() {
400                         @Override
401                         public void onClick(ClickEvent event) {
402                             onTableClicked(r, columnId, widget);
403                         }
404                     });
405         }
406         if (widget instanceof HasChangeHandlers) {
407             ((HasChangeHandlers) widget)
408                     .addChangeHandler(new ChangeHandler() {
409                         @Override
410                         public void onChange(ChangeEvent event) {
411                             onTableCellChanged(r, columnId, widget);
412 
413                         }
414                     });
415         }
416         table.setWidget(r, c, widget);
417     }
418 
419     private void updateTableStructure() {
420         int columnCount = tableModel.getColumnCount();
421         for (int i = 0; i < columnCount; i++) {
422             Column col = tableModel.getColumn(i);
423             header.setWidget(0, i, col.getColumnTitleWidget());
424         }
425         for (int i = 0; i < columnCount - 1; i++) {
426             Column col = tableModel.getColumn(i);
427             header.getColumnFormatter().setWidth(i, col.getWidth());
428         }
429         for (int i = 0; i < columnCount; i++) {
430             Column col = tableModel.getColumn(i);
431 
432             header.getCellFormatter().removeStyleName(0, i, selectionStyle.columnAscending());
433             header.getCellFormatter().removeStyleName(0, i, selectionStyle.columnDescending());
434 
435             if (col.getSortDirection() == Column.Ascending) {
436                 header.getCellFormatter().addStyleName(0, i, selectionStyle.columnAscending());
437             } else if (col.getSortDirection() == Column.Descending) {
438                 header.getCellFormatter().addStyleName(0, i, selectionStyle.columnDescending());
439             }
440         }
441     }
442 
443     @Override
444     public HandlerRegistration addRetrieveAdditionalDataHandler(
445             final RetrieveAdditionalDataHandler handler) {
446         retrieveDataHandlers.add(handler);
447         HandlerRegistration result = new HandlerRegistration() {
448             @Override
449             public void removeHandler() {
450                 retrieveDataHandlers.remove(handler);
451             }
452         };
453         return result;
454     }
455 
456     public void displayLoading(boolean isLoading) {
457         changeFocus(FocusType.BODY);
458         final int x = scrollPanel.getAbsoluteLeft() + scrollPanel.getOffsetWidth();
459         final int y = scrollPanel.getAbsoluteTop() + scrollPanel.getOffsetHeight();
460         if (isLoading) {
461             loading.setPopupPositionAndShow(new PositionCallback() {
462 
463                 @Override
464                 public void setPosition(int offsetWidth, int offsetHeight) {
465                     loading.setPopupPosition(x - offsetWidth, y + 1);
466                 }
467             });
468         } else {
469             loading.hide();
470         }
471     }
472 
473 	/**
474 	 * @see org.kuali.student.common.ui.client.widgets.list.HasSelectionChangeHandlers#addSelectionChangeHandler(org.kuali.student.common.ui.client.widgets.list.SelectionChangeHandler)
475 	 */
476 	@Override
477 	public HandlerRegistration addSelectionChangeHandler(
478 			SelectionChangeHandler handler) {
479 		return addHandler(handler, SelectionChangeEvent.getType());
480 	}
481 }