/* * Copyright 2003-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.nodeEditor.cellLayout; import jetbrains.mps.editor.runtime.TextBuilderImpl; import jetbrains.mps.editor.runtime.style.DefaultBaseLine; import jetbrains.mps.editor.runtime.style.StyleAttributes; import jetbrains.mps.editor.runtime.style.TableComponent; import jetbrains.mps.openapi.editor.TextBuilder; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.openapi.editor.cells.EditorCell_Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; /** * Sergey.Sinchuk, Oct 30, 2009 */ public class CellLayout_Table extends AbstractCellLayout { private CellLayout_Table myParentLayout = null; private int[] myColumnAscents = null; private int[] myColumnDescents = null; @Override public boolean canBeFolded() { return true; } private EditorCell_Collection findCollection(EditorCell cell, TableComponent tc) { if (cell instanceof EditorCell_Collection) { EditorCell_Collection col = (EditorCell_Collection) cell; jetbrains.mps.openapi.editor.cells.CellLayout l = col.getCellLayout(); if (l instanceof CellLayout_Table) { TableComponent comp = col.getStyle().get(StyleAttributes.TABLE_COMPONENT); if (comp == tc) { return col; } } for (EditorCell c : col) { EditorCell_Collection result = findCollection(c, tc); if (result != null) { return result; } } } return null; } private Iterable<EditorCell_Collection> getRowsCollection(EditorCell_Collection editorCells) { TableComponent tc = editorCells.getStyle().get(StyleAttributes.TABLE_COMPONENT); List<EditorCell_Collection> result = new LinkedList<EditorCell_Collection>(); for (EditorCell childCell : editorCells) { EditorCell_Collection col = findCollection(childCell, tc.transpose()); result.add(col); if (col != null) { ((CellLayout_Table) col.getCellLayout()).myParentLayout = this; } } return result; } @Override public void doLayout(EditorCell_Collection editorCells) { if (myParentLayout != null) { doLayoutInner(editorCells); } else { doLayoutOuter(editorCells); } } private void doLayoutOuter(EditorCell_Collection editorCells) { assert myParentLayout == null; final int x = editorCells.getX(); final int y = editorCells.getY(); TableComponent tc = editorCells.getStyle().get(StyleAttributes.TABLE_COMPONENT); Iterable<EditorCell_Collection> rowsCollection = getRowsCollection(editorCells); fillAscentsDescents(rowsCollection, tc); for (EditorCell c : editorCells) { c.relayout(); } int leftMargin = 0; int rightMargin = 0; int gridWidth = 0; Iterator<EditorCell_Collection> rowsIterator = rowsCollection.iterator(); for (EditorCell childCell : editorCells) { EditorCell innerCollection = rowsIterator.next(); if (innerCollection == null) { gridWidth = Math.max(gridWidth, tc.getWidth(childCell)); } else { gridWidth = Math.max(gridWidth, tc.getWidth(innerCollection)); leftMargin = Math.max(leftMargin, tc.getX(innerCollection) - tc.getX(childCell)); rightMargin = Math.max(rightMargin, (tc.getX(childCell) + tc.getWidth(childCell)) - (tc.getX(innerCollection) + tc.getWidth(innerCollection))); } } int currentWidth = leftMargin + gridWidth + rightMargin; int currentHeight = 0; rowsIterator = rowsCollection.iterator(); for (EditorCell childCell : editorCells) { EditorCell innerCollection = rowsIterator.next(); int deltaX; if (innerCollection == null) { deltaX = leftMargin + (gridWidth - tc.getWidth(childCell)) / 2; } else { deltaX = leftMargin - (tc.getX(innerCollection) - tc.getX(childCell)); } int deltaY = currentHeight; childCell.moveTo(x + tc.getX(deltaX, deltaY), y + tc.getY(deltaX, deltaY)); childCell.relayout(); currentHeight += tc.getX(childCell.getHeight(), childCell.getWidth()); } editorCells.setWidth(tc.getX(currentWidth, currentHeight)); editorCells.setHeight(tc.getY(currentWidth, currentHeight)); } private void fillAscentsDescents(Iterable<EditorCell_Collection> rowCollections, TableComponent tc) { List<Integer> columnAscents = new LinkedList<Integer>(); List<Integer> columnDescents = new LinkedList<Integer>(); for (EditorCell_Collection row : rowCollections) { if (row == null) { continue; } ListIterator<Integer> columnAscentsIterator = columnAscents.listIterator(); ListIterator<Integer> columnDescentsIterator = columnDescents.listIterator(); for (EditorCell column : row) { column.relayout(); if (columnAscentsIterator.hasNext()) { columnAscentsIterator.set(Math.max(columnAscentsIterator.next(), tc.transpose().getAccent(column))); } else { columnAscentsIterator.add(tc.transpose().getAccent(column)); } if (columnDescentsIterator.hasNext()) { columnDescentsIterator.set(Math.max(columnDescentsIterator.next(), tc.transpose().getDescent(column))); } else { columnDescentsIterator.add(tc.transpose().getDescent(column)); } } } myColumnAscents = new int[columnAscents.size()]; myColumnDescents = new int[columnDescents.size()]; assert columnAscents.size() == columnDescents.size(); Iterator<Integer> columnAscentsIterator = columnAscents.iterator(); Iterator<Integer> columnDescentsIterator = columnAscents.iterator(); for (int i = 0; i < columnAscents.size(); i++) { myColumnDescents[i] = columnAscentsIterator.next(); myColumnAscents[i] = columnDescentsIterator.next(); } } private void doLayoutInner(EditorCell_Collection editorCells) { assert myParentLayout != null; final int x = editorCells.getX(); final int y = editorCells.getY(); TableComponent tc = editorCells.getStyle().get(StyleAttributes.TABLE_COMPONENT); for (EditorCell c : editorCells) { c.relayout(); } int maxWidth = 0; for (EditorCell childCell : editorCells) { maxWidth = Math.max(maxWidth, tc.getWidth(childCell)); } int currentHeight = 0; int counter = 0; for (EditorCell childCell : editorCells) { int deltaX = (maxWidth - tc.getWidth(childCell)) / 2; int ascent = myParentLayout.myColumnAscents[counter]; int descent = myParentLayout.myColumnDescents[counter]; int cellAscent = tc.getAccent(childCell); int deltaY = currentHeight + (ascent - cellAscent); currentHeight += ascent + descent; childCell.setX(x + tc.getX(deltaX, deltaY)); childCell.setY(y + tc.getY(deltaX, deltaY)); counter++; } // TODO: looks like we don't need it here for "inner" table cells (rows/columns) for (EditorCell c : editorCells) { c.relayout(); } editorCells.setWidth(tc.getX(maxWidth, currentHeight)); editorCells.setHeight(tc.getY(maxWidth, currentHeight)); } @Override public TextBuilder doLayoutText(Iterable<EditorCell> editorCells) { TextBuilder result = new TextBuilderImpl(); for (EditorCell editorCell : editorCells) { result.appendToTheBottom(editorCell.renderText()); } return result; } @Override public int getAscent(EditorCell_Collection editorCells) { for (EditorCell cell : editorCells) { if (cell.getStyle().get(StyleAttributes.BASE_LINE_CELL)) { return cell.getY() - editorCells.getY() + cell.getAscent(); } } DefaultBaseLine bL = editorCells.getStyle().get(StyleAttributes.DEFAULT_BASE_LINE); int result = 0; for (EditorCell cell : editorCells) { result = cell.getAscent(); if (result > 0) { break; } } switch (bL) { case FIRST: // default behavior return result; case CENTER: return Math.max(result, editorCells.getHeight() / 2); case LAST: if (!editorCells.isEmpty()) { EditorCell lastCell = editorCells.lastCell(); return lastCell.getY() - editorCells.getY() + lastCell.getAscent(); } } return 0; } @Override public int getRightInternalInset(EditorCell_Collection editorCell_collection) { return editorCell_collection.isEmpty() ? 0 : editorCell_collection.firstCell().getRightInset(); } public String toString() { return "Vertical"; } }