diff options
| -rw-r--r-- | lisp/calendar/calendar.el | 242 |
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. | ||
| 401 | A positive/negative MINMAX enforces a minimum/maximum value. | ||
| 402 | Then 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. | ||
| 443 | Must 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. |
| 1171 | The calendar is inserted at the top of the buffer in which point is currently | 1272 | The 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. | ||
| 1217 | If the optional parameter NEWLINE is non-nil, leave point at start of next | ||
| 1218 | line, inserting a newline if there was no next line; otherwise, leave point | ||
| 1219 | after 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). | ||
| 1665 | If the cursor is in the right margin (i.e. beyond the last digit) of | ||
| 1666 | month N, returns -(N+1). If optional REAL is non-nil, return a | ||
| 1667 | cons (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. |
| 1576 | If cursor is not on a specific date, signals an error if optional parameter | 1699 | If 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.") |