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