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