-
Notifications
You must be signed in to change notification settings - Fork 7
/
calfw-blocks.el
1517 lines (1366 loc) · 68.5 KB
/
calfw-blocks.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
;;; calfw-blocks.el --- Visual time blocks and more for the Emacs Calendar Framework (calfw) -*- lexical-binding: t -*-
;;
;; Copyright (C) 2022 null
;;
;; Author: ml729
;; Maintainer: ml729 <null>
;; Created: July 06, 2022
;; Version: 0.0.1
;; Homepage: https://github.com/ml729/calfw-blocks
;; Package-Requires: ((emacs "25.4"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; Adds time blocks, n-day views, and transposed views to the Emacs Calendar
;; Framework (calfw)
;;
;;; Code:
(require 'calfw)
(require 'calfw-org)
;; (require 'posframe)
(defcustom calfw-blocks-initial-visible-time '(8 0)
"Earliest initial visible time as list (hours minutes)."
:group 'calfw-blocks
:type 'list)
(defcustom calfw-blocks-lines-per-hour 4
"Number of lines per hour in a block."
:group 'calfw-blocks
:type 'number)
(defcustom calfw-blocks-default-event-length 1
"Length in hours of events with same start and end time.
Also used for events with a start time and no end time."
:group 'calfw-blocks
:type 'number)
(defcustom calfw-blocks-min-block-width 3
"Minimum width of blocks in characters."
:group 'calfw-blocks
:type 'number)
(defcustom calfw-blocks-show-time-grid t
"Whether to show horizontal lines for each hour."
:group 'calfw-blocks
:type 'boolean)
(defcustom calfw-blocks-render-multiday-events t
"Whether to render (nonblock) multiday events."
:group 'calfw-blocks
:type 'boolean)
(defcustom calfw-blocks-time-grid-lines-on-top t
"Whether time grid lines should cut through vertical lines."
:group 'calfw-blocks
:type 'boolean)
(defcustom calfw-blocks-show-current-time-indicator t
"Whether to show a line indicating the current time."
:group 'calfw-blocks
:type 'boolean)
(defcustom calfw-blocks-grid-line-char (propertize " " 'face 'overline)
"Whether time grid lines should cut through vertical lines."
:group 'calfw-blocks
:type 'boolean)
(defcustom calfw-blocks-colors-list
'("#ef7969"
"#49c029"
"#7090ff"
"#e07fff"
"#70d3f0"
"#ffcf00")
"Colors to use for blocks. The default colors are
Modus Vivendi's colors for graphs."
:group 'calfw-blocks
:type 'list)
(defcustom calfw-blocks-display-end-times t
"Whether or not to display end times in blocks.")
(defcustom calfw-blocks-transpose-date-width 17
"Width (in characters) of date cell in transpose views.")
(defcustom calfw-blocks-transpose-day-name-length nil
"Number of characters of day of week to display in transpose views.
Displays full name if nil.")
(defface calfw-blocks-overline
'((t :overline t))
"Basic face for overline."
:group 'basic-faces)
(defvar calfw-blocks-earliest-visible-time '(0 0)
"Earliest visible time in a day as list (hours minutes).")
(defvar calfw-blocks-nday-views-alist
'((1 . block-day)
(2 . block-2-day)
(3 . block-3-day)
(4 . block-4-day)
(5 . block-5-day)
(10 . block-10-day)))
(defvar calfw-blocks-posframe-buffer " *cfw-calendar-sticky*")
(defvar-local calfw-blocks-header-line-string nil)
;; Faces
(defun calfw-blocks-create-faces ()
(let ((faces ())
newface)
(dolist (color calfw-blocks-colors-list)
(setq newface (make-face
(intern (concat "calfw-blocks-" color))))
(set-face-background newface color)
(set-face-foreground newface "black")
(push newface faces))
(reverse faces)))
(defvar calfw-blocks-faces-list
(calfw-blocks-create-faces)
"Faces for blocks.")
(defface calfw-blocks-today-indicator
'((t (:background "#e0a3ff")))
"Face for today indicator."
:group 'calfw-blocks)
;; Calendar model and params
(defun calfw-blocks-cp-dispatch-view-impl (view)
"[internal] Return a view function which is corresponding to the view symbol.
VIEW is a symbol of the view type."
(cond
((eq 'month view) 'cfw:view-month)
((eq 'week view) 'cfw:view-week)
((eq 'two-weeks view) 'cfw:view-two-weeks)
((eq 'day view) 'cfw:view-day)
((eq 'block-week view) 'calfw-blocks-view-block-week)
((eq 'block-day view) 'calfw-blocks-view-block-day)
((eq 'block-2-day view) 'calfw-blocks-view-block-2-day)
((eq 'block-3-day view) 'calfw-blocks-view-block-3-day)
((eq 'block-4-day view) 'calfw-blocks-view-block-4-day)
((eq 'block-5-day view) 'calfw-blocks-view-block-5-day)
((eq 'block-7-day view) 'calfw-blocks-view-block-7-day)
((eq 'transpose-8-day view) 'calfw-blocks-view-transpose-8-day)
((eq 'transpose-10-day view) 'calfw-blocks-view-transpose-10-day)
((eq 'transpose-12-day view) 'calfw-blocks-view-transpose-12-day)
((eq 'transpose-14-day view) 'calfw-blocks-view-transpose-14-day)
((eq 'transpose-two-weeks view) 'calfw-blocks-view-transpose-two-weeks)
(t (error "Not found such view : %s" view))))
;; Transpose
(defun calfw-blocks-render-append-transpose-parts (param)
"[internal] Append rendering parts to PARAM and return a new list."
(let* ((EOL "\n")
(date-cell-width (cfw:k 'date-cell-width param))
(cell-width (cfw:k 'cell-width param))
(columns (cfw:k 'columns param))
(num-cell-char
(/ cell-width (char-width cfw:fchar-horizontal-line)))
(num-date-cell-char
(/ date-cell-width (char-width cfw:fchar-horizontal-line)))
)
(append
param
`((eol . ,EOL) (vl . ,(cfw:rt (make-string 1 cfw:fchar-vertical-line) 'cfw:face-grid))
(hline . ,(cfw:rt
(concat
(loop for i from 0 below 2 concat
(concat
(make-string 1 (if (= i 0) cfw:fchar-top-left-corner cfw:fchar-top-junction))
(make-string num-date-cell-char cfw:fchar-horizontal-line)
(make-string 1 (if (= i 0) cfw:fchar-top-left-corner cfw:fchar-top-junction))
(make-string num-cell-char cfw:fchar-horizontal-line)
))
(make-string 1 cfw:fchar-top-right-corner) EOL)
'cfw:face-grid))
(cline . ,(cfw:rt
(concat
(loop for i from 0 below 2 concat
(concat
(make-string 1 (if (= i 0) cfw:fchar-left-junction cfw:fchar-junction))
(make-string num-date-cell-char cfw:fchar-horizontal-line)
(make-string 1 (if (= i 0) cfw:fchar-left-junction cfw:fchar-junction))
(make-string num-cell-char cfw:fchar-horizontal-line)
))
(make-string 1 cfw:fchar-right-junction) EOL) 'cfw:face-grid))))))
(defun calfw-blocks-view-nday-transpose-week-calc-param (n dest)
"[internal] Calculate cell size from the reference size and
return an alist of rendering parameters."
(let*
((time-width 5)
(time-hline (make-string time-width ? ))
(win-width (cfw:dest-width dest))
;; title 2, toolbar 1, header 2, hline 2, footer 1, margin 2 => 10
(win-height (max 15 (- (cfw:dest-height dest) 10)))
(junctions-width (* (char-width cfw:fchar-junction) 5))
(date-cell-width calfw-blocks-transpose-date-width)
(cell-width (/ (- win-width junctions-width (* 2 date-cell-width)) 2))
(cell-height (* 5 win-height)) ;; every cell has essentially unlimited height
(total-width (+ (* date-cell-width 2) (* cell-width 2) junctions-width)))
`((cell-width . ,cell-width)
(date-cell-width . ,date-cell-width)
(cell-height . ,cell-height)
(total-width . ,total-width)
(columns . ,n)
(time-width . ,time-width)
(time-hline . ,time-hline))))
(defun calfw-blocks-view-transpose-nday-week (n component &optional model)
"[internal] Render weekly calendar view."
(let* ((dest (cfw:component-dest component))
(param (calfw-blocks-render-append-transpose-parts (calfw-blocks-view-nday-transpose-week-calc-param n dest)))
(total-width (cfw:k 'total-width param))
(time-width (cfw:k 'time-width param))
(EOL (cfw:k 'eol param))
(VL (cfw:k 'vl param))
(time-hline (cfw:k 'time-hline param))
(hline (cfw:k 'hline param))
(cline (cfw:k 'cline param))
(model (if model model (calfw-blocks-view-block-nday-week-model n (cfw:component-model component))))
(begin-date (cfw:k 'begin-date model))
(end-date (cfw:k 'end-date model)))
;; update model
(setf (cfw:component-model component) model)
(setq header-line-format "")
;; ;; header
(insert
"\n"
(cfw:rt
(cfw:render-title-period begin-date end-date)
'cfw:face-title)
EOL (calfw-blocks-render-toolbar total-width 'week
(calfw-blocks-navi-previous-nday-week-command n)
(calfw-blocks-navi-next-nday-week-command n))
EOL)
(insert cline)
;; contents
(calfw-blocks-render-calendar-cells-transpose-weeks
model param
(lambda (date week-day hday)
(cfw:rt (format "%s" (calendar-extract-day date))
(if hday 'cfw:face-sunday
(cfw:render-get-week-face
week-day 'cfw:face-default-day)))))
;; footer
(insert (cfw:render-footer total-width (cfw:model-get-contents-sources model)))))
(defun calfw-blocks-render-calendar-cells-transpose-weeks (model param title-func)
"[internal] Insert calendar cells for week based views."
(let ((all-days (apply 'nconc (cfw:k 'weeks model))))
(calfw-blocks-render-calendar-cells-transpose-days model param title-func all-days
'calfw-blocks-render-content
t)))
(defun calfw-blocks-render-calendar-cells-transpose-days (model param title-func &optional
days content-fun do-weeks)
"[internal] Insert calendar cells for the linear views."
(calfw-blocks-render-columns-transpose
(loop with cell-width = (cfw:k 'cell-width param)
with days = (or days (cfw:k 'days model))
with content-fun = (or content-fun
'cfw:render-event-days-overview-content)
with holidays = (cfw:k 'holidays model)
with annotations = (cfw:k 'annotations model)
with headers = (cfw:k 'headers model)
with raw-periods-all = (calfw-blocks-render-periods-stacks model)
with sorter = (cfw:model-get-sorter model)
for date in days ; days columns loop
for count from 0 below (length days)
for hday = (car (cfw:contents-get date holidays))
for week-day = (nth (% count 7) headers)
for ant = (cfw:rt (cfw:contents-get date annotations)
'cfw:face-annotation)
for raw-periods = (cfw:contents-get date raw-periods-all)
for raw-contents = (cfw:render-sort-contents
(funcall content-fun
(cfw:model-get-contents-by-date date model))
sorter)
for prs-contents = (cfw:render-rows-prop
(append (calfw-blocks-render-transpose-periods-days
date raw-periods cell-width)
(mapcar 'cfw:render-default-content-face
raw-contents)))
for num-label = (if prs-contents
(format "(%s)"
(+ (length raw-contents)
(length raw-periods))) "")
for tday = (concat
;; " " ; margin
(funcall title-func date week-day hday)
(if num-label (concat " " num-label)))
;; separate holiday from rest of days in transposed view,
;; so it can be put on a new line
for hday-str = (if hday (cfw:rt (substring hday 0)
'cfw:face-holiday))
collect
(cons date (cons (cons tday (cons ant hday-str)) prs-contents)))
param))
(defun calfw-blocks-render-transpose-periods-days (date periods-stack cell-width)
"[internal] Insert period texts."
(when periods-stack
(let ((stack (sort (copy-sequence periods-stack)
(lambda (a b) (< (car a) (car b))))))
(loop for (row (begin end content props interval)) in stack
for beginp = (equal date begin)
for endp = (equal date end)
for width = (- cell-width 2)
for begintime = (if interval (calfw-blocks-format-time (car interval)))
for endtime = (if interval (calfw-blocks-format-time (cdr interval)))
for beginday = (cfw:strtime begin)
for endday = (cfw:strtime end)
for title =
(concat (if (not (string= beginday endday))
(concat beginday "-" endday " "))
(if (and begintime
(string= (substring content 0 5) begintime))
(concat begintime "-" endtime (substring content 5))
content))
collect
(if content
(cfw:render-default-content-face title)
"")))))
(defun calfw-blocks-render-columns-transpose (day-columns param)
"[internal] This function concatenates each rows on the days into a string of a physical line.
DAY-COLUMNS is a list of columns. A column is a list of following form: (DATE (DAY-TITLE . ANNOTATION-TITLE) STRING STRING...)."
(let* ((date-cell-width (cfw:k 'date-cell-width param))
(cell-width (cfw:k 'cell-width param))
(cell-height (cfw:k 'cell-height param))
(EOL (cfw:k 'eol param)) (VL (cfw:k 'vl param))
(hline (cfw:k 'hline param)) (cline (cfw:k 'cline param))
(num-days (length day-columns))
(first-half (seq-subseq day-columns 0 (/ num-days 2)))
(second-half (seq-subseq day-columns (/ num-days 2) num-days)))
(loop for j from 0 below (/ num-days 2)
for day1 = (nth j first-half)
for day2 = (nth j second-half)
do
(loop with breaked-day-columns =
(loop for day-rows in `(,day1 ,day2)
for date = (car day-rows)
for line = (cddr day-rows)
collect
(cons date (cfw:render-break-lines
line cell-width cell-height)))
with breaked-date-columns =
(loop for day-rows in `(,day1 ,day2)
for date = (car day-rows)
for dayname = (aref calendar-day-name-array
(calendar-day-of-week date))
for (tday . (ant . hday)) = (cadr day-rows)
collect
(cons date (cfw:render-break-lines
(list
(cfw:tp
(cfw:render-default-content-face
(concat
(substring dayname 0 calfw-blocks-transpose-day-name-length)
" "
tday)
'cfw:face-day-title)
'cfw:date date)
hday) date-cell-width cell-height)))
with max-height = (max 2
(length (cdr (nth 0 breaked-day-columns)))
(length (cdr (nth 1 breaked-day-columns)))
(length (cdr (nth 0 breaked-date-columns)))
(length (cdr (nth 1 breaked-date-columns))))
for i from 1 to max-height
do
(loop for k from 0 to 1
for day-rows = (nth k breaked-day-columns)
for date-rows = (nth k breaked-date-columns)
for date = (car day-rows)
for row = (nth i day-rows)
for date-row = (nth i date-rows)
do
(insert
VL (cfw:tp
(cfw:render-left date-cell-width (and date-row (format "%s" date-row)))
'cfw:date date))
(insert
VL (cfw:tp
(cfw:render-separator
(cfw:render-left cell-width (and row (format "%s" row))))
'cfw:date date)))
(insert VL EOL))
(insert cline))
(insert EOL)))
(advice-add 'cfw:cp-dispatch-view-impl :override 'calfw-blocks-cp-dispatch-view-impl)
;; Block views
(defun calfw-blocks-view-block-nday-week-model (n model)
"[internal] Create a logical view model of weekly calendar.
This function collects and arranges contents. This function does
not know how to display the contents in the destinations."
(let* ((init-date (cfw:k 'init-date model))
(begin-date (cfw:date-before init-date
(mod (cfw:days-diff (cfw:emacs-to-calendar (current-time)) init-date) n)))
(end-date (cfw:date-after begin-date (1- n))))
(calfw-blocks-view-model-make-common-data-for-nday-weeks n model begin-date end-date)))
(defun calfw-blocks-view-model-make-common-data-for-nday-weeks (n model begin-date end-date)
"[internal] Return a model object for week based views."
(cfw:model-create-updated-view-data
model
(cfw:view-model-make-common-data
model begin-date end-date
`((headers . ,(calfw-blocks-view-model-make-day-names-for-nday-week n begin-date)) ; a list of the index of day-of-week
(weeks . ,(calfw-blocks-view-model-make-nday-weeks ; a matrix of day-of-month, which corresponds to the index of `headers'
n
begin-date
end-date))))))
(defun calfw-blocks-view-model-make-day-names-for-nday-week (n begin-date)
"[internal] Return a list of index of day of the week."
(let ((begin-day (calendar-day-of-week begin-date)))
(cl-loop for i from 0 below n
collect (% (+ begin-day i) cfw:week-days))))
;;todo replace calendar week start day with day of the week of init date
(defun calfw-blocks-view-model-make-nday-weeks (n begin-date end-date)
"[internal] Return a list of weeks those have 7 days."
(let* ((first-day-day (calendar-day-of-week begin-date)) weeks)
(cl-loop with i = begin-date
with day = first-day-day
with week = nil
do
;; flush a week
(when (and (= 0 (mod (- day first-day-day) n)) week)
(push (nreverse week) weeks)
(setq week nil)
(when (cfw:date-less-equal-p end-date i) (cl-return)))
;; add a day
(push i week)
;; increment
(setq day (% (1+ day) n))
(setq i (cfw:date-after i 1)))
(nreverse weeks)))
(defun calfw-blocks-view-nday-week-calc-param (n dest)
"[internal] Calculate cell size from the reference size and
return an alist of rendering parameters."
(let*
((time-width 5)
(time-hline (make-string time-width ? ))
(win-width (cfw:dest-width dest))
;; title 2, toolbar 1, header 2, hline 2, footer 1, margin 2 => 10
(win-height (max 15 (- (cfw:dest-height dest) 10)))
(junctions-width (* (char-width cfw:fchar-junction) (1+ n)))
(cell-width (cfw:round-cell-width
(max 5 (/ (- win-width junctions-width time-width) n))))
(cell-height (* calfw-blocks-lines-per-hour 24))
(total-width (+ time-width (* cell-width n) junctions-width)))
`((cell-width . ,cell-width)
(cell-height . ,cell-height)
(total-width . ,total-width)
(columns . ,n)
(time-width . ,time-width)
(time-hline . ,time-hline))))
(defun calfw-blocks-view-block-day (component)
(calfw-blocks-view-block-nday-week 1 component))
(defun calfw-blocks-view-block-2-day (component)
(calfw-blocks-view-block-nday-week 2 component))
(defun calfw-blocks-view-block-3-day (component)
(calfw-blocks-view-block-nday-week 3 component))
(defun calfw-blocks-view-block-4-day (component)
(calfw-blocks-view-block-nday-week 4 component))
(defun calfw-blocks-view-block-5-day (component)
(calfw-blocks-view-block-nday-week 5 component))
(defun calfw-blocks-view-block-7-day (component)
(calfw-blocks-view-block-nday-week 7 component))
(defun calfw-blocks-view-transpose-8-day (component)
(calfw-blocks-view-transpose-nday-week 8 component))
(defun calfw-blocks-view-transpose-10-day (component)
(calfw-blocks-view-transpose-nday-week 10 component))
(defun calfw-blocks-view-transpose-12-day (component)
(calfw-blocks-view-transpose-nday-week 12 component))
(defun calfw-blocks-view-transpose-14-day (component)
(calfw-blocks-view-transpose-nday-week 14 component))
(defun calfw-blocks-view-transpose-two-weeks (component)
(calfw-blocks-view-transpose-nday-week 14 component
(cfw:view-two-weeks-model
(cfw:component-model component))))
(defun calfw-blocks-view-block-week (component)
(calfw-blocks-view-block-nday-week 7 component
(cfw:view-week-model
(cfw:component-model component))))
;; Rendering views
(defun calfw-blocks-view-block-nday-week (n component &optional model)
"[internal] Render weekly calendar view."
(let* ((dest (cfw:component-dest component))
(param (cfw:render-append-parts (calfw-blocks-view-nday-week-calc-param n dest)))
(total-width (cfw:k 'total-width param))
(time-width (cfw:k 'time-width param))
(EOL (cfw:k 'eol param))
(VL (cfw:k 'vl param))
(time-hline (cfw:k 'time-hline param))
(hline (concat time-hline (cfw:k 'hline param)))
(cline (concat time-hline (cfw:k 'cline param)))
(model (if model model (calfw-blocks-view-block-nday-week-model n (cfw:component-model component))))
(begin-date (cfw:k 'begin-date model))
(end-date (cfw:k 'end-date model)))
;; (print model)
;; update model
(setf (cfw:component-model component) model)
(setq calfw-blocks-header-line-string (concat
" " (substring (aref calendar-month-name-array (1- (calendar-extract-month begin-date)))
0 3)
;; (number-to-string (% (calendar-extract-year begin-date) 1000))
(cl-loop for i in (cfw:k 'headers model)
with VL = (cfw:k 'vl param) with cell-width = (cfw:k 'cell-width param)
for name = (concat (aref calendar-day-name-array i) " " (number-to-string
(nth 1 (cfw:date-after begin-date i))))
concat
(concat VL (cfw:rt (cfw:render-center cell-width name)
(cfw:render-get-week-face i 'cfw:face-header))))))
(setq header-line-format '((:eval
(if (< (line-number-at-pos (window-start)) 6)
""
calfw-blocks-header-line-string))))
;; header
(insert
"\n"
(cfw:rt
(cfw:render-title-period begin-date end-date)
'cfw:face-title)
EOL (calfw-blocks-render-toolbar total-width 'week
(calfw-blocks-navi-previous-nday-week-command n)
(calfw-blocks-navi-next-nday-week-command n))
EOL hline)
;; time header
(insert (cfw:rt (cfw:render-right time-width "Time")
'default))
;; day names
(calfw-blocks-render-day-of-week-names model param)
(insert VL EOL cline)
;; contents
(calfw-blocks-render-calendar-cells-block-weeks
model param
(lambda (date week-day hday)
(cfw:rt (format "%s" (calendar-extract-day date))
(if hday 'cfw:face-sunday
(cfw:render-get-week-face
week-day 'cfw:face-default-day)))))
;; footer
(insert (cfw:render-footer total-width (cfw:model-get-contents-sources model)))))
(defun calfw-blocks-navi-next-nday-week-command (n)
"Move the cursor forward NUM weeks. If NUM is nil, 1 is used.
Moves backward if NUM is negative."
(lambda (&optional num)
(interactive "p")
(cfw:navi-next-day-command (* n (or num 1)))))
(defun calfw-blocks-navi-previous-nday-week-command (n)
"Move the cursor back NUM weeks. If NUM is nil, 1 is used.
Moves forward if NUM is negative."
(lambda (&optional num)
(interactive "p")
(cfw:navi-next-day-command (* (- n) (or num 1)))))
(defun calfw-blocks-render-day-of-week-names (model param)
"[internal] Insert week names."
(cl-loop for i in (cfw:k 'headers model)
with VL = (cfw:k 'vl param) with cell-width = (cfw:k 'cell-width param)
for name = (aref calendar-day-name-array i) do
(insert VL (cfw:rt (cfw:render-center cell-width name)
(cfw:render-get-week-face i 'cfw:face-header)))))
(defun calfw-blocks-render-toolbar (width current-view prev-cmd next-cmd)
"[internal] Return a text of the toolbar.
WIDTH is width of the toolbar. CURRENT-VIEW is a symbol of the
current view type. This symbol is used to select the button faces
on the toolbar. PREV-CMD and NEXT-CMD are the moving view
command, such as `cfw:navi-previous(next)-month-command' and
`cfw:navi-previous(next)-week-command'."
(let* ((prev (cfw:render-button " < " prev-cmd))
(today (cfw:render-button "Today" 'cfw:navi-goto-today-command))
(next (cfw:render-button " > " next-cmd))
(month (cfw:render-button
"Month" 'cfw:change-view-month
(eq current-view 'month)))
(tweek (cfw:render-button
"Two Weeks" 'cfw:change-view-two-weeks
(eq current-view 'two-weeks)))
(transpose-two-week (cfw:render-button
"2W^T" 'calfw-blocks-change-view-transpose-14-day
(eq current-view 'transpose-14-day)))
(transpose-week (cfw:render-button
"W^T" 'calfw-blocks-change-view-transpose-8-day
(eq current-view 'transpose-8-day)))
(week (cfw:render-button
"Week" 'calfw-blocks-change-view-block-week
(eq current-view 'block-week)))
(3day (cfw:render-button
"3-Day" (lambda () (interactive) (calfw-blocks-change-view-block-nday 3))
(eq current-view 'block-3-day)))
(day (cfw:render-button
"Day" (lambda () (interactive) (calfw-blocks-change-view-block-nday 1))
(eq current-view 'block-day)))
(sp " ")
(toolbar-text
(cfw:render-add-right
width (concat sp prev sp next sp today sp)
(concat day sp 3day sp week sp tweek sp transpose-week sp transpose-two-week sp month sp))))
(cfw:render-default-content-face toolbar-text 'cfw:face-toolbar)))
(advice-add 'cfw:render-toolbar :override 'calfw-blocks-render-toolbar)
(defun calfw-blocks-change-view-transpose-two-weeks ()
"change-view-month"
(interactive)
(when (cfw:cp-get-component)
(cfw:cp-set-view (cfw:cp-get-component) 'transpose-two-weeks)))
(defun calfw-blocks-change-view-transpose-14-day ()
"change-view-month"
(interactive)
(when (cfw:cp-get-component)
(cfw:cp-set-view (cfw:cp-get-component) 'transpose-14-day)))
(defun calfw-blocks-change-view-transpose-12-day ()
"change-view-month"
(interactive)
(when (cfw:cp-get-component)
(cfw:cp-set-view (cfw:cp-get-component) 'transpose-12-day)))
(defun calfw-blocks-change-view-transpose-10-day ()
"change-view-month"
(interactive)
(when (cfw:cp-get-component)
(cfw:cp-set-view (cfw:cp-get-component) 'transpose-10-day)))
(defun calfw-blocks-change-view-transpose-8-day ()
"change-view-month"
(interactive)
(when (cfw:cp-get-component)
(cfw:cp-set-view (cfw:cp-get-component) 'transpose-8-day)))
(defun calfw-blocks-change-view-block-nday (n)
""
(interactive)
(when (cfw:cp-get-component)
(advice-add 'cfw:dest-ol-today-set :override 'calfw-blocks-dest-ol-today-set)
(cfw:cp-set-view (cfw:cp-get-component) (alist-get n calfw-blocks-nday-views-alist))
(advice-remove 'cfw:dest-ol-today-set 'calfw-blocks-dest-ol-today-set)))
(defun calfw-blocks-change-view-block-week ()
"change-view-week"
(interactive)
(when (cfw:cp-get-component)
(advice-add 'cfw:dest-ol-today-set :override 'calfw-blocks-dest-ol-today-set)
(cfw:cp-set-view (cfw:cp-get-component) 'block-week)
(advice-remove 'cfw:dest-ol-today-set 'calfw-blocks-dest-ol-today-set)))
(defun calfw-blocks-render-calendar-cells-block-weeks (model param title-func)
"[internal] Insert calendar cells for week based views."
(cl-loop for week in (cfw:k 'weeks model) do
;; (setq week (list (nth 4 week)))
;; (print week)
(calfw-blocks-render-calendar-cells-days model param title-func week
'calfw-blocks-render-content
t)))
(defun calfw-blocks-render-calendar-cells-days (model param title-func &optional
days content-fun do-weeks)
"[internal] Insert calendar cells for the linear views."
(calfw-blocks-render-columns
(cl-loop with cell-width = (cfw:k 'cell-width param)
with days = (or days (cfw:k 'days model))
with content-fun = (or content-fun
'cfw:render-event-days-overview-content)
with holidays = (cfw:k 'holidays model)
with annotations = (cfw:k 'annotations model)
with headers = (cfw:k 'headers model)
with raw-periods-all = (calfw-blocks-render-periods-stacks model)
with sorter = (cfw:model-get-sorter model)
for date in days ; days columns loop
for count from 0 below (length days)
for hday = (car (cfw:contents-get date holidays))
;; for hday = (if (stringp hday) (list hday) hday)
;; for prs-hday = (if hday (mapcar (lambda (h) (cfw:rt h 'cfw:face-holiday)) hday))
for week-day = (nth (% count 7) headers)
for ant = (cfw:rt (cfw:contents-get date annotations)
'cfw:face-annotation)
for raw-periods = (cfw:contents-get date raw-periods-all)
for raw-contents = (cfw:render-sort-contents
(funcall content-fun
(cfw:model-get-contents-by-date date model))
sorter)
for prs-contents = (cfw:render-rows-prop
(append (if do-weeks
(calfw-blocks-render-periods
date week-day raw-periods cell-width model)
(calfw-blocks-render-periods-days
date raw-periods cell-width))
(mapcar 'cfw:render-default-content-face
raw-contents)))
for num-label = (if prs-contents
(format "(%s)"
(+ (length raw-contents)
(length raw-periods))) "")
for tday = (concat
" " ; margin
(funcall title-func date week-day hday)
(if num-label (concat " " num-label))
(if hday (concat " " (cfw:rt (substring hday 0)
'cfw:face-holiday)))
)
collect
(cons date (cons (cons tday ant) prs-contents)))
param))
(defun calfw-blocks-render-periods-days (date periods-stack cell-width)
"[internal] Insert period texts.
Modified to not truncate events. TODO"
(when periods-stack
(let ((stack (sort (copy-sequence periods-stack)
(lambda (a b) (< (car a) (car b))))))
(cl-loop for (row (begin end content)) in stack
for beginp = (equal date begin)
for endp = (equal date end)
for width = (- cell-width 2)
for title = (cfw:render-truncate
(concat
(cfw:strtime begin) " - "
(cfw:strtime end) " : "
content) width t)
collect
(if content
(cfw:rt
(concat
(if beginp "(" " ")
(cfw:render-left width title ?-)
(if endp ")" " "))
(cfw:render-get-face-period content 'cfw:face-periods))
"")))))
(defun calfw-blocks-render-periods-stacks (model)
"Modified version of cfw:render-periods-stacks, where the last element of
period is a pair containing the start and end of time of each event.
[internal] Arrange the `periods' records of the model and
create period-stacks on the each days.
period-stack -> ((row-num . period) ... )"
(let* (periods-each-days)
(cl-loop for (begin end event) in (cfw:k 'periods model)
for content = (if (cfw:event-p event)
;; (cfw:event-period-overview event)
(cfw:event-period-overview event)
event)
for period = (list begin end content
(cfw:extract-text-props content 'face)
(if (cfw:event-p event) (calfw-blocks-get-time-interval event) nil))
for row = (cfw:render-periods-get-min periods-each-days begin end)
do
(setq periods-each-days (cfw:render-periods-place
periods-each-days row period)))
periods-each-days))
(defun calfw-blocks-get-time-interval (event)
"Return (start-time . end-time) of EVENT, a `cfw:event' struct.
start-time and end-time are both lists (a b) where a is the hour,
b is the minute."
(when (cfw:event-start-time event)
(cons (cfw:event-start-time event)
(cfw:event-end-time event))))
(defun calfw-blocks-render-content (lst)
"[internal] Apply `cfw:event-overview' on `cfw:event's in `lst'."
(mapcar (lambda (event)
(if (cfw:event-p event)
(progn
(propertize
(cfw:event-overview event)
'calfw-blocks-interval (calfw-blocks-get-time-interval event)))
event))
lst))
(defun calfw-blocks-render-periods (date week-day periods-stack cell-width model)
"[internal] This function translates PERIOD-STACK to display content on the DATE."
(mapcar (lambda (p)
(let* ((content (nth 2 (cadr p)))
(props (nth 3 (cadr p)))
(interval (nth 4 (cadr p)))
(begintime (if interval (calfw-blocks-format-time (car interval))))
(endtime (if interval (calfw-blocks-format-time (cdr interval)))))
(if (or interval (cfw:org-tp content 'calfw-blocks-interval)
(not calfw-blocks-render-multiday-events))
(apply 'propertize
(if (and calfw-blocks-display-end-times
begintime
(string= (substring content 0 5) begintime))
(concat begintime "-" endtime (substring content 5))
content)
'face (cfw:render-get-face-period content 'cfw:face-periods)
'font-lock-face (cfw:render-get-face-period content 'cfw:face-periods)
'cfw:period t
'calfw-blocks-interval interval
props)
(let* ((begin (nth 0 (cadr p)))
(end (nth 1 (cadr p)))
(beginp (equal date begin))
(endp (equal date end))
(width (- cell-width (if beginp 1 0) (if endp 1 0)))
(title (calfw-blocks-render-periods-title
date week-day begin end content cell-width model)))
(apply 'propertize (concat (when beginp cfw:fstring-period-start)
(cfw:render-left width title ?-)
(when endp cfw:fstring-period-end))
'face (cfw:render-get-face-period content 'cfw:face-periods)
'font-lock-face (cfw:render-get-face-period content 'cfw:face-periods)
'cfw:period t
props)))))
(seq-sort (lambda (a b) (< (car a) (car b)))
periods-stack)))
(defun calfw-blocks-render-periods-title (date week-day begin end content cell-width model)
"[internal] Return a title string.
Fix erroneous width in last line, should be fixed upstream in calfw."
(let* ((title-begin-abs
(max (calendar-absolute-from-gregorian begin)
(calendar-absolute-from-gregorian (cfw:k 'begin-date model))))
(title-begin (calendar-gregorian-from-absolute title-begin-abs))
(num (- (calendar-absolute-from-gregorian date) title-begin-abs)))
(when content
(cl-loop with title = (substring content 0)
for i from 0 below num
for pdate = (calendar-gregorian-from-absolute (+ title-begin-abs i))
for chopn = (+ (if (equal begin pdate) 1 0) (if (equal end pdate) 1 0))
for del = (truncate-string-to-width title (- cell-width chopn))
do
(setq title (substring title (length del)))
finally return
(cfw:render-truncate title cell-width (equal end date))))))
(defun calfw-blocks-format-time (time-obj)
(format "%02d:%02d" (car time-obj) (cadr time-obj)))
(defun calfw-blocks-time-column (time-width cell-height)
(let* ((num-hours (floor (/ cell-height calfw-blocks-lines-per-hour)))
(start-hour (car calfw-blocks-earliest-visible-time))
(start-minute (cadr calfw-blocks-earliest-visible-time))
(times-lst (mapcar (lambda (x) (list (mod (+ x start-hour) 24) start-minute))
(number-sequence 0 (1- num-hours)))))
(mapcan (lambda (x) (append (list (calfw-blocks-format-time x))
(mapcar (lambda (x) (make-string time-width ? ))
(number-sequence 0 (- calfw-blocks-lines-per-hour 2)))))
times-lst)))
(defun calfw-blocks-render-columns (day-columns param)
"[internal] Concatenate rows on the days into a string of a physical line.
DAY-COLUMNS is a list of columns. A column is a list of following
form: (DATE (DAY-TITLE . ANNOTATION-TITLE) STRING STRING...)."
(let* ((cell-width (cfw:k 'cell-width param))
(cell-height (cfw:k 'cell-height param))
(time-width (cfw:k 'time-width param))
(EOL (cfw:k 'eol param))
(VL (cfw:k 'vl param))
(time-hline (cfw:k 'time-hline param))
(hline (concat time-hline (cfw:k 'hline param)))
(cline (concat time-hline (cfw:k 'cline param)))
(earliest-date (caar day-columns))
(curr-time-linum (calfw-blocks--current-time-vertical-position)))
(insert time-hline)
;; convert to regular lisp style
;; (let ((breaked-date-columns
;; (dolist (day-rows day-columns)
;; (let ((date (car day-rows))
;; (tday (caadr day-rows))
;; (ant (cdadr day-rows)))
;; ))))
;; (dolist (day-rows breaked-date-columns)
;; (let ((date (car day-rows))
;; (tday (caadr day-rows))
;; (ant (cdadr day-rows)))
;; (insert
;; VL (if date
;; (cfw:tp
;; (cfw:render-default-content-face
;; (cfw:render-add-right cell-width tday ant)
;; 'cfw:face-day-title)
;; 'cfw:date date)
;; (cfw:render-left cell-width "")))
;; )))
(cl-loop for day-rows in day-columns
for date = (car day-rows)
for (tday . ant) = (cadr day-rows)
do
(insert
VL (if date
(cfw:tp
(cfw:render-default-content-face
(cfw:render-add-right cell-width tday ant)
'cfw:face-day-title)
'cfw:date date)
(cfw:render-left cell-width ""))))
(insert VL EOL)
;; (print cell-height) ;; 39
;; (print cell-width) ;; 17
;; day contents
(cl-loop with breaked-all-day-columns =
(cl-loop for day-rows in day-columns
for (date ants . lines) = day-rows
collect
(cons date (calfw-blocks-render-all-day-events
lines cell-width (1- cell-height))))
with breaked-all-day-columns-padded =
(calfw-blocks-pad-whitespace breaked-all-day-columns)
with all-day-columns-height = (seq-max (mapcar 'length breaked-all-day-columns))
for i from 1 below all-day-columns-height do
(insert (cfw:render-left time-width ""))
(cl-loop for day-rows in breaked-all-day-columns-padded
for date = (car day-rows)
for row = (nth i day-rows)
do
(insert
(if (and calfw-blocks-show-time-grid
calfw-blocks-time-grid-lines-on-top
(= (mod (1- i) calfw-blocks-lines-per-hour) 0)
(string= row (make-string cell-width ?-))
(not (eq date earliest-date)))
?-
VL)
(cfw:tp
(cfw:render-separator
(cfw:render-left cell-width (and row (format "%s" row))))
'cfw:date date)))
(insert VL EOL))
(cl-loop with breaked-day-columns =
(cl-loop for day-rows in day-columns
for (date ants . lines) = day-rows
collect
(cons date (calfw-blocks-render-event-blocks
lines cell-width (1- cell-height))))
with time-columns = (calfw-blocks-time-column time-width cell-height)
for i from 1 below cell-height do
(insert (cfw:render-left time-width (nth (1- i) time-columns)))
(cl-loop for day-rows in breaked-day-columns
for date = (car day-rows)
for row = (nth i day-rows)
do
(insert
(if (and calfw-blocks-show-time-grid
calfw-blocks-time-grid-lines-on-top
(= (mod (1- i) calfw-blocks-lines-per-hour) 0)
(string= row (make-string cell-width ?-))
(not (eq date earliest-date)))
?-
VL)