Provide a zoom widget in MussaAlignedWindow
[mussa.git] / qui / MussaAlignedWindow.cpp
1 #include <list>
2 #include <vector>
3 #include <sstream>
4
5 #include <QStatusBar>
6 #include <QString>
7 #include <QMenuBar>
8
9 #include "qui/MussaAlignedWindow.hpp"
10 #include "alg/sequence.hpp"
11
12 using namespace std;
13
14 MussaAlignedWindow::MussaAlignedWindow(Mussa& m, 
15                                        const set<int>& sel_paths, 
16                                        QWidget *parent)
17   : QMainWindow(parent),
18     analysis(m),
19     pick_align_menu(tr("Choose Alignment")),
20     view_align_menu(tr("View Alignment"))
21 {
22   browser.setSequences(analysis.sequences(), analysis.colorMapper());
23   setSelectedPaths(m, sel_paths);
24   setAlignment(0);
25   double zoom_level = browser.zoomToSequence();
26   zoom.setValue(zoom_level);
27   computeMatchLines();
28   setupMenus();
29
30   addToolBar(&alignTB);
31   alignTB.addWidget(&zoom);
32   
33   connect(&zoom, SIGNAL(valueChanged(double)), 
34           &browser, SLOT(setZoom(double)));
35  
36   setCentralWidget(&browser);
37   menuBar()->addMenu(&pick_align_menu);
38   menuBar()->addMenu(&view_align_menu);
39
40   ostringstream message;
41   message << "Selected " << selected_paths.size() << " paths";
42   statusBar()->showMessage(message.str().c_str(), 5000);
43   browser.updatePosition();
44 }
45
46
47 void MussaAlignedWindow::setSelectedPaths(Mussa &m, const set<int>& sel_paths)
48 {
49   // sets are sorted
50   set<int>::iterator sel_i = sel_paths.begin();
51   list<ExtendedConservedPath>::const_iterator path_i = m.paths().rpbegin();
52   list<ExtendedConservedPath>::const_iterator path_end = m.paths().rpend();
53   size_t path_size = m.paths().refined_pathz.size();
54   size_t pathid=0;
55
56   selected_paths.reserve(sel_paths.size());
57   view_paths.reserve(sel_paths.size());
58   while (pathid != path_size and sel_i != sel_paths.end())
59   {
60     assert (*sel_i >= 0);
61     size_t sel_pathid = (size_t)(*sel_i);
62     if (pathid == sel_pathid) {
63       selected_paths.push_back(*path_i);
64       view_paths.push_back(true);
65       ++pathid;
66       ++path_i;
67       ++sel_i;
68     } else if (pathid < sel_pathid) {
69       ++pathid;
70       ++path_i;
71     } else if (pathid > sel_pathid) {
72       ++sel_i;
73     }
74   }
75 }
76
77 void MussaAlignedWindow::setupMenus()
78 {
79   pick_align_menu.clear();
80   view_align_menu.clear();
81   pick_actions.clear();
82   view_actions.clear();
83
84   for(vector<ExtendedConservedPath >::iterator pathz_i=selected_paths.begin(); 
85       pathz_i != selected_paths.end(); 
86       ++pathz_i)
87   {
88     ConservedPath::path_type normalized_path = pathz_i->normalizedIndexes();
89     ostringstream menu_text;
90     menu_text << pathz_i->window_size << ":";
91     ConservedPath::iterator element_i = normalized_path.begin();
92     menu_text << *element_i++;
93     for (;
94          element_i != normalized_path.end();
95          ++element_i)
96     {
97       menu_text << ", ";
98       menu_text << *element_i;
99     }
100     int index = pathz_i - selected_paths.begin();
101     IntAction *pick = new IntAction(QString(menu_text.str().c_str()), index, this);
102     connect(pick, SIGNAL(triggered(int)), this, SLOT(setAlignment(int)));
103     pick_actions.push_back(pick);
104     pick_align_menu.addAction(pick);
105     IntAction *view = new IntAction(QString(menu_text.str().c_str()), index, this);
106     connect(view, SIGNAL(triggered(int)), this, SLOT(toggleViewAlignment(size_t)));
107     view->setCheckable(true);
108     view->setChecked(true);
109     view_actions.push_back(view);
110     view_align_menu.addAction(view);
111   }
112 }
113
114 void MussaAlignedWindow::setAlignment(int alignment_index)
115 {
116   if (selected_paths.size() > 0) {
117     browser.centerOnPath(selected_paths[alignment_index].normalizedIndexes());
118   }
119 }
120
121 void MussaAlignedWindow::toggleViewAlignment(size_t alignment_index)
122 {
123   view_paths[alignment_index]= not view_paths[alignment_index]; 
124   // perhaps it'd be better if we could erase specific sets
125   // of matches instead of erasing them all and recomputing them all
126   // but this is easier
127   computeMatchLines();
128 }
129
130 void MussaAlignedWindow::update()
131 {
132   browser.update();
133 }
134
135 void MussaAlignedWindow::computeMatchLines()
136 {
137   const vector<Sequence>& raw_sequence = analysis.sequences();
138   vector<int> aligned_path;
139   size_t i2, i3;
140   int x_start, x_end;
141   int window_length, win_i;
142   int rc_1 = 0; 
143   int rc_2 = 0;
144   vector<bool> rc_list;
145   bool full_match;
146   vector<bool> matched;
147   int align_counter;
148
149   browser.clear_links();
150   align_counter = 0;
151   for(vector<ExtendedConservedPath >::iterator pathz_i=selected_paths.begin(); 
152       pathz_i != selected_paths.end(); 
153       ++pathz_i)
154   {
155     if (view_paths[align_counter])
156     {
157       ExtendedConservedPath& a_path = *pathz_i;
158       window_length = a_path.window_size;
159       // determine which parts of the path are RC relative to first species
160       rc_list = a_path.reverseComplimented();
161       
162       // loop over each bp in the conserved region for all sequences
163       for(win_i = 0; win_i < window_length; win_i++)
164       {
165         aligned_path.clear();
166         // determine which exact base pairs match between the sequences
167         full_match = true;
168         for(i2 = 0; i2 < a_path.size()-1; i2++)
169         {
170           // assume not rc as most likely, adjust below
171           rc_1 = 0;
172           rc_2 = 0;
173           // no matter the case, any RC node needs adjustments
174           if (a_path[i2] < 0)
175             rc_1 = window_length-1;
176           if (a_path[i2+1] < 0)
177             rc_2 = window_length-1;        
178            
179           x_start = (abs(a_path[i2]-rc_1+win_i));
180           x_end =   (abs(a_path[i2+1]-rc_2+win_i));
181           
182           // RC case handling
183           // ugh, and xor...only want rc coloring if just one of the nodes is rc
184           // if both nodes are rc, then they are 'normal' relative to each other
185           if ( ( rc_list[i2] || rc_list[i2+1] ) &&
186               !(rc_list[i2] && rc_list[i2+1] ) )
187           { //the hideous rc matching logic - not complex, but annoying
188             if ( !( ( (raw_sequence[i2][x_start] == 'A') &&
189                     (raw_sequence[i2+1][x_end] == 'T') ) ||
190                   ( (raw_sequence[i2][x_start] == 'T') &&
191                     (raw_sequence[i2+1][x_end] == 'A') ) ||
192                   ( (raw_sequence[i2][x_start] == 'G') &&
193                     (raw_sequence[i2+1][x_end] == 'C') ) ||
194                   ( (raw_sequence[i2][x_start] == 'C') &&
195                     (raw_sequence[i2+1][x_end] == 'G') ) ) )
196               full_match = false;
197           }
198           else
199           {
200             if (!( (raw_sequence[i2][x_start] == raw_sequence[i2+1][x_end]) &&
201                   (raw_sequence[i2][x_start] != 'N') &&
202                   (raw_sequence[i2+1][x_end] != 'N') ) )
203               full_match = false;
204           }
205         }
206         
207         // draw for matches stretching across all sequences
208         if (full_match)
209         {
210           // now can draw the line for each bp in this window that matches
211           // grrr, need to ask if anyone cares if I switch the seq 
212           // top-bot order...
213           i3 = 0;
214           //y_loc = y_min + 5;
215           for(i2 = 0; i2 < a_path.size()-1; i2++)
216           {
217             // assume not rc as most likely, adjust below
218             rc_1 = 0;
219             rc_2 = 0;
220             // no matter the case, any RC node needs adjustments
221             if (a_path[i2] < 0)
222             {
223               rc_1 = window_length;        
224             }
225             if (a_path[i2] < 0)
226             {
227               rc_2 = window_length;        
228             }
229             
230             // maybe shouldn't recalc these, but store values from first loop
231             x_start = (abs((int) (a_path[i2]-rc_1+win_i)));
232             x_end =   (abs((int) (a_path[i2+1]-rc_2+win_i)));
233             aligned_path.push_back(x_start);
234             // if we're on the last time through the loop, save x_end too
235             if (i2 == a_path.size()-2) {
236               aligned_path.push_back(x_end);
237             }
238           }
239         }
240         if (aligned_path.size() > 0) {
241           browser.link(aligned_path, rc_list,1);
242         }
243       }
244     }
245     align_counter++;
246   }
247 }
248