CallTips.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 
00026 #ifndef _PreComp_
00027 # include <QApplication>
00028 # include <QKeyEvent>
00029 # include <QLabel>
00030 # include <QTextCursor>
00031 # include <QPlainTextEdit>
00032 # include <QToolTip>
00033 #endif
00034 
00035 #include <CXX/Objects.hxx>
00036 #include <Base/Interpreter.h>
00037 #include <Base/PyObjectBase.h>
00038 #include <App/Property.h>
00039 #include <App/PropertyContainer.h>
00040 #include <App/PropertyContainerPy.h>
00041 #include <App/Document.h>
00042 #include <App/DocumentObject.h>
00043 #include <App/DocumentPy.h>
00044 #include <Gui/BitmapFactory.h>
00045 #include <Gui/Document.h>
00046 #include <Gui/DocumentPy.h>
00047 #include "CallTips.h"
00048 
00049 using namespace Gui;
00050 
00051 CallTipsList::CallTipsList(QPlainTextEdit* parent)
00052   :  QListWidget(parent), textEdit(parent), cursorPos(0), validObject(true)
00053 {
00054     // make the user assume that the widget is active
00055     QPalette pal = parent->palette();
00056     pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Active, QPalette::Highlight));
00057     pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Active, QPalette::HighlightedText));
00058     parent->setPalette( pal );
00059 
00060     connect(this, SIGNAL(itemActivated(QListWidgetItem *)), 
00061             this, SLOT(callTipItemActivated(QListWidgetItem *)));
00062 
00063     hideKeys.append(Qt::Key_Space);
00064     hideKeys.append(Qt::Key_Exclam);
00065     hideKeys.append(Qt::Key_QuoteDbl);
00066     hideKeys.append(Qt::Key_NumberSign);
00067     hideKeys.append(Qt::Key_Dollar);
00068     hideKeys.append(Qt::Key_Percent);
00069     hideKeys.append(Qt::Key_Ampersand);
00070     hideKeys.append(Qt::Key_Apostrophe);
00071     hideKeys.append(Qt::Key_Asterisk);
00072     hideKeys.append(Qt::Key_Plus);
00073     hideKeys.append(Qt::Key_Comma);
00074     hideKeys.append(Qt::Key_Minus);
00075     hideKeys.append(Qt::Key_Period);
00076     hideKeys.append(Qt::Key_Slash);
00077     hideKeys.append(Qt::Key_Colon);
00078     hideKeys.append(Qt::Key_Semicolon);
00079     hideKeys.append(Qt::Key_Less);
00080     hideKeys.append(Qt::Key_Equal);
00081     hideKeys.append(Qt::Key_Greater);
00082     hideKeys.append(Qt::Key_Question);
00083     hideKeys.append(Qt::Key_At);
00084     hideKeys.append(Qt::Key_Backslash);
00085 
00086     compKeys.append(Qt::Key_ParenLeft);
00087     compKeys.append(Qt::Key_ParenRight);
00088     compKeys.append(Qt::Key_BracketLeft);
00089     compKeys.append(Qt::Key_BracketRight);
00090     compKeys.append(Qt::Key_BraceLeft);
00091     compKeys.append(Qt::Key_BraceRight);
00092 }
00093 
00094 CallTipsList::~CallTipsList()
00095 {
00096 }
00097 
00098 void CallTipsList::keyboardSearch(const QString& wordPrefix)
00099 { 
00100     // first search for the item that matches perfectly
00101     for (int i=0; i<count(); ++i) {
00102         QString text = item(i)->text();
00103         if (text.startsWith(wordPrefix)) {
00104             setCurrentRow(i);
00105             return;
00106         }
00107     }
00108 
00109     // now do a case insensitive comparison
00110     for (int i=0; i<count(); ++i) {
00111         QString text = item(i)->text();
00112         if (text.startsWith(wordPrefix, Qt::CaseInsensitive)) {
00113             setCurrentRow(i);
00114             return;
00115         }
00116     }
00117 
00118     setItemSelected(currentItem(), false);
00119 }
00120 
00121 void CallTipsList::validateCursor()
00122 {
00123     QTextCursor cursor = textEdit->textCursor();
00124     int currentPos = cursor.position();
00125     if (currentPos < this->cursorPos) {
00126         hide();
00127     }
00128     else {
00129         cursor.setPosition(this->cursorPos);
00130         cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00131         QString word = cursor.selectedText();
00132         if (currentPos > this->cursorPos+word.length()) {
00133             hide();
00134         }
00135         else if (!word.isEmpty()){
00136             // If the word is empty we should not allow to do a search,
00137             // otherwise we may select the next item which is not okay in this
00138             // context. This might happen if e.g. Shift is pressed.
00139             keyboardSearch(word);
00140         }
00141     }
00142 }
00143 
00144 QString CallTipsList::extractContext(const QString& line) const
00145 {
00146     int len = line.size();
00147     int index = len-1;
00148     for (int i=0; i<len; i++) {
00149         int pos = len-1-i;
00150         const char ch = line.at(pos).toAscii();
00151         if ((ch >= 48 && ch <= 57)  ||  // Numbers
00152             (ch >= 65 && ch <= 90)  ||  // Uppercase letters
00153             (ch >= 97 && ch <= 122) ||  // Lowercase letters
00154             (ch == '.') || (ch == '_'))
00155             index = pos;
00156         else 
00157             break;
00158     }
00159 
00160     return line.mid(index);
00161 }
00162 
00163 QMap<QString, CallTip> CallTipsList::extractTips(const QString& context) const
00164 {
00165     Base::PyGILStateLocker lock;
00166     QMap<QString, CallTip> tips;
00167     if (context.isEmpty())
00168         return tips;
00169 
00170     try {
00171         QStringList items = context.split(QLatin1Char('.'));
00172         Py::Module module("__main__");
00173         Py::Dict dict = module.getDict();
00174         QString modname = items.front();
00175         items.pop_front();
00176         if (!dict.hasKey(std::string(modname.toAscii())))
00177             return tips; // unknown object
00178 
00179         // get the Python object we need
00180         Py::Object obj = dict.getItem(std::string(modname.toAscii()));
00181         while (!items.isEmpty()) {
00182             QByteArray name = items.front().toAscii();
00183             std::string attr = name.constData();
00184             items.pop_front();
00185             if (obj.hasAttr(attr))
00186                 obj = obj.getAttr(attr);
00187             else
00188                 return tips;
00189         }
00190         
00191         // Checks whether the type is a subclass of PyObjectBase because to get the doc string
00192         // of a member we must get it by its type instead of its instance otherwise we get the
00193         // wrong string, namely that of the type of the member. 
00194         // Note: 3rd party libraries may use their own type object classes so that we cannot 
00195         // reliably use Py::Type. To be on the safe side we should use Py::Object to assign
00196         // the used type object to.
00197         //Py::Object type = obj.type();
00198         Py::Object type(PyObject_Type(obj.ptr()), true);
00199         Py::Object inst = obj; // the object instance 
00200         union PyType_Object typeobj = {&Base::PyObjectBase::Type};
00201         bool subclass = (PyObject_IsSubclass(type.ptr(), typeobj.o) == 1);
00202         if (subclass) obj = type;
00203         
00204         // If we have an instance of PyObjectBase then determine whether it's valid or not
00205         if (PyObject_IsInstance(inst.ptr(), typeobj.o) == 1) {
00206             Base::PyObjectBase* baseobj = static_cast<Base::PyObjectBase*>(inst.ptr());
00207             const_cast<CallTipsList*>(this)->validObject = baseobj->isValid();
00208         }
00209         else {
00210             // PyObject_IsInstance might set an exception
00211             PyErr_Clear();
00212         }
00213 
00214         Py::List list(PyObject_Dir(obj.ptr()), true);
00215 
00216         // If we derive from PropertyContainerPy we can search for the properties in the
00217         // C++ twin class.
00218         union PyType_Object proptypeobj = {&App::PropertyContainerPy::Type};
00219         if (PyObject_IsSubclass(type.ptr(), proptypeobj.o) == 1) {
00220             // These are the attributes of the instance itself which are NOT accessible by
00221             // its type object
00222             extractTipsFromProperties(inst, tips);
00223         }
00224 
00225         // If we derive from App::DocumentPy we have direct access to the objects by their internal
00226         // names. So, we add these names to the list, too.
00227         union PyType_Object appdoctypeobj = {&App::DocumentPy::Type};
00228         if (PyObject_IsSubclass(type.ptr(), appdoctypeobj.o) == 1) {
00229             App::DocumentPy* docpy = (App::DocumentPy*)(inst.ptr());
00230             App::Document* document = docpy->getDocumentPtr();
00231             // Make sure that the C++ object is alive
00232             if (document) {
00233                 std::vector<App::DocumentObject*> objects = document->getObjects();
00234                 Py::List list;
00235                 for (std::vector<App::DocumentObject*>::iterator it = objects.begin(); it != objects.end(); ++it)
00236                     list.append(Py::String((*it)->getNameInDocument()));
00237                 extractTipsFromObject(inst, list, tips);
00238             }
00239         }
00240 
00241         // If we derive from Gui::DocumentPy we have direct access to the objects by their internal
00242         // names. So, we add these names to the list, too.
00243         union PyType_Object guidoctypeobj = {&Gui::DocumentPy::Type};
00244         if (PyObject_IsSubclass(type.ptr(), guidoctypeobj.o) == 1) {
00245             Gui::DocumentPy* docpy = (Gui::DocumentPy*)(inst.ptr());
00246             if (docpy->getDocumentPtr()) {
00247                 App::Document* document = docpy->getDocumentPtr()->getDocument();
00248                 // Make sure that the C++ object is alive
00249                 if (document) {
00250                     std::vector<App::DocumentObject*> objects = document->getObjects();
00251                     Py::List list;
00252                     for (std::vector<App::DocumentObject*>::iterator it = objects.begin(); it != objects.end(); ++it)
00253                         list.append(Py::String((*it)->getNameInDocument()));
00254                     extractTipsFromObject(inst, list, tips);
00255                 }
00256             }
00257         }
00258 
00259         // These are the attributes from the type object
00260         extractTipsFromObject(obj, list, tips);
00261     }
00262     catch (Py::Exception& e) {
00263         // Just clear the Python exception
00264         e.clear();
00265     }
00266 
00267     return tips;
00268 }
00269 
00270 void CallTipsList::extractTipsFromObject(Py::Object& obj, Py::List& list, QMap<QString, CallTip>& tips) const
00271 {
00272     try {
00273         for (Py::List::iterator it = list.begin(); it != list.end(); ++it) {
00274             Py::String attrname(*it);
00275             Py::Object attr = obj.getAttr(attrname.as_string());
00276 
00277             CallTip tip;
00278             QString str = QString::fromAscii(attrname.as_string().c_str());
00279             tip.name = str;
00280 
00281             if (attr.isCallable()) {
00282                 union PyType_Object basetype = {&PyBaseObject_Type};
00283                 if (PyObject_IsSubclass(attr.ptr(), basetype.o) == 1) {
00284                     tip.type = CallTip::Class;
00285                 }
00286                 else {
00287                     PyErr_Clear(); // PyObject_IsSubclass might set an exception
00288                     tip.type = CallTip::Method;
00289                 }
00290             }
00291             else if (PyModule_Check(attr.ptr())) {
00292                 tip.type = CallTip::Module;
00293             }
00294             else {
00295                 tip.type = CallTip::Member;
00296             }
00297 
00298             if (str == QLatin1String("__doc__") && attr.isString()) {
00299                 Py::Object help = attr;
00300                 if (help.isString()) {
00301                     Py::String doc(help);
00302                     QString longdoc = QString::fromUtf8(doc.as_string().c_str());
00303                     int pos = longdoc.indexOf(QLatin1Char('\n'));
00304                     pos = qMin(pos, 70);
00305                     if (pos < 0) 
00306                         pos = qMin(longdoc.length(), 70);
00307                     tip.description = stripWhiteSpace(longdoc);
00308                     tip.parameter = longdoc.left(pos);
00309                 }
00310             }
00311             else if (attr.hasAttr("__doc__")) {
00312                 Py::Object help = attr.getAttr("__doc__");
00313                 if (help.isString()) {
00314                     Py::String doc(help);
00315                     QString longdoc = QString::fromUtf8(doc.as_string().c_str());
00316                     int pos = longdoc.indexOf(QLatin1Char('\n'));
00317                     pos = qMin(pos, 70);
00318                     if (pos < 0) 
00319                         pos = qMin(longdoc.length(), 70);
00320                     tip.description = stripWhiteSpace(longdoc);
00321                     tip.parameter = longdoc.left(pos);
00322                 }
00323             }
00324             tips[str] = tip;
00325         }
00326     }
00327     catch (Py::Exception& e) {
00328         // Just clear the Python exception
00329         e.clear();
00330     }
00331 }
00332 
00333 void CallTipsList::extractTipsFromProperties(Py::Object& obj, QMap<QString, CallTip>& tips) const
00334 {
00335     App::PropertyContainerPy* cont = (App::PropertyContainerPy*)(obj.ptr());
00336     App::PropertyContainer* container = cont->getPropertyContainerPtr();
00337     // Make sure that the C++ object is alive
00338     if (!container) return;
00339     std::map<std::string,App::Property*> Map;
00340     container->getPropertyMap(Map);
00341 
00342     for (std::map<std::string,App::Property*>::const_iterator It=Map.begin();It!=Map.end();++It) {
00343         CallTip tip;
00344         QString str = QString::fromAscii(It->first.c_str());
00345         tip.name = str;
00346         tip.type = CallTip::Property;
00347         QString longdoc = QString::fromUtf8(container->getPropertyDocumentation(It->second));
00348         // a point, mesh or shape property
00349         if (It->second->isDerivedFrom(Base::Type::fromName("App::PropertyComplexGeoData"))) {
00350             Py::Object data(It->second->getPyObject(), true);
00351             if (data.hasAttr("__doc__")) {
00352                 Py::Object help = data.getAttr("__doc__");
00353                 if (help.isString()) {
00354                     Py::String doc(help);
00355                     longdoc = QString::fromUtf8(doc.as_string().c_str());
00356                 }
00357             }
00358         }
00359         if (!longdoc.isEmpty()) {
00360             int pos = longdoc.indexOf(QLatin1Char('\n'));
00361             pos = qMin(pos, 70);
00362             if (pos < 0) 
00363                 pos = qMin(longdoc.length(), 70);
00364             tip.description = stripWhiteSpace(longdoc);
00365             tip.parameter = longdoc.left(pos);
00366         }
00367         tips[str] = tip;
00368     }
00369 }
00370 
00371 void CallTipsList::showTips(const QString& line)
00372 {
00373     // search only once
00374     static QPixmap type_module_icon = BitmapFactory().pixmap("ClassBrowser/type_module");
00375     static QPixmap type_class_icon = BitmapFactory().pixmap("ClassBrowser/type_class");
00376     static QPixmap method_icon = BitmapFactory().pixmap("ClassBrowser/method");
00377     static QPixmap member_icon = BitmapFactory().pixmap("ClassBrowser/member");
00378     static QPixmap property_icon = BitmapFactory().pixmap("ClassBrowser/property");
00379 
00380     // object is in error state
00381     static const char * const forbidden_xpm[]={
00382             "8 8 3 1",
00383             ". c None",
00384             "# c #ff0000",
00385             "a c #ffffff",
00386             "..####..",
00387             ".######.",
00388             "########",
00389             "#aaaaaa#",
00390             "#aaaaaa#",
00391             "########",
00392             ".######.",
00393             "..####.."};
00394     static QPixmap forbidden_icon(forbidden_xpm);
00395     static QPixmap forbidden_type_module_icon = BitmapFactory().merge(type_module_icon,forbidden_icon,BitmapFactoryInst::BottomLeft);
00396     static QPixmap forbidden_type_class_icon = BitmapFactory().merge(type_class_icon,forbidden_icon,BitmapFactoryInst::BottomLeft);
00397     static QPixmap forbidden_method_icon = BitmapFactory().merge(method_icon,forbidden_icon,BitmapFactoryInst::BottomLeft);
00398     static QPixmap forbidden_member_icon = BitmapFactory().merge(member_icon,forbidden_icon,BitmapFactoryInst::BottomLeft);
00399     static QPixmap forbidden_property_icon = BitmapFactory().merge(property_icon,forbidden_icon,BitmapFactoryInst::BottomLeft);
00400 
00401     this->validObject = true;
00402     QString context = extractContext(line);
00403     QMap<QString, CallTip> tips = extractTips(context);
00404     clear();
00405     for (QMap<QString, CallTip>::Iterator it = tips.begin(); it != tips.end(); ++it) {
00406         addItem(it.key());
00407         QListWidgetItem *item = this->item(this->count()-1);
00408         item->setData(Qt::ToolTipRole, QVariant(it.value().description));
00409         item->setData(Qt::UserRole, QVariant(it.value().parameter));
00410         switch (it.value().type)
00411         {
00412         case CallTip::Module:
00413             {
00414                 item->setIcon((this->validObject ? type_module_icon : forbidden_type_module_icon));
00415             }   break;
00416         case CallTip::Class:
00417             {
00418                 item->setIcon((this->validObject ? type_class_icon : forbidden_type_class_icon));
00419             }   break;
00420         case CallTip::Method:
00421             {
00422                 item->setIcon((this->validObject ? method_icon : forbidden_method_icon));
00423             }   break;
00424         case CallTip::Member:
00425             {
00426                 item->setIcon((this->validObject ? member_icon : forbidden_member_icon));
00427             }   break;
00428         case CallTip::Property:
00429             {
00430                 item->setIcon((this->validObject ? property_icon : forbidden_property_icon));
00431             }   break;
00432         default:
00433             break;
00434         }
00435     }
00436 
00437     if (count()==0)
00438         return; // nothing found
00439 
00440     // get the minimum width and height of the box
00441     int h = 0;
00442     int w = 0;
00443     for (int i = 0; i < count(); ++i) {
00444         QRect r = visualItemRect(item(i));
00445         w = qMax(w, r.width());
00446         h += r.height();
00447     }
00448 
00449     // Add an offset
00450     w += 2*frameWidth();
00451     h += 2*frameWidth();
00452 
00453     // get the start position of the word prefix
00454     QTextCursor cursor = textEdit->textCursor();
00455     this->cursorPos = cursor.position();
00456     QRect rect = textEdit->cursorRect(cursor);
00457     int posX = rect.x();
00458     int posY = rect.y();
00459     int boxH = h;
00460 
00461     // Decide whether to show downstairs or upstairs
00462     if (posY > textEdit->viewport()->height()/2) {
00463         h = qMin(qMin(h,posY), 250);
00464         if (h < boxH)
00465             w += textEdit->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
00466         setGeometry(posX,posY-h, w, h);
00467     }
00468     else {
00469         h = qMin(qMin(h,textEdit->viewport()->height()-fontMetrics().height()-posY), 250);
00470         if (h < boxH)
00471             w += textEdit->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
00472         setGeometry(posX, posY+fontMetrics().height(), w, h);
00473     }
00474     
00475     setCurrentRow(0);
00476     show();
00477 }
00478 
00479 void CallTipsList::showEvent(QShowEvent* e)
00480 {
00481     QListWidget::showEvent(e);
00482     // install this object to filter timer events for the tooltip label
00483     qApp->installEventFilter(this);
00484 }
00485 
00486 void CallTipsList::hideEvent(QHideEvent* e)
00487 {
00488     QListWidget::hideEvent(e);
00489     qApp->removeEventFilter(this);
00490 }
00491 
00496 bool CallTipsList::eventFilter(QObject * watched, QEvent * event)
00497 {
00498     // This is a trick to avoid to hide the tooltip window after the defined time span
00499     // of 10 seconds. We just filter out all timer events to keep the label visible.
00500     if (watched->inherits("QLabel")) {
00501         QLabel* label = qobject_cast<QLabel*>(watched);
00502         // Ignore the timer events to prevent from being closed
00503         if (label->windowFlags() & Qt::ToolTip && event->type() == QEvent::Timer)
00504             return true;
00505     }
00506     if (isVisible() && watched == textEdit->viewport()) {
00507         if (event->type() == QEvent::MouseButtonPress)
00508             hide();
00509     }
00510     else if (isVisible() && watched == textEdit) {
00511         if (event->type() == QEvent::KeyPress) {
00512             QKeyEvent* ke = (QKeyEvent*)event;
00513             if (ke->key() == Qt::Key_Up || ke->key() == Qt::Key_Down) {
00514                 keyPressEvent(ke);
00515                 return true;
00516             }
00517             else if (ke->key() == Qt::Key_PageUp || ke->key() == Qt::Key_PageDown) {
00518                 keyPressEvent(ke);
00519                 return true;
00520             }
00521             else if (ke->key() == Qt::Key_Escape) {
00522                 hide();
00523                 return true;
00524             }
00525             else if (this->hideKeys.indexOf(ke->key()) > -1) {
00526                 itemActivated(currentItem());
00527                 return false;
00528             }
00529             else if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Tab) {
00530                 itemActivated(currentItem());
00531                 return true;
00532             }
00533             else if (this->compKeys.indexOf(ke->key()) > -1) {
00534                 itemActivated(currentItem());
00535                 return false;
00536             }
00537             else if (ke->key() == Qt::Key_Shift || ke->key() == Qt::Key_Control || 
00538                        ke->key() == Qt::Key_Meta || ke->key() == Qt::Key_Alt || 
00539                        ke->key() == Qt::Key_AltGr) {
00540                 // filter these meta keys to avoid to call keyboardSearch()
00541                 return true;
00542             }
00543         }
00544         else if (event->type() == QEvent::KeyRelease) {
00545             QKeyEvent* ke = (QKeyEvent*)event;
00546             if (ke->key() == Qt::Key_Up || ke->key() == Qt::Key_Down ||
00547                 ke->key() == Qt::Key_PageUp || ke->key() == Qt::Key_PageDown) {
00548                 QList<QListWidgetItem *> items = selectedItems();
00549                 if (!items.isEmpty()) {
00550                     QPoint p(width(), 0);
00551                     QString text = items.front()->toolTip();
00552                     if (!text.isEmpty()){
00553                         QToolTip::showText(mapToGlobal(p), text);
00554                     } else {
00555                         QToolTip::showText(p, QString());
00556                     }
00557                 }
00558                 return true;
00559             }
00560         }
00561         else if (event->type() == QEvent::FocusOut) {
00562             if (!hasFocus())
00563                 hide();
00564         }
00565     }
00566 
00567     return QListWidget::eventFilter(watched, event);
00568 }
00569 
00570 void CallTipsList::callTipItemActivated(QListWidgetItem *item)
00571 {
00572     hide();
00573     if (!isItemSelected(item)) return;
00574     
00575     QString text = item->text();
00576     QTextCursor cursor = textEdit->textCursor();
00577     cursor.setPosition(this->cursorPos);
00578     cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
00579     QString sel = cursor.selectedText();
00580     if (!sel.isEmpty()) {
00581         // in case the cursor moved too far on the right side
00582         const QChar underscore =  QLatin1Char('_');
00583         const QChar ch = sel.at(sel.count()-1);
00584         if (!ch.isLetterOrNumber() && ch != underscore)
00585             cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
00586     }
00587     cursor.insertText( text );
00588     textEdit->ensureCursorVisible();
00589 
00590     QRect rect = textEdit->cursorRect(cursor);
00591     int posX = rect.x();
00592     int posY = rect.y();
00593 
00594     QPoint p(posX, posY);
00595     p = textEdit->mapToGlobal(p);
00596     QToolTip::showText(p, item->data(Qt::UserRole).toString());
00597 }
00598 
00599 QString CallTipsList::stripWhiteSpace(const QString& str) const
00600 {
00601     QString stripped = str;
00602     QStringList lines = str.split(QLatin1String("\n"));
00603     int minspace=INT_MAX;
00604     int line=0;
00605     for (QStringList::iterator it = lines.begin(); it != lines.end(); ++it, ++line) {
00606         if (it->count() > 0 && line > 0) {
00607             int space = 0;
00608             for (int i=0; i<it->count(); i++) {
00609                 if ((*it)[i] == QLatin1Char('\t'))
00610                     space++;
00611                 else
00612                     break;
00613             }
00614 
00615             if (it->count() > space)
00616                 minspace = std::min<int>(minspace, space);
00617         }
00618     }
00619 
00620     // remove all leading tabs from each line
00621     if (minspace > 0 && minspace < INT_MAX) {
00622         int line=0;
00623         QStringList strippedlines;
00624         for (QStringList::iterator it = lines.begin(); it != lines.end(); ++it, ++line) {
00625             if (line == 0 && !it->isEmpty()) {
00626                 strippedlines << *it;
00627             }
00628             else if (it->count() > 0 && line > 0) {
00629                 strippedlines << it->mid(minspace);
00630             }
00631         }
00632 
00633         stripped = strippedlines.join(QLatin1String("\n"));
00634     }
00635 
00636     return stripped;
00637 }
00638 
00639 #include "moc_CallTips.cpp" 

Generated on Wed Nov 23 19:00:00 2011 for FreeCAD by  doxygen 1.6.1