C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
pick_engine.c
Go to the documentation of this file.
1/** @file pick_engine.c
2 @brief pick from a list of choices
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10/** @defgroup pick_engine Object Selection
11 @brief Navigate, Select, and Perform Action on Objects
12 */
13
14#include "include/common.h"
15#include <fcntl.h>
16#include <stdbool.h>
17#include <string.h>
18#include <sys/mman.h>
19#include <sys/stat.h>
20#include <sys/wait.h>
21#include <termios.h>
22#include <unistd.h>
25int pick_engine(Init *);
26void save_object(Pick *, char *);
27int picker(Init *, char *field);
28void display_pick_page(Pick *);
29void reverse_object(Pick *);
30void unreverse_object(Pick *);
31void toggle_object(Pick *);
32int output_objects(Pick *);
33int exec_objects(Init *);
34int open_pick_win(Init *);
35void display_pick_help(Init *);
36int read_pick_input(Init *);
37void deselect_object(Pick *);
38int read_theme(Init *);
39int new_pick_view(Init *);
40void new_view_file(Init *, char *);
41void end_pick_view(Init *);
42void destroy_pick_view(Init *);
43int match_objects(Pick *pick, char *s);
44void remove_right_angle(Pick *);
45void pick_std_chyron(Pick *);
46int pipe_fd[2];
47
48char const pagers_editors[12][10] = {"view", "view", "less", "more",
49 "vi", "vim", "nano", "nvim",
50 "pico", "emacs", "edit", ""};
51
52/** @brief Initializes pick structure and opens pick input file or pipe
53 * @ingroup pick_engine
54 @param init Pointer to Init structure
55 @param argc Argument count
56 @param argv Argument vector
57 @param by Beginning y coordinate for pick window
58 @param bx Beginning x coordinate for pick window
59 @details If provider_cmd is specified, it takes precedence over in_spec and
60 input file arguments.
61 provider_cmd is executed and its output is read as pick input
62 If provider_cmd is not specified, in_spec is used to read pick
63 input from a file or stdin
64 If provider_cmd is specified, it is executed and its output is
65 read as pick input */
66int init_pick(Init *init, int argc, char **argv, int by, int bx) {
67 struct stat sb;
68 char *s_argv[MAXARGS];
69 int s_argc;
70 char tmp_str[MAXLEN];
71 pid_t pid = 0;
72
73 Pick *pick = new_pick(init, argc, argv, by, bx);
74 if (init->pick != pick)
75 abend(-1, "init->pick != pick\n");
76 // SIO *sio = init->sio;
77 if (pick->provider_cmd[0] != '\0') {
78 s_argc = str_to_args(s_argv, pick->provider_cmd, MAXARGS - 1);
79 if (pipe(pipe_fd) == -1) {
80 Perror("pipe(pipe_fd) failed in init_pick");
81 return (1);
82 }
83 if ((pid = fork()) == -1) {
84 Perror("fork() failed in init_pick");
85 return (1);
86 }
87 if (pid == 0) {
88 /** Prevent child process from writing to terminal */
89 int dev_null = open("/dev/null", O_WRONLY);
90 if (dev_null == -1) {
91 Perror("open(/dev/null) failed in init_pick child process");
92 exit(EXIT_FAILURE);
93 }
94 dup2(dev_null, STDERR_FILENO);
95 close(dev_null);
96 /** Close read end of pipe as Child only needs to write to pipe */
97 close(pipe_fd[P_READ]);
98 /** Connect CHILD STDOUT to write end of pipe */
99 dup2(pipe_fd[P_WRITE], STDOUT_FILENO);
100 /** STDOUT attached to write end of pipe, so close pipe fd */
101 close(pipe_fd[P_WRITE]);
102 execvp(s_argv[0], s_argv);
103 strnz__cpy(tmp_str, "Can't exec pick start cmd: ", MAXLEN - 1);
104 strnz__cat(tmp_str, s_argv[0], MAXLEN - 1);
105 Perror(tmp_str);
106 exit(EXIT_FAILURE);
107 }
108 /** Return to Parent
109 Close write end of pipe as Parent only needs to read from pipe */
110 close(pipe_fd[P_WRITE]);
111 /** Open a file pointer on read end of pipe */
112 pick->in_fp = fdopen(pipe_fd[P_READ], "rb");
113 pick->f_in_pipe = true;
114 destroy_argv(s_argc, s_argv);
115 } else {
116 if ((pick->in_spec[0] == '\0') || strcmp(pick->in_spec, "-") == 0 || strcmp(pick->in_spec, "/dev/stdin") == 0) {
117 strnz__cpy(pick->in_spec, "/dev/stdin", MAXLEN - 1);
118 pick->in_fp = fdopen(STDIN_FILENO, "rb");
119 pick->f_in_pipe = true;
120 }
121 }
122 if (!pick->f_in_pipe) {
123 /** No provider_cmd specified, so read pick input from file or stdin */
124 if (lstat(pick->in_spec, &sb) == -1) {
125 strnz__cpy(tmp_str, "Can\'t stat pick input file: ", MAXLEN - 1);
126 strnz__cat(tmp_str, pick->in_spec, MAXLEN - 1);
127 Perror(tmp_str);
128 return (1);
129 }
130 if (sb.st_size == 0) {
131 strnz__cpy(tmp_str, "Pick input file empty: ", MAXLEN - 1);
132 strnz__cat(tmp_str, pick->in_spec, MAXLEN - 1);
133 Perror(tmp_str);
134 return (1);
135 }
136 if ((pick->in_fp = fopen(pick->in_spec, "rb")) == nullptr) {
137 strnz__cpy(tmp_str, "Can't open pick input file: ", MAXLEN - 1);
138 strnz__cat(tmp_str, pick->in_spec, MAXLEN - 1);
139 Perror(tmp_str);
140 return (1);
141 }
142 }
143 /*------------------------------------------------------------*/
144 if (pick->in_fp == nullptr) {
145 Perror("No pick input available");
146 return (1);
147 }
148 /*------------------------------------------------------------*/
150 if (pick->f_in_pipe && pid > 0) {
151 //
152 // Wait for provider_cmd child process to finish before proceeding bool
153 // rc = waitpid_with_timeout(pid, wait_timeout);
154 //
155 // if (rc == false) {
156 // Perror("Timeout waiting for provider_cmd child process to finish");
157 // kill(pid, SIGKILL);
158 // waitpid(pid, nullptr, 0);
159 // }
160 waitpid(pid, nullptr, 0);
161 close(pipe_fd[P_READ]);
162 // dup2(sio->stdin_fd, STDIN_FILENO);
165 keypad(pick->win, true);
166 }
167 if (pick->m_cnt == 0) {
168 Perror("No pick objects available");
169 return (1);
170 }
171 /** Enter pick_engine */
172 pick->m_idx = 0;
173 pick->d_idx = 0;
174 while (pick->m_idx < pick->m_cnt)
175 pick->d_object[pick->d_idx++] = pick->m_object[pick->m_idx++];
176 pick->d_cnt = pick->d_idx;
178 set_chyron_key(pick->chyron, 1, "F1 Help", KEY_F(1));
179 set_chyron_key(pick->chyron, 2, "F9 Cancel", KEY_F(9));
180 set_chyron_key(pick->chyron, 3, "F10 Accept", KEY_F(10));
181 set_chyron_key(pick->chyron, 4, "<v> View", 'v');
182 set_chyron_key(pick->chyron, 5, "<q> Quit View", 'q');
183 set_chyron_key(pick->chyron, 6, "<Sp> Process", ' ');
184 set_chyron_key(pick->chyron, 7, "<Sp> Toggle", ' ');
185 set_chyron_key(pick->chyron, 9, "<Tab> Search", '\t');
186 set_chyron_key(pick->chyron, 10, "<Tab> Select", '\t');
187 set_chyron_key(pick->chyron, 11, "PgUp", KEY_PPAGE);
188 set_chyron_key(pick->chyron, 12, "PgDn", KEY_NPAGE);
189 set_chyron_key(pick->chyron, 13, "INS", KEY_IC);
192 pick_engine(init);
193 if (pick->p_view_files)
195 win_del();
196 return 0;
197}
198
199/** @brief Destroys pick view, unmaps file buffer, and cleans up resources
200 * @ingroup pick_engine
201 @param init Pointer to Init structure containing pick information
202 @details If pick->p_view_files is true and view->buf is not nullptr, destroys
203 line table, unmaps the file buffer, and sets view->buf to nullptr. Destroys
204 view window and view structure to free associated resources.
205 */
206void destroy_pick_view(Init *init) {
207 stdio_fdnames(stdio_names_str, "pick_engine.c 279");
208 Pick *pick = init->pick;
209 View *view = init->view;
210 if (pick->p_view_files) {
211 if (view->buf != nullptr) {
213 munmap(view->buf, view->file_size);
214 view->buf = nullptr;
215 }
216 }
218 destroy_view(init);
219}
220/** @brief Reads pick input from file pointer and saves objects into pick
221 structure
222 * @ingroup pick_engine
223 @param init Pointer to Init structure containing pick information
224 @return 0 on success, -1 if no objects were read
225 @details Reads lines from pick->in_fp and saves them as objects in the pick
226 structure using save_object function. If no objects are read, returns -1.
227 Otherwise, sets obj_cnt to the number of objects read and resets obj_idx to 0
228 before returning 0. */
229int read_pick_input(Init *init) {
230 int i;
231
232 Pick *pick = init->pick;
233 pick->select_cnt = 0;
234 pick->tbl_pages = 1;
235
236 if (pick->in_fp) {
237 while (fgets(pick->in_buf, sizeof(pick->in_buf), pick->in_fp) != nullptr)
238 save_object(pick, pick->in_buf);
239
240 } else
241 for (i = 1; i < pick->argc; i++)
242 save_object(pick, pick->argv[i]);
243 if (pick->in_fp != nullptr)
244 fclose(pick->in_fp);
245 if (!pick->m_idx)
246 return (-1);
247 pick->m_cnt = pick->m_idx;
248 return 0;
249}
250/** @brief Initializes pick interface, calculates window size and position, and
251 enters picker loop
252 * @ingroup pick_engine
253 @param init Pointer to Init structure containing pick information
254 @return Count of selected objects on success, -1 if user cancels
255 @details Initializes key command strings for chyron display and calculates
256 pick window size and position based on terminal size and pick parameters. Opens
257 pick window and displays first page of objects. Enters picker loop to handle
258 user input and interactions. If user cancels selection, returns -1. If user
259 accepts selection, returns count of selected objects. */
260int pick_engine(Init *init) {
261 int rc;
262 int maxy, maxx;
263 int whitespace_ratio = 15;
264 int usable_lines;
265 int pick_ratio = 50;
266 int tbl_max_cols, pg_max_objs;
267 bool f_processed = false;
268
269 Pick *pick = init->pick;
270 getmaxyx(stdscr, maxy, maxx);
271 // Screen Geometry
272 // Calculate pick window size and position based on terminal size and pick
273 // parameters
274 //
275 // Calculate line usage:
276 if (pick->begy == 0)
277 pick->begy = (maxy * whitespace_ratio) / 200;
278 usable_lines = maxy - (maxy * whitespace_ratio) / 100;
279 usable_lines -= pick->begy;
280 usable_lines -= 5; // Pick overhead lines
281 if (pick->p_view_files) {
282 if (pick->lines == 0) {
283 pick->lines = usable_lines * pick_ratio / 100;
284 usable_lines -= pick->lines;
285 }
286 init->begy = pick->begy + 5 + pick->lines;
287 init->begx = 0;
288 usable_lines -= 3; // View overhead lines
289 init->lines = usable_lines;
290 init->cols = maxx - 2;
291 } else if (pick->lines == 0)
292 pick->lines = usable_lines;
293
294 pick->tbl_col_width = max(pick->tbl_col_width, 4);
295 pick->tbl_col_width = min(pick->tbl_col_width, (maxx - (2 + pick->begx)));
296 if (pick->d_cnt <= pick->lines) {
297 pick->tbl_lines = pick->d_cnt;
298 pick->lines = pick->d_cnt;
299 pick->tbl_cols = 1;
300 } else {
301 tbl_max_cols = ((maxx - (2 + pick->begx)) / (pick->tbl_col_width + 1));
302 pg_max_objs = pick->lines * tbl_max_cols;
303 if (pick->d_cnt > pg_max_objs)
304 pick->tbl_cols = tbl_max_cols;
305 else
306 pick->tbl_cols = pick->d_cnt / pick->lines;
307 pick->tbl_lines = pick->d_cnt / tbl_max_cols;
308 }
309 pick->tbl_pages = (pick->tbl_lines / (pick->lines - 1)) + 1;
310 // pick->lines = (pick->tbl_lines + pick->tbl_pages - 1) / pick->tbl_pages;
311 pick->tbl_page = 0;
312 // if (pick->begy == 0)
313 // pick->begy = (LINES - pick->lines) / 5;
314 // else if (pick->begy + pick->lines > LINES - 4)
315 // pick->begy = LINES - pick->lines - 2;
316 pick->width = (pick->tbl_col_width + 1) * pick->tbl_cols;
317
318 // The following lines will accomodate the longest chyron line
319 pick->chyron->key[11]->active = true;
320 pick->chyron->key[12]->active = true;
322 pick->width = max(pick->width, pick->chyron->l);
324 rc = open_pick_win(init);
325 if (rc) {
326 Perror("Failed to open pick window");
327 exit(EXIT_FAILURE);
328 // return (rc);
329 }
330 /** Enter picker loop to handle user input and interactions */
331 pick->d_idx = 0;
332 pick->x = 1;
333 char field[MAXLEN]; /**< Buffer for user input in the field */
334 field[0] = '\0';
336
337 do {
338 rc = picker(init, field);
339 if (rc == KEY_F(9))
340 break;
341 if (rc == -1)
342 break;
343 else {
344 if (pick->select_cnt > 0) {
345 if (pick->f_out_spec && pick->out_spec[0]) {
347 f_processed = true;
348 }
349 if (pick->f_cmd && pick->cmd[0]) {
350 exec_objects(init);
351 f_processed = true;
352 }
353 if (pick->f_read_theme) {
354 read_theme(init);
355 f_processed = true;
356 }
357 if (f_processed) {
358 mvwaddstr(pick->win2, 0, 0, "Selection Processed");
359 wclrtoeol(pick->win2);
360 }
361 }
362 }
364 } while (1);
366 return (rc);
367}
368/** @brief Sets standard chyron key states based on pick structure
369 * @ingroup pick_engine
370 @param pick Pointer to Pick structure containing chyron and selection
371 information
372 @details Updates the active state of chyron keys based on the current state
373 of the pick structure. Enables or disables keys for help, cancel, accept,
374 view, quit view, process, toggle, search, select, page up, page down, and
375 insert based on selection count, view mode, and table pages.
376 */
377void pick_std_chyron(Pick *pick) {
378 // * 1 F1 Help KEY_F(1));
379 // * 2 F9 Cancel KEY_F(9));
380 // * 3 F10 Accept KEY_F(10));
381 // ? 4 <v> View <v>
382 // ? 5 <q> Quit View <q>
383 // ? 6 <Sp> Process <Sp>
384 // ? 7 <Sp> Toggle <Sp>
385 // ? 9 <Tab> Search <Tab>
386 // ? 10 <Tab> Select" <Tab>
387 // ? 11 PgUp KEY_PPAGE
388 // ? 12 PgDn KEY_NPAGE
389 // ? 13 INS KEY_IC
390 pick->chyron->key[1]->active = true; // F1 Help
391 pick->chyron->key[2]->active = true; // F9 Cancel
392 pick->chyron->key[3]->active = pick->select_max != 1 ? true : false; // F10 Accept
393 pick->chyron->key[4]->active = pick->p_view_files; // <v> View
394 pick->chyron->key[5]->active = false; // <q> Quit View
395 pick->chyron->key[6]->active = pick->select_max != 1 ? true : false; // <Sp> Toggle
396 pick->chyron->key[7]->active = pick->select_max == 1 ? true : false; // <Sp> Process
397 pick->chyron->key[9]->active = true; // <Tab> Search;
398 pick->chyron->key[10]->active = false; // <Tab> Select;
399 pick->chyron->key[11]->active = pick->tbl_page > 0 ? true : false; // PgUp
400 pick->chyron->key[12]->active = pick->tbl_page < pick->tbl_pages - 1 ? true : false; // PgDn
401 pick->chyron->key[13]->active = false; // INS
402}
403/** @brief Saves a string as an object in the pick structure
404 * @ingroup pick_engine
405 @param pick Pointer to Pick structure
406 @param s String to save as an object
407 @details If the current object index is less than the maximum allowed, saves
408 the string as an object in the pick structure. Updates the column width if
409 necessary and marks the object as not selected. Increments the object index
410 for the next object to be saved. */
411void save_object(Pick *pick, char *s) {
412 int l;
413
414 if (pick->m_idx < OBJ_MAXCNT - 1) {
415 l = strlen(s);
416 if (l > OBJ_MAXLEN - 1)
417 s[OBJ_MAXLEN - 1] = '\0';
418 pick->tbl_col_width = max(pick->tbl_col_width, l);
419 l = max(l, 1);
420 pick->m_object[pick->m_idx] = (char *)calloc(l + 1, sizeof(char));
422 pick->f_selected[pick->m_idx] = false;
423 pick->m_idx++;
424 }
425}
426
427/** @brief Displays current page of objects in pick window
428 * @ingroup pick_engine
429 @param pick Pointer to Pick structure containing objects and display
430 information
431 @details Clears the pick window and displays the current page of objects
432 based on the current table page, line, and column. Marks selected objects with
433 an asterisk. Updates the chyron with page information at the bottom of the pick
434 window. */
435void display_pick_page(Pick *pick) {
436 int col;
437 for (pick->y = 0; pick->y < pick->lines; pick->y++) {
438 wmove(pick->win, pick->y, 0);
439 wclrtoeol(pick->win);
440 }
441 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols;
442 for (col = 0; col < pick->tbl_cols; col++) {
443 pick->x = col * (pick->tbl_col_width + 1) + 1;
444 pick->y = 0;
445 while (pick->d_idx < pick->d_cnt && pick->y < pick->lines) {
446 if (pick->f_selected[pick->d_idx])
447 mvwaddstr(pick->win, pick->y, pick->x - 1, "*");
448 mvwaddstr_fill(pick->win, pick->y++, pick->x,
449 pick->d_object[pick->d_idx++], pick->tbl_col_width - 1);
450 }
451 }
452 pick->d_idx -= 1;
453 pick->tbl_lines = pick->d_cnt;
454 pick->tbl_pages = ((pick->tbl_lines + pick->lines - 1) / pick->lines);
455 if (pick->y < pick->lines) {
456 pick->y_offset = pick->lines - pick->y;
457 wscrl(pick->win, -pick->y_offset);
458 } else
459 pick->y_offset = 0;
460}
461/** @brief Displays current page of objects in pick window
462 @ingroup pick_engine
463 @param pick Pointer to Pick structure containing objects and display
464 information
465 @param s String to filter objects by
466 @details Clears the pick window and displays the current page of objects
467 based on the current table page, line, and column. Marks selected objects
468 with an asterisk. Updates the chyron with page information at the bottom of
469 the pick window. */
470int match_objects(Pick *pick, char *s) {
471 /** pick->m_idx Master (as read from input) */
472 /** pick->d_idx Display (to display) */
473 pick->m_idx = 0;
474 pick->d_idx = 0;
475 while (pick->m_idx < pick->m_cnt) {
476 if (s == nullptr || s[0] == '\0' || strstr(pick->m_object[pick->m_idx], s) != nullptr) {
477 pick->d_object[pick->d_idx++] = pick->m_object[pick->m_idx];
478 }
479 pick->m_idx++;
480 }
481 pick->d_cnt = pick->d_idx;
482 return pick->d_cnt;
483}
484/** @brief Reverses the display of the currently selected object in pick window
485 * @ingroup pick_engine
486 @param pick Pointer to Pick structure containing object and display
487 information
488 @details Calculates the x coordinate for the currently selected object based
489 on the current table column and column width. Moves the cursor to the object's
490 position in the pick window, turns on reverse video attribute, and displays
491 the object's text. Turns off reverse video attribute and refreshes the pick
492 window to show the updated display. Moves the cursor back to the position
493 before the object text for potential further interactions. */
494void reverse_object(Pick *pick) {
495 if (pick->d_idx >= pick->d_cnt)
496 pick->d_idx = pick->d_cnt - 1;
497 pick->x = pick->tbl_col * (pick->tbl_col_width + 1) + 1;
498 pick->tbl_line = (pick->d_idx / pick->tbl_cols) % pick->lines;
499 pick->y = pick->tbl_line + pick->y_offset;
500 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line;
501 wbkgrndset(pick->win, &CC_NT_REV);
502 wmove(pick->win, pick->y, pick->x);
504 pick->tbl_col_width - 1);
505 wmove(pick->win, pick->y, pick->x - 1);
506 if (pick->f_selected[pick->d_idx])
507 mvwadd_wchnstr(pick->win, pick->y, 0, &chk, 1);
508 else
509 mvwadd_wchnstr(pick->win, pick->y, 0, &ran, 1); // space
510 wbkgrndset(pick->win, &CC_NT);
511}
512/** @brief Unreverses the display of the currently selected object in pick
513 window
514 @ingroup pick_engine
515 @param pick Pointer to Pick structure containing object and display
516 information
517 @details Calculates the x coordinate for the currently selected object based
518 on the current table column and column width. Moves the cursor to the
519 object's position in the pick window and displays the object's text without
520 reverse video attribute. Refreshes the pick window to show the updated
521 display. Moves the cursor back to the position before the object text for
522 potential further interactions. */
523void unreverse_object(Pick *pick) {
524 if (pick->d_idx >= pick->d_cnt)
525 pick->d_idx = pick->d_cnt - 1;
526 pick->x = pick->tbl_col * (pick->tbl_col_width + 1) + 1;
527 pick->tbl_line = (pick->d_idx / pick->tbl_cols) % pick->lines;
528 pick->y = pick->tbl_line + pick->y_offset;
529 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line;
530 wbkgrndset(pick->win, &CC_NT);
532 pick->tbl_col_width - 1);
533 if (pick->f_selected[pick->d_idx])
534 mvwadd_wchnstr(pick->win, pick->y, 0, &chk, 1);
535 else
536 mvwadd_wchnstr(pick->win, pick->y, 0, &sp, 1); // space
537 wmove(pick->win, pick->y, 0);
538}
539void remove_right_angle(Pick *pick) {
540 if (pick->d_idx >= pick->d_cnt)
541 pick->d_idx = pick->d_cnt - 1;
542 pick->tbl_line = (pick->d_idx / pick->tbl_cols) % pick->lines;
543 pick->y = pick->tbl_line + pick->y_offset;
544 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line;
545 if (pick->f_selected[pick->d_idx])
546 mvwadd_wchnstr(pick->win, pick->y, 0, &chk, 1);
547 else
548 mvwadd_wchnstr(pick->win, pick->y, 0, &sp, 1); // space
549 wmove(pick->win, pick->y, 0);
550}
551/** @brief Toggles the selection state of the currently selected object in pick
552 window
553 @ingroup pick_engine
554 @param pick Pointer to Pick structure containing object and selection
555 information
556 @details Calculates the x coordinate for the currently selected object based
557 on the current table column and column width. If the object is currently
558 selected, it is deselected by updating the selection count, marking it as not
559 selected, and displaying a space before the object text. If the object is not
560 currently selected, it is selected by updating the selection count, marking
561 it as selected, and displaying an asterisk before the object text. Refreshes
562 the pick window to show the updated display. Moves the cursor back to the
563 position before the object text for potential further interactions. */
564void toggle_object(Pick *pick) {
565 pick->x = pick->tbl_col * (pick->tbl_col_width + 1) + 1;
566 if (pick->f_selected[pick->d_idx]) {
567 pick->select_cnt--;
568 pick->f_selected[pick->d_idx] = false;
569 mvwadd_wchnstr(pick->win, pick->y, 0, &sp, 1); // space
570 } else {
571 pick->select_cnt++;
572 pick->f_selected[pick->d_idx] = true;
573 mvwadd_wchnstr(pick->win, pick->y, 0, &chk, 1);
574 }
575}
576/** @brief Deselects the currently selected object in pick window
577 @ingroup pick_engine
578 @details like toggle, but only deselects object */
579void deselect_object(Pick *pick) {
580 pick->x = pick->tbl_col * (pick->tbl_col_width + 1) + 1;
581 if (pick->f_selected[pick->d_idx]) {
582 pick->select_cnt--;
583 pick->f_selected[pick->d_idx] = false;
584 mvwadd_wchnstr(pick->win, pick->y, 0, &sp, 1); // space
585 }
586}
587int read_theme(Init *init) {
588 int rc;
591 if (rc)
592 return rc;
593 SIO *sio = init->sio;
595 update_panels();
596 doupdate();
597 return 0;
598}
599
600/** @brief Outputs selected objects to specified output file
601 @ingroup pick_engine
602 @param pick Pointer to Pick structure containing selected objects and
603 output file information
604 @return 0 on success, 1 on failure
605 @details If output file cannot be opened, an error message is printed and
606 the function returns 1. Otherwise, selected objects are written to the
607 output file, one per line, and the file is closed before returning 0.
608*/
609int output_objects(Pick *pick) {
610 char tmp_str[MAXLEN];
611 int m;
612 if ((pick->out_fp = fopen(pick->out_spec, "w")) == nullptr) {
613 m = MAXLEN - 30;
614 strnz__cpy(tmp_str, "Can't open pick output file: ", m);
615 m -= strlen(pick->in_spec);
616 strnz__cat(tmp_str, pick->out_spec, m);
617 }
618 for (pick->d_idx = 0; pick->d_idx < pick->d_cnt; pick->d_idx++) {
619 if (pick->f_selected[pick->d_idx])
620 fprintf(stdout, "%s\n", pick->d_object[pick->d_idx]);
621 }
622 fflush(stdout);
623 if (pick->out_fp != nullptr)
624 fclose(pick->out_fp);
625 return (0);
626}
627/** @brief Executes specified command with selected objects as arguments
628 @ingroup pick_engine
629 @param init Pointer to Init structure
630 @return 0 on success, 1 on failure
631 @details Parses command string and appends selected objects as arguments to
632 the command. If command contains "%%", it is replaced with a space- separated
633 list of selected objects. Executes the command using execvp in a child
634 process and waits for it to finish. If the command is a pager or editor, it
635 is executed within the pick interface using popup_view instead of execvp.
636 If f_append_objects is true, the argument containing %% is replaced with
637 the concatenated selected objects. If f_append_objects is false, selected
638 objects are added as separate arguments and the original command arguments
639 remain unchanged.
640 eargv should be null-terminated to indicate the end of arguments for
641 execvp
642 Memory allocated for arguments is freed after execution to prevent
643 memory leaks.
644 If execvp fails, an error message is printed and the child process
645 exits with failure status
646 The parent process waits for the child process to finish before
647 proceeding and restores terminal settings
648 If the command is a pager or editor, it is executed within the pick
649 interface using popup_view instead of execvp
650 The base name of the command is extracted to check if it is a pager or
651 editor
652 If the command is a pager or editor, the pick interface is used to
653 display the command output instead of executing it in a separate terminal
654 This allows the user to view the command output without leaving the pick
655 interface and provides a more seamless user experience.
656 If the command is not a pager or editor, it is executed in a separate
657 terminal and the pick interface is restored after execution
658 If the command to be executed is view, an external command is not
659 needed, instead the popup_view function can be used to display the output
660 within the pick interface */
661int exec_objects(Init *init) {
662 int rc = -1;
663 int eargc;
664 char *eargv[MAXARGS];
665 char tmp_str[MAXLEN] = {'\0'};
666 char title[MAXLEN];
667 char sav_arg[MAXLEN];
668 char *out_s;
669 int eargx = 0;
670 int i = 0;
671 pid_t pid = 0;
672 bool f_append_objects = false;
673
674 Pick *pick = init->pick;
675 title[0] = '\0';
676 if (pick->cmd[0] == '\0')
677 return -1;
678 if (pick->cmd[0] == '\\' || pick->cmd[0] == '\"') {
679 size_t len = strlen(pick->cmd);
680 if (len > 1 && pick->cmd[len - 1] == '\"') {
681 memmove(pick->cmd, pick->cmd + 1, len - 2);
682 pick->cmd[len - 2] = '\0';
683 }
684 }
685 eargc = str_to_args(eargv, pick->cmd, MAXARGS - 1);
686 tmp_str[0] = '\0';
687 if (pick->f_multiple_cmd_args) {
688 for (i = 0; i < pick->d_cnt; i++) {
689 if (pick->f_selected[i] && eargc < MAXARGS) {
690 if (tmp_str[0] != '\0')
691 strnz__cat(tmp_str, " ", MAXLEN - 1);
692 strnz__cat(tmp_str, pick->d_object[i], MAXLEN - 1);
693 }
694 }
695 eargv[eargc++] = strdup(tmp_str);
696 } else {
697 f_append_objects = false;
698 i = 0;
699 while (i < eargc) {
700 /** This is the line that gets the selected objects */
701 if (strstr(eargv[i], "%%") != nullptr) {
702 tmp_str[0] = '\0';
703 f_append_objects = true;
704 strnz__cpy(sav_arg, eargv[i], MAXLEN - 1);
705 eargx = i;
706 break;
707 }
708 i++;
709 }
710 for (i = 0; i < pick->d_cnt; i++) {
711 /** append arguments onto tmp_str */
712 if (pick->f_selected[i] && eargc < MAXARGS - 1) {
713 if (f_append_objects == true) {
714 if (tmp_str[0] != '\0')
715 strnz__cat(tmp_str, " ", MAXLEN - 1);
716 strnz__cat(tmp_str, pick->d_object[i], MAXLEN - 1);
717 continue;
718 }
719 eargv[eargc++] = strdup(pick->d_object[i]);
720 }
721 }
722 if (f_append_objects == true) {
723 if (eargv[eargx] != nullptr) {
724 free(eargv[eargx]);
725 eargv[eargx] = nullptr;
726 }
727 out_s = rep_substring(sav_arg, "%%", tmp_str);
728 if (out_s == nullptr || out_s[0] == '\0') {
729 i = 0;
730 while (i < eargc) {
731 if (eargv[i] != nullptr)
732 free(eargv[i]);
733 i++;
734 }
735 Perror("rep_substring() failed in exec_objects");
736 return 1;
737 }
738 strnz__cpy(title, out_s, MAXLEN - 1);
739 eargv[eargx] = strdup(out_s);
740 if (out_s != nullptr) {
741 free(out_s);
742 out_s = nullptr;
743 }
744 }
745 }
746 strnz__cpy(tmp_str, eargv[0], MAXLEN - 1);
747 eargv[eargc] = nullptr;
748 char *sav_ptr;
749 char *tok;
750 tok = strtok_r(tmp_str, " ", &sav_ptr);
751 strnz__cpy(sav_arg, tok, MAXLEN - 1);
752 base_name(tmp_str, sav_arg);
753 if (tmp_str[0] != '\0' && (strcmp(tmp_str, "view") == 0 || strcmp(tmp_str, "view") == 0)) {
754 /** initialize popup_view arguments and execute popup_view to display
755 command output within pick interface */
756 init->lines = 60;
757 init->cols = 80;
758 init->begy = pick->begy + 1;
759 init->begx = pick->begx + 1;
760 if (title[0] != '\0')
761 strnz__cpy(init->title, title, MAXLEN - 1);
762 else
763 strnz__cpy(init->title, eargv[eargc], MAXLEN - 1);
764 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
765 init->begx);
766 i = 0;
767 while (i < eargc) {
768 if (eargv[i] != nullptr)
769 free(eargv[i]);
770 i++;
771 }
772 return 0;
773 } else {
774 werase(stdscr);
775 endwin();
776 if ((pid = fork()) == -1) {
777 /** fork failed, free eargv and return error */
778 i = 0;
779 while (i < eargc) {
780 if (eargv[i] != nullptr)
781 free(eargv[i]);
782 i++;
783 }
784 Perror("fork() failed in exec_objects");
785 return (1);
786 } else if (pid == 0) {
787 /** Prevent child process from writing to terminal */
788 // int dev_null = open("/dev/null", O_WRONLY);
789 // if (dev_null == -1) {
790 // Perror("open(/dev/null) failed in init_pick child process");
791 // exit(EXIT_FAILURE);
792 // }
793 // dup2(dev_null, STDERR_FILENO);
794 // close(dev_null);
795 execvp(eargv[0], eargv);
796 exit(EXIT_FAILURE);
797 }
798 }
799 waitpid(pid, nullptr, 0);
800 destroy_argv(eargc, eargv);
801 reset_prog_mode();
803 return rc;
804}
805/** @brief Initializes the pick window based on the parameters specified in the
806Pick structure
807 @ingroup pick_engine
808 @param init Pointer to Init structure containing pick information
809 @return 0 on success, 1 on failure
810 @details Creates a new window for the pick interface using win_new function
811with the specified parameters from the Pick structure. If window creation fails,
812an error message is printed and the function returns 1. Otherwise, initializes
813the window and box pointers in the Pick structure, sets scrollok and keypad
814options for the window, and returns 0 on success. */
815int open_pick_win(Init *init) {
816 char tmp_str[MAXLEN];
817 Pick *pick = init->pick;
818 pick = init->pick;
819 int split_win_lines = 2; // 1 text, 1 chyron
820 if (box_hsplit_new(pick->lines, split_win_lines, pick->width, pick->begy, pick->begx,
821 pick->title)) {
822 ssnprintf(tmp_str, MAXLEN - 1, "box_hsplit_new(%d, %d, %d, %d, %d, %s) failed",
823 pick->lines, split_win_lines, pick->width, pick->begy, pick->begx,
824 pick->title);
825 Perror(tmp_str);
826 return (1);
827 }
828 pick->separator_line = pick->lines + 1;
829 pick->win = win_win[win_ptr];
830 pick->win2 = win_win2[win_ptr];
831 pick->box = win_box[win_ptr];
832 keypad(pick->win, true);
833 if (pick->p_view_files)
834 new_pick_view(init);
835 return 0;
836}
837/** @brief Displays the help screen for the pick interface using view
838 @ingroup pick_engine
839 @param init Pointer to Init structure containing pick information
840 @details Initializes the help_spec field in the Pick structure with the
841 path to the pick help file. Then, constructs the argument list for
842 executing popup_view with the help file as an argument. Finally, calls
843 popup_view function to display the help screen within the pick interface. */
844void display_pick_help(Init *init) {
845 int eargc;
846 char *eargv[MAXARGS];
847 char tmp_str[MAXLEN];
848 Pick *pick = init->pick;
849 if (pick->f_help_spec && pick->help_spec[0] != '\0')
850 strnz__cpy(tmp_str, pick->help_spec, MAXLEN - 1);
851 else {
852 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
853 strnz__cat(tmp_str, "/", MAXLEN - 1);
855 }
856 eargc = 0;
857 eargv[eargc++] = strdup("view");
858 eargv[eargc++] = strdup("-Nf");
859 eargv[eargc++] = strdup(tmp_str);
860 eargv[eargc] = nullptr;
861 init->lines = 30;
862 init->cols = 76;
863 init->begy = pick->begy + 1;
864 init->begx = pick->begx + 1;
865 strnz__cpy(init->title, "Pick Help", MAXLEN - 1);
866 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
867 init->begx);
868 destroy_argv(eargc, eargv);
869 return;
870}
871/** @brief Main loop for handling user input and interactions in the pick
872 interface
873 @ingroup pick_engine
874 @param init Pointer to Init structure containing pick information
875 @param field Buffer for user input in the field
876 @return Count of selected objects on success, -1 if user cancels
877 @details The first loop handles navigation through the pick table.
878 The second loop handles user input for selecting/deselecting objects,
879 accepting the selection, or canceling the selection. Depending on the key
880 pressed, the appropriate action is taken, such as toggling selection, moving
881 to the next/previous object, or displaying the help screen. If the user
882 accepts the selection, the count of selected objects is returned. If the user
883 cancels the selection, -1 is returned.
884 */
885int picker(Init *init, char *field) {
886 bool f_insert = false; /* Flag to indicate if insert mode is active */
887 Pick *pick = init->pick;
888 int col = 0;
889 int flen = pick->width - 4;
890 char *accept_s = field; /* pointer to start of field buffer */
891 char *ptr = field; /* pointer to current cursor position in field buffer */
892 char *s = field; /* source pointer for editing operations */
893 char *d = field; /* destination pointer for editing operations */
894 char *fend = field + flen;
895 char *str_end = field + strlen(field); /* End of field content */
896 int pos = 0;
897 int prev_pos = 0;
898 char prev_field[MAXLEN];
899 char *prev_ptr = prev_field;
900 char view_file[MAXLEN] = {'\0'};
901 pick = init->pick;
902
903 ptr = accept_s;
904 ptr = str_end;
905 click_x = -1;
906 click_y = click_x = -1;
907 char tmp_str[MAXLEN];
908
909 mousemask(BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED, nullptr);
910
911 f_insert = false;
912
913 keypad(pick->win, true);
914
915 int in_key = 0;
916 while (1) {
917 while (1) {
918 /** ===========================================================
919 Pick Objects Loop
920 =========================================================== */
921 if (in_key == 0) {
923 pick->tbl_line = (pick->d_idx / pick->tbl_cols) % pick->lines;
924 pick->y = pick->tbl_line + pick->y_offset;
925 /** box display_pick_page_info */
926 ssnprintf(tmp_str, MAXLEN - 1, "Line %d, Page %d/%d",
927 pick->tbl_line + 1, pick->tbl_page + 1,
928 pick->tbl_pages);
930 if (pick->p_view_files)
931 if (strcmp(pick->d_object[pick->d_idx], view_file) != 0) {
932 strnz__cpy(view_file, pick->d_object[pick->d_idx], MAXLEN - 1);
933 new_view_file(init, view_file);
934 }
935 mouse_win = nullptr;
936 // 1
937 // top_panel(panel_win[win_ptr]);
942 update_panels();
943 wmove(pick->win, pick->y, pick->x);
944 doupdate();
945 in_key = dxwgetch(pick->win, pick->win, pick->win2, nullptr, pick->win2, pick->chyron, -1);
946 if (pick->f_selected[pick->d_idx])
947 mvwadd_wchnstr(pick->win, pick->y, 0, &chk, 1);
948 else
949 mvwadd_wchnstr(pick->win, pick->y, 0, &sp, 1); // space
950 if (mouse_win == pick->win2 && click_y == 0)
951 break;
952 }
953 switch (in_key) {
954 case KEY_F(1):
957 f_insert = false;
958
959 cchar_t cc = {0};
960 wchar_t wstr[2] = {BW_RAN, L'\0'};
961 setcchar(&cc, wstr, WA_NORMAL, cp_box, nullptr);
962 mvwadd_wch(pick->win2, 0, 0, &cc);
963 in_key = 0;
964 continue;
965
966 case 'v':
967 if (!pick->p_view_files) {
968 in_key = 0;
969 continue;
970 }
971 // * 1 F1 Help KEY_F(1));
972 // * 2 F9 Cancel KEY_F(9));
973 // * 3 F10 Accept KEY_F(10));
974 // ? 4 <v> View <v>
975 // ? 5 <q> Quit View <q>
976 // ? 6 <Sp> Process <Sp>
977 // ? 7 <Sp> Toggle <Sp>
978 // ? 9 <Tab> Search <Tab>
979 // ? 10 <Tab> Select" <Tab>
980 // ? 11 PgUp KEY_PPAGE
981 // ? 12 PgDn KEY_NPAGE
982 // ? 13 INS KEY_IC
983 pick->chyron->key[1]->active = true; // F1 Help
984 pick->chyron->key[2]->active = true; // F9 Cancel
985 pick->chyron->key[4]->active = false; // <v> View
986 pick->chyron->key[5]->active = true; // <q> Quit View
987 pick->chyron->key[6]->active = false; // <Sp> Toggle
988 pick->chyron->key[7]->active = false; // <Sp> Process
989 pick->chyron->key[9]->active = false; // <Tab> Search;
990 pick->chyron->key[10]->active = false; // <Tab> Select;
991 pick->chyron->key[11]->active = true; // PgUp
992 pick->chyron->key[12]->active = true; // PgDn
993 pick->chyron->key[13]->active = false; // INS
997 update_panels();
998 doupdate();
999 if (pick->p_view_files)
1004 in_key = 0;
1005 continue;
1006
1007 case 't':
1008 case ' ':
1009 reverse_object(pick);
1010 toggle_object(pick);
1011 if (pick->select_max > 0 && pick->select_cnt == pick->select_max)
1012 return pick->select_cnt;
1013 in_key = 0;
1014 continue;
1015
1016 /** 'q', or KEY_F(9) cancel selection and exit picker */
1017 case 'q':
1018 case KEY_F(9):
1020 return -1;
1021
1022 /** Enter or KEY_F(10) Accepts current selection and exits
1023 picker, returning count of selected objects */
1024 case KEY_F(10):
1025 case '\n':
1026 case KEY_ENTER:
1027 reverse_object(pick);
1028 return pick->select_cnt;
1029
1030 /** KEY_END Moves selection to last object in list */
1031 case KEY_END:
1032 mvwaddstr_fill(pick->win, pick->y, pick->x,
1033 pick->d_object[pick->d_idx],
1034 pick->tbl_col_width - 1);
1035 int display_tbl_page = pick->tbl_page;
1036 pick->d_idx = pick->d_cnt - 1;
1037 pick->tbl_page = pick->d_idx / (pick->lines * pick->tbl_cols);
1038 pick->tbl_line = (pick->d_idx / pick->tbl_cols) % pick->lines;
1039 pick->tbl_col = pick->d_idx % pick->tbl_cols;
1040 pick->y = pick->tbl_line;
1041 if (display_tbl_page != pick->tbl_page)
1043 in_key = 0;
1044 continue;
1045 case '\t':
1047 wnoutrefresh(pick->win);
1048 in_key = 0;
1049 break;
1050
1051 /** 'h' or KEY_LEFT or Backspace Moves selection to previous
1052 object in list */
1053 case 'h':
1054 case KEY_LEFT:
1055 case KEY_BACKSPACE:
1056 if (pick->tbl_col == 0) {
1057 in_key = 0;
1058 continue;
1059 }
1061 mvwaddstr_fill(pick->win, pick->y, pick->x,
1062 pick->d_object[pick->d_idx],
1063 pick->tbl_col_width - 1);
1064 if (pick->tbl_col > 0)
1065 pick->tbl_col--;
1066 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line;
1067 in_key = 0;
1068 reverse_object(pick);
1069 continue;
1070
1071 /** 'j' or KEY_DOWN Moves selection to next object in list */
1072 case 'j':
1073 case KEY_DOWN:
1074 if (pick->tbl_line == pick->tbl_lines - 1)
1075 break;
1076 mvwaddstr_fill(pick->win, pick->y, pick->x,
1077 pick->d_object[pick->d_idx],
1078 pick->tbl_col_width - 1);
1080 if (pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line < pick->d_cnt - 1 && pick->tbl_line < pick->lines - 1)
1081 pick->tbl_line++;
1082 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line;
1083 in_key = 0;
1084 reverse_object(pick);
1085 continue;
1086
1087 /** 'k' or KEY_UP Moves selection to previous object in list */
1088 case 'k':
1089 case KEY_UP:
1090 mvwaddstr_fill(pick->win, pick->y, pick->x,
1091 pick->d_object[pick->d_idx],
1092 pick->tbl_col_width - 1);
1094 if (pick->tbl_line > 0)
1095 pick->tbl_line--;
1096 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line;
1097 in_key = 0;
1098 reverse_object(pick);
1099 continue;
1100
1101 /** 'l' or KEY_RIGHT Moves selection to next object in list */
1102 case 'l':
1103 case KEY_RIGHT:
1104 if (pick->tbl_col == pick->tbl_cols - 1) {
1105 in_key = 0;
1106 continue;
1107 }
1108 mvwaddstr_fill(pick->win, pick->y, pick->x,
1109 pick->d_object[pick->d_idx],
1110 pick->tbl_col_width - 1);
1112 /** pick->obj_idx += pick->tbl_lines -> next column */
1113 if (pick->tbl_page * pick->lines * pick->tbl_cols + (pick->tbl_col + 1) * pick->lines + pick->tbl_line < pick->d_cnt - 1 && pick->tbl_col < pick->tbl_cols - 1)
1114 pick->tbl_col++;
1115 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->tbl_line;
1116 in_key = 0;
1117 reverse_object(pick);
1118 continue;
1119
1120 /** KEY_NPAGE or 'Ctrl+f' Moves selection to next page of
1121 objects */
1122 case KEY_NPAGE:
1123 case '\06':
1124 if (pick->tbl_pages == 1) {
1125 in_key = 0;
1126 continue;
1127 }
1128 if (pick->tbl_page < pick->tbl_pages - 1) {
1129 pick->tbl_page++;
1130 pick->pg_line = 0;
1131 pick->tbl_col = 0;
1132 }
1133 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_cols * pick->pg_line + pick->tbl_col;
1135 in_key = 0;
1136 continue;
1137
1138 /** KEY_PPAGE or 'Ctrl+b' Moves selection to previous page of
1139 objects */
1140 case KEY_PPAGE:
1141 case '\02':
1142 if (pick->tbl_pages == 1) {
1143 in_key = 0;
1144 continue;
1145 }
1146 if (pick->tbl_page > 0)
1147 pick->tbl_page--;
1148 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_cols * pick->pg_line + pick->tbl_col;
1150 in_key = 0;
1151 continue;
1152
1153 /** KEY_HOME Moves selection to first object in list */
1154 case KEY_HOME:
1155 pick->tbl_page = 0;
1156 pick->tbl_line = 0;
1157 pick->tbl_col = 0;
1158 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_cols * pick->pg_line + pick->tbl_col;
1160 in_key = 0;
1161 continue;
1162
1163 /** KEY_LL (lower left of numeric pad) Moves selection to last
1164 object in list */
1165 case KEY_LL:
1166 pick->tbl_page = pick->tbl_pages - 1;
1167 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_cols * pick->pg_line + pick->tbl_col;
1169 in_key = 0;
1170 continue;
1171
1172 /** KEY_MOUSE Handles mouse events for selection and chyron
1173 * key activation */
1174
1175 case KEY_MOUSE:
1176 if (click_y == -1 || click_x == -1)
1177 continue;
1178 if (click_y < pick->y_offset) {
1179 in_key = 0;
1180 continue;
1181 }
1183 pick->y = click_y;
1184 pick->tbl_col = (click_x - 1) / (pick->tbl_col_width + 1);
1185 if (pick->tbl_col < 0 || pick->tbl_col >= pick->tbl_cols)
1186 continue;
1187 pick->d_idx = pick->tbl_page * pick->lines * pick->tbl_cols + pick->tbl_col * pick->lines + pick->y;
1188 in_key = KEY_F(13); /** toggle selection on mouse click */
1189 click_y = click_x = -1;
1190 continue;
1191
1192 default:
1193 in_key = 0;
1194 continue;
1195 }
1196 in_key = 0;
1197 break;
1198 }
1199 /** ===============================================================
1200 Line editor loop
1201 =============================================================== */
1202 pick->chyron->key[1]->active = true; // F1 Help
1203 pick->chyron->key[2]->active = true; // F9 Cancel
1204 pick->chyron->key[3]->active = false; // F10 Accept
1205 pick->chyron->key[4]->active = false; // <v> View
1206 pick->chyron->key[5]->active = false; // <q> Quit View
1207 pick->chyron->key[5]->active = false; // <Sp> Edit
1208 pick->chyron->key[7]->active = false; // <Sp> Toggle
1209 pick->chyron->key[9]->active = false; // <Tab> Search
1210 pick->chyron->key[10]->active = true; // <Tab> Select
1211 pick->chyron->key[11]->active = false; // PgDn
1212 pick->chyron->key[12]->active = false; // PgUp
1213 pick->chyron->key[13]->active = true; // INS
1214
1215 while (1) {
1216
1217 if (in_key == 0) {
1218 mouse_win = nullptr;
1219 if (accept_s != nullptr && accept_s[0] != '\0') {
1220 if (match_objects(pick, accept_s) == 0) {
1221 strnz__cpy(field, prev_field, MAXLEN - 1);
1222 pos = prev_pos;
1223 ptr = prev_ptr;
1224 } else {
1226 ssnprintf(tmp_str, MAXLEN - 1, "Line %d, Page %d/%d",
1227 pick->tbl_line + 1, pick->tbl_page + 1,
1228 pick->tbl_pages);
1229 strnz__cat(tmp_str, " ", MAXLEN - 1);
1230 tmp_str[21] = '\0';
1231 mvwaddstr(pick->box, pick->separator_line, 3, tmp_str);
1232 }
1233 }
1236 wmove(pick->win2, 0, 1);
1237 /** display_field_content */
1238 rtrim(accept_s);
1239 col = 1;
1240 mvwaddstr(pick->win2, 0, col, accept_s);
1241 wclrtoeol(pick->win2);
1242 if (pick->p_view_files)
1243 if (strcmp(pick->d_object[pick->d_idx], view_file) != 0) {
1244 strnz__cpy(view_file, pick->d_object[pick->d_idx], MAXLEN - 1);
1245 new_view_file(init, view_file);
1246 }
1247 mouse_win = nullptr;
1248 pos = col + strlen(accept_s);
1249 // 2
1250 // top_panel(panel_win2[win_ptr]);
1251 mvwadd_wchnstr(pick->win2, 0, 0, &ran, 1);
1252 update_panels();
1253 wmove(pick->win2, 0, pos);
1254 doupdate();
1255 in_key = dxwgetch(pick->win2, pick->win, pick->win2, nullptr, pick->win2, pick->chyron, -1);
1256 if (mouse_win == pick->win)
1257 break;
1258 if (in_key == KEY_F(13)) {
1259 in_key = 0;
1260 continue;
1261 }
1262 }
1263 strnz__cpy(prev_field, accept_s, MAXLEN - 1);
1264 prev_pos = pos;
1265 prev_ptr = ptr;
1266 switch (in_key) {
1267 case '\n':
1268 case KEY_ENTER:
1269 in_key = 0;
1270 break;
1271
1272 case KEY_BTAB:
1273 case KEY_UP:
1274 case '\t':
1275 in_key = 0;
1276 break;
1277
1278 case KEY_F(1):
1279 return (in_key);
1280
1281 case KEY_F(2):
1282 if (pick->p_view_files)
1284 in_key = 0;
1285 continue;
1286
1287 /** KEY_F(9) Cancels the current operation */
1288 case KEY_BREAK:
1289 case KEY_F(9):
1290 in_key = KEY_F(9);
1291 return (in_key);
1292
1293 /** KEY_F(10) is the default key for accepting the field */
1294 case KEY_F(10):
1295 return (in_key);
1296
1297 case KEY_END:
1298 case Ctrl('e'):
1299 while (*ptr != '\0')
1300 ptr++;
1301 pos = col + (ptr - accept_s);
1302 in_key = 0;
1303 continue;
1304
1305 case KEY_IC:
1306 if (f_insert) {
1307 f_insert = FALSE;
1308 set_chyron_key_cp(pick->chyron, 12, "INS", KEY_IC,
1309 cp_nt_rev);
1310 } else {
1311 f_insert = TRUE;
1312 set_chyron_key_cp(pick->chyron, 12, "INS", KEY_IC,
1314 }
1317 in_key = 0;
1318 continue;
1319
1320 /** KEY_DC deletes character at cursor */
1321 case KEY_DC:
1322 s = ptr + 1;
1323 d = ptr;
1324 while (*s != '\0')
1325 *d++ = *s++;
1326 *d = '\0';
1327 str_end = d;
1328 f_insert = FALSE;
1329 in_key = 0;
1330 continue;
1331
1332 /** KEY_HOME moves cursor to start of field */
1333 case KEY_HOME:
1334 case Ctrl('a'):
1335 ptr = accept_s;
1336 pos = col;
1337 in_key = 0;
1338 continue;
1339
1340 /** KEY_BACKSPACE deletes character before cursor */
1341 case KEY_BACKSPACE:
1342 if (ptr > accept_s) {
1343 ptr--;
1344 pos--;
1345 } else {
1346 in_key = 0;
1347 continue;
1348 }
1349 s = ptr + 1;
1350 d = ptr;
1351 while (*s != '\0')
1352 *d++ = *s++;
1353 *d = '\0';
1354 str_end = d;
1355 if (ptr == accept_s) {
1356 match_objects(pick, accept_s);
1358 ssnprintf(tmp_str, MAXLEN - 1, "Line %d, Page %d/%d",
1359 pick->tbl_line + 1, pick->tbl_page + 1,
1360 pick->tbl_pages);
1361 strnz__cat(tmp_str, " ", MAXLEN - 1);
1362 tmp_str[21] = '\0';
1363 mvwaddstr(pick->box, pick->separator_line, 3, tmp_str);
1364 }
1365 in_key = 0;
1366 continue;
1367
1368 /** KEY_LEFT moves cursor left one character */
1369 case KEY_LEFT:
1370 if (ptr > accept_s) {
1371 ptr--;
1372 pos--;
1373 }
1374 in_key = 0;
1375 continue;
1376
1377 /** KEY_RIGHT moves cursor right one character */
1378 case KEY_RIGHT:
1379 if (ptr < fend && ptr <= str_end) {
1380 ptr++;
1381 pos++;
1382 }
1383 in_key = 0;
1384 continue;
1385
1386 /** Handles mouse events for field editing */
1387 case KEY_MOUSE:
1388 if (click_x < col || click_x >= col + flen) {
1389 in_key = 0;
1390 continue;
1391 }
1392 pos = click_x;
1393 fend = accept_s + flen;
1394 str_end = accept_s + strlen(accept_s);
1395 ptr = accept_s + (pos - col);
1396 ptr = min(ptr, str_end);
1397 pos = col + (ptr - accept_s);
1398 click_x = -1;
1399 in_key = 0;
1400 continue;
1401
1402 default:
1403 if (in_key < ' ' || in_key > '~') {
1404 in_key = 0;
1405 continue;
1406 }
1407 if (ptr >= fend) {
1408 in_key = 0;
1409 continue;
1410 }
1411 if (f_insert) {
1412 if (str_end < fend) {
1413 s = str_end - 1;
1414 d = str_end;
1415 while (s >= ptr)
1416 *d-- = *s--;
1417 *ptr++ = in_key;
1418 str_end++;
1419 pos++;
1420 }
1421 } else {
1422 if (ptr < fend) {
1423 if (ptr < str_end) {
1424 *ptr++ = in_key;
1425 pos++;
1426 } else if (ptr == str_end) {
1427 *ptr++ = in_key;
1428 *ptr = '\0';
1429 str_end = ptr;
1430 pos++;
1431 }
1432 }
1433 }
1434 in_key = 0;
1435 continue;
1436 }
1437 break;
1438 }
1439 mvwaddnwstr(pick->box, pick->separator_line + 1, 1, &bw_sp, 1);
1440 }
1441}
1442/** @brief Initializes a new view for displaying file contents in the pick
1443 interface
1444 @ingroup pick_engine
1445 @param init Pointer to Init structure containing pick information
1446 @return 0 on success, 1 on failure
1447 @details Allocates memory for a new View structure and initializes its
1448 fields based on the parameters specified in the Init structure. Sets up the
1449 help_spec field for the view, and calls init_view_boxwin to create the view's
1450 window. Returns 0 on success, or 1 if window creation fails.
1451 */
1452int new_pick_view(Init *init) {
1453 char *e;
1454 // if (init->view != nullptr)
1455 // view_stack_push(&view_stack, *init->view);
1456 init->view = nullptr;
1458 View *view = init->view;
1459 view = new_view(init); // View struct allocation
1460 view->lines = init->lines;
1461 view->cols = init->cols;
1462 view->begy = init->begy;
1463 view->begx = init->begx;
1464 view->receiver_cmd[0] = '\0';
1467 view->f_squeeze = init->f_squeeze;
1468 view->tab_stop = init->tab_stop;
1469 view->f_ln = init->f_ln;
1470 view->h_shift = init->h_shift;
1471 e = getenv("VIEW_HELP_FILE");
1472 if (e && e[0] != '\0') {
1474 }
1475 view->f_help_spec =
1477 "~/menuapp/help", R_OK);
1478 if (!view->f_help_spec) {
1480 strnz__cat(view->help_spec, "/help/", MAXLEN - 1);
1482 }
1483 int rc = init_view_boxwin(init);
1484 return rc;
1485}
1486/** @brief Initializes a new view for displaying the contents of a specified
1487 file in the pick interface
1488 @ingroup pick_engine
1489 @param init Pointer to Init structure containing pick information
1490 @param file Name of the file to be displayed in the view
1491 @details If a view is already initialized, it destroys the existing line
1492 table and unmaps the buffer. Then, it sets up the provider command for
1493 highlighting the specified file, initializes the view input, and if successful,
1494 resets various view parameters, initializes the line table, and displays the
1495 first page of the file contents along with the prompt.
1496 */
1497void new_view_file(Init *init, char *file) {
1498 View *view = init->view;
1499 if (view->buf != nullptr) {
1501 munmap(view->buf, view->file_size);
1502 view->buf = nullptr;
1503 }
1504 strnz__cpy(view->provider_cmd, "tree-sitter highlight ", MAXLEN - 1);
1506 strnz__cpy(view->title, file, MAXLEN - 1);
1507 if (view_init_input(init, file) == 0) {
1508 if (view->buf) {
1509 view->f_eod = 0;
1510 view->f_bod = 0;
1511 view->maxcol = 0;
1512 view->page_top_pos = 0;
1513 view->page_top_ln = 0;
1514 view->page_bot_ln = 0;
1515 view->ln_max_pos = 0;
1516 view->ln = 0;
1517 view->page_bot_pos = 0;
1518 view->file_pos = 0;
1521 next_page(view);
1522 build_prompt(view);
1524 pad_refresh(view);
1525 }
1526 }
1527}
int process_config_file(char *, Init *)
Definition init.c:631
#define P_READ
Definition common.h:50
#define PICK_HELP_FILE
Definition common.h:34
void destroy_view_win(Init *)
Definition init_view.c:318
int popup_view(Init *, int, char **, int, int, int, int)
instantiate a view popup window
Definition popups.c:144
#define VIEW_HELP_FILE
Definition common.h:35
#define P_WRITE
Definition common.h:51
void destroy_line_table(View *)
#define OBJ_MAXLEN
Definition pick.h:16
#define OBJ_MAXCNT
Definition pick.h:17
size_t rtrim(char *)
Trims trailing spaces from string s in place.
Definition futil.c:338
int cp_box
Definition dwin.c:179
WINDOW * win_win[MAXWIN]
Definition dwin.c:52
cchar_t chk
Definition cm.h:631
WINDOW * win_win2[MAXWIN]
Definition dwin.c:51
#define MAXARGS
Definition cm.h:48
WINDOW * mouse_win
Definition dwin.c:118
cchar_t CC_NT
Definition dwin.c:204
cchar_t ran
Definition cm.h:631
const wchar_t bw_sp
Definition dwin.c:146
cchar_t sp
Definition cm.h:631
#define BW_RAN
Definition cm.h:600
int click_x
Definition dwin.c:81
int cp_nt_hl_rev
Definition dwin.c:186
char stdio_names_str[MAXLEN]
Definition futil.c:134
int win_ptr
Definition dwin.c:164
int click_y
Definition dwin.c:80
#define min(x, y)
min macro evaluates two expressions, returning least result
Definition cm.h:89
cchar_t CC_NT_REV
Definition dwin.c:205
int cp_nt_rev
Definition dwin.c:184
char * stdio_fdnames(char *, char *)
Definition futil.c:1089
WINDOW * win_box[MAXWIN]
Definition dwin.c:53
#define Ctrl(c)
Definition cm.h:52
#define max(a, b)
max macro evaluates two expressions, returning greatest result.
Definition cm.h:82
#define TRUE
Definition iloan.c:19
#define FALSE
Definition iloan.c:18
#define MAXLEN
Definition curskeys.c:15
void end_pick_view(Init *)
int tbl_line
Definition pick_engine.c:23
int obj_idx
Definition pick_engine.c:24
int tbl_cols
Definition pick_engine.c:23
char const pagers_editors[12][10]
Definition pick_engine.c:48
int read_theme(Init *)
int tbl_col
Definition pick_engine.c:23
int pipe_fd[2]
Definition pick_engine.c:46
int tbl_pages
Definition pick_engine.c:23
int calculated_idx
Definition pick_engine.c:24
int pg_lines
Definition pick_engine.c:23
int tbl_page
Definition pick_engine.c:23
void remove_right_angle(Pick *)
int border_title(WINDOW *, char *)
Draw a box with a title around the specified window.
Definition dwin.c:1327
int dxwgetch(WINDOW *, WINDOW *, WINDOW *, WINDOW *, WINDOW *, Chyron *, int)
Wrapper for wgetch that handles signals, mouse events, checks for clicks on the chyron line,...
Definition dwin.c:2180
void restore_wins()
Restore all windows after a screen resize.
Definition dwin.c:1225
void win_del()
Delete the current window and its associated box window.
Definition dwin.c:1179
int border_hsplit_text(WINDOW *, char *, int)
Draw a box with a separator line and text around the specified window.
Definition dwin.c:1289
void mvwaddstr_fill(WINDOW *, int, int, char *, int)
For lines shorter than their display area, fill the rest with spaces.
Definition dwin.c:1941
int box_hsplit_new(int, int, int, int, int, char *)
Create a new window with optional box and title, and a second window inside it, split horizontally.
Definition dwin.c:925
void initialize_local_colors(SIO *)
Initialize local color variables and color pairs based on SIO settings.
Definition dwin.c:320
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1526
void abend(int, char *)
Abnormal program termination.
Definition dwin.c:2018
void set_chyron_key(Chyron *, int, char *, int)
Set chyron key with default color pair (cp_nt_rev).
Definition dwin.c:1783
void display_chyron(WINDOW *, Chyron *, int, int)
Display chyron on window.
Definition dwin.c:1892
Chyron * destroy_chyron(Chyron *chyron)
Destroy Chyron structure.
Definition dwin.c:1722
void set_chyron_key_cp(Chyron *, int, char *, int, int)
Set chyron key with color pair (cp).
Definition dwin.c:1759
void compile_chyron(Chyron *)
construct the chyron string from the chyron structure
Definition dwin.c:1848
Chyron * new_chyron()
Create and initialize Chyron structure.
Definition dwin.c:1701
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:544
int destroy_argv(int argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:494
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:420
bool expand_tilde(char *, int)
Replaces "~/" in string with the user's home directory.
Definition futil.c:970
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition futil.c:573
char * rep_substring(const char *, const char *, const char *)
Replace all occurrences of "tgt_s" in "org_s" with "rep_s".
Definition futil.c:1440
bool base_name(char *, char *)
Returns the base name of a file specification.
Definition futil.c:1123
int str_to_args(char **, char *, int)
Converts a string into an array of argument strings.
Definition futil.c:440
int init_view_boxwin(Init *)
Initialize the C-Menu View in box window mode.
Definition init_view.c:194
int view_init_input(Init *, char *)
Initialize the input for the C-Menu View.
Definition init_view.c:447
View * destroy_view(Init *init)
Destroy View structure.
Definition mem.c:356
bool verify_spec_arg(char *, char *, char *, char *, int)
Verify file specification argument.
Definition mem.c:380
View * new_view(Init *)
Create and initialize View structure.
Definition mem.c:310
Pick * new_pick(Init *, int, char **, int, int)
Create and initialize Pick structure.
Definition mem.c:197
void save_object(Pick *, char *)
Saves a string as an object in the pick structure.
void toggle_object(Pick *)
Toggles the selection state of the currently selected object in pick window.
int picker(Init *, char *field)
Main loop for handling user input and interactions in the pick interface.
int init_pick(Init *, int, char **, int, int)
Initializes pick structure and opens pick input file or pipe.
Definition pick_engine.c:66
int new_pick_view(Init *)
Initializes a new view for displaying file contents in the pick interface.
void unreverse_object(Pick *)
Unreverses the display of the currently selected object in pick window.
void destroy_pick_view(Init *)
Destroys pick view, unmaps file buffer, and cleans up resources.
void pick_std_chyron(Pick *)
Sets standard chyron key states based on pick structure.
int output_objects(Pick *)
Outputs selected objects to specified output file.
int match_objects(Pick *pick, char *s)
Displays current page of objects in pick window.
void deselect_object(Pick *)
Deselects the currently selected object in pick window.
int read_pick_input(Init *)
Reads pick input from file pointer and saves objects into pick structure.
void reverse_object(Pick *)
Reverses the display of the currently selected object in pick window.
int pick_engine(Init *)
Initializes pick interface, calculates window size and position, and enters picker loop.
void display_pick_page(Pick *)
Displays current page of objects in pick window.
void new_view_file(Init *, char *)
Initializes a new view for displaying the contents of a specified file in the pick interface.
void display_pick_help(Init *)
Displays the help screen for the pick interface using view.
int open_pick_win(Init *)
Initializes the pick window based on the parameters specified in the Pick structure.
int exec_objects(Init *)
Executes specified command with selected objects as arguments.
bool restore_curses_tioctl()
restore_curses_tioctl() - restore curses terminal settings
Definition scriou.c:81
void sig_prog_mode()
Set up signal handlers for interrupt signals.
Definition sig.c:62
int view_cmd_processor(Init *)
Main Command Processing Loop for View.
void build_prompt(View *)
Build Prompt String.
void initialize_line_table(View *)
Initialize Line Table.
void next_page(View *)
Advance to Next Page.
int pad_refresh(View *)
Refresh Pad and Line Number Window.
int display_prompt(View *, char *)
Display Command Line Prompt.
char title[MAXLEN]
Definition common.h:129
char mapp_help[MAXLEN]
Definition common.h:148
int begx
Definition common.h:118
SIO * sio
Definition common.h:114
int argc
Definition common.h:130
int cols
Definition common.h:116
int tab_stop
Definition common.h:180
char mapp_home[MAXLEN]
Definition common.h:146
bool f_squeeze
Definition common.h:137
int begy
Definition common.h:117
int h_shift
Definition common.h:181
bool f_ln
Definition common.h:143
View * view
Definition common.h:188
bool f_at_end_remove
Definition common.h:135
bool f_ignore_case
Definition common.h:133
char ** argv
Definition common.h:131
int lines
Definition common.h:115
char mapp_theme[MAXLEN]
Definition common.h:151
Pick * pick
Definition common.h:186
bool active
Definition cm.h:336
int l
Definition cm.h:348
ChyronKey * key[CHYRON_KEYS]
Definition cm.h:345
int pg_line
Definition pick.h:78
int tbl_lines
Definition pick.h:85
char ** m_object
Definition pick.h:68
int tbl_pages
Definition pick.h:82
int tbl_cols
Definition pick.h:86
bool f_cmd
Definition pick.h:66
WINDOW * win2
Definition pick.h:36
bool p_view_files
Definition pick.h:61
WINDOW * box
Definition pick.h:37
int m_idx
Definition pick.h:74
char ** d_object
Definition pick.h:76
char cmd[MAXLEN]
Definition pick.h:51
bool f_help_spec
Definition pick.h:58
int width
Definition pick.h:30
FILE * in_fp
Definition pick.h:41
int tbl_col_width
Definition pick.h:88
int d_idx
Definition pick.h:75
bool f_out_spec
Definition pick.h:55
int argc
Definition pick.h:39
int tbl_page
Definition pick.h:83
bool f_multiple_cmd_args
Definition pick.h:60
int d_cnt
Definition pick.h:73
int x
Definition pick.h:34
int separator_line
Definition pick.h:89
bool f_in_pipe
Definition pick.h:56
bool f_selected[OBJ_MAXCNT]
Definition pick.h:62
int select_cnt
Definition pick.h:70
char title[MAXLEN]
Definition pick.h:38
char help_spec[MAXLEN]
Definition pick.h:48
char in_buf[BUFSIZ]
Definition pick.h:67
int lines
Definition pick.h:29
int tbl_line
Definition pick.h:84
char ** argv
Definition pick.h:40
int m_cnt
Definition pick.h:72
int select_max
Definition pick.h:71
bool f_read_theme
Definition pick.h:59
char in_spec[MAXLEN]
Definition pick.h:46
WINDOW * win
Definition pick.h:35
FILE * out_fp
Definition pick.h:42
int begx
Definition pick.h:32
int y_offset
Definition pick.h:77
Chyron * chyron
Definition pick.h:91
int begy
Definition pick.h:31
int tbl_col
Definition pick.h:87
char provider_cmd[MAXLEN]
Definition pick.h:49
char out_spec[MAXLEN]
Definition pick.h:47
int y
Definition pick.h:33
char provider_cmd[MAXLEN]
Definition view.h:57
char * buf
Definition view.h:161
off_t page_top_pos
Definition view.h:148
off_t file_pos
Definition view.h:146
off_t page_top_ln
Definition view.h:178
char prompt_str[MAXLEN]
Definition view.h:61
bool f_bod
Definition view.h:95
char help_spec[MAXLEN]
Definition view.h:138
bool f_ignore_case
Definition view.h:66
bool f_help_spec
Definition view.h:141
off_t ln
Definition view.h:172
char receiver_cmd[MAXLEN]
Definition view.h:58
int cols
Definition view.h:54
bool f_ln
Definition view.h:171
bool f_eod
Definition view.h:96
int lines
Definition view.h:53
off_t ln_max_pos
Definition view.h:177
bool f_at_end_remove
Definition view.h:67
off_t file_size
Definition view.h:145
bool f_squeeze
Definition view.h:68
int tab_stop
Definition view.h:91
off_t page_bot_ln
Definition view.h:179
char title[MAXLEN]
Definition view.h:62
off_t page_bot_pos
Definition view.h:149
int begy
Definition view.h:55
int h_shift
Definition view.h:92
int maxcol
Definition view.h:121
int begx
Definition view.h:56
WINDOW * box_win
Definition view.h:72