listpick 0.1.16.8__py3-none-any.whl → 0.1.16.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- listpick/listpick_app.py +1028 -506
- listpick/pane/left_pane_functions.py +198 -0
- listpick/pane/pane_functions_1.py +175 -0
- listpick/ui/build_help.py +33 -8
- listpick/ui/input_field.py +0 -1
- listpick/ui/keys.py +14 -8
- listpick/ui/picker_colours.py +55 -0
- listpick/utils/generate_data_multiprocessing.py +10 -0
- listpick/utils/generate_data_multithreaded.py +36 -12
- listpick/utils/generate_data_utils.py +43 -0
- listpick/utils/utils.py +15 -12
- {listpick-0.1.16.8.dist-info → listpick-0.1.16.17.dist-info}/METADATA +29 -40
- {listpick-0.1.16.8.dist-info → listpick-0.1.16.17.dist-info}/RECORD +17 -13
- {listpick-0.1.16.8.dist-info → listpick-0.1.16.17.dist-info}/WHEEL +0 -0
- {listpick-0.1.16.8.dist-info → listpick-0.1.16.17.dist-info}/entry_points.txt +0 -0
- {listpick-0.1.16.8.dist-info → listpick-0.1.16.17.dist-info}/licenses/LICENSE.txt +0 -0
- {listpick-0.1.16.8.dist-info → listpick-0.1.16.17.dist-info}/top_level.txt +0 -0
listpick/listpick_app.py
CHANGED
|
@@ -18,10 +18,15 @@ from wcwidth import wcswidth
|
|
|
18
18
|
from typing import Callable, Optional, Tuple, Dict
|
|
19
19
|
import json
|
|
20
20
|
import threading
|
|
21
|
+
import multiprocessing
|
|
21
22
|
import string
|
|
22
23
|
import logging
|
|
24
|
+
import copy
|
|
25
|
+
import tempfile
|
|
26
|
+
import queue
|
|
23
27
|
|
|
24
28
|
from listpick.pane.pane_utils import get_file_attributes
|
|
29
|
+
from listpick.pane.left_pane_functions import *
|
|
25
30
|
from listpick.ui.picker_colours import get_colours, get_help_colours, get_notification_colours, get_theme_count, get_fallback_colours
|
|
26
31
|
from listpick.utils.options_selectors import default_option_input, output_file_option_selector, default_option_selector
|
|
27
32
|
from listpick.utils.table_to_list_of_lists import *
|
|
@@ -46,6 +51,8 @@ from listpick.pane.get_data import *
|
|
|
46
51
|
COLOURS_SET = False
|
|
47
52
|
help_colours, notification_colours = {}, {}
|
|
48
53
|
|
|
54
|
+
os.environ["TMPDIR"] = "/tmp"
|
|
55
|
+
|
|
49
56
|
class Command:
|
|
50
57
|
def __init__(self, command_type, command_value):
|
|
51
58
|
self.command_type = command_type
|
|
@@ -68,9 +75,9 @@ class Picker:
|
|
|
68
75
|
auto_refresh: bool =False,
|
|
69
76
|
timer: float = 5,
|
|
70
77
|
|
|
71
|
-
get_new_data: bool =False,
|
|
72
|
-
refresh_function: Optional[Callable] = lambda items, header, visible_rows_indices, getting_data: None,
|
|
73
|
-
get_data_startup: bool =False,
|
|
78
|
+
get_new_data: bool =False,
|
|
79
|
+
refresh_function: Optional[Callable] = lambda items, header, visible_rows_indices, getting_data: None,
|
|
80
|
+
get_data_startup: bool =False,
|
|
74
81
|
track_entries_upon_refresh: bool = True,
|
|
75
82
|
pin_cursor: bool = False,
|
|
76
83
|
id_column: int = 0,
|
|
@@ -93,7 +100,12 @@ class Picker:
|
|
|
93
100
|
user_opts : str = "",
|
|
94
101
|
options_list: list[str] = [],
|
|
95
102
|
user_settings : str = "",
|
|
103
|
+
|
|
96
104
|
separator : str = " ",
|
|
105
|
+
header_separator : str = " │",
|
|
106
|
+
header_separator_before_selected_column : str = " ▐",
|
|
107
|
+
|
|
108
|
+
|
|
97
109
|
search_query : str = "",
|
|
98
110
|
search_count : int = 0,
|
|
99
111
|
search_index : int = 0,
|
|
@@ -108,6 +120,10 @@ class Picker:
|
|
|
108
120
|
highlight_full_row: bool =False,
|
|
109
121
|
crosshair_cursor: bool = False,
|
|
110
122
|
cell_cursor: bool = True,
|
|
123
|
+
selected_char: str = "",
|
|
124
|
+
unselected_char: str = "",
|
|
125
|
+
selecting_char: str = "",
|
|
126
|
+
deselecting_char: str = "",
|
|
111
127
|
|
|
112
128
|
items_per_page : int = -1,
|
|
113
129
|
sort_method : int = 0,
|
|
@@ -148,6 +164,7 @@ class Picker:
|
|
|
148
164
|
reset_colours: bool = True,
|
|
149
165
|
key_remappings: dict = {},
|
|
150
166
|
keys_dict:dict = picker_keys,
|
|
167
|
+
macros: list = [],
|
|
151
168
|
display_infobox : bool = False,
|
|
152
169
|
infobox_items: list[list[str]] = [],
|
|
153
170
|
infobox_title: str = "",
|
|
@@ -189,9 +206,19 @@ class Picker:
|
|
|
189
206
|
right_panes: list = [],
|
|
190
207
|
right_pane_index: int = 0,
|
|
191
208
|
|
|
209
|
+
split_left: bool = False,
|
|
210
|
+
left_panes: list = [],
|
|
211
|
+
left_pane_index: int = 0,
|
|
212
|
+
|
|
213
|
+
screen_size_function = lambda stdscr: os.get_terminal_size()[::-1],
|
|
214
|
+
generate_data_for_hidden_columns: bool = False,
|
|
215
|
+
|
|
216
|
+
|
|
192
217
|
# getting_data: threading.Event = threading.Event(),
|
|
193
218
|
|
|
194
219
|
):
|
|
220
|
+
|
|
221
|
+
self.screen_size_function = screen_size_function
|
|
195
222
|
self.stdscr = stdscr
|
|
196
223
|
self.items = items
|
|
197
224
|
self.cursor_pos = cursor_pos
|
|
@@ -233,6 +260,8 @@ class Picker:
|
|
|
233
260
|
self.options_list = options_list
|
|
234
261
|
self.user_settings = user_settings
|
|
235
262
|
self.separator = separator
|
|
263
|
+
self.header_separator = header_separator
|
|
264
|
+
self.header_separator_before_selected_column = header_separator_before_selected_column
|
|
236
265
|
self.search_query = search_query
|
|
237
266
|
self.search_count = search_count
|
|
238
267
|
self.search_index = search_index
|
|
@@ -247,6 +276,10 @@ class Picker:
|
|
|
247
276
|
self.highlight_full_row = highlight_full_row
|
|
248
277
|
self.crosshair_cursor = crosshair_cursor
|
|
249
278
|
self.cell_cursor = cell_cursor
|
|
279
|
+
self.selected_char = selected_char
|
|
280
|
+
self.unselected_char = unselected_char
|
|
281
|
+
self.selecting_char = selecting_char
|
|
282
|
+
self.deselecting_char = deselecting_char
|
|
250
283
|
|
|
251
284
|
self.items_per_page = items_per_page
|
|
252
285
|
self.sort_method = sort_method
|
|
@@ -285,6 +318,7 @@ class Picker:
|
|
|
285
318
|
self.reset_colours = reset_colours
|
|
286
319
|
self.key_remappings = key_remappings
|
|
287
320
|
self.keys_dict = keys_dict
|
|
321
|
+
self.macros = macros
|
|
288
322
|
self.display_infobox = display_infobox
|
|
289
323
|
self.infobox_items = infobox_items
|
|
290
324
|
self.infobox_title = infobox_title
|
|
@@ -342,9 +376,24 @@ class Picker:
|
|
|
342
376
|
self.split_right = split_right
|
|
343
377
|
self.right_panes = right_panes
|
|
344
378
|
self.right_pane_index = right_pane_index
|
|
379
|
+
|
|
380
|
+
self.split_left = split_left
|
|
381
|
+
self.left_panes = left_panes
|
|
382
|
+
self.left_pane_index = left_pane_index
|
|
383
|
+
|
|
345
384
|
self.visible_rows_indices = []
|
|
346
385
|
|
|
386
|
+
self.generate_data_for_hidden_columns = generate_data_for_hidden_columns
|
|
387
|
+
self.thread_stop_event = threading.Event()
|
|
388
|
+
self.data_generation_queue = queue.PriorityQueue()
|
|
389
|
+
self.threads = []
|
|
390
|
+
|
|
347
391
|
|
|
392
|
+
self.process_manager = multiprocessing.Manager()
|
|
393
|
+
# self.data_generation_queue = ProcessSafePriorityQueue
|
|
394
|
+
self.processes = []
|
|
395
|
+
self.items_sync_loop_event = threading.Event()
|
|
396
|
+
self.items_sync_thread = None
|
|
348
397
|
|
|
349
398
|
|
|
350
399
|
self.initialise_picker_state(reset_colours=self.reset_colours)
|
|
@@ -361,6 +410,11 @@ class Picker:
|
|
|
361
410
|
self.getting_data.set()
|
|
362
411
|
|
|
363
412
|
def __sizeof__(self):
|
|
413
|
+
"""
|
|
414
|
+
Return the approximate memory footprint of the Picker instance.
|
|
415
|
+
|
|
416
|
+
This includes the size of the instance itself and the sizes of its attributes.
|
|
417
|
+
"""
|
|
364
418
|
|
|
365
419
|
size = super().__sizeof__()
|
|
366
420
|
|
|
@@ -371,7 +425,19 @@ class Picker:
|
|
|
371
425
|
return size
|
|
372
426
|
|
|
373
427
|
def set_config(self, path: str ="~/.config/listpick/config.toml") -> bool:
|
|
374
|
-
""" Set config from toml file.
|
|
428
|
+
""" Set config from toml file.
|
|
429
|
+
|
|
430
|
+
This method reads a configuration file in TOML format, applies settings
|
|
431
|
+
to the Picker, and returns a boolean indicating success or failure.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
path (str): The path to the configuration file.
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
bool: True if the configuration was successfully set; False otherwise.
|
|
438
|
+
"""
|
|
439
|
+
self.logger.info(f"function: set_config()")
|
|
440
|
+
|
|
375
441
|
path = os.path.expanduser(os.path.expandvars(path))
|
|
376
442
|
if not os.path.exists(path):
|
|
377
443
|
return False
|
|
@@ -380,7 +446,9 @@ class Picker:
|
|
|
380
446
|
except Exception as e:
|
|
381
447
|
self.logger.error(f"get_config({path}) load error. {e}")
|
|
382
448
|
return False
|
|
449
|
+
|
|
383
450
|
|
|
451
|
+
# Change the global theme if colour_theme_number is in the loaded config
|
|
384
452
|
if "general" in config:
|
|
385
453
|
if "colour_theme_number" in config["general"] and config["general"]["colour_theme_number"] != self.colour_theme_number:
|
|
386
454
|
global COLOURS_SET
|
|
@@ -388,7 +456,7 @@ class Picker:
|
|
|
388
456
|
self.colours_end = set_colours(pick=config["general"]["colour_theme_number"], start=1)
|
|
389
457
|
self.colours = get_colours(config["general"]["colour_theme_number"])
|
|
390
458
|
|
|
391
|
-
|
|
459
|
+
# load the rest of the config options
|
|
392
460
|
if "general" in config:
|
|
393
461
|
for key, val in config["general"].items():
|
|
394
462
|
self.logger.info(f"set_config: key={key}, val={val}.")
|
|
@@ -400,27 +468,58 @@ class Picker:
|
|
|
400
468
|
return True
|
|
401
469
|
|
|
402
470
|
def get_config(self, path: str ="~/.config/listpick/config.toml") -> dict:
|
|
403
|
-
"""
|
|
471
|
+
"""
|
|
472
|
+
Retrieve configuration settings from a specified TOML file.
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
path (str): The file path of the configuration file. Default is
|
|
476
|
+
~/.config/listpick/config.toml.
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
dict: A dictionary containing the configuration settings loaded
|
|
480
|
+
from the TOML file. In case of an error, an empty dictionary is returned.
|
|
481
|
+
"""
|
|
482
|
+
|
|
404
483
|
self.logger.info(f"function: get_config()")
|
|
405
484
|
import toml
|
|
406
485
|
with open(os.path.expanduser(path), "r") as f:
|
|
407
486
|
config = toml.load(f)
|
|
408
487
|
return config
|
|
409
488
|
|
|
410
|
-
def update_term_size(self):
|
|
411
|
-
|
|
489
|
+
def update_term_size(self) -> None:
|
|
490
|
+
"""
|
|
491
|
+
Update self.term_h, self.term_w the function provided to the Picker.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
None
|
|
495
|
+
"""
|
|
496
|
+
self.term_h, self.term_w = self.screen_size_function(self.stdscr)
|
|
497
|
+
# self.term_h, self.term_w = self.stdscr.getmaxyx()
|
|
498
|
+
# self.term_w, self.term_h = os.get_terminal_size()
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def get_term_size(self) -> Tuple[int, int]:
|
|
502
|
+
"""
|
|
503
|
+
Get the current terminal size using the function provided to the Picker.
|
|
412
504
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
return
|
|
505
|
+
Returns:
|
|
506
|
+
Tuple[int, int]: A tuple containing the (height, width) of the terminal.
|
|
507
|
+
"""
|
|
508
|
+
return self.screen_size_function(self.stdscr)
|
|
509
|
+
# return self.stdscr.getmaxyx()
|
|
510
|
+
# w, h = os.get_terminal_size()
|
|
511
|
+
# return h, w
|
|
417
512
|
|
|
418
|
-
def calculate_section_sizes(self):
|
|
513
|
+
def calculate_section_sizes(self) -> None:
|
|
419
514
|
"""
|
|
420
515
|
Calculte the following for the Picker:
|
|
421
516
|
self.items_per_page: the number of entry rows displayed
|
|
422
517
|
self.bottom_space: the size of the footer + the bottom buffer space
|
|
423
518
|
self.top_space: the size of the space at the top of the picker: title + modes + header + top_gap
|
|
519
|
+
Calculate and update the sizes of various sections of the Picker.
|
|
520
|
+
|
|
521
|
+
Returns:
|
|
522
|
+
None
|
|
424
523
|
"""
|
|
425
524
|
|
|
426
525
|
self.logger.debug(f"function: calculate_section_sizes()")
|
|
@@ -428,13 +527,35 @@ class Picker:
|
|
|
428
527
|
# self.bottom_space
|
|
429
528
|
self.bottom_space = self.footer.height if self.show_footer else 0
|
|
430
529
|
|
|
530
|
+
# self.left_gutter_width
|
|
531
|
+
self.left_gutter_width = 1 if self.highlight_full_row else 2
|
|
532
|
+
if self.show_row_header: self.left_gutter_width += len(str(len(self.items))) + 2
|
|
533
|
+
|
|
534
|
+
|
|
431
535
|
## self.top_space
|
|
432
536
|
self.update_term_size()
|
|
537
|
+
self.rows_w, self.rows_h = self.term_w, self.term_h
|
|
538
|
+
self.rows_box_x_i = 0
|
|
539
|
+
self.rows_box_x_f = self.term_w
|
|
540
|
+
self.left_pane_width = self.right_pane_width = 0
|
|
433
541
|
if self.split_right and len(self.right_panes):
|
|
434
542
|
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
435
|
-
self.
|
|
436
|
-
|
|
437
|
-
self.
|
|
543
|
+
self.right_pane_width = int(self.term_w*proportion)
|
|
544
|
+
self.rows_w -= self.right_pane_width
|
|
545
|
+
self.rows_box_x_f -= self.right_pane_width
|
|
546
|
+
if self.split_left and len(self.left_panes):
|
|
547
|
+
proportion = self.left_panes[self.left_pane_index]["proportion"]
|
|
548
|
+
self.left_pane_width = int(self.term_w*proportion)
|
|
549
|
+
self.rows_w -= self.left_pane_width
|
|
550
|
+
self.rows_box_x_i += self.left_pane_width
|
|
551
|
+
if self.left_pane_width + self.right_pane_width >= self.term_w-3:
|
|
552
|
+
self.rows_w += 10
|
|
553
|
+
self.left_pane_width -= 5
|
|
554
|
+
self.right_pane_width -= 5
|
|
555
|
+
self.rows_box_x_i -= 5
|
|
556
|
+
self.rows_box_x_f += 5
|
|
557
|
+
|
|
558
|
+
|
|
438
559
|
|
|
439
560
|
self.top_space = self.top_gap
|
|
440
561
|
if self.title: self.top_space+=1
|
|
@@ -452,26 +573,45 @@ class Picker:
|
|
|
452
573
|
self.top_space += ((self.term_h-(self.top_space+self.bottom_space))-len(self.indexed_items))//2
|
|
453
574
|
|
|
454
575
|
# self.column_widths
|
|
455
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
456
|
-
visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
|
|
576
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
577
|
+
visible_columns_total_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
457
578
|
|
|
458
579
|
# self.startx
|
|
459
580
|
self.startx = 1 if self.highlight_full_row else 2
|
|
460
581
|
if self.show_row_header: self.startx += len(str(len(self.items))) + 2
|
|
461
582
|
if visible_columns_total_width < self.rows_w and self.centre_in_terminal:
|
|
462
583
|
self.startx += (self.rows_w - visible_columns_total_width) // 2
|
|
584
|
+
self.startx += self.left_pane_width
|
|
585
|
+
# if self.split_left and len(self.left_panes):
|
|
586
|
+
# proportion = self.left_panes[self.left_pane_index]["proportion"]
|
|
587
|
+
# self.startx += int(self.term_w*proportion)
|
|
588
|
+
|
|
589
|
+
self.endx = self.startx+self.rows_w
|
|
590
|
+
|
|
591
|
+
|
|
463
592
|
|
|
464
593
|
def get_visible_rows(self) -> list[list[str]]:
|
|
594
|
+
"""
|
|
595
|
+
Calculate and return the currently visible rows based on the cursor position and pagination settings.
|
|
465
596
|
|
|
597
|
+
This method determines which rows from the indexed items are visible on the screen,
|
|
598
|
+
accounting for pagination and scrolling. It sets the starting and ending indices
|
|
599
|
+
based on the current cursor position and the number of items per page.
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
list[list[str]]: The currently visible rows as a list of lists, where each inner
|
|
603
|
+
list represents a row of data. If there are no indexed items, it returns the
|
|
604
|
+
items array.
|
|
605
|
+
"""
|
|
466
606
|
self.logger.debug(f"function: get_visible_rows()")
|
|
467
607
|
## Scroll with column select
|
|
468
608
|
if self.paginate:
|
|
469
|
-
start_index = (self.cursor_pos//self.items_per_page) * self.items_per_page
|
|
609
|
+
start_index = (self.cursor_pos // self.items_per_page) * self.items_per_page
|
|
470
610
|
end_index = min(start_index + self.items_per_page, len(self.indexed_items))
|
|
471
611
|
## Scroll
|
|
472
612
|
else:
|
|
473
|
-
scrolloff = self.items_per_page//2
|
|
474
|
-
start_index = max(0, min(self.cursor_pos - (self.items_per_page-scrolloff), len(self.indexed_items)-self.items_per_page))
|
|
613
|
+
scrolloff = self.items_per_page // 2
|
|
614
|
+
start_index = max(0, min(self.cursor_pos - (self.items_per_page - scrolloff), len(self.indexed_items) - self.items_per_page))
|
|
475
615
|
end_index = min(start_index + self.items_per_page, len(self.indexed_items))
|
|
476
616
|
if len(self.indexed_items) == 0: start_index, end_index = 0, 0
|
|
477
617
|
|
|
@@ -484,13 +624,15 @@ class Picker:
|
|
|
484
624
|
def initialise_picker_state(self, reset_colours=False) -> None:
|
|
485
625
|
""" Initialise state variables for the picker. These are: debugging and colours. """
|
|
486
626
|
|
|
627
|
+
# Define global curses colours
|
|
487
628
|
if curses.has_colors() and self.colours != None:
|
|
488
|
-
# raise Exception("Terminal does not support color")
|
|
489
629
|
curses.start_color()
|
|
630
|
+
|
|
490
631
|
if reset_colours:
|
|
491
632
|
global COLOURS_SET
|
|
492
633
|
COLOURS_SET = False
|
|
493
634
|
self.colours_end = set_colours(pick=self.colour_theme_number, start=self.colours_start)
|
|
635
|
+
|
|
494
636
|
if curses.COLORS >= 255 and curses.COLOR_PAIRS >= 150:
|
|
495
637
|
self.colours_start = self.colours_start
|
|
496
638
|
self.notification_colours_start = self.colours_start+50
|
|
@@ -507,64 +649,40 @@ class Picker:
|
|
|
507
649
|
self.colours = get_colours(self.colour_theme_number)
|
|
508
650
|
|
|
509
651
|
|
|
652
|
+
# Start logger
|
|
510
653
|
debug_levels = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL]
|
|
511
654
|
dbglvl = debug_levels[self.debug_level]
|
|
512
655
|
self.logger = setup_logger(name="picker_log", log_file="picker.log", log_enabled=self.debug, level =dbglvl)
|
|
513
656
|
self.logger.info(f"Initialiasing Picker.")
|
|
657
|
+
|
|
514
658
|
self.update_term_size()
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
# 1 2 3 4 5
|
|
519
|
-
# logger = logging.getLogger(__file__)
|
|
520
|
-
# if self.debug_level == 0:
|
|
521
|
-
# logger = logging.getLogger()
|
|
522
|
-
# logger.disabled = True
|
|
523
|
-
# else:
|
|
524
|
-
#
|
|
525
|
-
# file_handler = logging.FileHandler(f"{self.title}.log", mode='w')
|
|
526
|
-
# formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', '%m-%d-%Y %H:%M:%S')
|
|
527
|
-
# file_handler.setFormatter(formatter)
|
|
528
|
-
# logger.addHandler(file_handler)
|
|
529
|
-
# logger.setLevel(debug_levels[self.debug_level-1])
|
|
530
|
-
|
|
531
|
-
# logging.basicConfig(
|
|
532
|
-
# level=debug_levels[self.debug_level-1],
|
|
533
|
-
# format='%(asctime)s - %(levelname)s - %(message)s',
|
|
534
|
-
# datefmt='%m-%d-%Y %H:%M:%S',
|
|
535
|
-
# filename=f"{self.title}.log",
|
|
536
|
-
# filemode="w",
|
|
537
|
-
# )
|
|
538
|
-
#
|
|
539
|
-
# self.logger.info(f"Starging log. Log level {logger.getEffectiveLevel()}")
|
|
540
|
-
# self.logger.info(f"Starging log. Log level {repr(debug_levels)}, {self.debug_level}, {debug_levels[self.debug_level-1]}")
|
|
541
|
-
# self.notification(self.stdscr, f"Starging log. Log level {repr(debug_levels)}, {self.debug_level}, {debug_levels[self.debug_level-1]}")
|
|
542
|
-
# self.notification(self.stdscr, f"{__file__}")
|
|
543
|
-
|
|
544
|
-
## Logging level plan
|
|
545
|
-
# DEBUG: loop functions, draw screen, etc.
|
|
546
|
-
# INFO: main functions
|
|
547
|
-
# WARNING: any try-except fails
|
|
548
|
-
|
|
549
|
-
# No set_escdelay function on windows.
|
|
659
|
+
|
|
660
|
+
# The curses implementation for some systems (e.g., windows) does not allow set_escdelay
|
|
550
661
|
try:
|
|
551
662
|
curses.set_escdelay(25)
|
|
552
663
|
except:
|
|
553
664
|
logging.warning("Error trying to set curses.set_escdelay")
|
|
554
665
|
|
|
555
|
-
# self.stdscr.clear()
|
|
556
|
-
# self.stdscr.refresh()
|
|
557
|
-
# self.draw_screen()
|
|
558
|
-
|
|
559
666
|
def initialise_variables(self, get_data: bool = False) -> None:
|
|
560
|
-
"""
|
|
667
|
+
"""
|
|
668
|
+
This method sets up the internal state of the Picker by initialising various attributes,
|
|
669
|
+
getting new data (if get_data is True), and ensuring that the lists used for tracking
|
|
670
|
+
selections, options, and items are correctly of the correct type, size, and shape. If
|
|
671
|
+
filter or sort queries are set then they are applied (or re-applied as the case may be).
|
|
672
|
+
The cursor_pos and selections are retained by tracking the id of the rows (where the id
|
|
673
|
+
is row[self.id_column]).
|
|
674
|
+
|
|
675
|
+
Parameters:
|
|
676
|
+
- get_data (bool): If True, pulls data synchronously and updates tracking variables.
|
|
677
|
+
"""
|
|
561
678
|
|
|
562
679
|
self.logger.info(f"function: initialise_variables()")
|
|
563
680
|
|
|
564
681
|
tracking = False
|
|
565
682
|
|
|
566
683
|
## Get data synchronously
|
|
567
|
-
if get_data
|
|
684
|
+
if get_data:
|
|
685
|
+
# Track cursor_pos and selections by ther id (row[self.id_column][col])
|
|
568
686
|
if self.track_entries_upon_refresh and len(self.items) > 0:
|
|
569
687
|
tracking = True
|
|
570
688
|
selected_indices = get_selected_indices(self.selections)
|
|
@@ -572,34 +690,41 @@ class Picker:
|
|
|
572
690
|
self.ids = [item[self.id_column] for i, item in enumerate(self.items) if i in selected_indices]
|
|
573
691
|
self.ids_tuples = [(i, item[self.id_column]) for i, item in enumerate(self.items) if i in selected_indices]
|
|
574
692
|
|
|
575
|
-
if len(self.indexed_items) > 0 and len(self.indexed_items)
|
|
693
|
+
if len(self.indexed_items) > 0 and self.cursor_pos < len(self.indexed_items) and len(self.indexed_items[0][1]) >= self.id_column:
|
|
576
694
|
self.cursor_pos_id = self.indexed_items[self.cursor_pos][1][self.id_column]
|
|
577
695
|
self.cursor_pos_prev = self.cursor_pos
|
|
578
|
-
|
|
579
|
-
|
|
580
696
|
|
|
697
|
+
# Set the state of the threading event
|
|
698
|
+
# Though we are getting data synchronously, we ensure the correct state for self.getting_data
|
|
581
699
|
self.getting_data.clear()
|
|
582
|
-
self.refresh_function(
|
|
700
|
+
self.refresh_function(
|
|
701
|
+
self.items,
|
|
702
|
+
self.header,
|
|
703
|
+
self.visible_rows_indices,
|
|
704
|
+
self.getting_data,
|
|
705
|
+
self.get_function_data(),
|
|
706
|
+
)
|
|
583
707
|
|
|
584
|
-
self.items = pad_lists_to_same_length(self.items)
|
|
585
708
|
|
|
709
|
+
# Ensure that an emtpy items object has the form [[]]
|
|
586
710
|
if self.items == []: self.items = [[]]
|
|
587
|
-
|
|
711
|
+
|
|
712
|
+
# Ensure that items is a List[List[Str]] object
|
|
588
713
|
if len(self.items) > 0 and not isinstance(self.items[0], list):
|
|
589
714
|
self.items = [[item] for item in self.items]
|
|
590
715
|
# self.items = [[str(cell) for cell in row] for row in self.items]
|
|
591
716
|
|
|
717
|
+
# Ensure that the each of the rows of the items are of the same length
|
|
718
|
+
self.items = pad_lists_to_same_length(self.items)
|
|
592
719
|
|
|
593
720
|
# Ensure that header is of the same length as the rows
|
|
594
721
|
if self.header and len(self.items) > 0 and len(self.header) != len(self.items[0]):
|
|
595
722
|
self.header = [str(self.header[i]) if i < len(self.header) else "" for i in range(len(self.items[0]))]
|
|
596
723
|
|
|
597
|
-
# Constants
|
|
598
|
-
# DEFAULT_ITEMS_PER_PAGE = os.get_terminal_size().lines - top_gap*2-2-int(bool(header))
|
|
599
|
-
|
|
600
724
|
self.calculate_section_sizes()
|
|
601
725
|
|
|
602
|
-
|
|
726
|
+
|
|
727
|
+
# Ensure that the selection-tracking variables are the correct shape
|
|
603
728
|
if len(self.selections) != len(self.items):
|
|
604
729
|
self.selections = {i : False if i not in self.selections else bool(self.selections[i]) for i in range(len(self.items))}
|
|
605
730
|
|
|
@@ -610,31 +735,34 @@ class Picker:
|
|
|
610
735
|
self.cell_selections = {}
|
|
611
736
|
self.selected_cells_by_row = {}
|
|
612
737
|
|
|
738
|
+
def extend_list_to_length(lst, length, default_value):
|
|
739
|
+
"""Extend a list to the target length using a default value."""
|
|
740
|
+
if len(lst) < length:
|
|
741
|
+
lst.extend([copy.deepcopy(default_value) for _ in range(length - len(lst))])
|
|
613
742
|
|
|
614
743
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
if
|
|
620
|
-
self.
|
|
621
|
-
|
|
622
|
-
self.
|
|
623
|
-
|
|
624
|
-
self.editable_columns = self.
|
|
625
|
-
|
|
744
|
+
row_count = len(self.items)
|
|
745
|
+
col_count = len(self.items[0]) if row_count else 0
|
|
746
|
+
|
|
747
|
+
# Ensure that the length of the option lists are of the correct length.
|
|
748
|
+
if row_count > 0:
|
|
749
|
+
extend_list_to_length(self.require_option, length=row_count, default_value=self.require_option_default)
|
|
750
|
+
extend_list_to_length(self.option_functions, length=row_count, default_value=self.default_option_function)
|
|
751
|
+
extend_list_to_length(self.columns_sort_method, length=col_count, default_value=0)
|
|
752
|
+
extend_list_to_length(self.sort_reverse, length=col_count, default_value=False)
|
|
753
|
+
extend_list_to_length(self.editable_columns, length=col_count, default_value=self.editable_by_default)
|
|
754
|
+
|
|
755
|
+
if row_count > 0 and len(self.column_indices) < len(self.items[0]):
|
|
626
756
|
self.column_indices = self.column_indices + [i for i in range(len(self.column_indices), len(self.items[0]))]
|
|
627
757
|
|
|
628
758
|
|
|
629
759
|
|
|
630
|
-
#
|
|
631
|
-
# self.indexed_items = list(enumerate(items2))
|
|
760
|
+
# Create an indexed list of the items which will track the visible rows
|
|
632
761
|
if self.items == [[]]: self.indexed_items = []
|
|
633
762
|
else: self.indexed_items = list(enumerate(self.items))
|
|
634
763
|
|
|
635
|
-
#
|
|
764
|
+
# Apply the filter query
|
|
636
765
|
if self.filter_query:
|
|
637
|
-
# prev_index = self.indexed_items[cursor_pos][0] if len(self.indexed_items)>0 else 0
|
|
638
766
|
# prev_index = self.indexed_items[cursor_pos][0] if len(self.indexed_items)>0 else 0
|
|
639
767
|
self.indexed_items = filter_items(self.items, self.indexed_items, self.filter_query)
|
|
640
768
|
if self.cursor_pos in [x[0] for x in self.indexed_items]: self.cursor_pos = [x[0] for x in self.indexed_items].index(self.cursor_pos)
|
|
@@ -650,22 +778,30 @@ class Picker:
|
|
|
650
778
|
)
|
|
651
779
|
if return_val:
|
|
652
780
|
self.cursor_pos, self.search_index, self.search_count, self.highlights = tmp_cursor, tmp_index, tmp_count, tmp_highlights
|
|
653
|
-
|
|
781
|
+
|
|
782
|
+
# Apply the current sort method
|
|
654
783
|
if len(self.indexed_items) > 0:
|
|
655
784
|
sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort self.items based on new column
|
|
656
785
|
|
|
657
786
|
|
|
658
|
-
#
|
|
659
|
-
## Move to a selectable row (if applicable)
|
|
787
|
+
# If we have more unselectable indices than rows, clear the unselectable_indices
|
|
660
788
|
if len(self.items) <= len(self.unselectable_indices): self.unselectable_indices = []
|
|
661
|
-
new_pos = (self.cursor_pos)%len(self.items)
|
|
662
|
-
while new_pos in self.unselectable_indices and new_pos != self.cursor_pos:
|
|
663
|
-
new_pos = (new_pos + 1) % len(self.items)
|
|
664
789
|
|
|
665
|
-
|
|
666
|
-
self.cursor_pos
|
|
790
|
+
# Move cursur to a selectable row if we are currently on an unselectable row)
|
|
791
|
+
if self.cursor_pos * len(self.items) in self.unselectable_indices:
|
|
792
|
+
original_pos = new_pos = (self.cursor_pos)%len(self.items)
|
|
793
|
+
while new_pos in self.unselectable_indices:
|
|
794
|
+
new_pos = (new_pos + 1) % len(self.items)
|
|
795
|
+
|
|
796
|
+
# Break if we loop back to the original position
|
|
797
|
+
if new_pos == original_pos:
|
|
798
|
+
break
|
|
799
|
+
|
|
800
|
+
self.cursor_pos = max(0, min(new_pos, len(self.items)-1))
|
|
801
|
+
|
|
802
|
+
# Initialise sheets
|
|
803
|
+
extend_list_to_length(self.sheet_states, length=len(self.sheets), default_value={})
|
|
667
804
|
|
|
668
|
-
# Sheets and files
|
|
669
805
|
if len(self.sheet_states) < len(self.sheets):
|
|
670
806
|
self.sheet_states += [{} for _ in range(len(self.sheets) - len(self.sheet_states))]
|
|
671
807
|
if len(self.sheets):
|
|
@@ -673,15 +809,16 @@ class Picker:
|
|
|
673
809
|
self.sheet_index = 0
|
|
674
810
|
self.sheet_name = self.sheets[self.sheet_index]
|
|
675
811
|
|
|
676
|
-
|
|
677
|
-
|
|
812
|
+
# Initialise files
|
|
813
|
+
extend_list_to_length(self.loaded_file_states, length=len(self.loaded_files), default_value={})
|
|
678
814
|
if len(self.loaded_files):
|
|
679
815
|
if self.loaded_file_index >= len(self.loaded_files):
|
|
680
816
|
self.loaded_file_index = 0
|
|
681
817
|
self.loaded_file = self.loaded_files[self.loaded_file_index]
|
|
682
818
|
|
|
683
|
-
|
|
684
|
-
# Ensure that selected indices are
|
|
819
|
+
|
|
820
|
+
# Ensure that the correct cursor_pos and selected indices are reselected
|
|
821
|
+
# if we have fetched new data.
|
|
685
822
|
if self.track_entries_upon_refresh and (self.data_ready or tracking) and len(self.items) > 1:
|
|
686
823
|
selected_indices = []
|
|
687
824
|
all_ids = [item[self.id_column] for item in self.items]
|
|
@@ -704,6 +841,8 @@ class Picker:
|
|
|
704
841
|
|
|
705
842
|
|
|
706
843
|
|
|
844
|
+
# Ensure cursor_pos is set to a valid index
|
|
845
|
+
# If we have fetched new data then we attempt to set cursor_pos to the row with the same id as prev
|
|
707
846
|
if len(self.indexed_items):
|
|
708
847
|
if self.pin_cursor:
|
|
709
848
|
self.cursor_pos = min(self.cursor_pos_prev, len(self.indexed_items)-1)
|
|
@@ -715,7 +854,12 @@ class Picker:
|
|
|
715
854
|
else:
|
|
716
855
|
self.cursor_pos = 0
|
|
717
856
|
|
|
718
|
-
|
|
857
|
+
|
|
858
|
+
# Ensure that the pane indices are within the range of the available panes.
|
|
859
|
+
if len(self.left_panes): self.left_pane_index %= len(self.left_panes)
|
|
860
|
+
else: self.left_pane_index = 0
|
|
861
|
+
if len(self.right_panes): self.right_pane_index %= len(self.right_panes)
|
|
862
|
+
else: self.right_pane_index = 0
|
|
719
863
|
|
|
720
864
|
|
|
721
865
|
|
|
@@ -800,7 +944,8 @@ class Picker:
|
|
|
800
944
|
self.draw_screen_(clear)
|
|
801
945
|
except Exception as e:
|
|
802
946
|
self.logger.warning(f"self.draw_screen_() error. {e}")
|
|
803
|
-
|
|
947
|
+
finally:
|
|
948
|
+
self.stdscr.refresh()
|
|
804
949
|
|
|
805
950
|
def draw_screen_(self, clear: bool = True) -> None:
|
|
806
951
|
""" Draw Picker screen. """
|
|
@@ -811,14 +956,11 @@ class Picker:
|
|
|
811
956
|
self.stdscr.erase()
|
|
812
957
|
|
|
813
958
|
self.update_term_size()
|
|
814
|
-
if self.split_right and len(self.right_panes):
|
|
815
|
-
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
816
|
-
self.rows_w, self.rows_h = int(self.term_w*proportion), self.term_h
|
|
817
|
-
else:
|
|
818
|
-
self.rows_w, self.rows_h = self.term_w, self.term_h
|
|
819
959
|
|
|
820
|
-
#
|
|
960
|
+
# Determine footer size
|
|
821
961
|
self.footer.adjust_sizes(self.term_h,self.term_w)
|
|
962
|
+
|
|
963
|
+
# The height of the footer may need to be adjusted if the file changes.
|
|
822
964
|
self.calculate_section_sizes()
|
|
823
965
|
|
|
824
966
|
# Test if the terminal is of a sufficient size to display the picker
|
|
@@ -836,28 +978,18 @@ class Picker:
|
|
|
836
978
|
end_index = min(start_index + self.items_per_page, len(self.indexed_items))
|
|
837
979
|
if len(self.indexed_items) == 0: start_index, end_index = 0, 0
|
|
838
980
|
|
|
839
|
-
# self.column_widths = get_column_widths(self.items, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
|
|
840
|
-
# Determine widths based only on the currently indexed rows
|
|
841
|
-
# rows = [v[1] for v in self.indexed_items] if len(self.indexed_items) else self.items
|
|
842
|
-
# Determine widths based only on the currently displayed indexed rows
|
|
843
|
-
# rows = [v[1] for v in self.indexed_items[start_index:end_index]] if len(self.indexed_items) else self.items
|
|
844
981
|
self.get_visible_rows()
|
|
845
982
|
self.column_widths = get_column_widths(self.visible_rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=self.rows_w, unicode_char_width=self.unicode_char_width)
|
|
846
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
847
|
-
visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
|
|
848
|
-
|
|
849
|
-
# Determine the number of items_per_page, top_size and bottom_size
|
|
850
|
-
# self.calculate_section_sizes()
|
|
983
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
984
|
+
visible_columns_total_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
851
985
|
|
|
852
|
-
# top_space = self.top_gap
|
|
853
986
|
|
|
854
|
-
## Display title
|
|
987
|
+
## Display title
|
|
855
988
|
if self.title:
|
|
856
989
|
padded_title = f" {self.title.strip()} "
|
|
857
990
|
self.stdscr.addstr(self.top_gap, 0, f"{' ':^{self.term_w}}", curses.color_pair(self.colours_start+16))
|
|
858
991
|
title_x = (self.term_w-wcswidth(padded_title))//2
|
|
859
992
|
self.stdscr.addstr(self.top_gap, title_x, padded_title, curses.color_pair(self.colours_start+16) | curses.A_BOLD)
|
|
860
|
-
# top_space += 1
|
|
861
993
|
|
|
862
994
|
## Display modes
|
|
863
995
|
if self.display_modes and self.modes not in [[{}], []]:
|
|
@@ -879,7 +1011,6 @@ class Picker:
|
|
|
879
1011
|
else:
|
|
880
1012
|
self.stdscr.addstr(self.top_gap+1, xmode, mode_str, curses.color_pair(self.colours_start+15) | curses.A_UNDERLINE)
|
|
881
1013
|
xmode += split_space+mode_widths[i]
|
|
882
|
-
# top_space += 1
|
|
883
1014
|
|
|
884
1015
|
## Display header
|
|
885
1016
|
if self.header and self.show_header:
|
|
@@ -897,137 +1028,129 @@ class Picker:
|
|
|
897
1028
|
|
|
898
1029
|
|
|
899
1030
|
header_str += f"{col_str:^{self.column_widths[i]-len(number)}}"
|
|
900
|
-
|
|
1031
|
+
if i == self.selected_column-1:
|
|
1032
|
+
header_str += self.header_separator_before_selected_column
|
|
1033
|
+
else:
|
|
1034
|
+
header_str += self.header_separator
|
|
1035
|
+
header_str_w = min(self.rows_w-self.left_gutter_width, visible_columns_total_width+1, self.term_w-self.startx)
|
|
901
1036
|
|
|
902
1037
|
header_str = header_str[self.leftmost_char:]
|
|
1038
|
+
header_str = header_str[:header_str_w]
|
|
903
1039
|
header_ypos = self.top_gap + bool(self.title) + bool(self.display_modes and self.modes)
|
|
904
|
-
|
|
905
|
-
|
|
1040
|
+
|
|
1041
|
+
# Ensure that the full header width is filled--important if the header rows do not fill the terminal width
|
|
1042
|
+
self.stdscr.addstr(header_ypos, self.rows_box_x_i, ' '*self.rows_w, curses.color_pair(self.colours_start+28) | curses.A_BOLD)
|
|
1043
|
+
|
|
1044
|
+
# Draw header string
|
|
1045
|
+
self.stdscr.addstr(header_ypos, self.startx, header_str, curses.color_pair(self.colours_start+4) | curses.A_BOLD)
|
|
906
1046
|
|
|
907
1047
|
# Highlight sort column
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
x_pos = len(up_to_selected_col) - self.leftmost_char + self.startx
|
|
1048
|
+
if self.selected_column != None and self.selected_column not in self.hidden_columns:
|
|
1049
|
+
# start of string is on screen
|
|
1050
|
+
col_width = self.column_widths[self.selected_column]
|
|
1051
|
+
number = f"{self.selected_column}. " if self.number_columns else ""
|
|
1052
|
+
col_str = self.header[self.selected_column][:self.column_widths[self.selected_column]-len(number)]
|
|
1053
|
+
highlighted_col_str = (number+f"{col_str:^{self.column_widths[self.selected_column]-len(number)}}") + self.separator
|
|
1054
|
+
|
|
1055
|
+
if len(self.column_widths) == 1:
|
|
1056
|
+
colour = curses.color_pair(self.colours_start+28) | curses.A_BOLD
|
|
1057
|
+
else:
|
|
1058
|
+
colour = curses.color_pair(self.colours_start+19) | curses.A_BOLD
|
|
1059
|
+
# Start of selected column is on the screen
|
|
1060
|
+
if self.leftmost_char <= len(up_to_selected_col) and self.leftmost_char+self.rows_w-self.left_gutter_width > len(up_to_selected_col):
|
|
1061
|
+
x_pos = len(up_to_selected_col) - self.leftmost_char + self.startx
|
|
923
1062
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1063
|
+
# Whole cell of the selected column is on the screen
|
|
1064
|
+
if len(up_to_selected_col)+col_width - self.leftmost_char < self.rows_w-self.left_gutter_width:
|
|
1065
|
+
disp_str = highlighted_col_str
|
|
927
1066
|
|
|
928
|
-
|
|
929
|
-
else:
|
|
930
|
-
overflow = (len(up_to_selected_col)+len(highlighted_col_str)) - (self.leftmost_char+self.rows_w - self.startx)
|
|
931
|
-
disp_str = highlighted_col_str[:-overflow]
|
|
932
|
-
|
|
933
|
-
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
934
|
-
# Start of the cell is to the right of the screen
|
|
935
|
-
elif self.leftmost_char+self.rows_w <= len(up_to_selected_col):
|
|
936
|
-
pass
|
|
937
|
-
# The end of the cell is on the screen, the start of the cell is not
|
|
938
|
-
elif 0 <= len(up_to_selected_col)+col_width - self.leftmost_char <= self.rows_w :
|
|
939
|
-
x_pos = self.startx
|
|
940
|
-
beg = self.leftmost_char - len(up_to_selected_col)
|
|
941
|
-
disp_str = highlighted_col_str[beg:]
|
|
942
|
-
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
943
|
-
# The middle of the cell is on the screen, the start and end of the cell are not
|
|
944
|
-
elif self.leftmost_char <= len(up_to_selected_col) + col_width//2 <= self.leftmost_char+self.rows_w:
|
|
945
|
-
beg = self.leftmost_char - len(up_to_selected_col)
|
|
946
|
-
overflow = (len(up_to_selected_col)+len(highlighted_col_str)) - (self.leftmost_char+self.rows_w)
|
|
947
|
-
disp_str = highlighted_col_str[beg:-overflow]
|
|
948
|
-
|
|
949
|
-
x_pos = self.startx
|
|
950
|
-
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
951
|
-
# The cell is to the left of the screen
|
|
1067
|
+
# Start of the cell is on the screen, but the end of the cell is not
|
|
952
1068
|
else:
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1069
|
+
overflow = (len(up_to_selected_col)+len(highlighted_col_str)) - (self.leftmost_char+self.rows_w - self.left_gutter_width)
|
|
1070
|
+
disp_str = highlighted_col_str[:-overflow]
|
|
1071
|
+
disp_str_w = min(len(disp_str), self.term_w-x_pos)
|
|
1072
|
+
disp_str = truncate_to_display_width(disp_str, disp_str_w, self.centre_in_cols, self.unicode_char_width)
|
|
1073
|
+
|
|
1074
|
+
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
1075
|
+
# Start of the cell is to the right of the screen
|
|
1076
|
+
elif self.leftmost_char+self.rows_w <= len(up_to_selected_col):
|
|
1077
|
+
pass
|
|
1078
|
+
# The end of the cell is on the screen, the start of the cell is not
|
|
1079
|
+
elif 0 <= len(up_to_selected_col)+col_width - self.leftmost_char <= self.rows_w :
|
|
1080
|
+
x_pos = self.startx
|
|
1081
|
+
beg = self.leftmost_char - len(up_to_selected_col)
|
|
1082
|
+
disp_str = highlighted_col_str[beg:]
|
|
1083
|
+
disp_str_w = min(len(disp_str), self.term_w-x_pos)
|
|
1084
|
+
disp_str = truncate_to_display_width(disp_str, disp_str_w, self.centre_in_cols, self.unicode_char_width)
|
|
1085
|
+
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
1086
|
+
# The middle of the cell is on the screen, the start and end of the cell are not
|
|
1087
|
+
elif self.leftmost_char <= len(up_to_selected_col) + col_width//2 <= self.leftmost_char+self.rows_w:
|
|
1088
|
+
beg = self.leftmost_char - len(up_to_selected_col)
|
|
1089
|
+
overflow = (len(up_to_selected_col)+len(highlighted_col_str)) - (self.leftmost_char+self.rows_w)
|
|
1090
|
+
x_pos = self.startx
|
|
1091
|
+
disp_str = highlighted_col_str[beg:-overflow]
|
|
1092
|
+
disp_str_w = min(len(disp_str), self.term_w-x_pos)
|
|
1093
|
+
disp_str = truncate_to_display_width(disp_str, disp_str_w, self.centre_in_cols, self.unicode_char_width)
|
|
1094
|
+
|
|
1095
|
+
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
1096
|
+
|
|
1097
|
+
# The cell is to the left of the focused part of the screen
|
|
1098
|
+
else:
|
|
1099
|
+
pass
|
|
979
1100
|
|
|
980
1101
|
# Display row header
|
|
981
1102
|
if self.show_row_header:
|
|
982
1103
|
for idx in range(start_index, end_index):
|
|
983
1104
|
y = idx - start_index + self.top_space
|
|
984
1105
|
if idx == self.cursor_pos:
|
|
985
|
-
self.stdscr.addstr(y,
|
|
1106
|
+
self.stdscr.addstr(y, self.startx-self.left_gutter_width, f" {self.indexed_items[idx][0]} ", curses.color_pair(self.colours_start+19) | curses.A_BOLD)
|
|
986
1107
|
else:
|
|
987
|
-
self.stdscr.addstr(y,
|
|
1108
|
+
self.stdscr.addstr(y, self.startx-self.left_gutter_width, f" {self.indexed_items[idx][0]} ", curses.color_pair(self.colours_start+4) | curses.A_BOLD)
|
|
988
1109
|
|
|
989
1110
|
|
|
990
1111
|
def highlight_cell(row: int, col:int, visible_column_widths, colour_pair_number: int = 5, bold: bool = False, y:int = 0):
|
|
991
1112
|
|
|
992
1113
|
cell_pos = sum(visible_column_widths[:col])+col*len(self.separator)-self.leftmost_char + self.startx
|
|
1114
|
+
cell_pos_relative = sum(visible_column_widths[:col])+col*len(self.separator)-self.leftmost_char + self.left_gutter_width
|
|
993
1115
|
# cell_width = self.column_widths[self.selected_column]
|
|
994
1116
|
cell_width = visible_column_widths[col] + len(self.separator)
|
|
995
|
-
cell_max_width = self.rows_w-cell_pos
|
|
1117
|
+
cell_max_width = min(self.rows_w-self.left_gutter_width, self.term_w-cell_pos)
|
|
996
1118
|
|
|
997
1119
|
if bold:
|
|
998
1120
|
colour = curses.color_pair(self.colours_start+colour_pair_number) | curses.A_BOLD
|
|
999
1121
|
else:
|
|
1000
1122
|
colour = curses.color_pair(self.colours_start+colour_pair_number)
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
if
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
else:
|
|
1008
|
-
cell_value = self.indexed_items[row][1][col][:self.column_widths[col]] + self.separator
|
|
1009
|
-
# cell_value = cell_value[:min(cell_width, cell_max_width)-len(self.separator)]
|
|
1010
|
-
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
1011
|
-
# cell_value = cell_value + self.separator
|
|
1012
|
-
# cell_value = cell_value
|
|
1013
|
-
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
1014
|
-
self.stdscr.addstr(y, cell_pos, cell_value, colour)
|
|
1015
|
-
# Part of the cell is on screen
|
|
1016
|
-
elif self.startx <= cell_pos+cell_width and cell_pos < (self.rows_w):
|
|
1017
|
-
cell_start = self.startx - cell_pos
|
|
1018
|
-
# self.stdscr.addstr(y, self.startx, ' '*(cell_width-cell_start), curses.color_pair(self.colours_start+colour_pair_number))
|
|
1019
|
-
cell_value = self.indexed_items[row][1][col]
|
|
1020
|
-
cell_value = f"{cell_value:^{self.column_widths[col]}}"
|
|
1021
|
-
|
|
1022
|
-
cell_value = cell_value[cell_start:visible_column_widths[col]][:self.rows_w-self.startx]
|
|
1023
|
-
self.stdscr.addstr(y, self.startx, cell_value, colour)
|
|
1123
|
+
# Start of cell is on screen
|
|
1124
|
+
if self.startx <= cell_pos <= self.rows_w+self.startx:
|
|
1125
|
+
s = "max" if cell_max_width <= cell_width else "norm"
|
|
1126
|
+
self.stdscr.addstr(y, cell_pos, (' '*cell_width)[:cell_max_width], colour)
|
|
1127
|
+
if self.centre_in_cols:
|
|
1128
|
+
cell_value = f"{self.indexed_items[row][1][col]:^{cell_width-len(self.separator)}}" + self.separator
|
|
1024
1129
|
else:
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1130
|
+
cell_value = self.indexed_items[row][1][col][:self.column_widths[col]] + self.separator
|
|
1131
|
+
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
1132
|
+
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
1133
|
+
if wcswidth(cell_value) + cell_pos > self.term_w:
|
|
1134
|
+
cell_value = truncate_to_display_width(cell_value, self.term_w-cell_pos-10, self.centre_in_cols, self.unicode_char_width)
|
|
1135
|
+
self.stdscr.addstr(y, cell_pos, cell_value, colour)
|
|
1136
|
+
|
|
1137
|
+
# Part of the cell is on screen
|
|
1138
|
+
elif self.startx <= cell_pos+cell_width and cell_pos <= (self.rows_w):
|
|
1139
|
+
s = "max" if cell_max_width <= cell_width else "norm"
|
|
1140
|
+
cell_start = self.startx - cell_pos
|
|
1141
|
+
cell_value = self.indexed_items[row][1][col]
|
|
1142
|
+
cell_value = f"{cell_value:^{self.column_widths[col]}}"
|
|
1143
|
+
|
|
1144
|
+
cell_value = cell_value[cell_start:visible_column_widths[col]][:self.rows_w-self.left_gutter_width]
|
|
1145
|
+
cell_value = truncate_to_display_width(cell_value, min(wcswidth(cell_value), cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
1146
|
+
cell_value += self.separator
|
|
1147
|
+
cell_value = truncate_to_display_width(cell_value, min(wcswidth(cell_value), cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
1148
|
+
self.stdscr.addstr(y, self.startx, cell_value, colour)
|
|
1149
|
+
else:
|
|
1028
1150
|
pass
|
|
1029
1151
|
|
|
1030
1152
|
|
|
1153
|
+
|
|
1031
1154
|
def sort_highlights(highlights):
|
|
1032
1155
|
"""
|
|
1033
1156
|
Sort highlights into lists based on their display level.
|
|
@@ -1068,10 +1191,11 @@ class Picker:
|
|
|
1068
1191
|
match = re.search(highlight["match"], truncate_to_display_width(item[1][highlight["field"]], self.column_widths[highlight["field"]], centre=False, unicode_char_width=self.unicode_char_width), re.IGNORECASE)
|
|
1069
1192
|
if not match: continue
|
|
1070
1193
|
field_start = sum([width for i, width in enumerate(self.column_widths[:highlight["field"]]) if i not in self.hidden_columns]) + sum([1 for i in range(highlight["field"]) if i not in self.hidden_columns])*wcswidth(self.separator)
|
|
1194
|
+
width = min(self.column_widths[highlight["field"]]-(field_start-self.leftmost_char), self.rows_w-self.left_gutter_width)
|
|
1071
1195
|
|
|
1072
1196
|
## We want to search the non-centred values but highlight the centred values.
|
|
1073
1197
|
if self.centre_in_cols:
|
|
1074
|
-
tmp = truncate_to_display_width(item[1][highlight["field"]],
|
|
1198
|
+
tmp = truncate_to_display_width(item[1][highlight["field"]], width, self.centre_in_cols, self.unicode_char_width)
|
|
1075
1199
|
field_start += (len(tmp) - len(tmp.lstrip()))
|
|
1076
1200
|
|
|
1077
1201
|
highlight_start = field_start + match.start()
|
|
@@ -1082,7 +1206,7 @@ class Picker:
|
|
|
1082
1206
|
continue
|
|
1083
1207
|
highlight_start -= self.leftmost_char
|
|
1084
1208
|
highlight_end -= self.leftmost_char
|
|
1085
|
-
self.stdscr.addstr(y, max(self.startx, self.startx+highlight_start), row_str[max(highlight_start,0):min(self.rows_w-self.
|
|
1209
|
+
self.stdscr.addstr(y, max(self.startx, self.startx+highlight_start), row_str[max(highlight_start,0):min(self.rows_w-self.left_gutter_width, highlight_end)], curses.color_pair(self.colours_start+highlight["color"]) | curses.A_BOLD)
|
|
1086
1210
|
except:
|
|
1087
1211
|
pass
|
|
1088
1212
|
|
|
@@ -1098,6 +1222,7 @@ class Picker:
|
|
|
1098
1222
|
l0_highlights, l1_highlights, l2_highlights = sort_highlights(self.highlights)
|
|
1099
1223
|
|
|
1100
1224
|
|
|
1225
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
1101
1226
|
for idx in range(start_index, end_index):
|
|
1102
1227
|
item = self.indexed_items[idx]
|
|
1103
1228
|
y = idx - start_index + self.top_space
|
|
@@ -1109,23 +1234,26 @@ class Picker:
|
|
|
1109
1234
|
# rowstr off screen
|
|
1110
1235
|
# if self.leftmost_char > len(row_str_orig):
|
|
1111
1236
|
# trunc_width = 0
|
|
1112
|
-
if self.leftmost_char + (self.rows_w-self.
|
|
1113
|
-
|
|
1114
|
-
elif self.leftmost_char <= len(row_str_orig):
|
|
1115
|
-
|
|
1116
|
-
else:
|
|
1117
|
-
|
|
1237
|
+
# if self.leftmost_char + (self.rows_w-self.left_gutter_width) <= len(row_str_orig):
|
|
1238
|
+
# trunc_width = self.rows_w-self.startx
|
|
1239
|
+
# elif self.leftmost_char <= len(row_str_orig):
|
|
1240
|
+
# trunc_width = len(row_str_orig) - self.leftmost_char
|
|
1241
|
+
# else:
|
|
1242
|
+
# trunc_width = 0
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
trunc_width = max(0, min(self.rows_w-self.left_gutter_width, row_width, self.term_w - self.startx))
|
|
1118
1246
|
|
|
1119
1247
|
row_str = truncate_to_display_width(row_str_left_adj, trunc_width, self.unicode_char_width)
|
|
1120
1248
|
# row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))[self.leftmost_char:]
|
|
1121
1249
|
|
|
1122
1250
|
## Display the standard row
|
|
1123
|
-
self.stdscr.addstr(y, self.startx, row_str
|
|
1251
|
+
self.stdscr.addstr(y, self.startx, row_str, curses.color_pair(self.colours_start+2))
|
|
1124
1252
|
|
|
1125
1253
|
|
|
1126
1254
|
## Highlight column
|
|
1127
1255
|
if self.crosshair_cursor:
|
|
1128
|
-
highlight_cell(idx, self.selected_column, visible_column_widths, colour_pair_number=27, bold=False, y=y)
|
|
1256
|
+
highlight_cell(idx, self.selected_column, self.visible_column_widths, colour_pair_number=27, bold=False, y=y)
|
|
1129
1257
|
if idx == self.cursor_pos:
|
|
1130
1258
|
self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.startx, visible_columns_total_width)], curses.color_pair(self.colours_start+27))
|
|
1131
1259
|
|
|
@@ -1139,25 +1267,25 @@ class Picker:
|
|
|
1139
1267
|
# self.selected_cells_by_row = get_selected_cells_by_row(self.cell_selections)
|
|
1140
1268
|
if item[0] in self.selected_cells_by_row:
|
|
1141
1269
|
for j in self.selected_cells_by_row[item[0]]:
|
|
1142
|
-
highlight_cell(idx, j, visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1270
|
+
highlight_cell(idx, j, self.visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1143
1271
|
|
|
1144
1272
|
# Visually selected
|
|
1145
1273
|
if self.is_selecting:
|
|
1146
1274
|
if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
|
|
1147
1275
|
x_interval = range(min(self.start_selection_col, self.selected_column), max(self.start_selection_col, self.selected_column)+1)
|
|
1148
1276
|
for col in x_interval:
|
|
1149
|
-
highlight_cell(idx, col, visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1277
|
+
highlight_cell(idx, col, self.visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1150
1278
|
|
|
1151
1279
|
# Visually deslected
|
|
1152
1280
|
if self.is_deselecting:
|
|
1153
1281
|
if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
|
|
1154
1282
|
x_interval = range(min(self.start_selection_col, self.selected_column), max(self.start_selection_col, self.selected_column)+1)
|
|
1155
1283
|
for col in x_interval:
|
|
1156
|
-
highlight_cell(idx, col, visible_column_widths, colour_pair_number=26, bold=False,y=y)
|
|
1284
|
+
highlight_cell(idx, col, self.visible_column_widths, colour_pair_number=26, bold=False,y=y)
|
|
1157
1285
|
# Higlight cursor row and selected rows
|
|
1158
1286
|
elif self.highlight_full_row:
|
|
1159
1287
|
if self.selections[item[0]]:
|
|
1160
|
-
self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.
|
|
1288
|
+
self.stdscr.addstr(y, self.startx, row_str[:min(self.rows_w-self.left_gutter_width, visible_columns_total_width)], curses.color_pair(self.colours_start+25) | curses.A_BOLD)
|
|
1161
1289
|
|
|
1162
1290
|
# Visually selected
|
|
1163
1291
|
if self.is_selecting:
|
|
@@ -1170,16 +1298,30 @@ class Picker:
|
|
|
1170
1298
|
|
|
1171
1299
|
# Highlight the cursor row and the first char of the selected rows.
|
|
1172
1300
|
else:
|
|
1173
|
-
if self.
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1301
|
+
if self.selected_char:
|
|
1302
|
+
if self.selections[item[0]]:
|
|
1303
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.selected_char, curses.color_pair(self.colours_start+2))
|
|
1304
|
+
else:
|
|
1305
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.unselected_char, curses.color_pair(self.colours_start+2))
|
|
1306
|
+
# Visually selected
|
|
1307
|
+
if self.is_selecting:
|
|
1308
|
+
if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
|
|
1309
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.selecting_char, curses.color_pair(self.colours_start+2))
|
|
1310
|
+
# Visually deslected
|
|
1311
|
+
if self.is_deselecting:
|
|
1312
|
+
if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
|
|
1313
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.deselecting_char, curses.color_pair(self.colours_start+2))
|
|
1314
|
+
else:
|
|
1315
|
+
if self.selections[item[0]]:
|
|
1178
1316
|
self.stdscr.addstr(y, max(self.startx-2,0), ' ', curses.color_pair(self.colours_start+1))
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1317
|
+
# Visually selected
|
|
1318
|
+
if self.is_selecting:
|
|
1319
|
+
if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
|
|
1320
|
+
self.stdscr.addstr(y, max(self.startx-2,0), ' ', curses.color_pair(self.colours_start+1))
|
|
1321
|
+
# Visually deslected
|
|
1322
|
+
if self.is_deselecting:
|
|
1323
|
+
if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
|
|
1324
|
+
self.stdscr.addstr(y, max(self.startx-2,0), ' ', curses.color_pair(self.colours_start+10))
|
|
1183
1325
|
|
|
1184
1326
|
if not self.highlights_hide:
|
|
1185
1327
|
draw_highlights(l1_highlights, idx, y, item)
|
|
@@ -1189,9 +1331,9 @@ class Picker:
|
|
|
1189
1331
|
# Draw cursor
|
|
1190
1332
|
if idx == self.cursor_pos:
|
|
1191
1333
|
if self.cell_cursor:
|
|
1192
|
-
highlight_cell(idx, self.selected_column, visible_column_widths, colour_pair_number=5, bold=True, y=y)
|
|
1334
|
+
highlight_cell(idx, self.selected_column, self.visible_column_widths, colour_pair_number=5, bold=True, y=y)
|
|
1193
1335
|
else:
|
|
1194
|
-
self.stdscr.addstr(y, self.startx, row_str[:
|
|
1336
|
+
self.stdscr.addstr(y, self.startx, row_str[:self.rows_w-self.left_gutter_width], curses.color_pair(self.colours_start+5) | curses.A_BOLD)
|
|
1195
1337
|
|
|
1196
1338
|
if not self.highlights_hide:
|
|
1197
1339
|
draw_highlights(l2_highlights, idx, y, item)
|
|
@@ -1211,7 +1353,8 @@ class Picker:
|
|
|
1211
1353
|
scroll_bar_length = max(1, scroll_bar_length)
|
|
1212
1354
|
for i in range(scroll_bar_length):
|
|
1213
1355
|
v = max(self.top_space+int(bool(self.header)), scroll_bar_start-scroll_bar_length//2)
|
|
1214
|
-
self.stdscr.addstr(scroll_bar_start+i, self.rows_w-
|
|
1356
|
+
# self.stdscr.addstr(scroll_bar_start+i, self.startx+self.rows_w-self.left_gutter_width-2, ' ', curses.color_pair(self.colours_start+18))
|
|
1357
|
+
self.stdscr.addstr(scroll_bar_start+i, self.rows_box_x_f-1, ' ', curses.color_pair(self.colours_start+18))
|
|
1215
1358
|
|
|
1216
1359
|
# Display refresh symbol
|
|
1217
1360
|
if self.auto_refresh:
|
|
@@ -1240,20 +1383,46 @@ class Picker:
|
|
|
1240
1383
|
|
|
1241
1384
|
if self.split_right and len(self.right_panes):
|
|
1242
1385
|
# If we need to refresh the data then do so.
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1386
|
+
pane = self.right_panes[self.right_pane_index]
|
|
1387
|
+
if pane["auto_refresh"] and ((time.time() - self.initial_right_split_time) > pane["refresh_time"]):
|
|
1388
|
+
get_data = pane["get_data"]
|
|
1389
|
+
data = pane["data"]
|
|
1390
|
+
pane["data"] = get_data(data, self.get_function_data())
|
|
1391
|
+
self.initial_right_split_time = time.time()
|
|
1248
1392
|
|
|
1249
|
-
draw_pane =
|
|
1250
|
-
data =
|
|
1393
|
+
draw_pane = pane["display"]
|
|
1394
|
+
data = pane["data"]
|
|
1395
|
+
# pane_width = int(pane["proportion"]*self.term_w)
|
|
1251
1396
|
|
|
1252
1397
|
draw_pane(
|
|
1253
1398
|
self.stdscr,
|
|
1254
|
-
x = self.rows_w,
|
|
1399
|
+
x = self.rows_w + self.startx - self.left_gutter_width,
|
|
1255
1400
|
y = self.top_space - int(bool(self.show_header and self.header)),
|
|
1256
|
-
w = self.
|
|
1401
|
+
w = self.right_pane_width,
|
|
1402
|
+
h = self.items_per_page + int(bool(self.show_header and self.header)),
|
|
1403
|
+
state = self.get_function_data(),
|
|
1404
|
+
row = self.indexed_items[self.cursor_pos] if self.indexed_items else [],
|
|
1405
|
+
cell = self.indexed_items[self.cursor_pos][1][self.selected_column] if self.indexed_items else "",
|
|
1406
|
+
data=data,
|
|
1407
|
+
)
|
|
1408
|
+
if self.split_left and len(self.left_panes):
|
|
1409
|
+
# If we need to refresh the data then do so.
|
|
1410
|
+
pane = self.left_panes[self.left_pane_index]
|
|
1411
|
+
if pane["auto_refresh"] and ((time.time() - self.initial_left_split_time) > pane["refresh_time"]):
|
|
1412
|
+
get_data = pane["get_data"]
|
|
1413
|
+
data = pane["data"]
|
|
1414
|
+
pane["data"] = get_data(data, self.get_function_data())
|
|
1415
|
+
self.initial_left_split_time = time.time()
|
|
1416
|
+
|
|
1417
|
+
draw_pane = pane["display"]
|
|
1418
|
+
data = pane["data"]
|
|
1419
|
+
# pane_width = int(pane["proportion"]*self.term_w)
|
|
1420
|
+
|
|
1421
|
+
draw_pane(
|
|
1422
|
+
self.stdscr,
|
|
1423
|
+
x = 0,
|
|
1424
|
+
y = self.top_space - int(bool(self.show_header and self.header)),
|
|
1425
|
+
w = self.left_pane_width,
|
|
1257
1426
|
h = self.items_per_page + int(bool(self.show_header and self.header)),
|
|
1258
1427
|
state = self.get_function_data(),
|
|
1259
1428
|
row = self.indexed_items[self.cursor_pos] if self.indexed_items else [],
|
|
@@ -1261,13 +1430,27 @@ class Picker:
|
|
|
1261
1430
|
data=data,
|
|
1262
1431
|
)
|
|
1263
1432
|
|
|
1264
|
-
self.stdscr.refresh()
|
|
1265
1433
|
## Display infobox
|
|
1266
1434
|
if self.display_infobox:
|
|
1267
1435
|
self.infobox(self.stdscr, message=self.infobox_items, title=self.infobox_title)
|
|
1268
1436
|
# self.stdscr.timeout(2000) # timeout is set to 50 in order to get the infobox to be displayed so here we reset it to 2000
|
|
1269
1437
|
|
|
1270
1438
|
|
|
1439
|
+
def refresh_and_draw_screen(self):
|
|
1440
|
+
"""
|
|
1441
|
+
Clears and refreshes the screen, restricts and unrestricts curses,
|
|
1442
|
+
ensures correct terminal settings, and then draws the screen.
|
|
1443
|
+
"""
|
|
1444
|
+
|
|
1445
|
+
self.logger.info(f"key_function redraw_screen")
|
|
1446
|
+
self.stdscr.clear()
|
|
1447
|
+
self.stdscr.refresh()
|
|
1448
|
+
restrict_curses(self.stdscr)
|
|
1449
|
+
unrestrict_curses(self.stdscr)
|
|
1450
|
+
self.stdscr.clear()
|
|
1451
|
+
self.stdscr.refresh()
|
|
1452
|
+
|
|
1453
|
+
self.draw_screen()
|
|
1271
1454
|
|
|
1272
1455
|
def infobox(self, stdscr: curses.window, message: str ="", title: str ="Infobox", colours_end: int = 0, duration: int = 4) -> curses.window:
|
|
1273
1456
|
""" Display non-interactive infobox window. """
|
|
@@ -1309,6 +1492,7 @@ class Picker:
|
|
|
1309
1492
|
"reset_colours": False,
|
|
1310
1493
|
"cell_cursor": False,
|
|
1311
1494
|
"split_right": False,
|
|
1495
|
+
"split_left": False,
|
|
1312
1496
|
"crosshair_cursor": False,
|
|
1313
1497
|
}
|
|
1314
1498
|
|
|
@@ -1322,113 +1506,127 @@ class Picker:
|
|
|
1322
1506
|
self.logger.debug(f"function: get_function_data()")
|
|
1323
1507
|
""" Returns a dict of the main variables needed to restore the state of list_pikcer. """
|
|
1324
1508
|
function_data = {
|
|
1325
|
-
"
|
|
1326
|
-
"
|
|
1327
|
-
"
|
|
1328
|
-
"
|
|
1329
|
-
"
|
|
1330
|
-
"
|
|
1331
|
-
"
|
|
1332
|
-
"
|
|
1333
|
-
"
|
|
1334
|
-
"
|
|
1335
|
-
"
|
|
1336
|
-
"
|
|
1337
|
-
"
|
|
1338
|
-
"
|
|
1339
|
-
"
|
|
1340
|
-
"
|
|
1341
|
-
"
|
|
1342
|
-
"
|
|
1343
|
-
"
|
|
1344
|
-
"
|
|
1345
|
-
"
|
|
1346
|
-
"
|
|
1347
|
-
"
|
|
1348
|
-
"
|
|
1349
|
-
"
|
|
1350
|
-
"
|
|
1351
|
-
"
|
|
1352
|
-
"
|
|
1353
|
-
"
|
|
1354
|
-
"
|
|
1355
|
-
"
|
|
1356
|
-
"
|
|
1357
|
-
"
|
|
1358
|
-
"
|
|
1359
|
-
"
|
|
1360
|
-
"
|
|
1361
|
-
"
|
|
1362
|
-
"
|
|
1363
|
-
"
|
|
1364
|
-
"
|
|
1365
|
-
"
|
|
1366
|
-
"
|
|
1367
|
-
"
|
|
1368
|
-
"
|
|
1369
|
-
"
|
|
1370
|
-
"
|
|
1371
|
-
"
|
|
1372
|
-
"
|
|
1373
|
-
"
|
|
1374
|
-
"
|
|
1375
|
-
"
|
|
1376
|
-
"
|
|
1377
|
-
"
|
|
1378
|
-
"
|
|
1379
|
-
"
|
|
1380
|
-
"
|
|
1381
|
-
"
|
|
1382
|
-
"
|
|
1383
|
-
"
|
|
1384
|
-
"
|
|
1385
|
-
"
|
|
1386
|
-
"
|
|
1387
|
-
"
|
|
1388
|
-
"
|
|
1389
|
-
"
|
|
1390
|
-
"
|
|
1391
|
-
"
|
|
1392
|
-
"
|
|
1393
|
-
"
|
|
1394
|
-
"
|
|
1395
|
-
"
|
|
1396
|
-
"
|
|
1397
|
-
"
|
|
1398
|
-
"
|
|
1399
|
-
"
|
|
1400
|
-
"
|
|
1401
|
-
"
|
|
1402
|
-
"
|
|
1403
|
-
"
|
|
1404
|
-
"
|
|
1405
|
-
"
|
|
1406
|
-
"
|
|
1407
|
-
"
|
|
1408
|
-
"
|
|
1409
|
-
"
|
|
1410
|
-
"
|
|
1411
|
-
"
|
|
1412
|
-
"
|
|
1413
|
-
"
|
|
1414
|
-
"
|
|
1415
|
-
"
|
|
1416
|
-
"
|
|
1417
|
-
"
|
|
1418
|
-
"
|
|
1419
|
-
"
|
|
1420
|
-
"
|
|
1421
|
-
"
|
|
1422
|
-
"
|
|
1423
|
-
"
|
|
1424
|
-
"
|
|
1425
|
-
"
|
|
1426
|
-
"
|
|
1427
|
-
"
|
|
1428
|
-
"
|
|
1429
|
-
"
|
|
1430
|
-
"
|
|
1431
|
-
|
|
1509
|
+
"self": self,
|
|
1510
|
+
"selections": self.selections,
|
|
1511
|
+
"cell_selections": self.cell_selections,
|
|
1512
|
+
"selected_cells_by_row": self.selected_cells_by_row,
|
|
1513
|
+
"items_per_page": self.items_per_page,
|
|
1514
|
+
"current_row": self.current_row,
|
|
1515
|
+
"current_page": self.current_page,
|
|
1516
|
+
"cursor_pos": self.cursor_pos,
|
|
1517
|
+
"colours": self.colours,
|
|
1518
|
+
"colour_theme_number": self.colour_theme_number,
|
|
1519
|
+
"selected_column": self.selected_column,
|
|
1520
|
+
"sort_column": self.sort_column,
|
|
1521
|
+
"sort_method": self.sort_method,
|
|
1522
|
+
"sort_reverse": self.sort_reverse,
|
|
1523
|
+
"SORT_METHODS": self.SORT_METHODS,
|
|
1524
|
+
"hidden_columns": self.hidden_columns,
|
|
1525
|
+
"is_selecting": self.is_selecting,
|
|
1526
|
+
"is_deselecting": self.is_deselecting,
|
|
1527
|
+
"user_opts": self.user_opts,
|
|
1528
|
+
"options_list": self.options_list,
|
|
1529
|
+
"user_settings": self.user_settings,
|
|
1530
|
+
"separator": self.separator,
|
|
1531
|
+
"header_separator": self.header_separator,
|
|
1532
|
+
"header_separator_before_selected_column": self.header_separator_before_selected_column,
|
|
1533
|
+
"search_query": self.search_query,
|
|
1534
|
+
"search_count": self.search_count,
|
|
1535
|
+
"search_index": self.search_index,
|
|
1536
|
+
"filter_query": self.filter_query,
|
|
1537
|
+
"indexed_items": self.indexed_items,
|
|
1538
|
+
"start_selection": self.start_selection,
|
|
1539
|
+
"start_selection_col": self.start_selection_col,
|
|
1540
|
+
"end_selection": self.end_selection,
|
|
1541
|
+
"highlights": self.highlights,
|
|
1542
|
+
"max_column_width": self.max_column_width,
|
|
1543
|
+
"column_indices": self.column_indices,
|
|
1544
|
+
"mode_index": self.mode_index,
|
|
1545
|
+
"modes": self.modes,
|
|
1546
|
+
"title": self.title,
|
|
1547
|
+
"display_modes": self.display_modes,
|
|
1548
|
+
"require_option": self.require_option,
|
|
1549
|
+
"require_option_default": self.require_option_default,
|
|
1550
|
+
"option_functions": self.option_functions,
|
|
1551
|
+
"top_gap": self.top_gap,
|
|
1552
|
+
"number_columns": self.number_columns,
|
|
1553
|
+
"items": self.items,
|
|
1554
|
+
"indexed_items": self.indexed_items,
|
|
1555
|
+
"header": self.header,
|
|
1556
|
+
"scroll_bar": self.scroll_bar,
|
|
1557
|
+
"columns_sort_method": self.columns_sort_method,
|
|
1558
|
+
"disabled_keys": self.disabled_keys,
|
|
1559
|
+
"show_footer": self.show_footer,
|
|
1560
|
+
"footer_string": self.footer_string,
|
|
1561
|
+
"footer_string_auto_refresh": self.footer_string_auto_refresh,
|
|
1562
|
+
"footer_string_refresh_function": self.footer_string_refresh_function,
|
|
1563
|
+
"footer_timer": self.footer_timer,
|
|
1564
|
+
"footer_style": self.footer_style,
|
|
1565
|
+
"colours_start": self.colours_start,
|
|
1566
|
+
"colours_end": self.colours_end,
|
|
1567
|
+
"display_only": self.display_only,
|
|
1568
|
+
"infobox_items": self.infobox_items,
|
|
1569
|
+
"display_infobox": self.display_infobox,
|
|
1570
|
+
"infobox_title": self.infobox_title,
|
|
1571
|
+
"key_remappings": self.key_remappings,
|
|
1572
|
+
"auto_refresh": self.auto_refresh,
|
|
1573
|
+
"get_new_data": self.get_new_data,
|
|
1574
|
+
"refresh_function": self.refresh_function,
|
|
1575
|
+
"timer": self.timer,
|
|
1576
|
+
"get_data_startup": self.get_data_startup,
|
|
1577
|
+
"get_footer_string_startup": self.get_footer_string_startup,
|
|
1578
|
+
"editable_columns": self.editable_columns,
|
|
1579
|
+
"last_key": self.last_key,
|
|
1580
|
+
"centre_in_terminal": self.centre_in_terminal,
|
|
1581
|
+
"centre_in_terminal_vertical": self.centre_in_terminal_vertical,
|
|
1582
|
+
"centre_in_cols": self.centre_in_cols,
|
|
1583
|
+
"highlight_full_row": self.highlight_full_row,
|
|
1584
|
+
"cell_cursor": self.cell_cursor,
|
|
1585
|
+
"column_widths": self.column_widths,
|
|
1586
|
+
"track_entries_upon_refresh": self.track_entries_upon_refresh,
|
|
1587
|
+
"pin_cursor": self.pin_cursor,
|
|
1588
|
+
"id_column": self.id_column,
|
|
1589
|
+
"startup_notification": self.startup_notification,
|
|
1590
|
+
"keys_dict": self.keys_dict,
|
|
1591
|
+
"macros": self.macros,
|
|
1592
|
+
"cancel_is_back": self.cancel_is_back,
|
|
1593
|
+
"paginate": self.paginate,
|
|
1594
|
+
"leftmost_char": self.leftmost_char,
|
|
1595
|
+
"history_filter_and_search" : self.history_filter_and_search,
|
|
1596
|
+
"history_pipes" : self.history_pipes,
|
|
1597
|
+
"history_opts" : self.history_opts,
|
|
1598
|
+
"history_edits" : self.history_edits,
|
|
1599
|
+
"history_settings": self.history_settings,
|
|
1600
|
+
"show_header": self.show_header,
|
|
1601
|
+
"show_row_header": self.show_row_header,
|
|
1602
|
+
"debug": self.debug,
|
|
1603
|
+
"debug_level": self.debug_level,
|
|
1604
|
+
"reset_colours": self.reset_colours,
|
|
1605
|
+
"unicode_char_width": self.unicode_char_width,
|
|
1606
|
+
"command_stack": self.command_stack,
|
|
1607
|
+
"loaded_file": self.loaded_file,
|
|
1608
|
+
"loaded_files": self.loaded_files,
|
|
1609
|
+
"loaded_file_index": self.loaded_file_index,
|
|
1610
|
+
"loaded_file_states": self.loaded_file_states,
|
|
1611
|
+
"sheet_index": self.sheet_index,
|
|
1612
|
+
"sheets": self.sheets,
|
|
1613
|
+
"sheet_name": self.sheet_name,
|
|
1614
|
+
"sheet_states": self.sheet_states,
|
|
1615
|
+
"split_right": self.split_right,
|
|
1616
|
+
"right_panes": self.right_panes,
|
|
1617
|
+
"right_pane_index": self.right_pane_index,
|
|
1618
|
+
"split_left": self.split_left,
|
|
1619
|
+
"left_panes": self.left_panes,
|
|
1620
|
+
"left_pane_index": self.left_pane_index,
|
|
1621
|
+
"crosshair_cursor": self.crosshair_cursor,
|
|
1622
|
+
"generate_data_for_hidden_columns": self.generate_data_for_hidden_columns,
|
|
1623
|
+
"thread_stop_event": self.thread_stop_event,
|
|
1624
|
+
"data_generation_queue": self.data_generation_queue,
|
|
1625
|
+
"process_manager": self.process_manager,
|
|
1626
|
+
"threads": self.threads,
|
|
1627
|
+
"processes": self.processes,
|
|
1628
|
+
"items_sync_loop_event": self.items_sync_loop_event,
|
|
1629
|
+
"items_sync_thread": self.items_sync_thread,
|
|
1432
1630
|
}
|
|
1433
1631
|
return function_data
|
|
1434
1632
|
|
|
@@ -1464,6 +1662,9 @@ class Picker:
|
|
|
1464
1662
|
"centre_in_cols",
|
|
1465
1663
|
"centre_in_terminal",
|
|
1466
1664
|
"split_right",
|
|
1665
|
+
"left_pane_index",
|
|
1666
|
+
"split_left",
|
|
1667
|
+
"left_pane_index",
|
|
1467
1668
|
]
|
|
1468
1669
|
|
|
1469
1670
|
for var in variables:
|
|
@@ -1478,15 +1679,6 @@ class Picker:
|
|
|
1478
1679
|
self.initialise_picker_state(reset_colours=reset_colours)
|
|
1479
1680
|
|
|
1480
1681
|
self.initialise_variables()
|
|
1481
|
-
# if "colour_theme_number" in function_data:
|
|
1482
|
-
# global COLOURS_SET
|
|
1483
|
-
# COLOURS_SET = False
|
|
1484
|
-
# colours_end = set_colours(pick=self.colour_theme_number, start=self.colours_start)
|
|
1485
|
-
|
|
1486
|
-
# if "items" in function_data: self.items = function_data["items"]
|
|
1487
|
-
# if "header" in function_data: self.header = function_data["header"]
|
|
1488
|
-
# self.indexed_items = function_data["indexed_items"] if "indexed_items" in function_data else []
|
|
1489
|
-
|
|
1490
1682
|
|
|
1491
1683
|
|
|
1492
1684
|
def delete_entries(self) -> None:
|
|
@@ -1557,8 +1749,10 @@ class Picker:
|
|
|
1557
1749
|
"number_columns": False,
|
|
1558
1750
|
"reset_colours": False,
|
|
1559
1751
|
"split_right": False,
|
|
1752
|
+
"split_left": False,
|
|
1560
1753
|
"cell_cursor": False,
|
|
1561
1754
|
"crosshair_cursor": False,
|
|
1755
|
+
"header_separator": " │",
|
|
1562
1756
|
}
|
|
1563
1757
|
while True:
|
|
1564
1758
|
self.update_term_size()
|
|
@@ -1569,6 +1763,7 @@ class Picker:
|
|
|
1569
1763
|
|
|
1570
1764
|
submenu_win = curses.newwin(window_height, window_width, (self.term_h-window_height)//2, (self.term_w-window_width)//2)
|
|
1571
1765
|
submenu_win.keypad(True)
|
|
1766
|
+
option_picker_data["screen_size_function"] = lambda stdscr: (window_height, window_width)
|
|
1572
1767
|
OptionPicker = Picker(submenu_win, **option_picker_data)
|
|
1573
1768
|
s, o, f = OptionPicker.run()
|
|
1574
1769
|
|
|
@@ -1580,6 +1775,102 @@ class Picker:
|
|
|
1580
1775
|
return {}, "", f
|
|
1581
1776
|
|
|
1582
1777
|
|
|
1778
|
+
def select_columns(
|
|
1779
|
+
self,
|
|
1780
|
+
stdscr: curses.window,
|
|
1781
|
+
# options: list[list[str]] =[],
|
|
1782
|
+
# title: str = "Choose option",
|
|
1783
|
+
# x:int=0,
|
|
1784
|
+
# y:int=0,
|
|
1785
|
+
# literal:bool=False,
|
|
1786
|
+
# colours_start:int=0,
|
|
1787
|
+
# header: list[str] = [],
|
|
1788
|
+
# require_option:list = [],
|
|
1789
|
+
# option_functions: list = [],
|
|
1790
|
+
) -> Tuple[dict, str, dict]:
|
|
1791
|
+
"""
|
|
1792
|
+
Display input field at x,y
|
|
1793
|
+
|
|
1794
|
+
---Arguments
|
|
1795
|
+
stdscr: curses screen
|
|
1796
|
+
usrtxt (str): text to be edited by the user
|
|
1797
|
+
title (str): The text to be displayed at the start of the text option picker
|
|
1798
|
+
x (int): prompt begins at (x,y) in the screen given
|
|
1799
|
+
y (int): prompt begins at (x,y) in the screen given
|
|
1800
|
+
colours_start (bool): start index of curses init_pair.
|
|
1801
|
+
|
|
1802
|
+
---Returns
|
|
1803
|
+
usrtxt, return_code
|
|
1804
|
+
usrtxt: the text inputted by the user
|
|
1805
|
+
return_code:
|
|
1806
|
+
0: user hit escape
|
|
1807
|
+
1: user hit return
|
|
1808
|
+
"""
|
|
1809
|
+
self.logger.info(f"function: select_columns()")
|
|
1810
|
+
|
|
1811
|
+
cursor = 0
|
|
1812
|
+
|
|
1813
|
+
if self.header:
|
|
1814
|
+
columns = [s for i, s in enumerate(self.header)]
|
|
1815
|
+
else:
|
|
1816
|
+
columns = [f"" for i in range(len(self.column_widths))]
|
|
1817
|
+
|
|
1818
|
+
## Column info variable
|
|
1819
|
+
columns_set = [[f"{i}", columns[i]] for i in range(len(self.column_widths))]
|
|
1820
|
+
header = ["#", "Column Name"]
|
|
1821
|
+
|
|
1822
|
+
selected = [False if i in self.hidden_columns else True for i in range(len(self.column_widths))]
|
|
1823
|
+
selected = {i: False if i in self.hidden_columns else True for i in range(len(self.column_widths))}
|
|
1824
|
+
|
|
1825
|
+
option_picker_data = {
|
|
1826
|
+
"items": columns_set,
|
|
1827
|
+
"colours": notification_colours,
|
|
1828
|
+
"colours_start": self.notification_colours_start,
|
|
1829
|
+
"title":"Select Columns",
|
|
1830
|
+
"header": header,
|
|
1831
|
+
"hidden_columns":[],
|
|
1832
|
+
# "require_option":require_option,
|
|
1833
|
+
# "keys_dict": options_keys,
|
|
1834
|
+
"selections": selected,
|
|
1835
|
+
"show_footer": False,
|
|
1836
|
+
"cancel_is_back": True,
|
|
1837
|
+
"number_columns": False,
|
|
1838
|
+
"reset_colours": False,
|
|
1839
|
+
"split_right": False,
|
|
1840
|
+
"split_left": False,
|
|
1841
|
+
"cell_cursor": False,
|
|
1842
|
+
"crosshair_cursor": False,
|
|
1843
|
+
"separator": " ",
|
|
1844
|
+
"header_separator": " │",
|
|
1845
|
+
"header_separator_before_selected_column": " ▐",
|
|
1846
|
+
"selected_char": "☒",
|
|
1847
|
+
"unselected_char": "☐",
|
|
1848
|
+
"selecting_char": "☒",
|
|
1849
|
+
"deselecting_char": "☐",
|
|
1850
|
+
}
|
|
1851
|
+
while True:
|
|
1852
|
+
self.update_term_size()
|
|
1853
|
+
|
|
1854
|
+
choose_opts_widths = get_column_widths(columns_set, unicode_char_width=self.unicode_char_width)
|
|
1855
|
+
window_width = min(max(sum(choose_opts_widths) + 6, 50) + 6, self.term_w)
|
|
1856
|
+
window_height = min(self.term_h//2, max(6, len(columns_set)+3))
|
|
1857
|
+
|
|
1858
|
+
submenu_win = curses.newwin(window_height, window_width, (self.term_h-window_height)//2, (self.term_w-window_width)//2)
|
|
1859
|
+
submenu_win.keypad(True)
|
|
1860
|
+
option_picker_data["screen_size_function"] = lambda stdscr: (window_height, window_width)
|
|
1861
|
+
OptionPicker = Picker(submenu_win, **option_picker_data)
|
|
1862
|
+
s, o, f = OptionPicker.run()
|
|
1863
|
+
|
|
1864
|
+
if o == "refresh":
|
|
1865
|
+
self.draw_screen()
|
|
1866
|
+
continue
|
|
1867
|
+
if s:
|
|
1868
|
+
selected_columns = s
|
|
1869
|
+
self.hidden_columns = [i for i in range(len(self.column_widths)) if i not in selected_columns]
|
|
1870
|
+
|
|
1871
|
+
# return {x: options[x] for x in s}, o, f
|
|
1872
|
+
break
|
|
1873
|
+
return {}, "", f
|
|
1583
1874
|
|
|
1584
1875
|
def notification(self, stdscr: curses.window, message: str="", title:str="Notification", colours_end: int=0, duration:int=4) -> None:
|
|
1585
1876
|
|
|
@@ -1617,10 +1908,11 @@ class Picker:
|
|
|
1617
1908
|
"cancel_is_back": True,
|
|
1618
1909
|
"reset_colours": False,
|
|
1619
1910
|
"split_right": False,
|
|
1911
|
+
"split_left": False,
|
|
1620
1912
|
"cell_cursor": False,
|
|
1621
1913
|
"crosshair_cursor": False,
|
|
1622
1914
|
"show_header": False,
|
|
1623
|
-
|
|
1915
|
+
"screen_size_function": lambda stdscr: (notification_height, notification_width),
|
|
1624
1916
|
}
|
|
1625
1917
|
OptionPicker = Picker(submenu_win, **notification_data)
|
|
1626
1918
|
s, o, f = OptionPicker.run()
|
|
@@ -1757,14 +2049,24 @@ class Picker:
|
|
|
1757
2049
|
self.footer_style = (self.footer_style+1)%len(self.footer_options)
|
|
1758
2050
|
self.footer = self.footer_options[self.footer_style]
|
|
1759
2051
|
self.initialise_variables()
|
|
1760
|
-
elif setting == "
|
|
2052
|
+
elif setting == "rpane":
|
|
1761
2053
|
self.toggle_right_pane()
|
|
1762
2054
|
|
|
1763
|
-
elif setting == "
|
|
2055
|
+
elif setting == "rpane_cycle":
|
|
1764
2056
|
self.cycle_right_pane()
|
|
1765
2057
|
|
|
2058
|
+
elif setting == "lpane":
|
|
2059
|
+
self.toggle_left_pane()
|
|
2060
|
+
|
|
2061
|
+
elif setting == "lpane_cycle":
|
|
2062
|
+
self.cycle_left_pane()
|
|
2063
|
+
|
|
1766
2064
|
elif setting.startswith("cwd="):
|
|
1767
2065
|
os.chdir(os.path.expandvars(os.path.expanduser(setting[len("cwd="):])))
|
|
2066
|
+
elif setting.startswith("lmc="):
|
|
2067
|
+
rem = setting[4:]
|
|
2068
|
+
if rem.isnumeric():
|
|
2069
|
+
self.leftmost_char = int(rem)
|
|
1768
2070
|
elif setting.startswith("hl"):
|
|
1769
2071
|
hl_list = setting.split(",")
|
|
1770
2072
|
if len(hl_list) > 1:
|
|
@@ -1814,6 +2116,9 @@ class Picker:
|
|
|
1814
2116
|
self.draw_screen()
|
|
1815
2117
|
self.notification(self.stdscr, message=f"Theme {self.colour_theme_number} applied.")
|
|
1816
2118
|
self.colours = get_colours(self.colour_theme_number)
|
|
2119
|
+
elif setting == "colsel":
|
|
2120
|
+
self.draw_screen()
|
|
2121
|
+
self.select_columns(self.stdscr)
|
|
1817
2122
|
|
|
1818
2123
|
else:
|
|
1819
2124
|
self.user_settings = ""
|
|
@@ -1950,6 +2255,7 @@ class Picker:
|
|
|
1950
2255
|
if self.indexed_items[new_pos][0] in self.unselectable_indices: new_pos+=1
|
|
1951
2256
|
else: break
|
|
1952
2257
|
self.cursor_pos = new_pos
|
|
2258
|
+
self.ensure_no_overscroll()
|
|
1953
2259
|
return True
|
|
1954
2260
|
|
|
1955
2261
|
def cursor_up(self, count=1) -> bool:
|
|
@@ -1963,6 +2269,7 @@ class Picker:
|
|
|
1963
2269
|
elif new_pos in self.unselectable_indices: new_pos -= 1
|
|
1964
2270
|
else: break
|
|
1965
2271
|
self.cursor_pos = new_pos
|
|
2272
|
+
self.ensure_no_overscroll()
|
|
1966
2273
|
return True
|
|
1967
2274
|
|
|
1968
2275
|
def remapped_key(self, key: int, val: int, key_remappings: dict) -> bool:
|
|
@@ -1984,6 +2291,19 @@ class Picker:
|
|
|
1984
2291
|
return True
|
|
1985
2292
|
return False
|
|
1986
2293
|
|
|
2294
|
+
def check_and_run_macro(self, key: int) -> bool:
|
|
2295
|
+
macro_match = False
|
|
2296
|
+
for macro in self.macros:
|
|
2297
|
+
try:
|
|
2298
|
+
if key in macro["keys"]:
|
|
2299
|
+
macro_match = True
|
|
2300
|
+
macro["function"](self)
|
|
2301
|
+
break
|
|
2302
|
+
except:
|
|
2303
|
+
pass
|
|
2304
|
+
return macro_match
|
|
2305
|
+
|
|
2306
|
+
|
|
1987
2307
|
def copy_dialogue(self) -> None:
|
|
1988
2308
|
""" Display dialogue to select how rows/cells should be copied. """
|
|
1989
2309
|
self.logger.info(f"function: copy_dialogue()")
|
|
@@ -2177,7 +2497,13 @@ class Picker:
|
|
|
2177
2497
|
self.logger.info(f"function: fetch_data()")
|
|
2178
2498
|
tmp_items, tmp_header = [], []
|
|
2179
2499
|
self.getting_data.clear()
|
|
2180
|
-
self.refresh_function(
|
|
2500
|
+
self.refresh_function(
|
|
2501
|
+
tmp_items,
|
|
2502
|
+
tmp_header,
|
|
2503
|
+
self.visible_rows_indices,
|
|
2504
|
+
self.getting_data,
|
|
2505
|
+
self.get_function_data(),
|
|
2506
|
+
)
|
|
2181
2507
|
if self.track_entries_upon_refresh:
|
|
2182
2508
|
selected_indices = get_selected_indices(self.selections)
|
|
2183
2509
|
self.ids = [item[self.id_column] for i, item in enumerate(self.items) if i in selected_indices]
|
|
@@ -2275,10 +2601,6 @@ class Picker:
|
|
|
2275
2601
|
row_len = 1
|
|
2276
2602
|
if self.header: row_len = len(self.header)
|
|
2277
2603
|
elif len(self.items): row_len = len(self.items[0])
|
|
2278
|
-
# if len(self.indexed_items) == 0:
|
|
2279
|
-
# insert_at_pos = 0
|
|
2280
|
-
# else:
|
|
2281
|
-
# insert_at_pos = self.indexed_items[self.cursor_pos][0]
|
|
2282
2604
|
self.items = self.items[:pos] + [["" for x in range(row_len)]] + self.items[pos:]
|
|
2283
2605
|
if pos <= self.cursor_pos:
|
|
2284
2606
|
self.cursor_pos += 1
|
|
@@ -2388,17 +2710,85 @@ class Picker:
|
|
|
2388
2710
|
self.split_right = not self.split_right
|
|
2389
2711
|
if self.right_panes[self.right_pane_index]["data"] in [[], None, {}]:
|
|
2390
2712
|
self.right_panes[self.right_pane_index]["data"] = self.right_panes[self.right_pane_index]["get_data"](self.right_panes[self.right_pane_index]["data"], self.get_function_data())
|
|
2713
|
+
self.ensure_no_overscroll()
|
|
2714
|
+
|
|
2715
|
+
def toggle_left_pane(self):
|
|
2716
|
+
if len(self.left_panes):
|
|
2717
|
+
self.split_left = not self.split_left
|
|
2718
|
+
if self.left_panes[self.left_pane_index]["data"] in [[], None, {}]:
|
|
2719
|
+
self.left_panes[self.left_pane_index]["data"] = self.left_panes[self.left_pane_index]["get_data"](self.left_panes[self.left_pane_index]["data"], self.get_function_data())
|
|
2720
|
+
self.ensure_no_overscroll()
|
|
2391
2721
|
|
|
2392
2722
|
|
|
2393
2723
|
def cycle_right_pane(self, increment=1):
|
|
2394
2724
|
if len(self.right_panes) > 1:
|
|
2395
2725
|
self.right_pane_index = (self.right_pane_index+1)%len(self.right_panes)
|
|
2396
|
-
self.
|
|
2726
|
+
self.initial_right_split_time -= self.right_panes[self.right_pane_index]["refresh_time"]
|
|
2727
|
+
self.ensure_no_overscroll()
|
|
2728
|
+
|
|
2729
|
+
def cycle_left_pane(self, increment=1):
|
|
2730
|
+
if len(self.left_panes) > 1:
|
|
2731
|
+
self.left_pane_index = (self.left_pane_index+1)%len(self.left_panes)
|
|
2732
|
+
self.initial_left_split_time -= self.left_panes[self.left_pane_index]["refresh_time"]
|
|
2733
|
+
self.ensure_no_overscroll()
|
|
2734
|
+
|
|
2735
|
+
def ensure_no_overscroll(self):
|
|
2736
|
+
"""
|
|
2737
|
+
Ensure that we haven't scrolled past the last column.
|
|
2738
|
+
|
|
2739
|
+
This check should be performed after:
|
|
2740
|
+
- Terminal resize event
|
|
2741
|
+
- Scrolling down - i.e., rows with potentially different widths come into view
|
|
2742
|
+
"""
|
|
2743
|
+
self.calculate_section_sizes()
|
|
2744
|
+
self.get_visible_rows()
|
|
2745
|
+
self.column_widths = get_column_widths(
|
|
2746
|
+
self.visible_rows,
|
|
2747
|
+
header=self.header,
|
|
2748
|
+
max_column_width=self.max_column_width,
|
|
2749
|
+
number_columns=self.number_columns,
|
|
2750
|
+
max_total_width=self.rows_w,
|
|
2751
|
+
unicode_char_width=self.unicode_char_width
|
|
2752
|
+
)
|
|
2753
|
+
self.calculate_section_sizes()
|
|
2754
|
+
|
|
2755
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
2756
|
+
if row_width - self.leftmost_char < self.rows_w:
|
|
2757
|
+
if row_width <= self.rows_w - self.left_gutter_width:
|
|
2758
|
+
self.leftmost_char = 0
|
|
2759
|
+
else:
|
|
2760
|
+
self.leftmost_char = row_width - (self.rows_w - self.left_gutter_width) + 5
|
|
2761
|
+
|
|
2762
|
+
def cleanup_processes(self):
|
|
2763
|
+
self.thread_stop_event.set()
|
|
2764
|
+
self.data_generation_queue.clear()
|
|
2765
|
+
# with self.data_generation_queue.mutex:
|
|
2766
|
+
# self.data_generation_queue.queue.clear()
|
|
2767
|
+
function_data = self.get_function_data()
|
|
2768
|
+
for proc in self.processes:
|
|
2769
|
+
if proc.is_alive():
|
|
2770
|
+
proc.terminate()
|
|
2771
|
+
proc.join(timeout=0.01)
|
|
2772
|
+
self.processes = []
|
|
2773
|
+
self.items_sync_loop_event.set()
|
|
2774
|
+
if self.items_sync_thread != None:
|
|
2775
|
+
self.items_sync_thread.join(timeout=1)
|
|
2776
|
+
|
|
2777
|
+
def cleanup_threads(self):
|
|
2778
|
+
self.thread_stop_event.set()
|
|
2779
|
+
with self.data_generation_queue.mutex:
|
|
2780
|
+
self.data_generation_queue.queue.clear()
|
|
2781
|
+
function_data = self.get_function_data()
|
|
2782
|
+
for t in self.threads:
|
|
2783
|
+
if t.is_alive():
|
|
2784
|
+
t.join(timeout=0.01)
|
|
2397
2785
|
|
|
2398
2786
|
def run(self) -> Tuple[list[int], str, dict]:
|
|
2399
2787
|
""" Run the picker. """
|
|
2400
2788
|
self.logger.info(f"function: run()")
|
|
2401
2789
|
|
|
2790
|
+
self.thread_stop_event.clear()
|
|
2791
|
+
|
|
2402
2792
|
if self.get_footer_string_startup and self.footer_string_refresh_function != None:
|
|
2403
2793
|
self.footer_string = " "
|
|
2404
2794
|
self.footer.adjust_sizes(self.term_h, self.term_w)
|
|
@@ -2411,7 +2801,8 @@ class Picker:
|
|
|
2411
2801
|
|
|
2412
2802
|
self.initial_time = time.time()
|
|
2413
2803
|
self.initial_time_footer = time.time()-self.footer_timer
|
|
2414
|
-
self.
|
|
2804
|
+
self.initial_right_split_time = time.time()-200
|
|
2805
|
+
self.initial_left_split_time = time.time()-200
|
|
2415
2806
|
|
|
2416
2807
|
if self.startup_notification:
|
|
2417
2808
|
self.notification(self.stdscr, message=self.startup_notification)
|
|
@@ -2446,11 +2837,7 @@ class Picker:
|
|
|
2446
2837
|
tty_fd, self.saved_terminal_state = open_tty()
|
|
2447
2838
|
|
|
2448
2839
|
self.update_term_size()
|
|
2449
|
-
|
|
2450
|
-
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
2451
|
-
self.rows_w, self.rows_h = int(self.term_w*proportion), self.term_h
|
|
2452
|
-
else:
|
|
2453
|
-
self.rows_w, self.rows_h = self.term_w, self.term_h
|
|
2840
|
+
self.calculate_section_sizes()
|
|
2454
2841
|
|
|
2455
2842
|
def terminal_resized(old_w, old_h) -> bool:
|
|
2456
2843
|
w, h = os.get_terminal_size()
|
|
@@ -2486,14 +2873,6 @@ class Picker:
|
|
|
2486
2873
|
if self.term_resize_event:
|
|
2487
2874
|
key = curses.KEY_RESIZE
|
|
2488
2875
|
|
|
2489
|
-
self.update_term_size()
|
|
2490
|
-
|
|
2491
|
-
if self.split_right and len(self.right_panes):
|
|
2492
|
-
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
2493
|
-
self.rows_w, self.rows_h = int(self.term_w*proportion), self.term_h
|
|
2494
|
-
else:
|
|
2495
|
-
self.rows_w, self.rows_h = self.term_w, self.term_h
|
|
2496
|
-
|
|
2497
2876
|
if key in self.disabled_keys: continue
|
|
2498
2877
|
clear_screen=True
|
|
2499
2878
|
|
|
@@ -2512,11 +2891,15 @@ class Picker:
|
|
|
2512
2891
|
self.refreshing_data = False
|
|
2513
2892
|
self.data_ready = False
|
|
2514
2893
|
|
|
2894
|
+
|
|
2515
2895
|
elif self.check_key("refresh", key, self.keys_dict) or self.remapped_key(key, curses.KEY_F5, self.key_remappings) or (self.auto_refresh and (time.time() - self.initial_time) >= self.timer):
|
|
2516
2896
|
self.logger.debug(f"Get new data (refresh).")
|
|
2517
|
-
|
|
2897
|
+
try:
|
|
2898
|
+
self.stdscr.addstr(0,self.term_w-3," ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
|
|
2899
|
+
except:
|
|
2900
|
+
pass
|
|
2518
2901
|
self.stdscr.refresh()
|
|
2519
|
-
if self.get_new_data
|
|
2902
|
+
if self.get_new_data:
|
|
2520
2903
|
self.refreshing_data = True
|
|
2521
2904
|
|
|
2522
2905
|
t = threading.Thread(target=self.fetch_data)
|
|
@@ -2545,11 +2928,17 @@ class Picker:
|
|
|
2545
2928
|
self.initial_time_footer = time.time()
|
|
2546
2929
|
self.draw_screen()
|
|
2547
2930
|
|
|
2548
|
-
if self.split_right and len(self.right_panes) and self.right_panes[self.right_pane_index]["auto_refresh"] and ((time.time() - self.
|
|
2931
|
+
if self.split_right and len(self.right_panes) and self.right_panes[self.right_pane_index]["auto_refresh"] and ((time.time() - self.initial_right_split_time) > self.right_panes[self.right_pane_index]["refresh_time"]):
|
|
2549
2932
|
get_data = self.right_panes[self.right_pane_index]["get_data"]
|
|
2550
2933
|
data = self.right_panes[self.right_pane_index]["data"]
|
|
2551
2934
|
self.right_panes[self.right_pane_index]["data"] = get_data(data, self.get_function_data())
|
|
2552
|
-
self.
|
|
2935
|
+
self.initial_right_split_time = time.time()
|
|
2936
|
+
|
|
2937
|
+
if self.split_left and len(self.left_panes) and self.left_panes[self.left_pane_index]["auto_refresh"] and ((time.time() - self.initial_left_split_time) > self.left_panes[self.left_pane_index]["refresh_time"]):
|
|
2938
|
+
get_data = self.left_panes[self.left_pane_index]["get_data"]
|
|
2939
|
+
data = self.left_panes[self.left_pane_index]["data"]
|
|
2940
|
+
self.left_panes[self.right_pane_index]["data"] = get_data(data, self.get_function_data())
|
|
2941
|
+
self.initial_left_split_time = time.time()
|
|
2553
2942
|
|
|
2554
2943
|
if self.check_key("help", key, self.keys_dict):
|
|
2555
2944
|
self.logger.info(f"key_function help")
|
|
@@ -2557,7 +2946,7 @@ class Picker:
|
|
|
2557
2946
|
self.stdscr.refresh()
|
|
2558
2947
|
help_data = {
|
|
2559
2948
|
# "items": help_lines,
|
|
2560
|
-
"items": build_help_rows(self.keys_dict),
|
|
2949
|
+
"items": build_help_rows(self.keys_dict, self.macros),
|
|
2561
2950
|
"title": f"{self.title} Help",
|
|
2562
2951
|
"colours_start": self.help_colours_start,
|
|
2563
2952
|
"colours": help_colours,
|
|
@@ -2581,6 +2970,10 @@ class Picker:
|
|
|
2581
2970
|
s, o, f = OptionPicker.run()
|
|
2582
2971
|
self.draw_screen()
|
|
2583
2972
|
|
|
2973
|
+
if self.check_and_run_macro(key):
|
|
2974
|
+
self.draw_screen()
|
|
2975
|
+
continue
|
|
2976
|
+
|
|
2584
2977
|
if self.check_key("info", key, self.keys_dict):
|
|
2585
2978
|
self.logger.info(f"key_function help")
|
|
2586
2979
|
self.stdscr.clear()
|
|
@@ -2653,6 +3046,14 @@ class Picker:
|
|
|
2653
3046
|
data["option_functions"] = f"[...] length = {len(data['option_functions'])}"
|
|
2654
3047
|
data["loaded_file_states"] = f"[...] length = {len(data['loaded_file_states'])}"
|
|
2655
3048
|
data["sheet_states"] = f"[...] length = {len(data['sheet_states'])}"
|
|
3049
|
+
data["highlights"] = f"[...] length = {len(data['highlights'])}"
|
|
3050
|
+
data["colours"] = f"[...] length = {len(data['colours'])}"
|
|
3051
|
+
data["keys_dict"] = f"[...] length = {len(data['keys_dict'])}"
|
|
3052
|
+
data["history_filter_and_search"] = f"[...] length = {len(data['history_filter_and_search'])}"
|
|
3053
|
+
data["history_opts"] = f"[...] length = {len(data['history_opts'])}"
|
|
3054
|
+
data["history_edits"] = f"[...] length = {len(data['history_edits'])}"
|
|
3055
|
+
data["history_pipes"] = f"[...] length = {len(data['history_pipes'])}"
|
|
3056
|
+
data["history_settings"] = f"[...] length = {len(data['history_settings'])}"
|
|
2656
3057
|
info_items += [
|
|
2657
3058
|
["",""],
|
|
2658
3059
|
[" get_function_data()", "-*"*30],
|
|
@@ -2698,6 +3099,7 @@ class Picker:
|
|
|
2698
3099
|
elif self.check_key("exit", key, self.keys_dict):
|
|
2699
3100
|
self.stdscr.clear()
|
|
2700
3101
|
if len(self.loaded_files) <= 1:
|
|
3102
|
+
self.cleanup_threads()
|
|
2701
3103
|
function_data = self.get_function_data()
|
|
2702
3104
|
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2703
3105
|
return [], "", function_data
|
|
@@ -2719,6 +3121,7 @@ class Picker:
|
|
|
2719
3121
|
self.draw_screen()
|
|
2720
3122
|
|
|
2721
3123
|
elif self.check_key("full_exit", key, self.keys_dict):
|
|
3124
|
+
self.cleanup_threads()
|
|
2722
3125
|
close_curses(self.stdscr)
|
|
2723
3126
|
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2724
3127
|
exit()
|
|
@@ -2758,28 +3161,29 @@ class Picker:
|
|
|
2758
3161
|
|
|
2759
3162
|
elif self.check_key("settings_options", key, self.keys_dict):
|
|
2760
3163
|
options = []
|
|
3164
|
+
options += [["cv", "Centre rows vertically"]]
|
|
3165
|
+
options += [["pc", "Pin cursor to row index during data refresh."]]
|
|
3166
|
+
options += [["ct", "Centre column-set in terminal"]]
|
|
3167
|
+
options += [["cc", "Centre values in cells"]]
|
|
3168
|
+
options += [["!r", "Toggle auto-refresh"]]
|
|
3169
|
+
options += [["th", "Cycle between themes. (accepts th#)"]]
|
|
3170
|
+
options += [["colsel", "Toggle columns."]]
|
|
3171
|
+
options += [["nohl", "Toggle highlights"]]
|
|
3172
|
+
options += [["footer", "Toggle footer"]]
|
|
3173
|
+
options += [["header", "Toggle header"]]
|
|
3174
|
+
options += [["rh", "Toggle row header"]]
|
|
3175
|
+
options += [["modes", "Toggle modes"]]
|
|
3176
|
+
options += [["ft", "Cycle through footer styles (accepts ft#)"]]
|
|
3177
|
+
options += [["file_next", "Go to the next open file."]]
|
|
3178
|
+
options += [["file_prev", "Go to the previous open file."]]
|
|
3179
|
+
options += [["sheet_next", "Go to the next sheet."]]
|
|
3180
|
+
options += [["sheet_prev", "Go to the previous sheet."]]
|
|
3181
|
+
options += [["unicode", "Toggle b/w using len and wcwidth to calculate char width."]]
|
|
3182
|
+
options += [["ara", "Add empty row after cursor."]]
|
|
3183
|
+
options += [["arb", "Add empty row before the cursor."]]
|
|
3184
|
+
options += [["aca", "Add empty column after the selected column."]]
|
|
3185
|
+
options += [["acb", "Add empty column before the selected column."]]
|
|
2761
3186
|
if len(self.items) > 0:
|
|
2762
|
-
options += [["cv", "Centre rows vertically"]]
|
|
2763
|
-
options += [["pc", "Pin cursor to row number when data refreshes"]]
|
|
2764
|
-
options += [["ct", "Centre column-set in terminal"]]
|
|
2765
|
-
options += [["cc", "Centre values in cells"]]
|
|
2766
|
-
options += [["!r", "Toggle auto-refresh"]]
|
|
2767
|
-
options += [["th", "Cycle between themes. (accepts th#)"]]
|
|
2768
|
-
options += [["nohl", "Toggle highlights"]]
|
|
2769
|
-
options += [["footer", "Toggle footer"]]
|
|
2770
|
-
options += [["header", "Toggle header"]]
|
|
2771
|
-
options += [["rh", "Toggle row header"]]
|
|
2772
|
-
options += [["modes", "Toggle modes"]]
|
|
2773
|
-
options += [["ft", "Cycle through footer styles (accepts ft#)"]]
|
|
2774
|
-
options += [["file_next", "Go to the next open file."]]
|
|
2775
|
-
options += [["file_prev", "Go to the previous open file."]]
|
|
2776
|
-
options += [["sheet_next", "Go to the next sheet."]]
|
|
2777
|
-
options += [["sheet_prev", "Go to the previous sheet."]]
|
|
2778
|
-
options += [["unicode", "Toggle b/w using len and wcwidth to calculate char width."]]
|
|
2779
|
-
options += [["ara", "Add empty row after cursor."]]
|
|
2780
|
-
options += [["arb", "Add empty row before the cursor."]]
|
|
2781
|
-
options += [["aca", "Add empty column after the selected column."]]
|
|
2782
|
-
options += [["acb", "Add empty column before the selected column."]]
|
|
2783
3187
|
options += [[f"col{i}", f"Select column {i}"] for i in range(len(self.items[0]))]
|
|
2784
3188
|
options += [[f"s{i}", f"Sort by column {i}"] for i in range(len(self.items[0]))]
|
|
2785
3189
|
options += [[f"!{i}", f"Toggle visibility of column {i}"] for i in range(len(self.items[0]))]
|
|
@@ -2868,6 +3272,7 @@ class Picker:
|
|
|
2868
3272
|
self.selected_cells_by_row[row] = [col]
|
|
2869
3273
|
|
|
2870
3274
|
self.cursor_down()
|
|
3275
|
+
self.ensure_no_overscroll()
|
|
2871
3276
|
elif self.check_key("select_all", key, self.keys_dict): # Select all (m or ctrl-a)
|
|
2872
3277
|
self.select_all()
|
|
2873
3278
|
|
|
@@ -2882,6 +3287,7 @@ class Picker:
|
|
|
2882
3287
|
if new_pos < len(self.indexed_items):
|
|
2883
3288
|
self.cursor_pos = new_pos
|
|
2884
3289
|
|
|
3290
|
+
self.ensure_no_overscroll()
|
|
2885
3291
|
self.draw_screen()
|
|
2886
3292
|
|
|
2887
3293
|
elif self.check_key("cursor_bottom", key, self.keys_dict):
|
|
@@ -2891,17 +3297,15 @@ class Picker:
|
|
|
2891
3297
|
else: break
|
|
2892
3298
|
if new_pos < len(self.items) and new_pos >= 0:
|
|
2893
3299
|
self.cursor_pos = new_pos
|
|
3300
|
+
self.ensure_no_overscroll()
|
|
2894
3301
|
self.draw_screen()
|
|
2895
|
-
|
|
2896
|
-
# if current_page + 1 == (len(self.indexed_items) + items_per_page - 1) // items_per_page:
|
|
2897
|
-
#
|
|
2898
|
-
# current_row = (len(self.indexed_items) +items_per_page - 1) % items_per_page
|
|
2899
|
-
# self.draw_screen()
|
|
3302
|
+
|
|
2900
3303
|
elif self.check_key("enter", key, self.keys_dict):
|
|
2901
3304
|
self.logger.info(f"key_function enter")
|
|
2902
3305
|
# Print the selected indices if any, otherwise print the current index
|
|
2903
3306
|
if self.is_selecting or self.is_deselecting: self.handle_visual_selection()
|
|
2904
3307
|
if len(self.items) == 0:
|
|
3308
|
+
self.cleanup_threads()
|
|
2905
3309
|
function_data = self.get_function_data()
|
|
2906
3310
|
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2907
3311
|
return [], "", function_data
|
|
@@ -2928,6 +3332,7 @@ class Picker:
|
|
|
2928
3332
|
)
|
|
2929
3333
|
|
|
2930
3334
|
if options_sufficient:
|
|
3335
|
+
self.cleanup_threads()
|
|
2931
3336
|
self.user_opts = usrtxt
|
|
2932
3337
|
self.stdscr.clear()
|
|
2933
3338
|
self.stdscr.refresh()
|
|
@@ -2941,15 +3346,7 @@ class Picker:
|
|
|
2941
3346
|
self.cursor_pos = max(0, self.cursor_pos-self.items_per_page)
|
|
2942
3347
|
|
|
2943
3348
|
elif self.check_key("redraw_screen", key, self.keys_dict):
|
|
2944
|
-
self.
|
|
2945
|
-
self.stdscr.clear()
|
|
2946
|
-
self.stdscr.refresh()
|
|
2947
|
-
restrict_curses(self.stdscr)
|
|
2948
|
-
unrestrict_curses(self.stdscr)
|
|
2949
|
-
self.stdscr.clear()
|
|
2950
|
-
self.stdscr.refresh()
|
|
2951
|
-
|
|
2952
|
-
self.draw_screen()
|
|
3349
|
+
self.refresh_and_draw_screen()
|
|
2953
3350
|
|
|
2954
3351
|
elif self.check_key("cycle_sort_method", key, self.keys_dict):
|
|
2955
3352
|
if self.sort_column == self.selected_column:
|
|
@@ -2990,23 +3387,29 @@ class Picker:
|
|
|
2990
3387
|
sort_items(self.indexed_items, sort_method=self.columns_sort_method[self.sort_column], sort_column=self.sort_column, sort_reverse=self.sort_reverse[self.sort_column]) # Re-sort self.items based on new column
|
|
2991
3388
|
self.cursor_pos = [row[0] for row in self.indexed_items].index(current_index)
|
|
2992
3389
|
elif self.check_key("col_select_next", key, self.keys_dict):
|
|
2993
|
-
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
2994
|
-
col_index = (self.selected_column +1) % (len(self.items[0]))
|
|
2995
|
-
self.selected_column = col_index
|
|
2996
|
-
# Flash when we loop back to the first column
|
|
2997
|
-
# if self.selected_column == 0:
|
|
2998
|
-
# curses.flash()
|
|
2999
3390
|
self.logger.info(f"key_function col_select_next {self.selected_column}")
|
|
3391
|
+
if len(self.hidden_columns) != len(self.column_widths):
|
|
3392
|
+
if len(self.column_widths):
|
|
3393
|
+
while True:
|
|
3394
|
+
self.hidden_columns
|
|
3395
|
+
col_index = (self.selected_column +1) % (len(self.column_widths))
|
|
3396
|
+
self.selected_column = col_index
|
|
3397
|
+
if self.selected_column not in self.hidden_columns:
|
|
3398
|
+
break
|
|
3399
|
+
|
|
3400
|
+
# Flash when we loop back to the first column
|
|
3401
|
+
# if self.selected_column == 0:
|
|
3402
|
+
# curses.flash()
|
|
3000
3403
|
|
|
3001
3404
|
|
|
3002
3405
|
## Scroll with column select
|
|
3003
3406
|
self.get_visible_rows()
|
|
3004
3407
|
self.column_widths = get_column_widths(self.visible_rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=self.rows_w, unicode_char_width=self.unicode_char_width)
|
|
3005
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3006
|
-
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
3007
|
-
start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3008
|
-
end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3009
|
-
display_width = self.rows_w-self.
|
|
3408
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3409
|
+
column_set_width = sum(self.visible_column_widths)+len(self.separator)*len(self.visible_column_widths)
|
|
3410
|
+
start_of_cell = sum(self.visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3411
|
+
end_of_cell = sum(self.visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3412
|
+
display_width = self.rows_w-self.left_gutter_width
|
|
3010
3413
|
# If the full column is within the current display then don't do anything
|
|
3011
3414
|
if start_of_cell >= self.leftmost_char and end_of_cell <= self.leftmost_char + display_width:
|
|
3012
3415
|
pass
|
|
@@ -3015,13 +3418,20 @@ class Picker:
|
|
|
3015
3418
|
self.leftmost_char = end_of_cell - display_width
|
|
3016
3419
|
|
|
3017
3420
|
self.leftmost_char = max(0, min(column_set_width - display_width + 5, self.leftmost_char))
|
|
3421
|
+
self.ensure_no_overscroll()
|
|
3018
3422
|
|
|
3019
3423
|
elif self.check_key("col_select_prev", key, self.keys_dict):
|
|
3020
|
-
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
3021
|
-
col_index = (self.selected_column -1) % (len(self.items[0]))
|
|
3022
|
-
self.selected_column = col_index
|
|
3023
|
-
|
|
3024
3424
|
self.logger.info(f"key_function col_select_prev {self.selected_column}")
|
|
3425
|
+
|
|
3426
|
+
if len(self.hidden_columns) != len(self.column_widths):
|
|
3427
|
+
if len(self.column_widths):
|
|
3428
|
+
while True:
|
|
3429
|
+
self.hidden_columns
|
|
3430
|
+
col_index = (self.selected_column -1) % (len(self.column_widths))
|
|
3431
|
+
self.selected_column = col_index
|
|
3432
|
+
if self.selected_column not in self.hidden_columns:
|
|
3433
|
+
break
|
|
3434
|
+
|
|
3025
3435
|
# Flash when we loop back to the last column
|
|
3026
3436
|
# if self.selected_column == len(self.column_widths)-1:
|
|
3027
3437
|
# curses.flash()
|
|
@@ -3029,11 +3439,11 @@ class Picker:
|
|
|
3029
3439
|
## Scroll with column select
|
|
3030
3440
|
self.get_visible_rows()
|
|
3031
3441
|
self.column_widths = get_column_widths(self.visible_rows, header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=self.rows_w, unicode_char_width=self.unicode_char_width)
|
|
3032
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3033
|
-
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
3034
|
-
start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3035
|
-
end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3036
|
-
display_width = self.rows_w-self.
|
|
3442
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3443
|
+
column_set_width = sum(self.visible_column_widths)+len(self.separator)*len(self.visible_column_widths)
|
|
3444
|
+
start_of_cell = sum(self.visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3445
|
+
end_of_cell = sum(self.visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3446
|
+
display_width = self.rows_w-self.left_gutter_width
|
|
3037
3447
|
|
|
3038
3448
|
# If the entire column is within the current display then don't do anything
|
|
3039
3449
|
if start_of_cell >= self.leftmost_char and end_of_cell <= self.leftmost_char + display_width:
|
|
@@ -3043,25 +3453,26 @@ class Picker:
|
|
|
3043
3453
|
self.leftmost_char = start_of_cell
|
|
3044
3454
|
|
|
3045
3455
|
self.leftmost_char = max(0, min(column_set_width - display_width + 5, self.leftmost_char))
|
|
3456
|
+
self.ensure_no_overscroll()
|
|
3046
3457
|
|
|
3047
3458
|
elif self.check_key("scroll_right", key, self.keys_dict):
|
|
3048
3459
|
self.logger.info(f"key_function scroll_right")
|
|
3049
3460
|
if len(self.indexed_items):
|
|
3050
|
-
row_width = sum(self.
|
|
3051
|
-
if row_width-self.leftmost_char >= self.rows_w-
|
|
3461
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
3462
|
+
if row_width-self.leftmost_char >= self.rows_w-5:
|
|
3052
3463
|
self.leftmost_char += 5
|
|
3053
|
-
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w
|
|
3054
|
-
if sum(self.
|
|
3464
|
+
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w) + self.left_gutter_width+5)
|
|
3465
|
+
if sum(self.visible_column_widths) + len(self.visible_column_widths)*len(self.separator) < self.rows_w:
|
|
3055
3466
|
self.leftmost_char = 0
|
|
3056
3467
|
|
|
3057
3468
|
elif self.check_key("scroll_right_25", key, self.keys_dict):
|
|
3058
3469
|
self.logger.info(f"key_function scroll_right")
|
|
3059
3470
|
if len(self.indexed_items):
|
|
3060
|
-
row_width = sum(self.
|
|
3061
|
-
if row_width-self.leftmost_char >= self.rows_w-
|
|
3471
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
3472
|
+
if row_width-self.leftmost_char+5 >= self.rows_w-25:
|
|
3062
3473
|
self.leftmost_char += 25
|
|
3063
|
-
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w
|
|
3064
|
-
if sum(self.
|
|
3474
|
+
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w) + self.left_gutter_width+5)
|
|
3475
|
+
if sum(self.visible_column_widths) + len(self.visible_column_widths)*len(self.separator) < self.rows_w:
|
|
3065
3476
|
self.leftmost_char = 0
|
|
3066
3477
|
|
|
3067
3478
|
elif self.check_key("scroll_left", key, self.keys_dict):
|
|
@@ -3080,20 +3491,14 @@ class Picker:
|
|
|
3080
3491
|
elif self.check_key("scroll_far_right", key, self.keys_dict):
|
|
3081
3492
|
self.logger.info(f"key_function scroll_far_right")
|
|
3082
3493
|
longest_row_str_len = 0
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
# row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
|
|
3092
|
-
# if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
|
|
3093
|
-
# self.notification(self.stdscr, f"{longest_row_str_len}")
|
|
3094
|
-
self.leftmost_char = max(0, longest_row_str_len-self.rows_w+2+self.startx+5)
|
|
3095
|
-
if len(self.items):
|
|
3096
|
-
self.selected_column = len(self.items[0])-1
|
|
3494
|
+
longest_row_str_len = sum(self.visible_column_widths) + (len(self.visible_column_widths)-1)*len(self.separator)
|
|
3495
|
+
if len(self.column_widths):
|
|
3496
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
3497
|
+
self.leftmost_char = row_width - (self.rows_w) + self.left_gutter_width+5
|
|
3498
|
+
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w) + self.left_gutter_width+5)
|
|
3499
|
+
|
|
3500
|
+
longest_row_str_len = sum(self.visible_column_widths) + (len(self.visible_column_widths)-1)*len(self.separator)
|
|
3501
|
+
self.selected_column = len(self.column_widths)-1
|
|
3097
3502
|
|
|
3098
3503
|
elif self.check_key("add_column_before", key, self.keys_dict):
|
|
3099
3504
|
self.logger.info(f"key_function add_column_before")
|
|
@@ -3150,16 +3555,6 @@ class Picker:
|
|
|
3150
3555
|
self.selected_column = min(self.selected_column, row_len-2)
|
|
3151
3556
|
self.initialise_variables()
|
|
3152
3557
|
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
# elif self.check_key("increase_lines_per_page", key, self.keys_dict):
|
|
3157
|
-
# self.items_per_page += 1
|
|
3158
|
-
# self.draw_screen()
|
|
3159
|
-
# elif self.check_key("decrease_lines_per_page", key, self.keys_dict):
|
|
3160
|
-
# if self.items_per_page > 1:
|
|
3161
|
-
# self.items_per_page -= 1
|
|
3162
|
-
# self.draw_screen()
|
|
3163
3558
|
elif self.check_key("decrease_column_width", key, self.keys_dict):
|
|
3164
3559
|
self.logger.info(f"key_function decrease_column_width")
|
|
3165
3560
|
if self.max_column_width > 10:
|
|
@@ -3185,7 +3580,8 @@ class Picker:
|
|
|
3185
3580
|
elif key == curses.KEY_RESIZE: # Terminal resize signal
|
|
3186
3581
|
|
|
3187
3582
|
self.calculate_section_sizes()
|
|
3188
|
-
self.
|
|
3583
|
+
self.ensure_no_overscroll()
|
|
3584
|
+
|
|
3189
3585
|
self.stdscr.clear()
|
|
3190
3586
|
self.stdscr.refresh()
|
|
3191
3587
|
self.draw_screen()
|
|
@@ -3340,12 +3736,6 @@ class Picker:
|
|
|
3340
3736
|
function_data = self.get_function_data()
|
|
3341
3737
|
return [], "escape", function_data
|
|
3342
3738
|
|
|
3343
|
-
|
|
3344
|
-
# else:
|
|
3345
|
-
# self.search_query = ""
|
|
3346
|
-
# self.mode_index = 0
|
|
3347
|
-
# self.highlights = [highlight for highlight in self.highlights if "type" not in highlight or highlight["type"] != "search" ]
|
|
3348
|
-
# continue
|
|
3349
3739
|
self.draw_screen()
|
|
3350
3740
|
|
|
3351
3741
|
elif self.check_key("opts_input", key, self.keys_dict):
|
|
@@ -3442,6 +3832,12 @@ class Picker:
|
|
|
3442
3832
|
elif self.check_key("cycle_right_pane", key, self.keys_dict):
|
|
3443
3833
|
self.cycle_right_pane()
|
|
3444
3834
|
|
|
3835
|
+
elif self.check_key("toggle_left_pane", key, self.keys_dict):
|
|
3836
|
+
self.toggle_left_pane()
|
|
3837
|
+
|
|
3838
|
+
elif self.check_key("cycle_left_pane", key, self.keys_dict):
|
|
3839
|
+
self.cycle_left_pane()
|
|
3840
|
+
|
|
3445
3841
|
elif self.check_key("pipe_input", key, self.keys_dict):
|
|
3446
3842
|
self.logger.info(f"key_function pipe_input")
|
|
3447
3843
|
# usrtxt = "xargs -d '\n' -I{} "
|
|
@@ -3465,7 +3861,7 @@ class Picker:
|
|
|
3465
3861
|
field_prefix=" Command: ",
|
|
3466
3862
|
x=lambda:2,
|
|
3467
3863
|
y=lambda: self.get_term_size()[0]-2,
|
|
3468
|
-
literal=
|
|
3864
|
+
literal=False,
|
|
3469
3865
|
max_length=field_end_f,
|
|
3470
3866
|
registers=self.registers,
|
|
3471
3867
|
refresh_screen_function=lambda: self.draw_screen(),
|
|
@@ -3555,7 +3951,7 @@ class Picker:
|
|
|
3555
3951
|
|
|
3556
3952
|
elif self.check_key("edit", key, self.keys_dict):
|
|
3557
3953
|
self.logger.info(f"key_function edit")
|
|
3558
|
-
if len(self.indexed_items) > 0 and self.
|
|
3954
|
+
if len(self.indexed_items) > 0 and self.editable_columns[self.selected_column]:
|
|
3559
3955
|
current_val = self.indexed_items[self.cursor_pos][1][self.selected_column]
|
|
3560
3956
|
usrtxt = f"{current_val}"
|
|
3561
3957
|
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
@@ -3584,6 +3980,47 @@ class Picker:
|
|
|
3584
3980
|
usrtxt = str(eval(usrtxt[3:]))
|
|
3585
3981
|
self.indexed_items[self.cursor_pos][1][self.selected_column] = usrtxt
|
|
3586
3982
|
self.history_edits.append(usrtxt)
|
|
3983
|
+
elif self.check_key("edit_nvim", key, self.keys_dict):
|
|
3984
|
+
|
|
3985
|
+
def edit_strings_in_nvim(strings: list[str]) -> list[str]:
|
|
3986
|
+
"""
|
|
3987
|
+
Opens a list of strings in nvim for editing and returns the modified strings.
|
|
3988
|
+
|
|
3989
|
+
Args:
|
|
3990
|
+
strings (list[str]): The list of strings to edit.
|
|
3991
|
+
|
|
3992
|
+
Returns:
|
|
3993
|
+
list[str]: The updated list of strings after editing in nvim.
|
|
3994
|
+
"""
|
|
3995
|
+
|
|
3996
|
+
# Open the strings in a tmpfile for editing
|
|
3997
|
+
with tempfile.NamedTemporaryFile(mode="w+", suffix=".txt", delete=False) as tmp:
|
|
3998
|
+
tmp.write("\n".join(strings))
|
|
3999
|
+
tmp.flush()
|
|
4000
|
+
tmp_name = tmp.name
|
|
4001
|
+
|
|
4002
|
+
subprocess.run(["nvim", tmp_name])
|
|
4003
|
+
|
|
4004
|
+
# Read the modified strings into a list and return them.
|
|
4005
|
+
with open(tmp_name, "r") as tmp:
|
|
4006
|
+
edited_content = tmp.read().splitlines()
|
|
4007
|
+
|
|
4008
|
+
return edited_content
|
|
4009
|
+
|
|
4010
|
+
if len(self.indexed_items) > 0 and self.editable_columns[self.selected_column]:
|
|
4011
|
+
|
|
4012
|
+
selected_cells = [self.items[index][self.selected_column] for index, selected in self.selections.items() if selected ]
|
|
4013
|
+
selected_cells_indices = [(index, self.selected_column) for index, selected in self.selections.items() if selected ]
|
|
4014
|
+
|
|
4015
|
+
edited_cells = edit_strings_in_nvim(selected_cells)
|
|
4016
|
+
count = 0
|
|
4017
|
+
if len(edited_cells) == len(selected_cells_indices):
|
|
4018
|
+
for i, j in selected_cells_indices:
|
|
4019
|
+
self.items[i][j] = edited_cells[count]
|
|
4020
|
+
count += 1
|
|
4021
|
+
|
|
4022
|
+
self.refresh_and_draw_screen()
|
|
4023
|
+
|
|
3587
4024
|
|
|
3588
4025
|
elif self.check_key("edit_picker", key, self.keys_dict):
|
|
3589
4026
|
self.logger.info(f"key_function edit_picker")
|
|
@@ -3615,7 +4052,7 @@ class Picker:
|
|
|
3615
4052
|
self.indexed_items[self.cursor_pos][1][self.selected_column] = usrtxt
|
|
3616
4053
|
self.history_edits.append(usrtxt)
|
|
3617
4054
|
elif self.check_key("edit_ipython", key, self.keys_dict):
|
|
3618
|
-
self.logger.info(f"key_function
|
|
4055
|
+
self.logger.info(f"key_function edit_ipython")
|
|
3619
4056
|
import IPython, termios
|
|
3620
4057
|
self.stdscr.clear()
|
|
3621
4058
|
restrict_curses(self.stdscr)
|
|
@@ -3759,7 +4196,7 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
|
|
|
3759
4196
|
# input_arg = args.filename
|
|
3760
4197
|
|
|
3761
4198
|
elif args.generate:
|
|
3762
|
-
function_data["refresh_function"] = lambda items, header, visible_rows_indices, getting_data: generate_picker_data_from_file(args.generate, items, header, visible_rows_indices, getting_data)
|
|
4199
|
+
function_data["refresh_function"] = lambda items, header, visible_rows_indices, getting_data, state: generate_picker_data_from_file(args.generate, items, header, visible_rows_indices, getting_data, state)
|
|
3763
4200
|
function_data["get_data_startup"] = True
|
|
3764
4201
|
function_data["get_new_data"] = True
|
|
3765
4202
|
return args, function_data
|
|
@@ -4003,7 +4440,7 @@ def main() -> None:
|
|
|
4003
4440
|
# function_data["cell_cursor"] = True
|
|
4004
4441
|
# function_data["display_modes"] = True
|
|
4005
4442
|
# function_data["centre_in_cols"] = True
|
|
4006
|
-
|
|
4443
|
+
function_data["show_row_header"] = True
|
|
4007
4444
|
# function_data["keys_dict"] = picker_keys
|
|
4008
4445
|
# function_data["id_column"] = -1
|
|
4009
4446
|
# function_data["track_entries_upon_refresh"] = True
|
|
@@ -4019,10 +4456,23 @@ def main() -> None:
|
|
|
4019
4456
|
# function_data["debug"] = True
|
|
4020
4457
|
# function_data["debug_level"] = 1
|
|
4021
4458
|
|
|
4459
|
+
# function_data["cell_cursor"] = False
|
|
4460
|
+
|
|
4022
4461
|
function_data["split_right"] = False
|
|
4462
|
+
function_data["split_left"] = False
|
|
4023
4463
|
function_data["right_pane_index"] = 2
|
|
4464
|
+
function_data["left_pane_index"] = 0
|
|
4024
4465
|
|
|
4025
4466
|
function_data["right_panes"] = [
|
|
4467
|
+
# Nopane
|
|
4468
|
+
{
|
|
4469
|
+
"proportion": 1/3,
|
|
4470
|
+
"auto_refresh": False,
|
|
4471
|
+
"get_data": lambda data, state: [],
|
|
4472
|
+
"display": left_start_pane,
|
|
4473
|
+
"data": ["Files", []],
|
|
4474
|
+
"refresh_time": 1,
|
|
4475
|
+
},
|
|
4026
4476
|
# Graph or random numbers generated each second
|
|
4027
4477
|
{
|
|
4028
4478
|
"proportion": 1/2,
|
|
@@ -4034,7 +4484,7 @@ def main() -> None:
|
|
|
4034
4484
|
},
|
|
4035
4485
|
# list of numbers
|
|
4036
4486
|
{
|
|
4037
|
-
"proportion":
|
|
4487
|
+
"proportion": 1/3,
|
|
4038
4488
|
"auto_refresh": False,
|
|
4039
4489
|
"get_data": data_refresh_randint_title,
|
|
4040
4490
|
"display": right_split_display_list,
|
|
@@ -4043,7 +4493,7 @@ def main() -> None:
|
|
|
4043
4493
|
},
|
|
4044
4494
|
# File attributes
|
|
4045
4495
|
{
|
|
4046
|
-
"proportion":
|
|
4496
|
+
"proportion": 1/3,
|
|
4047
4497
|
"auto_refresh": False,
|
|
4048
4498
|
"get_data": lambda data, state: [],
|
|
4049
4499
|
"display": right_split_file_attributes,
|
|
@@ -4052,7 +4502,7 @@ def main() -> None:
|
|
|
4052
4502
|
},
|
|
4053
4503
|
# File attributes dynamic
|
|
4054
4504
|
{
|
|
4055
|
-
"proportion":
|
|
4505
|
+
"proportion": 1/3,
|
|
4056
4506
|
"auto_refresh": True,
|
|
4057
4507
|
"get_data": update_file_attributes,
|
|
4058
4508
|
"display": right_split_file_attributes_dynamic,
|
|
@@ -4078,7 +4528,79 @@ def main() -> None:
|
|
|
4078
4528
|
"refresh_time": 1,
|
|
4079
4529
|
},
|
|
4080
4530
|
]
|
|
4081
|
-
function_data["
|
|
4531
|
+
function_data["left_panes"] = [
|
|
4532
|
+
# Nopane
|
|
4533
|
+
{
|
|
4534
|
+
"proportion": 1/3,
|
|
4535
|
+
"auto_refresh": False,
|
|
4536
|
+
"get_data": lambda data, state: [],
|
|
4537
|
+
"display": left_start_pane,
|
|
4538
|
+
"data": ["Files", []],
|
|
4539
|
+
"refresh_time": 1,
|
|
4540
|
+
},
|
|
4541
|
+
# Graph or random numbers generated each second
|
|
4542
|
+
{
|
|
4543
|
+
"proportion": 1/2,
|
|
4544
|
+
"auto_refresh": True,
|
|
4545
|
+
"get_data": data_refresh_randint,
|
|
4546
|
+
"display": left_split_graph,
|
|
4547
|
+
"data": [],
|
|
4548
|
+
"refresh_time": 1.0,
|
|
4549
|
+
},
|
|
4550
|
+
# list of numbers
|
|
4551
|
+
{
|
|
4552
|
+
"proportion": 1/3,
|
|
4553
|
+
"auto_refresh": False,
|
|
4554
|
+
"get_data": data_refresh_randint_title,
|
|
4555
|
+
"display": left_split_display_list,
|
|
4556
|
+
"data": ["Files", [str(x) for x in range(100)]],
|
|
4557
|
+
"refresh_time": 1.0,
|
|
4558
|
+
},
|
|
4559
|
+
# File attributes
|
|
4560
|
+
{
|
|
4561
|
+
"proportion": 1/3,
|
|
4562
|
+
"auto_refresh": False,
|
|
4563
|
+
"get_data": lambda data, state: [],
|
|
4564
|
+
"display": left_split_file_attributes,
|
|
4565
|
+
"data": [],
|
|
4566
|
+
"refresh_time": 1.0,
|
|
4567
|
+
},
|
|
4568
|
+
# File attributes dynamic
|
|
4569
|
+
{
|
|
4570
|
+
"proportion": 1/3,
|
|
4571
|
+
"auto_refresh": True,
|
|
4572
|
+
"get_data": update_file_attributes,
|
|
4573
|
+
"display": left_split_file_attributes_dynamic,
|
|
4574
|
+
"data": [],
|
|
4575
|
+
"refresh_time": 2.0,
|
|
4576
|
+
},
|
|
4577
|
+
# List of random numbers generated each second
|
|
4578
|
+
{
|
|
4579
|
+
"proportion": 1/2,
|
|
4580
|
+
"auto_refresh": True,
|
|
4581
|
+
"get_data": data_refresh_randint_title,
|
|
4582
|
+
"display": left_split_display_list,
|
|
4583
|
+
"data": ["Files", []],
|
|
4584
|
+
"refresh_time": 2,
|
|
4585
|
+
},
|
|
4586
|
+
# Nopane
|
|
4587
|
+
{
|
|
4588
|
+
"proportion": 1/3,
|
|
4589
|
+
"auto_refresh": False,
|
|
4590
|
+
"get_data": lambda data, state: [],
|
|
4591
|
+
"display": lambda scr, x, y, w, h, state, row, cell, data: [],
|
|
4592
|
+
"data": ["Files", []],
|
|
4593
|
+
"refresh_time": 1,
|
|
4594
|
+
},
|
|
4595
|
+
]
|
|
4596
|
+
function_data["macros"] = [
|
|
4597
|
+
# {
|
|
4598
|
+
# "keys": [ord('z')],
|
|
4599
|
+
# "description": "Display message via dbus.",
|
|
4600
|
+
# "function": lambda picker_obj: os.system("notify-send 'zkey pressed'")
|
|
4601
|
+
# },
|
|
4602
|
+
]
|
|
4603
|
+
# function_data["require_option"] = [True for _ in function_data["items"]]
|
|
4082
4604
|
|
|
4083
4605
|
stdscr = start_curses()
|
|
4084
4606
|
try:
|