| /* |
| * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved. |
| * |
| * The program is provided "as is" without any warranty express or |
| * implied, including the warranty of non-infringement and the implied |
| * warranties of merchantibility and fitness for a particular purpose. |
| * IBM will not be liable for any damages suffered by you as a result |
| * of using the Program. In no event will IBM be liable for any |
| * special, indirect or consequential damages or lost profits even if |
| * IBM has been advised of the possibility of their occurrence. IBM |
| * will not be liable for any third party claims against you. |
| */ |
| package com.ibm.richtext.uiimpl; |
| |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.Dimension; |
| import java.awt.FontMetrics; |
| import java.awt.Graphics; |
| import java.awt.Image; |
| |
| import java.awt.event.MouseMotionListener; |
| import java.awt.event.MouseListener; |
| import java.awt.event.MouseEvent; |
| |
| import com.ibm.richtext.textlayout.attributes.AttributeMap; |
| import com.ibm.richtext.textlayout.attributes.TextAttribute; |
| |
| import com.ibm.richtext.styledtext.MTabRuler; |
| import com.ibm.richtext.styledtext.StandardTabRuler; |
| import com.ibm.richtext.styledtext.TabStop; |
| import com.ibm.richtext.styledtext.StyleModifier; |
| |
| import com.ibm.richtext.textpanel.TextPanelListener; |
| import com.ibm.richtext.textpanel.MTextPanel; |
| import com.ibm.richtext.textpanel.TextPanel; |
| import com.ibm.richtext.textpanel.TextPanelEvent; |
| |
| /** |
| * TabRuler is a Component which presents a user interface for |
| * setting the leading margin, trailing margin, first line indent, |
| * and tab types and positions. |
| * <p> |
| * TabRuler does not implement TextPanelListener directly; however, |
| * it can receive updates from a MTextPanel. To have a TabRuler listen |
| * to a panel, call <code>listenToPanel</code>. TabRuler responds to |
| * user manipulation by modifying the paragraph styles on its MTextPanel |
| * (if any). |
| */ |
| public final class TabRulerImpl implements MouseListener, MouseMotionListener |
| { |
| static final String COPYRIGHT = |
| "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved"; |
| private static final class TabStopBuffer { |
| public int fPosition; |
| public byte fType; |
| |
| TabStopBuffer(int position, byte type) { |
| fPosition = position; |
| fType = type; |
| } |
| |
| TabStopBuffer(TabStop tab) { |
| this(tab.getPosition(), tab.getType()); |
| } |
| |
| TabStop getTabStop() { |
| return new TabStop(fPosition, fType); |
| } |
| } |
| |
| private static final class TabRulerModifier extends StyleModifier { |
| |
| private TabStop fOldTab; // tab to remove |
| private TabStop fNewTab; // tab to add |
| private AttributeMap fPanelDefaults; |
| |
| TabRulerModifier(TabStop oldTab, |
| TabStop newTab, |
| AttributeMap panelDefaults) { |
| fOldTab = oldTab; |
| fNewTab = newTab; |
| fPanelDefaults = panelDefaults; |
| } |
| |
| public AttributeMap modifyStyle(AttributeMap oldStyle) { |
| |
| MTabRuler oldRuler = (MTabRuler) getWithDefault(TextAttribute.TAB_RULER, |
| oldStyle, |
| fPanelDefaults); |
| MTabRuler ruler = oldRuler; |
| |
| if (fOldTab != null) { |
| if (ruler.containsTab(fOldTab)) { |
| ruler = ruler.removeTab(fOldTab.getPosition()); |
| } |
| } |
| if (fNewTab != null) { |
| ruler = ruler.addTab(fNewTab); |
| } |
| |
| if (ruler != oldRuler) { |
| return oldStyle.addAttribute(TextAttribute.TAB_RULER, ruler); |
| } |
| else { |
| return oldStyle; |
| } |
| } |
| } |
| |
| private static final class ImageCache { |
| |
| static final String COPYRIGHT = |
| "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved"; |
| private Image fImage = null; |
| private boolean fIsValid = false; |
| private Component fComponent; // workaround for compiler bug, |
| // should just be able to say Component.this |
| // if this were not a static class |
| |
| ImageCache(Component component) { |
| |
| fComponent = component; |
| } |
| |
| Graphics getGraphics(int width, int height) { |
| |
| if (width <= 0 || height <= 0) { |
| return null; |
| } |
| |
| Image image = fImage; |
| if (image == null || image.getWidth(fComponent) < width |
| || image.getHeight(fComponent) < height) { |
| if (!fComponent.isVisible()) { // fix race condition if component not fully initialized |
| return null; |
| } |
| image = fComponent.createImage(width, height); |
| } |
| Graphics g = image.getGraphics(); |
| fImage = image; |
| return g; |
| } |
| |
| void drawImage(Graphics g, int x, int y, Color color) { |
| |
| if (!fIsValid) { |
| throw new Error("Drawing image when not valid"); |
| } |
| g.drawImage(fImage, x, y, color, fComponent); |
| } |
| |
| boolean isValid() { |
| |
| return fIsValid; |
| } |
| |
| void setValid(boolean isValid) { |
| |
| fIsValid = isValid; |
| } |
| } |
| |
| /** |
| * This class listens to a MTextPanel for changes which |
| * affect a TabRuler's appearance, and updates the TabRuler |
| * as necessary. |
| * @see TabRuler |
| * @see com.ibm.richtext.textpanel.MTextPanel |
| */ |
| private static final class Updater implements TextPanelListener { |
| |
| static final String COPYRIGHT = |
| "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved"; |
| private TabRulerImpl fTabRuler; |
| private MTextPanel fTextPanel; |
| |
| /** |
| * Create a new TabRulerUpdater. |
| * @param tabRuler the TabRuler to update when a change occurs |
| * in the MTextPanel |
| */ |
| Updater(TabRulerImpl tabRuler) { |
| |
| fTabRuler = tabRuler; |
| } |
| |
| /** |
| * Remove self as listener from previous MTextPanel, |
| * set current MTextPanel and listen to it (if not null). |
| */ |
| void setTextPanel(MTextPanel textPanel) { |
| |
| if (fTextPanel != null) { |
| fTextPanel.removeListener(this); |
| } |
| |
| fTextPanel = textPanel; |
| |
| if (fTextPanel != null) { |
| fTextPanel.addListener(this); |
| setAll(); |
| } |
| } |
| |
| private void setAll() { |
| |
| int offset = fTextPanel.getSelectionStart(); |
| boolean leftToRight = fTextPanel.paragraphIsLeftToRight(offset); |
| AttributeMap style = fTextPanel.getText().paragraphStyleAt(offset); |
| fTabRuler.set(style, false); |
| fTabRuler.setFormatWidth(fTextPanel.getFormatWidth(), false); |
| fTabRuler.setLeftToRight(leftToRight, true); |
| } |
| |
| /** |
| * TextPanelListener method. This class responds to text |
| * changes by updating its TabRuler. |
| */ |
| public void textEventOccurred(TextPanelEvent event) { |
| |
| int changeCode = event.getID(); |
| |
| if (changeCode == TextPanelEvent.SELECTION_STYLES_CHANGED || |
| changeCode == TextPanelEvent.TEXT_CHANGED) { |
| |
| int offset = fTextPanel.getSelectionStart(); |
| AttributeMap style = fTextPanel.getText().paragraphStyleAt(offset); |
| boolean leftToRight = fTextPanel.paragraphIsLeftToRight(offset); |
| fTabRuler.set(style, false); |
| fTabRuler.setLeftToRight(leftToRight, true); |
| } |
| else if (changeCode == TextPanelEvent.FORMAT_WIDTH_CHANGED) { |
| |
| fTabRuler.setFormatWidth(fTextPanel.getFormatWidth(), true); |
| } |
| } |
| |
| /** |
| * TextPanelListener method. |
| */ |
| public boolean respondsToEventType(int type) { |
| |
| return type == TextPanelEvent.SELECTION_STYLES_CHANGED || |
| type == TextPanelEvent.TEXT_CHANGED || |
| type == TextPanelEvent.FORMAT_WIDTH_CHANGED; |
| } |
| } |
| |
| /** |
| * The default background color for TabRulers. |
| * @see #setBackColor |
| */ |
| public static final Color DEFAULT_BACK_COLOR = Color.lightGray; |
| |
| private static final int kTrackNone = 0; |
| private static final int kTrackTab = 1; |
| private static final int kTrackLM = 2; |
| private static final int kTrackFLI = 3; |
| private static final int kTrackTM = 4; |
| |
| private Component fHost; |
| private MTabRuler fRuler; |
| private int fLeadingMargin; |
| private int fFirstLineIndent; |
| private int fFormatWidth; |
| private int fTrailingMarginPosition; // opposite of actual trailing margin |
| private boolean fLeftToRight; |
| private int fBaseline; |
| private int fOrigin; |
| private Color fBackColor = DEFAULT_BACK_COLOR; |
| |
| private int fTrackItem; // 0 - none, 1 - tab, 2 - lm, 3 - fli, 4 - tm |
| private TabStopBuffer fTrackTab; |
| private TabStop fOldTab; |
| private int fTrackDelta; |
| private boolean fTrackVisible; |
| private Updater fUpdater; |
| private MTextPanel fTextPanel = null; |
| |
| private ImageCache fImageCache; |
| |
| /** |
| * Create a new TabRuler. |
| * @param baseline the y-coordinate of the ruler's baseline |
| * @param origin the x-coordinate in this Component where |
| * the left margin appears |
| * @param textPanel the MTextPanel to listen to. This TabRuler |
| * will reflect the MTextPanel's paragraph styles, and update |
| * the paragraph styles when manipulated. |
| */ |
| public TabRulerImpl(int baseline, |
| int origin, |
| MTextPanel textPanel, |
| Component host) { |
| |
| fHost = host; |
| fImageCache = new ImageCache(host); |
| fUpdater = new Updater(this); |
| fBaseline = baseline; |
| fOrigin = origin; |
| host.addMouseListener(this); |
| host.addMouseMotionListener(this); |
| if (textPanel != null) { |
| listenToTextPanel(textPanel); |
| } |
| else { |
| fRuler = new StandardTabRuler(); |
| } |
| } |
| |
| /** |
| * Listen to the given MTextPanel and reflect its changes, |
| * and update its paragraph styles when TabRuler is |
| * manipulated. |
| * @param textPanel the MTextPanel to listen to |
| */ |
| public void listenToTextPanel(MTextPanel textPanel) { |
| |
| fTextPanel = textPanel; |
| fUpdater.setTextPanel(textPanel); |
| } |
| |
| /** |
| * Return the background color of this TabRuler. |
| * @return the background color of this TabRuler |
| */ |
| public Color getBackColor() { |
| |
| return fBackColor; |
| } |
| |
| /** |
| * Set the background color of this TabRuler. |
| * @param backColor the new background color of this TabRuler |
| */ |
| public void setBackColor(Color backColor) { |
| |
| if (!backColor.equals(fBackColor)) { |
| fBackColor = backColor; |
| Graphics g = fHost.getGraphics(); |
| if (g != null) { |
| paint(g); |
| } |
| } |
| } |
| |
| private static Object getWithDefault(Object key, |
| AttributeMap style, |
| AttributeMap defaults) { |
| Object value = style.get(key); |
| if (value == null) { |
| value = defaults.get(key); |
| } |
| return value; |
| } |
| |
| private static float getFloatWithDefault(Object key, |
| AttributeMap style, |
| AttributeMap defaults) { |
| Object value = getWithDefault(key, style, defaults); |
| return ((Float)value).floatValue(); |
| } |
| |
| private void setLeftToRight(boolean leftToRight, boolean update) { |
| |
| if (fLeftToRight != leftToRight) { |
| |
| fLeftToRight = leftToRight; |
| redrawSelf(update); |
| } |
| } |
| |
| private void setFormatWidth(int formatWidth, boolean update) { |
| |
| if (fFormatWidth != formatWidth) { |
| |
| fTrailingMarginPosition += (formatWidth - fFormatWidth); |
| fFormatWidth = formatWidth; |
| redrawSelf(update); |
| } |
| } |
| |
| /** |
| * Set TabRuler from values in paragraphStyle. Only TabRulerUpdater |
| * should call this method. |
| * @param paragraphStyle the paragraph style which the TabRuler will |
| * reflect |
| */ |
| private void set(AttributeMap paragraphStyle, boolean update) { |
| |
| AttributeMap panelDefaults; |
| |
| if (fTextPanel==null) { |
| panelDefaults = TextPanel.getDefaultSettings().getDefaultValues(); |
| } |
| else { |
| panelDefaults = fTextPanel.getDefaultValues(); |
| } |
| |
| int leadingMargin = (int) getFloatWithDefault(TextAttribute.LEADING_MARGIN, |
| paragraphStyle, |
| panelDefaults); |
| int firstLineIndent = (int) getFloatWithDefault(TextAttribute.FIRST_LINE_INDENT, |
| paragraphStyle, |
| panelDefaults); |
| int trailingMargin = (int) getFloatWithDefault(TextAttribute.TRAILING_MARGIN, |
| paragraphStyle, |
| panelDefaults); |
| |
| MTabRuler ruler = (MTabRuler) getWithDefault(TextAttribute.TAB_RULER, |
| paragraphStyle, |
| panelDefaults); |
| |
| int ourFli = leadingMargin + firstLineIndent; |
| int ourTmp = fFormatWidth - trailingMargin; |
| |
| if (leadingMargin == fLeadingMargin && |
| fFirstLineIndent == ourFli && |
| fTrailingMarginPosition == ourTmp && |
| ruler.equals(fRuler)) { |
| return; |
| } |
| |
| fLeadingMargin = leadingMargin; |
| fFirstLineIndent = ourFli; |
| fTrailingMarginPosition = ourTmp; |
| fRuler = ruler; |
| |
| redrawSelf(update); |
| } |
| |
| private void redrawSelf(boolean drawNow) { |
| |
| fImageCache.setValid(false); |
| |
| Graphics g = fHost.getGraphics(); |
| if (g != null) |
| paint(g); |
| } |
| |
| /** |
| * Return debugging info. |
| */ |
| public String toString() { |
| |
| return "TabRuler{fLeadingMargin="+fLeadingMargin+ |
| "}{fFirstLineIndent="+fFirstLineIndent+ |
| "}{fFormatWidth="+fFormatWidth+ |
| "}{fTrailingMarginPosition="+fTrailingMarginPosition+ |
| "}{fRuler="+fRuler+ |
| "}"; |
| } |
| |
| /** |
| * Return the MTabRuler represented by this TabRuler. |
| * @return the MTabRuler represented by this TabRuler |
| */ |
| public MTabRuler getRuler() |
| { |
| return fRuler; |
| } |
| |
| /** |
| * Return the leading margin of this TabRuler. |
| * @return the leading margin of this TabRuler |
| */ |
| public int getLeadingMargin() |
| { |
| return fLeadingMargin; |
| } |
| |
| /** |
| * Return the first line indent of this TabRuler. |
| * @return the first line indent of this TabRuler |
| */ |
| public int getFirstLineIndent() |
| { |
| return fFirstLineIndent - fLeadingMargin; |
| } |
| |
| /** |
| * Return the trailing margin of this TabRuler. |
| * @return the trailing margin of this TabRuler |
| */ |
| public final int getTrailingMargin() |
| { |
| return fFormatWidth - fTrailingMarginPosition; |
| } |
| |
| private int visualToRulerPos(int visPos) { |
| |
| if (fLeftToRight) { |
| return visPos - fOrigin; |
| } |
| else { |
| return fOrigin + fFormatWidth - visPos; |
| } |
| } |
| |
| private int rulerToVisualPos(int rulerPos) { |
| |
| if (fLeftToRight) { |
| return fOrigin + rulerPos; |
| } |
| else { |
| return fOrigin + fFormatWidth - rulerPos; |
| } |
| } |
| |
| private int dirMult() { |
| |
| return fLeftToRight? 1 : -1; |
| } |
| |
| /** |
| * @param tabPosition the logical (ruler) position of the tab |
| */ |
| private void drawTab(Graphics g, int tabPosition, byte tabType, int tabTop, int tabBottom) |
| { |
| int pos = rulerToVisualPos(tabPosition); |
| int wid = 0; |
| switch (tabType) { |
| case TabStop.kLeading: wid = 3; break; |
| case TabStop.kCenter: wid = 0; break; |
| case TabStop.kTrailing: wid = -3; break; |
| case TabStop.kDecimal: wid = 0; break; |
| default: break; |
| } |
| wid *= dirMult(); |
| |
| if (tabType != TabStop.kAuto) { |
| g.drawLine(pos, tabTop, pos, tabBottom); |
| if (wid != 0) |
| g.drawLine(pos, tabBottom, pos + wid, tabBottom); |
| } |
| g.drawLine(pos-2, tabTop+2, pos, tabTop); |
| g.drawLine(pos, tabTop, pos+2, tabTop+2); |
| if (tabType == TabStop.kDecimal) { |
| g.drawLine(pos + 3, tabBottom, pos + 4, tabBottom); |
| } |
| } |
| |
| private void drawLM(Graphics g) |
| { |
| int pos = rulerToVisualPos(fLeadingMargin); |
| int[] xpts = { pos, pos, pos + (4*dirMult()), pos }; |
| int[] ypts = { fBaseline + 12, fBaseline + 7, fBaseline + 7, fBaseline + 12 }; |
| g.fillPolygon(xpts, ypts, 3); |
| g.drawPolygon(xpts, ypts, 4); |
| } |
| |
| private void drawFLI(Graphics g) |
| { |
| int pos = rulerToVisualPos(fFirstLineIndent); |
| int[] xpts = { pos, pos, pos + (4*dirMult()), pos }; |
| int[] ypts = { fBaseline, fBaseline + 5, fBaseline + 5, fBaseline }; |
| g.fillPolygon(xpts, ypts, 3); |
| g.drawPolygon(xpts, ypts, 4); |
| } |
| |
| private void drawRM(Graphics g) |
| { |
| int pos = rulerToVisualPos(fTrailingMarginPosition); |
| int[] xpts = { pos, pos, pos - (6*dirMult()), pos }; |
| int[] ypts = { fBaseline, fBaseline + 12, fBaseline + 6, fBaseline }; |
| g.fillPolygon(xpts, ypts, 3); |
| g.drawPolygon(xpts, ypts, 4); |
| } |
| |
| private static int alignInt(int value) { |
| |
| return (int)((int)(value / 4.5) * 4.5); |
| } |
| |
| private static final int[] fgLengths = { 10, 2, 4, 2, 6, 2, 4, 2 }; |
| |
| /** |
| * Component method override. |
| */ |
| public void paint(Graphics g) |
| { |
| Dimension size = fHost.getSize(); |
| |
| int width = size.width; |
| int baseline = fBaseline; |
| int baseline2 = baseline + 2; |
| int baseline10 = baseline + 10; |
| int baseline12 = baseline + 12; |
| |
| if (!fImageCache.isValid()) { |
| |
| Graphics gCache = fImageCache.getGraphics(width, baseline12 + 1); |
| if (gCache == null) { |
| return; |
| } |
| |
| // set background color |
| |
| gCache.setColor(fBackColor); |
| gCache.setPaintMode(); |
| gCache.fillRect(0, 0, width, baseline12 + 1); |
| |
| // paint ticks |
| |
| gCache.setColor(Color.black); |
| gCache.drawLine(0, 0, width, 0); |
| gCache.drawLine(0, baseline, width, baseline); |
| |
| int[] lengths = fgLengths; |
| |
| int index = 0; |
| int inchnum = 0; |
| FontMetrics fm = null; |
| if (!fLeftToRight) { |
| fm = gCache.getFontMetrics(); |
| } |
| |
| for (int i = 0; i < fFormatWidth; i += 9) { |
| int len = lengths[index]; |
| int pos = rulerToVisualPos(i); |
| gCache.drawLine(pos, baseline, pos, baseline - len); |
| |
| if (index == 0) { |
| String str = Integer.toString(inchnum++); |
| int drawX; |
| if (fLeftToRight) { |
| drawX = pos + 2; |
| } |
| else { |
| drawX = pos - fm.stringWidth(str) - 2; |
| } |
| |
| gCache.drawString(str, drawX, baseline - 2); |
| } |
| |
| if (++index == lengths.length) |
| index = 0; |
| } |
| |
| // paint tabs |
| TabStop tab = fRuler.firstTab(); |
| while (tab != null && tab.getPosition() < fTrailingMarginPosition) { |
| boolean dodraw = true; |
| if (tab.getType() == TabStop.kAuto) { |
| if (tab.getPosition() <= Math.max(fLeadingMargin, fFirstLineIndent)) |
| dodraw = false; |
| else if (tab.getPosition() >= fTrailingMarginPosition) |
| dodraw = false; |
| } |
| |
| if (dodraw) |
| drawTab(gCache, tab.getPosition(), tab.getType(), baseline2, baseline10); |
| |
| tab = fRuler.nextTab(tab.getPosition()); |
| } |
| |
| gCache.drawLine(0, baseline12, width, baseline12); |
| |
| // paint others except for tracked item |
| if (fTrackItem != kTrackLM) drawLM(gCache); |
| if (fTrackItem != kTrackTM) drawRM(gCache); |
| if (fTrackItem != kTrackFLI && fTrackItem != kTrackLM) drawFLI(gCache); |
| fImageCache.setValid(true); |
| } |
| |
| fImageCache.drawImage(g, 0, 0, Color.lightGray); |
| |
| switch (fTrackItem) { |
| case kTrackTab: if (fTrackVisible) drawTab(g, fTrackTab.fPosition, fTrackTab.fType, baseline2, baseline10); break; |
| case kTrackLM: drawLM(g); drawFLI(g); break; |
| case kTrackTM: drawRM(g); break; |
| case kTrackFLI: drawFLI(g); break; |
| default: break; |
| } |
| } |
| |
| /** |
| * MouseListener method. |
| */ |
| public void mouseClicked(MouseEvent e) {} |
| /** |
| * MouseListener method. |
| */ |
| public void mouseEntered(MouseEvent e) {} |
| /** |
| * MouseListener method. |
| */ |
| public void mouseExited(MouseEvent e) {} |
| |
| /** |
| * MouseListener method. |
| */ |
| public void mousePressed(MouseEvent e) |
| { |
| // find out if we hit a tabstop |
| int x = visualToRulerPos(e.getX()); |
| int y = e.getY(); |
| |
| if (y > fBaseline && y < fBaseline + 12) { |
| if (y >= fBaseline + 7 && x >= fLeadingMargin - 3 && x <= fLeadingMargin + 3) { |
| fTrackItem = kTrackLM; |
| fTrackDelta = fLeadingMargin - x; |
| } else if (y < fBaseline + 7 && x >= fFirstLineIndent - 3 && x <= fFirstLineIndent + 3) { |
| fTrackItem = kTrackFLI; |
| fTrackDelta = fFirstLineIndent - x; |
| } else if (x >= fTrailingMarginPosition - 3 && x <= fTrailingMarginPosition + 3) { |
| fTrackItem = kTrackTM; |
| fTrackDelta = fTrailingMarginPosition - x; |
| } else if (e.isControlDown()) { |
| fTrackItem = kTrackTab; |
| fTrackTab = new TabStopBuffer(alignInt(x), TabStop.kLeading); |
| fTrackDelta = fTrackTab.fPosition - x; |
| fTrackVisible = true; |
| } else { |
| TabStop tab = fRuler.firstTab(); |
| while (tab.getType() != TabStop.kAuto) { |
| if (x < tab.getPosition() - 3) |
| break; |
| if (x < tab.getPosition() + 3) { |
| fOldTab = tab; |
| fTrackTab = new TabStopBuffer(tab); |
| fRuler = fRuler.removeTab(fOldTab.getPosition()); |
| |
| if (e.getClickCount() > 1) { |
| switch (fTrackTab.fType) { |
| case TabStop.kLeading: fTrackTab.fType = TabStop.kCenter; break; |
| case TabStop.kCenter: fTrackTab.fType = TabStop.kTrailing; break; |
| case TabStop.kTrailing: fTrackTab.fType = TabStop.kDecimal; break; |
| case TabStop.kDecimal: fTrackTab.fType = TabStop.kLeading; break; |
| default: break; |
| } |
| } |
| fTrackItem = kTrackTab; |
| fTrackDelta = tab.getPosition() - x; |
| fTrackVisible = true; |
| break; |
| } |
| |
| tab = fRuler.nextTab(tab.getPosition()); |
| } |
| } |
| |
| if (fTrackItem != kTrackNone) { |
| fImageCache.setValid(false); |
| paint(fHost.getGraphics()); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * MouseListener method. |
| */ |
| public void mouseDragged(MouseEvent e) |
| { |
| int x = visualToRulerPos(e.getX()); |
| int y = e.getY(); |
| |
| if (fTrackItem != kTrackNone) { |
| boolean repaint = false; |
| boolean inrange = y > fBaseline && y < fBaseline + 12; |
| boolean inbigrange = y > 0 && y < fHost.getSize().height + 20; |
| int newpos = alignInt(x + fTrackDelta); |
| if (newpos < 0) |
| newpos = 0; |
| |
| switch (fTrackItem) { |
| case kTrackTab: { |
| if (inrange) { |
| repaint = !fTrackVisible; |
| fTrackVisible = true; |
| if (newpos != fTrackTab.fPosition) { |
| fTrackTab.fPosition = newpos; |
| repaint = true; |
| } |
| } else if (fTrackVisible) { |
| fTrackVisible = false; |
| repaint = true; |
| } |
| } break; |
| |
| /* It would be nice to optionally track the margin 'independently' of the first line indent. |
| Unfortunately this makes for more work when we have multiple paragraph styles selected. |
| Since internally the first line indent is relative to the margin, moving the margin |
| independently so that all affected paragraphs share the same margin but retain first |
| line indents in the 'same' positions means that I need to also adjust the first line |
| indents in each paragraph by some delta. I'm not ready to do that yet. */ |
| |
| case kTrackLM: { |
| if (inbigrange && newpos != fLeadingMargin) { |
| fFirstLineIndent += newpos - fLeadingMargin; |
| fLeadingMargin = newpos; |
| repaint = true; |
| } |
| } break; |
| |
| case kTrackFLI: { |
| if (inbigrange && newpos != fFirstLineIndent) { |
| fFirstLineIndent = newpos; |
| repaint = true; |
| } |
| } break; |
| |
| case kTrackTM: { |
| if (inbigrange && newpos != fTrailingMarginPosition) { |
| fTrailingMarginPosition = newpos; |
| repaint = true; |
| } |
| } break; |
| } |
| |
| |
| if (repaint) |
| paint(fHost.getGraphics()); |
| } |
| } |
| |
| /** |
| * MouseListener method. |
| */ |
| public void mouseReleased(MouseEvent e) |
| { |
| if (fTrackItem != kTrackNone) { |
| if (fTrackItem == kTrackTab && fTrackVisible) { |
| fRuler = fRuler.addTab(fTrackTab.getTabStop()); |
| } else { |
| fTrackTab = null; |
| } |
| |
| notify(fTrackItem); |
| |
| fTrackItem = kTrackNone; |
| fTrackTab = null; |
| fOldTab = null; |
| |
| fImageCache.setValid(false); |
| paint(fHost.getGraphics()); |
| } |
| } |
| |
| /** |
| * MouseListener method. |
| */ |
| public void mouseMoved(MouseEvent e) {} |
| |
| private void notify(int change) |
| { |
| if (fTextPanel != null) { |
| |
| StyleModifier modifier; |
| |
| if (change == kTrackTab) { |
| TabStop newTab = fTrackTab==null? null : fTrackTab.getTabStop(); |
| modifier = new TabRulerModifier(fOldTab, newTab, fTextPanel.getDefaultValues()); |
| } |
| else { |
| Object key; |
| Object value; |
| |
| switch(change) { |
| case kTrackLM: |
| key = TextAttribute.LEADING_MARGIN; |
| value = new Float(getLeadingMargin()); |
| break; |
| |
| case kTrackTM: |
| key = TextAttribute.TRAILING_MARGIN; |
| value = new Float(getTrailingMargin()); |
| break; |
| |
| case kTrackFLI: |
| key = TextAttribute.FIRST_LINE_INDENT; |
| value = new Float(getFirstLineIndent()); |
| break; |
| |
| default: |
| throw new Error("Invalid change code."); |
| } |
| |
| modifier = StyleModifier.createAddModifier(key, value); |
| } |
| |
| fTextPanel.modifyParagraphStyleOnSelection(modifier); |
| } |
| } |
| |
| /** |
| * Component override. |
| */ |
| public Dimension getMinimumSize() |
| { |
| return new Dimension(100, fBaseline + 13); |
| } |
| |
| /** |
| * Component override. |
| */ |
| public Dimension getPreferredSize() |
| { |
| return getMinimumSize(); |
| } |
| } |