package com.gwt.ui.client.draggablesupertable; import java.util.ArrayList; import com.google.gwt.event.logical.shared.HasValueChangeHandlers; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; import com.gwt.ui.client.supertable.SuperTable; public class DraggableSuperTable extends SuperTable implements HasValueChangeHandlers<InfoDragFlexTable> { private HandlerRegistration hEventPere = null; private boolean isDragging = false; // Popup and FlexTable used for drag and drop object PopupPanel pDrag = new PopupPanel(); FlexTable ftDrag = new FlexTable(); private CoordFlexTable coordDrag = new CoordFlexTable(); private int colDragButton = CoordFlexTable.UNDEFINED_COORD_INDEX; private int rowSource = CoordFlexTable.UNDEFINED_COORD_INDEX; private ArrayList<Boolean> enabled = null; private int rowDest = CoordFlexTable.UNDEFINED_COORD_INDEX; public DraggableSuperTable() { super(); // Give the desired style for the popup used when dragging a row pDrag.setStyleName("SYST_DragnDrop"); // Disable animation because of a GWT issue : when animation is enabled, // it's impossible to move the popup !!! pDrag.setAnimationEnabled(false); colDragButton = CoordFlexTable.UNDEFINED_COORD_INDEX; isDragging = false; coordDrag = new CoordFlexTable(); // Initialization of the popup row pDrag.setWidget(ftDrag); pDrag.hide(); // Used events sinkEvents(Event.MOUSEEVENTS | Event.ONCONTEXTMENU); } public void onBrowserEvent(Event event) { // Disallow the default context menu in order to manage right-click if (event.getTypeInt() == Event.ONCONTEXTMENU) { event.stopPropagation(); event.preventDefault(); return; } super.onBrowserEvent(event); switch (DOM.eventGetType(event)) { case Event.ONMOUSEOVER: { Element td = getEventTargetCell(event); if (td == null) { return; } Element tr = DOM.getParent(td); Element body = DOM.getParent(tr); int row = DOM.getChildIndex(body, tr);// get the index row getRowFormatter().addStyleName(row, "gwtcomp-grid-row-highlight");// add the style gwtcomp-grid-row-highlight break; } case Event.ONMOUSEOUT: { Element td = getEventTargetCell(event); if (td == null) { return; } Element tr = DOM.getParent(td); Element body = DOM.getParent(tr); int row = DOM.getChildIndex(body, tr);// get the index row getRowFormatter().removeStyleName(row, "gwtcomp-grid-row-highlight");// remove the style gwtcomp-grid-row-highlight break; } case Event.ONMOUSEDOWN: { if (!isDragging) { // Take into account only if right-click or if the left-click // is on a dedicated widget if (!coordDrag.equals(CoordFlexTable.UNDEFINED_COORD)) { if ((coordDrag.getCol() == colDragButton) || (event.getButton() == Event.BUTTON_RIGHT)) { isDragging = true; rowSource = coordDrag.getRow(); setDragStyleCursor(true); // Remove the dragged row from the FlexTable and show it into a popup ftDrag.clear(); pDrag.remove(ftDrag); for (int i = 0; i < this.getCellCount(coordDrag.getRow()); i++) if (this.getWidget(coordDrag.getRow(), i) != null) { Widget wid = this.getWidget(coordDrag.getRow(), i); wid.addStyleName("SYST_NoWrap"); int width = wid.getElement().getOffsetWidth(); int height = wid.getElement().getOffsetHeight(); ftDrag.setCellPadding(this.getCellPadding()); ftDrag.setCellSpacing(this.getCellSpacing()); ftDrag.setWidget(0, i, wid); if (width > 0) ftDrag.getCellFormatter().setWidth(0, i, "" + width + "px"); if (height > 0) ftDrag.getCellFormatter().setHeight(0, i, "" + height + "px"); ftDrag.getFlexCellFormatter().setColSpan(0, i, this.getFlexCellFormatter().getColSpan(coordDrag.getRow(), i)); ftDrag.getFlexCellFormatter().setRowSpan(0, i, this.getFlexCellFormatter().getRowSpan(coordDrag.getRow(), i)); if (this.getCellFormatter().getStyleName(coordDrag.getRow(), i) != null) ftDrag.getCellFormatter().setStyleName(0, i, this.getCellFormatter().getStyleName(coordDrag.getRow(), i)); } this.removeRow(coordDrag.getRow()); // Memorize enable status for each widget // Be sure to call it after removing row getEnabled(); // Set enabled false on all the FlexTable to avoid // any unexpected action during dragging setEnabled(false); addLineDragStyle(coordDrag.getRow()); pDrag.setWidget(ftDrag); pDrag.setPopupPosition(event.getClientX() + 5 + event.getClientX(), event.getClientY() + 5 + event.getClientY()); pDrag.show(); } } } break; } case Event.ONMOUSEUP: { if (isDragging) { isDragging = false; rowDest = coordDrag.getRow(); removeLineDragStyle(coordDrag.getRow()); setDragStyleCursor(false); // Reset enabled widget // Be sure to call it before inserting row setEnabled(true); // Take the content of the popup and insert it into the FlexTable at the dropped row index this.insertRow(coordDrag.getRow()); for (int i = 0; i < ftDrag.getCellCount(0); i++) if (ftDrag.getWidget(0, i) != null) { this.setWidget(coordDrag.getRow(), i, ftDrag.getWidget(0, i)); this.getFlexCellFormatter().setColSpan(coordDrag.getRow(), i, ftDrag.getFlexCellFormatter().getColSpan(0, i)); this.getFlexCellFormatter().setRowSpan(coordDrag.getRow(), i, ftDrag.getFlexCellFormatter().getRowSpan(0, i)); if (ftDrag.getCellFormatter().getStyleName(0, i) != null) this.getCellFormatter().setStyleName(coordDrag.getRow(), i, ftDrag.getCellFormatter().getStyleName(0, i)); } pDrag.hide(); fireEvent(); } break; } case Event.ONMOUSEMOVE: { CoordFlexTable coord = CoordFlexTable.getCoordTarget(this, event); // Not dragging : just listen to the visited cell and memorize it for next drag (mouse down) if (!isDragging) { if (isDraggable(coord.getRow())) { coordDrag = new CoordFlexTable(coord); } else { coordDrag = new CoordFlexTable(); } } // Dragging : move the popup following the mouse cursor, show the currently visited row // for eventual next drop position and memorize it for next drop (mouse up) else { if (isDroppable(coord.getRow())) { setDragStyleCursor(true); pDrag.hide(); pDrag.setPopupPosition(event.getClientX() + 5 + event.getClientY(), event.getClientY() + 5 + event.getClientX()); pDrag.show(); // New row : show it by a top border line and memorize the new row index if (coord.getRow() != coordDrag.getRow()) { removeLineDragStyle(coordDrag.getRow()); addLineDragStyle(coord.getRow()); coordDrag = new CoordFlexTable(coord); } } } break; } default: { // Do nothing } } } /** * Checks if a row can be dragged or not depending on the header and footer defined * * @param int row index to be tested * @return boolean true if the row is draggable */ public boolean isDraggable(int row) { // To be draggable, at least two rows must exist in addition to header and footer if (this.getRowCount() > (2)) if (row != CoordFlexTable.UNDEFINED_COORD_INDEX) if ((row >= 1) && (row <= (this.getRowCount() - 1))) return true; return false; } /** * Checks if a row can be droppable or not depending on the header and footer defined * * @param int row index to be tested * @return boolean true if the row is droppable */ public boolean isDroppable(int row) { // One row is needed in addition to header and footer to allow drop if (this.getRowCount() > (1)) if (row != CoordFlexTable.UNDEFINED_COORD_INDEX) if ((row >= 1) && (row <= (this.getRowCount()))) return true; return false; } /** * Removes the style of the droppable row * * @param int row index which style must be removed */ private void removeLineDragStyle(int row) { for (int i = 0; i < this.getCellCount(row); i++) { this.getCellFormatter().removeStyleName(row, i, "SYST_DragnDropLine"); } } /** * Fire a InfoDragFlexTable event width memorized data */ public void fireEvent() { ValueChangeEvent.fire(this, new InfoDragFlexTable(rowSource, rowDest)); } /** * Sets the style of the droppable row * * @param int row index which style must be set */ private void addLineDragStyle(int row) { for (int i = 0; i < this.getCellCount(row); i++) { this.getCellFormatter().addStyleName(row, i, "SYST_DragnDropLine"); } } /** * Sets the style of the cursor * * @param boolean drag : true if the cursor must be forced to a move one */ private void setDragStyleCursor(boolean drag) { if (drag) { RootPanel.get().addStyleName("globalMoveCursor"); } else RootPanel.get().removeStyleName("globalMoveCursor"); } /** * Memorize the enabled status of each widget of the FlexTable */ public void getEnabled() { if (enabled == null) enabled = new ArrayList<Boolean>(); else enabled.clear(); for (int i = 0; i < this.getRowCount(); i++) for (int j = 0; j < this.getCellCount(i); j++) if (this.getWidget(i, j) != null) if (this.getWidget(i, j) instanceof FocusWidget) enabled.add(new Boolean(((FocusWidget)this.getWidget(i, j)).isEnabled())); } /** * Disabled or reset the status for each widget of the FlexTable * * @param possibleEnabled */ public void setEnabled(boolean possibleEnabled) { if (enabled == null) return; // Reset the previous status if (possibleEnabled) { int index = 0; for (int i = 0; i < this.getRowCount(); i++) for (int j = 0; j < this.getCellCount(i); j++) if (this.getWidget(i, j) != null) if (this.getWidget(i, j) instanceof FocusWidget) if (index < enabled.size()) { ((FocusWidget)this.getWidget(i, j)).setEnabled(enabled.get(index).booleanValue()); index++; } } else for (int i = 0; i < this.getRowCount(); i++) for (int j = 0; j < this.getCellCount(i); j++) if (this.getWidget(i, j) != null) if (this.getWidget(i, j) instanceof FocusWidget) ((FocusWidget)this.getWidget(i, j)).setEnabled(false); } /** * Add a listener on a InfoDragFlexTable event used when drop is finished * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) */ public HandlerRegistration addValueChangeHandler(ValueChangeHandler<InfoDragFlexTable> handler) { if ( hEventPere != null ) hEventPere.removeHandler(); hEventPere = addHandler (handler, ValueChangeEvent.getType()); return hEventPere; } }