diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index 671210f3ee8..35cac5d7310 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -1985,7 +1985,13 @@ todo-insert-item--basic
 		  (setq done-only t)
 		  (todo-toggle-view-done-only))
 		(if here
-                    (todo-insert-with-overlays new-item)
+		    (progn
+		      ;; Ensure item is inserted where command was invoked.
+		      (unless (= (point) opoint)
+			(todo-category-number ocat)
+			(todo-category-select)
+			(goto-char opoint))
+                      (todo-insert-with-overlays new-item))
 		  (todo-set-item-priority new-item cat t))
 		(setq item-added t))
 	    ;; If user cancels before setting priority, restore
@@ -2119,6 +2125,9 @@ todo-edit-item
 	  ((or marked (todo-item-string))
 	   (todo-edit-item--next-key 'todo arg)))))

+(defvar todo-edit-item--cat nil)
+(defvar todo-edit-item--pos nil)
+
 (defun todo-edit-item--text (&optional arg)
   "Function providing the text editing facilities of `todo-edit-item'."
   (let ((full-item (todo-item-string)))
@@ -2127,6 +2136,7 @@ todo-edit-item--text
     ;; 1+ signals an error, so just make this a noop.
     (when full-item
       (let* ((opoint (point))
+	     (ocat (todo-current-category))
 	     (start (todo-item-start))
 	     (end (save-excursion (todo-item-end)))
 	     (item-beg (progn
@@ -2151,8 +2161,7 @@ todo-edit-item--text
 			 (concat " \\[" (regexp-quote todo-comment-string)
 				 ": \\([^]]+\\)\\]")
                          end t)))
-	     (prompt (if comment "Edit comment: " "Enter a comment: "))
-	     (buffer-read-only nil))
+	     (prompt (if comment "Edit comment: " "Enter a comment: ")))
 	;; When there are marked items, user can invoke todo-edit-item
 	;; even if point is not on an item, but text editing only
 	;; applies to the item at point.
@@ -2170,22 +2179,43 @@ todo-edit-item--text
                                      end t)
 		  (if comment-delete
 		      (when (todo-y-or-n-p "Delete comment? ")
-			(delete-region (match-beginning 0) (match-end 0)))
-		    (replace-match (save-match-data
-                                     (read-string prompt
-                                                  (cons (match-string 1) 1)))
-				   nil nil nil 1))
+			(let ((buffer-read-only nil))
+			  (delete-region (match-beginning 0) (match-end 0))))
+		    (let ((buffer-read-only nil))
+		      (replace-match (save-match-data
+				       (prog1 (let ((buffer-read-only t))
+						(read-string
+						 prompt
+						 (cons (match-string 1) 1)))
+					 ;; If user moved point while editing
+					 ;; a comment, restore it and ensure
+					 ;; done items section is displayed.
+					 (unless (= (point) opoint)
+					   (todo-category-number ocat)
+					   (let ((todo-show-with-done t))
+					     (todo-category-select)
+					     (goto-char opoint)))))
+				     nil nil nil 1)))
 		(if comment-delete
 		    (user-error "There is no comment to delete")
-		  (insert " [" todo-comment-string ": "
-			  (prog1 (read-string prompt)
-			    ;; If user moved point during editing,
-			    ;; make sure it moves back.
-			    (goto-char opoint)
-			    (todo-item-end))
-			  "]")))))
+		  (let ((buffer-read-only nil))
+		    (insert " [" todo-comment-string ": "
+			    (prog1 (let ((buffer-read-only t))
+				     (read-string prompt))
+			      ;; If user moved point while inserting a
+			      ;; comment, restore it and ensure done items
+			      ;; section is displayed.
+			      (unless (= (point) opoint)
+				(todo-category-number ocat)
+				(let ((todo-show-with-done t))
+				  (todo-category-select)
+				  (goto-char opoint)))
+			      (todo-item-end))
+			    "]"))))))
 	   (multiline
 	    (let ((buf todo-edit-buffer))
+	      (setq todo-edit-item--cat ocat)
+	      (setq todo-edit-item--pos opoint)
 	      (set-window-buffer (selected-window)
 				 (set-buffer (make-indirect-buffer
 					      (buffer-name) buf)))
@@ -2208,10 +2238,14 @@ todo-edit-item--text
 	      ;; Ensure lines following hard newlines are indented.
 	      (setq new (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
 						  "\n\t" new nil nil 1))
-	      ;; If user moved point during editing, make sure it moves back.
-	      (goto-char opoint)
-	      (todo-remove-item)
-	      (todo-insert-with-overlays new)
+	      ;; If user moved point while editing item, restore it.
+	      (unless (= (point) opoint)
+		(todo-category-number ocat)
+		(todo-category-select)
+		(goto-char opoint))
+	      (let ((buffer-read-only nil))
+		(todo-remove-item)
+		(todo-insert-with-overlays new))
 	      (move-to-column item-beg)))))))))

 (defun todo-edit-quit ()
@@ -2243,6 +2277,9 @@ todo-edit-quit
 	(kill-buffer)
 	(unless (eq (current-buffer) buf)
 	  (set-window-buffer (selected-window) (set-buffer buf)))
+	(todo-category-number todo-edit-item--cat)
+	(todo-category-select)
+	(goto-char todo-edit-item--pos)
         (if transient-mark-mode (deactivate-mark)))
     ;; We got here via `F e'.
     (when (todo-check-format)
@@ -2315,117 +2352,118 @@ todo-edit-item--header
 	    ;; If there are marked items, use only the first to set
 	    ;; header changes, and apply these to all marked items.
 	    (when first
-	      (cond
-	       ((eq what 'date)
-		(setq ndate (todo-read-date)))
-	       ((eq what 'calendar)
-		(setq ndate (save-match-data (todo-set-date-from-calendar))))
-	       ((eq what 'today)
-		(setq ndate (calendar-date-string (calendar-current-date) t t)))
-	       ((eq what 'dayname)
-		(setq ndate (todo-read-dayname)))
-	       ((eq what 'time)
-		(setq ntime (save-match-data (todo-read-time)))
-		(when (> (length ntime) 0)
-		  (setq ntime (concat " " ntime))))
-	       ;; When date string consists only of a day name,
-	       ;; passing other date components is a noop.
-	       ((and odayname (memq what '(year month day))))
-	       ((eq what 'year)
-		(setq day oday
-		      monthname omonthname
-		      month omonth
-		      year (cond ((not current-prefix-arg)
-				  (todo-read-date 'year))
-				 ((string= oyear "*")
-				  (user-error "Cannot increment *"))
-				 (t
-				  (number-to-string (+ yy inc))))))
-	       ((eq what 'month)
-		(setf day oday
-		      year oyear
-		      (if (memq 'month calendar-date-display-form)
-			  month
-			monthname)
-		      (cond ((not current-prefix-arg)
-			     (todo-read-date 'month))
-			    ((or (string= omonth "*") (= mm 13))
-			     (user-error "Cannot increment *"))
-			    (t
-			     (let* ((mmo mm)
-                                    ;; Change by 12 or more months?
-                                    (bigincp (>= (abs inc) 12))
-                                    ;; Month number is in range 1..12.
-                                    (mminc (+ mm (% inc 12)))
-			            (mm (% (+ mminc 12) 12))
-			            ;; 12n mod 12 = 0, so 0 is December.
-			            (mm (if (= mm 0) 12 mm))
-                                    ;; Does change in month cross year?
-                                    (mmcmp (cond ((< inc 0) (> mm mmo))
-                                                 ((> inc 0) (< mm mmo))))
-                                    (yyadjust (if bigincp
-                                                  (+ (abs (/ inc 12))
-                                                     (if mmcmp 1 0))
-                                                1)))
-			       ;; Adjust year if necessary.
-                               (setq yy (cond ((and (< inc 0)
-                                                    (or mmcmp bigincp))
-                                               (- yy yyadjust))
-                                              ((and (> inc 0)
-                                                    (or mmcmp bigincp))
-                                               (+ yy yyadjust))
-                                              (t yy)))
-                               (setq year (number-to-string yy))
-			       ;; Return the changed numerical month as
-			       ;; a string or the corresponding month name.
-			       (if omonth
-				   (number-to-string mm)
-			         (aref tma-array (1- mm)))))))
-                ;; Since the number corresponding to the arbitrary
-                ;; month name "*" is out of the range of
-                ;; calendar-last-day-of-month, set it to 1
-                ;; (corresponding to January) to allow 31 days.
-                (let ((mm (if (= mm 13) 1 mm)))
-		  (if (> (string-to-number day)
-			 (calendar-last-day-of-month mm yy))
-		      (user-error "%s %s does not have %s days"
-			     (aref tmn-array (1- mm))
-			     (if (= mm 2) yy "") day))))
-	       ((eq what 'day)
-		(setq year oyear
-		      month omonth
-		      monthname omonthname
-		      day (cond
-			   ((not current-prefix-arg)
-			    (todo-read-date 'day mm yy))
-			   ((string= oday "*")
-			    (user-error "Cannot increment *"))
-			   ((or (string= omonth "*") (string= omonthname "*"))
-			    (setq dd (+ dd inc))
-			    (if (> dd 31)
-				(user-error
-				 "A month cannot have more than 31 days")
-			      (number-to-string dd)))
-			   ;; Increment or decrement day by INC,
-			   ;; adjusting month and year if necessary
-			   ;; (if year is "*" assume current year to
-			   ;; calculate adjustment).
-			   (t
-			    (let* ((yy (or yy (calendar-extract-year
-					       (calendar-current-date))))
-				   (date (calendar-gregorian-from-absolute
-					  (+ (calendar-absolute-from-gregorian
-					      (list mm dd yy))
-                                             inc)))
-				   (adjmm (nth 0 date)))
-			      ;; Set year and month(name) to adjusted values.
-			      (unless (string= year "*")
-				(setq year (number-to-string (nth 2 date))))
-			      (if month
-				  (setq month (number-to-string adjmm))
-				(setq monthname (aref tma-array (1- adjmm))))
-			      ;; Return changed numerical day as a string.
-			      (number-to-string (nth 1 date)))))))))
+	      (save-match-data
+		(cond
+		 ((eq what 'date)
+		  (setq ndate (todo-read-date)))
+		 ((eq what 'calendar)
+		  (setq ndate (todo-set-date-from-calendar)))
+		 ((eq what 'today)
+		  (setq ndate (calendar-date-string (calendar-current-date) t t)))
+		 ((eq what 'dayname)
+		  (setq ndate (todo-read-dayname)))
+		 ((eq what 'time)
+		  (setq ntime (todo-read-time))
+		  (when (> (length ntime) 0)
+		    (setq ntime (concat " " ntime))))
+		 ;; When date string consists only of a day name,
+		 ;; passing other date components is a noop.
+		 ((and odayname (memq what '(year month day))))
+		 ((eq what 'year)
+		  (setq day oday
+			monthname omonthname
+			month omonth
+			year (cond ((not current-prefix-arg)
+				    (todo-read-date 'year))
+				   ((string= oyear "*")
+				    (user-error "Cannot increment *"))
+				   (t
+				    (number-to-string (+ yy inc))))))
+		 ((eq what 'month)
+		  (setf day oday
+			year oyear
+			(if (memq 'month calendar-date-display-form)
+			    month
+			  monthname)
+			(cond ((not current-prefix-arg)
+			       (todo-read-date 'month))
+			      ((or (string= omonth "*") (= mm 13))
+			       (user-error "Cannot increment *"))
+			      (t
+			       (let* ((mmo mm)
+                                      ;; Change by 12 or more months?
+                                      (bigincp (>= (abs inc) 12))
+                                      ;; Month number is in range 1..12.
+                                      (mminc (+ mm (% inc 12)))
+			              (mm (% (+ mminc 12) 12))
+			              ;; 12n mod 12 = 0, so 0 is December.
+			              (mm (if (= mm 0) 12 mm))
+                                      ;; Does change in month cross year?
+                                      (mmcmp (cond ((< inc 0) (> mm mmo))
+                                                   ((> inc 0) (< mm mmo))))
+                                      (yyadjust (if bigincp
+                                                    (+ (abs (/ inc 12))
+                                                       (if mmcmp 1 0))
+                                                  1)))
+				 ;; Adjust year if necessary.
+				 (setq yy (cond ((and (< inc 0)
+                                                      (or mmcmp bigincp))
+						 (- yy yyadjust))
+						((and (> inc 0)
+                                                      (or mmcmp bigincp))
+						 (+ yy yyadjust))
+						(t yy)))
+				 (setq year (number-to-string yy))
+				 ;; Return the changed numerical month as
+				 ;; a string or the corresponding month name.
+				 (if omonth
+				     (number-to-string mm)
+			           (aref tma-array (1- mm)))))))
+                  ;; Since the number corresponding to the arbitrary
+                  ;; month name "*" is out of the range of
+                  ;; calendar-last-day-of-month, set it to 1
+                  ;; (corresponding to January) to allow 31 days.
+                  (let ((mm (if (= mm 13) 1 mm)))
+		    (if (> (string-to-number day)
+			   (calendar-last-day-of-month mm yy))
+			(user-error "%s %s does not have %s days"
+				    (aref tmn-array (1- mm))
+				    (if (= mm 2) yy "") day))))
+		 ((eq what 'day)
+		  (setq year oyear
+			month omonth
+			monthname omonthname
+			day (cond
+			     ((not current-prefix-arg)
+			      (todo-read-date 'day mm yy))
+			     ((string= oday "*")
+			      (user-error "Cannot increment *"))
+			     ((or (string= omonth "*") (string= omonthname "*"))
+			      (setq dd (+ dd inc))
+			      (if (> dd 31)
+				  (user-error
+				   "A month cannot have more than 31 days")
+				(number-to-string dd)))
+			     ;; Increment or decrement day by INC,
+			     ;; adjusting month and year if necessary
+			     ;; (if year is "*" assume current year to
+			     ;; calculate adjustment).
+			     (t
+			      (let* ((yy (or yy (calendar-extract-year
+						 (calendar-current-date))))
+				     (date (calendar-gregorian-from-absolute
+					    (+ (calendar-absolute-from-gregorian
+						(list mm dd yy))
+                                               inc)))
+				     (adjmm (nth 0 date)))
+				;; Set year and month(name) to adjusted values.
+				(unless (string= year "*")
+				  (setq year (number-to-string (nth 2 date))))
+				(if month
+				    (setq month (number-to-string adjmm))
+				  (setq monthname (aref tma-array (1- adjmm))))
+				;; Return changed numerical day as a string.
+				(number-to-string (nth 1 date))))))))))
 	    (unless odayname
 	      ;; If year, month or day date string components were
 	      ;; changed, rebuild the date string.
