scide: LookupDialog - subclass QTreeView to provide openDocumentation slot
[supercollider.git] / editors / sc-ide / widgets / lookup_dialog.cpp
blobbc17c431ea37eeb38edd51957337dc873e4b2736
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 bool LookupDialogTreeView::openDocumentation()
40 GenericLookupDialog * parent = qobject_cast<GenericLookupDialog*>(parentWidget());
41 parent->openDocumentation();
43 return true;
46 GenericLookupDialog::GenericLookupDialog( QWidget * parent ):
47 QDialog(parent, Qt::Popup | Qt::FramelessWindowHint)
49 addAction(MainWindow::instance()->action(MainWindow::HelpForSelection));
51 mQueryEdit = new QLineEdit(this);
53 mResult = new LookupDialogTreeView(this);
54 mResult->setRootIsDecorated(false);
55 mResult->setAllColumnsShowFocus(true);
56 mResult->setHeaderHidden(true);
57 mResult->header()->setStretchLastSection(false);
59 QVBoxLayout *layout = new QVBoxLayout;
60 layout->setContentsMargins(4,4,4,4);
61 layout->setSpacing(1);
62 layout->addWidget(mQueryEdit);
63 layout->addWidget(mResult);
64 setLayout(layout);
66 connect(mQueryEdit, SIGNAL(returnPressed()), this, SLOT(performQuery()));
67 connect(mResult, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onAccepted(QModelIndex)));
68 connect(mResult, SIGNAL(activated(QModelIndex)), this, SLOT(onAccepted(QModelIndex)));
70 mResult->installEventFilter(this);
72 QRect bounds(0,0,600,300);
73 if (parent) {
74 QRect parentRect = parent->rect();
75 bounds.moveCenter( parent->mapToGlobal( parentRect.center() ) );
76 } else {
77 QRect availableBounds = QApplication::desktop()->availableGeometry(this);
78 bounds.moveCenter( availableBounds.center() );
81 setGeometry(bounds);
83 mQueryEdit->setFocus( Qt::OtherFocusReason );
86 void GenericLookupDialog::openDocumentation()
88 QModelIndex currentIndex = mResult->currentIndex();
89 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
90 if (!model)
91 return;
93 currentIndex = currentIndex.sibling(currentIndex.row(), 0);
94 QStandardItem *currentItem = model->itemFromIndex(currentIndex);
95 if (!currentItem)
96 return;
98 bool isClass = currentItem->data(IsClassRole).toBool();
99 if (isClass) {
100 Main::openDocumentation(currentItem->text());
101 return;
105 void GenericLookupDialog::onAccepted(QModelIndex currentIndex)
107 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
108 currentIndex = currentIndex.sibling(currentIndex.row(), 0);
109 QStandardItem *currentItem = model->itemFromIndex(currentIndex);
110 if (!currentItem) {
111 reject();
112 return;
115 QString path = currentItem->data( PathRole ).toString();
116 int pos = currentItem->data( CharPosRole ).toInt();
118 Main::documentManager()->open(path, pos);
119 accept();
122 bool GenericLookupDialog::eventFilter( QObject *object, QEvent *event )
124 if (object == mResult && event->type() == QEvent::KeyPress) {
125 QKeyEvent *ke = static_cast<QKeyEvent*>(event);
126 switch(ke->key()){
127 case Qt::Key_Enter:
128 case Qt::Key_Return:
129 onAccepted(mResult->currentIndex());
130 return true;
131 default:;
135 return QDialog::eventFilter(object,event);
138 void GenericLookupDialog::paintEvent( QPaintEvent * )
140 QPainter painter(this);
141 painter.setBrush(Qt::NoBrush);
142 painter.setPen(palette().color(QPalette::Dark));
143 painter.drawRect(rect().adjusted(0,0,-1,-1));
146 void GenericLookupDialog::focusResults()
148 mResult->header()->resizeSections(QHeaderView::ResizeToContents);
149 mResult->setFocus(Qt::OtherFocusReason);
151 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
152 QStandardItem * firstItem = model->item(0, 0);
153 if (firstItem) {
154 QModelIndex firstIndex = model->indexFromItem(firstItem);
155 mResult->setCurrentIndex(firstIndex);
159 using namespace ScLanguage;
160 using std::pair;
161 using std::vector;
163 LookupDialog::LookupDialog( QWidget * parent ):
164 GenericLookupDialog(parent), mIsPartialQuery(false)
166 setWindowTitle(tr("Look Up Class or Method Definition"));
168 mQueryEdit->setText(tr("Enter symbol to look up"));
169 mQueryEdit->selectAll();
172 void LookupDialog::performQuery()
174 QString queryString = mQueryEdit->text();
176 if (queryString.isEmpty()) {
177 mResult->setModel(NULL);
178 return;
181 const Introspection & introspection = Main::scProcess()->introspection();
182 if (!introspection.introspectionAvailable()) {
183 MainWindow::instance()->showStatusMessage("Introspection data not yet available");
184 return;
187 mIsPartialQuery = false;
188 if (queryString[0].isUpper()) {
189 bool success = performClassQuery(queryString);
190 if (success) {
191 focusResults();
192 return;
194 } else {
195 bool success = performMethodQuery(queryString);
196 if (success) {
197 focusResults();
198 return;
202 bool success = performPartialQuery(queryString);
203 if (success)
204 focusResults();
207 void LookupDialog::onAccepted(QModelIndex currentIndex)
209 if (!mIsPartialQuery) {
210 GenericLookupDialog::onAccepted(currentIndex);
211 return;
214 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
215 currentIndex = currentIndex.sibling(currentIndex.row(), 0);
216 QStandardItem *currentItem = model->itemFromIndex(currentIndex);
218 if (!currentItem) {
219 reject();
220 return;
223 bool isClass = currentItem->data(IsClassRole).toBool();
224 if (!isClass) {
225 GenericLookupDialog::onAccepted(currentIndex);
226 return;
229 QString className = currentItem->text();
230 mQueryEdit->setText(className);
231 performQuery();
234 QList<QStandardItem*> GenericLookupDialog::makeDialogItem( QString const & name, QString const & displayPath,
235 QString const & path, int position, bool isClassItem )
237 QStandardItem * item = new QStandardItem( name );
238 item->setData( path, PathRole );
239 item->setData( position, CharPosRole );
240 item->setData( isClassItem, IsClassRole );
241 QStandardItem * pathItem = new QStandardItem(displayPath);
243 QList<QStandardItem*> ret;
244 ret << item << pathItem;
246 return ret;
249 QStandardItemModel * LookupDialog::modelForClass(const QString &className)
251 const Introspection & introspection = Main::scProcess()->introspection();
252 const Class *klass = introspection.findClass(className);
254 if (!klass)
255 return NULL;
257 QStandardItemModel * model = new QStandardItemModel(this);
258 QStandardItem *parentItem = model->invisibleRootItem();
260 while (klass) {
261 Class *metaClass = klass->metaClass;
263 QString displayPath = introspection.compactLibraryPath(klass->definition.path);
265 parentItem->appendRow(makeDialogItem(klass->name.get(), displayPath,
266 klass->definition.path.get(),
267 klass->definition.position, true ));
269 foreach (const Method * method, metaClass->methods) {
270 QString signature = method->signature( Method::SignatureWithoutArguments );
271 QString displayPath = introspection.compactLibraryPath(method->definition.path);
273 parentItem->appendRow(makeDialogItem( signature, displayPath,
274 method->definition.path.get(),
275 method->definition.position, false ));
278 foreach (const Method * method, klass->methods) {
279 QString signature = method->signature( Method::SignatureWithoutArguments );
280 QString displayPath = introspection.compactLibraryPath(method->definition.path);
282 parentItem->appendRow(makeDialogItem( signature, displayPath,
283 method->definition.path.get(),
284 method->definition.position, false ));
287 klass = klass->superClass;
290 return model;
293 QStandardItemModel * LookupDialog::modelForMethod(const QString & methodName)
295 const Introspection & introspection = Main::scProcess()->introspection();
297 const MethodMap & methods = introspection.methodMap();
298 pair<MethodMap::const_iterator, MethodMap::const_iterator> matchingMethods = methods.equal_range(methodName);
300 if (matchingMethods.first == matchingMethods.second)
301 return NULL;
303 QStandardItemModel * model = new QStandardItemModel(this);
304 QStandardItem *parentItem = model->invisibleRootItem();
306 for (MethodMap::const_iterator it = matchingMethods.first; it != matchingMethods.second; ++it) {
307 Method *method = it->second.data();
308 QString signature = method->signature( Method::SignatureWithoutArguments );
310 const QString & path = method->definition.path;
311 QString displayPath = introspection.compactLibraryPath(path);
313 parentItem->appendRow(makeDialogItem( signature, displayPath,
314 method->definition.path.get(),
315 method->definition.position, false ));
318 model->sort(0);
319 return model;
322 QStandardItemModel * LookupDialog::modelForPartialQuery(const QString & queryString)
324 const Introspection & introspection = Main::scProcess()->introspection();
325 vector<const Class *> classes = introspection.findClassPartial(queryString);
326 vector<const Method *> methods = introspection.findMethodPartial(queryString);
327 typedef vector<const Method *>::const_iterator MethodIterator;
328 typedef vector<const Class *>::const_iterator ClassIterator;
330 if (classes.empty() && methods.empty()) {
331 MainWindow::instance()->showStatusMessage("No result for query");
332 return NULL;
335 mIsPartialQuery = true;
336 QStandardItemModel * model = new QStandardItemModel(this);
337 QStandardItem *parentItem = model->invisibleRootItem();
339 for (MethodIterator it = methods.begin(); it != methods.end(); ++it) {
340 const Method *method = *it;
341 QString signature = method->signature( Method::SignatureWithoutArguments );
343 const QString & path = method->definition.path;
344 QString displayPath = introspection.compactLibraryPath(path);
346 parentItem->appendRow(makeDialogItem( signature, displayPath,
347 method->definition.path.get(),
348 method->definition.position, false ));
351 for (ClassIterator it = classes.begin(); it != classes.end(); ++it) {
352 const Class * klass = *it;
353 QString displayPath = introspection.compactLibraryPath(klass->definition.path);
355 parentItem->appendRow(makeDialogItem(klass->name.get(), displayPath,
356 klass->definition.path.get(),
357 klass->definition.position, true ));
360 model->sort(0);
361 return model;
364 bool LookupDialog::performClassQuery(const QString & className)
366 QStandardItemModel * model = modelForClass(className);
367 mResult->setModel(model);
368 return model;
371 bool LookupDialog::performMethodQuery(const QString & methodName)
373 QStandardItemModel * model = modelForMethod(methodName);
374 mResult->setModel(model);
375 return model;
378 bool LookupDialog::performPartialQuery(const QString & queryString)
380 QStandardItemModel * model = modelForPartialQuery(queryString);
381 mResult->setModel(model);
382 return model;
386 ReferencesDialog::ReferencesDialog(QWidget * parent):
387 LookupDialog(parent)
389 setWindowTitle(tr("Look Up References"));
391 mQueryEdit->setText(tr("Enter symbol to find references"));
392 mQueryEdit->selectAll();
395 void ReferencesDialog::performQuery()
397 QString queryString = mQueryEdit->text();
399 if (queryString.isEmpty()) {
400 mResult->setModel(NULL);
401 return;
404 SymbolReferenceRequest * request = new SymbolReferenceRequest(Main::scProcess(), this);
405 connect(request, SIGNAL(response(QString,QString)), this, SLOT(onResposeFromLanguage(QString,QString)));
406 connect(request, SIGNAL(requestCanceled()), this, SLOT(requestCanceled()));
407 request->sendRequest(queryString);
410 void ReferencesDialog::requestCanceled()
412 mResult->setModel(NULL);
415 void ReferencesDialog::onResposeFromLanguage(const QString &, const QString &responseData)
417 QStandardItemModel * model = parse(responseData);
418 mResult->setModel(model);
420 if (model)
421 focusResults();
424 QStandardItemModel * ReferencesDialog::parse(const QString &responseData)
426 using namespace ScLanguage;
427 const Introspection & introspection = Main::scProcess()->introspection();
429 if (!introspection.introspectionAvailable()) { // just required for short path name
430 MainWindow::instance()->showStatusMessage("Introspection data not yet available");
431 return NULL;
434 std::stringstream stream;
435 stream << responseData.toStdString();
436 YAML::Parser parser(stream);
438 YAML::Node doc;
439 if(!parser.GetNextDocument(doc)) {
440 qWarning("no YAML document");
441 return NULL;
444 assert (doc.Type() == YAML::NodeType::Sequence);
446 QString symbol = doc[0].to<std::string>().c_str();
448 QStandardItemModel * model = new QStandardItemModel(this);
449 QStandardItem *parentItem = model->invisibleRootItem();
451 YAML::Node const & references = doc[1];
453 for (YAML::Iterator refIt = references.begin(); refIt != references.end(); ++refIt ) {
454 YAML::Node const & reference = *refIt;
455 QString className = reference[0].to<std::string>().c_str();
456 QString methodName = reference[1].to<std::string>().c_str();
457 QString path = reference[2].to<std::string>().c_str();
458 int charPos = reference[3].to<int>();
460 QString displayPath = introspection.compactLibraryPath(path);
461 QString fullName = ScLanguage::makeFullMethodName(className, methodName);
463 parentItem->appendRow(makeDialogItem(fullName, displayPath, path, charPos, false));
466 return model;
470 } // namespace ScIDE