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