/* * ColumnBlock.java * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2010 Anshal Shukla * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.textarea; import java.util.Vector; import org.gjt.sp.jedit.buffer.JEditBuffer; import org.gjt.sp.jedit.textarea.Selection.Rect; public class ColumnBlock extends Rect implements Node { private Node parent; private Vector<Node> children = new Vector<Node>(); private Vector<ColumnBlockLine> lines = new Vector<ColumnBlockLine>(); float columnBlockWidth; private boolean tabSizesDirty = true; private JEditBuffer buffer; private boolean isDirty; @Override //{{{ addChild() method public void addChild(Node node) { // must add the children in sorted order ColumnBlock block = (ColumnBlock) node; ColumnBlock blockBelow = searchChildren(block.startLine); if (blockBelow != null) { if (blockBelow.isLineWithinThisBlock(block.endLine) >= 0) { throw new IllegalArgumentException("Overlapping column blocks: " + block + " \n&\n" + blockBelow); } int index = children.indexOf(blockBelow); children.add(index, node); } else { children.add(node); } }//}}} @Override //{{{ getChildren() method public Vector<Node> getChildren() { return children; }//}}} @Override //{{{ getParent() method public Node getParent() { return parent; }//}}} //{{{ setWidth() method public void setWidth(int width) { columnBlockWidth = width; }//}}} //{{{ setParent() method public void setParent(Node parent) { this.parent = parent; }//}}} //{{{ setLines() method public void setLines(Vector<ColumnBlockLine> lines) { this.lines = lines; }//}}} //{{{ getLines() method public Vector<ColumnBlockLine> getLines() { return lines; }//}}} //{{{ ColumnBlock() method public ColumnBlock() { }//}}} //{{{ ColumnBlock() method public ColumnBlock(JEditBuffer buffer, int startLine, int startColumn, int endLine, int endColumn) { super(buffer, startLine, startColumn, endLine, endColumn); this.buffer = buffer; }//}}} //{{{ ColumnBlock() method public ColumnBlock(JEditBuffer buffer, int startLine, int endLine) { this.startLine = startLine; this.endLine = endLine; this.buffer = buffer; }//}}} //{{{ getStartLine() method @Override public int getStartLine() { return startLine; }//}}} //{{{ getEndLine() method @Override public int getEndLine() { return endLine; }//}}} //{{{ getColumnWidth() method public int getColumnWidth() { return (int) columnBlockWidth; }//}}} //{{{ isLineWithinThisBlock() method public int isLineWithinThisBlock(int line) { if (line < startLine) { return line - startLine; } else if (line > endLine) { return line - endLine; } else { return 0; } }//}}} //{{{ getContainingBlock() method public ColumnBlock getContainingBlock(int line, int offset) { ColumnBlock retBlock = null; if (line >= startLine && line <= endLine) { int relativeOffset = offset - buffer.getLineStartOffset(line); if (lines != null && !lines.isEmpty()) { ColumnBlockLine blockLine = lines.get(line - startLine); if (blockLine.getColumnEndIndex() >= relativeOffset && blockLine.getColumnStartIndex() <= relativeOffset) { retBlock = this; } } if (retBlock == null && children != null && !children.isEmpty()) { ColumnBlock block = searchChildren(line); if (block != null && block.isLineWithinThisBlock(line) == 0) { retBlock = block.getContainingBlock(line, offset); } } } return retBlock; }//}}} //{{{ getContainingBlock() method public ColumnBlock getColumnBlock(int line, int offset) { if (isDirty) { return null; } // int tabSize=-5; synchronized (buffer.columnBlockLock) { ColumnBlock colBlock = null; if (line >= startLine && line <= endLine) { if (lines != null && !lines.isEmpty()) { ColumnBlockLine blockLine = lines.get(line - startLine); if (blockLine.getColumnEndIndex() + buffer .getLineStartOffset(line) == offset) { // tabSize = // blockLine.getTabSize(); colBlock = this; } } if (colBlock == null && children != null && !children.isEmpty()) { ColumnBlock block = searchChildren(line, 0, children.size() - 1); if (block == null || block.isLineWithinThisBlock(line) != 0) { throwException(offset, line); } // tabSize = // block.getColumnBlock(line,offset); colBlock = block.getColumnBlock(line, offset); } } // if(tabSize<0) if (colBlock == null) throwException(offset, line); // return tabSize; return colBlock; } }//}}} //{{{ searchChildren() method public ColumnBlock searchChildren(int line) { if (children != null && !children.isEmpty()) { return searchChildren(line, 0, children.size() - 1); } else { return null; } }//}}} //{{{ searchChildren() method /* * binary search on a sorted list searches the children for one * containing the line no. line returns an exact match or the closest * column block just below this line use isLineWithinThisBlock on the * column block returned by this method to determine whether there was * an exact match */ private ColumnBlock searchChildren(int line, int startIndex, int stopIndex) { if (children != null) { if (startIndex > stopIndex) { // no exact match found return the nearest // column block just below this line return (ColumnBlock) children.get(startIndex); } int currentSearchIndex = (startIndex + stopIndex) / 2; int found = ((ColumnBlock) children.get(currentSearchIndex)) .isLineWithinThisBlock(line); if (found == 0) { return (ColumnBlock) children.get(currentSearchIndex); } else if (found > 0) { if (children.size() - 1 > currentSearchIndex) { return searchChildren(line, currentSearchIndex + 1, stopIndex); } else { return null; } } else if (found < 0) { if (currentSearchIndex > 0) { return searchChildren(line, startIndex, currentSearchIndex - 1); } else { // no exact match found return the // nearest column block just below this // line return (ColumnBlock) children.get(0); } } } return null; }//}}} //{{{ toString() method public String toString() { StringBuilder buf = new StringBuilder(); buf.append("ColumnBlock[startLine : ").append(startLine).append(" ,endLine : ").append(endLine) .append(" ,columnBlockWidth : ").append(columnBlockWidth).append("] LINES:"); for (int i = 0; i < lines.size(); i++) { buf.append('\n'); buf.append("LINE ").append(i).append(':').append(lines.elementAt(i)); } for (int i = 0; i < children.size(); i++) { buf.append('\n'); buf.append("CHILD ").append(i).append(':').append(children.elementAt(i)); } return buf.toString(); }//}}} //{{{ throwException() method private void throwException(int offset, int line) { throw new IllegalArgumentException("{ELASTIC TABSTOP}CORRUPT DATA@{" + System.currentTimeMillis() + "} & Thread : " + Thread.currentThread().getName() + " :Cannot find the size for tab at offset " + (offset - buffer.getLineStartOffset(line)) + "in line " + line + "while searching in \n " + this); }//}}} //{{{ setDirtyStatus() method public void setDirtyStatus(boolean status) { synchronized (buffer.columnBlockLock) { isDirty = status; } }//}}} //{{{ updateLineNo() method public void updateLineNo(int line) { // Things to do in this method // update line no. in this column block // update column block lines in this column block // call this method on all children startLine += line; endLine += line; for (int i = 0; i < lines.size(); i++) { lines.elementAt(i).updateLineNo(line); } for (int i = 0; i < children.size(); i++) { ((ColumnBlock) children.elementAt(i)).updateLineNo(line); } }//}}} //{{{ updateColumnBlockLineOffset() method public void updateColumnBlockLineOffset(int line, int offsetAdd, boolean increaseStartOffset) { if (line >= startLine && line <= endLine) { if (lines != null && !lines.isEmpty()) { ColumnBlockLine blockLine = lines.get(line - startLine); if (increaseStartOffset) { blockLine.colStartIndex += offsetAdd; } blockLine.colEndIndex += offsetAdd; } if (children != null && !children.isEmpty()) { ColumnBlock block = searchChildren(line); if (block != null && block.isLineWithinThisBlock(line) == 0) { block.updateColumnBlockLineOffset(line, offsetAdd, true); } } } }//}}} //{{{ setTabSizeDirtyStatus() method /* * tab sizes become dirty on font changes or when char is added or * deleted inside ColumnBlock they become clean once they get calculated * again inside the tab expander */ public void setTabSizeDirtyStatus(boolean dirty, boolean recursive) { tabSizesDirty = dirty; if (recursive && children != null && !children.isEmpty()) { for (int i = 0; i < children.size(); i++) { ((ColumnBlock) children.elementAt(i)).setTabSizeDirtyStatus(true, true); } } }//}}} //{{{ areTabSizesDirty() method public boolean areTabSizesDirty() { return tabSizesDirty; }//}}} }