Kate Part itself already ships with several indenters, however it may be that users are not content or need another indenter to indent their code.
As Kate Part does not have a plugin architecture for indentation you have to hard code every indenter directly into the Kate Part sources. The code can be found in the KDE module kdelibs/kate/part/ — this HOWTO assumes you already have the code around.
The important files are kateautoindent.h and kateautoindent.cpp.
In the file kateautoindent.h there are basically two important classes:
All other indenters are directly derived from KateNormalIndent - so it is a good start to do the same: Derive a class KateMyIndent from KateNormalIndent, the real implementation is up to you, so let’s look first to register the new indenter in the Kate Part (see next section).
To make Kate Part aware of the new indenter you have to modify the base class KateAutoIndent.
Open the file kateconfig.h and add an entry to the enumeration IndentationMode (let’s call it imMyIndent). Then overwrite the function modeNumber() in your indenter, like:
virtual uint modeNumber() const { return KateDocumentConfig::imMyIndent; }
In the file kateautoindent.cpp add imMyIndent to the list in the function createIndenter, like for example
KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode) {
// ...
else if ( mode == KateDocumentConfig::imMyIndent )
return new KateMyIndent ( doc );
// ...
}
Do the same in the functions
QStringList KateAutoIndent::listModes () { ... }
QString KateAutoIndent::modeName (uint mode) { ... }
QString KateAutoIndent::modeDescription (uint mode) { ... }
uint KateAutoIndent::modeNumber (const QString &name) { ... }
Now the indenter should already appear in Settings > Configure Kate > Indentation in the combo box Indentation Mode.
Now still missing is the implementation of the new indenter.
The class declaration: class KateMyIndent : public KateNormalIndent { public: KateMyIndent (KateDocument *doc); ~KateMyIndent ();
If your indenter supports indentation when the users hits enter or return, canProcessNewLine() must return true.
virtual bool canProcessNewLine () const { return true; }
The same applies to line indentation. If the user indents a line and your indenter supports this sort of indentation canProcessLine must return true.
virtual bool canProcessLine() const { return true; }
processNewline() is called everytime a user inserts a newline-character.
virtual void processNewline (KateDocCursor &begin, bool needContinue);
processChar() is called everytime a user types a character. You’d need this for deindenting the character ‘}’ in realtime for example.
virtual void processChar (QChar c);
processLine() and processSection() are called if the user indends a line or multiple lines.
virtual void processLine (KateDocCursor &line);
virtual void processSection (const KateDocCursor &begin, const KateDocCursor &end);
virtual uint modeNumber () const { return KateDocumentConfig::imMyIndent; };
// other stuff maybe...
};
You can insert fillers (like tabs or spaces) with doc->insertText(int line, int column, const QString& text). Look into the code to see what the API provides. Indentation is tricky, to make your indenter work in all cases is hard to achieve. So happy testing.
Since KDE 3.5 you can add an additional config page. As there are already many global options available you usually would not need this, however it may be that some very indenter specific settings should be available (like insert * for doxygen comments automatically). In such situations it’s wiser to add a special config page to not clutter the available config pages even more.
To propagate your indenter has an own config page return true in kateautoindent.cpp in
bool KateAutoIndent::hasConfigPage (uint mode) { ... }
Second you have to derive a class MyIndentConfigPage from IndenterConfigPage, the base looks like this:
class MyIndentConfigPage : public IndenterConfigPage
{
Q_OBJECT
public:
MyIndentConfigPage ( QWidget *parent=0, const char *name=0 );
virtual ~MyIndentConfigPage ();
public slots:
/** Apply changes here. */
virtual void apply ();
};
As you see, you have to override the abstract virtual function apply(), which will be called if the user clicks OK (instead of Cancel). So here you can save the changes made. Finally return an instance of your config page in
IndenterConfigPage* KateAutoIndent::configPage(QWidget *parent, uint mode) { ... }
As there is not necessarily an instance of your indenter you cannot save settings directly in the indenter. The way to go right now is to get the application’s global KConfig* and save the settings into an own group like [Kate Indenter MyIndent]. The Kate prefix is important to not clash with other config groups the application embedding the Kate Part is using.
Get the config like
KConfig* config = kapp->config();
The Kate Part has a lot of options which can change during the editing session, important is that the indenter always uses the most recent settings. For example, if you use tabs to indent and change this to use spaces, the indenter should recognize this change and use spaces instead of tabs. An option can change because the user changes it
The class Kate::Document provides the signal
void Document::variableChanged( const QString& variable, const QString& value );
You can connect this signal to a slot (in the indenter’s constructor). The variable provides the changed option, and the value its new value.
Note: It is also possible to ask for variables by using the function
QString Document::variable(const QString& variable) const;
This way you can also access variables/values that are not (officially) known by KatePart.