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