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