4 A Programmer's Text Editor
8 Copyright (C) 1991-2007 Angel Ortega <angel@triptico.com>
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 http://www.triptico.com
30 mp.actions['new'] = sub (d) {
31 d = mp.find_file_by_name(L("<unnamed>"));
42 mp.actions['next'] = sub (d) { mp.next(); };
43 mp.actions['prev'] = sub (d) { mp.prev(); };
45 mp.actions['save_as'] = sub (d) {
49 if((t = mp.savefile(L("Save file as:"))) == NULL)
55 if(mp.long_op(mp.save, d) == -1)
56 mp.alert(sprintf(L("Error saving file: %s"), ERRNO));
61 mp.actions['save'] = sub (d) {
63 /* name is <unnamed> or something similar; ask for one */
64 if(regex("/^<.+>$/", d.name))
65 mp.actions.save_as(d);
67 if(mp.long_op(mp.save, d) == -1)
68 mp.alert(sprintf(L("Error saving file: %s"), ERRNO));
71 mp.actions['close'] = sub (d) {
76 r = mp.confirm(L("File has changed. Save changes?"));
78 /* cancel? don't close */
80 if(r == 1) mp.actions.save(d);
86 mp.actions['exit'] = sub (d) {
90 if (mp.config.auto_sessions)
93 if (mp.actions.close_all())
97 mp.actions['open'] = sub (d) {
101 if((n = mp.openfile(L("File to open:"))) != NULL && n ne "")
102 if(mp.long_op(mp.open, n) == NULL && ERRNO != NULL)
103 mp.alert(sprintf("Error opening '%s': %s", n, ERRNO));
106 mp.actions['revert'] = sub (d) {
107 /* save current name */
113 r = mp.confirm(L("File has changed. Are you sure?"));
115 /* cancel? don't close */
116 if(r == 0 || r == 2) return;
120 if(mp.long_op(mp.open, p) == NULL && ERRNO != NULL)
121 mp.alert(sprintf("Error opening '%s': %s", p, ERRNO));
124 mp.actions['open_config_file'] = sub (d) {
126 mp.open(HOMEDIR ~ ".mp.mpsl");
129 mp.actions['sync'] = sub (d) {
131 /* save all modified documents */
132 foreach(local d, grep(sub (e) { e.txt.mod; }, mp.docs))
136 mp.actions['exec_command'] = sub (d) {
139 { 'label' => L("System command:"),
141 'history' => 'system' }
148 /* does it start with a pipe? */
149 if(regex('/^\|/', cmd))
153 /* yes; current document should be fed to it */
154 cmd = sregex('/^\|/', cmd, NULL);
156 if((p = popen(cmd, "w")) != NULL)
158 foreach(local l, mp.get_active_area(d))
159 write(p, l ~ mp.config.eol);
165 sprintf(L("Error writing to command '%s'"), cmd));
169 /* no; execute command and insert into cursor */
172 if((p = popen(cmd, "r")) != NULL)
178 while((l = read(p)) != NULL)
185 sprintf(L("Error reading from command '%s'"), cmd));
190 mp.actions['close_all'] = sub {
194 while (s = size(mp.docs)) {
195 local doc = mp.docs[mp.active_i];
197 /* close current document */
198 mp.actions.close(doc);
200 /* if the size of the list hasn't changed,
201 action was cancelled, so don't exit */
202 if(s == size(mp.docs)) return (0);
208 /* default key bindings */
210 mp.keycodes['ctrl-n'] = 'next';
211 mp.keycodes['ctrl-o'] = 'open';
212 mp.keycodes['ctrl-q'] = 'exit';
213 mp.keycodes['ctrl-s'] = 'save';
214 mp.keycodes['ctrl-w'] = 'close';
216 mp.keycodes['close-window'] = 'exit';
218 /* action descriptions */
220 mp.actdesc['new'] = LL("New");
221 mp.actdesc['save'] = LL("Save...");
222 mp.actdesc['save_as'] = LL("Save as...");
223 mp.actdesc['next'] = LL("Next");
224 mp.actdesc['prev'] = LL("Previous");
225 mp.actdesc['open'] = LL("Open...");
226 mp.actdesc['exit'] = LL("Exit");
227 mp.actdesc['close'] = LL("Close");
228 mp.actdesc['revert'] = LL("Revert");
230 mp.actdesc['open_config_file'] = LL("Edit configuration file");
231 mp.actdesc['open_templates_file'] = LL("Edit templates file");
232 mp.actdesc['sync'] = LL("Save modified texts");
233 mp.actdesc['exec_command'] = LL("Run system command...");
238 /* chomps the end of file chars from a string */
240 sregex("/\r?\n$/", str, NULL);
244 sub mp.open_file_for_reading(filename)
245 /* the three-state file opening of text editors: open if possible,
246 fail on errors, create new if non-existent */
250 /* clear previous errors */
253 if((f = open(filename, "rb")) == NULL)
258 /* if a stat() can be done, it means the file
259 exists and can't be open; this is an error.
260 otherwise, it's a non-existent file, so not
261 an error for a text editor */
262 if(stat(filename) != NULL)
279 /* if unlink before write is desired, do it */
280 if(mp.config.unlink && (s = stat(doc.name)) != NULL)
283 if((f = open(doc.name, "wb")) == NULL)
285 /* can't write? delete name */
286 doc.name = L("<unnamed>");
290 /* if the document has a password, save it encrypted */
292 nl = mp.crypt1_save(f, doc.txt.lines, doc.password);
295 /* save as a plain text file */
296 foreach(local l, doc.txt.lines)
298 /* write a line separator if it's not the first line */
299 if(nl) write(f, mp.config.eol);
310 /* set back the permissions and ownership, if available */
313 chmod(doc.name, s[2]);
314 chown(doc.name, s[4], s[5]);
321 sub mp.new(filename, lines)
322 /* creates a new document */
331 txt.lines = lines || [ '' ];
335 doc.name = filename || L("<unnamed>");
343 /* store in the list and set as active */
345 mp.active_i = size(mp.docs) - 1;
347 /* autodetect syntax */
348 mp.detect_syntax(doc);
355 /* rotates through the document list */
357 if(++mp.active_i == size(mp.docs))
363 /* rotates through the document list, backwards */
365 if(--mp.active_i == -1)
366 mp.active_i = size(mp.docs) - 1;
371 /* closes the active document */
373 local k = mp.active_i;
375 /* delete from the list */
376 adel(mp.docs, mp.active_i);
378 /* rotate if it was the last one */
379 if(mp.active_i == size(mp.docs))
384 sub mp.find_file_by_name(filename)
385 /* finds an open file by its name */
387 seek(map(sub(d) { d.name; }, mp.docs), filename);
391 sub mp.open(filename)
392 /* opens a new document (uses UI) */
394 local d = mp.find_file_by_name(filename);
396 /* looks first if the file is already open */
406 if((f = mp.open_file_for_reading(filename)) == NULL)
411 /* file doesn't exist: new document */
415 if(mp.crypt1_detect(f))
417 /* password needed; ask for it */
419 { 'label' => L("Password:"),
420 'type' => 'password' }
423 /* cancel? fail, but not on error */
428 /* get the password */
431 /* an empty password is equal to cancellation */
432 if(p eq '') return(NULL);
434 /* and load the file */
435 l = mp.crypt1_load(f, p);
438 l = mp.plain_load(f);
443 d = mp.new(filename, l);
445 /* does it have a password? store it */
446 if(p != NULL) d.password = p;