public void appendText(String text, long type) { if (m_Text.isDisposed()) return; // This is needed because the text control (on Windows) stores newlines // as \r\n and selections and character counts will get out of synch if // we // work in the text control but reason about the text and they have // different newlines. // (We still use \n everywhere else as the newline marker because that's // what Soar uses) text = text.replaceAll(AbstractView.kLineSeparator, AbstractView.kSystemLineSeparator); boolean show = true; int recordIndex = -1; if (m_FilteringEnabled) { // Record the text in our master filter document. // Also decide if we should show this particular line of text. recordIndex = m_FilterDoc.addRecord(text, false, type); show = m_FilterDoc.show(type); } if (show) { appendTextInternal(text, false, recordIndex); // Decide if we have caused the window to scroll or not if (m_LastTopIndex != m_Text.getTopIndex()) { scrolled(); m_LastTopIndex = m_Text.getTopIndex(); } } }
/** * Runs a runnable, which executes some code. The top visible line number of the text field is * remembered, and after the runnable has finished, the top visible line is restored is set to the * remembered number. * * @param runnable */ protected void doTxfieldActionPreservingVisibleLines(final Runnable runnable, Text textfield) { final Display display = textfield.getDisplay(); final int topIndex = textfield.getTopIndex(); runnable.run(); new Thread() { @Override public void run() { try { Thread.sleep(20); } catch (InterruptedException e) { LogUtil.logError(e); } Runnable r = new Runnable() { @Override public void run() { txtInputText.setTopIndex(topIndex); } }; display.syncExec(r); }; }.start(); }
// Returns the line we clicked on based on mouse coordinates public int getLine(int mouseY) { int topLine = m_Text.getTopIndex(); int lineHeight = m_Text.getLineHeight(); int screenLine = mouseY / lineHeight; int line = topLine + screenLine; if (line > m_Text.getLineCount()) return -1; return line; }
/** Expand/contract all blocks currently on screen */ public void expandPage(boolean state) { // Get all the information about which part of the text window is // visible int topLine = m_Text.getTopIndex(); int lineHeight = m_Text.getLineHeight(); int visibleLines = m_Text.getClientArea().height / lineHeight; int lastLine = Math.min(m_Text.getLineCount(), m_Text.getTopIndex() + visibleLines); boolean atBottom = (lastLine == m_Text.getLineCount()); // Start with the first block that starts at topLine or includes // topLine. Block topBlock = m_FoldingDoc.getBlockByLineNumber(topLine); Block bottomBlock = m_FoldingDoc.getBlockByLineNumber(lastLine); if (topBlock == null) return; // Stop redrawing while we expand/collapse everything then turn it back // on setRedraw(false); // If the lastLine is after the bottom block, use the last block in the // document if (bottomBlock == null) bottomBlock = m_FoldingDoc.getBlock(m_FoldingDoc.getNumberBlocks() - 1); int topIndex = topBlock.getIndex(); int bottomIndex = bottomBlock.getIndex(); for (int i = topIndex; i <= bottomIndex; i++) { Block block = m_FoldingDoc.getBlock(i); m_FoldingDoc.expandBlock(block, state); } // If the selection was set to the bottom before we expanded make sure // it stays there after the expansion. if (state && atBottom) scrollBottom(); // Redraw everything setRedraw(true); }
protected void iconBarMouseClick(MouseEvent e) { // Make sure the text control is properly initialized if (m_Text.getLineHeight() == 0) return; int topLine = m_Text.getTopIndex(); int lineHeight = m_Text.getLineHeight(); int line = (e.y / lineHeight) + topLine; // By using the "getBlockByLineNumber" method we get either the start // of a block or the block that encloses this line. // This means that clicking on the line beneath an expanded block will // cause it to collapse. I think that's a nice feature, but if we'd // rather // not have that happen switch this to getBlockStartsAtLineNumber() and // only clicks right on the icon will cause behavior. Block block = m_FoldingDoc.getBlockByLineNumber(line); if (block == null) return; m_FoldingDoc.expandBlock(block, !block.isExpanded()); m_IconBar.redraw(); }
public FoldingText(Composite parent) { m_Container = new Composite(parent, 0); // The icon bar is used to paint the "+" signs. It is double-buffered or // we'll get a little flicker effect because we repaint it on a timer. m_IconBar = new Canvas(m_Container, SWT.DOUBLE_BUFFERED); m_Text = new Text(m_Container, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.READ_ONLY); m_DrawingDisabled = false; GridLayout layout = new GridLayout(); layout.numColumns = 2; m_Container.setLayout(layout); GridData data1 = new GridData(GridData.FILL_VERTICAL); data1.widthHint = 13; m_IconBar.setLayoutData(data1); GridData data2 = new GridData(GridData.FILL_BOTH); m_Text.setLayoutData(data2); m_IconBar.addPaintListener( new PaintListener() { public void paintControl(PaintEvent e) { paintIcons(e); } }); m_IconBar.setBackground(m_IconBar.getDisplay().getSystemColor(SWT.COLOR_WHITE)); m_IconBar.addMouseListener( new MouseAdapter() { public void mouseUp(MouseEvent e) { iconBarMouseClick(e); } }); // Think we'll need this so we update the icons while we're scrolling m_Text .getVerticalBar() .addSelectionListener( new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { scrolled(); } }); // The user can trigger a scroll in the text window in other ways than // grabbing the scroll bar thumb and moving it so we // need to detect those too. Listener listener = new Listener() { int lastIndex = m_Text.getTopIndex(); public void handleEvent(Event e) { int index = m_Text.getTopIndex(); if (index != lastIndex) { lastIndex = index; scrolled(); } } }; // NOTE: Only detects scrolling by the user and not quite correct if you // drag out of the window // (This code came from the SWT web site itself) m_Text.addListener(SWT.MouseDown, listener); m_Text.addListener(SWT.MouseMove, listener); m_Text.addListener(SWT.MouseUp, listener); m_Text.addListener(SWT.KeyDown, listener); m_Text.addListener(SWT.KeyUp, listener); m_Text.addListener(SWT.Resize, listener); // Because the above tests aren't always correct we'll also use a timer // to redraw the icon bar // at a slow rate. This ensures the icons are correct after the delay // has passed in all cases. // (We're using a slow timer rather than calling redraw after each line // of text is added, for instance, // to boost overall performance). In practice this timer is correcting a // minor cosmetic issue so it's // not worth trading real performance for. periodicRepaint(500); m_LastTopIndex = m_Text.getTopIndex(); }
protected void paintIcons(PaintEvent e) { // Check if we've turned off redraws if (m_DrawingDisabled) return; GC gc = e.gc; Rectangle client = m_IconBar.getClientArea(); // Make sure the text control is properly initialized if (m_Text.getLineHeight() == 0) return; // Get all the information about which part of the text window is // visible int topLine = m_Text.getTopIndex(); int lineHeight = m_Text.getLineHeight(); int visibleLines = m_Text.getClientArea().height / lineHeight; int lastLine = Math.min(m_Text.getLineCount(), m_Text.getTopIndex() + visibleLines); // Start with the first block that starts at topLine or includes // topLine. Block topBlock = m_FoldingDoc.getBlockByLineNumber(topLine); int blockCount = m_FoldingDoc.getNumberBlocks(); if (topBlock == null) return; int blockIndex = topBlock.getIndex(); int outerSize = 9; int innerSize = 6; int offset = (outerSize - innerSize) / 2 + 1; Color gray = m_IconBar.getDisplay().getSystemColor(SWT.COLOR_GRAY); Color black = m_IconBar.getDisplay().getSystemColor(SWT.COLOR_BLACK); // Go through each block in turn until we're off the bottom of the // screen // or at the end of the list of blocks drawing icons while (blockIndex != -1 && blockIndex < blockCount) { Block block = m_FoldingDoc.getBlock(blockIndex); int line = block.getStart(); // Once we drop off the bottom of the screen we're done if (line >= lastLine) break; int pos = line - topLine; int y = pos * lineHeight + (lineHeight / 2) - (outerSize / 2) - 1; int x = 1; boolean expanded = block.isExpanded(); if (block.canExpand()) { gc.drawRectangle(x, y, x + outerSize, x + outerSize); // Start with a - sign int y1 = y + 1 + (outerSize / 2); gc.drawLine(x + offset, y1, x + offset + innerSize, y1); if (!expanded) { // If not expanded turn the - into a + int x1 = x + 1 + (outerSize / 2); gc.drawLine(x1, y + offset, x1, y + offset + innerSize); } else { // If expanded draw a line to show what is in the expanded // area gc.setForeground(gray); int x1 = x + 1 + (outerSize / 2); int yTop = y + outerSize + 2; int yBottom = y + ((block.getSize() - 1) * lineHeight) + (outerSize / 2); gc.drawLine(x1, yTop, x1, yBottom); gc.drawLine(x1, yBottom, client.width - 1, yBottom); gc.setForeground(black); } } blockIndex++; } }
protected void appendSubTextInternal(String text, boolean autoExpand, int recordIndex) { Block last = m_FoldingDoc.m_LastBlock; if (last == null) { // This is an odd situation where we're adding subtext but have no // supertext to append to. // It probably will never occur, but if it does we'll add a blank // super text block just // to get us going. last = new Block(true, recordIndex); last.appendLine(""); last.setExpand(autoExpand); m_FoldingDoc.addBlock(last); appendTextToWidget(m_Text, ""); m_IconBar.redraw(); } if (!last.canExpand()) { // Need to remove the last line from the block and move it to a new // block // which can expand and then proceed with the addition to this new // block // Thus we ensure that "canExpand" items have at least one child. int size = last.getSize(); if (size == 1) { // If the last block only contains exactly one line we can // convert it safely // to a block which does expand. This way we also preserve the // "all blocks contain // at least one line" rule which makes the code simpler. last.setCanExpand(true); last.setExpand(autoExpand); } else if (size > 1) { // NOTE: Blocks should always have at least one line so these // calls // should never fail (if they do it's a programming error // somewhere else that // allowed an empty block to be created). String lastLine = last.getLastLine(); last.removeLastLine(); last = new Block(true, recordIndex); last.appendLine(lastLine); last.setExpand(autoExpand); m_FoldingDoc.addBlock(last); } else if (size == 0) { throw new IllegalStateException("Last block should not be empty"); } // There's no change to the text sprite (because the line is just // moved between logical blocks) // but we will need to draw the icons to show there's a new block. m_IconBar.redraw(); } last.appendLine(text); if (last.isExpanded()) { appendTextToWidget(m_Text, text); // Decide if we have caused the window to scroll or not if (!autoExpand && (m_LastTopIndex != m_Text.getTopIndex())) { scrolled(); m_LastTopIndex = m_Text.getTopIndex(); } } }