bookmark_forms.py 13.6 KB
Newer Older
mathieui's avatar
mathieui committed
1 2 3 4
"""
Windows used inthe bookmarkstab
"""
import curses
5
from typing import List, Tuple, Optional
mathieui's avatar
mathieui committed
6

mathieui's avatar
mathieui committed
7 8
from poezio.windows import base_wins
from poezio.windows.base_wins import Win
mathieui's avatar
mathieui committed
9
from poezio.windows.inputs import Input
mathieui's avatar
mathieui committed
10
from poezio.windows.data_forms import FieldInput, FieldInputMixin
11

12 13
from poezio.theming import to_curses_attr, get_theme
from poezio.common import safeJID
14
from poezio.bookmarks import Bookmark, BookmarkList
mathieui's avatar
mathieui committed
15

mathieui's avatar
mathieui committed
16

17
class BookmarkNameInput(FieldInput, Input):
18
    def __init__(self, field: Bookmark) -> None:
19 20 21 22 23 24
        FieldInput.__init__(self, field)
        Input.__init__(self)
        self.text = field.name
        self.pos = len(self.text)
        self.color = get_theme().COLOR_NORMAL_TEXT

25
    def save(self) -> None:
26 27
        self._field.name = self.get_text()

28
    def get_help_message(self) -> str:
29 30 31
        return 'Edit the text'


mathieui's avatar
mathieui committed
32
class BookmarkJIDInput(FieldInput, Input):
33
    def __init__(self, field: Bookmark) -> None:
mathieui's avatar
mathieui committed
34 35 36
        FieldInput.__init__(self, field)
        Input.__init__(self)
        jid = safeJID(field.jid)
37
        jid.resource = field.nick or None
mathieui's avatar
mathieui committed
38 39 40 41
        self.text = jid.full
        self.pos = len(self.text)
        self.color = get_theme().COLOR_NORMAL_TEXT

42
    def save(self) -> None:
mathieui's avatar
mathieui committed
43 44 45 46
        jid = safeJID(self.get_text())
        self._field.jid = jid.bare
        self._field.nick = jid.resource

47
    def get_help_message(self) -> str:
mathieui's avatar
mathieui committed
48 49
        return 'Edit the text'

mathieui's avatar
mathieui committed
50

mathieui's avatar
mathieui committed
51
class BookmarkMethodInput(FieldInputMixin):
52
    def __init__(self, field: Bookmark) -> None:
mathieui's avatar
mathieui committed
53 54 55 56 57 58
        FieldInput.__init__(self, field)
        Win.__init__(self)
        self.options = ('local', 'remote')
        # val_pos is the position of the currently selected option
        self.val_pos = self.options.index(field.method)

59
    def do_command(self, key: str) -> None:
mathieui's avatar
mathieui committed
60 61 62 63
        if key == 'KEY_LEFT':
            if self.val_pos > 0:
                self.val_pos -= 1
        elif key == 'KEY_RIGHT':
mathieui's avatar
mathieui committed
64
            if self.val_pos < len(self.options) - 1:
mathieui's avatar
mathieui committed
65 66 67 68 69
                self.val_pos += 1
        else:
            return
        self.refresh()

70
    def refresh(self) -> None:
mathieui's avatar
mathieui committed
71 72
        self._win.erase()
        self._win.attron(to_curses_attr(self.color))
mathieui's avatar
mathieui committed
73
        self.addnstr(0, 0, ' ' * self.width, self.width)
mathieui's avatar
mathieui committed
74 75
        if self.val_pos > 0:
            self.addstr(0, 0, '←')
mathieui's avatar
mathieui committed
76 77
        if self.val_pos < len(self.options) - 1:
            self.addstr(0, self.width - 1, '→')
mathieui's avatar
mathieui committed
78 79
        if self.options:
            option = self.options[self.val_pos]
mathieui's avatar
mathieui committed
80
            self.addstr(0, self.width // 2 - len(option) // 2, option)
mathieui's avatar
mathieui committed
81 82 83
        self._win.attroff(to_curses_attr(self.color))
        self._refresh()

84
    def save(self) -> None:
mathieui's avatar
mathieui committed
85 86
        self._field.method = self.options[self.val_pos]

87
    def get_help_message(self) -> str:
mathieui's avatar
mathieui committed
88 89
        return '←, →: Select a value amongst the others'

mathieui's avatar
mathieui committed
90

mathieui's avatar
mathieui committed
91
class BookmarkPasswordInput(FieldInput, Input):
92
    def __init__(self, field: Bookmark) -> None:
mathieui's avatar
mathieui committed
93 94 95 96 97 98
        FieldInput.__init__(self, field)
        Input.__init__(self)
        self.text = field.password or ''
        self.pos = len(self.text)
        self.color = get_theme().COLOR_NORMAL_TEXT

99
    def rewrite_text(self) -> None:
mathieui's avatar
mathieui committed
100 101 102
        self._win.erase()
        if self.color:
            self._win.attron(to_curses_attr(self.color))
mathieui's avatar
mathieui committed
103 104
        self.addstr(
            '*' * len(self.text[self.view_pos:self.view_pos + self.width - 1]))
mathieui's avatar
mathieui committed
105 106
        if self.color:
            (y, x) = self._win.getyx()
mathieui's avatar
mathieui committed
107 108
            size = self.width - x
            self.addnstr(' ' * size, size, to_curses_attr(self.color))
mathieui's avatar
mathieui committed
109 110 111 112 113
        self.addstr(0, self.pos, '')
        if self.color:
            self._win.attroff(to_curses_attr(self.color))
        self._refresh()

114
    def save(self) -> None:
mathieui's avatar
mathieui committed
115 116
        self._field.password = self.get_text() or None

117
    def get_help_message(self) -> str:
mathieui's avatar
mathieui committed
118 119
        return 'Edit the secret text'

mathieui's avatar
mathieui committed
120

mathieui's avatar
mathieui committed
121
class BookmarkAutojoinWin(FieldInputMixin):
122
    def __init__(self, field: Bookmark) -> None:
mathieui's avatar
mathieui committed
123 124 125 126 127
        FieldInput.__init__(self, field)
        Win.__init__(self)
        self.last_key = 'KEY_RIGHT'
        self.value = field.autojoin

128
    def do_command(self, key: str) -> None:
mathieui's avatar
mathieui committed
129 130 131 132 133
        if key == 'KEY_LEFT' or key == 'KEY_RIGHT':
            self.value = not self.value
            self.last_key = key
        self.refresh()

134
    def refresh(self) -> None:
mathieui's avatar
mathieui committed
135 136 137 138 139 140 141 142 143 144 145 146
        self._win.erase()
        self._win.attron(to_curses_attr(self.color))
        format_string = '←{:^%s}→' % 7
        inp = format_string.format(repr(self.value))
        self.addstr(0, 0, inp)
        if self.last_key == 'KEY_RIGHT':
            self.move(0, 8)
        else:
            self.move(0, 0)
        self._win.attroff(to_curses_attr(self.color))
        self._refresh()

147
    def save(self) -> None:
mathieui's avatar
mathieui committed
148 149
        self._field.autojoin = self.value

150
    def get_help_message(self) -> str:
mathieui's avatar
mathieui committed
151 152 153 154
        return '← and →: change the value between True and False'


class BookmarksWin(Win):
155 156 157
    __slots__ = ('scroll_pos', '_current_input', 'current_horizontal_input',
                 '_bookmarks', 'lines')

158
    def __init__(self, bookmarks: BookmarkList, height: int, width: int, y: int, x: int) -> None:
mathieui's avatar
mathieui committed
159
        self._win = base_wins.TAB_WIN.derwin(height, width, y, x)
mathieui's avatar
mathieui committed
160 161 162 163
        self.scroll_pos = 0
        self._current_input = 0
        self.current_horizontal_input = 0
        self._bookmarks = list(bookmarks)
164
        self.lines = []  # type: List[Tuple[BookmarkNameInput, BookmarkJIDInput, BookmarkPasswordInput, BookmarkAutojoinWin, BookmarkMethodInput]]
165
        for bookmark in sorted(self._bookmarks, key=lambda x: str(x.jid)):
166 167
            self.lines.append((BookmarkNameInput(bookmark),
                               BookmarkJIDInput(bookmark),
mathieui's avatar
mathieui committed
168 169 170
                               BookmarkPasswordInput(bookmark),
                               BookmarkAutojoinWin(bookmark),
                               BookmarkMethodInput(bookmark)))
mathieui's avatar
mathieui committed
171 172

    @property
173
    def current_input(self) -> int:
mathieui's avatar
mathieui committed
174 175 176
        return self._current_input

    @current_input.setter
177
    def current_input(self, value: int) -> None:
mathieui's avatar
mathieui committed
178 179
        if 0 <= self._current_input < len(self.lines):
            if 0 <= value < len(self.lines):
mathieui's avatar
mathieui committed
180 181 182
                self.lines[self._current_input][
                    self.current_horizontal_input].set_color(
                        get_theme().COLOR_NORMAL_TEXT)
mathieui's avatar
mathieui committed
183 184 185 186
                self._current_input = value
        else:
            self._current_input = 0

187
    def add_bookmark(self, bookmark: Bookmark) -> None:
188 189
        self.lines.append((BookmarkNameInput(bookmark),
                           BookmarkJIDInput(bookmark),
mathieui's avatar
mathieui committed
190 191 192
                           BookmarkPasswordInput(bookmark),
                           BookmarkAutojoinWin(bookmark),
                           BookmarkMethodInput(bookmark)))
193 194 195 196
        if len(self.lines) > 1:
            self.lines[self.current_input][
                self.current_horizontal_input].set_color(
                    get_theme().COLOR_NORMAL_TEXT)
mathieui's avatar
mathieui committed
197
        self.current_horizontal_input = 0
198 199
        if len(self.lines) > 1:
            self.current_input = len(self.lines) - 1
mathieui's avatar
mathieui committed
200
        if self.current_input - self.scroll_pos > self.height - 1:
mathieui's avatar
mathieui committed
201 202 203
            self.scroll_pos = self.current_input - self.height + 1
        self.refresh()

204 205 206 207 208 209 210 211 212 213 214
    def del_current_bookmark(self) -> Optional[Bookmark]:
        if not self.lines:
            return None
        bm = self.lines[self.current_input][0]._field
        to_delete = self.current_input
        self.current_input -= 1
        del self.lines[to_delete]
        if self.scroll_pos:
            self.scroll_pos -= 1
        self.refresh()
        return bm
mathieui's avatar
mathieui committed
215

216
    def resize(self, height: int, width: int, y: int, x: int) -> None:
mathieui's avatar
mathieui committed
217 218
        self.height = height
        self.width = width
mathieui's avatar
mathieui committed
219
        self._win = base_wins.TAB_WIN.derwin(height, width, y, x)
mathieui's avatar
mathieui committed
220 221
        # Adjust the scroll position, if resizing made the window too small
        # for the cursor to be visible
mathieui's avatar
mathieui committed
222
        while self.current_input - self.scroll_pos > self.height - 1:
mathieui's avatar
mathieui committed
223 224
            self.scroll_pos += 1

225
    def go_to_next_line_input(self) -> None:
mathieui's avatar
mathieui committed
226 227 228 229
        if not self.lines:
            return
        if self.current_input == len(self.lines) - 1:
            return
mathieui's avatar
mathieui committed
230 231
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
mathieui's avatar
mathieui committed
232
                get_theme().COLOR_NORMAL_TEXT)
mathieui's avatar
mathieui committed
233 234
        # Adjust the scroll position if the current_input would be outside
        # of the visible area
mathieui's avatar
mathieui committed
235
        if self.current_input + 1 - self.scroll_pos > self.height - 1:
mathieui's avatar
mathieui committed
236 237 238 239 240
            self.current_input += 1
            self.scroll_pos += 1
            self.refresh()
        else:
            self.current_input += 1
mathieui's avatar
mathieui committed
241 242 243
            self.lines[self.current_input][
                self.current_horizontal_input].set_color(
                    get_theme().COLOR_SELECTED_ROW)
mathieui's avatar
mathieui committed
244

245
    def go_to_previous_line_input(self) -> None:
mathieui's avatar
mathieui committed
246 247 248 249
        if not self.lines:
            return
        if self.current_input == 0:
            return
250
        theme = get_theme()
mathieui's avatar
mathieui committed
251 252
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
253
                theme.COLOR_NORMAL_TEXT)
mathieui's avatar
mathieui committed
254 255 256 257 258 259
        self.current_input -= 1
        # Adjust the scroll position if the current_input would be outside
        # of the visible area
        if self.current_input < self.scroll_pos:
            self.scroll_pos = self.current_input
            self.refresh()
mathieui's avatar
mathieui committed
260 261
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
262
                theme.COLOR_SELECTED_ROW)
mathieui's avatar
mathieui committed
263

264
    def go_to_next_horizontal_input(self) -> None:
mathieui's avatar
mathieui committed
265 266
        if not self.lines:
            return
267
        theme = get_theme()
mathieui's avatar
mathieui committed
268 269
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
270
                theme.COLOR_NORMAL_TEXT)
mathieui's avatar
mathieui committed
271 272 273
        self.current_horizontal_input += 1
        if self.current_horizontal_input > 3:
            self.current_horizontal_input = 0
mathieui's avatar
mathieui committed
274 275
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
276
                theme.COLOR_SELECTED_ROW)
mathieui's avatar
mathieui committed
277

278
    def go_to_next_page(self) -> bool:
279
        if not self.lines:
280
            return False
281 282

        if self.current_input == len(self.lines) - 1:
283
            return False
284

285
        theme = get_theme()
mathieui's avatar
mathieui committed
286 287
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
288
                theme.COLOR_NORMAL_TEXT)
289 290 291 292 293 294 295 296
        inc = min(self.height, len(self.lines) - self.current_input - 1)

        if self.current_input + inc - self.scroll_pos > self.height - 1:
            self.current_input += inc
            self.scroll_pos += inc
            self.refresh()
        else:
            self.current_input += inc
mathieui's avatar
mathieui committed
297 298
            self.lines[self.current_input][
                self.current_horizontal_input].set_color(
299
                    theme.COLOR_SELECTED_ROW)
300 301
        return True

302
    def go_to_previous_page(self) -> bool:
303
        if not self.lines:
304
            return False
305 306

        if self.current_input == 0:
307
            return False
308

309
        theme = get_theme()
mathieui's avatar
mathieui committed
310 311
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
312
                theme.COLOR_NORMAL_TEXT)
313 314 315 316 317 318 319 320

        dec = min(self.height, self.current_input)
        self.current_input -= dec
        # Adjust the scroll position if the current_input would be outside
        # of the visible area
        if self.current_input < self.scroll_pos:
            self.scroll_pos = self.current_input
            self.refresh()
mathieui's avatar
mathieui committed
321 322
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
323
                theme.COLOR_SELECTED_ROW)
324 325
        return True

326
    def go_to_previous_horizontal_input(self) -> None:
mathieui's avatar
mathieui committed
327 328 329 330
        if not self.lines:
            return
        if self.current_horizontal_input == 0:
            return
331
        theme = get_theme()
mathieui's avatar
mathieui committed
332 333
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
334
                theme.COLOR_NORMAL_TEXT)
mathieui's avatar
mathieui committed
335
        self.current_horizontal_input -= 1
mathieui's avatar
mathieui committed
336 337
        self.lines[self.current_input][
            self.current_horizontal_input].set_color(
338
                theme.COLOR_SELECTED_ROW)
mathieui's avatar
mathieui committed
339

340
    def on_input(self, key: str) -> None:
mathieui's avatar
mathieui committed
341 342
        if not self.lines:
            return
mathieui's avatar
mathieui committed
343 344
        self.lines[self.current_input][
            self.current_horizontal_input].do_command(key)
mathieui's avatar
mathieui committed
345

346
    def refresh(self) -> None:
mathieui's avatar
mathieui committed
347 348
        # store the cursor status
        self._win.erase()
mathieui's avatar
mathieui committed
349
        y = -self.scroll_pos
mathieui's avatar
mathieui committed
350
        for i in range(len(self.lines)):
351 352
            self.lines[i][0].resize(1, self.width // 4, y + 1, 0)
            self.lines[i][1].resize(1, self.width // 4, y + 1, self.width // 4)
mathieui's avatar
mathieui committed
353 354 355 356 357 358
            self.lines[i][2].resize(1, self.width // 6, y + 1,
                                    3 * self.width // 6)
            self.lines[i][3].resize(1, self.width // 6, y + 1,
                                    4 * self.width // 6)
            self.lines[i][4].resize(1, self.width // 6, y + 1,
                                    5 * self.width // 6)
mathieui's avatar
mathieui committed
359 360 361 362 363 364 365 366 367 368
            y += 1
        self._refresh()
        for i, inp in enumerate(self.lines):
            if i < self.scroll_pos:
                continue
            if i >= self.height + self.scroll_pos:
                break
            for j in range(4):
                inp[j].refresh()

mathieui's avatar
mathieui committed
369 370 371 372 373 374
        if self.lines and self.current_input < self.height - 1:
            self.lines[self.current_input][
                self.current_horizontal_input].set_color(
                    get_theme().COLOR_SELECTED_ROW)
            self.lines[self.current_input][
                self.current_horizontal_input].refresh()
mathieui's avatar
mathieui committed
375 376 377 378 379
        if not self.lines:
            curses.curs_set(0)
        else:
            curses.curs_set(1)

380
    def refresh_current_input(self) -> None:
mathieui's avatar
mathieui committed
381
        if self.lines:
mathieui's avatar
mathieui committed
382 383
            self.lines[self.current_input][
                self.current_horizontal_input].refresh()
mathieui's avatar
mathieui committed
384

385
    def save(self) -> None:
mathieui's avatar
mathieui committed
386 387 388
        for line in self.lines:
            for item in line:
                item.save()