commit | author | age
|
76bbd0
|
1 |
;;; org-mouse.el --- Better mouse support for Org -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2006-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Piotr Zielinski <piotr dot zielinski at gmail dot com> |
|
6 |
;; Maintainer: Carsten Dominik <carsten at orgmode dot org> |
|
7 |
|
|
8 |
;; This file is part of GNU Emacs. |
|
9 |
|
|
10 |
;; GNU Emacs is free software: you can redistribute it and/or modify |
|
11 |
;; it under the terms of the GNU General Public License as published by |
|
12 |
;; the Free Software Foundation, either version 3 of the License, or |
|
13 |
;; (at your option) any later version. |
|
14 |
|
|
15 |
;; GNU Emacs is distributed in the hope that it will be useful, |
|
16 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
18 |
;; GNU General Public License for more details. |
|
19 |
|
|
20 |
;; You should have received a copy of the GNU General Public License |
|
21 |
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
|
22 |
|
|
23 |
;;; Commentary: |
|
24 |
;; |
|
25 |
;; Org-mouse provides mouse support for org-mode. |
|
26 |
;; |
|
27 |
;; https://orgmode.org |
|
28 |
;; |
|
29 |
;; Org mouse implements the following features: |
|
30 |
;; * following links with the left mouse button |
|
31 |
;; * subtree expansion/collapse (org-cycle) with the left mouse button |
|
32 |
;; * several context menus on the right mouse button: |
|
33 |
;; + general text |
|
34 |
;; + headlines |
|
35 |
;; + timestamps |
|
36 |
;; + priorities |
|
37 |
;; + links |
|
38 |
;; + tags |
|
39 |
;; * promoting/demoting/moving subtrees with mouse-3 |
|
40 |
;; + if the drag starts and ends in the same line then promote/demote |
|
41 |
;; + otherwise move the subtree |
|
42 |
;; |
|
43 |
;; Use |
|
44 |
;; --- |
|
45 |
;; |
|
46 |
;; To use this package, put the following line in your .emacs: |
|
47 |
;; |
|
48 |
;; (require 'org-mouse) |
|
49 |
;; |
|
50 |
|
|
51 |
;; FIXME: |
|
52 |
;; + deal with folding / unfolding issues |
|
53 |
|
|
54 |
;; TODO (This list is only theoretical, if you'd like to have some |
|
55 |
;; feature implemented or a bug fix please send me an email, even if |
|
56 |
;; something similar appears in the list below. This will help me get |
|
57 |
;; the priorities right.): |
|
58 |
;; |
|
59 |
;; + org-store-link, insert link |
|
60 |
;; + org tables |
|
61 |
;; + occur with the current word/tag (same menu item) |
|
62 |
;; + ctrl-c ctrl-c, for example, renumber the current list |
|
63 |
;; + internal links |
|
64 |
|
|
65 |
;; Please email the maintainer with new feature suggestions / bugs |
|
66 |
|
|
67 |
;; History: |
|
68 |
;; |
|
69 |
;; Since version 5.10: Changes are listed in the general Org docs. |
|
70 |
;; |
|
71 |
;; Version 5.09;; + Version number synchronization with Org mode. |
|
72 |
;; |
|
73 |
;; Version 0.25 |
|
74 |
;; + made compatible with Org 4.70 (thanks to Carsten for the patch) |
|
75 |
;; |
|
76 |
;; Version 0.24 |
|
77 |
;; + minor changes to the table menu |
|
78 |
;; |
|
79 |
;; Version 0.23 |
|
80 |
;; + preliminary support for tables and calculation marks |
|
81 |
;; + context menu support for org-agenda-undo & org-sort-entries |
|
82 |
;; |
|
83 |
;; Version 0.22 |
|
84 |
;; + handles undo support for the agenda buffer (requires Org >=4.58) |
|
85 |
;; |
|
86 |
;; Version 0.21 |
|
87 |
;; + selected text activates its context menu |
|
88 |
;; + shift-middleclick or right-drag inserts the text from the clipboard in the form of a link |
|
89 |
;; |
|
90 |
;; Version 0.20 |
|
91 |
;; + the new "TODO Status" submenu replaces the "Cycle TODO" menu item |
|
92 |
;; + the TODO menu can now list occurrences of a specific TODO keyword |
|
93 |
;; + #+STARTUP line is now recognized |
|
94 |
;; |
|
95 |
;; Version 0.19 |
|
96 |
;; + added support for dragging URLs to the org-buffer |
|
97 |
;; |
|
98 |
;; Version 0.18 |
|
99 |
;; + added support for agenda blocks |
|
100 |
;; |
|
101 |
;; Version 0.17 |
|
102 |
;; + toggle checkboxes with a single click |
|
103 |
;; |
|
104 |
;; Version 0.16 |
|
105 |
;; + added support for checkboxes |
|
106 |
;; |
|
107 |
;; Version 0.15 |
|
108 |
;; + Org now works with the Agenda buffer as well |
|
109 |
;; |
|
110 |
;; Version 0.14 |
|
111 |
;; + added a menu option that converts plain list items to outline items |
|
112 |
;; |
|
113 |
;; Version 0.13 |
|
114 |
;; + "Insert Heading" now inserts a sibling heading if the point is |
|
115 |
;; on "***" and a child heading otherwise |
|
116 |
;; |
|
117 |
;; Version 0.12 |
|
118 |
;; + compatible with Emacs 21 |
|
119 |
;; + custom agenda commands added to the main menu |
|
120 |
;; + moving trees should now work between windows in the same frame |
|
121 |
;; |
|
122 |
;; Version 0.11 |
|
123 |
;; + fixed org-mouse-at-link (thanks to Carsten) |
|
124 |
;; + removed [follow-link] bindings |
|
125 |
;; |
|
126 |
;; Version 0.10 |
|
127 |
;; + added a menu option to remove highlights |
|
128 |
;; + compatible with Org 4.21 now |
|
129 |
;; |
|
130 |
;; Version 0.08: |
|
131 |
;; + trees can be moved/promoted/demoted by dragging with the right |
|
132 |
;; mouse button (mouse-3) |
|
133 |
;; + small changes in the above function |
|
134 |
;; |
|
135 |
;; Versions 0.01 -- 0.07: (I don't remember) |
|
136 |
|
|
137 |
;;; Code: |
|
138 |
|
|
139 |
(require 'org) |
|
140 |
(require 'cl-lib) |
|
141 |
|
|
142 |
(defvar org-agenda-allow-remote-undo) |
|
143 |
(defvar org-agenda-undo-list) |
|
144 |
(defvar org-agenda-custom-commands) |
|
145 |
(declare-function org-agenda-change-all-lines "org-agenda" |
|
146 |
(newhead hdmarker &optional fixface just-this)) |
|
147 |
(declare-function org-verify-change-for-undo "org-agenda" (l1 l2)) |
|
148 |
(declare-function org-apply-on-list "org-list" (function init-value &rest args)) |
|
149 |
(declare-function org-agenda-earlier "org-agenda" (arg)) |
|
150 |
(declare-function org-agenda-later "org-agenda" (arg)) |
|
151 |
|
|
152 |
(defvar org-mouse-main-buffer nil |
|
153 |
"Active buffer for mouse operations.") |
|
154 |
(defvar org-mouse-plain-list-regexp "\\([ \t]*\\)\\([-+*]\\|[0-9]+[.)]\\) " |
|
155 |
"Regular expression that matches a plain list.") |
|
156 |
(defvar org-mouse-direct t |
|
157 |
"Internal variable indicating whether the current action is direct. |
|
158 |
|
|
159 |
If t, then the current action has been invoked directly through the buffer |
|
160 |
it is intended to operate on. If nil, then the action has been invoked |
|
161 |
indirectly, for example, through the agenda buffer.") |
|
162 |
|
|
163 |
(defgroup org-mouse nil |
|
164 |
"Mouse support for org-mode." |
|
165 |
:tag "Org Mouse" |
|
166 |
:group 'org) |
|
167 |
|
|
168 |
(defcustom org-mouse-punctuation ":" |
|
169 |
"Punctuation used when inserting text by drag and drop." |
|
170 |
:group 'org-mouse |
|
171 |
:type 'string) |
|
172 |
|
|
173 |
(defcustom org-mouse-features |
|
174 |
'(context-menu yank-link activate-stars activate-bullets activate-checkboxes) |
|
175 |
"The features of org-mouse that should be activated. |
|
176 |
Changing this variable requires a restart of Emacs to get activated." |
|
177 |
:group 'org-mouse |
|
178 |
:type '(set :greedy t |
|
179 |
(const :tag "Mouse-3 shows context menu" context-menu) |
|
180 |
(const :tag "C-mouse-1 and mouse-3 move trees" move-tree) |
|
181 |
(const :tag "S-mouse-2 and drag-mouse-3 yank link" yank-link) |
|
182 |
(const :tag "Activate headline stars" activate-stars) |
|
183 |
(const :tag "Activate item bullets" activate-bullets) |
|
184 |
(const :tag "Activate checkboxes" activate-checkboxes))) |
|
185 |
|
|
186 |
(defun org-mouse-re-search-line (regexp) |
|
187 |
"Search the current line for a given regular expression." |
|
188 |
(beginning-of-line) |
|
189 |
(re-search-forward regexp (point-at-eol) t)) |
|
190 |
|
|
191 |
(defun org-mouse-end-headline () |
|
192 |
"Go to the end of current headline (ignoring tags)." |
|
193 |
(interactive) |
|
194 |
(end-of-line) |
|
195 |
(skip-chars-backward "\t ") |
|
196 |
(when (looking-back ":[A-Za-z]+:" (line-beginning-position)) |
|
197 |
(skip-chars-backward ":A-Za-z") |
|
198 |
(skip-chars-backward "\t "))) |
|
199 |
|
|
200 |
(defvar-local org-mouse-context-menu-function nil |
|
201 |
"Function to create the context menu. |
|
202 |
The value of this variable is the function invoked by |
|
203 |
`org-mouse-context-menu' as the context menu.") |
|
204 |
|
|
205 |
(defun org-mouse-show-context-menu (event prefix) |
|
206 |
"Invoke the context menu. |
|
207 |
|
|
208 |
If the value of `org-mouse-context-menu-function' is a function, then |
|
209 |
this function is called. Otherwise, the current major mode menu is used." |
|
210 |
(interactive "@e \nP") |
|
211 |
(if (and (= (event-click-count event) 1) |
|
212 |
(or (not mark-active) |
|
213 |
(sit-for (/ double-click-time 1000.0)))) |
|
214 |
(progn |
|
215 |
(select-window (posn-window (event-start event))) |
|
216 |
(when (not (org-mouse-mark-active)) |
|
217 |
(goto-char (posn-point (event-start event))) |
|
218 |
(when (not (eolp)) (save-excursion (run-hooks 'post-command-hook))) |
|
219 |
(sit-for 0)) |
|
220 |
(if (functionp org-mouse-context-menu-function) |
|
221 |
(funcall org-mouse-context-menu-function event) |
|
222 |
(if (fboundp 'mouse-menu-major-mode-map) |
|
223 |
(popup-menu (mouse-menu-major-mode-map) event prefix) |
|
224 |
(with-no-warnings ; don't warn about fallback, obsolete since 23.1 |
|
225 |
(mouse-major-mode-menu event prefix))))) |
|
226 |
(setq this-command 'mouse-save-then-kill) |
|
227 |
(mouse-save-then-kill event))) |
|
228 |
|
|
229 |
(defun org-mouse-line-position () |
|
230 |
"Return `:beginning' or `:middle' or `:end', depending on the point position. |
|
231 |
|
|
232 |
If the point is at the end of the line, return `:end'. |
|
233 |
If the point is separated from the beginning of the line only by white |
|
234 |
space and *'s (`org-mouse-bolp'), return `:beginning'. Otherwise, |
|
235 |
return `:middle'." |
|
236 |
(cond |
|
237 |
((eolp) :end) |
|
238 |
((org-mouse-bolp) :beginning) |
|
239 |
(t :middle))) |
|
240 |
|
|
241 |
(defun org-mouse-empty-line () |
|
242 |
"Return non-nil iff the line contains only white space." |
|
243 |
(save-excursion (beginning-of-line) (looking-at "[ \t]*$"))) |
|
244 |
|
|
245 |
(defun org-mouse-next-heading () |
|
246 |
"Go to the next heading. |
|
247 |
If there is none, ensure that the point is at the beginning of an empty line." |
|
248 |
(unless (outline-next-heading) |
|
249 |
(beginning-of-line) |
|
250 |
(unless (org-mouse-empty-line) |
|
251 |
(end-of-line) |
|
252 |
(newline)))) |
|
253 |
|
|
254 |
(defun org-mouse-insert-heading () |
|
255 |
"Insert a new heading, as `org-insert-heading'. |
|
256 |
|
|
257 |
If the point is at the :beginning (`org-mouse-line-position') of the line, |
|
258 |
insert the new heading before the current line. Otherwise, insert it |
|
259 |
after the current heading." |
|
260 |
(interactive) |
|
261 |
(cl-case (org-mouse-line-position) |
|
262 |
(:beginning (beginning-of-line) |
|
263 |
(org-insert-heading)) |
|
264 |
(t (org-mouse-next-heading) |
|
265 |
(org-insert-heading)))) |
|
266 |
|
|
267 |
(defun org-mouse-timestamp-today (&optional shift units) |
|
268 |
"Change the timestamp into SHIFT UNITS in the future. |
|
269 |
|
|
270 |
For the acceptable UNITS, see `org-timestamp-change'." |
|
271 |
(interactive) |
|
272 |
(org-time-stamp nil) |
|
273 |
(when shift (org-timestamp-change shift units))) |
|
274 |
|
|
275 |
(defun org-mouse-keyword-menu (keywords function &optional selected itemformat) |
|
276 |
"A helper function. |
|
277 |
|
|
278 |
Returns a menu fragment consisting of KEYWORDS. When a keyword |
|
279 |
is selected by the user, FUNCTION is called with the selected |
|
280 |
keyword as the only argument. |
|
281 |
|
|
282 |
If SELECTED is nil, then all items are normal menu items. If |
|
283 |
SELECTED is a function, then each item is a checkbox, which is |
|
284 |
enabled for a given keyword iff (funcall SELECTED keyword) return |
|
285 |
non-nil. If SELECTED is neither nil nor a function, then the |
|
286 |
items are radio buttons. A radio button is enabled for the |
|
287 |
keyword `equal' to SELECTED. |
|
288 |
|
|
289 |
ITEMFORMAT governs formatting of the elements of KEYWORDS. If it |
|
290 |
is a function, it is invoked with the keyword as the only |
|
291 |
argument. If it is a string, it is interpreted as the format |
|
292 |
string to (format ITEMFORMAT keyword). If it is neither a string |
|
293 |
nor a function, elements of KEYWORDS are used directly." |
|
294 |
(mapcar |
|
295 |
`(lambda (keyword) |
|
296 |
(vector (cond |
|
297 |
((functionp ,itemformat) (funcall ,itemformat keyword)) |
|
298 |
((stringp ,itemformat) (format ,itemformat keyword)) |
|
299 |
(t keyword)) |
|
300 |
(list 'funcall ,function keyword) |
|
301 |
:style (cond |
|
302 |
((null ,selected) t) |
|
303 |
((functionp ,selected) 'toggle) |
|
304 |
(t 'radio)) |
|
305 |
:selected (if (functionp ,selected) |
|
306 |
(and (funcall ,selected keyword) t) |
|
307 |
(equal ,selected keyword)))) |
|
308 |
keywords)) |
|
309 |
|
|
310 |
(defun org-mouse-remove-match-and-spaces () |
|
311 |
"Remove the match, make just one space around the point." |
|
312 |
(interactive) |
|
313 |
(replace-match "") |
|
314 |
(just-one-space)) |
|
315 |
|
|
316 |
(defvar org-mouse-rest) |
|
317 |
(defun org-mouse-replace-match-and-surround |
|
318 |
(_newtext &optional _fixedcase _literal _string subexp) |
|
319 |
"The same as `replace-match', but surrounds the replacement with spaces." |
|
320 |
(apply #'replace-match org-mouse-rest) |
|
321 |
(save-excursion |
|
322 |
(goto-char (match-beginning (or subexp 0))) |
|
323 |
(just-one-space) |
|
324 |
(goto-char (match-end (or subexp 0))) |
|
325 |
(just-one-space))) |
|
326 |
|
|
327 |
(defun org-mouse-keyword-replace-menu (keywords &optional group itemformat |
|
328 |
nosurround) |
|
329 |
"A helper function. |
|
330 |
|
|
331 |
Returns a menu fragment consisting of KEYWORDS. When a keyword |
|
332 |
is selected, group GROUP of the current match is replaced by the |
|
333 |
keyword. The method ensures that both ends of the replacement |
|
334 |
are separated from the rest of the text in the buffer by |
|
335 |
individual spaces (unless NOSURROUND is non-nil). |
|
336 |
|
|
337 |
The final entry of the menu is always \"None\", which removes the |
|
338 |
match. |
|
339 |
|
|
340 |
ITEMFORMAT governs formatting of the elements of KEYWORDS. If it |
|
341 |
is a function, it is invoked with the keyword as the only |
|
342 |
argument. If it is a string, it is interpreted as the format |
|
343 |
string to (format ITEMFORMAT keyword). If it is neither a string |
|
344 |
nor a function, elements of KEYWORDS are used directly." |
|
345 |
(setq group (or group 0)) |
|
346 |
(let ((replace (org-mouse-match-closure |
|
347 |
(if nosurround 'replace-match |
|
348 |
'org-mouse-replace-match-and-surround)))) |
|
349 |
(append |
|
350 |
(org-mouse-keyword-menu |
|
351 |
keywords |
|
352 |
`(lambda (keyword) (funcall ,replace keyword t t nil ,group)) |
|
353 |
(match-string group) |
|
354 |
itemformat) |
|
355 |
`(["None" org-mouse-remove-match-and-spaces |
|
356 |
:style radio |
|
357 |
:selected ,(not (member (match-string group) keywords))])))) |
|
358 |
|
|
359 |
(defun org-mouse-show-headlines () |
|
360 |
"Change the visibility of the current org buffer to only show headlines." |
|
361 |
(interactive) |
|
362 |
(let ((this-command 'org-cycle) |
|
363 |
(last-command 'org-cycle) |
|
364 |
(org-cycle-global-status nil)) |
|
365 |
(org-cycle '(4)) |
|
366 |
(org-cycle '(4)))) |
|
367 |
|
|
368 |
(defun org-mouse-show-overview () |
|
369 |
"Change visibility of current org buffer to first-level headlines only." |
|
370 |
(interactive) |
|
371 |
(let ((org-cycle-global-status nil)) |
|
372 |
(org-cycle '(4)))) |
|
373 |
|
|
374 |
(defun org-mouse-set-priority (priority) |
|
375 |
"Set the priority of the current headline to PRIORITY." |
|
376 |
(org-priority priority)) |
|
377 |
|
|
378 |
(defvar org-mouse-priority-regexp "\\[#\\([A-Z]\\)\\]" |
|
379 |
"Regular expression matching the priority indicator. |
|
380 |
Differs from `org-priority-regexp' in that it doesn't contain the |
|
381 |
leading `.*?'.") |
|
382 |
|
|
383 |
(defun org-mouse-get-priority (&optional default) |
|
384 |
"Return the priority of the current headline. |
|
385 |
DEFAULT is returned if no priority is given in the headline." |
|
386 |
(save-excursion |
|
387 |
(if (org-mouse-re-search-line org-mouse-priority-regexp) |
|
388 |
(match-string 1) |
|
389 |
(when default (char-to-string org-default-priority))))) |
|
390 |
|
|
391 |
(defun org-mouse-delete-timestamp () |
|
392 |
"Deletes the current timestamp as well as the preceding keyword. |
|
393 |
SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:" |
|
394 |
(when (or (org-at-date-range-p) (org-at-timestamp-p 'lax)) |
|
395 |
(replace-match "") ;delete the timestamp |
|
396 |
(skip-chars-backward " :A-Z") |
|
397 |
(when (looking-at " *[A-Z][A-Z]+:") |
|
398 |
(replace-match "")))) |
|
399 |
|
|
400 |
(defun org-mouse-looking-at (regexp skipchars &optional movechars) |
|
401 |
(save-excursion |
|
402 |
(let ((point (point))) |
|
403 |
(if (looking-at regexp) t |
|
404 |
(skip-chars-backward skipchars) |
|
405 |
(forward-char (or movechars 0)) |
|
406 |
(when (looking-at regexp) |
|
407 |
(> (match-end 0) point)))))) |
|
408 |
|
|
409 |
(defun org-mouse-priority-list () |
|
410 |
(cl-loop for priority from ?A to org-lowest-priority |
|
411 |
collect (char-to-string priority))) |
|
412 |
|
|
413 |
(defun org-mouse-todo-menu (state) |
|
414 |
"Create the menu with TODO keywords." |
|
415 |
(append |
|
416 |
(let ((kwds org-todo-keywords-1)) |
|
417 |
(org-mouse-keyword-menu |
|
418 |
kwds |
|
419 |
`(lambda (kwd) (org-todo kwd)) |
|
420 |
(lambda (kwd) (equal state kwd)))))) |
|
421 |
|
|
422 |
(defun org-mouse-tag-menu () ;todo |
|
423 |
"Create the tags menu." |
|
424 |
(append |
|
425 |
(let ((tags (org-get-tags))) |
|
426 |
(org-mouse-keyword-menu |
|
427 |
(sort (mapcar 'car (org-get-buffer-tags)) 'string-lessp) |
|
428 |
`(lambda (tag) |
|
429 |
(org-mouse-set-tags |
|
430 |
(sort (if (member tag (quote ,tags)) |
|
431 |
(delete tag (quote ,tags)) |
|
432 |
(cons tag (quote ,tags))) |
|
433 |
'string-lessp))) |
|
434 |
`(lambda (tag) (member tag (quote ,tags))) |
|
435 |
)) |
|
436 |
'("--" |
|
437 |
["Align Tags Here" (org-set-tags nil t) t] |
|
438 |
["Align Tags in Buffer" (org-set-tags t t) t] |
|
439 |
["Set Tags ..." (org-set-tags) t]))) |
|
440 |
|
|
441 |
(defun org-mouse-set-tags (tags) |
|
442 |
(save-excursion |
|
443 |
;; remove existing tags first |
|
444 |
(beginning-of-line) |
|
445 |
(when (org-mouse-re-search-line ":\\(\\([A-Za-z_]+:\\)+\\)") |
|
446 |
(replace-match "")) |
|
447 |
|
|
448 |
;; set new tags if any |
|
449 |
(when tags |
|
450 |
(end-of-line) |
|
451 |
(insert " :" (mapconcat 'identity tags ":") ":") |
|
452 |
(org-set-tags nil t)))) |
|
453 |
|
|
454 |
(defun org-mouse-insert-checkbox () |
|
455 |
(interactive) |
|
456 |
(and (org-at-item-p) |
|
457 |
(goto-char (match-end 0)) |
|
458 |
(unless (org-at-item-checkbox-p) |
|
459 |
(delete-horizontal-space) |
|
460 |
(insert " [ ] ")))) |
|
461 |
|
|
462 |
(defun org-mouse-agenda-type (type) |
|
463 |
(pcase type |
|
464 |
(`tags "Tags: ") |
|
465 |
(`todo "TODO: ") |
|
466 |
(`tags-tree "Tags tree: ") |
|
467 |
(`todo-tree "TODO tree: ") |
|
468 |
(`occur-tree "Occur tree: ") |
|
469 |
(_ "Agenda command ???"))) |
|
470 |
|
|
471 |
(defun org-mouse-list-options-menu (alloptions &optional function) |
|
472 |
(let ((options (save-match-data |
|
473 |
(split-string (match-string-no-properties 1))))) |
|
474 |
(print options) |
|
475 |
(cl-loop for name in alloptions |
|
476 |
collect |
|
477 |
(vector name |
|
478 |
`(progn |
|
479 |
(replace-match |
|
480 |
(mapconcat 'identity |
|
481 |
(sort (if (member ',name ',options) |
|
482 |
(delete ',name ',options) |
|
483 |
(cons ',name ',options)) |
|
484 |
'string-lessp) |
|
485 |
" ") |
|
486 |
nil nil nil 1) |
|
487 |
(when (functionp ',function) (funcall ',function))) |
|
488 |
:style 'toggle |
|
489 |
:selected (and (member name options) t))))) |
|
490 |
|
|
491 |
(defun org-mouse-clip-text (text maxlength) |
|
492 |
(if (> (length text) maxlength) |
|
493 |
(concat (substring text 0 (- maxlength 3)) "...") |
|
494 |
text)) |
|
495 |
|
|
496 |
(defun org-mouse-popup-global-menu () |
|
497 |
(popup-menu |
|
498 |
`("Main Menu" |
|
499 |
["Show Overview" org-mouse-show-overview t] |
|
500 |
["Show Headlines" org-mouse-show-headlines t] |
|
501 |
["Show All" outline-show-all t] |
|
502 |
["Remove Highlights" org-remove-occur-highlights |
|
503 |
:visible org-occur-highlights] |
|
504 |
"--" |
|
505 |
["Check Deadlines" |
|
506 |
(if (functionp 'org-check-deadlines-and-todos) |
|
507 |
(org-check-deadlines-and-todos org-deadline-warning-days) |
|
508 |
(org-check-deadlines org-deadline-warning-days)) t] |
|
509 |
["Check TODOs" org-show-todo-tree t] |
|
510 |
("Check Tags" |
|
511 |
,@(org-mouse-keyword-menu |
|
512 |
(sort (mapcar 'car (org-get-buffer-tags)) 'string-lessp) |
|
513 |
#'(lambda (tag) (org-tags-sparse-tree nil tag))) |
|
514 |
"--" |
|
515 |
["Custom Tag ..." org-tags-sparse-tree t]) |
|
516 |
["Check Phrase ..." org-occur] |
|
517 |
"--" |
|
518 |
["Display Agenda" org-agenda-list t] |
|
519 |
["Display TODO List" org-todo-list t] |
|
520 |
("Display Tags" |
|
521 |
,@(org-mouse-keyword-menu |
|
522 |
(sort (mapcar 'car (org-get-buffer-tags)) 'string-lessp) |
|
523 |
#'(lambda (tag) (org-tags-view nil tag))) |
|
524 |
"--" |
|
525 |
["Custom Tag ..." org-tags-view t]) |
|
526 |
["Display Calendar" org-goto-calendar t] |
|
527 |
"--" |
|
528 |
,@(org-mouse-keyword-menu |
|
529 |
(mapcar 'car org-agenda-custom-commands) |
|
530 |
#'(lambda (key) |
|
531 |
(eval `(org-agenda nil (string-to-char ,key)))) |
|
532 |
nil |
|
533 |
#'(lambda (key) |
|
534 |
(let ((entry (assoc key org-agenda-custom-commands))) |
|
535 |
(org-mouse-clip-text |
|
536 |
(cond |
|
537 |
((stringp (nth 1 entry)) (nth 1 entry)) |
|
538 |
((stringp (nth 2 entry)) |
|
539 |
(concat (org-mouse-agenda-type (nth 1 entry)) |
|
540 |
(nth 2 entry))) |
|
541 |
(t "Agenda Command `%s'")) |
|
542 |
30)))) |
|
543 |
"--" |
|
544 |
["Delete Blank Lines" delete-blank-lines |
|
545 |
:visible (org-mouse-empty-line)] |
|
546 |
["Insert Checkbox" org-mouse-insert-checkbox |
|
547 |
:visible (and (org-at-item-p) (not (org-at-item-checkbox-p)))] |
|
548 |
["Insert Checkboxes" |
|
549 |
(org-mouse-for-each-item 'org-mouse-insert-checkbox) |
|
550 |
:visible (and (org-at-item-p) (not (org-at-item-checkbox-p)))] |
|
551 |
["Plain List to Outline" org-mouse-transform-to-outline |
|
552 |
:visible (org-at-item-p)]))) |
|
553 |
|
|
554 |
(defun org-mouse-get-context (contextlist context) |
|
555 |
(let ((contextdata (assq context contextlist))) |
|
556 |
(when contextdata |
|
557 |
(save-excursion |
|
558 |
(goto-char (nth 1 contextdata)) |
|
559 |
(re-search-forward ".*" (nth 2 contextdata)))))) |
|
560 |
|
|
561 |
(defun org-mouse-for-each-item (funct) |
|
562 |
;; Functions called by `org-apply-on-list' need an argument. |
|
563 |
(let ((wrap-fun (lambda (_) (funcall funct)))) |
|
564 |
(when (ignore-errors (goto-char (org-in-item-p))) |
|
565 |
(save-excursion (org-apply-on-list wrap-fun nil))))) |
|
566 |
|
|
567 |
(defun org-mouse-bolp () |
|
568 |
"Return true if there only spaces, tabs, and `*' before point. |
|
569 |
This means, between the beginning of line and the point." |
|
570 |
(save-excursion |
|
571 |
(skip-chars-backward " \t*") (bolp))) |
|
572 |
|
|
573 |
(defun org-mouse-insert-item (text) |
|
574 |
(cl-case (org-mouse-line-position) |
|
575 |
(:beginning ; insert before |
|
576 |
(beginning-of-line) |
|
577 |
(looking-at "[ \t]*") |
|
578 |
(open-line 1) |
|
579 |
(indent-to-column (- (match-end 0) (match-beginning 0))) |
|
580 |
(insert "+ ")) |
|
581 |
(:middle ; insert after |
|
582 |
(end-of-line) |
|
583 |
(newline t) |
|
584 |
(indent-relative) |
|
585 |
(insert "+ ")) |
|
586 |
(:end ; insert text here |
|
587 |
(skip-chars-backward " \t") |
|
588 |
(kill-region (point) (point-at-eol)) |
|
589 |
(unless (looking-back org-mouse-punctuation (line-beginning-position)) |
|
590 |
(insert (concat org-mouse-punctuation " "))))) |
|
591 |
(insert text) |
|
592 |
(beginning-of-line)) |
|
593 |
|
|
594 |
(defadvice dnd-insert-text (around org-mouse-dnd-insert-text activate) |
|
595 |
(if (derived-mode-p 'org-mode) |
|
596 |
(org-mouse-insert-item text) |
|
597 |
ad-do-it)) |
|
598 |
|
|
599 |
(defadvice dnd-open-file (around org-mouse-dnd-open-file activate) |
|
600 |
(if (derived-mode-p 'org-mode) |
|
601 |
(org-mouse-insert-item uri) |
|
602 |
ad-do-it)) |
|
603 |
|
|
604 |
(defun org-mouse-match-closure (function) |
|
605 |
(let ((match (match-data t))) |
|
606 |
`(lambda (&rest rest) |
|
607 |
(save-match-data |
|
608 |
(set-match-data ',match) |
|
609 |
(apply ',function rest))))) |
|
610 |
|
|
611 |
(defun org-mouse-yank-link (click) |
|
612 |
(interactive "e") |
|
613 |
;; Give temporary modes such as isearch a chance to turn off. |
|
614 |
(run-hooks 'mouse-leave-buffer-hook) |
|
615 |
(mouse-set-point click) |
|
616 |
(setq mouse-selection-click-count 0) |
|
617 |
(delete-horizontal-space) |
|
618 |
(insert-for-yank (concat " [[" (current-kill 0) "]] "))) |
|
619 |
|
|
620 |
(defun org-mouse-context-menu (&optional event) |
|
621 |
(let* ((stamp-prefixes (list org-deadline-string org-scheduled-string)) |
|
622 |
(contextlist (org-context)) |
|
623 |
(get-context (lambda (context) (org-mouse-get-context contextlist context)))) |
|
624 |
(cond |
|
625 |
((org-mouse-mark-active) |
|
626 |
(let ((region-string (buffer-substring (region-beginning) (region-end)))) |
|
627 |
(popup-menu |
|
628 |
`(nil |
|
629 |
["Sparse Tree" (org-occur ',region-string)] |
|
630 |
["Find in Buffer" (occur ',region-string)] |
|
631 |
["Grep in Current Dir" |
|
632 |
(grep (format "grep -rnH -e '%s' *" ',region-string))] |
|
633 |
["Grep in Parent Dir" |
|
634 |
(grep (format "grep -rnH -e '%s' ../*" ',region-string))] |
|
635 |
"--" |
|
636 |
["Convert to Link" |
|
637 |
(progn (save-excursion (goto-char (region-beginning)) (insert "[[")) |
|
638 |
(save-excursion (goto-char (region-end)) (insert "]]")))] |
|
639 |
["Insert Link Here" (org-mouse-yank-link ',event)])))) |
|
640 |
((save-excursion (beginning-of-line) (looking-at "[ \t]*#\\+STARTUP: \\(.*\\)")) |
|
641 |
(popup-menu |
|
642 |
`(nil |
|
643 |
,@(org-mouse-list-options-menu (mapcar 'car org-startup-options) |
|
644 |
'org-mode-restart)))) |
|
645 |
((or (eolp) |
|
646 |
(and (looking-at "\\( \\|\t\\)\\(+:[0-9a-zA-Z_:]+\\)?\\( \\|\t\\)+$") |
|
647 |
(looking-back " \\|\t" (- (point) 2) |
|
648 |
(line-beginning-position)))) |
|
649 |
(org-mouse-popup-global-menu)) |
|
650 |
((funcall get-context :checkbox) |
|
651 |
(popup-menu |
|
652 |
'(nil |
|
653 |
["Toggle" org-toggle-checkbox t] |
|
654 |
["Remove" org-mouse-remove-match-and-spaces t] |
|
655 |
"" |
|
656 |
["All Clear" (org-mouse-for-each-item |
|
657 |
(lambda () |
|
658 |
(when (save-excursion (org-at-item-checkbox-p)) |
|
659 |
(replace-match "[ ] "))))] |
|
660 |
["All Set" (org-mouse-for-each-item |
|
661 |
(lambda () |
|
662 |
(when (save-excursion (org-at-item-checkbox-p)) |
|
663 |
(replace-match "[X] "))))] |
|
664 |
["All Toggle" (org-mouse-for-each-item 'org-toggle-checkbox) t] |
|
665 |
["All Remove" (org-mouse-for-each-item |
|
666 |
(lambda () |
|
667 |
(when (save-excursion (org-at-item-checkbox-p)) |
|
668 |
(org-mouse-remove-match-and-spaces))))] |
|
669 |
))) |
|
670 |
((and (org-mouse-looking-at "\\b\\w+" "a-zA-Z0-9_") |
|
671 |
(member (match-string 0) org-todo-keywords-1)) |
|
672 |
(popup-menu |
|
673 |
`(nil |
|
674 |
,@(org-mouse-todo-menu (match-string 0)) |
|
675 |
"--" |
|
676 |
["Check TODOs" org-show-todo-tree t] |
|
677 |
["List all TODO keywords" org-todo-list t] |
|
678 |
[,(format "List only %s" (match-string 0)) |
|
679 |
(org-todo-list (match-string 0)) t] |
|
680 |
))) |
|
681 |
((and (org-mouse-looking-at "\\b[A-Z]+:" "A-Z") |
|
682 |
(member (match-string 0) stamp-prefixes)) |
|
683 |
(popup-menu |
|
684 |
`(nil |
|
685 |
,@(org-mouse-keyword-replace-menu stamp-prefixes) |
|
686 |
"--" |
|
687 |
["Check Deadlines" org-check-deadlines t] |
|
688 |
))) |
|
689 |
((org-mouse-looking-at org-mouse-priority-regexp "[]A-Z#") ; priority |
|
690 |
(popup-menu `(nil ,@(org-mouse-keyword-replace-menu |
|
691 |
(org-mouse-priority-list) 1 "Priority %s" t)))) |
|
692 |
((funcall get-context :link) |
|
693 |
(popup-menu |
|
694 |
'(nil |
|
695 |
["Open" org-open-at-point t] |
|
696 |
["Open in Emacs" (org-open-at-point t) t] |
|
697 |
"--" |
|
698 |
["Copy link" (org-kill-new (match-string 0))] |
|
699 |
["Cut link" |
|
700 |
(progn |
|
701 |
(kill-region (match-beginning 0) (match-end 0)) |
|
702 |
(just-one-space))] |
|
703 |
"--" |
|
704 |
["Grep for TODOs" |
|
705 |
(grep (format "grep -nH -i 'todo\\|fixme' %s*" (match-string 2)))] |
|
706 |
; ["Paste file link" ((insert "file:") (yank))] |
|
707 |
))) |
|
708 |
((org-mouse-looking-at ":\\([A-Za-z0-9_]+\\):" "A-Za-z0-9_" -1) ;tags |
|
709 |
(popup-menu |
|
710 |
`(nil |
|
711 |
[,(format-message "Display `%s'" (match-string 1)) |
|
712 |
(org-tags-view nil ,(match-string 1))] |
|
713 |
[,(format-message "Sparse Tree `%s'" (match-string 1)) |
|
714 |
(org-tags-sparse-tree nil ,(match-string 1))] |
|
715 |
"--" |
|
716 |
,@(org-mouse-tag-menu)))) |
|
717 |
((org-at-timestamp-p 'lax) |
|
718 |
(popup-menu |
|
719 |
'(nil |
|
720 |
["Show Day" org-open-at-point t] |
|
721 |
["Change Timestamp" org-time-stamp t] |
|
722 |
["Delete Timestamp" (org-mouse-delete-timestamp) t] |
|
723 |
["Compute Time Range" org-evaluate-time-range (org-at-date-range-p)] |
|
724 |
"--" |
|
725 |
["Set for Today" org-mouse-timestamp-today] |
|
726 |
["Set for Tomorrow" (org-mouse-timestamp-today 1 'day)] |
|
727 |
["Set in 1 Week" (org-mouse-timestamp-today 7 'day)] |
|
728 |
["Set in 2 Weeks" (org-mouse-timestamp-today 14 'day)] |
|
729 |
["Set in a Month" (org-mouse-timestamp-today 1 'month)] |
|
730 |
"--" |
|
731 |
["+ 1 Day" (org-timestamp-change 1 'day)] |
|
732 |
["+ 1 Week" (org-timestamp-change 7 'day)] |
|
733 |
["+ 1 Month" (org-timestamp-change 1 'month)] |
|
734 |
"--" |
|
735 |
["- 1 Day" (org-timestamp-change -1 'day)] |
|
736 |
["- 1 Week" (org-timestamp-change -7 'day)] |
|
737 |
["- 1 Month" (org-timestamp-change -1 'month)]))) |
|
738 |
((funcall get-context :table-special) |
|
739 |
(let ((mdata (match-data))) |
|
740 |
(cl-incf (car mdata) 2) |
|
741 |
(store-match-data mdata)) |
|
742 |
(message "match: %S" (match-string 0)) |
|
743 |
(popup-menu `(nil ,@(org-mouse-keyword-replace-menu |
|
744 |
'(" " "!" "^" "_" "$" "#" "*" "'") 0 |
|
745 |
(lambda (mark) |
|
746 |
(cl-case (string-to-char mark) |
|
747 |
(? "( ) Nothing Special") |
|
748 |
(?! "(!) Column Names") |
|
749 |
(?^ "(^) Field Names Above") |
|
750 |
(?_ "(^) Field Names Below") |
|
751 |
(?$ "($) Formula Parameters") |
|
752 |
(?# "(#) Recalculation: Auto") |
|
753 |
(?* "(*) Recalculation: Manual") |
|
754 |
(?' "(') Recalculation: None"))) t)))) |
|
755 |
((assq :table contextlist) |
|
756 |
(popup-menu |
|
757 |
'(nil |
|
758 |
["Align Table" org-ctrl-c-ctrl-c] |
|
759 |
["Blank Field" org-table-blank-field] |
|
760 |
["Edit Field" org-table-edit-field] |
|
761 |
"--" |
|
762 |
("Column" |
|
763 |
["Move Column Left" org-metaleft] |
|
764 |
["Move Column Right" org-metaright] |
|
765 |
["Delete Column" org-shiftmetaleft] |
|
766 |
["Insert Column" org-shiftmetaright] |
|
767 |
"--" |
|
768 |
["Enable Narrowing" (setq org-table-limit-column-width (not org-table-limit-column-width)) :selected org-table-limit-column-width :style toggle]) |
|
769 |
("Row" |
|
770 |
["Move Row Up" org-metaup] |
|
771 |
["Move Row Down" org-metadown] |
|
772 |
["Delete Row" org-shiftmetaup] |
|
773 |
["Insert Row" org-shiftmetadown] |
|
774 |
["Sort lines in region" org-table-sort-lines (org-at-table-p)] |
|
775 |
"--" |
|
776 |
["Insert Hline" org-table-insert-hline]) |
|
777 |
("Rectangle" |
|
778 |
["Copy Rectangle" org-copy-special] |
|
779 |
["Cut Rectangle" org-cut-special] |
|
780 |
["Paste Rectangle" org-paste-special] |
|
781 |
["Fill Rectangle" org-table-wrap-region]) |
|
782 |
"--" |
|
783 |
["Set Column Formula" org-table-eval-formula] |
|
784 |
["Set Field Formula" (org-table-eval-formula '(4))] |
|
785 |
["Edit Formulas" org-table-edit-formulas] |
|
786 |
"--" |
|
787 |
["Recalculate Line" org-table-recalculate] |
|
788 |
["Recalculate All" (org-table-recalculate '(4))] |
|
789 |
["Iterate All" (org-table-recalculate '(16))] |
|
790 |
"--" |
|
791 |
["Toggle Recalculate Mark" org-table-rotate-recalc-marks] |
|
792 |
["Sum Column/Rectangle" org-table-sum |
|
793 |
:active (or (org-at-table-p) (org-region-active-p))] |
|
794 |
["Field Info" org-table-field-info] |
|
795 |
["Debug Formulas" |
|
796 |
(setq org-table-formula-debug (not org-table-formula-debug)) |
|
797 |
:style toggle :selected org-table-formula-debug] |
|
798 |
))) |
|
799 |
((and (assq :headline contextlist) (not (eolp))) |
|
800 |
(let ((priority (org-mouse-get-priority t))) |
|
801 |
(popup-menu |
|
802 |
`("Headline Menu" |
|
803 |
("Tags and Priorities" |
|
804 |
,@(org-mouse-keyword-menu |
|
805 |
(org-mouse-priority-list) |
|
806 |
#'(lambda (keyword) |
|
807 |
(org-mouse-set-priority (string-to-char keyword))) |
|
808 |
priority "Priority %s") |
|
809 |
"--" |
|
810 |
,@(org-mouse-tag-menu)) |
|
811 |
("TODO Status" |
|
812 |
,@(org-mouse-todo-menu (org-get-todo-state))) |
|
813 |
["Show Tags" |
|
814 |
(with-current-buffer org-mouse-main-buffer (org-agenda-show-tags)) |
|
815 |
:visible (not org-mouse-direct)] |
|
816 |
["Show Priority" |
|
817 |
(with-current-buffer org-mouse-main-buffer (org-agenda-show-priority)) |
|
818 |
:visible (not org-mouse-direct)] |
|
819 |
,@(if org-mouse-direct '("--") nil) |
|
820 |
["New Heading" org-mouse-insert-heading :visible org-mouse-direct] |
|
821 |
["Set Deadline" |
|
822 |
(progn (org-mouse-end-headline) (insert " ") (org-deadline)) |
|
823 |
:active (not (save-excursion |
|
824 |
(org-mouse-re-search-line org-deadline-regexp)))] |
|
825 |
["Schedule Task" |
|
826 |
(progn (org-mouse-end-headline) (insert " ") (org-schedule)) |
|
827 |
:active (not (save-excursion |
|
828 |
(org-mouse-re-search-line org-scheduled-regexp)))] |
|
829 |
["Insert Timestamp" |
|
830 |
(progn (org-mouse-end-headline) (insert " ") (org-time-stamp nil)) t] |
|
831 |
; ["Timestamp (inactive)" org-time-stamp-inactive t] |
|
832 |
"--" |
|
833 |
["Archive Subtree" org-archive-subtree] |
|
834 |
["Cut Subtree" org-cut-special] |
|
835 |
["Copy Subtree" org-copy-special] |
|
836 |
["Paste Subtree" org-paste-special :visible org-mouse-direct] |
|
837 |
("Sort Children" |
|
838 |
["Alphabetically" (org-sort-entries nil ?a)] |
|
839 |
["Numerically" (org-sort-entries nil ?n)] |
|
840 |
["By Time/Date" (org-sort-entries nil ?t)] |
|
841 |
"--" |
|
842 |
["Reverse Alphabetically" (org-sort-entries nil ?A)] |
|
843 |
["Reverse Numerically" (org-sort-entries nil ?N)] |
|
844 |
["Reverse By Time/Date" (org-sort-entries nil ?T)]) |
|
845 |
"--" |
|
846 |
["Move Trees" org-mouse-move-tree :active nil] |
|
847 |
)))) |
|
848 |
(t |
|
849 |
(org-mouse-popup-global-menu))))) |
|
850 |
|
|
851 |
(defun org-mouse-mark-active () |
|
852 |
(and mark-active transient-mark-mode)) |
|
853 |
|
|
854 |
(defun org-mouse-in-region-p (pos) |
|
855 |
(and (org-mouse-mark-active) |
|
856 |
(>= pos (region-beginning)) |
|
857 |
(< pos (region-end)))) |
|
858 |
|
|
859 |
(defun org-mouse-down-mouse (event) |
|
860 |
(interactive "e") |
|
861 |
(setq this-command last-command) |
|
862 |
(unless (and (= 1 (event-click-count event)) |
|
863 |
(org-mouse-in-region-p (posn-point (event-start event)))) |
|
864 |
(mouse-drag-region event))) |
|
865 |
|
|
866 |
(add-hook 'org-mode-hook |
|
867 |
#'(lambda () |
|
868 |
(setq org-mouse-context-menu-function 'org-mouse-context-menu) |
|
869 |
|
|
870 |
(when (memq 'context-menu org-mouse-features) |
|
871 |
(org-defkey org-mouse-map [mouse-3] nil) |
|
872 |
(org-defkey org-mode-map [mouse-3] 'org-mouse-show-context-menu)) |
|
873 |
(org-defkey org-mode-map [down-mouse-1] 'org-mouse-down-mouse) |
|
874 |
(when (memq 'context-menu org-mouse-features) |
|
875 |
(org-defkey org-mouse-map [C-drag-mouse-1] 'org-mouse-move-tree) |
|
876 |
(org-defkey org-mouse-map [C-down-mouse-1] 'org-mouse-move-tree-start)) |
|
877 |
(when (memq 'yank-link org-mouse-features) |
|
878 |
(org-defkey org-mode-map [S-mouse-2] 'org-mouse-yank-link) |
|
879 |
(org-defkey org-mode-map [drag-mouse-3] 'org-mouse-yank-link)) |
|
880 |
(when (memq 'move-tree org-mouse-features) |
|
881 |
(org-defkey org-mouse-map [drag-mouse-3] 'org-mouse-move-tree) |
|
882 |
(org-defkey org-mouse-map [down-mouse-3] 'org-mouse-move-tree-start)) |
|
883 |
|
|
884 |
(when (memq 'activate-stars org-mouse-features) |
|
885 |
(font-lock-add-keywords |
|
886 |
nil |
|
887 |
`((,org-outline-regexp |
|
888 |
0 `(face org-link mouse-face highlight keymap ,org-mouse-map) |
|
889 |
'prepend)) |
|
890 |
t)) |
|
891 |
|
|
892 |
(when (memq 'activate-bullets org-mouse-features) |
|
893 |
(font-lock-add-keywords |
|
894 |
nil |
|
895 |
`(("^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +" |
|
896 |
(1 `(face org-link keymap ,org-mouse-map mouse-face highlight) |
|
897 |
'prepend))) |
|
898 |
t)) |
|
899 |
|
|
900 |
(when (memq 'activate-checkboxes org-mouse-features) |
|
901 |
(font-lock-add-keywords |
|
902 |
nil |
|
903 |
`(("^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[ X]\\]\\)" |
|
904 |
(2 `(face bold keymap ,org-mouse-map mouse-face highlight) t))) |
|
905 |
t)) |
|
906 |
|
|
907 |
(defadvice org-open-at-point (around org-mouse-open-at-point activate) |
|
908 |
(let ((context (org-context))) |
|
909 |
(cond |
|
910 |
((assq :headline-stars context) (org-cycle)) |
|
911 |
((assq :checkbox context) (org-toggle-checkbox)) |
|
912 |
((assq :item-bullet context) |
|
913 |
(let ((org-cycle-include-plain-lists t)) (org-cycle))) |
|
914 |
((org-footnote-at-reference-p) nil) |
|
915 |
(t ad-do-it)))))) |
|
916 |
|
|
917 |
(defun org-mouse-move-tree-start (_event) |
|
918 |
(interactive "e") |
|
919 |
(message "Same line: promote/demote, (***):move before, (text): make a child")) |
|
920 |
|
|
921 |
|
|
922 |
(defun org-mouse-make-marker (position) |
|
923 |
(with-current-buffer (window-buffer (posn-window position)) |
|
924 |
(copy-marker (posn-point position)))) |
|
925 |
|
|
926 |
(defun org-mouse-move-tree (event) |
|
927 |
;; todo: handle movements between different buffers |
|
928 |
(interactive "e") |
|
929 |
(save-excursion |
|
930 |
(let* ((start (org-mouse-make-marker (event-start event))) |
|
931 |
(end (org-mouse-make-marker (event-end event))) |
|
932 |
(sbuf (marker-buffer start)) |
|
933 |
(ebuf (marker-buffer end))) |
|
934 |
|
|
935 |
(when (and sbuf ebuf) |
|
936 |
(set-buffer sbuf) |
|
937 |
(goto-char start) |
|
938 |
(org-back-to-heading) |
|
939 |
(if (and (eq sbuf ebuf) |
|
940 |
(equal |
|
941 |
(point) |
|
942 |
(save-excursion (goto-char end) (org-back-to-heading) (point)))) |
|
943 |
;; if the same line then promote/demote |
|
944 |
(if (>= end start) (org-demote-subtree) (org-promote-subtree)) |
|
945 |
;; if different lines then move |
|
946 |
(org-cut-subtree) |
|
947 |
|
|
948 |
(set-buffer ebuf) |
|
949 |
(goto-char end) |
|
950 |
(org-back-to-heading) |
|
951 |
(when (and (eq sbuf ebuf) |
|
952 |
(equal |
|
953 |
(point) |
|
954 |
(save-excursion (goto-char start) |
|
955 |
(org-back-to-heading) (point)))) |
|
956 |
(progn (org-end-of-subtree nil t) |
|
957 |
(unless (eobp) (backward-char))) |
|
958 |
(end-of-line) |
|
959 |
(if (eobp) (newline) (forward-char))) |
|
960 |
|
|
961 |
(when (looking-at org-outline-regexp) |
|
962 |
(let ((level (- (match-end 0) (match-beginning 0)))) |
|
963 |
(when (> end (match-end 0)) |
|
964 |
(progn (org-end-of-subtree nil t) |
|
965 |
(unless (eobp) (backward-char))) |
|
966 |
(end-of-line) |
|
967 |
(if (eobp) (newline) (forward-char)) |
|
968 |
(setq level (1+ level))) |
|
969 |
(org-paste-subtree level) |
|
970 |
(save-excursion |
|
971 |
(progn (org-end-of-subtree nil t) |
|
972 |
(unless (eobp) (backward-char))) |
|
973 |
(when (bolp) (delete-char -1)))))))))) |
|
974 |
|
|
975 |
|
|
976 |
(defun org-mouse-transform-to-outline () |
|
977 |
(interactive) |
|
978 |
(org-back-to-heading) |
|
979 |
(let ((minlevel 1000) |
|
980 |
(replace-text (concat (match-string 0) "* "))) |
|
981 |
(beginning-of-line 2) |
|
982 |
(save-excursion |
|
983 |
(while (not (or (eobp) (looking-at org-outline-regexp))) |
|
984 |
(when (looking-at org-mouse-plain-list-regexp) |
|
985 |
(setq minlevel (min minlevel (- (match-end 1) (match-beginning 1))))) |
|
986 |
(forward-line))) |
|
987 |
(while (not (or (eobp) (looking-at org-outline-regexp))) |
|
988 |
(when (and (looking-at org-mouse-plain-list-regexp) |
|
989 |
(eq minlevel (- (match-end 1) (match-beginning 1)))) |
|
990 |
(replace-match replace-text)) |
|
991 |
(forward-line)))) |
|
992 |
|
|
993 |
(defvar org-mouse-cmd) ;dynamically scoped from `org-with-remote-undo'. |
|
994 |
|
|
995 |
(defun org-mouse-do-remotely (command) |
|
996 |
;; (org-agenda-check-no-diary) |
|
997 |
(when (get-text-property (point) 'org-marker) |
|
998 |
(let* ((anticol (- (point-at-eol) (point))) |
|
999 |
(marker (get-text-property (point) 'org-marker)) |
|
1000 |
(buffer (marker-buffer marker)) |
|
1001 |
(pos (marker-position marker)) |
|
1002 |
(hdmarker (get-text-property (point) 'org-hd-marker)) |
|
1003 |
(buffer-read-only nil) |
|
1004 |
(newhead "--- removed ---") |
|
1005 |
(org-mouse-direct nil) |
|
1006 |
(org-mouse-main-buffer (current-buffer))) |
|
1007 |
(when (eq (with-current-buffer buffer major-mode) 'org-mode) |
|
1008 |
(let ((endmarker (with-current-buffer buffer |
|
1009 |
(org-end-of-subtree nil t) |
|
1010 |
(unless (eobp) (forward-char 1)) |
|
1011 |
(point-marker)))) |
|
1012 |
(org-with-remote-undo buffer |
|
1013 |
(with-current-buffer buffer |
|
1014 |
(widen) |
|
1015 |
(goto-char pos) |
|
1016 |
(org-show-hidden-entry) |
|
1017 |
(save-excursion |
|
1018 |
(and (outline-next-heading) |
|
1019 |
(org-flag-heading nil))) ; show the next heading |
|
1020 |
(org-back-to-heading) |
|
1021 |
(setq marker (point-marker)) |
|
1022 |
(goto-char (max (point-at-bol) (- (point-at-eol) anticol))) |
|
1023 |
(funcall command) |
|
1024 |
(message "_cmd: %S" org-mouse-cmd) |
|
1025 |
(message "this-command: %S" this-command) |
|
1026 |
(unless (eq (marker-position marker) (marker-position endmarker)) |
|
1027 |
(setq newhead (org-get-heading)))) |
|
1028 |
|
|
1029 |
(beginning-of-line 1) |
|
1030 |
(save-excursion |
|
1031 |
(org-agenda-change-all-lines newhead hdmarker 'fixface)))) |
|
1032 |
t)))) |
|
1033 |
|
|
1034 |
(defun org-mouse-agenda-context-menu (&optional _event) |
|
1035 |
(or (org-mouse-do-remotely 'org-mouse-context-menu) |
|
1036 |
(popup-menu |
|
1037 |
'("Agenda" |
|
1038 |
("Agenda Files") |
|
1039 |
"--" |
|
1040 |
["Undo" (progn (message "last command: %S" last-command) (setq this-command 'org-agenda-undo) (org-agenda-undo)) |
|
1041 |
:visible (if (eq last-command 'org-agenda-undo) |
|
1042 |
org-agenda-pending-undo-list |
|
1043 |
org-agenda-undo-list)] |
|
1044 |
["Rebuild Buffer" org-agenda-redo t] |
|
1045 |
["New Diary Entry" |
|
1046 |
org-agenda-diary-entry (org-agenda-check-type nil 'agenda) t] |
|
1047 |
"--" |
|
1048 |
["Goto Today" org-agenda-goto-today |
|
1049 |
(org-agenda-check-type nil 'agenda) t] |
|
1050 |
["Display Calendar" org-agenda-goto-calendar |
|
1051 |
(org-agenda-check-type nil 'agenda) t] |
|
1052 |
("Calendar Commands" |
|
1053 |
["Phases of the Moon" org-agenda-phases-of-moon |
|
1054 |
(org-agenda-check-type nil 'agenda)] |
|
1055 |
["Sunrise/Sunset" org-agenda-sunrise-sunset |
|
1056 |
(org-agenda-check-type nil 'agenda)] |
|
1057 |
["Holidays" org-agenda-holidays |
|
1058 |
(org-agenda-check-type nil 'agenda)] |
|
1059 |
["Convert" org-agenda-convert-date |
|
1060 |
(org-agenda-check-type nil 'agenda)] |
|
1061 |
"--" |
|
1062 |
["Create iCalendar file" org-icalendar-combine-agenda-files t]) |
|
1063 |
"--" |
|
1064 |
["Day View" org-agenda-day-view |
|
1065 |
:active (org-agenda-check-type nil 'agenda) |
|
1066 |
:style radio :selected (eq org-agenda-current-span 'day)] |
|
1067 |
["Week View" org-agenda-week-view |
|
1068 |
:active (org-agenda-check-type nil 'agenda) |
|
1069 |
:style radio :selected (eq org-agenda-current-span 'week)] |
|
1070 |
"--" |
|
1071 |
["Show Logbook entries" org-agenda-log-mode |
|
1072 |
:style toggle :selected org-agenda-show-log |
|
1073 |
:active (org-agenda-check-type nil 'agenda)] |
|
1074 |
["Include Diary" org-agenda-toggle-diary |
|
1075 |
:style toggle :selected org-agenda-include-diary |
|
1076 |
:active (org-agenda-check-type nil 'agenda)] |
|
1077 |
["Use Time Grid" org-agenda-toggle-time-grid |
|
1078 |
:style toggle :selected org-agenda-use-time-grid |
|
1079 |
:active (org-agenda-check-type nil 'agenda)] |
|
1080 |
["Follow Mode" org-agenda-follow-mode |
|
1081 |
:style toggle :selected org-agenda-follow-mode] |
|
1082 |
"--" |
|
1083 |
["Quit" org-agenda-quit t] |
|
1084 |
["Exit and Release Buffers" org-agenda-exit t] |
|
1085 |
)))) |
|
1086 |
|
|
1087 |
(defun org-mouse-get-gesture (event) |
|
1088 |
(let ((startxy (posn-x-y (event-start event))) |
|
1089 |
(endxy (posn-x-y (event-end event)))) |
|
1090 |
(if (< (car startxy) (car endxy)) :right :left))) |
|
1091 |
|
|
1092 |
|
|
1093 |
; (setq org-agenda-mode-hook nil) |
|
1094 |
(defvar org-agenda-mode-map) |
|
1095 |
(add-hook 'org-agenda-mode-hook |
|
1096 |
(lambda () |
|
1097 |
(setq org-mouse-context-menu-function 'org-mouse-agenda-context-menu) |
|
1098 |
(org-defkey org-agenda-mode-map [mouse-3] 'org-mouse-show-context-menu) |
|
1099 |
(org-defkey org-agenda-mode-map [down-mouse-3] 'org-mouse-move-tree-start) |
|
1100 |
(org-defkey org-agenda-mode-map [C-mouse-4] 'org-agenda-earlier) |
|
1101 |
(org-defkey org-agenda-mode-map [C-mouse-5] 'org-agenda-later) |
|
1102 |
(org-defkey org-agenda-mode-map [drag-mouse-3] |
|
1103 |
(lambda (event) (interactive "e") |
|
1104 |
(cl-case (org-mouse-get-gesture event) |
|
1105 |
(:left (org-agenda-earlier 1)) |
|
1106 |
(:right (org-agenda-later 1))))))) |
|
1107 |
|
|
1108 |
(provide 'org-mouse) |
|
1109 |
|
|
1110 |
;;; org-mouse.el ends here |