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