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