-
Notifications
You must be signed in to change notification settings - Fork 2
/
igo-editor.el
2520 lines (2184 loc) · 99.8 KB
/
igo-editor.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; igo-editor.el --- SGF(Go) Editor -*- lexical-binding: t; -*-
;; Copyright (C) 2020 AKIYAMA Kouhei
;; Author: AKIYAMA Kouhei
;; Keywords: games
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'widget)
(require 'wid-edit)
(require 'igo-model)
(require 'igo-sgf-parser)
(require 'igo-view)
(defcustom igo-editor-move-point-on-click t
"If non-nil, move point to editor clicked."
:type '(boolean)
:group 'el-igo)
(defcustom igo-editor-status-bar-visible t
"If non-nil, status bar is displayed by default."
:type '(boolean)
:group 'el-igo)
;; Editor Management
(defun igo-edit-region (begin end)
(interactive "r")
(let ((editors (igo-editors-in begin end)))
(cond
;; create a new editor
((null editors)
(let ((editor (igo-editor begin end)))
(overlay-put (igo-editor-overlay editor) 'evaporate t))) ;; auto delete
;; change begin and end
((= (length editors) 1)
(igo-editor-set-region (car editors) begin end)
(igo-editor-update (car editors)))
;; (>= (length editors) 2)
(t
(error "Multiple editors already exist.")))))
(defun igo-editor-at (&optional pos)
(if (null pos)
(setq pos (point)))
(or
(seq-some (lambda (ov) (overlay-get ov 'igo-editor)) (overlays-at pos))
(seq-some (lambda (ov) (overlay-get ov 'igo-editor)) (overlays-at (1- pos)))
(seq-some (lambda (ov) (overlay-get ov 'igo-editor)) (overlays-in (1- pos) (1+ pos)))))
(defun igo-editor-at-input ()
(if (or (mouse-event-p last-input-event)
(memq (event-basic-type last-input-event) '(wheel-up wheel-down)))
(let* ((mouse-pos (event-start last-input-event))
(window (posn-window mouse-pos))
(buffer (window-buffer window))
(pos (posn-point mouse-pos)))
(if igo-editor-move-point-on-click
(set-window-point window pos))
(with-current-buffer buffer
(igo-editor-at pos)))
(igo-editor-at (point))))
(defun igo-editors-in (begin end)
(delq nil (mapcar (lambda (ov) (overlay-get ov 'igo-editor))
(overlays-in begin end))))
;;
;; Editor Overlay Object
;;
(defun igo-editor (begin end &optional buffer front-advance rear-advance)
"Create a new editor object."
(let* ((ov (make-overlay begin end buffer front-advance rear-advance))
(editor (vector
ov ;;0:overlay
nil ;;1:game
nil ;;2:layout (includes board-view and iamge-scale)
nil ;;3:svg
(list nil) ;;4:image-input-map
nil ;;5:image
nil ;;6:buffer text last updated (see: update-buffer-text)
nil ;;7:last error
nil ;;8:text mode
nil ;;9:current mode
(list
:show-status-bar igo-editor-status-bar-visible
:show-branches t
:show-move-number nil
:show-last-move t
;;:rotate180 nil
:editable t
:allow-illegal-move nil
:move-opposite-color nil
:grid-interval nil
) ;;10:properties
(list
(cons 'keymap-change nil)
(cons 'text-mode nil)
(cons 'graphical-mode nil)
) ;;11:hooks
nil ;;12:copied node
)))
;;(message "make overlay %s" ov)
(overlay-put ov 'igo-editor editor)
;;(overlay-put-ov 'evaporate t)
(igo-editor-text-mode editor t)
(igo-editor-update editor)
editor))
;; Editor - Basic Accessors
(defun igo-editor-overlay (editor) (aref editor 0))
(defun igo-editor-game (editor) (aref editor 1))
(defun igo-editor-layout (editor) (aref editor 2))
(defun igo-editor-svg (editor) (aref editor 3))
(defun igo-editor-image-input-map (editor) (aref editor 4))
(defun igo-editor-image (editor) (aref editor 5))
(defun igo-editor-last-buffer-text (editor) (aref editor 6))
(defun igo-editor-last-error (editor) (aref editor 7))
(defun igo-editor-display-mode (editor) (aref editor 8))
(defun igo-editor-curr-mode (editor) (aref editor 9))
(defun igo-editor-properties (editor) (aref editor 10))
(defun igo-editor-event-hooks (editor) (aref editor 11))
(defun igo-editor-copied-node (editor) (aref editor 12))
(defun igo-editor-board-view (editor);;for compatibility
(if-let ((layout (igo-editor-layout editor)))
(igo-editor-layout-board-view layout)))
(defun igo-editor-image-scale (editor);;for compatibility
(if-let ((layout (igo-editor-layout editor)))
(igo-editor-layout-image-scale layout)
1.0))
;;(defun igo-editor--overlay-set (editor ov) (aset editor 0 ov))
(defun igo-editor--game-set (editor game) (aset editor 1 game))
(defun igo-editor--layout-set (editor layout) (aset editor 2 layout))
(defun igo-editor--svg-set (editor svg) (aset editor 3 svg))
;;(defun igo-editor--image-input-map-set (editor image) (aset editor 4 image))
(defun igo-editor--image-set (editor image) (aset editor 5 image))
(defun igo-editor--last-buffer-text-set (editor text) (aset editor 6 text))
(defun igo-editor--last-error-set (editor err) (aset editor 7 err))
(defun igo-editor--display-mode-set (editor dmode) (aset editor 8 dmode))
(defun igo-editor--curr-mode-set (editor mode) (aset editor 9 mode))
(defun igo-editor--properties-set (editor props) (aset editor 10 props))
;;(defun igo-editor--event-hooks-set (editor events) (aset editor 11 events))
(defun igo-editor--copied-node-set (editor node) (aset editor 12 node))
(defun igo-editor-begin (editor) (overlay-start (igo-editor-overlay editor)))
(defun igo-editor-end (editor) (overlay-end (igo-editor-overlay editor)))
(defun igo-editor-board (editor)
(if editor
(let ((game (igo-editor-game editor)))
(if game
(igo-game-board game)))))
(defun igo-editor-current-node (editor)
(if editor
(let ((game (igo-editor-game editor)))
(if game
(igo-game-current-node game)))))
(defun igo-editor-root-node (editor)
(if editor
(let ((game (igo-editor-game editor)))
(if game
(igo-game-root-node game)))))
;; Editor - Properties
(defun igo-editor-get-property (editor key)
(if editor
(plist-get (igo-editor-properties editor) key)))
(defun igo-editor-set-property (editor key value)
(when editor
(igo-editor--properties-set
editor
(plist-put (igo-editor-properties editor) key value))
value))
(defun igo-editor-toggle-property (editor key)
(igo-editor-set-property
editor
key
(not (igo-editor-get-property editor key))))
;; Editor - Hooks
(defun igo-editor-add-hook (editor type fun)
(let ((event-hooks (assq type (igo-editor-event-hooks editor))))
(if event-hooks
(setcdr event-hooks (cons fun (cdr event-hooks))))))
(defun igo-editor-remove-hook (editor type fun)
(let ((event-hooks (assq type (igo-editor-event-hooks editor))))
(if event-hooks
(setcdr event-hooks (delete fun (cdr event-hooks))))))
(defun igo-editor-call-hooks (editor type &rest args)
(let ((event-hooks (assq type (igo-editor-event-hooks editor))))
(if event-hooks
;; Call hooks and return a list of return values
(mapcar (lambda (fun) (apply fun args)) (cdr event-hooks)))))
(defun igo-editor-call-hooks-until-t (editor type &rest args)
"Call hooks until t is returned."
(let ((hooks (cdr (assq type (igo-editor-event-hooks editor))))
value)
(while (and hooks (null (setq value (apply (car hooks) args))))
(setq hooks (cdr hooks)))
value))
;; Editor - Keymaps
(defun igo-editor-set-keymap (editor keymap)
;; Call hooks before change and do not change if t is returned from a hook.
;; igo-sgf-mode sets KEYMAP to local-map.
(if (not (igo-editor-call-hooks-until-t editor 'keymap-change editor keymap))
;; Set KEYMAP to overlay's keymap property
(let ((ov (igo-editor-overlay editor)))
(if ov
(overlay-put ov 'keymap keymap)))))
(defun igo-editor-self-insert-command ()
(interactive))
(defvar igo-editor-text-mode-map
(let ((km (make-sparse-keymap)))
(define-key km (kbd "C-c q") #'igo-editor-quit)
(define-key km (kbd "C-c g") #'igo-editor-graphical-mode)
(define-key km (kbd "C-c i") #'igo-editor-init-board)
km))
(defvar igo-editor-graphical-mode-map
(let ((km (make-sparse-keymap)))
(define-key km [remap self-insert-command] #'igo-editor-self-insert-command)
(define-key km "\C-m" 'igo-editor-self-insert-command)
;;
(define-key km (kbd "C-c q") #'igo-editor-quit)
(define-key km (kbd "C-x C-q") #'igo-editor-toggle-editable)
;; display mode
;;(define-key km "t" #'igo-editor-text-mode)
(define-key km (kbd "C-c g") #'igo-editor-text-mode)
;; navigation
(define-key km "a" #'igo-editor-first-node)
(define-key km "e" #'igo-editor-last-node)
(define-key km "b" #'igo-editor-previous-node)
(define-key km "f" #'igo-editor-next-node)
(define-key km "n" #'igo-editor-select-next-node)
(define-key km (kbd "M-f") #'igo-editor-next-fork)
(define-key km (kbd "M-b") #'igo-editor-previous-fork)
(define-key km [igo-editor-first mouse-1] #'igo-editor-first-node)
(define-key km [igo-editor-previous mouse-1] #'igo-editor-previous-node)
(define-key km [igo-editor-forward mouse-1] #'igo-editor-next-node)
(define-key km [igo-editor-last mouse-1] #'igo-editor-last-node)
;; editing mode
(define-key km "Q" #'igo-editor-move-mode)
(define-key km "F" #'igo-editor-free-edit-mode)
(define-key km "M" #'igo-editor-mark-edit-mode)
;; visibility
(define-key km (kbd "s s") #'igo-editor-toggle-status-bar)
(define-key km (kbd "s n") #'igo-editor-toggle-move-number)
(define-key km (kbd "s b") #'igo-editor-toggle-branch-text)
;; edit
(define-key km "c" #'igo-editor-edit-comment)
(define-key km "N" #'igo-editor-edit-move-number)
(define-key km "g" #'igo-editor-edit-game-info)
(define-key km (kbd "C-c i") #'igo-editor-init-board)
;; export
(define-key km (kbd "x i") #'igo-editor-export-image)
;; menu
(define-key km [igo-editor-menu mouse-1] #'igo-editor-main-menu)
km))
(defvar igo-editor-move-mode-map
(let ((km (make-sparse-keymap)))
(set-keymap-parent km igo-editor-graphical-mode-map)
(define-key km "P" #'igo-editor-pass)
(define-key km "p" #'igo-editor-put-stone)
(define-key km "$" #'igo-editor-make-current-node-root)
(define-key km "I" #'igo-editor-toggle-allow-illegal-move)
(define-key km "R" #'igo-editor-toggle-move-opposite-color)
(define-key km [igo-editor-pass mouse-1] #'igo-editor-pass)
(define-key km [igo-editor-pass mouse-3] #'igo-editor-pass-click-r)
(define-key km [igo-grid mouse-1] #'igo-editor-move-mode-board-click)
(define-key km [igo-grid mouse-3] #'igo-editor-move-mode-board-click-r)
(define-key km [igo-grid wheel-up] #'igo-editor-previous-node)
(define-key km [igo-grid wheel-down] #'igo-editor-next-node)
(define-key km [igo-setup-nodes-area mouse-1] #'igo-editor-setup-nodes-area-click)
(define-key km [igo-setup-nodes-area mouse-3] #'igo-editor-setup-nodes-area-click-r)
km))
(defvar igo-editor-free-edit-mode-map
(let ((km (make-sparse-keymap)))
(set-keymap-parent km igo-editor-graphical-mode-map)
(define-key km [igo-grid down-mouse-1] #'igo-editor-free-edit-board-down)
(define-key km [igo-grid mouse-3] #'igo-editor-free-edit-board-click-r)
(define-key km [igo-editor-free-edit-quit mouse-1] #'igo-editor-move-mode)
(define-key km [igo-editor-free-edit-black mouse-1] #'igo-editor-free-edit-black)
(define-key km [igo-editor-free-edit-white mouse-1] #'igo-editor-free-edit-white)
(define-key km [igo-editor-free-edit-empty mouse-1] #'igo-editor-free-edit-empty)
(define-key km [igo-editor-free-edit-turn mouse-1] #'igo-editor-free-edit-toggle-turn)
(define-key km "Q" #'igo-editor-move-mode)
(define-key km "B" #'igo-editor-free-edit-black)
(define-key km "W" #'igo-editor-free-edit-white)
(define-key km "E" #'igo-editor-free-edit-empty)
(define-key km "T" #'igo-editor-free-edit-toggle-turn)
(define-key km "p" #'igo-editor-free-edit-put)
km))
(defvar igo-editor-mark-edit-mode-map
(let ((km (make-sparse-keymap)))
(set-keymap-parent km igo-editor-graphical-mode-map)
(define-key km [igo-grid down-mouse-1] #'igo-editor-mark-edit-board-down)
;;(define-key km [igo-grid mouse-3] #'igo-editor-mark-edit-board-click-r)
(define-key km [igo-editor-mark-edit-quit mouse-1] #'igo-editor-move-mode)
(define-key km [igo-editor-mark-edit-cross mouse-1] #'igo-editor-mark-edit-cross)
(define-key km [igo-editor-mark-edit-circle mouse-1] #'igo-editor-mark-edit-circle)
(define-key km [igo-editor-mark-edit-square mouse-1] #'igo-editor-mark-edit-square)
(define-key km [igo-editor-mark-edit-triangle mouse-1] #'igo-editor-mark-edit-triangle)
(define-key km [igo-editor-mark-edit-text mouse-1] #'igo-editor-mark-edit-text)
(define-key km [igo-editor-mark-edit-del mouse-1] #'igo-editor-mark-edit-del)
(define-key km "Q" #'igo-editor-move-mode)
(define-key km "X" #'igo-editor-mark-edit-cross)
(define-key km "O" #'igo-editor-mark-edit-circle)
(define-key km "S" #'igo-editor-mark-edit-square)
(define-key km "T" #'igo-editor-mark-edit-triangle)
(define-key km "E" #'igo-editor-mark-edit-text)
(define-key km "D" #'igo-editor-mark-edit-del)
(define-key km "p" #'igo-editor-mark-edit-put)
km))
(defvar igo-editor-main-menu-map
'(keymap "Main Menu"
(view-menu menu-item "View"
(keymap
(igo-editor-toggle-status-bar menu-item "Status Bar" igo-editor-toggle-status-bar :button (:toggle . (igo-editor-get-property (igo-editor-at-input) :show-status-bar)))
(igo-editor-toggle-move-number menu-item "Move Number" igo-editor-toggle-move-number :button (:toggle . (igo-editor-get-property (igo-editor-at-input) :show-move-number)))
(igo-editor-toggle-branch-text menu-item "Branch Text" igo-editor-toggle-branch-text :button (:toggle . (igo-editor-get-property (igo-editor-at-input) :show-branches)))))
(navi-menu menu-item "Navigation"
(keymap
(igo-editor-previous-fork menu-item "Previous Fork" igo-editor-previous-fork)
(igo-editor-previous-node menu-item "Previous Node" igo-editor-previous-node)
(igo-editor-next-node menu-item "Next Node" igo-editor-next-node)
(igo-editor-next-fork menu-item "Next Fork" igo-editor-next-fork)
(igo-editor-select-next-node menu-item "Select Next Node" igo-editor-select-next-node)))
(sep-1 menu-item "--")
(igo-editor-toggle-allow-illegal-move
menu-item "Allow Illegal Move" igo-editor-toggle-allow-illegal-move
:button (:toggle . (igo-editor-allow-illegal-move-p (igo-editor-at-input))))
(igo-editor-toggle-move-opposite-color
menu-item "Reverse Color in Next Move" igo-editor-toggle-move-opposite-color
:button (:toggle . (igo-editor-move-opposite-color-p (igo-editor-at-input)))
:enable (igo-editor-allow-illegal-move-p (igo-editor-at-input)))
(igo-editor-toggle-editable
menu-item "Editable" igo-editor-toggle-editable
:button (:toggle . (igo-editor-editable-p (igo-editor-at-input))))
(sep-2 menu-item "--")
(igo-editor-move-mode menu-item "Move Mode" igo-editor-move-mode :button (:radio . (eq (igo-editor-get-mode-name (igo-editor-at-input)) 'move)))
(igo-editor-free-edit-mode menu-item "Free Edit Mode" igo-editor-free-edit-mode :button (:radio . (eq (igo-editor-get-mode-name (igo-editor-at-input)) 'free)))
(igo-editor-mark-edit-mode menu-item "Mark Edit Mode" igo-editor-mark-edit-mode :button (:radio . (eq (igo-editor-get-mode-name (igo-editor-at-input)) 'mark)))
(sep-3 menu-item "--")
(tree-menu menu-item "Edit Structure"
(keymap
(igo-editor-make-current-node-root menu-item "Make Current Node Root" igo-editor-make-current-node-root)
(igo-editor-cut-current-node menu-item "Cut Current Node" igo-editor-cut-current-node)
(igo-editor-copy-current-node menu-item "Copy Current Node" igo-editor-copy-current-node)
(igo-editor-paste-current-node menu-item "Paste to Current Node" igo-editor-paste-current-node)))
(igo-editor-edit-comment menu-item "Edit Comment" igo-editor-edit-comment)
(igo-editor-edit-move-number menu-item "Edit Move Number" igo-editor-edit-move-number)
(igo-editor-edit-game-info menu-item "Edit Game Info" igo-editor-edit-game-info)
(sep-4 menu-item "--")
(igo-editor-text-mode menu-item "Text Mode" igo-editor-text-mode)
(sep-5 menu-item "--")
(igo-editor-export-image menu-item "Export Image" igo-editor-export-image)
(sep-6 menu-item "--")
(igo-editor-quit menu-item "Quit" igo-editor-quit)
))
(defun igo-editor-main-menu (&optional editor)
(interactive)
(if (null editor) (setq editor (igo-editor-at-input)))
(if editor
(let ((fn (car (last (x-popup-menu last-input-event igo-editor-main-menu-map)))))
(if (and (symbolp fn) (fboundp fn))
(funcall fn editor)))))
;; Editor - Update Editor
(defun igo-editor-set-region (editor begin end)
"Resize editor's region."
(move-overlay (igo-editor-overlay editor) begin end)
(igo-editor-update editor))
(defun igo-editor-update (editor)
"Update editor state from buffer text."
(if (igo-editor-update-model editor)
;; Update image if graphical mode
(if (igo-editor-graphical-mode-p editor)
(igo-editor-update-image editor)))
;; Change display mode depending on error state
(cond
;; Text(Auto Recovery) => Graphical
((igo-editor-text-mode-p editor)
(if (and (igo-editor-text-mode-auto-recovery-p editor)
(null (igo-editor-last-error editor)))
(igo-editor-graphical-mode editor)))
;; Graphical => Text(Auto Recovery)
((igo-editor-graphical-mode-p editor)
(if (igo-editor-last-error editor)
(igo-editor-text-mode editor t)))))
(defun igo-editor-update-model (editor)
"Update game, last-buffer-text, last-error from buffer text."
(with-current-buffer (overlay-buffer (igo-editor-overlay editor))
(let* ((begin (igo-editor-begin editor))
(end (igo-editor-end editor))
(curr-text (buffer-substring-no-properties begin end)))
(if (equal curr-text (igo-editor-last-buffer-text editor))
;; Return nil (not update)
nil
;; Re-parse buffer text
;;(message "Parse buffer text %s %s" begin end)
(condition-case err
(let ((game (igo-game-from-sgf-buffer begin end)))
;; Set current node
(let ((old-game (igo-editor-game editor)))
(if old-game
;; Reproduce the board of old-game
(igo-game-redo-by-path
game
(igo-node-path-from-root (igo-game-current-node old-game)))
;; show first branch or last node
(igo-game-redo-all game)))
;; Update game and text
(igo-editor--game-set editor game)
(igo-editor--last-buffer-text-set editor curr-text)
(igo-editor--last-error-set editor nil)
;; Return t (update)
t)
;; error
(error
(message "SGF error %s" (error-message-string err))
(igo-editor--game-set editor nil)
(igo-editor--last-buffer-text-set editor curr-text)
(igo-editor--last-error-set editor (igo-editor-split-error editor err))
;; Return nil (not update)
nil))))))
;; Editor - Update Buffer Text
(defun igo-editor-update-buffer-text (editor)
"Reflect editor state to buffer text."
(let* ((game (igo-editor-game editor))
(ov (igo-editor-overlay editor)))
(if (and game ov)
(let ((text (igo-editor-game-to-sgf-string game))
(begin (overlay-start ov))
(end (overlay-end ov)))
(with-current-buffer
(overlay-buffer ov)
(when (not (string= text (buffer-substring-no-properties begin end))) ;;need to modify?
;; Record last update text
(igo-editor--last-buffer-text-set editor text)
;; Replace text from BEGIN to END
(igo-editor-replace-buffer-text begin end text)))))))
(defun igo-editor-update-buffer-text-forced (editor &optional game)
(igo-editor-replace-buffer-text
(igo-editor-begin editor)
(igo-editor-end editor)
(igo-editor-game-to-sgf-string (or game (igo-editor-game editor)))))
(defun igo-editor-game-to-sgf-string (game)
(igo-sgf-string-from-game-tree
(igo-game-root-node game)
(igo-board-w (igo-game-board game))
(igo-board-h (igo-game-board game))))
(defun igo-editor-replace-buffer-text (begin end text)
(save-excursion
(let ((inhibit-read-only t))
(goto-char begin)
(insert text)
(delete-region (point) (+ (point) (- end begin)))
(if (not (equal (char-after) ?\n))
(insert ?\n) ))))
;; Editor - Error
(defun igo-editor-show-last-error (editor)
(if (and editor (igo-editor-last-error editor))
(let ((begin (igo-editor-last-error-begin editor))
(end (igo-editor-last-error-end editor))
(msg (igo-editor-last-error-message editor)))
(if (= begin end)
(message "%s: %s" begin msg)
(message "%s-%s: %s" begin end msg)))))
(defun igo-editor-split-error (editor err)
"Return ((begin-relative . end-relative) . message)"
(if err
(let ((err-str (error-message-string err)))
;; <msg>
;; <point>: <msg>
;; <begin>-<end>: <msg>
(if (string-match "^\\(\\([0-9]+\\)\\(-\\([0-9]+\\)\\)?:\\)? *\\(.*\\)$" err-str)
(let* ((editor-begin (igo-editor-begin editor))
(begin-str (match-string-no-properties 2 err-str))
(end-str (match-string-no-properties 4 err-str))
(begin (if begin-str (- (string-to-number begin-str)
editor-begin)))
(end (if end-str (- (string-to-number end-str)
editor-begin)))
(msg (match-string-no-properties 5 err-str)))
;; ((begin-relative . end-relative) . message)
(cons
(cond
((and begin end) (cons begin end))
(begin (cons begin (1+ begin)))
(t (cons 0 (- (igo-editor-end editor) (igo-editor-begin editor)))))
msg))))))
(defun igo-editor-last-error-begin (editor)
(+ (caar (igo-editor-last-error editor)) (igo-editor-begin editor)))
(defun igo-editor-last-error-end (editor)
(+ (cdar (igo-editor-last-error editor)) (igo-editor-begin editor)))
(defun igo-editor-last-error-message (editor)
(cdr (igo-editor-last-error editor)))
;; Editor - Display Mode(Text or Graphical)
(defun igo-editor-text-mode-p (editor)
(or
(eq (igo-editor-display-mode editor) 'text)
(eq (igo-editor-display-mode editor) 'text-auto-recovery)))
(defun igo-editor-text-mode-auto-recovery-p (editor)
(eq (igo-editor-display-mode editor) 'text-auto-recovery))
(defun igo-editor-graphical-mode-p (editor)
(eq (igo-editor-display-mode editor) 'graphical))
(defun igo-editor-text-mode (&optional editor auto-recovery)
(interactive)
(if (null editor) (setq editor (igo-editor-at-input)))
(when (and editor (not (igo-editor-text-mode-p editor)))
(let ((ov (igo-editor-overlay editor)))
(igo-editor-mode-set editor nil) ;;clear editing mode
(overlay-put ov 'display nil)
(igo-editor-set-keymap editor igo-editor-text-mode-map)
(igo-editor--svg-set editor nil)
(igo-editor--image-set editor nil)
(igo-editor--display-mode-set
editor (if auto-recovery 'text-auto-recovery 'text))
(igo-editor-call-hooks editor 'text-mode editor))))
(defun igo-editor-graphical-mode (&optional editor)
(interactive)
(if (null editor) (setq editor (igo-editor-at-input)))
(when (and editor
(not (igo-editor-graphical-mode-p editor)))
;; Update model if text changed after last parse.
(igo-editor-update-model editor)
;; Change mode if succeeded.
(if (igo-editor-last-error editor)
(igo-editor-show-last-error editor)
(igo-editor--display-mode-set editor 'graphical)
(igo-editor-update-image editor)
(igo-editor-set-keymap editor igo-editor-graphical-mode-map) ;;Unnecessary? (set keymap in start move mode)
(igo-editor-move-mode editor)
(igo-editor-call-hooks editor 'graphical-mode editor))))
;; Editor - Image Layout
(defun igo-editor-layout-create (image-scale board-view show-status-bar show-main-bar)
(vector
'igo-editor-layout
image-scale
board-view
show-status-bar
show-main-bar))
(defun igo-editor-layout-image-scale (layout) (aref layout 1))
(defun igo-editor-layout-board-view (layout) (aref layout 2))
(defun igo-editor-layout-status-bar-p (layout) (aref layout 3))
(defun igo-editor-layout-main-bar-p (layout) (aref layout 4))
(defun igo-editor-layout-board-w (layout)
(igo-board-view-pixel-w (igo-editor-layout-board-view layout)))
(defun igo-editor-layout-board-h (layout)
(igo-board-view-pixel-h (igo-editor-layout-board-view layout)))
(defun igo-editor-layout-bar-w (layout)
(igo-editor-layout-board-w layout))
(defun igo-editor-layout-status-bar-top (_layout)
0)
(defun igo-editor-layout-board-top (layout)
(if (igo-editor-layout-status-bar-p layout)
(igo-ui-bar-h) 0))
(defun igo-editor-layout-main-bar-top (layout)
(+ (igo-editor-layout-board-top layout)
(igo-editor-layout-board-h layout)))
(defun igo-editor-layout-image-w (layout)
(ceiling
(* (igo-editor-layout-image-scale layout)
(igo-editor-layout-board-w layout))))
(defun igo-editor-layout-image-h (layout)
(ceiling
(* (igo-editor-layout-image-scale layout)
(+ (igo-editor-layout-main-bar-top layout)
(if (igo-editor-layout-main-bar-p layout)
(igo-ui-bar-h) 0)))))
(defun igo-editor-main-bar-top (editor)
(igo-editor-layout-main-bar-top (igo-editor-layout editor)))
(defun igo-editor-bar-pixel-w (editor)
(igo-editor-layout-bar-w (igo-editor-layout editor)))
(defun igo-editor-board-top (editor)
(igo-editor-layout-board-top (igo-editor-layout editor)))
;; Editor - Image
(defun igo-editor-update-image (editor &optional recreate)
"Update svg, image, overlay from game model."
(let ((game (igo-editor-game editor))
(board (igo-editor-board editor))
(board-view (igo-editor-board-view editor))
(layout (igo-editor-layout editor))
(svg (igo-editor-svg editor))
(image-input-map (igo-editor-image-input-map editor))
(image-scale (igo-editor-image-scale editor)))
(when game
;; Create a new SVG Tree
(when (or recreate
(null svg)
;; Board size changed
(or
(null board-view)
(/= (igo-board-view-w board-view) (igo-board-w board))
(/= (igo-board-view-h board-view) (igo-board-h board))))
;; Determine Scaling Factor
(setq image-scale (image-compute-scaling-factor image-scaling-factor))
;; New Board View (Determine grid interval and board pixel sizes)
(setq board-view (igo-board-view
board
(igo-editor-get-property editor :grid-interval)))
;; Make a layout object
(setq layout
(igo-editor-layout-create
image-scale
board-view
(igo-editor-get-property editor :show-status-bar)
t))
(igo-editor--layout-set editor layout)
;; Clear clickable areas
(igo-editor-clear-image-input-map image-input-map)
;; New SVG Root
(setq svg (igo-editor-create-svg
(igo-editor-layout-image-w layout)
(igo-editor-layout-image-h layout)
layout
image-input-map))
(igo-editor--svg-set editor svg)
;; Create Initial Main Bar
;; @todo Should not be there. Normally, the navi bar is created with igo-editor-move-mode-start function.
(igo-editor-create-navi-bar editor))
;; Update SVG
(igo-editor-update-svg svg layout image-input-map game editor)
;; Update image descriptor & display property
(let ((image (svg-image svg :scale 1.0 :map (car image-input-map))))
(overlay-put (igo-editor-overlay editor) 'display image)
(igo-editor--image-set editor image)))))
(defun igo-editor-create-svg (image-w image-h layout image-input-map)
;; New SVG Root
(let ((svg (svg-create
image-w image-h
:transform (format "scale(%s)"
(igo-editor-layout-image-scale layout)))))
;; Initialize SVG Parts
(when (igo-editor-layout-status-bar-p layout)
(igo-editor-create-status-bar svg layout))
(igo-editor-create-svg-board svg layout)
(when image-input-map
(igo-editor-create-svg-board-input image-input-map layout))
;; Main bar is empty for initial state
svg))
(defun igo-editor-clear-image-input-map (image-input-map)
(setcar image-input-map nil))
(defun igo-editor-create-svg-board (svg layout)
(igo-board-view-create-board
(igo-editor-layout-board-view layout)
svg 0
(igo-editor-layout-board-top layout)))
(defun igo-editor-create-svg-board-input (image-input-map layout)
(let ((board-view (igo-editor-layout-board-view layout)))
(igo-ui-push-clickable-rect
image-input-map
'igo-grid
(igo-board-view-clickable-left board-view)
(igo-board-view-clickable-top board-view
(igo-editor-layout-board-top layout))
(igo-board-view-clickable-width board-view)
(igo-board-view-clickable-height board-view)
(igo-editor-layout-image-scale layout))))
(defun igo-editor-update-svg (svg layout image-input-map game editor)
(let ((board-view (igo-editor-layout-board-view layout)))
;; Update game status
(if (igo-editor-layout-status-bar-p layout)
(igo-editor-update-status-bar svg layout game))
;; Update intersection States(Stones)
(igo-board-view-update-stones board-view svg (igo-game-board game))
;; Update move numbers
(igo-board-view-update-move-numbers
board-view svg
(igo-editor-get-property editor :show-move-number)
game)
;; Update branches text (and clickable rect)
(igo-editor-update-branches-text svg layout image-input-map game editor)
;; Update last move mark
(igo-board-view-update-last-move-mark
board-view svg
(and (igo-editor-get-property editor :show-last-move)
(not (igo-editor-get-property editor :show-move-number)))
game)
;; Update Marks
(igo-board-view-update-marks board-view svg t game)))
;; Editor - Image - Properties
(defun igo-editor-set-property-and-update-image
(editor key value &optional recreate)
(if (null editor) (setq editor (igo-editor-at-input)))
(when editor
(igo-editor-set-property editor key value)
(igo-editor-update-image editor recreate)))
(defun igo-editor-toggle-property-and-update-image
(editor key &optional recreate)
(if (null editor) (setq editor (igo-editor-at-input)))
(when editor
(igo-editor-set-property-and-update-image
editor
key
(not (igo-editor-get-property editor key))
recreate)))
(defun igo-editor-set-grid-interval (editor interval)
(igo-editor-set-property-and-update-image editor :grid-interval interval t))
(defun igo-editor-set-status-bar-visible (editor visible)
;;needs recreate image
(igo-editor-set-property-and-update-image editor :show-status-bar visible t))
(defun igo-editor-set-move-number-visible (editor visible)
(igo-editor-set-property-and-update-image editor :show-move-number visible))
(defun igo-editor-set-branch-text-visible (editor visible)
(igo-editor-set-property-and-update-image editor :show-branches visible))
(defun igo-editor-toggle-status-bar (&optional editor)
(interactive)
(igo-editor-toggle-property-and-update-image
editor :show-status-bar t));;needs recreate image
(defun igo-editor-toggle-move-number (&optional editor)
(interactive)
(igo-editor-toggle-property-and-update-image
editor :show-move-number))
(defun igo-editor-toggle-branch-text (&optional editor)
(interactive)
(igo-editor-toggle-property-and-update-image
editor :show-branches))
;; Editor - Image - Status Bar
(defun igo-editor-create-status-bar (svg layout)
(let* ((bar-w (igo-editor-layout-bar-w layout))
(bar (igo-ui-create-bar svg
0
(igo-editor-layout-status-bar-top layout)
bar-w
"status-bar")))
(svg-circle
bar
(- bar-w (/ (igo-ui-bar-h) 2))
(/ (igo-ui-bar-h) 2)
(/ (* 3 (igo-ui-bar-h)) 10)
:gradient "stone-black"
:id "status-stone-b")
(svg-circle
bar
(/ (igo-ui-bar-h) 2)
(/ (igo-ui-bar-h) 2)
(/ (* 3 (igo-ui-bar-h)) 10)
:gradient "stone-white"
:id "status-stone-w")
))
(defun igo-editor-update-status-bar (svg layout game)
(let ((bar (car (dom-by-id svg "^status-bar$"))))
(when bar
(let* ((bar-w (igo-editor-layout-bar-w layout))
(bar-y 0)
(turn-line-h 4)
(text-y (+ bar-y (/ (- (igo-ui-bar-h) (igo-ui-font-h)) 2) (igo-ui-font-ascent)))
(w-prisoners (igo-game-get-prisoners game 'white))
(b-prisoners (igo-game-get-prisoners game 'black)))
;; Turn
(svg-rectangle
bar
(if (igo-black-p (igo-game-turn game)) ;;not next-move-color
(- bar-w (igo-ui-bar-h))
0)
(+ bar-y (- (igo-ui-bar-h) turn-line-h))
(igo-ui-bar-h) turn-line-h :fill "#f00" :id "status-turn")
;; Prisoners(Captured Stones)
(if (> b-prisoners 0)
(svg-text
bar
(number-to-string b-prisoners)
:x (+ (igo-ui-bar-h) (/ (igo-ui-bar-h) 8))
:y text-y
:font-family (igo-ui-font-family) :font-size (igo-ui-font-h)
:text-anchor "start" :fill "#fff" :id "status-prisoners-w")
(let ((n (car (dom-by-id bar "status-prisoners-w"))))
(if n (dom-remove-node bar n))))
(if (> w-prisoners 0)
(svg-text
bar
(number-to-string w-prisoners)
:x (- bar-w (igo-ui-bar-h) (/ (igo-ui-bar-h) 8))
:y text-y
:font-family (igo-ui-font-family) :font-size (igo-ui-font-h)
:text-anchor "end" :fill "#fff" :id "status-prisoners-b")
(let ((n (car (dom-by-id bar "status-prisoners-b"))))
(if n (dom-remove-node bar n))))
;; Move Number
(svg-text
bar
(number-to-string (1+ (igo-node-move-number (igo-game-current-node game))))
:x (/ bar-w 2)
:y text-y
:font-family (igo-ui-font-family) :font-size (igo-ui-font-h)
:text-anchor "middle" :fill "#fff" :id "status-move-number")
))))
;; Editor - Image - Branch Text
(defun igo-editor-update-branches-text (svg layout image-input-map game editor)
(let* ((board (igo-game-board game))
(curr-node (igo-game-current-node game))
(setup-node-index 0))
(igo-board-view-update-branches
(igo-editor-layout-board-view layout)
svg
(igo-editor-get-property editor :show-branches)
board
(igo-editor-next-move-color editor)
curr-node
;; Called when branch is pass
(lambda (_index _num-nodes text text-color move-color class-name)
(when image-input-map
(igo-editor-put-branch-text-on-button
svg image-input-map
(igo-editor-layout-board-view layout)
'igo-editor-pass text text-color move-color class-name)))
;; Called when branch is resign
nil
;; Called when branch is setup node
(lambda (_index _num-nodes text text-color move-color class-name)
(igo-editor-setup-nodes-area-put-branch-text
svg layout setup-node-index text text-color move-color class-name)
(setq setup-node-index (1+ setup-node-index))))
;; update clickable rect for setup nodes
(when image-input-map
(igo-editor-setup-nodes-area-update-image-input-map
layout setup-node-index image-input-map))))
(defun igo-editor-put-branch-text-on-button (svg image-input-map board-view button-id text text-color turn class-name)
(let ((xy (igo-ui-left-top-of-clickable-area image-input-map button-id)))
(if xy
(let* ((grid-interval (igo-board-view-interval board-view))
(font-size (igo-svg-font-size-on-board grid-interval))
(x (+ (car xy) (/ font-size 2)))
(y (cdr xy))
(group (svg-node svg 'g :class class-name)))
(svg-rectangle group
(- x (/ font-size 2))
(- y (/ font-size 2))
font-size font-size
:fill (if (igo-black-p turn) "#ccc" "#444"))
(igo-svg-text-on-board group x y grid-interval text text-color)))))
;; Editor - Image - Branch Text - Setup Nodes Area
(defun igo-editor-setup-nodes-area-update-image-input-map (layout num-setup-nodes image-input-map)
(igo-ui-remove-clickable-area
image-input-map
'igo-setup-nodes-area)
(if (> num-setup-nodes 0)
(igo-ui-push-clickable-rect
image-input-map 'igo-setup-nodes-area
(igo-editor-setup-nodes-area-left layout)
(igo-editor-setup-nodes-area-top layout)
(igo-editor-setup-nodes-area-width layout num-setup-nodes)
(igo-editor-setup-nodes-area-height layout)
(igo-editor-layout-image-scale layout))))
(defun igo-editor-setup-nodes-area-text-x (layout index)
(let ((board-view (igo-editor-layout-board-view layout)))
(+ (igo-board-view-margin board-view)
(* (igo-board-view-interval board-view) index))))
(defun igo-editor-setup-nodes-area-text-y (layout)
(let ((board-view (igo-editor-layout-board-view layout)))
(+ (igo-editor-layout-board-top layout)
(igo-board-view-margin board-view)
(* (igo-board-view-interval board-view)
(igo-board-view-h board-view)))))
(defun igo-editor-setup-nodes-area-left (layout)
(let ((board-view (igo-editor-layout-board-view layout)))
(- (igo-board-view-margin board-view)
(/ (igo-board-view-interval board-view) 2))))
(defun igo-editor-setup-nodes-area-top (layout)
(let* ((board-view (igo-editor-layout-board-view layout))
(interval (igo-board-view-interval board-view)))
(+ (igo-editor-layout-board-top layout)
(igo-board-view-margin board-view)
(* interval (igo-board-view-h board-view))
(- (/ interval 2)))))
(defun igo-editor-setup-nodes-area-width (layout num-setup-nodes)
(let* ((board-view (igo-editor-layout-board-view layout))
(interval (igo-board-view-interval board-view)))
(* interval num-setup-nodes)))
(defun igo-editor-setup-nodes-area-height (layout)
(igo-board-view-interval
(igo-editor-layout-board-view layout)))