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