package org.geogebra.web.web.gui.view.spreadsheet; import org.geogebra.common.awt.GPoint; import org.geogebra.common.awt.GRectangle; import org.geogebra.common.euclidian.event.PointerEventType; import org.geogebra.common.gui.view.spreadsheet.MyTableInterface; import org.geogebra.common.kernel.geos.GeoElementSpreadsheet; import org.geogebra.common.util.debug.Log; import org.geogebra.web.html5.event.PointerEvent; import org.geogebra.web.html5.event.ZeroOffset; import org.geogebra.web.html5.gui.util.CancelEventTimer; import org.geogebra.web.html5.gui.util.LongTouchManager; import org.geogebra.web.html5.gui.util.LongTouchTimer.LongTouchHandler; import org.geogebra.web.html5.main.AppW; import org.geogebra.web.html5.util.EventUtil; import org.geogebra.web.web.gui.GuiManagerW; import org.geogebra.web.web.javax.swing.GPopupMenuW; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DoubleClickEvent; import com.google.gwt.event.dom.client.DoubleClickHandler; import com.google.gwt.event.dom.client.HumanInputEvent; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.event.dom.client.TouchEndEvent; import com.google.gwt.event.dom.client.TouchEndHandler; import com.google.gwt.event.dom.client.TouchMoveEvent; import com.google.gwt.event.dom.client.TouchMoveHandler; import com.google.gwt.event.dom.client.TouchStartEvent; import com.google.gwt.event.dom.client.TouchStartHandler; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Widget; public class SpreadsheetColumnHeaderW implements MouseDownHandler, MouseUpHandler, MouseMoveHandler, ClickHandler, DoubleClickHandler, KeyDownHandler, LongTouchHandler, TouchStartHandler, TouchMoveHandler, TouchEndHandler { private AppW app; private MyTableW table; private Grid grid; private FlowPanel container; private FocusPanel focusPanel; private int mouseXOffset, resizingColumn = -1; private boolean isMouseDown = false; protected int column0 = -1; private boolean doColumnResize = false; private int overTraceButtonColumn = -1; private LongTouchManager longTouchManager; /*************************************************** * Constructor */ public SpreadsheetColumnHeaderW(AppW app, MyTableW table) { this.app = app; this.table = table; prepareGUI(); registerListeners(); longTouchManager = LongTouchManager.getInstance(); } private void registerListeners() { grid.addDomHandler(this, MouseDownEvent.getType()); grid.addDomHandler(this, MouseUpEvent.getType()); grid.addDomHandler(this, MouseMoveEvent.getType()); grid.addDomHandler(this, ClickEvent.getType()); grid.addDomHandler(this, DoubleClickEvent.getType()); grid.addDomHandler(this, TouchStartEvent.getType()); grid.addDomHandler(this, TouchEndEvent.getType()); grid.addDomHandler(this, TouchMoveEvent.getType()); } // ============================================ // GUI handlers // ============================================ private void initializeCell(int colIndex) { String name = GeoElementSpreadsheet.getSpreadsheetColumnName(colIndex); grid.setText(0, colIndex, name); int columnWidth = table.preferredColumnWidth; grid.getColumnFormatter().getElement(colIndex).getStyle() .setWidth(columnWidth, Style.Unit.PX); Element elm = grid.getCellFormatter().getElement(0, colIndex); elm.addClassName("SVheader"); /*elm.getStyle().setBackgroundColor( MyTableW.BACKGROUND_COLOR_HEADER.toString());*/ } private void prepareGUI() { grid = new Grid(1, table.getModel().getColumnCount()); grid.setCellPadding(0); grid.setCellSpacing(0); grid.setHeight("0px"); grid.getElement().addClassName("geogebraweb-table-spreadsheet"); int rowHeight = app.getSettings().getSpreadsheet().preferredRowHeight(); grid.getRowFormatter().getElement(0).getStyle() .setHeight(rowHeight, Style.Unit.PX); for (int col = 0; col < grid.getColumnCount(); col++) { initializeCell(col); } focusPanel = new FocusPanel(); focusPanel.addKeyDownHandler(this); Style s = focusPanel.getElement().getStyle(); // s.setDisplay(Style.Display.NONE); s.setPosition(Style.Position.ABSOLUTE); s.setTop(0, Unit.PX); s.setLeft(0, Unit.PX); container = new FlowPanel(); container.add(grid); container.add(focusPanel); } public void updateColumnCount() { if (grid.getColumnCount() >= table.getColumnCount()) { return; } int oldColumnCount = grid.getColumnCount(); grid.resizeColumns(table.getColumnCount()); for (int i = oldColumnCount; i < table.getColumnCount(); ++i) { initializeCell(i); } } // ============================================ // Getters/Setters // ============================================ public Widget getContainer() { return container; } public void setLeft(int left) { container.getElement().getStyle().setLeft(left, Unit.PX); } public int getOffsetHeight() { return getContainer().getOffsetHeight(); } private String getCursor() { return grid.getElement().getStyle().getCursor(); } private void setColumnResizeCursor() { grid.getElement().getStyle().setCursor(Style.Cursor.COL_RESIZE); } private void setDefaultCursor() { grid.getElement().getStyle().setCursor(Style.Cursor.DEFAULT); } public void renderSelection() { /*String defaultBackground = MyTableW.BACKGROUND_COLOR_HEADER.toString(); String selectedBackground = MyTableW.SELECTED_BACKGROUND_COLOR_HEADER .toString();*/ for (int colIndex = 0; colIndex < grid.getColumnCount(); colIndex++) { /*Style s = grid.getCellFormatter().getElement(0, colIndex) .getStyle();*/ if (table.getSelectionType() == MyTableInterface.ROW_SELECT) { //setBgColorIfNeeded(s, defaultBackground); updateCellSelection(false, colIndex); } else { if (table.selectedColumnSet.contains(colIndex) || (colIndex >= table.minSelectionColumn && colIndex <= table.maxSelectionColumn)) { //setBgColorIfNeeded(s, selectedBackground); updateCellSelection(true, colIndex); } else { //setBgColorIfNeeded(s, defaultBackground); updateCellSelection(false, colIndex); } } } } /** * Added "selected" class to the table headers of the selected cell * needed for css styling * @param selected * @param index */ private void updateCellSelection(boolean selected, int index) { if(selected) { grid.getCellFormatter().addStyleName(0, index, "selected"); } else { grid.getCellFormatter().removeStyleName(0, index, "selected"); } } /** * @param rowIndex * index of row to set height * @param rowHeight * new row height */ public void setColumnWidth(int columnIndex, int width) { if (columnIndex >= grid.getColumnCount()) { return; } grid.getColumnFormatter().getElement(columnIndex).getStyle() .setWidth(width, Style.Unit.PX); } /* Steffi: not needed anymore -> NOW: updateCellSelection * private static void setBgColorIfNeeded(Style s, String bgColor) { if (!s.getBackgroundColor().equals(bgColor)) s.setBackgroundColor(bgColor); }*/ /** * @param p * location of mouse (in client area pixels) * @param boundary * the boundary * @return index of the column to be resized if mouse point p is near a * column boundary */ private int getResizingColumn(GPoint p, int boundary) { int resizeColumn = -1; GPoint point = table.getIndexFromPixel(p.x, 0); if (point != null) { // test if mouse is 3 pixels from column boundary int cellColumn = point.getX(); if (cellColumn >= 0) { GRectangle r = table.getCellRect(0, cellColumn, true); // near column left ? if (p.x < r.getX() + boundary) { resizeColumn = cellColumn - 1; } // near column right ? if (p.x > r.getX() + r.getWidth() - boundary) { resizeColumn = cellColumn; } } } return resizeColumn; } private static int getBoundary(PointerEventType eventType) { return eventType == PointerEventType.MOUSE ? 3 : 6; } // =============================================== // Renderer // =============================================== /** * Update the rowHeader list when row selection changes in the table */ /* * public void valueChanged(ListSelectionEvent e) { ListSelectionModel * selectionModel = (ListSelectionModel) e.getSource(); minSelectionRow = * selectionModel.getMinSelectionIndex(); maxSelectionRow = * selectionModel.getMaxSelectionIndex(); repaint(); } */ // Returns index of row to be resized if mouse point P is // near a row boundary (within 3 pixels) /* * private int getResizingRow(java.awt.Point p) { int resizeRow = -1; GPoint * point = table.getIndexFromPixel(p.x, p.y); if (point != null) { // test * if mouse is 3 pixels from row boundary int cellRow = point.getY(); if * (cellRow >= 0) { Rectangle r = table.getCellRect(cellRow, 0, true); // * near row bottom if (p.y < r.y + 3) { resizeRow = cellRow - 1; } // near * row top if (p.y > r.y + r.height - 3) { resizeRow = cellRow; } } } return * resizeRow; } */ // Cursor change for when mouse is over a row boundary /* * private void swapCursor() { Cursor tmp = getCursor(); * setCursor(otherCursor); otherCursor = tmp; } */ // =============================================== // Mouse Listener Methods // =============================================== /* * public void mouseClicked(MouseEvent e) { * * // Double clicking on a row boundary auto-adjusts the // height of the * row above the boundary (the resizingRow) * * if (resizingRow >= 0 && !AppD.isRightClick(e) && e.getClickCount() == 2) * { table.fitRow(resizingRow); e.consume(); } } * * public void mouseEntered(MouseEvent e) { } * * public void mouseExited(MouseEvent e) { } */ @Override public void onMouseDown(MouseDownEvent e) { if (CancelEventTimer.cancelMouseEvent()) { return; } e.preventDefault(); PointerEvent event = PointerEvent.wrapEvent(e, ZeroOffset.instance); onPointerDown(event); } @Override public void onMouseUp(MouseUpEvent e) { if (CancelEventTimer.cancelMouseEvent()) { return; } e.preventDefault(); PointerEvent event = PointerEvent.wrapEvent(e, ZeroOffset.instance); onPointerUp(event); } // =============================================== // MouseMotion Listener Methods // =============================================== @Override public void onMouseMove(MouseMoveEvent e) { if (CancelEventTimer.cancelMouseEvent()) { return; } e.preventDefault(); PointerEvent event = PointerEvent.wrapEvent(e, ZeroOffset.instance); onPointerMove(event); } private void onPointerDown(PointerEvent e) { Event.setCapture(grid.getElement()); isMouseDown = true; if (table.getEditor().isEditing()) { table.getEditor().setAllowProcessGeo(true); table.getEditor().stopCellEditing(); table.getEditor().setAllowProcessGeo(false); table.finishEditing(false); } int x = SpreadsheetMouseListenerW.getAbsoluteX(e.getWrappedEvent(), app); int y = SpreadsheetMouseListenerW.getAbsoluteY(e.getWrappedEvent(), app); boolean shiftDown = e.isShiftDown(); boolean rightClick = e.isRightClick(); if (!rightClick) { GPoint point = table.getIndexFromPixel(x, y); if (point == null) { return; } // mouse down in resizing region GPoint p = new GPoint(x, y); resizingColumn = getResizingColumn(p, getBoundary(e.getType())); if (resizingColumn >= 0) { mouseXOffset = p.x - table.getColumnWidth(resizingColumn); } // standard mouse down else { // launch trace dialog if over a trace button if (point.x == this.overTraceButtonColumn) { int column = point.getX(); table.setColumnSelectionInterval(column, column); // ?//view.showTraceDialog(null, // ?// table.selectedCellRanges.get(0)); // ?//e.consume(); return; } // otherwise handle column selection if (table .getSelectionType() != MyTableInterface.COLUMN_SELECT) { table.setSelectionType(MyTableInterface.COLUMN_SELECT); // ?//if (table.getTableHeader() != null) { // ?// table.getTableHeader().requestFocusInWindow(); // ?//} } if (shiftDown) { if (column0 != -1) { int column = point.getX(); table.setColumnSelectionInterval(column0, column); } // } else if (metaDown) { // column0 = point.getX(); // // Note: ctrl-select now handled in // // table.changeSelection // table.setColumnSelectionInterval(column0, column0); } else { column0 = point.getX(); table.setColumnSelectionInterval(column0, column0); } renderSelection(); } } } private void onPointerUp(PointerEvent e) { Event.releaseCapture(grid.getElement()); isMouseDown = false; boolean rightClick = (e.isRightClick()); if (rightClick) { if (!app.letShowPopupMenu()) { return; } GPoint p = table.getIndexFromPixel( SpreadsheetMouseListenerW.getAbsoluteX(e.getWrappedEvent(), app), SpreadsheetMouseListenerW.getAbsoluteY(e.getWrappedEvent(), app)); if (p == null) { return; } // if click is outside current selection then change selection if (p.getY() < table.minSelectionRow || p.getY() > table.maxSelectionRow || p.getX() < table.minSelectionColumn || p.getX() > table.maxSelectionColumn) { // switch to column selection mode and select column if (table .getSelectionType() != MyTableInterface.COLUMN_SELECT) { table.setSelectionType(MyTableInterface.COLUMN_SELECT); } // selectNone(); table.setColumnSelectionInterval(p.getX(), p.getX()); renderSelection(); } showContextMenu(e.getX(), e.getY(), true); } // left click if (doColumnResize) { // If column resize has happened, resize all other selected columns Log.debug("doing column resize"); int columnWidth = table.getColumnWidth(resizingColumn); // Log.debug("doRowResiz for selection: " + rowHeight); // Log.debug("min/max " + table.minSelectionRow + " , " + // table.maxSelectionRow); if (table.minSelectionColumn != -1 && table.maxSelectionColumn != -1 && (table.maxSelectionColumn - table.minSelectionColumn > 0)) { if (table.isSelectAll()) { table.setColumnWidth(columnWidth); } else { for (int col = table.minSelectionColumn; col <= table.maxSelectionColumn; col++) { Log.debug("setting column, width: " + col + " , " + columnWidth); table.setColumnWidth(col, columnWidth); } } } table.repaint(); table.renderSelectionDeferred(); doColumnResize = false; } } private void onPointerMove(PointerEvent e) { // Show resize cursor when mouse is over a row boundary HumanInputEvent<?> event = e.getWrappedEvent(); GPoint p = new GPoint( EventUtil.getTouchOrClickClientX(event), EventUtil.getTouchOrClickClientY(event)); int r = this.getResizingColumn(p, getBoundary(e.getType())); if (r >= 0 && !getCursor().equals(Style.Cursor.COL_RESIZE.getCssName())) { setColumnResizeCursor(); } else if (!getCursor().equals(Style.Cursor.DEFAULT.getCssName())) { setDefaultCursor(); } // DRAG if (isMouseDown) { if (e.isRightClick()) { return; } int x = SpreadsheetMouseListenerW.getAbsoluteX(e.getWrappedEvent(), app); int y = SpreadsheetMouseListenerW.getAbsoluteY(e.getWrappedEvent(), app); // Handle mouse drag if (resizingColumn >= 0) { // Resize a column int newWidth = x - mouseXOffset; if (newWidth > 0) { table.setColumnWidth(resizingColumn, newWidth); // flag to resize all selected rows on mouse release doColumnResize = true; table.repaint(); renderSelection(); } } else { // Select a column GPoint point = table.getIndexFromPixel(x, y); if (point != null) { int column = point.getX(); if (column0 == -1) { column0 = column; } table.setColumnSelectionInterval(column0, column); renderSelection(); } } } } @Override public void onDoubleClick(DoubleClickEvent event) { // TODO Auto-generated method stub } @Override public void onClick(ClickEvent event) { // TODO Auto-generated method stub } @Override public void onKeyDown(KeyDownEvent event) { // TODO Auto-generated method stub } @Override public void onTouchEnd(TouchEndEvent event) { longTouchManager.cancelTimer(); event.preventDefault(); PointerEvent e = PointerEvent.wrapEvent(event, ZeroOffset.instance); onPointerUp(e); CancelEventTimer.touchEventOccured(); } @Override public void onTouchMove(TouchMoveEvent event) { event.preventDefault(); PointerEvent e = PointerEvent.wrapEvent(event, ZeroOffset.instance); if (doColumnResize) { // resizing a column cancel long touch longTouchManager.cancelTimer(); } else { longTouchManager.rescheduleTimerIfRunning(this, e.getX(), e.getY(), false); } onPointerMove(e); CancelEventTimer.touchEventOccured(); } @Override public void onTouchStart(TouchStartEvent event) { event.preventDefault(); PointerEvent e = PointerEvent.wrapEvent(event, ZeroOffset.instance); longTouchManager.scheduleTimer(this, e.getX(), e.getY()); onPointerDown(e); CancelEventTimer.touchEventOccured(); } @Override public void handleLongTouch(int x, int y) { showContextMenu(x, y, false); } private void showContextMenu(int x, int y, boolean relative) { if (!app.letShowPopupMenu()) { return; } SpreadsheetContextMenuW contextMenu = ((GuiManagerW) app .getGuiManager()).getSpreadsheetContextMenu(table); GPopupMenuW popup = (GPopupMenuW) contextMenu.getMenuContainer(); if (relative) { popup.show(grid, x, y); } else { popup.show(new GPoint(x, y)); } } }