aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/calendar/calendar.el242
1 files changed, 184 insertions, 58 deletions
diff --git a/lisp/calendar/calendar.el b/lisp/calendar/calendar.el
index e43132fd4e1..6de0f01d553 100644
--- a/lisp/calendar/calendar.el
+++ b/lisp/calendar/calendar.el
@@ -375,6 +375,90 @@ pre-existing calendar windows."
375 (sexp :tag "Lisp expression")) 375 (sexp :tag "Lisp expression"))
376 :version "23.1") 376 :version "23.1")
377 377
378
379(defvar calendar-month-digit-width nil
380 "Width of the region with numbers in each month in the calendar.")
381
382(defvar calendar-month-width nil
383 "Full width of each month in the calendar.")
384
385(defvar calendar-right-margin nil
386 "Right margin of the calendar.")
387
388(defun calendar-recompute-layout-variables ()
389 "Recompute some layout-related calendar \"constants\"."
390 (setq calendar-month-digit-width (+ (* 6 calendar-column-width)
391 calendar-day-digit-width)
392 calendar-month-width (+ (* 7 calendar-column-width)
393 calendar-intermonth-spacing)
394 calendar-right-margin (+ calendar-left-margin
395 (* 3 (* 7 calendar-column-width))
396 (* 2 calendar-intermonth-spacing))))
397
398;; FIXME add font-lock-keywords.
399(defun calendar-set-layout-variable (symbol value &optional minmax)
400 "Set SYMBOL's value to VALUE, an integer.
401A positive/negative MINMAX enforces a minimum/maximum value.
402Then redraw the calendar, if necessary."
403 (let ((oldvalue (symbol-value symbol)))
404 (custom-set-default symbol (if minmax
405 (if (< minmax 0)
406 (min value (- minmax))
407 (max value minmax))
408 value))
409 (unless (equal value oldvalue)
410 (calendar-recompute-layout-variables)
411 (calendar-redraw))))
412
413(defcustom calendar-left-margin 5
414 "Empty space to the left of the first month in the calendar."
415 :group 'calendar
416 :initialize 'custom-initialize-default
417 :set 'calendar-set-layout-variable
418 :type 'integer
419 :version "23.1")
420
421;; Or you can view it as columns of width 2, with 1 space, no space
422;; after the last column, and a 5 space gap between month.
423;; FIXME check things work if this is odd.
424(defcustom calendar-intermonth-spacing 4
425 "Space between months in the calendar. Minimum value is 1."
426 :group 'calendar
427 :initialize 'custom-initialize-default
428 :set (lambda (sym val)
429 (calendar-set-layout-variable sym val 1))
430 :type 'integer
431 :version "23.1")
432
433(defcustom calendar-column-width 3
434 "Width of each day column in the calendar. Minimum value is 3."
435 :initialize 'custom-initialize-default
436 :set (lambda (sym val)
437 (calendar-set-layout-variable sym val 3))
438 :type 'integer
439 :version "23.1")
440
441(defcustom calendar-day-header-width 2
442 "Width of the day column headers in the calendar.
443Must be at least one less than `calendar-column-width'."
444 :group 'calendar
445 :initialize 'custom-initialize-default
446 :set (lambda (sym val)
447 (calendar-set-layout-variable sym val (- 1 calendar-column-width)))
448 :type 'integer
449 :version "23.1")
450
451;; FIXME a format specifier instead?
452(defcustom calendar-day-digit-width 2
453 "Width of the day digits in the calendar. Minimum value is 2."
454 :group 'calendar
455 :initialize 'custom-initialize-default
456 :set (lambda (sym val)
457 (calendar-set-layout-variable sym val 2))
458 :type 'integer
459 :version "23.1")
460
461
378(defcustom diary-file "~/diary" 462(defcustom diary-file "~/diary"
379 "Name of the file in which one's personal diary of dates is kept. 463 "Name of the file in which one's personal diary of dates is kept.
380 464
@@ -824,6 +908,11 @@ calendar."
824 908
825;;; End of user options. 909;;; End of user options.
826 910
911(calendar-recompute-layout-variables)
912
913(defconst calendar-first-date-row 3
914 "First row in the calendar with actual dates.")
915
827(defconst calendar-buffer "*Calendar*" 916(defconst calendar-buffer "*Calendar*"
828 "Name of the buffer used for the calendar.") 917 "Name of the buffer used for the calendar.")
829 918
@@ -1163,9 +1252,21 @@ Optional integers MON and YR are used instead of today's date."
1163 (erase-buffer) 1252 (erase-buffer)
1164 (calendar-increment-month month year -1) 1253 (calendar-increment-month month year -1)
1165 (dotimes (i 3) 1254 (dotimes (i 3)
1166 (calendar-generate-month month year (+ 5 (* 25 i))) 1255 (calendar-generate-month month year
1256 (+ calendar-left-margin
1257 (* calendar-month-width i)))
1167 (calendar-increment-month month year 1))) 1258 (calendar-increment-month month year 1)))
1168 1259
1260(defun calendar-move-to-column (indent)
1261 "Like `move-to-column', but indents if the line is too short."
1262 (if (< (move-to-column indent) indent)
1263 (indent-to indent)))
1264
1265(defun calendar-ensure-newline ()
1266 "Move to the next line, adding a newline if necessary."
1267 (or (zerop (forward-line 1))
1268 (insert "\n")))
1269
1169(defun calendar-generate-month (month year indent) 1270(defun calendar-generate-month (month year indent)
1170 "Produce a calendar for MONTH, YEAR on the Gregorian calendar. 1271 "Produce a calendar for MONTH, YEAR on the Gregorian calendar.
1171The calendar is inserted at the top of the buffer in which point is currently 1272The calendar is inserted at the top of the buffer in which point is currently
@@ -1180,11 +1281,13 @@ line."
1180 (last (calendar-last-day-of-month month year)) 1281 (last (calendar-last-day-of-month month year))
1181 string day) 1282 string day)
1182 (goto-char (point-min)) 1283 (goto-char (point-min))
1183 (calendar-insert-indented 1284 (calendar-move-to-column indent)
1285 (insert
1184 (calendar-string-spread 1286 (calendar-string-spread
1185 (list (format "%s %d" (calendar-month-name month) year)) ?\s 20) 1287 (list (format "%s %d" (calendar-month-name month) year))
1186 indent t) 1288 ?\s calendar-month-digit-width))
1187 (calendar-insert-indented "" indent) ; go to proper spot 1289 (calendar-ensure-newline)
1290 (calendar-move-to-column indent) ; go to proper spot
1188 ;; Use the first two characters of each day to head the columns. 1291 ;; Use the first two characters of each day to head the columns.
1189 (dotimes (i 7) 1292 (dotimes (i 7)
1190 (insert 1293 (insert
@@ -1192,43 +1295,29 @@ line."
1192 (setq string 1295 (setq string
1193 (calendar-day-name (mod (+ calendar-week-start-day i) 7) nil t)) 1296 (calendar-day-name (mod (+ calendar-week-start-day i) 7) nil t))
1194 (if enable-multibyte-characters 1297 (if enable-multibyte-characters
1195 (truncate-string-to-width string 2) 1298 (truncate-string-to-width string calendar-day-header-width)
1196 (substring string 0 2))) 1299 (substring string 0 calendar-day-header-width)))
1197 " ")) 1300 (make-string (- calendar-column-width calendar-day-header-width) ?\s)))
1198 (calendar-insert-indented "" 0 t) ; force onto following line 1301 (calendar-ensure-newline)
1199 (calendar-insert-indented "" indent) ; go to proper spot 1302 (calendar-move-to-column indent)
1200 ;; Add blank days before the first of the month. 1303 ;; Add blank days before the first of the month.
1201 (dotimes (idummy blank-days) (insert " ")) 1304 (insert (make-string (* blank-days calendar-column-width) ?\s))
1202 ;; Put in the days of the month. 1305 ;; Put in the days of the month.
1203 (dotimes (i last) 1306 (dotimes (i last)
1204 (setq day (1+ i)) 1307 (setq day (1+ i))
1205 (insert (format "%2d " day)) 1308 ;; TODO should numbers be left-justified, centred...?
1309 (insert (format (format "%%%dd%%s" calendar-day-digit-width) day
1310 (make-string
1311 (- calendar-column-width calendar-day-digit-width) ?\s)))
1206 ;; FIXME set-text-properties? 1312 ;; FIXME set-text-properties?
1207 (add-text-properties 1313 (add-text-properties
1208 (- (point) 3) (1- (point)) 1314 (- (point) (1+ calendar-day-digit-width)) (1- (point))
1209 `(mouse-face highlight help-echo ,(eval calendar-date-echo-text))) 1315 `(mouse-face highlight help-echo ,(eval calendar-date-echo-text)))
1210 (and (zerop (mod (+ day blank-days) 7)) 1316 (and (zerop (mod (+ day blank-days) 7))
1211 (/= day last) 1317 (/= day last)
1212 (calendar-insert-indented "" 0 t) ; force onto following line 1318 (progn
1213 (calendar-insert-indented "" indent))))) ; go to proper spot 1319 (calendar-ensure-newline)
1214 1320 (calendar-move-to-column indent))))))
1215(defun calendar-insert-indented (string indent &optional newline)
1216 "Insert STRING at column INDENT.
1217If the optional parameter NEWLINE is non-nil, leave point at start of next
1218line, inserting a newline if there was no next line; otherwise, leave point
1219after the inserted text. Returns t."
1220 ;; Try to move to that column.
1221 (move-to-column indent)
1222 ;; If line is too short, indent out to that column.
1223 (if (< (current-column) indent)
1224 (indent-to indent))
1225 (insert string)
1226 ;; Advance to next line, if requested.
1227 (when newline
1228 (end-of-line)
1229 (or (zerop (forward-line 1))
1230 (insert "\n")))
1231 t)
1232 1321
1233(defun calendar-redraw () 1322(defun calendar-redraw ()
1234 "Redraw the calendar display, if `calendar-buffer' is live." 1323 "Redraw the calendar display, if `calendar-buffer' is live."
@@ -1497,17 +1586,17 @@ the STRINGS are just concatenated and the result truncated."
1497 "Update the calendar mode line with the current date and date style." 1586 "Update the calendar mode line with the current date and date style."
1498 (if (bufferp (get-buffer calendar-buffer)) 1587 (if (bufferp (get-buffer calendar-buffer))
1499 (with-current-buffer calendar-buffer 1588 (with-current-buffer calendar-buffer
1500 (setq mode-line-format 1589 (let ((start (- calendar-left-margin 2))
1501 ;; The magic numbers are based on the fixed calendar layout. 1590 (date (condition-case nil
1502 (concat (make-string (+ 3 1591 (calendar-cursor-to-nearest-date)
1503 (- (car (window-inside-edges)) 1592 (error (calendar-current-date)))))
1504 (car (window-edges)))) ?\s) 1593 (setq mode-line-format
1505 (calendar-string-spread 1594 (concat (make-string (max 0 (+ start
1506 (let ((date (condition-case nil 1595 (- (car (window-inside-edges))
1507 (calendar-cursor-to-nearest-date) 1596 (car (window-edges))))) ?\s)
1508 (error (calendar-current-date))))) 1597 (calendar-string-spread
1509 (mapcar 'eval calendar-mode-line-format)) 1598 (mapcar 'eval calendar-mode-line-format)
1510 ?\s 74))) 1599 ?\s (- calendar-right-margin (1- start))))))
1511 (force-mode-line-update)))) 1600 (force-mode-line-update))))
1512 1601
1513(defun calendar-window-list () 1602(defun calendar-window-list ()
@@ -1571,6 +1660,40 @@ the STRINGS are just concatenated and the result truncated."
1571 (let ((now (decode-time))) 1660 (let ((now (decode-time)))
1572 (list (nth 4 now) (nth 3 now) (nth 5 now)))) 1661 (list (nth 4 now) (nth 3 now) (nth 5 now))))
1573 1662
1663(defun calendar-column-to-month (&optional real)
1664 "Convert current column to calendar month offset number (leftmost is 0).
1665If the cursor is in the right margin (i.e. beyond the last digit) of
1666month N, returns -(N+1). If optional REAL is non-nil, return a
1667cons (month year), where month is the real month number (1-12)."
1668 (let* ((ccol (current-column))
1669 (col (max 0 (+ ccol (/ calendar-intermonth-spacing 2)
1670 (- calendar-left-margin))))
1671 (segment (/ col (+ (* 7 calendar-column-width)
1672 calendar-intermonth-spacing)))
1673 month year lastdigit edge)
1674 (if real
1675 (progn
1676 ;; NB assumes 3 month display.
1677 (if (zerop (setq month (% (+ displayed-month segment -1) 12)))
1678 (setq month 12))
1679 (setq year (cond
1680 ((and (= 12 month) (zerop segment)) (1- displayed-year))
1681 ((and (= 1 month) (= segment 2)) (1+ displayed-year))
1682 (t displayed-year)))
1683 (cons month year))
1684 ;; The rightmost column with a digit in it in this month segment.
1685 (setq lastdigit (+ calendar-left-margin
1686 calendar-month-digit-width -1
1687 (* segment calendar-month-width))
1688 ;; The rightmost edge of this month segment, dividing the
1689 ;; space between months in two.
1690 edge (+ calendar-left-margin
1691 (* (1+ segment) calendar-month-width)
1692 (- (/ calendar-intermonth-spacing 2))))
1693 (if (and (> ccol lastdigit) (< ccol edge))
1694 (- (1+ segment))
1695 segment))))
1696
1574(defun calendar-cursor-to-date (&optional error event) 1697(defun calendar-cursor-to-date (&optional error event)
1575 "Return a list (month day year) of current cursor position. 1698 "Return a list (month day year) of current cursor position.
1576If cursor is not on a specific date, signals an error if optional parameter 1699If cursor is not on a specific date, signals an error if optional parameter
@@ -1582,21 +1705,22 @@ use instead of point."
1582 (current-buffer)) 1705 (current-buffer))
1583 (save-excursion 1706 (save-excursion
1584 (if event (goto-char (posn-point (event-start event)))) 1707 (if event (goto-char (posn-point (event-start event))))
1585 (let* ((segment (/ (current-column) 25)) 1708 (let* ((month (calendar-column-to-month t))
1586 (month (% (+ displayed-month segment -1) 12)) 1709 (year (cdr month))
1587 (month (if (zerop month) 12 month)) 1710 (month (car month)))
1588 (year 1711 ;; Call with point on either of the two digits in a 2-digit date,
1589 (cond 1712 ;; or on or before the digit of a 1-digit date.
1590 ((and (= 12 month) (zerop segment)) (1- displayed-year))
1591 ((and (= 1 month) (= segment 2)) (1+ displayed-year))
1592 (t displayed-year))))
1593 (if (not (and (looking-at "[ 0-9]?[0-9][^0-9]") 1713 (if (not (and (looking-at "[ 0-9]?[0-9][^0-9]")
1594 (< 2 (count-lines (point-min) (point))))) 1714 (>= (count-lines (point-min) (point))
1715 calendar-first-date-row)))
1595 (if error (error "Not on a date!")) 1716 (if error (error "Not on a date!"))
1596 (if (not (looking-at " ")) 1717 ;; Go back to before the first date digit.
1718 (or (looking-at " ")
1597 (re-search-backward "[^0-9]")) 1719 (re-search-backward "[^0-9]"))
1598 (list month 1720 (list month
1599 (string-to-number (buffer-substring (1+ (point)) (+ 4 (point)))) 1721 (string-to-number
1722 (buffer-substring (1+ (point))
1723 (+ 1 calendar-day-digit-width (point))))
1600 year)))))) 1724 year))))))
1601 1725
1602(add-to-list 'debug-ignored-errors "Not on a date!") 1726(add-to-list 'debug-ignored-errors "Not on a date!")
@@ -1884,12 +2008,14 @@ each element returned has a final `.' character."
1884 " -?[0-9]+") 2008 " -?[0-9]+")
1885 . font-lock-function-name-face) ; month and year 2009 . font-lock-function-name-face) ; month and year
1886 (,(regexp-opt 2010 (,(regexp-opt
1887 (list (substring (aref calendar-day-name-array 6) 0 2) 2011 (list (substring (aref calendar-day-name-array 6)
1888 (substring (aref calendar-day-name-array 0) 0 2))) 2012 0 calendar-day-header-width)
2013 (substring (aref calendar-day-name-array 0)
2014 0 calendar-day-header-width)))
1889 ;; Saturdays and Sundays are highlighted differently. 2015 ;; Saturdays and Sundays are highlighted differently.
1890 . font-lock-comment-face) 2016 . font-lock-comment-face)
1891 ;; First two chars of each day are used in the calendar. 2017 ;; First two chars of each day are used in the calendar.
1892 (,(regexp-opt (mapcar (lambda (x) (substring x 0 2)) 2018 (,(regexp-opt (mapcar (lambda (x) (substring x 0 calendar-day-header-width))
1893 calendar-day-name-array)) 2019 calendar-day-name-array))
1894 . font-lock-reference-face)) 2020 . font-lock-reference-face))
1895 "Default keywords to highlight in Calendar mode.") 2021 "Default keywords to highlight in Calendar mode.")