1 /**************************************************************************
2 **
3 ** sngrep - SIP Messages flow viewer
4 **
5 ** Copyright (C) 2013-2018 Ivan Alonso (Kaian)
6 ** Copyright (C) 2013-2018 Irontec SL. All rights reserved.
7 **
8 ** This program is free software: you can redistribute it and/or modify
9 ** it under the terms of the GNU General Public License as published by
10 ** the Free Software Foundation, either version 3 of the License, or
11 ** (at your option) any later version.
12 **
13 ** This program is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ** GNU General Public License for more details.
17 **
18 ** You should have received a copy of the GNU General Public License
19 ** along with this program. If not, see <http://www.gnu.org/licenses/>.
20 **
21 ****************************************************************************/
22
23 /**
24 * @file ui_column_select.c
25 * @author Ivan Alonso [aka Kaian] <kaian@irontec.com>
26 *
27 * @brief Source of functions defined in ui_column_select.h
28 *
29 */
30 #include <string.h>
31 #include <stdlib.h>
32 #include <regex.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include "ui_manager.h"
36 #include "ui_call_list.h"
37 #include "ui_column_select.h"
38
39 /**
40 * Ui Structure definition for Message Diff panel
41 */
42 ui_t ui_column_select = {
43 .type = PANEL_COLUMN_SELECT,
44 .panel = NULL,
45 .create = column_select_create,
46 .handle_key = column_select_handle_key,
47 .destroy = column_select_destroy
48 };
49
50 void
column_select_create(ui_t * ui)51 column_select_create(ui_t *ui)
52 {
53 int attr_id, column;
54 MENU *menu;
55 column_select_info_t *info;
56
57 // Cerate a new indow for the panel and form
58 ui_panel_create(ui, 20, 60);
59
60 // Initialize Filter panel specific data
61 info = sng_malloc(sizeof(column_select_info_t));
62
63 // Store it into panel userptr
64 set_panel_userptr(ui->panel, (void*) info);
65
66 // Initialize the fields
67 info->fields[FLD_COLUMNS_ACCEPT] = new_field(1, 10, ui->height - 2, 13, 0, 0);
68 info->fields[FLD_COLUMNS_SAVE] = new_field(1, 10, ui->height - 2, 25, 0, 0);
69 info->fields[FLD_COLUMNS_CANCEL] = new_field(1, 10, ui->height - 2, 37, 0, 0);
70 info->fields[FLD_COLUMNS_COUNT] = NULL;
71
72 // Field Labels
73 set_field_buffer(info->fields[FLD_COLUMNS_ACCEPT], 0, "[ Accept ]");
74 set_field_buffer(info->fields[FLD_COLUMNS_SAVE], 0, "[ Save ]");
75 set_field_buffer(info->fields[FLD_COLUMNS_CANCEL], 0, "[ Cancel ]");
76
77 // Create the form and post it
78 info->form = new_form(info->fields);
79 set_form_sub(info->form, ui->win);
80 post_form(info->form);
81
82 // Create a subwin for the menu area
83 info->menu_win = derwin(ui->win, 10, ui->width - 2, 7, 0);
84
85 // Initialize one field for each attribute
86 for (attr_id = 0; attr_id < SIP_ATTR_COUNT; attr_id++) {
87 // Create a new field for this column
88 info->items[attr_id] = new_item("[ ]", sip_attr_get_description(attr_id));
89 set_item_userptr(info->items[attr_id], (void*) sip_attr_get_name(attr_id));
90 }
91 info->items[SIP_ATTR_COUNT] = NULL;
92
93 // Create the columns menu and post it
94 info->menu = menu = new_menu(info->items);
95
96 // Set current enabled fields
97 // FIXME Stealing Call list columns :/
98 call_list_info_t *list_info = call_list_info(ui_find_by_type(PANEL_CALL_LIST));
99
100 // Enable current enabled fields and move them to the top
101 for (column = 0; column < list_info->columncnt; column++) {
102 const char *attr = list_info->columns[column].attr;
103 for (attr_id = 0; attr_id < item_count(menu); attr_id++) {
104 if (!strcmp(item_userptr(info->items[attr_id]), attr)) {
105 column_select_toggle_item(ui, info->items[attr_id]);
106 column_select_move_item(ui, info->items[attr_id], column);
107 break;
108 }
109 }
110 }
111
112 // Set main window and sub window
113 set_menu_win(menu, ui->win);
114 set_menu_sub(menu, derwin(ui->win, 10, ui->width - 5, 7, 2));
115 set_menu_format(menu, 10, 1);
116 set_menu_mark(menu, "");
117 set_menu_fore(menu, COLOR_PAIR(CP_DEF_ON_BLUE));
118 menu_opts_off(menu, O_ONEVALUE);
119 post_menu(menu);
120
121 // Draw a scrollbar to the right
122 info->scroll = ui_set_scrollbar(info->menu_win, SB_VERTICAL, SB_RIGHT);
123 info->scroll.max = item_count(menu) - 1;
124 ui_scrollbar_draw(info->scroll);
125
126 // Set the window title and boxes
127 mvwprintw(ui->win, 1, ui->width / 2 - 14, "Call List columns selection");
128 wattron(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF));
129 title_foot_box(ui->panel);
130 mvwhline(ui->win, 6, 1, ACS_HLINE, ui->width - 1);
131 mvwaddch(ui->win, 6, 0, ACS_LTEE);
132 mvwaddch(ui->win, 6, ui->width - 1, ACS_RTEE);
133 wattroff(ui->win, COLOR_PAIR(CP_BLUE_ON_DEF));
134
135 // Some brief explanation abotu what window shows
136 wattron(ui->win, COLOR_PAIR(CP_CYAN_ON_DEF));
137 mvwprintw(ui->win, 3, 2, "This windows show the list of columns displayed on Call");
138 mvwprintw(ui->win, 4, 2, "List. You can enable/disable using Space Bar and reorder");
139 mvwprintw(ui->win, 5, 2, "them using + and - keys.");
140 wattroff(ui->win, COLOR_PAIR(CP_CYAN_ON_DEF));
141
142 info->form_active = 0;
143 }
144
145 void
column_select_destroy(ui_t * ui)146 column_select_destroy(ui_t *ui)
147 {
148 int i;
149 column_select_info_t *info = column_select_info(ui);
150
151 // Remove menu and items
152 unpost_menu(info->menu);
153 free_menu(info->menu);
154 for (i = 0; i < SIP_ATTR_COUNT; i++)
155 free_item(info->items[i]);
156
157 // Remove form and fields
158 unpost_form(info->form);
159 free_form(info->form);
160 for (i = 0; i < FLD_COLUMNS_COUNT; i++)
161 free_field(info->fields[i]);
162
163 sng_free(info);
164
165 // Remove panel window and custom info
166 ui_panel_destroy(ui);
167 }
168
169
170 column_select_info_t *
column_select_info(ui_t * ui)171 column_select_info(ui_t *ui)
172 {
173 return (column_select_info_t*) panel_userptr(ui->panel);
174 }
175
176 int
column_select_handle_key(ui_t * ui,int key)177 column_select_handle_key(ui_t *ui, int key)
178 {
179 // Get panel information
180 column_select_info_t *info = column_select_info(ui);
181
182 if (info->form_active) {
183 return column_select_handle_key_form(ui, key);
184 } else {
185 return column_select_handle_key_menu(ui, key);
186 }
187 return 0;
188 }
189
190 int
column_select_handle_key_menu(ui_t * ui,int key)191 column_select_handle_key_menu(ui_t *ui, int key)
192 {
193 MENU *menu;
194 ITEM *current;
195 int current_idx;
196 int action = -1;
197
198 // Get panel information
199 column_select_info_t *info = column_select_info(ui);
200
201 menu = info->menu;
202 current = current_item(menu);
203 current_idx = item_index(current);
204
205 // Check actions for this key
206 while ((action = key_find_action(key, action)) != ERR) {
207 // Check if we handle this action
208 switch (action) {
209 case ACTION_DOWN:
210 menu_driver(menu, REQ_DOWN_ITEM);
211 break;
212 case ACTION_UP:
213 menu_driver(menu, REQ_UP_ITEM);
214 break;
215 case ACTION_NPAGE:
216 menu_driver(menu, REQ_SCR_DPAGE);
217 break;
218 case ACTION_PPAGE:
219 menu_driver(menu, REQ_SCR_UPAGE);
220 break;
221 case ACTION_SELECT:
222 column_select_toggle_item(ui, current);
223 column_select_update_menu(ui);
224 break;
225 case ACTION_COLUMN_MOVE_DOWN:
226 column_select_move_item(ui, current, current_idx + 1);
227 column_select_update_menu(ui);
228 break;
229 case ACTION_COLUMN_MOVE_UP:
230 column_select_move_item(ui, current, current_idx - 1);
231 column_select_update_menu(ui);
232 break;
233 case ACTION_NEXT_FIELD:
234 info->form_active = 1;
235 set_menu_fore(menu, COLOR_PAIR(CP_DEFAULT));
236 set_field_back(info->fields[FLD_COLUMNS_ACCEPT], A_REVERSE);
237 form_driver(info->form, REQ_VALIDATION);
238 break;
239 case ACTION_CONFIRM:
240 column_select_update_columns(ui);
241 ui_destroy(ui);
242 return KEY_HANDLED;
243 default:
244 // Parse next action
245 continue;
246 }
247
248 // This panel has handled the key successfully
249 break;
250 }
251
252 // Draw a scrollbar to the right
253 info->scroll.pos = top_row(menu);
254 ui_scrollbar_draw(info->scroll);
255 wnoutrefresh(info->menu_win);
256
257 // Return if this panel has handled or not the key
258 return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED;
259 }
260
261 int
column_select_handle_key_form(ui_t * ui,int key)262 column_select_handle_key_form(ui_t *ui, int key)
263 {
264 int field_idx, new_field_idx;
265 char field_value[48];
266 int action = -1;
267
268 // Get panel information
269 column_select_info_t *info = column_select_info(ui);
270
271 // Get current field id
272 field_idx = field_index(current_field(info->form));
273
274 // Get current field value.
275 memset(field_value, 0, sizeof(field_value));
276 strcpy(field_value, field_buffer(current_field(info->form), 0));
277 strtrim(field_value);
278
279 // Check actions for this key
280 while ((action = key_find_action(key, action)) != ERR) {
281 // Check if we handle this action
282 switch (action) {
283 case ACTION_RIGHT:
284 case ACTION_NEXT_FIELD:
285 form_driver(info->form, REQ_NEXT_FIELD);
286 break;
287 case ACTION_LEFT:
288 case ACTION_PREV_FIELD:
289 form_driver(info->form, REQ_PREV_FIELD);
290 break;
291 case ACTION_SELECT:
292 case ACTION_CONFIRM:
293 switch(field_idx) {
294 case FLD_COLUMNS_ACCEPT:
295 column_select_update_columns(ui);
296 ui_destroy(ui);
297 return KEY_HANDLED;
298 case FLD_COLUMNS_CANCEL:
299 ui_destroy(ui);
300 return KEY_HANDLED;
301 case FLD_COLUMNS_SAVE:
302 column_select_update_columns(ui);
303 column_select_save_columns(ui);
304 ui_destroy(ui);
305 return KEY_HANDLED;
306 }
307 break;
308 default:
309 // Parse next action
310 continue;
311 }
312
313 // This panel has handled the key successfully
314 break;
315 }
316
317 // Validate all input data
318 form_driver(info->form, REQ_VALIDATION);
319
320 // Change background and cursor of "button fields"
321 set_field_back(info->fields[FLD_COLUMNS_ACCEPT], A_NORMAL);
322 set_field_back(info->fields[FLD_COLUMNS_SAVE], A_NORMAL);
323 set_field_back(info->fields[FLD_COLUMNS_CANCEL], A_NORMAL);
324
325 // Get current selected field
326 new_field_idx = field_index(current_field(info->form));
327
328 // Swap between menu and form
329 if (field_idx == FLD_COLUMNS_CANCEL && new_field_idx == FLD_COLUMNS_ACCEPT) {
330 set_menu_fore(info->menu, COLOR_PAIR(CP_DEF_ON_BLUE));
331 info->form_active = 0;
332 } else {
333 // Change current field background
334 set_field_back(info->fields[new_field_idx], A_REVERSE);
335 }
336
337 // Return if this panel has handled or not the key
338 return (action == ERR) ? KEY_NOT_HANDLED : KEY_HANDLED;
339 }
340
341 void
column_select_update_columns(ui_t * ui)342 column_select_update_columns(ui_t *ui)
343 {
344 int column, attr_id;
345
346 // Get panel information
347 column_select_info_t *info = column_select_info(ui);
348
349 // Set enabled fields
350 ui_t *ui_list = ui_find_by_type(PANEL_CALL_LIST);
351 call_list_info_t *list_info = call_list_info(ui_list);
352
353 // Reset column count
354 list_info->columncnt = 0;
355
356 // Add all selected columns
357 for (column = 0; column < item_count(info->menu); column++) {
358 // If column is active
359 if (!strncmp(item_name(info->items[column]), "[ ]", 3))
360 continue;
361
362 // Get column attribute
363 attr_id = sip_attr_from_name(item_userptr(info->items[column]));
364 // Add a new column to the list
365 call_list_add_column(ui_list, attr_id, sip_attr_get_name(attr_id),
366 sip_attr_get_title(attr_id), sip_attr_get_width(attr_id));
367 }
368 }
369
370 void
column_select_save_columns(ui_t * ui)371 column_select_save_columns(ui_t *ui)
372 {
373 int column;
374 FILE *fi, *fo;
375 char columnopt[128];
376 char line[1024];
377 char *rcfile;
378 char *userconf = NULL;
379 char *tmpfile = NULL;
380
381 // Use current $SNGREPRC or $HOME/.sngreprc file
382 if ((rcfile = getenv("SNGREPRC"))) {
383 if ((userconf = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) {
384 if ((tmpfile = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) {
385 sprintf(userconf, "%s", rcfile);
386 sprintf(tmpfile, "%s.old", rcfile);
387 } else {
388 sng_free(userconf);
389 return;
390 }
391 } else {
392 return;
393 }
394 } else if ((rcfile = getenv("HOME"))) {
395 if ((userconf = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) {
396 if ((tmpfile = sng_malloc(strlen(rcfile) + RCFILE_EXTRA_LEN))) {
397 sprintf(userconf, "%s/.sngreprc", rcfile);
398 sprintf(tmpfile, "%s/.sngreprc.old", rcfile);
399 } else {
400 sng_free(userconf);
401 return;
402 }
403 } else {
404 return;
405 }
406 } else {
407 return;
408 }
409
410 // Remove old config file
411 unlink(tmpfile);
412
413 // Move user conf file to temporal file
414 rename(userconf, tmpfile);
415
416 // Create a new user conf file
417 if (!(fo = fopen(userconf, "w"))) {
418 dialog_run("Unable to open %s: %s", userconf, strerror(errno));
419 sng_free(userconf);
420 sng_free(tmpfile);
421 return;
422 }
423
424 // Read all lines of old sngreprc file
425 if ((fi = fopen(tmpfile, "r"))) {
426
427 // Read all configuration file
428 while (fgets(line, 1024, fi) != NULL) {
429 // Ignore lines starting with set (but keep settings)
430 if (strncmp(line, "set ", 4) || strncmp(line, "set cl.column", 13)) {
431 // Put everyting in new user conf file
432 fputs(line, fo);
433 }
434 }
435 fclose(fi);
436 }
437
438 // Get panel information
439 column_select_info_t *info = column_select_info(ui);
440
441 // Add all selected columns
442 for (column = 0; column < item_count(info->menu); column++) {
443 // If column is active
444 if (!strncmp(item_name(info->items[column]), "[ ]", 3))
445 continue;
446
447 // Add the columns settings
448 sprintf(columnopt, "set cl.column%d %s\n", column, (const char*) item_userptr(info->items[column]));
449 fputs(columnopt, fo);
450 }
451 fclose(fo);
452
453 // Show a information dialog
454 dialog_run("Column layout successfully saved to %s", userconf);
455
456 sng_free(userconf);
457 sng_free(tmpfile);
458 }
459
460
461 void
column_select_move_item(ui_t * ui,ITEM * item,int pos)462 column_select_move_item(ui_t *ui, ITEM *item, int pos)
463 {
464 // Get panel information
465 column_select_info_t *info = column_select_info(ui);
466
467 // Check we have a valid position
468 if (pos == item_count(info->menu) || pos < 0)
469 return;
470
471 // Swap position with destination
472 int item_pos = item_index(item);
473 info->items[item_pos] = info->items[pos];
474 info->items[pos] = item;
475 set_menu_items(info->menu, info->items);
476 }
477
478 void
column_select_toggle_item(ui_t * ui,ITEM * item)479 column_select_toggle_item(ui_t *ui, ITEM *item)
480 {
481 // Get panel information
482 column_select_info_t *info = column_select_info(ui);
483
484 int pos = item_index(item);
485
486 // Change item name
487 if (!strncmp(item_name(item), "[ ]", 3)) {
488 info->items[pos] = new_item("[*]", item_description(item));
489 } else {
490 info->items[pos] = new_item("[ ]", item_description(item));
491 }
492
493 // Restore menu item
494 set_item_userptr(info->items[pos], item_userptr(item));
495 set_menu_items(info->menu, info->items);
496
497 // Destroy old item
498 free_item(item);
499 }
500
501 void
column_select_update_menu(ui_t * ui)502 column_select_update_menu(ui_t *ui)
503 {
504 // Get panel information
505 column_select_info_t *info = column_select_info(ui);
506 ITEM *current = current_item(info->menu);
507 int top_idx = top_row(info->menu);
508
509 // Remove the menu from the subwindow
510 unpost_menu(info->menu);
511 // Set menu items
512 set_menu_items(info->menu, info->items);
513 // Put the menu agin into its subwindow
514 post_menu(info->menu);
515
516 // Move until the current position is set
517 set_top_row(info->menu, top_idx);
518 set_current_item(info->menu, current);
519
520 // Force menu redraw
521 menu_driver(info->menu, REQ_UP_ITEM);
522 menu_driver(info->menu, REQ_DOWN_ITEM);
523 }
524