1 /*  bam_tview_curses.c -- curses tview implementation.
2 
3     Copyright (C) 2008-2015, 2019 Genome Research Ltd.
4     Portions copyright (C) 2013 Pierre Lindenbaum, Institut du Thorax, INSERM U1087, Université de Nantes.
5 
6     Author: Heng Li <lh3@sanger.ac.uk>
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14 
15 The above copyright notices and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 DEALINGS IN THE SOFTWARE.  */
25 
26 #include <config.h>
27 
28 #include "bam_tview.h"
29 
30 #ifdef HAVE_CURSES
31 
32 #if defined HAVE_NCURSESW_CURSES_H
33 #include <ncursesw/curses.h>
34 #elif defined HAVE_NCURSESW_H
35 #include <ncursesw.h>
36 #elif defined HAVE_NCURSES_CURSES_H
37 #include <ncurses/curses.h>
38 #elif defined HAVE_NCURSES_H
39 #include <ncurses.h>
40 #elif defined HAVE_CURSES_H
41 #include <curses.h>
42 #else
43 // Have the library, but no header file
44 #warning "Curses header file not found; tview with curses is disabled."
45 #undef HAVE_CURSES
46 #endif
47 #else
48 #warning "No curses library is available; tview with curses is disabled."
49 #endif
50 
51 #ifdef HAVE_CURSES
52 
53 typedef struct CursesTview {
54     tview_t view;
55     WINDOW *wgoto, *whelp;
56 } curses_tview_t;
57 
58 #define FROM_TV(ptr) ((curses_tview_t*)ptr)
59 
curses_destroy(tview_t * base)60 static void curses_destroy(tview_t* base) {
61     curses_tview_t* tv=(curses_tview_t*)base;
62 
63     delwin(tv->wgoto); delwin(tv->whelp);
64     endwin();
65 
66     base_tv_destroy(base);
67 
68     free(tv);
69 }
70 
71 /*
72  void (*my_mvprintw)(struct AbstractTview* ,int,int,const char*,...);
73     void (*my_)(struct AbstractTview*,int,int,int);
74     void (*my_attron)(struct AbstractTview*,int);
75     void (*my_attroff)(struct AbstractTview*,int);
76     void (*my_clear)(struct AbstractTview*);
77     int (*my_colorpair)(struct AbstractTview*,int);
78 */
79 
curses_mvprintw(struct AbstractTview * tv,int y,int x,const char * fmt,...)80 static void curses_mvprintw(struct AbstractTview* tv,int y ,int x,const char* fmt,...) {
81     va_list argptr;
82     va_start(argptr, fmt);
83     if (wmove(stdscr, y, x) != ERR)
84         vw_printw(stdscr, fmt, argptr);
85     va_end(argptr);
86 }
87 
curses_mvaddch(struct AbstractTview * tv,int y,int x,int ch)88 static void curses_mvaddch(struct AbstractTview* tv,int y,int x,int ch) {
89     mvaddch(y,x,ch);
90 }
91 
curses_attron(struct AbstractTview * tv,int flag)92 static void curses_attron(struct AbstractTview* tv,int flag) {
93     attron(flag);
94 }
95 
curses_attroff(struct AbstractTview * tv,int flag)96 static void curses_attroff(struct AbstractTview* tv,int flag) {
97     attroff(flag);
98 }
99 
curses_clear(struct AbstractTview * tv)100 static void curses_clear(struct AbstractTview* tv) {
101     clear();
102 }
103 
curses_init_colors(int inverse)104 static int curses_init_colors(int inverse)
105 {
106     if (inverse) {
107         init_pair(1, COLOR_WHITE, COLOR_BLUE);
108         init_pair(2, COLOR_BLACK, COLOR_GREEN);
109         init_pair(3, COLOR_BLACK, COLOR_YELLOW);
110         init_pair(4, COLOR_BLACK, COLOR_WHITE);
111         init_pair(5, COLOR_BLACK, COLOR_GREEN);
112         init_pair(6, COLOR_BLACK, COLOR_CYAN);
113         init_pair(7, COLOR_WHITE, COLOR_MAGENTA);
114         init_pair(8, COLOR_WHITE, COLOR_RED);
115         init_pair(9, COLOR_WHITE, COLOR_BLUE);
116     } else {
117         init_pair(1, COLOR_BLUE, COLOR_BLACK);
118         init_pair(2, COLOR_GREEN, COLOR_BLACK);
119         init_pair(3, COLOR_YELLOW, COLOR_BLACK);
120         init_pair(4, COLOR_WHITE, COLOR_BLACK);
121         init_pair(5, COLOR_GREEN, COLOR_BLACK);
122         init_pair(6, COLOR_CYAN, COLOR_BLACK);
123         init_pair(7, COLOR_MAGENTA, COLOR_BLACK);
124         init_pair(8, COLOR_RED, COLOR_BLACK);
125         init_pair(9, COLOR_BLUE, COLOR_BLACK);
126     }
127 
128     return 0;
129 }
130 
curses_colorpair(struct AbstractTview * tv,int flag)131 static int curses_colorpair(struct AbstractTview* tv,int flag) {
132     return COLOR_PAIR(flag);
133 }
134 
curses_drawaln(struct AbstractTview * tv,int tid,hts_pos_t pos)135 static int curses_drawaln(struct AbstractTview* tv, int tid, hts_pos_t pos) {
136     return base_draw_aln(tv,  tid, pos);
137 }
138 
tv_win_goto(curses_tview_t * tv,int * tid,hts_pos_t * pos)139 static void tv_win_goto(curses_tview_t *tv, int *tid, hts_pos_t *pos) {
140     char str[256], *p;
141     int i, l = 0;
142     tview_t *base=(tview_t*)tv;
143     str[0] = '\0';
144     wborder(tv->wgoto, '|', '|', '-', '-', '+', '+', '+', '+');
145     mvwprintw(tv->wgoto, 1, 2, "Goto: ");
146     for (;;) {
147         int invalid = 0;
148         int c = wgetch(tv->wgoto);
149         wrefresh(tv->wgoto);
150         if (c == KEY_BACKSPACE || c == '\010' || c == '\177') {
151             if(l > 0) --l;
152         } else if (c == KEY_ENTER || c == '\012' || c == '\015') {
153             int _tid = -1;
154             hts_pos_t _beg, _end;
155             if (str[0] == '=') {
156                 _beg = strtoll(str+1, &p, 10) - 1;
157                 if (_beg > 0) {
158                     *pos = _beg;
159                     return;
160                 }
161             } else {
162                 if (sam_parse_region(base->header, str, &_tid, &_beg, &_end, 0) && _tid >= 0) {
163                     *tid = _tid; *pos = _beg;
164                     return;
165                 }
166             }
167 
168             // If we get here, the region string is invalid
169             invalid = 1;
170         } else if (isgraph(c)) {
171             if (l < TV_MAX_GOTO) str[l++] = c;
172         } else if (c == '\027') l = 0;
173         else if (c == '\033') return;
174         str[l] = '\0';
175         for (i = 0; i < TV_MAX_GOTO; ++i) mvwaddch(tv->wgoto, 1, 8 + i, ' ');
176         if (invalid) mvwprintw(tv->wgoto, 1, TV_MAX_GOTO - 1, "[Invalid]");
177         mvwprintw(tv->wgoto, 1, 8, "%s", str);
178     }
179 }
180 
tv_win_help(curses_tview_t * tv)181 static void tv_win_help(curses_tview_t *tv) {
182     int r = 1;
183     tview_t* base=(tview_t*)base;
184     WINDOW *win = tv->whelp;
185     wborder(win, '|', '|', '-', '-', '+', '+', '+', '+');
186     mvwprintw(win, r++, 2, "        -=-    Help    -=- ");
187     r++;
188     mvwprintw(win, r++, 2, "?          This window");
189     mvwprintw(win, r++, 2, "Arrows     Small scroll movement");
190     mvwprintw(win, r++, 2, "h,j,k,l    Small scroll movement");
191     mvwprintw(win, r++, 2, "H,J,K,L    Large scroll movement");
192     mvwprintw(win, r++, 2, "ctrl-H     Scroll 1k left");
193     mvwprintw(win, r++, 2, "ctrl-L     Scroll 1k right");
194     mvwprintw(win, r++, 2, "space      Scroll one screen");
195     mvwprintw(win, r++, 2, "backspace  Scroll back one screen");
196     mvwprintw(win, r++, 2, "g          Go to specific location");
197     mvwprintw(win, r++, 2, "m          Color for mapping qual");
198     mvwprintw(win, r++, 2, "n          Color for nucleotide");
199     mvwprintw(win, r++, 2, "b          Color for base quality");
200     mvwprintw(win, r++, 2, "c          Color for cs color");
201     mvwprintw(win, r++, 2, "z          Color for cs qual");
202     mvwprintw(win, r++, 2, ".          Toggle on/off dot view");
203     mvwprintw(win, r++, 2, "s          Toggle on/off ref skip");
204     mvwprintw(win, r++, 2, "r          Toggle on/off rd name");
205     mvwprintw(win, r++, 2, "N          Turn on nt view");
206     mvwprintw(win, r++, 2, "C          Turn on cs view");
207     mvwprintw(win, r++, 2, "i          Toggle on/off ins");
208     mvwprintw(win, r++, 2, "v          Inverse video");
209     mvwprintw(win, r++, 2, "q          Exit");
210     r++;
211     mvwprintw(win, r++, 2, "Underline:      Secondary or orphan");
212     mvwprintw(win, r++, 2, "Blue:    0-9    Green: 10-19");
213     mvwprintw(win, r++, 2, "Yellow: 20-29   White: >=30");
214     wrefresh(win);
215     wgetch(win);
216 }
217 
curses_underline(tview_t * tv)218 static int curses_underline(tview_t* tv) {
219     return A_UNDERLINE;
220 }
221 
curses_loop(tview_t * tv)222 static int curses_loop(tview_t* tv) {
223     int tid;
224     hts_pos_t pos;
225     curses_tview_t *CTV=(curses_tview_t *)tv;
226     tid = tv->curr_tid; pos = tv->left_pos;
227     while (1) {
228         int c = getch();
229         switch (c) {
230             case '?': tv_win_help(CTV); break;
231             case '\033':
232             case 'q': goto end_loop;
233             case '/':
234             case 'g': tv_win_goto(CTV, &tid, &pos); break;
235             case 'm': tv->color_for = TV_COLOR_MAPQ; break;
236             case 'b': tv->color_for = TV_COLOR_BASEQ; break;
237             case 'n': tv->color_for = TV_COLOR_NUCL; break;
238             case 'c': tv->color_for = TV_COLOR_COL; break;
239             case 'z': tv->color_for = TV_COLOR_COLQ; break;
240             case 'v': curses_init_colors(tv->inverse = !tv->inverse); break;
241             case 's': tv->no_skip = !tv->no_skip; break;
242             case 'r': tv->show_name = !tv->show_name; break;
243             case KEY_LEFT:
244             case 'h': --pos; break;
245             case KEY_RIGHT:
246             case 'l': ++pos; break;
247             case KEY_SLEFT:
248             case 'H': pos -= 20; break;
249             case KEY_SRIGHT:
250             case 'L': pos += 20; break;
251             case '.': tv->is_dot = !tv->is_dot; break;
252             case 'N': tv->base_for = TV_BASE_NUCL; break;
253             case 'C': tv->base_for = TV_BASE_COLOR_SPACE; break;
254             case 'i': tv->ins = !tv->ins; break;
255             case '\010': pos -= 1000; break;
256             case '\014': pos += 1000; break;
257             case ' ': pos += tv->mcol; break;
258             case KEY_UP:
259             case 'j': --tv->row_shift; break;
260             case 'J': tv->row_shift -= 20; break;
261             case KEY_DOWN:
262             case 'k': ++tv->row_shift; break;
263             case 'K': tv->row_shift += 20; break;
264             case KEY_BACKSPACE:
265             case '\177': pos -= tv->mcol; break;
266 #ifdef KEY_RESIZE
267             case KEY_RESIZE: getmaxyx(stdscr, tv->mrow, tv->mcol); break;
268 #endif
269             default: continue;
270         }
271         if (pos < 0) pos = 0;
272         if (tv->row_shift < 0) tv->row_shift = 0;
273         tv->my_drawaln(tv, tid, pos);
274     }
275 end_loop:
276     return 0;
277 }
278 
curses_tv_init(const char * fn,const char * fn_fa,const char * samples,const htsFormat * fmt)279 tview_t* curses_tv_init(const char *fn, const char *fn_fa, const char *samples,
280                         const htsFormat *fmt) {
281     curses_tview_t *tv = (curses_tview_t*)calloc(1, sizeof(curses_tview_t));
282     tview_t* base=(tview_t*)tv;
283     if(tv==0) {
284         fprintf(stderr,"Calloc failed\n");
285         return 0;
286     }
287 
288     base_tv_init(base,fn,fn_fa,NULL,samples,fmt);
289     /* initialize callbacks */
290 #define SET_CALLBACK(fun) base->my_##fun=curses_##fun;
291     SET_CALLBACK(destroy);
292     SET_CALLBACK(mvprintw);
293     SET_CALLBACK(mvaddch);
294     SET_CALLBACK(attron);
295     SET_CALLBACK(attroff);
296     SET_CALLBACK(clear);
297     SET_CALLBACK(colorpair);
298     SET_CALLBACK(drawaln);
299     SET_CALLBACK(loop);
300     SET_CALLBACK(underline);
301 #undef SET_CALLBACK
302 
303     initscr();
304     keypad(stdscr, TRUE);
305     clear();
306     noecho();
307     cbreak();
308 
309     getmaxyx(stdscr, base->mrow, base->mcol);
310     tv->wgoto = newwin(3, TV_MAX_GOTO + 10, 10, 5);
311     tv->whelp = newwin(30, 40, 5, 5);
312 
313     start_color();
314     curses_init_colors(0);
315     return base;
316 }
317 
318 #else // !HAVE_CURSES
319 
320 extern tview_t* text_tv_init(const char *fn, const char *fn_fa, const char *fn_idx, const char *samples,
321                              const htsFormat *fmt);
322 
curses_tv_init(const char * fn,const char * fn_fa,const char * samples,const htsFormat * fmt)323 tview_t* curses_tv_init(const char *fn, const char *fn_fa, const char *samples,
324                         const htsFormat *fmt) {
325     return text_tv_init(fn,fn_fa,NULL,samples,fmt);
326 }
327 
328 #endif
329