tools/designer/src/lib/shared/textpropertyeditor.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Designer of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "textpropertyeditor_p.h"
       
    43 #include "propertylineedit_p.h"
       
    44 #include "stylesheeteditor_p.h"
       
    45 
       
    46 #include <QtGui/QLineEdit>
       
    47 #include <QtGui/QRegExpValidator>
       
    48 #include <QtGui/QResizeEvent>
       
    49 #include <QtGui/QCompleter>
       
    50 #include <QtGui/QAbstractItemView>
       
    51 #include <QtCore/QRegExp>
       
    52 #include <QtCore/QUrl>
       
    53 #include <QtCore/QFile>
       
    54 #include <QtCore/QDebug>
       
    55 
       
    56 QT_BEGIN_NAMESPACE
       
    57 
       
    58 namespace {
       
    59     const QChar NewLineChar(QLatin1Char('\n'));
       
    60     const QLatin1String EscapedNewLine("\\n");
       
    61 
       
    62     // A validator that replaces offending strings
       
    63     class ReplacementValidator : public QValidator {
       
    64     public:
       
    65         ReplacementValidator (QObject * parent,
       
    66                               const QString &offending,
       
    67                               const QString &replacement);
       
    68         virtual void fixup ( QString & input ) const;
       
    69         virtual State validate ( QString & input, int &pos) const;
       
    70     private:
       
    71         const QString m_offending;
       
    72         const QString m_replacement;
       
    73     };
       
    74 
       
    75     ReplacementValidator::ReplacementValidator (QObject * parent,
       
    76                                         const QString &offending,
       
    77                                         const QString &replacement) :
       
    78       QValidator(parent ),
       
    79       m_offending(offending),
       
    80       m_replacement(replacement)
       
    81     {
       
    82     }
       
    83 
       
    84     void ReplacementValidator::fixup ( QString & input ) const {
       
    85         input.replace(m_offending, m_replacement);
       
    86     }
       
    87 
       
    88     QValidator::State ReplacementValidator::validate ( QString & input, int &/* pos */) const {
       
    89         fixup (input);
       
    90         return Acceptable;
       
    91     }
       
    92 
       
    93     // A validator for style sheets. Does newline handling and validates sheets.
       
    94     class StyleSheetValidator : public ReplacementValidator {
       
    95     public:
       
    96         StyleSheetValidator (QObject * parent);
       
    97         virtual State validate(QString & input, int &pos) const;
       
    98     };
       
    99 
       
   100     StyleSheetValidator::StyleSheetValidator (QObject * parent) :
       
   101        ReplacementValidator(parent, NewLineChar, EscapedNewLine)
       
   102     {
       
   103     }
       
   104 
       
   105     QValidator::State StyleSheetValidator::validate ( QString & input, int &pos) const
       
   106     {
       
   107         // base class
       
   108         const State state = ReplacementValidator:: validate(input, pos);
       
   109         if (state != Acceptable)
       
   110             return state;
       
   111         // now check style sheet, create string with newlines
       
   112         const QString styleSheet = qdesigner_internal::TextPropertyEditor::editorStringToString(input, qdesigner_internal::ValidationStyleSheet);
       
   113         const bool valid = qdesigner_internal::StyleSheetEditorDialog::isStyleSheetValid(styleSheet);
       
   114         return valid ? Acceptable : Intermediate;
       
   115     }
       
   116 
       
   117     // A validator for URLs based on QUrl. Enforces complete protocol
       
   118     // specification with a completer (adds a trailing slash)
       
   119     class UrlValidator : public QValidator {
       
   120     public:
       
   121         UrlValidator(QCompleter *completer, QObject *parent);
       
   122 
       
   123         virtual State validate(QString &input, int &pos) const;
       
   124         virtual void fixup(QString &input) const;
       
   125     private:
       
   126         QUrl guessUrlFromString(const QString &string) const;
       
   127         QCompleter *m_completer;
       
   128     };
       
   129 
       
   130     UrlValidator::UrlValidator(QCompleter *completer, QObject *parent) :
       
   131         QValidator(parent),
       
   132         m_completer(completer)
       
   133     {
       
   134     }
       
   135 
       
   136     QValidator::State UrlValidator::validate(QString &input, int &pos) const
       
   137     {
       
   138         Q_UNUSED(pos);
       
   139 
       
   140         if (input.isEmpty())
       
   141             return Acceptable;
       
   142 
       
   143         const QUrl url(input, QUrl::StrictMode);
       
   144 
       
   145         if (!url.isValid() || url.isEmpty())
       
   146             return Intermediate;
       
   147 
       
   148         if (url.scheme().isEmpty())
       
   149             return Intermediate;
       
   150 
       
   151         if (url.host().isEmpty() && url.path().isEmpty())
       
   152             return Intermediate;
       
   153 
       
   154         return Acceptable;
       
   155     }
       
   156 
       
   157     void UrlValidator::fixup(QString &input) const
       
   158     {
       
   159         // Don't try to fixup if the user is busy selecting a completion proposal
       
   160         if (const QAbstractItemView *iv = m_completer->popup()) {
       
   161             if (iv->isVisible())
       
   162                 return;
       
   163         }
       
   164 
       
   165         input = guessUrlFromString(input).toString();
       
   166     }
       
   167 
       
   168     QUrl UrlValidator::guessUrlFromString(const QString &string) const
       
   169     {
       
   170         const QString urlStr = string.trimmed();
       
   171         const QRegExp qualifiedUrl(QLatin1String("^[a-zA-Z]+\\:.*"));
       
   172 
       
   173         // Check if it looks like a qualified URL. Try parsing it and see.
       
   174         const bool hasSchema = qualifiedUrl.exactMatch(urlStr);
       
   175         if (hasSchema) {
       
   176             const QUrl url(urlStr, QUrl::TolerantMode);
       
   177             if (url.isValid())
       
   178                 return url;
       
   179         }
       
   180 
       
   181         // Might be a Qt resource
       
   182         if (string.startsWith(QLatin1String(":/")))
       
   183             return QUrl(QLatin1String("qrc") + string);
       
   184 
       
   185         // Might be a file.
       
   186         if (QFile::exists(urlStr))
       
   187             return QUrl::fromLocalFile(urlStr);
       
   188 
       
   189         // Might be a short url - try to detect the schema.
       
   190         if (!hasSchema) {
       
   191             const int dotIndex = urlStr.indexOf(QLatin1Char('.'));
       
   192             if (dotIndex != -1) {
       
   193                 const QString prefix = urlStr.left(dotIndex).toLower();
       
   194                 QString urlString;
       
   195                 if (prefix == QLatin1String("ftp"))
       
   196                     urlString += prefix;
       
   197                 else
       
   198                     urlString += QLatin1String("http");
       
   199                 urlString += QLatin1String("://");
       
   200                 urlString += urlStr;
       
   201                 const QUrl url(urlString, QUrl::TolerantMode);
       
   202                 if (url.isValid())
       
   203                     return url;
       
   204             }
       
   205         }
       
   206 
       
   207         // Fall back to QUrl's own tolerant parser.
       
   208         return QUrl(string, QUrl::TolerantMode);
       
   209     }
       
   210 }
       
   211 
       
   212 namespace qdesigner_internal {
       
   213     // TextPropertyEditor
       
   214     TextPropertyEditor::TextPropertyEditor(QWidget *parent,
       
   215                                            EmbeddingMode embeddingMode,
       
   216                                            TextPropertyValidationMode validationMode) :
       
   217         QWidget(parent),
       
   218         m_validationMode(ValidationSingleLine),
       
   219         m_updateMode(UpdateAsYouType),
       
   220         m_lineEdit(new PropertyLineEdit(this)),
       
   221         m_textEdited(false)
       
   222     {
       
   223         switch (embeddingMode) {
       
   224         case EmbeddingNone:
       
   225             break;
       
   226         case EmbeddingTreeView:
       
   227             m_lineEdit->setFrame(false);
       
   228             break;
       
   229         case EmbeddingInPlace:
       
   230             m_lineEdit->setFrame(false);
       
   231             Q_ASSERT(parent);
       
   232             m_lineEdit->setBackgroundRole(parent->backgroundRole());
       
   233             break;
       
   234         }
       
   235 
       
   236         setFocusProxy(m_lineEdit);
       
   237 
       
   238         connect(m_lineEdit,SIGNAL(editingFinished()), this, SIGNAL(editingFinished()));
       
   239         connect(m_lineEdit,SIGNAL(returnPressed()), this, SLOT(slotEditingFinished()));
       
   240         connect(m_lineEdit,SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString)));
       
   241         connect(m_lineEdit,SIGNAL(textEdited(QString)), this, SLOT(slotTextEdited()));
       
   242 
       
   243         setTextPropertyValidationMode(validationMode);
       
   244     }
       
   245 
       
   246     void TextPropertyEditor::setTextPropertyValidationMode(TextPropertyValidationMode vm) {
       
   247         m_validationMode = vm;
       
   248         m_lineEdit->setWantNewLine(multiLine(m_validationMode));
       
   249         switch (m_validationMode) {
       
   250         case ValidationStyleSheet:
       
   251             m_lineEdit->setValidator(new  StyleSheetValidator(m_lineEdit));
       
   252             m_lineEdit->setCompleter(0);
       
   253             break;
       
   254         case ValidationMultiLine:
       
   255         case ValidationRichText:
       
   256             // Set a  validator that replaces newline characters by literal "\\n".
       
   257             // While it is not possible to actually type a newline  characters,
       
   258             // it can be pasted into the line edit.
       
   259             m_lineEdit->setValidator(new ReplacementValidator(m_lineEdit, NewLineChar, EscapedNewLine));
       
   260             m_lineEdit->setCompleter(0);
       
   261             break;
       
   262         case ValidationSingleLine:
       
   263             // Set a  validator that replaces newline characters by a blank.
       
   264             m_lineEdit->setValidator(new ReplacementValidator(m_lineEdit, NewLineChar, QString(QLatin1Char(' '))));
       
   265              m_lineEdit->setCompleter(0);
       
   266             break;
       
   267         case ValidationObjectName:
       
   268             setRegExpValidator(QLatin1String("[_a-zA-Z][_a-zA-Z0-9]{,1023}"));
       
   269              m_lineEdit->setCompleter(0);
       
   270              break;
       
   271         case ValidationObjectNameScope:
       
   272             setRegExpValidator(QLatin1String("[_a-zA-Z:][_a-zA-Z0-9:]{,1023}"));
       
   273             m_lineEdit->setCompleter(0);
       
   274             break;
       
   275         case ValidationURL: {
       
   276             static QStringList urlCompletions;
       
   277             if (urlCompletions.empty()) {
       
   278                 urlCompletions.push_back(QLatin1String("about:blank"));
       
   279                 urlCompletions.push_back(QLatin1String("http://"));
       
   280                 urlCompletions.push_back(QLatin1String("http://www."));
       
   281                 urlCompletions.push_back(QLatin1String("http://qt.nokia.com/"));
       
   282                 urlCompletions.push_back(QLatin1String("file://"));
       
   283                 urlCompletions.push_back(QLatin1String("ftp://"));
       
   284                 urlCompletions.push_back(QLatin1String("data:"));
       
   285                 urlCompletions.push_back(QLatin1String("data:text/html,"));
       
   286                 urlCompletions.push_back(QLatin1String("qrc:/"));
       
   287             }
       
   288             QCompleter *completer = new QCompleter(urlCompletions, m_lineEdit);
       
   289             m_lineEdit->setCompleter(completer);
       
   290             m_lineEdit->setValidator(new UrlValidator(completer, m_lineEdit));
       
   291         }
       
   292             break;
       
   293         }
       
   294 
       
   295         setFocusProxy(m_lineEdit);
       
   296         setText(m_cachedText);
       
   297         markIntermediateState();
       
   298     }
       
   299 
       
   300     void TextPropertyEditor::setRegExpValidator(const QString &pattern)
       
   301     {
       
   302         const QRegExp regExp(pattern);
       
   303         Q_ASSERT(regExp.isValid());
       
   304         m_lineEdit->setValidator(new QRegExpValidator(regExp,m_lineEdit));
       
   305     }
       
   306 
       
   307     QString TextPropertyEditor::text() const
       
   308     {
       
   309         return m_cachedText;
       
   310     }
       
   311 
       
   312     void TextPropertyEditor::markIntermediateState()
       
   313     {
       
   314         if (m_lineEdit->hasAcceptableInput()) {
       
   315             m_lineEdit->setPalette(QPalette());
       
   316         } else {
       
   317             QPalette palette = m_lineEdit->palette();
       
   318             palette.setColor(QPalette::Active, QPalette::Text, Qt::red);
       
   319             m_lineEdit->setPalette(palette);
       
   320         }
       
   321 
       
   322     }
       
   323 
       
   324     void TextPropertyEditor::setText(const QString &text)
       
   325     {
       
   326         m_cachedText = text;
       
   327         m_lineEdit->setText(stringToEditorString(text, m_validationMode));
       
   328         markIntermediateState();
       
   329         m_textEdited = false;
       
   330     }
       
   331 
       
   332     void TextPropertyEditor::slotTextEdited()
       
   333     {
       
   334         m_textEdited = true;
       
   335     }
       
   336 
       
   337     void  TextPropertyEditor::slotTextChanged(const QString &text) {
       
   338         m_cachedText = editorStringToString(text, m_validationMode);
       
   339         markIntermediateState();
       
   340         if (m_updateMode == UpdateAsYouType)
       
   341             emit textChanged(m_cachedText);
       
   342     }
       
   343 
       
   344     void TextPropertyEditor::slotEditingFinished()
       
   345     {
       
   346         if (m_updateMode == UpdateOnFinished && m_textEdited) {
       
   347             emit textChanged(m_cachedText);
       
   348             m_textEdited = false;
       
   349         }
       
   350     }
       
   351 
       
   352     void TextPropertyEditor::selectAll() {
       
   353         m_lineEdit->selectAll();
       
   354     }
       
   355 
       
   356     void TextPropertyEditor::clear() {
       
   357         m_lineEdit->clear();
       
   358     }
       
   359 
       
   360     void TextPropertyEditor::setAlignment(Qt::Alignment alignment) {
       
   361         m_lineEdit->setAlignment(alignment);
       
   362     }
       
   363 
       
   364     void TextPropertyEditor::installEventFilter(QObject *filterObject)
       
   365     {
       
   366         if (m_lineEdit)
       
   367             m_lineEdit->installEventFilter(filterObject);
       
   368     }
       
   369 
       
   370     void TextPropertyEditor::resizeEvent ( QResizeEvent * event ) {
       
   371         m_lineEdit->resize( event->size());
       
   372     }
       
   373 
       
   374     QSize TextPropertyEditor::sizeHint () const {
       
   375         return  m_lineEdit->sizeHint ();
       
   376     }
       
   377 
       
   378     QSize TextPropertyEditor::minimumSizeHint () const {
       
   379         return  m_lineEdit->minimumSizeHint ();
       
   380     }
       
   381 
       
   382     // Returns whether newline characters are valid in validationMode.
       
   383     bool TextPropertyEditor::multiLine(TextPropertyValidationMode validationMode) {
       
   384         return validationMode == ValidationMultiLine || validationMode == ValidationStyleSheet || validationMode == ValidationRichText;
       
   385     }
       
   386 
       
   387     // Replace newline characters literal "\n"  for inline editing in mode ValidationMultiLine
       
   388     QString TextPropertyEditor::stringToEditorString(const QString &s, TextPropertyValidationMode  validationMode) {
       
   389         if (s.isEmpty() || !multiLine(validationMode))
       
   390             return s;
       
   391 
       
   392         QString rc(s);
       
   393         // protect backslashes
       
   394         rc.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
       
   395         // escape newlines
       
   396         rc.replace(NewLineChar, QString(EscapedNewLine));
       
   397         return rc;
       
   398 
       
   399     }
       
   400 
       
   401     // Replace literal "\n"  by actual new lines for inline editing in mode ValidationMultiLine
       
   402     // Note: As the properties are updated while the user types, it is important
       
   403     // that trailing slashes ('bla\') are not deleted nor ignored, else this will
       
   404     // cause jumping of the  cursor
       
   405     QString  TextPropertyEditor::editorStringToString(const QString &s, TextPropertyValidationMode  validationMode) {
       
   406         if (s.isEmpty() || !multiLine(validationMode))
       
   407             return s;
       
   408 
       
   409         QString rc(s);
       
   410         for (int pos = 0; (pos = rc.indexOf(QLatin1Char('\\'),pos)) >= 0 ; ) {
       
   411             // found an escaped character. If not a newline or at end of string, leave as is, else insert '\n'
       
   412             const int nextpos = pos + 1;
       
   413             if (nextpos  >= rc.length())  // trailing '\\'
       
   414                  break;
       
   415             // Escaped NewLine
       
   416             if (rc.at(nextpos) ==  QChar(QLatin1Char('n')))
       
   417                  rc[nextpos] =  NewLineChar;
       
   418             // Remove escape, go past escaped
       
   419             rc.remove(pos,1);
       
   420             pos++;
       
   421         }
       
   422         return rc;
       
   423     }
       
   424 
       
   425     bool TextPropertyEditor::hasAcceptableInput() const {
       
   426         return m_lineEdit->hasAcceptableInput();
       
   427     }
       
   428 }
       
   429 
       
   430 QT_END_NAMESPACE