001: /*
002: * Sun Public License Notice
003: *
004: * The contents of this file are subject to the Sun Public License
005: * Version 1.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://www.sun.com/
008: *
009: * The Original Code is NetBeans. The Initial Developer of the Original
010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
011: * Microsystems, Inc. All Rights Reserved.
012: */
013:
014: package org.netbeans.editor;
015:
016: import java.awt.Graphics;
017: import java.awt.Rectangle;
018: import java.awt.Shape;
019:
020: import javax.swing.event.DocumentEvent;
021: import javax.swing.text.BadLocationException;
022: import javax.swing.text.Document;
023: import javax.swing.text.Element;
024: import javax.swing.text.Position;
025: import javax.swing.text.View;
026: import javax.swing.text.ViewFactory;
027:
028: /**
029: * Leaf view implementation. This corresponds and requires leaf element to be
030: * element for this view.
031: *
032: * The view has the following structure:
033: * +---------------------------------------------------------+ | insets.top area |
034: * A | | | insets.top | | V |
035: * +--------------------------------------------------+ | | | A | | | | | i | | | |
036: * n | | | | s | | | | e | | | | t | | | | s | | | | . | | | | l | | | | e |
037: * Main area of this view | | mainHeight | f | | | | t | | | | | | | | a | | | |
038: * r | | | | e | | | | a | | | | | | | | | | | | | | | | | | | | | | V |
039: * +--------------------------------------------------+ | insets.bottom area | A | | |
040: * insets.bottom | | V
041: * +---------------------------------------------------------+
042: *
043: * @author Miloslav Metelka
044: * @version 1.00
045: */
046:
047: public class LeafView extends BaseView {
048:
049: /**
050: * Height of the area this view manages excluding areas managed by its
051: * children and excluding insets.
052: */
053: protected int mainHeight;
054:
055: /** Draw graphics for converting position to coords */
056: ModelToViewDG modelToViewDG = new ModelToViewDG();
057:
058: /** Draw graphics for converting coords to position */
059: ViewToModelDG viewToModelDG = new ViewToModelDG();
060:
061: /** Construct new base view */
062: public LeafView(Element elem) {
063: super (elem);
064: }
065:
066: /** Returns binary composition of paint areas */
067: protected int getPaintAreas(Graphics g, int clipY, int clipHeight) {
068: // invalid or empty height
069: if (clipHeight <= 0) {
070: return 0;
071: }
072:
073: int clipEndY = clipY + clipHeight;
074: int startY = getStartY();
075: if (insets != null) { // valid insets
076: int mainAreaY = startY + insets.top;
077: if (clipEndY <= mainAreaY) {
078: return INSETS_TOP;
079: }
080: int bottomInsetsY = mainAreaY + mainHeight;
081: if (clipEndY <= bottomInsetsY) {
082: if (clipY <= mainAreaY) {
083: return INSETS_TOP + MAIN_AREA;
084: } else {
085: return MAIN_AREA;
086: }
087: }
088: if (clipY <= mainAreaY) {
089: return INSETS_TOP + MAIN_AREA + INSETS_BOTTOM;
090: } else if (clipY <= bottomInsetsY) {
091: return MAIN_AREA + INSETS_BOTTOM;
092: } else if (clipY <= bottomInsetsY + insets.bottom) {
093: return INSETS_BOTTOM;
094: } else {
095: return 0;
096: }
097: } else { // no insets
098: if (clipEndY <= startY || clipY >= startY + getHeight()) {
099: return 0;
100: } else {
101: return MAIN_AREA;
102: }
103: }
104: }
105:
106: /**
107: * Paint either top insets, main area, or bottom insets depending on
108: * paintAreas variable
109: */
110: protected void paintAreas(Graphics g, int clipY, int clipHeight,
111: int paintAreas) {
112: if ((paintAreas & MAIN_AREA) == MAIN_AREA) {
113: EditorUI editorUI = getEditorUI();
114: int paintY = Math.max(clipY, 0); // relative start of area to
115: // paint
116: int startPos = getPosFromY(paintY);
117: if (clipHeight > 0) { // needs to be painted
118: BaseDocument doc = (BaseDocument) getDocument();
119: try {
120: int endPos = doc.op.getEOL(getPosFromY(clipY
121: + clipHeight - 1));
122: int baseY = getYFromPos(startPos);
123: DrawEngine.getDrawEngine().draw(
124: new DrawGraphics.GraphicsDG(g), editorUI,
125: startPos, endPos, getBaseX(baseY), baseY,
126: Integer.MAX_VALUE);
127: } catch (BadLocationException e) {
128: e.printStackTrace();
129: }
130: }
131: }
132: }
133:
134: /** Get total height of this view */
135: public int getHeight() {
136: if (insets != null) {
137: return insets.top + mainHeight + insets.bottom;
138: } else {
139: return mainHeight;
140: }
141: }
142:
143: /** Compute and update main area height */
144: public void updateMainHeight() {
145: LeafElement elem = (LeafElement) getElement(); // need leaf element
146: try {
147: int lineDiff = (elem.getEndMark().getLine()
148: - elem.getStartMark().getLine() + 1);
149: mainHeight = lineDiff * getEditorUI().getLineHeight();
150: } catch (InvalidMarkException e) {
151: mainHeight = 0;
152: }
153: }
154:
155: /**
156: * Get begin of line position from y-coord. If the position is before main
157: * area begining it returns start position. If it's beyond the end of view
158: * it returns end position.
159: *
160: * @param y
161: * y-coord to inspect always returns startOffset for y < start of
162: * main area
163: * @param eol
164: * means to return end of specified line instead of begining
165: * @return position in the document
166: */
167: protected int getPosFromY(int y) {
168: // relative distance from begining of main area
169: int relY = y - getStartY()
170: - ((insets != null) ? insets.top : 0);
171: if (relY < 0) { // before the view
172: return getStartOffset();
173: }
174: if (relY >= mainHeight) { // beyond the view
175: return getEndOffset();
176: }
177:
178: int line = 0;
179: // get the begining line of the element
180: try {
181: line = ((BaseElement) getElement()).getStartMark()
182: .getLine();
183: } catch (InvalidMarkException e) {
184: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
185: e.printStackTrace();
186: }
187: }
188: // advance the line by by relative distance
189: line += relY / getEditorUI().getLineHeight();
190:
191: int startOffset = getStartOffset();
192: int pos;
193: pos = Utilities.getRowStartFromLineOffset(
194: ((BaseDocument) getDocument()), line);
195: if (pos == -1) {
196: pos = startOffset;
197: }
198: return Math.max(pos, startOffset);
199: }
200:
201: public int getBaseX(int y) {
202: return getEditorUI().getTextMargin().left
203: + ((insets != null) ? insets.left : 0);
204: }
205:
206: /** Returns the number of child views in this view. */
207: public final int getViewCount() {
208: return 0;
209: }
210:
211: /** Gets the n-th child view. */
212: public final View getView(int n) {
213: return null;
214: }
215:
216: /** !!! osetrit konec view -> jump na dalsi v branchview */
217: public int getNextVisualPositionFrom(int pos, Position.Bias b,
218: Shape a, int direction, Position.Bias[] biasRet)
219: throws BadLocationException {
220: if (biasRet != null) {
221: biasRet[0] = Position.Bias.Forward;
222: }
223: switch (direction) {
224: case NORTH: {
225: try {
226: BaseDocument doc = (BaseDocument) getDocument();
227: int visCol = doc.op.getVisColFromPos(pos);
228: pos = doc.op.getOffsetFromVisCol(visCol, doc.op
229: .getBOLRelLine(pos, -1));
230: } catch (BadLocationException e) {
231: // leave the original position
232: }
233: return pos;
234: }
235: case SOUTH: {
236: try {
237: BaseDocument doc = (BaseDocument) getDocument();
238: int visCol = doc.op.getVisColFromPos(pos);
239: pos = doc.op.getOffsetFromVisCol(visCol, doc.op
240: .getBOLRelLine(pos, 1));
241: } catch (BadLocationException e) {
242: // leave the original position
243: }
244: return pos;
245: }
246: case WEST:
247: return (pos == -1) ? getStartOffset() : (pos - 1);
248: case EAST:
249: return (pos == -1) ? getEndOffset() : (pos + 1);
250: default:
251: throw new IllegalArgumentException("Bad direction: "
252: + direction); // NOI18N
253: }
254: }
255:
256: /**
257: * Get y coordinate from position. The position can lay anywhere inside this
258: * view.
259: */
260: protected int getYFromPos(int pos) throws BadLocationException {
261: int relLine = 0;
262: try {
263: relLine = ((BaseDocument) getDocument()).op.getLine(pos)
264: - ((BaseElement) getElement()).getStartMark()
265: .getLine();
266: } catch (InvalidMarkException e) {
267: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
268: e.printStackTrace();
269: }
270: }
271: return getStartY() + ((insets != null) ? insets.top : 0)
272: + relLine * getEditorUI().getLineHeight();
273: }
274:
275: public Shape modelToView(int pos, Shape a, Position.Bias b)
276: throws BadLocationException {
277: EditorUI editorUI = getEditorUI();
278: Rectangle ret = new Rectangle();
279: BaseDocument doc = (BaseDocument) getDocument();
280: ret.y = getYFromPos(pos);
281:
282: try {
283: synchronized (modelToViewDG) {
284: modelToViewDG.r = ret; // set the current rectangle
285: int bolPos = doc.op.getBOL(pos);
286: int eolPos = doc.op.getEOL(pos);
287: DrawEngine.getDrawEngine().draw(modelToViewDG,
288: editorUI, bolPos, eolPos, getBaseX(ret.y),
289: ret.y, pos);
290: modelToViewDG.r = null;
291: }
292: } catch (BadLocationException e) {
293: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
294: e.printStackTrace();
295: }
296: }
297:
298: return ret;
299: }
300:
301: public Shape modelToView(int p0, Position.Bias b0, int p1,
302: Position.Bias b1, Shape a) throws BadLocationException {
303: Rectangle r0 = (Rectangle) modelToView(p0, a, b0);
304: Rectangle r1 = (Rectangle) modelToView(p1, a, b1);
305: if (r0.y != r1.y) {
306: // If it spans lines, force it to be the width of the view.
307: r0.x = getComponent().getX();
308: r0.width = getComponent().getWidth();
309: }
310: r0.add(r1);
311: return r0;
312: }
313:
314: public void modelToViewDG(int pos, DrawGraphics dg)
315: throws BadLocationException {
316: EditorUI editorUI = getEditorUI();
317: BaseDocument doc = (BaseDocument) getDocument();
318: int y = getYFromPos(pos);
319: DrawEngine.getDrawEngine().draw(dg, editorUI,
320: doc.op.getBOL(pos), doc.op.getEOL(pos), getBaseX(y), y,
321: pos);
322: }
323:
324: /**
325: * Get position from location on screen.
326: *
327: * @param x
328: * the X coordinate >= 0
329: * @param y
330: * the Y coordinate >= 0
331: * @param a
332: * the allocated region to render into
333: * @return the location within the model that best represents the given
334: * point in the view >= 0
335: */
336: public int viewToModel(float x, float y, Shape a,
337: Position.Bias[] biasReturn) {
338: int intX = (int) x;
339: int intY = (int) y;
340: if (biasReturn != null) {
341: biasReturn[0] = Position.Bias.Forward;
342: }
343: int begMainY = getStartY()
344: + ((insets != null) ? insets.top : 0);
345: if (intY < begMainY) { // top insets or before this view
346: return -1; // getStartOffset();
347: } else if (intY > begMainY + mainHeight) { // bottom insets or beyond
348: return getEndOffset();
349: } else { // inside the view
350: int pos = getPosFromY(intY); // first get BOL of target line
351: EditorUI editorUI = getEditorUI();
352: try {
353: int eolPos = ((BaseDocument) getDocument()).op
354: .getEOL(pos);
355: synchronized (viewToModelDG) {
356: viewToModelDG.setTargetX(intX);
357: viewToModelDG.setEOLOffset(eolPos);
358: DrawEngine.getDrawEngine().draw(viewToModelDG,
359: editorUI, pos, eolPos, getBaseX(intY), 0,
360: -1);
361: pos = viewToModelDG.getOffset();
362: }
363: } catch (BadLocationException e) {
364: // return begining of line in this case
365: }
366: return pos;
367: }
368: }
369:
370: /**
371: * Gives notification that something was inserted into the document in a
372: * location that this view is responsible for.
373: *
374: * @param e
375: * the change information from the associated document
376: * @param a
377: * the current allocation of the view
378: * @param f
379: * the factory to use to rebuild if the view has children
380: */
381: public void insertUpdate(DocumentEvent evt, Shape a, ViewFactory f) {
382: if (evt.getLength() == 0) { // initial read was performed
383: updateMainHeight();
384:
385: } else { // regular insertion (or undone removal)
386:
387: try {
388: BaseDocumentEvent bevt = (BaseDocumentEvent) evt;
389: EditorUI editorUI = getEditorUI();
390: int y = getYFromPos(evt.getOffset());
391: int lineHeight = editorUI.getLineHeight();
392: if (bevt.getLFCount() > 0) { // one or more lines inserted
393: int addHeight = bevt.getLFCount() * lineHeight;
394: mainHeight += addHeight;
395: editorUI.repaint(y);
396:
397: } else { // inserting on one line
398:
399: int syntaxY = getYFromPos(bevt
400: .getSyntaxUpdateOffset());
401: // !!! patch for case when DocMarksOp.eolMark is at the end
402: // of document
403: if (bevt.getSyntaxUpdateOffset() == evt
404: .getDocument().getLength()) {
405: syntaxY += lineHeight;
406: }
407:
408: if (getComponent().isShowing()) {
409: editorUI.repaint(y, Math.max(lineHeight,
410: syntaxY - y));
411: }
412: }
413:
414: } catch (BadLocationException ex) {
415: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
416: ex.printStackTrace();
417: }
418: }
419:
420: }
421: }
422:
423: /**
424: * Gives notification from the document that attributes were removed in a
425: * location that this view is responsible for.
426: *
427: * @param e
428: * the change information from the associated document
429: * @param a
430: * the current allocation of the view
431: * @param f
432: * the factory to use to rebuild if the view has children
433: */
434: public void removeUpdate(DocumentEvent evt, Shape a, ViewFactory f) {
435: try {
436: BaseDocumentEvent bevt = (BaseDocumentEvent) evt;
437: EditorUI editorUI = getEditorUI();
438: int y = getYFromPos(evt.getOffset());
439: int lineHeight = editorUI.getLineHeight();
440: if (bevt.getLFCount() > 0) { // one or more lines removed
441: int removeHeight = bevt.getLFCount() * lineHeight;
442: mainHeight -= removeHeight;
443: editorUI.repaint(y);
444:
445: } else { // removing on one line
446: int syntaxY = getYFromPos(bevt.getSyntaxUpdateOffset());
447: // !!! patch for case when DocMarksOp.eolMark is at the end of
448: // document
449: if (bevt.getSyntaxUpdateOffset() == evt.getDocument()
450: .getLength()) {
451: syntaxY += lineHeight;
452: }
453:
454: if (getComponent().isShowing()) {
455: editorUI.repaint(y, Math.max(lineHeight, syntaxY
456: - y));
457: }
458: }
459:
460: } catch (BadLocationException ex) {
461: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
462: ex.printStackTrace();
463: }
464: }
465: }
466:
467: /**
468: * Attributes were changed in the are this view is responsible for.
469: *
470: * @param e
471: * the change information from the associated document
472: * @param a
473: * the current allocation of the view
474: * @param f
475: * the factory to use to rebuild if the view has children
476: */
477: public void changedUpdate(DocumentEvent evt, Shape a, ViewFactory f) {
478: try {
479: if (getComponent().isShowing()) {
480: getEditorUI().repaintBlock(evt.getOffset(),
481: evt.getOffset() + evt.getLength());
482: }
483: } catch (BadLocationException ex) {
484: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
485: ex.printStackTrace();
486: }
487: }
488: }
489:
490: /** Get child view's y base value. Invalid in this case. */
491: protected int getViewStartY(BaseView view, int helperInd) {
492: return 0; // invalid in this case
493: }
494:
495: static final class ModelToViewDG extends DrawGraphics.SimpleDG {
496:
497: Rectangle r;
498:
499: public boolean targetOffsetReached(int pos, char ch, int x,
500: int charWidth, DrawContext ctx) {
501: r.x = x;
502: r.y = getY();
503: r.width = charWidth;
504: r.height = getLineHeight();
505: return false;
506: }
507:
508: }
509:
510: static final class ViewToModelDG extends DrawGraphics.SimpleDG {
511:
512: int targetX;
513:
514: int offset;
515:
516: int eolOffset;
517:
518: void setTargetX(int targetX) {
519: this .targetX = targetX;
520: }
521:
522: void setEOLOffset(int eolOffset) {
523: this .eolOffset = eolOffset;
524: this .offset = eolOffset;
525: }
526:
527: int getOffset() {
528: return offset;
529: }
530:
531: public boolean targetOffsetReached(int offset, char ch, int x,
532: int charWidth, DrawContext ctx) {
533: if (offset <= eolOffset) {
534: if (x + charWidth < targetX) {
535: this .offset = offset;
536: return true;
537:
538: } else { // target position inside the char
539: this .offset = offset;
540: if (targetX > x + charWidth / 2) {
541: Document doc = ctx.getEditorUI().getDocument();
542: if (ch != '\n' && doc != null
543: && offset < doc.getLength()) {
544: this .offset++;
545: }
546: }
547:
548: return false;
549: }
550: }
551: return false;
552: }
553:
554: }
555:
556: }
|