ttable navigation minimally working
[fanfix.git] / src / jexer / TTableWidget.java
index 32f3e55ac5d95926b002c02c256e35810cbd3469..bd53977341d93799f93d6fde43903a24712fb52c 100644 (file)
@@ -31,8 +31,10 @@ package jexer;
 import java.util.ArrayList;
 import java.util.List;
 
+import jexer.bits.CellAttributes;
 import jexer.event.TKeypressEvent;
 import jexer.event.TMenuEvent;
+import jexer.event.TResizeEvent;
 import jexer.menu.TMenu;
 import static jexer.TKeypress.*;
 
@@ -125,6 +127,11 @@ public class TTableWidget extends TWidget {
      */
     private boolean showRowLabels = true;
 
+    /**
+     * If true, show the column labels as the first row.
+     */
+    private boolean showColumnLabels = true;
+
     /**
      * Column represents a column of cells.
      */
@@ -143,7 +150,7 @@ public class TTableWidget extends TWidget {
         /**
          * Column label.
          */
-        private String label;
+        private String label = "";
 
         /**
          * The border for this column.
@@ -302,7 +309,7 @@ public class TTableWidget extends TWidget {
             this.column = column;
             this.row = row;
 
-            field = addField(0, 0, width - 1, false);
+            field = addField(0, 0, width, false);
             field.setEnabled(false);
             field.setBackgroundChar(' ');
         }
@@ -382,6 +389,8 @@ public class TTableWidget extends TWidget {
                 field.setInactiveColorKey("ttable.inactive");
             }
 
+            assert (isVisible() == true);
+
             super.draw();
         }
 
@@ -455,6 +464,18 @@ public class TTableWidget extends TWidget {
         activate(columns.get(selectedColumn).get(selectedRow));
 
         alignGrid();
+
+        // Set the menu to match the flags.
+        getApplication().getMenuItem(TMenu.MID_TABLE_VIEW_ROW_LABELS).
+                setChecked(showRowLabels);
+        getApplication().getMenuItem(TMenu.MID_TABLE_VIEW_COLUMN_LABELS).
+                setChecked(showColumnLabels);
+        getApplication().getMenuItem(TMenu.MID_TABLE_VIEW_HIGHLIGHT_ROW).
+                setChecked(highlightRow);
+        getApplication().getMenuItem(TMenu.MID_TABLE_VIEW_HIGHLIGHT_COLUMN).
+                setChecked(highlightColumn);
+
+
     }
 
     // ------------------------------------------------------------------------
@@ -526,6 +547,18 @@ public class TTableWidget extends TWidget {
         alignGrid();
     }
 
+    /**
+     * Handle widget resize events.
+     *
+     * @param event resize event
+     */
+    @Override
+    public void onResize(final TResizeEvent event) {
+        super.onResize(event);
+
+        alignGrid();
+    }
+
     /**
      * Handle posted menu events.
      *
@@ -534,6 +567,18 @@ public class TTableWidget extends TWidget {
     @Override
     public void onMenu(final TMenuEvent menu) {
         switch (menu.getId()) {
+        case TMenu.MID_TABLE_VIEW_ROW_LABELS:
+            showRowLabels = getApplication().getMenuItem(menu.getId()).getChecked();
+            break;
+        case TMenu.MID_TABLE_VIEW_COLUMN_LABELS:
+            showColumnLabels = getApplication().getMenuItem(menu.getId()).getChecked();
+            break;
+        case TMenu.MID_TABLE_VIEW_HIGHLIGHT_ROW:
+            highlightRow = getApplication().getMenuItem(menu.getId()).getChecked();
+            break;
+        case TMenu.MID_TABLE_VIEW_HIGHLIGHT_COLUMN:
+            highlightColumn = getApplication().getMenuItem(menu.getId()).getChecked();
+            break;
         case TMenu.MID_TABLE_BORDER_NONE:
         case TMenu.MID_TABLE_BORDER_ALL:
         case TMenu.MID_TABLE_BORDER_RIGHT:
@@ -583,12 +628,51 @@ public class TTableWidget extends TWidget {
         default:
             super.onMenu(menu);
         }
+
+        alignGrid();
     }
 
     // ------------------------------------------------------------------------
     // TWidget ----------------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Draw the table row/column labels, and borders.
+     */
+    @Override
+    public void draw() {
+        CellAttributes labelColor = getTheme().getColor("ttable.label");
+        CellAttributes labelColorSelected = getTheme().getColor("ttable.label.selected");
+        CellAttributes borderColor = getTheme().getColor("ttable.border");
+
+        // Column labels.
+        if (showColumnLabels == true) {
+            for (int i = left; i < columns.size(); i++) {
+                if (columns.get(i).get(top).isVisible() == false) {
+                    break;
+                }
+                putStringXY(columns.get(i).get(top).getX(), 0,
+                    String.format(" %-6s ", columns.get(i).label),
+                    (i == selectedColumn ? labelColorSelected : labelColor));
+            }
+        }
+
+        // Row labels.
+        if (showRowLabels == true) {
+            for (int i = top; i < rows.size(); i++) {
+                if (rows.get(i).get(left).isVisible() == false) {
+                    break;
+                }
+                putStringXY(0, rows.get(i).get(left).getY(),
+                    String.format(" %-6s ", rows.get(i).label),
+                    (i == selectedRow ? labelColorSelected : labelColor));
+            }
+        }
+
+        // Now draw the window borders.
+        super.draw();
+    }
+
     // ------------------------------------------------------------------------
     // TTable -----------------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -651,23 +735,202 @@ public class TTableWidget extends TWidget {
         return totalWidth;
     }
 
+    /**
+     * Get the full vertical height of this table.
+     *
+     * @return the height required to render the entire table
+     */
+    public int getMaximumHeight() {
+        int totalHeight = 0;
+        if (showColumnLabels == true) {
+            // For now, all column labels are 1 cell tall.  TODO: make this
+            // adjustable.
+            totalHeight += 1;
+        }
+        for (Cell cell: getSelectedColumn().cells) {
+            totalHeight += cell.getHeight();
+            // TODO: handle top/bottom borders.
+        }
+        return totalHeight;
+    }
+
     /**
      * Align the grid so that the selected cell is fully visible.
      */
     private void alignGrid() {
+
+        /*
+         * We start by assuming that all cells are visible, and then mark as
+         * invisible those that are outside the viewable area.
+         */
+        for (int x = 0; x < columns.size(); x++) {
+            for (int y = 0; y < rows.size(); y++) {
+                rows.get(y).cells.get(x).setVisible(true);
+            }
+        }
+
+        // Adjust X locations to be visible -----------------------------------
+
         // Determine if we need to shift left or right.
-        int width = getMaximumWidth();
         int leftCellX = 0;
         if (showRowLabels == true) {
             // For now, all row labels are 8 cells wide.  TODO: make this
             // adjustable.
             leftCellX += 8;
         }
-        // TODO
+        Row row = getSelectedRow();
+        Cell selectedColumnCell = null;
+        for (int i = 0; i < row.cells.size(); i++) {
+            if (i == selectedColumn) {
+                selectedColumnCell = row.cells.get(i);
+                break;
+            }
+            leftCellX += row.cells.get(i).getWidth() + 1;
+        }
+        // There should always be a selected column.
+        assert (selectedColumnCell != null);
+
+        int excessWidth = leftCellX + selectedColumnCell.getWidth() + 1 - getWidth();
+        if (excessWidth > 0) {
+            leftCellX -= excessWidth;
+        }
+        if (leftCellX < 0) {
+            if (showRowLabels == true) {
+                leftCellX = 8;
+            } else {
+                leftCellX = 0;
+            }
+        }
+
+        /*
+         * leftCellX now contains the basic left offset necessary to draw the
+         * cells such that the selected cell (column) is fully visible within
+         * this widget's given width.  Or, if the widget is too narrow to
+         * display the full cell, leftCellX is 0 or 8.
+         *
+         * Now reset all of the X positions of the other cells so that the
+         * selected cell X is leftCellX.
+         */
+        for (int y = 0; y < rows.size(); y++) {
+            // All cells to the left of selected cell.
+            int newCellX = leftCellX;
+            left = selectedColumn;
+            for (int x = selectedColumn - 1; x >= 0; x--) {
+                newCellX -= rows.get(y).cells.get(x).getWidth() + 1;
+                if (newCellX - rows.get(y).cells.get(x).getWidth() + 1 >= 0) {
+                    rows.get(y).cells.get(x).setVisible(true);
+                    rows.get(y).cells.get(x).setX(newCellX);
+                    left--;
+                } else {
+                    // This cell won't be visible.
+                    rows.get(y).cells.get(x).setVisible(false);
+                }
+            }
+
+            // Selected cell.
+            rows.get(y).cells.get(selectedColumn).setX(leftCellX);
+            assert (rows.get(y).cells.get(selectedColumn).isVisible());
 
+            // All cells to the right of selected cell.
+            newCellX = leftCellX + selectedColumnCell.getWidth() + 1;
+            for (int x = selectedColumn + 1; x < columns.size(); x++) {
+                if (newCellX <= getWidth()) {
+                    rows.get(y).cells.get(x).setVisible(true);
+                    rows.get(y).cells.get(x).setX(newCellX);
+                } else {
+                    // This cell won't be visible.
+                    rows.get(y).cells.get(x).setVisible(false);
+                }
+                newCellX += rows.get(y).cells.get(x).getWidth() + 1;
+            }
+        }
 
-        // TODO: determine shift up/down
+        // Adjust Y locations to be visible -----------------------------------
+        // The same logic as above, but applied to the column Y.
 
+        // Determine if we need to shift up or down.
+        int topCellY = 0;
+        if (showColumnLabels == true) {
+            // For now, all column labels are 1 cell high.  TODO: make this
+            // adjustable.
+            topCellY += 1;
+        }
+        Column column = getSelectedColumn();
+        Cell selectedRowCell = null;
+        for (int i = 0; i < column.cells.size(); i++) {
+            if (i == selectedRow) {
+                selectedRowCell = column.cells.get(i);
+                break;
+            }
+            topCellY += column.cells.get(i).getHeight();
+            // TODO: if a border is selected, add 1 to topCellY.
+        }
+        // There should always be a selected row.
+        assert (selectedRowCell != null);
+
+        int excessHeight = topCellY + selectedRowCell.getHeight() - getHeight() - 1;
+        if (showColumnLabels == true) {
+            excessHeight += 1;
+        }
+        if (excessHeight > 0) {
+            topCellY -= excessHeight;
+        }
+        if (topCellY < 0) {
+            if (showColumnLabels == true) {
+                topCellY = 1;
+            } else {
+                topCellY = 0;
+            }
+        }
+
+        /*
+         * topCellY now contains the basic top offset necessary to draw the
+         * cells such that the selected cell (row) is fully visible within
+         * this widget's given height.  Or, if the widget is too short to
+         * display the full cell, topCellY is 0 or 1.
+         *
+         * Now reset all of the Y positions of the other cells so that the
+         * selected cell Y is topCellY.
+         */
+        for (int x = 0; x < columns.size(); x++) {
+
+            if (columns.get(x).get(0).isVisible() == false) {
+                // This column won't be visible as determined by the checks
+                // above, just continue to the next.
+                continue;
+            }
+
+            // All cells above the selected cell.
+            int newCellY = topCellY;
+            top = selectedRow;
+            for (int y = selectedRow - 1; y >= 0; y--) {
+                newCellY -= rows.get(y).cells.get(x).getHeight();
+                if (newCellY >= (showColumnLabels == true ? 1 : 0)) {
+                    rows.get(y).cells.get(x).setVisible(true);
+                    rows.get(y).cells.get(x).setY(newCellY);
+                    top--;
+                } else {
+                    // This cell won't be visible.
+                    rows.get(y).cells.get(x).setVisible(false);
+                }
+            }
+
+            // Selected cell.
+            columns.get(x).cells.get(selectedRow).setY(topCellY);
+
+            // All cells below the selected cell.
+            newCellY = topCellY + selectedRowCell.getHeight();
+            for (int y = selectedRow + 1; y < rows.size(); y++) {
+                if (newCellY <= getHeight()) {
+                    rows.get(y).cells.get(x).setVisible(true);
+                    rows.get(y).cells.get(x).setY(newCellY);
+                } else {
+                    // This cell won't be visible.
+                    rows.get(y).cells.get(x).setVisible(false);
+                }
+                newCellY += rows.get(y).cells.get(x).getHeight();
+            }
+        }
 
     }