listpick 0.1.15.19__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 +1235 -660
- listpick/pane/get_data.py +20 -0
- listpick/pane/left_pane_functions.py +198 -0
- listpick/pane/pane_functions.py +45 -5
- listpick/pane/pane_functions_1.py +175 -0
- listpick/pane/pane_utils.py +3 -3
- listpick/ui/build_help.py +37 -12
- listpick/ui/draw_screen.py +0 -0
- listpick/ui/footer.py +3 -3
- listpick/ui/input_field.py +1 -20
- listpick/ui/keys.py +16 -10
- listpick/ui/picker_colours.py +55 -0
- listpick/utils/generate_data.py +45 -2
- listpick/utils/generate_data_multiprocessing.py +10 -0
- listpick/utils/generate_data_multithreaded.py +234 -0
- listpick/utils/generate_data_utils.py +43 -0
- listpick/utils/user_input.py +7 -1
- listpick/utils/utils.py +17 -15
- {listpick-0.1.15.19.dist-info → listpick-0.1.16.17.dist-info}/METADATA +31 -42
- listpick-0.1.16.17.dist-info/RECORD +43 -0
- listpick-0.1.15.19.dist-info/RECORD +0 -37
- {listpick-0.1.15.19.dist-info → listpick-0.1.16.17.dist-info}/WHEEL +0 -0
- {listpick-0.1.15.19.dist-info → listpick-0.1.16.17.dist-info}/entry_points.txt +0 -0
- {listpick-0.1.15.19.dist-info → listpick-0.1.16.17.dist-info}/licenses/LICENSE.txt +0 -0
- {listpick-0.1.15.19.dist-info → listpick-0.1.16.17.dist-info}/top_level.txt +0 -0
listpick/listpick_app.py
CHANGED
|
@@ -18,12 +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
|
|
23
|
-
import
|
|
24
|
-
import
|
|
24
|
+
import copy
|
|
25
|
+
import tempfile
|
|
26
|
+
import queue
|
|
25
27
|
|
|
26
28
|
from listpick.pane.pane_utils import get_file_attributes
|
|
29
|
+
from listpick.pane.left_pane_functions import *
|
|
27
30
|
from listpick.ui.picker_colours import get_colours, get_help_colours, get_notification_colours, get_theme_count, get_fallback_colours
|
|
28
31
|
from listpick.utils.options_selectors import default_option_input, output_file_option_selector, default_option_selector
|
|
29
32
|
from listpick.utils.table_to_list_of_lists import *
|
|
@@ -36,24 +39,20 @@ from listpick.utils.paste_operations import *
|
|
|
36
39
|
from listpick.utils.searching import search
|
|
37
40
|
from listpick.ui.help_screen import help_lines
|
|
38
41
|
from listpick.ui.keys import picker_keys, notification_keys, options_keys, help_keys
|
|
39
|
-
from listpick.utils.
|
|
42
|
+
from listpick.utils.generate_data_multithreaded import generate_picker_data_from_file
|
|
40
43
|
from listpick.utils.dump import dump_state, load_state, dump_data
|
|
41
44
|
from listpick.ui.build_help import build_help_rows
|
|
42
45
|
from listpick.ui.footer import StandardFooter, CompactFooter, NoFooter
|
|
43
46
|
from listpick.utils.picker_log import setup_logger
|
|
44
|
-
from listpick.utils.user_input import get_char, open_tty
|
|
45
|
-
from listpick.pane.pane_functions import right_split_file_attributes, right_split_graph, right_split_display_list
|
|
47
|
+
from listpick.utils.user_input import get_char, open_tty, restore_terminal_settings
|
|
48
|
+
from listpick.pane.pane_functions import right_split_file_attributes, right_split_file_attributes_dynamic, right_split_graph, right_split_display_list
|
|
46
49
|
from listpick.pane.get_data import *
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
from tmp.data_stuff import test_items, test_highlights, test_header
|
|
51
|
-
except:
|
|
52
|
-
test_items, test_highlights, test_header = [], [], []
|
|
53
|
-
|
|
54
51
|
COLOURS_SET = False
|
|
55
52
|
help_colours, notification_colours = {}, {}
|
|
56
53
|
|
|
54
|
+
os.environ["TMPDIR"] = "/tmp"
|
|
55
|
+
|
|
57
56
|
class Command:
|
|
58
57
|
def __init__(self, command_type, command_value):
|
|
59
58
|
self.command_type = command_type
|
|
@@ -64,7 +63,7 @@ class Picker:
|
|
|
64
63
|
stdscr: curses.window,
|
|
65
64
|
items: list[list[str]] = [],
|
|
66
65
|
cursor_pos: int = 0,
|
|
67
|
-
colours: dict =
|
|
66
|
+
colours: dict = {},
|
|
68
67
|
colour_theme_number: int = 3,
|
|
69
68
|
max_selected: int = -1,
|
|
70
69
|
top_gap: int =0,
|
|
@@ -76,9 +75,9 @@ class Picker:
|
|
|
76
75
|
auto_refresh: bool =False,
|
|
77
76
|
timer: float = 5,
|
|
78
77
|
|
|
79
|
-
get_new_data: bool =False,
|
|
80
|
-
refresh_function: Optional[Callable] = lambda
|
|
81
|
-
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,
|
|
82
81
|
track_entries_upon_refresh: bool = True,
|
|
83
82
|
pin_cursor: bool = False,
|
|
84
83
|
id_column: int = 0,
|
|
@@ -101,7 +100,12 @@ class Picker:
|
|
|
101
100
|
user_opts : str = "",
|
|
102
101
|
options_list: list[str] = [],
|
|
103
102
|
user_settings : str = "",
|
|
103
|
+
|
|
104
104
|
separator : str = " ",
|
|
105
|
+
header_separator : str = " │",
|
|
106
|
+
header_separator_before_selected_column : str = " ▐",
|
|
107
|
+
|
|
108
|
+
|
|
105
109
|
search_query : str = "",
|
|
106
110
|
search_count : int = 0,
|
|
107
111
|
search_index : int = 0,
|
|
@@ -116,6 +120,10 @@ class Picker:
|
|
|
116
120
|
highlight_full_row: bool =False,
|
|
117
121
|
crosshair_cursor: bool = False,
|
|
118
122
|
cell_cursor: bool = True,
|
|
123
|
+
selected_char: str = "",
|
|
124
|
+
unselected_char: str = "",
|
|
125
|
+
selecting_char: str = "",
|
|
126
|
+
deselecting_char: str = "",
|
|
119
127
|
|
|
120
128
|
items_per_page : int = -1,
|
|
121
129
|
sort_method : int = 0,
|
|
@@ -127,17 +135,18 @@ class Picker:
|
|
|
127
135
|
columns_sort_method: list[int] = [0],
|
|
128
136
|
key_chain: str = "",
|
|
129
137
|
last_key: Optional[str] = None,
|
|
138
|
+
disabled_keys: list=[],
|
|
130
139
|
|
|
131
140
|
paginate: bool =False,
|
|
132
141
|
cancel_is_back: bool = False,
|
|
133
142
|
mode_index: int = 0,
|
|
134
143
|
modes: list[dict] = [],
|
|
135
|
-
display_modes: bool =False,
|
|
136
|
-
require_option: list=[],
|
|
137
|
-
require_option_default:
|
|
144
|
+
display_modes: bool = False,
|
|
145
|
+
require_option: list[bool] = [],
|
|
146
|
+
require_option_default: bool = False,
|
|
138
147
|
option_functions: list[Callable[..., Tuple[bool, str]]] = [],
|
|
139
148
|
default_option_function: Callable[..., Tuple[bool, str]] = default_option_input,
|
|
140
|
-
|
|
149
|
+
|
|
141
150
|
|
|
142
151
|
show_header: bool = True,
|
|
143
152
|
show_row_header: bool = False,
|
|
@@ -145,7 +154,7 @@ class Picker:
|
|
|
145
154
|
footer_style: int = 0,
|
|
146
155
|
footer_string: str="",
|
|
147
156
|
footer_string_auto_refresh: bool=False,
|
|
148
|
-
footer_string_refresh_function: Optional[Callable] = None,
|
|
157
|
+
footer_string_refresh_function: Optional[Callable] = lambda : None,
|
|
149
158
|
footer_timer: float=1,
|
|
150
159
|
get_footer_string_startup=False,
|
|
151
160
|
unicode_char_width: bool = True,
|
|
@@ -155,6 +164,7 @@ class Picker:
|
|
|
155
164
|
reset_colours: bool = True,
|
|
156
165
|
key_remappings: dict = {},
|
|
157
166
|
keys_dict:dict = picker_keys,
|
|
167
|
+
macros: list = [],
|
|
158
168
|
display_infobox : bool = False,
|
|
159
169
|
infobox_items: list[list[str]] = [],
|
|
160
170
|
infobox_title: str = "",
|
|
@@ -195,11 +205,24 @@ class Picker:
|
|
|
195
205
|
split_right: bool = False,
|
|
196
206
|
right_panes: list = [],
|
|
197
207
|
right_pane_index: int = 0,
|
|
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
|
+
|
|
217
|
+
# getting_data: threading.Event = threading.Event(),
|
|
218
|
+
|
|
198
219
|
):
|
|
220
|
+
|
|
221
|
+
self.screen_size_function = screen_size_function
|
|
199
222
|
self.stdscr = stdscr
|
|
200
223
|
self.items = items
|
|
201
224
|
self.cursor_pos = cursor_pos
|
|
202
|
-
self.colours =
|
|
225
|
+
self.colours = get_colours(colour_theme_number)
|
|
203
226
|
self.colour_theme_number = colour_theme_number
|
|
204
227
|
self.max_selected = max_selected
|
|
205
228
|
self.top_gap = top_gap
|
|
@@ -237,6 +260,8 @@ class Picker:
|
|
|
237
260
|
self.options_list = options_list
|
|
238
261
|
self.user_settings = user_settings
|
|
239
262
|
self.separator = separator
|
|
263
|
+
self.header_separator = header_separator
|
|
264
|
+
self.header_separator_before_selected_column = header_separator_before_selected_column
|
|
240
265
|
self.search_query = search_query
|
|
241
266
|
self.search_count = search_count
|
|
242
267
|
self.search_index = search_index
|
|
@@ -251,6 +276,10 @@ class Picker:
|
|
|
251
276
|
self.highlight_full_row = highlight_full_row
|
|
252
277
|
self.crosshair_cursor = crosshair_cursor
|
|
253
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
|
|
254
283
|
|
|
255
284
|
self.items_per_page = items_per_page
|
|
256
285
|
self.sort_method = sort_method
|
|
@@ -280,7 +309,7 @@ class Picker:
|
|
|
280
309
|
self.footer_string_auto_refresh = footer_string_auto_refresh
|
|
281
310
|
self.footer_string_refresh_function = footer_string_refresh_function
|
|
282
311
|
self.footer_timer = footer_timer
|
|
283
|
-
self.get_footer_string_startup = get_footer_string_startup
|
|
312
|
+
self.get_footer_string_startup = get_footer_string_startup
|
|
284
313
|
self.unicode_char_width = unicode_char_width
|
|
285
314
|
|
|
286
315
|
|
|
@@ -289,6 +318,7 @@ class Picker:
|
|
|
289
318
|
self.reset_colours = reset_colours
|
|
290
319
|
self.key_remappings = key_remappings
|
|
291
320
|
self.keys_dict = keys_dict
|
|
321
|
+
self.macros = macros
|
|
292
322
|
self.display_infobox = display_infobox
|
|
293
323
|
self.infobox_items = infobox_items
|
|
294
324
|
self.infobox_title = infobox_title
|
|
@@ -312,7 +342,6 @@ class Picker:
|
|
|
312
342
|
|
|
313
343
|
|
|
314
344
|
# Refresh function variables
|
|
315
|
-
self.data_refreshed = False
|
|
316
345
|
self.refreshing_data = False
|
|
317
346
|
self.data_lock = threading.Lock()
|
|
318
347
|
self.data_ready = False
|
|
@@ -347,14 +376,45 @@ class Picker:
|
|
|
347
376
|
self.split_right = split_right
|
|
348
377
|
self.right_panes = right_panes
|
|
349
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
|
+
|
|
384
|
+
self.visible_rows_indices = []
|
|
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
|
+
|
|
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
|
|
397
|
+
|
|
398
|
+
|
|
350
399
|
self.initialise_picker_state(reset_colours=self.reset_colours)
|
|
351
400
|
|
|
352
401
|
# Note: We have to set the footer after initialising the picker state so that the footer can use the get_function_data method
|
|
353
402
|
self.footer_options = [StandardFooter(self.stdscr, colours_start, self.get_function_data), CompactFooter(self.stdscr, colours_start, self.get_function_data), NoFooter(self.stdscr, colours_start, self.get_function_data)]
|
|
354
403
|
self.footer = self.footer_options[self.footer_style]
|
|
355
|
-
self.
|
|
404
|
+
self.footer.adjust_sizes(self.term_h, self.term_w)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
# getting_data.is_set() is True when we are getting data
|
|
409
|
+
self.getting_data = threading.Event()
|
|
410
|
+
self.getting_data.set()
|
|
356
411
|
|
|
357
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
|
+
"""
|
|
358
418
|
|
|
359
419
|
size = super().__sizeof__()
|
|
360
420
|
|
|
@@ -365,7 +425,19 @@ class Picker:
|
|
|
365
425
|
return size
|
|
366
426
|
|
|
367
427
|
def set_config(self, path: str ="~/.config/listpick/config.toml") -> bool:
|
|
368
|
-
""" 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
|
+
|
|
369
441
|
path = os.path.expanduser(os.path.expandvars(path))
|
|
370
442
|
if not os.path.exists(path):
|
|
371
443
|
return False
|
|
@@ -374,14 +446,17 @@ class Picker:
|
|
|
374
446
|
except Exception as e:
|
|
375
447
|
self.logger.error(f"get_config({path}) load error. {e}")
|
|
376
448
|
return False
|
|
449
|
+
|
|
377
450
|
|
|
451
|
+
# Change the global theme if colour_theme_number is in the loaded config
|
|
378
452
|
if "general" in config:
|
|
379
453
|
if "colour_theme_number" in config["general"] and config["general"]["colour_theme_number"] != self.colour_theme_number:
|
|
380
454
|
global COLOURS_SET
|
|
381
455
|
COLOURS_SET = False
|
|
382
456
|
self.colours_end = set_colours(pick=config["general"]["colour_theme_number"], start=1)
|
|
457
|
+
self.colours = get_colours(config["general"]["colour_theme_number"])
|
|
383
458
|
|
|
384
|
-
|
|
459
|
+
# load the rest of the config options
|
|
385
460
|
if "general" in config:
|
|
386
461
|
for key, val in config["general"].items():
|
|
387
462
|
self.logger.info(f"set_config: key={key}, val={val}.")
|
|
@@ -393,19 +468,58 @@ class Picker:
|
|
|
393
468
|
return True
|
|
394
469
|
|
|
395
470
|
def get_config(self, path: str ="~/.config/listpick/config.toml") -> dict:
|
|
396
|
-
"""
|
|
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
|
+
|
|
397
483
|
self.logger.info(f"function: get_config()")
|
|
398
484
|
import toml
|
|
399
485
|
with open(os.path.expanduser(path), "r") as f:
|
|
400
486
|
config = toml.load(f)
|
|
401
487
|
return config
|
|
402
488
|
|
|
403
|
-
def
|
|
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.
|
|
504
|
+
|
|
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
|
|
512
|
+
|
|
513
|
+
def calculate_section_sizes(self) -> None:
|
|
404
514
|
"""
|
|
405
515
|
Calculte the following for the Picker:
|
|
406
516
|
self.items_per_page: the number of entry rows displayed
|
|
407
517
|
self.bottom_space: the size of the footer + the bottom buffer space
|
|
408
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
|
|
409
523
|
"""
|
|
410
524
|
|
|
411
525
|
self.logger.debug(f"function: calculate_section_sizes()")
|
|
@@ -413,14 +527,35 @@ class Picker:
|
|
|
413
527
|
# self.bottom_space
|
|
414
528
|
self.bottom_space = self.footer.height if self.show_footer else 0
|
|
415
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
|
+
|
|
416
535
|
## self.top_space
|
|
417
|
-
|
|
418
|
-
self.
|
|
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
|
|
419
541
|
if self.split_right and len(self.right_panes):
|
|
420
542
|
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
421
|
-
self.
|
|
422
|
-
|
|
423
|
-
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
|
+
|
|
424
559
|
|
|
425
560
|
self.top_space = self.top_gap
|
|
426
561
|
if self.title: self.top_space+=1
|
|
@@ -438,42 +573,66 @@ class Picker:
|
|
|
438
573
|
self.top_space += ((self.term_h-(self.top_space+self.bottom_space))-len(self.indexed_items))//2
|
|
439
574
|
|
|
440
575
|
# self.column_widths
|
|
441
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
442
|
-
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)
|
|
443
578
|
|
|
444
579
|
# self.startx
|
|
445
580
|
self.startx = 1 if self.highlight_full_row else 2
|
|
446
581
|
if self.show_row_header: self.startx += len(str(len(self.items))) + 2
|
|
447
582
|
if visible_columns_total_width < self.rows_w and self.centre_in_terminal:
|
|
448
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
|
+
|
|
449
592
|
|
|
450
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.
|
|
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.
|
|
451
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
|
+
"""
|
|
452
606
|
self.logger.debug(f"function: get_visible_rows()")
|
|
453
607
|
## Scroll with column select
|
|
454
608
|
if self.paginate:
|
|
455
|
-
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
|
|
456
610
|
end_index = min(start_index + self.items_per_page, len(self.indexed_items))
|
|
457
611
|
## Scroll
|
|
458
612
|
else:
|
|
459
|
-
scrolloff = self.items_per_page//2
|
|
460
|
-
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))
|
|
461
615
|
end_index = min(start_index + self.items_per_page, len(self.indexed_items))
|
|
462
616
|
if len(self.indexed_items) == 0: start_index, end_index = 0, 0
|
|
463
617
|
|
|
464
|
-
|
|
465
|
-
|
|
618
|
+
self.visible_rows = [v[1] for v in self.indexed_items[start_index:end_index]] if len(self.indexed_items) else self.items
|
|
619
|
+
# self.visible_rows_indices = [v[0] for v in self.indexed_items[start_index:end_index]] if len(self.indexed_items) else []
|
|
620
|
+
self.visible_rows_indices.clear()
|
|
621
|
+
self.visible_rows_indices.extend([v[0] for v in self.indexed_items[start_index:end_index]])
|
|
622
|
+
return self.visible_rows
|
|
466
623
|
|
|
467
624
|
def initialise_picker_state(self, reset_colours=False) -> None:
|
|
468
625
|
""" Initialise state variables for the picker. These are: debugging and colours. """
|
|
469
626
|
|
|
627
|
+
# Define global curses colours
|
|
470
628
|
if curses.has_colors() and self.colours != None:
|
|
471
|
-
# raise Exception("Terminal does not support color")
|
|
472
629
|
curses.start_color()
|
|
630
|
+
|
|
473
631
|
if reset_colours:
|
|
474
632
|
global COLOURS_SET
|
|
475
633
|
COLOURS_SET = False
|
|
476
634
|
self.colours_end = set_colours(pick=self.colour_theme_number, start=self.colours_start)
|
|
635
|
+
|
|
477
636
|
if curses.COLORS >= 255 and curses.COLOR_PAIRS >= 150:
|
|
478
637
|
self.colours_start = self.colours_start
|
|
479
638
|
self.notification_colours_start = self.colours_start+50
|
|
@@ -487,64 +646,43 @@ class Picker:
|
|
|
487
646
|
self.notification_colours_start = 0
|
|
488
647
|
self.help_colours_start = 0
|
|
489
648
|
|
|
649
|
+
self.colours = get_colours(self.colour_theme_number)
|
|
650
|
+
|
|
490
651
|
|
|
652
|
+
# Start logger
|
|
491
653
|
debug_levels = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL]
|
|
492
654
|
dbglvl = debug_levels[self.debug_level]
|
|
493
655
|
self.logger = setup_logger(name="picker_log", log_file="picker.log", log_enabled=self.debug, level =dbglvl)
|
|
494
656
|
self.logger.info(f"Initialiasing Picker.")
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
#
|
|
499
|
-
# logger = logging.getLogger(__file__)
|
|
500
|
-
# if self.debug_level == 0:
|
|
501
|
-
# logger = logging.getLogger()
|
|
502
|
-
# logger.disabled = True
|
|
503
|
-
# else:
|
|
504
|
-
#
|
|
505
|
-
# file_handler = logging.FileHandler(f"{self.title}.log", mode='w')
|
|
506
|
-
# formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s', '%m-%d-%Y %H:%M:%S')
|
|
507
|
-
# file_handler.setFormatter(formatter)
|
|
508
|
-
# logger.addHandler(file_handler)
|
|
509
|
-
# logger.setLevel(debug_levels[self.debug_level-1])
|
|
510
|
-
|
|
511
|
-
# logging.basicConfig(
|
|
512
|
-
# level=debug_levels[self.debug_level-1],
|
|
513
|
-
# format='%(asctime)s - %(levelname)s - %(message)s',
|
|
514
|
-
# datefmt='%m-%d-%Y %H:%M:%S',
|
|
515
|
-
# filename=f"{self.title}.log",
|
|
516
|
-
# filemode="w",
|
|
517
|
-
# )
|
|
518
|
-
#
|
|
519
|
-
# self.logger.info(f"Starging log. Log level {logger.getEffectiveLevel()}")
|
|
520
|
-
# self.logger.info(f"Starging log. Log level {repr(debug_levels)}, {self.debug_level}, {debug_levels[self.debug_level-1]}")
|
|
521
|
-
# self.notification(self.stdscr, f"Starging log. Log level {repr(debug_levels)}, {self.debug_level}, {debug_levels[self.debug_level-1]}")
|
|
522
|
-
# self.notification(self.stdscr, f"{__file__}")
|
|
523
|
-
|
|
524
|
-
## Logging level plan
|
|
525
|
-
# DEBUG: loop functions, draw screen, etc.
|
|
526
|
-
# INFO: main functions
|
|
527
|
-
# WARNING: any try-except fails
|
|
528
|
-
|
|
529
|
-
# No set_escdelay function on windows.
|
|
657
|
+
|
|
658
|
+
self.update_term_size()
|
|
659
|
+
|
|
660
|
+
# The curses implementation for some systems (e.g., windows) does not allow set_escdelay
|
|
530
661
|
try:
|
|
531
662
|
curses.set_escdelay(25)
|
|
532
663
|
except:
|
|
533
664
|
logging.warning("Error trying to set curses.set_escdelay")
|
|
534
665
|
|
|
535
|
-
# self.stdscr.clear()
|
|
536
|
-
# self.stdscr.refresh()
|
|
537
|
-
# self.draw_screen(self.indexed_items, self.highlights)
|
|
538
|
-
|
|
539
666
|
def initialise_variables(self, get_data: bool = False) -> None:
|
|
540
|
-
"""
|
|
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
|
+
"""
|
|
541
678
|
|
|
542
679
|
self.logger.info(f"function: initialise_variables()")
|
|
543
680
|
|
|
544
681
|
tracking = False
|
|
545
682
|
|
|
546
683
|
## Get data synchronously
|
|
547
|
-
if get_data
|
|
684
|
+
if get_data:
|
|
685
|
+
# Track cursor_pos and selections by ther id (row[self.id_column][col])
|
|
548
686
|
if self.track_entries_upon_refresh and len(self.items) > 0:
|
|
549
687
|
tracking = True
|
|
550
688
|
selected_indices = get_selected_indices(self.selections)
|
|
@@ -552,31 +690,41 @@ class Picker:
|
|
|
552
690
|
self.ids = [item[self.id_column] for i, item in enumerate(self.items) if i in selected_indices]
|
|
553
691
|
self.ids_tuples = [(i, item[self.id_column]) for i, item in enumerate(self.items) if i in selected_indices]
|
|
554
692
|
|
|
555
|
-
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:
|
|
556
694
|
self.cursor_pos_id = self.indexed_items[self.cursor_pos][1][self.id_column]
|
|
557
695
|
self.cursor_pos_prev = self.cursor_pos
|
|
558
|
-
|
|
559
|
-
self.items, self.header = self.refresh_function()
|
|
560
696
|
|
|
561
|
-
|
|
697
|
+
# Set the state of the threading event
|
|
698
|
+
# Though we are getting data synchronously, we ensure the correct state for self.getting_data
|
|
699
|
+
self.getting_data.clear()
|
|
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
|
+
)
|
|
707
|
+
|
|
562
708
|
|
|
709
|
+
# Ensure that an emtpy items object has the form [[]]
|
|
563
710
|
if self.items == []: self.items = [[]]
|
|
564
|
-
|
|
711
|
+
|
|
712
|
+
# Ensure that items is a List[List[Str]] object
|
|
565
713
|
if len(self.items) > 0 and not isinstance(self.items[0], list):
|
|
566
714
|
self.items = [[item] for item in self.items]
|
|
567
|
-
self.items = [[str(cell) for cell in row] for row in self.items]
|
|
715
|
+
# self.items = [[str(cell) for cell in row] for row in self.items]
|
|
568
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)
|
|
569
719
|
|
|
570
720
|
# Ensure that header is of the same length as the rows
|
|
571
721
|
if self.header and len(self.items) > 0 and len(self.header) != len(self.items[0]):
|
|
572
722
|
self.header = [str(self.header[i]) if i < len(self.header) else "" for i in range(len(self.items[0]))]
|
|
573
723
|
|
|
574
|
-
# Constants
|
|
575
|
-
# DEFAULT_ITEMS_PER_PAGE = os.get_terminal_size().lines - top_gap*2-2-int(bool(header))
|
|
576
|
-
|
|
577
724
|
self.calculate_section_sizes()
|
|
578
725
|
|
|
579
|
-
|
|
726
|
+
|
|
727
|
+
# Ensure that the selection-tracking variables are the correct shape
|
|
580
728
|
if len(self.selections) != len(self.items):
|
|
581
729
|
self.selections = {i : False if i not in self.selections else bool(self.selections[i]) for i in range(len(self.items))}
|
|
582
730
|
|
|
@@ -587,31 +735,34 @@ class Picker:
|
|
|
587
735
|
self.cell_selections = {}
|
|
588
736
|
self.selected_cells_by_row = {}
|
|
589
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))])
|
|
590
742
|
|
|
591
743
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if
|
|
597
|
-
self.
|
|
598
|
-
|
|
599
|
-
self.
|
|
600
|
-
|
|
601
|
-
self.editable_columns = self.
|
|
602
|
-
|
|
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]):
|
|
603
756
|
self.column_indices = self.column_indices + [i for i in range(len(self.column_indices), len(self.items[0]))]
|
|
604
757
|
|
|
605
758
|
|
|
606
759
|
|
|
607
|
-
#
|
|
608
|
-
# self.indexed_items = list(enumerate(items2))
|
|
760
|
+
# Create an indexed list of the items which will track the visible rows
|
|
609
761
|
if self.items == [[]]: self.indexed_items = []
|
|
610
762
|
else: self.indexed_items = list(enumerate(self.items))
|
|
611
763
|
|
|
612
|
-
#
|
|
764
|
+
# Apply the filter query
|
|
613
765
|
if self.filter_query:
|
|
614
|
-
# prev_index = self.indexed_items[cursor_pos][0] if len(self.indexed_items)>0 else 0
|
|
615
766
|
# prev_index = self.indexed_items[cursor_pos][0] if len(self.indexed_items)>0 else 0
|
|
616
767
|
self.indexed_items = filter_items(self.items, self.indexed_items, self.filter_query)
|
|
617
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)
|
|
@@ -627,22 +778,30 @@ class Picker:
|
|
|
627
778
|
)
|
|
628
779
|
if return_val:
|
|
629
780
|
self.cursor_pos, self.search_index, self.search_count, self.highlights = tmp_cursor, tmp_index, tmp_count, tmp_highlights
|
|
630
|
-
|
|
781
|
+
|
|
782
|
+
# Apply the current sort method
|
|
631
783
|
if len(self.indexed_items) > 0:
|
|
632
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
|
|
633
785
|
|
|
634
786
|
|
|
635
|
-
#
|
|
636
|
-
## Move to a selectable row (if applicable)
|
|
787
|
+
# If we have more unselectable indices than rows, clear the unselectable_indices
|
|
637
788
|
if len(self.items) <= len(self.unselectable_indices): self.unselectable_indices = []
|
|
638
|
-
new_pos = (self.cursor_pos)%len(self.items)
|
|
639
|
-
while new_pos in self.unselectable_indices and new_pos != self.cursor_pos:
|
|
640
|
-
new_pos = (new_pos + 1) % len(self.items)
|
|
641
789
|
|
|
642
|
-
|
|
643
|
-
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={})
|
|
644
804
|
|
|
645
|
-
# Sheets and files
|
|
646
805
|
if len(self.sheet_states) < len(self.sheets):
|
|
647
806
|
self.sheet_states += [{} for _ in range(len(self.sheets) - len(self.sheet_states))]
|
|
648
807
|
if len(self.sheets):
|
|
@@ -650,15 +809,16 @@ class Picker:
|
|
|
650
809
|
self.sheet_index = 0
|
|
651
810
|
self.sheet_name = self.sheets[self.sheet_index]
|
|
652
811
|
|
|
653
|
-
|
|
654
|
-
|
|
812
|
+
# Initialise files
|
|
813
|
+
extend_list_to_length(self.loaded_file_states, length=len(self.loaded_files), default_value={})
|
|
655
814
|
if len(self.loaded_files):
|
|
656
815
|
if self.loaded_file_index >= len(self.loaded_files):
|
|
657
816
|
self.loaded_file_index = 0
|
|
658
817
|
self.loaded_file = self.loaded_files[self.loaded_file_index]
|
|
659
818
|
|
|
660
|
-
|
|
661
|
-
# 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.
|
|
662
822
|
if self.track_entries_upon_refresh and (self.data_ready or tracking) and len(self.items) > 1:
|
|
663
823
|
selected_indices = []
|
|
664
824
|
all_ids = [item[self.id_column] for item in self.items]
|
|
@@ -681,6 +841,8 @@ class Picker:
|
|
|
681
841
|
|
|
682
842
|
|
|
683
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
|
|
684
846
|
if len(self.indexed_items):
|
|
685
847
|
if self.pin_cursor:
|
|
686
848
|
self.cursor_pos = min(self.cursor_pos_prev, len(self.indexed_items)-1)
|
|
@@ -692,7 +854,12 @@ class Picker:
|
|
|
692
854
|
else:
|
|
693
855
|
self.cursor_pos = 0
|
|
694
856
|
|
|
695
|
-
|
|
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
|
|
696
863
|
|
|
697
864
|
|
|
698
865
|
|
|
@@ -739,13 +906,13 @@ class Picker:
|
|
|
739
906
|
|
|
740
907
|
"""
|
|
741
908
|
self.logger.debug("function: test_screen_size()")
|
|
742
|
-
|
|
909
|
+
self.update_term_size()
|
|
743
910
|
## Terminal too small to display Picker
|
|
744
|
-
if
|
|
745
|
-
if (self.show_footer or self.footer_string) and (
|
|
746
|
-
self.stdscr.addstr(
|
|
747
|
-
self.stdscr.addstr(
|
|
748
|
-
self.stdscr.addstr(
|
|
911
|
+
if self.term_h<3 or self.term_w<len("Terminal"): return False
|
|
912
|
+
if (self.show_footer or self.footer_string) and (self.term_h<12 or self.term_w<35) or (self.term_h<12 and self.term_w<10):
|
|
913
|
+
self.stdscr.addstr(self.term_h//2-1, (self.term_w-len("Terminal"))//2, "Terminal")
|
|
914
|
+
self.stdscr.addstr(self.term_h//2, (self.term_w-len("Too"))//2, "Too")
|
|
915
|
+
self.stdscr.addstr(self.term_h//2+1, (self.term_w-len("Small"))//2, "Small")
|
|
749
916
|
return False
|
|
750
917
|
return True
|
|
751
918
|
|
|
@@ -758,28 +925,29 @@ class Picker:
|
|
|
758
925
|
|
|
759
926
|
if type(message) == type(""): message = [message]
|
|
760
927
|
|
|
761
|
-
|
|
762
|
-
if len(message) >
|
|
763
|
-
else: start_y = (
|
|
928
|
+
self.update_term_size()
|
|
929
|
+
if len(message) > self.term_h: start_y = 0
|
|
930
|
+
else: start_y = (self.term_h-len(message))//2
|
|
764
931
|
|
|
765
932
|
for i in range(len(message)):
|
|
766
933
|
try:
|
|
767
934
|
s = message[i]
|
|
768
|
-
if len(s) >
|
|
769
|
-
self.stdscr.addstr(start_y+i, (
|
|
935
|
+
if len(s) > self.term_w: s = s[:self.term_w-2]
|
|
936
|
+
self.stdscr.addstr(start_y+i, (self.term_w-len(s))//2, s, curses.color_pair(2))
|
|
770
937
|
except:
|
|
771
938
|
pass
|
|
772
939
|
self.stdscr.refresh()
|
|
773
940
|
|
|
774
|
-
def draw_screen(self,
|
|
941
|
+
def draw_screen(self, clear: bool = True) -> None:
|
|
775
942
|
""" Try-except wrapper for the draw_screen_ function. """
|
|
776
943
|
try:
|
|
777
|
-
self.draw_screen_(
|
|
944
|
+
self.draw_screen_(clear)
|
|
778
945
|
except Exception as e:
|
|
779
946
|
self.logger.warning(f"self.draw_screen_() error. {e}")
|
|
780
|
-
|
|
947
|
+
finally:
|
|
948
|
+
self.stdscr.refresh()
|
|
781
949
|
|
|
782
|
-
def draw_screen_(self,
|
|
950
|
+
def draw_screen_(self, clear: bool = True) -> None:
|
|
783
951
|
""" Draw Picker screen. """
|
|
784
952
|
|
|
785
953
|
self.logger.debug("Draw screen.")
|
|
@@ -787,16 +955,12 @@ class Picker:
|
|
|
787
955
|
if clear:
|
|
788
956
|
self.stdscr.erase()
|
|
789
957
|
|
|
790
|
-
|
|
791
|
-
self.term_h, self.term_w = self.stdscr.getmaxyx()
|
|
792
|
-
if self.split_right and len(self.right_panes):
|
|
793
|
-
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
794
|
-
self.rows_w, self.rows_h = int(self.term_w*proportion), self.term_h
|
|
795
|
-
else:
|
|
796
|
-
self.rows_w, self.rows_h = self.term_w, self.term_h
|
|
958
|
+
self.update_term_size()
|
|
797
959
|
|
|
798
|
-
#
|
|
960
|
+
# Determine footer size
|
|
799
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.
|
|
800
964
|
self.calculate_section_sizes()
|
|
801
965
|
|
|
802
966
|
# Test if the terminal is of a sufficient size to display the picker
|
|
@@ -814,27 +978,18 @@ class Picker:
|
|
|
814
978
|
end_index = min(start_index + self.items_per_page, len(self.indexed_items))
|
|
815
979
|
if len(self.indexed_items) == 0: start_index, end_index = 0, 0
|
|
816
980
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
self.column_widths = get_column_widths(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)
|
|
823
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
824
|
-
visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
|
|
825
|
-
|
|
826
|
-
# Determine the number of items_per_page, top_size and bottom_size
|
|
827
|
-
# self.calculate_section_sizes()
|
|
828
|
-
|
|
829
|
-
# top_space = self.top_gap
|
|
981
|
+
self.get_visible_rows()
|
|
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)
|
|
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)
|
|
985
|
+
|
|
830
986
|
|
|
831
|
-
## Display title
|
|
987
|
+
## Display title
|
|
832
988
|
if self.title:
|
|
833
989
|
padded_title = f" {self.title.strip()} "
|
|
834
990
|
self.stdscr.addstr(self.top_gap, 0, f"{' ':^{self.term_w}}", curses.color_pair(self.colours_start+16))
|
|
835
991
|
title_x = (self.term_w-wcswidth(padded_title))//2
|
|
836
992
|
self.stdscr.addstr(self.top_gap, title_x, padded_title, curses.color_pair(self.colours_start+16) | curses.A_BOLD)
|
|
837
|
-
# top_space += 1
|
|
838
993
|
|
|
839
994
|
## Display modes
|
|
840
995
|
if self.display_modes and self.modes not in [[{}], []]:
|
|
@@ -856,7 +1011,6 @@ class Picker:
|
|
|
856
1011
|
else:
|
|
857
1012
|
self.stdscr.addstr(self.top_gap+1, xmode, mode_str, curses.color_pair(self.colours_start+15) | curses.A_UNDERLINE)
|
|
858
1013
|
xmode += split_space+mode_widths[i]
|
|
859
|
-
# top_space += 1
|
|
860
1014
|
|
|
861
1015
|
## Display header
|
|
862
1016
|
if self.header and self.show_header:
|
|
@@ -874,137 +1028,129 @@ class Picker:
|
|
|
874
1028
|
|
|
875
1029
|
|
|
876
1030
|
header_str += f"{col_str:^{self.column_widths[i]-len(number)}}"
|
|
877
|
-
|
|
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)
|
|
878
1036
|
|
|
879
1037
|
header_str = header_str[self.leftmost_char:]
|
|
1038
|
+
header_str = header_str[:header_str_w]
|
|
880
1039
|
header_ypos = self.top_gap + bool(self.title) + bool(self.display_modes and self.modes)
|
|
881
|
-
|
|
882
|
-
|
|
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)
|
|
883
1046
|
|
|
884
1047
|
# Highlight sort column
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
# Start of the cell is on the screen, but the end of the cell is not
|
|
906
|
-
else:
|
|
907
|
-
overflow = (len(up_to_selected_col)+len(highlighted_col_str)) - (self.leftmost_char+self.rows_w - self.startx)
|
|
908
|
-
disp_str = highlighted_col_str[:-overflow]
|
|
909
|
-
|
|
910
|
-
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
911
|
-
# Start of the cell is to the right of the screen
|
|
912
|
-
elif self.leftmost_char+self.rows_w <= len(up_to_selected_col):
|
|
913
|
-
pass
|
|
914
|
-
# The end of the cell is on the screen, the start of the cell is not
|
|
915
|
-
elif 0 <= len(up_to_selected_col)+col_width - self.leftmost_char <= self.rows_w :
|
|
916
|
-
x_pos = self.startx
|
|
917
|
-
beg = self.leftmost_char - len(up_to_selected_col)
|
|
918
|
-
disp_str = highlighted_col_str[beg:]
|
|
919
|
-
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
920
|
-
# The middle of the cell is on the screen, the start and end of the cell are not
|
|
921
|
-
elif self.leftmost_char <= len(up_to_selected_col) + col_width//2 <= self.leftmost_char+self.rows_w:
|
|
922
|
-
beg = self.leftmost_char - len(up_to_selected_col)
|
|
923
|
-
overflow = (len(up_to_selected_col)+len(highlighted_col_str)) - (self.leftmost_char+self.rows_w)
|
|
924
|
-
disp_str = highlighted_col_str[beg:-overflow]
|
|
925
|
-
|
|
926
|
-
x_pos = self.startx
|
|
927
|
-
self.stdscr.addstr(header_ypos, x_pos , disp_str, colour)
|
|
928
|
-
# The cell is to the left of the screen
|
|
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
|
|
1062
|
+
|
|
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
|
|
1066
|
+
|
|
1067
|
+
# Start of the cell is on the screen, but the end of the cell is not
|
|
929
1068
|
else:
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
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
|
|
1100
|
+
|
|
957
1101
|
# Display row header
|
|
958
1102
|
if self.show_row_header:
|
|
959
1103
|
for idx in range(start_index, end_index):
|
|
960
1104
|
y = idx - start_index + self.top_space
|
|
961
1105
|
if idx == self.cursor_pos:
|
|
962
|
-
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)
|
|
963
1107
|
else:
|
|
964
|
-
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)
|
|
965
1109
|
|
|
966
1110
|
|
|
967
1111
|
def highlight_cell(row: int, col:int, visible_column_widths, colour_pair_number: int = 5, bold: bool = False, y:int = 0):
|
|
968
|
-
|
|
1112
|
+
|
|
969
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
|
|
970
1115
|
# cell_width = self.column_widths[self.selected_column]
|
|
971
1116
|
cell_width = visible_column_widths[col] + len(self.separator)
|
|
972
|
-
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)
|
|
973
1118
|
|
|
974
1119
|
if bold:
|
|
975
1120
|
colour = curses.color_pair(self.colours_start+colour_pair_number) | curses.A_BOLD
|
|
976
1121
|
else:
|
|
977
1122
|
colour = curses.color_pair(self.colours_start+colour_pair_number)
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
if
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
else:
|
|
985
|
-
cell_value = self.indexed_items[row][1][col][:self.column_widths[col]] + self.separator
|
|
986
|
-
# cell_value = cell_value[:min(cell_width, cell_max_width)-len(self.separator)]
|
|
987
|
-
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
988
|
-
# cell_value = cell_value + self.separator
|
|
989
|
-
# cell_value = cell_value
|
|
990
|
-
cell_value = truncate_to_display_width(cell_value, min(cell_width, cell_max_width), self.centre_in_cols, self.unicode_char_width)
|
|
991
|
-
self.stdscr.addstr(y, cell_pos, cell_value, colour)
|
|
992
|
-
# Part of the cell is on screen
|
|
993
|
-
elif self.startx <= cell_pos+cell_width and cell_pos < (self.rows_w):
|
|
994
|
-
cell_start = self.startx - cell_pos
|
|
995
|
-
# self.stdscr.addstr(y, self.startx, ' '*(cell_width-cell_start), curses.color_pair(self.colours_start+colour_pair_number))
|
|
996
|
-
cell_value = self.indexed_items[row][1][col]
|
|
997
|
-
cell_value = f"{cell_value:^{self.column_widths[col]}}"
|
|
998
|
-
|
|
999
|
-
cell_value = cell_value[cell_start:visible_column_widths[col]][:self.rows_w-self.startx]
|
|
1000
|
-
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
|
|
1001
1129
|
else:
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
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:
|
|
1005
1150
|
pass
|
|
1006
1151
|
|
|
1007
1152
|
|
|
1153
|
+
|
|
1008
1154
|
def sort_highlights(highlights):
|
|
1009
1155
|
"""
|
|
1010
1156
|
Sort highlights into lists based on their display level.
|
|
@@ -1045,10 +1191,11 @@ class Picker:
|
|
|
1045
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)
|
|
1046
1192
|
if not match: continue
|
|
1047
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)
|
|
1048
1195
|
|
|
1049
1196
|
## We want to search the non-centred values but highlight the centred values.
|
|
1050
1197
|
if self.centre_in_cols:
|
|
1051
|
-
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)
|
|
1052
1199
|
field_start += (len(tmp) - len(tmp.lstrip()))
|
|
1053
1200
|
|
|
1054
1201
|
highlight_start = field_start + match.start()
|
|
@@ -1059,7 +1206,7 @@ class Picker:
|
|
|
1059
1206
|
continue
|
|
1060
1207
|
highlight_start -= self.leftmost_char
|
|
1061
1208
|
highlight_end -= self.leftmost_char
|
|
1062
|
-
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)
|
|
1063
1210
|
except:
|
|
1064
1211
|
pass
|
|
1065
1212
|
|
|
@@ -1075,6 +1222,7 @@ class Picker:
|
|
|
1075
1222
|
l0_highlights, l1_highlights, l2_highlights = sort_highlights(self.highlights)
|
|
1076
1223
|
|
|
1077
1224
|
|
|
1225
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
1078
1226
|
for idx in range(start_index, end_index):
|
|
1079
1227
|
item = self.indexed_items[idx]
|
|
1080
1228
|
y = idx - start_index + self.top_space
|
|
@@ -1086,23 +1234,26 @@ class Picker:
|
|
|
1086
1234
|
# rowstr off screen
|
|
1087
1235
|
# if self.leftmost_char > len(row_str_orig):
|
|
1088
1236
|
# trunc_width = 0
|
|
1089
|
-
if self.leftmost_char + (self.rows_w-self.
|
|
1090
|
-
|
|
1091
|
-
elif self.leftmost_char <= len(row_str_orig):
|
|
1092
|
-
|
|
1093
|
-
else:
|
|
1094
|
-
|
|
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))
|
|
1095
1246
|
|
|
1096
1247
|
row_str = truncate_to_display_width(row_str_left_adj, trunc_width, self.unicode_char_width)
|
|
1097
1248
|
# row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))[self.leftmost_char:]
|
|
1098
1249
|
|
|
1099
1250
|
## Display the standard row
|
|
1100
|
-
self.stdscr.addstr(y, self.startx, row_str
|
|
1101
|
-
|
|
1251
|
+
self.stdscr.addstr(y, self.startx, row_str, curses.color_pair(self.colours_start+2))
|
|
1252
|
+
|
|
1102
1253
|
|
|
1103
1254
|
## Highlight column
|
|
1104
1255
|
if self.crosshair_cursor:
|
|
1105
|
-
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)
|
|
1106
1257
|
if idx == self.cursor_pos:
|
|
1107
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))
|
|
1108
1259
|
|
|
@@ -1116,25 +1267,25 @@ class Picker:
|
|
|
1116
1267
|
# self.selected_cells_by_row = get_selected_cells_by_row(self.cell_selections)
|
|
1117
1268
|
if item[0] in self.selected_cells_by_row:
|
|
1118
1269
|
for j in self.selected_cells_by_row[item[0]]:
|
|
1119
|
-
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)
|
|
1120
1271
|
|
|
1121
1272
|
# Visually selected
|
|
1122
1273
|
if self.is_selecting:
|
|
1123
1274
|
if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
|
|
1124
1275
|
x_interval = range(min(self.start_selection_col, self.selected_column), max(self.start_selection_col, self.selected_column)+1)
|
|
1125
1276
|
for col in x_interval:
|
|
1126
|
-
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)
|
|
1127
1278
|
|
|
1128
1279
|
# Visually deslected
|
|
1129
1280
|
if self.is_deselecting:
|
|
1130
1281
|
if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
|
|
1131
1282
|
x_interval = range(min(self.start_selection_col, self.selected_column), max(self.start_selection_col, self.selected_column)+1)
|
|
1132
1283
|
for col in x_interval:
|
|
1133
|
-
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)
|
|
1134
1285
|
# Higlight cursor row and selected rows
|
|
1135
1286
|
elif self.highlight_full_row:
|
|
1136
1287
|
if self.selections[item[0]]:
|
|
1137
|
-
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)
|
|
1138
1289
|
|
|
1139
1290
|
# Visually selected
|
|
1140
1291
|
if self.is_selecting:
|
|
@@ -1147,16 +1298,30 @@ class Picker:
|
|
|
1147
1298
|
|
|
1148
1299
|
# Highlight the cursor row and the first char of the selected rows.
|
|
1149
1300
|
else:
|
|
1150
|
-
if self.
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
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]]:
|
|
1155
1316
|
self.stdscr.addstr(y, max(self.startx-2,0), ' ', curses.color_pair(self.colours_start+1))
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
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))
|
|
1160
1325
|
|
|
1161
1326
|
if not self.highlights_hide:
|
|
1162
1327
|
draw_highlights(l1_highlights, idx, y, item)
|
|
@@ -1166,10 +1331,10 @@ class Picker:
|
|
|
1166
1331
|
# Draw cursor
|
|
1167
1332
|
if idx == self.cursor_pos:
|
|
1168
1333
|
if self.cell_cursor:
|
|
1169
|
-
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)
|
|
1170
1335
|
else:
|
|
1171
|
-
self.stdscr.addstr(y, self.startx, row_str[:
|
|
1172
|
-
|
|
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)
|
|
1337
|
+
|
|
1173
1338
|
if not self.highlights_hide:
|
|
1174
1339
|
draw_highlights(l2_highlights, idx, y, item)
|
|
1175
1340
|
|
|
@@ -1188,7 +1353,8 @@ class Picker:
|
|
|
1188
1353
|
scroll_bar_length = max(1, scroll_bar_length)
|
|
1189
1354
|
for i in range(scroll_bar_length):
|
|
1190
1355
|
v = max(self.top_space+int(bool(self.header)), scroll_bar_start-scroll_bar_length//2)
|
|
1191
|
-
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))
|
|
1192
1358
|
|
|
1193
1359
|
# Display refresh symbol
|
|
1194
1360
|
if self.auto_refresh:
|
|
@@ -1197,10 +1363,14 @@ class Picker:
|
|
|
1197
1363
|
else:
|
|
1198
1364
|
self.stdscr.addstr(0,self.term_w-3," ", curses.color_pair(self.colours_start+23) | curses.A_BOLD)
|
|
1199
1365
|
|
|
1366
|
+
# Display data fetch symbol
|
|
1367
|
+
if not self.getting_data.is_set():
|
|
1368
|
+
self.stdscr.addstr(0,self.term_w-3," ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
|
|
1369
|
+
# self.stdscr.addstr(0,self.term_w-6,"⏳", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
|
|
1370
|
+
|
|
1200
1371
|
## Display footer
|
|
1201
1372
|
if self.show_footer:
|
|
1202
1373
|
# self.footer = NoFooter(self.stdscr, self.colours_start, self.get_function_data)
|
|
1203
|
-
h, w = self.stdscr.getmaxyx()
|
|
1204
1374
|
try:
|
|
1205
1375
|
self.footer.draw(self.term_h, self.term_w)
|
|
1206
1376
|
except:
|
|
@@ -1213,41 +1383,83 @@ class Picker:
|
|
|
1213
1383
|
|
|
1214
1384
|
if self.split_right and len(self.right_panes):
|
|
1215
1385
|
# If we need to refresh the data then do so.
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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()
|
|
1221
1392
|
|
|
1222
|
-
draw_pane =
|
|
1223
|
-
data =
|
|
1393
|
+
draw_pane = pane["display"]
|
|
1394
|
+
data = pane["data"]
|
|
1395
|
+
# pane_width = int(pane["proportion"]*self.term_w)
|
|
1224
1396
|
|
|
1225
1397
|
draw_pane(
|
|
1226
1398
|
self.stdscr,
|
|
1227
|
-
x = self.rows_w,
|
|
1399
|
+
x = self.rows_w + self.startx - self.left_gutter_width,
|
|
1228
1400
|
y = self.top_space - int(bool(self.show_header and self.header)),
|
|
1229
|
-
w = self.
|
|
1401
|
+
w = self.right_pane_width,
|
|
1230
1402
|
h = self.items_per_page + int(bool(self.show_header and self.header)),
|
|
1231
1403
|
state = self.get_function_data(),
|
|
1232
1404
|
row = self.indexed_items[self.cursor_pos] if self.indexed_items else [],
|
|
1233
1405
|
cell = self.indexed_items[self.cursor_pos][1][self.selected_column] if self.indexed_items else "",
|
|
1234
1406
|
data=data,
|
|
1235
1407
|
)
|
|
1236
|
-
|
|
1237
|
-
|
|
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,
|
|
1426
|
+
h = self.items_per_page + int(bool(self.show_header and self.header)),
|
|
1427
|
+
state = self.get_function_data(),
|
|
1428
|
+
row = self.indexed_items[self.cursor_pos] if self.indexed_items else [],
|
|
1429
|
+
cell = self.indexed_items[self.cursor_pos][1][self.selected_column] if self.indexed_items else "",
|
|
1430
|
+
data=data,
|
|
1431
|
+
)
|
|
1432
|
+
|
|
1238
1433
|
## Display infobox
|
|
1239
1434
|
if self.display_infobox:
|
|
1240
1435
|
self.infobox(self.stdscr, message=self.infobox_items, title=self.infobox_title)
|
|
1241
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
|
|
1242
1437
|
|
|
1243
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()
|
|
1244
1454
|
|
|
1245
1455
|
def infobox(self, stdscr: curses.window, message: str ="", title: str ="Infobox", colours_end: int = 0, duration: int = 4) -> curses.window:
|
|
1246
1456
|
""" Display non-interactive infobox window. """
|
|
1247
1457
|
|
|
1248
1458
|
self.logger.info(f"function: infobox()")
|
|
1249
|
-
|
|
1250
|
-
|
|
1459
|
+
self.update_term_size()
|
|
1460
|
+
|
|
1461
|
+
|
|
1462
|
+
notification_width, notification_height = self.term_w//2, 3*self.term_h//5
|
|
1251
1463
|
message_width = notification_width-5
|
|
1252
1464
|
|
|
1253
1465
|
if not message: message = "!!"
|
|
@@ -1263,9 +1475,9 @@ class Picker:
|
|
|
1263
1475
|
if len(submenu_items) > notification_height - 2:
|
|
1264
1476
|
submenu_items = submenu_items[:notification_height-3] + [f"{'....':^{notification_width}}"]
|
|
1265
1477
|
while True:
|
|
1266
|
-
h, w = stdscr.getmaxyx()
|
|
1267
1478
|
|
|
1268
|
-
|
|
1479
|
+
self.update_term_size()
|
|
1480
|
+
submenu_win = curses.newwin(notification_height, notification_width, 3, self.term_w - (notification_width+4))
|
|
1269
1481
|
infobox_data = {
|
|
1270
1482
|
"items": submenu_items,
|
|
1271
1483
|
"colours": notification_colours,
|
|
@@ -1280,6 +1492,7 @@ class Picker:
|
|
|
1280
1492
|
"reset_colours": False,
|
|
1281
1493
|
"cell_cursor": False,
|
|
1282
1494
|
"split_right": False,
|
|
1495
|
+
"split_left": False,
|
|
1283
1496
|
"crosshair_cursor": False,
|
|
1284
1497
|
}
|
|
1285
1498
|
|
|
@@ -1293,113 +1506,127 @@ class Picker:
|
|
|
1293
1506
|
self.logger.debug(f"function: get_function_data()")
|
|
1294
1507
|
""" Returns a dict of the main variables needed to restore the state of list_pikcer. """
|
|
1295
1508
|
function_data = {
|
|
1296
|
-
"
|
|
1297
|
-
"
|
|
1298
|
-
"
|
|
1299
|
-
"
|
|
1300
|
-
"
|
|
1301
|
-
"
|
|
1302
|
-
"
|
|
1303
|
-
"
|
|
1304
|
-
"
|
|
1305
|
-
"
|
|
1306
|
-
"
|
|
1307
|
-
"
|
|
1308
|
-
"
|
|
1309
|
-
"
|
|
1310
|
-
"
|
|
1311
|
-
"
|
|
1312
|
-
"
|
|
1313
|
-
"
|
|
1314
|
-
"
|
|
1315
|
-
"
|
|
1316
|
-
"
|
|
1317
|
-
"
|
|
1318
|
-
"
|
|
1319
|
-
"
|
|
1320
|
-
"
|
|
1321
|
-
"
|
|
1322
|
-
"
|
|
1323
|
-
"
|
|
1324
|
-
"
|
|
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
|
-
|
|
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,
|
|
1403
1630
|
}
|
|
1404
1631
|
return function_data
|
|
1405
1632
|
|
|
@@ -1435,6 +1662,9 @@ class Picker:
|
|
|
1435
1662
|
"centre_in_cols",
|
|
1436
1663
|
"centre_in_terminal",
|
|
1437
1664
|
"split_right",
|
|
1665
|
+
"left_pane_index",
|
|
1666
|
+
"split_left",
|
|
1667
|
+
"left_pane_index",
|
|
1438
1668
|
]
|
|
1439
1669
|
|
|
1440
1670
|
for var in variables:
|
|
@@ -1449,15 +1679,6 @@ class Picker:
|
|
|
1449
1679
|
self.initialise_picker_state(reset_colours=reset_colours)
|
|
1450
1680
|
|
|
1451
1681
|
self.initialise_variables()
|
|
1452
|
-
# if "colour_theme_number" in function_data:
|
|
1453
|
-
# global COLOURS_SET
|
|
1454
|
-
# COLOURS_SET = False
|
|
1455
|
-
# colours_end = set_colours(pick=self.colour_theme_number, start=self.colours_start)
|
|
1456
|
-
|
|
1457
|
-
# if "items" in function_data: self.items = function_data["items"]
|
|
1458
|
-
# if "header" in function_data: self.header = function_data["header"]
|
|
1459
|
-
# self.indexed_items = function_data["indexed_items"] if "indexed_items" in function_data else []
|
|
1460
|
-
|
|
1461
1682
|
|
|
1462
1683
|
|
|
1463
1684
|
def delete_entries(self) -> None:
|
|
@@ -1475,7 +1696,7 @@ class Picker:
|
|
|
1475
1696
|
self.selections = {i:False for i in range(len(self.indexed_items))}
|
|
1476
1697
|
self.cursor_pos = min(self.cursor_pos, len(self.indexed_items)-1)
|
|
1477
1698
|
self.initialise_variables()
|
|
1478
|
-
self.draw_screen(
|
|
1699
|
+
self.draw_screen()
|
|
1479
1700
|
|
|
1480
1701
|
|
|
1481
1702
|
def choose_option(
|
|
@@ -1528,35 +1749,134 @@ class Picker:
|
|
|
1528
1749
|
"number_columns": False,
|
|
1529
1750
|
"reset_colours": False,
|
|
1530
1751
|
"split_right": False,
|
|
1752
|
+
"split_left": False,
|
|
1531
1753
|
"cell_cursor": False,
|
|
1532
1754
|
"crosshair_cursor": False,
|
|
1755
|
+
"header_separator": " │",
|
|
1533
1756
|
}
|
|
1534
1757
|
while True:
|
|
1535
|
-
|
|
1758
|
+
self.update_term_size()
|
|
1536
1759
|
|
|
1537
1760
|
choose_opts_widths = get_column_widths(options, unicode_char_width=self.unicode_char_width)
|
|
1538
|
-
window_width = min(max(sum(choose_opts_widths) + 6, 50) + 6,
|
|
1539
|
-
window_height = min(
|
|
1761
|
+
window_width = min(max(sum(choose_opts_widths) + 6, 50) + 6, self.term_w)
|
|
1762
|
+
window_height = min(self.term_h//2, max(6, len(options)+3))
|
|
1540
1763
|
|
|
1541
|
-
submenu_win = curses.newwin(window_height, window_width, (
|
|
1764
|
+
submenu_win = curses.newwin(window_height, window_width, (self.term_h-window_height)//2, (self.term_w-window_width)//2)
|
|
1542
1765
|
submenu_win.keypad(True)
|
|
1766
|
+
option_picker_data["screen_size_function"] = lambda stdscr: (window_height, window_width)
|
|
1543
1767
|
OptionPicker = Picker(submenu_win, **option_picker_data)
|
|
1544
1768
|
s, o, f = OptionPicker.run()
|
|
1545
1769
|
|
|
1546
1770
|
if o == "refresh":
|
|
1547
|
-
self.draw_screen(
|
|
1771
|
+
self.draw_screen()
|
|
1548
1772
|
continue
|
|
1549
1773
|
if s:
|
|
1550
1774
|
return {x: options[x] for x in s}, o, f
|
|
1551
1775
|
return {}, "", f
|
|
1552
1776
|
|
|
1553
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
|
|
1554
1874
|
|
|
1555
1875
|
def notification(self, stdscr: curses.window, message: str="", title:str="Notification", colours_end: int=0, duration:int=4) -> None:
|
|
1556
1876
|
|
|
1557
1877
|
self.logger.info(f"function: notification()")
|
|
1558
1878
|
""" Notification box. """
|
|
1559
|
-
notification_width, notification_height = 50, 7
|
|
1879
|
+
notification_width, notification_height = min(self.term_w-4, 50), 7
|
|
1560
1880
|
message_width = notification_width-5
|
|
1561
1881
|
|
|
1562
1882
|
if not message: message = "!!"
|
|
@@ -1569,9 +1889,9 @@ class Picker:
|
|
|
1569
1889
|
27: ord('q')
|
|
1570
1890
|
}
|
|
1571
1891
|
while True:
|
|
1572
|
-
|
|
1892
|
+
self.update_term_size()
|
|
1573
1893
|
|
|
1574
|
-
submenu_win = curses.newwin(notification_height, notification_width, 3,
|
|
1894
|
+
submenu_win = curses.newwin(notification_height, notification_width, 3, self.term_w - (notification_width+2))
|
|
1575
1895
|
notification_data = {
|
|
1576
1896
|
"items": submenu_items,
|
|
1577
1897
|
"title": title,
|
|
@@ -1588,9 +1908,11 @@ class Picker:
|
|
|
1588
1908
|
"cancel_is_back": True,
|
|
1589
1909
|
"reset_colours": False,
|
|
1590
1910
|
"split_right": False,
|
|
1911
|
+
"split_left": False,
|
|
1591
1912
|
"cell_cursor": False,
|
|
1592
1913
|
"crosshair_cursor": False,
|
|
1593
|
-
|
|
1914
|
+
"show_header": False,
|
|
1915
|
+
"screen_size_function": lambda stdscr: (notification_height, notification_width),
|
|
1594
1916
|
}
|
|
1595
1917
|
OptionPicker = Picker(submenu_win, **notification_data)
|
|
1596
1918
|
s, o, f = OptionPicker.run()
|
|
@@ -1601,7 +1923,7 @@ class Picker:
|
|
|
1601
1923
|
del submenu_win
|
|
1602
1924
|
stdscr.clear()
|
|
1603
1925
|
stdscr.refresh()
|
|
1604
|
-
self.draw_screen(
|
|
1926
|
+
self.draw_screen()
|
|
1605
1927
|
# set_colours(colours=get_colours(0))
|
|
1606
1928
|
|
|
1607
1929
|
def toggle_column_visibility(self, col_index:int) -> None:
|
|
@@ -1708,7 +2030,7 @@ class Picker:
|
|
|
1708
2030
|
elif setting == "file_prev":
|
|
1709
2031
|
self.command_stack.append(Command("setting", self.user_settings))
|
|
1710
2032
|
self.switch_file(increment=-1)
|
|
1711
|
-
# self.draw_screen(
|
|
2033
|
+
# self.draw_screen()
|
|
1712
2034
|
# self.stdscr.refresh()
|
|
1713
2035
|
|
|
1714
2036
|
elif setting == "sheet_next":
|
|
@@ -1727,14 +2049,24 @@ class Picker:
|
|
|
1727
2049
|
self.footer_style = (self.footer_style+1)%len(self.footer_options)
|
|
1728
2050
|
self.footer = self.footer_options[self.footer_style]
|
|
1729
2051
|
self.initialise_variables()
|
|
1730
|
-
elif setting == "
|
|
2052
|
+
elif setting == "rpane":
|
|
1731
2053
|
self.toggle_right_pane()
|
|
1732
2054
|
|
|
1733
|
-
elif setting == "
|
|
2055
|
+
elif setting == "rpane_cycle":
|
|
1734
2056
|
self.cycle_right_pane()
|
|
1735
2057
|
|
|
2058
|
+
elif setting == "lpane":
|
|
2059
|
+
self.toggle_left_pane()
|
|
2060
|
+
|
|
2061
|
+
elif setting == "lpane_cycle":
|
|
2062
|
+
self.cycle_left_pane()
|
|
2063
|
+
|
|
1736
2064
|
elif setting.startswith("cwd="):
|
|
1737
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)
|
|
1738
2070
|
elif setting.startswith("hl"):
|
|
1739
2071
|
hl_list = setting.split(",")
|
|
1740
2072
|
if len(hl_list) > 1:
|
|
@@ -1772,7 +2104,7 @@ class Picker:
|
|
|
1772
2104
|
theme_number = int(setting[2:].strip())
|
|
1773
2105
|
self.colour_theme_number = min(get_theme_count()-1, theme_number)
|
|
1774
2106
|
set_colours(self.colour_theme_number)
|
|
1775
|
-
self.draw_screen(
|
|
2107
|
+
self.draw_screen()
|
|
1776
2108
|
self.notification(self.stdscr, message=f"Theme {self.colour_theme_number} applied.")
|
|
1777
2109
|
except:
|
|
1778
2110
|
pass
|
|
@@ -1781,8 +2113,12 @@ class Picker:
|
|
|
1781
2113
|
self.colour_theme_number = (self.colour_theme_number + 1)%get_theme_count()
|
|
1782
2114
|
# self.colour_theme_number = int(not bool(self.colour_theme_number))
|
|
1783
2115
|
set_colours(self.colour_theme_number)
|
|
1784
|
-
self.draw_screen(
|
|
2116
|
+
self.draw_screen()
|
|
1785
2117
|
self.notification(self.stdscr, message=f"Theme {self.colour_theme_number} applied.")
|
|
2118
|
+
self.colours = get_colours(self.colour_theme_number)
|
|
2119
|
+
elif setting == "colsel":
|
|
2120
|
+
self.draw_screen()
|
|
2121
|
+
self.select_columns(self.stdscr)
|
|
1786
2122
|
|
|
1787
2123
|
else:
|
|
1788
2124
|
self.user_settings = ""
|
|
@@ -1807,7 +2143,7 @@ class Picker:
|
|
|
1807
2143
|
""" Toggle selection of item at index. """
|
|
1808
2144
|
self.logger.info(f"function: toggle_item()")
|
|
1809
2145
|
self.selections[index] = not self.selections[index]
|
|
1810
|
-
self.draw_screen(
|
|
2146
|
+
self.draw_screen()
|
|
1811
2147
|
|
|
1812
2148
|
def select_all(self) -> None:
|
|
1813
2149
|
""" Select all in indexed_items. """
|
|
@@ -1818,7 +2154,7 @@ class Picker:
|
|
|
1818
2154
|
self.cell_selections[i] = True
|
|
1819
2155
|
for row in range(len(self.indexed_items)):
|
|
1820
2156
|
self.selected_cells_by_row[row] = list(range(len(self.indexed_items[row][1])))
|
|
1821
|
-
self.draw_screen(
|
|
2157
|
+
self.draw_screen()
|
|
1822
2158
|
|
|
1823
2159
|
def deselect_all(self) -> None:
|
|
1824
2160
|
""" Deselect all items in indexed_items. """
|
|
@@ -1828,7 +2164,7 @@ class Picker:
|
|
|
1828
2164
|
for i in self.cell_selections.keys():
|
|
1829
2165
|
self.cell_selections[i] = False
|
|
1830
2166
|
self.selected_cells_by_row = {}
|
|
1831
|
-
self.draw_screen(
|
|
2167
|
+
self.draw_screen()
|
|
1832
2168
|
|
|
1833
2169
|
def handle_visual_selection(self, selecting:bool = True) -> None:
|
|
1834
2170
|
""" Toggle visual selection or deselection. """
|
|
@@ -1872,7 +2208,7 @@ class Picker:
|
|
|
1872
2208
|
self.end_selection = -1
|
|
1873
2209
|
self.is_selecting = False
|
|
1874
2210
|
|
|
1875
|
-
self.draw_screen(
|
|
2211
|
+
self.draw_screen()
|
|
1876
2212
|
|
|
1877
2213
|
elif self.is_deselecting:
|
|
1878
2214
|
self.end_selection = self.indexed_items[self.cursor_pos][0]
|
|
@@ -1905,7 +2241,7 @@ class Picker:
|
|
|
1905
2241
|
self.start_selection = -1
|
|
1906
2242
|
self.end_selection = -1
|
|
1907
2243
|
self.is_deselecting = False
|
|
1908
|
-
self.draw_screen(
|
|
2244
|
+
self.draw_screen()
|
|
1909
2245
|
|
|
1910
2246
|
def cursor_down(self, count=1) -> bool:
|
|
1911
2247
|
""" Move cursor down. """
|
|
@@ -1919,6 +2255,7 @@ class Picker:
|
|
|
1919
2255
|
if self.indexed_items[new_pos][0] in self.unselectable_indices: new_pos+=1
|
|
1920
2256
|
else: break
|
|
1921
2257
|
self.cursor_pos = new_pos
|
|
2258
|
+
self.ensure_no_overscroll()
|
|
1922
2259
|
return True
|
|
1923
2260
|
|
|
1924
2261
|
def cursor_up(self, count=1) -> bool:
|
|
@@ -1932,6 +2269,7 @@ class Picker:
|
|
|
1932
2269
|
elif new_pos in self.unselectable_indices: new_pos -= 1
|
|
1933
2270
|
else: break
|
|
1934
2271
|
self.cursor_pos = new_pos
|
|
2272
|
+
self.ensure_no_overscroll()
|
|
1935
2273
|
return True
|
|
1936
2274
|
|
|
1937
2275
|
def remapped_key(self, key: int, val: int, key_remappings: dict) -> bool:
|
|
@@ -1953,6 +2291,19 @@ class Picker:
|
|
|
1953
2291
|
return True
|
|
1954
2292
|
return False
|
|
1955
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
|
+
|
|
1956
2307
|
def copy_dialogue(self) -> None:
|
|
1957
2308
|
""" Display dialogue to select how rows/cells should be copied. """
|
|
1958
2309
|
self.logger.info(f"function: copy_dialogue()")
|
|
@@ -2023,12 +2374,12 @@ class Picker:
|
|
|
2023
2374
|
if not acceptable_data_type:
|
|
2024
2375
|
break
|
|
2025
2376
|
if not acceptable_data_type:
|
|
2026
|
-
self.draw_screen(
|
|
2377
|
+
self.draw_screen()
|
|
2027
2378
|
self.notification(self.stdscr, message="Error pasting data.")
|
|
2028
2379
|
return None
|
|
2029
2380
|
|
|
2030
2381
|
except:
|
|
2031
|
-
self.draw_screen(
|
|
2382
|
+
self.draw_screen()
|
|
2032
2383
|
self.notification(self.stdscr, message="Error pasting data.")
|
|
2033
2384
|
return None
|
|
2034
2385
|
if type(pasta) == type([]) and len(pasta) > 0 and type(pasta[0]) == type([]):
|
|
@@ -2075,7 +2426,7 @@ class Picker:
|
|
|
2075
2426
|
for idx in s.keys():
|
|
2076
2427
|
save_path_entered, save_path = output_file_option_selector(
|
|
2077
2428
|
self.stdscr,
|
|
2078
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
2429
|
+
refresh_screen_function=lambda: self.draw_screen()
|
|
2079
2430
|
)
|
|
2080
2431
|
if save_path_entered:
|
|
2081
2432
|
return_val = funcs[idx](save_path)
|
|
@@ -2109,7 +2460,7 @@ class Picker:
|
|
|
2109
2460
|
self.loaded_file_states[self.loaded_file_index] = self.get_function_data()
|
|
2110
2461
|
|
|
2111
2462
|
self.stdscr.clear()
|
|
2112
|
-
self.draw_screen(
|
|
2463
|
+
self.draw_screen()
|
|
2113
2464
|
|
|
2114
2465
|
tmp = self.stdscr
|
|
2115
2466
|
|
|
@@ -2121,7 +2472,7 @@ class Picker:
|
|
|
2121
2472
|
|
|
2122
2473
|
self.stdscr = tmp
|
|
2123
2474
|
|
|
2124
|
-
self.notification(self.stdscr, f"{repr(file_to_load)} has been loaded!")
|
|
2475
|
+
# self.notification(self.stdscr, f"{repr(file_to_load)} has been loaded!")
|
|
2125
2476
|
|
|
2126
2477
|
self.set_function_data({}, reset_absent_variables=True)
|
|
2127
2478
|
self.load_file(self.loaded_file)
|
|
@@ -2129,7 +2480,7 @@ class Picker:
|
|
|
2129
2480
|
# header = return_val["header"]
|
|
2130
2481
|
self.stdscr.clear()
|
|
2131
2482
|
# self.initialise_variables()
|
|
2132
|
-
self.draw_screen(
|
|
2483
|
+
self.draw_screen()
|
|
2133
2484
|
# self.stdscr.refresh()
|
|
2134
2485
|
|
|
2135
2486
|
# if return_val:
|
|
@@ -2144,7 +2495,15 @@ class Picker:
|
|
|
2144
2495
|
def fetch_data(self) -> None:
|
|
2145
2496
|
""" Refesh data asynchronously. When data has been fetched self.data_ready is set to True. """
|
|
2146
2497
|
self.logger.info(f"function: fetch_data()")
|
|
2147
|
-
tmp_items, tmp_header =
|
|
2498
|
+
tmp_items, tmp_header = [], []
|
|
2499
|
+
self.getting_data.clear()
|
|
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
|
+
)
|
|
2148
2507
|
if self.track_entries_upon_refresh:
|
|
2149
2508
|
selected_indices = get_selected_indices(self.selections)
|
|
2150
2509
|
self.ids = [item[self.id_column] for i, item in enumerate(self.items) if i in selected_indices]
|
|
@@ -2242,10 +2601,6 @@ class Picker:
|
|
|
2242
2601
|
row_len = 1
|
|
2243
2602
|
if self.header: row_len = len(self.header)
|
|
2244
2603
|
elif len(self.items): row_len = len(self.items[0])
|
|
2245
|
-
# if len(self.indexed_items) == 0:
|
|
2246
|
-
# insert_at_pos = 0
|
|
2247
|
-
# else:
|
|
2248
|
-
# insert_at_pos = self.indexed_items[self.cursor_pos][0]
|
|
2249
2604
|
self.items = self.items[:pos] + [["" for x in range(row_len)]] + self.items[pos:]
|
|
2250
2605
|
if pos <= self.cursor_pos:
|
|
2251
2606
|
self.cursor_pos += 1
|
|
@@ -2355,27 +2710,99 @@ class Picker:
|
|
|
2355
2710
|
self.split_right = not self.split_right
|
|
2356
2711
|
if self.right_panes[self.right_pane_index]["data"] in [[], None, {}]:
|
|
2357
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()
|
|
2358
2721
|
|
|
2359
2722
|
|
|
2360
2723
|
def cycle_right_pane(self, increment=1):
|
|
2361
2724
|
if len(self.right_panes) > 1:
|
|
2362
2725
|
self.right_pane_index = (self.right_pane_index+1)%len(self.right_panes)
|
|
2363
|
-
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)
|
|
2364
2785
|
|
|
2365
2786
|
def run(self) -> Tuple[list[int], str, dict]:
|
|
2366
2787
|
""" Run the picker. """
|
|
2367
2788
|
self.logger.info(f"function: run()")
|
|
2368
2789
|
|
|
2790
|
+
self.thread_stop_event.clear()
|
|
2791
|
+
|
|
2369
2792
|
if self.get_footer_string_startup and self.footer_string_refresh_function != None:
|
|
2793
|
+
self.footer_string = " "
|
|
2794
|
+
self.footer.adjust_sizes(self.term_h, self.term_w)
|
|
2795
|
+
self.draw_screen()
|
|
2370
2796
|
self.footer_string = self.footer_string_refresh_function()
|
|
2371
2797
|
|
|
2372
2798
|
self.initialise_variables(get_data=self.get_data_startup)
|
|
2373
2799
|
|
|
2374
|
-
self.draw_screen(
|
|
2800
|
+
self.draw_screen()
|
|
2375
2801
|
|
|
2376
2802
|
self.initial_time = time.time()
|
|
2377
2803
|
self.initial_time_footer = time.time()-self.footer_timer
|
|
2378
|
-
self.
|
|
2804
|
+
self.initial_right_split_time = time.time()-200
|
|
2805
|
+
self.initial_left_split_time = time.time()-200
|
|
2379
2806
|
|
|
2380
2807
|
if self.startup_notification:
|
|
2381
2808
|
self.notification(self.stdscr, message=self.startup_notification)
|
|
@@ -2399,7 +2826,7 @@ class Picker:
|
|
|
2399
2826
|
|
|
2400
2827
|
# Set terminal background color
|
|
2401
2828
|
self.stdscr.bkgd(' ', curses.color_pair(self.colours_start+3)) # Apply background color
|
|
2402
|
-
self.draw_screen(
|
|
2829
|
+
self.draw_screen()
|
|
2403
2830
|
|
|
2404
2831
|
if self.display_only:
|
|
2405
2832
|
self.stdscr.refresh()
|
|
@@ -2407,21 +2834,21 @@ class Picker:
|
|
|
2407
2834
|
return [], "", function_data
|
|
2408
2835
|
|
|
2409
2836
|
# Open tty to accept input
|
|
2410
|
-
tty_fd = open_tty()
|
|
2837
|
+
tty_fd, self.saved_terminal_state = open_tty()
|
|
2838
|
+
|
|
2839
|
+
self.update_term_size()
|
|
2840
|
+
self.calculate_section_sizes()
|
|
2411
2841
|
|
|
2412
|
-
h, w = self.stdscr.getmaxyx()
|
|
2413
|
-
self.term_h, self.term_w = self.stdscr.getmaxyx()
|
|
2414
|
-
if self.split_right and len(self.right_panes):
|
|
2415
|
-
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
2416
|
-
self.rows_w, self.rows_h = int(self.term_w*proportion), self.term_h
|
|
2417
|
-
else:
|
|
2418
|
-
self.rows_w, self.rows_h = self.term_w, self.term_h
|
|
2419
2842
|
def terminal_resized(old_w, old_h) -> bool:
|
|
2420
2843
|
w, h = os.get_terminal_size()
|
|
2421
2844
|
if old_h != h or old_w != w: return True
|
|
2422
2845
|
else: return False
|
|
2423
2846
|
|
|
2424
2847
|
COLS, LINES = os.get_terminal_size()
|
|
2848
|
+
|
|
2849
|
+
|
|
2850
|
+
getting_data_prev = False
|
|
2851
|
+
|
|
2425
2852
|
# Main loop
|
|
2426
2853
|
while True:
|
|
2427
2854
|
# key = self.stdscr.getch()
|
|
@@ -2429,21 +2856,23 @@ class Picker:
|
|
|
2429
2856
|
key = get_char(tty_fd, timeout=0.2)
|
|
2430
2857
|
if key != -1:
|
|
2431
2858
|
self.logger.info(f"key={key}")
|
|
2859
|
+
self.last_key = key
|
|
2860
|
+
|
|
2861
|
+
# Ensure that
|
|
2862
|
+
|
|
2863
|
+
if not self.getting_data.is_set():
|
|
2864
|
+
self.initialise_variables()
|
|
2865
|
+
getting_data_prev = True
|
|
2866
|
+
elif getting_data_prev:
|
|
2867
|
+
## Ensure that we reinitialise one final time after all data is retrieved.
|
|
2868
|
+
self.initialise_variables()
|
|
2869
|
+
getting_data_prev = False
|
|
2432
2870
|
|
|
2433
2871
|
self.term_resize_event = terminal_resized(COLS, LINES)
|
|
2434
2872
|
COLS, LINES = os.get_terminal_size()
|
|
2435
2873
|
if self.term_resize_event:
|
|
2436
2874
|
key = curses.KEY_RESIZE
|
|
2437
2875
|
|
|
2438
|
-
h, w = self.stdscr.getmaxyx()
|
|
2439
|
-
self.term_h, self.term_w = self.stdscr.getmaxyx()
|
|
2440
|
-
|
|
2441
|
-
if self.split_right and len(self.right_panes):
|
|
2442
|
-
proportion = self.right_panes[self.right_pane_index]["proportion"]
|
|
2443
|
-
self.rows_w, self.rows_h = int(self.term_w*proportion), self.term_h
|
|
2444
|
-
else:
|
|
2445
|
-
self.rows_w, self.rows_h = self.term_w, self.term_h
|
|
2446
|
-
|
|
2447
2876
|
if key in self.disabled_keys: continue
|
|
2448
2877
|
clear_screen=True
|
|
2449
2878
|
|
|
@@ -2457,16 +2886,20 @@ class Picker:
|
|
|
2457
2886
|
|
|
2458
2887
|
self.initial_time = time.time()
|
|
2459
2888
|
|
|
2460
|
-
self.draw_screen(
|
|
2889
|
+
self.draw_screen(clear=False)
|
|
2461
2890
|
|
|
2462
2891
|
self.refreshing_data = False
|
|
2463
2892
|
self.data_ready = False
|
|
2464
2893
|
|
|
2894
|
+
|
|
2465
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):
|
|
2466
2896
|
self.logger.debug(f"Get new data (refresh).")
|
|
2467
|
-
|
|
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
|
|
2468
2901
|
self.stdscr.refresh()
|
|
2469
|
-
if self.get_new_data
|
|
2902
|
+
if self.get_new_data:
|
|
2470
2903
|
self.refreshing_data = True
|
|
2471
2904
|
|
|
2472
2905
|
t = threading.Thread(target=self.fetch_data)
|
|
@@ -2493,13 +2926,19 @@ class Picker:
|
|
|
2493
2926
|
self.logger.debug(f"footer_string_auto_refresh")
|
|
2494
2927
|
self.footer_string = self.footer_string_refresh_function()
|
|
2495
2928
|
self.initial_time_footer = time.time()
|
|
2496
|
-
self.draw_screen(
|
|
2929
|
+
self.draw_screen()
|
|
2497
2930
|
|
|
2498
|
-
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"]):
|
|
2499
2932
|
get_data = self.right_panes[self.right_pane_index]["get_data"]
|
|
2500
2933
|
data = self.right_panes[self.right_pane_index]["data"]
|
|
2501
2934
|
self.right_panes[self.right_pane_index]["data"] = get_data(data, self.get_function_data())
|
|
2502
|
-
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()
|
|
2503
2942
|
|
|
2504
2943
|
if self.check_key("help", key, self.keys_dict):
|
|
2505
2944
|
self.logger.info(f"key_function help")
|
|
@@ -2507,7 +2946,7 @@ class Picker:
|
|
|
2507
2946
|
self.stdscr.refresh()
|
|
2508
2947
|
help_data = {
|
|
2509
2948
|
# "items": help_lines,
|
|
2510
|
-
"items": build_help_rows(self.keys_dict),
|
|
2949
|
+
"items": build_help_rows(self.keys_dict, self.macros),
|
|
2511
2950
|
"title": f"{self.title} Help",
|
|
2512
2951
|
"colours_start": self.help_colours_start,
|
|
2513
2952
|
"colours": help_colours,
|
|
@@ -2529,7 +2968,11 @@ class Picker:
|
|
|
2529
2968
|
}
|
|
2530
2969
|
OptionPicker = Picker(self.stdscr, **help_data)
|
|
2531
2970
|
s, o, f = OptionPicker.run()
|
|
2532
|
-
self.draw_screen(
|
|
2971
|
+
self.draw_screen()
|
|
2972
|
+
|
|
2973
|
+
if self.check_and_run_macro(key):
|
|
2974
|
+
self.draw_screen()
|
|
2975
|
+
continue
|
|
2533
2976
|
|
|
2534
2977
|
if self.check_key("info", key, self.keys_dict):
|
|
2535
2978
|
self.logger.info(f"key_function help")
|
|
@@ -2603,6 +3046,14 @@ class Picker:
|
|
|
2603
3046
|
data["option_functions"] = f"[...] length = {len(data['option_functions'])}"
|
|
2604
3047
|
data["loaded_file_states"] = f"[...] length = {len(data['loaded_file_states'])}"
|
|
2605
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'])}"
|
|
2606
3057
|
info_items += [
|
|
2607
3058
|
["",""],
|
|
2608
3059
|
[" get_function_data()", "-*"*30],
|
|
@@ -2643,13 +3094,14 @@ class Picker:
|
|
|
2643
3094
|
OptionPicker = Picker(self.stdscr, **info_data)
|
|
2644
3095
|
s, o, f = OptionPicker.run()
|
|
2645
3096
|
|
|
2646
|
-
self.draw_screen(
|
|
3097
|
+
self.draw_screen()
|
|
2647
3098
|
|
|
2648
3099
|
elif self.check_key("exit", key, self.keys_dict):
|
|
2649
3100
|
self.stdscr.clear()
|
|
2650
3101
|
if len(self.loaded_files) <= 1:
|
|
3102
|
+
self.cleanup_threads()
|
|
2651
3103
|
function_data = self.get_function_data()
|
|
2652
|
-
|
|
3104
|
+
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2653
3105
|
return [], "", function_data
|
|
2654
3106
|
else:
|
|
2655
3107
|
del self.loaded_files[self.loaded_file_index]
|
|
@@ -2666,28 +3118,30 @@ class Picker:
|
|
|
2666
3118
|
self.set_function_data({}, reset_absent_variables=True)
|
|
2667
3119
|
self.load_file(self.loaded_file)
|
|
2668
3120
|
self.loaded_file_index, self.loaded_file = idx, file
|
|
2669
|
-
self.draw_screen(
|
|
3121
|
+
self.draw_screen()
|
|
2670
3122
|
|
|
2671
3123
|
elif self.check_key("full_exit", key, self.keys_dict):
|
|
3124
|
+
self.cleanup_threads()
|
|
2672
3125
|
close_curses(self.stdscr)
|
|
3126
|
+
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2673
3127
|
exit()
|
|
2674
3128
|
|
|
2675
3129
|
elif self.check_key("settings_input", key, self.keys_dict):
|
|
2676
3130
|
self.logger.info(f"Settings input")
|
|
2677
3131
|
usrtxt = f"{self.user_settings.strip()} " if self.user_settings else ""
|
|
2678
|
-
field_end_f = lambda: self.
|
|
2679
|
-
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.
|
|
2680
|
-
else: field_end_f = lambda: self.
|
|
3132
|
+
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
3133
|
+
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.get_term_size()[1]-38
|
|
3134
|
+
else: field_end_f = lambda: self.get_term_size()[1]-3
|
|
2681
3135
|
self.set_registers()
|
|
2682
3136
|
usrtxt, return_val = input_field(
|
|
2683
3137
|
self.stdscr,
|
|
2684
3138
|
usrtxt=usrtxt,
|
|
2685
3139
|
field_prefix=" Settings: ",
|
|
2686
3140
|
x=lambda:2,
|
|
2687
|
-
y=lambda: self.
|
|
3141
|
+
y=lambda: self.get_term_size()[0]-1,
|
|
2688
3142
|
max_length=field_end_f,
|
|
2689
3143
|
registers=self.registers,
|
|
2690
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
3144
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
2691
3145
|
history=self.history_settings,
|
|
2692
3146
|
path_auto_complete=True,
|
|
2693
3147
|
formula_auto_complete=False,
|
|
@@ -2707,28 +3161,29 @@ class Picker:
|
|
|
2707
3161
|
|
|
2708
3162
|
elif self.check_key("settings_options", key, self.keys_dict):
|
|
2709
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."]]
|
|
2710
3186
|
if len(self.items) > 0:
|
|
2711
|
-
options += [["cv", "Centre rows vertically"]]
|
|
2712
|
-
options += [["pc", "Pin cursor to row number when data refreshes"]]
|
|
2713
|
-
options += [["ct", "Centre column-set in terminal"]]
|
|
2714
|
-
options += [["cc", "Centre values in cells"]]
|
|
2715
|
-
options += [["!r", "Toggle auto-refresh"]]
|
|
2716
|
-
options += [["th", "Cycle between themes. (accepts th#)"]]
|
|
2717
|
-
options += [["nohl", "Toggle highlights"]]
|
|
2718
|
-
options += [["footer", "Toggle footer"]]
|
|
2719
|
-
options += [["header", "Toggle header"]]
|
|
2720
|
-
options += [["rh", "Toggle row header"]]
|
|
2721
|
-
options += [["modes", "Toggle modes"]]
|
|
2722
|
-
options += [["ft", "Cycle through footer styles (accepts ft#)"]]
|
|
2723
|
-
options += [["file_next", "Go to the next open file."]]
|
|
2724
|
-
options += [["file_prev", "Go to the previous open file."]]
|
|
2725
|
-
options += [["sheet_next", "Go to the next sheet."]]
|
|
2726
|
-
options += [["sheet_prev", "Go to the previous sheet."]]
|
|
2727
|
-
options += [["unicode", "Toggle b/w using len and wcwidth to calculate char width."]]
|
|
2728
|
-
options += [["ara", "Add empty row after cursor."]]
|
|
2729
|
-
options += [["arb", "Add empty row before the cursor."]]
|
|
2730
|
-
options += [["aca", "Add empty column after the selected column."]]
|
|
2731
|
-
options += [["acb", "Add empty column before the selected column."]]
|
|
2732
3187
|
options += [[f"col{i}", f"Select column {i}"] for i in range(len(self.items[0]))]
|
|
2733
3188
|
options += [[f"s{i}", f"Sort by column {i}"] for i in range(len(self.items[0]))]
|
|
2734
3189
|
options += [[f"!{i}", f"Toggle visibility of column {i}"] for i in range(len(self.items[0]))]
|
|
@@ -2743,6 +3198,7 @@ class Picker:
|
|
|
2743
3198
|
|
|
2744
3199
|
elif self.check_key("redo", key, self.keys_dict):
|
|
2745
3200
|
self.redo()
|
|
3201
|
+
|
|
2746
3202
|
# elif self.check_key("move_column_left", key, self.keys_dict):
|
|
2747
3203
|
# tmp1 = self.column_indices[self.selected_column]
|
|
2748
3204
|
# tmp2 = self.column_indices[(self.selected_column-1)%len(self.column_indices)]
|
|
@@ -2752,7 +3208,7 @@ class Picker:
|
|
|
2752
3208
|
# # self.notification(self.stdscr, f"{str(self.column_indices)}, {tmp1}, {tmp2}")
|
|
2753
3209
|
# self.initialise_variables()
|
|
2754
3210
|
# self.column_widths = get_column_widths([v[1] for v in self.indexed_items], header=self.header, max_column_width=self.max_column_width, number_columns=self.number_columns, max_total_width=w)
|
|
2755
|
-
# self.draw_screen(
|
|
3211
|
+
# self.draw_screen()
|
|
2756
3212
|
# # self.move_column(direction=-1)
|
|
2757
3213
|
#
|
|
2758
3214
|
# elif self.check_key("move_column_right", key, self.keys_dict):
|
|
@@ -2762,7 +3218,7 @@ class Picker:
|
|
|
2762
3218
|
# self.column_indices[(self.selected_column+1)%(len(self.column_indices))] = tmp1
|
|
2763
3219
|
# self.selected_column = (self.selected_column+1)%len(self.column_indices)
|
|
2764
3220
|
# self.initialise_variables()
|
|
2765
|
-
# self.draw_screen(
|
|
3221
|
+
# self.draw_screen()
|
|
2766
3222
|
# # self.move_column(direction=1)
|
|
2767
3223
|
|
|
2768
3224
|
elif self.check_key("cursor_down", key, self.keys_dict):
|
|
@@ -2816,6 +3272,7 @@ class Picker:
|
|
|
2816
3272
|
self.selected_cells_by_row[row] = [col]
|
|
2817
3273
|
|
|
2818
3274
|
self.cursor_down()
|
|
3275
|
+
self.ensure_no_overscroll()
|
|
2819
3276
|
elif self.check_key("select_all", key, self.keys_dict): # Select all (m or ctrl-a)
|
|
2820
3277
|
self.select_all()
|
|
2821
3278
|
|
|
@@ -2830,7 +3287,8 @@ class Picker:
|
|
|
2830
3287
|
if new_pos < len(self.indexed_items):
|
|
2831
3288
|
self.cursor_pos = new_pos
|
|
2832
3289
|
|
|
2833
|
-
self.
|
|
3290
|
+
self.ensure_no_overscroll()
|
|
3291
|
+
self.draw_screen()
|
|
2834
3292
|
|
|
2835
3293
|
elif self.check_key("cursor_bottom", key, self.keys_dict):
|
|
2836
3294
|
new_pos = len(self.indexed_items)-1
|
|
@@ -2839,19 +3297,17 @@ class Picker:
|
|
|
2839
3297
|
else: break
|
|
2840
3298
|
if new_pos < len(self.items) and new_pos >= 0:
|
|
2841
3299
|
self.cursor_pos = new_pos
|
|
2842
|
-
self.
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
#
|
|
2846
|
-
# current_row = (len(self.indexed_items) +items_per_page - 1) % items_per_page
|
|
2847
|
-
# self.draw_screen(self.indexed_items, self.highlights)
|
|
3300
|
+
self.ensure_no_overscroll()
|
|
3301
|
+
self.draw_screen()
|
|
3302
|
+
|
|
2848
3303
|
elif self.check_key("enter", key, self.keys_dict):
|
|
2849
3304
|
self.logger.info(f"key_function enter")
|
|
2850
3305
|
# Print the selected indices if any, otherwise print the current index
|
|
2851
3306
|
if self.is_selecting or self.is_deselecting: self.handle_visual_selection()
|
|
2852
3307
|
if len(self.items) == 0:
|
|
3308
|
+
self.cleanup_threads()
|
|
2853
3309
|
function_data = self.get_function_data()
|
|
2854
|
-
|
|
3310
|
+
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2855
3311
|
return [], "", function_data
|
|
2856
3312
|
selected_indices = get_selected_indices(self.selections)
|
|
2857
3313
|
if not selected_indices and len(self.indexed_items):
|
|
@@ -2864,8 +3320,7 @@ class Picker:
|
|
|
2864
3320
|
if self.option_functions[index] != None:
|
|
2865
3321
|
options_sufficient, usrtxt = self.option_functions[index](
|
|
2866
3322
|
stdscr=self.stdscr,
|
|
2867
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
2868
|
-
field_prefix=f" Opts ({index}): ",
|
|
3323
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
2869
3324
|
)
|
|
2870
3325
|
else:
|
|
2871
3326
|
self.set_registers()
|
|
@@ -2877,11 +3332,12 @@ class Picker:
|
|
|
2877
3332
|
)
|
|
2878
3333
|
|
|
2879
3334
|
if options_sufficient:
|
|
3335
|
+
self.cleanup_threads()
|
|
2880
3336
|
self.user_opts = usrtxt
|
|
2881
3337
|
self.stdscr.clear()
|
|
2882
3338
|
self.stdscr.refresh()
|
|
2883
3339
|
function_data = self.get_function_data()
|
|
2884
|
-
|
|
3340
|
+
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2885
3341
|
return selected_indices, usrtxt, function_data
|
|
2886
3342
|
elif self.check_key("page_down", key, self.keys_dict): # Next page
|
|
2887
3343
|
self.cursor_pos = min(len(self.indexed_items) - 1, self.cursor_pos+self.items_per_page)
|
|
@@ -2890,15 +3346,7 @@ class Picker:
|
|
|
2890
3346
|
self.cursor_pos = max(0, self.cursor_pos-self.items_per_page)
|
|
2891
3347
|
|
|
2892
3348
|
elif self.check_key("redraw_screen", key, self.keys_dict):
|
|
2893
|
-
self.
|
|
2894
|
-
self.stdscr.clear()
|
|
2895
|
-
self.stdscr.refresh()
|
|
2896
|
-
restrict_curses(self.stdscr)
|
|
2897
|
-
unrestrict_curses(self.stdscr)
|
|
2898
|
-
self.stdscr.clear()
|
|
2899
|
-
self.stdscr.refresh()
|
|
2900
|
-
|
|
2901
|
-
self.draw_screen(self.indexed_items, self.highlights)
|
|
3349
|
+
self.refresh_and_draw_screen()
|
|
2902
3350
|
|
|
2903
3351
|
elif self.check_key("cycle_sort_method", key, self.keys_dict):
|
|
2904
3352
|
if self.sort_column == self.selected_column:
|
|
@@ -2926,7 +3374,7 @@ class Picker:
|
|
|
2926
3374
|
if len(self.indexed_items) > 0:
|
|
2927
3375
|
current_index = self.indexed_items[self.cursor_pos][0]
|
|
2928
3376
|
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
|
|
2929
|
-
self.draw_screen(
|
|
3377
|
+
self.draw_screen()
|
|
2930
3378
|
self.cursor_pos = [row[0] for row in self.indexed_items].index(current_index)
|
|
2931
3379
|
self.logger.info(f"key_function cycle_sort_order. (sort_column, sort_method, sort_reverse) = ({self.sort_column}, {self.columns_sort_method[self.sort_column]}, {self.sort_reverse[self.sort_column]})")
|
|
2932
3380
|
elif self.check_key("col_select", key, self.keys_dict):
|
|
@@ -2939,23 +3387,29 @@ class Picker:
|
|
|
2939
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
|
|
2940
3388
|
self.cursor_pos = [row[0] for row in self.indexed_items].index(current_index)
|
|
2941
3389
|
elif self.check_key("col_select_next", key, self.keys_dict):
|
|
2942
|
-
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
2943
|
-
col_index = (self.selected_column +1) % (len(self.items[0]))
|
|
2944
|
-
self.selected_column = col_index
|
|
2945
|
-
# Flash when we loop back to the first column
|
|
2946
|
-
# if self.selected_column == 0:
|
|
2947
|
-
# curses.flash()
|
|
2948
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()
|
|
2949
3403
|
|
|
2950
3404
|
|
|
2951
3405
|
## Scroll with column select
|
|
2952
|
-
|
|
2953
|
-
self.column_widths = get_column_widths(
|
|
2954
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
2955
|
-
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
2956
|
-
start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
2957
|
-
end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
2958
|
-
display_width = self.rows_w-self.
|
|
3406
|
+
self.get_visible_rows()
|
|
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)
|
|
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
|
|
2959
3413
|
# If the full column is within the current display then don't do anything
|
|
2960
3414
|
if start_of_cell >= self.leftmost_char and end_of_cell <= self.leftmost_char + display_width:
|
|
2961
3415
|
pass
|
|
@@ -2964,25 +3418,32 @@ class Picker:
|
|
|
2964
3418
|
self.leftmost_char = end_of_cell - display_width
|
|
2965
3419
|
|
|
2966
3420
|
self.leftmost_char = max(0, min(column_set_width - display_width + 5, self.leftmost_char))
|
|
3421
|
+
self.ensure_no_overscroll()
|
|
2967
3422
|
|
|
2968
3423
|
elif self.check_key("col_select_prev", key, self.keys_dict):
|
|
2969
|
-
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
2970
|
-
col_index = (self.selected_column -1) % (len(self.items[0]))
|
|
2971
|
-
self.selected_column = col_index
|
|
2972
|
-
|
|
2973
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
|
+
|
|
2974
3435
|
# Flash when we loop back to the last column
|
|
2975
3436
|
# if self.selected_column == len(self.column_widths)-1:
|
|
2976
3437
|
# curses.flash()
|
|
2977
3438
|
|
|
2978
3439
|
## Scroll with column select
|
|
2979
|
-
|
|
2980
|
-
self.column_widths = get_column_widths(
|
|
2981
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
2982
|
-
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
2983
|
-
start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
2984
|
-
end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
2985
|
-
display_width = self.rows_w-self.
|
|
3440
|
+
self.get_visible_rows()
|
|
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)
|
|
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
|
|
2986
3447
|
|
|
2987
3448
|
# If the entire column is within the current display then don't do anything
|
|
2988
3449
|
if start_of_cell >= self.leftmost_char and end_of_cell <= self.leftmost_char + display_width:
|
|
@@ -2992,25 +3453,26 @@ class Picker:
|
|
|
2992
3453
|
self.leftmost_char = start_of_cell
|
|
2993
3454
|
|
|
2994
3455
|
self.leftmost_char = max(0, min(column_set_width - display_width + 5, self.leftmost_char))
|
|
3456
|
+
self.ensure_no_overscroll()
|
|
2995
3457
|
|
|
2996
3458
|
elif self.check_key("scroll_right", key, self.keys_dict):
|
|
2997
3459
|
self.logger.info(f"key_function scroll_right")
|
|
2998
3460
|
if len(self.indexed_items):
|
|
2999
|
-
row_width = sum(self.
|
|
3000
|
-
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:
|
|
3001
3463
|
self.leftmost_char += 5
|
|
3002
|
-
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w
|
|
3003
|
-
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:
|
|
3004
3466
|
self.leftmost_char = 0
|
|
3005
3467
|
|
|
3006
3468
|
elif self.check_key("scroll_right_25", key, self.keys_dict):
|
|
3007
3469
|
self.logger.info(f"key_function scroll_right")
|
|
3008
3470
|
if len(self.indexed_items):
|
|
3009
|
-
row_width = sum(self.
|
|
3010
|
-
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:
|
|
3011
3473
|
self.leftmost_char += 25
|
|
3012
|
-
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w
|
|
3013
|
-
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:
|
|
3014
3476
|
self.leftmost_char = 0
|
|
3015
3477
|
|
|
3016
3478
|
elif self.check_key("scroll_left", key, self.keys_dict):
|
|
@@ -3029,20 +3491,14 @@ class Picker:
|
|
|
3029
3491
|
elif self.check_key("scroll_far_right", key, self.keys_dict):
|
|
3030
3492
|
self.logger.info(f"key_function scroll_far_right")
|
|
3031
3493
|
longest_row_str_len = 0
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
# row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
|
|
3041
|
-
# if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
|
|
3042
|
-
# self.notification(self.stdscr, f"{longest_row_str_len}")
|
|
3043
|
-
self.leftmost_char = max(0, longest_row_str_len-self.rows_w+2+self.startx+5)
|
|
3044
|
-
if len(self.items):
|
|
3045
|
-
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
|
|
3046
3502
|
|
|
3047
3503
|
elif self.check_key("add_column_before", key, self.keys_dict):
|
|
3048
3504
|
self.logger.info(f"key_function add_column_before")
|
|
@@ -3099,52 +3555,45 @@ class Picker:
|
|
|
3099
3555
|
self.selected_column = min(self.selected_column, row_len-2)
|
|
3100
3556
|
self.initialise_variables()
|
|
3101
3557
|
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
# elif self.check_key("increase_lines_per_page", key, self.keys_dict):
|
|
3106
|
-
# self.items_per_page += 1
|
|
3107
|
-
# self.draw_screen(self.indexed_items, self.highlights)
|
|
3108
|
-
# elif self.check_key("decrease_lines_per_page", key, self.keys_dict):
|
|
3109
|
-
# if self.items_per_page > 1:
|
|
3110
|
-
# self.items_per_page -= 1
|
|
3111
|
-
# self.draw_screen(self.indexed_items, self.highlights)
|
|
3112
3558
|
elif self.check_key("decrease_column_width", key, self.keys_dict):
|
|
3113
3559
|
self.logger.info(f"key_function decrease_column_width")
|
|
3114
3560
|
if self.max_column_width > 10:
|
|
3115
3561
|
self.max_column_width -= 10
|
|
3116
3562
|
# 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=2)
|
|
3117
|
-
self.draw_screen(
|
|
3563
|
+
self.draw_screen()
|
|
3118
3564
|
elif self.check_key("increase_column_width", key, self.keys_dict):
|
|
3119
3565
|
self.logger.info(f"key_function increase_column_width")
|
|
3120
3566
|
if self.max_column_width < 1000:
|
|
3121
3567
|
self.max_column_width += 10
|
|
3122
3568
|
# 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)
|
|
3123
|
-
self.draw_screen(
|
|
3569
|
+
self.draw_screen()
|
|
3124
3570
|
elif self.check_key("visual_selection_toggle", key, self.keys_dict):
|
|
3125
3571
|
self.logger.info(f"key_function visual_selection_toggle")
|
|
3126
3572
|
self.handle_visual_selection()
|
|
3127
|
-
self.draw_screen(
|
|
3573
|
+
self.draw_screen()
|
|
3128
3574
|
|
|
3129
3575
|
elif self.check_key("visual_deselection_toggle", key, self.keys_dict):
|
|
3130
3576
|
self.logger.info(f"key_function visual_deselection_toggle")
|
|
3131
3577
|
self.handle_visual_selection(selecting=False)
|
|
3132
|
-
self.draw_screen(
|
|
3578
|
+
self.draw_screen()
|
|
3133
3579
|
|
|
3134
3580
|
elif key == curses.KEY_RESIZE: # Terminal resize signal
|
|
3135
3581
|
|
|
3136
3582
|
self.calculate_section_sizes()
|
|
3137
|
-
self.
|
|
3138
|
-
|
|
3583
|
+
self.ensure_no_overscroll()
|
|
3584
|
+
|
|
3585
|
+
self.stdscr.clear()
|
|
3586
|
+
self.stdscr.refresh()
|
|
3587
|
+
self.draw_screen()
|
|
3139
3588
|
|
|
3140
3589
|
|
|
3141
3590
|
elif self.check_key("filter_input", key, self.keys_dict):
|
|
3142
3591
|
self.logger.info(f"key_function filter_input")
|
|
3143
|
-
self.draw_screen(
|
|
3592
|
+
self.draw_screen()
|
|
3144
3593
|
usrtxt = f"{self.filter_query} " if self.filter_query else ""
|
|
3145
|
-
field_end_f = lambda: self.
|
|
3146
|
-
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.
|
|
3147
|
-
else: field_end_f = lambda: self.
|
|
3594
|
+
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
3595
|
+
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.get_term_size()[1]-38
|
|
3596
|
+
else: field_end_f = lambda: self.get_term_size()[1]-3
|
|
3148
3597
|
self.set_registers()
|
|
3149
3598
|
words = self.get_word_list()
|
|
3150
3599
|
usrtxt, return_val = input_field(
|
|
@@ -3152,11 +3601,11 @@ class Picker:
|
|
|
3152
3601
|
usrtxt=usrtxt,
|
|
3153
3602
|
field_prefix=" Filter: ",
|
|
3154
3603
|
x=lambda:2,
|
|
3155
|
-
y=lambda: self.
|
|
3604
|
+
y=lambda: self.get_term_size()[0]-2,
|
|
3156
3605
|
# max_length=field_end,
|
|
3157
3606
|
max_length=field_end_f,
|
|
3158
3607
|
registers=self.registers,
|
|
3159
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
3608
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
3160
3609
|
history=self.history_filter_and_search,
|
|
3161
3610
|
path_auto_complete=True,
|
|
3162
3611
|
formula_auto_complete=False,
|
|
@@ -3185,11 +3634,11 @@ class Picker:
|
|
|
3185
3634
|
|
|
3186
3635
|
elif self.check_key("search_input", key, self.keys_dict):
|
|
3187
3636
|
self.logger.info(f"key_function search_input")
|
|
3188
|
-
self.draw_screen(
|
|
3637
|
+
self.draw_screen()
|
|
3189
3638
|
usrtxt = f"{self.search_query} " if self.search_query else ""
|
|
3190
|
-
field_end_f = lambda: self.
|
|
3191
|
-
if self.show_footer and self.footer.height >= 3: field_end_f = lambda: self.
|
|
3192
|
-
else: field_end_f = lambda: self.
|
|
3639
|
+
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
3640
|
+
if self.show_footer and self.footer.height >= 3: field_end_f = lambda: self.get_term_size()[1]-38
|
|
3641
|
+
else: field_end_f = lambda: self.get_term_size()[1]-3
|
|
3193
3642
|
self.set_registers()
|
|
3194
3643
|
words = self.get_word_list()
|
|
3195
3644
|
usrtxt, return_val = input_field(
|
|
@@ -3197,10 +3646,10 @@ class Picker:
|
|
|
3197
3646
|
usrtxt=usrtxt,
|
|
3198
3647
|
field_prefix=" Search: ",
|
|
3199
3648
|
x=lambda:2,
|
|
3200
|
-
y=lambda: self.
|
|
3649
|
+
y=lambda: self.get_term_size()[0]-3,
|
|
3201
3650
|
max_length=field_end_f,
|
|
3202
3651
|
registers=self.registers,
|
|
3203
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
3652
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
3204
3653
|
history=self.history_filter_and_search,
|
|
3205
3654
|
path_auto_complete=True,
|
|
3206
3655
|
formula_auto_complete=False,
|
|
@@ -3285,23 +3734,16 @@ class Picker:
|
|
|
3285
3734
|
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
|
|
3286
3735
|
elif self.cancel_is_back:
|
|
3287
3736
|
function_data = self.get_function_data()
|
|
3288
|
-
function_data["last_key"] = key
|
|
3289
3737
|
return [], "escape", function_data
|
|
3290
3738
|
|
|
3291
|
-
|
|
3292
|
-
# else:
|
|
3293
|
-
# self.search_query = ""
|
|
3294
|
-
# self.mode_index = 0
|
|
3295
|
-
# self.highlights = [highlight for highlight in self.highlights if "type" not in highlight or highlight["type"] != "search" ]
|
|
3296
|
-
# continue
|
|
3297
|
-
self.draw_screen(self.indexed_items, self.highlights)
|
|
3739
|
+
self.draw_screen()
|
|
3298
3740
|
|
|
3299
3741
|
elif self.check_key("opts_input", key, self.keys_dict):
|
|
3300
3742
|
self.logger.info(f"key_function opts_input")
|
|
3301
3743
|
usrtxt = f"{self.user_opts} " if self.user_opts else ""
|
|
3302
|
-
field_end_f = lambda: self.
|
|
3303
|
-
if self.show_footer and self.footer.height >= 1: field_end_f = lambda: self.
|
|
3304
|
-
else: field_end_f = lambda: self.
|
|
3744
|
+
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
3745
|
+
if self.show_footer and self.footer.height >= 1: field_end_f = lambda: self.get_term_size()[1]-38
|
|
3746
|
+
else: field_end_f = lambda: self.get_term_size()[1]-3
|
|
3305
3747
|
self.set_registers()
|
|
3306
3748
|
words = self.get_word_list()
|
|
3307
3749
|
usrtxt, return_val = input_field(
|
|
@@ -3309,10 +3751,10 @@ class Picker:
|
|
|
3309
3751
|
usrtxt=usrtxt,
|
|
3310
3752
|
field_prefix=" Opts: ",
|
|
3311
3753
|
x=lambda:2,
|
|
3312
|
-
y=lambda: self.
|
|
3754
|
+
y=lambda: self.get_term_size()[0]-1,
|
|
3313
3755
|
max_length=field_end_f,
|
|
3314
3756
|
registers=self.registers,
|
|
3315
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
3757
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
3316
3758
|
history=self.history_opts,
|
|
3317
3759
|
path_auto_complete=True,
|
|
3318
3760
|
formula_auto_complete=False,
|
|
@@ -3390,13 +3832,19 @@ class Picker:
|
|
|
3390
3832
|
elif self.check_key("cycle_right_pane", key, self.keys_dict):
|
|
3391
3833
|
self.cycle_right_pane()
|
|
3392
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
|
+
|
|
3393
3841
|
elif self.check_key("pipe_input", key, self.keys_dict):
|
|
3394
3842
|
self.logger.info(f"key_function pipe_input")
|
|
3395
3843
|
# usrtxt = "xargs -d '\n' -I{} "
|
|
3396
3844
|
usrtxt = "xargs "
|
|
3397
|
-
field_end_f = lambda: self.
|
|
3398
|
-
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.
|
|
3399
|
-
else: field_end_f = lambda: self.
|
|
3845
|
+
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
3846
|
+
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.get_term_size()[1]-38
|
|
3847
|
+
else: field_end_f = lambda: self.get_term_size()[1]-3
|
|
3400
3848
|
self.set_registers()
|
|
3401
3849
|
|
|
3402
3850
|
# Get list of available shell commands
|
|
@@ -3412,11 +3860,11 @@ class Picker:
|
|
|
3412
3860
|
usrtxt=usrtxt,
|
|
3413
3861
|
field_prefix=" Command: ",
|
|
3414
3862
|
x=lambda:2,
|
|
3415
|
-
y=lambda: self.
|
|
3416
|
-
literal=
|
|
3863
|
+
y=lambda: self.get_term_size()[0]-2,
|
|
3864
|
+
literal=False,
|
|
3417
3865
|
max_length=field_end_f,
|
|
3418
3866
|
registers=self.registers,
|
|
3419
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
3867
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
3420
3868
|
history=self.history_pipes,
|
|
3421
3869
|
path_auto_complete=True,
|
|
3422
3870
|
formula_auto_complete=False,
|
|
@@ -3503,12 +3951,12 @@ class Picker:
|
|
|
3503
3951
|
|
|
3504
3952
|
elif self.check_key("edit", key, self.keys_dict):
|
|
3505
3953
|
self.logger.info(f"key_function edit")
|
|
3506
|
-
if len(self.indexed_items) > 0 and self.
|
|
3954
|
+
if len(self.indexed_items) > 0 and self.editable_columns[self.selected_column]:
|
|
3507
3955
|
current_val = self.indexed_items[self.cursor_pos][1][self.selected_column]
|
|
3508
3956
|
usrtxt = f"{current_val}"
|
|
3509
|
-
field_end_f = lambda: self.
|
|
3510
|
-
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.
|
|
3511
|
-
else: field_end_f = lambda: self.
|
|
3957
|
+
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
3958
|
+
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.get_term_size()[1]-38
|
|
3959
|
+
else: field_end_f = lambda: self.get_term_size()[1]-3
|
|
3512
3960
|
self.set_registers()
|
|
3513
3961
|
words = self.get_word_list()
|
|
3514
3962
|
usrtxt, return_val = input_field(
|
|
@@ -3516,10 +3964,10 @@ class Picker:
|
|
|
3516
3964
|
usrtxt=usrtxt,
|
|
3517
3965
|
field_prefix=" Edit value: ",
|
|
3518
3966
|
x=lambda:2,
|
|
3519
|
-
y=lambda: self.
|
|
3967
|
+
y=lambda: self.get_term_size()[0]-2,
|
|
3520
3968
|
max_length=field_end_f,
|
|
3521
3969
|
registers=self.registers,
|
|
3522
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
3970
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
3523
3971
|
history = self.history_edits,
|
|
3524
3972
|
path_auto_complete=True,
|
|
3525
3973
|
formula_auto_complete=True,
|
|
@@ -3532,15 +3980,56 @@ class Picker:
|
|
|
3532
3980
|
usrtxt = str(eval(usrtxt[3:]))
|
|
3533
3981
|
self.indexed_items[self.cursor_pos][1][self.selected_column] = usrtxt
|
|
3534
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
|
+
|
|
3535
4024
|
|
|
3536
4025
|
elif self.check_key("edit_picker", key, self.keys_dict):
|
|
3537
4026
|
self.logger.info(f"key_function edit_picker")
|
|
3538
4027
|
if len(self.indexed_items) > 0 and self.selected_column >=0 and self.editable_columns[self.selected_column]:
|
|
3539
4028
|
current_val = self.indexed_items[self.cursor_pos][1][self.selected_column]
|
|
3540
4029
|
usrtxt = f"{current_val}"
|
|
3541
|
-
field_end_f = lambda: self.
|
|
3542
|
-
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.
|
|
3543
|
-
else: field_end_f = lambda: self.
|
|
4030
|
+
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
4031
|
+
if self.show_footer and self.footer.height >= 2: field_end_f = lambda: self.get_term_size()[1]-38
|
|
4032
|
+
else: field_end_f = lambda: self.get_term_size()[1]-3
|
|
3544
4033
|
self.set_registers()
|
|
3545
4034
|
words = self.get_word_list()
|
|
3546
4035
|
usrtxt, return_val = input_field(
|
|
@@ -3548,10 +4037,10 @@ class Picker:
|
|
|
3548
4037
|
usrtxt=usrtxt,
|
|
3549
4038
|
field_prefix=" Edit value: ",
|
|
3550
4039
|
x=lambda:2,
|
|
3551
|
-
y=lambda: self.
|
|
4040
|
+
y=lambda: self.get_term_size()[0]-2,
|
|
3552
4041
|
max_length=field_end_f,
|
|
3553
4042
|
registers=self.registers,
|
|
3554
|
-
refresh_screen_function=lambda: self.draw_screen(
|
|
4043
|
+
refresh_screen_function=lambda: self.draw_screen(),
|
|
3555
4044
|
history = self.history_edits,
|
|
3556
4045
|
path_auto_complete=True,
|
|
3557
4046
|
formula_auto_complete=True,
|
|
@@ -3563,7 +4052,7 @@ class Picker:
|
|
|
3563
4052
|
self.indexed_items[self.cursor_pos][1][self.selected_column] = usrtxt
|
|
3564
4053
|
self.history_edits.append(usrtxt)
|
|
3565
4054
|
elif self.check_key("edit_ipython", key, self.keys_dict):
|
|
3566
|
-
self.logger.info(f"key_function
|
|
4055
|
+
self.logger.info(f"key_function edit_ipython")
|
|
3567
4056
|
import IPython, termios
|
|
3568
4057
|
self.stdscr.clear()
|
|
3569
4058
|
restrict_curses(self.stdscr)
|
|
@@ -3600,11 +4089,11 @@ class Picker:
|
|
|
3600
4089
|
self.stdscr.clear()
|
|
3601
4090
|
self.stdscr.refresh()
|
|
3602
4091
|
self.initialise_variables()
|
|
3603
|
-
self.draw_screen(
|
|
4092
|
+
self.draw_screen()
|
|
3604
4093
|
|
|
3605
4094
|
|
|
3606
4095
|
|
|
3607
|
-
self.draw_screen(
|
|
4096
|
+
self.draw_screen(clear=clear_screen)
|
|
3608
4097
|
|
|
3609
4098
|
|
|
3610
4099
|
|
|
@@ -3707,7 +4196,7 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
|
|
|
3707
4196
|
# input_arg = args.filename
|
|
3708
4197
|
|
|
3709
4198
|
elif args.generate:
|
|
3710
|
-
function_data["refresh_function"] = lambda :
|
|
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)
|
|
3711
4200
|
function_data["get_data_startup"] = True
|
|
3712
4201
|
function_data["get_new_data"] = True
|
|
3713
4202
|
return args, function_data
|
|
@@ -3806,14 +4295,6 @@ def unrestrict_curses(stdscr: curses.window) -> None:
|
|
|
3806
4295
|
def main() -> None:
|
|
3807
4296
|
""" Main function when listpick is executed. Deals with command line arguments and starts a Picker. """
|
|
3808
4297
|
args, function_data = parse_arguments()
|
|
3809
|
-
|
|
3810
|
-
try:
|
|
3811
|
-
if function_data["items"] == []:
|
|
3812
|
-
function_data["items"] = test_items
|
|
3813
|
-
function_data["highlights"] = test_highlights
|
|
3814
|
-
function_data["header"] = test_header
|
|
3815
|
-
except:
|
|
3816
|
-
pass
|
|
3817
4298
|
|
|
3818
4299
|
# function_data["colour_theme_number"] = 3
|
|
3819
4300
|
function_data["highlights"] = [
|
|
@@ -3959,7 +4440,7 @@ def main() -> None:
|
|
|
3959
4440
|
# function_data["cell_cursor"] = True
|
|
3960
4441
|
# function_data["display_modes"] = True
|
|
3961
4442
|
# function_data["centre_in_cols"] = True
|
|
3962
|
-
|
|
4443
|
+
function_data["show_row_header"] = True
|
|
3963
4444
|
# function_data["keys_dict"] = picker_keys
|
|
3964
4445
|
# function_data["id_column"] = -1
|
|
3965
4446
|
# function_data["track_entries_upon_refresh"] = True
|
|
@@ -3975,10 +4456,23 @@ def main() -> None:
|
|
|
3975
4456
|
# function_data["debug"] = True
|
|
3976
4457
|
# function_data["debug_level"] = 1
|
|
3977
4458
|
|
|
4459
|
+
# function_data["cell_cursor"] = False
|
|
4460
|
+
|
|
3978
4461
|
function_data["split_right"] = False
|
|
3979
|
-
function_data["
|
|
4462
|
+
function_data["split_left"] = False
|
|
4463
|
+
function_data["right_pane_index"] = 2
|
|
4464
|
+
function_data["left_pane_index"] = 0
|
|
3980
4465
|
|
|
3981
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
|
+
},
|
|
3982
4476
|
# Graph or random numbers generated each second
|
|
3983
4477
|
{
|
|
3984
4478
|
"proportion": 1/2,
|
|
@@ -3990,22 +4484,31 @@ def main() -> None:
|
|
|
3990
4484
|
},
|
|
3991
4485
|
# list of numbers
|
|
3992
4486
|
{
|
|
3993
|
-
"proportion":
|
|
4487
|
+
"proportion": 1/3,
|
|
3994
4488
|
"auto_refresh": False,
|
|
3995
4489
|
"get_data": data_refresh_randint_title,
|
|
3996
4490
|
"display": right_split_display_list,
|
|
3997
4491
|
"data": ["Files", [str(x) for x in range(100)]],
|
|
3998
4492
|
"refresh_time": 1.0,
|
|
3999
4493
|
},
|
|
4000
|
-
# File
|
|
4494
|
+
# File attributes
|
|
4001
4495
|
{
|
|
4002
|
-
"proportion":
|
|
4496
|
+
"proportion": 1/3,
|
|
4003
4497
|
"auto_refresh": False,
|
|
4004
4498
|
"get_data": lambda data, state: [],
|
|
4005
4499
|
"display": right_split_file_attributes,
|
|
4006
|
-
"data": [
|
|
4500
|
+
"data": [],
|
|
4007
4501
|
"refresh_time": 1.0,
|
|
4008
4502
|
},
|
|
4503
|
+
# File attributes dynamic
|
|
4504
|
+
{
|
|
4505
|
+
"proportion": 1/3,
|
|
4506
|
+
"auto_refresh": True,
|
|
4507
|
+
"get_data": update_file_attributes,
|
|
4508
|
+
"display": right_split_file_attributes_dynamic,
|
|
4509
|
+
"data": [],
|
|
4510
|
+
"refresh_time": 2.0,
|
|
4511
|
+
},
|
|
4009
4512
|
# List of random numbers generated each second
|
|
4010
4513
|
{
|
|
4011
4514
|
"proportion": 1/2,
|
|
@@ -4025,7 +4528,79 @@ def main() -> None:
|
|
|
4025
4528
|
"refresh_time": 1,
|
|
4026
4529
|
},
|
|
4027
4530
|
]
|
|
4028
|
-
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"]]
|
|
4029
4604
|
|
|
4030
4605
|
stdscr = start_curses()
|
|
4031
4606
|
try:
|