scide: LookupDialog - implement partial query
[supercollider.git] / editors / sc-ide / widgets / lookup_dialog.cpp
blob64388362f9e5768e4e4a9dc28577308ac168a930
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 namespace ScIDE {
35 GenericLookupDialog::GenericLookupDialog( QWidget * parent ):
36 QDialog(parent, Qt::Popup | Qt::FramelessWindowHint)
38 mQueryEdit = new QLineEdit(this);
40 mResult = new QTreeView(this);
41 mResult->setRootIsDecorated(false);
42 mResult->setAllColumnsShowFocus(true);
43 mResult->setHeaderHidden(true);
44 mResult->header()->setStretchLastSection(false);
46 QVBoxLayout *layout = new QVBoxLayout;
47 layout->setContentsMargins(4,4,4,4);
48 layout->setSpacing(1);
49 layout->addWidget(mQueryEdit);
50 layout->addWidget(mResult);
51 setLayout(layout);
53 connect(mQueryEdit, SIGNAL(returnPressed()), this, SLOT(performQuery()));
54 connect(mResult, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onAccepted(QModelIndex)));
55 connect(mResult, SIGNAL(activated(QModelIndex)), this, SLOT(onAccepted(QModelIndex)));
57 mResult->installEventFilter(this);
59 QRect bounds(0,0,600,300);
60 if (parent) {
61 QRect parentRect = parent->rect();
62 bounds.moveCenter( parent->mapToGlobal( parentRect.center() ) );
63 } else {
64 QRect availableBounds = QApplication::desktop()->availableGeometry(this);
65 bounds.moveCenter( availableBounds.center() );
68 setGeometry(bounds);
70 mQueryEdit->setFocus( Qt::OtherFocusReason );
73 void GenericLookupDialog::onAccepted(QModelIndex currentIndex)
75 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
76 currentIndex = currentIndex.sibling(currentIndex.row(), 0);
77 QStandardItem *currentItem = model->itemFromIndex(currentIndex);
78 if (!currentItem) {
79 reject();
80 return;
83 QString path = currentItem->data( PathRole ).toString();
84 int pos = currentItem->data( CharPosRole ).toInt();
86 Main::documentManager()->open(path, pos);
87 accept();
90 bool GenericLookupDialog::eventFilter( QObject *object, QEvent *event )
92 if (object == mResult && event->type() == QEvent::KeyPress) {
93 QKeyEvent *ke = static_cast<QKeyEvent*>(event);
94 switch(ke->key()){
95 case Qt::Key_Enter:
96 case Qt::Key_Return:
97 onAccepted(mResult->currentIndex());
98 return true;
99 default:;
103 return QDialog::eventFilter(object,event);
106 void GenericLookupDialog::paintEvent( QPaintEvent * )
108 QPainter painter(this);
109 painter.setBrush(Qt::NoBrush);
110 painter.setPen(palette().color(QPalette::Dark));
111 painter.drawRect(rect().adjusted(0,0,-1,-1));
114 void GenericLookupDialog::focusResults()
116 mResult->header()->resizeSections(QHeaderView::ResizeToContents);
117 mResult->setFocus(Qt::OtherFocusReason);
119 QStandardItemModel * model = qobject_cast<QStandardItemModel*>(mResult->model());
120 QStandardItem * firstItem = model->item(0, 0);
121 if (firstItem) {
122 QModelIndex firstIndex = model->indexFromItem(firstItem);
123 mResult->setCurrentIndex(firstIndex);
127 using namespace ScLanguage;
128 using std::pair;
129 using std::vector;
131 LookupDialog::LookupDialog( QWidget * parent ):
132 GenericLookupDialog(parent)
134 setWindowTitle(tr("Look Up Class or Method Definition"));
136 mQueryEdit->setText(tr("Enter symbol to look up"));
137 mQueryEdit->selectAll();
140 void LookupDialog::performQuery()
142 QString queryString = mQueryEdit->text();
144 if (queryString.isEmpty()) {
145 mResult->setModel(NULL);
146 return;
149 const Introspection & introspection = Main::scProcess()->introspection();
150 if (!introspection.introspectionAvailable()) {
151 MainWindow::instance()->showStatusMessage("Introspection Data not available");
152 return;
155 if (queryString[0].isUpper()) {
156 bool success = performClassQuery(queryString);
157 if (success) {
158 focusResults();
159 return;
161 } else {
162 bool success = performMethodQuery(queryString);
163 if (success) {
164 focusResults();
165 return;
169 bool success = performPartialQuery(queryString);
170 if (success)
171 focusResults();
174 QList<QStandardItem*> GenericLookupDialog::makeDialogItem( QString const & name, QString const & displayPath,
175 QString const & path, int position )
177 QStandardItem * item = new QStandardItem( name );
178 item->setData( path, PathRole );
179 item->setData( position, CharPosRole );
180 QStandardItem * pathItem = new QStandardItem(displayPath);
182 QList<QStandardItem*> ret;
183 ret << item << pathItem;
185 return ret;
188 QStandardItemModel * LookupDialog::modelForClass(const QString &className)
190 const Introspection & introspection = Main::scProcess()->introspection();
191 const Class *klass = introspection.findClass(className);
193 if (!klass)
194 return NULL;
196 QStandardItemModel * model = new QStandardItemModel(this);
197 QStandardItem *parentItem = model->invisibleRootItem();
199 while (klass) {
200 Class *metaClass = klass->metaClass;
202 QString displayPath = introspection.compactLibraryPath(klass->definition.path);
204 parentItem->appendRow(makeDialogItem(klass->name.get(), displayPath,
205 klass->definition.path.get(),
206 klass->definition.position));
208 foreach (const Method * method, metaClass->methods) {
209 QString signature = method->signature( Method::SignatureWithoutArguments );
210 QString displayPath = introspection.compactLibraryPath(method->definition.path);
212 parentItem->appendRow(makeDialogItem( signature, displayPath,
213 method->definition.path.get(),
214 method->definition.position ));
217 foreach (const Method * method, klass->methods) {
218 QString signature = method->signature( Method::SignatureWithoutArguments );
219 QString displayPath = introspection.compactLibraryPath(method->definition.path);
221 parentItem->appendRow(makeDialogItem( signature, displayPath,
222 method->definition.path.get(),
223 method->definition.position ));
226 klass = klass->superClass;
229 return model;
232 QStandardItemModel * LookupDialog::modelForMethod(const QString & methodName)
234 const Introspection & introspection = Main::scProcess()->introspection();
236 const MethodMap & methods = introspection.methodMap();
237 pair<MethodMap::const_iterator, MethodMap::const_iterator> matchingMethods = methods.equal_range(methodName);
239 if (matchingMethods.first == matchingMethods.second)
240 return NULL;
242 QStandardItemModel * model = new QStandardItemModel(this);
243 QStandardItem *parentItem = model->invisibleRootItem();
245 for (MethodMap::const_iterator it = matchingMethods.first; it != matchingMethods.second; ++it) {
246 Method *method = it->second.data();
247 QString signature = method->signature( Method::SignatureWithoutArguments );
249 const QString & path = method->definition.path;
250 QString displayPath = introspection.compactLibraryPath(path);
252 parentItem->appendRow(makeDialogItem( signature, displayPath,
253 method->definition.path.get(),
254 method->definition.position ));
257 model->sort(0);
258 return model;
261 QStandardItemModel * LookupDialog::modelForCaseInsensitiveQuery(const QString & queryString)
263 const Introspection & introspection = Main::scProcess()->introspection();
264 vector<const Class *> classes = introspection.findClassPartial(queryString);
265 vector<const Method *> methods = introspection.findMethodPartial(queryString);
266 typedef vector<const Method *>::const_iterator MethodIterator;
267 typedef vector<const Class *>::const_iterator ClassIterator;
269 if (classes.empty() && methods.empty()) {
270 MainWindow::instance()->showStatusMessage("No result for query");
271 return NULL;
274 QStandardItemModel * model = new QStandardItemModel(this);
275 QStandardItem *parentItem = model->invisibleRootItem();
277 for (MethodIterator it = methods.begin(); it != methods.end(); ++it) {
278 const Method *method = *it;
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 ));
289 for (ClassIterator it = classes.begin(); it != classes.end(); ++it) {
290 const Class * klass = *it;
291 QString displayPath = introspection.compactLibraryPath(klass->definition.path);
293 parentItem->appendRow(makeDialogItem(klass->name.get(), displayPath,
294 klass->definition.path.get(),
295 klass->definition.position));
298 model->sort(0);
299 return model;
302 bool LookupDialog::performClassQuery(const QString & className)
304 QStandardItemModel * model = modelForClass(className);
305 mResult->setModel(model);
306 return model;
309 bool LookupDialog::performMethodQuery(const QString & methodName)
311 QStandardItemModel * model = modelForMethod(methodName);
312 mResult->setModel(model);
313 return model;
316 bool LookupDialog::performPartialQuery(const QString & queryString)
318 QStandardItemModel * model = modelForCaseInsensitiveQuery(queryString);
319 mResult->setModel(model);
320 return model;
324 } // namespace ScIDE