001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package javax.swing.text;
018:
019: import java.awt.Color;
020: import java.awt.event.ActionEvent;
021: import java.util.Enumeration;
022: import javax.swing.Action;
023: import javax.swing.JEditorPane;
024: import javax.swing.event.CaretEvent;
025: import javax.swing.event.CaretListener;
026:
027: import org.apache.harmony.awt.text.TextUtils;
028:
029: import org.apache.harmony.x.swing.internal.nls.Messages;
030:
031: /**
032: * <p>
033: * <i>StyledEditorKit</i>
034: * </p>
035: * <h3>Implementation Notes:</h3>
036: * <ul>
037: * <li>The <code>serialVersionUID</code> fields are explicitly declared as a performance
038: * optimization, not as a guarantee of serialization compatibility.
039: * <em>This applies to nested classes as well</em>.</li>
040: * </ul>
041: */
042: public class StyledEditorKit extends DefaultEditorKit {
043:
044: public static class AlignmentAction extends StyledTextAction {
045: private static final long serialVersionUID = 1L;
046:
047: private final Object defaultValue;
048:
049: public AlignmentAction(final String name, final int alignment) {
050: super (name);
051: defaultValue = new Integer(alignment);
052: }
053:
054: public void actionPerformed(final ActionEvent event) {
055: JEditorPane pane = getEditorPane(event);
056: if (pane == null) {
057: return;
058: }
059: MutableAttributeSet attr = new SimpleAttributeSet();
060: Object newValue = null;
061: if (event != null) {
062: try {
063: newValue = new Integer(event.getActionCommand());
064: } catch (NumberFormatException e) {
065: }
066: }
067: newValue = (newValue != null) ? newValue : defaultValue;
068: attr.addAttribute(StyleConstants.Alignment, newValue);
069: setParagraphAttributes(pane, attr, false);
070: }
071: }
072:
073: public static class BoldAction extends StyledTextAction {
074: private static final long serialVersionUID = 1L;
075:
076: public BoldAction() {
077: super ("font-bold");
078: }
079:
080: public void actionPerformed(final ActionEvent event) {
081: performAction(event, StyleConstants.Bold, null, null, null,
082: true);
083: }
084: }
085:
086: public static class FontFamilyAction extends StyledTextAction {
087: private static final long serialVersionUID = 1L;
088:
089: private final Object defaultValue;
090:
091: public FontFamilyAction(final String name, final String family) {
092: super (name);
093: defaultValue = family;
094: }
095:
096: public void actionPerformed(final ActionEvent event) {
097: Object newValue = null;
098: if (event != null) {
099: try {
100: newValue = event.getActionCommand();
101: } catch (NumberFormatException e) {
102: }
103: }
104: performAction(event, StyleConstants.FontFamily, null,
105: defaultValue, newValue, false);
106: }
107: }
108:
109: public static class FontSizeAction extends StyledTextAction {
110: private static final long serialVersionUID = 1L;
111:
112: private final Object defaultValue;
113:
114: public FontSizeAction(final String name, final int size) {
115: super (name);
116: defaultValue = new Integer(size);
117: }
118:
119: public void actionPerformed(final ActionEvent event) {
120: Object newValue = null;
121: if (event != null) {
122: try {
123: newValue = new Integer(event.getActionCommand());
124: } catch (NumberFormatException e) {
125: }
126: }
127: performAction(event, StyleConstants.FontSize, null,
128: defaultValue, newValue, false);
129: }
130:
131: }
132:
133: public static class ForegroundAction extends StyledTextAction {
134: private static final long serialVersionUID = 1L;
135:
136: private final Object defaultValue;
137:
138: public ForegroundAction(final String name, final Color color) {
139: super (name);
140: defaultValue = color;
141: }
142:
143: public void actionPerformed(final ActionEvent event) {
144: Object newValue = null;
145: if (event != null) {
146: try {
147: newValue = Color.decode(event.getActionCommand());
148: } catch (NumberFormatException e) {
149: }
150: }
151: performAction(event, StyleConstants.Foreground, null,
152: defaultValue, newValue, false);
153: }
154: }
155:
156: public static class ItalicAction extends StyledTextAction {
157: private static final long serialVersionUID = 1L;
158:
159: public ItalicAction() {
160: super ("font-italic");
161: }
162:
163: public void actionPerformed(final ActionEvent event) {
164: performAction(event, StyleConstants.Italic, null, null,
165: null, true);
166: }
167: }
168:
169: public abstract static class StyledTextAction extends TextAction {
170: private static final String documentExceptionMessage = Messages
171: .getString("swing.48", "StyledDocument"); //$NON-NLS-1$ //$NON-NLS-2$
172: private static final String editorKitExceptionMessage = Messages
173: .getString("swing.49", "StyledEditorKit"); //$NON-NLS-1$ //$NON-NLS-2$
174:
175: public StyledTextAction(final String name) {
176: super (name);
177: }
178:
179: protected final JEditorPane getEditor(final ActionEvent event) {
180: if (event == null) {
181: return getFocusedEditorPane();
182: }
183: Object source = event.getSource();
184: if (source instanceof JEditorPane) {
185: return (JEditorPane) source;
186: } else {
187: return getFocusedEditorPane();
188: }
189: }
190:
191: final JEditorPane getFocusedEditorPane() {
192: JTextComponent textComponent = getFocusedComponent();
193: return (textComponent instanceof JEditorPane) ? (JEditorPane) textComponent
194: : null;
195:
196: }
197:
198: final JEditorPane getEditorPane(final ActionEvent e) {
199: JEditorPane pane = getEditor(e);
200: return (pane != null && pane.isEditable()) ? pane : null;
201: }
202:
203: protected final void setCharacterAttributes(
204: final JEditorPane c, final AttributeSet set,
205: final boolean replace) {
206: int selectionStart = c.getSelectionStart();
207: int selectionEnd = c.getSelectionEnd();
208: getStyledDocument(c).setCharacterAttributes(selectionStart,
209: selectionEnd - selectionStart, set, replace);
210:
211: MutableAttributeSet atts = getStyledEditorKit(c)
212: .getInputAttributes();
213: if (replace) {
214: atts.removeAttributes(atts.getAttributeNames());
215: }
216: atts.addAttributes(set);
217: }
218:
219: final AttributeSet getAttributeSetByOffset(final Document doc,
220: final int offset) {
221: Element elem = getElementByOffset(doc, offset);
222: return (elem == null) ? null : elem.getAttributes();
223: }
224:
225: final Boolean getNewValue(final JEditorPane pane,
226: final Object key) {
227: StyledEditorKit kit = getStyledEditorKit(pane);
228: Object oldValue = kit.getInputAttributes()
229: .getAttribute(key);
230: return (oldValue instanceof Boolean) ? Boolean
231: .valueOf(!((Boolean) oldValue).booleanValue())
232: : Boolean.TRUE;
233:
234: }
235:
236: protected final StyledDocument getStyledDocument(
237: final JEditorPane c) {
238: Document doc = c.getDocument();
239: if (!(doc instanceof StyledDocument)) {
240: throw new IllegalArgumentException(
241: documentExceptionMessage);
242: }
243: return (StyledDocument) doc;
244: }
245:
246: protected final StyledEditorKit getStyledEditorKit(
247: final JEditorPane c) {
248: EditorKit kit = c.getEditorKit();
249: if (!(kit instanceof StyledEditorKit)) {
250: throw new IllegalArgumentException(
251: editorKitExceptionMessage);
252: }
253: return (StyledEditorKit) kit;
254: }
255:
256: final void performAction(final ActionEvent event,
257: final Object attribute, final MutableAttributeSet set,
258: final Object defaultValue, final Object newValue,
259: final boolean isToggleAction) {
260: JEditorPane pane = getEditorPane(event);
261: if (pane == null) {
262: return;
263: }
264: MutableAttributeSet attr = (set == null) ? new SimpleAttributeSet()
265: : set;
266: Object value;
267: if (isToggleAction) {
268: value = getNewValue(pane, attribute);
269: } else {
270: value = (newValue != null) ? newValue : defaultValue;
271: }
272: attr.addAttribute(attribute, value);
273: setCharacterAttributes(pane, attr, false);
274: }
275:
276: protected final void setParagraphAttributes(
277: final JEditorPane c, final AttributeSet set,
278: final boolean replace) {
279:
280: TextUtils.setParagraphAttributes(set, replace, c,
281: getStyledDocument(c));
282: }
283:
284: }
285:
286: public static class UnderlineAction extends StyledTextAction {
287: private static final long serialVersionUID = 1L;
288:
289: public UnderlineAction() {
290: super ("font-underline");
291: }
292:
293: public void actionPerformed(final ActionEvent event) {
294: performAction(event, StyleConstants.Underline, null, null,
295: null, true);
296: }
297: }
298:
299: static final class ViewFactoryImpl implements ViewFactory {
300: public View create(final Element element) {
301: // if (true) {
302: // throw new UnsupportedOperationException(Messages.getString("swing.27")); //$NON-NLS-1$
303: // }
304: // return null;
305: //This code committed-out temporarily (while these views is not
306: //implemented)
307: String name = element.getName();
308: if (AbstractDocument.ParagraphElementName.equals(name)) {
309: return new ParagraphView(element);
310: } else if (AbstractDocument.SectionElementName.equals(name)) {
311: return new BoxView(element, View.Y_AXIS);
312: } else if (StyleConstants.ComponentElementName.equals(name)) {
313: return new ComponentView(element);
314: } else if (StyleConstants.IconElementName.equals(name)) {
315: return new IconView(element);
316: } else {
317: return new LabelView(element);
318: }
319: }
320: }
321:
322: private class CaretListenerImpl implements CaretListener {
323: int dot;
324:
325: public void caretUpdate(final CaretEvent ce) {
326: int newDot = ce.getDot();
327: if (newDot != dot) {
328: dot = newDot;
329: updateInputAttributes(dot);
330: }
331: }
332:
333: final void componentChanged() {
334: dot = editorPane.getCaretPosition();
335: updateInputAttributes(dot);
336: }
337: }
338:
339: private static final long serialVersionUID = 1L;
340:
341: private static final ViewFactory factory = new ViewFactoryImpl();
342:
343: private static Action[] actions;
344:
345: private JEditorPane editorPane;
346:
347: private CaretListenerImpl caretListener;
348:
349: private final MutableAttributeSet inputAttributes = new SimpleAttributeSet();
350:
351: public StyledEditorKit() {
352: createStaticActions();
353: }
354:
355: @Override
356: public Action[] getActions() {
357: return actions.clone();
358: }
359:
360: @Override
361: public Document createDefaultDocument() {
362: return new DefaultStyledDocument();
363: }
364:
365: protected void createInputAttributes(final Element element,
366: final MutableAttributeSet set) {
367: AttributeSet as = element.getAttributes();
368: set.removeAttributes(set);
369: for (Enumeration keys = as.getAttributeNames(); keys
370: .hasMoreElements();) {
371: Object key = keys.nextElement();
372: if (!StyleConstants.IconAttribute.equals(key)
373: && !StyleConstants.ComponentAttribute.equals(key)
374: && !AbstractDocument.ElementNameAttribute
375: .equals(key)) {
376: set.addAttribute(key, as.getAttribute(key));
377: }
378: }
379: }
380:
381: @Override
382: public void deinstall(final JEditorPane component) {
383: if (component == editorPane) {
384: if (editorPane != null && caretListener != null) {
385: editorPane.removeCaretListener(caretListener);
386: }
387: editorPane = null;
388: }
389: }
390:
391: private Action[] createStaticActions() {
392: if (actions == null) {
393: Action[] styledActions = getDefaultActions();
394: int styledActionsCount = styledActions.length;
395: Action[] super Actions = super .getActions();
396: int super ActionsCount = super Actions.length;
397: actions = new Action[styledActionsCount + super ActionsCount];
398: System.arraycopy(styledActions, 0, actions, 0,
399: styledActionsCount);
400: System.arraycopy(super Actions, 0, actions,
401: styledActionsCount, super ActionsCount);
402: }
403: return actions;
404: }
405:
406: public Element getCharacterAttributeRun() {
407: if (editorPane == null) {
408: return null;
409: }
410: return getElement();
411: }
412:
413: private Action[] getDefaultActions() {
414: return new Action[] {
415: new StyledEditorKit.FontSizeAction("font-size-48", 48),
416: new StyledEditorKit.FontSizeAction("font-size-36", 36),
417: new StyledEditorKit.FontSizeAction("font-size-24", 24),
418: new StyledEditorKit.FontSizeAction("font-size-18", 18),
419: new StyledEditorKit.FontSizeAction("font-size-16", 16),
420: new StyledEditorKit.FontSizeAction("font-size-14", 14),
421: new StyledEditorKit.FontSizeAction("font-size-12", 12),
422: new StyledEditorKit.FontSizeAction("font-size-10", 10),
423: new StyledEditorKit.FontSizeAction("font-size-8", 8),
424: new StyledEditorKit.FontFamilyAction(
425: "font-family-SansSerif", "SansSerif"),
426: new StyledEditorKit.FontFamilyAction(
427: "font-family-Serif", "Serif"),
428: new StyledEditorKit.FontFamilyAction(
429: "font-family-Monospaced", "Monospaced"),
430: new StyledEditorKit.BoldAction(),
431: new StyledEditorKit.UnderlineAction(),
432: new StyledEditorKit.ItalicAction(),
433: new StyledEditorKit.AlignmentAction("right-justify",
434: StyleConstants.ALIGN_RIGHT),
435: new StyledEditorKit.AlignmentAction("left-justify",
436: StyleConstants.ALIGN_LEFT),
437: new StyledEditorKit.AlignmentAction("center-justify",
438: StyleConstants.ALIGN_CENTER),
439: new StyledEditorKit.ForegroundAction("font-foreground",
440: Color.BLACK) };
441: }
442:
443: public MutableAttributeSet getInputAttributes() {
444: return inputAttributes;
445: }
446:
447: private Element getElement() {
448: int dot = editorPane.getCaretPosition();
449: return getElementByOffset(editorPane.getDocument(), dot);
450: }
451:
452: private static Element getElementByOffset(final Document doc,
453: final int offset) {
454: if (doc == null) {
455: return null;
456: }
457: int pos = offset;
458: Element elem = doc.getDefaultRootElement();
459: while (elem.getElementCount() > 0) {
460: Element tmp = elem.getElement(elem.getElementIndex(pos));
461: elem = tmp.getElementCount() > 0 ? tmp : elem
462: .getElement(elem.getElementIndex(Math.max(0,
463: pos - 1)));
464: }
465: return elem;
466: }
467:
468: @Override
469: public ViewFactory getViewFactory() {
470: return factory;
471: }
472:
473: @Override
474: public void install(final JEditorPane component) {
475: editorPane = component;
476: if (caretListener == null) {
477: caretListener = new CaretListenerImpl();
478: }
479: if (editorPane != null) {
480: editorPane.addCaretListener(caretListener);
481: caretListener.componentChanged();
482: }
483: }
484:
485: private void updateInputAttributes(final int dot) {
486: inputAttributes.removeAttributes(inputAttributes);
487: Element element = getElementByOffset(editorPane.getDocument(),
488: dot);
489: createInputAttributes(element, inputAttributes);
490: }
491:
492: static final void writeLock(final Document doc) {
493: if (doc instanceof AbstractDocument) {
494: ((AbstractDocument) doc).writeLock();
495: }
496: }
497:
498: static final void writeUnlock(final Document doc) {
499: if (doc instanceof AbstractDocument) {
500: ((AbstractDocument) doc).writeUnlock();
501: }
502: }
503: }
|