001: // Copyright (c) 2002 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.jemacs.swing;
005:
006: import gnu.jemacs.buffer.*;
007:
008: import java.awt.event.FocusEvent;
009: import java.awt.event.KeyEvent;
010: import javax.swing.*;
011: import javax.swing.text.*;
012: import java.awt.*;
013: import java.util.Hashtable;
014:
015: /** An Emacs window (EWindow) implemented using the Swing toolkits. */
016:
017: public class SwingWindow extends EWindow implements
018: java.awt.event.FocusListener, java.awt.event.KeyListener,
019: javax.swing.event.ChangeListener {
020: /* Map JTextPane to SwingWindow. */
021: static Hashtable panemap = new Hashtable();
022:
023: JTextPane jtextpane;
024: /** The panel that contains this window and the modeline. */
025: JPanel panel;
026: JScrollPane scrollPane;
027:
028: public Modeline modeline;
029:
030: public SwingWindow(Buffer buffer) {
031: this (buffer, true);
032: }
033:
034: /** Create new Window.
035: * @param buffer the Buffer containing the data.
036: * @param wantModeline true if we should create a mode line
037: */
038: public SwingWindow(Buffer buffer, boolean wantModeline) {
039: super (null);
040: jtextpane = new JTextPane(((SwingBuffer) buffer).doc);
041: panemap.put(jtextpane, this );
042: if (wantModeline)
043: modeline = new Modeline(this ,
044: ((SwingBuffer) buffer).modelineDocument);
045: this .buffer = buffer;
046: jtextpane.addFocusListener(this );
047: jtextpane.addKeyListener(this );
048: }
049:
050: /** Warp this (and optional modeline) inside a ScrollPane in a new JPanel. */
051: public JPanel wrap() {
052: BorderLayout layout = new BorderLayout();
053: JPanel panel = new JPanel(layout);
054: scrollPane = new JScrollPane(jtextpane,
055: JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
056: JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
057: panel.add(scrollPane, BorderLayout.CENTER);
058: if (modeline != null)
059: panel.add(modeline, BorderLayout.SOUTH);
060: this .panel = panel;
061: return panel;
062: }
063:
064: /** Get the JPanel containing this Window. */
065: public JPanel getPanel() {
066: return panel;
067: }
068:
069: public void focusGained(FocusEvent e) {
070: setSelected();
071: }
072:
073: public void focusLost(FocusEvent e) {
074: }
075:
076: public void requestFocus() {
077: jtextpane.requestFocus();
078: }
079:
080: public void setBuffer(Buffer buffer) {
081: if (this .buffer == buffer)
082: return;
083:
084: super .setBuffer(buffer);
085: jtextpane.setDocument(((SwingBuffer) buffer).doc);
086: if (modeline != null)
087: modeline
088: .setDocument(((SwingBuffer) buffer).modelineDocument);
089: EWindow selected = getSelected();
090: if (selected == this ) {
091: unselect();
092: // Change buffer's pointMarker so it follows this EWindow's Caret.
093: Caret caret = jtextpane.getCaret();
094: caret.setDot(buffer.getDot());
095: select(caret);
096: }
097: }
098:
099: public void unselect() {
100: Caret caret = ((SwingBuffer) buffer).curPosition;
101: if (caret == null)
102: return;
103: int point = caret.getDot();
104: int index = ((SwingBuffer) buffer).content.buffer.createPos(
105: point, true);
106: buffer.pointMarker.ipos = index;
107: ((SwingBuffer) buffer).curPosition = null;
108: jtextpane.getCaret().removeChangeListener(this );
109: // ?? selected = null;
110: }
111:
112: public void setSelected() {
113: super .setSelected();
114: select(jtextpane.getCaret());
115: }
116:
117: public int getPoint() {
118: return 1 + jtextpane.getCaret().getDot();
119: }
120:
121: public void setDot(int offset) {
122: jtextpane.getCaret().setDot(offset);
123: }
124:
125: public EWindow split(Buffer buffer, int lines, boolean horizontal) {
126: SwingWindow window = new SwingWindow(buffer);
127: EFrame frame = this .frame;
128: window.frame = frame;
129: linkSibling(window, horizontal);
130: window.modeline = new Modeline(window,
131: ((SwingBuffer) buffer).modelineDocument);
132:
133: JPanel panel = this .getPanel();
134: java.awt.Dimension oldSize = panel.getSize();
135: java.awt.Container oldParent = panel.getParent();
136: oldParent.remove(panel);
137: JSplitPane split = new JSplitPane(
138: horizontal ? JSplitPane.HORIZONTAL_SPLIT
139: : JSplitPane.VERTICAL_SPLIT, panel, window
140: .wrap());
141: split.setDividerSize(2);
142: // FIXME - use lines.
143: split.setDividerLocation((horizontal ? oldSize.width
144: : oldSize.height) / 2);
145: oldParent.add(split);
146: oldParent.validate();
147: if (this == EWindow.getSelected())
148: this .requestFocus();
149: return window;
150: }
151:
152: private void select(Caret caret) {
153: // Change buffer's pointMarker so it follows this EWindow's Caret.
154: ((SwingBuffer) buffer).curPosition = caret;
155: if (!buffer.pointMarker.isPoint())
156: ((SwingBuffer) buffer).content.buffer
157: .releasePos(buffer.pointMarker.ipos);
158: buffer.pointMarker.sequence = null;
159: caret.addChangeListener(this );
160: }
161:
162: public void stateChanged(javax.swing.event.ChangeEvent e) {
163: Object source = e.getSource();
164: if (source instanceof Caret && buffer != null) {
165: Caret caret = (Caret) source;
166: int mark = caret.getMark();
167: int dot = caret.getDot();
168: if (mark != dot) {
169: buffer.markMarker.set(buffer, mark);
170: }
171: }
172: }
173:
174: public static JTextPane getContainedWindow(Container cont,
175: boolean last) {
176: for (;;) {
177: if (cont instanceof JTextPane)
178: return (JTextPane) cont;
179: if (cont instanceof JScrollPane)
180: cont = (Container) ((JScrollPane) cont).getViewport()
181: .getView();
182: else if (cont instanceof JFrame)
183: cont = ((JFrame) cont).getContentPane();
184: else if (cont instanceof JSplitPane) {
185: JSplitPane split = (JSplitPane) cont;
186: cont = (Container) (last ? split.getRightComponent()
187: : split.getLeftComponent());
188: } else {
189: int count = cont.getComponentCount();
190: if (count == 0)
191: return null;
192: cont = (Container) cont.getComponent(last ? (count - 1)
193: : 0);
194: }
195: }
196: }
197:
198: /*
199: public SwingWindow getNextWindow(boolean forwards)
200: {
201: Container prev = this;
202: for (;;)
203: {
204: Container parent = prev.getParent();
205: if (parent == null)
206: return null;
207: if (parent instanceof JSplitPane)
208: {
209: JSplitPane split =(JSplitPane) parent;
210: if (prev == split.getLeftComponent())
211: {
212: if (forwards)
213: return getFirstWindow((Container) split.getRightComponent());
214: }
215: else // prev == split.getRightComponent():
216: {
217: if (!forwards)
218: return getLastWindow((Container) split.getLeftComponent());
219: }
220: }
221: prev = parent;
222: }
223: }
224: */
225:
226: public static SwingWindow getWindow(java.awt.event.ActionEvent event) {
227: // Maybe use TextAction.getTextComponent instead? FIXME.
228: Component component = (Component) event.getSource();
229: for (;;) {
230: if (component instanceof JTextPane)
231: return (SwingWindow) panemap.get(component);
232: component = component.getParent();
233: }
234: }
235:
236: protected void deleteNoValidate() {
237: super .deleteNoValidate();
238: if (modeline != null)
239: panel.remove(modeline);
240: panel.remove(scrollPane);
241:
242: // Mow remove the Panel from its parent.
243: Container oldParent = panel.getParent();
244: if (oldParent instanceof JSplitPane) {
245: JSplitPane split = (JSplitPane) oldParent;
246: Component other;
247: if (panel == split.getLeftComponent())
248: other = split.getRightComponent();
249: else
250: other = split.getLeftComponent();
251: split.remove(jtextpane);
252: split.remove(other);
253: // In the JSplitPane's parent, replace the JSplitPane by just other
254: Container splitParent = split.getParent();
255: if (splitParent instanceof JSplitPane) {
256: JSplitPane outerSplit = (JSplitPane) splitParent;
257: if (split == outerSplit.getLeftComponent())
258: outerSplit.setLeftComponent(other);
259: else
260: outerSplit.setRightComponent(other);
261: } else {
262: splitParent.remove(split);
263: splitParent.add(other);
264: }
265: } else
266: oldParent.remove(panel);
267:
268: panemap.remove(jtextpane);
269: jtextpane = null;
270: panel = null;
271: scrollPane = null;
272: }
273:
274: public void activateRegion() {
275: System.err.println("(activateRegions)");
276: Caret caret = jtextpane.getCaret();
277: caret.setDot(buffer.markMarker.getOffset());
278: caret.moveDot(buffer.getDot());
279: }
280:
281: public Dimension getPreferredScrollableViewportSize() {
282: Dimension size = panel.getSize();
283: if (modeline != null)
284: size = new Dimension(size.width, size.height
285: - modeline.getPreferredSize().height);
286: return size;
287: }
288:
289: protected void getCharSize() {
290: java.awt.Font defaultFont = ((SwingBuffer) buffer).doc
291: .getFont(SwingBuffer.defaultStyle);
292: java.awt.FontMetrics fm = jtextpane.getGraphics()
293: .getFontMetrics(defaultFont);
294: charHeight = fm.getHeight();
295: charWidth = fm.charWidth('m');
296: }
297:
298: public int getWidth() {
299: return jtextpane.getWidth();
300: }
301:
302: public int getHeight() {
303: return jtextpane.getWidth();
304: }
305:
306: public void keyTyped(KeyEvent e) {
307: handle(e, toInt(e, 0));
308: }
309:
310: public void keyPressed(KeyEvent e) {
311: handle(e, toInt(e, EKeymap.PRESSED));
312: }
313:
314: public void keyReleased(KeyEvent e) {
315: handle(e, toInt(e, EKeymap.RELEASED));
316: }
317:
318: protected void handle(KeyEvent e, int code) {
319: Object action = lookupKey(code);
320:
321: pushPrefix(code);
322: pendingLength--;
323:
324: if (action == null || action instanceof IgnoreAction) {
325: e.consume();
326: return;
327: }
328: handleCommand(action);
329: e.consume();
330: }
331:
332: int toInt(KeyEvent e, int kind) {
333: int code = e.getKeyCode();
334: if (code == 0)
335: code = e.getKeyChar();
336: else
337: code |= (e.getModifiers() | kind) << 16;
338: return code;
339: }
340:
341: /**
342: *
343: */
344: void flushPending() {
345: pendingLength = 0;
346: }
347:
348: /**
349: * @see gnu.jemacs.buffer.EWindow#tooLong(int)
350: */
351: public Object tooLong(int pendingLength) {
352: return new TooLongAction(pendingLength);
353: }
354:
355: }
|