commit
a086743480
|
@ -18,6 +18,52 @@
|
||||||
|
|
||||||
** Details
|
** Details
|
||||||
|
|
||||||
|
*** Enhancements to secondary agenda filtering
|
||||||
|
|
||||||
|
**** You can now refining the current filter by an additional criterion
|
||||||
|
When filtering an existing agenda view with =/=, you can
|
||||||
|
now narrow down the existing selection by an additional
|
||||||
|
condition. Do do this, use =\= instead of =/= to add the
|
||||||
|
aditional criterion. You can also press =+= or =-= after
|
||||||
|
=/= to add a positive or negative condition. A condition
|
||||||
|
can be a TAG, or an effort estimate limit, see below.
|
||||||
|
|
||||||
|
**** It is now possible to filter for effort estimates
|
||||||
|
This means to filter the agenda for the value of the Effort
|
||||||
|
property. For this you should best set up global allowed
|
||||||
|
values for effort estimates, with
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(setq org-global-properties
|
||||||
|
'(("Effort_ALL" . "0 0:10 0:30 1:00 2:00 3:00 4:00")))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
You may then select effort limits with single keys in the
|
||||||
|
filter. It works like this: After =/= or =\=, first select
|
||||||
|
the operater which you want to use to compare effort
|
||||||
|
estimates:
|
||||||
|
|
||||||
|
: < Select entries with effort smaller than or equal to the limit
|
||||||
|
: > Select entries with effort larger than or equal to the limit
|
||||||
|
: = Select entries with effort equal to the limit
|
||||||
|
|
||||||
|
After that, you can press a single digit number which is
|
||||||
|
used as an index to the allowed effort estimates.
|
||||||
|
|
||||||
|
If you do not use digits to fast-select tags, you can even
|
||||||
|
skip the operator, which will then default to
|
||||||
|
`org-agenda-filter-effort-default-operator', which is by
|
||||||
|
default =<=.
|
||||||
|
|
||||||
|
Thanks to Manish for the great idea to include fast effort
|
||||||
|
filtering into the agenda filtering process.
|
||||||
|
|
||||||
|
**** The mode line will show the active filter
|
||||||
|
For example, if there is a filter in place that does select
|
||||||
|
for HOME tags, against EMAILtags, and for tasks with an
|
||||||
|
estimated effort smaller than 30 minutes, the mode-line with
|
||||||
|
show =+HOME-EMAIL+<0:30=
|
||||||
|
|
||||||
*** Setting tags has now its own binding, =C-c C-q=
|
*** Setting tags has now its own binding, =C-c C-q=
|
||||||
|
|
||||||
You can still use =C-c C-c= on a headline, but the new
|
You can still use =C-c C-c= on a headline, but the new
|
||||||
|
|
61
doc/org.texi
61
doc/org.texi
|
@ -4863,10 +4863,10 @@ you want to clock your time). For a specific buffer you can use
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
or you can set up these values globally by customizing the variables
|
or, even better, you can set up these values globally by customizing the
|
||||||
@code{org-global-properties} and @code{org-columns-default-format}. In
|
variables @code{org-global-properties} and @code{org-columns-default-format}.
|
||||||
particular if you want to use this setup also in the agenda, a global setup
|
In particular if you want to use this setup also in the agenda, a global
|
||||||
may be advised.
|
setup may be advised.
|
||||||
|
|
||||||
The way to assign estimates to individual items is then to switch to column
|
The way to assign estimates to individual items is then to switch to column
|
||||||
mode, and to use @kbd{S-@key{right}} and @kbd{S-@key{left}} to change the
|
mode, and to use @kbd{S-@key{right}} and @kbd{S-@key{left}} to change the
|
||||||
|
@ -4882,6 +4882,10 @@ option @code{org-agenda-columns-add-appointments-to-effort-sum}. The
|
||||||
appointments on a day that take place over a specified time interval will
|
appointments on a day that take place over a specified time interval will
|
||||||
then also be added to the load estimate of the day.
|
then also be added to the load estimate of the day.
|
||||||
|
|
||||||
|
Effort estimates can be used in secondary agenda filtering that is triggered
|
||||||
|
with the @kbd{/} key in the agenda (@pxref{Agenda commands}). If you have
|
||||||
|
these estimates defined consistently, two or three key presses will narrow
|
||||||
|
down the list to stuff that fits into an available time slot.
|
||||||
|
|
||||||
@node Capture, Agenda Views, Dates and Times, Top
|
@node Capture, Agenda Views, Dates and Times, Top
|
||||||
@chapter Capture
|
@chapter Capture
|
||||||
|
@ -5835,9 +5839,7 @@ sequence in which they are found in the agenda files.
|
||||||
|
|
||||||
Sorting can be customized using the variable
|
Sorting can be customized using the variable
|
||||||
@code{org-agenda-sorting-strategy}, and may also include criteria based on
|
@code{org-agenda-sorting-strategy}, and may also include criteria based on
|
||||||
the estimated effort of an entry.
|
the estimated effort of an entry (@pxref{Effort estimates}).
|
||||||
@c FIXME: link!!!!!!!!
|
|
||||||
|
|
||||||
|
|
||||||
@node Agenda commands, Custom agenda views, Presentation and sorting, Agenda Views
|
@node Agenda commands, Custom agenda views, Presentation and sorting, Agenda Views
|
||||||
@section Commands in the agenda buffer
|
@section Commands in the agenda buffer
|
||||||
|
@ -5994,17 +5996,48 @@ that entry would be in the original buffer (taken from a property, from a
|
||||||
@code{org-columns-default-format}), will be used in the agenda.
|
@code{org-columns-default-format}), will be used in the agenda.
|
||||||
|
|
||||||
@tsubheading{Secondary filtering and query editing}
|
@tsubheading{Secondary filtering and query editing}
|
||||||
|
@cindex filtering, by tag and effort, in agenda
|
||||||
|
@cindex tag filtering, in agenda
|
||||||
|
@cindex effort filtering, in agenda
|
||||||
@cindex query editing, in agenda
|
@cindex query editing, in agenda
|
||||||
|
|
||||||
@kindex /
|
@kindex /
|
||||||
@item /
|
@item /
|
||||||
Filter the current agenda view with respect to a tag. You will be prompted
|
Filter the current agenda view with respect to a tag and/or effort estimates.
|
||||||
for a tag selection letter. Pressing @key{TAB} at that prompt will offer use
|
The difference between this and a custom agenda commands is that filtering is
|
||||||
completion to select a tag (including any tags that do not have a selection
|
very fast, so that you can switch quickly between different filters without
|
||||||
character). The command then hides all entries that do not contain or
|
having to recreate the agenda.
|
||||||
inherit this tag. When called with prefix arg, remove the entries that
|
|
||||||
@emph{do} have the tag. A second @kbd{/} at the prompt will unhide any
|
You will be prompted for a tag selection letter. Pressing @key{TAB} at that
|
||||||
hidden entries.
|
prompt will offer use completion to select a tag (including any tags that do
|
||||||
|
not have a selection character). The command then hides all entries that do
|
||||||
|
not contain or inherit this tag. When called with prefix arg, remove the
|
||||||
|
entries that @emph{do} have the tag. A second @kbd{/} at the prompt will
|
||||||
|
turn off the filter and unhide any hidden entries. If the first key you
|
||||||
|
press is either @kbd{+} or @kbd{-}, the previous filter will be narrowed by
|
||||||
|
requiring or forbidding the selected additional tag. Instead of pressing
|
||||||
|
@kbd{+} or {-}, you can also use the @kbd{\} command.
|
||||||
|
|
||||||
|
In order to filter for effort estimates, you should set-up allowed
|
||||||
|
efforts globally, for example
|
||||||
|
@lisp
|
||||||
|
(setq org-global-properties
|
||||||
|
'(("Effort_ALL". "0 0:10 0:30 1:00 2:00 3:00 4:00")))
|
||||||
|
@end lisp
|
||||||
|
You can then filter for an effort by first typing an operator, one of @kbd{<},
|
||||||
|
@kbd{>}, and @kbd{=}, and then the one-digit index of an effort estimate in
|
||||||
|
your array of allowed values, where @kbd{0} means the 10th value. The filter
|
||||||
|
will then restrict to entries with effort smaller-or-equal, equal, or
|
||||||
|
larger-or-equal than the selected value. If the digits 0-9 are not used as
|
||||||
|
fast access keys to tags, you can also simply press the index digit directly
|
||||||
|
without an operator. In this case, @kbd{<} will be assumed.
|
||||||
|
|
||||||
|
@kindex \
|
||||||
|
@item \
|
||||||
|
Narrow the current agenda filter by an additional condition. When called with
|
||||||
|
prefix arg, remove the entries that @emph{do} have the tag, or that do match
|
||||||
|
the effort criterion. You can achieve the same effect by pressing @kbd{+} or
|
||||||
|
@kbd{-} as the first key after the @kbd{/} command.
|
||||||
|
|
||||||
@kindex [
|
@kindex [
|
||||||
@kindex ]
|
@kindex ]
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
(org-add-log-note): Mask prefix argument when immediately storing
|
(org-add-log-note): Mask prefix argument when immediately storing
|
||||||
the note.
|
the note.
|
||||||
|
|
||||||
|
* org-agenda.el (org-agenda-filter-effort-default-operator): New
|
||||||
|
option.
|
||||||
|
|
||||||
2008-10-19 James TD Smith <ahktenzero@mohorovi.cc>
|
2008-10-19 James TD Smith <ahktenzero@mohorovi.cc>
|
||||||
|
|
||||||
* org.el (org-add-log-setup): Bugfix; code to find insertion point
|
* org.el (org-add-log-setup): Bugfix; code to find insertion point
|
||||||
|
@ -13,6 +16,12 @@
|
||||||
|
|
||||||
2008-10-18 Carsten Dominik <dominik@science.uva.nl>
|
2008-10-18 Carsten Dominik <dominik@science.uva.nl>
|
||||||
|
|
||||||
|
* org-agenda.el (org-agenda-filter-tags,org-agenda-filter-form):
|
||||||
|
New variables.
|
||||||
|
(org-prepare-agenda): Reset the filter tags.
|
||||||
|
(org-agenda-filter-by-tag, org-agenda-filter-by-tag-show-all):
|
||||||
|
Show filter tags in mode line.
|
||||||
|
|
||||||
* org-table.el (orgtbl-to-html): Bind `html-table-tag' for the
|
* org-table.el (orgtbl-to-html): Bind `html-table-tag' for the
|
||||||
formatter.
|
formatter.
|
||||||
|
|
||||||
|
|
|
@ -387,6 +387,14 @@ or `C-c a #' to produce the list."
|
||||||
(repeat :tag "Projects are *not* stuck if they have an entry with TAG being any of" (string))
|
(repeat :tag "Projects are *not* stuck if they have an entry with TAG being any of" (string))
|
||||||
(regexp :tag "Projects are *not* stuck if this regexp matches\ninside the subtree")))
|
(regexp :tag "Projects are *not* stuck if this regexp matches\ninside the subtree")))
|
||||||
|
|
||||||
|
(defcustom org-agenda-filter-effort-default-operator "<"
|
||||||
|
"The default operator for effort estimate filtering.
|
||||||
|
If you select an effort estimate limit with first pressing an operator,
|
||||||
|
this one will be used."
|
||||||
|
:group 'org-agenda-custom-commands
|
||||||
|
:type '(choice (const :tag "less or equal" "<")
|
||||||
|
(const :tag "greater or equal"">")
|
||||||
|
(const :tag "equal" "=")))
|
||||||
|
|
||||||
(defgroup org-agenda-skip nil
|
(defgroup org-agenda-skip nil
|
||||||
"Options concerning skipping parts of agenda files."
|
"Options concerning skipping parts of agenda files."
|
||||||
|
@ -1169,6 +1177,7 @@ The following commands are available:
|
||||||
(org-defkey org-agenda-mode-map "{" 'org-agenda-manipulate-query-add-re)
|
(org-defkey org-agenda-mode-map "{" 'org-agenda-manipulate-query-add-re)
|
||||||
(org-defkey org-agenda-mode-map "}" 'org-agenda-manipulate-query-subtract-re)
|
(org-defkey org-agenda-mode-map "}" 'org-agenda-manipulate-query-subtract-re)
|
||||||
(org-defkey org-agenda-mode-map "/" 'org-agenda-filter-by-tag)
|
(org-defkey org-agenda-mode-map "/" 'org-agenda-filter-by-tag)
|
||||||
|
(org-defkey org-agenda-mode-map "\\" 'org-agenda-filter-by-tag-refine)
|
||||||
|
|
||||||
(defvar org-agenda-keymap (copy-keymap org-agenda-mode-map)
|
(defvar org-agenda-keymap (copy-keymap org-agenda-mode-map)
|
||||||
"Local keymap for agenda entries from Org-mode.")
|
"Local keymap for agenda entries from Org-mode.")
|
||||||
|
@ -1958,6 +1967,7 @@ higher priority settings."
|
||||||
(defun org-prepare-agenda (&optional name)
|
(defun org-prepare-agenda (&optional name)
|
||||||
(setq org-todo-keywords-for-agenda nil)
|
(setq org-todo-keywords-for-agenda nil)
|
||||||
(setq org-done-keywords-for-agenda nil)
|
(setq org-done-keywords-for-agenda nil)
|
||||||
|
(setq org-agenda-filter-tags nil)
|
||||||
(if org-agenda-multi
|
(if org-agenda-multi
|
||||||
(progn
|
(progn
|
||||||
(setq buffer-read-only nil)
|
(setq buffer-read-only nil)
|
||||||
|
@ -4117,18 +4127,57 @@ When this is the global TODO list, a prefix argument will be interpreted."
|
||||||
(goto-line line)
|
(goto-line line)
|
||||||
(recenter window-line)))
|
(recenter window-line)))
|
||||||
|
|
||||||
|
|
||||||
(defvar org-global-tags-completion-table nil)
|
(defvar org-global-tags-completion-table nil)
|
||||||
(defun org-agenda-filter-by-tag (strip &optional char)
|
(defvar org-agenda-filter-tags nil)
|
||||||
|
(defvar org-agenda-filter-form nil)
|
||||||
|
(defun org-agenda-filter-by-tag (strip &optional char narrow)
|
||||||
"Keep only those lines in the agenda buffer that have a specific tag.
|
"Keep only those lines in the agenda buffer that have a specific tag.
|
||||||
The tag is selected with its fast selection letter, as configured.
|
The tag is selected with its fast selection letter, as configured.
|
||||||
With prefix argument STRIP, remove all lines that do have the tag."
|
With prefix argument STRIP, remove all lines that do have the tag.
|
||||||
|
A lisp caller can specify CHAR. NARROW means that the new tag should be
|
||||||
|
used to narrow the search - the interactive user can also press `-' or `+'
|
||||||
|
to switch to narrowing."
|
||||||
(interactive "P")
|
(interactive "P")
|
||||||
(let (char a tag tags (inhibit-read-only t))
|
(let* ((alist org-tag-alist-for-agenda)
|
||||||
(message "Select tag [%s] or no tag [ ], [TAB] to complete, [/] to restore: "
|
(tag-chars (mapconcat
|
||||||
(mapconcat
|
|
||||||
(lambda (x) (if (cdr x) (char-to-string (cdr x)) ""))
|
(lambda (x) (if (cdr x) (char-to-string (cdr x)) ""))
|
||||||
org-tag-alist-for-agenda ""))
|
alist ""))
|
||||||
|
(efforts (org-split-string
|
||||||
|
(or (cdr (assoc (concat org-effort-property "_ALL")
|
||||||
|
org-global-properties))
|
||||||
|
"0 0:10 0:30 1:00 2:00 3:00 4:00 5:00 6:00 7:00 8:00" "")))
|
||||||
|
(effort-op org-agenda-filter-effort-default-operator)
|
||||||
|
(effort-prompt "")
|
||||||
|
(inhibit-read-only t)
|
||||||
|
(current org-agenda-filter-tags)
|
||||||
|
char a tag tags)
|
||||||
|
(unless char
|
||||||
|
(message
|
||||||
|
"%s by tag [%s ], [TAB], [/]:off, [+-]:narrow, [>=<]:effort: "
|
||||||
|
(if narrow "Narrow" "Filter") tag-chars)
|
||||||
|
(setq char (read-char)))
|
||||||
|
(when (member char '(?+ ?-))
|
||||||
|
;; Narrowing down
|
||||||
|
(cond ((equal char ?-) (setq strip t narrow t))
|
||||||
|
((equal char ?+) (setq strip nil narrow t)))
|
||||||
|
(message
|
||||||
|
"Narrow by tag [%s ], [TAB], [/]:off, [>=<]:effort: " tag-chars)
|
||||||
|
(setq char (read-char)))
|
||||||
|
(when (member char '(?< ?> ?=))
|
||||||
|
;; An effort operator
|
||||||
|
(setq effort-op (char-to-string char))
|
||||||
|
(loop for i from 0 to 9 do
|
||||||
|
(setq effort-prompt
|
||||||
|
(concat
|
||||||
|
effort-prompt " ["
|
||||||
|
(if (= i 9) "0" (int-to-string (1+ i)))
|
||||||
|
"]" (nth i efforts))))
|
||||||
|
(setq alist nil) ; to make sure it will be interpreted as effort.
|
||||||
|
(message "Effort%s: %s " effort-op effort-prompt)
|
||||||
(setq char (read-char))
|
(setq char (read-char))
|
||||||
|
(when (or (< char ?0) (> char ?9))
|
||||||
|
(error "Need 1-9,0 to select effort" )))
|
||||||
(when (equal char ?\t)
|
(when (equal char ?\t)
|
||||||
(unless (local-variable-p 'org-global-tags-completion-table (current-buffer))
|
(unless (local-variable-p 'org-global-tags-completion-table (current-buffer))
|
||||||
(org-set-local 'org-global-tags-completion-table
|
(org-set-local 'org-global-tags-completion-table
|
||||||
|
@ -4139,27 +4188,65 @@ With prefix argument STRIP, remove all lines that do have the tag."
|
||||||
(cond
|
(cond
|
||||||
((equal char ?/) (org-agenda-filter-by-tag-show-all))
|
((equal char ?/) (org-agenda-filter-by-tag-show-all))
|
||||||
((or (equal char ?\ )
|
((or (equal char ?\ )
|
||||||
(setq a (rassoc char org-tag-alist-for-agenda))
|
(setq a (rassoc char alist))
|
||||||
|
(and (>= char ?0) (<= char ?9)
|
||||||
|
(setq n (if (= char ?0) 9 (- char ?0 1))
|
||||||
|
tag (concat effort-op (nth n efforts))
|
||||||
|
a (cons tag nil)))
|
||||||
(and tag (setq a (cons tag nil))))
|
(and tag (setq a (cons tag nil))))
|
||||||
(org-agenda-filter-by-tag-show-all)
|
(org-agenda-filter-by-tag-show-all)
|
||||||
(setq tag (car a))
|
(setq tag (car a))
|
||||||
|
(setq org-agenda-filter-tags
|
||||||
|
(cons (concat (if strip "-" "+") tag)
|
||||||
|
(if narrow current nil)))
|
||||||
|
(setq org-agenda-filter-form (org-agenda-filter-make-matcher))
|
||||||
|
(org-agenda-set-mode-name)
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(while (not (eobp))
|
(while (not (eobp))
|
||||||
(if (get-text-property (point) 'org-marker)
|
(if (get-text-property (point) 'org-marker)
|
||||||
(progn
|
(progn
|
||||||
(setq tags (get-text-property (point) 'tags))
|
(setq tags (get-text-property (point) 'tags))
|
||||||
(if (not tag)
|
(if (not (eval org-agenda-filter-form))
|
||||||
(if (or (and strip (not tags))
|
|
||||||
(and (not strip) tags))
|
|
||||||
(org-agenda-filter-by-tag-hide-line))
|
(org-agenda-filter-by-tag-hide-line))
|
||||||
(if (or (and (member tag tags) strip)
|
|
||||||
(and (not (member tag tags)) (not strip)))
|
|
||||||
(org-agenda-filter-by-tag-hide-line)))
|
|
||||||
(beginning-of-line 2))
|
(beginning-of-line 2))
|
||||||
(beginning-of-line 2)))))
|
(beginning-of-line 2)))))
|
||||||
(t (error "Invalid tag selection character %c" char)))))
|
(t (error "Invalid tag selection character %c" char)))))
|
||||||
|
|
||||||
|
(defun org-agenda-filter-by-tag-refine (strip &optional char)
|
||||||
|
"Refine the current filter. See `org-agenda-filter-by-tag."
|
||||||
|
(interactive "P")
|
||||||
|
(org-agenda-filter-by-tag strip char 'refine))
|
||||||
|
|
||||||
|
(defun org-agenda-filter-make-matcher ()
|
||||||
|
(let (f f1)
|
||||||
|
(dolist (x org-agenda-filter-tags)
|
||||||
|
(if (member x '("-" "+"))
|
||||||
|
(setq f1 '(not tags))
|
||||||
|
(if (string-match "[<=>]" x)
|
||||||
|
(setq f1 (org-agenda-filter-effort-form x))
|
||||||
|
(setq f1 (list 'member (substring x 1) 'tags)))
|
||||||
|
(if (equal (string-to-char x) ?-)
|
||||||
|
(setq f1 (list 'not f1))))
|
||||||
|
(push f1 f))
|
||||||
|
(cons 'and (nreverse f))))
|
||||||
|
|
||||||
|
(defun org-agenda-filter-effort-form (e)
|
||||||
|
"Return the form to compare the effort of the current line with what E says.
|
||||||
|
E looks line \"+<2:25\"."
|
||||||
|
(let (op)
|
||||||
|
(setq e (substring e 1))
|
||||||
|
(setq op (string-to-char e) e (substring e 1))
|
||||||
|
(setq op (if (equal op ?<) '<= (if (equal op ?>) '>= '=)))
|
||||||
|
(list 'org-agenda-compare-effort (list 'quote op)
|
||||||
|
(org-hh:mm-string-to-minutes e))))
|
||||||
|
|
||||||
|
(defun org-agenda-compare-effort (op value)
|
||||||
|
(let ((eff (get-text-property 'effort-minutes)))
|
||||||
|
(if (not eff)
|
||||||
|
nil ; we don't have an effort defined
|
||||||
|
(funcall op eff value))))
|
||||||
|
|
||||||
(defvar org-agenda-filter-overlays nil)
|
(defvar org-agenda-filter-overlays nil)
|
||||||
|
|
||||||
(defun org-agenda-filter-by-tag-hide-line ()
|
(defun org-agenda-filter-by-tag-hide-line ()
|
||||||
|
@ -4183,7 +4270,10 @@ With prefix argument STRIP, remove all lines that do have the tag."
|
||||||
|
|
||||||
(defun org-agenda-filter-by-tag-show-all ()
|
(defun org-agenda-filter-by-tag-show-all ()
|
||||||
(mapc 'org-delete-overlay org-agenda-filter-overlays)
|
(mapc 'org-delete-overlay org-agenda-filter-overlays)
|
||||||
(setq org-agenda-filter-overlays nil))
|
(setq org-agenda-filter-overlays nil)
|
||||||
|
(setq org-agenda-filter-tags nil)
|
||||||
|
(setq org-agenda-filter-form nil)
|
||||||
|
(org-agenda-set-mode-name))
|
||||||
|
|
||||||
(defun org-agenda-manipulate-query-add ()
|
(defun org-agenda-manipulate-query-add ()
|
||||||
"Manipulate the query by adding a search term with positive selection.
|
"Manipulate the query by adding a search term with positive selection.
|
||||||
|
@ -4522,6 +4612,9 @@ so that the date SD will be in that range."
|
||||||
(if org-agenda-include-diary " Diary" "")
|
(if org-agenda-include-diary " Diary" "")
|
||||||
(if org-agenda-use-time-grid " Grid" "")
|
(if org-agenda-use-time-grid " Grid" "")
|
||||||
(if org-agenda-show-log " Log" "")
|
(if org-agenda-show-log " Log" "")
|
||||||
|
(if org-agenda-filter-tags
|
||||||
|
(concat " {" (mapconcat 'identity org-agenda-filter-tags "") "}")
|
||||||
|
"")
|
||||||
(if org-agenda-archives-mode
|
(if org-agenda-archives-mode
|
||||||
(if (eq org-agenda-archives-mode t)
|
(if (eq org-agenda-archives-mode t)
|
||||||
" Archives"
|
" Archives"
|
||||||
|
|
Loading…
Reference in New Issue