• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

kateautoindent.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu>
00003    Copyright (C) 2004 >Anders Lund <anders@alweb.dk> (KateVarIndent class)
00004    Copyright (C) 2005 Dominik Haumann <dhdev@gmx.de> (basic support for config page)
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 version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "kateautoindent.h"
00022 #include "kateautoindent.moc"
00023 
00024 #include "kateconfig.h"
00025 #include "katehighlight.h"
00026 #include "kateglobal.h"
00027 #include "kateindentscript.h"
00028 #include "katescriptmanager.h"
00029 #include "kateview.h"
00030 #include "kateextendedattribute.h"
00031 #include "katedocument.h"
00032 
00033 #include <klocale.h>
00034 #include <kdebug.h>
00035 #include <kmenu.h>
00036 
00037 #include <cctype>
00038 
00039 const QString MODE_NONE = QLatin1String("none");
00040 const QString MODE_NORMAL = QLatin1String("normal");
00041 
00042 //BEGIN KateAutoIndent
00043 
00044 QStringList KateAutoIndent::listModes ()
00045 {
00046   QStringList l;
00047 
00048   for (int i = 0; i < modeCount(); ++i)
00049     l << modeDescription(i);
00050 
00051   return l;
00052 }
00053 
00054 int KateAutoIndent::modeCount ()
00055 {
00056   // inbuild modes + scripts
00057   return 2 +  KateGlobal::self()->scriptManager()->indentationScripts();
00058 }
00059 
00060 
00061 QString KateAutoIndent::modeName (int mode)
00062 {
00063   if (mode == 0 || mode >= modeCount ())
00064     return MODE_NONE;
00065 
00066   if (mode == 1)
00067     return MODE_NORMAL;
00068 
00069   return KateGlobal::self()->scriptManager()->indentationScriptByIndex(mode-2)->information().baseName;
00070 }
00071 
00072 QString KateAutoIndent::modeDescription (int mode)
00073 {
00074   if (mode == 0 || mode >= modeCount ())
00075     return i18nc ("Autoindent mode", "None");
00076 
00077   if (mode == 1)
00078     return i18nc ("Autoindent mode", "Normal");
00079 
00080   return KateGlobal::self()->scriptManager()->indentationScriptByIndex(mode-2)->information().name;
00081 }
00082 
00083 QString KateAutoIndent::modeRequiredStyle(int mode)
00084 {
00085   if (mode == 0 || mode == 1 || mode >= modeCount())
00086     return QString();
00087 
00088   return KateGlobal::self()->scriptManager()->indentationScriptByIndex(mode-2)->information().requiredStyle;
00089 }
00090 
00091 uint KateAutoIndent::modeNumber (const QString &name)
00092 {
00093   for (int i = 0; i < modeCount(); ++i)
00094     if (modeName(i) == name)
00095       return i;
00096 
00097   return 0;
00098 }
00099 
00100 KateAutoIndent::KateAutoIndent (KateDocument *_doc)
00101   : doc(_doc), m_normal (false), m_script (0)
00102 {
00103   // don't call updateConfig() here, document might is not ready for that....
00104 }
00105 
00106 KateAutoIndent::~KateAutoIndent ()
00107 {
00108 }
00109 
00110 QString KateAutoIndent::tabString (int length, int align) const
00111 {
00112   QString s;
00113   length = qMin (length, 256); // sanity check for large values of pos
00114   int spaces = qBound(0, align - length, 256);
00115 
00116   if (!useSpaces)
00117   {
00118     s.append (QString (length / tabWidth, '\t'));
00119     length = length % tabWidth;
00120   }
00121   s.append(QString(length + spaces, ' '));
00122 
00123   return s;
00124 }
00125 
00126 bool KateAutoIndent::doIndent(KateView *view, int line, int indentDepth, int align)
00127 {
00128   kDebug (13060) << "doIndent: line: " << line << " indentDepth: " << indentDepth << " align: " << align;
00129 
00130   KateTextLine::Ptr textline = doc->plainKateTextLine(line);
00131 
00132   // textline not found, cu
00133   if (!textline)
00134     return false;
00135 
00136   // sanity check
00137   if (indentDepth < 0)
00138     indentDepth = 0;
00139 
00140   QString indentString = tabString (indentDepth, align);
00141 
00142   int first_char = textline->firstChar();
00143 
00144   if (first_char < 0)
00145     first_char = textline->length();
00146 
00147   // remove leading whitespace, then insert the leading indentation
00148   doc->editStart (view);
00149   doc->editRemoveText (line, 0, first_char);
00150   doc->editInsertText (line, 0, indentString);
00151   doc->editEnd ();
00152 
00153   return true;
00154 }
00155 
00156 bool KateAutoIndent::doIndentRelative(KateView *view, int line, int change)
00157 {
00158   kDebug (13060) << "doIndentRelative: line: " << line << " change: " << change;
00159 
00160   KateTextLine::Ptr textline = doc->plainKateTextLine(line);
00161 
00162   // get indent width of current line
00163   int indentDepth = textline->indentDepth (tabWidth);
00164   int extraSpaces = indentDepth % indentWidth;
00165 
00166   // add change
00167   indentDepth += change;
00168 
00169   // if keepExtra is off, snap to a multiple of the indentWidth
00170   if (!keepExtra && extraSpaces > 0)
00171   {
00172     if (change < 0)
00173       indentDepth += indentWidth - extraSpaces;
00174     else
00175       indentDepth -= extraSpaces;
00176   }
00177 
00178   // do indent
00179   return doIndent(view, line, indentDepth);
00180 }
00181 
00182 void KateAutoIndent::keepIndent ( KateView *view, int line )
00183 {
00184   // no line in front, no work...
00185   if (line <= 0)
00186     return;
00187 
00188   KateTextLine::Ptr textline = doc->plainKateTextLine(line-1);
00189 
00190   // textline not found, cu
00191   if (!textline)
00192     return;
00193 
00194   doIndent (view, line, textline->indentDepth (tabWidth));
00195 }
00196 
00197 void KateAutoIndent::scriptIndent (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00198 {
00199   QPair<int, int> result = m_script->indent (view, position, typedChar, indentWidth);
00200   int newIndentInChars = result.first;
00201 
00202   // handle negative values special
00203   if (newIndentInChars < -1)
00204     return;
00205 
00206   // reuse indentation of the previous line, just like the "normal" indenter
00207   if (newIndentInChars == -1)
00208   {
00209     // keep indent of previous line
00210     keepIndent (view, position.line());
00211 
00212     return;
00213   }
00214 
00215   int align = result.second;
00216   if (align > 0)
00217     kDebug (13060) << "Align: " << align;
00218 
00219   // we got a positive or zero indent to use...
00220   doIndent (view, position.line(), newIndentInChars, align);
00221 }
00222 
00223 bool KateAutoIndent::isStyleProvided(KateIndentScript *script)
00224 {
00225   QString requiredStyle = script->information().requiredStyle;
00226   return (requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style());
00227 }
00228 
00229 void KateAutoIndent::setMode (const QString &name)
00230 {
00231   // bail out, already set correct mode...
00232   if (m_mode == name)
00233     return;
00234   
00235   // cleanup
00236   m_script = 0;
00237   m_normal = false;
00238 
00239   // first, catch easy stuff... normal mode and none, easy...
00240   if ( name.isEmpty() || name == MODE_NONE )
00241   {
00242     m_mode = MODE_NONE;
00243     return;
00244   }
00245   
00246   if ( name == MODE_NORMAL )
00247   {
00248     m_normal = true;
00249     m_mode = MODE_NORMAL;
00250     return;
00251   }
00252   
00253   // handle script indenters, if any for this name...
00254   KateIndentScript *script = KateGlobal::self()->scriptManager()->indentationScript(name);
00255   if ( script )
00256   {
00257     if (isStyleProvided(script))
00258     {
00259       m_script = script;
00260       m_mode = name;
00261       
00262       kDebug( 13060 ) << "mode: " << name << "accepted";
00263       return;
00264     }
00265     else
00266     {
00267       kWarning( 13060 ) << "mode" << name << "requires a different highlight style";
00268     }
00269   }
00270   else 
00271   {
00272     kWarning( 13060 ) << "mode" << name << "does not exist";
00273   }
00274   
00275   // Fall back to normal
00276   m_normal = true;
00277   m_mode = MODE_NORMAL;
00278 }
00279 
00280 void KateAutoIndent::checkRequiredStyle()
00281 {
00282   if (m_script)
00283   {
00284     if (!isStyleProvided(m_script))
00285     {
00286       kDebug( 13060 ) << "mode" << m_mode << "requires a different highlight style";
00287       doc->config()->setIndentationMode(MODE_NORMAL);
00288     }
00289   }
00290 }
00291 
00292 void KateAutoIndent::updateConfig ()
00293 {
00294   KateDocumentConfig *config = doc->config();
00295 
00296   useSpaces   = config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
00297   keepExtra   = config->configFlags() & KateDocumentConfig::cfKeepExtraSpaces;
00298   tabWidth    = config->tabWidth();
00299   indentWidth = config->indentationWidth();
00300 }
00301 
00302 
00303 bool KateAutoIndent::changeIndent (KateView *view, const KTextEditor::Range &range, int change)
00304 {
00305   QList<int> skippedLines;
00306 
00307   // loop over all lines given...
00308   for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00309        line <= qMin (range.end().line (), doc->lines()-1); ++line)
00310   {
00311     // don't indent empty lines
00312     if (doc->line(line).isEmpty())
00313     {
00314       skippedLines.append (line);
00315       continue;
00316     }
00317     // don't indent the last line when the cursor is on the first column
00318     if (line == range.end().line() && range.end().column() == 0)
00319     {
00320       skippedLines.append (line);
00321       continue;
00322     }
00323 
00324     doIndentRelative(view, line, change * indentWidth);
00325   }
00326 
00327   if (skippedLines.count() > range.numberOfLines())
00328   {
00329     // all lines were empty, so indent them nevertheless
00330     foreach (int line, skippedLines)
00331       doIndentRelative(view, line, change * indentWidth);
00332   }
00333 
00334   return true;
00335 }
00336 
00337 void KateAutoIndent::indent (KateView *view, const KTextEditor::Range &range)
00338 {
00339   // no script, do nothing...
00340   if (!m_script)
00341     return;
00342 
00343   doc->pushEditState();
00344   doc->editStart();
00345   // loop over all lines given...
00346   for (int line = range.start().line () < 0 ? 0 : range.start().line ();
00347        line <= qMin (range.end().line (), doc->lines()-1); ++line)
00348   {
00349     // let the script indent for us...
00350     scriptIndent (view, KTextEditor::Cursor (line, 0), QChar());
00351   }
00352   doc->editEnd ();
00353   doc->popEditState();
00354 }
00355 
00356 void KateAutoIndent::userTypedChar (KateView *view, const KTextEditor::Cursor &position, QChar typedChar)
00357 {
00358   // normal mode
00359   if (m_normal)
00360   {
00361     // only indent on new line, per default
00362     if (typedChar != '\n')
00363       return;
00364 
00365     // keep indent of previous line
00366     keepIndent (view, position.line());
00367 
00368     return;
00369   }
00370 
00371   // no script, do nothing...
00372   if (!m_script)
00373     return;
00374 
00375   // does the script allow this char as trigger?
00376   if (typedChar != '\n' && !m_script->triggerCharacters().contains(typedChar))
00377     return;
00378 
00379   // let the script indent for us...
00380   scriptIndent (view, position, typedChar);
00381 }
00382 //END KateAutoIndent
00383 
00384 //BEGIN KateViewIndentAction
00385 KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject *parent)
00386        : KActionMenu (text, parent), doc(_doc)
00387 {
00388   connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
00389 
00390 }
00391 
00392 void KateViewIndentationAction::slotAboutToShow()
00393 {
00394   QStringList modes = KateAutoIndent::listModes ();
00395 
00396   menu()->clear ();
00397   for (int z=0; z<modes.size(); ++z) {
00398     QAction *action = menu()->addAction( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&") );
00399     action->setCheckable( true );
00400     action->setData( z );
00401 
00402     QString requiredStyle = KateAutoIndent::modeRequiredStyle(z);
00403     action->setEnabled(requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style());
00404 
00405     if ( doc->config()->indentationMode() == KateAutoIndent::modeName (z) )
00406       action->setChecked( true );
00407   }
00408 
00409   disconnect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00410   connect( menu(), SIGNAL( triggered( QAction* ) ), this, SLOT( setMode( QAction* ) ) );
00411 }
00412 
00413 void KateViewIndentationAction::setMode (QAction *action)
00414 {
00415   // set new mode
00416   doc->config()->setIndentationMode(KateAutoIndent::modeName (action->data().toInt()));
00417 }
00418 //END KateViewIndentationAction
00419 
00420 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal