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