|
|
1.1 ! root 1: ;; Display a calendar inside GNU Emacs. ! 2: ;; Copyright (C) 1988 Free Software Foundation, Inc. ! 3: ! 4: ;; This file is part of GNU Emacs. ! 5: ! 6: ;; GNU Emacs is free software; you can redistribute it and/or modify ! 7: ;; it under the terms of the GNU General Public License as published by ! 8: ;; the Free Software Foundation; either version 1, or (at your option) ! 9: ;; any later version. ! 10: ! 11: ;; GNU Emacs is distributed in the hope that it will be useful, ! 12: ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 14: ;; GNU General Public License for more details. ! 15: ! 16: ;; You should have received a copy of the GNU General Public License ! 17: ;; along with GNU Emacs; see the file COPYING. If not, write to ! 18: ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ! 19: ;; ! 20: ;; Comments, corrections, and improvements should be sent to ! 21: ;; Edward M. Reingold Department of Computer Science ! 22: ;; (217) 333-6733 University of Illinois at Urbana-Champaign ! 23: ;; [email protected] 1304 West Springfield Avenue ! 24: ;; Urbana, Illinois 61801 ! 25: ;; ! 26: ;; The author gratefully acknowledges the patient help of Richard Stallman ! 27: ;; in making this function into a reasonable piece of code! ! 28: ;; ! 29: ;; Modification for month-offset arguments suggested and implemented by ! 30: ;; Constantine Rasmussen Sun Microsystems, East Coast Division ! 31: ;; (617) 671-0404 2 Federal Street; Billerica, Ma. 01824 ! 32: ;; ARPA: [email protected] USENET: {cbosgd,decvax,hplabs,seismo}!sun!suneast!cdr ! 33: ;; ! 34: ;; Modification to mark current day with stars suggested by ! 35: ;; Franklin Davis Thinking Machines Corp ! 36: ;; (617) 876-1111 245 First Street, Cambridge, MA 02142 ! 37: ;; [email protected] ! 38: ! 39: (defvar calendar-hook nil ! 40: "List of functions called after the calendar buffer has been prepared with ! 41: the calendar of the current month. This can be used, for example, to highlight ! 42: today's date with asterisks--a function star-date is included for this purpose. ! 43: The variable offset-calendar-hook is the list of functions called when the ! 44: calendar function was called for a past or future month.") ! 45: ! 46: (defvar offset-calendar-hook nil ! 47: "List of functions called after the calendar buffer has been prepared with ! 48: the calendar of a past or future month. The variable calendar-hook is the ! 49: list of functions called when the calendar function was called for the ! 50: current month.") ! 51: ! 52: (defun calendar (&optional month-offset) ! 53: "Display three-month calendar in another window. ! 54: The three months appear side by side, with the current month in the middle ! 55: surrounded by the previous and next months. The cursor is put on today's date. ! 56: ! 57: An optional prefix argument ARG causes the calendar displayed to be ! 58: ARG months in the future if ARG is positive or in the past if ARG is ! 59: negative; in this case the cursor goes on the first day of the month. ! 60: ! 61: The Gregorian calendar is assumed. ! 62: ! 63: After preparing the calendar window, the hooks calendar-hook are run ! 64: when the calendar is for the current month--that is, the was no prefix ! 65: argument. If the calendar is for a future or past month--that is, there ! 66: was a prefix argument--the hooks offset-calendar-hook are run. Thus, for ! 67: example, setting calendar-hooks to 'star-date will cause today's date to be ! 68: replaced by asterisks to highlight it in the window." ! 69: (interactive "P") ! 70: (if month-offset (setq month-offset (prefix-numeric-value month-offset))) ! 71: (let ((today (make-marker))) ! 72: (save-excursion ! 73: (set-buffer (get-buffer-create "*Calendar*")) ! 74: (setq buffer-read-only t) ! 75: (let* ! 76: ((buffer-read-only nil) ! 77: ;; Get today's date and extract the day, month and year. ! 78: (date (current-time-string)) ! 79: (garbage (string-match ! 80: " \\([A-Z][a-z][a-z]\\) *\\([0-9]*\\) .* \\([0-9]*\\)$" ! 81: date)) ! 82: (day (or (and month-offset 1) ! 83: (string-to-int ! 84: (substring date (match-beginning 2) (match-end 2))))) ! 85: (month ! 86: (cdr (assoc ! 87: (substring date (match-beginning 1) (match-end 1)) ! 88: '(("Jan" . 1) ("Feb" . 2) ("Mar" . 3) ("Apr" . 4) ! 89: ("May" . 5) ("Jun" . 6) ("Jul" . 7) ("Aug" . 8) ! 90: ("Sep" . 9) ("Oct" . 10) ("Nov" . 11) ("Dec" . 12))))) ! 91: (year (string-to-int ! 92: (substring date (match-beginning 3) (match-end 3))))) ! 93: (erase-buffer) ! 94: ;; If user requested a month in the future or the past, ! 95: ;; advance the variables MONTH and YEAR to describe that one. ! 96: (cond ! 97: (month-offset ! 98: (let ((year-month (+ (+ (* year 12) (- month 1)) month-offset))) ! 99: (setq month (+ (% year-month 12) 1)) ! 100: (setq year (/ year-month 12))))) ! 101: ;; Generate previous month, starting at left margin. ! 102: (generate-month;; previous month ! 103: (if (= month 1) 12 (1- month)) ! 104: (if (= month 1) (1- year) year) ! 105: 0) ! 106: ;; Generate this month, starting at column 24, ! 107: ;; and record where today's date appears, in the marker TODAY. ! 108: (goto-char (point-min)) ! 109: (set-marker today (generate-month month year 24 day)) ! 110: ;; Generate the following month, starting at column 48. ! 111: (goto-char (point-min)) ! 112: (generate-month ! 113: (if (= month 12) 1 (1+ month)) ! 114: (if (= month 12) (1+ year) year) ! 115: 48))) ! 116: ;; Display the buffer and put cursor on today's date. ! 117: ;; Do it in another window, but if this buffer is already visible, ! 118: ;; just select its window. ! 119: (pop-to-buffer "*Calendar*") ! 120: (goto-char (marker-position today)) ! 121: ;; Make TODAY point nowhere so it won't slow down buffer editing until GC. ! 122: (set-marker today nil)) ! 123: ;; Make the window just tall enough for its contents. ! 124: (let ((h (1- (window-height))) ! 125: (l (count-lines (point-min) (point-max)))) ! 126: (or (= (+ (window-height (selected-window)) ! 127: (window-height (minibuffer-window))) ! 128: (screen-height)) ! 129: (<= h l) ! 130: (shrink-window (- h l)))) ! 131: (if month-offset ! 132: (run-hooks 'offset-calendar-hook) ! 133: (run-hooks 'calendar-hook))) ! 134: ! 135: (defun leap-year-p (year) ! 136: "Returns true if YEAR is a Gregorian leap year, and false if not." ! 137: (or ! 138: (and (= (% year 4) 0) ! 139: (/= (% year 100) 0)) ! 140: (= (% year 400) 0))) ! 141: ! 142: (defun day-number (month day year) ! 143: "Return day-number within year (origin-1) of the date MONTH DAY YEAR. ! 144: For example, (day-number 1 1 1987) returns the value 1, ! 145: while (day-number 12 31 1980) returns 366." ! 146: ;; ! 147: ;; an explanation of the calculation can be found in PascAlgorithms by ! 148: ;; Edward and Ruth Reingold, Scott-foresman/Little, Brown, 1988. ! 149: ;; ! 150: (let ((day-of-year (+ day (* 31 (1- month))))) ! 151: (if (> month 2) ! 152: (progn ! 153: (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10))) ! 154: (if (leap-year-p year) ! 155: (setq day-of-year (1+ day-of-year))))) ! 156: day-of-year)) ! 157: ! 158: (defun day-of-week (month day year) ! 159: "Returns the day-of-the-week index of MONTH DAY, YEAR. ! 160: Value is 0 for Sunday, 1 for Monday, etc." ! 161: ;; ! 162: ;; Done by calculating the number of days elapsed since the (imaginary) ! 163: ;; Gregorian date Sunday, December 31, 1 BC and taking that number mod 7. ! 164: ;; ! 165: (% ! 166: (- ! 167: (+ (day-number month day year) ! 168: (* 365 (1- year)) ! 169: (/ (1- year) 4)) ! 170: (let ((correction (* (/ (1- year) 100) 3))) ! 171: (if (= (% correction 4) 0) ! 172: (/ correction 4) ! 173: (1+ (/ correction 4))))) ! 174: 7)) ! 175: ! 176: (defun generate-month (month year indent &optional day) ! 177: "Produce a calendar for MONTH, YEAR on the Gregorian calendar, inserted ! 178: in the buffer starting at the line on which point is currently located, but ! 179: indented INDENT spaces. The position in the buffer of the optional ! 180: parameter DAY is returned. The indentation is done from the first ! 181: character on the line and does not disturb the first INDENT characters on ! 182: the line." ! 183: (let* ((first-day-of-month (day-of-week month 1 year)) ! 184: (first-saturday (- 7 first-day-of-month)) ! 185: (last-of-month ! 186: (if (and (leap-year-p year) (= month 2)) ! 187: 29 ! 188: (aref [31 28 31 30 31 30 31 31 30 31 30 31] (1- month)))) ! 189: (month-name ! 190: (aref ["January" "February" "March" "April" "May" "June" ! 191: "July" "August" "September" "October" "November" "December"] ! 192: (1- month)))) ! 193: (insert-indented (format " %s %d" month-name year) indent t) ! 194: (insert-indented " S M Tu W Th F S" indent t) ! 195: (insert-indented "" indent);; move point to appropriate spot on line ! 196: (let ((i 0)) ;; add blank days before the first of the month ! 197: (while (<= (setq i (1+ i)) first-day-of-month) ! 198: (insert " "))) ! 199: (let ((i 0) ! 200: (day-marker)) ;; put in the days of the month ! 201: (while (<= (setq i (1+ i)) last-of-month) ! 202: (insert (format "%2d " i)) ! 203: (and ! 204: day ! 205: (= i day) ;; save the location of the specified day ! 206: (setq day-marker (- (point) 2))) ! 207: (and (= (% i 7) (% first-saturday 7)) ! 208: (/= i last-of-month) ! 209: (insert-indented "" 0 t) ;; force onto following line ! 210: (insert-indented "" indent))) ;; go to proper spot on line ! 211: day-marker))) ! 212: ! 213: (defun insert-indented (string indent &optional newline) ! 214: "Insert STRING at column INDENT. ! 215: If the optional parameter NEWLINE is true, leave point at start of next ! 216: line, inserting a newline if there was no next line; otherwise, leave point ! 217: after the inserted text. Value is always `t'." ! 218: ;; Try to move to that column. ! 219: (move-to-column indent) ! 220: ;; If line is too short, indent out to that column. ! 221: (if (< (current-column) indent) ! 222: (indent-to indent)) ! 223: (insert string) ! 224: ;; Advance to next line, if requested. ! 225: (if newline ! 226: (progn ! 227: (end-of-line) ! 228: (if (eobp) ! 229: (newline) ! 230: (forward-line 1)))) ! 231: t) ! 232: ! 233: (defun star-date () ! 234: "Replace today's date with asterisks in the calendar window. ! 235: This function can be used with the calendar-hook run after the ! 236: calendar window has been prepared." ! 237: (let ((buffer-read-only nil)) ! 238: (forward-char 1) ! 239: (delete-backward-char 2) ! 240: (insert "**") ! 241: (backward-char 1))) ! 242:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.