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