001: /*
002: * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved.
003: *
004: * The program is provided "as is" without any warranty express or
005: * implied, including the warranty of non-infringement and the implied
006: * warranties of merchantibility and fitness for a particular purpose.
007: * IBM will not be liable for any damages suffered by you as a result
008: * of using the Program. In no event will IBM be liable for any
009: * special, indirect or consequential damages or lost profits even if
010: * IBM has been advised of the possibility of their occurrence. IBM
011: * will not be liable for any third party claims against you.
012: */
013: package com.ibm.richtext.uiimpl;
014:
015: import java.awt.Color;
016: import java.awt.Component;
017: import java.awt.Dimension;
018: import java.awt.FontMetrics;
019: import java.awt.Graphics;
020: import java.awt.Image;
021:
022: import java.awt.event.MouseMotionListener;
023: import java.awt.event.MouseListener;
024: import java.awt.event.MouseEvent;
025:
026: import com.ibm.richtext.textlayout.attributes.AttributeMap;
027: import com.ibm.richtext.textlayout.attributes.TextAttribute;
028:
029: import com.ibm.richtext.styledtext.MTabRuler;
030: import com.ibm.richtext.styledtext.StandardTabRuler;
031: import com.ibm.richtext.styledtext.TabStop;
032: import com.ibm.richtext.styledtext.StyleModifier;
033:
034: import com.ibm.richtext.textpanel.TextPanelListener;
035: import com.ibm.richtext.textpanel.MTextPanel;
036: import com.ibm.richtext.textpanel.TextPanel;
037: import com.ibm.richtext.textpanel.TextPanelEvent;
038:
039: /**
040: * TabRuler is a Component which presents a user interface for
041: * setting the leading margin, trailing margin, first line indent,
042: * and tab types and positions.
043: * <p>
044: * TabRuler does not implement TextPanelListener directly; however,
045: * it can receive updates from a MTextPanel. To have a TabRuler listen
046: * to a panel, call <code>listenToPanel</code>. TabRuler responds to
047: * user manipulation by modifying the paragraph styles on its MTextPanel
048: * (if any).
049: */
050: public final class TabRulerImpl implements MouseListener,
051: MouseMotionListener {
052: static final String COPYRIGHT = "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
053:
054: private static final class TabStopBuffer {
055: public int fPosition;
056: public byte fType;
057:
058: TabStopBuffer(int position, byte type) {
059: fPosition = position;
060: fType = type;
061: }
062:
063: TabStopBuffer(TabStop tab) {
064: this (tab.getPosition(), tab.getType());
065: }
066:
067: TabStop getTabStop() {
068: return new TabStop(fPosition, fType);
069: }
070: }
071:
072: private static final class TabRulerModifier extends StyleModifier {
073:
074: private TabStop fOldTab; // tab to remove
075: private TabStop fNewTab; // tab to add
076: private AttributeMap fPanelDefaults;
077:
078: TabRulerModifier(TabStop oldTab, TabStop newTab,
079: AttributeMap panelDefaults) {
080: fOldTab = oldTab;
081: fNewTab = newTab;
082: fPanelDefaults = panelDefaults;
083: }
084:
085: public AttributeMap modifyStyle(AttributeMap oldStyle) {
086:
087: MTabRuler oldRuler = (MTabRuler) getWithDefault(
088: TextAttribute.TAB_RULER, oldStyle, fPanelDefaults);
089: MTabRuler ruler = oldRuler;
090:
091: if (fOldTab != null) {
092: if (ruler.containsTab(fOldTab)) {
093: ruler = ruler.removeTab(fOldTab.getPosition());
094: }
095: }
096: if (fNewTab != null) {
097: ruler = ruler.addTab(fNewTab);
098: }
099:
100: if (ruler != oldRuler) {
101: return oldStyle.addAttribute(TextAttribute.TAB_RULER,
102: ruler);
103: } else {
104: return oldStyle;
105: }
106: }
107: }
108:
109: private static final class ImageCache {
110:
111: static final String COPYRIGHT = "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
112: private Image fImage = null;
113: private boolean fIsValid = false;
114: private Component fComponent; // workaround for compiler bug,
115:
116: // should just be able to say Component.this
117: // if this were not a static class
118:
119: ImageCache(Component component) {
120:
121: fComponent = component;
122: }
123:
124: Graphics getGraphics(int width, int height) {
125:
126: if (width <= 0 || height <= 0) {
127: return null;
128: }
129:
130: Image image = fImage;
131: if (image == null || image.getWidth(fComponent) < width
132: || image.getHeight(fComponent) < height) {
133: if (!fComponent.isVisible()) { // fix race condition if component not fully initialized
134: return null;
135: }
136: image = fComponent.createImage(width, height);
137: }
138: Graphics g = image.getGraphics();
139: fImage = image;
140: return g;
141: }
142:
143: void drawImage(Graphics g, int x, int y, Color color) {
144:
145: if (!fIsValid) {
146: throw new Error("Drawing image when not valid");
147: }
148: g.drawImage(fImage, x, y, color, fComponent);
149: }
150:
151: boolean isValid() {
152:
153: return fIsValid;
154: }
155:
156: void setValid(boolean isValid) {
157:
158: fIsValid = isValid;
159: }
160: }
161:
162: /**
163: * This class listens to a MTextPanel for changes which
164: * affect a TabRuler's appearance, and updates the TabRuler
165: * as necessary.
166: * @see TabRuler
167: * @see com.ibm.richtext.textpanel.MTextPanel
168: */
169: private static final class Updater implements TextPanelListener {
170:
171: static final String COPYRIGHT = "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
172: private TabRulerImpl fTabRuler;
173: private MTextPanel fTextPanel;
174:
175: /**
176: * Create a new TabRulerUpdater.
177: * @param tabRuler the TabRuler to update when a change occurs
178: * in the MTextPanel
179: */
180: Updater(TabRulerImpl tabRuler) {
181:
182: fTabRuler = tabRuler;
183: }
184:
185: /**
186: * Remove self as listener from previous MTextPanel,
187: * set current MTextPanel and listen to it (if not null).
188: */
189: void setTextPanel(MTextPanel textPanel) {
190:
191: if (fTextPanel != null) {
192: fTextPanel.removeListener(this );
193: }
194:
195: fTextPanel = textPanel;
196:
197: if (fTextPanel != null) {
198: fTextPanel.addListener(this );
199: setAll();
200: }
201: }
202:
203: private void setAll() {
204:
205: int offset = fTextPanel.getSelectionStart();
206: boolean leftToRight = fTextPanel
207: .paragraphIsLeftToRight(offset);
208: AttributeMap style = fTextPanel.getText().paragraphStyleAt(
209: offset);
210: fTabRuler.set(style, false);
211: fTabRuler
212: .setFormatWidth(fTextPanel.getFormatWidth(), false);
213: fTabRuler.setLeftToRight(leftToRight, true);
214: }
215:
216: /**
217: * TextPanelListener method. This class responds to text
218: * changes by updating its TabRuler.
219: */
220: public void textEventOccurred(TextPanelEvent event) {
221:
222: int changeCode = event.getID();
223:
224: if (changeCode == TextPanelEvent.SELECTION_STYLES_CHANGED
225: || changeCode == TextPanelEvent.TEXT_CHANGED) {
226:
227: int offset = fTextPanel.getSelectionStart();
228: AttributeMap style = fTextPanel.getText()
229: .paragraphStyleAt(offset);
230: boolean leftToRight = fTextPanel
231: .paragraphIsLeftToRight(offset);
232: fTabRuler.set(style, false);
233: fTabRuler.setLeftToRight(leftToRight, true);
234: } else if (changeCode == TextPanelEvent.FORMAT_WIDTH_CHANGED) {
235:
236: fTabRuler.setFormatWidth(fTextPanel.getFormatWidth(),
237: true);
238: }
239: }
240:
241: /**
242: * TextPanelListener method.
243: */
244: public boolean respondsToEventType(int type) {
245:
246: return type == TextPanelEvent.SELECTION_STYLES_CHANGED
247: || type == TextPanelEvent.TEXT_CHANGED
248: || type == TextPanelEvent.FORMAT_WIDTH_CHANGED;
249: }
250: }
251:
252: /**
253: * The default background color for TabRulers.
254: * @see #setBackColor
255: */
256: public static final Color DEFAULT_BACK_COLOR = Color.lightGray;
257:
258: private static final int kTrackNone = 0;
259: private static final int kTrackTab = 1;
260: private static final int kTrackLM = 2;
261: private static final int kTrackFLI = 3;
262: private static final int kTrackTM = 4;
263:
264: private Component fHost;
265: private MTabRuler fRuler;
266: private int fLeadingMargin;
267: private int fFirstLineIndent;
268: private int fFormatWidth;
269: private int fTrailingMarginPosition; // opposite of actual trailing margin
270: private boolean fLeftToRight;
271: private int fBaseline;
272: private int fOrigin;
273: private Color fBackColor = DEFAULT_BACK_COLOR;
274:
275: private int fTrackItem; // 0 - none, 1 - tab, 2 - lm, 3 - fli, 4 - tm
276: private TabStopBuffer fTrackTab;
277: private TabStop fOldTab;
278: private int fTrackDelta;
279: private boolean fTrackVisible;
280: private Updater fUpdater;
281: private MTextPanel fTextPanel = null;
282:
283: private ImageCache fImageCache;
284:
285: /**
286: * Create a new TabRuler.
287: * @param baseline the y-coordinate of the ruler's baseline
288: * @param origin the x-coordinate in this Component where
289: * the left margin appears
290: * @param textPanel the MTextPanel to listen to. This TabRuler
291: * will reflect the MTextPanel's paragraph styles, and update
292: * the paragraph styles when manipulated.
293: */
294: public TabRulerImpl(int baseline, int origin, MTextPanel textPanel,
295: Component host) {
296:
297: fHost = host;
298: fImageCache = new ImageCache(host);
299: fUpdater = new Updater(this );
300: fBaseline = baseline;
301: fOrigin = origin;
302: host.addMouseListener(this );
303: host.addMouseMotionListener(this );
304: if (textPanel != null) {
305: listenToTextPanel(textPanel);
306: } else {
307: fRuler = new StandardTabRuler();
308: }
309: }
310:
311: /**
312: * Listen to the given MTextPanel and reflect its changes,
313: * and update its paragraph styles when TabRuler is
314: * manipulated.
315: * @param textPanel the MTextPanel to listen to
316: */
317: public void listenToTextPanel(MTextPanel textPanel) {
318:
319: fTextPanel = textPanel;
320: fUpdater.setTextPanel(textPanel);
321: }
322:
323: /**
324: * Return the background color of this TabRuler.
325: * @return the background color of this TabRuler
326: */
327: public Color getBackColor() {
328:
329: return fBackColor;
330: }
331:
332: /**
333: * Set the background color of this TabRuler.
334: * @param backColor the new background color of this TabRuler
335: */
336: public void setBackColor(Color backColor) {
337:
338: if (!backColor.equals(fBackColor)) {
339: fBackColor = backColor;
340: Graphics g = fHost.getGraphics();
341: if (g != null) {
342: paint(g);
343: }
344: }
345: }
346:
347: private static Object getWithDefault(Object key,
348: AttributeMap style, AttributeMap defaults) {
349: Object value = style.get(key);
350: if (value == null) {
351: value = defaults.get(key);
352: }
353: return value;
354: }
355:
356: private static float getFloatWithDefault(Object key,
357: AttributeMap style, AttributeMap defaults) {
358: Object value = getWithDefault(key, style, defaults);
359: return ((Float) value).floatValue();
360: }
361:
362: private void setLeftToRight(boolean leftToRight, boolean update) {
363:
364: if (fLeftToRight != leftToRight) {
365:
366: fLeftToRight = leftToRight;
367: redrawSelf(update);
368: }
369: }
370:
371: private void setFormatWidth(int formatWidth, boolean update) {
372:
373: if (fFormatWidth != formatWidth) {
374:
375: fTrailingMarginPosition += (formatWidth - fFormatWidth);
376: fFormatWidth = formatWidth;
377: redrawSelf(update);
378: }
379: }
380:
381: /**
382: * Set TabRuler from values in paragraphStyle. Only TabRulerUpdater
383: * should call this method.
384: * @param paragraphStyle the paragraph style which the TabRuler will
385: * reflect
386: */
387: private void set(AttributeMap paragraphStyle, boolean update) {
388:
389: AttributeMap panelDefaults;
390:
391: if (fTextPanel == null) {
392: panelDefaults = TextPanel.getDefaultSettings()
393: .getDefaultValues();
394: } else {
395: panelDefaults = fTextPanel.getDefaultValues();
396: }
397:
398: int leadingMargin = (int) getFloatWithDefault(
399: TextAttribute.LEADING_MARGIN, paragraphStyle,
400: panelDefaults);
401: int firstLineIndent = (int) getFloatWithDefault(
402: TextAttribute.FIRST_LINE_INDENT, paragraphStyle,
403: panelDefaults);
404: int trailingMargin = (int) getFloatWithDefault(
405: TextAttribute.TRAILING_MARGIN, paragraphStyle,
406: panelDefaults);
407:
408: MTabRuler ruler = (MTabRuler) getWithDefault(
409: TextAttribute.TAB_RULER, paragraphStyle, panelDefaults);
410:
411: int ourFli = leadingMargin + firstLineIndent;
412: int ourTmp = fFormatWidth - trailingMargin;
413:
414: if (leadingMargin == fLeadingMargin
415: && fFirstLineIndent == ourFli
416: && fTrailingMarginPosition == ourTmp
417: && ruler.equals(fRuler)) {
418: return;
419: }
420:
421: fLeadingMargin = leadingMargin;
422: fFirstLineIndent = ourFli;
423: fTrailingMarginPosition = ourTmp;
424: fRuler = ruler;
425:
426: redrawSelf(update);
427: }
428:
429: private void redrawSelf(boolean drawNow) {
430:
431: fImageCache.setValid(false);
432:
433: Graphics g = fHost.getGraphics();
434: if (g != null)
435: paint(g);
436: }
437:
438: /**
439: * Return debugging info.
440: */
441: public String toString() {
442:
443: return "TabRuler{fLeadingMargin=" + fLeadingMargin
444: + "}{fFirstLineIndent=" + fFirstLineIndent
445: + "}{fFormatWidth=" + fFormatWidth
446: + "}{fTrailingMarginPosition="
447: + fTrailingMarginPosition + "}{fRuler=" + fRuler + "}";
448: }
449:
450: /**
451: * Return the MTabRuler represented by this TabRuler.
452: * @return the MTabRuler represented by this TabRuler
453: */
454: public MTabRuler getRuler() {
455: return fRuler;
456: }
457:
458: /**
459: * Return the leading margin of this TabRuler.
460: * @return the leading margin of this TabRuler
461: */
462: public int getLeadingMargin() {
463: return fLeadingMargin;
464: }
465:
466: /**
467: * Return the first line indent of this TabRuler.
468: * @return the first line indent of this TabRuler
469: */
470: public int getFirstLineIndent() {
471: return fFirstLineIndent - fLeadingMargin;
472: }
473:
474: /**
475: * Return the trailing margin of this TabRuler.
476: * @return the trailing margin of this TabRuler
477: */
478: public final int getTrailingMargin() {
479: return fFormatWidth - fTrailingMarginPosition;
480: }
481:
482: private int visualToRulerPos(int visPos) {
483:
484: if (fLeftToRight) {
485: return visPos - fOrigin;
486: } else {
487: return fOrigin + fFormatWidth - visPos;
488: }
489: }
490:
491: private int rulerToVisualPos(int rulerPos) {
492:
493: if (fLeftToRight) {
494: return fOrigin + rulerPos;
495: } else {
496: return fOrigin + fFormatWidth - rulerPos;
497: }
498: }
499:
500: private int dirMult() {
501:
502: return fLeftToRight ? 1 : -1;
503: }
504:
505: /**
506: * @param tabPosition the logical (ruler) position of the tab
507: */
508: private void drawTab(Graphics g, int tabPosition, byte tabType,
509: int tabTop, int tabBottom) {
510: int pos = rulerToVisualPos(tabPosition);
511: int wid = 0;
512: switch (tabType) {
513: case TabStop.kLeading:
514: wid = 3;
515: break;
516: case TabStop.kCenter:
517: wid = 0;
518: break;
519: case TabStop.kTrailing:
520: wid = -3;
521: break;
522: case TabStop.kDecimal:
523: wid = 0;
524: break;
525: default:
526: break;
527: }
528: wid *= dirMult();
529:
530: if (tabType != TabStop.kAuto) {
531: g.drawLine(pos, tabTop, pos, tabBottom);
532: if (wid != 0)
533: g.drawLine(pos, tabBottom, pos + wid, tabBottom);
534: }
535: g.drawLine(pos - 2, tabTop + 2, pos, tabTop);
536: g.drawLine(pos, tabTop, pos + 2, tabTop + 2);
537: if (tabType == TabStop.kDecimal) {
538: g.drawLine(pos + 3, tabBottom, pos + 4, tabBottom);
539: }
540: }
541:
542: private void drawLM(Graphics g) {
543: int pos = rulerToVisualPos(fLeadingMargin);
544: int[] xpts = { pos, pos, pos + (4 * dirMult()), pos };
545: int[] ypts = { fBaseline + 12, fBaseline + 7, fBaseline + 7,
546: fBaseline + 12 };
547: g.fillPolygon(xpts, ypts, 3);
548: g.drawPolygon(xpts, ypts, 4);
549: }
550:
551: private void drawFLI(Graphics g) {
552: int pos = rulerToVisualPos(fFirstLineIndent);
553: int[] xpts = { pos, pos, pos + (4 * dirMult()), pos };
554: int[] ypts = { fBaseline, fBaseline + 5, fBaseline + 5,
555: fBaseline };
556: g.fillPolygon(xpts, ypts, 3);
557: g.drawPolygon(xpts, ypts, 4);
558: }
559:
560: private void drawRM(Graphics g) {
561: int pos = rulerToVisualPos(fTrailingMarginPosition);
562: int[] xpts = { pos, pos, pos - (6 * dirMult()), pos };
563: int[] ypts = { fBaseline, fBaseline + 12, fBaseline + 6,
564: fBaseline };
565: g.fillPolygon(xpts, ypts, 3);
566: g.drawPolygon(xpts, ypts, 4);
567: }
568:
569: private static int alignInt(int value) {
570:
571: return (int) ((int) (value / 4.5) * 4.5);
572: }
573:
574: private static final int[] fgLengths = { 10, 2, 4, 2, 6, 2, 4, 2 };
575:
576: /**
577: * Component method override.
578: */
579: public void paint(Graphics g) {
580: Dimension size = fHost.getSize();
581:
582: int width = size.width;
583: int baseline = fBaseline;
584: int baseline2 = baseline + 2;
585: int baseline10 = baseline + 10;
586: int baseline12 = baseline + 12;
587:
588: if (!fImageCache.isValid()) {
589:
590: Graphics gCache = fImageCache.getGraphics(width,
591: baseline12 + 1);
592: if (gCache == null) {
593: return;
594: }
595:
596: // set background color
597:
598: gCache.setColor(fBackColor);
599: gCache.setPaintMode();
600: gCache.fillRect(0, 0, width, baseline12 + 1);
601:
602: // paint ticks
603:
604: gCache.setColor(Color.black);
605: gCache.drawLine(0, 0, width, 0);
606: gCache.drawLine(0, baseline, width, baseline);
607:
608: int[] lengths = fgLengths;
609:
610: int index = 0;
611: int inchnum = 0;
612: FontMetrics fm = null;
613: if (!fLeftToRight) {
614: fm = gCache.getFontMetrics();
615: }
616:
617: for (int i = 0; i < fFormatWidth; i += 9) {
618: int len = lengths[index];
619: int pos = rulerToVisualPos(i);
620: gCache.drawLine(pos, baseline, pos, baseline - len);
621:
622: if (index == 0) {
623: String str = Integer.toString(inchnum++);
624: int drawX;
625: if (fLeftToRight) {
626: drawX = pos + 2;
627: } else {
628: drawX = pos - fm.stringWidth(str) - 2;
629: }
630:
631: gCache.drawString(str, drawX, baseline - 2);
632: }
633:
634: if (++index == lengths.length)
635: index = 0;
636: }
637:
638: // paint tabs
639: TabStop tab = fRuler.firstTab();
640: while (tab != null
641: && tab.getPosition() < fTrailingMarginPosition) {
642: boolean dodraw = true;
643: if (tab.getType() == TabStop.kAuto) {
644: if (tab.getPosition() <= Math.max(fLeadingMargin,
645: fFirstLineIndent))
646: dodraw = false;
647: else if (tab.getPosition() >= fTrailingMarginPosition)
648: dodraw = false;
649: }
650:
651: if (dodraw)
652: drawTab(gCache, tab.getPosition(), tab.getType(),
653: baseline2, baseline10);
654:
655: tab = fRuler.nextTab(tab.getPosition());
656: }
657:
658: gCache.drawLine(0, baseline12, width, baseline12);
659:
660: // paint others except for tracked item
661: if (fTrackItem != kTrackLM)
662: drawLM(gCache);
663: if (fTrackItem != kTrackTM)
664: drawRM(gCache);
665: if (fTrackItem != kTrackFLI && fTrackItem != kTrackLM)
666: drawFLI(gCache);
667: fImageCache.setValid(true);
668: }
669:
670: fImageCache.drawImage(g, 0, 0, Color.lightGray);
671:
672: switch (fTrackItem) {
673: case kTrackTab:
674: if (fTrackVisible)
675: drawTab(g, fTrackTab.fPosition, fTrackTab.fType,
676: baseline2, baseline10);
677: break;
678: case kTrackLM:
679: drawLM(g);
680: drawFLI(g);
681: break;
682: case kTrackTM:
683: drawRM(g);
684: break;
685: case kTrackFLI:
686: drawFLI(g);
687: break;
688: default:
689: break;
690: }
691: }
692:
693: /**
694: * MouseListener method.
695: */
696: public void mouseClicked(MouseEvent e) {
697: }
698:
699: /**
700: * MouseListener method.
701: */
702: public void mouseEntered(MouseEvent e) {
703: }
704:
705: /**
706: * MouseListener method.
707: */
708: public void mouseExited(MouseEvent e) {
709: }
710:
711: /**
712: * MouseListener method.
713: */
714: public void mousePressed(MouseEvent e) {
715: // find out if we hit a tabstop
716: int x = visualToRulerPos(e.getX());
717: int y = e.getY();
718:
719: if (y > fBaseline && y < fBaseline + 12) {
720: if (y >= fBaseline + 7 && x >= fLeadingMargin - 3
721: && x <= fLeadingMargin + 3) {
722: fTrackItem = kTrackLM;
723: fTrackDelta = fLeadingMargin - x;
724: } else if (y < fBaseline + 7 && x >= fFirstLineIndent - 3
725: && x <= fFirstLineIndent + 3) {
726: fTrackItem = kTrackFLI;
727: fTrackDelta = fFirstLineIndent - x;
728: } else if (x >= fTrailingMarginPosition - 3
729: && x <= fTrailingMarginPosition + 3) {
730: fTrackItem = kTrackTM;
731: fTrackDelta = fTrailingMarginPosition - x;
732: } else if (e.isControlDown()) {
733: fTrackItem = kTrackTab;
734: fTrackTab = new TabStopBuffer(alignInt(x),
735: TabStop.kLeading);
736: fTrackDelta = fTrackTab.fPosition - x;
737: fTrackVisible = true;
738: } else {
739: TabStop tab = fRuler.firstTab();
740: while (tab.getType() != TabStop.kAuto) {
741: if (x < tab.getPosition() - 3)
742: break;
743: if (x < tab.getPosition() + 3) {
744: fOldTab = tab;
745: fTrackTab = new TabStopBuffer(tab);
746: fRuler = fRuler
747: .removeTab(fOldTab.getPosition());
748:
749: if (e.getClickCount() > 1) {
750: switch (fTrackTab.fType) {
751: case TabStop.kLeading:
752: fTrackTab.fType = TabStop.kCenter;
753: break;
754: case TabStop.kCenter:
755: fTrackTab.fType = TabStop.kTrailing;
756: break;
757: case TabStop.kTrailing:
758: fTrackTab.fType = TabStop.kDecimal;
759: break;
760: case TabStop.kDecimal:
761: fTrackTab.fType = TabStop.kLeading;
762: break;
763: default:
764: break;
765: }
766: }
767: fTrackItem = kTrackTab;
768: fTrackDelta = tab.getPosition() - x;
769: fTrackVisible = true;
770: break;
771: }
772:
773: tab = fRuler.nextTab(tab.getPosition());
774: }
775: }
776:
777: if (fTrackItem != kTrackNone) {
778: fImageCache.setValid(false);
779: paint(fHost.getGraphics());
780: return;
781: }
782: }
783: }
784:
785: /**
786: * MouseListener method.
787: */
788: public void mouseDragged(MouseEvent e) {
789: int x = visualToRulerPos(e.getX());
790: int y = e.getY();
791:
792: if (fTrackItem != kTrackNone) {
793: boolean repaint = false;
794: boolean inrange = y > fBaseline && y < fBaseline + 12;
795: boolean inbigrange = y > 0
796: && y < fHost.getSize().height + 20;
797: int newpos = alignInt(x + fTrackDelta);
798: if (newpos < 0)
799: newpos = 0;
800:
801: switch (fTrackItem) {
802: case kTrackTab: {
803: if (inrange) {
804: repaint = !fTrackVisible;
805: fTrackVisible = true;
806: if (newpos != fTrackTab.fPosition) {
807: fTrackTab.fPosition = newpos;
808: repaint = true;
809: }
810: } else if (fTrackVisible) {
811: fTrackVisible = false;
812: repaint = true;
813: }
814: }
815: break;
816:
817: /* It would be nice to optionally track the margin 'independently' of the first line indent.
818: Unfortunately this makes for more work when we have multiple paragraph styles selected.
819: Since internally the first line indent is relative to the margin, moving the margin
820: independently so that all affected paragraphs share the same margin but retain first
821: line indents in the 'same' positions means that I need to also adjust the first line
822: indents in each paragraph by some delta. I'm not ready to do that yet. */
823:
824: case kTrackLM: {
825: if (inbigrange && newpos != fLeadingMargin) {
826: fFirstLineIndent += newpos - fLeadingMargin;
827: fLeadingMargin = newpos;
828: repaint = true;
829: }
830: }
831: break;
832:
833: case kTrackFLI: {
834: if (inbigrange && newpos != fFirstLineIndent) {
835: fFirstLineIndent = newpos;
836: repaint = true;
837: }
838: }
839: break;
840:
841: case kTrackTM: {
842: if (inbigrange && newpos != fTrailingMarginPosition) {
843: fTrailingMarginPosition = newpos;
844: repaint = true;
845: }
846: }
847: break;
848: }
849:
850: if (repaint)
851: paint(fHost.getGraphics());
852: }
853: }
854:
855: /**
856: * MouseListener method.
857: */
858: public void mouseReleased(MouseEvent e) {
859: if (fTrackItem != kTrackNone) {
860: if (fTrackItem == kTrackTab && fTrackVisible) {
861: fRuler = fRuler.addTab(fTrackTab.getTabStop());
862: } else {
863: fTrackTab = null;
864: }
865:
866: notify(fTrackItem);
867:
868: fTrackItem = kTrackNone;
869: fTrackTab = null;
870: fOldTab = null;
871:
872: fImageCache.setValid(false);
873: paint(fHost.getGraphics());
874: }
875: }
876:
877: /**
878: * MouseListener method.
879: */
880: public void mouseMoved(MouseEvent e) {
881: }
882:
883: private void notify(int change) {
884: if (fTextPanel != null) {
885:
886: StyleModifier modifier;
887:
888: if (change == kTrackTab) {
889: TabStop newTab = fTrackTab == null ? null : fTrackTab
890: .getTabStop();
891: modifier = new TabRulerModifier(fOldTab, newTab,
892: fTextPanel.getDefaultValues());
893: } else {
894: Object key;
895: Object value;
896:
897: switch (change) {
898: case kTrackLM:
899: key = TextAttribute.LEADING_MARGIN;
900: value = new Float(getLeadingMargin());
901: break;
902:
903: case kTrackTM:
904: key = TextAttribute.TRAILING_MARGIN;
905: value = new Float(getTrailingMargin());
906: break;
907:
908: case kTrackFLI:
909: key = TextAttribute.FIRST_LINE_INDENT;
910: value = new Float(getFirstLineIndent());
911: break;
912:
913: default:
914: throw new Error("Invalid change code.");
915: }
916:
917: modifier = StyleModifier.createAddModifier(key, value);
918: }
919:
920: fTextPanel.modifyParagraphStyleOnSelection(modifier);
921: }
922: }
923:
924: /**
925: * Component override.
926: */
927: public Dimension getMinimumSize() {
928: return new Dimension(100, fBaseline + 13);
929: }
930:
931: /**
932: * Component override.
933: */
934: public Dimension getPreferredSize() {
935: return getMinimumSize();
936: }
937: }
|