define window for showing a mussa alignment
[mussa.git] / gui / SeqView.cpp
1 //  This file is part of the Mussa source distribution.
2 //  http://mussa.caltech.edu/
3 //  Contact author: Tristan  De Buysscher, tristan@caltech.edu
4
5 // This program and all associated source code files are Copyright (C) 2005
6 // the California Institute of Technology, Pasadena, CA, 91125 USA.  It is
7 // under the GNU Public License; please see the included LICENSE.txt
8 // file for more information, or contact Tristan directly.
9
10
11 #include "gui/SeqView.hpp"
12 #include <iostream>
13 #include <sstream>
14
15 #include <FL/Fl.H>
16 #include <FL/fl_draw.H>
17
18 using namespace std;
19
20 void
21 SeqView::setup(string name, int sq_num, 
22                vector<Sequence> *some_seqs,
23                list<ExtendedConservedPath > some_paths, vector<int> some_lens,
24                vector<fltk_motif> *some_motifs)
25 {
26   int seq_i;
27
28   analysis_name = name;
29   seq_num = sq_num;
30   S = some_seqs;
31   P = some_paths;
32   seq_lens = some_lens;
33   the_motifs = some_motifs;
34
35   y_seq_incre = (y_max-10-10) / (seq_num - 1);
36
37   fl_font(FL_COURIER, 14);
38   cout << "font width: " << fl_width('A') << endl;
39   
40   index_pad = 6 * (int) fl_width('A') + 5;
41
42   align_offsets(0);
43
44   raw_sequence.clear();
45   for(seq_i = 0; seq_i < seq_num; ++seq_i)
46     raw_sequence.push_back( (*S)[seq_i].get_seq() );
47
48   for(list<ExtendedConservedPath >::iterator pathz_i = P.begin(); 
49       pathz_i != P.end(); 
50       ++pathz_i)
51   {
52     show_aligns.push_back(true);
53   }  
54
55   dragging = false;
56   scroll_offset = 0;
57   //scroll_pos = 0;
58   drag_change = 0;
59
60   cout << "waaaaa!\n";
61   cout << x() << " " << y() << " " << w() << " " << h() << endl;
62 }
63
64 void
65 SeqView::resize(int new_x, int new_y, int new_w, int new_h)
66 {
67   x(new_x);
68   y(new_y);
69   w(new_w);
70   h(new_h);
71
72   // hmmm, why do I use these values on the the widgets inherent ones?
73   // I think there was a reason...once?
74   x_max = new_w;
75   y_max = new_h;
76   x_min = new_x;
77   y_min = new_y;
78
79   // things that need to be recalculated
80   y_seq_incre = (y_max-10-10) / (seq_num - 1);
81 }
82
83
84 void
85 SeqView::draw()
86 {
87   double ch_width;
88
89
90   // clear drawing area and set background to white
91   fl_color(FL_WHITE);
92   fl_rectf(x(), y(), w(), h());
93
94   fl_font(FL_COURIER, 14);
95   //blatantly stolen from FR2
96   ch_width = fl_width('A');     // monospaced: all characters are same width
97
98
99   if (show_bars)
100   {
101     int grey_box_num, grey_box_len;
102     int i;
103     // draw in some light grey boxes every 10bp to delineate for user
104     //fl_color(200,235,235);
105     fl_color(180,180,180);
106     grey_box_num = (((x_max - index_pad) / (int) ch_width) / 10) + 1;
107     grey_box_len = 10 * (int) ch_width;
108     for(i = 0; i < grey_box_num; i++)
109       if ((i % 2) != 0)
110         fl_rectf(x()+index_pad-1 + (i * grey_box_len), y(), grey_box_len, h());
111   }
112
113   if (show_motifs)
114     draw_motifs(ch_width);
115
116   draw_sequence(ch_width);
117
118   draw_match_lines(ch_width);
119
120   draw_indices(ch_width);
121
122
123   // draw some bounding boxes to visually separate things
124   fl_color(150,200,255);
125   fl_line_style(FL_SOLID, 2, NULL);
126   fl_rect(x(), y(), w(), h());
127   fl_rect(x()+index_pad-1, y(), w()-2*(index_pad-1), h());
128 }
129
130 void
131 SeqView::draw_motifs(double ch_width)
132 {
133   vector<fltk_motif>::iterator motif_i;
134   vector<int> some_motif_locs;
135   vector<int>::iterator i_locs;
136   int scale_len, motif_len, i2;
137   int y_loc, x_start;
138
139   //fl_color(255,0,255);
140   fl_line_style(FL_SOLID, 10, NULL);
141   motif_i = the_motifs->begin();
142   while (motif_i != the_motifs->end())
143   {
144     fl_color(motif_i->color);
145     motif_len = motif_i->seq.length();
146     scale_len = (int) (motif_len * ch_width);
147     y_loc = y_min + 10;
148     if (!motif_i->locations.empty())
149     for(i2 = 0; i2 < seq_num; i2++)
150     {
151       some_motif_locs = (*motif_i).locations[i2];
152
153       i_locs = some_motif_locs.begin();
154       while (i_locs != some_motif_locs.end())
155       {
156         x_start = (int) ((*i_locs +scroll_offset - seq_align_offsets[i2]) 
157                          * ch_width) + index_pad + x();
158
159         //cout << *i_locs << ":" << x_start << " ";
160         // don't draw highlights outside of box range, they can wraparound past some boundry
161         if ((x_start > 0) && (x_start < w()))
162           fl_line(x_start,y_loc,x_start+scale_len,y_loc);
163         ++i_locs;
164       }
165       //cout << endl;
166     y_loc += y_seq_incre;      
167     }
168     ++motif_i;
169   }
170 }
171
172 void
173 SeqView::draw_sequence(double ch_width)
174 {
175   int i, seq_i, y_loc;
176   Sequence a_seq;
177   string sub_seq;
178   int seq_len, sub_seq_start, sub_seq_len;
179   
180
181
182   fl_color(FL_BLACK);
183   y_loc = y_min + 10 + 5;
184
185   for(seq_i = 0; seq_i < seq_num; seq_i++)
186   {
187     seq_len = raw_sequence[seq_i].length();
188     sub_seq_start = seq_align_offsets[seq_i] - scroll_offset;
189     sub_seq_len = (x_max - index_pad) / (int) ch_width;
190     //cout << x_max << " index_pad: " << index_pad << " y - ip: " <<  (x_max - index_pad) << endl;
191
192     // gotta check thru boundary conditions to make sure we're producing the
193     // right string for the current position (otherwise gonna crash due to 
194     // illegal indexing into substr
195     if (sub_seq_start < 0)
196     {
197       sub_seq_len = sub_seq_len - sub_seq_start;
198       if (sub_seq_len < 0)
199         sub_seq_len = 0;
200       sub_seq_start = 0;
201     }
202     else if (sub_seq_start >= seq_len)
203     {
204       sub_seq_start = seq_len;
205       sub_seq_len = 0;
206     }
207     else if ((sub_seq_start + sub_seq_len) >= seq_len)
208       sub_seq_len = seq_len - sub_seq_start;
209
210     sub_seq = raw_sequence[seq_i].substr(sub_seq_start, sub_seq_len);
211     fl_draw(sub_seq.c_str(), index_pad, y_loc); 
212     y_loc += y_seq_incre;
213   }
214 }
215
216
217 void
218 SeqView::draw_match_lines(double ch_width)
219 {
220   int i, y_loc;
221   int i2, i3;
222   int x_start, y_start, x_end, y_end;
223   int window_length, win_i;
224   int rc_1 = 0; 
225   int rc_2 = 0;
226   int offset1, offset2;
227   float center1, center2;
228   bool rc_color;
229   vector<bool> rc_list;
230   bool full_match;
231   vector<bool> matched;
232   int align_counter;
233
234
235   align_counter = 0;
236   for(list<ExtendedConservedPath >::iterator pathz_i = P.begin(); 
237       pathz_i != P.end(); 
238       ++pathz_i)
239   {
240     if (show_aligns[align_counter])
241     {
242       ExtendedConservedPath& a_path = *pathz_i;
243       window_length = a_path.window_size;
244       // determine which parts of the path are RC relative to first species
245       rc_list = a_path.reverseComplimented();
246       
247       // loop over each bp in the conserved region for all sequences
248       for(win_i = 0; win_i < window_length; win_i++)
249       {
250         // determine which exact base pairs match between the sequences
251         full_match = true;
252         for(i2 = 0; i2 < a_path.size()-1; i2++)
253         {
254           // assume not rc as most likely, adjust below
255           rc_1 = 0;
256           rc_2 = 0;
257           // no matter the case, any RC node needs adjustments
258           if (a_path[i2] < 0)
259             rc_1 = window_length-1;
260           if (a_path[i2+1] < 0)
261             rc_2 = window_length-1;        
262           
263           x_start = (abs(a_path[i2]-rc_1+win_i));
264           x_end =   (abs(a_path[i2+1]-rc_2+win_i));
265           
266           // RC case handling
267           // ugh, and xor...only want rc coloring if just one of the nodes is rc
268           // if both nodes are rc, then they are 'normal' relative to each other
269           if ( ( rc_list[i2] || rc_list[i2+1] ) &&
270               !(rc_list[i2] && rc_list[i2+1] ) )
271           { //the hideous rc matching logic - not complex, but annoying
272             if ( !( ( (raw_sequence[i2][x_start] == 'A') &&
273                     (raw_sequence[i2+1][x_end] == 'T') ) ||
274                   ( (raw_sequence[i2][x_start] == 'T') &&
275                     (raw_sequence[i2+1][x_end] == 'A') ) ||
276                   ( (raw_sequence[i2][x_start] == 'G') &&
277                     (raw_sequence[i2+1][x_end] == 'C') ) ||
278                   ( (raw_sequence[i2][x_start] == 'C') &&
279                     (raw_sequence[i2+1][x_end] == 'G') ) ) )
280               full_match = false;
281           }
282           else
283           {
284             if (!( (raw_sequence[i2][x_start] == raw_sequence[i2+1][x_end]) &&
285                   (raw_sequence[i2][x_start] != 'N') &&
286                   (raw_sequence[i2+1][x_end] != 'N') ) )
287               full_match = false;
288           }
289         }
290         
291         // draw for matches stretching across all sequences
292         if (full_match)
293         {
294           fl_line_style(FL_SOLID, 1, NULL);
295           
296           // now can draw the line for each bp in this window that matches
297           // grrr, need to ask if anyone cares if I switch the seq 
298           // top-bot order...
299           i3 = 0;
300           y_loc = y_min + 5;
301           for(i2 = 0; i2 < a_path.size()-1; i2++)
302           {
303             // assume not rc as most likely, adjust below
304             rc_1 = 0;
305             rc_2 = 0;
306             // this makes the lines start in the middle of the drawn char/bp
307             center1 = 0.5;
308             center2 = 0.5;
309             // no matter the case, any RC node needs adjustments
310             if (a_path[i2] < 0)
311             {
312               rc_1 = window_length;        
313               center1 = -center1;
314             }
315             if (a_path[i2] < 0)
316             {
317               rc_2 = window_length;        
318               center2 = -center2;
319             }
320             
321             // set offset based on current alignment for which bp to show
322             offset1 = seq_align_offsets[i2];
323             offset2 = seq_align_offsets[i2+1];
324             
325             if ( ( rc_list[i2] || rc_list[i2+1] ) &&
326                 !(rc_list[i2] && rc_list[i2+1] ) )
327               fl_color(FL_BLUE);
328             else
329               fl_color(FL_RED);
330             
331             // maybe shouldn't recalc these, but store values from first loop
332             x_start = (abs((int) (a_path[i2]-rc_1+win_i)));
333             x_end =   (abs((int) (a_path[i2+1]-rc_2+win_i)));
334             
335             fl_line( (int)((x_start+center1-offset1+scroll_offset)*ch_width)
336                     + index_pad, y_loc + 10,
337                     (int)((x_end+center2-offset2+scroll_offset)*ch_width)
338                     + index_pad, y_loc+y_seq_incre );
339             y_loc += y_seq_incre;
340           }
341         }
342       }
343     }
344     align_counter++;
345   }
346 }
347
348 void
349 SeqView::draw_indices(double ch_width)
350 {
351   int seq_i;
352   Sequence a_seq;
353   int y_loc;
354   ostringstream an_index;
355   int shown_seq_len;
356   
357
358   // clear out space on sides to draw in index values
359   // I block out the sides rather than adding offsets to the drawing of seq
360   // and conservation lines since the lines need to draw off the 'edge' 
361   fl_color(FL_WHITE);
362   fl_rectf(0, y_min, index_pad, h()+y_min);
363   fl_rectf(w()-index_pad, y_min, index_pad, h()+y_min);
364
365   // now can draw in the indices of the start and end of the shown sequence
366   fl_color(FL_BLACK);
367   y_loc = y_min + 10 + 5;
368   for(seq_i = 0; seq_i < seq_num; seq_i++)
369   {
370     an_index.str("");
371     an_index << (seq_align_offsets[seq_i]-scroll_offset);
372     fl_draw((an_index.str()).c_str(), 2, y_loc); 
373
374     shown_seq_len = (w() - 2 * index_pad) / (int) ch_width;
375     an_index.str("");
376     an_index << (seq_align_offsets[seq_i]-scroll_offset+shown_seq_len);
377     fl_draw((an_index.str()).c_str(), w()-index_pad+2, y_loc); 
378     y_loc += y_seq_incre;
379   }
380 }
381
382
383 void
384 SeqView::toggle_align(int align_num)
385 {
386   //cout << align_num << endl;
387   //cout << show_aligns[align_num] << endl;
388   show_aligns[align_num] = !show_aligns[align_num];
389   //cout << show_aligns[align_num] << endl;
390 }
391
392
393 void
394 SeqView::align_offsets(int align_num)
395 {
396   list<ExtendedConservedPath >::iterator pathz_i;
397   int i;
398   int window_length;
399
400   cout << "alignment: " << align_num << endl;
401
402   if (P.begin() == P.end())
403     cout << "crud....\n";
404   else
405   {
406     pathz_i = P.begin();
407
408     while(pathz_i != P.end())
409       ++pathz_i;
410
411     // find the path specified
412     i = 0;
413     pathz_i = P.begin();
414     while( (i < align_num) && (pathz_i != P.end()) )
415     {
416       ++i;
417       ++pathz_i;
418     }
419
420     // now set the alignment offsets - basically where the path starts
421     seq_align_offsets.clear();
422     for(i = 0; i < seq_num ; i++)
423     {
424       //cout << (*pathz_i)[i+1] << endl;
425       seq_align_offsets.push_back( abs((*pathz_i).track_indexes[i]) );
426       // for testing purposes: to see everything in the short test sequences
427       //seq_align_offsets.push_back(0);
428     }
429     // reset any dragging done, otherwise might be hard to find selected align
430     scroll_offset = 0;
431   }
432 }
433
434
435
436 int
437 SeqView::handle(int e)
438 {
439   int return_value;
440
441   // this empty string needs to be put on cout, otherwise the -O optimize
442   // compile option seems to throw this function away with the following:
443   // gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
444   cout << "";
445
446   switch(e)
447   {
448   case FL_PUSH:
449     if (Fl::event_button3())
450     {
451       cout << "menu menu menu!\n";
452       return_value = 1;
453     }
454     break;
455   case FL_DRAG:
456     if (!dragging)
457     {
458       drag_change = Fl::event_x();
459       dragging = true;
460       return_value = 1;
461     }
462     drag_change = Fl::event_x() - drag_change;
463     scroll_offset += drag_change;
464     drag_change = Fl::event_x();
465     redraw();
466     break;
467   case FL_RELEASE:
468     if (dragging)
469     {
470       //scroll_offset += Fl::event_x() - drag_start;
471       //scroll_dist = Fl::event_x() - drag_start;
472       //scroll_offset += scroll_dist;
473       //scroll_offset += scroll_pos;
474       dragging = false;
475       redraw();
476       return_value = 1;
477     }
478     break;
479   default:
480     return_value = Fl_Widget::handle(e);
481   }
482
483   return return_value;
484 }
485
486 void
487 SeqView::toggle_bars()
488 {
489   show_bars = !show_bars;
490   redraw();
491 }
492
493
494 void
495 SeqView::toggle_motifs()
496 {
497   show_motifs = !show_motifs;
498   redraw();
499 }
500
501
502 void
503 SeqView::reporter(string id, int value)
504 {
505   cout << id << " : " << value << endl;
506 }
507
508
509 /*
510     cout << "fee\n";
511     cout << "fie\n";
512     cout << "foe\n";
513     cout << "fum\n";    
514 */