Texmacs/Mogan Note
Note
graphics-utils.scm
1;;These abbreviations are very convenient
2;;to use. A nice naming scheme is :
3;;
4;; -> b=bool ;
5;; -> i=integer ;
6;; -> f=float ;
7;; -> sy=symbol ;
8;; -> s=string ;
9;; -> o=Scheme object ;
10;; -> p=path.
11;; -> t=tree.
12;;
13;; One can add the missing ones on demand.
14(tm-define f2s float->string)
15(tm-define s2f string->float)
16(tm-define sy2s symbol->string)
17(tm-define s2sy string->symbol)
18(tm-define o2s object->string)
19(tm-define s2o string->object)
20(tm-define t2o tree->object)
21(tm-define o2t object->tree)
graphics-single.scm
editing routines for single graphical objects.
graphics-env.scm
routines for managing the graphical context.
current-x在此定义。
graphics-object.scm
routines for managing the graphical object.
研究图形移动功能
在edit_move函数中有下面一段
1(cond ((== (cadr mode) 'move)
2 (sketch-transform
3 (group-translate (- x group-old-x)
4 (- y group-old-y))))
其中sketch-transform的定义为
1(tm-define (sketch-transform opn)
2 (set! the-sketch (map opn the-sketch))
3 (set! current-obj
4 (if (graphics-group-mode? (graphics-mode)) '(nothing) #f))
5 (set! current-path #f)
6 (graphics-decorations-update))
而group-translate定义为:
1(define (group-translate x y)
2 (lambda (o)
3 (traverse-transform o (translate-point x y))))
其中用到了
1(define (traverse-transform o opn)
2 (define (traverse o)
3 (opn (if (pair? o) (map traverse o) o)))
4 (traverse o))
5
6(define (translate-point x y)
7 (lambda (o)
8 (if (match? o '(point :%2))
9 `(point ,(f2s (+ x (s2f (cadr o)))) ,(f2s (+ y (s2f (caddr o)))))
10 o)))
研究测试相关代码
tm-ref
1(define-public (tm-ref t . l)
2 (and (tm? t)
3 (with r (select t l)
4 (and (nnull? r) (car r)))))
tm?
1 tmscm_install_procedure ("tm?", contentP, 1, 0, 0);
1tmscm
2contentP (tmscm t) {
3 bool b= tmscm_is_content (t);
4 return bool_to_tmscm (b);
5}
1bool
2tmscm_is_content (tmscm p) {
3 if (tmscm_is_string (p) || tmscm_is_tree (p)) return true;
4 else if (!tmscm_is_pair (p) || !tmscm_is_symbol (tmscm_car (p))) return false;
5 else {
6 for (p= tmscm_cdr (p); !tmscm_is_null (p); p= tmscm_cdr (p))
7 if (!tmscm_is_pair (p) || !tmscm_is_content (tmscm_car (p))) return false;
8 return true;
9 }
10}
因此tm-ref等代码实际上应用于tree数据结构。tm?
就是用来检测这个的。例如:
1Scheme] (tm? '(point 1 2))
2
3#f
4Scheme] (tm? (stree->tree '(point 1 2)))
5
6#t
之后tm-ref可以取出列表中除了car的第i个元素。
1Scheme] (tm-ref (stree->tree '(1 2 3 4)) 0)
2
32
研究宏包
为什么有的包已经引入,有的需要作为宏包引入呢? init中会初始化一些包。
SRFI
列表长度length
1 (assuming (style-has? "std-markup-dtd")
2 (with u '(arrow-with-text arrow-with-text*)
3 (with l (list-filter u (lambda (s) (style-has? (symbol->string s))))
4 (for (tag (sort l symbol<=?))
5 ((eval (upcase-first (symbol->string tag)))
6 (import-from (graphics graphics-markup))
7 (graphics-set-mode `(edit ,tag))))))
8 (with u (list-difference gr-tags-user '(arrow-with-text arrow-with-text*))
9 (with l (list-filter u (lambda (s) (style-has? (symbol->string s))))
10 (assuming (nnull? l)
11 ---
12 (for (tag (sort l symbol<=?))
13 ((eval (upcase-first (symbol->string tag)))
14 (import-from (graphics graphics-markup))
15 (graphics-set-mode `(edit ,tag))))))))
23_15
1if (N(p) == 0)
2 typeset_dynamic (tree (ERROR, "bad text-at"), ip);
3 else {
4 SI ox= (SI) p[0], oy= (SI) p[1], axis= (b->h() >> 1), x= ox, y= oy;
5 if (halign == "left") x -= b->x1;
6 else if (halign == "center") x -= ((b->x1 + b->x2) >> 1);
7 else if (halign == "right") x -= b->x2;
8 if (valign == "bottom") y -= b->y1;
9 else if (valign == "axis") {
10 axis= env->fn->yfrac - b->y1;
11 y -= env->fn->yfrac;
12 }
13 else if (valign == "center") y -= ((b->y1 + b->y2) >> 1);
14 else if (valign == "top") y -= b->y2;
15 SI snap= env->get_length (TEXT_AT_SNAPPING);
16 print (text_at_box (ip, b, x, y, ox - x, oy - y, axis, snap));
17 SI pad = env->text_at_repulse;
18 if (pad >= 0)
19 env->white_zones << rectangle (x + b->x1 - pad, y + b->y1 - pad,
20 x + b->x2 + pad, y + b->y2 + pad);
21 }
在这里设置了偏移量。
1(tm-define (edit_left-button mode x y)
2 (:require (== mode 'edit))
3 (:state graphics-state)
4 (display "graphics-single 377\n")
5 (display* sticky-point "\n")
6 (display* (current-in? (graphical-text-tag-list)) "\n")
7 (set-texmacs-pointer 'graphics-cross)
8 (cond (sticky-point
9 (if (current-in? (graphical-text-tag-list))
10 (object_commit)
11 (next-point)))
12 ((and (current-in? (graphical-text-tag-list))
13 (== (car (graphics-mode)) 'edit)
14 (graphical-contains-text-tag? (cadr (graphics-mode)))
15 (not (graphical-contains-curve-tag? (cadr (graphics-mode))))
16 (pointer-inside-graphical-text?))
17 (set-texmacs-pointer 'text-arrow)
18 (go-to (car (select-first (s2f current-x) (s2f current-y)))))
19 (else
20 (edit-insert x y)
21 (display* "HERE\n")))
22 (set! previous-leftclick `(point ,current-x ,current-y)))
进入edit-insert
1(define (edit-insert x y)
2 (edit-clean-up)
3 (object_create (cadr (graphics-mode)) x y))
进入object_create
1(tm-define (object_create tag x y)
2 (texmacs-error "object-create" "invalid tag"))
3
4(tm-define (object_create tag x y)
5 (:require (== tag 'point))
6 (object-set! `(point ,x ,y) 'new))
7
8(tm-define (object_create tag x y)
9 (:require (or (in? tag gr-tags-curves) (in? tag gr-tags-user)))
10 (with o (graphics-enrich `(,tag (point ,x ,y) (point ,x ,y)))
11 (graphics-store-state 'start-create)
12 (set! current-point-no 1)
13 (object-set! o 'checkout)
14 (graphics-store-state #f)))
15
16(tm-define (object_create tag x y)
17 (:require (graphical-text-tag? tag))
18 (with long? (graphical-long-text-tag? tag)
19 (object-set! `(,tag ,(if long? `(document "") "") (point ,x ,y)) 'new)
20 (and-with d (path->tree (cDr (cursor-path)))
21 (when (tree-func? d 'document)
22 (tree-go-to d 0 :start)))))
得到对应的重载为
1(tm-define (object_create tag x y)
2 (:require (graphical-text-tag? tag))
3 (display* "HERE\n")
4 (with long? (graphical-long-text-tag? tag)
5 (object-set! `(,tag ,(if long? `(document "") "") (point ,x ,y)) 'new)
6 (and-with d (path->tree (cDr (cursor-path)))
7 (when (tree-func? d 'document)
8 (tree-go-to d 0 :start)))))
进入了这一行(graphics-group-enrich-insert o)
1(tm-define (graphics-group-enrich-insert t)
2 (graphics-group-insert (graphics-enrich t)))
1(tm-define (graphics-enrich t)
2 (let* ((l1 (graphics-all-attributes))
3 (l2 (map gr-prefix l1))
4 (l3 (map graphics-get-property l2))
5 (tab (list->ahash-table (map cons l1 l3))))
6 (graphics-enrich-bis t "default" tab)))
1(tm-define (graphics-enrich-bis t id tab)
2 (set! tab (list->ahash-table (ahash-table->list tab)))
3 (ahash-remove! tab "gid")
4 (let* ((attrs (graphical-relevant-attributes t))
5 (sel (ahash-table-select tab attrs))
6 (l1 (cons (cons "gid" id) (ahash-table->list sel)))
7 (l2 (map (lambda (x) (list (car x) (cdr x))) l1)))
8 ;;(display* "l= " l2 "\n")
9 (graphics-enrich-sub t l2)))
1class concater_rep {
2 edit_env env; // the environment
3 array<line_item> a; // the line items
4 bool rigid; // when surely not wrappable
设置属性
1(tm-define (object-set-text-at-halign val)
2 (:argument val "Horizontal alignment")
3 (:check-mark "*" (object-test-property? "text-at-halign"))
4 (object-set-property "text-at-halign" val))
1(tm-define (object-set-property var val)
2 (and-with t (tree-innermost graphical-context?)
3 (object-set-property-bis t var val)))
1(define (object-set-property-bis t var val)
2 (cond ((tree-is? t :up 'with)
3 (with-set (tree-up t) var val 0))
4 ((!= val "default")
5 (tree-set! t `(with ,var ,val ,t)))))
菜单
1(tm-menu (text-at-halign-menu)
2 ("Center" (object-set-text-at-halign "default"))
3 ("Right" (object-set-text-at-halign "right"))
4 ("Left1111" (object-set-text-at-halign "left")))
1(tm-define-macro (tm-menu head . l)
2 (receive (opts body) (list-break l not-define-option?)
3 `(tm-define ,head ,@opts (menu-dynamic ,@body))))
1(tm-define-macro (menu-dynamic . l)
2 `($list ,@(map gui-make l)))
1(tm-define (gui-make x)
2 ;;(display* "x= " x "\n")
3 (cond ((symbol? x)
4 (cond ((== x '---) '$---)
5 ((== x '===) (gui-make '(glue #f #f 0 5)))
6 ((== x '======) (gui-make '(glue #f #f 0 15)))
7 ((== x '/) '$/)
8 ((== x '//) (gui-make '(glue #f #f 5 0)))
9 ((== x '///) (gui-make '(glue #f #f 15 0)))
10 ((== x '>>) (gui-make '(glue #t #f 5 0)))
11 ((== x '>>>) (gui-make '(glue #t #f 15 0)))
12 ((== x (string->symbol "|")) '$/)
13 (else
14 (texmacs-error "gui-make" "invalid menu item ~S" x))))
15 ((string? x) x)
16 ((and (pair? x) (ahash-ref gui-make-table (car x)))
17 (apply (car (ahash-ref gui-make-table (car x))) (list x)))
18 ((and (pair? x) (or (string? (car x)) (pair? (car x))))
19 `($> ,(gui-make (car x)) ,@(cdr x)))
20 (else
21 (texmacs-error "gui-make" "invalid menu item ~S" x))))
菜单中的勾选 可能是check-mark和object-test-property?
1(tm-define (object-set-text-at-halign val)
2 (:argument val "Horizontal alignment")
3 (:check-mark "*" (object-test-property? "text-at-halign"))
4 (object-set-property "text-at-halign" val))
1(define (object-test-property? var)
2 (lambda (val)
3 (if (== val "default") (set! val (tree->stree (get-init-tree var))))
4 (== (object-get-property var) val)))
下一步观察get-init-tree
使用了GLUE
1{
2 scm_name = "get-init-tree",
3 cpp_name = "get_init_value",
4 ret_type = "tree",
5 arg_list = {
6 "string"
7 }
8 },
1tree
2edit_typeset_rep::get_init_value (string var) {
3 if (init->contains (var)) {
4 tree t= init [var];
5 if (var == BG_COLOR && is_func (t, PATTERN)) t= env->exec (t);
6 return is_func (t, BACKUP, 2)? t[0]: t;
7 }
8 if (N(pre)==0) typeset_preamble ();
9 tree t= pre [var];
10 if (var == BG_COLOR && is_func (t, PATTERN)) t= env->exec (t);
11 return is_func (t, BACKUP, 2)? t[0]: t;
12}
查到一个叫init的hashmap
1class edit_typeset_rep: virtual public editor_rep {
2protected:
3 tree the_style; // document style
4 hashmap<path,hashmap<string,tree> > cur; // environment at different paths
5 hashmap<string,tree> stydef; // environment after styles
6 hashmap<string,tree> pre; // environment after styles and init
7 hashmap<string,tree> init; // environment changes w.r.t. style
1void
2edit_typeset_rep::set_init (hashmap<string,tree> H) {
3 init= hashmap<string,tree> (UNINIT);
4 add_init (H);
5}
6
7void
8edit_typeset_rep::add_init (hashmap<string,tree> H) {
9 init->join (H);
10 ::notify_assign (ttt, path(), subtree (et, rp));
11 notify_change (THE_ENVIRONMENT);
12}
在这里调用了set_init
1void
2edit_typeset_rep::set_data (new_data data) {
3 set_style (data->style);
4 set_init (data->init);
5 set_fin (data->fin);
6 set_ref (data->ref);
7 set_aux (data->aux);
8 set_att (data->att);
9 notify_page_change ();
10 add_init (data->init);
11 notify_change (THE_DECORATIONS);
12 typeset_invalidate_env ();
13 iterator<string> it = iterate (data->att);
14 while (it->busy()) {
15 string key= it->next ();
16 (void) call (string ("notify-set-attachment"),
17 buf->buf->name, key, data->att [key]);
18 }
19}
set_data
的调用位置是
1void
2set_buffer_data (url name, new_data data) {
3 array<url> vs= buffer_to_views (name);
4 for (int i=0; i<N(vs); i++) {
5 view_to_editor (vs[i]) -> set_data (data);
6 view_to_editor (vs[i]) -> init_update ();
7 }
8}
set_buffer_data
的调用位置是
1void
2set_buffer_tree (url name, tree doc) {
3 tm_buffer buf= concrete_buffer (name);
4 if (is_nil (buf)) {
5 insert_buffer (name);
6 buf= concrete_buffer (name);
7 tree body= detach_data (doc, buf->data);
8 set_document (buf->rp, body);
9 buf->buf->title= propose_title (buf->buf->title, name, body);
10 if (buf->data->project != "") {
11 url prj_name= head (name) * as_string (buf->data->project);
12 buf->prj= concrete_buffer_insist (prj_name);
13 }
14 }
15 else {
16 string old_title= buf->buf->title;
17 string old_project= buf->data->project->label;
18 tree body= detach_data (doc, buf->data);
19 assign (buf->rp, body);
20 set_buffer_data (name, buf->data);
21 buf->buf->title= propose_title (old_title, name, body);
22 if (buf->data->project != "" && buf->data->project != old_project) {
23 url prj_name= head (name) * as_string (buf->data->project);
24 buf->prj= concrete_buffer_insist (prj_name);
25 }
26 }
27 pretend_buffer_saved (name);
28}
其中set_buffer_data (name, buf->data);
而 函数第一行有tm_buffer buf= concrete_buffer (name);
1tm_buffer
2concrete_buffer (url name) {
3 int i, n= N(bufs);
4 for (i=0; i<n; i++)
5 if (bufs[i]->buf->name == name)
6 return bufs[i];
7 return nil_buffer ();
8}
下一步问题:buf中的data怎么创建
1class tm_buffer_rep {
2public:
3 new_buffer buf; // file related information
4 new_data data; // data associated to document
5 array<tm_view> vws; // views attached to buffer
6 tm_buffer prj; // buffer which corresponds to the project
7 path rp; // path to the document's root in the_et
8 link_repository lns; // global links
9 bool notify; // notify modifications to scheme
10
11 inline tm_buffer_rep (url name):
12 buf (name), data (),
13 vws (0), prj (NULL), rp (new_document ()), notify (false) {}
14
15 inline ~tm_buffer_rep () {
16 delete_document (rp); }
17
18 void attach_notifier ();
19 bool needs_to_be_saved ();
20 bool needs_to_be_autosaved ();
21};
疑似相关代码:
1url temp= get_current_view_safe ();
2 set_current_view (abstract_view (vw));
3 if (is_none (tm_init_buffer_file))
4 tm_init_buffer_file= "$TEXMACS_PATH/progs/init-buffer.scm";
5 if (is_none (my_init_buffer_file))
6 my_init_buffer_file= "$TEXMACS_HOME_PATH/progs/my-init-buffer.scm";
7 if (exists (tm_init_buffer_file)) exec_file (tm_init_buffer_file);
8 if (exists (my_init_buffer_file)) exec_file (my_init_buffer_file);
9 set_current_view (temp);
object-get-property
1(tm-define (object-get-property var)
2 (tree->stree (get-env-tree var)))
1{
2 scm_name = "get-env-tree",
3 cpp_name = "get_env_value",
4 ret_type = "tree",
5 arg_list = {
6 "string"
7 }
8 },
1tree
2edit_typeset_rep::get_env_value (string var, path p) {
3 typeset_exec_until (p);
4 tree t= cur[p][var];
5 return is_func (t, BACKUP, 2)? t[0]: t;
6}
7
8tree
9edit_typeset_rep::get_env_value (string var) {
10 /* FIXME: tp is wrong (and consequently, crashes TeXmacs)
11 * when we call this routine from inside the code which
12 * is triggered by a button, for example.
13 *
14 * Test: fire TeXmacs, then open a new Graphics, then click
15 * on the icon for going in spline mode. Then it crashes,
16 * because we call (get-env-tree) from inside the Scheme.
17 * If we call (get-env-tree-at ... (cDr (cursor-path))),
18 * then it works.
19 */
20 return get_env_value (var, tp);
21}