@Override public final void mousePressed(final MouseEvent e) { if (!isEnabled() || !isFocusable()) return; requestFocusInWindow(); cursor(true); if (SwingUtilities.isMiddleMouseButton(e)) copy(); final boolean marking = e.isShiftDown(); final boolean nomark = !text.marked(); if (SwingUtilities.isLeftMouseButton(e)) { final int c = e.getClickCount(); if (c == 1) { // selection mode if (marking && nomark) text.startMark(); rend.select(scroll.pos(), e.getPoint(), marking); } else if (c == 2) { text.selectWord(); } else { text.selectLine(); } } else if (nomark) { rend.select(scroll.pos(), e.getPoint(), false); } }
/** * Checks if the text has more words to print. * * @param g graphics reference * @return true if the text has more words */ private boolean more(final Graphics g) { // no more words found; quit if (!text.moreWords()) return false; // calculate word width int ww = 0; final int p = text.pos(); while (text.more()) { final int ch = text.next(); // internal special codes... if (ch == 0x02) { font(bfont); } else if (ch == 0x03) { font(dfont); } else { ww += charW(g, ch); } } text.pos(p); // jump to new line if (x + ww > w) { x = off; y += fontH; } wordW = ww; // check if word has been found, and word is still visible return y < h; }
/** Redoes the text. */ final void redo() { if (undo == null) return; text = new BaseXTextTokens(undo.next()); rend.setText(text); text.pos(undo.cursor()); text.setCaret(); }
/** * Sets the output text. * * @param t output text * @param s text size */ public final void setText(final byte[] t, final int s) { // remove invalid characters and compare old with new string int ns = 0; final int ts = text.size(); final byte[] tt = text.text(); boolean eq = true; for (int r = 0; r < s; ++r) { final byte b = t[r]; // support characters, highlighting codes, tabs and newlines if (b >= ' ' || b <= TokenBuilder.MARK || b == 0x09 || b == 0x0A) { t[ns++] = t[r]; } eq &= ns < ts && ns < s && t[ns] == tt[ns]; } eq &= ns == ts; // new text is different... if (!eq) { text = new BaseXTextTokens(Arrays.copyOf(t, ns)); rend.setText(text); scroll.pos(0); } if (undo != null) undo.store(t.length != ns ? Arrays.copyOf(t, ns) : t, 0); SwingUtilities.invokeLater(calc); }
/** * Copies the selected text to the clipboard. * * @return true if text was copied */ final boolean copy() { final String txt = text.copy(); if (txt.isEmpty()) { text.noMark(); return false; } // copy selection to clipboard final Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard(); clip.setContents(new StringSelection(txt), null); return true; }
/** * Returns true if the keyword is found. * * @return result of check */ private boolean found() { if (keyword.isEmpty()) return false; final int sl = keyword.length(); final int wl = text.length(); if (wl < sl) return false; final int p = text.pos(); int s = -1; while (++s != sl) { if (Character.toLowerCase(text.next()) != keyword.charAt(s)) break; } text.pos(p); return s == sl; }
/** * Pastes the specified string. * * @param txt string to be pasted * @return success flag */ final boolean paste(final String txt) { if (txt == null || undo == null) return false; text.pos(text.cursor()); undo.cursor(text.cursor()); if (text.marked()) text.delete(); text.add(txt); undo.store(text.text(), text.cursor()); return true; }
/** * Initializes the renderer. * * @param g graphics reference * @param pos current text position */ private void init(final Graphics g, final int pos) { font = dfont; color = Color.black; syntax.init(); text.init(); x = off; y = off + fontH - pos - 2; if (g != null) g.setFont(font); }
/** * Returns the current vertical cursor position. * * @return new position */ int cursorY() { final int hh = h; h = Integer.MAX_VALUE; final Graphics g = getGraphics(); init(g, 0); while (more(g) && !text.edited()) next(); h = hh; return y - fontH; }
/** Deletes the selected text. */ final void delete() { if (undo == null) return; text.pos(text.cursor()); undo.cursor(text.cursor()); text.delete(); undo.store(text.text(), text.cursor()); text.setCaret(); }
/** * Finishes the current token. * * @return true for new line */ private boolean next() { final int ch = text.curr(); if (ch == TokenBuilder.NLINE || ch == TokenBuilder.HLINE) { x = off; y += fontH >> (ch == TokenBuilder.NLINE ? 0 : 1); return true; } x += wordW; return false; }
/** * Returns the cursor coordinates. * * @return line/column */ int[] pos() { final int hh = h; h = Integer.MAX_VALUE; final Graphics g = getGraphics(); int col = 1; int line = 1; init(g, 0); boolean more = true; while (more(g)) { final int p = text.pos(); while (text.more()) { more = text.pos() < text.cursor(); if (!more) break; text.next(); col++; } if (!more) break; text.pos(p); if (next()) { line++; col = 1; } } h = hh; return new int[] {line, col}; }
/** * Moves the cursor down. The current column position is remembered in {@link #lastCol} and, if * possible, restored. * * @param l number of lines to move cursor * @param mark mark flag */ private void down(final int l, final boolean mark) { if (!mark) text.noMark(); final int x = text.bol(mark); if (lastCol == -1) lastCol = x; for (int i = 0; i < l; ++i) { text.eol(mark); text.next(mark); } text.forward(lastCol, mark); if (text.pos() == text.size()) lastCol = -1; }
@Override public Dimension getPreferredSize() { final Graphics g = getGraphics(); w = Integer.MAX_VALUE; h = Integer.MAX_VALUE; init(g, 0); int max = 0; while (more(g)) { if (text.curr() == 0x0A) max = Math.max(x, max); next(); } return new Dimension(Math.max(x, max) + fwidth[' '], y + fontH); }
/** * Finds the current keyword and returns the text position. * * @param forward backward browsing * @param same string is the same as last time * @return new position */ int find(final boolean forward, final boolean same) { if (keyword.isEmpty()) return 0; while (true) { final int hh = h; int lp = 0; int ly = 0; int cp = text.cursor(); h = Integer.MAX_VALUE; final Graphics g = getGraphics(); for (init(g, 0); more(g); next()) { if (!found()) continue; final int np = text.pos(); final int ny = y - fontH; if (np >= cp && (np > cp || !same || !forward)) { if (forward || lp != 0) { h = hh; text.setCaret(forward ? np : lp); return forward ? ny : ly; } cp = Integer.MAX_VALUE; } lp = np; ly = ny; } h = hh; if (cp == 0 || cp == Integer.MAX_VALUE) { text.setCaret(lp); return ly; } text.setCaret(0); } }
/** * Moves the cursor up. * * @param l number of lines to move cursor * @param mark mark flag */ private void up(final int l, final boolean mark) { if (!mark) text.noMark(); final int x = text.bol(mark); if (lastCol == -1) lastCol = x; if (text.pos() == 0) { lastCol = -1; return; } for (int i = 0; i < l; ++i) { text.prev(mark); text.bol(mark); } text.forward(lastCol, mark); }
/** * Selects the text at the specified position. * * @param pos current text position * @param p mouse position * @param finish states if selection is in progress */ void select(final int pos, final Point p, final boolean finish) { if (!finish) text.noMark(); p.y -= 3; final Graphics g = getGraphics(); init(g, pos); if (p.y > y - fontH) { int s = text.pos(); while (true) { // end of line if (p.x > x && p.y < y - fontH) { text.pos(s); break; } // end of text - skip last characters if (!more(g)) { while (text.more()) text.next(); break; } // beginning of line if (p.x <= x && p.y < y) break; // middle of line if (p.x > x && p.x <= x + wordW && p.y > y - fontH && p.y <= y) { while (text.more()) { final int ww = charW(g, text.curr()); if (p.x < x + ww) break; x += ww; text.next(); } break; } s = text.pos(); next(); } if (!finish) text.startMark(); else text.endMark(); text.setCaret(); } repaint(); }
/** Finishes the selection. */ void stopSelect() { text.checkMark(); }
/** * Returns the output text. * * @return output text */ public final byte[] getText() { return text.text(); }
/** * Writes the current string to the graphics reference. * * @param g graphics reference */ private void write(final Graphics g) { if (high) { high = false; } else { color = isEnabled() ? syntax.getColor(text) : Color.gray; } final int ch = text.curr(); if (y > 0 && y < h) { if (ch == TokenBuilder.MARK) { color = GUIConstants.GREEN; high = true; } // mark error if (text.erroneous()) { g.setColor(GUIConstants.LRED); g.fillRect(x, y - fontH + 4, wordW, fontH); } // mark text int xx = x; if (text.markStart()) { final int p = text.pos(); while (text.more()) { final int cw = charW(g, text.curr()); if (text.inMark()) { g.setColor(GUIConstants.color(3)); g.fillRect(xx, y - fontH + 4, cw, fontH); } xx += cw; text.next(); } text.pos(p); } if (found()) { int cw = 0; for (int c = 0; c < keyword.length(); ++c) { cw += charW(g, keyword.charAt(c)); } g.setColor(GUIConstants.color(text.cursor() == text.pos() ? 5 : 2)); g.fillRect(x, y - fontH + 4, cw, fontH); } // don't write whitespaces if (ch > ' ') { g.setColor(color); String n = text.nextWord(); int ww = w - x; if (x + wordW > ww) { // shorten string if it cannot be completely shown (saves memory) int c = 0; for (final int nl = n.length(); c < nl && ww > 0; c++) { ww -= charW(g, n.charAt(c)); } n = n.substring(0, c); } g.drawString(n, x, y); } else if (ch <= TokenBuilder.MARK) { g.setFont(font); } // show cursor if (cursor && text.edited()) { xx = x; final int p = text.pos(); while (text.more()) { if (text.cursor() == text.pos()) { cursor(g, xx); break; } xx += charW(g, text.next()); } text.pos(p); } } next(); }
@Override public void keyTyped(final KeyEvent e) { if (undo == null || control(e) || DELNEXT.is(e) || DELPREV.is(e) || ESCAPE.is(e)) return; text.pos(text.cursor()); // string to be added String ch = String.valueOf(e.getKeyChar()); // remember if marked text is to be deleted boolean del = true; final byte[] txt = text.text(); if (TAB.is(e)) { if (text.marked()) { // check if lines are to be indented final int s = Math.min(text.pos(), text.start()); final int l = Math.max(text.pos(), text.start()) - 1; for (int p = s; p <= l && p < txt.length; p++) del &= txt[p] != '\n'; if (!del) { text.indent(s, l, e.isShiftDown()); ch = null; } } else { boolean c = true; for (int p = text.pos() - 1; p >= 0 && c; p--) { final byte b = txt[p]; c = ws(b); if (b == '\n') break; } if (c) ch = " "; } } // delete marked text if (text.marked() && del) text.delete(); if (ENTER.is(e)) { // adopt indentation from previous line final StringBuilder sb = new StringBuilder(1).append(e.getKeyChar()); int s = 0; for (int p = text.pos() - 1; p >= 0; p--) { final byte b = txt[p]; if (b == '\n') break; if (b == '\t') { s += 2; } else if (b == ' ') { s++; } else { s = 0; } } for (int p = 0; p < s; p++) sb.append(' '); ch = sb.toString(); } if (ch != null) text.add(ch); text.setCaret(); rend.calc(); showCursor(2); e.consume(); }
/** * Moves the error marker. {@code -1} removes the marker. * * @param s start of optional error mark */ public final void error(final int s) { text.error(s); rend.repaint(); }
/** * Writes the text. * * @param g graphics reference * @param pos current text position */ private void write(final Graphics g, final int pos) { init(g, pos); while (more(g)) write(g); if (cursor && text.cursor() == text.size()) cursor(g, x); }
@Override public void keyReleased(final KeyEvent e) { if (undo != null) undo.store(text.text(), text.cursor()); }
@Override public void keyPressed(final KeyEvent e) { if (modifier(e)) return; // operations that change the focus are put first.. if (PREVTAB.is(e)) { transferFocusBackward(); return; } if (NEXTTAB.is(e)) { transferFocus(); return; } if (FIND.is(e)) { if (find != null) find.requestFocusInWindow(); return; } // re-animate cursor cursor(true); // operations without cursor movement... final int fh = rend.fontH(); if (SCROLLDOWN.is(e)) { scroll.pos(scroll.pos() + fh); return; } if (SCROLLUP.is(e)) { scroll.pos(scroll.pos() - fh); return; } if (COPY1.is(e) || COPY2.is(e)) { copy(); return; } // set cursor position and reset last column text.pos(text.cursor()); if (!PREVLINE.is(e) && !NEXTLINE.is(e)) lastCol = -1; if (FINDNEXT.is(e) || FINDNEXT2.is(e)) { scroll(rend.find(true, true)); return; } if (FINDPREV.is(e) || FINDPREV2.is(e)) { scroll(rend.find(false, true)); return; } if (SELECTALL.is(e)) { selectAll(); return; } // necessary on Macs as the shift button is pressed for REDO final boolean marking = e.isShiftDown() && !DELNEXT.is(e) && !DELPREV.is(e) && !PASTE2.is(e) && !COMMENT.is(e) && !DELLINE.is(e) && !REDOSTEP.is(e); final boolean nomark = !text.marked(); if (marking && nomark) text.startMark(); boolean down = true; boolean consumed = true; // operations that consider the last text mark.. final byte[] txt = text.text(); if (NEXTWORD.is(e)) { text.nextToken(marking); } else if (PREVWORD.is(e)) { text.prevToken(marking); down = false; } else if (TEXTSTART.is(e)) { if (!marking) text.noMark(); text.pos(0); down = false; } else if (TEXTEND.is(e)) { if (!marking) text.noMark(); text.pos(text.size()); } else if (LINESTART.is(e)) { text.bol(marking); down = false; } else if (LINEEND.is(e)) { text.eol(marking); } else if (NEXTPAGE.is(e)) { down(getHeight() / fh, marking); } else if (PREVPAGE.is(e)) { up(getHeight() / fh, marking); down = false; } else if (NEXT.is(e)) { text.next(marking); } else if (PREV.is(e)) { text.prev(marking); down = false; } else if (PREVLINE.is(e)) { up(1, marking); down = false; } else if (NEXTLINE.is(e)) { down(1, marking); } else if (FINDERROR.is(e)) { final int p = text.error(); if (p != -1) setCaret(p); } else { consumed = false; } if (marking) { // refresh scroll position text.endMark(); text.checkMark(); } else if (undo != null) { // edit operations... if (CUT1.is(e) || CUT2.is(e)) { cut(); } else if (PASTE1.is(e) || PASTE2.is(e)) { paste(); } else if (UNDOSTEP.is(e)) { undo(); } else if (REDOSTEP.is(e)) { redo(); } else if (COMMENT.is(e)) { text.comment(rend.getSyntax()); } else if (DELLINE.is(e)) { text.deleteLine(); } else if (DELLINEEND.is(e) || DELNEXTWORD.is(e) || DELNEXT.is(e)) { if (nomark) { if (text.pos() == text.size()) return; text.startMark(); if (DELNEXTWORD.is(e)) { text.nextToken(true); } else if (DELLINEEND.is(e)) { text.eol(true); } else { text.next(true); } text.endMark(); } undo.cursor(text.cursor()); text.delete(); } else if (DELLINESTART.is(e) || DELPREVWORD.is(e) || DELPREV.is(e)) { if (nomark) { if (text.pos() == 0) return; text.startMark(); if (DELPREVWORD.is(e)) { text.prevToken(true); } else if (DELLINESTART.is(e)) { text.bol(true); } else { text.prev(); } text.endMark(); } undo.cursor(text.cursor()); text.delete(); down = false; } else { consumed = false; } } if (consumed) e.consume(); text.setCaret(); if (txt != text.text()) rend.calc(); showCursor(down ? 2 : 0); }
/** Finishes a command. */ void finish() { text.setCaret(); rend.calc(); showCursor(2); release(false); }
/** Cuts the selected text to the clipboard. */ final void cut() { text.pos(text.cursor()); if (copy()) delete(); }
/** * Sets a new cursor position. * * @param p cursor position */ public final void setCaret(final int p) { text.setCaret(p); showCursor(1); cursor(true); }
/** Selects the whole text. */ final void selectAll() { text.selectAll(); text.setCaret(); rend.repaint(); }