listpick 0.1.4.0__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.
File without changes
@@ -0,0 +1,91 @@
1
+ #!/bin/python
2
+ """
3
+ Lines to be displayed on the help screen.
4
+ """
5
+ help_lines = [
6
+ ['Navigation:', ''],
7
+ [' Up/Down and k/j', 'Move up/down'],
8
+ [' K and J', 'Move up/down by five'],
9
+ [' Ctrl+f and Ctrl+b', 'Page up/down'],
10
+ [' u and d', 'Half page up/down'],
11
+ [' g/home', 'Go to top of page'],
12
+ [' G/end', 'Go to bottom of page'],
13
+ ['', ''],
14
+ ['Selection:', ''],
15
+ [' Space', 'Toggle selection'],
16
+ [' m or ^a', 'Select all'],
17
+ [' M or ^r', 'Deselect all'],
18
+ [' v', 'Start and stop visual selection'],
19
+ [' V', 'Start and stop visual deselection'],
20
+ [' l/Enter', 'Submit selection'],
21
+ ['', ''],
22
+ ['Modes:', ''],
23
+ [' Tab', 'Cycle between modes forwards'],
24
+ [' Shift+Tab', 'Cycle between modes backwards'],
25
+ ['', ''],
26
+ ['Sorting:', ''],
27
+ [' 0-9', 'Select column (1-based index)'],
28
+ [' </>', 'Focus on next/previous column'],
29
+ [' s', 'Cycle sort method'],
30
+ [' t', 'Toggle sort order (asc. or desc.)'],
31
+ ['', ''],
32
+ ['Filter or search rows (regexp):', ''],
33
+ [' f', 'Begin filtering rows'],
34
+ [' /', 'Begin searching rows'],
35
+ [' --[1-9]', 'Specify column'],
36
+ [' --i', 'Make match case sensitive'],
37
+ [' --v', 'Invert filter'],
38
+ [' n/i', 'Continue search forwards'],
39
+ [' N/I', 'Continue search backwards'],
40
+ ['', ''],
41
+ ['Pipe', ''],
42
+ [' |', 'Pipe selected rows from active column into a specified command'],
43
+ [' e.g.,', 'xargs -d \n -I{} echo {} > ~/rubbish.txt'],
44
+ [' e.g.,', 'xargs -d \n -I{} mpv {}'],
45
+ ['', ''],
46
+
47
+ ['Load and Save', ''],
48
+ [' Ctrl+s', 'Open save dialog. Then pick output type and location to save.'],
49
+ [' Ctrl+o', 'Open previously saved data.'],
50
+ ['', ''],
51
+ ['Columns:', ''],
52
+ [' 0-9', 'Select column (1-based index)'],
53
+ [' </>', 'Focus on next/previous column'],
54
+ [' Shift+1-9', 'Hide/show column'],
55
+ [' [/]', 'Increase/Decrease column width'],
56
+ # [' {', 'Move focused column left'],
57
+ # [' }', 'Move focused column right'],
58
+ ['', ''],
59
+ ['Clipboard:', ''],
60
+ [' y', 'Open dialogue to select how you want the selected rows to be copied'],
61
+ ['', ''],
62
+ ['Settings:', ''],
63
+ [' ~', 'Open settings picker'],
64
+ [' `', 'settings input'],
65
+ [' !\\d+', 'Show/hide column'],
66
+ [' !r', 'toggle auto refresh'],
67
+ [' \\', 'Clear settings'],
68
+ ['', ''],
69
+ ['Input fields:', ''],
70
+ [' Ctrl+r', 'Insert current cell (cursor, sort_col) into input bar'],
71
+ [' Ctrl+e', 'End'],
72
+ [' Ctrl+a', 'Start'],
73
+ [' Ctrl+u', 'Delete to start'],
74
+ [' Ctrl+k', 'Delete to end'],
75
+ [' Ctrl+f/Ctrl+b', 'Forwards/Backwards one character.'],
76
+ ['Misc:', ''],
77
+ [' ?', 'Show this help screen'],
78
+ [' _', 'Quick toggle footer'],
79
+ [' O', 'Try to open the data in the focused column of the selected rows as file paths.'],
80
+ [' ', 'You may need to change the cwd by pressing ` and then entering e.g., "cwd=~/Pictures"'],
81
+ [' +', 'Increase lines per page'],
82
+ [' -', 'Decrease lines per page'],
83
+ [' :', 'Specify user optios to be returned with selection'],
84
+ [' o', 'Open submenu to specify option'],
85
+ [' q', 'Exit menu with no selection.'],
86
+ [' ?', 'Show this help page'],
87
+ [' Ctrl+c', 'Immediate quit.'],
88
+ [' Ctrl+c', 'Immediate quit.'],
89
+ [' Ctrl+l', 'Redraw screen.'],
90
+ ['', ''],
91
+ ]
@@ -0,0 +1,165 @@
1
+ import curses
2
+ from typing import Tuple, Optional, Callable
3
+
4
+ def input_field(
5
+ stdscr: curses.window,
6
+ usrtxt:str="",
7
+ field_name:str="Input",
8
+ x:Callable=lambda:0,
9
+ y:Callable=lambda:0,
10
+ colours_start:int=0,
11
+ literal:bool=False,
12
+ max_length:Callable = lambda: 1000,
13
+ registers={},
14
+ refresh_screen_function:Optional[Callable]=None,
15
+ cursor: int = 0,
16
+ ) -> Tuple[str, bool]:
17
+ """
18
+ Display input field at x,y for the user to enter text.
19
+
20
+ ---Arguments
21
+ stdscr: curses screen
22
+ usrtxt (str): text to be edited by the user
23
+ field_name (str): The text to be displayed at the start of the text input
24
+ x (int): prompt begins at (x,y) in the screen given
25
+ y (Callable): prompt begins at (x,y) in the screen given
26
+ colours_start (int): where to start when initialising the colour pairs with curses.
27
+ literal: whether to display the repr() of the string; e.g., if we want to display escape sequences literally
28
+ max_length (callable): function that returns the length of input field
29
+
30
+
31
+ ---Returns
32
+ usrtxt, return_code
33
+ usrtxt: the text inputted by the user
34
+ return_code:
35
+ 0: user hit escape
36
+ 1: user hit return
37
+ """
38
+ while True:
39
+
40
+ h, w = stdscr.getmaxyx()
41
+
42
+ if refresh_screen_function != None:
43
+ refresh_screen_function()
44
+ field_end = min(w-3, max_length())
45
+ field_y = min(h-1, y())
46
+ field_x = min(h-1, x())
47
+
48
+ # Clear background to end of row
49
+ stdscr.addstr(field_y, x(), " "*(field_end-x()), curses.color_pair(colours_start+20))
50
+ stdscr.refresh()
51
+ # Display the field name and current text
52
+ field_length = 0
53
+
54
+ if literal:
55
+ stdscr.addstr(field_y, x(), f"{field_name}: {repr(usrtxt)} "[:field_end], curses.color_pair(colours_start+13) | curses.A_BOLD)
56
+ field_length=len(f"{field_name}: {repr(usrtxt)} ")
57
+ else:
58
+ stdscr.addstr(field_y, x(), f" {field_name}: {usrtxt} "[:field_end], curses.color_pair(colours_start+13) | curses.A_BOLD)
59
+ field_length=len(f" {field_name}: {usrtxt} ")
60
+
61
+ visible_cursor_x = x()+len(usrtxt)-cursor+len(f" {field_name}: ")
62
+ if literal:
63
+ visible_cursor_x = x()+len(repr(usrtxt))-cursor+len(f" {field_name}: ")-2
64
+
65
+ # if key == curses.KEY_RESIZE: # Terminal resize signal
66
+
67
+ # Display cursor if the field fits onto the screen
68
+ if field_length + 1 < field_end:
69
+ if not literal:
70
+ if usrtxt and cursor != 0:
71
+ stdscr.addstr(field_y, visible_cursor_x, f"{usrtxt[-(cursor)]}", curses.color_pair(colours_start+13) | curses.A_REVERSE | curses.A_BOLD)
72
+ else:
73
+ stdscr.addstr(field_y, visible_cursor_x, f" ", curses.color_pair(colours_start+13) | curses.A_REVERSE | curses.A_BOLD)
74
+ elif literal:
75
+ stdscr.addstr(field_y, visible_cursor_x, f"{repr(usrtxt)[-(cursor+1)]}", curses.color_pair(colours_start+13) | curses.A_REVERSE | curses.A_BOLD)
76
+
77
+ key = stdscr.getch()
78
+
79
+ if key == 27: # ESC key
80
+ return "", False
81
+ elif key == 3: # ESC key
82
+ stdscr.keypad(False)
83
+ curses.nocbreak()
84
+ curses.noraw()
85
+ curses.echo()
86
+ curses.endwin()
87
+ exit()
88
+ elif key == 10: # Enter/return key
89
+ return usrtxt, True
90
+ # selected_indices = print_selected_indices()
91
+ # if not selected_indices:
92
+ # selected_indices = [indexed_items[cursor_pos][0]]
93
+ # full_values = [format_row_full(items[i], hidden_columns) for i in selected_indices] # Use format_row_full for full data
94
+ # # selected_data = [format_full_row(items[i]).strip() for i, selected in enumerate(selections) if selected and i not in hidden_columns]
95
+ # if full_values:
96
+ # process = subprocess.Popen(usrtxt, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
97
+ # process.communicate(input='\n'.join(full_values).encode('utf-8'))
98
+ # break
99
+ elif key in [curses.KEY_BACKSPACE, "KEY_BACKSPACE", 263, 127]:
100
+
101
+ if cursor == 0:
102
+ usrtxt = usrtxt[:-1]
103
+ else:
104
+ usrtxt = usrtxt[:-(cursor+1)] + usrtxt[-cursor:]
105
+
106
+ elif key in [curses.KEY_LEFT, 2]: # CTRL+B
107
+ cursor = min(len(usrtxt), cursor + 1)
108
+
109
+ elif key in [curses.KEY_RIGHT, 6]: # CTRL-F
110
+ cursor = max(0, cursor - 1)
111
+
112
+ elif key == curses.KEY_UP:
113
+ cursor = max(0, cursor - 1)
114
+
115
+ elif key in [4, 330]: # Ctrl+D, Delete
116
+ if cursor != 0 and usrtxt != "":
117
+ if cursor == 1:
118
+ usrtxt = usrtxt[:-(cursor)]
119
+ else:
120
+ usrtxt = usrtxt[:-(cursor)] + usrtxt[-(cursor-1):]
121
+ cursor = max(0, cursor - 1)
122
+
123
+ elif key == 21 or key == "^U": # CTRL+U
124
+ if cursor == 0:
125
+ usrtxt = ""
126
+ else:
127
+ usrtxt = usrtxt[-(cursor+1):]
128
+ cursor = len(usrtxt)
129
+ elif key == 11 or key == "^K": # CTRL+K
130
+ if cursor: usrtxt = usrtxt[:-cursor]
131
+ cursor = 0
132
+
133
+ elif key in [1, 262]: # CTRL+A (beginning)
134
+ cursor = len(usrtxt)
135
+
136
+ elif key in [5, 360]: # CTRL+E (end)
137
+ cursor = 0
138
+ elif key in [18]: # CTRL+E (end)
139
+ if "*" in registers:
140
+ if cursor == 0:
141
+ addtxt = registers["*"]
142
+ usrtxt = usrtxt[-cursor:] + registers["*"]
143
+ else:
144
+ usrtxt = usrtxt[:-cursor] + registers["*"] + usrtxt[-cursor:]
145
+
146
+ # elif key in [23,8]: # Ctrl+BACKSPACE, CTRL+W
147
+ # if cursor == 0: tmp = usrtxt[::-1]
148
+ # else: tmp = usrtxt[:-cursor][::-1]
149
+ # index = tmp.find(" ")
150
+ # if index == -1: index = len(usrtxt)-1-cursor
151
+ #
152
+ # cursor = len(usrtxt)
153
+ elif key == curses.KEY_RESIZE: pass
154
+
155
+ else:
156
+ if isinstance(key, int):
157
+ try:
158
+ val = chr(key) if chr(key).isprintable() else ''
159
+ except:
160
+ val = ''
161
+ else: val = key
162
+ if cursor == 0:
163
+ usrtxt += val
164
+ else:
165
+ usrtxt = usrtxt[:-cursor] + val + usrtxt[-cursor:]
list_picker/ui/keys.py ADDED
@@ -0,0 +1,228 @@
1
+ #!/bin/python
2
+ import curses
3
+ """
4
+ Define key dictionary for controlling list_picker.
5
+
6
+ """
7
+
8
+ list_picker_keys = {
9
+ "refresh": [curses.KEY_F5],
10
+ "help": [ord('?')],
11
+ "exit": [ord('q'), ord('h')],
12
+ "full_exit": [3], # Ctrl+c
13
+ "move_column_left": [ord('{')],
14
+ "move_column_right": [ord('}')],
15
+ "cursor_down": [ord('j'), curses.KEY_DOWN],
16
+ "cursor_up": [ord('k'), curses.KEY_UP],
17
+ "half_page_up": [ord('u')],
18
+ "half_page_down": [ord('d')],
19
+ "page_up": [curses.KEY_PPAGE, 2], # Ctrl+b
20
+ "page_down": [curses.KEY_NPAGE, 6], # Ctrl+f
21
+ "cursor_bottom": [ord('G'), curses.KEY_END],
22
+ "cursor_top": [ord('g'), curses.KEY_HOME],
23
+ "five_up": [ord('K')],
24
+ "five_down": [ord('J')],
25
+ "toggle_select": [ord(' ')],
26
+ "select_all": [ord('m'), 1], # Ctrl-a
27
+ "select_none": [ord('M'), 18], # Ctrl-r
28
+ "visual_selection_toggle": [ord('v')],
29
+ "visual_deselection_toggle": [ord('V')],
30
+ "enter": [ord('\n'), curses.KEY_ENTER, ord('l')],
31
+ "redraw_screen": [12], # Ctrl-l
32
+ "cycle_sort_method": [ord('s')],
33
+ "cycle_sort_method_reverse": [ord('S')],
34
+ "cycle_sort_order": [ord('t')],
35
+ "delete": [curses.KEY_DC],
36
+ "decrease_lines_per_page": [ord('-')],
37
+ "increase_lines_per_page": [ord('+')],
38
+ "increase_column_width": [ord(']')],
39
+ "decrease_column_width": [ord('[')],
40
+ "filter_input": [ord('f')],
41
+ "search_input": [ord('/')],
42
+ "settings_input": [ord('`')],
43
+ "settings_options": [ord('~')],
44
+ "continue_search_forward": [ord('n'), ord('i')],
45
+ "continue_search_backward": [ord('N'), ord('I')],
46
+ "cancel": [27], # Escape key
47
+ "opts_input": [ord(':')],
48
+ "opts_select": [ord('o')],
49
+ "mode_next": [9], # Tab key
50
+ "mode_prev": [353], # Shift+Tab key
51
+ "pipe_input": [ord('|')],
52
+ "reset_opts": [ord('\\')],
53
+ "col_select": [ord('0'), ord('1'), ord('2'), ord('3'), ord('4'), ord('5'), ord('6'), ord('7'), ord('8'), ord('9')],
54
+ "col_select_next": [ord('>')],
55
+ "col_select_prev": [ord('<')],
56
+ "col_hide": [ord('!'), ord('@'), ord('#'), ord('$'), ord('%'), ord('^'), ord('&'), ord('*'), ord('('), ord(')')],
57
+ "edit": [ord('e')],
58
+ "edit_picker": [ord('E')],
59
+ "edit_ipython": [5], # Ctrl+e
60
+ "copy": [ord('y')],
61
+ "save": [19, ord('D')], # Ctrl+s
62
+ "load": [ord('L'), 15], # Ctrl+o
63
+ "open": [ord('O')],
64
+ "toggle_footer": [ord('_')],
65
+ "notification_toggle": [ord('z')],
66
+ }
67
+
68
+
69
+ help_keys = {
70
+ "exit": [ord('q'), ord('h')],
71
+ "full_exit": [3], # Ctrl+c
72
+ "cursor_down": [ord('j'), curses.KEY_DOWN],
73
+ "cursor_up": [ord('k'), curses.KEY_UP],
74
+ "half_page_up": [ord('u')],
75
+ "half_page_down": [ord('d')],
76
+ "page_up": [curses.KEY_PPAGE, 2],
77
+ "page_down": [curses.KEY_NPAGE, 6],
78
+ "cursor_bottom": [ord('G'), curses.KEY_END],
79
+ "cursor_top": [ord('g'), curses.KEY_HOME],
80
+ "five_up": [ord('K')],
81
+ "five_down": [ord('J')],
82
+ "redraw_screen": [12], # Ctrl+l
83
+ "cycle_sort_method": [ord('s')],
84
+ "cycle_sort_method_reverse": [ord('S')],
85
+ "cycle_sort_order": [ord('t')],
86
+ "increase_column_width": [ord(']')],
87
+ "decrease_column_width": [ord('[')],
88
+ "filter_input": [ord('f')],
89
+ "search_input": [ord('/')],
90
+ "settings_input": [ord('`')],
91
+ "settings_options": [ord('~')],
92
+ "continue_search_forward": [ord('n'), ord('i')],
93
+ "continue_search_backward": [ord('N'), ord('I')],
94
+ "cancel": [27], # Escape key
95
+ "col_select": [ord('0'), ord('1'), ord('2'), ord('3'), ord('4'), ord('5'), ord('6'), ord('7'), ord('8'), ord('9')],
96
+ "col_select_next": [ord('>')],
97
+ "col_select_prev": [ord('<')],
98
+ "col_hide": [ord('!'), ord('@'), ord('#'), ord('$'), ord('%'), ord('^'), ord('&'), ord('*'), ord('('), ord(')')],
99
+ "toggle_footer": [ord('_')],
100
+ }
101
+
102
+
103
+
104
+ notification_keys = {
105
+ "exit": [ord('q'), ord('h'), curses.KEY_ENTER, ord('\n'), ord(' '), 27],
106
+ "full_exit": [3], # Ctrl+c
107
+ "cursor_down": [ord('j'), curses.KEY_DOWN],
108
+ "cursor_up": [ord('k'), curses.KEY_UP],
109
+ "half_page_up": [ord('u')],
110
+ "half_page_down": [ord('d')],
111
+ "page_up": [curses.KEY_PPAGE, 2],
112
+ "page_down": [curses.KEY_NPAGE, 6],
113
+ "cursor_bottom": [ord('G'), curses.KEY_END],
114
+ "cursor_top": [ord('g'), curses.KEY_HOME],
115
+ "five_up": [ord('K')],
116
+ "five_down": [ord('J')],
117
+ "redraw_screen": [12], # Ctrl-l
118
+ }
119
+
120
+
121
+ menu_keys = {
122
+ "help": [ord('?')],
123
+ "exit": [ord('q'), ord('h')],
124
+ "full_exit": [3], # Ctrl+c
125
+ "cursor_down": [ord('j'), curses.KEY_DOWN],
126
+ "cursor_up": [ord('k'), curses.KEY_UP],
127
+ "half_page_up": [ord('u')],
128
+ "half_page_down": [ord('d')],
129
+ "page_up": [curses.KEY_PPAGE, 2],
130
+ "page_down": [curses.KEY_NPAGE, 6],
131
+ "cursor_bottom": [ord('G'), curses.KEY_END],
132
+ "cursor_top": [ord('g'), curses.KEY_HOME],
133
+ "five_up": [ord('K')],
134
+ "five_down": [ord('J')],
135
+ "enter": [ord('\n'), curses.KEY_ENTER, ord('l')],
136
+ "redraw_screen": [12], # Ctrl-l
137
+ "filter_input": [ord('f')],
138
+ "search_input": [ord('/')],
139
+ "continue_search_forward": [ord('n'), ord('i')],
140
+ "continue_search_backward": [ord('N'), ord('I')],
141
+ "cancel": [27], # Escape key
142
+ "opts_input": [ord(':')],
143
+ "mode_next": [9], # Tab key
144
+ "mode_prev": [353], # Shift+Tab key
145
+ }
146
+
147
+
148
+ options_keys = {
149
+ "exit": [ord('q'), ord('h')],
150
+ "full_exit": [3], # Ctrl+c
151
+ "cursor_down": [ord('j'), curses.KEY_DOWN],
152
+ "cursor_up": [ord('k'), curses.KEY_UP],
153
+ "half_page_up": [ord('u')],
154
+ "half_page_down": [ord('d')],
155
+ "page_up": [curses.KEY_PPAGE, 2],
156
+ "page_down": [curses.KEY_NPAGE, 6],
157
+ "cursor_bottom": [ord('G'), curses.KEY_END],
158
+ "cursor_top": [ord('g'), curses.KEY_HOME],
159
+ "five_up": [ord('K')],
160
+ "five_down": [ord('J')],
161
+ "toggle_select": [ord(' ')],
162
+ "select_all": [ord('m'), 1], # Ctrl-a
163
+ "select_none": [ord('M'), 18], # Ctrl-r
164
+ "visual_selection_toggle": [ord('v')],
165
+ "visual_deselection_toggle": [ord('V')],
166
+ "enter": [ord('\n'), curses.KEY_ENTER, ord('l')],
167
+ "redraw_screen": [12], # Ctrl-l
168
+ "cycle_sort_method": [ord('s')],
169
+ "cycle_sort_method_reverse": [ord('S')],
170
+ "cycle_sort_order": [ord('t')],
171
+ "filter_input": [ord('f')],
172
+ "search_input": [ord('/')],
173
+ "settings_input": [ord('`')],
174
+ "continue_search_forward": [ord('n'), ord('i')],
175
+ "continue_search_backward": [ord('N'), ord('I')],
176
+ "cancel": [27], # Escape key
177
+ "col_select": [ord('0'), ord('1'), ord('2'), ord('3'), ord('4'), ord('5'), ord('6'), ord('7'), ord('8'), ord('9')],
178
+ "refresh": [curses.KEY_F5, curses.KEY_RESIZE],
179
+ }
180
+
181
+
182
+
183
+ edit_menu_keys = {
184
+ "exit": [ord('q')],
185
+ "full_exit": [3], # Ctrl+c
186
+ "cursor_down": [ord('j'), curses.KEY_DOWN],
187
+ "cursor_up": [ord('k'), curses.KEY_UP],
188
+ "half_page_up": [ord('u')],
189
+ "half_page_down": [ord('d')],
190
+ "page_up": [curses.KEY_PPAGE, 2], # Ctrl+b
191
+ "page_down": [curses.KEY_NPAGE, 6], # Ctrl+f
192
+ "cursor_bottom": [ord('G'), curses.KEY_END],
193
+ "cursor_top": [ord('g'), curses.KEY_HOME],
194
+ "five_up": [ord('K')],
195
+ "five_down": [ord('J')],
196
+ "enter": [ord('\n'), curses.KEY_ENTER],
197
+ "redraw_screen": [12], # Ctrl-l
198
+ "cycle_sort_method": [ord('s')],
199
+ "cycle_sort_method_reverse": [ord('S')],
200
+ "cycle_sort_order": [ord('t')],
201
+ "increase_column_width": [ord(']')],
202
+ "decrease_column_width": [ord('[')],
203
+ "filter_input": [ord('f')],
204
+ "search_input": [ord('/')],
205
+ "settings_input": [ord('`')],
206
+ "settings_options": [ord('~')],
207
+ "continue_search_forward": [ord('n'), ord('i')],
208
+ "continue_search_backward": [ord('N'), ord('I')],
209
+ "cancel": [27], # Escape key
210
+ "opts_input": [ord(':')],
211
+ "opts_select": [ord('o')],
212
+ "mode_next": [9], # Tab key
213
+ "mode_prev": [353], # Shift+Tab key
214
+ "pipe_input": [ord('|')],
215
+ "reset_opts": [ord('\\')],
216
+ "col_select": [ord('0'), ord('1'), ord('2'), ord('3'), ord('4'), ord('5'), ord('6'), ord('7'), ord('8'), ord('9')],
217
+ "col_select_next": [ord('>'), ord('l')],
218
+ "col_select_prev": [ord('<'), ord('h')],
219
+ "col_hide": [ord('!'), ord('@'), ord('#'), ord('$'), ord('%'), ord('^'), ord('&'), ord('*'), ord('('), ord(')')],
220
+ "edit": [ord('e')],
221
+ "edit_picker": [ord('E')],
222
+ "edit_ipython": [5], # Ctrl+e
223
+ "copy": [ord('y')],
224
+ "save": [19, ord('D')], # Ctrl+s
225
+ "load": [ord('L'), 15], # Ctrl+o
226
+ "open": [ord('O')],
227
+ "toggle_footer": [ord('_')],
228
+ }