001: /*
002: * RegionCommands.java
003: *
004: * Copyright (C) 1998-2003 Peter Graves
005: * $Id: RegionCommands.java,v 1.3 2003/06/28 16:11:54 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import gnu.regexp.RE;
025: import gnu.regexp.REMatch;
026: import gnu.regexp.UncheckedRE;
027: import java.io.IOException;
028: import java.io.OutputStream;
029: import javax.swing.undo.CompoundEdit;
030: import org.armedbear.j.mail.Base64Decoder;
031:
032: public final class RegionCommands {
033: public static void detabRegion() {
034: detabOrEntabRegion(false);
035: }
036:
037: public static void entabRegion() {
038: detabOrEntabRegion(true);
039: }
040:
041: private static void detabOrEntabRegion(boolean entab) {
042: final Editor editor = Editor.currentEditor();
043: final Position mark = editor.getMark(); // Alias, not copy.
044: if (mark == null)
045: return;
046: editor.setWaitCursor();
047: final Buffer buffer = editor.getBuffer();
048: final int count = Editor.getEditorCount();
049: Editor[] editors = new Editor[count];
050: int[] dotCol = new int[count];
051: int[] markCol = new int[count];
052: int i = 0;
053: for (EditorIterator it = new EditorIterator(); it.hasNext();) {
054: Editor ed = it.nextEditor();
055: editors[i] = ed;
056: if (ed.getBuffer() == buffer) {
057: dotCol[i] = ed.getDotCol();
058: if (ed.getMark() != null)
059: markCol[i] = buffer.getCol(ed.getMark());
060: }
061: ++i;
062: }
063: final Region r = new Region(editor);
064: final int tabWidth = buffer.getTabWidth();
065: try {
066: buffer.lockWrite();
067: } catch (InterruptedException e) {
068: Log.error(e);
069: return;
070: }
071: try {
072: detabOrEntabRegion(editor, buffer, r, entab, tabWidth);
073: } finally {
074: buffer.unlockWrite();
075: }
076: for (i = 0; i < count; i++) {
077: Editor ed = editors[i];
078: if (ed.getBuffer() == buffer) {
079: ed.moveDotToCol(dotCol[i]);
080: if (ed.getMark() != null)
081: ed.getMark().moveToCol(markCol[i], tabWidth);
082: }
083: }
084: buffer.repaint();
085: editor.setDefaultCursor();
086: }
087:
088: private static void detabOrEntabRegion(Editor editor,
089: Buffer buffer, Region r, boolean entab, int tabWidth) {
090: CompoundEdit compoundEdit = new CompoundEdit();
091: compoundEdit.addEdit(new UndoMove(editor));
092: boolean changed = false;
093: final Line beginLine = r.getBeginLine();
094: final int beginOffset = r.getBeginOffset();
095: final Line endLine = r.getEndLine();
096: final int endOffset = r.getEndOffset();
097: final int startCol = buffer.getCol(beginLine, beginOffset);
098: if (beginLine == endLine) {
099: final String oldText = beginLine.getText();
100: final String head = oldText.substring(0, beginOffset);
101: final String toBeChanged = oldText.substring(beginOffset,
102: endOffset);
103: final String tail = oldText.substring(endOffset);
104: FastStringBuffer sb = new FastStringBuffer(head);
105: if (entab)
106: sb.append(Utilities.entab(toBeChanged, tabWidth,
107: startCol));
108: else
109: sb.append(Utilities.detab(toBeChanged, tabWidth,
110: startCol));
111: sb.append(tail);
112: final String newText = sb.toString();
113: if (!newText.equals(oldText)) {
114: compoundEdit
115: .addEdit(new UndoLineEdit(buffer, beginLine));
116: beginLine.setText(newText);
117: changed = true;
118: }
119: } else {
120: // Begin line.
121: String oldText = beginLine.getText();
122: final String head = oldText.substring(0, beginOffset);
123: String toBeChanged = oldText.substring(beginOffset);
124: FastStringBuffer sb = new FastStringBuffer(head);
125: if (entab)
126: sb.append(Utilities.entab(toBeChanged, tabWidth,
127: startCol));
128: else
129: sb.append(Utilities.detab(toBeChanged, tabWidth,
130: startCol));
131: String newText = sb.toString();
132: if (!newText.equals(oldText)) {
133: compoundEdit
134: .addEdit(new UndoLineEdit(buffer, beginLine));
135: beginLine.setText(newText);
136: changed = true;
137: }
138:
139: // Lines in middle of region.
140: Line line = beginLine.next();
141: while (line != null && line != endLine) {
142: oldText = line.getText();
143: if (entab)
144: newText = Utilities.entab(oldText, tabWidth);
145: else
146: newText = Utilities.detab(oldText, tabWidth);
147: if (!newText.equals(oldText)) {
148: compoundEdit
149: .addEdit(new UndoLineEdit(buffer, line));
150: line.setText(newText);
151: changed = true;
152: }
153: line = line.next();
154: }
155:
156: // End line.
157: oldText = endLine.getText();
158: toBeChanged = oldText.substring(0, endOffset);
159: final String tail = oldText.substring(endOffset);
160: sb.setLength(0);
161: if (entab)
162: sb.append(Utilities.entab(toBeChanged, tabWidth));
163: else
164: sb.append(Utilities.detab(toBeChanged, tabWidth));
165: sb.append(tail);
166: newText = sb.toString();
167: if (!newText.equals(oldText)) {
168: compoundEdit.addEdit(new UndoLineEdit(buffer, endLine));
169: endLine.setText(newText);
170: changed = true;
171: }
172: }
173: if (changed) {
174: compoundEdit.end();
175: buffer.addEdit(compoundEdit);
176: buffer.modified();
177: }
178: }
179:
180: public static void upperCaseRegion() {
181: final Editor editor = Editor.currentEditor();
182: editor.setWaitCursor();
183: changeCaseRegion(editor, true);
184: editor.setDefaultCursor();
185: }
186:
187: public static void lowerCaseRegion() {
188: final Editor editor = Editor.currentEditor();
189: editor.setWaitCursor();
190: changeCaseRegion(editor, false);
191: editor.setDefaultCursor();
192: }
193:
194: private static void changeCaseRegion(Editor editor, boolean toUpper) {
195: if (!editor.checkReadOnly())
196: return;
197: if (editor.getDot() == null || editor.getMark() == null)
198: return;
199: if (editor.getDot().equals(editor.getMark()))
200: return;
201: final Buffer buffer = editor.getBuffer();
202:
203: // A hard update is only necessary if the region spans a line boundary.
204: boolean hard = editor.getDotLine() != editor.getMarkLine();
205:
206: try {
207: buffer.lockWrite();
208: } catch (InterruptedException e) {
209: Log.error(e);
210: return;
211: }
212: try {
213: CompoundEdit compoundEdit = editor.beginCompoundEdit();
214: editor.addUndo(SimpleEdit.MOVE);
215: final Region r = new Region(editor);
216: final String s = r.toString();
217: editor.getDot().moveTo(r.getBegin());
218:
219: // Save undo information before calling Region.delete so modified
220: // flag will be correct if we revert.
221: editor.addUndoDeleteRegion(r);
222:
223: // Sets buffer modified flag.
224: r.delete();
225:
226: editor.addUndo(SimpleEdit.INSERT_STRING);
227: editor.insertStringInternal(toUpper ? s.toUpperCase() : s
228: .toLowerCase());
229: editor.endCompoundEdit(compoundEdit);
230: editor.moveCaretToDotCol();
231: if (hard)
232: buffer.repaint();
233: else
234: Editor.updateInAllEditors(editor.getDotLine());
235: editor.setMark(null);
236: } finally {
237: buffer.unlockWrite();
238: }
239: }
240:
241: public static void decodeRegion() {
242: final Editor editor = Editor.currentEditor();
243: ByteBuffer bb = new ByteBuffer();
244: if (editor.getMark() != null) {
245: if (editor.getMarkOffset() == 0
246: && editor.getDotOffset() == 0) {
247: Region r = new Region(editor);
248: for (Line line = r.getBeginLine(); line != r
249: .getEndLine(); line = line.next()) {
250: byte[] decodedBytes = decodeLine(line);
251: if (decodedBytes == null) {
252: MessageDialog.showMessageDialog(
253: "Unable to decode region",
254: "Decode Region");
255: return;
256: }
257: bb.append(decodedBytes);
258: }
259: } else {
260: MessageDialog.showMessageDialog(
261: "Region must consist of whole lines only.",
262: "Decode Region");
263: return;
264: }
265: } else {
266: byte[] decodedBytes = decodeLine(editor.getDotLine());
267: if (decodedBytes == null) {
268: MessageDialog.showMessageDialog(
269: "Unable to decode region", "Decode Region");
270: return;
271: }
272: bb.append(decodedBytes);
273: }
274: byte[] bytes = bb.getBytes();
275: final int length = bb.length();
276: boolean isBinary = false;
277: for (int i = 0; i < length; i++) {
278: byte b = bytes[i];
279: if (b == 0) {
280: isBinary = true;
281: break;
282: }
283: }
284: if (isBinary) {
285: SaveFileDialog d = new SaveFileDialog(editor, "Save As");
286: editor.centerDialog(d);
287: d.show();
288: File saveAs = d.getDestination();
289: if (saveAs == null)
290: return;
291: // At this point, if the target file exists, the user has said
292: // it's OK to overwrite it.
293: try {
294: OutputStream out = saveAs.getOutputStream();
295: if (out != null) {
296: out.write(bytes, 0, length);
297: out.flush();
298: out.close();
299: }
300: } catch (IOException e) {
301: Log.error(e);
302: }
303: } else {
304: Buffer buf = new Buffer(0);
305: buf.setText(new String(bytes, 0, length));
306: editor.makeNext(buf);
307: editor.activate(buf);
308: }
309: }
310:
311: private static final byte[] decodeLine(Line line) {
312: return Base64Decoder.decode(line.trim());
313: }
314:
315: public static void renumberRegion() {
316: renumberRegion(null);
317: }
318:
319: public static void renumberRegion(String arg) {
320: final Editor editor = Editor.currentEditor();
321: if (editor.getMark() == null)
322: return;
323: final Region region = new Region(editor);
324: if (region.getEndLineNumber() - region.getBeginLineNumber() < 2)
325: return;
326: if (!editor.checkReadOnly())
327: return;
328: int start = -1;
329: if (arg != null) {
330: try {
331: start = Integer.parseInt(arg);
332: } catch (NumberFormatException e) {
333: MessageDialog.showMessageDialog("Invalid number \""
334: + arg + '"', "Error");
335: return;
336: }
337: }
338: final Buffer buffer = editor.getBuffer();
339: try {
340: buffer.lockWrite();
341: } catch (InterruptedException e) {
342: Log.error(e);
343: return;
344: }
345: try {
346: _renumberRegion(editor, buffer, region, start);
347: } finally {
348: buffer.unlockWrite();
349: }
350: }
351:
352: private static void _renumberRegion(Editor editor, Buffer buffer,
353: Region region, int start) {
354: CompoundEdit compoundEdit = null;
355: if (start < 0) {
356: for (Line line = region.getBeginLine(); line != region
357: .getEndLine(); line = line.next()) {
358: final String text = line.getText();
359: int index = findNumber(text, buffer.getMode());
360: if (index >= 0) {
361: FastStringBuffer sb = new FastStringBuffer();
362: while (index < text.length()) {
363: char c = text.charAt(index++);
364: if (c >= '0' && c <= '9')
365: sb.append(c);
366: else
367: break;
368: }
369: try {
370: int n = Integer.parseInt(sb.toString());
371: if (start < 0)
372: start = n;
373: else if (n < start)
374: start = n;
375: } catch (NumberFormatException e) {
376: Log.error(e);
377: }
378: }
379: }
380: }
381: for (Line line = region.getBeginLine(); line != region
382: .getEndLine(); line = line.next()) {
383: final String text = line.getText();
384: int index = findNumber(text, buffer.getMode());
385: if (index < 0)
386: continue;
387: FastStringBuffer sb = new FastStringBuffer(text.substring(
388: 0, index));
389: while (index < text.length()
390: && Character.isDigit(text.charAt(index)))
391: ++index;
392: sb.append(start++);
393: sb.append(text.substring(index));
394: String newText = sb.toString();
395: if (!newText.equals(text)) {
396: if (compoundEdit == null) {
397: compoundEdit = new CompoundEdit();
398: compoundEdit.addEdit(new UndoMove(editor));
399: }
400: compoundEdit.addEdit(new UndoLineEdit(buffer, line));
401: line.setText(newText);
402: }
403: }
404: if (compoundEdit != null) {
405: compoundEdit.end();
406: buffer.addEdit(compoundEdit);
407: buffer.modified();
408: }
409: buffer.setNeedsParsing(true);
410: buffer.getFormatter().parseBuffer();
411: buffer.repaint();
412: }
413:
414: private static int findNumber(String text, Mode mode) {
415: RE re = new UncheckedRE("[0-9]+");
416: int index = 0;
417: int limit = text.length();
418: while (index <= limit) {
419: REMatch match = re.getMatch(text, index);
420: if (match == null)
421: return -1;
422: if (isDelimited(text, match.getStartIndex(), match
423: .toString().length(), mode))
424: return match.getStartIndex();
425: index = match.getStartIndex() + 1;
426: }
427: return -1;
428: }
429:
430: private static boolean isDelimited(String text, int index,
431: int length, Mode mode) {
432: final int before = index - 1;
433: if (before >= 0 && mode.isIdentifierPart(text.charAt(before)))
434: return false;
435: final int after = index + length;
436: if (after < text.length()
437: && mode.isIdentifierPart(text.charAt(after)))
438: return false;
439: return true;
440: }
441:
442: public void doShellCommandOnRegion() {
443: if (!Editor.checkExperimental())
444: return;
445: final Editor editor = Editor.currentEditor();
446: if (!editor.checkReadOnly())
447: return;
448: if (editor.getMark() == null) {
449: MessageDialog.showMessageDialog(editor,
450: "No region selected", "Error");
451: return;
452: }
453: InputDialog d = new InputDialog(editor, "Command:",
454: "Do Shell Command On Region", null);
455: d.setHistory(new History("doShellCommandOnRegion"));
456: editor.centerDialog(d);
457: d.show();
458: String command = d.getInput();
459: if (command == null || command.length() == 0)
460: return;
461: editor.setWaitCursor();
462: Region r = new Region(editor);
463: // BUG! We should pass the contents of the region line by line in case
464: // it's big.
465: ShellCommand shellCommand = new ShellCommand(command, null, r
466: .toString());
467: shellCommand.run();
468: String output = shellCommand.getOutput();
469: if (output != null && output.length() > 0) {
470: CompoundEdit compoundEdit = editor.beginCompoundEdit();
471: editor.deleteRegion();
472: editor.addUndo(SimpleEdit.INSERT_STRING);
473: editor.insertStringInternal(output);
474: editor.moveCaretToDotCol();
475: editor.endCompoundEdit(compoundEdit);
476: if (editor.getFormatter().parseBuffer())
477: editor.getBuffer().repaint();
478: }
479: editor.setDefaultCursor();
480: }
481: }
|