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