* progmodes/ada-xref.el (ada-prj-find-prj-file):
[bpt/emacs.git] / lisp / textmodes / table.el
CommitLineData
238240c9
RS
1;;; table.el --- create and edit WYSIWYG text based embedded tables
2
3731a850 3;; Copyright (C) 2000, 2001, 2002, 2003, 2004,
d7a0267c 4;; 2005, 2006, 2007 Free Software Foundation, Inc.
238240c9
RS
5
6;; Keywords: wp, convenience
7;; Author: Takaaki Ota <Takaaki.Ota@am.sony.com>
8;; Created: Sat Jul 08 2000 13:28:45 (PST)
047ecb4e 9;; Revised: Wed Jan 03 2007 13:23:46 (PST)
238240c9
RS
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
5a9dffec 15;; the Free Software Foundation; either version 3, or (at your option)
238240c9
RS
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs; see the file COPYING. If not, write to the
4fc5845f
LK
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
238240c9
RS
27
28;;; Commentary:
29
30;; -------------
31;; Introduction:
32;; -------------
33;;
34;; This package provides text based table creation and editing
35;; feature. With this package Emacs is capable of editing tables that
36;; are embedded inside a text document, the feature similar to the
37;; ones seen in modern WYSIWYG word processors. A table is a
38;; rectangular text area consisting from a surrounding frame and
39;; content inside the frame. The content is usually subdivided into
40;; multiple rectangular cells, see the actual tables used below in
41;; this document. Once a table is recognized, editing operation
42;; inside a table cell is confined into that specific cell's
43;; rectangular area. This means that typing and deleting characters
44;; inside a cell do not affect any outside text but introduces
45;; appropriate formatting only to the cell contents. If necessary for
46;; accommodating added text in the cell, the cell automatically grows
47;; vertically and/or horizontally. The package uses no major mode nor
48;; minor mode for its implementation because the subject text is
49;; localized within a buffer. Therefore the special behaviors inside
50;; a table cells are implemented by using keymap text property
51;; instead of buffer wide mode-map.
52;;
53;;
54;; -----------
55;; Background:
56;; -----------
57;;
58;; Paul Georgief is one of my best friends. He became an Emacs
59;; convert after I recommended him trying it several years ago. Now
60;; we both are devoted disciples of Emacsism and elisp cult. One day
61;; in his Emacs exploration he asked me "Tak, what is a command to
62;; edit tables in Emacs?". This question started my journey of this
63;; table package development. May the code be with me! In the
64;; software world Emacs is probably one of the longest lifetime record
65;; holders. Amazingly there have been no direct support for WYSIWYG
66;; table editing tasks in Emacs. Many people must have experienced
67;; manipulating existing overwrite-mode and picture-mode for this task
68;; and only dreamed of having such a lisp package which supports this
69;; specific task directly. Certainly, I have been one of them. The
70;; most difficult part of dealing with table editing in Emacs probably
71;; is how to realize localized rectangular editing effect. Emacs has
72;; no rectangular narrowing mechanism. Existing rect package provides
73;; basically kill, delete and yank operations of a rectangle, which
74;; internally is a mere list of strings. A simple approach for
75;; realizing the localized virtual rectangular operation is combining
76;; rect package capability with a temporary buffer. Insertion and
77;; deletion of a character to a table cell can be trapped by a
78;; function that copies the cell rectangle to a temporary buffer then
79;; apply the insertion/deletion to the temporary contents. Then it
80;; formats the contents by filling the paragraphs in order to fit it
81;; into the original rectangular area and finally copy it back to the
82;; original buffer. This simplistic approach has to bear with
83;; significant performance hit. As cell grows larger the copying
84;; rectangle back and forth between the original buffer and the
85;; temporary buffer becomes expensive and unbearably slow. It was
86;; completely impractical and an obvious failure. An idea has been
87;; borrowed from the original Emacs design to overcome this
88;; shortcoming. When the terminal screen update was slow and
89;; expensive Emacs employed a clever algorithm to reduce actual screen
90;; update by removing redundant redrawing operations. Also the actual
91;; redrawing was done only when there was enough idling time. This
92;; technique significantly improved the previously mentioned
93;; undesirable situation. Now the original buffer's rectangle is
94;; copied into a cache buffer only once. Any cell editing operation
95;; is done only to the cache contents. When there is enough idling
96;; time the original buffer's rectangle is updated with the current
97;; cache contents. This delayed operation is implemented by using
98;; Emacs's timer function. To reduce the visual awkwardness
99;; introduced by the delayed effect the cursor location is updated in
100;; real-time as a user types while the cell contents remains the same
101;; until the next idling time. A key to the success of this approach
102;; is how to maintain cache coherency. As a user moves point in and
103;; out of a cell the table buffer contents and the cache buffer
104;; contents must be synchronized without a mistake. By observing user
105;; action carefully this is possible however not easy. Once this
106;; mechanism is firmly implemented the rest of table features grew in
107;; relatively painless progression. Those users who are familiar with
108;; Emacs internals appreciate this table package more. Because it
109;; demonstrates how extensible Emacs is by showing something that
110;; appears like a magic. It lets you re-discover the potential of
111;; Emacs.
112;;
113;;
114;; -------------
115;; Entry Points:
116;; -------------
117;;
118;; If this is the first time for you to try this package, go ahead and
119;; load the package by M-x `load-file' RET. Specify the package file
120;; name "table.el". Then switch to a new test buffer and issue the
121;; command M-x `table-insert' RET. It'll ask you number of columns,
122;; number of rows, cell width and cell height. Give some small
123;; numbers for each of them. Play with the resulted table for a
124;; while. If you have menu system find the item "Table" under "Tools"
125;; and "Table" in the menu bar when the point is in a table cell.
126;; Some of them are pretty intuitive and you can easily guess what
127;; they do. M-x `describe-function' and get the documentation of
128;; `table-insert'. The document includes a short tutorial. When you
129;; are tired of guessing how it works come back to this document
130;; again.
131;;
132;; To use the package regularly place this file in the site library
133;; directory and add the next expression in your .emacs file. Make
134;; sure that directory is included in the `load-path'.
135;;
136;; (require 'table)
137;;
138;; Have the next expression also, if you want always be ready to edit
139;; tables inside text files. This mechanism is analogous to
140;; fontification in a sense that tables are recognized at editing time
141;; without having table information saved along with the text itself.
142;;
143;; (add-hook 'text-mode-hook 'table-recognize)
144;;
145;; Following is a table of entry points and brief description of each
146;; of them. The tables below are of course generated and edited by
147;; using this package. Not all the commands are bound to keys. Many
148;; of them must be invoked by "M-x" (`execute-extended-command')
149;; command. Refer to the section "Keymap" below for the commands
150;; available from keys.
151;;
152;; +------------------------------------------------------------------+
153;; | User Visible Entry Points |
154;; +-------------------------------+----------------------------------+
155;; | Function | Description |
156;; +-------------------------------+----------------------------------+
157;; |`table-insert' |Insert a table consisting of grid |
158;; | |of cells by specifying the number |
159;; | |of COLUMNS, number of ROWS, cell |
160;; | |WIDTH and cell HEIGHT. |
161;; +-------------------------------+----------------------------------+
162;; |`table-insert-row' |Insert row(s) of cells before the |
163;; | |current row that matches the |
164;; | |current row structure. |
165;; +-------------------------------+----------------------------------+
166;; |`table-insert-column' |Insert column(s) of cells before |
167;; | |the current column that matches |
168;; | |the current column structure. |
169;; +-------------------------------+----------------------------------+
170;; |`table-delete-row' |Delete row(s) of cells. The row |
171;; | |must consist from cells of the |
172;; | |same height. |
173;; +-------------------------------+----------------------------------+
174;; |`table-delete-column' |Delete column(s) of cells. The |
175;; | |column must consist from cells of |
176;; | |the same width. |
177;; +-------------------------------+----------------------------------+
178;; |`table-recognize' |Recognize all tables in the |
179;; |`table-unrecognize' |current buffer and |
180;; | |activate/inactivate them. |
181;; +-------------------------------+----------------------------------+
182;; |`table-recognize-region' |Recognize all the cells in a |
183;; |`table-unrecognize-region' |region and activate/inactivate |
184;; | |them. |
185;; +-------------------------------+----------------------------------+
186;; |`table-recognize-table' |Recognize all the cells in a |
187;; |`table-unrecognize-table' |single table and |
188;; | |activate/inactivate them. |
189;; +-------------------------------+----------------------------------+
190;; |`table-recognize-cell' |Recognize a cell. Find a cell |
191;; |`table-unrecognize-cell' |which contains the current point |
192;; | |and activate/inactivate that cell.|
193;; +-------------------------------+----------------------------------+
194;; |`table-forward-cell' |Move point to the next Nth cell in|
195;; | |a table. |
196;; +-------------------------------+----------------------------------+
197;; |`table-backward-cell' |Move point to the previous Nth |
198;; | |cell in a table. |
199;; +-------------------------------+----------------------------------+
200;; |`table-span-cell' |Span the current cell toward the |
201;; | |specified direction and merge it |
202;; | |with the adjacent cell. The |
203;; | |direction is right, left, above or|
204;; | |below. |
205;; +-------------------------------+----------------------------------+
206;; |`table-split-cell-vertically' |Split the current cell vertically |
207;; | |and create a cell above and a cell|
208;; | |below the point location. |
209;; +-------------------------------+----------------------------------+
210;; |`table-split-cell-horizontally'|Split the current cell |
211;; | |horizontally and create a cell on |
212;; | |the left and a cell on the right |
213;; | |of the point location. |
214;; +-------------------------------+----------------------------------+
215;; |`table-split-cell' |Split the current cell vertically |
216;; | |or horizontally. This is a |
217;; | |wrapper command to the other two |
218;; | |orientation specific commands. |
219;; +-------------------------------+----------------------------------+
220;; |`table-heighten-cell' |Heighten the current cell. |
221;; +-------------------------------+----------------------------------+
222;; |`table-shorten-cell' |Shorten the current cell. |
223;; +-------------------------------+----------------------------------+
224;; |`table-widen-cell' |Widen the current cell. |
225;; +-------------------------------+----------------------------------+
226;; |`table-narrow-cell' |Narrow the current cell. |
227;; +-------------------------------+----------------------------------+
228;; |`table-fixed-width-mode' |Toggle fixed width mode. In the |
229;; | |fixed width mode, typing inside a |
230;; | |cell never changes the cell width,|
231;; | |while in the normal mode the cell |
232;; | |width expands automatically in |
233;; | |order to prevent a word being |
234;; | |folded into multiple lines. Fixed|
235;; | |width mode reverses video or |
236;; | |underline the cell contents for |
237;; | |its indication. |
238;; +-------------------------------+----------------------------------+
239;; |`table-query-dimension' |Compute and report the current |
240;; | |cell dimension, current table |
241;; | |dimension and the number of |
242;; | |columns and rows in the table. |
243;; +-------------------------------+----------------------------------+
244;; |`table-generate-source' |Generate the source of the current|
245;; | |table in the specified language |
246;; | |and insert it into a specified |
247;; | |buffer. |
248;; +-------------------------------+----------------------------------+
249;; |`table-insert-sequence' |Travel cells forward while |
250;; | |inserting a specified sequence |
251;; | |string into each cell. |
252;; +-------------------------------+----------------------------------+
253;; |`table-capture' |Convert plain text into a table by|
254;; | |capturing the text in the region. |
255;; +-------------------------------+----------------------------------+
256;; |`table-release' |Convert a table into plain text by|
257;; | |removing the frame from a table. |
258;; +-------------------------------+----------------------------------+
259;; |`table-justify' |Justify the contents of cell(s). |
260;; +-------------------------------+----------------------------------+
261;;
262;;
263;; *Note*
264;;
265;; You may find that some of commonly expected table commands are
266;; missing such as copying a row/column and yanking it. Those
267;; functions can be obtained through existing Emacs text editing
268;; commands. Rows are easily manipulated with region commands and
269;; columns can be copied and pasted through rectangle commands. After
270;; all a table is still a part of text in the buffer. Only the
271;; special behaviors exist inside each cell through text properties.
272;;
273;; `table-generate-html' which appeared in earlier releases is
274;; deprecated in favor of `table-generate-source'. Now HTML is
275;; treated as one of the languages used for describing the table's
276;; logical structure.
277;;
278;;
279;; -------
280;; Keymap:
281;; -------
282;;
283;; Although this package does not use a mode it does use its own
284;; keymap inside a table cell by way of keymap text property. Some of
285;; the standard basic editing commands bound to certain keys are
286;; replaced with the table specific version of corresponding commands.
287;; This replacement combination is listed in the constant alist
288;; `table-command-remap-alist' declared below. This alist is
289;; not meant to be user configurable but mentioned here for your
290;; better understanding of using this package. In addition, table
291;; cells have some table specific bindings for cell navigation and
292;; cell reformation. You can find these additional bindings in the
293;; constant `table-cell-bindings'. Those key bound functions are
294;; considered as internal functions instead of normal commands,
295;; therefore they have special prefix, *table-- instead of table-, for
296;; symbols. The purpose of this is to make it easier for a user to
297;; use command name completion. There is a "normal hooks" variable
298;; `table-cell-map-hook' prepared for users to override the default
299;; table cell bindings. Following is the table of predefined default
300;; key bound commands inside a table cell. Remember these bindings
301;; exist only inside a table cell. When your terminal is a tty, the
302;; control modifier may not be available or applicable for those
303;; special characters. In this case use "C-cC-c", which is
304;; customizable via `table-command-prefix', as the prefix key
305;; sequence. This should preceding the following special character
306;; without the control modifier. For example, use "C-cC-c|" instead
307;; of "C-|".
308;;
309;; +------------------------------------------------------------------+
310;; | Default Bindings in a Table Cell |
311;; +-------+----------------------------------------------------------+
312;; | Key | Function |
313;; +-------+----------------------------------------------------------+
314;; | TAB |Move point forward to the beginning of the next cell. |
315;; +-------+----------------------------------------------------------+
316;; | "C->" |Widen the current cell. |
317;; +-------+----------------------------------------------------------+
318;; | "C-<" |Narrow the current cell. |
319;; +-------+----------------------------------------------------------+
320;; | "C-}" |Heighten the current cell. |
321;; +-------+----------------------------------------------------------+
322;; | "C-{" |Shorten the current cell. |
323;; +-------+----------------------------------------------------------+
324;; | "C--" |Split current cell vertically. (one above and one below) |
325;; +-------+----------------------------------------------------------+
326;; | "C-|" |Split current cell horizontally. (one left and one right) |
327;; +-------+----------------------------------------------------------+
328;; | "C-*" |Span current cell into adjacent one. |
329;; +-------+----------------------------------------------------------+
330;; | "C-+" |Insert row(s)/column(s). |
331;; +-------+----------------------------------------------------------+
332;; | "C-!" |Toggle between normal mode and fixed width mode. |
333;; +-------+----------------------------------------------------------+
334;; | "C-#" |Report cell and table dimension. |
335;; +-------+----------------------------------------------------------+
336;; | "C-^" |Generate the source in a language from the current table. |
337;; +-------+----------------------------------------------------------+
338;; | "C-:" |Justify the contents of cell(s). |
339;; +-------+----------------------------------------------------------+
340;;
341;; *Note*
342;;
343;; When using `table-cell-map-hook' do not use `local-set-key'.
344;;
345;; (add-hook 'table-cell-map-hook
346;; (function (lambda ()
347;; (local-set-key [<key sequence>] '<function>))))
348;;
349;; Above code is well known ~/.emacs idiom for customizing a mode
350;; specific keymap however it does not work for this package. This is
351;; because there is no table mode in effect. This package does not
352;; use a local map therefor you must modify `table-cell-map'
353;; explicitly. The correct way of achieving above task is:
354;;
355;; (add-hook 'table-cell-map-hook
356;; (function (lambda ()
357;; (define-key table-cell-map [<key sequence>] '<function>))))
358;;
359;; -----
360;; Menu:
361;; -----
362;;
363;; If a menu system is available a group of table specific menu items,
364;; "Table" under "Tools" section of the menu bar, is globally added
365;; after this package is loaded. The commands in this group are
366;; limited to the ones that are related to creation and initialization
367;; of tables, such as to insert a table, to insert rows and columns,
368;; or recognize and unrecognize tables. Once tables are created and
369;; point is placed inside of a table cell a table specific menu item
370;; "Table" appears directly on the menu bar. The commands in this
371;; menu give full control on table manipulation that include cell
372;; navigation, insertion, splitting, spanning, shrinking, expansion
373;; and unrecognizing. In addition to above two types of menu there is
374;; a pop-up menu available within a table cell. The content of pop-up
375;; menu is identical to the full table menu. [mouse-3] is the default
376;; button, defined in `table-cell-bindings', to bring up the pop-up
377;; menu. It can be reconfigured via `table-cell-map-hook'. The
378;; benefit of a pop-up menu is that it combines selection of the
379;; location (which cell, where in the cell) and selection of the
380;; desired operation into a single clicking action.
381;;
382;;
383;; -------------------------------
384;; Definition of tables and cells:
385;; -------------------------------
386;;
387;; There is no artificial-intelligence magic in this package. The
388;; definition of a table and the cells inside the table is reasonably
389;; limited in order to achieve acceptable performance in the
390;; interactive operation under Emacs lisp implementation. A valid
391;; table is a rectangular text area completely filled with valid
392;; cells. A valid cell is a rectangle text area, which four borders
393;; consist of valid border characters. Cells can not be nested one to
394;; another or overlapped to each other except sharing the border
395;; lines. A valid character of a cell's vertical border is either
396;; table-cell-vertical-char `|' or table-cell-intersection-char `+'.
397;; A valid character of a cell's horizontal border is either
398;; table-cell-horizontal-char `-' or table-cell-intersection-char `+'.
399;; A valid character of the four corners of a cell must be
400;; table-cell-intersection-char `+'. A cell must contain at least one
401;; character space inside. There is no restriction about the contents
402;; of a table cell, however it is advised if possible to avoid using
403;; any of the border characters inside a table cell. Normally a few
404;; boarder characters inside a table cell are harmless. But it is
405;; possible that they accidentally align up to emulate a bogus cell
406;; corner on which software relies on for cell recognition. When this
407;; happens the software may be fooled by it and fail to determine
408;; correct cell dimension.
409;;
410;; Following are the examples of valid tables.
411;;
412;; +--+----+---+ +-+ +--+-----+
413;; | | | | | | | | |
414;; +--+----+---+ +-+ | +--+--+
415;; | | | | | | | |
416;; +--+----+---+ +--+--+ |
417;; | | |
418;; +-----+--+
419;;
420;; The next five tables are the examples of invalid tables. (From
421;; left to right, 1. nested cells 2. overlapped cells and a
422;; non-rectangle cell 3. non-rectangle table 4. zero width/height
423;; cells 5. zero sized cell)
424;;
425;; +-----+ +-----+ +--+ +-++--+ ++
426;; | | | | | | | || | ++
427;; | +-+ | | | | | | || |
428;; | | | | +--+ | +--+--+ +-++--+
429;; | +-+ | | | | | | | +-++--+
430;; | | | | | | | | | || |
431;; +-----+ +--+--+ +--+--+ +-++--+
432;;
433;; Although the program may recognizes some of these invalid tables,
434;; results from the subsequent editing operations inside those cells
435;; are not predictable and will most likely start destroying the table
436;; structures.
437;;
438;; It is strongly recommended to have at least one blank line above
439;; and below a table. For a table to coexist peacefully with
440;; surrounding environment table needs to be separated from unrelated
441;; text. This is necessary for the left table to grow or shrink
442;; horizontally without breaking the right table in the following
443;; example.
444;;
445;; +-----+-----+-----+
446;; +-----+-----+ | | | |
447;; | | | +-----+-----+-----+
448;; +-----+-----+ | | | |
449;; +-----+-----+-----+
450;;
451;;
452;; -------------------------
453;; Cell contents formatting:
454;; -------------------------
455;;
456;; The cell contents are formatted by filling a paragraph immediately
457;; after characters are inserted into or deleted from a cell. Because
458;; of this, cell contents always remain fit inside a cell neatly. One
459;; drawback of this is that users do not have full control over
460;; spacing between words and line breaking. Only one space can be
461;; entered between words and up to two spaces between sentences. For
462;; a newline to be effective the new line must form a beginning of
463;; paragraph, otherwise it'll automatically be merged with the
464;; previous line in a same paragraph. To form a new paragraph the
465;; line must start with some space characters or immediately follow a
466;; blank line. Here is a typical example of how to list items within
467;; a cell. Without a space at the beginning of each line the items
468;; can not stand on their own.
469;;
470;; +---------------------------------+
471;; |Each one of the following three |
472;; |items starts with a space |
473;; |character thus forms a paragraph |
474;; |of its own. Limitations in cell |
475;; |contents formatting are: |
476;; | |
477;; | 1. Only one space between words.|
478;; | 2. Up to two spaces between |
479;; |sentences. |
480;; | 3. A paragraph must start with |
481;; |spaces or follow a blank line. |
482;; | |
483;; |This paragraph stays away from |
484;; |the item 3 because there is a |
485;; |blank line between them. |
486;; +---------------------------------+
487;;
488;; In the normal operation table cell width grows automatically when
489;; certain word has to be folded into the next line if the width had
490;; not been increased. This normal operation is useful and
491;; appropriate for most of the time, however, it is sometimes useful
492;; or necessary to fix the width of table and width of table cells.
493;; For this purpose the package provides fixed width mode. You can
494;; toggle between fixed width mode and normal mode by "C-!".
495;;
496;; Here is a simple example of the fixed width mode. Suppose we have
497;; a table like this one.
498;;
499;; +-----+
500;; | |
501;; +-----+
502;;
503;; In normal mode if you type a word "antidisestablishmentarianism" it
504;; grows the cell horizontally like this.
505;;
506;; +----------------------------+
507;; |antidisestablishmentarianism|
508;; +----------------------------+
509;;
510;; In the fixed width mode the same action produces the following
511;; result. The folded locations are indicated by a continuation
512;; character (`\' is the default). The continuation character is
513;; treated specially so it is recommended to choose a character that
514;; does not appear elsewhere in table cells. This character is
515;; configurable via customization and is kept in the variable
516;; `table-word-continuation-char'. The continuation character is
517;; treated specially only in the fixed width mode and has no special
518;; meaning in the normal mode however.
519;;
520;; +-----+
521;; |anti\|
522;; |dise\|
523;; |stab\|
524;; |lish\|
525;; |ment\|
526;; |aria\|
527;; |nism |
528;; +-----+
529;;
530;;
531;; -------------------
532;; Cell Justification:
533;; -------------------
534;;
535;; By default the cell contents are filled with left justification and
536;; no vertical justification. A paragraph can be justified
537;; individually but only horizontally. Paragraph justification is for
538;; appearance only and does not change any structural information
539;; while cell justification affects table's structural information.
540;; For cell justification a user can select horizontal justification
541;; and vertical justification independently. Horizontal justification
542;; must be one of the three 'left, 'center or 'right. Vertical
543;; justification can be 'top, 'middle, 'bottom or 'none. When a cell
544;; is justified, that information is recorded as a part of text
545;; property therefore the information is persistent as long as the
546;; cell remains within the Emacs world. Even copying tables by region
547;; and rectangle manipulation commands preserve this information.
548;; However, once the table text is saved as a file and the buffer is
549;; killed the justification information vanishes permanently. To
550;; alleviate this shortcoming without forcing users to save and
551;; maintain a separate attribute file, the table code detects
552;; justification of each cell when recognizing a table. This
553;; detection is done by guessing the justification by looking at the
554;; appearance of the cell contents. Since it is a guessing work it
555;; does not guarantee the perfectness but it is designed to be
556;; practically good enough. The guessing algorithm is implemented in
557;; the function `table--detect-cell-alignment'. If you have better
558;; algorithm or idea any suggestion is welcome.
559;;
560;;
561;; -----
562;; Todo: (in the order of priority, some are just possibility)
563;; -----
564;;
565;; Fix compatibilities with other input method than quail
566;; Resolve conflict with flyspell
567;; Use mouse for resizing cells
568;; A mechanism to link cells internally
569;; Consider the use of variable width font under Emacs 21
570;; Consider the use of `:box' face attribute under Emacs 21
571;; Consider the use of `modification-hooks' text property instead of
572;; rebinding the keymap
573;; Maybe provide complete XEmacs support in the future however the
574;; "extent" is the single largest obstacle lying ahead, read the
575;; document in Emacs info.
576;; (eval '(progn (require 'info) (Info-find-node "elisp" "Not Intervals")))
577;;
578;;
579;; ---------------
580;; Acknowledgment:
581;; ---------------
582;;
583;; Table would not have been possible without the help and
584;; encouragement of the following spirited contributors.
585;;
586;; Paul Georgief <georgief@igpp.ucsd.edu> has been the best tester
587;; of the code as well as the constructive criticizer.
588;;
589;; Gerd Moellmann <gerd@gnu.org> gave me useful suggestions from Emacs
590;; 21 point of view.
591;;
592;; Richard Stallman <rms@gnu.org> showed the initial interest in this
593;; attempt of implementing the table feature to Emacs. This greatly
594;; motivated me to follow through to its completion.
595;;
596;; Kenichi Handa <handa@etl.go.jp> kindly guided me through to
597;; overcome many technical issues while I was struggling with quail
598;; related internationalization problems.
599;;
600;; Christoph Conrad <christoph.conrad@gmx.de> suggested making symbol
601;; names consistent as well as fixing several bugs.
602;;
603;; Paul Lew <paullew@cisco.com> suggested implementing fixed width
604;; mode as well as multi column width (row height) input interface.
605;;
606;; Michael Smith <smith@xml-doc.org> a well-informed DocBook user
607;; asked for CALS table source generation and helped me following
608;; through the work by offering valuable suggestions and testing out
609;; the code. Jorge Godoy <godoy@conectiva.com> has also suggested
610;; supporting for DocBook tables.
611;;
612;; And many other individuals who reported bugs and suggestions.
613
614;;; Code:
615
616\f
fc53ebf6 617(require 'regexp-opt)
238240c9
RS
618
619;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
620;;;
621;;; Compatibility:
622;;;
623
624;; hush up the byte-compiler
1042fc7f
SM
625(defvar quail-translating)
626(defvar quail-converting)
627(defvar flyspell-mode)
628(defvar real-last-command)
629(defvar delete-selection-mode)
630;; This is evil!!
631;; (eval-when-compile
632;; (unless (fboundp 'set-face-property)
633;; (defun set-face-property (face prop value)))
634;; (unless (fboundp 'unibyte-char-to-multibyte)
635;; (defun unibyte-char-to-multibyte (char)))
636;; (defun table--point-in-cell-p (&optional location)))
238240c9
RS
637
638;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
639;;;
640;;; Customization:
641;;;
642
643(defgroup table nil
644 "Text based table manipulation utilities.
645See `table-insert' for examples about how to use."
646 :tag "Table"
647 :prefix "table-"
648 :group 'editing
649 :group 'wp
650 :group 'paragraphs
83650443 651 :group 'fill
bf247b6e 652 :version "22.1")
238240c9 653
23179a73 654(defgroup table-hooks nil
ec85195e 655 "Hooks for table manipulation utilities."
23179a73
MR
656 :group 'table)
657
238240c9
RS
658(defcustom table-time-before-update 0.2
659 "*Time in seconds before updating the cell contents after typing.
660Updating the cell contents on the screen takes place only after this
661specified amount of time has passed after the last modification to the
662cell contents. When the contents of a table cell changes repetitively
663and frequently the updating the cell contents on the screen is
664deferred until at least this specified amount of quiet time passes. A
665smaller number wastes more computation resource by unnecessarily
666frequent screen update. A large number presents noticeable and
667annoying delay before the typed result start appearing on the screen."
668 :tag "Time Before Cell Update"
669 :type 'number
670 :group 'table)
671
672(defcustom table-time-before-reformat 0.2
673 "*Time in seconds before reformatting the table.
674This many seconds must pass in addition to `table-time-before-update'
675before the table is updated with newly widened width or heightened
676height."
677 :tag "Time Before Cell Reformat"
678 :type 'number
679 :group 'table)
680
681(defcustom table-command-prefix [(control c) (control c)]
682 "*Key sequence to be used as prefix for table command key bindings."
790f437c 683 :type '(vector (repeat :inline t sexp))
238240c9
RS
684 :tag "Table Command Prefix"
685 :group 'table)
686
b4c925d8 687(defface table-cell
ea81d57e
DN
688 '((((min-colors 88) (class color))
689 (:foreground "gray90" :background "blue1"))
690 (((class color))
238240c9
RS
691 (:foreground "gray90" :background "blue"))
692 (t (:bold t)))
693 "*Face used for table cell contents."
694 :tag "Cell Face"
695 :group 'table)
696
e99add21
JB
697(defcustom table-cell-horizontal-chars "-="
698 "*Characters that may be used for table cell's horizontal border line."
699 :tag "Cell Horizontal Boundary Characters"
700 :type 'string
238240c9
RS
701 :group 'table)
702
703(defcustom table-cell-vertical-char ?\|
704 "*Character that forms table cell's vertical border line."
705 :tag "Cell Vertical Boundary Character"
706 :type 'character
707 :group 'table)
708
709(defcustom table-cell-intersection-char ?\+
710 "*Character that forms table cell's corner."
711 :tag "Cell Intersection Character"
712 :type 'character
713 :group 'table)
714
715(defcustom table-word-continuation-char ?\\
716 "*Character that indicates word continuation into the next line.
717This character has a special meaning only in the fixed width mode,
718that is when `table-fixed-width-mode' is non-nil . In the fixed width
719mode this character indicates that the location is continuing into the
720next line. Be careful about the choice of this character. It is
721treated substantially different manner than ordinary characters. Try
722select a character that is unlikely to appear in your document."
723 :tag "Cell Word Continuation Character"
724 :type 'character
725 :group 'table)
726
727(defun table-set-table-fixed-width-mode (variable value)
728 (if (fboundp variable)
729 (funcall variable (if value 1 -1))))
730
731(defun table-initialize-table-fixed-width-mode (variable value)
732 (set variable value))
733
734(defcustom table-fixed-width-mode nil
735 "*Cell width is fixed when this is non-nil.
736Normally it should be nil for allowing automatic cell width expansion
737that widens a cell when it is necessary. When non-nil, typing in a
738cell does not automatically expand the cell width. A word that is too
739long to fit in a cell is chopped into multiple lines. The chopped
740location is indicated by `table-word-continuation-char'. This
741variable's value can be toggled by \\[table-fixed-width-mode] at
742run-time."
743 :tag "Fix Cell Width"
744 :type 'boolean
745 :initialize 'table-initialize-table-fixed-width-mode
746 :set 'table-set-table-fixed-width-mode
747 :group 'table)
748
749(defcustom table-detect-cell-alignment t
750 "*Detect cell contents alignment automatically.
751When non-nil cell alignment is automatically determined by the
752appearance of the current cell contents when recognizing tables as a
753whole. This applies to `table-recognize', `table-recognize-region'
754and `table-recognize-table' but not to `table-recognize-cell'."
755 :tag "Detect Cell Alignment"
756 :type 'boolean
757 :group 'table)
758
759(defcustom table-dest-buffer-name "table"
760 "*Default buffer name (without a suffix) for source generation."
761 :tag "Source Buffer Name"
762 :type 'string
763 :group 'table)
764
765(defcustom table-html-delegate-spacing-to-user-agent nil
766 "*Non-nil delegates cell contents spacing entirely to user agent.
767Otherwise, when nil, it preserves the original spacing and line breaks."
768 :tag "HTML delegate spacing"
769 :type 'boolean
770 :group 'table)
771
772(defcustom table-html-th-rows 0
773 "*Number of top rows to become header cells automatically in HTML generation."
774 :tag "HTML Header Rows"
775 :type 'integer
776 :group 'table)
777
778(defcustom table-html-th-columns 0
779 "*Number of left columns to become header cells automatically in HTML generation."
780 :tag "HTML Header Columns"
781 :type 'integer
782 :group 'table)
783
784(defcustom table-html-table-attribute "border=\"1\""
785 "*Table attribute that applies to the table in HTML generation."
786 :tag "HTML table attribute"
787 :type 'string
788 :group 'table)
789
790(defcustom table-html-cell-attribute ""
791 "*Cell attribute that applies to all cells in HTML generation.
792Do not specify \"align\" and \"valign\" because they are determined by
793the cell contents dynamically."
794 :tag "HTML cell attribute"
795 :type 'string
796 :group 'table)
797
798(defcustom table-cals-thead-rows 1
799 "*Number of top rows to become header rows in CALS table."
800 :tag "CALS Header Rows"
801 :type 'integer
802 :group 'table)
803
804;;;###autoload
805(defcustom table-cell-map-hook nil
806 "*Normal hooks run when finishing construction of `table-cell-map'.
807User can modify `table-cell-map' by adding custom functions here."
808 :tag "Cell Keymap Hooks"
809 :type 'hook
23179a73 810 :group 'table-hooks)
238240c9
RS
811
812(defcustom table-disable-incompatibility-warning nil
813 "*Disable compatibility warning notice.
814When nil user is reminded of known incompatible issues."
815 :tag "Disable Incompatibility Warning"
816 :type 'boolean
817 :group 'table)
818
819(defcustom table-abort-recognition-when-input-pending t
820 "*Abort current recognition process when input pending.
821Abort current recognition process when we are not sure that no input
822is available. When non-nil lengthy recognition process is aborted
823simply by any key input."
824 :tag "Abort Recognition When Input Pending"
825 :type 'boolean
826 :group 'table)
827
828;;;###autoload
829(defcustom table-load-hook nil
830 "*List of functions to be called after the table is first loaded."
831 :type 'hook
832 :group 'table-hooks)
833
834;;;###autoload
835(defcustom table-point-entered-cell-hook nil
836 "*List of functions to be called after point entered a table cell."
837 :type 'hook
838 :group 'table-hooks)
839
840;;;###autoload
841(defcustom table-point-left-cell-hook nil
842 "*List of functions to be called after point left a table cell."
843 :type 'hook
844 :group 'table-hooks)
845
273681e1
KS
846(defvar table-yank-handler '(nil nil t nil)
847 "Yank handler for tables.")
d6db9101 848
238240c9
RS
849(setplist 'table-disable-incompatibility-warning nil)
850
851(defvar table-disable-menu (null (and (locate-library "easymenu")
852 (require 'easymenu)
853 (fboundp 'easy-menu-add-item)))
854 "*When non-nil, use of menu by table package is disabled.
855It must be set before loading this package `table.el' for the first
856time.")
857
858\f
859;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
860;;;
861;;; Implementation:
862;;;
863
864;;; Internal variables and constants
865;;; No need of user configuration
866
867(defconst table-paragraph-start "[ \t\n\f]"
868 "*Regexp for beginning of a line that starts OR separates paragraphs.")
869(defconst table-cache-buffer-name " *table cell cache*"
870 "Cell cache buffer name.")
871(defvar table-cell-info-lu-coordinate nil
872 "Zero based coordinate of the cached cell's left upper corner.")
873(defvar table-cell-info-rb-coordinate nil
874 "Zero based coordinate of the cached cell's right bottom corner.")
875(defvar table-cell-info-width nil
876 "Number of characters per cached cell width.")
877(defvar table-cell-info-height nil
878 "Number of lines per cached cell height.")
879(defvar table-cell-info-justify nil
880 "Justification information of the cached cell.")
881(defvar table-cell-info-valign nil
882 "Vertical alignment information of the cached cell.")
883(defvar table-cell-self-insert-command-count 0
884 "Counter for undo control.")
885(defvar table-cell-map nil
886 "Keymap for table cell contents.")
887(defvar table-cell-global-map-alist nil
888 "Alist of copy of global maps that are substituted in `table-cell-map'.")
889(defvar table-global-menu-map nil
890 "Menu map created via `easy-menu-define'.")
891(defvar table-cell-menu-map nil
892 "Menu map created via `easy-menu-define'.")
893(defvar table-cell-buffer nil
894 "Buffer that contains the table cell.")
895(defvar table-cell-cache-point-coordinate nil
896 "Cache point coordinate based from the cell origin.")
897(defvar table-cell-cache-mark-coordinate nil
898 "Cache mark coordinate based from the cell origin.")
899(defvar table-cell-entered-state nil
900 "Records the state whether currently in a cell or nor.")
901(defvar table-update-timer nil
902 "Timer id for deferred cell update.")
903(defvar table-widen-timer nil
904 "Timer id for deferred cell update.")
905(defvar table-heighten-timer nil
906 "Timer id for deferred cell update.")
907(defvar table-inhibit-update nil
908 "Non-nil inhibits implicit cell and cache updates.
909It inhibits `table-with-cache-buffer' to update data in both direction, cell to cache and cache to cell.")
910(defvar table-inhibit-auto-fill-paragraph nil
911 "Non-nil inhibits auto fill paragraph when `table-with-cache-buffer' exits.
912This is always set to nil at the entry to `table-with-cache-buffer' before executing body forms.")
913(defvar table-mode-indicator nil
914 "For mode line indicator")
1042fc7f
SM
915;; This is not a real minor-mode but placed in the minor-mode-alist
916;; so that we can show the indicator on the mode line handy.
d2eed686 917(make-variable-buffer-local 'table-mode-indicator)
1042fc7f
SM
918(unless (assq table-mode-indicator minor-mode-alist)
919 (push '(table-mode-indicator (table-fixed-width-mode " Fixed-Table" " Table"))
920 minor-mode-alist))
921
238240c9
RS
922(defconst table-source-languages '(html latex cals)
923 "Supported source languages.")
924(defvar table-source-info-plist nil
925 "General storage for temporary information used while generating source.")
1042fc7f 926
238240c9
RS
927;;; The following history containers not only keep the history of user
928;;; entries but also serve as the default value providers. When an
929;;; interactive command is invoked it offers a user the latest entry
930;;; of the history as a default selection. Therefore the values below
931;;; are the first default value when a command is invoked for the very
932;;; first time when there is no real history existing yet.
933(defvar table-cell-span-direction-history '("right"))
934(defvar table-cell-split-orientation-history '("horizontally"))
935(defvar table-cell-split-contents-to-history '("split"))
936(defvar table-insert-row-column-history '("row"))
937(defvar table-justify-history '("center"))
938(defvar table-columns-history '("3"))
939(defvar table-rows-history '("3"))
940(defvar table-cell-width-history '("5"))
941(defvar table-cell-height-history '("1"))
942(defvar table-source-caption-history '("Table"))
943(defvar table-sequence-string-history '("0"))
944(defvar table-sequence-count-history '("0"))
945(defvar table-sequence-increment-history '("1"))
946(defvar table-sequence-interval-history '("1"))
947(defvar table-sequence-justify-history '("left"))
948(defvar table-source-language-history '("html"))
949(defvar table-col-delim-regexp-history '(""))
950(defvar table-row-delim-regexp-history '(""))
951(defvar table-capture-justify-history '("left"))
952(defvar table-capture-min-cell-width-history '("5"))
953(defvar table-capture-columns-history '(""))
954(defvar table-target-history '("cell"))
955
956;;; Some entries in `table-cell-bindings' are duplicated in
957;;; `table-command-remap-alist'. There is a good reason for
958;;; this. Common key like return key may be taken by some other
959;;; function than normal `newline' function. Thus binding return key
960;;; directly for `*table--cell-newline' ensures that the correct enter
961;;; operation in a table cell. However
962;;; `table-command-remap-alist' has an additional role than
963;;; replacing commands. It is also used to construct a table command
964;;; list. This list is very important because it is used to check if
965;;; the previous command was one of them in this list or not. If the
966;;; previous command is found in the list the current command will not
967;;; refill the table cache. If the command were not listed fast
968;;; typing can cause unwanted cache refill.
969(defconst table-cell-bindings
970 '(([(control i)] . table-forward-cell)
971 ([(control I)] . table-backward-cell)
972 ([tab] . table-forward-cell)
973 ([(shift backtab)] . table-backward-cell) ; for HPUX console keyboard
974 ([(shift iso-lefttab)] . table-backward-cell) ; shift-tab on a microsoft natural keyboard and redhat linux
975 ([(shift tab)] . table-backward-cell)
976 ([return] . *table--cell-newline)
977 ([(control m)] . *table--cell-newline)
978 ([(control j)] . *table--cell-newline-and-indent)
979 ([mouse-3] . *table--present-cell-popup-menu)
980 ([(control ?>)] . table-widen-cell)
981 ([(control ?<)] . table-narrow-cell)
982 ([(control ?})] . table-heighten-cell)
983 ([(control ?{)] . table-shorten-cell)
984 ([(control ?-)] . table-split-cell-vertically)
985 ([(control ?|)] . table-split-cell-horizontally)
986 ([(control ?*)] . table-span-cell)
987 ([(control ?+)] . table-insert-row-column)
988 ([(control ?!)] . table-fixed-width-mode)
989 ([(control ?#)] . table-query-dimension)
990 ([(control ?^)] . table-generate-source)
991 ([(control ?:)] . table-justify)
992 )
993 "Bindings for table cell commands.")
994
e99add21 995(defvar table-command-remap-alist
238240c9
RS
996 '((self-insert-command . *table--cell-self-insert-command)
997 (completion-separator-self-insert-autofilling . *table--cell-self-insert-command)
998 (completion-separator-self-insert-command . *table--cell-self-insert-command)
999 (delete-char . *table--cell-delete-char)
1000 (delete-backward-char . *table--cell-delete-backward-char)
1001 (backward-delete-char . *table--cell-delete-backward-char)
1002 (backward-delete-char-untabify . *table--cell-delete-backward-char)
1003 (newline . *table--cell-newline)
1004 (newline-and-indent . *table--cell-newline-and-indent)
1005 (open-line . *table--cell-open-line)
1006 (quoted-insert . *table--cell-quoted-insert)
1007 (describe-mode . *table--cell-describe-mode)
1008 (describe-bindings . *table--cell-describe-bindings)
1009 (dabbrev-expand . *table--cell-dabbrev-expand)
1010 (dabbrev-completion . *table--cell-dabbrev-completion))
1011 "List of cons cells consisting of (ORIGINAL-COMMAND . TABLE-VERSION-OF-THE-COMMAND).")
1012
e99add21 1013(defvar table-command-list nil
238240c9
RS
1014 "List of commands that override original commands.")
1015;; construct the real contents of the `table-command-list'
1016(let ((remap-alist table-command-remap-alist))
1017 (setq table-command-list nil)
1018 (while remap-alist
1019 (setq table-command-list (cons (cdar remap-alist) table-command-list))
1020 (setq remap-alist (cdr remap-alist))))
1021
1022(defconst table-global-menu
1023 '("Table"
1024 ("Insert"
1025 ["a Table..." table-insert
1026 :active (and (not buffer-read-only) (not (table--probe-cell)))
1027 :help "Insert a text based table at point"]
1028 ["Row" table-insert-row
951f97e6 1029 :active (table--row-column-insertion-point-p)
238240c9
RS
1030 :help "Insert row(s) of cells in table"]
1031 ["Column" table-insert-column
951f97e6 1032 :active (table--row-column-insertion-point-p 'column)
238240c9
RS
1033 :help "Insert column(s) of cells in table"])
1034 "----"
1035 ("Recognize"
1036 ["in Buffer" table-recognize
1037 :active t
1038 :help "Recognize all tables in the current buffer"]
1039 ["in Region" table-recognize-region
1040 :active (and mark-active (not (eq (mark t) (point))))
1041 :help "Recognize all tables in the current region"]
1042 ["a Table" table-recognize-table
1043 :active (table--probe-cell)
1044 :help "Recognize a table at point"]
1045 ["a Cell" table-recognize-cell
1046 :active (let ((cell (table--probe-cell)))
1047 (and cell (null (table--at-cell-p (car cell)))))
1048 :help "Recognize a cell at point"])
1049 ("Unrecognize"
1050 ["in Buffer" table-unrecognize
1051 :active t
1052 :help "Unrecognize all tables in the current buffer"]
1053 ["in Region" table-unrecognize-region
1054 :active (and mark-active (not (eq (mark t) (point))))
1055 :help "Unrecognize all tables in the current region"]
1056 ["a Table" table-unrecognize-table
1057 :active (table--probe-cell)
1058 :help "Unrecognize the current table"]
1059 ["a Cell" table-unrecognize-cell
1060 :active (let ((cell (table--probe-cell)))
1061 (and cell (table--at-cell-p (car cell))))
1062 :help "Unrecognize the current cell"])
1063 "----"
1064 ["Capture Region" table-capture
1065 :active (and (not buffer-read-only) mark-active (not (eq (mark t) (point))) (not (table--probe-cell)))
1066 :help "Capture text in the current region as a table"]
1067 ["Release" table-release
1068 :active (table--editable-cell-p)
1069 :help "Release the current table as plain text"]))
1070
1071(defconst table-cell-menu
1072 '("Table"
1073 ("Insert"
1074 ["Row" table-insert-row
951f97e6 1075 :active (table--row-column-insertion-point-p)
238240c9
RS
1076 :help "Insert row(s) of cells in table"]
1077 ["Column" table-insert-column
951f97e6 1078 :active (table--row-column-insertion-point-p 'column)
238240c9
RS
1079 :help "Insert column(s) of cells in table"])
1080 ("Delete"
1081 ["Row" table-delete-row
1082 :active (table--editable-cell-p)
1083 :help "Delete row(s) of cells in table"]
1084 ["Column" table-delete-column
1085 :active (table--editable-cell-p)
1086 :help "Delete column(s) of cells in table"])
1087 "----"
1088 ("Split a Cell"
1089 ["Horizontally" table-split-cell-horizontally
1090 :active (table--cell-can-split-horizontally-p)
1091 :help "Split the current cell horizontally at point"]
1092 ["Vertically" table-split-cell-vertically
1093 :active (table--cell-can-split-vertically-p)
1094 :help "Split the current cell vertical at point"])
1095 ("Span a Cell to"
1096 ["Right" (table-span-cell 'right)
1097 :active (table--cell-can-span-p 'right)
1098 :help "Span the current cell into the right cell"]
1099 ["Left" (table-span-cell 'left)
1100 :active (table--cell-can-span-p 'left)
1101 :help "Span the current cell into the left cell"]
1102 ["Above" (table-span-cell 'above)
1103 :active (table--cell-can-span-p 'above)
1104 :help "Span the current cell into the cell above"]
1105 ["Below" (table-span-cell 'below)
1106 :active (table--cell-can-span-p 'below)
1107 :help "Span the current cell into the cell below"])
1108 "----"
1109 ("Shrink Cells"
1110 ["Horizontally" table-narrow-cell
1111 :active (table--editable-cell-p)
1112 :help "Shrink the current cell horizontally"]
1113 ["Vertically" table-shorten-cell
1114 :active (table--editable-cell-p)
1115 :help "Shrink the current cell vertically"])
1116 ("Expand Cells"
1117 ["Horizontally" table-widen-cell
1118 :active (table--editable-cell-p)
1119 :help "Expand the current cell horizontally"]
1120 ["Vertically" table-heighten-cell
1121 :active (table--editable-cell-p)
1122 :help "Expand the current cell vertically"])
1123 "----"
1124 ("Justify"
1125 ("a Cell"
1126 ["Left" (table-justify-cell 'left)
1127 :active (table--editable-cell-p)
1128 :help "Left justify the contents of the current cell"]
1129 ["Center" (table-justify-cell 'center)
1130 :active (table--editable-cell-p)
1131 :help "Center justify the contents of the current cell"]
1132 ["Right" (table-justify-cell 'right)
1133 :active (table--editable-cell-p)
1134 :help "Right justify the contents of the current cell"]
1135 "----"
1136 ["Top" (table-justify-cell 'top)
1137 :active (table--editable-cell-p)
1138 :help "Top align the contents of the current cell"]
1139 ["Middle" (table-justify-cell 'middle)
1140 :active (table--editable-cell-p)
1141 :help "Middle align the contents of the current cell"]
1142 ["Bottom" (table-justify-cell 'bottom)
1143 :active (table--editable-cell-p)
1144 :help "Bottom align the contents of the current cell"]
1145 ["None" (table-justify-cell 'none)
1146 :active (table--editable-cell-p)
1147 :help "Remove vertical alignment from the current cell"])
1148 ("a Row"
1149 ["Left" (table-justify-row 'left)
1150 :active (table--editable-cell-p)
1151 :help "Left justify the contents of all cells in the current row"]
1152 ["Center" (table-justify-row 'center)
1153 :active (table--editable-cell-p)
1154 :help "Center justify the contents of all cells in the current row"]
1155 ["Right" (table-justify-row 'right)
1156 :active (table--editable-cell-p)
1157 :help "Right justify the contents of all cells in the current row"]
1158 "----"
1159 ["Top" (table-justify-row 'top)
1160 :active (table--editable-cell-p)
1161 :help "Top align the contents of all cells in the current row"]
1162 ["Middle" (table-justify-row 'middle)
1163 :active (table--editable-cell-p)
1164 :help "Middle align the contents of all cells in the current row"]
1165 ["Bottom" (table-justify-row 'bottom)
1166 :active (table--editable-cell-p)
1167 :help "Bottom align the contents of all cells in the current row"]
1168 ["None" (table-justify-cell 'none)
1169 :active (table--editable-cell-p)
1170 :help "Remove vertical alignment from all cells in the current row"])
1171 ("a Column"
1172 ["Left" (table-justify-column 'left)
1173 :active (table--editable-cell-p)
1174 :help "Left justify the contents of all cells in the current column"]
1175 ["Center" (table-justify-column 'center)
1176 :active (table--editable-cell-p)
1177 :help "Center justify the contents of all cells in the current column"]
1178 ["Right" (table-justify-column 'right)
1179 :active (table--editable-cell-p)
1180 :help "Right justify the contents of all cells in the current column"]
1181 "----"
1182 ["Top" (table-justify-column 'top)
1183 :active (table--editable-cell-p)
1184 :help "Top align the contents of all cells in the current column"]
1185 ["Middle" (table-justify-column 'middle)
1186 :active (table--editable-cell-p)
1187 :help "Middle align the contents of all cells in the current column"]
1188 ["Bottom" (table-justify-column 'bottom)
1189 :active (table--editable-cell-p)
1190 :help "Bottom align the contents of all cells in the current column"]
1191 ["None" (table-justify-cell 'none)
1192 :active (table--editable-cell-p)
1193 :help "Remove vertical alignment from all cells in the current column"])
1194 ("a Paragraph"
1195 ["Left" (table-justify-cell 'left t)
1196 :active (table--editable-cell-p)
1197 :help "Left justify the current paragraph"]
1198 ["Center" (table-justify-cell 'center t)
1199 :active (table--editable-cell-p)
1200 :help "Center justify the current paragraph"]
1201 ["Right" (table-justify-cell 'right t)
1202 :active (table--editable-cell-p)
1203 :help "Right justify the current paragraph"]))
1204 "----"
1205 ["Query Dimension" table-query-dimension
1206 :active (table--probe-cell)
1207 :help "Get the dimension of the current cell and the current table"]
1208 ["Generate Source" table-generate-source
1209 :active (table--probe-cell)
1210 :help "Generate source of the current table in the specified language"]
1211 ["Insert Sequence" table-insert-sequence
1212 :active (table--editable-cell-p)
1213 :help "Travel cells forward while inserting a specified sequence string in each cell"]
1214 ("Unrecognize"
1215 ["a Table" table-unrecognize-table
1216 :active (table--probe-cell)
1217 :help "Unrecognize the current table"]
1218 ["a Cell" table-unrecognize-cell
1219 :active (let ((cell (table--probe-cell)))
1220 (and cell (table--at-cell-p (car cell))))
1221 :help "Unrecognize the current cell"])
1222 ["Release" table-release
1223 :active (table--editable-cell-p)
1224 :help "Release the current table as plain text"]
1225 ("Configure Width to"
1226 ["Auto Expand Mode" (table-fixed-width-mode -1)
1227 :active t
1228 :style radio
1229 :selected (not table-fixed-width-mode)
1230 :help "A mode that allows automatic horizontal cell expansion"]
1231 ["Fixed Width Mode" (table-fixed-width-mode 1)
1232 :active t
1233 :style radio
1234 :selected table-fixed-width-mode
1235 :help "A mode that does not allow automatic horizontal cell expansion"])
1236 ("Navigate"
1237 ["Forward Cell" table-forward-cell
1238 :active (table--probe-cell)
1239 :help "Move point forward by cell(s)"]
1240 ["Backward Cell" table-backward-cell
1241 :active (table--probe-cell)
1242 :help "Move point backward by cell(s)"])
1243 ))
1244
1245;; XEmacs causes an error when encountering unknown keywords in the
1246;; menu definition. Specifically the :help keyword is new in Emacs 21
1247;; and causes error for the XEmacs function `check-menu-syntax'. IMHO
1248;; it is unwise to generate an error for unknown keywords because it
1249;; kills the nice backward compatible extensibility of keyword use.
1250;; Unknown keywords should be quietly ignore so that future extension
1251;; does not cause a problem in the old implementation. Sigh...
1252(when (featurep 'xemacs)
1253 (mapcar
1254 (defun table--tweak-menu-for-xemacs (menu)
1255 (cond
1256 ((listp menu)
1257 (mapcar 'table--tweak-menu-for-xemacs menu))
1258 ((vectorp menu)
1259 (let ((i 0) (len (length menu)))
1260 (while (< i len)
1261 ;; replace :help with something harmless.
1262 (if (eq (aref menu i) :help) (aset menu i :included))
1263 (setq i (1+ i)))))))
1264 (list table-global-menu table-cell-menu))
1265 (defvar mark-active t))
1266
1267;; register table menu under global tools menu
1268(unless table-disable-menu
1269 (easy-menu-define table-global-menu-map nil "Table global menu" table-global-menu)
1270 (if (featurep 'xemacs)
1271 (progn
1272 (easy-menu-add-item nil '("Tools") table-global-menu-map))
6e50f7e0 1273 (easy-menu-add-item (current-global-map) '("menu-bar" "tools") "--")
238240c9
RS
1274 (easy-menu-add-item (current-global-map) '("menu-bar" "tools") table-global-menu-map)))
1275
1276;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1277;;
1278;; Macros
1279;;
1280
1281(defmacro table-with-cache-buffer (&rest body)
1282 "Execute the forms in BODY with table cache buffer as the current buffer.
1283This macro simplifies the rest of the work greatly by condensing the
1284common idiom used in many of the cell manipulation functions. It does
1285not return any meaningful value.
1286
1287Save the current buffer and set the cache buffer as the current
1288buffer. Move the point to the cache buffer coordinate
1289`table-cell-cache-point-coordinate'. After BODY forms are executed,
1290the paragraph is filled as long as `table-inhibit-auto-fill-paragraph'
1291remains nil. BODY can set it to t when it does not want to fill the
1292paragraph. If necessary the cell width and height are extended as the
1293consequence of cell content modification by the BODY. Then the
1294current buffer is restored to the original one. The last cache point
1295coordinate is stored in `table-cell-cache-point-coordinate'. The
1296original buffer's point is moved to the location that corresponds to
1297the last cache point coordinate."
1298 (let ((height-expansion (make-symbol "height-expansion-var-symbol"))
1299 (width-expansion (make-symbol "width-expansion-var-symbol")))
1300 `(let (,height-expansion ,width-expansion)
1301 ;; make sure cache has valid data unless it is explicitly inhibited.
1302 (unless table-inhibit-update
1303 (table-recognize-cell))
1304 (with-current-buffer (get-buffer-create table-cache-buffer-name)
1305 ;; goto the cell coordinate based on `table-cell-cache-point-coordinate'.
1306 (set-mark (table--goto-coordinate table-cell-cache-mark-coordinate))
1307 (table--goto-coordinate table-cell-cache-point-coordinate)
1308 (table--untabify-line)
1309 ;; always reset before executing body forms because auto-fill behavior is the default.
1310 (setq table-inhibit-auto-fill-paragraph nil)
1311 ;; do the body
1312 ,@body
1313 ;; fill paragraph unless the body does not want to by setting `table-inhibit-auto-fill-paragraph'.
1314 (unless table-inhibit-auto-fill-paragraph
1315 (if (and table-cell-info-justify
1316 (not (eq table-cell-info-justify 'left)))
1317 (table--fill-region (point-min) (point-max))
1318 (table--fill-region
1319 (save-excursion (forward-paragraph -1) (point))
1320 (save-excursion (forward-paragraph 1) (point)))))
1321 ;; keep the updated cell coordinate.
1322 (setq table-cell-cache-point-coordinate (table--get-coordinate))
1323 ;; determine the cell width expansion.
1324 (setq ,width-expansion (table--measure-max-width))
1325 (if (<= ,width-expansion table-cell-info-width) nil
1326 (table--fill-region (point-min) (point-max) ,width-expansion)
1327 ;; keep the updated cell coordinate.
1328 (setq table-cell-cache-point-coordinate (table--get-coordinate)))
1329 (setq ,width-expansion (- ,width-expansion table-cell-info-width))
1330 ;; determine the cell height expansion.
1331 (if (looking-at "\\s *\\'") nil
1332 (goto-char (point-min))
1333 (if (re-search-forward "\\(\\s *\\)\\'" nil t)
1334 (goto-char (match-beginning 1))))
1335 (setq ,height-expansion (- (cdr (table--get-coordinate)) (1- table-cell-info-height))))
1336 ;; now back to the table buffer.
1337 ;; expand the cell width in the table buffer if necessary.
1338 (if (> ,width-expansion 0)
1339 (table-widen-cell ,width-expansion 'no-copy 'no-update))
1340 ;; expand the cell height in the table buffer if necessary.
1341 (if (> ,height-expansion 0)
1342 (table-heighten-cell ,height-expansion 'no-copy 'no-update))
1343 ;; do valign
1344 (with-current-buffer (get-buffer-create table-cache-buffer-name)
1345 (table--goto-coordinate table-cell-cache-point-coordinate)
1346 (setq table-cell-cache-point-coordinate (table--valign)))
1347 ;; move the point in the table buffer to the location that corresponds to
1348 ;; the location in the cell cache buffer
1349 (table--goto-coordinate (table--transcoord-cache-to-table table-cell-cache-point-coordinate))
1350 ;; set up the update timer unless it is explicitly inhibited.
1351 (unless table-inhibit-update
1352 (table--update-cell)))))
1353
1354;; for debugging the body form of the macro
1355(put 'table-with-cache-buffer 'edebug-form-spec '(body))
1356;; for neat presentation use the same indentation as `progn'
1357(put 'table-with-cache-buffer 'lisp-indent-function 0)
1358(if (or (featurep 'xemacs)
1359 (null (fboundp 'font-lock-add-keywords))) nil
1360 ;; color it as a keyword
1361 (font-lock-add-keywords
1362 'emacs-lisp-mode
1363 '("\\<table-with-cache-buffer\\>")))
1364
1365(defmacro table-put-source-info (prop value)
1366 "Register source generation information."
1367 `(put 'table-source-info-plist ,prop ,value))
1368
1369(defmacro table-get-source-info (prop)
1370 "Retrieve source generation information."
1371 `(get 'table-source-info-plist ,prop))
1372
1373;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1374;;
1375;; Modified commands for cell operation
1376;;
1377
1378;; Point Motion Only Group
4e454e5b 1379(mapc
238240c9
RS
1380 (lambda (command)
1381 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1382 (doc-string (format "Table remapped function for `%s'." command)))
1383 (fset func-symbol
1384 `(lambda
1385 (&rest args)
1386 ,doc-string
1387 (interactive)
1388 (let ((table-inhibit-update t)
1389 (deactivate-mark nil))
1390 (table--finish-delayed-tasks)
1391 (table-recognize-cell 'force)
1392 (table-with-cache-buffer
1393 (call-interactively ',command)
1394 (setq table-inhibit-auto-fill-paragraph t)))))
1395 (setq table-command-remap-alist
1396 (cons (cons command func-symbol)
1397 table-command-remap-alist))))
04437a8f
EZ
1398 '(move-beginning-of-line
1399 beginning-of-line
1400 move-end-of-line
238240c9
RS
1401 end-of-line
1402 beginning-of-buffer
1403 end-of-buffer
1404 forward-word
1405 backward-word
7780d793
JB
1406 forward-sentence
1407 backward-sentence
238240c9
RS
1408 forward-paragraph
1409 backward-paragraph))
1410
1411;; Extraction Group
4e454e5b 1412(mapc
238240c9
RS
1413 (lambda (command)
1414 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1415 (doc-string (format "Table remapped function for `%s'." command)))
1416 (fset func-symbol
1417 `(lambda
1418 (&rest args)
1419 ,doc-string
1420 (interactive)
1421 (table--finish-delayed-tasks)
1422 (table-recognize-cell 'force)
1423 (table-with-cache-buffer
1424 (table--remove-cell-properties (point-min) (point-max))
1425 (table--remove-eol-spaces (point-min) (point-max))
1426 (call-interactively ',command))
1427 (table--finish-delayed-tasks)))
1428 (setq table-command-remap-alist
1429 (cons (cons command func-symbol)
1430 table-command-remap-alist))))
1431 '(kill-region
7780d793 1432 kill-ring-save
238240c9
RS
1433 delete-region
1434 copy-region-as-kill
7780d793
JB
1435 kill-line
1436 kill-word
1437 backward-kill-word
1438 kill-sentence
1439 backward-kill-sentence
1440 kill-paragraph
1441 backward-kill-paragraph
1442 kill-sexp
1443 backward-kill-sexp))
238240c9
RS
1444
1445;; Pasting Group
4e454e5b 1446(mapc
238240c9
RS
1447 (lambda (command)
1448 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1449 (doc-string (format "Table remapped function for `%s'." command)))
1450 (fset func-symbol
1451 `(lambda
1452 (&rest args)
1453 ,doc-string
1454 (interactive)
1455 (table--finish-delayed-tasks)
1456 (table-recognize-cell 'force)
1457 (table-with-cache-buffer
1458 (call-interactively ',command)
1459 (table--untabify (point-min) (point-max))
1460 (table--fill-region (point-min) (point-max))
1461 (setq table-inhibit-auto-fill-paragraph t))
1462 (table--finish-delayed-tasks)))
1463 (setq table-command-remap-alist
1464 (cons (cons command func-symbol)
1465 table-command-remap-alist))))
1466 '(yank
1467 clipboard-yank
1468 yank-clipboard-selection
1469 insert))
1470
1471;; Formatting Group
4e454e5b 1472(mapc
238240c9
RS
1473 (lambda (command)
1474 (let ((func-symbol (intern (format "*table--cell-%s" command)))
1475 (doc-string (format "Table remapped function for `%s'." command)))
1476 (fset func-symbol
1477 `(lambda
1478 (&rest args)
1479 ,doc-string
1480 (interactive)
1481 (table--finish-delayed-tasks)
1482 (table-recognize-cell 'force)
1483 (table-with-cache-buffer
1484 (let ((fill-column table-cell-info-width))
1485 (call-interactively ',command))
1486 (setq table-inhibit-auto-fill-paragraph t))
1487 (table--finish-delayed-tasks)))
1488 (setq table-command-remap-alist
1489 (cons (cons command func-symbol)
1490 table-command-remap-alist))))
1491 '(center-line
1492 conter-region
1493 center-paragraph
1494 fill-paragraph))
1495
1496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1497;;
1498;; Commands
1499;;
1500
1501;;;###autoload
1502(defun table-insert (columns rows &optional cell-width cell-height)
1503 "Insert an editable text table.
1504Insert a table of specified number of COLUMNS and ROWS. Optional
1505parameter CELL-WIDTH and CELL-HEIGHT can specify the size of each
1506cell. The cell size is uniform across the table if the specified size
1507is a number. They can be a list of numbers to specify different size
1508for each cell. When called interactively, the list of number is
1509entered by simply listing all the numbers with space characters
1510delimiting them.
1511
1512Examples:
1513
1514\\[table-insert] inserts a table at the current point location.
1515
1516Suppose we have the following situation where `-!-' indicates the
1517location of point.
1518
1519 -!-
1520
1521Type \\[table-insert] and hit ENTER key. As it asks table
1522specification, provide 3 for number of columns, 1 for number of rows,
15235 for cell width and 1 for cell height. Now you shall see the next
1524table and the point is automatically moved to the beginning of the
1525first cell.
1526
1527 +-----+-----+-----+
1528 |-!- | | |
1529 +-----+-----+-----+
1530
1531Inside a table cell, there are special key bindings. \\<table-cell-map>
1532
1533M-9 \\[table-widen-cell] (or \\[universal-argument] 9 \\[table-widen-cell]) widens the first cell by 9 character
1534width, which results as
1535
1536 +--------------+-----+-----+
1537 |-!- | | |
1538 +--------------+-----+-----+
1539
1540Type TAB \\[table-widen-cell] then type TAB M-2 M-7 \\[table-widen-cell] (or \\[universal-argument] 2 7 \\[table-widen-cell]). Typing
1541TAB moves the point forward by a cell. The result now looks like this:
1542
1543 +--------------+------+--------------------------------+
1544 | | |-!- |
1545 +--------------+------+--------------------------------+
1546
1547If you knew each width of the columns prior to the table creation,
1548what you could have done better was to have had given the complete
1549width information to `table-insert'.
1550
1551Cell width(s): 14 6 32
1552
db95369b 1553instead of
238240c9
RS
1554
1555Cell width(s): 5
1556
1557This would have eliminated the previously mentioned width adjustment
1558work all together.
1559
1560If the point is in the last cell type S-TAB S-TAB to move it to the
1561first cell. Now type \\[table-heighten-cell] which heighten the row by a line.
1562
1563 +--------------+------+--------------------------------+
1564 |-!- | | |
1565 | | | |
1566 +--------------+------+--------------------------------+
1567
1568Type \\[table-insert-row-column] and tell it to insert a row.
1569
1570 +--------------+------+--------------------------------+
1571 |-!- | | |
1572 | | | |
1573 +--------------+------+--------------------------------+
1574 | | | |
1575 | | | |
1576 +--------------+------+--------------------------------+
1577
1578Move the point under the table as shown below.
1579
1580 +--------------+------+--------------------------------+
1581 | | | |
1582 | | | |
1583 +--------------+------+--------------------------------+
1584 | | | |
1585 | | | |
1586 +--------------+------+--------------------------------+
1587 -!-
1588
1589Type M-x table-insert-row instead of \\[table-insert-row-column]. \\[table-insert-row-column] does not work
1590when the point is outside of the table. This insertion at
1591outside of the table effectively appends a row at the end.
1592
1593 +--------------+------+--------------------------------+
1594 | | | |
1595 | | | |
1596 +--------------+------+--------------------------------+
1597 | | | |
1598 | | | |
1599 +--------------+------+--------------------------------+
1600 |-!- | | |
1601 | | | |
1602 +--------------+------+--------------------------------+
1603
1604Text editing inside the table cell produces reasonably expected
1605results.
1606
1607 +--------------+------+--------------------------------+
1608 | | | |
1609 | | | |
1610 +--------------+------+--------------------------------+
1611 | | |Text editing inside the table |
1612 | | |cell produces reasonably |
1613 | | |expected results.-!- |
1614 +--------------+------+--------------------------------+
1615 | | | |
1616 | | | |
1617 +--------------+------+--------------------------------+
1618
1619Inside a table cell has a special keymap.
1620
1621\\{table-cell-map}
1622"
1623 (interactive
1624 (progn
1625 (barf-if-buffer-read-only)
1626 (if (table--probe-cell)
1627 (error "Can't insert a table inside a table"))
1628 (mapcar (function table--read-from-minibuffer)
1629 '(("Number of columns" . table-columns-history)
1630 ("Number of rows" . table-rows-history)
1631 ("Cell width(s)" . table-cell-width-history)
1632 ("Cell height(s)" . table-cell-height-history)))))
1633 (table--make-cell-map)
1634 ;; reform the arguments.
1635 (if (null cell-width) (setq cell-width (car table-cell-width-history)))
1636 (if (null cell-height) (setq cell-height (car table-cell-height-history)))
1637 (if (stringp columns) (setq columns (string-to-number columns)))
1638 (if (stringp rows) (setq rows (string-to-number rows)))
1639 (if (stringp cell-width) (setq cell-width (table--string-to-number-list cell-width)))
1640 (if (stringp cell-height) (setq cell-height (table--string-to-number-list cell-height)))
1641 (if (numberp cell-width) (setq cell-width (cons cell-width nil)))
1642 (if (numberp cell-height) (setq cell-height (cons cell-height nil)))
1643 ;; test validity of the arguments.
4e454e5b
JB
1644 (mapc (lambda (arg)
1645 (let* ((value (symbol-value arg))
1646 (error-handler
1647 (function (lambda ()
1648 (error "%s must be a positive integer%s" arg
1649 (if (listp value) " or a list of positive integers" ""))))))
1650 (if (null value) (funcall error-handler))
1651 (mapcar (function (lambda (arg1)
1652 (if (or (not (integerp arg1))
1653 (< arg1 1))
1654 (funcall error-handler))))
1655 (if (listp value) value
1656 (cons value nil)))))
1657 '(columns rows cell-width cell-height))
238240c9
RS
1658 (let ((orig-coord (table--get-coordinate))
1659 (coord (table--get-coordinate))
1660 r i cw ch cell-str border-str)
1661 ;; prefabricate the building blocks border-str and cell-str.
1662 (with-temp-buffer
1663 ;; construct border-str
1664 (insert table-cell-intersection-char)
1665 (setq cw cell-width)
1666 (setq i 0)
1667 (while (< i columns)
e99add21 1668 (insert (make-string (car cw) (string-to-char table-cell-horizontal-chars)) table-cell-intersection-char)
238240c9
RS
1669 (if (cdr cw) (setq cw (cdr cw)))
1670 (setq i (1+ i)))
1671 (setq border-str (buffer-substring (point-min) (point-max)))
1672 ;; construct cell-str
1673 (erase-buffer)
1674 (insert table-cell-vertical-char)
1675 (setq cw cell-width)
1676 (setq i 0)
1677 (while (< i columns)
1678 (let ((beg (point)))
ec85195e 1679 (insert (make-string (car cw) ?\s))
238240c9
RS
1680 (insert table-cell-vertical-char)
1681 (table--put-cell-line-property beg (1- (point))))
1682 (if (cdr cw) (setq cw (cdr cw)))
1683 (setq i (1+ i)))
1684 (setq cell-str (buffer-substring (point-min) (point-max))))
1685 ;; if the construction site has an empty border push that border down.
1686 (save-excursion
1687 (beginning-of-line)
1688 (if (looking-at "\\s *$")
1689 (progn
1690 (setq border-str (concat border-str "\n"))
1691 (setq cell-str (concat cell-str "\n")))))
1692 ;; now build the table using the prefabricated building blocks
1693 (setq r 0)
1694 (setq ch cell-height)
1695 (while (< r rows)
1696 (if (> r 0) nil
1697 (table--goto-coordinate coord) (setcdr coord (1+ (cdr coord)))
1698 (table--untabify-line (point))
1699 (insert border-str))
1700 (setq i 0)
1701 (while (< i (car ch))
1702 (table--goto-coordinate coord) (setcdr coord (1+ (cdr coord)))
1703 (table--untabify-line (point))
1704 (insert cell-str)
1705 (setq i (1+ i)))
1706 (table--goto-coordinate coord) (setcdr coord (1+ (cdr coord)))
1707 (table--untabify-line (point))
1708 (insert border-str)
1709 (if (cdr ch) (setq ch (cdr ch)))
1710 (setq r (1+ r)))
1711 ;; stand by at the first cell
1712 (table--goto-coordinate (table--offset-coordinate orig-coord '(1 . 1)))
1713 (table-recognize-cell 'force)))
1714
1715;;;###autoload
1716(defun table-insert-row (n)
1717 "Insert N table row(s).
1718When point is in a table the newly inserted row(s) are placed above
1719the current row. When point is outside of the table it must be below
1720the table within the table width range, then the newly created row(s)
1721are appended at the bottom of the table."
1722 (interactive "*p")
1723 (if (< n 0) (setq n 1))
1724 (let* ((current-coordinate (table--get-coordinate))
1725 (coord-list (table--cell-list-to-coord-list (table--horizontal-cell-list t nil 'top)))
1726 (append-row (if coord-list nil (setq coord-list (table--find-row-column))))
1727 (cell-height (cdr (table--min-coord-list coord-list)))
1728 (left-list nil)
1729 (this-list coord-list)
1730 (right-list (cdr coord-list))
1731 (bottom-border-y (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))
1732 (vertical-str (string table-cell-vertical-char))
1733 (vertical-str-with-properties (let ((str (string table-cell-vertical-char)))
1734 (table--put-cell-keymap-property 0 (length str) str)
1735 (table--put-cell-rear-nonsticky 0 (length str) str) str))
1736 (first-time t))
1737 ;; create the space below for the table to grow
1738 (table--create-growing-space-below (* n (+ 1 cell-height)) coord-list bottom-border-y)
1739 ;; vertically expand each cell from left to right
1740 (while this-list
1741 (let* ((left (prog1 (car left-list) (setq left-list (if left-list (cdr left-list) coord-list))))
1742 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
1743 (right (prog1 (car right-list) (setq right-list (cdr right-list))))
1744 (exclude-left (and left (< (cdar left) (cdar this))))
1745 (exclude-right (and right (<= (cdar right) (cdar this))))
1746 (beg (table--goto-coordinate
1747 (cons (if exclude-left (caar this) (1- (caar this)))
1748 (cdar this))))
1749 (end (table--goto-coordinate
1750 (cons (if exclude-right (cadr this) (1+ (cadr this)))
1751 bottom-border-y)))
1752 (rect (if append-row nil (extract-rectangle beg end))))
1753 ;; prepend blank cell lines to the extracted rectangle
1754 (let ((i n))
1755 (while (> i 0)
1756 (setq rect (cons
1757 (concat (if exclude-left "" (char-to-string table-cell-intersection-char))
e99add21 1758 (make-string (- (cadr this) (caar this)) (string-to-char table-cell-horizontal-chars))
238240c9
RS
1759 (if exclude-right "" (char-to-string table-cell-intersection-char)))
1760 rect))
1761 (let ((j cell-height))
1762 (while (> j 0)
1763 (setq rect (cons
1764 (concat (if exclude-left ""
1765 (if first-time vertical-str vertical-str-with-properties))
1766 (table--cell-blank-str (- (cadr this) (caar this)))
1767 (if exclude-right "" vertical-str-with-properties))
1768 rect))
1769 (setq j (1- j))))
1770 (setq i (1- i))))
1771 (setq first-time nil)
1772 (if append-row
1773 (table--goto-coordinate (cons (if exclude-left (caar this) (1- (caar this)))
1774 (1+ bottom-border-y)))
1775 (delete-rectangle beg end)
1776 (goto-char beg))
1777 (table--insert-rectangle rect)))
1778 ;; fix up the intersections
1779 (setq this-list (if append-row nil coord-list))
1780 (while this-list
1781 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list))))
1782 (i 0))
1783 (while (< i n)
1784 (let ((y (1- (* i (+ 1 cell-height)))))
1785 (table--goto-coordinate (table--offset-coordinate (car this) (cons -1 y)))
1786 (delete-char 1) (insert table-cell-intersection-char)
1787 (table--goto-coordinate (table--offset-coordinate (cons (cadr this) (cdar this)) (cons 0 y)))
1788 (delete-char 1) (insert table-cell-intersection-char)
1789 (setq i (1+ i))))))
1790 ;; move the point to the beginning of the first newly inserted cell.
1791 (if (table--goto-coordinate
1792 (if append-row (cons (car (caar coord-list)) (1+ bottom-border-y))
1793 (caar coord-list))) nil
1794 (table--goto-coordinate current-coordinate))
1795 ;; re-recognize the current cell's new dimension
1796 (table-recognize-cell 'force)))
1797
1798;;;###autoload
1799(defun table-insert-column (n)
1800 "Insert N table column(s).
1801When point is in a table the newly inserted column(s) are placed left
1802of the current column. When point is outside of the table it must be
1803right side of the table within the table height range, then the newly
1804created column(s) are appended at the right of the table."
1805 (interactive "*p")
1806 (if (< n 0) (setq n 1))
1807 (let* ((current-coordinate (table--get-coordinate))
1808 (coord-list (table--cell-list-to-coord-list (table--vertical-cell-list t nil 'left)))
1809 (append-column (if coord-list nil (setq coord-list (table--find-row-column 'column))))
1810 (cell-width (car (table--min-coord-list coord-list)))
e99add21 1811 (border-str (table--multiply-string (concat (make-string cell-width (string-to-char table-cell-horizontal-chars))
238240c9
RS
1812 (char-to-string table-cell-intersection-char)) n))
1813 (cell-str (table--multiply-string (concat (table--cell-blank-str cell-width)
1814 (let ((str (string table-cell-vertical-char)))
1815 (table--put-cell-keymap-property 0 (length str) str)
1816 (table--put-cell-rear-nonsticky 0 (length str) str) str)) n))
1817 (columns-to-extend (* n (+ 1 cell-width)))
1818 (above-list nil)
1819 (this-list coord-list)
1820 (below-list (cdr coord-list))
1821 (right-border-x (car (table--get-coordinate (cdr (table--horizontal-cell-list nil t))))))
1822 ;; push back the affected area above and below this table
1823 (table--horizontally-shift-above-and-below columns-to-extend coord-list)
1824 ;; process each cell vertically from top to bottom
1825 (while this-list
1826 (let* ((above (prog1 (car above-list) (setq above-list (if above-list (cdr above-list) coord-list))))
1827 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
1828 (below (prog1 (car below-list) (setq below-list (cdr below-list))))
1829 (exclude-above (and above (<= (caar above) (caar this))))
1830 (exclude-below (and below (< (caar below) (caar this))))
1831 (beg-coord (cons (if append-column (1+ right-border-x) (caar this))
1832 (if exclude-above (cdar this) (1- (cdar this)))))
1833 (end-coord (cons (1+ right-border-x)
1834 (if exclude-below (cddr this) (1+ (cddr this)))))
1835 rect)
1836 ;; untabify the area right of the bar that is about to be inserted
1837 (let ((coord (table--copy-coordinate beg-coord))
1838 (i 0)
1839 (len (length rect)))
1840 (while (< i len)
1841 (if (table--goto-coordinate coord 'no-extension)
1842 (table--untabify-line (point)))
1843 (setcdr coord (1+ (cdr coord)))
1844 (setq i (1+ i))))
1845 ;; extract and delete the rectangle area including the current
1846 ;; cell and to the right border of the table.
1847 (setq rect (extract-rectangle (table--goto-coordinate beg-coord)
1848 (table--goto-coordinate end-coord)))
1849 (delete-rectangle (table--goto-coordinate beg-coord)
1850 (table--goto-coordinate end-coord))
1851 ;; prepend the empty column string at the beginning of each
1852 ;; rectangle string extracted before.
1853 (let ((rect-str rect)
1854 (first t))
1855 (while rect-str
1856 (if (and first (null exclude-above))
1857 (setcar rect-str (concat border-str (car rect-str)))
1858 (if (and (null (cdr rect-str)) (null exclude-below))
1859 (setcar rect-str (concat border-str (car rect-str)))
1860 (setcar rect-str (concat cell-str (car rect-str)))))
1861 (setq first nil)
1862 (setq rect-str (cdr rect-str))))
1863 ;; insert the extended rectangle
1864 (table--goto-coordinate beg-coord)
1865 (table--insert-rectangle rect)))
1866 ;; fix up the intersections
1867 (setq this-list (if append-column nil coord-list))
1868 (while this-list
1869 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list))))
1870 (i 0))
1871 (while (< i n)
1872 (let ((x (1- (* (1+ i) (+ 1 cell-width)))))
1873 (table--goto-coordinate (table--offset-coordinate (car this) (cons x -1)))
1874 (delete-char 1) (insert table-cell-intersection-char)
1875 (table--goto-coordinate (table--offset-coordinate (cons (caar this) (cddr this)) (cons x 1)))
1876 (delete-char 1) (insert table-cell-intersection-char)
1877 (setq i (1+ i))))))
1878 ;; move the point to the beginning of the first newly inserted cell.
1879 (if (table--goto-coordinate
1880 (if append-column
1881 (cons (1+ right-border-x)
1882 (cdar (car coord-list)))
1883 (caar coord-list))) nil
1884 (table--goto-coordinate current-coordinate))
1885 ;; re-recognize the current cell's new dimension
1886 (table-recognize-cell 'force)))
1887
1888;;;###autoload
1889(defun table-insert-row-column (row-column n)
1890 "Insert row(s) or column(s).
1891See `table-insert-row' and `table-insert-column'."
1892 (interactive
1893 (let ((n (prefix-numeric-value current-prefix-arg)))
1894 (if (< n 0) (setq n 1))
1895 (list (intern (let ((completion-ignore-case t)
1896 (default (car table-insert-row-column-history)))
1897 (downcase (completing-read
1898 (format "Insert %s row%s/column%s (default %s): "
1899 (if (> n 1) (format "%d" n) "a")
1900 (if (> n 1) "s" "")
1901 (if (> n 1) "s" "")
1902 default)
1903 '(("row") ("column"))
1904 nil t nil 'table-insert-row-column-history default))))
1905 n)))
1906 (cond ((eq row-column 'row)
1907 (table-insert-row n))
1908 ((eq row-column 'column)
1909 (table-insert-column n))))
1910
1911;;;###autoload
1912(defun table-recognize (&optional arg)
1913 "Recognize all tables within the current buffer and activate them.
1914Scans the entire buffer and recognizes valid table cells. If the
1915optional numeric prefix argument ARG is negative the tables in the
1916buffer become inactive, meaning the tables become plain text and loses
1917all the table specific features."
1918 (interactive "P")
1919 (setq arg (prefix-numeric-value arg))
1920 (let* ((inhibit-read-only t))
1921 (table-recognize-region (point-min) (point-max) -1)
1922 (if (>= arg 0)
1923 (save-excursion
1924 (goto-char (point-min))
e99add21
JB
1925 (let* ((border (format "[%s%c%c]"
1926 table-cell-horizontal-chars
238240c9
RS
1927 table-cell-vertical-char
1928 table-cell-intersection-char))
1929 (border3 (concat border border border))
e99add21
JB
1930 (non-border (format "^[^%s%c%c]*$"
1931 table-cell-horizontal-chars
238240c9
RS
1932 table-cell-vertical-char
1933 table-cell-intersection-char)))
1934 ;; `table-recognize-region' is an expensive function so minimize
1935 ;; the search area. A minimum table at least consists of three consecutive
1936 ;; table border characters to begin with such as
1937 ;; +-+
1938 ;; |A|
1939 ;; +-+
1940 ;; and any tables end with a line containing no table border characters
1941 ;; or the end of buffer.
1942 (while (and (re-search-forward border3 (point-max) t)
1943 (not (and (input-pending-p)
1944 table-abort-recognition-when-input-pending)))
1945 (message "Recognizing tables...(%d%%)" (/ (* 100 (match-beginning 0)) (- (point-max) (point-min))))
1946 (let ((beg (match-beginning 0))
1947 end)
1948 (if (re-search-forward non-border (point-max) t)
1949 (setq end (match-beginning 0))
1950 (setq end (goto-char (point-max))))
1951 (table-recognize-region beg end arg)))
1952 (message "Recognizing tables...done"))))))
1953
1954;;;###autoload
1955(defun table-unrecognize ()
1956 (interactive)
1957 (table-recognize -1))
1958
1959;;;###autoload
1960(defun table-recognize-region (beg end &optional arg)
1961 "Recognize all tables within region.
1962BEG and END specify the region to work on. If the optional numeric
1963prefix argument ARG is negative the tables in the region become
1964inactive, meaning the tables become plain text and lose all the table
1965specific features."
1966 (interactive "r\nP")
1967 (setq arg (prefix-numeric-value arg))
1968 (let ((inhibit-read-only t)
1969 (modified-flag (buffer-modified-p)))
1970 (if (< arg 0)
1971 (table--remove-cell-properties beg end)
1972 (save-excursion
1973 (goto-char beg)
e99add21
JB
1974 (let* ((border (format "[%s%c%c]"
1975 table-cell-horizontal-chars
238240c9
RS
1976 table-cell-vertical-char
1977 table-cell-intersection-char))
e99add21
JB
1978 (non-border (format "[^%s%c%c]"
1979 table-cell-horizontal-chars
238240c9
RS
1980 table-cell-vertical-char
1981 table-cell-intersection-char))
1982 (inhibit-read-only t))
1983 (unwind-protect
1984 (progn
1985 (remove-text-properties beg end '(table-cell nil))
1986 (while (and (< (point) end)
1987 (not (and (input-pending-p)
1988 table-abort-recognition-when-input-pending)))
1989 (cond
1990 ((looking-at "\n")
1991 (forward-char 1))
1992 ((looking-at border)
1993 (if (re-search-forward non-border end t)
1994 (goto-char (match-beginning 0))
1995 (goto-char end)))
1996 ((table--at-cell-p (point))
1997 (goto-char (next-single-property-change (point) 'table-cell nil end)))
1998 (t
1999 (let ((cell (table-recognize-cell 'force 'no-copy)))
2000 (if (and cell table-detect-cell-alignment)
2001 (table--detect-cell-alignment cell)))
2002 (unless (re-search-forward border end t)
2003 (goto-char end))))))))))
1042fc7f 2004 (restore-buffer-modified-p modified-flag)))
238240c9
RS
2005
2006;;;###autoload
2007(defun table-unrecognize-region (beg end)
2008 (interactive "r")
2009 (table-recognize-region beg end -1))
2010
2011;;;###autoload
2012(defun table-recognize-table (&optional arg)
2013 "Recognize a table at point.
2014If the optional numeric prefix argument ARG is negative the table
2015becomes inactive, meaning the table becomes plain text and loses all
2016the table specific features."
2017 (interactive "P")
2018 (setq arg (prefix-numeric-value arg))
2019 (let ((unrecognize (< arg 0))
2020 (origin-cell (table--probe-cell))
2021 (inhibit-read-only t))
2022 (if origin-cell
2023 (save-excursion
2024 (while
2025 (progn
2026 (table-forward-cell 1 nil unrecognize)
2027 (let ((cell (table--probe-cell)))
2028 (if (and cell table-detect-cell-alignment)
2029 (table--detect-cell-alignment cell))
2030 (and cell (not (equal cell origin-cell))))))))))
2031
2032;;;###autoload
2033(defun table-unrecognize-table ()
2034 (interactive)
2035 (table-recognize-table -1))
2036
2037;;;###autoload
2038(defun table-recognize-cell (&optional force no-copy arg)
2039 "Recognize a table cell that contains current point.
2040Probe the cell dimension and prepare the cell information. The
2041optional two arguments FORCE and NO-COPY are for internal use only and
2042must not be specified. When the optional numeric prefix argument ARG
2043is negative the cell becomes inactive, meaning that the cell becomes
2044plain text and loses all the table specific features."
2045 (interactive "i\ni\np")
2046 (table--make-cell-map)
2047 (if (or force (not (memq (table--get-last-command) table-command-list)))
2048 (let* ((cell (table--probe-cell (interactive-p)))
2049 (cache-buffer (get-buffer-create table-cache-buffer-name))
2050 (modified-flag (buffer-modified-p))
2051 (inhibit-read-only t))
2052 (unwind-protect
2053 (unless (null cell)
2054 ;; initialize the cell info variables
2055 (let ((lu-coordinate (table--get-coordinate (car cell)))
2056 (rb-coordinate (table--get-coordinate (cdr cell))))
2057 ;; update the previous cell if this cell is different from the previous one.
2058 ;; care only lu but ignore rb since size change does not matter.
2059 (unless (equal table-cell-info-lu-coordinate lu-coordinate)
2060 (table--finish-delayed-tasks))
2061 (setq table-cell-info-lu-coordinate lu-coordinate)
2062 (setq table-cell-info-rb-coordinate rb-coordinate)
2063 (setq table-cell-info-width (- (car table-cell-info-rb-coordinate)
2064 (car table-cell-info-lu-coordinate)))
2065 (setq table-cell-info-height (+ (- (cdr table-cell-info-rb-coordinate)
2066 (cdr table-cell-info-lu-coordinate)) 1))
2067 (setq table-cell-info-justify (table--get-cell-justify-property cell))
2068 (setq table-cell-info-valign (table--get-cell-valign-property cell)))
2069 ;; set/remove table cell properties
2070 (if (< (prefix-numeric-value arg) 0)
2071 (let ((coord (table--get-coordinate (car cell)))
2072 (n table-cell-info-height))
2073 (save-excursion
2074 (while (> n 0)
2075 (table--remove-cell-properties
2076 (table--goto-coordinate coord)
2077 (table--goto-coordinate (cons (+ (car coord) table-cell-info-width 1) (cdr coord))))
2078 (setq n (1- n))
2079 (setcdr coord (1+ (cdr coord))))))
2080 (table--put-cell-property cell))
2081 ;; copy the cell contents to the cache buffer
2082 ;; only if no-copy is nil and timers are not set
2083 (unless no-copy
2084 (setq table-cell-cache-point-coordinate (table--transcoord-table-to-cache))
2085 (setq table-cell-cache-mark-coordinate (table--transcoord-table-to-cache
2086 (table--get-coordinate (marker-position (mark-marker)))))
2087 (setq table-cell-buffer (current-buffer))
2088 (let ((rectangle (extract-rectangle (car cell)
2089 (cdr cell))))
2090 (save-current-buffer
2091 (set-buffer cache-buffer)
2092 (erase-buffer)
2093 (table--insert-rectangle rectangle)))))
1042fc7f 2094 (restore-buffer-modified-p modified-flag))
238240c9
RS
2095 (if (featurep 'xemacs)
2096 (table--warn-incompatibility))
2097 cell)))
2098
2099;;;###autoload
2100(defun table-unrecognize-cell ()
2101 (interactive)
2102 (table-recognize-cell nil nil -1))
2103
2104;;;###autoload
2105(defun table-heighten-cell (n &optional no-copy no-update)
2106 "Heighten the current cell by N lines by expanding the cell vertically.
2107Heightening is done by adding blank lines at the bottom of the current
2108cell. Other cells aligned horizontally with the current one are also
2109heightened in order to keep the rectangular table structure. The
2110optional argument NO-COPY is internal use only and must not be
2111specified."
2112 (interactive "*p")
2113 (if (< n 0) (setq n 1))
2114 (let* ((coord-list (table--cell-list-to-coord-list (table--horizontal-cell-list t)))
2115 (left-list nil)
2116 (this-list coord-list)
2117 (right-list (cdr coord-list))
2118 (bottom-border-y (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))
2119 (vertical-str (string table-cell-vertical-char))
2120 (vertical-str-with-properties (string table-cell-vertical-char))
2121 (first-time t)
2122 (current-coordinate (table--get-coordinate)))
2123 ;; prepare the right vertical string with appropriate properties put
2124 (table--put-cell-keymap-property 0 (length vertical-str-with-properties) vertical-str-with-properties)
2125 ;; create the space below for the table to grow
2126 (table--create-growing-space-below n coord-list bottom-border-y)
2127 ;; vertically expand each cell from left to right
2128 (while this-list
2129 (let* ((left (prog1 (car left-list) (setq left-list (if left-list (cdr left-list) coord-list))))
2130 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2131 (right (prog1 (car right-list) (setq right-list (cdr right-list))))
2132 (exclude-left (and left (< (cddr left) (cddr this))))
2133 (exclude-right (and right (<= (cddr right) (cddr this))))
2134 (beg (table--goto-coordinate
2135 (cons (if exclude-left (caar this) (1- (caar this)))
2136 (1+ (cddr this)))))
2137 (end (table--goto-coordinate
2138 (cons (if exclude-right (cadr this) (1+ (cadr this)))
2139 bottom-border-y)))
2140 (rect (extract-rectangle beg end)))
2141 ;; prepend blank cell lines to the extracted rectangle
2142 (let ((i n))
2143 (while (> i 0)
2144 (setq rect (cons
2145 (concat (if exclude-left ""
2146 (if first-time vertical-str vertical-str-with-properties))
2147 (table--cell-blank-str (- (cadr this) (caar this)))
2148 (if exclude-right "" vertical-str-with-properties))
2149 rect))
2150 (setq i (1- i))))
2151 (setq first-time nil)
2152 (delete-rectangle beg end)
2153 (goto-char beg)
2154 (table--insert-rectangle rect)))
2155 (table--goto-coordinate current-coordinate)
2156 ;; re-recognize the current cell's new dimension
2157 (table-recognize-cell 'force no-copy)
2158 (unless no-update
2159 (table--update-cell-heightened))))
2160
2161;;;###autoload
2162(defun table-shorten-cell (n)
2163 "Shorten the current cell by N lines by shrinking the cell vertically.
2164Shortening is done by removing blank lines from the bottom of the cell
2165and possibly from the top of the cell as well. Therefor, the cell
2166must have some bottom/top blank lines to be shorten effectively. This
2167is applicable to all the cells aligned horizontally with the current
2168one because they are also shortened in order to keep the rectangular
2169table structure."
2170 (interactive "*p")
2171 (if (< n 0) (setq n 1))
2172 (table--finish-delayed-tasks)
2173 (let* ((table-inhibit-update t)
2174 (coord-list (table--cell-list-to-coord-list (table--horizontal-cell-list t)))
2175 (left-list nil)
2176 (this-list coord-list)
2177 (right-list (cdr coord-list))
2178 (bottom-budget-list nil)
2179 (bottom-border-y (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))
2180 (current-coordinate (table--get-coordinate))
2181 (current-cell-coordinate (table--cell-to-coord (table--probe-cell)))
2182 (blank-line-regexp "\\s *$"))
2183 (message "Shortening...");; this operation may be lengthy
2184 ;; for each cell calculate the maximum number of blank lines we can delete
2185 ;; and adjust the argument n. n is adjusted so that the total number of
2186 ;; blank lines from top and bottom of a cell do not exceed n, all cell has
2187 ;; at least one line height after blank line deletion.
2188 (while this-list
2189 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list)))))
2190 (table--goto-coordinate (car this))
2191 (table-recognize-cell 'force)
2192 (table-with-cache-buffer
2193 (catch 'end-count
2194 (let ((blank-line-count 0))
2195 (table--goto-coordinate (cons 0 (1- table-cell-info-height)))
2196 ;; count bottom
2197 (while (and (looking-at blank-line-regexp)
2198 (setq blank-line-count (1+ blank-line-count))
2199 ;; need to leave at least one blank line
2200 (if (> blank-line-count n) (throw 'end-count nil) t)
2201 (if (zerop (forward-line -1)) t
2202 (setq n (if (zerop blank-line-count) 0
2203 (1- blank-line-count)))
2204 (throw 'end-count nil))))
2205 (table--goto-coordinate (cons 0 0))
2206 ;; count top
2207 (while (and (looking-at blank-line-regexp)
2208 (setq blank-line-count (1+ blank-line-count))
2209 ;; can consume all blank lines
2210 (if (>= blank-line-count n) (throw 'end-count nil) t)
2211 (zerop (forward-line 1))))
2212 (setq n blank-line-count))))))
2213 ;; construct the bottom-budget-list which is a list of numbers where each number
2214 ;; corresponds to how many lines to be deleted from the bottom of each cell. If
2215 ;; this number, say bb, is smaller than n (bb < n) that means the difference (n - bb)
2216 ;; number of lines must be deleted from the top of the cell in addition to deleting
2217 ;; bb lines from the bottom of the cell.
2218 (setq this-list coord-list)
2219 (while this-list
2220 (let ((this (prog1 (car this-list) (setq this-list (cdr this-list)))))
2221 (table--goto-coordinate (car this))
2222 (table-recognize-cell 'force)
2223 (table-with-cache-buffer
2224 (setq bottom-budget-list
2225 (cons
2226 (let ((blank-line-count 0))
2227 (table--goto-coordinate (cons 0 (1- table-cell-info-height)))
2228 (while (and (looking-at blank-line-regexp)
2229 (< blank-line-count n)
2230 (setq blank-line-count (1+ blank-line-count))
2231 (zerop (forward-line -1))))
2232 blank-line-count)
2233 bottom-budget-list)))))
2234 (setq bottom-budget-list (nreverse bottom-budget-list))
2235 ;; vertically shorten each cell from left to right
2236 (setq this-list coord-list)
2237 (while this-list
2238 (let* ((left (prog1 (car left-list) (setq left-list (if left-list (cdr left-list) coord-list))))
2239 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2240 (right (prog1 (car right-list) (setq right-list (cdr right-list))))
2241 (bottom-budget (prog1 (car bottom-budget-list) (setq bottom-budget-list (cdr bottom-budget-list))))
2242 (exclude-left (and left (< (cddr left) (cddr this))))
2243 (exclude-right (and right (<= (cddr right) (cddr this))))
2244 (beg (table--goto-coordinate (cons (caar this) (cdar this))))
2245 (end (table--goto-coordinate (cons (cadr this) bottom-border-y)))
2246 (rect (extract-rectangle beg end))
2247 (height (+ (- (cddr this) (cdar this)) 1))
ec85195e 2248 (blank-line (make-string (- (cadr this) (caar this)) ?\s)))
238240c9
RS
2249 ;; delete lines from the bottom of the cell
2250 (setcdr (nthcdr (- height bottom-budget 1) rect) (nthcdr height rect))
2251 ;; delete lines from the top of the cell
2252 (if (> n bottom-budget)
2253 (let ((props (text-properties-at 0 (car rect))))
2254 (setq rect (nthcdr (- n bottom-budget) rect))
2255 (set-text-properties 0 1 props (car rect))))
2256 ;; append blank lines below the table
2257 (setq rect (append rect (make-list n blank-line)))
2258 ;; now swap the area with the prepared rect of the same size
2259 (delete-rectangle beg end)
2260 (goto-char beg)
2261 (table--insert-rectangle rect)
2262 ;; for the left and right borders always delete lines from the bottom of the cell
2263 (unless exclude-left
2264 (let* ((beg (table--goto-coordinate (cons (1- (caar this)) (cdar this))))
2265 (end (table--goto-coordinate (cons (caar this) bottom-border-y)))
2266 (rect (extract-rectangle beg end)))
2267 (setcdr (nthcdr (- height n 1) rect) (nthcdr height rect))
2268 (setq rect (append rect (make-list n " ")))
2269 (delete-rectangle beg end)
2270 (goto-char beg)
2271 (table--insert-rectangle rect)))
2272 (unless exclude-right
2273 (let* ((beg (table--goto-coordinate (cons (cadr this) (cdar this))))
2274 (end (table--goto-coordinate (cons (1+ (cadr this)) bottom-border-y)))
2275 (rect (extract-rectangle beg end)))
2276 (setcdr (nthcdr (- height n 1) rect) (nthcdr height rect))
2277 (setq rect (append rect (make-list n " ")))
2278 (delete-rectangle beg end)
2279 (goto-char beg)
2280 (table--insert-rectangle rect)))
2281 ;; if this is the cell where the original point was in, adjust the point location
2282 (if (null (equal this current-cell-coordinate)) nil
2283 (let ((y (- (cdr current-coordinate) (cdar this))))
2284 (if (< y (- n bottom-budget))
2285 (setcdr current-coordinate (cdar this))
2286 (if (< (- y (- n bottom-budget)) (- height n))
2287 (setcdr current-coordinate (+ (cdar this) (- y (- n bottom-budget))))
2288 (setcdr current-coordinate (+ (cdar this) (- height n 1)))))))))
2289 ;; remove the appended blank lines below the table if they are unnecessary
2290 (table--goto-coordinate (cons 0 (1+ (- bottom-border-y n))))
2291 (table--remove-blank-lines n)
2292 ;; re-recognize the current cell's new dimension
2293 (table--goto-coordinate current-coordinate)
2294 (table-recognize-cell 'force)
2295 (table--update-cell-heightened)
2296 (message "")))
2297
2298;;;###autoload
2299(defun table-widen-cell (n &optional no-copy no-update)
2300 "Widen the current cell by N columns and expand the cell horizontally.
2301Some other cells in the same table are widen as well to keep the
2302table's rectangle structure."
2303 (interactive "*p")
2304 (if (< n 0) (setq n 1))
2305 (let* ((coord-list (table--cell-list-to-coord-list (table--vertical-cell-list)))
2306 (below-list nil)
2307 (this-list coord-list)
2308 (above-list (cdr coord-list)))
2309 (save-excursion
2310 ;; push back the affected area above and below this table
2311 (table--horizontally-shift-above-and-below n (reverse coord-list))
2312 ;; now widen vertically for each cell
2313 (while this-list
2314 (let* ((below (prog1 (car below-list) (setq below-list (if below-list (cdr below-list) coord-list))))
2315 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2316 (above (prog1 (car above-list) (setq above-list (cdr above-list))))
2317 (beg (table--goto-coordinate
2318 (cons (car (cdr this))
2319 (if (or (null above) (<= (car (cdr this)) (car (cdr above))))
2320 (1- (cdr (car this)))
2321 (cdr (car this))))))
2322 (end (table--goto-coordinate
2323 (cons (1+ (car (cdr this)))
2324 (if (or (null below) (< (car (cdr this)) (car (cdr below))))
2325 (1+ (cdr (cdr this)))
2326 (cdr (cdr this))))))
2327 (tmp (extract-rectangle (1- beg) end))
e99add21
JB
2328 (border (format "[%s%c]\\%c"
2329 table-cell-horizontal-chars
238240c9
RS
2330 table-cell-intersection-char
2331 table-cell-intersection-char))
2332 (blank (table--cell-blank-str))
2333 rectangle)
2334 ;; create a single wide vertical bar of empty cell fragment
2335 (while tmp
e99add21
JB
2336; (message "tmp is %s" tmp)
2337 (setq rectangle (cons
2338 (if (string-match border (car tmp))
2339 (substring (car tmp) 0 1)
238240c9
RS
2340 blank)
2341 rectangle))
e99add21 2342; (message "rectangle is %s" rectangle)
238240c9
RS
2343 (setq tmp (cdr tmp)))
2344 (setq rectangle (nreverse rectangle))
2345 ;; untabify the area right of the bar that is about to be inserted
2346 (let ((coord (table--get-coordinate beg))
2347 (i 0)
2348 (len (length rectangle)))
2349 (while (< i len)
2350 (if (table--goto-coordinate coord 'no-extension)
2351 (table--untabify-line (point)))
2352 (setcdr coord (1+ (cdr coord)))
2353 (setq i (1+ i))))
2354 ;; insert the bar n times
2355 (goto-char beg)
2356 (let ((i 0))
2357 (while (< i n)
2358 (save-excursion
2359 (table--insert-rectangle rectangle))
2360 (setq i (1+ i)))))))
2361 (table-recognize-cell 'force no-copy)
2362 (unless no-update
2363 (table--update-cell-widened))))
2364
2365;;;###autoload
2366(defun table-narrow-cell (n)
2367 "Narrow the current cell by N columns and shrink the cell horizontally.
2368Some other cells in the same table are narrowed as well to keep the
2369table's rectangle structure."
2370 (interactive "*p")
2371 (if (< n 0) (setq n 1))
2372 (table--finish-delayed-tasks)
2373 (let* ((coord-list (table--cell-list-to-coord-list (table--vertical-cell-list)))
2374 (current-cell (table--cell-to-coord (table--probe-cell)))
2375 (current-coordinate (table--get-coordinate))
2376 tmp-list)
2377 (message "Narrowing...");; this operation may be lengthy
2378 ;; determine the doable n by try narrowing each cell.
2379 (setq tmp-list coord-list)
2380 (while tmp-list
2381 (let ((cell (prog1 (car tmp-list) (setq tmp-list (cdr tmp-list))))
2382 (table-inhibit-update t)
2383 cell-n)
2384 (table--goto-coordinate (car cell))
2385 (table-recognize-cell 'force)
2386 (table-with-cache-buffer
2387 (table--fill-region (point-min) (point-max) (- table-cell-info-width n))
2388 (if (< (setq cell-n (- table-cell-info-width (table--measure-max-width))) n)
2389 (setq n cell-n))
2390 (erase-buffer)
2391 (setq table-inhibit-auto-fill-paragraph t))))
2392 (if (< n 1) nil
2393 ;; narrow only the contents of each cell but leave the cell frame as is because
2394 ;; we need to have valid frame structure in order for table-with-cache-buffer
2395 ;; to work correctly.
2396 (setq tmp-list coord-list)
2397 (while tmp-list
2398 (let* ((cell (prog1 (car tmp-list) (setq tmp-list (cdr tmp-list))))
2399 (table-inhibit-update t)
2400 (currentp (equal cell current-cell))
2401 old-height)
2402 (if currentp (table--goto-coordinate current-coordinate)
2403 (table--goto-coordinate (car cell)))
2404 (table-recognize-cell 'force)
2405 (setq old-height table-cell-info-height)
2406 (table-with-cache-buffer
2407 (let ((out-of-bound (>= (- (car current-coordinate) (car table-cell-info-lu-coordinate))
2408 (- table-cell-info-width n)))
2409 (sticky (and currentp
2410 (save-excursion
2411 (unless (bolp) (forward-char -1))
2412 (looking-at ".*\\S ")))))
2413 (table--fill-region (point-min) (point-max) (- table-cell-info-width n))
2414 (if (or sticky (and currentp (looking-at ".*\\S ")))
2415 (setq current-coordinate (table--transcoord-cache-to-table))
2416 (if out-of-bound (setcar current-coordinate
2417 (+ (car table-cell-info-lu-coordinate) (- table-cell-info-width n 1))))))
2418 (setq table-inhibit-auto-fill-paragraph t))
2419 (table--update-cell 'now)
2420 ;; if this cell heightens and pushes the current cell below, move
2421 ;; the current-coordinate (point location) down accordingly.
2422 (if currentp (setq current-coordinate (table--get-coordinate))
2423 (if (and (> table-cell-info-height old-height)
2424 (> (cdr current-coordinate) (cdr table-cell-info-lu-coordinate)))
2425 (setcdr current-coordinate (+ (cdr current-coordinate)
2426 (- table-cell-info-height old-height)))))
2427 ))
2428 ;; coord-list is now possibly invalid since some cells may have already
2429 ;; been heightened so recompute them by table--vertical-cell-list.
2430 (table--goto-coordinate current-coordinate)
2431 (setq coord-list (table--cell-list-to-coord-list (table--vertical-cell-list)))
2432 ;; push in the affected area above and below this table so that things
2433 ;; on the right side of the table are shifted horizontally neatly.
2434 (table--horizontally-shift-above-and-below (- n) (reverse coord-list))
2435 ;; finally narrow the frames for each cell.
2436 (let* ((below-list nil)
2437 (this-list coord-list)
2438 (above-list (cdr coord-list)))
2439 (while this-list
2440 (let* ((below (prog1 (car below-list) (setq below-list (if below-list (cdr below-list) coord-list))))
2441 (this (prog1 (car this-list) (setq this-list (cdr this-list))))
2442 (above (prog1 (car above-list) (setq above-list (cdr above-list)))))
2443 (delete-rectangle
2444 (table--goto-coordinate
2445 (cons (- (cadr this) n)
2446 (if (or (null above) (<= (cadr this) (cadr above)))
2447 (1- (cdar this))
2448 (cdar this))))
2449 (table--goto-coordinate
2450 (cons (cadr this)
2451 (if (or (null below) (< (cadr this) (cadr below)))
2452 (1+ (cddr this))
2453 (cddr this)))))))))
2454 (table--goto-coordinate current-coordinate)
2455 ;; re-recognize the current cell's new dimension
2456 (table-recognize-cell 'force)
2457 (message "")))
2458
2459;;;###autoload
2460(defun table-forward-cell (&optional arg no-recognize unrecognize)
2461 "Move point forward to the beginning of the next cell.
2462With argument ARG, do it ARG times;
2463a negative argument ARG = -N means move backward N cells.
2464Do not specify NO-RECOGNIZE and UNRECOGNIZE. They are for internal use only.
2465
2466Sample Cell Traveling Order (In Irregular Table Cases)
2467
2468You can actually try how it works in this buffer. Press
2469\\[table-recognize] and go to cells in the following tables and press
2470\\[table-forward-cell] or TAB key.
2471
2472+-----+--+ +--+-----+ +--+--+--+ +--+--+--+ +---------+ +--+---+--+
2473|0 |1 | |0 |1 | |0 |1 |2 | |0 |1 |2 | |0 | |0 |1 |2 |
2474+--+--+ | | +--+--+ +--+ | | | | +--+ +----+----+ +--+-+-+--+
2475|2 |3 | | | |2 |3 | |3 +--+ | | +--+3 | |1 |2 | |3 |4 |
2476| +--+--+ +--+--+ | +--+4 | | | |4 +--+ +--+-+-+--+ +----+----+
2477| |4 | |4 | | |5 | | | | | |5 | |3 |4 |5 | |5 |
2478+--+-----+ +-----+--+ +--+--+--+ +--+--+--+ +--+---+--+ +---------+
2479
2480+--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+
2481|0 |1 |2 | |0 |1 |2 | |0 |1 |2 | |0 |1 |2 |
2482| | | | | +--+ | | | | | +--+ +--+
2483+--+ +--+ +--+3 +--+ | +--+ | |3 +--+4 |
2484|3 | |4 | |4 +--+5 | | |3 | | +--+5 +--+
2485| | | | | |6 | | | | | | |6 | |7 |
2486+--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+
2487
2488+--+--+--+ +--+--+--+ +--+--+--+--+ +--+-----+--+ +--+--+--+--+
2489|0 |1 |2 | |0 |1 |2 | |0 |1 |2 |3 | |0 |1 |2 | |0 |1 |2 |3 |
2490| +--+ | | +--+ | | +--+--+ | | | | | | +--+--+ |
2491| |3 +--+ +--+3 | | +--+4 +--+ +--+ +--+ +--+4 +--+
2492+--+ |4 | |4 | +--+ |5 +--+--+6 | |3 +--+--+4 | |5 | |6 |
2493|5 +--+ | | +--+5 | | |7 |8 | | | |5 |6 | | | | | |
2494| |6 | | | |6 | | +--+--+--+--+ +--+--+--+--+ +--+-----+--+
2495+--+--+--+ +--+--+--+
2496"
2497 ;; After modifying this function, test against the above tables in
2498 ;; the doc string. It is quite tricky. The tables above do not
2499 ;; mean to cover every possible cases of cell layout, of course.
2500 ;; They are examples of tricky cases from implementation point of
2501 ;; view and provided for simple regression test purpose.
2502 (interactive "p")
2503 (or arg (setq arg 1))
2504 (table--finish-delayed-tasks)
2505 (while (null (zerop arg))
2506 (let* ((pivot (table--probe-cell 'abort-on-error))
2507 (cell pivot) edge tip)
2508 ;; go to the beginning of the first right/left cell with same height if exists
2509 (while (and (setq cell (table--goto-coordinate
2510 (cons (if (> arg 0) (1+ (car (table--get-coordinate (cdr cell))))
2511 (1- (car (table--get-coordinate (car cell)))))
2512 (cdr (table--get-coordinate (car pivot)))) 'no-extension))
2513 (setq cell (table--probe-cell))
2514 (/= (cdr (table--get-coordinate (car cell)))
2515 (cdr (table--get-coordinate (car pivot))))))
2516 (if cell (goto-char (car cell)) ; done
2517 ;; if the horizontal move fails search the most left/right edge cell below/above the pivot
2518 ;; but first find the edge cell
2519 (setq edge pivot)
2520 (while (and (table--goto-coordinate
2521 (cons (if (> arg 0) (1- (car (table--get-coordinate (car edge))))
2522 (1+ (car (table--get-coordinate (cdr edge)))))
2523 (cdr (table--get-coordinate (car pivot)))) 'no-extension)
2524 (setq cell (table--probe-cell))
2525 (setq edge cell)))
2526 (setq cell (if (> arg 0) edge
2527 (or (and (table--goto-coordinate
2528 (cons (car (table--get-coordinate (cdr edge)))
2529 (1- (cdr (table--get-coordinate (car edge))))))
2530 (table--probe-cell))
2531 edge)))
2532 ;; now search for the tip which is the highest/lowest below/above cell
2533 (while cell
2534 (let (below/above)
2535 (and (table--goto-coordinate
2536 (cons (car (table--get-coordinate (if (> arg 0) (car cell)
2537 (cdr cell))))
2538 (if (> arg 0) (+ 2 (cdr (table--get-coordinate (cdr cell))))
2539 (1- (cdr (table--get-coordinate (car pivot)))))) 'no-extension)
2540 (setq below/above (table--probe-cell))
2541 (or (null tip)
2542 (if (> arg 0)
2543 (< (cdr (table--get-coordinate (car below/above)))
2544 (cdr (table--get-coordinate (car tip))))
2545 (> (cdr (table--get-coordinate (car below/above)))
2546 (cdr (table--get-coordinate (car tip))))))
2547 (setq tip below/above)))
2548 (and (setq cell (table--goto-coordinate
2549 (cons (if (> arg 0) (1+ (car (table--get-coordinate (cdr cell))))
2550 (1- (car (table--get-coordinate (car cell)))))
2551 (if (> arg 0) (cdr (table--get-coordinate (car pivot)))
2552 (1- (cdr (table--get-coordinate (car pivot)))))) 'no-extension))
2553 (setq cell (table--probe-cell))))
2554 (if tip (goto-char (car tip)) ; done
2555 ;; let's climb up/down to the top/bottom from the edge
2556 (while (and (table--goto-coordinate
2557 (cons (if (> arg 0) (car (table--get-coordinate (car edge)))
2558 (car (table--get-coordinate (cdr edge))))
2559 (if (> arg 0) (1- (cdr (table--get-coordinate (car edge))))
2560 (+ 2 (cdr (table--get-coordinate (cdr edge)))))) 'no-extension)
2561 (setq cell (table--probe-cell))
2562 (setq edge cell)))
2563 (if (< arg 0)
2564 (progn
2565 (setq cell edge)
2566 (while (and (table--goto-coordinate
2567 (cons (1- (car (table--get-coordinate (car cell))))
2568 (cdr (table--get-coordinate (cdr cell)))) 'no-extension)
2569 (setq cell (table--probe-cell)))
2570 (if (> (cdr (table--get-coordinate (car cell)))
2571 (cdr (table--get-coordinate (car edge))))
2572 (setq edge cell)))))
2573 (goto-char (car edge))))) ; the top left cell
2574 (setq arg (if (> arg 0) (1- arg) (1+ arg))))
2575 (unless no-recognize
2576 (table-recognize-cell 'force nil (if unrecognize -1 nil)))) ; refill the cache with new cell contents
2577
2578;;;###autoload
2579(defun table-backward-cell (&optional arg)
2580 "Move backward to the beginning of the previous cell.
2581With argument ARG, do it ARG times;
2582a negative argument ARG = -N means move forward N cells."
2583 (interactive "p")
2584 (or arg (setq arg 1))
2585 (table-forward-cell (- arg)))
2586
2587;;;###autoload
2588(defun table-span-cell (direction)
2589 "Span current cell into adjacent cell in DIRECTION.
2590DIRECTION is one of symbols; right, left, above or below."
2591 (interactive
2592 (list
2593 (let* ((dummy (barf-if-buffer-read-only))
2594 (direction-list
2595 (let* ((tmp (delete nil
2596 (mapcar (lambda (d)
2597 (if (table--cell-can-span-p d)
2598 (list (symbol-name d))))
2599 '(right left above below)))))
2600 (if (null tmp)
2601 (error "Can't span this cell"))
2602 tmp))
2603 (default-direction (if (member (list (car table-cell-span-direction-history)) direction-list)
2604 (car table-cell-span-direction-history)
2605 (caar direction-list)))
2606 (completion-ignore-case t))
2607 (intern (downcase (completing-read
2608 (format "Span into (default %s): " default-direction)
2609 direction-list
2610 nil t nil 'table-cell-span-direction-history default-direction))))))
2611 (unless (memq direction '(right left above below))
2612 (error "Invalid direction %s, must be right, left, above or below"
2613 (symbol-name direction)))
2614 (table-recognize-cell 'force)
2615 (unless (table--cell-can-span-p direction)
2616 (error "Can't span %s" (symbol-name direction)))
2617 ;; prepare beginning and ending positions of the border bar to strike through
2618 (let ((beg (cond
2619 ((eq direction 'right)
2620 (save-excursion
2621 (table--goto-coordinate
2622 (cons (car table-cell-info-rb-coordinate)
2623 (1- (cdr table-cell-info-lu-coordinate))) 'no-extension)))
2624 ((eq direction 'below)
2625 (save-excursion
2626 (table--goto-coordinate
2627 (cons (1- (car table-cell-info-lu-coordinate))
2628 (1+ (cdr table-cell-info-rb-coordinate))) 'no-extension)))
2629 (t
2630 (save-excursion
2631 (table--goto-coordinate
2632 (cons (1- (car table-cell-info-lu-coordinate))
2633 (1- (cdr table-cell-info-lu-coordinate))) 'no-extension)))))
2634 (end (cond
2635 ((eq direction 'left)
2636 (save-excursion
2637 (table--goto-coordinate
2638 (cons (car table-cell-info-lu-coordinate)
2639 (1+ (cdr table-cell-info-rb-coordinate))) 'no-extension)))
2640 ((eq direction 'above)
2641 (save-excursion
2642 (table--goto-coordinate
2643 (cons (1+ (car table-cell-info-rb-coordinate))
2644 (1- (cdr table-cell-info-lu-coordinate))) 'no-extension)))
2645 (t
2646 (save-excursion
2647 (table--goto-coordinate
2648 (cons (1+ (car table-cell-info-rb-coordinate))
2649 (1+ (cdr table-cell-info-rb-coordinate))) 'no-extension))))))
2650 ;; replace the bar with blank space while taking care of edges to be border or intersection
2651 (save-excursion
2652 (goto-char beg)
2653 (if (memq direction '(left right))
2654 (let* ((column (current-column))
2655 rectangle
2656 (n-element (- (length (extract-rectangle beg end)) 2))
2657 (above-contp (and (goto-char beg)
2658 (zerop (forward-line -1))
2659 (= (move-to-column column) column)
2660 (looking-at (regexp-quote (char-to-string table-cell-vertical-char)))))
2661 (below-contp (and (goto-char end)
2662 (progn (forward-char -1) t)
2663 (zerop (forward-line 1))
2664 (= (move-to-column column) column)
2665 (looking-at (regexp-quote (char-to-string table-cell-vertical-char))))))
2666 (setq rectangle
2667 (cons (if below-contp
2668 (char-to-string table-cell-intersection-char)
e99add21 2669 (substring table-cell-horizontal-chars 0 1))
238240c9
RS
2670 rectangle))
2671 (while (> n-element 0)
2672 (setq rectangle (cons (table--cell-blank-str 1) rectangle))
2673 (setq n-element (1- n-element)))
2674 (setq rectangle
2675 (cons (if above-contp
2676 (char-to-string table-cell-intersection-char)
e99add21 2677 (substring table-cell-horizontal-chars 0 1))
238240c9
RS
2678 rectangle))
2679 (delete-rectangle beg end)
2680 (goto-char beg)
2681 (table--insert-rectangle rectangle))
2682 (delete-region beg end)
2683 (insert (if (and (> (point) (point-min))
2684 (save-excursion
2685 (forward-char -1)
e99add21
JB
2686 (looking-at (regexp-opt-charset
2687 (string-to-list table-cell-horizontal-chars)))))
238240c9
RS
2688 table-cell-intersection-char
2689 table-cell-vertical-char)
2690 (table--cell-blank-str (- end beg 2))
e99add21
JB
2691 (if (looking-at (regexp-opt-charset
2692 (string-to-list table-cell-horizontal-chars)))
238240c9
RS
2693 table-cell-intersection-char
2694 table-cell-vertical-char))))
2695 ;; recognize the newly created spanned cell
2696 (table-recognize-cell 'force)
2697 (if (member direction '(right left))
2698 (table-with-cache-buffer
2699 (table--fill-region (point-min) (point-max))
2700 (setq table-inhibit-auto-fill-paragraph t)))))
2701
2702;;;###autoload
2703(defun table-split-cell-vertically ()
2704 "Split current cell vertically.
2705Creates a cell above and a cell below the current point location."
2706 (interactive "*")
2707 (table-recognize-cell 'force)
2708 (let ((point-y (cdr (table--get-coordinate))))
2709 (unless (table--cell-can-split-vertically-p)
2710 (error "Can't split here"))
2711 (let* ((old-coordinate (table--get-coordinate))
2712 (column (current-column))
2713 (beg (table--goto-coordinate
2714 (cons (1- (car table-cell-info-lu-coordinate))
2715 point-y)))
2716 (end (table--goto-coordinate
2717 (cons (1+ (car table-cell-info-rb-coordinate))
2718 point-y)))
2719 (line (buffer-substring (1+ beg) (1- end))))
2720 (when (= (cdr old-coordinate) (cdr table-cell-info-rb-coordinate))
2721 (table--goto-coordinate old-coordinate)
2722 (table-heighten-cell 1 'no-copy 'no-update))
2723 (goto-char beg)
2724 (delete-region beg end)
2725 (insert table-cell-intersection-char
e99add21 2726 (make-string table-cell-info-width (string-to-char table-cell-horizontal-chars))
238240c9
RS
2727 table-cell-intersection-char)
2728 (table--goto-coordinate old-coordinate)
2729 (forward-line 1)
2730 (move-to-column column)
2731 (setq old-coordinate (table--get-coordinate))
2732 (table-recognize-cell 'force)
2733 (unless (string-match "^\\s *$" line)
2734 (table-with-cache-buffer
2735 (goto-char (point-min))
2736 (insert line ?\n)
2737 (goto-char (point-min)) ;; don't heighten cell unnecessarily
2738 (setq table-inhibit-auto-fill-paragraph t)))
2739 (table--update-cell 'now) ;; can't defer this operation
2740 (table--goto-coordinate old-coordinate)
2741 (move-to-column column)
2742 (table-recognize-cell 'force))))
2743
2744;;;###autoload
2745(defun table-split-cell-horizontally ()
2746 "Split current cell horizontally.
2747Creates a cell on the left and a cell on the right of the current point location."
2748 (interactive "*")
2749 (table-recognize-cell 'force)
2750 (let* ((o-coordinate (table--get-coordinate))
2751 (point-x (car o-coordinate))
2752 cell-empty cell-contents cell-coordinate
2753 contents-to beg end rectangle strip-rect
2754 (right-edge (= (car o-coordinate) (1- (car table-cell-info-rb-coordinate)))))
2755 (unless (table--cell-can-split-horizontally-p)
2756 (error "Can't split here"))
2757 (let ((table-inhibit-update t))
2758 (table-with-cache-buffer
2759 (setq cell-coordinate (table--get-coordinate))
2760 (save-excursion
2761 (goto-char (point-min))
2762 (setq cell-empty (null (re-search-forward "\\S " nil t))))
2763 (setq cell-contents (buffer-substring (point-min) (point-max)))
2764 (setq table-inhibit-auto-fill-paragraph t)))
2765 (setq contents-to
2766 (if cell-empty 'left
2767 (let* ((completion-ignore-case t)
2768 (default (car table-cell-split-contents-to-history)))
2769 (intern
2770 (if (member 'click (event-modifiers last-input-event))
2771 (x-popup-menu last-input-event
2772 '("Existing cell contents to:"
2773 ("Title"
2774 ("Split" . "split") ("Left" . "left") ("Right" . "right"))))
2775 (downcase (completing-read
2776 (format "Existing cell contents to (default %s): " default)
2777 '(("split") ("left") ("right"))
2778 nil t nil 'table-cell-split-contents-to-history default)))))))
2779 (unless (eq contents-to 'split)
2780 (table-with-cache-buffer
2781 (erase-buffer)
2782 (setq table-inhibit-auto-fill-paragraph t)))
2783 (table--update-cell 'now)
2784 (setq beg (table--goto-coordinate
2785 (cons point-x
2786 (1- (cdr table-cell-info-lu-coordinate)))))
2787 (setq end (table--goto-coordinate
2788 (cons (1+ point-x)
2789 (1+ (cdr table-cell-info-rb-coordinate)))))
2790 (setq rectangle (cons (char-to-string table-cell-intersection-char) nil))
2791 (let ((n table-cell-info-height))
2792 (while (prog1 (> n 0) (setq n (1- n)))
2793 (setq rectangle (cons (char-to-string table-cell-vertical-char) rectangle))))
2794 (setq rectangle (cons (char-to-string table-cell-intersection-char) rectangle))
2795 (if (eq contents-to 'split)
2796 (setq strip-rect (extract-rectangle beg end)))
2797 (delete-rectangle beg end)
2798 (goto-char beg)
2799 (table--insert-rectangle rectangle)
2800 (table--goto-coordinate o-coordinate)
2801 (if cell-empty
2802 (progn
2803 (forward-char 1)
2804 (if right-edge
2805 (table-widen-cell 1)))
2806 (unless (eq contents-to 'left)
2807 (forward-char 1))
2808 (table-recognize-cell 'force)
2809 (table-with-cache-buffer
2810 (if (eq contents-to 'split)
2811 ;; split inserts strip-rect after removing
2812 ;; top and bottom borders
2813 (let ((o-coord (table--get-coordinate))
2814 (l (setq strip-rect (cdr strip-rect))))
2815 (while (cddr l) (setq l (cdr l)))
2816 (setcdr l nil)
2817 ;; insert the strip only when it is not a completely blank one
2818 (unless (let ((cl (mapcar (lambda (s) (string= s " ")) strip-rect)))
2819 (and (car cl)
2820 (table--uniform-list-p cl)))
2821 (goto-char (point-min))
2822 (table--insert-rectangle strip-rect)
2823 (table--goto-coordinate o-coord)))
2824 ;; left or right inserts original contents
2825 (erase-buffer)
2826 (insert cell-contents)
2827 (table--goto-coordinate cell-coordinate)
2828 (table--fill-region (point-min) (point-max))
2829 ;; avoid unnecessary vertical cell expansion
2830 (and (looking-at "\\s *\\'")
2831 (re-search-backward "\\S \\(\\s *\\)\\=" nil t)
2832 (goto-char (match-beginning 1))))
2833 ;; in either case do not fill paragraph
2834 (setq table-inhibit-auto-fill-paragraph t))
2835 (table--update-cell 'now)) ;; can't defer this operation
2836 (table-recognize-cell 'force)))
2837
2838;;;###autoload
2839(defun table-split-cell (orientation)
2840 "Split current cell in ORIENTATION.
2841ORIENTATION is a symbol either horizontally or vertically."
2842 (interactive
2843 (list
2844 (let* ((dummy (barf-if-buffer-read-only))
2845 (completion-ignore-case t)
2846 (default (car table-cell-split-orientation-history)))
2847 (intern (downcase (completing-read
2848 (format "Split orientation (default %s): " default)
2849 '(("horizontally") ("vertically"))
2850 nil t nil 'table-cell-split-orientation-history default))))))
2851 (unless (memq orientation '(horizontally vertically))
2852 (error "Invalid orientation %s, must be horizontally or vertically"
2853 (symbol-name orientation)))
2854 (if (eq orientation 'horizontally)
2855 (table-split-cell-horizontally)
2856 (table-split-cell-vertically)))
2857
2858;;;###autoload
2859(defun table-justify (what justify)
2860 "Justify contents of a cell, a row of cells or a column of cells.
2861WHAT is a symbol 'cell, 'row or 'column. JUSTIFY is a symbol 'left,
2862'center, 'right, 'top, 'middle, 'bottom or 'none."
2863 (interactive
2864 (list (let* ((dummy (barf-if-buffer-read-only))
2865 (completion-ignore-case t)
2866 (default (car table-target-history)))
2867 (intern (downcase (completing-read
2868 (format "Justify what (default %s): " default)
2869 '(("cell") ("row") ("column"))
2870 nil t nil 'table-target-history default))))
2871 (table--query-justification)))
2872 (funcall (intern (concat "table-justify-" (symbol-name what))) justify))
2873
2874;;;###autoload
2875(defun table-justify-cell (justify &optional paragraph)
2876 "Justify cell contents.
2877JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or 'top,
2878'middle, 'bottom or 'none for vertical. When optional PARAGRAPH is
2879non-nil the justify operation is limited to the current paragraph,
2880otherwise the entire cell contents is justified."
2881 (interactive
2882 (list (table--query-justification)))
2883 (table--finish-delayed-tasks)
2884 (table-recognize-cell 'force)
2885 (table--justify-cell-contents justify paragraph))
2886
2887;;;###autoload
2888(defun table-justify-row (justify)
2889 "Justify cells of a row.
2890JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or top,
2891'middle, 'bottom or 'none for vertical."
2892 (interactive
2893 (list (table--query-justification)))
2894 (let((cell-list (table--horizontal-cell-list nil nil 'top)))
2895 (table--finish-delayed-tasks)
2896 (save-excursion
2897 (while cell-list
2898 (let ((cell (car cell-list)))
2899 (setq cell-list (cdr cell-list))
2900 (goto-char (car cell))
2901 (table-recognize-cell 'force)
2902 (table--justify-cell-contents justify))))))
2903
2904;;;###autoload
2905(defun table-justify-column (justify)
2906 "Justify cells of a column.
2907JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or top,
2908'middle, 'bottom or 'none for vertical."
2909 (interactive
2910 (list (table--query-justification)))
2911 (let((cell-list (table--vertical-cell-list nil nil 'left)))
2912 (table--finish-delayed-tasks)
2913 (save-excursion
2914 (while cell-list
2915 (let ((cell (car cell-list)))
2916 (setq cell-list (cdr cell-list))
2917 (goto-char (car cell))
2918 (table-recognize-cell 'force)
2919 (table--justify-cell-contents justify))))))
2920
2921;;;###autoload
2922(defun table-fixed-width-mode (&optional arg)
2923 "Toggle fixing width mode.
2924In the fixed width mode, typing inside a cell never changes the cell
2925width where in the normal mode the cell width expands automatically in
2926order to prevent a word being folded into multiple lines."
2927 (interactive "P")
2928 (table--finish-delayed-tasks)
2929 (setq table-fixed-width-mode
2930 (if (null arg)
2931 (not table-fixed-width-mode)
2932 (> (prefix-numeric-value arg) 0)))
238240c9
RS
2933 (table--update-cell-face))
2934
2935;;;###autoload
2936(defun table-query-dimension (&optional where)
2937 "Return the dimension of the current cell and the current table.
2938The result is a list (cw ch tw th c r cells) where cw is the cell
2939width, ch is the cell height, tw is the table width, th is the table
2940height, c is the number of columns, r is the number of rows and cells
2941is the total number of cells. The cell dimension excludes the cell
2942frame while the table dimension includes the table frame. The columns
2943and the rows are counted by the number of cell boundaries. Therefore
2944the number tends to be larger than it appears for the tables with
2945non-uniform cell structure (heavily spanned and split). When optional
2946WHERE is provided the cell and table at that location is reported."
2947 (interactive)
2948 (save-excursion
2949 (if where (goto-char where))
2950 (let ((starting-cell (table--probe-cell))
2951 cell table-lu table-rb col-list row-list (cells 0))
2952 (if (null starting-cell) nil
2953 (setq table-lu (car starting-cell))
2954 (setq table-rb (cdr starting-cell))
2955 (setq col-list (cons (car (table--get-coordinate (car starting-cell))) nil))
2956 (setq row-list (cons (cdr (table--get-coordinate (car starting-cell))) nil))
2957 (and (interactive-p)
2958 (message "Computing cell dimension..."))
2959 (while
2960 (progn
2961 (table-forward-cell 1 t)
2962 (setq cells (1+ cells))
2963 (and (setq cell (table--probe-cell))
2964 (not (equal cell starting-cell))))
2965 (if (< (car cell) table-lu)
2966 (setq table-lu (car cell)))
2967 (if (> (cdr cell) table-rb)
2968 (setq table-rb (cdr cell)))
2969 (let ((lu-coordinate (table--get-coordinate (car cell))))
2970 (if (memq (car lu-coordinate) col-list) nil
2971 (setq col-list (cons (car lu-coordinate) col-list)))
2972 (if (memq (cdr lu-coordinate) row-list) nil
2973 (setq row-list (cons (cdr lu-coordinate) row-list)))))
2974 (let* ((cell-lu-coordinate (table--get-coordinate (car starting-cell)))
2975 (cell-rb-coordinate (table--get-coordinate (cdr starting-cell)))
2976 (table-lu-coordinate (table--get-coordinate table-lu))
2977 (table-rb-coordinate (table--get-coordinate table-rb))
2978 (cw (- (car cell-rb-coordinate) (car cell-lu-coordinate)))
2979 (ch (1+ (- (cdr cell-rb-coordinate) (cdr cell-lu-coordinate))))
2980 (tw (+ 2 (- (car table-rb-coordinate) (car table-lu-coordinate))))
2981 (th (+ 3 (- (cdr table-rb-coordinate) (cdr table-lu-coordinate))))
2982 (c (length col-list))
2983 (r (length row-list)))
2984 (and (interactive-p)
2985 (message "Cell: (%dw, %dh), Table: (%dw, %dh), Dim: (%dc, %dr), Total Cells: %d" cw ch tw th c r cells))
2986 (list cw ch tw th c r cells))))))
2987
2988;;;###autoload
2989(defun table-generate-source (language &optional dest-buffer caption)
2990 "Generate source of the current table in the specified language.
2991LANGUAGE is a symbol that specifies the language to describe the
2992structure of the table. It must be either 'html, 'latex or 'cals.
2993The resulted source text is inserted into DEST-BUFFER and the buffer
2994object is returned. When DEST-BUFFER is omitted or nil the default
2995buffer specified in `table-dest-buffer-name' is used. In this case
2996the content of the default buffer is erased prior to the generation.
2997When DEST-BUFFER is non-nil it is expected to be either a destination
2998buffer or a name of the destination buffer. In this case the
2999generated result is inserted at the current point in the destination
3000buffer and the previously existing contents in the buffer are
3001untouched.
3002
3003References used for this implementation:
3004
3005HTML:
3006 http://www.w3.org
3007
3008LaTeX:
3009 http://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/Tables.html
3010
3011CALS (DocBook DTD):
3012 http://www.oasis-open.org/html/a502.htm
3013 http://www.oreilly.com/catalog/docbook/chapter/book/table.html#AEN114751
3014"
3015 (interactive
3016 (let* ((dummy (unless (table--probe-cell) (error "Table not found here")))
3017 (completion-ignore-case t)
3018 (default (car table-source-language-history))
3019 (language (downcase (completing-read
3020 (format "Language (default %s): " default)
3021 (mapcar (lambda (s) (list (symbol-name s)))
3022 table-source-languages)
3023 nil t nil 'table-source-language-history default))))
3024 (list
3025 (intern language)
3026 (read-buffer "Destination buffer: " (concat table-dest-buffer-name "." language))
3027 (table--read-from-minibuffer '("Table Caption" . table-source-caption-history)))))
3028 (let ((default-buffer-name (concat table-dest-buffer-name "." (symbol-name language))))
3029 (unless (or (interactive-p) (table--probe-cell)) (error "Table not found here"))
3030 (unless (bufferp dest-buffer)
3031 (setq dest-buffer (get-buffer-create (or dest-buffer default-buffer-name))))
3032 (if (string= (buffer-name dest-buffer) default-buffer-name)
3033 (with-current-buffer dest-buffer
3034 (erase-buffer)))
3035 (save-excursion
3036 (let ((starting-cell (table--probe-cell))
3037 cell origin-cell tail-cell col-list row-list (n 0) i)
3038 ;; first analyze the table structure and prepare:
3039 ;; 1. origin cell (left up corner cell)
3040 ;; 2. tail cell (right bottom corner cell)
3041 ;; 3. column boundary list
3042 ;; 4. row boundary list
3043 (setq origin-cell starting-cell)
3044 (setq tail-cell starting-cell)
3045 (setq col-list (cons (car (table--get-coordinate (car starting-cell))) nil))
3046 (setq row-list (cons (cdr (table--get-coordinate (car starting-cell))) nil))
3047 (setq i 0)
4fb81250 3048 (let ((wheel [?- ?\\ ?| ?/]))
238240c9
RS
3049 (while
3050 (progn
3051 (if (interactive-p)
3052 (progn
3053 (message "Analyzing table...%c" (aref wheel i))
3054 (if (eq (setq i (1+ i)) (length wheel))
3055 (setq i 0))
3056 (setq n (1+ n))))
3057 (table-forward-cell 1 t)
3058 (and (setq cell (table--probe-cell))
3059 (not (equal cell starting-cell))))
3060 (if (< (car cell) (car origin-cell))
3061 (setq origin-cell cell))
3062 (if (> (cdr cell) (cdr tail-cell))
3063 (setq tail-cell cell))
3064 (let ((lu-coordinate (table--get-coordinate (car cell))))
3065 (unless (memq (car lu-coordinate) col-list)
3066 (setq col-list (cons (car lu-coordinate) col-list)))
3067 (unless (memq (cdr lu-coordinate) row-list)
3068 (setq row-list (cons (cdr lu-coordinate) row-list))))))
3069 (setq col-list (sort col-list '<))
3070 (setq row-list (sort row-list '<))
3071 (message "Generating source...")
3072 ;; clear the source generation property list
3073 (setplist 'table-source-info-plist nil)
3074 ;; prepare to start from the origin cell
3075 (goto-char (car origin-cell))
3076 ;; first put some header information
3077 (table--generate-source-prologue dest-buffer language caption col-list row-list)
3078 (cond
3079 ((eq language 'latex)
3080 ;; scan by character lines
3081 (table--generate-source-scan-lines dest-buffer language origin-cell tail-cell col-list row-list))
3082 (t
3083 ;; scan by table cells
3084 (table--generate-source-scan-rows dest-buffer language origin-cell col-list row-list)))
3085 ;; insert closing
3086 (table--generate-source-epilogue dest-buffer language col-list row-list))
3087 ;; lastly do some convenience work
3088 (if (interactive-p)
3089 (save-selected-window
3090 (pop-to-buffer dest-buffer t)
3091 (goto-char (point-min))
3092 (and (string= (buffer-name dest-buffer) default-buffer-name)
3093 (buffer-file-name dest-buffer)
3094 (save-buffer))
3095 (message "Generating source...done")
3096 (let ((mode
3097 (if (memq language '(cals)) 'sgml-mode
3098 (intern (concat (symbol-name language) "-mode")))))
3099 (if (fboundp mode)
3100 (call-interactively mode)))
3101 )))
3102 dest-buffer))
3103
3104(defun table--generate-source-prologue (dest-buffer language caption col-list row-list)
3105 "Generate and insert source prologue into DEST-BUFFER."
3106 (with-current-buffer dest-buffer
3107 (cond
3108 ((eq language 'html)
3109 (insert (format "<!-- This HTML table template is generated by emacs %s -->\n" emacs-version)
e66ca63c 3110 (format "<table %s>\n" table-html-table-attribute)
238240c9
RS
3111 (if (and (stringp caption)
3112 (not (string= caption "")))
e66ca63c 3113 (format " <caption>%s</caption>\n" caption)
238240c9
RS
3114 "")))
3115 ((eq language 'latex)
3116 (insert (format "%% This LaTeX table template is generated by emacs %s\n" emacs-version)
3117 "\\begin{tabular}{|" (apply 'concat (make-list (length col-list) "l|")) "}\n"
3118 "\\hline\n"))
3119 ((eq language 'cals)
3120 (insert (format "<!-- This CALS table template is generated by emacs %s -->\n" emacs-version)
3121 "<table frame=\"all\">\n")
3122 (if (and (stringp caption)
3123 (not (string= caption "")))
3124 (insert " <title>" caption "</title>\n"))
3125 (insert (format " <tgroup cols=\"%d\" align=\"left\" colsep=\"1\" rowsep=\"1\">\n" (length col-list)))
3126 (table-put-source-info 'colspec-marker (point-marker))
3127 (table-put-source-info 'row-type (if (zerop table-cals-thead-rows) "tbody" "thead"))
3128 (set-marker-insertion-type (table-get-source-info 'colspec-marker) nil) ;; insert after
3129 (insert (format " <%s valign=\"top\">\n" (table-get-source-info 'row-type))))
3130 )))
3131
3132(defun table--generate-source-epilogue (dest-buffer language col-list row-list)
3133 "Generate and insert source epilogue into DEST-BUFFER."
3134 (with-current-buffer dest-buffer
3135 (cond
3136 ((eq language 'html)
e66ca63c 3137 (insert "</table>\n"))
238240c9
RS
3138 ((eq language 'latex)
3139 (insert "\\end{tabular}\n"))
3140 ((eq language 'cals)
3141 (set-marker-insertion-type (table-get-source-info 'colspec-marker) t) ;; insert before
3142 (save-excursion
3143 (goto-char (table-get-source-info 'colspec-marker))
4e454e5b 3144 (mapc
238240c9
RS
3145 (lambda (col)
3146 (insert (format " <colspec colnum=\"%d\" colname=\"c%d\"/>\n" col col)))
3147 (sort (table-get-source-info 'colnum-list) '<)))
3148 (insert (format " </%s>\n </tgroup>\n</table>\n" (table-get-source-info 'row-type))))
3149 )))
3150
3151(defun table--generate-source-scan-rows (dest-buffer language origin-cell col-list row-list)
3152 "Generate and insert source rows into DEST-BUFFER."
3153 (table-put-source-info 'current-row 1)
3154 (while row-list
3155 (with-current-buffer dest-buffer
3156 (cond
3157 ((eq language 'html)
e66ca63c 3158 (insert " <tr>\n"))
238240c9
RS
3159 ((eq language 'cals)
3160 (insert " <row>\n"))
3161 ))
3162 (table--generate-source-cells-in-a-row dest-buffer language col-list row-list)
3163 (with-current-buffer dest-buffer
3164 (cond
3165 ((eq language 'html)
e66ca63c 3166 (insert " </tr>\n"))
238240c9
RS
3167 ((eq language 'cals)
3168 (insert " </row>\n")
3169 (unless (/= (table-get-source-info 'current-row) table-cals-thead-rows)
db95369b 3170 (insert (format " </%s>\n" (table-get-source-info 'row-type)))
238240c9
RS
3171 (insert (format " <%s valign=\"top\">\n" (table-put-source-info 'row-type "tbody")))))))
3172 (table-put-source-info 'current-row (1+ (table-get-source-info 'current-row)))
3173 (setq row-list (cdr row-list))))
3174
3175(defun table--generate-source-cells-in-a-row (dest-buffer language col-list row-list)
3176 "Generate and insert source cells into DEST-BUFFER."
3177 (table-put-source-info 'current-column 1)
3178 (while col-list
3179 (let* ((cell (table--probe-cell))
3180 (lu (table--get-coordinate (car cell)))
3181 (rb (table--get-coordinate (cdr cell)))
3182 (alignment (table--get-cell-justify-property cell))
3183 (valign (table--get-cell-valign-property cell))
3184 (row-list row-list)
3185 (colspan 1)
3186 (rowspan 1))
3187 (if (< (car lu) (car col-list))
3188 (setq col-list nil)
3189 (while (and col-list
3190 (> (car lu) (car col-list)))
3191 (setq col-list (cdr col-list))
3192 (table-put-source-info 'current-column (1+ (table-get-source-info 'current-column))))
3193 (setq col-list (cdr col-list))
3194 (table-put-source-info 'next-column (1+ (table-get-source-info 'current-column)))
3195 (while (and col-list
3196 (> (1+ (car rb)) (car col-list)))
3197 (setq colspan (1+ colspan))
3198 (setq col-list (cdr col-list))
3199 (table-put-source-info 'next-column (1+ (table-get-source-info 'next-column))))
3200 (setq row-list (cdr row-list))
3201 (while (and row-list
3202 (> (+ (cdr rb) 2) (car row-list)))
3203 (setq rowspan (1+ rowspan))
3204 (setq row-list (cdr row-list)))
3205 (with-current-buffer dest-buffer
3206 (cond
3207 ((eq language 'html)
3208 (insert (format " <%s"
3209 (table-put-source-info
3210 'cell-type
3211 (if (or (<= (table-get-source-info 'current-row) table-html-th-rows)
3212 (<= (table-get-source-info 'current-column) table-html-th-columns))
e66ca63c 3213 "th" "td"))))
238240c9
RS
3214 (if (and table-html-cell-attribute (not (string= table-html-cell-attribute "")))
3215 (insert " " table-html-cell-attribute))
3216 (if (> colspan 1) (insert (format " colspan=\"%d\"" colspan)))
3217 (if (> rowspan 1) (insert (format " rowspan=\"%d\"" rowspan)))
3218 (insert (format " align=\"%s\"" (if alignment (symbol-name alignment) "left")))
3219 (insert (format " valign=\"%s\"" (if valign (symbol-name valign) "top")))
3220 (insert ">\n"))
3221 ((eq language 'cals)
3222 (insert " <entry")
3223 (if (> colspan 1)
3224 (let ((scol (table-get-source-info 'current-column))
3225 (ecol (+ (table-get-source-info 'current-column) colspan -1)))
4e454e5b
JB
3226 (mapc (lambda (col)
3227 (unless (memq col (table-get-source-info 'colnum-list))
3228 (table-put-source-info 'colnum-list
3229 (cons col (table-get-source-info 'colnum-list)))))
3230 (list scol ecol))
238240c9
RS
3231 (insert (format " namest=\"c%d\" nameend=\"c%d\"" scol ecol))))
3232 (if (> rowspan 1) (insert (format " morerows=\"%d\"" (1- rowspan))))
3233 (if (and alignment
3234 (not (memq alignment '(left none))))
3235 (insert " align=\"" (symbol-name alignment) "\""))
3236 (if (and valign
3237 (not (memq valign '(top none))))
3238 (insert " valign=\"" (symbol-name valign) "\""))
3239 (insert ">\n"))
3240 ))
3241 (table--generate-source-cell-contents dest-buffer language cell)
3242 (with-current-buffer dest-buffer
3243 (cond
3244 ((eq language 'html)
3245 (insert (format" </%s>\n" (table-get-source-info 'cell-type))))
3246 ((eq language 'cals)
3247 (insert " </entry>\n"))
3248 ))
3249 (table-forward-cell 1 t)
3250 (table-put-source-info 'current-column (table-get-source-info 'next-column))
3251 ))))
3252
3253(defun table--generate-source-cell-contents (dest-buffer language cell)
3254 "Generate and insert source cell contents of a CELL into DEST-BUFFER."
3255 (let ((cell-contents (extract-rectangle (car cell) (cdr cell))))
3256 (with-temp-buffer
3257 (table--insert-rectangle cell-contents)
3258 (table--remove-cell-properties (point-min) (point-max))
3259 (goto-char (point-min))
3260 (cond
3261 ((eq language 'html)
3262 (if table-html-delegate-spacing-to-user-agent
3263 (progn
3264 (table--remove-eol-spaces (point-min) (point-max))
3265 (if (re-search-forward "\\s +\\'" nil t)
3266 (replace-match "")))
3267 (while (search-forward " " nil t)
3268 (replace-match "&nbsp;"))
3269 (goto-char (point-min))
3270 (while (and (re-search-forward "$" nil t)
3271 (not (eobp)))
e66ca63c 3272 (insert "<br />")
238240c9
RS
3273 (forward-char 1)))
3274 (unless (and table-html-delegate-spacing-to-user-agent
3275 (progn
3276 (goto-char (point-min))
3277 (looking-at "\\s *\\'")))))
3278 ((eq language 'cals)
3279 (table--remove-eol-spaces (point-min) (point-max))
3280 (if (re-search-forward "\\s +\\'" nil t)
3281 (replace-match "")))
3282 )
3283 (setq cell-contents (buffer-substring (point-min) (point-max))))
3284 (with-current-buffer dest-buffer
3285 (let ((beg (point)))
3286 (insert cell-contents)
3287 (indent-rigidly beg (point)
3288 (cond
3289 ((eq language 'html) 6)
3290 ((eq language 'cals) 10)))
3291 (insert ?\n)))))
3292
e99add21
JB
3293(defun table--cell-horizontal-char-p (c)
3294 "Test if character C is one of the horizontal characters"
3295 (memq c (string-to-list table-cell-horizontal-chars)))
3296
238240c9
RS
3297(defun table--generate-source-scan-lines (dest-buffer language origin-cell tail-cell col-list row-list)
3298 "Scan the table line by line.
3299Currently this method is for LaTeX only."
3300 (let* ((lu-coord (table--get-coordinate (car origin-cell)))
3301 (rb-coord (table--get-coordinate (cdr tail-cell)))
3302 (x0 (car lu-coord))
3303 (x1 (car rb-coord))
3304 (y (cdr lu-coord))
3305 (y1 (cdr rb-coord)))
3306 (while (<= y y1)
3307 (let* ((border-p (memq (1+ y) row-list))
3308 (border-char-list
3309 (mapcar (lambda (x)
3310 (if border-p (char-after (table--goto-coordinate (cons x y)))
3311 (char-before (table--goto-coordinate (cons x y)))))
3312 col-list))
3313 start i c)
3314 (if border-p
3315 ;; horizontal cell border processing
e99add21 3316 (if (and (table--cell-horizontal-char-p (car border-char-list))
238240c9
RS
3317 (table--uniform-list-p border-char-list))
3318 (with-current-buffer dest-buffer
3319 (insert "\\hline\n"))
3320 (setq i 0)
3321 (while (setq c (nth i border-char-list))
e99add21 3322 (if (and start (not (table--cell-horizontal-char-p c)))
238240c9
RS
3323 (progn
3324 (with-current-buffer dest-buffer
3325 (insert (format "\\cline{%d-%d}\n" (1+ start) i)))
3326 (setq start nil)))
e99add21 3327 (if (and (not start) (table--cell-horizontal-char-p c))
238240c9
RS
3328 (setq start i))
3329 (setq i (1+ i)))
3330 (if start
3331 (with-current-buffer dest-buffer
3332 (insert (format "\\cline{%d-%d}\n" (1+ start) i)))))
3333 ;; horizontal cell contents processing
3334 (let* ((span 1) ;; spanning length
3335 (first-p t) ;; first in a row
3336 (insert-column ;; a function that processes one column/multicolumn
3337 (function
3338 (lambda (from to)
3339 (let ((line (table--buffer-substring-and-trim
3340 (table--goto-coordinate (cons from y))
3341 (table--goto-coordinate (cons to y)))))
3342 ;; escape special characters
3343 (with-temp-buffer
3344 (insert line)
3345 (goto-char (point-min))
3346 (while (re-search-forward "\\([#$~_^%{}]\\)\\|\\(\\\\\\)\\|\\([<>|]\\)" nil t)
3347 (if (match-beginning 1)
3348 (save-excursion
3349 (goto-char (match-beginning 1))
3350 (insert "\\"))
3351 (if (match-beginning 2)
3352 (replace-match "$\\backslash$" t t)
3353 (replace-match (concat "$" (match-string 3) "$")) t t)))
3354 (setq line (buffer-substring (point-min) (point-max))))
3355 ;; insert a column separator and column/multicolumn contents
3356 (with-current-buffer dest-buffer
3357 (unless first-p
ec85195e 3358 (insert (if (eq (char-before) ?\s) "" " ") "& "))
238240c9
RS
3359 (if (> span 1)
3360 (insert (format "\\multicolumn{%d}{%sl|}{%s}" span (if first-p "|" "") line))
3361 (insert line)))
3362 (setq first-p nil)
3363 (setq span 1)
3364 (setq start (nth i col-list)))))))
3365 (setq start x0)
3366 (setq i 1)
3367 (while (setq c (nth i border-char-list))
3368 (if (eq c table-cell-vertical-char)
3369 (funcall insert-column start (1- (nth i col-list)))
3370 (setq span (1+ span)))
3371 (setq i (1+ i)))
3372 (funcall insert-column start x1))
3373 (with-current-buffer dest-buffer
ec85195e 3374 (insert (if (eq (char-before) ?\s) "" " ") "\\\\\n"))))
238240c9
RS
3375 (setq y (1+ y)))
3376 (with-current-buffer dest-buffer
3377 (insert "\\hline\n"))
3378 ))
3379
3380;;;###autoload
3381(defun table-insert-sequence (str n increment interval justify)
3382 "Travel cells forward while inserting a specified sequence string in each cell.
3383STR is the base string from which the sequence starts. When STR is an
3384empty string then each cell content is erased. When STR ends with
3385numerical characters (they may optionally be surrounded by a pair of
3386parentheses) they are incremented as a decimal number. Otherwise the
3387last character in STR is incremented in ASCII code order. N is the
3388number of sequence elements to insert. When N is negative the cell
3389traveling direction is backward. When N is zero it travels forward
3390entire table. INCREMENT is the increment between adjacent sequence
3391elements and can be a negative number for effectively decrementing.
3392INTERVAL is the number of cells to travel between sequence element
3393insertion which is normally 1. When zero or less is given for
3394INTERVAL it is interpreted as number of cells per row so that sequence
3395is placed straight down vertically as long as the table's cell
3396structure is uniform. JUSTIFY is one of the symbol 'left, 'center or
3397'right, that specifies justification of the inserted string.
3398
3399Example:
3400
3401 (progn
3402 (table-insert 16 3 5 1)
3403 (table-forward-cell 15)
3404 (table-insert-sequence \"D0\" -16 1 1 'center)
3405 (table-forward-cell 16)
3406 (table-insert-sequence \"A[0]\" -16 1 1 'center)
3407 (table-forward-cell 1)
3408 (table-insert-sequence \"-\" 16 0 1 'center))
3409
3410 (progn
3411 (table-insert 16 8 5 1)
3412 (table-insert-sequence \"@\" 0 1 2 'right)
3413 (table-forward-cell 1)
3414 (table-insert-sequence \"64\" 0 1 2 'left))
3415"
3416 (interactive
3417 (progn
3418 (barf-if-buffer-read-only)
3419 (unless (table--probe-cell) (error "Table not found here"))
3420 (list (read-from-minibuffer
3421 "Sequence base string: " (car table-sequence-string-history) nil nil 'table-sequence-string-history)
3422 (string-to-number
3423 (table--read-from-minibuffer
3424 '("How many elements (0: maximum, negative: backward traveling)" . table-sequence-count-history)))
3425 (string-to-number
3426 (table--read-from-minibuffer
3427 '("Increment element by" . table-sequence-increment-history)))
3428 (string-to-number
3429 (table--read-from-minibuffer
3430 '("Cell interval (0: vertical, 1:horizontal)" . table-sequence-interval-history)))
3431 (let* ((completion-ignore-case t)
3432 (default (car table-sequence-justify-history)))
3433 (intern (downcase (completing-read
3434 (format "Justify (default %s): " default)
3435 '(("left") ("center") ("right"))
3436 nil t nil 'table-sequence-justify-history default)))))))
3437 (unless (or (interactive-p) (table--probe-cell)) (error "Table not found here"))
3438 (string-match "\\([0-9]*\\)\\([]})>]*\\)\\'" str)
3439 (if (interactive-p)
3440 (message "Sequencing..."))
3441 (let* ((prefix (substring str 0 (match-beginning 1)))
3442 (index (match-string 1 str))
3443 (fmt (format "%%%s%dd" (if (eq (string-to-char index) ?0) "0" "") (length index)))
3444 (postfix (match-string 2 str))
3445 (dim (table-query-dimension))
3446 (cells (nth 6 dim))
3447 (direction (if (< n 0) -1 1))
3448 (interval-count 0))
3449 (if (string= index "")
3450 (progn
3451 (setq index nil)
3452 (if (string= prefix "")
3453 (setq prefix nil)))
3454 (setq index (string-to-number index)))
3455 (if (< n 0) (setq n (- n)))
3456 (if (or (zerop n) (> n cells)) (setq n cells))
3457 (if (< interval 0) (setq interval (- interval)))
3458 (if (zerop interval) (setq interval (nth 4 dim)))
3459 (save-excursion
3460 (while (progn
3461 (if (> interval-count 0) nil
3462 (setq interval-count interval)
3463 (table-with-cache-buffer
3464 (goto-char (point-min))
3465 (if (not (or prefix index))
3466 (erase-buffer)
3467 (insert prefix)
3468 (if index (insert (format fmt index)))
3469 (insert postfix)
3470 (table--fill-region (point-min) (point) table-cell-info-width justify)
3471 (setq table-cell-info-justify justify))
3472 (setq table-inhibit-auto-fill-paragraph t))
3473 (table--update-cell 'now)
3474 (if index
3475 (setq index (+ index increment))
3476 (if (and prefix (string= postfix ""))
3477 (let ((len-1 (1- (length prefix))))
3478 (setq prefix (concat (substring prefix 0 len-1)
3479 (char-to-string
3480 (+ (string-to-char (substring prefix len-1)) increment)))))))
3481 (setq n (1- n)))
3482 (table-forward-cell direction t)
3483 (setq interval-count (1- interval-count))
3484 (setq cells (1- cells))
3485 (and (> n 0) (> cells 0)))))
3486 (table-recognize-cell 'force)
3487 (if (interactive-p)
3488 (message "Sequencing...done"))
3489 ))
3490
3491;;;###autoload
3492(defun table-delete-row (n)
3493 "Delete N row(s) of cells.
3494Delete N rows of cells from current row. The current row is the row
3495contains the current cell where point is located. Each row must
3496consists from cells of same height."
3497 (interactive "*p")
3498 (let ((orig-coord (table--get-coordinate))
3499 (bt-coord (table--get-coordinate (cdr (table--vertical-cell-list nil 'first-only))))
3500 lu-coord rb-coord rect)
3501 ;; determine the area to delete while testing row height uniformity
3502 (while (> n 0)
3503 (setq n (1- n))
3504 (unless (table--probe-cell)
3505 (error "Table not found"))
3506 (let ((cell-list (table--horizontal-cell-list 'left-to-right)))
3507 (unless
3508 (and (table--uniform-list-p
3509 (mapcar (lambda (cell) (cdr (table--get-coordinate (car cell)))) cell-list))
3510 (table--uniform-list-p
3511 (mapcar (lambda (cell) (cdr (table--get-coordinate (cdr cell)))) cell-list)))
3512 (error "Cells in this row are not in uniform height"))
3513 (unless lu-coord
3514 (setq lu-coord (table--get-coordinate (caar cell-list))))
3515 (setq rb-coord (table--get-coordinate (cdar (last cell-list))))
3516 (table--goto-coordinate (cons (car orig-coord) (+ 2 (cdr rb-coord))))))
3517 ;; copy the remaining area (below the deleting area)
3518 (setq rect (extract-rectangle
3519 (table--goto-coordinate (cons (1- (car lu-coord)) (1+ (cdr rb-coord))))
3520 (table--goto-coordinate (cons (1+ (car rb-coord)) (1+ (cdr bt-coord))))))
3521 ;; delete the deleting area and below together
3522 (delete-rectangle
3523 (table--goto-coordinate (cons (1- (car lu-coord)) (1- (cdr lu-coord))))
3524 (table--goto-coordinate (cons (1+ (car rb-coord)) (1+ (cdr bt-coord)))))
3525 (table--goto-coordinate (cons (1- (car lu-coord)) (1- (cdr lu-coord))))
3526 ;; insert the remaining area while appending blank lines below it
3527 (table--insert-rectangle
3528 (append rect (make-list (+ 2 (- (cdr rb-coord) (cdr lu-coord)))
ec85195e 3529 (make-string (+ 2 (- (car rb-coord) (car lu-coord))) ?\s))))
238240c9
RS
3530 ;; remove the appended blank lines below the table if they are unnecessary
3531 (table--goto-coordinate (cons 0 (- (cdr bt-coord) (- (cdr rb-coord) (cdr lu-coord)))))
3532 (table--remove-blank-lines (+ 2 (- (cdr rb-coord) (cdr lu-coord))))
3533 ;; fix up intersections
3534 (let ((coord (cons (car lu-coord) (1- (cdr lu-coord))))
3535 (n (1+ (- (car rb-coord) (car lu-coord)))))
3536 (while (> n 0)
3537 (table--goto-coordinate coord)
3538 (if (save-excursion
3539 (or (and (table--goto-coordinate (cons (car coord) (1- (cdr coord))) 'no-extension)
3540 (looking-at (regexp-quote (char-to-string table-cell-vertical-char))))
3541 (and (table--goto-coordinate (cons (car coord) (1+ (cdr coord))) 'no-extension)
3542 (looking-at (regexp-quote (char-to-string table-cell-vertical-char))))))
3543 (progn
3544 (delete-char 1)
3545 (insert table-cell-intersection-char))
3546 (delete-char 1)
e99add21 3547 (insert (string-to-char table-cell-horizontal-chars)))
238240c9
RS
3548 (setq n (1- n))
3549 (setcar coord (1+ (car coord)))))
3550 ;; goto appropriate end point
3551 (table--goto-coordinate (cons (car orig-coord) (cdr lu-coord)))))
3552
3553;;;###autoload
3554(defun table-delete-column (n)
3555 "Delete N column(s) of cells.
3556Delete N columns of cells from current column. The current column is
3557the column contains the current cell where point is located. Each
3558column must consists from cells of same width."
3559 (interactive "*p")
3560 (let ((orig-coord (table--get-coordinate))
3561 lu-coord rb-coord)
3562 ;; determine the area to delete while testing column width uniformity
3563 (while (> n 0)
3564 (setq n (1- n))
3565 (unless (table--probe-cell)
3566 (error "Table not found"))
3567 (let ((cell-list (table--vertical-cell-list 'top-to-bottom)))
3568 (unless
3569 (and (table--uniform-list-p
3570 (mapcar (function (lambda (cell) (car (table--get-coordinate (car cell))))) cell-list))
3571 (table--uniform-list-p
3572 (mapcar (function (lambda (cell) (car (table--get-coordinate (cdr cell))))) cell-list)))
3573 (error "Cells in this column are not in uniform width"))
3574 (unless lu-coord
3575 (setq lu-coord (table--get-coordinate (caar cell-list))))
3576 (setq rb-coord (table--get-coordinate (cdar (last cell-list))))
3577 (table--goto-coordinate (cons (1+ (car rb-coord)) (cdr orig-coord)))))
3578 ;; delete the area
3579 (delete-rectangle
3580 (table--goto-coordinate (cons (car lu-coord) (1- (cdr lu-coord))))
3581 (table--goto-coordinate (cons (1+ (car rb-coord)) (1+ (cdr rb-coord)))))
3582 ;; fix up the intersections
3583 (let ((coord (cons (1- (car lu-coord)) (cdr lu-coord)))
3584 (n (1+ (- (cdr rb-coord) (cdr lu-coord)))))
3585 (while (> n 0)
3586 (table--goto-coordinate coord)
3587 (if (save-excursion
3588 (or (and (table--goto-coordinate (cons (1- (car coord)) (cdr coord)) 'no-extension)
e99add21
JB
3589 (looking-at (regexp-opt-charset
3590 (string-to-list table-cell-horizontal-chars))))
238240c9 3591 (and (table--goto-coordinate (cons (1+ (car coord)) (cdr coord)) 'no-extension)
e99add21
JB
3592 (looking-at (regexp-opt-charset
3593 (string-to-list table-cell-horizontal-chars))))))
238240c9
RS
3594 (progn
3595 (delete-char 1)
3596 (insert table-cell-intersection-char))
3597 (delete-char 1)
3598 (insert table-cell-vertical-char))
3599 (setq n (1- n))
3600 (setcdr coord (1+ (cdr coord)))))
3601 ;; goto appropriate end point
3602 (table--goto-coordinate (cons (car lu-coord) (cdr orig-coord)))))
3603
3604;;;###autoload
3605(defun table-capture (beg end &optional col-delim-regexp row-delim-regexp justify min-cell-width columns)
3606 "Convert plain text into a table by capturing the text in the region.
3607Create a table with the text in region as cell contents. BEG and END
3608specify the region. The text in the region is replaced with a table.
3609The removed text is inserted in the table. When optional
3610COL-DELIM-REGEXP and ROW-DELIM-REGEXP are provided the region contents
3611is parsed and separated into individual cell contents by using the
3612delimiter regular expressions. This parsing determines the number of
3613columns and rows of the table automatically. If COL-DELIM-REGEXP and
3614ROW-DELIM-REGEXP are omitted the result table has only one cell and
3615the entire region contents is placed in that cell. Optional JUSTIFY
3616is one of 'left, 'center or 'right, which specifies the cell
3617justification. Optional MIN-CELL-WIDTH specifies the minimum cell
3618width. Optional COLUMNS specify the number of columns when
3619ROW-DELIM-REGEXP is not specified.
3620
3621
3622Example 1:
3623
36241, 2, 3, 4
36255, 6, 7, 8
3626, 9, 10
3627
3628Running `table-capture' on above 3 line region with COL-DELIM-REGEXP
3629\",\" and ROW-DELIM-REGEXP \"\\n\" creates the following table. In
3630this example the cells are centered and minimum cell width is
3631specified as 5.
3632
3633+-----+-----+-----+-----+
3634| 1 | 2 | 3 | 4 |
3635+-----+-----+-----+-----+
3636| 5 | 6 | 7 | 8 |
3637+-----+-----+-----+-----+
3638| | 9 | 10 | |
3639+-----+-----+-----+-----+
3640
3641Note:
3642
3643In case the function is called interactively user must use \\[quoted-insert] `quoted-insert'
3644in order to enter \"\\n\" successfully. COL-DELIM-REGEXP at the end
3645of each row is optional.
3646
3647
3648Example 2:
3649
3650This example shows how a table can be used for text layout editing.
3651Let `table-capture' capture the following region starting from
3652-!- and ending at -*-, that contains three paragraphs and two item
3653name headers. This time specify empty string for both
3654COL-DELIM-REGEXP and ROW-DELIM-REGEXP.
3655
3656-!-`table-capture' is a powerful command however mastering its power
3657requires some practice. Here is a list of items what it can do.
3658
3659Parse Cell Items By using column delimiter regular
3660 expression and raw delimiter regular
3661 expression, it parses the specified text
3662 area and extracts cell items from
3663 non-table text and then forms a table out
3664 of them.
3665
3666Capture Text Area When no delimiters are specified it
3667 creates a single cell table. The text in
3668 the specified region is placed in that
3669 cell.-*-
3670
3671Now the entire content is captured in a cell which is itself a table
3672like this.
3673
3674+-----------------------------------------------------------------+
3675|`table-capture' is a powerful command however mastering its power|
3676|requires some practice. Here is a list of items what it can do. |
3677| |
3678|Parse Cell Items By using column delimiter regular |
3679| expression and raw delimiter regular |
3680| expression, it parses the specified text |
3681| area and extracts cell items from |
3682| non-table text and then forms a table out |
3683| of them. |
3684| |
3685|Capture Text Area When no delimiters are specified it |
3686| creates a single cell table. The text in |
3687| the specified region is placed in that |
3688| cell. |
3689+-----------------------------------------------------------------+
3690
3691By splitting the cell appropriately we now have a table consisting of
3692paragraphs occupying its own cell. Each cell can now be edited
3693independently.
3694
3695+-----------------------------------------------------------------+
3696|`table-capture' is a powerful command however mastering its power|
3697|requires some practice. Here is a list of items what it can do. |
3698+---------------------+-------------------------------------------+
3699|Parse Cell Items |By using column delimiter regular |
3700| |expression and raw delimiter regular |
3701| |expression, it parses the specified text |
3702| |area and extracts cell items from |
3703| |non-table text and then forms a table out |
3704| |of them. |
3705+---------------------+-------------------------------------------+
3706|Capture Text Area |When no delimiters are specified it |
3707| |creates a single cell table. The text in |
3708| |the specified region is placed in that |
3709| |cell. |
3710+---------------------+-------------------------------------------+
3711
3712By applying `table-release', which does the opposite process, the
3713contents become once again plain text. `table-release' works as
3714companion command to `table-capture' this way.
3715"
3716 (interactive
3717 (let ((col-delim-regexp)
3718 (row-delim-regexp))
3719 (barf-if-buffer-read-only)
3720 (if (table--probe-cell)
3721 (error "Can't insert a table inside a table"))
3722 (list
3723 (mark) (point)
3724 (setq col-delim-regexp
3725 (read-from-minibuffer "Column delimiter regexp: "
3726 (car table-col-delim-regexp-history) nil nil 'table-col-delim-regexp-history))
3727 (setq row-delim-regexp
3728 (read-from-minibuffer "Row delimiter regexp: "
3729 (car table-row-delim-regexp-history) nil nil 'table-row-delim-regexp-history))
3730 (let* ((completion-ignore-case t)
3731 (default (car table-capture-justify-history)))
3732 (if (and (string= col-delim-regexp "") (string= row-delim-regexp "")) 'left
3733 (intern
3734 (downcase (completing-read
3735 (format "Justify (default %s): " default)
3736 '(("left") ("center") ("right"))
3737 nil t nil 'table-capture-justify-history default)))))
3738 (if (and (string= col-delim-regexp "") (string= row-delim-regexp "")) "1"
3739 (table--read-from-minibuffer '("Minimum cell width" . table-capture-min-cell-width-history)))
3740 (if (and (not (string= col-delim-regexp "")) (string= row-delim-regexp ""))
3741 (string-to-number
3742 (table--read-from-minibuffer '("Number of columns" . 'table-capture-columns-history)))
3743 nil)
3744 )))
3745 (if (> beg end) (let ((tmp beg)) (setq beg end) (setq end tmp)))
3746 (if (string= col-delim-regexp "") (setq col-delim-regexp nil))
3747 (if (string= row-delim-regexp "") (setq row-delim-regexp nil))
3748 (if (and columns (< columns 1)) (setq columns nil))
3749 (unless min-cell-width (setq min-cell-width "5"))
3750 (let ((contents (buffer-substring beg end))
3751 (cols 0) (rows 0) c r cell-list
3752 (delim-pattern
3753 (if (and col-delim-regexp row-delim-regexp)
3754 (format "\\(\\(%s\\)?\\s *\\(%s\\)\\s *\\)\\|\\(\\(%s\\)\\s *\\)"
3755 col-delim-regexp row-delim-regexp col-delim-regexp)
3756 (if col-delim-regexp
3757 (format "\\(\\)\\(\\)\\(\\)\\(\\(%s\\)\\s *\\)" col-delim-regexp))))
3758 (contents-list))
3759 ;; when delimiters are specified extract cells and determine the cell dimension
3760 (if delim-pattern
3761 (with-temp-buffer
3762 (insert contents)
3763 ;; make sure the contents ends with a newline
3764 (goto-char (point-max))
3765 (unless (zerop (current-column))
3766 (insert ?\n))
3767 ;; skip the preceding white spaces
3768 (goto-char (point-min))
3769 (if (looking-at "\\s +")
3770 (goto-char (match-end 0)))
3771 ;; extract cell contents
3772 (let ((from (point)))
3773 (setq cell-list nil)
3774 (setq c 0)
3775 (while (and (re-search-forward delim-pattern nil t)
3776 (cond
3777 ;; row delimiter
3778 ((and (match-string 1) (not (string= (match-string 1) "")))
3779 (setq rows (1+ rows))
3780 (setq cell-list
3781 (append cell-list (list (buffer-substring from (match-beginning 1)))))
3782 (setq from (match-end 1))
3783 (setq contents-list
3784 (append contents-list (list cell-list)))
3785 (setq cell-list nil)
3786 (setq c (1+ c))
3787 (if (> c cols) (setq cols c))
3788 (setq c 0)
3789 t)
3790 ;; column delimiter
3791 ((and (match-string 4) (not (string= (match-string 4) "")))
3792 (setq cell-list
3793 (append cell-list (list (buffer-substring from (match-beginning 4)))))
3794 (setq from (match-end 4))
3795 (setq c (1+ c))
3796 (if (> c cols) (setq cols c))
3797 t)
3798 (t nil))))
3799 ;; take care of the last element without a post delimiter
3800 (unless (null (looking-at ".+$"))
3801 (setq cell-list
3802 (append cell-list (list (match-string 0))))
3803 (setq cols (1+ cols)))
3804 ;; take care of the last row without a terminating delimiter
3805 (unless (null cell-list)
3806 (setq rows (1+ rows))
3807 (setq contents-list
3808 (append contents-list (list cell-list)))))))
3809 ;; finalize the table dimension
3810 (if (and columns contents-list)
3811 ;; when number of columns are specified and cells are parsed determine the dimension
3812 (progn
3813 (setq cols columns)
3814 (setq rows (/ (+ (length (car contents-list)) columns -1) columns)))
3815 ;; when dimensions are not specified default to a single cell table
3816 (if (zerop rows) (setq rows 1))
3817 (if (zerop cols) (setq cols 1)))
3818 ;; delete the region and reform line breaks
3819 (delete-region beg end)
3820 (goto-char beg)
3821 (unless (zerop (current-column))
3822 (insert ?\n))
3823 (unless (looking-at "\\s *$")
3824 (save-excursion
3825 (insert ?\n)))
db95369b 3826 ;; insert the table
238240c9
RS
3827 ;; insert the cell contents
3828 (if (null contents-list)
3829 ;; single cell
3830 (let ((width) (height))
3831 (with-temp-buffer
3832 (insert contents)
3833 (table--remove-eol-spaces (point-min) (point-max))
3834 (table--untabify (point-min) (point-max))
3835 (setq width (table--measure-max-width))
3836 (setq height (1+ (table--current-line (point-max))))
3837 (setq contents (buffer-substring (point-min) (point-max))))
3838 (table-insert cols rows width height)
3839 (table-with-cache-buffer
3840 (insert contents)
3841 (setq table-inhibit-auto-fill-paragraph t)))
3842 ;; multi cells
3843 (table-insert cols rows min-cell-width 1)
3844 (setq r 0)
3845 (setq cell-list nil)
3846 (while (< r rows)
3847 (setq r (1+ r))
3848 (setq c 0)
3849 (unless cell-list
3850 (setq cell-list (car contents-list))
3851 (setq contents-list (cdr contents-list)))
3852 (while (< c cols)
3853 (setq c (1+ c))
3854 (if (car cell-list)
3855 (table-with-cache-buffer
3856 (insert (car cell-list))
3857 (setq cell-list (cdr cell-list))
3858 (setq table-cell-info-justify justify)))
3859 (table-forward-cell 1))))))
3860
3861;;;###autoload
3862(defun table-release ()
3863 "Convert a table into plain text by removing the frame from a table.
3864Remove the frame from a table and inactivate the table. This command
3865converts a table into plain text without frames. It is a companion to
3866`table-capture' which does the opposite process."
3867 (interactive)
3868 (let ((origin-cell (table--probe-cell))
3869 table-lu table-rb)
3870 (if origin-cell
3871 (let ((old-point (point-marker)))
3872 ;; save-excursion is not sufficient for this
3873 ;; because untabify operation moves point
3874 (set-marker-insertion-type old-point t)
3875 (unwind-protect
3876 (progn
3877 (while
3878 (progn
3879 (table-forward-cell 1 nil 'unrecognize)
3880 (let ((cell (table--probe-cell)))
3881 (if (or (null table-lu)
3882 (< (car cell) table-lu))
3883 (setq table-lu (car cell)))
3884 (if (or (null table-rb)
3885 (> (cdr cell) table-rb))
3886 (setq table-rb (cdr cell)))
3887 (and cell (not (equal cell origin-cell))))))
3888 (let* ((lu-coord (table--get-coordinate table-lu))
3889 (rb-coord (table--get-coordinate table-rb))
3890 (lu (table--goto-coordinate (table--offset-coordinate lu-coord '(-1 . -1)))))
3891 (table--spacify-frame)
3892 (setcdr rb-coord (1+ (cdr rb-coord)))
3893 (delete-rectangle lu (table--goto-coordinate (cons (car lu-coord) (cdr rb-coord))))
3894 (table--remove-eol-spaces
3895 (table--goto-coordinate (cons 0 (1- (cdr lu-coord))))
3896 (table--goto-coordinate rb-coord) nil t)))
3897 (goto-char old-point))))))
3898
3899;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3900;;
3901;; Worker functions (executed implicitly)
3902;;
3903
3904(defun table--make-cell-map ()
3905 "Make the table cell keymap if it does not exist yet."
3906 ;; this is irrelevant to keymap but good place to make sure to be executed
3907 (table--update-cell-face)
3908 (unless table-cell-map
3909 (let ((map (make-sparse-keymap))
3910 (remap-alist table-command-remap-alist))
3911 ;; table-command-prefix mode specific bindings
3912 (if (vectorp table-command-prefix)
4e454e5b
JB
3913 (mapc (lambda (binding)
3914 (let ((seq (copy-sequence (car binding))))
3915 (and (vectorp seq)
3916 (listp (aref seq 0))
3917 (eq (car (aref seq 0)) 'control)
3918 (progn
3919 (aset seq 0 (cadr (aref seq 0)))
3920 (define-key map (vconcat table-command-prefix seq) (cdr binding))))))
3921 table-cell-bindings))
238240c9 3922 ;; shorthand control bindings
4e454e5b
JB
3923 (mapc (lambda (binding)
3924 (define-key map (car binding) (cdr binding)))
3925 table-cell-bindings)
238240c9
RS
3926 ;; remap normal commands to table specific version
3927 (while remap-alist
3928 (define-key map (vector 'remap (caar remap-alist)) (cdar remap-alist))
3929 (setq remap-alist (cdr remap-alist)))
3930 ;;
3931 (setq table-cell-map map)
3932 (fset 'table-cell-map map)))
3933 ;; add menu for table cells
3934 (unless table-disable-menu
3935 (easy-menu-define table-cell-menu-map table-cell-map "Table cell menu" table-cell-menu)
3936 (if (featurep 'xemacs)
3937 (easy-menu-add table-cell-menu)))
3938 (run-hooks 'table-cell-map-hook))
3939
3940;; Create the keymap after running the user init file so that the user
3941;; modification to the global-map is accounted.
3942(add-hook 'after-init-hook 'table--make-cell-map t)
3943
3944(defun *table--cell-self-insert-command ()
3945 "Table cell version of `self-insert-command'."
3946 (interactive "*")
3947 (let ((char (table--unibyte-char-to-multibyte last-command-char)))
3948 (if (eq buffer-undo-list t) nil
3949 (if (not (eq last-command this-command))
3950 (setq table-cell-self-insert-command-count 0)
3951 (if (car buffer-undo-list) nil
3952 (if (>= table-cell-self-insert-command-count 19)
3953 (setq table-cell-self-insert-command-count 0)
3954 (setq buffer-undo-list (cdr buffer-undo-list))
3955 (setq table-cell-self-insert-command-count (1+ table-cell-self-insert-command-count))))))
3956 (table--cell-insert-char char overwrite-mode)))
3957
3958(defun *table--cell-delete-backward-char (n)
3959 "Table cell version of `delete-backward-char'."
3960 (interactive "*p")
3961 (*table--cell-delete-char (- n)))
3962
3963(defun *table--cell-newline (&optional indent)
3964 "Table cell version of `newline'."
3965 (interactive "*")
3966 (table-with-cache-buffer
3967 (let ((column (current-column)))
3968 (insert ?\n)
3969 (if indent (indent-to-column column))
3970 ;; fill only when at the beginning of paragraph
3971 (if (= (point)
3972 (save-excursion
3973 (forward-paragraph -1)
3974 (if (looking-at "\\s *$")
3975 (forward-line 1))
3976 (point)))
3977 nil ; yes, at the beginning of the paragraph
3978 (setq table-inhibit-auto-fill-paragraph t)))))
3979
3980(defun *table--cell-open-line (n)
3981 "Table cell version of `open-line'."
3982 (interactive "*p")
3983 (table-with-cache-buffer
3984 (save-excursion
3985 (insert (make-string n ?\n))
3986 (table--fill-region (point) (point))
3987 (setq table-inhibit-auto-fill-paragraph t))))
3988
3989(defun *table--cell-newline-and-indent ()
3990 "Table cell version of `newline-and-indent'."
3991 (interactive)
3992 (*table--cell-newline t))
3993
3994(defun *table--cell-delete-char (n)
3995 "Table cell version of `delete-char'."
3996 (interactive "*p")
3997 (let ((overwrite overwrite-mode))
3998 (table-with-cache-buffer
3999 (if (and overwrite (< n 0))
4000 (progn
4001 (while (not (zerop n))
4002 (let ((coordinate (table--get-coordinate)))
4003 (if (zerop (car coordinate))
4004 (unless (zerop (cdr coordinate))
4005 (table--goto-coordinate (cons (1- table-cell-info-width) (1- (cdr coordinate))))
4006 (unless (eolp)
4007 (delete-char 1)))
4008 (delete-char -1)
ec85195e 4009 (insert ?\s)
238240c9
RS
4010 (forward-char -1)))
4011 (setq n (1+ n)))
4012 (setq table-inhibit-auto-fill-paragraph t))
4013 (let ((coordinate (table--get-coordinate))
4014 (end-marker (copy-marker (+ (point) n)))
4015 (deleted))
4016 (if (or (< end-marker (point-min))
4017 (> end-marker (point-max))) nil
4018 (table--remove-eol-spaces (point-min) (point-max))
4019 (setq deleted (buffer-substring (point) end-marker))
4020 (delete-char n)
4021 ;; in fixed width mode when two lines are concatenated
4022 ;; remove continuation character if there is one.
4023 (and table-fixed-width-mode
4024 (string-match "^\n" deleted)
4025 (equal (char-before) table-word-continuation-char)
4026 (delete-char -2))
4027 ;; see if the point is placed at the right tip of the previous
4028 ;; blank line, if so get rid of the preceding blanks.
4029 (if (and (not (bolp))
4030 (/= (cdr coordinate) (cdr (table--get-coordinate)))
4031 (let ((end (point)))
4032 (save-excursion
4033 (beginning-of-line)
4034 (re-search-forward "\\s +" end t)
4035 (= (point) end))))
4036 (replace-match ""))
4037 ;; do not fill the paragraph if the point is already at the end
4038 ;; of this paragraph and is following a blank character
4039 ;; (otherwise the filling squeezes the preceding blanks)
4040 (if (and (looking-at "\\s *$")
4041 (or (bobp)
4042 (save-excursion
4043 (backward-char)
4044 (looking-at "\\s "))))
4045 (setq table-inhibit-auto-fill-paragraph t))
4046 )
4047 (set-marker end-marker nil))))))
4048
4049(defun *table--cell-quoted-insert (arg)
4050 "Table cell version of `quoted-insert'."
4051 (interactive "*p")
4052 (let ((char (table--unibyte-char-to-multibyte (read-quoted-char))))
4053 (while (> arg 0)
4054 (table--cell-insert-char char nil)
4055 (setq arg (1- arg)))))
4056
4057(defun *table--cell-describe-mode ()
4058 "Table cell version of `describe-mode'."
4059 (interactive)
4060 (if (not (table--point-in-cell-p))
4061 (call-interactively 'describe-mode)
4062 (with-output-to-temp-buffer "*Help*"
4063 (princ "Table mode: (in ")
4064 (princ mode-name)
4065 (princ " mode)
4066
4067Table is not a mode technically. You can regard it as a pseudo mode
4068which exists locally within a buffer. It overrides some standard
4069editing behaviors. Editing operations in a table produces confined
4070effects to the current cell. It may grow the cell horizontally and/or
4071vertically depending on the newly entered or deleted contents of the
4072cell, and also depending on the current mode of cell.
4073
4074In the normal mode the table preserves word continuity. Which means
4075that a word never gets folded into multiple lines. For this purpose
4076table will occasionally grow the cell width. On the other hand, when
4077in a fixed width mode all cell width are fixed. When a word can not
4078fit in the cell width the word is folded into the next line. The
4079folded location is marked by a continuation character which is
4080specified in the variable `table-word-continuation-char'.
4081")
7b8e06aa 4082 (print-help-return-message))))
238240c9
RS
4083
4084(defun *table--cell-describe-bindings ()
4085 "Table cell version of `describe-bindings'."
4086 (interactive)
4087 (if (not (table--point-in-cell-p))
4088 (call-interactively 'describe-bindings)
4089 (with-output-to-temp-buffer "*Help*"
4090 (princ "Table Bindings:
4091key binding
4092--- -------
4093
4094")
4e454e5b
JB
4095 (mapc (lambda (binding)
4096 (princ (format "%-16s%s\n"
4097 (key-description (car binding))
4098 (cdr binding))))
4099 table-cell-bindings)
7b8e06aa 4100 (print-help-return-message))))
238240c9
RS
4101
4102(defun *table--cell-dabbrev-expand (arg)
4103 "Table cell version of `dabbrev-expand'."
4104 (interactive "*P")
4105 (let ((dabbrev-abbrev-char-regexp (concat "[^"
4106 (char-to-string table-cell-vertical-char)
4107 (char-to-string table-cell-intersection-char)
4108 " \n]")))
4109 (table-with-cache-buffer
4110 (dabbrev-expand arg))))
4111
4112(defun *table--cell-dabbrev-completion (&optional arg)
4113 "Table cell version of `dabbrev-completion'."
4114 (interactive "*P")
4115 (error "`dabbrev-completion' is incompatible with table")
4116 (let ((dabbrev-abbrev-char-regexp (concat "[^"
4117 (char-to-string table-cell-vertical-char)
4118 (char-to-string table-cell-intersection-char)
4119 " \n]")))
4120 (table-with-cache-buffer
4121 (dabbrev-completion arg))))
4122
4123(defun *table--present-cell-popup-menu (event)
4124 "Present and handle cell popup menu."
4125 (interactive "e")
4126 (unless table-disable-menu
4127 (select-window (posn-window (event-start event)))
4128 (goto-char (posn-point (event-start event)))
4129 (let ((item-list (x-popup-menu event table-cell-menu-map))
4130 (func table-cell-menu-map))
4131 (while item-list
4132 (setq func (nth 3 (assoc (car item-list) func)))
4133 (setq item-list (cdr item-list)))
4134 (if (and (symbolp func) (fboundp func))
4135 (call-interactively func)))))
4136
4137;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4138;;
4139;; Cell updating functions
4140;;
4141
4142(defun table--update-cell (&optional now)
4143 "Update the table cell contents.
4144When the optional parameter NOW is nil it only sets up the update
4145timer. If it is non-nil the function copies the contents of the cell
4146cache buffer into the designated cell in the table buffer."
4147 (if (null table-update-timer) nil
4148 (table--cancel-timer table-update-timer)
4149 (setq table-update-timer nil))
4150 (if (or (not now)
4151 (and (boundp 'quail-converting)
4152 quail-converting) ;; defer operation while current quail work is not finished.
4153 (and (boundp 'quail-translating)
4154 quail-translating))
4155 (setq table-update-timer
4156 (table--set-timer table-time-before-update
4157 (function table--update-cell)
4158 'now))
4159 (save-current-buffer
4160 (set-buffer table-cell-buffer)
4161 (let ((cache-buffer (get-buffer-create table-cache-buffer-name))
4162 (org-coord (table--get-coordinate))
4163 (in-cell (equal (table--cell-to-coord (table--probe-cell))
4164 (cons table-cell-info-lu-coordinate table-cell-info-rb-coordinate)))
4165 rectangle)
4166 (set-buffer cache-buffer)
4167 (setq rectangle
4168 (extract-rectangle
4169 1
4170 (table--goto-coordinate (cons table-cell-info-width (1- table-cell-info-height)))))
4171 (set-buffer table-cell-buffer)
4172 (delete-rectangle (table--goto-coordinate table-cell-info-lu-coordinate)
4173 (table--goto-coordinate table-cell-info-rb-coordinate))
4174 (table--goto-coordinate table-cell-info-lu-coordinate)
4175 (table--insert-rectangle rectangle)
4176 (let* ((cell (table--probe-cell))) ; must probe again in case of wide characters
4177 (table--put-cell-property cell)
4178 (table--put-cell-justify-property cell table-cell-info-justify)
4179 (table--put-cell-valign-property cell table-cell-info-valign))
4180 (table--goto-coordinate
4181 (if in-cell
4182 (table--transcoord-cache-to-table table-cell-cache-point-coordinate)
4183 org-coord))))
4184 ;; simulate undo behavior under overwrite-mode
4185 (if (and overwrite-mode (not (eq buffer-undo-list t)))
4186 (setq buffer-undo-list (cons nil buffer-undo-list)))))
4187
4188(defun table--update-cell-widened (&optional now)
4189 "Update the contents of the cells that are affected by widening operation."
4190 (if (null table-widen-timer) nil
4191 (table--cancel-timer table-widen-timer)
4192 (setq table-widen-timer nil))
4193 (if (not now)
4194 (setq table-widen-timer
4195 (table--set-timer (+ table-time-before-update table-time-before-reformat)
4196 (function table--update-cell-widened)
4197 'now))
4198 (save-current-buffer
4199 (if table-update-timer
4200 (table--update-cell 'now))
4201 (set-buffer table-cell-buffer)
4202 (let* ((current-coordinate (table--get-coordinate))
4203 (current-cell-coordinate (table--cell-to-coord (table--probe-cell)))
4204 (cell-coord-list (progn
4205 (table--goto-coordinate table-cell-info-lu-coordinate)
4206 (table--cell-list-to-coord-list (table--vertical-cell-list)))))
4207 (while cell-coord-list
4208 (let* ((cell-coord (prog1 (car cell-coord-list) (setq cell-coord-list (cdr cell-coord-list))))
4209 (currentp (equal cell-coord current-cell-coordinate)))
4210 (if currentp (table--goto-coordinate current-coordinate)
4211 (table--goto-coordinate (car cell-coord)))
4212 (table-recognize-cell 'froce)
4213 (let ((table-inhibit-update t))
4214 (table-with-cache-buffer
4215 (let ((sticky (and currentp
4216 (save-excursion
4217 (unless (bolp) (forward-char -1))
4218 (looking-at ".*\\S ")))))
4219 (table--fill-region (point-min) (point-max))
4220 (if sticky
4221 (setq current-coordinate (table--transcoord-cache-to-table))))))
4222 (table--update-cell 'now)
4223 ))
4224 (table--goto-coordinate current-coordinate)
4225 (table-recognize-cell 'froce)))))
4226
4227(defun table--update-cell-heightened (&optional now)
4228 "Update the contents of the cells that are affected by heightening operation."
4229 (if (null table-heighten-timer) nil
4230 (table--cancel-timer table-heighten-timer)
4231 (setq table-heighten-timer nil))
4232 (if (not now)
4233 (setq table-heighten-timer
4234 (table--set-timer (+ table-time-before-update table-time-before-reformat)
4235 (function table--update-cell-heightened)
4236 'now))
4237 (save-current-buffer
4238 (if table-update-timer
4239 (table--update-cell 'now))
4240 (if table-widen-timer
4241 (table--update-cell-widened 'now))
4242 (set-buffer table-cell-buffer)
4243 (let* ((current-coordinate (table--get-coordinate))
4244 (current-cell-coordinate (table--cell-to-coord (table--probe-cell)))
4245 (cell-coord-list (progn
4246 (table--goto-coordinate table-cell-info-lu-coordinate)
4247 (table--cell-list-to-coord-list (table--horizontal-cell-list)))))
4248 (while cell-coord-list
4249 (let* ((cell-coord (prog1 (car cell-coord-list) (setq cell-coord-list (cdr cell-coord-list))))
4250 (currentp (equal cell-coord current-cell-coordinate)))
4251 (if currentp (table--goto-coordinate current-coordinate)
4252 (table--goto-coordinate (car cell-coord)))
4253 (table-recognize-cell 'froce)
4254 (let ((table-inhibit-update t))
4255 (table-with-cache-buffer
4256 (let ((sticky (and currentp
4257 (save-excursion
4258 (unless (bolp) (forward-char -1))
4259 (looking-at ".*\\S ")))))
4260 (table--valign)
4261 (if sticky
4262 (setq current-coordinate (table--transcoord-cache-to-table))))))
4263 (table--update-cell 'now)
4264 ))
4265 (table--goto-coordinate current-coordinate)
4266 (table-recognize-cell 'froce)))))
4267
4268;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4269;;
4270;; Service functions (for external packages)
4271;;
4272
4273(defun table-goto-top-left-corner ()
4274 "Move point to top left corner of the current table and return the char position."
4275 (table--goto-coordinate
4276 (cons
4277 (1- (car (table--get-coordinate (car (table--horizontal-cell-list t t)))))
4278 (1- (cdr (table--get-coordinate (car (table--vertical-cell-list t t))))))))
4279
4280(defun table-goto-top-right-corner ()
4281 "Move point to top right corner of the current table and return the char position."
4282 (table--goto-coordinate
4283 (cons
4284 (car (table--get-coordinate (cdr (table--horizontal-cell-list nil t))))
4285 (1- (cdr (table--get-coordinate (car (table--vertical-cell-list t t))))))))
4286
4287(defun table-goto-bottom-left-corner ()
4288 "Move point to bottom left corner of the current table and return the char position."
4289 (table--goto-coordinate
4290 (cons
4291 (1- (car (table--get-coordinate (car (table--horizontal-cell-list t t)))))
4292 (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))))
4293
4294(defun table-goto-bottom-right-corner ()
4295 "Move point to bottom right corner of the current table and return the char position."
4296 (table--goto-coordinate
4297 (cons
4298 (car (table--get-coordinate (cdr (table--horizontal-cell-list nil t))))
4299 (1+ (cdr (table--get-coordinate (cdr (table--vertical-cell-list nil t))))))))
4300
4301(defun table-call-interactively (function &optional recoard-flag keys)
4302 "Call FUNCTION, or a table version of it if applicable.
4303See `call-interactively' for full description of the arguments."
4304 (let ((table-func (intern-soft (format "*table--cell-%s" function))))
4305 (call-interactively
4306 (if (and table-func
4307 (table--point-in-cell-p))
4308 table-func
4309 function) recoard-flag keys)))
4310
4311(defun table-funcall (function &rest arguments)
4312 "Call FUNCTION, or a table version of it if applicable.
4313See `funcall' for full description of the arguments."
4314 (let ((table-func (intern-soft (format "*table--cell-%s" function))))
4315 (apply
4316 (if (and table-func
4317 (table--point-in-cell-p))
4318 table-func
4319 function)
4320 arguments)))
4321
4322(defmacro table-apply (function &rest arguments)
4323 "Call FUNCTION, or a table version of it if applicable.
4324See `apply' for full description of the arguments."
4325 (let ((table-func (make-symbol "table-func")))
4326 `(let ((,table-func (intern-soft (format "*table--cell-%s" ,function))))
4327 (apply
4328 (if (and ,table-func
4329 (table--point-in-cell-p))
4330 ,table-func
4331 ,function)
4332 ,@arguments))))
4333
4334;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4335;;
4336;; Utility functions
4337;;
4338
4339(defun table--read-from-minibuffer (prompt-history)
4340 "A wrapper to `read-from-minibuffer'.
4341PROMPT-HISTORY is a cons cell which car is the prompt string and the
4342cdr is the history symbol."
4343 (let ((default (car (symbol-value (cdr prompt-history)))))
4344 (read-from-minibuffer
4345 (format "%s (default %s): " (car prompt-history) default)
4346 "" nil nil (cdr prompt-history) default))
4347 (and (featurep 'xemacs)
4348 (equal (car (symbol-value (cdr prompt-history))) "")
4349 (set (cdr prompt-history)
4350 (cdr (symbol-value (cdr prompt-history)))))
4351 (car (symbol-value (cdr prompt-history))))
4352
4353(defun table--unibyte-char-to-multibyte (char)
4354 "Convert CHAR by `unibyte-char-to-multibyte' when possible and necessary."
4355 ;; This part is take from `quoted-insert'.
4356 ;; Assume character codes 0240 - 0377 stand for characters in some
4357 ;; single-byte character set, and convert them to Emacs
4358 ;; characters.
4359 (if (and enable-multibyte-characters
4360 (fboundp 'unibyte-char-to-multibyte)
4361 (>= char ?\240)
4362 (<= char ?\377))
4363 (unibyte-char-to-multibyte char)
4364 char))
4365
4366(defun table--buffer-substring-and-trim (beg end)
4367 "Extract buffer substring and remove blanks from front and the rear of it."
4368 (save-excursion
4369 (save-restriction
4370 (narrow-to-region (goto-char beg) end)
4371 (if (re-search-forward "\\s *")
4372 (setq beg (match-end 0)))
4373 (if (re-search-forward "\\s *\\'" end t)
4374 (setq end (match-beginning 0)))
4375 (table--remove-cell-properties
4376 0 (- end beg)
4377 (buffer-substring beg end)))))
4378
4379(defun table--valign ()
4380 "Vertically align the cache cell contents.
4381Current buffer must be the cache buffer at the entry to this function.
4382Returns the coordinate of the final point location."
4383 (if (or (null table-cell-info-valign)
4384 (eq table-cell-info-valign 'none))
4385 (table--get-coordinate)
4386 (let ((saved-point (point-marker)))
4387 ;;(set-marker-insertion-type saved-point t)
4388 (goto-char (point-min))
4389 (let* ((from (and (re-search-forward "^.*\\S " nil t)
4390 (table--current-line)))
4391 (to (let ((tmp from))
4392 (while (re-search-forward "^.*\\S " nil t)
4393 (setq tmp (table--current-line)))
4394 tmp))
4395 (content-height (and from to (1+ (- to from)))))
4396 (unless (null content-height)
4397 (goto-char (point-min))
4398 (if (looking-at "\\s *\n")
4399 (replace-match ""))
4400 (cond ((eq table-cell-info-valign 'middle)
4401 (insert (make-string (/ (- table-cell-info-height content-height) 2) ?\n)))
4402 ((eq table-cell-info-valign 'bottom)
4403 (insert (make-string (- table-cell-info-height content-height) ?\n))))
4404 (table--goto-coordinate (cons table-cell-info-width (1- table-cell-info-height)))
4405 (if (re-search-forward "\\s +\\'" nil t)
4406 (replace-match ""))))
4407 (goto-char saved-point)
4408 (set-marker saved-point nil)
4409 (let ((coord (table--get-coordinate)))
4410 (unless (< (cdr coord) table-cell-info-height)
4411 (setcdr coord (1- table-cell-info-height))
4412 (table--goto-coordinate coord))
4413 coord))))
4414
4415(defun table--query-justification ()
4416 (barf-if-buffer-read-only)
4417 (let* ((completion-ignore-case t)
4418 (default (car table-justify-history)))
4419 (intern (downcase (completing-read
4420 (format "Justify (default %s): " default)
4421 '(("left") ("center") ("right") ("top") ("middle") ("bottom") ("none"))
4422 nil t nil 'table-justify-history default)))))
4423
4424(defun table--spacify-frame ()
4425 "Spacify table frame.
4426Replace frame characters with spaces."
e99add21
JB
4427 (let ((frame-char
4428 (append (string-to-list table-cell-horizontal-chars)
4429 (list table-cell-intersection-char table-cell-vertical-char))))
238240c9
RS
4430 (while
4431 (progn
4432 (cond
4433 ((eq (char-after) table-cell-intersection-char)
4434 (save-excursion
4435 (let ((col (current-column)))
4436 (and (zerop (forward-line 1))
4437 (zerop (current-column))
4438 (move-to-column col)
4439 (table--spacify-frame))))
4440 (delete-char 1)
ec85195e 4441 (insert-before-markers ?\s))
e99add21 4442 ((table--cell-horizontal-char-p (char-after))
238240c9
RS
4443 (while (progn
4444 (delete-char 1)
ec85195e 4445 (insert-before-markers ?\s)
e99add21 4446 (table--cell-horizontal-char-p (char-after)))))
238240c9
RS
4447 ((eq (char-after) table-cell-vertical-char)
4448 (while (let ((col (current-column)))
4449 (delete-char 1)
ec85195e 4450 (insert-before-markers ?\s)
238240c9
RS
4451 (and (zerop (forward-line 1))
4452 (zerop (current-column))
4453 (move-to-column col)
4454 (eq (char-after) table-cell-vertical-char))))))
4455 (memq (char-after) frame-char)))))
4456
4457(defun table--remove-blank-lines (n)
4458 "Delete N blank lines from the current line.
4459For adjusting below area of the table when the table is shortened."
4460 (move-to-column 0)
4461 (let ((first-blank t))
4462 (while (> n 0)
4463 (setq n (1- n))
4464 (cond ((looking-at "\\s *\\'")
4465 (delete-region (match-beginning 0) (match-end 0))
4466 (setq n 0))
4467 ((and (looking-at "\\([ \t]*\n[ \t]*\\)\n") first-blank)
4468 (delete-region (match-beginning 1) (match-end 1)))
4469 ((looking-at "[ \t]*$")
4470 (delete-region (match-beginning 0) (match-end 0))
4471 (forward-line 1))
4472 (t
4473 (setq first-blank nil)
4474 (forward-line 1))))))
4475
4476(defun table--uniform-list-p (l)
4477 "Return nil when LIST contains non equal elements. Otherwise return t."
4478 (if (null l) t
4479 (catch 'end
4480 (while (cdr l)
4481 (if (not (equal (car l) (cadr l))) (throw 'end nil))
4482 (setq l (cdr l)))
4483 t)))
4484
4485(defun table--detect-cell-alignment (cell)
4486 "Detect CELL contents alignment.
4487Guess CELL contents alignment both horizontally and vertically by
4488looking at the appearance of the CELL contents."
4489 (let ((cell-contents (extract-rectangle (car cell) (cdr cell)))
4490 (left-margin 0)
4491 (right-margin 0)
4492 (top-margin 0)
4493 (bottom-margin 0)
4494 (margin-diff 0)
4495 (margin-info-available nil)
4496 justify valign)
4497 (with-temp-buffer
4498 (table--insert-rectangle cell-contents)
4499 ;; determine the horizontal justification
4500 (goto-char (point-min))
4501 (while (re-search-forward "^\\( *\\).*[^ \n]\\( *\\)$" nil t)
4502 (setq margin-info-available t)
4503 (let* ((lm (- (match-end 1) (match-beginning 1)))
4504 (rm (- (match-end 2) (match-beginning 2)))
4505 (md (abs (- lm rm))))
4506 (if (> lm left-margin)
4507 (setq left-margin lm))
4508 (if (> rm right-margin)
4509 (setq right-margin rm))
4510 (if (> md margin-diff)
4511 (setq margin-diff md))))
4512 (setq justify
4513 (cond
4514 ((and margin-info-available
4515 (<= margin-diff 1)
4516 (> left-margin 0)) 'center)
4517 ((and margin-info-available
4518 (zerop right-margin)
4519 (> left-margin 0)) 'right)
4520 (t 'left)))
4521 ;; determine the vertical justification
4522 (goto-char (point-min))
4523 (if (and (re-search-forward "\\s *\\S " nil t)
4524 (/= (match-beginning 0) (match-end 0)))
4525 (setq top-margin (1- (count-lines (match-beginning 0) (match-end 0)))))
4526 (if (and (re-search-forward "\\s *\\'" nil t)
4527 (/= (match-beginning 0) (match-end 0)))
4528 (setq bottom-margin (1- (count-lines (match-beginning 0) (match-end 0)))))
4529 (setq valign
4530 (cond
4531 ((and (> top-margin 0)
4532 (> bottom-margin 0)
4533 (<= (abs (- top-margin bottom-margin)) 1)) 'middle)
4534 ((and (> top-margin 0)
4535 (zerop bottom-margin)) 'bottom)
4536 (t nil))))
4537 (table--put-cell-justify-property cell justify)
4538 (table--put-cell-valign-property cell valign)))
4539
4540(defun table--string-to-number-list (str)
4541 "Return a list of numbers in STR."
4542 (let ((idx 0)
4543 (nl nil))
4544 (while (string-match "[-0-9.]+" str idx)
4545 (setq idx (match-end 0))
4546 (setq nl (cons (string-to-number (match-string 0 str)) nl)))
4547 (nreverse nl)))
4548
4549(defun table--justify-cell-contents (justify &optional paragraph)
4550 "Justify the current cell contents.
4551JUSTIFY is a symbol 'left, 'center or 'right for horizontal, or 'top,
4552'middle, 'bottom or 'none for vertical. When PARAGRAPH is non-nil the
4553justify operation is limited to the current paragraph."
4554 (table-with-cache-buffer
4555 (let ((beg (point-min))
4556 (end (point-max-marker))
4557 (fill-column table-cell-info-width)
4558 (adaptive-fill-mode nil)
4559 (valign-symbols '(top middle bottom none)))
4560 (unless paragraph
4561 (if (memq justify valign-symbols)
4562 (setq table-cell-info-valign
4563 (if (eq justify 'none) nil justify))
4564 (setq table-cell-info-justify justify)))
4565 (save-excursion
4566 (if paragraph
4567 (let ((paragraph-start "\n"))
4568 (forward-paragraph)
4569 (or (bolp) (newline 1))
4570 (set-marker end (point))
4571 (setq beg (progn (forward-paragraph -1) (point)))))
4572 (if (memq justify valign-symbols)
4573 (table--valign)
4574 (table--remove-eol-spaces beg end 'bol)
4575 (let ((paragraph-start table-paragraph-start))
4576 (fill-region beg end table-cell-info-justify))))
4577 (setq table-inhibit-auto-fill-paragraph t)
4578 (set-marker end nil)))
4579 (table--update-cell 'now))
4580
4581(defun table--horizontally-shift-above-and-below (columns-to-extend top-to-bottom-coord-list)
4582 "Horizontally shift outside contents right above and right below of the table.
4583This function moves the surrounding text outside of the table so that
4584they match the horizontal growth/shrink of the table. It also
4585untabify the shift affected area including the right side of the table
4586so that tab related uneven shifting is avoided. COLUMNS-TO-EXTEND
4587specifies the number of columns the table grows, or shrinks if
4588negative. TOP-TO-BOTTOM-COORD-LIST is the vertical cell coordinate
4589list. This list can be any vertical list within the table."
4590 (save-excursion
4591 (let (beg-coord end-coord)
4592 (table--goto-coordinate (caar top-to-bottom-coord-list))
4593 (let* ((cell (table--horizontal-cell-list nil 'first-only 'top))
4594 (coord (cons (car (table--get-coordinate (cdr cell)))
4595 (cdr (table--get-coordinate (car cell))))))
4596 (setcar coord (1+ (car coord)))
4597 (setcdr coord (- (cdr coord) 2))
4598 (setq beg-coord (cons (car coord) (1+ (cdr coord))))
4599 (while (and (table--goto-coordinate coord 'no-extension)
4600 (not (looking-at "\\s *$")))
4601 (if (< columns-to-extend 0)
4602 (progn
4603 (table--untabify-line)
4604 (delete-char columns-to-extend))
4605 (table--untabify-line (point))
ec85195e 4606 (insert (make-string columns-to-extend ?\s)))
238240c9
RS
4607 (setcdr coord (1- (cdr coord)))))
4608 (table--goto-coordinate (caar (last top-to-bottom-coord-list)))
4609 (let ((coord (table--get-coordinate (cdr (table--horizontal-cell-list nil 'first-only 'bottom)))))
4610 (setcar coord (1+ (car coord)))
4611 (setcdr coord (+ (cdr coord) 2))
4612 (setq end-coord (cons (car coord) (1- (cdr coord))))
4613 (while (and (table--goto-coordinate coord 'no-extension)
4614 (not (looking-at "\\s *$")))
4615 (if (< columns-to-extend 0)
4616 (progn
4617 (table--untabify-line)
4618 (delete-char columns-to-extend))
4619 (table--untabify-line (point))
ec85195e 4620 (insert (make-string columns-to-extend ?\s)))
238240c9
RS
4621 (setcdr coord (1+ (cdr coord)))))
4622 (while (<= (cdr beg-coord) (cdr end-coord))
4623 (table--untabify-line (table--goto-coordinate beg-coord 'no-extension))
4624 (setcdr beg-coord (1+ (cdr beg-coord)))))))
4625
4626(defun table--create-growing-space-below (lines-to-extend left-to-right-coord-list bottom-border-y)
4627 "Create growing space below the table.
4628This function creates growing space below the table slightly
4629intelligent fashion. Following is the cases it handles for each
4630growing line:
4631 1. When the first line below the table is a complete blank line it
4632inserts a blank line.
4633 2. When the line starts with a prefix that matches the prefix of the
4634bottom line of the table it inserts a line consisting of prefix alone.
4635 3. Otherwise it deletes the rectangular contents where table will
4636grow into."
4637 (save-excursion
4638 (let ((i 0)
4639 (prefix (and (table--goto-coordinate (cons 0 bottom-border-y))
4640 (re-search-forward
4641 ".*\\S "
4642 (save-excursion
4643 (table--goto-coordinate
4644 (cons (1- (caar (car left-to-right-coord-list))) bottom-border-y)))
4645 t)
4646 (buffer-substring (match-beginning 0) (match-end 0)))))
4647 (while (< i lines-to-extend)
4648 (let ((y (+ i bottom-border-y 1)))
4649 (table--goto-coordinate (cons 0 y))
4650 (cond
4651 ((looking-at "\\s *$")
4652 (insert ?\n))
4653 ((and prefix (looking-at (concat (regexp-quote prefix) "\\s *$")))
4654 (insert prefix ?\n))
4655 (t
4656 (delete-rectangle
4657 (table--goto-coordinate (cons (1- (caar (car left-to-right-coord-list))) y))
4658 (table--goto-coordinate (cons (1+ (cadr (car (last left-to-right-coord-list)))) y))))))
4659 (setq i (1+ i))))))
4660
4661(defun table--untabify-line (&optional from)
4662 "Untabify current line.
4663Unlike save-excursion this guarantees preserving the cursor location
4664even when the point is on a tab character which is to be removed.
4665Optional FROM narrows the subject operation from this point to the end
4666of line."
4667 (let ((current-coordinate (table--get-coordinate)))
4668 (table--untabify (or from (progn (beginning-of-line) (point)))
4669 (progn (end-of-line) (point)))
4670 (table--goto-coordinate current-coordinate)))
4671
4672(defun table--untabify (beg end)
4673 "Wrapper to raw untabify."
4674 (untabify beg end)
4675 (if (featurep 'xemacs)
4676 ;; Cancel strange behavior of xemacs
4677 (message "")))
4678
4679(defun table--multiply-string (string multiplier)
4680 "Multiply string and return it."
4681 (let ((ret-str ""))
4682 (while (> multiplier 0)
4683 (setq ret-str (concat ret-str string))
4684 (setq multiplier (1- multiplier)))
4685 ret-str))
4686
951f97e6
SM
4687(defun table--line-column-position (line column)
4688 "Return the location of LINE forward at COLUMN."
4689 (save-excursion
4690 (forward-line line)
4691 (move-to-column column)
4692 (point)))
4693
4694(defun table--row-column-insertion-point-p (&optional columnp)
9861d11e 4695 "Return non-nil if it makes sense to insert a row or a column at point."
951f97e6
SM
4696 (and (not buffer-read-only)
4697 (or (get-text-property (point) 'table-cell)
4698 (let ((column (current-column)))
4699 (if columnp
4700 (or (text-property-any (line-beginning-position 0)
4701 (table--line-column-position -1 column)
4702 'table-cell t)
4703 (text-property-any (line-beginning-position) (point) 'table-cell t)
4704 (text-property-any (line-beginning-position 2)
4705 (table--line-column-position 1 column)
4706 'table-cell t))
4707 (text-property-any (table--line-column-position -2 column)
4708 (table--line-column-position -2 (+ 2 column))
4709 'table-cell t))))))
4710
238240c9
RS
4711(defun table--find-row-column (&optional columnp no-error)
4712 "Search table and return a cell coordinate list of row or column."
4713 (let ((current-coordinate (table--get-coordinate)))
4714 (catch 'end
4715 (catch 'error
4716 (let ((coord (table--get-coordinate)))
4717 (while
4718 (progn
4719 (if columnp (setcar coord (1- (car coord)))
4720 (setcdr coord (1- (cdr coord))))
4721 (>= (if columnp (car coord) (cdr coord)) 0))
4722 (while (progn
4723 (table--goto-coordinate coord 'no-extension 'no-tab-expansion)
e99add21
JB
4724 (not (looking-at (format "[%s%c%c]"
4725 table-cell-horizontal-chars
238240c9
RS
4726 table-cell-vertical-char
4727 table-cell-intersection-char))))
4728 (if columnp (setcar coord (1- (car coord)))
4729 (setcdr coord (1- (cdr coord))))
4730 (if (< (if columnp (car coord) (cdr coord)) 0)
4731 (throw 'error nil)))
4732 (if (table--probe-cell)
4733 (throw 'end (table--cell-list-to-coord-list (if columnp
4734 (table--vertical-cell-list t nil 'left)
4735 (table--horizontal-cell-list t nil 'top))))
4736 (table--goto-coordinate (table--offset-coordinate coord (if columnp '(0 . 1) '(1 . 0)))
4737 'no-extension 'no-tab-expansion)
4738 (if (table--probe-cell)
4739 (throw 'end (table--cell-list-to-coord-list (if columnp
4740 (table--vertical-cell-list t nil 'left)
4741 (table--horizontal-cell-list t nil 'top)))))))))
4742 (table--goto-coordinate current-coordinate)
4743 (if no-error nil
4744 (error "Table not found")))))
4745
4746(defun table--min-coord-list (coord-list)
4747 "Return minimum cell dimension of COORD-LIST.
4748COORD-LIST is a list of coordinate pairs (lu-coord . rb-coord), where
4749each pair in the list represents a cell. lu-coord is the left upper
4750coordinate of a cell and rb-coord is the right bottom coordinate of a
4751cell. A coordinate is a pair of x and y axis coordinate values. The
4752return value is a cons cell (min-w . min-h), where min-w and min-h are
4753respectively the minimum width and the minimum height of all the cells
4754in the list."
4755 (if (null coord-list) nil
4756 (let ((min-width 134217727)
4757 (min-height 134217727))
4758 (while coord-list
4759 (let* ((coord (prog1 (car coord-list) (setq coord-list (cdr coord-list))))
4760 (width (- (cadr coord) (caar coord)))
4761 (height (1+ (- (cddr coord) (cdar coord)))))
4762 (if (< width min-width) (setq min-width width))
4763 (if (< height min-height) (setq min-height height))))
4764 (cons min-width min-height))))
4765
4766(defun table--cell-can-split-horizontally-p ()
4767 "Test if a cell can split at current location horizontally."
4768 (and (not buffer-read-only)
4769 (let ((point-x (car (table--get-coordinate))))
4770 (table-recognize-cell 'force)
4771 (and (> point-x (car table-cell-info-lu-coordinate))
4772 (<= point-x (1- (car table-cell-info-rb-coordinate)))))))
4773
4774(defun table--cell-can-split-vertically-p ()
4775 "Test if a cell can split at current location vertically."
4776 (and (not buffer-read-only)
4777 (let ((point-y (cdr (table--get-coordinate))))
4778 (table-recognize-cell 'force)
4779 (and (> point-y (cdr table-cell-info-lu-coordinate))
4780 (<= point-y (cdr table-cell-info-rb-coordinate))))))
4781
4782(defun table--cell-can-span-p (direction)
4783 "Test if the current cell can span to DIRECTION."
4784 (table-recognize-cell 'force)
4785 (and (not buffer-read-only)
4786 (table--probe-cell)
4787 ;; get two adjacent cells from each corner
4788 (let ((cell (save-excursion
4789 (and
4790 (table--goto-coordinate
4791 (cons (cond ((eq direction 'right) (1+ (car table-cell-info-rb-coordinate)))
4792 ((eq direction 'left) (1- (car table-cell-info-lu-coordinate)))
4793 (t (car table-cell-info-lu-coordinate)))
4794 (cond ((eq direction 'above) (- (cdr table-cell-info-lu-coordinate) 2))
4795 ((eq direction 'below) (+ (cdr table-cell-info-rb-coordinate) 2))
4796 (t (cdr table-cell-info-lu-coordinate)))) 'no-extension)
4797 (table--probe-cell))))
4798 (cell2 (save-excursion
4799 (and
4800 (table--goto-coordinate
4801 (cons (cond ((eq direction 'right) (1+ (car table-cell-info-rb-coordinate)))
4802 ((eq direction 'left) (1- (car table-cell-info-lu-coordinate)))
4803 (t (car table-cell-info-rb-coordinate)))
4804 (cond ((eq direction 'above) (- (cdr table-cell-info-lu-coordinate) 2))
4805 ((eq direction 'below) (+ (cdr table-cell-info-rb-coordinate) 2))
4806 (t (cdr table-cell-info-rb-coordinate)))) 'no-extension)
4807 (table--probe-cell)))))
4808 ;; make sure the two cells exist, and they are identical, that cell's size matches the current one
4809 (and cell
4810 (equal cell cell2)
4811 (if (or (eq direction 'right) (eq direction 'left))
4812 (and (= (cdr (table--get-coordinate (car cell)))
4813 (cdr table-cell-info-lu-coordinate))
4814 (= (cdr (table--get-coordinate (cdr cell)))
4815 (cdr table-cell-info-rb-coordinate)))
4816 (and (= (car (table--get-coordinate (car cell)))
4817 (car table-cell-info-lu-coordinate))
4818 (= (car (table--get-coordinate (cdr cell)))
4819 (car table-cell-info-rb-coordinate))))))))
4820
4821(defun table--cell-insert-char (char &optional overwrite)
4822 "Insert CHAR inside a table cell."
4823 (let ((delete-selection-p (and (boundp 'delete-selection-mode)
4824 delete-selection-mode
4825 transient-mark-mode mark-active
4826 (not buffer-read-only)))
4827 (mark-coordinate (table--transcoord-table-to-cache (table--get-coordinate (mark t)))))
4828 (table-with-cache-buffer
4829 (and delete-selection-p
4830 (>= (car mark-coordinate) 0)
4831 (<= (car mark-coordinate) table-cell-info-width)
4832 (>= (cdr mark-coordinate) 0)
4833 (<= (cdr mark-coordinate) table-cell-info-height)
4834 (save-excursion
4835 (delete-region (point) (table--goto-coordinate mark-coordinate))))
4836 (if overwrite
4837 (let ((coordinate (table--get-coordinate)))
4838 (setq table-inhibit-auto-fill-paragraph t)
4839 (if (>= (car coordinate) table-cell-info-width)
4840 (if (>= (cdr coordinate) (1- table-cell-info-height))
4841 (insert "\n" char)
4842 (forward-line 1)
4843 (insert char)
4844 (unless (eolp)
4845 (delete-char 1)))
4846 (insert char)
4847 (unless (eolp)
4848 (delete-char 1))))
ec85195e 4849 (if (not (eq char ?\s))
238240c9
RS
4850 (if char (insert char))
4851 (if (not (looking-at "\\s *$"))
4852 (if (and table-fixed-width-mode
4853 (> (point) 2)
4854 (save-excursion
4855 (forward-char -2)
4856 (looking-at (concat "\\("
4857 (regexp-quote (char-to-string table-word-continuation-char))
4858 "\\)\n"))))
4859 (save-excursion
4860 (replace-match " " nil nil nil 1))
4861 (insert char))
4862 (let ((coordinate (table--get-coordinate)))
4863 (if (< (car coordinate) table-cell-info-width)
4864 (move-to-column (1+ (car coordinate)) t)
4865 (insert (make-string (forward-line 1) ?\n))
4866 (unless (bolp) (insert ?\n))))
4867 (setq table-inhibit-auto-fill-paragraph t))
4868 (save-excursion
4869 (let ((o-point (point)))
4870 (if (and (bolp)
4871 (or (progn
4872 (forward-paragraph)
4873 (forward-paragraph -1)
4874 (= o-point (point)))
4875 (progn
4876 (goto-char o-point)
4877 (forward-line)
4878 (setq o-point (point))
4879 (forward-paragraph)
4880 (forward-paragraph -1)
4881 (= o-point (point)))))
4882 (insert ?\n)))))))))
4883
4884(defun table--finish-delayed-tasks ()
4885 "Finish all outstanding delayed tasks."
4886 (if table-update-timer
4887 (table--update-cell 'now))
4888 (if table-widen-timer
4889 (table--update-cell-widened 'now))
4890 (if table-heighten-timer
4891 (table--update-cell-heightened 'now)))
4892
4893(defmacro table--log (&rest body)
4894 "Debug logging macro."
1042fc7f 4895 `(with-current-buffer (get-buffer-create "log")
238240c9
RS
4896 (goto-char (point-min))
4897 (let ((standard-output (current-buffer)))
4898 ,@body)))
4899
4900(defun table--measure-max-width (&optional unlimited)
4901 "Return maximum width of current buffer.
4902Normally the current buffer is expected to be already the cache
4903buffer. The width excludes following spaces at the end of each line.
4904Unless UNLIMITED is non-nil minimum return value is 1."
4905 (save-excursion
4906 (let ((width 0))
4907 (goto-char (point-min))
4908 (while
4909 (progn
4910 ;; do not count the following white spaces
4911 (re-search-forward "\\s *$")
4912 (goto-char (match-beginning 0))
4913 (if (> (current-column) width)
4914 (setq width (current-column)))
4915 (forward-line)
4916 (not (eobp))))
4917 (if unlimited width
4918 (max 1 width)))))
4919
4920(defun table--cell-to-coord (cell)
4921 "Create a cell coordinate pair from cell location pair."
4922 (if cell
4923 (cons (table--get-coordinate (car cell))
4924 (table--get-coordinate (cdr cell)))
4925 nil))
4926
4927(defun table--cell-list-to-coord-list (cell-list)
4928 "Create and return a coordinate list that corresponds to CELL-LIST.
4929CELL-LIST is a list of location pairs (lu . rb), where each pair
4930represents a cell in the list. lu is the left upper location and rb
4931is the right bottom location of a cell. The return value is a list of
4932coordinate pairs (lu-coord . rb-coord), where lu-coord is the left
4933upper coordinate and rb-coord is the right bottom coordinate of a
4934cell."
4935 (let ((coord-list))
4936 (while cell-list
4937 (let ((cell (prog1 (car cell-list) (setq cell-list (cdr cell-list)))))
4938 (setq coord-list
4939 (cons (table--cell-to-coord cell) coord-list))))
4940 (nreverse coord-list)))
4941
4942(defun table--test-cell-list (&optional horizontal reverse first-only pivot)
4943 "For testing `table--vertical-cell-list' and `table--horizontal-cell-list'."
4944 (let* ((current-coordinate (table--get-coordinate))
4945 (cell-list (if horizontal
4946 (table--horizontal-cell-list reverse first-only pivot)
4947 (table--vertical-cell-list reverse first-only pivot)))
4948 (count 0))
4949 (while cell-list
4950 (let* ((cell (if first-only (prog1 cell-list (setq cell-list nil))
4951 (prog1 (car cell-list) (setq cell-list (cdr cell-list)))))
4952 (dig1-str (format "%1d" (prog1 (% count 10) (setq count (1+ count))))))
4953 (goto-char (car cell))
4954 (table-with-cache-buffer
1042fc7f
SM
4955 (while (re-search-forward "." nil t)
4956 (replace-match dig1-str nil nil))
238240c9
RS
4957 (setq table-inhibit-auto-fill-paragraph t))
4958 (table--finish-delayed-tasks)))
4959 (table--goto-coordinate current-coordinate)))
4960
4961(defun table--vertical-cell-list (&optional top-to-bottom first-only pivot internal-dir internal-list internal-px)
4962 "Return a vertical cell list from the table.
4963The return value represents a list of cells including the current cell
4964that align vertically. Each element of the list is a cons cell (lu
4965. rb) where lu is the cell's left upper location and rb is the cell's
4966right bottom location. The cell order in the list is from bottom to
4967top of the table. If optional argument TOP-TO-BOTTOM is non-nil the
4968order is reversed as from top to bottom of the table. If optional
4969argument FIRST-ONLY is non-nil the return value is not a list of cells
4970but a single cons cell that is the first cell of the list, if the list
4971had been created. If optional argument PIVOT is a symbol `left' the
4972vertical cell search is aligned with the left edge of the current
4973cell, otherwise aligned with the right edge of the current cell. The
4974arguments INTERNAL-DIR, INTERNAL-LIST and INTERNAL-PX are internal use
4975only and must not be specified."
4976 (save-excursion
4977 (let* ((cell (table--probe-cell))
4978 (lu-coordinate (table--get-coordinate (car cell)))
4979 (rb-coordinate (table--get-coordinate (cdr cell)))
4980 (px (or internal-px (car (if (eq pivot 'left) lu-coordinate rb-coordinate))))
4981 (ty (- (cdr lu-coordinate) 2))
4982 (by (+ (cdr rb-coordinate) 2)))
4983 ;; in case of finding the the first cell, get the last adding item on the list
4984 (if (and (null internal-dir) first-only) (setq top-to-bottom (null top-to-bottom)))
4985 ;; travel up and process as recursion traces back (reverse order)
4986 (and cell
4987 (or (eq internal-dir 'up) (null internal-dir))
4988 (table--goto-coordinate (cons px (if top-to-bottom by ty)) 'no-extension 'no-tab-expansion)
4989 (setq internal-list (table--vertical-cell-list top-to-bottom first-only nil 'up nil px)))
4990 ;; return the last cell or add this cell to the list
4991 (if first-only (or internal-list cell)
4992 (setq internal-list (if cell (cons cell internal-list) internal-list))
4993 ;; travel down and process as entering each recursion (forward order)
4994 (and cell
4995 (or (eq internal-dir 'down) (null internal-dir))
4996 (table--goto-coordinate (cons px (if top-to-bottom ty by)) 'no-extension 'no-tab-expansion)
4997 (setq internal-list (table--vertical-cell-list top-to-bottom nil nil 'down internal-list px)))
4998 ;; return the result
4999 internal-list))))
5000
5001(defun table--horizontal-cell-list (&optional left-to-right first-only pivot internal-dir internal-list internal-py)
5002 "Return a horizontal cell list from the table.
5003The return value represents a list of cells including the current cell
5004that align horizontally. Each element of the list is a cons cells (lu
5005. rb) where lu is the cell's left upper location and rb is the cell's
5006right bottom location. The cell order in the list is from right to
5007left of the table. If optional argument LEFT-TO-RIGHT is non-nil the
5008order is reversed as from left to right of the table. If optional
5009argument FIRST-ONLY is non-nil the return value is not a list of cells
5010but a single cons cell that is the first cell of the list, if the
5011list had been created. If optional argument PIVOT is a symbol `top'
5012the horizontal cell search is aligned with the top edge of the current
5013cell, otherwise aligned with the bottom edge of the current cell. The
5014arguments INTERNAL-DIR, INTERNAL-LIST and INTERNAL-PY are internal use
5015only and must not be specified."
5016 (save-excursion
5017 (let* ((cell (table--probe-cell))
5018 (lu-coordinate (table--get-coordinate (car cell)))
5019 (rb-coordinate (table--get-coordinate (cdr cell)))
5020 (py (or internal-py (if (eq pivot 'top) (cdr lu-coordinate) (1+ (cdr rb-coordinate)))))
5021 (lx (1- (car lu-coordinate)))
5022 (rx (1+ (car rb-coordinate))))
5023 ;; in case of finding the the first cell, get the last adding item on the list
5024 (if (and (null internal-dir) first-only) (setq left-to-right (null left-to-right)))
5025 ;; travel left and process as recursion traces back (reverse order)
5026 (and cell
5027 (or (eq internal-dir 'left) (null internal-dir))
5028 (table--goto-coordinate (cons (if left-to-right rx lx) py) 'no-extension 'no-tab-expansion)
5029 (setq internal-list (table--horizontal-cell-list left-to-right first-only nil 'left nil py)))
5030 ;; return the last cell or add this cell to the list
5031 (if first-only (or internal-list cell)
5032 (setq internal-list (if cell (cons cell internal-list) internal-list))
5033 ;; travel right and process as entering each recursion (forward order)
5034 (and cell
5035 (or (eq internal-dir 'right) (null internal-dir))
5036 (table--goto-coordinate (cons (if left-to-right lx rx) py) 'no-extension 'no-tab-expansion)
5037 (setq internal-list (table--horizontal-cell-list left-to-right nil nil 'right internal-list py)))
5038 ;; return the result
5039 internal-list))))
5040
5041(defun table--point-in-cell-p (&optional location)
5042 "Return t when point is in a valid table cell in the current buffer.
5043When optional LOCATION is provided the test is performed at that location."
5044 (and (table--at-cell-p (or location (point)))
5045 (if location
5046 (save-excursion
5047 (goto-char location)
5048 (table--probe-cell))
5049 (table--probe-cell))))
5050
5051(defun table--region-in-cell-p (beg end)
5052 "Return t when location BEG and END are in a valid table cell in the current buffer."
5053 (and (table--at-cell-p (min beg end))
5054 (save-excursion
5055 (let ((cell-beg (progn (goto-char beg) (table--probe-cell))))
5056 (and cell-beg
5057 (equal cell-beg (progn (goto-char end) (table--probe-cell))))))))
5058
5059(defun table--at-cell-p (position &optional object at-column)
5060 "Returns non-nil if POSITION has table-cell property in OBJECT.
5061OBJECT is optional and defaults to the current buffer.
5062If POSITION is at the end of OBJECT, the value is nil."
5063 (if (and at-column (stringp object))
5064 (setq position (table--str-index-at-column object position)))
5065 (get-text-property position 'table-cell object))
5066
5067(defun table--probe-cell-left-up ()
5068 "Probe left up corner pattern of a cell.
5069If it finds a valid corner returns a position otherwise returns nil.
5070The position is the location before the first cell character.
5071Focus only on the corner pattern. Further cell validity check is required."
5072 (save-excursion
5073 (let ((vertical-str (regexp-quote (char-to-string table-cell-vertical-char)))
5074 (intersection-str (regexp-quote (char-to-string table-cell-intersection-char)))
5075 (v-border (format "[%c%c]" table-cell-vertical-char table-cell-intersection-char))
e99add21 5076 (h-border (format "[%s%c]" table-cell-horizontal-chars table-cell-intersection-char))
238240c9
RS
5077 (limit (save-excursion (beginning-of-line) (point))))
5078 (catch 'end
5079 (while t
5080 (catch 'retry-horizontal
5081 (if (not (search-backward-regexp v-border limit t))
5082 (throw 'end nil))
5083 (save-excursion
5084 (let ((column (current-column)))
5085 (while t
5086 (catch 'retry-vertical
5087 (if (zerop (forward-line -1)) nil (throw 'end nil))
5088 (move-to-column column)
5089 (while (and (looking-at vertical-str)
5090 (= column (current-column)))
5091 (if (zerop (forward-line -1)) nil (throw 'end nil))
5092 (move-to-column column))
5093 (cond
5094 ((/= column (current-column))
5095 (throw 'end nil))
5096 ((looking-at (concat intersection-str h-border))
5097 (forward-line 1)
5098 (move-to-column column)
5099 (forward-char 1)
5100 (throw 'end (point)))
5101 ((looking-at intersection-str)
5102 (throw 'retry-vertical nil))
5103 (t (throw 'retry-horizontal nil)))))))))))))
5104
5105(defun table--probe-cell-right-bottom ()
5106 "Probe right bottom corner pattern of a cell.
5107If it finds a valid corner returns a position otherwise returns nil.
5108The position is the location after the last cell character.
5109Focus only on the corner pattern. Further cell validity check is required."
5110 (save-excursion
5111 (let ((vertical-str (regexp-quote (char-to-string table-cell-vertical-char)))
5112 (intersection-str (regexp-quote (char-to-string table-cell-intersection-char)))
5113 (v-border (format "[%c%c]" table-cell-vertical-char table-cell-intersection-char))
e99add21 5114 (h-border (format "[%s%c]" table-cell-horizontal-chars table-cell-intersection-char))
238240c9
RS
5115 (limit (save-excursion (end-of-line) (point))))
5116 (catch 'end
5117 (while t
5118 (catch 'retry-horizontal
5119 (if (not (search-forward-regexp v-border limit t))
5120 (throw 'end nil))
5121 (save-excursion
5122 (forward-char -1)
5123 (let ((column (current-column)))
5124 (while t
5125 (catch 'retry-vertical
5126 (while (and (looking-at vertical-str)
5127 (= column (current-column)))
5128 (if (and (zerop (forward-line 1)) (zerop (current-column))) nil (throw 'end nil))
5129 (move-to-column column))
5130 (cond
5131 ((/= column (current-column))
5132 (throw 'end nil))
5133 ((save-excursion (forward-char -1) (looking-at (concat h-border intersection-str)))
5134 (save-excursion
5135 (and (zerop (forward-line -1))
5136 (move-to-column column)
5137 (looking-at v-border)
5138 (throw 'end (point))))
5139 (forward-char 1)
5140 (throw 'retry-horizontal nil))
5141 ((looking-at intersection-str)
5142 (if (and (zerop (forward-line 1)) (zerop (current-column))) nil (throw 'end nil))
5143 (move-to-column column)
5144 (throw 'retry-vertical nil))
5145 (t (throw 'retry-horizontal nil)))))))))))))
5146
5147(defun table--editable-cell-p (&optional abort-on-error)
5148 (and (not buffer-read-only)
951f97e6 5149 (get-text-property (point) 'table-cell)))
238240c9
RS
5150
5151(defun table--probe-cell (&optional abort-on-error)
5152 "Probes a table cell around the point.
5153Searches for the left upper corner and the right bottom corner of a table
5154cell which contains the current point location.
5155
5156The result is a cons cell (left-upper . right-bottom) where
5157the left-upper is the position before the cell's left upper corner character,
5158the right-bottom is the position after the cell's right bottom corner character.
5159
5160When it fails to find either one of the cell corners it returns nil or
5161signals error if the optional ABORT-ON-ERROR is non-nil."
5162 (let (lu rb
e99add21
JB
5163 (border (format "^[%s%c%c]+$"
5164 table-cell-horizontal-chars
238240c9
RS
5165 table-cell-vertical-char
5166 table-cell-intersection-char)))
5167 (if (and (condition-case nil
5168 (progn
5169 (and (setq lu (table--probe-cell-left-up))
5170 (setq rb (table--probe-cell-right-bottom))))
5171 (error nil))
5172 (< lu rb)
5173 (let ((lu-coordinate (table--get-coordinate lu))
5174 (rb-coordinate (table--get-coordinate rb)))
5175 ;; test for valid upper and lower borders
5176 (and (string-match
5177 border
5178 (buffer-substring
5179 (save-excursion
5180 (table--goto-coordinate
5181 (cons (1- (car lu-coordinate))
5182 (1- (cdr lu-coordinate)))))
5183 (save-excursion
5184 (table--goto-coordinate
5185 (cons (1+ (car rb-coordinate))
5186 (1- (cdr lu-coordinate)))))))
5187 (string-match
5188 border
5189 (buffer-substring
5190 (save-excursion
5191 (table--goto-coordinate
5192 (cons (1- (car lu-coordinate))
5193 (1+ (cdr rb-coordinate)))))
5194 (save-excursion
5195 (table--goto-coordinate
5196 (cons (1+ (car rb-coordinate))
5197 (1+ (cdr rb-coordinate))))))))))
5198 (cons lu rb)
5199 (if abort-on-error
5200 (error "Table cell not found")
5201 nil))))
5202
5203(defun table--insert-rectangle (rectangle)
5204 "Insert text of RECTANGLE with upper left corner at point.
5205Same as insert-rectangle except that mark operation is eliminated."
5206 (let ((lines rectangle)
5207 (insertcolumn (current-column))
5208 (first t))
5209 (while lines
5210 (or first
5211 (progn
5212 (forward-line 1)
5213 (or (bolp) (insert ?\n))
5214 (move-to-column insertcolumn t)))
5215 (setq first nil)
5216 (insert (car lines))
5217 (setq lines (cdr lines)))))
5218
5219(defun table--put-cell-property (cell)
5220 "Put standard text properties to the CELL.
5221The CELL is a cons cell (left-upper . right-bottom) where the
5222left-upper is the position before the cell's left upper corner
5223character, the right-bottom is the position after the cell's right
5224bottom corner character."
5225 (let ((lu (table--get-coordinate (car cell)))
5226 (rb (table--get-coordinate (cdr cell))))
5227 (save-excursion
5228 (while (<= (cdr lu) (cdr rb))
5229 (let ((beg (table--goto-coordinate lu 'no-extension))
5230 (end (table--goto-coordinate (cons (car rb) (cdr lu)))))
5231 (table--put-cell-line-property beg end))
5232 (setcdr lu (1+ (cdr lu))))
5233 (table--put-cell-justify-property cell table-cell-info-justify)
5234 (table--put-cell-valign-property cell table-cell-info-valign))))
5235
5236(defun table--put-cell-line-property (beg end &optional object)
5237 "Put standard text properties to a line of a cell.
5238BEG is the beginning of the line that is the location between left
5239cell border character and the first content character. END is the end
5240of the line that is the location between the last content character
5241and the right cell border character."
5242 (table--put-cell-content-property beg end object)
5243 (table--put-cell-keymap-property end (1+ end) object)
5244 (table--put-cell-indicator-property end (1+ end) object)
5245 (table--put-cell-rear-nonsticky end (1+ end) object))
5246
5247(defun table--put-cell-content-property (beg end &optional object)
5248 "Put cell content text properties."
5249 (table--put-cell-keymap-property beg end object)
5250 (table--put-cell-indicator-property beg end object)
5251 (table--put-cell-face-property beg end object)
5252 (table--put-cell-point-entered/left-property beg end object))
5253
5254(defun table--put-cell-indicator-property (beg end &optional object)
5255 "Put cell property which indicates that the location is within a table cell."
d6db9101
EZ
5256 (put-text-property beg end 'table-cell t object)
5257 (put-text-property beg end 'yank-handler table-yank-handler object))
238240c9
RS
5258
5259(defun table--put-cell-face-property (beg end &optional object)
5260 "Put cell face property."
b4c925d8 5261 (put-text-property beg end 'face 'table-cell object))
238240c9
RS
5262
5263(defun table--put-cell-keymap-property (beg end &optional object)
5264 "Put cell keymap property."
5265 (put-text-property beg end 'keymap 'table-cell-map object))
5266
5267(defun table--put-cell-rear-nonsticky (beg end &optional object)
5268 "Put rear-nonsticky property."
5269 (put-text-property beg end 'rear-nonsticky t object))
db95369b 5270
238240c9
RS
5271(defun table--put-cell-point-entered/left-property (beg end &optional object)
5272 "Put point-entered/left property."
5273 (put-text-property beg end 'point-entered 'table--point-entered-cell-function object)
5274 (put-text-property beg end 'point-left 'table--point-left-cell-function object))
5275
5276(defun table--remove-cell-properties (beg end &optional object)
5277 "Remove all cell properties.
5278If OBJECT is non-nil cell properties are removed from the OBJECT
5279instead of the current buffer and returns the OBJECT."
5280 (while (< beg end)
5281 (let ((next (next-single-property-change beg 'table-cell object end)))
5282 (if (get-text-property beg 'table-cell object)
5283 (remove-text-properties beg next
5284 (list
5285 'table-cell nil
5286 'table-justify nil
5287 'table-valign nil
5288 'face nil
5289 'rear-nonsticky nil
5290 'point-entered nil
5291 'point-left nil
5292 'keymap nil)
5293 object))
5294 (setq beg next)))
5295 object)
5296
5297(defun table--update-cell-face ()
5298 "Update cell face according to the current mode."
5299 (if (featurep 'xemacs)
b4c925d8
MB
5300 (set-face-property 'table-cell 'underline table-fixed-width-mode)
5301 (set-face-inverse-video-p 'table-cell table-fixed-width-mode)))
238240c9
RS
5302
5303(table--update-cell-face)
5304
5305(defun table--get-property (cell property)
5306 "Get CELL's PROPERTY."
5307 (or (get-text-property (car cell) property)
5308 (get-text-property (1- (cdr cell)) property)))
5309
5310(defun table--get-cell-justify-property (cell)
5311 "Get cell's justify property."
5312 (table--get-property cell 'table-justify))
5313
5314(defun table--get-cell-valign-property (cell)
5315 "Get cell's vertical alignment property."
5316 (table--get-property cell 'table-valign))
5317
5318(defun table--put-property (cell property value)
5319 "Put CELL's PROPERTY the VALUE."
5320 (let ((beg (car cell))
5321 (end (cdr cell)))
5322 (put-text-property beg (1+ beg) property value)
5323 (put-text-property (1- end) end property value)))
5324
5325(defun table--put-cell-justify-property (cell justify)
5326 "Put cell's justify property."
5327 (table--put-property cell 'table-justify justify))
5328
5329(defun table--put-cell-valign-property (cell valign)
5330 "Put cell's vertical alignment property."
5331 (table--put-property cell 'table-valign valign))
5332
5333(defun table--point-entered-cell-function (&optional old-point new-point)
5334 "Point has entered a cell.
5335Refresh the menu bar."
250495a7
MR
5336 ;; Avoid calling point-motion-hooks recursively.
5337 (let ((inhibit-point-motion-hooks t))
5338 (unless table-cell-entered-state
5339 (setq table-cell-entered-state t)
5340 (setq table-mode-indicator t)
5341 (force-mode-line-update)
5342 (table--warn-incompatibility)
5343 (run-hooks 'table-point-entered-cell-hook))))
238240c9
RS
5344
5345(defun table--point-left-cell-function (&optional old-point new-point)
5346 "Point has left a cell.
5347Refresh the menu bar."
250495a7
MR
5348 ;; Avoid calling point-motion-hooks recursively.
5349 (let ((inhibit-point-motion-hooks t))
5350 (when table-cell-entered-state
5351 (setq table-cell-entered-state nil)
5352 (setq table-mode-indicator nil)
5353 (force-mode-line-update)
5354 (run-hooks 'table-point-left-cell-hook))))
238240c9
RS
5355
5356(defun table--warn-incompatibility ()
5357 "If called from interactive operation warn the know incompatibilities.
5358This feature is disabled when `table-disable-incompatibility-warning'
5359is non-nil. The warning is done only once per session for each item."
5360 (unless (and table-disable-incompatibility-warning
5361 (not (interactive-p)))
5362 (cond ((and (featurep 'xemacs)
5363 (not (get 'table-disable-incompatibility-warning 'xemacs)))
5364 (put 'table-disable-incompatibility-warning 'xemacs t)
047ecb4e 5365 (display-warning 'table
238240c9
RS
5366 "
5367*** Warning ***
5368
5369Table package mostly works fine under XEmacs, however, due to the
5370peculiar implementation of text property under XEmacs, cell splitting
5371and any undo operation of table exhibit some known strange problems,
5372such that a border characters dissolve into adjacent cells. Please be
5373aware of this.
5374
5375"
047ecb4e 5376 :warning))
238240c9
RS
5377 ((and (boundp 'flyspell-mode)
5378 flyspell-mode
5379 (not (get 'table-disable-incompatibility-warning 'flyspell)))
5380 (put 'table-disable-incompatibility-warning 'flyspell t)
047ecb4e 5381 (display-warning 'table
238240c9
RS
5382 "
5383*** Warning ***
5384
5385Flyspell minor mode is known to be incompatible with this table
5386package. The flyspell version 1.5d at http://kaolin.unice.fr/~serrano
5387works better than the previous versions however not fully compatible.
5388
5389"
047ecb4e 5390 :warning))
238240c9
RS
5391 )))
5392
5393(defun table--cell-blank-str (&optional n)
5394 "Return blank table cell string of length N."
ec85195e 5395 (let ((str (make-string (or n 1) ?\s)))
238240c9
RS
5396 (table--put-cell-content-property 0 (length str) str)
5397 str))
5398
5399(defun table--remove-eol-spaces (beg end &optional bol force)
5400 "Remove spaces at the end of each line in the BEG END region of the current buffer.
5401When optional BOL is non-nil spaces at the beginning of line are
5402removed. When optional FORCE is non-nil removal operation is enforced
5403even when point is within the removal area."
5404 (if (> beg end)
5405 (let ((tmp beg))
5406 (setq beg end)
5407 (setq end tmp)))
5408 (let ((saved-point (point-marker))
5409 (end-marker (copy-marker end)))
5410 (save-excursion
5411 (goto-char beg)
5412 (while (if bol (re-search-forward "^\\( +\\)" end-marker t)
5413 (re-search-forward "\\( +\\)$" end-marker t))
5414 ;; avoid removal that causes the saved point to lose its location.
5415 (if (and (null bol)
5416 (<= (match-beginning 1) saved-point)
5417 (<= saved-point (match-end 1))
5418 (not force))
5419 (delete-region saved-point (match-end 1))
5420 (delete-region (match-beginning 1) (match-end 1)))))
5421 (set-marker saved-point nil)
5422 (set-marker end-marker nil)))
5423
5424(defun table--fill-region (beg end &optional col justify)
5425 "Fill paragraphs in table cell cache.
5426Current buffer must already be set to the cache buffer."
5427 (let ((fill-column (or col table-cell-info-width))
5428 (fill-prefix nil)
5429 (enable-kinsoku nil)
5430 (adaptive-fill-mode nil)
5431 (marker-beg (copy-marker beg))
5432 (marker-end (copy-marker end))
5433 (marker-point (point-marker)))
5434 (setq justify (or justify table-cell-info-justify))
5435 (and justify
5436 (not (eq justify 'left))
5437 (not (featurep 'xemacs))
5438 (set-marker-insertion-type marker-point t))
5439 (table--remove-eol-spaces (point-min) (point-max))
5440 (if table-fixed-width-mode
5441 (table--fill-region-strictly marker-beg marker-end)
5442 (let ((paragraph-start table-paragraph-start))
5443 (fill-region marker-beg marker-end justify nil t)))
5444 (goto-char marker-point)
5445 (set-marker marker-beg nil)
5446 (set-marker marker-end nil)
5447 (set-marker marker-point nil)))
5448
5449(defun table--fill-region-strictly (beg end)
5450 "Fill region strictly so that no line exceeds fill-column.
5451When a word exceeds fill-column the word is chopped into pieces. The
5452chopped location is indicated with table-word-continuation-char."
5453 (or (and (markerp beg) (markerp end))
5454 (error "markerp"))
5455 (if (< fill-column 2)
5456 (setq fill-column 2))
5457 ;; first remove all continuation characters.
5458 (goto-char beg)
5459 (while (re-search-forward (concat
5460 (format "[^%c ]\\(" table-word-continuation-char)
5461 (regexp-quote (char-to-string table-word-continuation-char))
5462 "\\s +\\)")
5463 end t)
5464 (delete-region (match-beginning 1) (match-end 1)))
5465 ;; then fill as normal
5466 (let ((paragraph-start table-paragraph-start))
5467 (fill-region beg end nil nil t))
5468 ;; now fix up
5469 (goto-char beg)
5470 (while (let ((col (move-to-column fill-column t)))
5471 (cond
5472 ((and (<= col fill-column)
5473 (looking-at " *$"))
5474 (delete-region (match-beginning 0) (match-end 0))
5475 (and (zerop (forward-line 1))
5476 (< (point) end)))
5477 (t (forward-char -1)
ec85195e 5478 (insert-before-markers (if (equal (char-before) ?\s) ?\s table-word-continuation-char)
238240c9
RS
5479 "\n")
5480 t)))))
5481
5482(defun table--goto-coordinate (coordinate &optional no-extension no-tab-expansion)
5483 "Move point to the given COORDINATE and return the location.
5484When optional NO-EXTENSION is non-nil and the specified coordinate is
5485not reachable returns nil otherwise the blanks are added if necessary
5486to achieve the goal coordinate and returns the goal point. It
5487intentionally does not preserve the original point in case it fails
5488achieving the goal. When optional NO-TAB-EXPANSION is non-nil and the
5489goad happens to be in a tab character the tab is not expanded but the
5490goal ends at the beginning of tab."
5491 (if (or (null coordinate)
5492 (< (car coordinate) 0)
5493 (< (cdr coordinate) 0)) nil
5494 (goto-char (point-min))
5495 (let ((x (car coordinate))
5496 (more-lines (forward-line (cdr coordinate))))
5497 (catch 'exit
5498 (if (zerop (current-column)) nil
5499 (if no-extension
5500 (progn
5501 (move-to-column x)
5502 (throw 'exit nil))
5503 (setq more-lines (1+ more-lines))))
5504 (if (zerop more-lines) nil
5505 (newline more-lines))
5506 (if no-extension
5507 (if (/= (move-to-column x) x)
5508 (if (> (move-to-column x) x)
5509 (if no-tab-expansion
5510 (progn
5511 (while (> (move-to-column x) x)
5512 (setq x (1- x)))
5513 (point))
5514 (throw 'exit (move-to-column x t)))
5515 (throw 'exit nil)))
5516 (move-to-column x t))
5517 (point)))))
5518
5519(defun table--copy-coordinate (coord)
5520 "Copy coordinate in a new cons cell."
5521 (cons (car coord) (cdr coord)))
5522
5523(defun table--get-coordinate (&optional where)
5524 "Return the coordinate of point in current buffer.
5525When optional WHERE is given it returns the coordinate of that
5526location instead of point in the current buffer. It does not move the
5527point"
5528 (save-excursion
5529 (if where (goto-char where))
5530 (cons (current-column)
5531 (table--current-line))))
5532
5533(defun table--current-line (&optional location)
5534 "Return zero based line count of current line or if non-nil LOCATION line."
5535 (save-excursion
5536 (if location (goto-char location))
5537 (beginning-of-line)
5538 (count-lines (point-min) (point))))
5539
5540(defun table--transcoord-table-to-cache (&optional coordinate)
5541 "Transpose COORDINATE from table coordinate system to cache coordinate system.
5542When COORDINATE is omitted or nil the point in current buffer is assumed in place."
5543 (table--offset-coordinate
5544 (or coordinate (table--get-coordinate))
5545 table-cell-info-lu-coordinate
5546 'negative))
5547
5548(defun table--transcoord-cache-to-table (&optional coordinate)
5549 "Transpose COORDINATE from cache coordinate system to table coordinate system.
5550When COORDINATE is omitted or nil the point in current buffer is assumed in place."
5551 (table--offset-coordinate
5552 (or coordinate (table--get-coordinate))
5553 table-cell-info-lu-coordinate))
5554
5555(defun table--offset-coordinate (coordinate offset &optional negative)
5556 "Return the offseted COORDINATE by OFFSET.
5557When optional NEGATIVE is non-nil offsetting direction is negative."
5558 (cons (if negative (- (car coordinate) (car offset))
5559 (+ (car coordinate) (car offset)))
5560 (if negative (- (cdr coordinate) (cdr offset))
5561 (+ (cdr coordinate) (cdr offset)))))
5562
5563(defun table--char-in-str-at-column (str column)
5564 "Return the character in STR at COLUMN location.
5565When COLUMN is out of range it returns null character."
5566 (let ((idx (table--str-index-at-column str column)))
5567 (if idx (aref str idx)
5568 ?\0)))
5569
5570(defun table--str-index-at-column (str column)
5571 "Return the character index in STR that corresponds to COLUMN location.
5572It returns COLUMN unless STR contains some wide characters."
5573 (let ((col 0)
5574 (idx 0)
5575 (len (length str)))
5576 (while (and (< col column) (< idx len))
5577 (setq col (+ col (char-width (aref str idx))))
5578 (setq idx (1+ idx)))
5579 (if (< idx len)
5580 idx
5581 nil)))
5582
5583(defun table--set-timer (seconds func args)
5584 "Generic wrapper for setting up a timer."
5585 (if (featurep 'xemacs)
5586 ;; the picky xemacs refuses to accept zero
5587 (add-timeout (if (zerop seconds) 0.01 seconds) func args nil)
5588 ;;(run-at-time seconds nil func args)))
5589 ;; somehow run-at-time causes strange problem under Emacs 20.7
5590 ;; this problem does not show up under Emacs 21.0.90
5591 (run-with-idle-timer seconds nil func args)))
5592
5593(defun table--cancel-timer (timer)
5594 "Generic wrapper for canceling a timer."
5595 (if (featurep 'xemacs)
5596 (disable-timeout timer)
5597 (cancel-timer timer)))
5598
5599(defun table--get-last-command ()
5600 "Generic wrapper for getting the real last command."
5601 (if (boundp 'real-last-command)
5602 real-last-command
5603 last-command))
5604
5605(run-hooks 'table-load-hook)
5606
5607(provide 'table)
5608
5609;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5610;; Local Variables: ***
5611;; time-stamp-line-limit: 16 ***
5612;; time-stamp-start: ";; Revised:[ \t]+" ***
5613;; time-stamp-end: "$" ***
5614;; time-stamp-format: "%3a %3b %02d %:y %02H:%02M:%02S (%Z)" ***
5615;; End: ***
5616;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5617
951f97e6 5618;; arch-tag: 0d69b03e-aa5f-4e72-8806-5727217617e0
238240c9 5619;;; table.el ends here