listpick 0.1.16.11__py3-none-any.whl → 0.1.16.12__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.
Potentially problematic release.
This version of listpick might be problematic. Click here for more details.
- listpick/listpick_app.py +454 -227
- listpick/ui/build_help.py +8 -7
- listpick/ui/keys.py +2 -1
- listpick/utils/generate_data_multiprocessing.py +10 -0
- listpick/utils/generate_data_multithreaded.py +36 -12
- listpick/utils/generate_data_utils.py +43 -0
- listpick/utils/utils.py +3 -0
- {listpick-0.1.16.11.dist-info → listpick-0.1.16.12.dist-info}/METADATA +24 -36
- {listpick-0.1.16.11.dist-info → listpick-0.1.16.12.dist-info}/RECORD +13 -11
- {listpick-0.1.16.11.dist-info → listpick-0.1.16.12.dist-info}/WHEEL +0 -0
- {listpick-0.1.16.11.dist-info → listpick-0.1.16.12.dist-info}/entry_points.txt +0 -0
- {listpick-0.1.16.11.dist-info → listpick-0.1.16.12.dist-info}/licenses/LICENSE.txt +0 -0
- {listpick-0.1.16.11.dist-info → listpick-0.1.16.12.dist-info}/top_level.txt +0 -0
listpick/listpick_app.py
CHANGED
|
@@ -18,9 +18,13 @@ 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
24
|
import copy
|
|
25
|
+
import tempfile
|
|
26
|
+
import queue
|
|
27
|
+
from listpick.utils.generate_data_utils import ProcessSafePriorityQueue
|
|
24
28
|
|
|
25
29
|
from listpick.pane.pane_utils import get_file_attributes
|
|
26
30
|
from listpick.pane.left_pane_functions import *
|
|
@@ -70,9 +74,9 @@ class Picker:
|
|
|
70
74
|
auto_refresh: bool =False,
|
|
71
75
|
timer: float = 5,
|
|
72
76
|
|
|
73
|
-
get_new_data: bool =False,
|
|
74
|
-
refresh_function: Optional[Callable] = lambda items, header, visible_rows_indices, getting_data: None,
|
|
75
|
-
get_data_startup: bool =False,
|
|
77
|
+
get_new_data: bool =False,
|
|
78
|
+
refresh_function: Optional[Callable] = lambda items, header, visible_rows_indices, getting_data: None,
|
|
79
|
+
get_data_startup: bool =False,
|
|
76
80
|
track_entries_upon_refresh: bool = True,
|
|
77
81
|
pin_cursor: bool = False,
|
|
78
82
|
id_column: int = 0,
|
|
@@ -95,8 +99,12 @@ class Picker:
|
|
|
95
99
|
user_opts : str = "",
|
|
96
100
|
options_list: list[str] = [],
|
|
97
101
|
user_settings : str = "",
|
|
102
|
+
|
|
98
103
|
separator : str = " ",
|
|
99
104
|
header_separator : str = " │",
|
|
105
|
+
header_separator_before_selected_column : str = " ▐",
|
|
106
|
+
|
|
107
|
+
|
|
100
108
|
search_query : str = "",
|
|
101
109
|
search_count : int = 0,
|
|
102
110
|
search_index : int = 0,
|
|
@@ -111,6 +119,10 @@ class Picker:
|
|
|
111
119
|
highlight_full_row: bool =False,
|
|
112
120
|
crosshair_cursor: bool = False,
|
|
113
121
|
cell_cursor: bool = True,
|
|
122
|
+
selected_char: str = "",
|
|
123
|
+
unselected_char: str = "",
|
|
124
|
+
selecting_char: str = "",
|
|
125
|
+
deselecting_char: str = "",
|
|
114
126
|
|
|
115
127
|
items_per_page : int = -1,
|
|
116
128
|
sort_method : int = 0,
|
|
@@ -197,6 +209,8 @@ class Picker:
|
|
|
197
209
|
left_pane_index: int = 0,
|
|
198
210
|
|
|
199
211
|
screen_size_function = lambda stdscr: os.get_terminal_size()[::-1],
|
|
212
|
+
generate_data_for_hidden_columns: bool = False,
|
|
213
|
+
|
|
200
214
|
|
|
201
215
|
# getting_data: threading.Event = threading.Event(),
|
|
202
216
|
|
|
@@ -245,6 +259,7 @@ class Picker:
|
|
|
245
259
|
self.user_settings = user_settings
|
|
246
260
|
self.separator = separator
|
|
247
261
|
self.header_separator = header_separator
|
|
262
|
+
self.header_separator_before_selected_column = header_separator_before_selected_column
|
|
248
263
|
self.search_query = search_query
|
|
249
264
|
self.search_count = search_count
|
|
250
265
|
self.search_index = search_index
|
|
@@ -259,6 +274,10 @@ class Picker:
|
|
|
259
274
|
self.highlight_full_row = highlight_full_row
|
|
260
275
|
self.crosshair_cursor = crosshair_cursor
|
|
261
276
|
self.cell_cursor = cell_cursor
|
|
277
|
+
self.selected_char = selected_char
|
|
278
|
+
self.unselected_char = unselected_char
|
|
279
|
+
self.selecting_char = selecting_char
|
|
280
|
+
self.deselecting_char = deselecting_char
|
|
262
281
|
|
|
263
282
|
self.items_per_page = items_per_page
|
|
264
283
|
self.sort_method = sort_method
|
|
@@ -361,7 +380,17 @@ class Picker:
|
|
|
361
380
|
|
|
362
381
|
self.visible_rows_indices = []
|
|
363
382
|
|
|
383
|
+
self.generate_data_for_hidden_columns = generate_data_for_hidden_columns
|
|
384
|
+
self.thread_stop_event = threading.Event()
|
|
385
|
+
self.data_generation_queue = queue.PriorityQueue()
|
|
386
|
+
self.threads = []
|
|
387
|
+
|
|
364
388
|
|
|
389
|
+
self.process_manager = multiprocessing.Manager()
|
|
390
|
+
# self.data_generation_queue = ProcessSafePriorityQueue
|
|
391
|
+
self.processes = []
|
|
392
|
+
self.items_sync_loop_event = threading.Event()
|
|
393
|
+
self.items_sync_thread = None
|
|
365
394
|
|
|
366
395
|
|
|
367
396
|
self.initialise_picker_state(reset_colours=self.reset_colours)
|
|
@@ -541,8 +570,8 @@ class Picker:
|
|
|
541
570
|
self.top_space += ((self.term_h-(self.top_space+self.bottom_space))-len(self.indexed_items))//2
|
|
542
571
|
|
|
543
572
|
# self.column_widths
|
|
544
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
545
|
-
visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
|
|
573
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
574
|
+
visible_columns_total_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
546
575
|
|
|
547
576
|
# self.startx
|
|
548
577
|
self.startx = 1 if self.highlight_full_row else 2
|
|
@@ -649,7 +678,7 @@ class Picker:
|
|
|
649
678
|
tracking = False
|
|
650
679
|
|
|
651
680
|
## Get data synchronously
|
|
652
|
-
if get_data
|
|
681
|
+
if get_data:
|
|
653
682
|
# Track cursor_pos and selections by ther id (row[self.id_column][col])
|
|
654
683
|
if self.track_entries_upon_refresh and len(self.items) > 0:
|
|
655
684
|
tracking = True
|
|
@@ -665,7 +694,13 @@ class Picker:
|
|
|
665
694
|
# Set the state of the threading event
|
|
666
695
|
# Though we are getting data synchronously, we ensure the correct state for self.getting_data
|
|
667
696
|
self.getting_data.clear()
|
|
668
|
-
self.refresh_function(
|
|
697
|
+
self.refresh_function(
|
|
698
|
+
self.items,
|
|
699
|
+
self.header,
|
|
700
|
+
self.visible_rows_indices,
|
|
701
|
+
self.getting_data,
|
|
702
|
+
self.get_function_data(),
|
|
703
|
+
)
|
|
669
704
|
|
|
670
705
|
|
|
671
706
|
# Ensure that an emtpy items object has the form [[]]
|
|
@@ -942,8 +977,8 @@ class Picker:
|
|
|
942
977
|
|
|
943
978
|
self.get_visible_rows()
|
|
944
979
|
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)
|
|
945
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
946
|
-
visible_columns_total_width = sum(visible_column_widths) + len(self.separator)*(len(visible_column_widths)-1)
|
|
980
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
981
|
+
visible_columns_total_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
947
982
|
|
|
948
983
|
|
|
949
984
|
## Display title
|
|
@@ -990,7 +1025,10 @@ class Picker:
|
|
|
990
1025
|
|
|
991
1026
|
|
|
992
1027
|
header_str += f"{col_str:^{self.column_widths[i]-len(number)}}"
|
|
993
|
-
|
|
1028
|
+
if i == self.selected_column-1:
|
|
1029
|
+
header_str += self.header_separator_before_selected_column
|
|
1030
|
+
else:
|
|
1031
|
+
header_str += self.header_separator
|
|
994
1032
|
header_str_w = min(self.rows_w-self.left_gutter_width, visible_columns_total_width+1, self.term_w-self.startx)
|
|
995
1033
|
|
|
996
1034
|
header_str = header_str[self.leftmost_char:]
|
|
@@ -1150,10 +1188,11 @@ class Picker:
|
|
|
1150
1188
|
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)
|
|
1151
1189
|
if not match: continue
|
|
1152
1190
|
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)
|
|
1191
|
+
width = min(self.column_widths[highlight["field"]]-(field_start-self.leftmost_char), self.rows_w-self.left_gutter_width)
|
|
1153
1192
|
|
|
1154
1193
|
## We want to search the non-centred values but highlight the centred values.
|
|
1155
1194
|
if self.centre_in_cols:
|
|
1156
|
-
tmp = truncate_to_display_width(item[1][highlight["field"]],
|
|
1195
|
+
tmp = truncate_to_display_width(item[1][highlight["field"]], width, self.centre_in_cols, self.unicode_char_width)
|
|
1157
1196
|
field_start += (len(tmp) - len(tmp.lstrip()))
|
|
1158
1197
|
|
|
1159
1198
|
highlight_start = field_start + match.start()
|
|
@@ -1164,7 +1203,7 @@ class Picker:
|
|
|
1164
1203
|
continue
|
|
1165
1204
|
highlight_start -= self.leftmost_char
|
|
1166
1205
|
highlight_end -= self.leftmost_char
|
|
1167
|
-
self.stdscr.addstr(y, max(self.startx, self.startx+highlight_start), row_str[max(highlight_start,0):min(self.rows_w-self.
|
|
1206
|
+
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)
|
|
1168
1207
|
except:
|
|
1169
1208
|
pass
|
|
1170
1209
|
|
|
@@ -1180,7 +1219,7 @@ class Picker:
|
|
|
1180
1219
|
l0_highlights, l1_highlights, l2_highlights = sort_highlights(self.highlights)
|
|
1181
1220
|
|
|
1182
1221
|
|
|
1183
|
-
row_width = sum(self.
|
|
1222
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
1184
1223
|
for idx in range(start_index, end_index):
|
|
1185
1224
|
item = self.indexed_items[idx]
|
|
1186
1225
|
y = idx - start_index + self.top_space
|
|
@@ -1200,7 +1239,7 @@ class Picker:
|
|
|
1200
1239
|
# trunc_width = 0
|
|
1201
1240
|
|
|
1202
1241
|
|
|
1203
|
-
trunc_width = min(self.rows_w-self.left_gutter_width, row_width, self.term_w - self.startx)
|
|
1242
|
+
trunc_width = max(0, min(self.rows_w-self.left_gutter_width, row_width, self.term_w - self.startx))
|
|
1204
1243
|
|
|
1205
1244
|
row_str = truncate_to_display_width(row_str_left_adj, trunc_width, self.unicode_char_width)
|
|
1206
1245
|
# row_str = truncate_to_display_width(row_str, min(w-self.startx, visible_columns_total_width))[self.leftmost_char:]
|
|
@@ -1211,7 +1250,7 @@ class Picker:
|
|
|
1211
1250
|
|
|
1212
1251
|
## Highlight column
|
|
1213
1252
|
if self.crosshair_cursor:
|
|
1214
|
-
highlight_cell(idx, self.selected_column, visible_column_widths, colour_pair_number=27, bold=False, y=y)
|
|
1253
|
+
highlight_cell(idx, self.selected_column, self.visible_column_widths, colour_pair_number=27, bold=False, y=y)
|
|
1215
1254
|
if idx == self.cursor_pos:
|
|
1216
1255
|
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))
|
|
1217
1256
|
|
|
@@ -1225,21 +1264,21 @@ class Picker:
|
|
|
1225
1264
|
# self.selected_cells_by_row = get_selected_cells_by_row(self.cell_selections)
|
|
1226
1265
|
if item[0] in self.selected_cells_by_row:
|
|
1227
1266
|
for j in self.selected_cells_by_row[item[0]]:
|
|
1228
|
-
highlight_cell(idx, j, visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1267
|
+
highlight_cell(idx, j, self.visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1229
1268
|
|
|
1230
1269
|
# Visually selected
|
|
1231
1270
|
if self.is_selecting:
|
|
1232
1271
|
if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
|
|
1233
1272
|
x_interval = range(min(self.start_selection_col, self.selected_column), max(self.start_selection_col, self.selected_column)+1)
|
|
1234
1273
|
for col in x_interval:
|
|
1235
|
-
highlight_cell(idx, col, visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1274
|
+
highlight_cell(idx, col, self.visible_column_widths, colour_pair_number=25, bold=False, y=y)
|
|
1236
1275
|
|
|
1237
1276
|
# Visually deslected
|
|
1238
1277
|
if self.is_deselecting:
|
|
1239
1278
|
if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
|
|
1240
1279
|
x_interval = range(min(self.start_selection_col, self.selected_column), max(self.start_selection_col, self.selected_column)+1)
|
|
1241
1280
|
for col in x_interval:
|
|
1242
|
-
highlight_cell(idx, col, visible_column_widths, colour_pair_number=26, bold=False,y=y)
|
|
1281
|
+
highlight_cell(idx, col, self.visible_column_widths, colour_pair_number=26, bold=False,y=y)
|
|
1243
1282
|
# Higlight cursor row and selected rows
|
|
1244
1283
|
elif self.highlight_full_row:
|
|
1245
1284
|
if self.selections[item[0]]:
|
|
@@ -1256,16 +1295,30 @@ class Picker:
|
|
|
1256
1295
|
|
|
1257
1296
|
# Highlight the cursor row and the first char of the selected rows.
|
|
1258
1297
|
else:
|
|
1259
|
-
if self.
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1298
|
+
if self.selected_char:
|
|
1299
|
+
if self.selections[item[0]]:
|
|
1300
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.selected_char, curses.color_pair(self.colours_start+2))
|
|
1301
|
+
else:
|
|
1302
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.unselected_char, curses.color_pair(self.colours_start+2))
|
|
1303
|
+
# Visually selected
|
|
1304
|
+
if self.is_selecting:
|
|
1305
|
+
if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
|
|
1306
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.selecting_char, curses.color_pair(self.colours_start+2))
|
|
1307
|
+
# Visually deslected
|
|
1308
|
+
if self.is_deselecting:
|
|
1309
|
+
if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
|
|
1310
|
+
self.stdscr.addstr(y, max(self.startx-2,0), self.deselecting_char, curses.color_pair(self.colours_start+2))
|
|
1311
|
+
else:
|
|
1312
|
+
if self.selections[item[0]]:
|
|
1264
1313
|
self.stdscr.addstr(y, max(self.startx-2,0), ' ', curses.color_pair(self.colours_start+1))
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1314
|
+
# Visually selected
|
|
1315
|
+
if self.is_selecting:
|
|
1316
|
+
if self.start_selection <= idx <= self.cursor_pos or self.start_selection >= idx >= self.cursor_pos:
|
|
1317
|
+
self.stdscr.addstr(y, max(self.startx-2,0), ' ', curses.color_pair(self.colours_start+1))
|
|
1318
|
+
# Visually deslected
|
|
1319
|
+
if self.is_deselecting:
|
|
1320
|
+
if self.start_selection >= idx >= self.cursor_pos or self.start_selection <= idx <= self.cursor_pos:
|
|
1321
|
+
self.stdscr.addstr(y, max(self.startx-2,0), ' ', curses.color_pair(self.colours_start+10))
|
|
1269
1322
|
|
|
1270
1323
|
if not self.highlights_hide:
|
|
1271
1324
|
draw_highlights(l1_highlights, idx, y, item)
|
|
@@ -1275,7 +1328,7 @@ class Picker:
|
|
|
1275
1328
|
# Draw cursor
|
|
1276
1329
|
if idx == self.cursor_pos:
|
|
1277
1330
|
if self.cell_cursor:
|
|
1278
|
-
highlight_cell(idx, self.selected_column, visible_column_widths, colour_pair_number=5, bold=True, y=y)
|
|
1331
|
+
highlight_cell(idx, self.selected_column, self.visible_column_widths, colour_pair_number=5, bold=True, y=y)
|
|
1279
1332
|
else:
|
|
1280
1333
|
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)
|
|
1281
1334
|
|
|
@@ -1380,6 +1433,21 @@ class Picker:
|
|
|
1380
1433
|
# 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
|
|
1381
1434
|
|
|
1382
1435
|
|
|
1436
|
+
def refresh_and_draw_screen(self):
|
|
1437
|
+
"""
|
|
1438
|
+
Clears and refreshes the screen, restricts and unrestricts curses,
|
|
1439
|
+
ensures correct terminal settings, and then draws the screen.
|
|
1440
|
+
"""
|
|
1441
|
+
|
|
1442
|
+
self.logger.info(f"key_function redraw_screen")
|
|
1443
|
+
self.stdscr.clear()
|
|
1444
|
+
self.stdscr.refresh()
|
|
1445
|
+
restrict_curses(self.stdscr)
|
|
1446
|
+
unrestrict_curses(self.stdscr)
|
|
1447
|
+
self.stdscr.clear()
|
|
1448
|
+
self.stdscr.refresh()
|
|
1449
|
+
|
|
1450
|
+
self.draw_screen()
|
|
1383
1451
|
|
|
1384
1452
|
def infobox(self, stdscr: curses.window, message: str ="", title: str ="Infobox", colours_end: int = 0, duration: int = 4) -> curses.window:
|
|
1385
1453
|
""" Display non-interactive infobox window. """
|
|
@@ -1435,116 +1503,126 @@ class Picker:
|
|
|
1435
1503
|
self.logger.debug(f"function: get_function_data()")
|
|
1436
1504
|
""" Returns a dict of the main variables needed to restore the state of list_pikcer. """
|
|
1437
1505
|
function_data = {
|
|
1438
|
-
"
|
|
1439
|
-
"
|
|
1440
|
-
"
|
|
1441
|
-
"
|
|
1442
|
-
"
|
|
1443
|
-
"
|
|
1444
|
-
"
|
|
1445
|
-
"
|
|
1446
|
-
"
|
|
1447
|
-
"
|
|
1448
|
-
"
|
|
1449
|
-
"
|
|
1450
|
-
"
|
|
1451
|
-
"
|
|
1452
|
-
"
|
|
1453
|
-
"
|
|
1454
|
-
"
|
|
1455
|
-
"
|
|
1456
|
-
"
|
|
1457
|
-
"
|
|
1458
|
-
"
|
|
1459
|
-
"
|
|
1460
|
-
"
|
|
1461
|
-
"
|
|
1462
|
-
"
|
|
1463
|
-
"
|
|
1464
|
-
"
|
|
1465
|
-
"
|
|
1466
|
-
"
|
|
1467
|
-
"
|
|
1468
|
-
"
|
|
1469
|
-
"
|
|
1470
|
-
"
|
|
1471
|
-
"
|
|
1472
|
-
"
|
|
1473
|
-
"
|
|
1474
|
-
"
|
|
1475
|
-
"
|
|
1476
|
-
"
|
|
1477
|
-
"
|
|
1478
|
-
"
|
|
1479
|
-
"
|
|
1480
|
-
"
|
|
1481
|
-
"
|
|
1482
|
-
"
|
|
1483
|
-
"
|
|
1484
|
-
"
|
|
1485
|
-
"
|
|
1486
|
-
"
|
|
1487
|
-
"
|
|
1488
|
-
"
|
|
1489
|
-
"
|
|
1490
|
-
"
|
|
1491
|
-
"
|
|
1492
|
-
"
|
|
1493
|
-
"
|
|
1494
|
-
"
|
|
1495
|
-
"
|
|
1496
|
-
"
|
|
1497
|
-
"
|
|
1498
|
-
"
|
|
1499
|
-
"
|
|
1500
|
-
"
|
|
1501
|
-
"
|
|
1502
|
-
"
|
|
1503
|
-
"
|
|
1504
|
-
"
|
|
1505
|
-
"
|
|
1506
|
-
"
|
|
1507
|
-
"
|
|
1508
|
-
"
|
|
1509
|
-
"
|
|
1510
|
-
"
|
|
1511
|
-
"
|
|
1512
|
-
"
|
|
1513
|
-
"
|
|
1514
|
-
"
|
|
1515
|
-
"
|
|
1516
|
-
"
|
|
1517
|
-
"
|
|
1518
|
-
"
|
|
1519
|
-
"
|
|
1520
|
-
"
|
|
1521
|
-
"
|
|
1522
|
-
"
|
|
1523
|
-
"
|
|
1524
|
-
"
|
|
1525
|
-
"
|
|
1526
|
-
"
|
|
1527
|
-
"
|
|
1528
|
-
"
|
|
1529
|
-
"
|
|
1530
|
-
"
|
|
1531
|
-
"
|
|
1532
|
-
"
|
|
1533
|
-
"
|
|
1534
|
-
"
|
|
1535
|
-
"
|
|
1536
|
-
"
|
|
1537
|
-
"
|
|
1538
|
-
"
|
|
1539
|
-
"
|
|
1540
|
-
"
|
|
1541
|
-
"
|
|
1542
|
-
"
|
|
1543
|
-
"
|
|
1544
|
-
"
|
|
1545
|
-
"
|
|
1546
|
-
"
|
|
1547
|
-
|
|
1506
|
+
"self": self,
|
|
1507
|
+
"selections": self.selections,
|
|
1508
|
+
"cell_selections": self.cell_selections,
|
|
1509
|
+
"selected_cells_by_row": self.selected_cells_by_row,
|
|
1510
|
+
"items_per_page": self.items_per_page,
|
|
1511
|
+
"current_row": self.current_row,
|
|
1512
|
+
"current_page": self.current_page,
|
|
1513
|
+
"cursor_pos": self.cursor_pos,
|
|
1514
|
+
"colours": self.colours,
|
|
1515
|
+
"colour_theme_number": self.colour_theme_number,
|
|
1516
|
+
"selected_column": self.selected_column,
|
|
1517
|
+
"sort_column": self.sort_column,
|
|
1518
|
+
"sort_method": self.sort_method,
|
|
1519
|
+
"sort_reverse": self.sort_reverse,
|
|
1520
|
+
"SORT_METHODS": self.SORT_METHODS,
|
|
1521
|
+
"hidden_columns": self.hidden_columns,
|
|
1522
|
+
"is_selecting": self.is_selecting,
|
|
1523
|
+
"is_deselecting": self.is_deselecting,
|
|
1524
|
+
"user_opts": self.user_opts,
|
|
1525
|
+
"options_list": self.options_list,
|
|
1526
|
+
"user_settings": self.user_settings,
|
|
1527
|
+
"separator": self.separator,
|
|
1528
|
+
"header_separator": self.header_separator,
|
|
1529
|
+
"header_separator_before_selected_column": self.header_separator_before_selected_column,
|
|
1530
|
+
"search_query": self.search_query,
|
|
1531
|
+
"search_count": self.search_count,
|
|
1532
|
+
"search_index": self.search_index,
|
|
1533
|
+
"filter_query": self.filter_query,
|
|
1534
|
+
"indexed_items": self.indexed_items,
|
|
1535
|
+
"start_selection": self.start_selection,
|
|
1536
|
+
"start_selection_col": self.start_selection_col,
|
|
1537
|
+
"end_selection": self.end_selection,
|
|
1538
|
+
"highlights": self.highlights,
|
|
1539
|
+
"max_column_width": self.max_column_width,
|
|
1540
|
+
"column_indices": self.column_indices,
|
|
1541
|
+
"mode_index": self.mode_index,
|
|
1542
|
+
"modes": self.modes,
|
|
1543
|
+
"title": self.title,
|
|
1544
|
+
"display_modes": self.display_modes,
|
|
1545
|
+
"require_option": self.require_option,
|
|
1546
|
+
"require_option_default": self.require_option_default,
|
|
1547
|
+
"option_functions": self.option_functions,
|
|
1548
|
+
"top_gap": self.top_gap,
|
|
1549
|
+
"number_columns": self.number_columns,
|
|
1550
|
+
"items": self.items,
|
|
1551
|
+
"indexed_items": self.indexed_items,
|
|
1552
|
+
"header": self.header,
|
|
1553
|
+
"scroll_bar": self.scroll_bar,
|
|
1554
|
+
"columns_sort_method": self.columns_sort_method,
|
|
1555
|
+
"disabled_keys": self.disabled_keys,
|
|
1556
|
+
"show_footer": self.show_footer,
|
|
1557
|
+
"footer_string": self.footer_string,
|
|
1558
|
+
"footer_string_auto_refresh": self.footer_string_auto_refresh,
|
|
1559
|
+
"footer_string_refresh_function": self.footer_string_refresh_function,
|
|
1560
|
+
"footer_timer": self.footer_timer,
|
|
1561
|
+
"footer_style": self.footer_style,
|
|
1562
|
+
"colours_start": self.colours_start,
|
|
1563
|
+
"colours_end": self.colours_end,
|
|
1564
|
+
"display_only": self.display_only,
|
|
1565
|
+
"infobox_items": self.infobox_items,
|
|
1566
|
+
"display_infobox": self.display_infobox,
|
|
1567
|
+
"infobox_title": self.infobox_title,
|
|
1568
|
+
"key_remappings": self.key_remappings,
|
|
1569
|
+
"auto_refresh": self.auto_refresh,
|
|
1570
|
+
"get_new_data": self.get_new_data,
|
|
1571
|
+
"refresh_function": self.refresh_function,
|
|
1572
|
+
"timer": self.timer,
|
|
1573
|
+
"get_data_startup": self.get_data_startup,
|
|
1574
|
+
"get_footer_string_startup": self.get_footer_string_startup,
|
|
1575
|
+
"editable_columns": self.editable_columns,
|
|
1576
|
+
"last_key": self.last_key,
|
|
1577
|
+
"centre_in_terminal": self.centre_in_terminal,
|
|
1578
|
+
"centre_in_terminal_vertical": self.centre_in_terminal_vertical,
|
|
1579
|
+
"centre_in_cols": self.centre_in_cols,
|
|
1580
|
+
"highlight_full_row": self.highlight_full_row,
|
|
1581
|
+
"cell_cursor": self.cell_cursor,
|
|
1582
|
+
"column_widths": self.column_widths,
|
|
1583
|
+
"track_entries_upon_refresh": self.track_entries_upon_refresh,
|
|
1584
|
+
"pin_cursor": self.pin_cursor,
|
|
1585
|
+
"id_column": self.id_column,
|
|
1586
|
+
"startup_notification": self.startup_notification,
|
|
1587
|
+
"keys_dict": self.keys_dict,
|
|
1588
|
+
"cancel_is_back": self.cancel_is_back,
|
|
1589
|
+
"paginate": self.paginate,
|
|
1590
|
+
"leftmost_char": self.leftmost_char,
|
|
1591
|
+
"history_filter_and_search" : self.history_filter_and_search,
|
|
1592
|
+
"history_pipes" : self.history_pipes,
|
|
1593
|
+
"history_opts" : self.history_opts,
|
|
1594
|
+
"history_edits" : self.history_edits,
|
|
1595
|
+
"history_settings": self.history_settings,
|
|
1596
|
+
"show_header": self.show_header,
|
|
1597
|
+
"show_row_header": self.show_row_header,
|
|
1598
|
+
"debug": self.debug,
|
|
1599
|
+
"debug_level": self.debug_level,
|
|
1600
|
+
"reset_colours": self.reset_colours,
|
|
1601
|
+
"unicode_char_width": self.unicode_char_width,
|
|
1602
|
+
"command_stack": self.command_stack,
|
|
1603
|
+
"loaded_file": self.loaded_file,
|
|
1604
|
+
"loaded_files": self.loaded_files,
|
|
1605
|
+
"loaded_file_index": self.loaded_file_index,
|
|
1606
|
+
"loaded_file_states": self.loaded_file_states,
|
|
1607
|
+
"sheet_index": self.sheet_index,
|
|
1608
|
+
"sheets": self.sheets,
|
|
1609
|
+
"sheet_name": self.sheet_name,
|
|
1610
|
+
"sheet_states": self.sheet_states,
|
|
1611
|
+
"split_right": self.split_right,
|
|
1612
|
+
"right_panes": self.right_panes,
|
|
1613
|
+
"right_pane_index": self.right_pane_index,
|
|
1614
|
+
"split_left": self.split_left,
|
|
1615
|
+
"left_panes": self.left_panes,
|
|
1616
|
+
"left_pane_index": self.left_pane_index,
|
|
1617
|
+
"crosshair_cursor": self.crosshair_cursor,
|
|
1618
|
+
"generate_data_for_hidden_columns": self.generate_data_for_hidden_columns,
|
|
1619
|
+
"thread_stop_event": self.thread_stop_event,
|
|
1620
|
+
"data_generation_queue": self.data_generation_queue,
|
|
1621
|
+
"process_manager": self.process_manager,
|
|
1622
|
+
"threads": self.threads,
|
|
1623
|
+
"processes": self.processes,
|
|
1624
|
+
"items_sync_loop_event": self.items_sync_loop_event,
|
|
1625
|
+
"items_sync_thread": self.items_sync_thread,
|
|
1548
1626
|
}
|
|
1549
1627
|
return function_data
|
|
1550
1628
|
|
|
@@ -1597,15 +1675,6 @@ class Picker:
|
|
|
1597
1675
|
self.initialise_picker_state(reset_colours=reset_colours)
|
|
1598
1676
|
|
|
1599
1677
|
self.initialise_variables()
|
|
1600
|
-
# if "colour_theme_number" in function_data:
|
|
1601
|
-
# global COLOURS_SET
|
|
1602
|
-
# COLOURS_SET = False
|
|
1603
|
-
# colours_end = set_colours(pick=self.colour_theme_number, start=self.colours_start)
|
|
1604
|
-
|
|
1605
|
-
# if "items" in function_data: self.items = function_data["items"]
|
|
1606
|
-
# if "header" in function_data: self.header = function_data["header"]
|
|
1607
|
-
# self.indexed_items = function_data["indexed_items"] if "indexed_items" in function_data else []
|
|
1608
|
-
|
|
1609
1678
|
|
|
1610
1679
|
|
|
1611
1680
|
def delete_entries(self) -> None:
|
|
@@ -1679,6 +1748,7 @@ class Picker:
|
|
|
1679
1748
|
"split_left": False,
|
|
1680
1749
|
"cell_cursor": False,
|
|
1681
1750
|
"crosshair_cursor": False,
|
|
1751
|
+
"header_separator": " │",
|
|
1682
1752
|
}
|
|
1683
1753
|
while True:
|
|
1684
1754
|
self.update_term_size()
|
|
@@ -1701,6 +1771,102 @@ class Picker:
|
|
|
1701
1771
|
return {}, "", f
|
|
1702
1772
|
|
|
1703
1773
|
|
|
1774
|
+
def select_columns(
|
|
1775
|
+
self,
|
|
1776
|
+
stdscr: curses.window,
|
|
1777
|
+
# options: list[list[str]] =[],
|
|
1778
|
+
# title: str = "Choose option",
|
|
1779
|
+
# x:int=0,
|
|
1780
|
+
# y:int=0,
|
|
1781
|
+
# literal:bool=False,
|
|
1782
|
+
# colours_start:int=0,
|
|
1783
|
+
# header: list[str] = [],
|
|
1784
|
+
# require_option:list = [],
|
|
1785
|
+
# option_functions: list = [],
|
|
1786
|
+
) -> Tuple[dict, str, dict]:
|
|
1787
|
+
"""
|
|
1788
|
+
Display input field at x,y
|
|
1789
|
+
|
|
1790
|
+
---Arguments
|
|
1791
|
+
stdscr: curses screen
|
|
1792
|
+
usrtxt (str): text to be edited by the user
|
|
1793
|
+
title (str): The text to be displayed at the start of the text option picker
|
|
1794
|
+
x (int): prompt begins at (x,y) in the screen given
|
|
1795
|
+
y (int): prompt begins at (x,y) in the screen given
|
|
1796
|
+
colours_start (bool): start index of curses init_pair.
|
|
1797
|
+
|
|
1798
|
+
---Returns
|
|
1799
|
+
usrtxt, return_code
|
|
1800
|
+
usrtxt: the text inputted by the user
|
|
1801
|
+
return_code:
|
|
1802
|
+
0: user hit escape
|
|
1803
|
+
1: user hit return
|
|
1804
|
+
"""
|
|
1805
|
+
self.logger.info(f"function: select_columns()")
|
|
1806
|
+
|
|
1807
|
+
cursor = 0
|
|
1808
|
+
|
|
1809
|
+
if self.header:
|
|
1810
|
+
columns = [s for i, s in enumerate(self.header)]
|
|
1811
|
+
else:
|
|
1812
|
+
columns = [f"" for i in range(len(self.column_widths))]
|
|
1813
|
+
|
|
1814
|
+
## Column info variable
|
|
1815
|
+
columns_set = [[f"{i}", columns[i]] for i in range(len(self.column_widths))]
|
|
1816
|
+
header = ["#", "Column Name"]
|
|
1817
|
+
|
|
1818
|
+
selected = [False if i in self.hidden_columns else True for i in range(len(self.column_widths))]
|
|
1819
|
+
selected = {i: False if i in self.hidden_columns else True for i in range(len(self.column_widths))}
|
|
1820
|
+
|
|
1821
|
+
option_picker_data = {
|
|
1822
|
+
"items": columns_set,
|
|
1823
|
+
"colours": notification_colours,
|
|
1824
|
+
"colours_start": self.notification_colours_start,
|
|
1825
|
+
"title":"Select Columns",
|
|
1826
|
+
"header": header,
|
|
1827
|
+
"hidden_columns":[],
|
|
1828
|
+
# "require_option":require_option,
|
|
1829
|
+
# "keys_dict": options_keys,
|
|
1830
|
+
"selections": selected,
|
|
1831
|
+
"show_footer": False,
|
|
1832
|
+
"cancel_is_back": True,
|
|
1833
|
+
"number_columns": False,
|
|
1834
|
+
"reset_colours": False,
|
|
1835
|
+
"split_right": False,
|
|
1836
|
+
"split_left": False,
|
|
1837
|
+
"cell_cursor": False,
|
|
1838
|
+
"crosshair_cursor": False,
|
|
1839
|
+
"separator": " ",
|
|
1840
|
+
"header_separator": " │",
|
|
1841
|
+
"header_separator_before_selected_column": " ▐",
|
|
1842
|
+
"selected_char": "☒",
|
|
1843
|
+
"unselected_char": "☐",
|
|
1844
|
+
"selecting_char": "☒",
|
|
1845
|
+
"deselecting_char": "☐",
|
|
1846
|
+
}
|
|
1847
|
+
while True:
|
|
1848
|
+
self.update_term_size()
|
|
1849
|
+
|
|
1850
|
+
choose_opts_widths = get_column_widths(columns_set, unicode_char_width=self.unicode_char_width)
|
|
1851
|
+
window_width = min(max(sum(choose_opts_widths) + 6, 50) + 6, self.term_w)
|
|
1852
|
+
window_height = min(self.term_h//2, max(6, len(columns_set)+3))
|
|
1853
|
+
|
|
1854
|
+
submenu_win = curses.newwin(window_height, window_width, (self.term_h-window_height)//2, (self.term_w-window_width)//2)
|
|
1855
|
+
submenu_win.keypad(True)
|
|
1856
|
+
option_picker_data["screen_size_function"] = lambda stdscr: (window_height, window_width)
|
|
1857
|
+
OptionPicker = Picker(submenu_win, **option_picker_data)
|
|
1858
|
+
s, o, f = OptionPicker.run()
|
|
1859
|
+
|
|
1860
|
+
if o == "refresh":
|
|
1861
|
+
self.draw_screen()
|
|
1862
|
+
continue
|
|
1863
|
+
if s:
|
|
1864
|
+
selected_columns = s
|
|
1865
|
+
self.hidden_columns = [i for i in range(len(self.column_widths)) if i not in selected_columns]
|
|
1866
|
+
|
|
1867
|
+
# return {x: options[x] for x in s}, o, f
|
|
1868
|
+
break
|
|
1869
|
+
return {}, "", f
|
|
1704
1870
|
|
|
1705
1871
|
def notification(self, stdscr: curses.window, message: str="", title:str="Notification", colours_end: int=0, duration:int=4) -> None:
|
|
1706
1872
|
|
|
@@ -1743,7 +1909,6 @@ class Picker:
|
|
|
1743
1909
|
"crosshair_cursor": False,
|
|
1744
1910
|
"show_header": False,
|
|
1745
1911
|
"screen_size_function": lambda stdscr: (notification_height, notification_width),
|
|
1746
|
-
|
|
1747
1912
|
}
|
|
1748
1913
|
OptionPicker = Picker(submenu_win, **notification_data)
|
|
1749
1914
|
s, o, f = OptionPicker.run()
|
|
@@ -1947,6 +2112,9 @@ class Picker:
|
|
|
1947
2112
|
self.draw_screen()
|
|
1948
2113
|
self.notification(self.stdscr, message=f"Theme {self.colour_theme_number} applied.")
|
|
1949
2114
|
self.colours = get_colours(self.colour_theme_number)
|
|
2115
|
+
elif setting == "colsel":
|
|
2116
|
+
self.draw_screen()
|
|
2117
|
+
self.select_columns(self.stdscr)
|
|
1950
2118
|
|
|
1951
2119
|
else:
|
|
1952
2120
|
self.user_settings = ""
|
|
@@ -2312,7 +2480,13 @@ class Picker:
|
|
|
2312
2480
|
self.logger.info(f"function: fetch_data()")
|
|
2313
2481
|
tmp_items, tmp_header = [], []
|
|
2314
2482
|
self.getting_data.clear()
|
|
2315
|
-
self.refresh_function(
|
|
2483
|
+
self.refresh_function(
|
|
2484
|
+
tmp_items,
|
|
2485
|
+
tmp_header,
|
|
2486
|
+
self.visible_rows_indices,
|
|
2487
|
+
self.getting_data,
|
|
2488
|
+
self.get_function_data(),
|
|
2489
|
+
)
|
|
2316
2490
|
if self.track_entries_upon_refresh:
|
|
2317
2491
|
selected_indices = get_selected_indices(self.selections)
|
|
2318
2492
|
self.ids = [item[self.id_column] for i, item in enumerate(self.items) if i in selected_indices]
|
|
@@ -2410,10 +2584,6 @@ class Picker:
|
|
|
2410
2584
|
row_len = 1
|
|
2411
2585
|
if self.header: row_len = len(self.header)
|
|
2412
2586
|
elif len(self.items): row_len = len(self.items[0])
|
|
2413
|
-
# if len(self.indexed_items) == 0:
|
|
2414
|
-
# insert_at_pos = 0
|
|
2415
|
-
# else:
|
|
2416
|
-
# insert_at_pos = self.indexed_items[self.cursor_pos][0]
|
|
2417
2587
|
self.items = self.items[:pos] + [["" for x in range(row_len)]] + self.items[pos:]
|
|
2418
2588
|
if pos <= self.cursor_pos:
|
|
2419
2589
|
self.cursor_pos += 1
|
|
@@ -2563,18 +2733,45 @@ class Picker:
|
|
|
2563
2733
|
max_total_width=self.rows_w,
|
|
2564
2734
|
unicode_char_width=self.unicode_char_width
|
|
2565
2735
|
)
|
|
2736
|
+
self.calculate_section_sizes()
|
|
2566
2737
|
|
|
2567
|
-
row_width = sum(self.
|
|
2738
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
2568
2739
|
if row_width - self.leftmost_char < self.rows_w:
|
|
2569
2740
|
if row_width <= self.rows_w - self.left_gutter_width:
|
|
2570
2741
|
self.leftmost_char = 0
|
|
2571
2742
|
else:
|
|
2572
2743
|
self.leftmost_char = row_width - (self.rows_w - self.left_gutter_width) + 5
|
|
2573
2744
|
|
|
2745
|
+
def cleanup_processes(self):
|
|
2746
|
+
self.thread_stop_event.set()
|
|
2747
|
+
self.data_generation_queue.clear()
|
|
2748
|
+
# with self.data_generation_queue.mutex:
|
|
2749
|
+
# self.data_generation_queue.queue.clear()
|
|
2750
|
+
function_data = self.get_function_data()
|
|
2751
|
+
for proc in self.processes:
|
|
2752
|
+
if proc.is_alive():
|
|
2753
|
+
proc.terminate()
|
|
2754
|
+
proc.join(timeout=0.01)
|
|
2755
|
+
self.processes = []
|
|
2756
|
+
self.items_sync_loop_event.set()
|
|
2757
|
+
if self.items_sync_thread != None:
|
|
2758
|
+
self.items_sync_thread.join(timeout=1)
|
|
2759
|
+
|
|
2760
|
+
def cleanup_threads(self):
|
|
2761
|
+
self.thread_stop_event.set()
|
|
2762
|
+
with self.data_generation_queue.mutex:
|
|
2763
|
+
self.data_generation_queue.queue.clear()
|
|
2764
|
+
function_data = self.get_function_data()
|
|
2765
|
+
for t in self.threads:
|
|
2766
|
+
if t.is_alive():
|
|
2767
|
+
t.join(timeout=0.01)
|
|
2768
|
+
|
|
2574
2769
|
def run(self) -> Tuple[list[int], str, dict]:
|
|
2575
2770
|
""" Run the picker. """
|
|
2576
2771
|
self.logger.info(f"function: run()")
|
|
2577
2772
|
|
|
2773
|
+
self.thread_stop_event.clear()
|
|
2774
|
+
|
|
2578
2775
|
if self.get_footer_string_startup and self.footer_string_refresh_function != None:
|
|
2579
2776
|
self.footer_string = " "
|
|
2580
2777
|
self.footer.adjust_sizes(self.term_h, self.term_w)
|
|
@@ -2679,9 +2876,12 @@ class Picker:
|
|
|
2679
2876
|
|
|
2680
2877
|
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):
|
|
2681
2878
|
self.logger.debug(f"Get new data (refresh).")
|
|
2682
|
-
|
|
2879
|
+
try:
|
|
2880
|
+
self.stdscr.addstr(0,self.term_w-3," ", curses.color_pair(self.colours_start+21) | curses.A_BOLD)
|
|
2881
|
+
except:
|
|
2882
|
+
pass
|
|
2683
2883
|
self.stdscr.refresh()
|
|
2684
|
-
if self.get_new_data
|
|
2884
|
+
if self.get_new_data:
|
|
2685
2885
|
self.refreshing_data = True
|
|
2686
2886
|
|
|
2687
2887
|
t = threading.Thread(target=self.fetch_data)
|
|
@@ -2824,6 +3024,14 @@ class Picker:
|
|
|
2824
3024
|
data["option_functions"] = f"[...] length = {len(data['option_functions'])}"
|
|
2825
3025
|
data["loaded_file_states"] = f"[...] length = {len(data['loaded_file_states'])}"
|
|
2826
3026
|
data["sheet_states"] = f"[...] length = {len(data['sheet_states'])}"
|
|
3027
|
+
data["highlights"] = f"[...] length = {len(data['highlights'])}"
|
|
3028
|
+
data["colours"] = f"[...] length = {len(data['colours'])}"
|
|
3029
|
+
data["keys_dict"] = f"[...] length = {len(data['keys_dict'])}"
|
|
3030
|
+
data["history_filter_and_search"] = f"[...] length = {len(data['history_filter_and_search'])}"
|
|
3031
|
+
data["history_opts"] = f"[...] length = {len(data['history_opts'])}"
|
|
3032
|
+
data["history_edits"] = f"[...] length = {len(data['history_edits'])}"
|
|
3033
|
+
data["history_pipes"] = f"[...] length = {len(data['history_pipes'])}"
|
|
3034
|
+
data["history_settings"] = f"[...] length = {len(data['history_settings'])}"
|
|
2827
3035
|
info_items += [
|
|
2828
3036
|
["",""],
|
|
2829
3037
|
[" get_function_data()", "-*"*30],
|
|
@@ -2869,6 +3077,7 @@ class Picker:
|
|
|
2869
3077
|
elif self.check_key("exit", key, self.keys_dict):
|
|
2870
3078
|
self.stdscr.clear()
|
|
2871
3079
|
if len(self.loaded_files) <= 1:
|
|
3080
|
+
self.cleanup_threads()
|
|
2872
3081
|
function_data = self.get_function_data()
|
|
2873
3082
|
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2874
3083
|
return [], "", function_data
|
|
@@ -2890,6 +3099,7 @@ class Picker:
|
|
|
2890
3099
|
self.draw_screen()
|
|
2891
3100
|
|
|
2892
3101
|
elif self.check_key("full_exit", key, self.keys_dict):
|
|
3102
|
+
self.cleanup_threads()
|
|
2893
3103
|
close_curses(self.stdscr)
|
|
2894
3104
|
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
2895
3105
|
exit()
|
|
@@ -2931,11 +3141,12 @@ class Picker:
|
|
|
2931
3141
|
options = []
|
|
2932
3142
|
if len(self.items) > 0:
|
|
2933
3143
|
options += [["cv", "Centre rows vertically"]]
|
|
2934
|
-
options += [["pc", "Pin cursor to row
|
|
3144
|
+
options += [["pc", "Pin cursor to row index during data refresh."]]
|
|
2935
3145
|
options += [["ct", "Centre column-set in terminal"]]
|
|
2936
3146
|
options += [["cc", "Centre values in cells"]]
|
|
2937
3147
|
options += [["!r", "Toggle auto-refresh"]]
|
|
2938
3148
|
options += [["th", "Cycle between themes. (accepts th#)"]]
|
|
3149
|
+
options += [["colsel", "Toggle columns."]]
|
|
2939
3150
|
options += [["nohl", "Toggle highlights"]]
|
|
2940
3151
|
options += [["footer", "Toggle footer"]]
|
|
2941
3152
|
options += [["header", "Toggle header"]]
|
|
@@ -3066,16 +3277,13 @@ class Picker:
|
|
|
3066
3277
|
self.cursor_pos = new_pos
|
|
3067
3278
|
self.ensure_no_overscroll()
|
|
3068
3279
|
self.draw_screen()
|
|
3069
|
-
|
|
3070
|
-
# if current_page + 1 == (len(self.indexed_items) + items_per_page - 1) // items_per_page:
|
|
3071
|
-
#
|
|
3072
|
-
# current_row = (len(self.indexed_items) +items_per_page - 1) % items_per_page
|
|
3073
|
-
# self.draw_screen()
|
|
3280
|
+
|
|
3074
3281
|
elif self.check_key("enter", key, self.keys_dict):
|
|
3075
3282
|
self.logger.info(f"key_function enter")
|
|
3076
3283
|
# Print the selected indices if any, otherwise print the current index
|
|
3077
3284
|
if self.is_selecting or self.is_deselecting: self.handle_visual_selection()
|
|
3078
3285
|
if len(self.items) == 0:
|
|
3286
|
+
self.cleanup_threads()
|
|
3079
3287
|
function_data = self.get_function_data()
|
|
3080
3288
|
restore_terminal_settings(tty_fd, self.saved_terminal_state)
|
|
3081
3289
|
return [], "", function_data
|
|
@@ -3102,6 +3310,7 @@ class Picker:
|
|
|
3102
3310
|
)
|
|
3103
3311
|
|
|
3104
3312
|
if options_sufficient:
|
|
3313
|
+
self.cleanup_threads()
|
|
3105
3314
|
self.user_opts = usrtxt
|
|
3106
3315
|
self.stdscr.clear()
|
|
3107
3316
|
self.stdscr.refresh()
|
|
@@ -3115,15 +3324,7 @@ class Picker:
|
|
|
3115
3324
|
self.cursor_pos = max(0, self.cursor_pos-self.items_per_page)
|
|
3116
3325
|
|
|
3117
3326
|
elif self.check_key("redraw_screen", key, self.keys_dict):
|
|
3118
|
-
self.
|
|
3119
|
-
self.stdscr.clear()
|
|
3120
|
-
self.stdscr.refresh()
|
|
3121
|
-
restrict_curses(self.stdscr)
|
|
3122
|
-
unrestrict_curses(self.stdscr)
|
|
3123
|
-
self.stdscr.clear()
|
|
3124
|
-
self.stdscr.refresh()
|
|
3125
|
-
|
|
3126
|
-
self.draw_screen()
|
|
3327
|
+
self.refresh_and_draw_screen()
|
|
3127
3328
|
|
|
3128
3329
|
elif self.check_key("cycle_sort_method", key, self.keys_dict):
|
|
3129
3330
|
if self.sort_column == self.selected_column:
|
|
@@ -3164,22 +3365,28 @@ class Picker:
|
|
|
3164
3365
|
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
|
|
3165
3366
|
self.cursor_pos = [row[0] for row in self.indexed_items].index(current_index)
|
|
3166
3367
|
elif self.check_key("col_select_next", key, self.keys_dict):
|
|
3167
|
-
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
3168
|
-
col_index = (self.selected_column +1) % (len(self.items[0]))
|
|
3169
|
-
self.selected_column = col_index
|
|
3170
|
-
# Flash when we loop back to the first column
|
|
3171
|
-
# if self.selected_column == 0:
|
|
3172
|
-
# curses.flash()
|
|
3173
3368
|
self.logger.info(f"key_function col_select_next {self.selected_column}")
|
|
3369
|
+
if len(self.hidden_columns) != len(self.column_widths):
|
|
3370
|
+
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
3371
|
+
while True:
|
|
3372
|
+
self.hidden_columns
|
|
3373
|
+
col_index = (self.selected_column +1) % (len(self.items[0]))
|
|
3374
|
+
self.selected_column = col_index
|
|
3375
|
+
if self.selected_column not in self.hidden_columns:
|
|
3376
|
+
break
|
|
3377
|
+
|
|
3378
|
+
# Flash when we loop back to the first column
|
|
3379
|
+
# if self.selected_column == 0:
|
|
3380
|
+
# curses.flash()
|
|
3174
3381
|
|
|
3175
3382
|
|
|
3176
3383
|
## Scroll with column select
|
|
3177
3384
|
self.get_visible_rows()
|
|
3178
3385
|
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)
|
|
3179
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3180
|
-
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
3181
|
-
start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3182
|
-
end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3386
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3387
|
+
column_set_width = sum(self.visible_column_widths)+len(self.separator)*len(self.visible_column_widths)
|
|
3388
|
+
start_of_cell = sum(self.visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3389
|
+
end_of_cell = sum(self.visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3183
3390
|
display_width = self.rows_w-self.left_gutter_width
|
|
3184
3391
|
# If the full column is within the current display then don't do anything
|
|
3185
3392
|
if start_of_cell >= self.leftmost_char and end_of_cell <= self.leftmost_char + display_width:
|
|
@@ -3192,11 +3399,17 @@ class Picker:
|
|
|
3192
3399
|
self.ensure_no_overscroll()
|
|
3193
3400
|
|
|
3194
3401
|
elif self.check_key("col_select_prev", key, self.keys_dict):
|
|
3195
|
-
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
3196
|
-
col_index = (self.selected_column -1) % (len(self.items[0]))
|
|
3197
|
-
self.selected_column = col_index
|
|
3198
|
-
|
|
3199
3402
|
self.logger.info(f"key_function col_select_prev {self.selected_column}")
|
|
3403
|
+
|
|
3404
|
+
if len(self.hidden_columns) != len(self.column_widths):
|
|
3405
|
+
if len(self.items) > 0 and len(self.items[0]) > 0:
|
|
3406
|
+
while True:
|
|
3407
|
+
self.hidden_columns
|
|
3408
|
+
col_index = (self.selected_column -1) % (len(self.items[0]))
|
|
3409
|
+
self.selected_column = col_index
|
|
3410
|
+
if self.selected_column not in self.hidden_columns:
|
|
3411
|
+
break
|
|
3412
|
+
|
|
3200
3413
|
# Flash when we loop back to the last column
|
|
3201
3414
|
# if self.selected_column == len(self.column_widths)-1:
|
|
3202
3415
|
# curses.flash()
|
|
@@ -3204,10 +3417,10 @@ class Picker:
|
|
|
3204
3417
|
## Scroll with column select
|
|
3205
3418
|
self.get_visible_rows()
|
|
3206
3419
|
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)
|
|
3207
|
-
visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3208
|
-
column_set_width = sum(visible_column_widths)+len(self.separator)*len(visible_column_widths)
|
|
3209
|
-
start_of_cell = sum(visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3210
|
-
end_of_cell = sum(visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3420
|
+
self.visible_column_widths = [c for i,c in enumerate(self.column_widths) if i not in self.hidden_columns]
|
|
3421
|
+
column_set_width = sum(self.visible_column_widths)+len(self.separator)*len(self.visible_column_widths)
|
|
3422
|
+
start_of_cell = sum(self.visible_column_widths[:self.selected_column])+len(self.separator)*self.selected_column
|
|
3423
|
+
end_of_cell = sum(self.visible_column_widths[:self.selected_column+1])+len(self.separator)*(self.selected_column+1)
|
|
3211
3424
|
display_width = self.rows_w-self.left_gutter_width
|
|
3212
3425
|
|
|
3213
3426
|
# If the entire column is within the current display then don't do anything
|
|
@@ -3223,21 +3436,21 @@ class Picker:
|
|
|
3223
3436
|
elif self.check_key("scroll_right", key, self.keys_dict):
|
|
3224
3437
|
self.logger.info(f"key_function scroll_right")
|
|
3225
3438
|
if len(self.indexed_items):
|
|
3226
|
-
row_width = sum(self.
|
|
3439
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
3227
3440
|
if row_width-self.leftmost_char >= self.rows_w-5:
|
|
3228
3441
|
self.leftmost_char += 5
|
|
3229
3442
|
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w) + self.left_gutter_width+5)
|
|
3230
|
-
if sum(self.
|
|
3443
|
+
if sum(self.visible_column_widths) + len(self.visible_column_widths)*len(self.separator) < self.rows_w:
|
|
3231
3444
|
self.leftmost_char = 0
|
|
3232
3445
|
|
|
3233
3446
|
elif self.check_key("scroll_right_25", key, self.keys_dict):
|
|
3234
3447
|
self.logger.info(f"key_function scroll_right")
|
|
3235
3448
|
if len(self.indexed_items):
|
|
3236
|
-
row_width = sum(self.
|
|
3449
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
3237
3450
|
if row_width-self.leftmost_char+5 >= self.rows_w-25:
|
|
3238
3451
|
self.leftmost_char += 25
|
|
3239
3452
|
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w) + self.left_gutter_width+5)
|
|
3240
|
-
if sum(self.
|
|
3453
|
+
if sum(self.visible_column_widths) + len(self.visible_column_widths)*len(self.separator) < self.rows_w:
|
|
3241
3454
|
self.leftmost_char = 0
|
|
3242
3455
|
|
|
3243
3456
|
elif self.check_key("scroll_left", key, self.keys_dict):
|
|
@@ -3256,24 +3469,13 @@ class Picker:
|
|
|
3256
3469
|
elif self.check_key("scroll_far_right", key, self.keys_dict):
|
|
3257
3470
|
self.logger.info(f"key_function scroll_far_right")
|
|
3258
3471
|
longest_row_str_len = 0
|
|
3259
|
-
|
|
3260
|
-
# for i in range(len(rows)):
|
|
3261
|
-
# item = rows[i]
|
|
3262
|
-
# row_str = format_row(item, self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols, self.unicode_char_width)
|
|
3263
|
-
# if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
|
|
3264
|
-
longest_row_str_len = sum(self.column_widths) + (len(self.column_widths)-1)*len(self.separator)
|
|
3265
|
-
# for i in range(len(self.indexed_items)):
|
|
3266
|
-
# item = self.indexed_items[i]
|
|
3267
|
-
# row_str = format_row(item[1], self.hidden_columns, self.column_widths, self.separator, self.centre_in_cols)
|
|
3268
|
-
# if len(row_str) > longest_row_str_len: longest_row_str_len=len(row_str)
|
|
3269
|
-
# self.notification(self.stdscr, f"{longest_row_str_len}")
|
|
3472
|
+
longest_row_str_len = sum(self.visible_column_widths) + (len(self.visible_column_widths)-1)*len(self.separator)
|
|
3270
3473
|
if len(self.indexed_items):
|
|
3271
|
-
row_width = sum(self.
|
|
3474
|
+
row_width = sum(self.visible_column_widths) + len(self.separator)*(len(self.visible_column_widths)-1)
|
|
3272
3475
|
self.leftmost_char = row_width - (self.rows_w) + self.left_gutter_width+5
|
|
3273
3476
|
self.leftmost_char = min(self.leftmost_char, row_width - (self.rows_w) + self.left_gutter_width+5)
|
|
3274
3477
|
|
|
3275
|
-
longest_row_str_len = sum(self.
|
|
3276
|
-
# self.leftmost_char = max(0, longest_row_str_len-self.rows_w+2+5)
|
|
3478
|
+
longest_row_str_len = sum(self.visible_column_widths) + (len(self.visible_column_widths)-1)*len(self.separator)
|
|
3277
3479
|
|
|
3278
3480
|
|
|
3279
3481
|
|
|
@@ -3335,16 +3537,6 @@ class Picker:
|
|
|
3335
3537
|
self.selected_column = min(self.selected_column, row_len-2)
|
|
3336
3538
|
self.initialise_variables()
|
|
3337
3539
|
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
# elif self.check_key("increase_lines_per_page", key, self.keys_dict):
|
|
3342
|
-
# self.items_per_page += 1
|
|
3343
|
-
# self.draw_screen()
|
|
3344
|
-
# elif self.check_key("decrease_lines_per_page", key, self.keys_dict):
|
|
3345
|
-
# if self.items_per_page > 1:
|
|
3346
|
-
# self.items_per_page -= 1
|
|
3347
|
-
# self.draw_screen()
|
|
3348
3540
|
elif self.check_key("decrease_column_width", key, self.keys_dict):
|
|
3349
3541
|
self.logger.info(f"key_function decrease_column_width")
|
|
3350
3542
|
if self.max_column_width > 10:
|
|
@@ -3526,12 +3718,6 @@ class Picker:
|
|
|
3526
3718
|
function_data = self.get_function_data()
|
|
3527
3719
|
return [], "escape", function_data
|
|
3528
3720
|
|
|
3529
|
-
|
|
3530
|
-
# else:
|
|
3531
|
-
# self.search_query = ""
|
|
3532
|
-
# self.mode_index = 0
|
|
3533
|
-
# self.highlights = [highlight for highlight in self.highlights if "type" not in highlight or highlight["type"] != "search" ]
|
|
3534
|
-
# continue
|
|
3535
3721
|
self.draw_screen()
|
|
3536
3722
|
|
|
3537
3723
|
elif self.check_key("opts_input", key, self.keys_dict):
|
|
@@ -3747,7 +3933,7 @@ class Picker:
|
|
|
3747
3933
|
|
|
3748
3934
|
elif self.check_key("edit", key, self.keys_dict):
|
|
3749
3935
|
self.logger.info(f"key_function edit")
|
|
3750
|
-
if len(self.indexed_items) > 0 and self.
|
|
3936
|
+
if len(self.indexed_items) > 0 and self.editable_columns[self.selected_column]:
|
|
3751
3937
|
current_val = self.indexed_items[self.cursor_pos][1][self.selected_column]
|
|
3752
3938
|
usrtxt = f"{current_val}"
|
|
3753
3939
|
field_end_f = lambda: self.get_term_size()[1]-38 if self.show_footer else lambda: self.get_term_size()[1]-3
|
|
@@ -3776,6 +3962,47 @@ class Picker:
|
|
|
3776
3962
|
usrtxt = str(eval(usrtxt[3:]))
|
|
3777
3963
|
self.indexed_items[self.cursor_pos][1][self.selected_column] = usrtxt
|
|
3778
3964
|
self.history_edits.append(usrtxt)
|
|
3965
|
+
elif self.check_key("edit_nvim", key, self.keys_dict):
|
|
3966
|
+
|
|
3967
|
+
def edit_strings_in_nvim(strings: list[str]) -> list[str]:
|
|
3968
|
+
"""
|
|
3969
|
+
Opens a list of strings in nvim for editing and returns the modified strings.
|
|
3970
|
+
|
|
3971
|
+
Args:
|
|
3972
|
+
strings (list[str]): The list of strings to edit.
|
|
3973
|
+
|
|
3974
|
+
Returns:
|
|
3975
|
+
list[str]: The updated list of strings after editing in nvim.
|
|
3976
|
+
"""
|
|
3977
|
+
|
|
3978
|
+
# Open the strings in a tmpfile for editing
|
|
3979
|
+
with tempfile.NamedTemporaryFile(mode="w+", suffix=".txt", delete=False) as tmp:
|
|
3980
|
+
tmp.write("\n".join(strings))
|
|
3981
|
+
tmp.flush()
|
|
3982
|
+
tmp_name = tmp.name
|
|
3983
|
+
|
|
3984
|
+
subprocess.run(["nvim", tmp_name])
|
|
3985
|
+
|
|
3986
|
+
# Read the modified strings into a list and return them.
|
|
3987
|
+
with open(tmp_name, "r") as tmp:
|
|
3988
|
+
edited_content = tmp.read().splitlines()
|
|
3989
|
+
|
|
3990
|
+
return edited_content
|
|
3991
|
+
|
|
3992
|
+
if len(self.indexed_items) > 0 and self.editable_columns[self.selected_column]:
|
|
3993
|
+
|
|
3994
|
+
selected_cells = [self.items[index][self.selected_column] for index, selected in self.selections.items() if selected ]
|
|
3995
|
+
selected_cells_indices = [(index, self.selected_column) for index, selected in self.selections.items() if selected ]
|
|
3996
|
+
|
|
3997
|
+
edited_cells = edit_strings_in_nvim(selected_cells)
|
|
3998
|
+
count = 0
|
|
3999
|
+
if len(edited_cells) == len(selected_cells_indices):
|
|
4000
|
+
for i, j in selected_cells_indices:
|
|
4001
|
+
self.items[i][j] = edited_cells[count]
|
|
4002
|
+
count += 1
|
|
4003
|
+
|
|
4004
|
+
self.refresh_and_draw_screen()
|
|
4005
|
+
|
|
3779
4006
|
|
|
3780
4007
|
elif self.check_key("edit_picker", key, self.keys_dict):
|
|
3781
4008
|
self.logger.info(f"key_function edit_picker")
|
|
@@ -3807,7 +4034,7 @@ class Picker:
|
|
|
3807
4034
|
self.indexed_items[self.cursor_pos][1][self.selected_column] = usrtxt
|
|
3808
4035
|
self.history_edits.append(usrtxt)
|
|
3809
4036
|
elif self.check_key("edit_ipython", key, self.keys_dict):
|
|
3810
|
-
self.logger.info(f"key_function
|
|
4037
|
+
self.logger.info(f"key_function edit_ipython")
|
|
3811
4038
|
import IPython, termios
|
|
3812
4039
|
self.stdscr.clear()
|
|
3813
4040
|
restrict_curses(self.stdscr)
|
|
@@ -3951,7 +4178,7 @@ def parse_arguments() -> Tuple[argparse.Namespace, dict]:
|
|
|
3951
4178
|
# input_arg = args.filename
|
|
3952
4179
|
|
|
3953
4180
|
elif args.generate:
|
|
3954
|
-
function_data["refresh_function"] = lambda items, header, visible_rows_indices, getting_data: generate_picker_data_from_file(args.generate, items, header, visible_rows_indices, getting_data)
|
|
4181
|
+
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)
|
|
3955
4182
|
function_data["get_data_startup"] = True
|
|
3956
4183
|
function_data["get_new_data"] = True
|
|
3957
4184
|
return args, function_data
|
|
@@ -4211,7 +4438,7 @@ def main() -> None:
|
|
|
4211
4438
|
# function_data["debug"] = True
|
|
4212
4439
|
# function_data["debug_level"] = 1
|
|
4213
4440
|
|
|
4214
|
-
function_data["cell_cursor"] = False
|
|
4441
|
+
# function_data["cell_cursor"] = False
|
|
4215
4442
|
|
|
4216
4443
|
function_data["split_right"] = False
|
|
4217
4444
|
function_data["split_left"] = False
|