display number of basepairs copied
[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 #include <iostream>
13 using namespace std;
14
15 MussaAlignedWindow::MussaAlignedWindow(MussaRef m,
16                                        boost::shared_ptr<QDir> default_dir_,
17                                        const set<int>& sel_paths,
18                                        SubanalysisWindowRef window, 
19                                        QWidget *parent)
20   : QMainWindow(parent),
21     analysis(m),
22     default_dir(default_dir_),
23     subanalysis_window(window),
24     browser(default_dir),
25     pick_align_menu(tr("Choose Alignment")),
26     view_align_menu(tr("View Alignment")),
27     threshold_widget(0),
28     zoom(0),
29     alignTB(0)
30 {
31   setupActions();
32   connect(&browser, SIGNAL(basepairsCopied(size_t)), 
33           this, SLOT(showBasePairsCopied(size_t)));
34   browser.setSequences(analysis->sequences(), analysis->colorMapper());
35   setSelectedPaths(analysis, sel_paths);
36   setAlignment(0);
37   double zoom_level = browser.zoomToSequence();
38
39   zoom = new ZoomWidget();
40   connect(zoom, SIGNAL(valueChanged(double)), 
41           &browser, SLOT(setZoom(double)));
42   zoom->setValue(zoom_level);
43   computeMatchLines();
44   setupMenus();
45   setupAlignmentMenus();
46   
47   setCentralWidget(&browser);
48
49   // Add a threhold widget set to our current threshold and make it readonly 
50   threshold_widget = new ThresholdWidget;
51   threshold_widget->setRange(analysis->get_threshold(),analysis->get_window());
52   threshold_widget->setBasepairThreshold(analysis->get_soft_threshold());
53   threshold_widget->setReadOnly(true);
54   
55   alignTB = new QToolBar();
56   alignTB->addWidget(zoom);
57   alignTB->addWidget(threshold_widget);
58   addToolBar(alignTB);
59   
60   ostringstream message;
61   message << "Selected " << selected_paths.size() << " paths";
62   statusBar()->showMessage(message.str().c_str(), 5000);
63   browser.updatePosition();
64   
65   updateTitle();
66 }
67
68 void MussaAlignedWindow::setupActions()
69 {
70   // more cut-n-paste from MussaWindow
71   createSubAnalysisAction = new QAction(tr("Add to Subanalysis"), this);
72   connect(createSubAnalysisAction, SIGNAL(triggered()), 
73           this, SLOT(createSubAnalysis()));
74             
75   //Save pixel map action
76   saveBrowserPixmapAction = new QAction(tr("Save to image..."), this);
77   connect(saveBrowserPixmapAction, (SIGNAL(triggered())),
78           &browser, SLOT(promptSaveBrowserPixmap()));
79   saveBrowserPixmapAction->setIcon(QIcon(":/icons/image2.png"));
80 }
81
82 void MussaAlignedWindow::setupMenus()
83 {
84   QMenu *newMenu = menuBar()->addMenu(tr("&File"));
85   newMenu->addAction(saveBrowserPixmapAction);
86
87   newMenu = menuBar()->addMenu(tr("&Edit"));
88   newMenu->addAction(browser.getCopySelectedSequenceAsFastaAction());
89   newMenu->addAction(createSubAnalysisAction);
90   
91   // add some extra features to the context menu
92   QMenu *popupMenu = browser.getPopupMenu();
93   if (popupMenu) {
94     popupMenu->addAction(createSubAnalysisAction);
95   }  
96 }
97
98 void MussaAlignedWindow::setupAlignmentMenus()
99 {
100   pick_align_menu.clear();
101   view_align_menu.clear();
102   pick_actions.clear();
103   view_actions.clear();
104
105   for(vector<ConservedPath >::iterator pathz_i=selected_paths.begin(); 
106       pathz_i != selected_paths.end(); 
107       ++pathz_i)
108   {
109     ConservedPath::path_type normalized_path = pathz_i->normalizedIndexes();
110     ostringstream menu_text;
111     menu_text << pathz_i->window_size << ":";
112     ConservedPath::iterator element_i = normalized_path.begin();
113     menu_text << *element_i++;
114     for (;
115          element_i != normalized_path.end();
116          ++element_i)
117     {
118       menu_text << ", ";
119       menu_text << *element_i;
120     }
121     int index = pathz_i - selected_paths.begin();
122     IntAction *pick = new IntAction(QString(menu_text.str().c_str()), index, this);
123     connect(pick, SIGNAL(triggered(int)), this, SLOT(setAlignment(int)));
124     pick_actions.push_back(pick);
125     pick_align_menu.addAction(pick);
126     IntAction *view = new IntAction(QString(menu_text.str().c_str()), index, this);
127     connect(view, SIGNAL(triggered(int)), this, SLOT(toggleViewAlignment(int)));
128     view->setCheckable(true);
129     view->setChecked(true);
130     view_actions.push_back(view);
131     view_align_menu.addAction(view);
132   }
133
134   menuBar()->addMenu(&pick_align_menu);
135   menuBar()->addMenu(&view_align_menu);
136 }
137
138
139 void MussaAlignedWindow::setSelectedPaths(MussaRef m, const set<int>& sel_paths)
140 {
141   // sets are sorted
142   set<int>::iterator sel_i = sel_paths.begin();
143   list<ConservedPath>::const_iterator path_i = m->paths().refined_pathz.begin();
144   list<ConservedPath>::const_iterator path_end = m->paths().refined_pathz.end();
145   size_t path_size = m->paths().refined_pathz.size();
146   size_t pathid=0;
147
148   selected_paths.reserve(sel_paths.size());
149   view_paths.reserve(sel_paths.size());
150   while (pathid != path_size and sel_i != sel_paths.end())
151   {
152     assert (*sel_i >= 0);
153     size_t sel_pathid = (size_t)(*sel_i);
154     if (pathid == sel_pathid) {
155       selected_paths.push_back(*path_i);
156       view_paths.push_back(true);
157       ++pathid;
158       ++path_i;
159       ++sel_i;
160     } else if (pathid < sel_pathid) {
161       ++pathid;
162       ++path_i;
163     } else if (pathid > sel_pathid) {
164       ++sel_i;
165     }
166   }
167 }
168
169 // FIXME: this is a cut-n-paste from MussaWindow, perhaps they should be refactored to
170 // some shared place
171 void MussaAlignedWindow::createSubAnalysis()
172 {
173   list<SequenceLocation> result;
174   SequenceLocationModel& model = subanalysis_window->getModel();
175   browser.copySelectedTracksAsSeqLocation(result);
176   for(list<SequenceLocation>::iterator result_itor = result.begin();
177       result_itor != result.end();
178       ++result_itor)
179   {
180     model.push_back(*result_itor);
181   }
182
183   if (not subanalysis_window->isVisible()) {
184     subanalysis_window->show();
185   }
186 }
187
188 void MussaAlignedWindow::setAlignment(int alignment_index)
189 {
190   if (selected_paths.size() > 0) {
191     browser.centerOnPath(selected_paths[alignment_index].normalizedIndexes());
192   }
193 }
194
195 void MussaAlignedWindow::showBasePairsCopied(size_t bp_copied)
196 {
197   QString msg("Copied ");
198   QString num;
199   num.setNum(bp_copied);
200   msg += num + " base pairs";
201   statusBar()->showMessage(msg, 5000);
202 }
203
204 void MussaAlignedWindow::toggleViewAlignment(int alignment_index)
205 {
206   view_paths[alignment_index]= not view_paths[alignment_index]; 
207   // perhaps it'd be better if we could erase specific sets
208   // of matches instead of erasing them all and recomputing them all
209   // but this is easier
210   computeMatchLines();
211 }
212
213 void MussaAlignedWindow::update()
214 {
215   browser.update();
216 }
217
218 void MussaAlignedWindow::updateTitle()
219 {
220   std::string title("Sequence View: ");
221   if (analysis) {
222     title += analysis->get_title();
223   }
224   setWindowTitle(title.c_str());
225 }
226
227 void MussaAlignedWindow::computeMatchLines()
228 {
229   browser.clear_links();
230   
231   // filter out conserved paths
232   list<ConservedPath> filtered_paths;
233   vector<ConservedPath>::iterator path_i = selected_paths.begin();
234   list<ConservedPath::path_type> result;
235   list<vector<bool> > reversed;
236
237   for(vector<ConservedPath>::size_type count = 0; 
238       count != selected_paths.size();
239       ++count, ++path_i)
240   {
241     if (view_paths[count]) 
242       filtered_paths.push_back(*path_i);
243   }
244   analysis->createLocalAlignment(filtered_paths.begin(), 
245                                  filtered_paths.end(),
246                                  result, 
247                                  reversed);
248
249   list<ConservedPath::path_type>::const_iterator result_i = result.begin();
250   list<vector<bool> >::const_iterator reversed_i = reversed.begin();
251   for(int i = 0; i != result.size(); ++i, ++result_i, ++reversed_i)
252   {
253     // make 1 base long links
254     browser.link(*result_i, *reversed_i, 1);
255   }
256 }