Qt Creator Plugin HOWTO documentation first and second cut

Signed-off-by: Abhishek Patil <abhishek.patil@vcreatelogic.com>

Merge-request: 145
Reviewed-by: con <qtc-committer@nokia.com>
This commit is contained in:
Abhishek Patil
2010-06-21 15:27:59 +02:00
committed by con
parent cdbe93285b
commit abcb9358c2
155 changed files with 7841 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
/*!
\page adv-find-filter.html
\title 15.Adding "advanced" find filter
This page needs to be written still!
*/

102
doc/pluginhowto/classic.css Normal file
View File

@@ -0,0 +1,102 @@
h3.fn,span.fn
{
margin-left: 1cm;
text-indent: -1cm;
}
a:link
{
color: #004faf;
text-decoration: none
}
a:visited
{
color: #672967;
text-decoration: none
}
td.postheader
{
font-family: sans-serif
}
tr.address
{
font-family: sans-serif
}
body
{
background: #ffffff;
color: black
}
table tr.odd {
background: #f0f0f0;
color: black;
}
table tr.even {
background: #e4e4e4;
color: black;
}
table.annotated th {
padding: 3px;
text-align: left
}
table.annotated td {
padding: 3px;
}
table tr pre
{
padding-top: none;
padding-bottom: none;
padding-left: none;
padding-right: none;
border: none;
background: none
}
tr.qt-style
{
background: #a2c511;
color: black
}
body pre
{
padding: 0.2em;
border: #e7e7e7 1px solid;
background: #f1f1f1;
color: black
}
span.preprocessor, span.preprocessor a
{
color: darkblue;
}
span.comment
{
color: darkred;
font-style: italic
}
span.string,span.char
{
color: darkgreen;
}
.subtitle
{
font-size: 0.8em
}
.small-subtitle
{
font-size: 0.65em
}

View File

@@ -0,0 +1,998 @@
/*!
\page editor-type.html
\title 10. Adding editor type
At the very basic level Qt Creator is a text editor. On top of supporting editing of text files, Qt Creator also allows users
to edit UI (Qt Designer) files, QRC (Resource) files, PRO/PRI (Project) files and EXE/DLL/SO (Binary) files.
\inlineimage qtc-editor-10.png
In this chapter we will understand how to provide editors for custom file formats, specifically the HTML file format.
When we are done, we will be able to load HTML files from the local file system and view/edit them.
\inlineimage qtc-fileformat-10.png
\section1 10.1 Core Classes and Interfaces
To support a new editor type, we need to
\list
\o Implement a plugin \bold {(Core::IPlugin} implementation) class that exposes an "editor factory". Chapter 2 in this
document provides a detailed description on creating plugins by implementing the \bold {Core::IPlugin interface}.
\o Implement the editor factory, \bold {Core::IEditorFactory}, interface. This interface implementation provides
methods to help create instances of "editor" object for a specified mime-type.
\o Implement the editor, \bold {Core::IEditor}, interface. This interface implementation provides a widget that helps
edit a file type (for example: HTML, ODF etc). Editors must provide access to the "file" that it is currently being
shown or edited.
\o Implement the file, \bold{Core::IFile}, interface to help customize the loading and saving of data into disk files.
\endlist
In the following subsections, we will take a look at each of the above mentioned core interfaces.
\section2 10.1.1 The Core::IFile interface
This interface abstracts file operations from the user-interface point of view. It provides virtual methods to load and
save files, given a file name. It also helps in understanding the mime-type of file and value of certain flags like <20>modified<65>
and "read-only". The \bold{Core::IFile} interface is declared as follows in src/plugins/coreplugin/ifile.h
\code
namespace Core
{
class IFile : public QObject
{
Q_OBJECT
public:
enum ReloadBehavior { AskForReload, ReloadAll, ReloadPermissions, ReloadNone };
IFile(QObject *parent = 0) : QObject(parent) {}
virtual ~IFile() {}
virtual bool save(const QString &fileName = QString()) = 0;
virtual QString fileName() const = 0;
virtual QString defaultPath() const = 0;
virtual QString suggestedFileName() const = 0;
virtual QString mimeType() const = 0;
virtual bool isModified() const = 0;
virtual bool isReadOnly() const = 0;
virtual bool isSaveAsAllowed() const = 0;
virtual void modified(ReloadBehavior *behavior) = 0;
virtual void checkPermissions() {}
signals:
void changed();
};
} // namespace Core
\endcode
You may be wondering: "Why go for another interface called \bold {IFile} when we already have a class called \bold {QFile} that
provides the exact same functionality?" Good point. The responses to this question are listed below
\list
\o \bold {IFile} has to take care of loading contents of a filename into an editor \bold {(Core::IEditor} interface discussed
next). \bold {QFile} on the other hand simply loads contents into a \bold {QByteArray}.
\o \bold {IFile} has to emit the \bold {modified()} signal when the user edits the contents of the file in the editor, but the
actual disk-file contents have not been modified. \bold {QFile} emits the \bold {bytesWritten()} signal only when the
disk-file contents have been modified.
\o \bold {IFile} has to handle how a modified file, on the disk, is reloaded. \bold {QFile} on the other-hand doesn<73>t need to
handle this.
\endlist
We will learn more about implementing the \bold {Core::IFile} interface in a future section.
\section2 10.1.2 The Core::IEditor interface
Implementations of the \bold {Core::IEditor} interface provide editors for different types of files. It is declared as follows
in src/plugins/coreplugin/editormanager/ieditor.h.
\code
namespace Core
{
class IContext : public QObject
{
Q_OBJECT
public:
IContext(QObject *parent = 0) : QObject(parent) {}
virtual ~IContext() {}
virtual QList<int> context() const = 0;
virtual QWidget *widget() = 0;
virtual QString contextHelpId() const { return QString(); }
};
class IEditor : public IContext
{
Q_OBJECT
public:
IEditor(QObject *parent = 0) : IContext(parent) {}
virtual ~IEditor() {}
virtual bool createNew(const QString &contents = QString()) = 0;
virtual bool open(const QString &fileName = QString()) = 0;
virtual IFile *file() = 0;
virtual const char *kind() const = 0;
virtual QString displayName() const = 0;
virtual void setDisplayName(const QString &title) = 0;
virtual bool duplicateSupported() const = 0;
virtual IEditor *duplicate(QWidget *parent) = 0;
virtual QByteArray saveState() const = 0;
virtual bool restoreState(const QByteArray &state) = 0;
virtual int currentLine() const { return 0; }
virtual int currentColumn() const { return 0; }
virtual bool isTemporary() const = 0;
virtual QToolBar *toolBar() = 0;
signals:
void changed();
};
} // namespace Core
\endcode
The \bold {Core::IEditor} interface primary provides access to
\list
\o An \underline{editor widget}\bold{ (Core::IEditor::widget()} method) that Qt Creator can use to display contents of the
file being edited.
\o The file \bold {(Core::IEditor::file() } method), which is a \bold {Core::IFile} implementation, that Qt Creator
can use to trigger the loading and saving of data from disk-files.
\o An optional custom toolbar that Qt Creator can show whenever the editor becomes active.
\o The current position of the edit-cursor within the file \bold {(Core::IEditor::currentLine()} and
\bold {Core::IEditor::currentColumn())}
\o The name that needs to be displayed in the \underline{open-files combo box}.
\endlist
Take a look at the following screenshot to get a better understanding.
\inlineimage qtc-ieditor-10.png
We will understand more about implementing the \bold {Core::IEditor} interface in a future section.
\section2 10.1.3 The Core::IEditorFactory interface
Implementations of \bold {Core::IEditorFactory} interface provide methods to create instances of Core::IEditor
for a supported mime-type. It is declared as follows in src/plugins/coreplugin/editormanager/ieditorfactory.h.
\code
namespace Core
{
class IFileFactory : public QObject
{
Q_OBJECT
public:
IFileFactory(QObject *parent = 0) : QObject(parent) {}
virtual ~IFileFactory() {}
virtual QStringList mimeTypes() const = 0;
virtual QString kind() const = 0;
virtual Core::IFile *open(const QString &fileName) = 0;
};
class IEditorFactory : public Core::IFileFactory
{
Q_OBJECT
public:
IEditorFactory(QObject *parent = 0) : IFileFactory(parent) {}
virtual ~IEditorFactory() {}
virtual IEditor *createEditor(QWidget *parent) = 0;
};
} // namespace Core
\endcode
The \bold {IEditorFactory::mimeType()} method should be implemented to return the mime-type supported by the
editor for which the factory is implemented. The \bold {IEditorFactory::createEditor()} method should be
implemented to actually create a concrete editor and return the same.
\section2 10.1.4 The Core::MimeDatabase class
The \bold {Core::MimeDatabase} class keeps track of all the mime-types supported by Qt Creator. It also helps figure out
the mime-type of a given file. Take the following code for example:
\code
#include <coreplugin/mimedatabase.h>
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
Core::MimeType type1 = mdb->findByFile( QFileInfo("C:/Temp/sample.html") );
qDebug("File Type for sample.html = %s", qPrintable(type1.type()));
Core::MimeType type2 = mdb->findByFile( QFileInfo("C:/Temp/TextEdit/Main.cpp") );
qDebug("File Type for Main.cpp = %s", qPrintable(type2.type()));
Core::MimeType type3 = mdb->findByFile( QFileInfo("C:/Temp/TextEdit/TextEdit.pro") );
qDebug("File Type for TextEdit.pro = %s", qPrintable(type3.type()));
\endcode
When the above code is compiled and executed, we get the following as output.
\code
File Type for sample.html = text/plain
File Type for Main.cpp = text/x-c++src
File Type for TextEdit.pro = text/plain
\endcode
The \bold { Core::MimeDatabase } uses filename suffix, glob patterns and "magic" matchers to figure out the mime-type of
a given filename. At this point however, lets not dig into how the mime-database manages to figure out the mime-type
from a filename; it is enough if we know that mime-type discovery is possible.
The \bold Core::IEditorFactory interface, as described in the previous section, provides an editor \bold{(Core::IEditor}
implementation) for a specific mime-type. The following points help us understand how Qt Creator manages to pick the
appropriate \bold {Core::IEditorFactory} for a given filename.
\list 1
\o User selects File -> Open and chooses a file to open
\o Qt Creator uses \bold {Core::MimeDatabase} to figure out the mime-type of the selected file
\o Qt Creator runs through all \bold {Core::IEditorFactory} implementations and picks the editor-factory that
supports the mime-type evaluated in step 2
\o Qt Creator asks the selected editor factory to create an editor (Core::IEditor implementation)
\o The widget returned by \bold {Core::IEditor::widget()} is then shown in the workspace area of the mainwindow
\o The \bold {Core::IEditor::open()} method is then called to open the file selected in step 1.
\endlist
\section2 10.1.5 Adding a new mime-type
If we wanted to support a new editor type, then we need to register the mime-type supported by the new editor with
the \bold {Core::MimeDatabase}. Several mechanisms can be used to register a new mime-type. In this chapter we will
learn the simplest way to register a new mime-type from an XML file.
Suppose that we wanted to register the \bold {text/html} mime-type and associate it with \bold {*.html} filenames. We create an XML
file as and save it as \bold {text-html-mimetype.xml}.
\code
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="text/html">
<sub-class-of type="text/plain"/>
<comment>HTML File</comment>
<glob pattern="*.html"/>
</mime-type>
</mime-info>
\endcode
We then register the mime-type described in the XML file (above) using the
\bold {Core::MimeDatabase::addMimeTypes()} method. Take a look at the code snippet below
\code
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
QString errMsg;
bool success = mdb->addMimeTypes("text-html-mimetype.xml", errMsg);
\endcode
Once registered, Qt Creator will begin to map all *.html filenames to text/plain mime-type.
\section1 10.2 Providing a HTML Editor in Qt Creator
Let's implement a plugin for Qt Creator that provides support for viewing and editing HTML files. The following
screenshots show the expected results.
The user select a HTML file to open using the standard File -> Open menuitem.
\inlineimage qtc-menuitem-10.png
Upon selecting a HTML file, Qt Creator will show a custom editor as shown below.
\inlineimage qtc-customeditor-10.png
Notice that the editor has two tabs called "Preview" and "Source" at the bottom edge.
\inlineimage qtc-editortabs-10.png
Clicking on the "Source" tab shows the HTML code in a \bold { QPlainTextEdit} widget.
\inlineimage qtc-plaintextedit-10.png
Users can edit the HTML in this tab, and when he moves back to the "preview" tab the changes are reflected. Users can
continue to make use of File -> Save menu item to save changes to the HTML file.
For achieving the above we implement the following classes
\table
\header
\o Class
\o Interface\\Baseclass
\o Description
\row
\o \c{HtmlEditorWidget}
\o \bold {QTabWidget}
\o Provides a tab widget with two tabs, one for showing the
HTML preview and other for showing the HTML code.
\row
\o \c{HtmlFile}
\o \bold {Core::IFile}
\o Implements the \bold {IFile} interface for the file shown in
\bold {HtmlEditorWidget}.
\row
\o \c {HtmlEditor}
\o \bold {Core::IEditor}
\o Implements the \bold{IEditor} interface for managing the
\bold{HtmlEditorWidget} and hooking up an \bold{IFile} with it.
\row
\o \c {HtmlEditorFactory}
\o \bold {Core::IEditorFactory}
\o Implements the \bold{IEditorFactory} interface for creating
instances of \bold{IEditor} for the "text/html" mime-type.
\row
\o \c {HtmlEditorPlugin}
\o \bold {Core::IPlugin }
\o Implements the \bold{IPlugin} interface for hooking all of the
above into Qt Creator.
\endtable
For every html file there is an instance each of \bold{HtmlEditorWidget},\bold{HtmlFile} and \bold{HtmlEditor} handling the
loading, editing and saving functionality. In our implementation we provide mechanisms to help establish association
between instances of the above classes that handle the same file.
\section2 10.2.1 Implementing the HTML Editor Widget
By default, Qt Creator uses a plain-text editor widget to display the contents of the HTML file being edited. We would
like to offer a tabbed widget as editor. One of the tabs shows the HTML preview, the other shows the HTML code. The
class \bold {HtmlEditorWidget} is our editor; and it is declared as follows.
\code
struct HtmlEditorWidgetData;
class HtmlEditorWidget : public QTabWidget
{
Q_OBJECT
public:
HtmlEditorWidget(QWidget* parent = 0);
~HtmlEditorWidget();
void setContent(const QByteArray& ba, const QString& path=QString());
QByteArray content() const;
QString title() const;
protected slots:
void slotCurrentTabChanged(int tab);
void slotContentModified();
signals:
void contentModified();
void titleChanged(const QString&);
private:
HtmlEditorWidgetData* d;
};
\endcode
The constructor basically creates two different views from \bold{QWebView} and \bold {QPlainTextEdit} and adds them as tabs
at the bottom position of the tab widget. We will learn about the signal-slot connections later.
\code
HtmlEditorWidget::HtmlEditorWidget(QWidget* parent):QTabWidget(parent)
{
d = new HtmlEditorWidgetData;
d->webView = new QWebView;
d->textEdit = new QPlainTextEdit;
addTab(d->webView, "Preview");
addTab(d->textEdit, "Source");
setTabPosition(QTabWidget::South);
setTabShape(QTabWidget::Triangular);
d->textEdit->setFont( QFont("Courier", 12) );
connect(this, SIGNAL(currentChanged(int)),this, SLOT(slotCurrentTabChanged(int)));
connect(d->textEdit, SIGNAL(textChanged()),this, SLOT(slotContentModified()));
connect(d->webView, SIGNAL(titleChanged(QString)),this, SIGNAL(titleChanged(QString)));
d->modified = false;
}
\endcode
The destructor does nothing other than deleting the private \bold{"d"} object.
\code
HtmlEditorWidget::~HtmlEditorWidget()
{
delete d;
}
\endcode
The setContent method simply sets the contents of html file to \bold{webView} and \bold {textEdit}.
\code
void HtmlEditorWidget::setContent(const QByteArray& ba, const QString& path)
{
if(path.isEmpty())
d->webView->setHtml(ba);
else
d->webView->setHtml(ba, "file:///" + path);
d->textEdit->setPlainText(ba);
d->modified = false;
d->path = path;
}
\endcode
The \bold {content} method returns the contents of the \bold {textEdit}.
\code
QByteArray HtmlEditorWidget::content() const
{
QString htmlText = d->textEdit->toPlainText();
return htmlText.toAscii();
}
\endcode
The \bold {title()} method returns the title from the webView. The string returned from this method will be used in the
open-file combo box.
\inlineimage qtc-title-10.png
\code
QString HtmlEditorWidget::title() const
{
return d->webView->title();
}
\endcode
The following connection made in the constructor of \bold {HtmlEditorWidget} makes sure that when the user moves from
"source" to "preview" tab, the HTML content viewed in the preview tab is updated.
\code
connect(this, SIGNAL(currentChanged(int)),this, SLOT(slotCurrentTabChanged(int)));
void HtmlEditorWidget::slotCurrentTabChanged(int tab)
{
if(tab == 0 && d->modified)
setContent( content(), d->path );
}
\endcode
Following connection makes sure that \bold {setContentModified()} slot is called whenever user edits the html source.
The slot \bold {setContentModified()} simply sets modified to true and emits the signal "contentModified" We will
know the usability of this signal later in the section while understanding \bold {HtmlFile} class.
\code
connect(d->textEdit, SIGNAL(textChanged()), this, SLOT(slotContentModified()));
void HtmlEditorWidget::slotContentModified()
{
d->modified = true;
emit contentModified();
}
\endcode
Following connection simply emits \bold {titleChanged()} signal on title change of \bold {webView}. We will know more about
this signal later.
\code
connect(d->webView, SIGNAL(titleChanged(QString)),this, SIGNAL(titleChanged(QString)));
\endcode
\section2 10.2.2 Implementing the Core::IFile interface
We implement the \bold {Core::IFile} interface in the \bold {HtmlFile} class. Apart from implementing the pure virtual
functions from \bold {IFile} (introduced in section 5.1.1); the \bold {HtmlFile} class has methods to get/set the modified flag
which indicates the modification status of the file contents.
\code
struct HtmlFileData;
class HtmlFile : public Core::IFile
{
Q_OBJECT
public:
HtmlFile(HtmlEditor* editor, HtmlEditorWidget* editorWidget);
~HtmlFile();
void setModified(bool val=true);
// Declare all the virtual functions from IFile here..
protected slots:
void modified() { setModified(true); }
private:
HtmlFileData* d;
};
struct HtmlFileData
{
HtmlFileData(): mimeType(HtmlEditorConstants::C_HTMLEDITOR_MIMETYPE),editorWidget(0), editor(0), modified(false) { }
const QString mimeType;
HtmlEditorWidget* editorWidget;
HtmlEditor* editor;
QString fileName;
bool modified;
};
\endcode
In the constructor implementation is simple. It establishes the association between an instance of \bold {HtmlFile} with the
corresponding \bold {HtmlEditor} and the \bold {HtmlEditorWidget} instances.
\code
HtmlFile::HtmlFile(HtmlEditor* editor, HtmlEditorWidget* editorWidget): Core::IFile(editor)
{
d = new HtmlFileData;
d->editor = editor;
d->editorWidget = editorWidget;
}
\endcode
The destructor does nothing other than deleting the private \bold {"d"} object.
\code
HtmlFile::~HtmlFile()
{
delete d;
}
\endcode
The \bold {setModified()} function stores the modification flag and emits the \bold {changed()} signal.
\code
void HtmlFile::setModified(bool val)
{
if(d->modified == val)
return;
d->modified = val;
emit changed();
}
bool HtmlFile::isModified() const
{
return d->modified;
}
\endcode
Returns the mime-type handled by this class.
\code
QString HtmlFile::mimeType() const
{
return d->mimeType;
}
\endcode
The save method is called when file->save action (Ctrl+s) is triggered. This saves the contents of \bold {HtmlEditorWidget}
(the contents shown by plain text editor) in the file as shown below. The modified flag is set to false after the contents
are saved into the file.
\code
bool HtmlFile::save(const QString &fileName)
{
QFile file(fileName);
if(file.open(QFile::WriteOnly))
{
d->fileName = fileName;
QByteArray content = d->editorWidget->content();
file.write(content);
setModified(false);
return true;
}
return false;
}
\endcode
The \bold {open} method is called when file->open action is triggered. This opens the file and calls the setContent()
method of \bold {HtmlEditorWidget}. The display name is set to the title of the html file.
\code
bool HtmlFile::open(const QString &fileName)
{
QFile file(fileName);
if(file.open(QFile::ReadOnly))
{
d->fileName = fileName;
QString path = QFileInfo(fileName).absolutePath();
d->editorWidget->setContent(file.readAll(), path);
d->editor->setDisplayName(d->editorWidget->title());
return true;
}
return false;
}
\endcode
The following methods implement the "filename" property.
\code
void HtmlFile::setFilename(const QString& filename)
{
d->fileName = filename;
}
QString HtmlFile::fileName() const
{
return d->fileName;
}
\endcode
We implement the \bold {defaultPath()},\bold { suggestedFileName()},\bold {fileFilter()} and fileExtension()
methods to do nothing at the moment.
\code
QString HtmlFile::defaultPath() const
{
return QString();
}
QString HtmlFile::suggestedFileName() const
{
return QString();
}
QString HtmlFile::fileFilter() const
{
return QString();
}
QString HtmlFile::fileExtension() const
{
return QString();
}
\endcode
Since we want to edit the file, we return false in \bold {isReadOnly()} method and true from \bold {isSaveAsAllowed()}
method.
\code
bool HtmlFile::isReadOnly() const
{
return false;
}
bool HtmlFile::isSaveAsAllowed() const
{
return true;
}
\endcode
The \bold {modified()} function has to be implemented to customize the way in which the Html editor should handle
reloading of a modified file. This function is called if a html-file was modified outside of Qt Creator, while it is being
edited. For now we do nothing.
\code
void HtmlFile::modified(ReloadBehavior* behavior)
{
Q_UNUSED(behavior);
}
\endcode
\section2 10.2.3 Implementing the Core::IEditor interface
The \bold {HtmlEditor} class implements \bold {IEditor} interface to provide an editor widget for html (*.html) files and
associate a \bold {HtmlFile} instance with it.
\code
#include <coreplugin/editormanager/ieditor.h>
struct HtmlEditorData;
class HtmlEditor : public Core::IEditor
{
Q_OBJECT
public:
HtmlEditor(HtmlEditorWidget* editorWidget);
~HtmlEditor();
bool createNew(const QString& contents = QString());
QString displayName() const;
IEditor* duplicate(QWidget* parent);
bool duplicateSupported() const;
Core::IFile* file();
bool isTemporary() const;
const char* kind() const;
bool open(const QString& fileName = QString()) ;
bool restoreState(const QByteArray& state);
QByteArray saveState() const;
void setDisplayName(const QString &title);
QToolBar* toolBar();
// From Core::IContext
QWidget* widget();
QList<int> context() const;
protected slots:
void slotTitleChanged(const QString& title)
{ setDisplayName(title); }
private:
HtmlEditorData* d;
};
\endcode
\bold {HtmlEditorData} holds object of \bold {HtmlEditorWidget} and \bold {HtmlFile}. displayName is used as visual
description of the document.
\code
struct HtmlEditorData
{
HtmlEditorData() : editorWidget(0), file(0) { }
HtmlEditorWidget* editorWidget;
QString displayName;
HtmlFile* file;
QList<int> context;
};
\endcode
The constructor creates initializes itself on an \bold {HtmlEditorWidget}. It creates an \bold {HtmlFile} instance so that its
association with \bold {HtmlEditor} and widget is set.
\code
HtmlEditor::HtmlEditor(HtmlEditorWidget* editorWidget): Core::IEditor(editorWidget)
{
d = new HtmlEditorData;
d->editorWidget = editorWidget;
d->file = new HtmlFile(this, editorWidget);
Core::UniqueIDManager* uidm = Core::UniqueIDManager::instance();
d->context << uidm->uniqueIdentifier(HtmlEditorConstants::C_HTMLEDITOR);
connect(d->editorWidget, SIGNAL(contentModified()),d->file,SLOT(modified()));
connect(d->editorWidget, SIGNAL(titleChanged(QString)),this,SLOT(slotTitleChanged(QString)));
connect(d->editorWidget, SIGNAL(contentModified()),this,SIGNAL(changed()));
}
\endcode
The destructor does nothing other than deleting the private \bold {'d'} object.
\code
HtmlEditor::~HtmlEditor()
{
delete d;
}
\endcode
The following functions are self explanatory.
\code
QWidget* HtmlEditor::widget()
{
return d->editorWidget;
}
QList<int> HtmlEditor::context() const
{
return d->context;
}
Core::IFile* HtmlEditor::file()
{
return d->file;
}
\endcode
The \bold {createNew()} method is implemented to reset the contents of the \bold {HtmlEditorWidget} and \bold {HtmlFile}
objects. For now we ignore the contents parameter.
\code
bool HtmlEditor::createNew(const QString& contents)
{
Q_UNUSED(contents);
d->editorWidget->setContent(QByteArray());
d->file->setFilename(QString());
return true;
}
\endcode
The \bold {open()} method causes the \bold {HtmlFile} to open a given filename. It is assumed that the filename is a HTML
filename.
\code
bool HtmlEditor::open(const QString &fileName)
{
return d->file->open(fileName);
}
\endcode
The following method returns the \bold {'kind'} of the editor.
\code
namespace HtmlEditorConstants
{
const char* const C_HTMLEDITOR_MIMETYPE = "text/html";
const char* const C_HTMLEDITOR = "HTML Editor";
}
const char* HtmlEditor::kind() const
{
return HtmlEditorConstants::C_HTMLEDITOR;
}
\endcode
The string returned by \bold {displayName} is used in the open-file combobox. The following methods help set and fetch the
display name.
\code
QString HtmlEditor::displayName() const
{
return d->displayName;
}
void HtmlEditor::setDisplayName(const QString& title)
{
if(d->displayName == title)
return;
d->displayName = title;
emit changed();
}
\endcode
We implement the following methods to do nothing in this example.
\code
bool HtmlEditor::duplicateSupported() const
{
return false;
}
Core::IEditor* HtmlEditor::duplicate(QWidget* parent)
{
Q_UNUSED(parent);
return 0;
}
QByteArray HtmlEditor::saveState() const
{
return QByteArray();
}
bool HtmlEditor::restoreState(const QByteArray& state)
{
Q_UNUSED(state);
return false;
}
QToolBar* HtmlEditor::toolBar()
{
return 0;
}
bool HtmlEditor::isTemporary() const
{
return false;
}
\endcode
\section2 10.2.4 Implementing the Core::IEditorFactory interface
The \bold HtmlEditorFactory class implements the \bold Core::IEditorFactory interface; and is declared as follows.
\code
#include <coreplugin/editormanager/ieditorfactory.h>
struct HtmlEditorFactoryData;
class HtmlEditorFactory : public Core::IEditorFactory
{
Q_OBJECT
public:
HtmlEditorFactory(HtmlEditorPlugin* owner);
~HtmlEditorFactory();
QStringList mimeTypes() const;
QString kind() const;
Core::IEditor* createEditor(QWidget* parent);
Core::IFile* open(const QString &fileName);
private:
HtmlEditorFactoryData* d;
};
\endcode
\bold {HtmlEditorFactoryData} structure holds the private data of the \bold {HtmlEditorFactory} class. Notice that the
constructor initializes the mime-types to \bold {HtmlEditorConstants::C_HTMLEDITOR_MYMETYPE}. It also
initializes the \bold {kind} of the editor. This kind should be same as \bold{kind} of \bold {HtmlEditor}.
\code
namespace HtmlEditorConstants
{
const char* const C_HTMLEDITOR_MIMETYPE = "text/html";
const char* const C_HTMLEDITOR = "HTML Editor";
}
struct HtmlEditorFactoryData
{
HtmlEditorFactoryData(): kind(HtmlEditorConstants::C_HTMLEDITOR)
{
mimeTypes << QString(HtmlEditorConstants::C_HTMLEDITOR_MIMETYPE);
}
QString kind;
QStringList mimeTypes;
};
\endcode
The following methods are self-explanatory.
\code
HtmlEditorFactory::HtmlEditorFactory(HtmlEditorPlugin* owner):Core::IEditorFactory(owner)
{
d = new HtmlEditorFactoryData;
}
HtmlEditorFactory::~HtmlEditorFactory()
{
delete d;
}
QStringList HtmlEditorFactory::mimeTypes() const
{
return d->mimeTypes;
}
QString HtmlEditorFactory::kind() const
{
return d->kind;
}
\endcode
The \bold {open()} method should be implemented to return the IFile that is currently handling the given filename. If there
are none, then a new editor for the file is created and it's IFile is returned. To fully understand this process take a
look at the implementation of \bold {Core::EditorManager::openEditor()} method.
\code
Core::IFile* HtmlEditorFactory::open(const QString& fileName)
{
Core::EditorManager* em = Core::EditorManager::instance();
Core::IEditor* iface = em->openEditor(fileName, d->kind);
return iface ? iface->file() : 0;
}
\endcode
This method creates and returns an instance of the \bold {HtmlEditor} class.
\code
Core::IEditor* HtmlEditorFactory::createEditor(QWidget* parent)
{
HtmlEditorWidget* editorWidget = new HtmlEditorWidget(parent);
return new HtmlEditor(editorWidget);
}
\endcode
\section2 10.2.5 Implementing the plugin
We implement the \bold {HtmlEditorPlugin} plugin class using the same means described in Chapter 2. The only change is
the \bold {initialize()} method implementation.
\code
bool HtmlEditorPlugin::initialize(const QStringList &arguments, QString* errMsg)
{
Q_UNUSED(arguments);
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
if(!mdb->addMimeTypes("text-html-mimetype.xml", errMsg))
return false;
addAutoReleasedObject(new HtmlEditorFactory(this));
return true;
}
\endcode
When the plugin is compiled and Qt Creator is (re)started; we will be able to load HTML files using the newly
implemented editor plugin.
\inlineimage qtc-htmleditor-10.png
*/

View File

@@ -0,0 +1,549 @@
/*!
\page find-filter.html
\title 9. Find Filter
Qt Creator's find dialog box allows users to search for a text or regular expression in opened projects and unloaded disk
files. Clicking on "Edit -> Find/Replace -> Find Dialog" shows the find dialog box.
\inlineimage qtc-finddialog-9.png
In the find dialog box the "scope" and "configuration widget" are extensible. It is possible to add more items to the
scope combo box and against every item it is possible to provide a configuration widget that needs to be shown below.
Each item in the "scope" combo box is called "find filter" in Qt Creator lingo.
\section1 9.1 Find::IFindFilter interface
The \underline {find} plugin in Qt Creator exposes an interface called Find::IFindFilter. The interface is declared as follows in
the src/plugins/find/ifindfilter.h header.
\code
class FIND_EXPORT IFindFilter : public QObject
{
Q_OBJECT
public:
virtual ~IFindFilter() {}
virtual QString id() const = 0;
virtual QString name() const = 0;
virtual bool isEnabled() const = 0;
virtual QKeySequence defaultShortcut() const = 0;
virtual void findAll(const QString &txt, QTextDocument::FindFlags findFlags) = 0;
virtual QWidget *createConfigWidget() { return 0; }
virtual void writeSettings(QSettings *settings) { Q_UNUSED(settings); }
virtual void readSettings(QSettings *settings) { Q_UNUSED(settings); }
signals:
void changed();
};
\endcode
By implementing the \bold {IFindFilter} interface and adding an instance of it to the object pool, we will be able to add a
new find-filter; which means another entry in the "scope" combo box of the find dialog box.
\section1 9.2 Providing a custom filter
Suppose that we wanted to provide a custom filter that will allow us to look for files in the loaded projects that include a
given header. In the following steps we will understand how to write a find filter for this.
\section2 Step 1: Declaring the HeaderFilter class
We first begin by declaring a class called HeaderFilter that implements the Find::IFindFilter interface. The class
definition is as follows.
\code
#include <find/ifindfilter.h>
class HeaderFilter : public Find::IFindFilter
{
Q_OBJECT
public:
HeaderFilter();
~HeaderFilter();
QString id() const;
QString name() const;
bool isEnabled() const;
QKeySequence defaultShortcut() const;
void findAll(const QString &txt,
QTextDocument::FindFlags findFlags);
QWidget *createConfigWidget();
private:
HeaderFilterData *d;
};
\endcode
\section2 Step 2: Implementing the HeaderFilter class
The constructor and destructors are currently empty. We will fill in more code as we progress with our understanding of
the \bold {IFindFilter} interface.
\code
struct HeaderFilterData
{
};
HeaderFilter:: HeaderFilter()
{
d = new HeaderFilterData;
}
HeaderFilter::~ HeaderFilter()
{
delete d;
}
\endcode
The \bold{id()} method should be implemented to return a unique identifier for the find filter.
\code
QString HeaderFilter::id() const
{
return "HeaderFilter";
}
\endcode
The \bold {name()} method should be implemented to return the string that gets shown in the "scope" combo box of the find
dialog box.
\code
QString HeaderFilter::name() const
{
return tr("Header Filter");
}
\endcode
The \bold {isEnabled()} method should be implemented to return whether the find filter is enabled or not. In our case we
would like to show the filter enabled if projects are loaded in Qt Creator, false otherwise. To fully understand the
implementation of the function, we must first study the \bold {ProjectExplorer} namespace. For now let's just return true
and revisit the function after learning about the \bold {ProjectExplorer} namespace.
\code
bool HeaderFilter::isEnabled() const
{
return true;
}
\endcode
The \bold{defaultShortcut()} method should be implemented to return a key-sequence that the user can use to launch
the find dialog box with the "header filter" selected in "cope". In our implementation we return an invalid keysequence.
\code
QKeySequence HeaderFilter::defaultShortcut() const
{
return QKeySequence();
}
\endcode
The \bold {createConfigWidget()} method should be implemented to return a configuration widget that gets shown at
the bottom edge of the find dialog box.
\inlineimage qtc-configwidget-9.png
For our header-filter; let's return a simple QLabel because we don't want to provide any configuration options.
\code
QWidget *HeaderFilter::createConfigWidget()
{
return (new QLabel("This is a header filter"));
}
\endcode
The \bold{findAll()} method should be implemented to perform the actual "find" or "search" operation. We need to
understand few key classes in the \bold {ProjectExplorer},\bold {TextEditor},\bold {Find }and \bold {Core::Utils} namespace before
attempting to implement the filter. For now implement the method to do nothing.
\code
void HeaderFilter::findAll(const QString &text,QTextDocument::FindFlags findFlags)
{
// Do nothing
}
\endcode
\section2 Step 3: Implementing the "header-filter" plugin.
We implement the header-filter plugin very similar to the \bold {DoNothingPlugin} class described in Chapter 2. Here we
only look at the implementation if the \bold {initialize()} method.
\code
bool HeaderFilterPlugin::initialize(const QStringList& args,QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
addAutoReleasedObject(new HeaderFilter);
return true;
}
\endcode
\section2 Step 4: Testing the plugin
Upon compiling the plugin and restarting Qt Creator, we can notice the "Header Filter" item in the scope combo box of
the find dialog box.
\inlineimage qtc-testplugin-9.png
Currently no "finding" or "searching" is done by our plugin because we have not yet implemented the
\bold {HeaderFilter::findAll()} method.
\section1 9.3 The ProjectExplorer namespace
The \bold {ProjectExplorer} namespace comprises of classes and interfaces that make up the project management
system in Qt Creator. This namespace is provided by the \bold {projectexplorer} plugin. Support for project types are provided
by plugins. For example
\list
\o \bold {cmakeprojectmanager} plugin provides implementations of interfaces in \bold {ProjectExplorer} namespace for
supporting CMake projects
\o \bold {qt4projectmanager} plugin provides support for Qt 4 projects
\o \bold {qmlprojectmanager} plugin provides support for QML projects
\endlist
\table
\header
\o Class/Interface
\o Description
\row
\o \c {ProjectExplorer::IProjectManager}
\o This interface must be implemented to provide support for a kind of
project. Implementations of this interface help load projects into Qt
Creator.
\row
\o \c {ProjectExplorer::Project}
\o This interface describes a project in terms of
\list
\o A file (Core::IFile) that describes the project.
\o A Boolean flag describing whether the project builds an
application or library
\o Build steps (ProjectExplorer:: BuildStep) that
need to be performed in order to build and clean the project
\o Run configurations that need to be used for running the
project
\o The environment within which the project needs to be run
\o The root node in the project explorer panel
\o Include paths and macros to be used while building the project
\endlist
\row
\o \c {ProjectManager::}
\o This class is the implementation of the Core::IPlugin interface for the
\row
\o \c {ProjectExplorerPlugin}
\o project explorer plugin. Through this class we can
\list
\o gain access to all the open projects
\o gain access the current project
\o gain access to the currently selected node (file/folder) in the
project explorer panel
\o gain access to the build manager
\bold {(ProjectManager::BuildManager)}
\endlist
\endtable
\section2 9.3.1 Getting a list of open-projects
Using the \bold {ProjectManager::ProjectExplorerPlugin} class we can catch hold of all the open-projects in Qt
Creator. The following code snippet shows how to do that
\code
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/projectexplorer.h>
// Catch hold of the plugin-manager
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
// Look for the ProjectExplorerPlugin object
ProjectExplorer::ProjectExplorerPlugin* projectExplorerPlugin
= pm->getObject<ProjectExplorer::ProjectExplorerPlugin>();
// Fetch a list of all open projects
QList<ProjectExplorer::Project*> projects = d->projectPlugin->session()->projects();
\endcode
From the projects list we can gain access to the project file and all the other (source, header, resource etc) files in the
project. To gain access to the project file(s) we can
\code
Q_FOREACH(ProjectExplorer::Project* project, projects)
{
QString name = project->name();
Core::IFile* projectFile = project->file();
// Do something with the above. For example:
qDebug("Project %s has project file as %s",
qPrintable(name),
qPrintable(projectFile->fileName()));
}
\endcode
While the above code snippet helps with fetching the project file (CMakeLists.txt, .pro etc..), it doesn't help us fetch all
the files associated with the project.
\section2 9.3.2 Getting a list of files
From the projects list we can get a string-list of all file names associated with the project using the following code
snippet.
\code
// Make a list of files in each project
QStringList files;
Q_FOREACH(ProjectManager::Project* project, projects)
files += project->files(Project::AllFiles);
\endcode
\section2 9.3.3 Enabling the HeaderFilter conditionally
Ideally the header-filter should be enabled only if there is atleast one open project. To make this happen, we upgrade
the HeaderFilter implementation as follows
\code
struct HeaderFilterData
{
ProjectExplorer::ProjectExplorerPlugin* projectExplorer()
{
if(m_projectPlugin)
return m_projectPlugin;
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
m_projectPlugin = pm->getObject<ProjectExplorer::ProjectExplorerPlugin>();
return m_projectPlugin;
}
private:
ProjectExplorer::ProjectExplorerPlugin* m_projectPlugin;
};
bool HeaderFilter::isEnabled() const
{
QList<ProjectExplorer::Project*> projects = d->projectExplorer()->session()->projects();
if(projects.count())
return true;
return false;
}
\endcode
\section1 9.4 Searching in files
In the previous section we understood how to gain access to the file names of all files associated with open projects. We
are now in a position to search within files. Let's begin the implementation of the \bold {HeaderFilter::findAll()}
method, and understand more concepts as we progress.
\code
void HeaderFilter::findAll(const QString &text,QTextDocument::FindFlags findFlags)
{
// Fetch a list of all open projects
QList<Project*> projects = d->projectPlugin->session()->projects();
// Make a list of files in each project
QStringList files;
Q_FOREACH(Project* project, projects)
files += project->files(Project::AllFiles);
// Remove duplicates
files.removeDuplicates();
// Search for text in "files"
// ...
}
\endcode
The number of files that need to be searched can be varying. It may be as little as 1 and as high as 1000 or more! Hence
searching for something in files within the \bold {findAll()} method is a bad idea. If the \bold {findAll()} method takes too long
then it may cause Qt Creator to appear frozen until searching is finished.
The solution to this is
\list
\o We make use of QtConcurrent and spawn multiple threads to perform the actual searching
\o We initialize a QFutureWatcher on the QFuture returned by QtConcurrent to emit signals as and when
search results are generated
\o We catch the signals generated by QFutureWatcher and list search results as they come
\endlist
Qt Creator's core utils library provides a readymade function called \bold {findInFiles()} that looks for a string within a list
of files and returns a \bold {QFuture} to monitor search results. The function is declared as follows in src/libs/utils/filesearch.h
\code
namespace Core {
namespace Utils {
class FileSearchResult
{
public:
QString fileName;
int lineNumber;
QString matchingLine;
int matchStart;
int matchLength;
};
QFuture<FileSearchResult> findInFiles(const QString &searchTerm,const QStringList &files,
QTextDocument::FindFlags flags
);
QFuture<FileSearchResult> findInFilesRegExp(const QString &searchTerm,const QStringList &files,
QTextDocument::FindFlags flags
);
} // namespace Utils
} // namespace Core
\endcode
Lets now continue with the implementation of \bold {HeaderFilter::findAll()} by making use of the
\bold {findInFiles()} method.
\code
struct HeaderFilterData
{
QFutureWatcher<FileSearchResult> watcher;
ProjectExplorer::ProjectExplorerPlugin* projectExplorer() {
...
}
};
HeaderFilter::HeaderFilter()
{
d->watcher.setPendingResultsLimit(1);
connect(&d->watcher, SIGNAL(resultReadyAt(int)),
this, SLOT(displayResult(int)));
}
void HeaderFilter::findAll(const QString &text,QTextDocument::FindFlags findFlags)
{
....
// Remove duplicates
files.removeDuplicates();
// Begin searching
QString includeline = "#include <" + text + ">";
Find::SearchResult *result = d->searchResultWindow()->startNewSearch();
// Let the watcher monitor the search results
d->watcher.setFuture(QFuture<FileSearchResult>());
}
void HeaderFilter::displayResult(int index)
{
// TODO
}
\endcode
In the revised \bold {findAll()} implementation we make use of the \bold {findInFiles()} method to spawn multiple
background threads to do all the finding. As search results are generated, the \bold {displayResult(int)} slot is called. In
this slot we can now show search results to the user.
\section1 9.5 Showing search results
The "find" plugin provides an object of class \bold {Find::SearchResultWindow}. This class provides access to the widget
that displays search results.
\inlineimage qtc-search-9.png
We would like to show our search results in the "search result window" as well. To do so, we modify the
\bold {HeaderFilter} code as follows
\code
#include <find/searchresultwindow.h>
struct HeaderFilterData
{
// Method to search and return the search window
Find::SearchResultWindow* searchResultWindow()
{
if(m_searchResultWindow)
return m_searchResultWindow;
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
m_searchResultWindow = pm->getObject<Find::SearchResultWindow>();
return m_searchResultWindow;
}
private:
Find::SearchResultWindow *m_searchResultWindow;
};
HeaderFilter::HeaderFilter()
{
// displayResult(int) is called when every a new
// search result is generated
connect(&d->watcher, SIGNAL(resultReadyAt(int)),this, SLOT(displayResult(int)));
}
void HeaderFilter::findAll(const QString &text,QTextDocument::FindFlags findFlags)
{
...
//When result gets activated it invokes the openEditor function
connect(result, SIGNAL(activated(Find::SearchResultItem)),
this, SLOT(openEditor(Find::SearchResultItem)));
d->searchResultWindow()->popup(true);
d->watcher.setFuture(Utils::findInFiles(includeline, files, findFlags));
}
void HeaderFilter::displayResult(int index)
{
FileSearchResult result = d->watcher.future().resultAt(index);
d->searchResultWindow()->addResult(result.fileName,
result.lineNumber,
result.matchingLine,
result.matchStart,
result.matchLength);
}
void HeaderFilter::openEditor(const QString &fileName, int line, int column)
{
// TODO
}
\endcode
Whenever the user double clicks on the search results, the \bold {openEditor()} method is called. In that method we should
have Qt Creator open the corresponding file and mark the searched text.
\section1 9.6 Opening the searched files
Without going into too many details about the texteditor plugin, lets just take a look at the function that loads a named
file for us and moves the cursor to a specified location. The \bold {BaseTextEditor} class in the \bold {TextEditor} namespace
provides a static method called \bold {openEditorAt()} that serves our purpose.
\code
namespace TextEditor {
class BaseTextEditor : public QPlainTextEdit
{
public:
....
static ITextEditor *openEditorAt(const QString &fileName,int line, int column = 0,
const QString &editorKind);
....
};
} // TextEditor namespace
\endcode
We now update the \bold {HeaderFilter::openEditor()} slot as follows
\code
#include <texteditor/basetexteditor.h>
void HeaderFilter::openEditor(const Find::SearchResultItem &item)
{
TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.index);
}
\endcode
With this the \bold {HeaderFilter} is now complete.
*/

View File

@@ -0,0 +1,6 @@
/*!
\page find-support.html
\title 9.Find support
This page needs to be written still!
*/

View File

@@ -0,0 +1,327 @@
/*!
\page first-plugin.html
itle 2. First Plugin
The best way to learn about writing Qt Creator plugins is to actually start by writing the very first plugin.
There are two ways for writing plugins for Qt Creator:
\list
\o \l {Writing plugin inside the source tree.}
\o \l {Writing plugin outside the source tree.}
\endlist
For Writing plugin in the any of the above mentioned way Qt Creator source scould have been compiled.
Lets keep our goals very simple for this one. We are going to provide a plugin for Qt Creator that does nothing. The purpose behind
this "Do nothing" plugin is to discover the basic classes in Qt Creator and to feel happy when our plugin shows up in the
"plugin list"
\inlineimage qtc-aboutplugin-2.png
arget {Writing plugin inside the source tree.}
\section1 2.1 Create a plugin project in Qt Creator
Create a folder called DoNothing in $$QT_CREATOR_ROOT/src/plugins directory. The entire source code of
the plugin will be put into this directory.
\bold {Note:}\underline {It may be possible to write and build Qt Creator plugins outside of its source tree, but it is
much easier to write plugins within the source tree}.
Lets first create the \c {DoNothing.pro} file with the following contents
\code
TEMPLATE = lib
TARGET = DoNothing
PROVIDER = FooCompanyInc
include(../../qtcreatorplugin.pri)
include(../../plugins/coreplugin/coreplugin.pri)
HEADERS += DoNothingPlugin.h
SOURCES += DoNothingPlugin.cpp
OTHER_FILES += DoNothing.pluginspec
\endcode
The project file configures the following aspects of the plugin:
\list 1
\o Declares that DoNothing is a library. The output will be DoNothing.dll
\o Configures DoNothing to make use of settings defined in qtcreatorplugin.pri
\o Overrides the default destination directory to $$IDE_PLUGIN_PATH/FooCompanyInc. By default the value
will be to $$IDE_PLUGIN_PATH/Nokia
\o Configures DoNothing to make use of settings defined in coreplugin.pri
\o Provides information about the .h and .cpp files that make up the plugin
\endlist
\section1 2.2 Marking the plugin for build
Edit the \c {$$QT_CREATOR_ROOT/src/plugins/plugins.pro } file and include the following lines at the end of
the file and save the changes.
\code
SUBDIRS += plugin_DoNothing
plugin_DoNothing.subdir = DoNothing
\endcode
The above lines make sure that the next time we build Qt Creator, the DoNothing plugin is compiled along with the rest
of Qt Creator plugins.
\section1 2.3 Implementing the plugin
So far we have only written the project file and marked our plugin for compilation. We now do the actual
implementation of the plugin. All plugins implement the IPlugin interface. Lets take a look at how the DoNothing plugin
implements the interface and understand it in stages.
In \c {$$QT_CREATOR_ROOT/src/plugins/DoNothing/DoNothingPluigin.h} enter the following code.
\code
#ifndef DONOTHINGPLUGIN_H
#define DONOTHINGPLUGIN_H
#include <extensionsystem/iplugin.h>
class DoNothingPlugin : public ExtensionSystem::IPlugin
{
public:
DoNothingPlugin();
~DoNothingPlugin();
void extensionsInitialized();
bool initialize(const QStringList & arguments, QString * errorString);
void shutdown();
};
#endif // DONOTHINGPLUGIN_H
\endcode
As you can see the DoNothingPlugin class implements the IPlugin interface and nothing else. Lets look at how the
functions are implemented.
\code
#include "DoNothingPlugin.h"
#include <QtPlugin>
#include <QStringList>
DoNothingPlugin::DoNothingPlugin()
{
// Do nothing
}
DoNothingPlugin::~DoNothingPlugin()
{
// Do notning
}
\endcode
Apart from initializing local (non widget and non action) variables; the constructor and destructor don't do much else.
\code
bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
return true;
}
\endcode
The \bold initialize() method is called when Qt Creator wants the plugin to initialize itself. This function is ideally used to
initialize the internal state of the plugin and register actions/objects with Qt Creator. The function is called after all the
dependencies of this plugin have been loaded.
Since our plugin really does nothing, we return \bold {true} signifying that the initialization was successful. If the initialization
was unsuccessful (for some wired reason); the \bold {errMsg} string should be set to a human readable error message.
\code
void DoNothingPlugin::extensionsInitialized()
{
// Do nothing
}
\endcode
The \bold extensionsInitialized() method is called after this plugin has been initialized (ie. after initialize() method has been
called). This method is called on plugins that depend on this plugin first.
\code
void DoNothingPlugin::shutdown()
{
// Do nothing
}
\endcode
The \bold shutdown() method is called when the plugin is about to be unloaded.
\code
Q_EXPORT_PLUGIN(DoNothingPlugin)
\endcode
Finally we export the plugin class by making use of the \bold {Q_EXPORT_PLUGIN()} macro.
\section1 2.4 Writing the pluginspec file
Each plugin should accompany a pluginspec file that provides some meta data about the plugin. For our plugin the
pluginspec file is as follows
\code
<plugin name="DoNothing" version="0.0.1">
<vendor>FooCompanyInc</vendor>
<copyright>(C) 2009-2010 FooCompanyInc Pvt. Ltd.</copyright>
<license>Do anything you want</license>
<description>A plugin that does nothing</description>
<url>http://www.FooCompanyInc.com</url>
<dependencyList>
<dependency name="Core"/>
</dependencyList>
</plugin>
\endcode
The pluginspec file provides the following fields of information:
\list 1
\o Name of the plugin, which is also used as the name of the library file that provides the plugin implementation.
(In our case DoNothing.dll on Windows, libDoNothing.so on Unix)
\o Version of the plugin
\o Vendor name
\o Copyright
\o License text
\o Description
\o URL of the plugin vendor
\o Dependency List provides all the plugins that this plugin depends on. Qt Creator ensures that dependencies
are loaded and initialized before this plugin.
\endlist
\bold {Note:}\underline {The pluginspec file should be in the same directory as the plugin's project file. Just to make things clear, the
contents of the DoNothing plugin directory is as shown below}
\inlineimage qtc-plugindirectory-2.png
\section1 2.5 Compiling the plugin
Open a command prompt and move to the Qt Creator build directory (the same build directory you created in the
previous chapter). Execute the following commands
\code
qmake ..\qtcreator.pro -recursive
nmake
\endcode
After nmake returns, you will notice a FooCompanyInc folder within plugins folder whose contents are shown in the image
below.
\inlineimage qtc-compiledplugin-2.png
\section1 2.6 Check out the new plugin
Launch (or relaunch) Qt Creator and notice that the "Installed Plugins" dialog box now reports that DoNothing plugin
was infact loaded and initialized.
\inlineimage qtc-installedplugin-2.png
In the coming chapters we will learn to write more complicated plugins for Qt Creator.
arget {Writing plugin outside the source tree.}
\section1 2.7 Building out-of-source plugins
Thus far we have understood how to build plugins within the source tree of Qt Creator. It may not be practical for us to
use the Qt Creator source tree for plugin development all the time. Suppose that you are the author of a specialized
library (or application) and you want integrate your product into Qt Creator. Since you are a 3rd party developer you
cannot expect to have your code in Qt Creator source tree all the time. In this section we will look at how to build
plugins that are outside the Qt Creator source tree.
\section2 2.7.1 The plugin project file
The whole magic of out-of-source plugin builds lies in the project (.pro) file of your plugin. Lets the DoNothing plugin
discussed in the previous section and modify (its ".pro" file) so that plugins can be built from a directory outside Qt
Creator source.
The following table lists out the directory structure
able
\header
\o Description
\o Directory
\row
\o Qt Creator Source Code
\o C:\\Work\\QtCreator
\row
\o Qt Creator Build Directory
\o C:\\Work\\QtCreator\\build
\row
\o DoNothing Plugin Source
\o C:\\Work\\Research\\QtCreator\\Plugins\\DoNothing
This directory currently contains
\list
\o DoNothing.pluginspec
\o DoNothing.pro
\o DoNothingPlugin.cpp
\o DoNothingPlugin.h
\endlist
\row
\o Target plugin directory
\o C:\\Work\\QtCreator\\build\\lib\\qtcreator\\plugins\\FooCompanyInc
\endtable
Let's now modify the DoNothing.pro file in C:\\Work\\Research\\QtCreator\\Plugins\\DoNothing as follows.
\code
QTC_SOURCE = C:/Work/QtCreator/
QTC_BUILD = C:/Work/QtCreator/build/
TEMPLATE = lib
TARGET = DoNothing
IDE_SOURCE_TREE = $$QTC_SOURCE
IDE_BUILD_TREE = $$QTC_BUILD
PROVIDER = FooCompanyInc
LIBS += -L$$IDE_PLUGIN_PATH/Nokia
include($$QTC_SOURCE/src/qtcreatorplugin.pri)
include($$QTC_SOURCE/src/plugins/coreplugin/coreplugin.pri)
HEADERS = DoNothingPlugin.h
SOURCES = DoNothingPlugin.cpp
OTHER_FILES = DoNothingPlugin.pluginspec
\endcode
The \bold{QTC_SOURCE} and \bold {QTC_BUILD} variables in the project file point to the source and build directories of Qt Creator.
If you prefer setting these as environment variables, then use \bold{$$(QTC_BUILD)} instead of \bold{$$QTC_BUILD} in the
project file.
The \bold {IDE_SOURCE_TREE} and \bold {IDE_BUILD_TREE} variables are used by qtcreatorplugin.pri to establish the include
and library paths.
The \bold {PROVIDER} and \bold {DESTDIR} directories must be set before including qtcreatorplugin.pri. This is because the variables
will be provided default values are \bold {Nokia} and \bold {$$IDE_BUILD_TREE/lib/qtcreator/plugins/Nokia} otherwise.
By default qtcreatorplugin.pri assumes that all the libs that a plugin may depend on are present inside the \bold {DESTDIR}. If
our \bold {DESTDIR} is different from the default (Nokia) one, then we will need to explicitly set that. The remaining things are
just the same.
\section2 2.7.2 Compiling the plugin
Once the project file has been created, we make use of the standard qmake and make commands to compile the plugin.
\code
C:\Work\Research\QtCreator\Plugins\DoNothing>qmake
C:\Work\Research\QtCreator\Plugins\DoNothing>nmake
Microsoft (R) Program Maintenance Utility Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
"C:\Program Files\Microsoft Visual Studio 8\VC\BIN\nmake.exe" -f Makefile.Debug
Microsoft (R) Program Maintenance Utility Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
copy /y "DoNothing.pluginspec"
"..\..\..\..\QtCreator\build\lib\qtcreator\plugins\FooCompanyInc\DoNothing.pluginspec"
1 file(s) copied.
........................................
mt.exe -nologo -manifest "debug\DoNothingd.intermediate.manifest" -
outputresource:..\..\..\..\QtCreator\build\lib\qtcreator\plugins\FooCompanyInc\DoNothingd.dll;2
C:\Work\Research\QtCreator\Plugins\DoNothing>
\endcode
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,6 @@
/*!
\page location-filter.html
\title 14.Adding location filter
This page needs to be written still!
*/

304
doc/pluginhowto/menu.qdoc Normal file
View File

@@ -0,0 +1,304 @@
/*!
\page menu.html
\title 5. Adding menu and menu-items
In this chapter we will understand how to add entries to existing menus in Qt Creator. We will also learn how to add new
menu entries. Before moving ahead let's take a look at the menu bar in Qt Creator
\inlineimage qtc-menubar-5.png
The menu bar consists of the following set of default menus
\list
\o File
\list
\o New
\o Open
\o Recent Files
\endlist
\o Edit
\list
\o Advanced
\endlist
\o Tools
\o Window
\list
\o Panes
\endlist
\o Help
\endlist
\bold {Note:}\underline{Other menu items like Build and Debug come from plugins. They are not a part of the default menu set}.
As Qt developers we know that the above menus are shown within a \bold {QMenuBar}; and that there is a \bold {QMenu} associated
with each of the above menus.
\section1 5.1 Core::ActionManager
The main Qt Creator program is nothing but a plugin loader. All of the functionality provided by Qt Creator is provided
by the plugins. The main plugin for Qt Creator is called "core" Without core, Qt Creator really doesn't have a personality
One of the key components of the "core" is the \bold {ActionManager}.\bold {ActionManager} is responsible for registration of menus,
menu-items and keyboard shortcuts. So if we wanted to add a new menu-item or menu, we would have to use
\bold {ActionManager}. The coming subsections explain this better.
To gain access to the \bold {ActionManager}, the following piece of code can be used.
\code
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
Core::ActionManager* am = Core::ICore::instance()->actionManager();
\endcode
\section1 5.2 Core::ActionContainer
ActionContianer represents menu or menubar in Qt Creator. Instances of ActionContainer are never created directly,
instead they are accessed using ActionManager::createMenu(), ActionManager::createMenuBar() etc; but more on that
later.
There is an ActionContainer associated with each of the default menus in Qt Creator. Fetching ActionContainer for a
given menu can be done using the following code snippet
\code
#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
Core::ActionManager* am = Core::ICore::instance()->actionManager();
Core::ActionContainer* ac = am->actionContainer( ID );
\endcode
The following table lists out the ID to use for each of the menus in Qt Creator. Each of the IDs are defined as \bold {const}
\bold {char*} static variables within the \bold {Core} namespace.
\table
\header
\o Menu
\o ID
\row
\o File
\o Core::Constants::M_FILE
\row
\o File->New
\o Core::Constants::M_FILE_NEW
\row
\o File->Open
\o Core::Constants::M_FILE_OPEN
\row
\o Edit
\o Core::Constants::M_FILE_RECENTFILES
\row
\o Edit->Advanced
\o Core::Constants::M_EDIT_ADVANCED
\row
\o Tools
\o Core::Constants::M_TOOLS
\row
\o Window
\o Core::Constants::M_WINDOW
\row
\o Window Panes
\o Core::Constants::M_WINDOW_PANES
\row
\o Help
\o Core::Constants::M_HELP
\endtable
So if we want to catch hold of the "Help" menu, we can use the code snippet as follows
\code
#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
Core::ActionManager* am = Core::ICore::instance()->actionManager();
Core::ActionContainer* ac = am->actionContainer( Core::Constants::M_HELP );
\endcode
\section1 5.3 Registering menu-items.
The Core::Command class represents an action like a menu item, tool button, or shortcut. You don't create Command
objects directly, instead use we use ActionManager::registerAction() to register an action and retrieve a Command. The
Command object represents the user visible action and its properties.
Shown below is the right way to add the "About DoNothing" menu-item from the DoNothing plugin.
\code
#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/icore.h>
#include <QKeySequence>
bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
// Fetch the action manager
Core::ActionManager* am = Core::ICore::instance()->actionManager();
// Create a command for "About DoNothing".
Core::Command* cmd = am->registerAction(
new QAction(tr("About DoNothing"),this),
"DoNothingPlugin.AboutDoNothing",
QList<int>() <<Core::Constants::C_GLOBAL_ID
);
// Add the command to Help menu
am->actionContainer(Core::Constants::M_HELP)->addAction(cmd);
return true;
}
\endcode
\bold {Warning:}\underline { ac->menu()->addAction("About DoNothing") should never be used when adding menu items to Qt Creator.}
After compiling the changes, we can notice that the "About DoNothing" action shows up in the "Help" menu; but at the
beginning.
\inlineimage qtc-helpdonothing-5.png
If the "About DoNothing" menu item is to be placed in between specified menu item the we will make a small update in the code block
\code
bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
.....
// Add the command to Help menu
am->actionContainer(Core::Constants::M_HELP)->addAction(showCmd, Core::Constants::G_HELP_HELP);
return true;
}
\endcode
The effect of the change in code can be seen in the image below.
\inlineimage qtc-menuitemposition-5.png
When added this way, we will be able to find the "About DoNothing" action in the "Keyboard Shortcuts" dialog box and
also associate a keyboard shortcut with it.
\inlineimage qtc-options-keyboard-5.png
\section1 5.4 Responding to menu-items
Since menu-items are QActions, we can connect to their triggered(bool) or toggled(bool) signal and respond to
trigger/toggled events. The code below shows how to do this
\code
class DoNothingPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
private slots:
void about();
};
bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
......
QAction *action = new QAction(tr("About DoNothing"),this);
Core::Command* cmd = am->registerAction(action,
"DoNothingPlugin.AboutDoNothing",
QList<int>() << 0);
......
connect(action, SIGNAL(triggered(bool)), this, SLOT(about()));
return true;
}
void DoNothingPlugin::about()
{
QMessageBox::information(0, "About DoNothing Plugin",
"Seriously dude, this plugin does nothing");
}
\endcode
After compiling the changes and clicking on the "About DoNothing" menu-item, we can see the information dialog box
as shown below.
\inlineimage qtc-menuresponse-5.png
If you wanted the message box to have the Qt Creator main window as its parent, then you can use the following code
for the about() slot.
\code
void DoNothingPlugin::about()
{
QMessageBox::information(Core::ICore::instance()->mainWindow(),
"About DoNothing Plugin",
"Seriously dude, this plugin does nothing");
}
\endcode
\section1 5.5 Adding menus
The procedure for adding menus is the same. Instead of creating a \bold {Core::Command}, we create a \bold {Core::ActionContainer}
and add it to the \bold {MENU_BAR}. The following code snippet highlights the changes from our previous version.
\code
bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
// Fetch the action manager
Core::ActionManager* am = Core::ICore::instance()->actionManager();
// Create a DoNothing menu
Core::ActionContainer* ac = am->createMenu("DoNothingPlugin.DoNothingMenu");
ac->menu()->setTitle("DoNothing");
// Create a command for "About DoNothing".
QAction *action = new QAction(tr("About DoNothing",this));
Core::Command* cmd = am->registerAction(action,"DoNothingPlugin.AboutDoNothing",QList<int>() << 0);
// Add DoNothing menu to the menubar
am->actionContainer(Core::Constants::MENU_BAR)->addMenu(ac);
// Add the "About DoNothing" action to the DoNothing menu
ac->addAction(cmd);
// Connect the action
connect(action, SIGNAL(triggered(bool)), this, SLOT(about()));
return true;
}
\endcode
After recompiling the changes, you will be able to notice the DoNothing menu as shown in the screenshot below.
\inlineimage qtc-donothingleft-5.png
\section1 5.6 Placing menus and menu-items
It is possible to insert menus and menu-items anywhere you want. Shown below is a code snippet that inserts the
"DoNothing" menu before the "Help" menu.
\code
bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
// Fetch the action manager
Core::ActionManager* am = Core::ICore::instance()->actionManager();
// Create a DoNothing menu
Core::ActionContainer* ac = am->createMenu("DoNothingPlugin.DoNothingMenu");
ac->menu()->setTitle("DoNothing");
// Create a command for "About DoNothing".
QAction *action = new QAction(tr("About DoNothing"),this);
Core::Command* cmd = am->registerAction(action,"DoNothingPlugin.AboutDoNothing",QList<int>() << 0);
// Insert the "DoNothing" menu between "Window" and "Help".
QMenu* windowMenu = am->actionContainer(Core::Constants::M_HELP)->menu();
QMenuBar* menuBar = am->actionContainer(Core::Constants::MENU_BAR)->menuBar();
menuBar->insertMenu(windowMenu->menuAction(), ac->menu());
// Add the "About DoNothing" action to the DoNothing menu
ac->addAction(cmd);
// Connect the action
connect(action, SIGNAL(triggered(bool)), this, SLOT(about()));
return true;
}
\endcode
After compiling the changes, we can now notice that change in position of the "DoNothing" menu.
\inlineimage qtc-donothingright-5.png
You can use a similar technique for customizing the position of menu-items.
*/

537
doc/pluginhowto/mode.qdoc Normal file
View File

@@ -0,0 +1,537 @@
/*!
\page mode.html
\title 13. Adding a mode
When we are working in Qt Creator then we in one of the six modes below.
\list
\o Welcome
\o Edit
\o Debug
\o Project
\o Help
\o Output
\endlist
By selecting different modes in Qt creator we can quickly switch between different task spaces, such
as editing, browsing the Qt Creator manual, setting up the build environment, etc.
Different modes can be selected either by clicking on the mode selectors or through keyboard shortcuts.
The following figure shows the mode selectors in the Qt Creator.
\inlineimage qtc-modes-13.png
Now in this chapter we are concerned about learning to add a new mode and a "LoggerMode" mode selector will be availble in Qt Creator.
\section1 13.1 Core Classes and Interfaces
To support a new mode we need to
\list
\o Implement a plugin \bold {(ExtensionSystem::IPlugin } implementation) class that exposes the new mode.Chapter 2 in this
document provides a detailed description on creating plugins by implementing the ExtensionSystem::IPlugin interface.
\o Implement the \bold{(Core::IMode)} interface. This interface allows us to add a new mode in Qt creator
\endlist
\section2 Step 1. Core::IMode interface
This interface abstracts the creation of a mode, adding an icon to the mode selector, setting the mode priority,
setting a unique made name. The \bold {Core::IMode} interface is declared as follows in scr/plugins/coreplugin/imode.h
\code
namespace Core {
class CORE_EXPORT IMode : public IContext
{
Q_OBJECT
public:
IMode(QObject *parent = 0) : IContext(parent) {}
virtual ~IMode() {}
virtual QString name() const = 0;
virtual QIcon icon() const = 0;
virtual int priority() const = 0;
virtual const char *uniqueModeName() const = 0;
};
} // namespace Core
\endcode
Notice that Core::IMode inherits Core::IContext. Core::IContext is an interface through which
Qt Creator picks up the widget corresponding to the mode. The Core::IContext interface is declared
as follows.
\code
namespace Core
{
class CORE_EXPORT IContext : public QObject
{
Q_OBJECT
public:
IContext(QObject *parent = 0) : QObject(parent) {}
virtual ~IContext() {}
virtual QList<int> context() const = 0;
virtual QWidget *widget() = 0;
virtual QString contextHelpId() const { return QString(); }
};
}
\endcode
\section1 13.2 Adding a new mode
Lets add a mode to Qt Creator called "LoggerMode". LoggerMode allows the user (developer) to log the number of hours he/she
has worked on a project. The following steps explain how the new mode "LoggerMode" is added.
\section2 Step 1. Declaring the LoggerMode class
We first begin by declaring a class called LoggerMode that implements the Core::IMode interface.
The class definition is as follows.
\code
#include <coreplugin/imode.h>
struct LoggerModeData;
class LoggerMode : public Core::IMode
{
Q_OBJECT
public:
LoggerMode();
~LoggerMode();
QString name() const;
QIcon icon() const;
int priority() const;
QWidget *widget();
const char *uniqueModeName() const;
QList<int> context() const;
void activated();
QString contextHelpId() const { return QLatin1String("Qt Creator"); }
private:
LoggerModeData *d;
};
\endcode
\section2 Step 2. Implementing the LoggerMode class
Declaring the private variables of the LoggerMode class.
\code
struct LoggerModeData
{
QWidget *m_widget;
};
\endcode
The constructor creates a new blank widget just for now to get started with the mode.
The constructor and the destructor are implemented as follows
\code
LoggerMode::LoggerMode()
{
d = new LoggerModeData;
d->m_widget = new QWidget;
}
LoggerMode::~LoggerMode()
{
delete d->m_widget;
delete d;
}
\endcode
The \bold {name()} method returns a name for the new mode selector.
\code
QString LoggerMode::name() const
{
return tr("LoggerMode");
}
\endcode
The \bold {icon()} method returns a logo for the new mode selector.
\code
QIcon LoggerMode::icon() const
{
return QIcon(QLatin1String(":/core/images/qtcreator_logo_32.png"));
}
\endcode
The \bold {priority()} method is implemented to return a priority for the mode. Modes of higher
priority are shown above modes of lower priority. The highest priority in Qt Creator is currently
100 and is associated with the Welcome mode. We return 0 from LoggerMode, becasue we want this
mode to appear at the end.
\code
int LoggerMode::priority() const
{
return 0;
}
\endcode
Thw \bold {widget()} method returns a widget to the new mode.
\code
QWidget* LoggerMode::widget()
{
return d->m_widget;
}
\endcode
The \bold {uniqueModeName()} returns the name of the mode to the "LoggerMode" Class.
\code
const char* LoggerMode::uniqueModeName() const
{
return "LoggerMode" ;
}
\endcode
The \bold {context()} method returns an empty list of integers to tne "LoggerMode" class.
\code
QList<int> LoggerMode::context() const
{
return QList<int>();
}
\endcode
\section2 Step 3. Implementing the "LoggerMode" plugin
We implement the "LoggerMode" plugin very similar to the DoNothingPlugin class described in Chapter 2.
Here we only look at the implementation of the initialize() method.
\code
bool LoggerModePlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
loggerMode = new LoggerMode;
addAutoReleasedObject(loggerMode);
return true;
}
\endcode
\section2 Step 4. Testing the plugin.
Upon compiling the plugin and restarting Qt Creator, we can notice the "LoggerMode" mode selector in the Qt creator.
\inlineimage qtc-loggermode-13.png
\section1 13.3 Adding Functionality to the "LoggerMode"
The actual function of the "LoggerMode" is to log data in a text file about a project, such as progress , hours worked,
description etc.
\section2 Step 1. Modifying the "LoggerMode" Class
To add the functionalities, the LoggerMode class is further modified.
The code block is given as follows.
\code
struct LoggerModeData;
class LoggerMode :public Core::IMode
{
Q_OBJECT
public:
LoggerMode();
~LoggerMode();
...
...
protected slots:
void addNewStackWidgetPage(const QString projectName);
void addItem();
private:
...
};
\endcode
The constructor of the \bold {LoggerMode} class is modified as follows.
\code
LoggerMode::LoggerMode()
{
d = new LoggerModeData;
d->m_widget = new QWidget;
//Current Projects Label and combobox widget are created.
d->currentProjectsLabel = new QLabel("Current projects :");
d->currentProjectsLabel->setFixedWidth(90);
d->currentProjectsCombobox = new QComboBox;
d->currentProjectsCombobox->setSizePolicy(QSizePolicy::Preferred,
QSizePolicy::Preferred);
//Add Projects Label and combobox widget are created.
d->addProjectLabel = new QLabel("Add Project :");
d->addProjectLabel->setAlignment(Qt::AlignRight);
d->addProjectComboBox = new QComboBox;
d->addProjectComboBox->setSizePolicy(QSizePolicy::Preferred,
QSizePolicy::Preferred);
d->addProjectComboBox->setEditable(true);
d->addToProjectButton = new QPushButton(tr("Add Project"));
d->addToProjectButton->setFixedWidth(80);
//Creating a horizontal Layout
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addWidget(d->currentProjectsLabel);
hLayout->addWidget(d->currentProjectsCombobox);
hLayout->addWidget(d->addProjectLabel);
hLayout->addWidget(d->addProjectComboBox);
hLayout->addWidget(d->addToProjectButton);
//creating a stackedWidget
d->stackedWidget = new QStackedWidget;
//creating a vertical layout
QVBoxLayout* layout = new QVBoxLayout;
layout->addLayout(hLayout);
layout->addWidget(d->stackedWidget);
d->m_widget->setLayout(layout);
d->addProjectComboBox->addItem("Project 1");
d->addProjectComboBox->addItem("Project 2");
d->addProjectComboBox->addItem("Project 3");
connect(d->addToProjectButton,SIGNAL(clicked()),
this,SLOT(addItem()));
connect(d->currentProjectsCombobox, SIGNAL(currentIndexChanged(int)),
d->stackedWidget, SLOT(setCurrentIndex(int)));
}
\endcode
Implementation of the \bold{addNewStackWidgetPage()} slot of \bold {LoggerMode} class
\code
void LoggerMode::addNewStackWidgetPage(const QString projectName)
{
d->stackedWidget->addWidget(new LoggerModeWidget(projectName));
}
\endcode
Implementation of the \bold{addItem()} slot of \bold {LoggerMode} class
\code
void LoggerMode::addItem()
{
d->currentProjectsCombobox->addItem(d->addProjectComboBox->currentText());
addNewStackWidgetPage(d->currentProjectsCombobox->itemText(0));
}
\endcode
The effect of the above block of code can be seen in the following image.
\inlineimage qtc-loggerpage-13.png
\section2 Step 2. Declaration of the "LoggerModeWidget" class
Reffering to the above image, when ever we select a project from the \bold {Add Project} combobox and click
\bold {Add Project} button a new project is added to the \bold {Current projects} list and and a corresponding
\bold {LoggerModeWidget} is opened, for logging different informations about the project.
The declaration of the \bold {LoggerModeWidget} class is shown in the following code block.
\code
#include <QWidget>
struct LoggerModeWidgetData;
class LoggerModeWidget : public QWidget
{
Q_OBJECT
public:
LoggerModeWidget(const QString projectName, QWidget* parent = 0);
~LoggerModeWidget();
public slots:
void setProjectName(QString name);
protected slots:
bool saveToFile();
void startTimeLog();
void endTimeLog();
void updateTime();
private:
LoggerModeWidgetData* d;
};
\endcode
\section2 Step 3. Implementing the "LoggerModeWidget" class
Declaring the private member variables in the structure called \bold {LoggerModeWidgetData}
\code
struct LoggerModeWidgetData
{
QLabel *progressLabel;
QLabel *hoursWorkedLabel;
QLabel *dateLabel;
QLabel *descriptionLabel;
QCalendarWidget *calendar;
QComboBox *progressComboBox;
QLineEdit *hoursWorkedLineEdit;
QPushButton *startTimerButton;
QPushButton *stopTimerButton;
QPushButton *saveButton;
QTimer *timer;
QTextEdit *textEdit;
QString projectName;
int totalTime;
};
\endcode
Implementation of the constructor and destructor of the \bold {LoggerModeWidget} class
\code
LoggerModeWidget::LoggerModeWidget(const QString projectName, QWidget* parent)
:QWidget(parent)
{
d = new LoggerModeWidgetData;
d->projectName = projectName;
d->totalTime = 0;
QStringList percentList;
percentList <<"10%" <<"20%" <<"30%" <<"40%" <<"50%"
<<"60%" <<"70%" <<"80%" <<"90%" <<"100%" ;
d->progressLabel = new QLabel("Progress:");
d->hoursWorkedLabel = new QLabel("Hours Worked:");
d->dateLabel = new QLabel("Date:");
d->descriptionLabel = new QLabel("Description :");
d->hoursWorkedLineEdit = new QLineEdit;
d->hoursWorkedLineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
d->progressComboBox = new QComboBox;
d->progressComboBox->addItems(percentList);
d->progressComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
d->startTimerButton = new QPushButton(tr("Start Timer"));
d->startTimerButton->setFixedWidth(80);
d->stopTimerButton = new QPushButton(tr("Pause Timer"));
d->stopTimerButton->setFixedWidth(80);
d->stopTimerButton->setCheckable(true);
d->textEdit = new QTextEdit(this);
d->textEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
d->calendar = new QCalendarWidget;
d->saveButton = new QPushButton(tr("Save To File"));
d->saveButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
QGroupBox *timeLoggerBox = new QGroupBox(tr("Time Logger"));
QGridLayout *gLayout = new QGridLayout;
gLayout->addWidget(d->dateLabel, 0, 0, 1, 1);
gLayout->addWidget(d->calendar, 1, 0, 1, 3);
gLayout->addWidget(d->progressLabel, 2, 0, 1, 1);
gLayout->addWidget(d->progressComboBox, 2, 1, 1, 1);
gLayout->addWidget(d->hoursWorkedLabel, 3, 0, 1, 1);
gLayout->addWidget(d->hoursWorkedLineEdit, 3, 1, 1, 1);
gLayout->addWidget(d->startTimerButton, 4, 1, 1, 1);
gLayout->addWidget(d->stopTimerButton, 4, 2, 1, 1);
timeLoggerBox->setLayout(gLayout);
d->timer = new QTimer(this);
// connection of SIGNALS and SLOTS
connect(d->timer, SIGNAL(timeout()), this, SLOT(updateTime()));
connect(d->startTimerButton,SIGNAL(clicked()),this,SLOT(startTimeLog()));
connect(d->stopTimerButton,SIGNAL(clicked()),this,SLOT(endTimeLog()));
connect(d->saveButton, SIGNAL(clicked()), this, SLOT(saveToFile()));
QVBoxLayout *vLayout = new QVBoxLayout;
vLayout->addWidget(d->descriptionLabel);
vLayout->addWidget(d->textEdit);
QHBoxLayout * hLayout = new QHBoxLayout;
hLayout->addWidget(timeLoggerBox);
hLayout->addLayout(vLayout);
QHBoxLayout *bLayout = new QHBoxLayout;
bLayout->addStretch(1);
bLayout->addWidget(d->saveButton);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(hLayout);
mainLayout->addLayout(bLayout);
mainLayout->addStretch(1);
}
LoggerModeWidget::~LoggerModeWidget()
{
delete d;
}
\endcode
The \bold {saveToFile()} slot is used by the "SaveTofile" button to save the contents of the "LoggerModeWidget" to a
text file.
\code
bool LoggerModeWidget::saveToFile()
{
QString fileName = QFileDialog::getSaveFileName(this);
if (fileName.isEmpty())
return false;
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::critical(this, tr("Application"),
tr("Unable to open file %1 for writing :\n%2.")
.arg(fileName)
.arg(file.errorString()));
return false;
}
QTextStream out(&file);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
out << "Project name : " << d->projectName << "\n";
out << "Date : " << d->calendar->selectedDate().toString() << "\n";
out << "Progress : " << d->progressComboBox->currentText() << "\n";
out << "Duration : " << d->hoursWorkedLineEdit->text() << "\n\n";
out << "Description : " << d->textEdit->toPlainText();
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return true;
}
\endcode
The \bold {startTimeLog()} slot is used by the \bold {Start Timer} button to start the timer.
\code
void LoggerModeWidget::startTimeLog()
{
d->totalTime = 0;
d->timer->start(1000);
}
\endcode
The \bold {endTimeLog()} slot is used by the \bold (Pause Timer } button to pause and resume the timer.
\code
void LoggerModeWidget::endTimeLog()
{
if(d->stopTimerButton->isChecked())
{
d->stopTimerButton->setText("Continue Timer");
d->timer->stop();
}
else
{
d->stopTimerButton->setText("Pause Timer");
d->timer->start(1000);
}
}
\endcode
The \bold {updateTime()} slot is used to update the time.
\code
void LoggerModeWidget::updateTime()
{
d->totalTime++;
QTime time(0,0,0);
time = time.addSecs(d->totalTime);
d->hoursWorkedLineEdit->setText(time.toString());
}
\endcode
\section1 13.4 Testing the final LoggerMode plugin.
\section2 Step 1. A "LoggerMode" mode selector can be seen int the Qt Creator.
\inlineimage qtc-loggermode-13.png
\section2 Step 2. A LoggerMode widget opens with options to "Add Projects"
\inlineimage qtc-loggerpage-13.png
\section2 Step 3. The "Add Project" button adds a project to the "Current Project list and opens the the "LoggerModeWidget"
\inlineimage qtc-loggermodewidget-13.png
*/

View File

@@ -0,0 +1,171 @@
/*!
\page nav-widget.html
\title 6. Adding Navigation Widget
Navigation panel in Qt Creator is the area where Project, File System, Bookmark and Open Documents siderbars are
shown. Sidebar is one of the widgets in the "Navigation Panel" Take a look at the marked area in the screenshot below.
\inlineimage qtc-firstnavigation-6.png
Qt Creator makes it possible for us to divide the navigation panel into windows and view more than one side bar at the
same time. Take a look at the screenshot below.
\inlineimage qtc-secondnavigation-6.png
In this chapter we will understand how to add a new side bar to Qt Creator.
\section1 6.1 Core::INavigationWidgetFactory interface
The Core of Qt Creator exposes an interface called Core::INavigationWidgetFactory. The interface is defined as follows in
plugins/corelib/inavigationwidgetfactory.h
\code
class CORE_EXPORT INavigationWidgetFactory : public QObject
{
Q_OBJECT
public:
INavigationWidgetFactory();
virtual ~INavigationWidgetFactory();
virtual QString displayName() = 0;
virtual QKeySequence activationSequence();
virtual NavigationView createWidget() = 0;
virtual void saveSettings(int position, QWidget *widget);
virtual void restoreSettings(int position, QWidget *widget);
};
\endcode
And NavigationView (the return type of createWidget()) is
\code
struct NavigationView
{
QWidget *widget;
QList<QToolButton *> doockToolBarWidgets;
};
\endcode
Plugins that provide a navigation siderbar (or widget) must implement this interface. In addition to implementing the
interface, the plugin has to use "expose" an instance of that interface using methods described in section 4.2.2.
\section1 6.2 Preparing a navigation sidebar (widget)
Suppose that we wanted to provide a Directory browser as a side bar widget from our plugin.
\section2 Step 1: Let's Implement FileSystemModel such that it will show only one column.
The Implementation of FileSystemModel is as follows:
\code
#include <QFileSystemModel>
class FileSystemModel : public QFileSystemModel
{
public:
FileSystemModel(QObject* parent=0);
~FileSystemModel();
int columnCount(const QModelIndex &parent = QModelIndex()}const;
};
\endcode
General Constructor and Destructor
\code
FileSystemModel::FileSyatemModel(QObject *parent)
:QFileSystemModel(parent)
{
}
FileSystemModel::~FileSystemModel()
{
}
\endcode
Implement the virtual function columnCount to return only one column.
\code
int FileSystemModel::columnCount(const QModelIndex &parent)const
{
Q_UNUSED(parent)
return 1;
}
\endcode
With this FileSystemModel is ready.
\section2 Step 2: Implementing the INavigationWidgetFactory Interface
We implement the INavigationWidgetFactory interface in a class whose defination is as follows
\code
#include<coreplugin/inavigationwidgetfactory.h>
class DirNavigationFactory:: public Core::INavigationWidgetFactory
{
public:
DirNavigationFactory(){}
~DirNavigationFactory(){}
Core::NavigationView createWidget();
QString displayName();
};
\endcode
The createWidget() method is implemented to return an instance of the QTreeView which uses
FileSystemModel that was explained in previous step.
\code
Core::NavigationView DirNavigationFactory::createWidget()
{
Core::NavigationView view;
// Create FileSystemModel and set the defauls path as home path
FileSystemModel* model = new FileSystemModel;
model->setRootPath(QDir::homePath());
// Create TreeView and set model
QTreeView* tree = new QTreeView;
tree->setModel(model);
view.widget = tree;
return view;
}
\endcode
The \bold {displayName()} method is implemented to return a descriptive name that Qt Creator should use for showing the
side-bar.
\code
QString DirNavigationFactory::displayName()
{
return "Dir View";
}
\endcode
With this the \bold {INavigationWidgetFactory} implementation is ready.
\section2 Step 3: Implementing the Dir-view plugin
We implement the Dir-view plugin class similar to the \bold {DoNothingPlugin} class described in Chapter 2. Hence, we only
describe the implementation of the initialize method of the \bold {DirModelPluginPlugin} class here.
\code
bool DirModelPluginPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
addAutoReleasedObject(new DirNavigationFactory);
return true;
}
\endcode
In the \bold {initialize()} method an instance of the \bold {INavigationWidgetFactory} implementation is created and added to the object
pool. Once the object is added to the pool, \bold {ExtensionSystem::PluginManager} emits the \bold {objectAdded()} signal, which is
then trapped by the Core of Qt Creator. The Core then makes use of our implementation of \bold {INavigationWidgetFactory}
interface and places an instance of DirExplorerSideBar in the navigation panel.
\section2 Step 4: Testing the plugin
Upon compiling the plugin and restarting Qt Creator, we can notice the "Dir View" side bar as shown below.
\inlineimage qtc-dirview-6.png
*/

View File

@@ -0,0 +1,205 @@
/*!
\page pref-pane.html
\title 7. Adding Preferences Pane
Preferences dialog in Qt Creator is used to configure the Qt Creator settings. Since Qt Creator is just a plugin loader that
loads all the relevant plugins, the preferences dialog shows pages that configure plugins. You can get to it by clicking
Tools->Options.
\inlineimage qtc-options-7.png
Each plugin provides one or more options pages that get shown in the preferences dialog. In the following sub-sections
we will learn how to add our own pages to the dialog.
\section1 7.1 Core::IOptionsPage interface
The Core of Qt Creator exposes an interface called \bold{Core::IOptionsPage}. The interface is defined in
plugins/coreplugin/dialogs/ioptionspage.h.
\code
class CORE_EXPORT IOptionsPage : public QObject
{
Q_OBJECT
public:
IOptionsPage( *parent = 0) : QObject(parent) {}
virtual ~IOptionsPage() {}
virtual QString id() const = 0;
virtual QString trName() const = 0;
virtual QString category() const = 0;
virtual QString trCategory() const = 0;
virtual QWidget *createPage(QWidget *parent) = 0;
virtual void apply() = 0;
virtual void finish() = 0;
};
\endcode
By implementing the above interface and exposing an instance of it, we will be able to register new pages with the
preferences dialog.
\section1 7.2 Preparing the options-page
Let's implement a plugin that shows an options page that lists out all the open and modified files.
\section2 Step 1: Implementing the "modified file" list widget
The modified file list widget is simply a \bold{QListWidget} that shows all the modified files from the project manager. The
class declaration is as follows
\code
#include <QListWidget>
class ModifiedFileListWidget: public QListWidget
{
Q_OBJECT
public:
ModifiedFileListWidget(QWidget* parent=0);
~ModifiedFileListWidget();
};
\endcode
Within the constructor we populate the list widget with names of the modified pages
\code
#include <coreplugin/filemanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/ifile.h>
ModifiedFileListWidget::ModifiedFileListWidget(QWidget* parent):QListWidget(parent)
{
// Show the list of modified pages
Core::FileManager* fm = Core::ICore::instance()->fileManager();
QList<Core::IFile*> files = fm->modifiedFiles();
for(int i=0; i<files.count();i++)
this->addItem(files.at(i)->fileName());
}
\endcode
The destructor does nothing.
\code
ModifiedFileListerPage::~ModifiedFileListerPage()
{
}
\endcode
\section2 Step 2: Implementing the Core::IOptionsPage interface
We implement the \bold {Core::IOptionsPage} interface in a class called \bold {ModifiedFileLister}. The class declaration
is as follows
\code
#include <coreplugin/dialogs/ioptionspage.h>
class ModifiedFileLister : public Core::IOptionsPage
{
Q_OBJECT
public:
ModifiedFileLister(QObject *parent = 0);
~ModifiedFileLister();
// IOptionsPage implementation
QString id() const;
QString trName() const;
QString category() const;
QString trCategory() const;
QWidget *createPage(QWidget *parent);
void apply();
void finish();
};
\endcode
The constructor and destructor are straightforward and easy to understand.
\code
ModifiedFileLister::ModifiedFileLister(QObject *parent): IOptionsPage(parent)
{
}
ModifiedFileLister::~ModifiedFileLister()
{
}
\endcode
The \bold{id()} method should be implemented to return a unique identifier for the options page provided by this class. The
string will be used internally to \underline{\bold{id}}entify the page.
\code
QString ModifiedFileLister::id() const
{
return "ModifiedFiles";
}
\endcode
The \bold {trName()} method should be implemented to return a translated string name that will be shown in the options
dialog.
\code
QString ModifiedFileLister::trName() const
{
return tr("Modified Files");
}
\endcode
The \bold{category()} and \bold{trCategory()} methods should be implemented to return the group under which we want to
show the page. The latter returns the translated version of the string returned by the former.
\code
QString ModifiedFileLister::category() const
{
return "Help";
}
QString ModifiedFileLister::trCategory() const
{
return tr("Help");
}
\endcode
The \bold{createPage()} method should be implemented to return a new instance of the page implemented in step 1.
\code
QWidget *ModifiedFileLister::createPage(QWidget *parent)
{
return new ModifiedFileListWidget(parent);
}
\endcode
The methods \bold {apply()} and \bold {finish()} can be implemented to accept the changes made by the user made on the
page. In our case we don't have any changes to accept, so we leave the methods empty.
\code
void ModifiedFileLister::apply()
{
// Do nothing
}
void ModifiedFileLister::finish()
{
// Do nothing
}
\endcode
\section2 Step 3: Implementing the modified-file-lister plugin
We implement the plugin class similar to the \bold {DoNothingPlugin} class described in Chapter 2. Hence, we only
describe the implementation of the initialize method of the \bold {ModifiedFileListerPlugin} class here.
\code
bool ModifiedFileListerPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
addAutoReleasedObject(new ModifiedFileLister);
return true;
}
\endcode
\section2 Step 4: Testing the plugin
Upon compiling the plugin and restarting Qt Creator, we can notice in the options dialog the newly added "Modified
Files" page.
\inlineimage qtc-testplugin-7.png
*/

View File

@@ -0,0 +1,111 @@
/*!
\page progress.html
\title 11.Showing and reacting to progress information
The progress bars give us the information about the progress status of a current task.We can find
progress bar showing up in the Qt Creator window at the left side. Whenever a task is executed
a progress bar pops up showing the progress status until the task is completed.
\inlineimage qtc-progressbar-11.png
\section1 11.1 Creating a progress bar
First we will declare the methods required for implementing a "Progress Bar" widget and then we will attach the widget with "Header Filter" in "Find/Replace"
and see it working according to the search progress status.
\section2 Step 1. Modification of "HeaderFilter" class
The "HeaderFilter" class discussed in Chapter 9. is further modified.
So we will now declare the methods required for a "Progress Bar" in the following block of code.
\code
struct HeaderFilterData;
class HeaderFilter : public Find::IFindFilter
{
Q_OBJECT
public:
HeaderFilter();
~HeaderFilter();
...
QWidget *createProgressWidget();
private:
HeaderFilterData *d;
};
\endcode
Here \bold {createProgressWidget()} is not implemented from Find::IFindFilter. It is a custom function written within HeaderFilter class,
which takes up the onus of returning a progress display widget.
\section2 Step 2. Implementation of the "HeaderFilter" class
The private member variables of the \bold {HeaderFilter} class are declared in the structure \bold {HeaderFilterData}
\code
struct HeaderFilterData
{
QFutureWatcher<FileSearchResult> watcher;
QLabel *resultLabel;
...
...
};
\endcode
The \bold {constructor} and the \bold {destructor} are as follows.
\code
HeaderFilter::HeaderFilter()
{
d = new HeaderFilterProgressData;
d->watcher.setPendingResultsLimit(1);
d->resultLabel = 0 ;
...
}
HeaderFilter::~HeaderFilter()
{
delete d;
}
\endcode
The \bold {findAll()} method is further modified to create a progress bar popup the bar while searching
is going on.
\code
void HeaderFilter::findAll(const QString &text,QTextDocument::FindFlags findFlags)
{
...
...
...
//The "progress" is the instance of FutureProgress class.
//The "progress" is the pointer to the progress bar created
//Creates and shows the "progress" bar for searching task.
Core::FutureProgress *progress =
Core::ICore::instance()->progressManager()->addTask(d->watcher.future(),
"MySearch",
Find::Constants::TASK_SEARCH,
Core::ProgressManager::KeepOnFinish
);
progress->setWidget(createProgressWidget());
connect(progress, SIGNAL(clicked()), d->searchResultWindow(), SLOT(popup()));
}
\endcode
The \bold {createProgressWidget()} function creates the progress widget.It shows
the number of searched items found below and is placed below the progress bar.
\code
QWidget *HeaderFilter::createProgressWidget()
{
d->resultLabel = new QLabel;
d->resultLabel->setAlignment(Qt::AlignCenter);
QFont f = d->resultLabel->font();
f.setBold(true);
f.setPointSizeF(StyleHelper::sidebarFontSize());
d->resultLabel->setFont(f);
d->resultLabel->setPalette(StyleHelper::sidebarFontPalette(d->resultLabel->palette()));
d->resultLabel->setText(tr("%1 found").arg(d->searchResultWindow()->numberOfResults()));
return d->resultLabel;
}
\endcode
\section2 Step 3.Testing the plugin.
Now its the time to test the plugin. For searching we use our "HeaderFilter" and the searched result is shown in the
"Search Results" window and the progress bar pops up while searching.
\inlineimage qtc-searchprogress-11.png
*/

View File

@@ -0,0 +1,667 @@
/*!
\page project-file-wizard.html
\title 8. Project/File Wizards
New projects in Qt Creator can be created by clicking on the "File -> New" menu item and selecting the required project
type. Shown below is the new project dialog box.
\inlineimage qtc-newprojectdialog-8.png
In this chapter we will learn how to add new project types into the dialog box above.
\section1 8.1 Core::IWizard interface
Qt Creator provides a Core::IWizard interface that can be implemented to support new project types. The interface is
defined as follows in \bold {src/plugins/coreplugin/dialogs/iwizard.h}.
\code
class CORE_EXPORT IWizard : public QObject
{
Q_OBJECT
public:
enum Kind {FileWizard,ClassWizard,ProjectWizard};
virtual Kind kind() const = 0;
virtual QIcon icon() const = 0;
virtual QString description() const = 0;
virtual QString name() const = 0;
virtual QString category() const = 0;
virtual QString trCategory() const = 0;
virtual QStringList runWizard(const QString &path, QWidget *parent) = 0;
};
\endcode
Qt Creator supports the following types of new entities
\list
\o File
\o Class
\o Project
\endlist
\bold {Core::IWizard} has to be implemented to support any of the above project types.
\section2 8.1.1 Sample implementation of Core::IWizard
Let's implement the \bold {IWizard} interface to support a new project type called "Custom Project". The idea is to see the
new project type listed in the new project wizard that shows up on clicking "File -> New".
\section3 Step 1: Implementing the Core::IWizard interface
Lets create a class called \bold {CustomProjectWizard} and subclass it from \bold {Core::IWizard}.
\code
class CustomProjectWizard : public Core::IWizard
{
public:
CustomProjectWizard() { }
~CustomProjectWizard() { }
Core::IWizard::Kind kind() const;
QIcon icon() const;
QString description() const;
QString name() const;
QString category() const;
QString trCategory() const;
QStringList runWizard(const QString &path, QWidget *parent);
};
\endcode
Below we will discuss the implementation of each of the functions.
\bold{The kind()} function should be implemented to return the type of "new" project we support in our implementation of
\bold {IWizard}. Valid values are \bold {FileWizard},\bold{ClassWizard} and \bold {ProjectWizard}. In our implementation we return
\bold{ProjectWizard}.
\code
Core::IWizard::Kind CustomProjectWizard::kind() const
{
return IWizard::ProjectWizard;
}
\endcode
The \bold{icon()} implementation must return an icon to use against the project type in the New project dialog box. In our
implementation we return the Qt Creator icon itself.
\code
QIcon CustomProjectWizard::icon() const
{
return qApp->windowIcon();
}
\endcode
The \bold {description()},\bold {name()} and \bold {category()} methods must return some meta data of the new project/file/class
type we are providing in the \bold {IWizard} implementation.
\code
QString CustomProjectWizard::description() const
{
return "A custom project";
}
QString CustomProjectWizard::name() const
{
return "CustomProject";
}
QString CustomProjectWizard::category() const
{
return "FooCompanyInc";
}
\endcode
The \bold{trCategory()} method should be implemented to return a translated category string. This is the name that is
shown on the "New.." dialog box.
\code
QString CustomProjectWizard::trCategory() const
{
return tr("FooCompanyInc");
}
\endcode
If the user selects the "CustomProject" category supported by our implementation of \bold{IWizard} and selects Ok in the
"New.." dialog box; then the \bold{runWizard()} method is called. This method must be implemented to show a dialog box
or \bold{QWizard}, ask questions from the user about the new project/file/class being created and return a list of newly
created files. In our implementation of the \bold{IWizard} we will return an empty string list.
\code
QStringList CustomProjectWizard::runWizard(const QString &path, QWidget *parent)
{
Q_UNUSED(path);
Q_UNUSED(parent);
QMessageBox::information(parent, "Custom Wizard Dialog", "Hi there!");
return QStringList();
}
\endcode
\section3 Step 2: Providing the wizard from a plugin
We implement a custom-project plugin using the same means as described in Chapter 2. The only change is in the
\bold{initialize()} method implementation of the plugin.
\code
bool CustomProjectPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
addAutoReleasedObject(new CustomProjectWizard);
return true;
}
\endcode
\section3 Step 3: Testing the plugin
Upon compiling the plugin and restarting Qt Creator, we can notice the new project type in the "New.." dialog box. Take
a look at the screenshot below.
\inlineimage qtc-testplugin-8.png
\section1 8.2 Predefined IWizard implementation - Core::BaseFileWizard
Qt Creator's core provides a default implementation of the \bold{IWizard} interface in the form of the
\bold {Core::BaseFileWizard} class. This class implements provides default implementation of all the methods in the
\bold {IWizard} interface and adds some virtual methods of its own. To make use of the class, we need to subclass from it and
implement one or more methods.
\section2 8.2.1 Core::GeneratedFile and Core::GeneratedFiles
Normally a new wizard (\bold{IWizard} implementation) is implemented to allow the user to provide some hints and have
one or more files automatically generated. The \bold{Core::GeneratedFile} helps abstract each of the files that need
generation. We will learn later on that within subclasses of \bold{Core::BaseFileWizard}, we create an instance of
\bold{Core::GeneratedFile} for each file that is automatically generated.
The \bold{Core::GeneratedFile} class is defined as follows in \bold{coreplugin/basefilewizard.h}
\code
class GeneratedFile
{
public:
GeneratedFile();
explicit GeneratedFile(const QString &path);
GeneratedFile(const GeneratedFile &);
GeneratedFile &operator=(const GeneratedFile &);
~GeneratedFile();
QString path() const;
void setPath(const QString &p);
QString contents() const;
void setContents(const QString &c);
QString editorKind() const;
void setEditorKind(const QString &k);
bool write(QString *errorMessage) const;
private:
QSharedDataPointer<GeneratedFilePrivate> m_d;
};
typedef QList<GeneratedFile> GeneratedFiles;
\endcode
Files that need to be generated by subclasses of \bold {Core::BaseFileWizard} are represented by the
\bold {Core::GeneratedFile} class. The class contains three key properties of a file that needs generation
\list 1
\o Name of the file (with its absolute path).
\o The kind of editor needed for editing the file. Some valid values for editor kind are
\list a
\o \bold{CppEditor::Constants::CPPEDITOR_KIND}
\o \bold{GenericProjectManager::Constants::PROJECT_KIND}
\o \bold{Git::Constants:: GIT_COMMAND_LOG_EDITOR_KIND}
\o \bold {Git::Constants:: C_GIT_COMMAND_LOG_EDITOR}
\endlist
\o Contents of the file.
\endlist
Suppose that we wanted to generate a C++ source file with the following contents
\code
#include <iostream>
int main()
{
cout << "Hello World\n";
return 0;
}
\endcode
We would use \bold{Core::GeneratedFile} for generating the above contents as follows
\code
#include <coreplugin/basefilewizard.h>
#include <cppeditor/cppeditorconstants.h>
Core::GeneratedFile genFile("C:/Path/To/Source.cpp");
genFile.setEditorKind(CppEditor::Constants::CPPEDITOR_KIND);
genFile.setContents(
"#include <iostream>\n"
"\n"
"int main()\n"
"{\n"
" cout << \"Hello World\n\";\n"
" \n"
" return 0;\n"
"}
);
genFile.write();
\endcode
\section2 8.2.2 The "Item Model" class wizard
Suppose that we wanted to provide a new class wizard that helps automatically generate the skeleton of an item model
based on few hints like
\list
\o Model Class Name
\o Base Class Name (can be \bold {QAbstractItemModel},\bold QAbstractListModel and
\bold{QAbstractTableModel})
\o Header file name and
\o Source file name
\endlist
Lets implement a plugin that will provide the new "Item Model" class wizard in Qt Creator.
\section3 Step 1: Design the class wizard page
Lets design a simple page in Qt Designer that accepts hints as described above.
\inlineimage qtc-designer-8.png
The design is saved as ModelNamePage.ui.
\section3 Step 2: Implement the class wizard page
Lets import the UI in a Qt/C++ and provide easy to use methods to help fetch information from the page. First we design
a structure that captures all the "item model" class hints.
\code
struct ModelClassParameters
{
QString className;
QString headerFile;
QString sourceFile;
QString baseClass;
QString path;
};
\endcode
Next we declare a wizard page class that imports the UI designed in the previous step and provides methods to access
the hints provided by the user in the page.
\code
#include <QWizardPage>
#include "ui_ModelNamePage.h"
class ModelNamePage : public QWizardPage
{
Q_OBJECT
public:
ModelNamePage(QWidget* parent=0);
~ModelNamePage();
void setPath(const QString& path);
ModelClassParameters parameters() const;
private slots:
void on_txtModelClass_textEdited(const QString& txt);
private:
Ui::ModelNamePage ui;
QString path;
};
\endcode
The constructor and destructor are straight forward and easy to understand.
\code
ModelNamePage::ModelNamePage(QWidget* parent)
:QWizardPage(parent)
{
setTitle("Enter model class information");
setSubTitle("The header and source file names will be derived from the class name");
ui.setupUi(this);
}
ModelNamePage::~ModelNamePage()
{
}
\endcode
The \bold{setPath()} method basically stores the path in the private variable.
\code
void ModelNamePage::setPath(const QString& path)
{
this->path = path;
}
\endcode
The \bold{on_txtModelClass_textEdited()} slot computes the header and source file names based on the
classname.
\code
void ModelNamePage::on_txtModelClass_textEdited(const QString& txt)
{
ui.txtHeaderFile->setText(txt + ".h");
ui.txtImplFile->setText(txt + ".cpp");
}
\endcode
Finally the \bold{parameters()} method returns all the hints entered by the user in a ModelClassParameters
instance.
\code
ModelClassParameters ModelNamePage::parameters() const
{
ModelClassParameters params;
params.className = ui.txtModelClass->text();
params.headerFile = ui.txtHeaderFile->text();
params.sourceFile = ui.txtImplFile->text();
params.baseClass = ui.cmbBaseClass->currentText();
params.path = path;
return params;
}
\endcode
\section3 Step 3: Subclass Core::BaseFileWizard
The\bold {Core::BaseFileWizard} class is defined as follows in \bold{coreplugin/basefilewizard.h}
\code
class CORE_EXPORT BaseFileWizard : public IWizard
{
public:
virtual ~BaseFileWizard();
// IWizard
virtual Kind kind() const;
virtual QIcon icon() const;
virtual QString description() const;
virtual QString name() const;
virtual QString category() const;
virtual QString trCategory() const;
virtual QStringList runWizard(const QString &path, QWidget *parent);
protected:
typedef QList<QWizardPage *> WizardPageList;
explicit BaseFileWizard(const BaseFileWizardParameters &parameters,QObject *parent = 0);
virtual QWizard *createWizardDialog(QWidget *parent,const QString &defaultPath,
const WizardPageList &extensionPages) const = 0;
virtual GeneratedFiles generateFiles(const QWizard *w,QString *errorMessage) const = 0;
virtual bool postGenerateFiles(const GeneratedFiles &l,QString *errorMessage);
};
\endcode
\underline {\bold{Note: Some methods from the actual BaseFileWizard class are not shown here.}}
The \bold{BaseFileWizard} class implements the \bold{IWizard} interface and offers three new functions
\list
\o \bold{createWizardDialog} - This function can be over-ridden by subclasses to provide a wizard that the
\bold{runWizard()} method is supposed to show.
\list
\o The \bold{parent} parameter should be used as the parent widget of the returned QWizard
\o The \bold{defaultPath} parameter should be the default location for generated files
\o The \bold{extensionPages} parameter lists out all the pages that should be shown in the wizard by default.
\endlist
\o \bold{generateFiles} - This method is called after the user is done with the wizard. Implementations of this
method must create the required files as instances of \bold{Core::GeneratedFile} class.
\o \bold{postGenerateFiles} - This method is called after \bold{generateFiles()} returns. The default
implementation opens the newly generated files; however subclasses can choose to do anything they want.
\endlist
We subclass the BaseFileWizard as follows for our "item model" wizard
\code
#include <coreplugin/basefilewizard.h>
class ModelClassWizard : public Core::BaseFileWizard
{
Q_OBJECT
public:
ModelClassWizard(const Core::BaseFileWizardParameters &parameters,
QObject *parent = 0);
~ModelClassWizard();
QWizard *createWizardDialog(QWidget *parent,
const QString &defaultPath,
const WizardPageList &extensionPages) const;
Core::GeneratedFiles generateFiles(const QWizard *w,
QString *errorMessage) const;
private:
QString readFile(const QString& fileName,
const QMap<QString,QString>& replacementMap) const;
};
\endcode
The constructor and destructor methods are straight forward and easy to understand.
\code
ModelClassWizard::ModelClassWizard(
const Core::BaseFileWizardParameters &parameters,QObject *parent)
: Core::BaseFileWizard(parameters, parent)
{
}
ModelClassWizard::~ModelClassWizard()
{
}
\endcode
The \bold{createWizardDialog()} method is implemented to create a \bold{QWizard} with its first page as the
\bold{ModelNamePage} class implemented step 2. Other default pages are added as usual.
\code
QWizard* ModelClassWizard::createWizardDialog(
QWidget *parent,
const QString &defaultPath,
const WizardPageList &extensionPages) const
{
// Create a wizard
QWizard* wizard = new QWizard(parent);
wizard->setWindowTitle("Model Class Wizard");
// Make our page as first page
ModelNamePage* page = new ModelNamePage(wizard);
int pageId = wizard->addPage(page);
wizard->setProperty("_PageId_", pageId);
page->setPath(defaultPath);
// Now add the remaining pages
foreach (QWizardPage *p, extensionPages)
wizard->addPage(p);
return wizard;
}
\endcode
The \bold{readFile()} method is implemented to read a file and return its contents as a string. Before returning the file<6C>s
contents as string, the function uses the replacement table passed as second parameter to fix the string.
\code
QString ModelClassWizard::readFile(const QString& fileName, const QMap<QString,QString>&
replacementMap) const
{
QFile file(fileName);
file.open(QFile::ReadOnly);
QString retStr = file.readAll();
QMap<QString,QString>::const_iterator it = replacementMap.begin();
QMap<QString,QString>::const_iterator end = replacementMap.end();
while(it != end)
{
retStr.replace(it.key(), it.value());
++it;
}
return retStr;
}
\endcode
Suppose we have a file (\bold{sample.txt}) whose contents are as follows
\code
#ifndef {{UPPER_CLASS_NAME}}_H
#define {{UPPER_CLASS_NAME}}_H
#include <{{BASE_CLASS_NAME}}>
struct {{CLASS_NAME}}Data;
class {{CLASS_NAME}} : public {{BASE_CLASS_NAME}}
{
Q_OBJECT
public:
{{CLASS_NAME}}(QObject* parent=0);
~{{CLASS_NAME}}();
int rowCount(const QModelIndex& parent) const;
QVariant data(const QModelIndex& index, int role) const;
private:
{{CLASS_NAME}}Data* d;
};
#endif // {{UPPER_CLASS_NAME}}_H
\endcode
Lets say we wanted to replace the hints in {{xyz}} with something more appropriate, we could use the following code
snippet.
\code
QMap<QString,QString> replacementMap;
replacementMap["{{UPPER_CLASS_NAME}}"] = "LIST_MODEL";
replacementMap["{{BASE_CLASS_NAME}}"] = "QAbstractListModel";
replacementMap["{{CLASS_NAME}}"] = "ListModel";
QString contents = readFile("Sample.txt", replacementTable);
\endcode
When the above code is executed, the contents string will contain
\code
#ifndef LIST_MODEL_H
#define LIST_MODEL_H
#include <QAbstractListModel>
struct ListModelData;
class ListModel : public QAbstractListModel
{
Q_OBJECT
public:
ListModel(QObject* parent=0);
~ListModel();
int rowCount(const QModelIndex& parent) const;
QVariant data(const QModelIndex& index, int role) const;
private:
ListModelData* d;
};
#endif // LIST_MODEL_H
\endcode
Seems like magic isnt it? ?. We create similar "template" header and source files for item, list and table model classes
and create a resource for use in our project.
Now, lets look at the implementation of the \bold{generateFiles()} method. This method basically creates two
\bold{Core::GeneratedFile} instances and populates them with appropriate data before returning them in a list.
\code
Core::GeneratedFiles ModelClassWizard::generateFiles(
const QWizard *w,QString *errorMessage) const
{
Q_UNUSED(errorMessage);
Core::GeneratedFiles ret;
int pageId = w->property("_PageId_").toInt();
ModelNamePage* page = qobject_cast<ModelNamePage*>(w->page(pageId));
if(!page)
return ret;
ModelClassParameters params = page->parameters();
QMap<QString,QString> replacementMap;
replacementMap["{{UPPER_CLASS_NAME}}"] = params.className.toUpper();
replacementMap["{{BASE_CLASS_NAME}}"] = params.baseClass;
replacementMap["{{CLASS_NAME}}"] = params.className;
replacementMap["{{CLASS_HEADER}}"] = QFileInfo(params.headerFile).fileName();
Core::GeneratedFile headerFile(params.path + "/" + params.headerFile);
headerFile.setEditorKind(CppEditor::Constants::CPPEDITOR_KIND);
Core::GeneratedFile sourceFile(params.path + "/" + params.sourceFile);
sourceFile.setEditorKind(CppEditor::Constants::CPPEDITOR_KIND);
if(params.baseClass == "QAbstractItemModel")
{
headerFile.setContents(readFile(":/CustomProject/ItemModelHeader", replacementMap) );
sourceFile.setContents(readFile(":/CustomProject/ItemModelSource", replacementMap) );
}
else if(params.baseClass == "QAbstractTableModel")
{
headerFile.setContents(readFile(":/CustomProject/TableModelHeader", replacementMap) );
sourceFile.setContents(readFile(":/CustomProject/TableModelSource", replacementMap) );
}
else if(params.baseClass == "QAbstractListModel")
{
headerFile.setContents(readFile(":/CustomProject/ListModelHeader", replacementMap) );
sourceFile.setContents(readFile(":/CustomProject/ListModelSource", replacementMap) );
}
ret << headerFile << sourceFile;
return ret;
}
\endcode
\section3 Step 4: Implementing the plugin
We implement the item model wizard plugin using the same means as described in Chapter 2. The only change is in the
\bold {initialize()} method implementation of the plugin.
\code
bool ItemModelWizard::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
Core::BaseFileWizardParameters params;
params.setKind(Core::IWizard::ClassWizard);
params.setIcon(qApp->windowIcon());
params.setDescription("Generates an item-model class");
params.setName("Item Model");
params.setCategory("FooCompany");
params.setTrCategory(tr("FooCompany"));
addAutoReleasedObject(new ModelClassWizard(params, this));
return true;
}
\endcode
\section3 Step 5: Testing the plugin
Upon compiling the plugin and restarting Qt Creator, we can notice the new project type in the "New.." dialog box. The
following screenshots showcase the wizard that was just implemented.
\inlineimage qtc-newitemmodel-8.png
Upon selecting the "Item Model" class wizard, we can see the ModelNamePage in a custom wizard.
\inlineimage qtc-customwizardone-8.png
We enter the appropriate details and click "Next" Qt Creator then shows us a built-in page to allow addition of the
newly generated files into the current project.
\inlineimage qtc-customwizardtwo-8.png
Upon clicking "Finish", we can notice the newly generated files in the editor.
\inlineimage qtc-editor-8.png
*/

View File

@@ -0,0 +1,329 @@
/*!
\page qtc-arch.html
\title 4. Qt Creator Architecture
Every large system has a well defined "system architecture" which if understood well makes it easy for us to find out
way in it. Qt Creator is no different. In this chapter we will understand the basic architecture of Qt Creator so that we
can continue our understanding of writing plugins.
\section1 4.1 Nuts and Bolts of Qt Creator
Qt Creator is Nokia's cross-platform IDE. Currently Qt Creator is mainly used for writing Qt/C++ code.
The core of Qt Creator is basically only a "plugin loader" All functionality is implemented in plugins.
\inlineimage qtc-pluginmanager-4.png
The core "personality"of Qt Creator is implemented in the \bold {Core Plugin (Core::ICore)}. We have already had a brush
with the core plugin in the previous chapter. In the rest of this document we will refer to "core plugin" as Core.
The plugin manager \bold{(ExtensionSystem::PluginManager)} provides simple means for plugin cooperation that
allow plugins to provide hooks for other plugin's extensions.
\section1 4.2 What exactly is a plugin?
At the most fundamental level plugin is a shared library (DLL file on Windows, SO file on Linux, DYLIB file on Mac). From
a developer's point of view plugin is a module that
\list 1
\o Implements the ExtensionSystem::IPlugin interface in a class. This class will be referred to as "Plugin Class" in the
rest of the document.
\o Exports the Plugin Class using the Q_EXPORT_PLUGIN macro
\o Provides a pluginspec file that provides some meta information about the plugin
\o Registers one or more objects that might be of some interest to other plugins
\o Searches for the availability of one or more objects registered by other plugins.
\endlist
We have already had some experience with the first three aspects listed above, but we have not touched upon the last
two.
\section2 4.2.1 What are registered objects?
objects are those that land up in the \bold {PluginManager's} object pool. The \bold {allObjects()} method in
\bold {PluginManager} returns the object pool as a list of QObject pointers. Shown below is the code that we can use to list
all objects in the object-pool in a \bold {QListWidget}.
\code
#include <extensionsystem/pluginmanager.h>
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
QList<QObject*> objects = pm->allObjects();
QListWidget* listWidget = new QListWidget;
Q_FOREACH(QObject* obj, objects)
{
QString objInfo = QString("%1 (%2)").arg(obj->objectName()).arg(obj->metaObject()->className());
listWidget->addItem(objInfo);
}
listWidget->show();
\endcode
When such a list widget is constructed and shown; you will see a window as shown below.
\inlineimage qtc-objectlist-4.png
From the class names it is easy to picture the fact that each of those objects came from different plugins.
\bold {\underline {A registered object is an instance of QObject (or one of its subclasses) registered by a plugin and is available in the
object-pool for other plugins to make use of}}.
\section2 4.2.2 How to "register" an object from a plugin?
There are three ways to register an object from a plugin:
\list
\o \bold {IPlugin::addAutoReleasedObject(QObject*)}
\o \bold {IPlugin::addObject(QObject*)}
\o \bold {PluginManager::addObject(QObject*)}
\endlist
The \bold {IPlugin::addObject()} and \bold {IPlugin::addAutoReleasedObject()} essentially call the
\bold {PluginManager::addObject()} method. The \bold {IPlugin} methods are only provided for convenience. It is
recommended that plugins make use of the \bold {IPlugin} methods for adding objects.
The only difference between \bold {addAutoReleasedObject()} and \bold {addObject()} is that objects added using the
former method are automatically removed and deleted in the reverse order of registration when the plugin is destroyed.
At anytime plugins can make use of the \bold {IPlugin::removeObject(QObject*)} method to remove its object from
the object pool.
\section2 4.2.3 What objects to register?
Plugins can register just about any object. Normally objects that provide some sort of functionality used by other
plugin(s) are registered. Functionalities in Qt Creator are defined by means of interfaces. Listed below are some interfaces
\list
\o \bold {Core::INavigationWidgetFactory}
\o \bold {Core::IEditor}
\o \bold {Core::IOptionsPage}
\o \bold {Core::IWizard}
\endlist
\bold{\underline { C++ developers normally assume interfaces to be classes with all its functions are public pure
virtual functions. In Qt Creator interfaces are subclasses of QObject that offer one or more
methods are pure virtual}}.
If a plugin has objects that implement an interface, then such an object has to be registered. For example if a plugin
implements the \bold{INavigationWidgetFactory} interface in an object and registered it, the Core will automatically use that
object to show the widget provided by it as navigation widget. Take a look at the code snippet below. We provide a
simple \bold{QTableWidget} as navigation widget via an implementation of \bold {Core::INavigationWidgetFactory}.
\code
#include <coreplugin/inavigationwidgetfactory.h>
class NavWidgetFactory : public Core::INavigationWidgetFactory
{
public:
NavWidgetFactory();
~NavWidgetFactory();
Core::NavigationView createWidget();
QString displayName();
};
#include <QTableWidget>
NavWidgetFactory::NavWidgetFactory() { }
NavWidgetFactory::~NavWidgetFactory() { }
Core::NavigationView NavWidgetFactory::createWidget()
{
Core::NavigationView view;
view.widget = new QTableWidget(50, 3);
}
QString NavWidgetFactory::displayName()
{
return "Spreadsheet";
}
bool MyPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
// Provide a navigation widget factory.
// Qt Creator's navigation widget will automatically
// hook to our INavigationWidgetFactory implementation, which
// is the NavWidgetFactory class, and show the QTableWidget
// created by it in the navigation panel.
addAutoReleasedObject(new NavWidgetFactory);
return true;
}
\endcode
The effect of the above code is
\inlineimage qtc-codeeffect-4.png
\section2 4.2.4 Becoming aware of registered objects
Whenever the \bold {PluginManager::addObject()} is used to add an object, it \bold{(PluginManager)} emits the
\bold {objectAdded(QObject*)} signal. This signal can be used within our applications to figure out the objects that got
added.
Obviously a plugin will begin receiving the signal only after it makes a connection to it. That happens only after the
plugin is initialized; which also means that the plugin will receive the \bold {objectAdded()} signal only for objects added
after the plugin was initialized.
Usually the slot that is connected to the objectAdded() signal will look for one or more known interfaces. Suppose that
your plugin is looking for the INavigationWidgetFactory interface, the slot connected to objectAdded() will be like the
one shown below.
\code
void Plugin::slotObjectAdded(QObject * obj)
{
INavigationWidgetFactory *factory = Aggregation::query<INavigationWidgetFactory>(obj);
if(factory)
{
// use it here...
}
}
\endcode
\section2 4.2.5 Searching for objects
Sometimes a plugin might want to search for an object in the application that offers some functionality. We already
know by now that
\list
\o \bold {PluginManager::allObjects()} returns the object pool as a \bold {QList<QObject*>}
\o Connecting to \bold {PluginManager::objectAdded()} signal helps in known objects as they get registered
\endlist
Using both of the above mentioned methods you can look for objects. Lets now understand yet another way to find
objects.
Suppose that you wanted to look for objects that implement the \bold {INavigationWidgetFactory} interface and show it in a
\bold {QListWidget}. You can make use of the \bold {PluginManager::getObjects<T>()} method for this purpose. The following code
snippet explains this
\code
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
QList<Core::INavigationWidgetFactory*> objects = pm->getObjects<Core::INavigationWidgetFactory>();
QListWidget* listWidget = new QListWidget();
Q_FOREACH(Core::INavigationWidgetFactory* obj, objects)
{
QString objInfo = QString("%1 (%2)").arg(obj->displayName()).arg(obj->metaObject()->className());
listWidget->addItem(objInfo);
}
\endcode
When the list widget is shown you will notice that the navigation widgets are shown in the same order as they are
shown in the navigation combo box. Take a look at the screenshot below.
\inlineimage qtc-nevigationwidget-4.png
\section1 4.3 Aggregations
Aggregations are provided by the \bold {Aggregation} namespace. It adds functionality for "glueing" \bold {QObjects} of different
types together, so you can "cast" between them. Using the classes and methods in this namespace you can bundle
related objects into a single entity. Objects that are bundled into an aggregate can be "cast" from the aggregate into the
object class type.
\section2 4.3.1 Aggregations - the old fashioned way
Suppose that you wanted an object that provided implementations of two interfaces. Normally we would go about
coding the object like this.
\code
class Interface1
{
....
};
Q_DECLARE_INTERFACE("Interface1", "Interface1");
class Interface2
{
....
};
Q_DECLARE_INTERFACE("Interface2", "Interface2");
class Bundle : public QObject,public Interface1,public Interface2
{
Q_OBJECT(Interface1 Interface2)
....
};
Bundle bundle;
\endcode
Now we can think of \bold {bundle} as an object that provides \bold {Interface1} and \bold {Interface2} implementations. We can
make use of casting operators on the bundle object to extract \bold{Interface1} and \bold {Interface2}.
\code
Interface1* iface1Ptr = qobject_cast<Interface1*>(&bundle);
Interface2* iface2Ptr = qobject_cast<Interface2*>(&bundle);
\endcode
\section2 4.3.2 Aggregations - the Qt Creator way
Qt Creator's Aggregation library offers a cleaner way to define interfaces and bundle them into a single object. Instances
of Aggregation::Aggregate can be created and objects can be added to it. Each of the objects added to the aggregation
can implement an interface. The following code snippet shows how to create an aggregation.
\code
#include <aggregation/aggregate.h>
class Interface1 : public QObject
{
Q_OBJECT
public:
Interface1() { }
~Interface1() { }
};
class Interface2 : public QObject
{
Q_OBJECT
public:
Interface2() { }
~Interface2() { }
};
Aggregation::Aggregate bundle;
bundle.add(new Interface1);
bundle.add(new Interface2);
\endcode
The aggregation instance "bundle" now conceptually contains implementations of two interfaces. To extract the
interfaces we can make use of the following code
\code
Interface1* iface1Ptr = Aggregation::query<Interface1>( &bundle );
Interface2* iface2Ptr = Aggregation::query<Interface2>( &bundle );
\endcode
With aggregation you can also several objects of the same interface into a single bundle. For example
\code
Aggregation::Aggregate bundle;
bundle.add(new Interface1);
bundle.add(new Interface2);
bundle.add(new Interface1);
bundle.add(new Interface1);
QList<Interface1*> iface1Ptrs = Aggregation::query_all<Interface1>( &bundle );
\endcode
Another key advantage of Aggregation is that, you can delete any one of the objects in the bundle to delete the whole
bundle. Example
\code
Aggregation::Aggregate* bundle = new Aggregation::Aggregate;
bundle->add(new Interface1);
bundle->add(new Interface2);
Interface1* iface1Ptr = Aggregation::query<Interface1>(bundle);
delete iface1Ptr;
// deletes the bundle and all objects in it
// same as delete bundle
\endcode
The use of aggregation will become clearer when we deal with real plugin examples in the coming chapters.
*/

View File

@@ -0,0 +1,6 @@
/*!
\page qtc-project-plugin.html
\title 3. Creating plugins using Qt Creator
This page needs to be written still!
*/

View File

@@ -0,0 +1,25 @@
/*!
\page qtcreator-compile.html
\title 1. Compiling Qt Creator
To start with building Qt creator, you need to get the Qt Creator source. The Qt Creator source is avaiable
from the following locations:
\list
\o Nokia Website: \l {http://qt.nokia.com/downloads}
\o GIT Repository: \l {http://qt.gitorious.org/qt-creator}
\endlist
It is recommended that you build Qt Creator in a separate directory.
The following steps will guide you to get Qt Creator source compiled:
\list
\o Step 1: Create "build" directory inside main source directory
\o Step 2: Run qmake ../qtcreator.pro -recursive command to get makefile.
\o Step 3: Finally run make (or mingw32-make or nmake, depending on your platform).
\endlist
After successful compilation, you should be able to find \bold {qtcreator.exe} inside \bold {build/bin} directory.
You can now launch Qt Creator.
\bold {Note:}\underline{It is important that you compile Qt Creator from its sources. Otherwise you wont be able to write
and test plugins for Qt Creator}.
*/

View File

@@ -0,0 +1,28 @@
/*!
\page index.html
\title Qt Creator Plugin HOWTO
Qt Creator is not only a great developer tool, it is also a tool
can accomodate plugins to help......
Qt Creator plugin development can be understood by going through
the following chapters in this manual
\list 1
\o \l {qtcreator-compile.html} {Compiling Qt Creator}
\o \l {first-plugin.html} {First Plugin}
\o \l {qtc-project-plugin.html} {Creating plugins using Qt Creator}
\o \l {qtc-arch.html} {Qt Creator Architecture}
\o \l {menu.html} {Adding menu and menu-items}
\o \l {nav-widget.html} {Adding Navigation Widget}
\o \l {pref-pane.html} {Adding Preferences Pane}
\o \l {project-file-wizard.html} {Project/File Wizards}
\o \l {find-filter.html} {Find Filter}
\o \l {editor-type.html} {Adding editor type}
\o \l {progress.html} {Showing and reacting to progress information}
\o \l {vcs.html} {Implementing version control system}
\o \l {mode.html} {Adding a mode}
\o \l {location-filter.html} {Adding location filter}
\o \l {adv-find-filter.html} {Adding "advanced" find filter}
\endlist
*/

View File

@@ -0,0 +1,221 @@
project = Qt Creator Plugin HOWTO API
description = Qt Creator Plugin HOWTO Documentation
language = Cpp
sources.fileextensions = "*.qdoc"
sourcedirs = .
imagedirs = images
indexes = $QTDIR/doc/html/qt.index
outputdir = ./html
base = file:./html
versionsym = 1.3.1
codeindent = 1
tabsize = 4
## compat.qdocconf
alias.i = e
alias.include = input
macro.0 = "\\\\0"
macro.b = "\\\\b"
macro.n = "\\\\n"
macro.r = "\\\\r"
macro.i = "\\o"
macro.i11 = "\\o{1,1}"
macro.i12 = "\\o{1,2}"
macro.i13 = "\\o{1,3}"
macro.i14 = "\\o{1,4}"
macro.i15 = "\\o{1,5}"
macro.i16 = "\\o{1,6}"
macro.i17 = "\\o{1,7}"
macro.i18 = "\\o{1,8}"
macro.i19 = "\\o{1,9}"
macro.i21 = "\\o{2,1}"
macro.i31 = "\\o{3,1}"
macro.i41 = "\\o{4,1}"
macro.i51 = "\\o{5,1}"
macro.i61 = "\\o{6,1}"
macro.i71 = "\\o{7,1}"
macro.i81 = "\\o{8,1}"
macro.i91 = "\\o{9,1}"
macro.img = "\\image"
macro.endquote = "\\endquotation"
macro.relatesto = "\\relates"
spurious = "Missing comma in .*" \
"Missing pattern .*"
## macros.qdocconf
macro.aring.HTML = "&aring;"
macro.Auml.HTML = "&Auml;"
macro.author = "\\bold{Author:}"
macro.br.HTML = "<br />"
macro.BR.HTML = "<br />"
macro.aacute.HTML = "&aacute;"
macro.eacute.HTML = "&eacute;"
macro.iacute.HTML = "&iacute;"
macro.gui = "\\bold"
macro.hr.HTML = "<hr />"
macro.key = "\\bold"
macro.menu = "\\bold"
macro.note = "\\bold{Note:}"
macro.oslash.HTML = "&oslash;"
macro.ouml.HTML = "&ouml;"
macro.QA = "\\e{Qt Assistant}"
macro.QD = "\\e{Qt Designer}"
macro.QL = "\\e{Qt Linguist}"
macro.QC = "\\e{Qt Creator}"
macro.param = "\\e"
macro.raisedaster.HTML = "<sup>*</sup>"
macro.reg.HTML = "<sup>&reg;</sup>"
macro.return = "Returns"
macro.starslash = "\\c{*/}"
macro.uuml.HTML = "&uuml;"
macro.mdash.HTML = "&mdash;"
## qt-cpp-ignore.qdocconf
Cpp.ignoretokens = QAXFACTORY_EXPORT \
QDESIGNER_COMPONENTS_LIBRARY \
QDESIGNER_EXTENSION_LIBRARY \
QDESIGNER_SDK_LIBRARY \
QDESIGNER_SHARED_LIBRARY \
QDESIGNER_UILIB_LIBRARY \
QM_EXPORT_CANVAS \
QM_EXPORT_DNS \
QM_EXPORT_DOM \
QM_EXPORT_FTP \
QM_EXPORT_HTTP \
QM_EXPORT_ICONVIEW \
QM_EXPORT_NETWORK \
QM_EXPORT_OPENGL \
QM_EXPORT_SQL \
QM_EXPORT_TABLE \
QM_EXPORT_WORKSPACE \
QM_EXPORT_XML \
QT_ASCII_CAST_WARN \
QT_ASCII_CAST_WARN_CONSTRUCTOR \
QT_BEGIN_HEADER \
QT_DESIGNER_STATIC \
QT_END_HEADER \
QT_FASTCALL \
QT_WIDGET_PLUGIN_EXPORT \
Q_COMPAT_EXPORT \
Q_CORE_EXPORT \
Q_EXPLICIT \
Q_EXPORT \
Q_EXPORT_CODECS_CN \
Q_EXPORT_CODECS_JP \
Q_EXPORT_CODECS_KR \
Q_EXPORT_PLUGIN \
Q_GFX_INLINE \
Q_AUTOTEST_EXPORT \
Q_GUI_EXPORT \
Q_GUI_EXPORT_INLINE \
Q_GUI_EXPORT_STYLE_CDE \
Q_GUI_EXPORT_STYLE_COMPACT \
Q_GUI_EXPORT_STYLE_MAC \
Q_GUI_EXPORT_STYLE_MOTIF \
Q_GUI_EXPORT_STYLE_MOTIFPLUS \
Q_GUI_EXPORT_STYLE_PLATINUM \
Q_GUI_EXPORT_STYLE_POCKETPC \
Q_GUI_EXPORT_STYLE_SGI \
Q_GUI_EXPORT_STYLE_WINDOWS \
Q_GUI_EXPORT_STYLE_WINDOWSXP \
QHELP_EXPORT \
Q_INLINE_TEMPLATE \
Q_INTERNAL_WIN_NO_THROW \
Q_NETWORK_EXPORT \
Q_OPENGL_EXPORT \
Q_OUTOFLINE_TEMPLATE \
Q_SQL_EXPORT \
Q_SVG_EXPORT \
Q_SCRIPT_EXPORT \
Q_SCRIPTTOOLS_EXPORT \
Q_TESTLIB_EXPORT \
Q_TYPENAME \
Q_XML_EXPORT \
Q_XMLSTREAM_EXPORT \
Q_XMLPATTERNS_EXPORT \
QDBUS_EXPORT \
QT_BEGIN_NAMESPACE \
QT_BEGIN_INCLUDE_NAMESPACE \
QT_END_NAMESPACE \
QT_END_INCLUDE_NAMESPACE \
PHONON_EXPORT
Cpp.ignoredirectives = Q_DECLARE_HANDLE \
Q_DECLARE_INTERFACE \
Q_DECLARE_METATYPE \
Q_DECLARE_OPERATORS_FOR_FLAGS \
Q_DECLARE_PRIVATE \
Q_DECLARE_PUBLIC \
Q_DECLARE_SHARED \
Q_DECLARE_TR_FUNCTIONS \
Q_DECLARE_TYPEINFO \
Q_DISABLE_COPY \
QT_FORWARD_DECLARE_CLASS \
Q_DUMMY_COMPARISON_OPERATOR \
Q_ENUMS \
Q_FLAGS \
Q_INTERFACES \
__attribute__ \
K_DECLARE_PRIVATE \
PHONON_OBJECT \
PHONON_HEIR
## qt-html-templates.qdocconf
HTML.stylesheets = classic.css
HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \
"<tr>\n" \
"<td align=\"left\" valign=\"top\" width=\"32\">" \
"<a href=\"http://www.trolltech.com/products/qt\"><img src=\"images/qt-logo.png\" align=\"left\" border=\"0\" /></a>" \
"</td>\n" \
"<td width=\"1\">&nbsp;&nbsp;</td>" \
"<td class=\"postheader\" valign=\"center\">" \
"<a href=\"index.html\">" \
"<font color=\"#004faf\">Home</font></a>&nbsp;&middot;" \
" <a href=\"namespaces.html\">" \
"<font color=\"#004faf\">All&nbsp;Namespaces</font></a>&nbsp;&middot;" \
" <a href=\"classes.html\">" \
"<font color=\"#004faf\">All&nbsp;Classes</font></a>&nbsp;&middot;" \
" <a href=\"mainclasses.html\">" \
"<font color=\"#004faf\">Main&nbsp;Classes</font></a>&nbsp;&middot;" \
" <a href=\"groups.html\">" \
"<font color=\"#004faf\">Grouped&nbsp;Classes</font></a>&nbsp;&middot;" \
" <a href=\"modules.html\">" \
"<font color=\"#004faf\">Modules</font></a>&nbsp;&middot;" \
" <a href=\"functions.html\">" \
"<font color=\"#004faf\">Functions</font></a>" \
"</td>\n" \
"<td align=\"right\" valign=\"top\" width=\"230\"></td></tr></table>"
HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \
"<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \
"<td width=\"30%\" align=\"left\">Copyright &copy; 2010 Nokia Corporation " \
"and/or its subsidiary(-ies)</td>\n" \
"<td width=\"40%\" align=\"center\"><a href=\"trademarks.html\">Trademarks</a></td>\n" \
"<td width=\"30%\" align=\"right\"><div align=\"right\">Qt Creator Plugin HOWTO\\version</div></td>\n" \
"</tr></table></div></address>"
## qt-defines.qdocconf
defines = Q_QDOC \
QT_.*_SUPPORT \
QT_.*_LIB \
QT_COMPAT \
QT_KEYPAD_NAVIGATION \
QT3_SUPPORT \
Q_WS_.* \
Q_OS_.* \
Q_BYTE_ORDER \
QT_DEPRECATED \
Q_NO_USING_KEYWORD \
__cplusplus
# Files not referenced in any qdoc file (last four needed by qtdemo)
# See also qhp.Qt.extraFiles
extraimages.HTML = qt-logo \
trolltech-logo

6
doc/pluginhowto/vcs.qdoc Normal file
View File

@@ -0,0 +1,6 @@
/*!
\page vcs.html
\title 12.Implementing version control system
This page needs to be written still!
*/