initial commit
[emacs-init.git] / nxhtml / util / chartg.el
1 ;;; chartg.el --- Google charts (and maybe other)
2 ;;
3 ;; Author: Lennart Borgman (lennart O borgman A gmail O com)
4 ;; Created: 2008-04-06 Sun
5 (defconst chart:version "0.2") ;; Version:
6 ;; Last-Updated:
7 ;; URL:
8 ;; Keywords:
9 ;; Compatibility:
10 ;;
11 ;; Features that might be required by this library:
12 ;;
13 ;;   None
14 ;;
15 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
16 ;;
17 ;;; Commentary:
18 ;;
19 ;;
20 ;;
21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
22 ;;
23 ;;; Change log:
24 ;;
25 ;;
26 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27 ;;
28 ;; This program is free software; you can redistribute it and/or
29 ;; modify it under the terms of the GNU General Public License as
30 ;; published by the Free Software Foundation; either version 2, or
31 ;; (at your option) any later version.
32 ;;
33 ;; This program is distributed in the hope that it will be useful,
34 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
35 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36 ;; General Public License for more details.
37 ;;
38 ;; You should have received a copy of the GNU General Public License
39 ;; along with this program; see the file COPYING.  If not, write to
40 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
41 ;; Floor, Boston, MA 02110-1301, USA.
42 ;;
43 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
44 ;;
45 ;;; Code:
46
47 (eval-when-compile (require 'cl))
48
49 (defconst chartg-types
50   '((line-chartg-x  lc)
51     (line-chartg-xy lxy)
52     (line-chart    ls)
53
54     (bar-chartg-horizontal         bhs)
55     (bar-chartg-vertical           bvs)
56     (bar-chartg-horizontal-grouped bhg)
57     (bar-chartg-vertical-grouped   bvg)
58
59     (pie-2-dimensional p)
60     (pie-3-dimensional p3)
61
62     (venn-diagram v)
63     (scatter-plot s)
64
65     (radar-chart           r)
66     (radar-chartg-w-splines rs)
67
68     (geographical-map t)
69     (meter gom)))
70
71 (defconst chartg-types-keywords
72   (mapcar (lambda (rec)
73             (symbol-name (car rec)))
74           chartg-types))
75
76 (defvar chartg-mode-keywords-and-states
77   '(("Output-file:" (accept file-name))
78     ("Size:" (accept number))
79     ("Data:" (accept number))
80     ("Type:" (accept chartg-type))
81     ))
82
83 (defvar chartg-mode-keywords
84   (mapcar (lambda (rec)
85             (car rec))
86           chartg-mode-keywords-and-states))
87
88 ;; Fix-me: I started to implement a parser, but I think I will drop it
89 ;; and wait for Semantic to be easily available instead. Or just use
90 ;; Calc/Org Tables.
91
92 (defvar chartg-intermediate-states
93   '((end-or-label (or end-of-file label))
94     ))
95
96 (defvar chartg-extra-keywords-and-states
97   '(
98     ;;("Provider:")
99     ("Colors:")
100     ("Solid-fill:")
101     ("Linear-gradient:")
102     ("Linear-stripes:")
103     ("Chartg-title:" (and string end-or-label))
104     ("Legends:" (accept string))
105     ("Axis-types:")
106     ("Axis-labels:")
107     ("Axis-ranges:")
108     ("Axis-styles:")
109     ("Bar-thickness:")
110     ("Bar-chartg-zero-line:")
111     ("Bar-chartg-zero-line-2:")
112     ("Line-styles-1:")
113     ("Line-styles-2:")
114     ("Grid-lines:")
115     ("Shape-markers:")
116     ("Range-markers:")
117     ))
118
119 (defvar chartg-extra-keywords
120   (mapcar (lambda (rec)
121             (car rec))
122           chartg-extra-keywords-and-states))
123
124 (defvar chartg-raw-keywords-and-states
125   '(
126     ("Google-chartg-raw:" (accept string))
127     ))
128
129 (defvar chartg-raw-keywords
130   (mapcar (lambda (rec)
131             (car rec))
132           chartg-raw-keywords-and-states))
133
134 (defvar chartg-mode-keywords-re (regexp-opt chartg-mode-keywords))
135 (defvar chartg-extra-keywords-re (regexp-opt chartg-extra-keywords))
136 (defvar chartg-types-keywords-re (regexp-opt chartg-types-keywords))
137 (defvar chartg-raw-keywords-re (regexp-opt chartg-raw-keywords))
138
139 (defvar chartg-font-lock-keywords
140   `((,chartg-mode-keywords-re . font-lock-keyword-face)
141     (,chartg-extra-keywords-re . font-lock-variable-name-face)
142     (,chartg-types-keywords-re . font-lock-function-name-face)
143     (,chartg-raw-keywords-re . font-lock-preprocessor-face)
144     ))
145
146 (defvar chartg-font-lock-defaults
147   '(chartg-font-lock-keywords nil t))
148
149 (defvar chartg-mode-syntax-table
150   (let ((table (make-syntax-table)))
151     (modify-syntax-entry ?\n ">   " table)
152     (modify-syntax-entry ?\; "<   " table)
153     table))
154
155 (defun chartg-create (provider out-file size data type
156                               title legends &optional extras)
157   "Create a chart image.
158 PROVIDER is what to use for creating the chart. Currently only
159 `google' for Google's chart API is supported.
160
161 OUT-FILE is where the image goes.
162
163 SIZE is a cons cell with pixel width and height.
164
165 DATA is the data to draw the chart from. It is a list of data
166 sets where each data set has the form:
167
168   (list (list NUMBERS ...) (MIN . MAX)))
169
170 TYPE can be the following:
171
172 * Line charts
173
174   - lc: Line chart with only y values. Each dataset is a new
175     line.
176
177   - lxy: Line chart with both x and y values. For each line there
178     should be a pair of datasets, the first for x and the second
179     for y. If the x dataset just contains a single -1 then values
180     are evenly spaced along the x-axis.
181
182   - ls: Like above, but axis are not drawn.
183
184 * Bar charts:
185
186   - bhs: horizontal bars.
187   - bvs: vertical bars.
188   - bhg, bvg: dito grouped.
189
190 * Pie charts:
191
192   - cht=p: one dimensional
193   - cht=p3: three dimensional
194
195 * Venn diagrams
196
197   - cht=v: data should be specified as
198     * the first three values specify the relative sizes of three
199       circles, A, B, and C
200     * the fourth value specifies the area of A intersecting B
201     * the fifth value specifies the area of A intersecting C
202     * the sixth value specifies the area of B intersecting C
203     * the seventh value specifies the area of A intersecting B
204       intersecting C
205
206 * Scatter plots
207
208   - cht=s: Supply a pair of datasets, first for x and second for
209     y coordinates.
210
211 * Radar charts
212
213   - cht=r: straight lines.
214   - cht=rs: splines.
215
216     You will have to find out the format of the datasets
217     yourself, I don't understand it ;-)
218
219     Or perhaps mail google?
220
221 * Maps
222
223   - cht=t
224
225   together with
226
227   - chtm=AREA: AREA for provider `google' is currently one of
228     *  africa
229     * asia
230     * europe
231     * middle_east
232     * south_america
233     * usa
234     * world
235
236 * Meter
237
238   - cht=gom: A speed meter type meter. Takes a single value.
239
240 TITLE is a string to use as title.
241
242 LEGENDS is a list of labels to put on the data.
243
244 EXTRAS is a list of extra arguments with the form
245
246   (EXTRA-TYPE EXTRA-VALUE)
247
248 Where EXTRA-TYPE is the extra argument type and EXTRA-VALUE the
249 value. The following EXTRA-TYPEs are supported:
250
251 * COLORS: value is a list of colors corresponding to the list of
252   DATA. Each color have the format RRGGBB or RRGGBBTT where the
253   first form is the normal way to specify colors in rgb-format
254   and the second has an additional TT for transparence. TT=00
255   means completely transparent and TT=FF means completely opaque.
256
257 FILL-AREA are fill colors for data sets in line charts. It should
258 be a list
259
260   (list COLOR START-INDEX END-INDEX)
261
262 "
263   (message "(chartg-create %s %s %s %s %s %s %s" provider out-file size data type
264                               title legends)
265   (unless (symbolp type)
266     (error "Argument TYPE should be a symbol"))
267   (unless (assoc type chartg-types)
268     (error "Unknown chart type: %s" type))
269   (cond
270    ((eq provider 'google)
271     (let* ((g-type (nth 1 (assoc type chartg-types)))
272            (width  (car size))
273            (height (cdr size))
274            ;;(size-par (format "&chs=%sx%s" width height))
275            ;;
276            numbers
277            scales
278            colors-par
279            ;;
280            url
281            content
282            )
283       (setq url
284             (format
285              "http://chart.apis.google.com/chart?cht=%s&chs=%dx%d" g-type width height))
286       ;;(setq url (concat url size-par))
287       ;; Data and scales
288       (unless data
289         (error "No data"))
290       (dolist (rec data)
291         (let* ((rec-numbers (car rec))
292                (number-str
293                 (let (str)
294                   (dolist (num rec-numbers)
295                     (setq str
296                           (if (not str)
297                               (number-to-string num)
298                             (concat str "," (number-to-string num)))))
299                   str))
300                (rec-scale (cadr rec))
301                (rec-min  (car rec-scale))
302                (rec-max  (cdr rec-scale))
303                (scale-str (when rec-scale (format "%s,%s" rec-min rec-max)))
304                )
305           (if (not numbers)
306               (progn
307                 (setq numbers (concat "&chd=t:" number-str))
308                 (when (or scale-str
309                           (memq g-type '(p p3 gom)))
310                   (setq scales (concat "&chds=" scale-str))))
311             (setq numbers (concat numbers "|" number-str))
312             (when scale-str
313               (setq scales (concat scales "," scale-str))))))
314       (setq url (concat url numbers))
315       (when scales (setq url (concat url scales)))
316       ;; fix-me: encode the url
317       (when title (setq url (concat url "&chtt=" (url-hexify-string title))))
318       (when legends
319         (let ((url-legends (mapconcat 'url-hexify-string legends "|"))
320               (arg (if (memq g-type '(p p3 gom))
321                        "&chl="
322                      "&chdl=")))
323           (setq url (concat url arg url-legends))))
324       (dolist (extra extras)
325         (let ((extra-type (car extra))
326               (extra-value (cdr extra)))
327           (cond
328            ((eq extra-type 'GOOGLE-RAW)
329             (setq url (concat url extra-value)))
330            ((eq extra-type 'colors)
331             ;; Colors
332             (dolist (color extra-value)
333               (if (not colors-par)
334                   (setq colors-par (concat "&chco=" color))
335                 (setq colors-par (concat colors-par "," color))))
336             (when colors-par (setq url (concat url colors-par))))
337            (t (error "Unsupported extra type: %s" extra-type)))))
338
339       ;;(lwarn t :warning "url=%s" url)(top-level)
340       ;;(setq url (concat url "&chxt=y"))
341       (message "Sending %s" url)
342       (setq content
343             (with-current-buffer (url-retrieve-synchronously url)
344               (goto-char (point-min))
345               (if (search-forward "\n\n" nil t)
346                   (buffer-substring-no-properties (point) (point-max))
347                 (view-buffer-other-window (current-buffer))
348                 (error "Bad content"))))
349       (let* ((is-html (string-match-p "</body></html>" content))
350              (fname (progn
351                       (when is-html
352                         (setq out-file (concat (file-name-sans-extension out-file) ".html")))
353                       (expand-file-name out-file)
354                       ))
355              (do-it (or (not (file-exists-p fname))
356                         (y-or-n-p
357                          (concat "File " fname " exists. Replace it? "))))
358              (buf (find-buffer-visiting fname))
359              (this-window (selected-window)))
360         (when do-it
361           (when buf (kill-buffer buf))
362           (with-temp-file fname
363             (insert content))
364           (if (not is-html)
365               (view-file-other-window fname)
366             (chartg-show-last-error-file fname))
367           (select-window this-window)))))
368    (t (error "Unknown provider: %s" provider)))
369   )
370
371 (defun chartg-show-last-error-file (fname)
372   (interactive)
373   (with-output-to-temp-buffer (help-buffer)
374     (help-setup-xref (list #'chartg-show-last-error-file fname) (interactive-p))
375     (with-current-buffer (help-buffer)
376       (insert "Error, see ")
377       (insert-text-button "result error page"
378                           'action
379                           `(lambda (btn)
380                              (browse-url ,fname))))))
381
382 (defvar chartg-mode-map
383   (let ((map (make-sparse-keymap)))
384     (define-key map [(meta tab)] 'chartg-complete)
385     (define-key map [(control ?c) (control ?c)] 'chartg-make-chart)
386     map))
387
388 (defun chartg-missing-keywords ()
389   (let ((collection (copy-sequence chartg-mode-keywords)))
390     (save-excursion
391       (save-restriction
392         (widen)
393         (goto-char (point-min))
394         (while (re-search-forward chartg-mode-keywords-re nil t)
395           (setq collection
396                 (delete (match-string-no-properties 0)
397                         collection)))))
398     collection))
399
400 ;;;###autoload
401 (defun chartg-complete ()
402   (interactive)
403   (let* ((here (point))
404          (partial (when (looking-back (rx word-start
405                                           (optional ?\")
406                                           (0+ (any "[a-z]"))))
407                     (match-string-no-properties 0)))
408          (part-pos (if partial
409                        (match-beginning 0)
410                      (setq partial "")
411                      (point)))
412          (state (catch 'pos-state (chartg-get-state (point))))
413          (msg "No completions")
414          collection
415          all
416          prompt
417          res)
418     (when state
419       (cond
420        ((or (= (current-column) 0)
421             (equal state 'need-label))
422         (setq collection (append (chartg-missing-keywords)
423                                  chartg-extra-keywords
424                                  chartg-raw-keywords
425                                  nil))
426         (setq prompt "Label: "))
427        ((equal state '(accept number))
428         (setq res nil)
429         (setq msg (propertize "Needs a number here!"
430                               'face 'secondary-selection)))
431        ((equal state '(accept chartg-type))
432         (setq collection chartg-types-keywords)
433         (setq prompt "Chart type: "))
434        ((equal state '(accept file-name))
435         (setq res
436               (concat "\"" (read-file-name "Output-file: "
437                                            nil
438                                            ;; fix-me: handle partial
439                                            partial)
440                       "\""))))
441       (when collection
442         (let ((all (if partial
443                        (all-completions partial collection)
444                      collection)))
445           (setq res (when all
446                       (if (= (length all) 1)
447                           (car all)
448                         (completing-read prompt collection nil t partial)))))))
449     (if (not res)
450         (message "%s" msg)
451       (insert (substring res (length partial))))))
452
453
454 (defun chartg-get-state (want-pos-state)
455   (let* (par-output-file
456          par-provider
457          par-size
458          par-data par-data-temp
459          par-data-min par-data-max
460          par-type
461          par-title
462          par-legends
463          par-google-raw
464          (here (point))
465          token-before-pos
466          pos-state
467          (state 'need-label)
468          (problems
469           (catch 'problems
470             (save-restriction
471               ;;(widen)
472               (if want-pos-state
473                   (unless (re-search-backward chartg-mode-keywords-re nil t)
474                     (goto-char (point-min)))
475                 (goto-char (point-min)))
476               (let (this-keyword
477                     this-start
478                     this-end
479                     params
480                     token
481                     token-pos
482                     next-token
483                     found-labels
484                     current-label)
485                 (while (or token
486                            (progn
487                              (setq pos-state state)
488                              (setq token-before-pos (point))
489                              (condition-case err
490                                  (setq token (read (current-buffer)))
491                                (error
492                                 (if (eq (car err) 'end-of-file)
493                                     (unless (or (eq state 'need-label)
494                                                 (member '(quote |) state))
495                                       (throw 'problems (format "Unexpected end, state=%s" state)))
496                                   (throw 'problems
497                                          (error-message-string err)))))))
498                   (message "token=%s, label=%s, state=%s" token current-label state)
499                   (when (and want-pos-state
500                              (>= (point) want-pos-state))
501                     (when (= (point) want-pos-state)
502                       ;; right after item
503                       (setq pos-state nil))
504                     (goto-char here)
505                     (throw 'pos-state pos-state))
506                   (when (and (listp state) (memq 'number state))
507                     (unless (numberp token)
508                       (save-match-data
509                         (let ((token-str (format "%s" token)))
510                           (setq token-str (replace-regexp-in-string "\\([0-9]\\),\\([0-9]\\)" "\\1\\2" token-str))
511                           (when (string-match-p "^[0-9]+$" token-str)
512                             (setq token (string-to-number token-str)))))))
513                   (cond ;; state
514                    ;; Label
515                    ((eq state 'need-label)
516                     (unless (symbolp token)
517                       (throw 'problems (format "Expected label, got %s" token)))
518                     (unless (member (symbol-name token)
519                                     (append chartg-mode-keywords
520                                             chartg-extra-keywords
521                                             chartg-raw-keywords
522                                             nil))
523                       (throw 'problems (format "Unknown label %s" token)))
524                     (when (member (symbol-name token) found-labels)
525                       (throw 'problems (format "Label %s defined twice" token)))
526                     (setq current-label token)
527                     (setq found-labels (cons current-label found-labels))
528                     (setq token nil)
529                     ;;(setq state 'need-value)
530                     (case current-label
531                       ('Output-file:
532                        (setq state '(accept file-name)))
533                       ('Size:
534                        (setq state '(accept number)))
535                       ('Data:
536                        (setq state '(accept number)))
537                       ('Type:
538                        (setq state '(accept chartg-type)))
539                       ('Chartg-title:
540                        (setq state '(accept string)))
541                       ('Legends:
542                        (setq state '(accept string)))
543                       ('Google-chartg-raw:
544                        (setq state '(accept string)))
545                       ))
546                     ;;;; Values
547                    ;; Alt
548                    ((equal state '(accept '| symbol))
549                     (if (eq '| token)
550                         (case current-label
551                           ('Legends:
552                            (setq token nil)
553                            (setq state '(accept string)))
554                           (t (error "internal error, current-label=%s, state=%s" current-label state)))
555                       (if (symbolp token)
556                           (progn
557                             ;;(setq token nil)
558                             (setq state 'need-label))
559                         (throw 'problems (format "Expected | or label, got %s" token)))))
560                    ;; Strings
561                    ((equal state '(accept string))
562                     (unless (stringp token)
563                       (throw 'problems "Expected string"))
564                     (case current-label
565                       ('Chartg-title:
566                        (setq par-title token)
567                        (setq token nil)
568                        (setq state 'need-label))
569                       ('Legends:
570                        (setq par-legends (cons token par-legends))
571                        (setq token nil)
572                        (setq state '(accept '| symbol)))
573                       ('Google-chartg-raw:
574                        (setq par-google-raw token)
575                        (setq token nil)
576                        (setq state 'need-label))
577                       (t (error "internal error, current-label=%s, state=%s" current-label state))))
578                    ;; Output file
579                    ((equal state '(accept file-name))
580                     (unless (stringp token)
581                       (throw 'problems "Expected file name string"))
582                     (assert (eq current-label 'Output-file:))
583                     (setq par-output-file token)
584                     (setq token nil)
585                     (setq state 'need-label))
586                    ;; Numbers
587                    ((equal state '(accept number))
588                     (unless (numberp token)
589                       (throw 'problems "Expected number"))
590                     (case current-label
591                       ('Size:
592                        (if (not par-size)
593                            (progn
594                              (setq par-size token)
595                              (setq token nil)
596                              (setq state '(accept number 'x 'X)))
597                          (setq par-size (cons par-size token))
598                          (setq token nil)
599                          (setq state 'need-label)))
600                       ('Data:
601                        ;;(assert (not par-data-temp))
602                        (setq par-data-temp (cons token par-data-temp))
603                        (setq par-data-min token)
604                        (setq par-data-max token)
605                        (setq token nil)
606                        (setq state '(accept number ', '| symbol))
607                        )
608                       (t (error "internal error, state=%s, current-label=%s" state current-label)))
609                     )
610                    ;; Numbers or |
611                    ((equal state '(accept number ', '| symbol))
612                     (if (numberp token)
613                         (progn
614                           (setq par-data-min (if par-data-min (min par-data-min token) token))
615                           (setq par-data-max (if par-data-max (max par-data-max token) token))
616                           (setq par-data-temp (cons token par-data-temp))
617                           (message "par-data-min/max=%s/%s, token=%s -- %s" par-data-min par-data-max token par-data-temp)
618                           (setq token nil))
619                       (if (eq ', token)
620                           (setq token nil)
621                         (if (or (eq '| token)
622                                 (symbolp token))
623                             (progn
624                               (unless par-data-temp
625                                 (throw 'problems "Empty data set"))
626                               (setq par-data (cons (list (reverse par-data-temp) (cons par-data-min par-data-max)) par-data))
627                               (setq par-data-temp nil)
628                               (setq par-data-min nil)
629                               (setq par-data-max nil)
630                               (if (not (eq '| token))
631                                   (setq state 'need-label)
632                                 (setq state '(accept number))
633                                 (setq token nil)))
634                           (throw 'problems "Expected | or EOF")
635                           ))))
636                    ;; Numbers or x/X
637                    ((equal state '(accept number 'x 'X))
638                     (assert (eq current-label 'Size:))
639                     (let ((is-n (numberp token))
640                           (is-x (memq token '(x X))))
641                       (unless (or is-n is-x)
642                         (throw 'problems "Expected X or number"))
643                       (if is-x
644                           (progn
645                             (setq token nil)
646                             (setq state '(accept number)))
647                         (setq par-size (cons par-size token))
648                         (setq token nil)
649                         (setq state 'need-label))))
650                    ;; Chart type
651                    ((equal state '(accept chartg-type))
652                     (setq par-type token)
653                     (unless (assoc par-type chartg-types)
654                       (throw 'problems (format "Unknown chart type: %s" par-type)))
655                     (setq token nil)
656                     (setq state 'need-label))
657                    (t (error "internal error, state=%s" state))))))
658             ;; fix-me here
659
660             nil)))
661     (when want-pos-state
662       (goto-char here)
663       (throw 'pos-state state))
664     (unless problems
665       (let ((missing-lab (chartg-missing-keywords)))
666         (when missing-lab
667           (setq problems (format "Missing required labels: %s" missing-lab)))))
668     (if problems
669         (let ((msg   (if (listp problems)
670                          (nth 1 problems)
671                        problems))
672               (where (if (listp problems)
673                          (nth 0 problems)
674                        token-before-pos)))
675           (goto-char where)
676           (skip-chars-forward " \t")
677           (error msg))
678       (goto-char here)
679       ;;(defun chartg-create (out-file provider size data type &rest extras)
680       (setq par-provider 'google)
681       (setq par-legends (nreverse par-legends))
682       (let ((extras nil))
683         (when par-google-raw
684           (setq extras (cons (cons 'GOOGLE-RAW par-google-raw) extras)))
685         (chartg-create par-provider par-output-file par-size
686                       par-data par-type par-title par-legends extras))
687       nil)))
688
689 ;;;###autoload
690 (defun chartg-make-chart ()
691   "Try to make a new chart.
692 If region is active then make a new chart from data in the
693 selected region.
694
695 Else if current buffer is in `chartg-mode' then do it from the
696 chart specifications in this buffer.  Otherwise create a new
697 buffer and initialize it with `chartg-mode'.
698
699 If the chart specifications are complete enough to make a chart
700 then do it and show the resulting chart image.  If not then tell
701 user what is missing.
702
703 NOTE: This is beta, no alpha code. It is not ready.
704
705 Below are some examples.  To test them mark an example and do
706
707   M-x chartg-make-chart
708
709 * Example, simple x-y chart:
710
711   Output-file: \"~/temp-chart.png\"
712   Size: 200 200
713   Data: 3 8 5 | 10 20 30
714   Type: line-chartg-xy
715
716 * Example, pie:
717
718   Output-file: \"~/temp-depression.png\"
719   Size: 400 200
720   Data:
721   2,160,000
722   3,110,000
723   1,510,000
724   73,600
725   775,000
726   726,000
727   8,180,000
728   419,000
729   Type: pie-3-dimensional
730   Chartg-title: \"Depression hits on Google\"
731   Legends:
732   \"SSRI\"
733   | \"Psychotherapy\"
734   | \"CBT\"
735   | \"IPT\"
736   | \"Psychoanalysis\"
737   | \"Mindfulness\"
738   | \"Meditation\"
739   | \"Exercise\"
740
741
742 * Example, pie:
743
744   Output-file: \"~/temp-panic.png\"
745   Size: 400 200
746   Data:
747   979,000
748   969,000
749   500,000
750   71,900
751   193,000
752   154,000
753   2,500,000
754   9,310,000
755   Type: pie-3-dimensional
756   Chartg-title: \"Depression hits on Google\"
757   Legends:
758   \"SSRI\"
759   | \"Psychotherapy\"
760   | \"CBT\"
761   | \"IPT\"
762   | \"Psychoanalysis\"
763   | \"Mindfulness\"
764   | \"Meditation\"
765   | \"Exercise\"
766
767
768 * Example using raw:
769
770   Output-file: \"~/temp-chartg-slipsen-kostar.png\"
771   Size: 400 130
772   Data: 300 1000 30000
773   Type: bar-chartg-horizontal
774   Chartg-title: \"Vad killen i slips tjänar jämfört med dig och mig\"
775   Google-chartg-raw: \"&chds=0,30000&chco=00cd00|ff4500|483d8b&chxt=y,x&chxl=0:|Killen+i+slips|Partiledarna|Du+och+jag&chf=bg,s,ffd700\"
776
777
778 "
779   (interactive)
780   (if mark-active
781       (let* ((rb (region-beginning))
782              (re (region-end))
783              (data (buffer-substring-no-properties rb re))
784              (buf (generate-new-buffer "*Chart from region*")))
785         (switch-to-buffer buf)
786         (insert data)
787         (chartg-mode))
788     (unless (eq major-mode 'chartg-mode)
789       (switch-to-buffer (generate-new-buffer "*Chart*"))
790       (chartg-mode)))
791   (chartg-get-state nil))
792
793 ;; (defun chartg-from-region (min max)
794 ;;   "Try to make a new chart from data in selected region.
795 ;; See `chartg-mode' for examples you can test with this function."
796 ;;   (interactive "r")
797 ;;   (unless mark-active (error "No region selected"))
798 ;;   (let* ((rb (region-beginning))
799 ;;          (re (region-end))
800 ;;          (data (buffer-substring-no-properties rb re))
801 ;;          (buf (generate-new-buffer "*Chart from region*")))
802 ;;     (switch-to-buffer buf)
803 ;;     (insert data)
804 ;;     (chartg-mode)
805 ;;     (chartg-get-state nil)))
806
807 (define-derived-mode chartg-mode fundamental-mode "Chart"
808   "Mode for specifying charts.
809 \\{chartg-mode-map}
810
811 To make a chart see `chartg-make-chart'.
812
813 "
814   (set (make-local-variable 'font-lock-defaults) chartg-font-lock-defaults)
815   (set (make-local-variable 'comment-start) ";")
816   ;; Look within the line for a ; following an even number of backslashes
817   ;; after either a non-backslash or the line beginning.
818   (set (make-local-variable 'comment-start-skip)
819        "\\(\\(^\\|[^\\\\\n]\\)\\(\\\\\\\\\\)*\\);+ *")
820   ;; Font lock mode uses this only when it KNOWS a comment is starting.
821   (set (make-local-variable 'font-lock-comment-start-skip) ";+ *")
822   (set (make-local-variable 'comment-add) 1) ;default to `;;' in comment-region
823   (set (make-local-variable 'comment-column) 40)
824   ;; Don't get confused by `;' in doc strings when paragraph-filling.
825   (set (make-local-variable 'comment-use-global-state) t)
826   (set-syntax-table chartg-mode-syntax-table)
827   (when (looking-at (rx buffer-start (0+ whitespace) buffer-end))
828     (insert ";; Type C-c C-c to make a chart, M-Tab to complete\n"))
829   (let ((missing (chartg-missing-keywords)))
830     (when missing
831       (save-excursion
832         (goto-char (point-max))
833         (dolist (miss missing)
834           (insert "\n" miss " "))))))
835
836 ;; Tests
837 ;;(chartg-create 'google "temp.png" '(200 . 150) '(((90 70) . nil)) 'pie-3-dimensional "test title" nil '((colors "FFFFFF" "00FF00")))
838
839 ;; Fix-me
840 (add-to-list 'auto-mode-alist '("\\.mx-chart\\'" . chartg-mode))
841
842 (provide 'chartg)
843 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
844 ;;; chartg.el ends here