scide: DocManager - report warnings via the status bar
[supercollider.git] / editors / sc-ide / widgets / lookup_dialog.cpp
blob639b5dd8b7474cac6074361caf75c6aac14858b3
1 /*
2 SuperCollider Qt IDE
3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "lookup_dialog.hpp"
22 #include "main_window.hpp"
23 #include "../core/sc_introspection.hpp"
24 #include "../core/main.hpp"
26 #include <QVBoxLayout>
27 #include <QHeaderView>
28 #include <QKeyEvent>
29 #include <QDesktopWidget>
30 #include <QApplication>
31 #include <QPainter>
33 #include "yaml-cpp/node.h"
34 #include "yaml-cpp/parser.h"
36 namespace ScIDE {
38 GenericLookupDialog::GenericLookupDialog( QWidget * parent ):
39 QDialog(parent, Qt::Popup | Qt::FramelessWindowHint)
41 mQueryEdit = new QLineEdit(this);
43 mResult = new QTreeView(this);
44 mResult->setRootIsDecorated(false);
45 mResult->setAllColumnsShowFocus(true);
46 mResult->setHeaderHidden(true);
47 mResult->header()->setStretchLastSection(false);
49 QVBoxLayout *layout = new QVBoxLayout;
50 layout->setContentsMargins(4,4,4,4);
51 layout->setSpacing(1);
52 layout->addWidget(mQueryEdit);
53 layout->addWidget(mResult);
54 setLayout(layout);
56 connect(mQueryEdit, SIGNAL(returnPressed()), this, SLOT(performQuery()));
57 connect(mResult, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onAccepted(QModelIndex)));
58 connect(mResult, SIGNAL(activated(QModelIndex)), this, SLOT(onAccepted(QModelIndex)));
60 mResult->installEventFilter(this);
62 QRect bounds(0,0,600,300);
63 if (parent) {
64 QRect parentRect = parent->rect();
65 bounds.moveCenter( parent->mapToGlobal( parentRect.center() ) );
66 } else {
67 QRect availableBounds = QApplication::desktop()->availableGeometry(this);
68 bounds.moveCenter( availableBounds.center() );
71 setGeometry(bounds);
73 mQueryEdit->setFocus( Qt::OtherFocusReason );
76 void GenericLookupDialog::onAccepted(QModelIndex currentIndex)
78 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
79 currentIndex = currentIndex.sibling(currentIndex.row(), 0);
80 QStandardItem *currentItem = model->itemFromIndex(currentIndex);
81 if (!currentItem) {
82 reject();
83 return;
86 QString path = currentItem->data( PathRole ).toString();
87 int pos = currentItem->data( CharPosRole ).toInt();
89 Main::documentManager()->open(path, pos);
90 accept();
93 bool GenericLookupDialog::eventFilter( QObject *object, QEvent *event )
95 if (object == mResult && event->type() == QEvent::KeyPress) {
96 QKeyEvent *ke = static_cast<QKeyEvent*>(event);
97 switch(ke->key()){
98 case Qt::Key_Enter:
99 case Qt::Key_Return:
100 onAccepted(mResult->currentIndex());
101 return true;
102 default:;
106 return QDialog::eventFilter(object,event);
109 void GenericLookupDialog::paintEvent( QPaintEvent * )
111 QPainter painter(this);
112 painter.setBrush(Qt::NoBrush);
113 painter.setPen(palette().color(QPalette::Dark));
114 painter.drawRect(rect().adjusted(0,0,-1,-1));
117 void GenericLookupDialog::focusResults()
119 mResult->header()->resizeSections(QHeaderView::ResizeToContents);
120 mResult->setFocus(Qt::OtherFocusReason);
122 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
123 QStandardItem * firstItem = model->item(0, 0);
124 if (firstItem) {
125 QModelIndex firstIndex = model->indexFromItem(firstItem);
126 mResult->setCurrentIndex(firstIndex);
130 using namespace ScLanguage;
131 using std::pair;
132 using std::vector;
134 LookupDialog::LookupDialog( QWidget * parent ):
135 GenericLookupDialog(parent), mIsPartialQuery(false)
137 setWindowTitle(tr("Look Up Class or Method Definition"));
139 mQueryEdit->setText(tr("Enter symbol to look up"));
140 mQueryEdit->selectAll();
143 void LookupDialog::performQuery()
145 QString queryString = mQueryEdit->text();
147 if (queryString.isEmpty()) {
148 mResult->setModel(NULL);
149 return;
152 const Introspection & introspection = Main::scProcess()->introspection();
153 if (!introspection.introspectionAvailable()) {
154 MainWindow::instance()->showStatusMessage("Introspection data not yet available");
155 return;
158 mIsPartialQuery = false;
159 if (queryString[0].isUpper()) {
160 bool success = performClassQuery(queryString);
161 if (success) {
162 focusResults();
163 return;
165 } else {
166 bool success = performMethodQuery(queryString);
167 if (success) {
168 focusResults();
169 return;
173 bool success = performPartialQuery(queryString);
174 if (success)
175 focusResults();
178 void LookupDialog::onAccepted(QModelIndex currentIndex)
180 if (!mIsPartialQuery) {
181 GenericLookupDialog::onAccepted(currentIndex);
182 return;
185 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
186 currentIndex = currentIndex.sibling(currentIndex.row(), 0);
187 QStandardItem *currentItem = model->itemFromIndex(currentIndex);
189 if (!currentItem) {
190 reject();
191 return;
194 bool isClass = currentItem->data(IsClassRole).toBool();
195 if (!isClass) {
196 GenericLookupDialog::onAccepted(currentIndex);
197 return;
200 QString className = currentItem->text();
201 mQueryEdit->setText(className);
202 performQuery();
205 QList<QStandardItem*> GenericLookupDialog::makeDialogItem( QString const & name, QString const & displayPath,
206 QString const & path, int position, bool isClassItem )
208 QStandardItem * item = new QStandardItem( name );
209 item->setData( path, PathRole );
210 item->setData( position, CharPosRole );
211 item->setData( isClassItem, IsClassRole );
212 QStandardItem * pathItem = new QStandardItem(displayPath);
214 QList<QStandardItem*> ret;
215 ret << item << pathItem;
217 return ret;
220 QStandardItemModel * LookupDialog::modelForClass(const QString &className)
222 const Introspection & introspection = Main::scProcess()->introspection();
223 const Class *klass = introspection.findClass(className);
225 if (!klass)
226 return NULL;
228 QStandardItemModel * model = new QStandardItemModel(this);
229 QStandardItem *parentItem = model->invisibleRootItem();
231 while (klass) {
232 Class *metaClass = klass->metaClass;
234 QString displayPath = introspection.compactLibraryPath(klass->definition.path);
236 parentItem->appendRow(makeDialogItem(klass->name.get(), displayPath,
237 klass->definition.path.get(),
238 klass->definition.position, true ));
240 foreach (const Method * method, metaClass->methods) {
241 QString signature = method->signature( Method::SignatureWithoutArguments );
242 QString displayPath = introspection.compactLibraryPath(method->definition.path);
244 parentItem->appendRow(makeDialogItem( signature, displayPath,
245 method->definition.path.get(),
246 method->definition.position, false ));
249 foreach (const Method * method, klass->methods) {
250 QString signature = method->signature( Method::SignatureWithoutArguments );
251 QString displayPath = introspection.compactLibraryPath(method->definition.path);
253 parentItem->appendRow(makeDialogItem( signature, displayPath,
254 method->definition.path.get(),
255 method->definition.position, false ));
258 klass = klass->superClass;
261 return model;
264 QStandardItemModel * LookupDialog::modelForMethod(const QString & methodName)
266 const Introspection & introspection = Main::scProcess()->introspection();
268 const MethodMap & methods = introspection.methodMap();
269 pair<MethodMap::const_iterator, MethodMap::const_iterator> matchingMethods = methods.equal_range(methodName);
271 if (matchingMethods.first == matchingMethods.second)
272 return NULL;
274 QStandardItemModel * model = new QStandardItemModel(this);
275 QStandardItem *parentItem = model->invisibleRootItem();
277 for (MethodMap::const_iterator it = matchingMethods.first; it != matchingMethods.second; ++it) {
278 Method *method = it->second.data();
279 QString signature = method->signature( Method::SignatureWithoutArguments );
281 const QString & path = method->definition.path;
282 QString displayPath = introspection.compactLibraryPath(path);
284 parentItem->appendRow(makeDialogItem( signature, displayPath,
285 method->definition.path.get(),
286 method->definition.position, false ));
289 model->sort(0);
290 return model;
293 QStandardItemModel * LookupDialog::modelForPartialQuery(const QString & queryString)
295 const Introspection & introspection = Main::scProcess()->introspection();
296 vector<const Class *> classes = introspection.findClassPartial(queryString);
297 vector<const Method *> methods = introspection.findMethodPartial(queryString);
298 typedef vector<const Method *>::const_iterator MethodIterator;
299 typedef vector<const Class *>::const_iterator ClassIterator;
301 if (classes.empty() && methods.empty()) {
302 MainWindow::instance()->showStatusMessage("No result for query");
303 return NULL;
306 mIsPartialQuery = true;
307 QStandardItemModel * model = new QStandardItemModel(this);
308 QStandardItem *parentItem = model->invisibleRootItem();
310 for (MethodIterator it = methods.begin(); it != methods.end(); ++it) {
311 const Method *method = *it;
312 QString signature = method->signature( Method::SignatureWithoutArguments );
314 const QString & path = method->definition.path;
315 QString displayPath = introspection.compactLibraryPath(path);
317 parentItem->appendRow(makeDialogItem( signature, displayPath,
318 method->definition.path.get(),
319 method->definition.position, false ));
322 for (ClassIterator it = classes.begin(); it != classes.end(); ++it) {
323 const Class * klass = *it;
324 QString displayPath = introspection.compactLibraryPath(klass->definition.path);
326 parentItem->appendRow(makeDialogItem(klass->name.get(), displayPath,
327 klass->definition.path.get(),
328 klass->definition.position, true ));
331 model->sort(0);
332 return model;
335 bool LookupDialog::performClassQuery(const QString & className)
337 QStandardItemModel * model = modelForClass(className);
338 mResult->setModel(model);
339 return model;
342 bool LookupDialog::performMethodQuery(const QString & methodName)
344 QStandardItemModel * model = modelForMethod(methodName);
345 mResult->setModel(model);
346 return model;
349 bool LookupDialog::performPartialQuery(const QString & queryString)
351 QStandardItemModel * model = modelForPartialQuery(queryString);
352 mResult->setModel(model);
353 return model;
357 ReferencesDialog::ReferencesDialog(QWidget * parent):
358 LookupDialog(parent)
360 setWindowTitle(tr("Look Up References"));
362 mQueryEdit->setText(tr("Enter symbol to find references"));
363 mQueryEdit->selectAll();
366 void ReferencesDialog::performQuery()
368 QString queryString = mQueryEdit->text();
370 if (queryString.isEmpty()) {
371 mResult->setModel(NULL);
372 return;
375 SymbolReferenceRequest * request = new SymbolReferenceRequest(Main::scProcess(), this);
376 connect(request, SIGNAL(response(QString,QString)), this, SLOT(onResposeFromLanguage(QString,QString)));
377 connect(request, SIGNAL(requestCanceled()), this, SLOT(requestCanceled()));
378 request->sendRequest(queryString);
381 void ReferencesDialog::requestCanceled()
383 mResult->setModel(NULL);
386 void ReferencesDialog::onResposeFromLanguage(const QString &, const QString &responseData)
388 QStandardItemModel * model = parse(responseData);
389 mResult->setModel(model);
391 if (model)
392 focusResults();
395 QStandardItemModel * ReferencesDialog::parse(const QString &responseData)
397 using namespace ScLanguage;
398 const Introspection & introspection = Main::scProcess()->introspection();
400 if (!introspection.introspectionAvailable()) { // just required for short path name
401 MainWindow::instance()->showStatusMessage("Introspection data not yet available");
402 return NULL;
405 std::stringstream stream;
406 stream << responseData.toStdString();
407 YAML::Parser parser(stream);
409 YAML::Node doc;
410 if(!parser.GetNextDocument(doc)) {
411 qWarning("no YAML document");
412 return NULL;
415 assert (doc.Type() == YAML::NodeType::Sequence);
417 QString symbol = doc[0].to<std::string>().c_str();
419 QStandardItemModel * model = new QStandardItemModel(this);
420 QStandardItem *parentItem = model->invisibleRootItem();
422 YAML::Node const & references = doc[1];
424 for (YAML::Iterator refIt = references.begin(); refIt != references.end(); ++refIt ) {
425 YAML::Node const & reference = *refIt;
426 QString className = reference[0].to<std::string>().c_str();
427 QString methodName = reference[1].to<std::string>().c_str();
428 QString path = reference[2].to<std::string>().c_str();
429 int charPos = reference[3].to<int>();
431 QString displayPath = introspection.compactLibraryPath(path);
432 QString fullName = ScLanguage::makeFullMethodName(className, methodName);
434 parentItem->appendRow(makeDialogItem(fullName, displayPath, path, charPos, false));
437 return model;
441 } // namespace ScIDE