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