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