1# Copyright (c) 2016-2020 Hugo Osvaldo Barrera
2# Copyright (c) 2013-2016 Christian Geier et al.
3#
4# Permission is hereby granted, free of charge, to any person obtaining
5# a copy of this software and associated documentation files (the
6# "Software"), to deal in the Software without restriction, including
7# without limitation the rights to use, copy, modify, merge, publish,
8# distribute, sublicense, and/or sell copies of the Software, and to
9# permit persons to whom the Software is furnished to do so, subject to
10# the following conditions:
11#
12# The above copyright notice and this permission notice shall be
13# included in all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22import re
23
24import click
25import urwid
26
27
28class ExtendedEdit(urwid.Edit):
29    """A text editing widget supporting some more editing commands"""
30
31    HELP = [
32        ("Ctrl-W", "Delete word"),
33        ("Ctrl-U", "Delete until beginning of line"),
34        ("Ctrl-K", "Delete until end of line"),
35        ("Ctrl-A", "Go to beginning of line"),
36        ("Ctrl-E", "Go to end of line"),
37        ("Ctrl-D", "Delete forward letter"),
38        ("Ctrl-O", "Edit in $EDITOR"),
39    ]
40
41    def __init__(self, parent, *a, **kw):
42        self._parent = parent
43        super().__init__(*a, **kw)
44
45    def keypress(self, size, key):
46        if key == "ctrl w":
47            self._delete_word()
48        elif key == "ctrl u":
49            self._delete_till_beginning_of_line()
50        elif key == "ctrl k":
51            self._delete_till_end_of_line()
52        elif key == "ctrl a":
53            self._goto_beginning_of_line()
54        elif key == "ctrl e":
55            self._goto_end_of_line()
56        elif key == "ctrl d":
57            self._delete_forward_letter()
58        elif key == "ctrl o":
59            # Allow editing in $EDITOR
60            self._editor()
61        # TODO: alt b, alt f
62        else:
63            return super().keypress(size, key)
64
65    def _delete_forward_letter(self):
66        text = self.get_edit_text()
67        pos = self.edit_pos
68        text = text[:pos] + text[pos + 1 :]
69        self.set_edit_text(text)
70
71    def _delete_word(self):
72        """delete word before cursor"""
73        text = self.get_edit_text()
74        t = text[: self.edit_pos].rstrip()
75
76        words = re.findall(r"[\w]+|[^\w\s]", t, re.UNICODE)
77        if t == "":
78            f_text = t
79        else:
80            f_text = t[: len(t) - len(words[-1])]
81
82        self.set_edit_text(f_text + text[self.edit_pos :])
83        self.set_edit_pos(len(f_text))
84
85    def _delete_till_beginning_of_line(self):
86        """delete till start of line before cursor"""
87        text = self.get_edit_text()
88        sol = text.rfind("\n", 0, self.edit_pos) + 1
89
90        before_line = text[:sol]
91
92        self.set_edit_text(before_line + text[self.edit_pos :])
93        self.set_edit_pos(sol)
94
95    def _delete_till_end_of_line(self):
96        """delete till end of line before cursor"""
97        text = self.get_edit_text()
98        eol = text.find("\n", self.edit_pos)
99
100        if eol == -1:
101            after_eol = ""
102        else:
103            after_eol = text[eol:]
104
105        self.set_edit_text(text[: self.edit_pos] + after_eol)
106
107    def _goto_beginning_of_line(self):
108        text = self.get_edit_text()
109        sol = text.rfind("\n", 0, self.edit_pos) + 1
110        self.set_edit_pos(sol)
111
112    def _goto_end_of_line(self):
113        text = self.get_edit_text()
114        eol = text.find("\n", self.edit_pos)
115        if eol == -1:
116            eol = len(text)
117        self.set_edit_pos(eol)
118
119    def _editor(self):
120        self._parent._loop.screen.clear()
121        new_text = click.edit(self.get_edit_text())
122        if new_text is not None:
123            self.set_edit_text(new_text.strip())
124
125
126class PrioritySelector(urwid.Button):
127
128    HELP = [
129        ("left", "Lower Priority"),
130        ("right", "Higher Priority"),
131    ]
132
133    RANGES = [
134        [0],
135        [9, 8, 7, 6],
136        [5],
137        [1, 2, 3, 4],
138    ]
139
140    def __init__(self, parent, priority, formatter_function):
141        self._parent = parent
142        self._label = urwid.SelectableIcon("", 0)
143        urwid.WidgetWrap.__init__(self, self._label)
144
145        self._priority = priority
146        self._formatter = formatter_function
147        self._set_label()
148
149    def _set_label(self):
150        self.set_label(self._formatter(self._priority))
151
152    def _update_label(self, delta=0):
153        for i, r in enumerate(PrioritySelector.RANGES):
154            if self._priority in r:
155                self._priority = PrioritySelector.RANGES[
156                    (i + delta) % len(PrioritySelector.RANGES)
157                ][0]
158                self._set_label()
159                return
160
161    def keypress(self, size, key):
162        if key in ["right", "enter"]:
163            self._update_label(1)
164            return
165        if key == "left":
166            self._update_label(-1)
167            return
168
169        return super().keypress(size, key)
170
171    @property
172    def priority(self):
173        return self._priority
174