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