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>
7
doc/pluginhowto/adv-find-filter.qdoc
Normal 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
@@ -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
|
||||
}
|
||||
998
doc/pluginhowto/editor-type.qdoc
Normal 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
|
||||
|
||||
*/
|
||||
549
doc/pluginhowto/find-filter.qdoc
Normal 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.
|
||||
|
||||
*/
|
||||
6
doc/pluginhowto/find-support.qdoc
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
\page find-support.html
|
||||
\title 9.Find support
|
||||
|
||||
This page needs to be written still!
|
||||
*/
|
||||
327
doc/pluginhowto/first-plugin.qdoc
Normal 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
|
||||
*/
|
||||
BIN
doc/pluginhowto/images/qtc-aboutplugin-2.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
doc/pluginhowto/images/qtc-codeeffect-4.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
doc/pluginhowto/images/qtc-compiledplugin-2.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
doc/pluginhowto/images/qtc-configwidget-9.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
doc/pluginhowto/images/qtc-customeditor-10.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
doc/pluginhowto/images/qtc-customwizardone-8.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
doc/pluginhowto/images/qtc-customwizardtwo-8.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
doc/pluginhowto/images/qtc-designer-8.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
doc/pluginhowto/images/qtc-dirview-6.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
doc/pluginhowto/images/qtc-donothingleft-5.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/pluginhowto/images/qtc-donothingright-5.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
doc/pluginhowto/images/qtc-editor-10.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
doc/pluginhowto/images/qtc-editor-8.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
doc/pluginhowto/images/qtc-editortabs-10.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
doc/pluginhowto/images/qtc-fileformat-10.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
doc/pluginhowto/images/qtc-finddialog-9.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/pluginhowto/images/qtc-firstnavigation-6.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
doc/pluginhowto/images/qtc-help-5.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/pluginhowto/images/qtc-helpdonothing-5.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
doc/pluginhowto/images/qtc-htmleditor-10.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
doc/pluginhowto/images/qtc-ieditor-10.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
doc/pluginhowto/images/qtc-installedplugin-2.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
doc/pluginhowto/images/qtc-loggermode-13.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
doc/pluginhowto/images/qtc-loggermodewidget-13.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
doc/pluginhowto/images/qtc-loggerpage-13.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
doc/pluginhowto/images/qtc-menubar-5.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
doc/pluginhowto/images/qtc-menuitem-10.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
doc/pluginhowto/images/qtc-menuitemposition-5.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
doc/pluginhowto/images/qtc-menuresponse-5.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
doc/pluginhowto/images/qtc-modes-13.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
doc/pluginhowto/images/qtc-nevigationwidget-4.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/pluginhowto/images/qtc-newitemmodel-8.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
doc/pluginhowto/images/qtc-newmode-13.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
doc/pluginhowto/images/qtc-newprojectdialog-8.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
doc/pluginhowto/images/qtc-objectlist-4.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
doc/pluginhowto/images/qtc-options-5.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
doc/pluginhowto/images/qtc-options-7.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/pluginhowto/images/qtc-options-keyboard-5.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/pluginhowto/images/qtc-plaintextedit-10.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
doc/pluginhowto/images/qtc-plugindirectory-2.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
doc/pluginhowto/images/qtc-pluginmanager-4.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc/pluginhowto/images/qtc-progressbar-11.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
doc/pluginhowto/images/qtc-search-9.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
doc/pluginhowto/images/qtc-searchprogress-11.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
doc/pluginhowto/images/qtc-secondnavigation-6.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
doc/pluginhowto/images/qtc-testplugin-7.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
doc/pluginhowto/images/qtc-testplugin-8.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
doc/pluginhowto/images/qtc-testplugin-9.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/pluginhowto/images/qtc-title-10.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
6
doc/pluginhowto/location-filter.qdoc
Normal 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
@@ -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
@@ -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
|
||||
|
||||
*/
|
||||
171
doc/pluginhowto/nav-widget.qdoc
Normal 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
|
||||
|
||||
*/
|
||||
205
doc/pluginhowto/pref-pane.qdoc
Normal 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
|
||||
|
||||
*/
|
||||
111
doc/pluginhowto/progress.qdoc
Normal 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
|
||||
|
||||
*/
|
||||
667
doc/pluginhowto/project-file-wizard.qdoc
Normal 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 ¶meters,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 ¶meters,
|
||||
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 ¶meters,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
|
||||
|
||||
*/
|
||||
329
doc/pluginhowto/qtc-arch.qdoc
Normal 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.
|
||||
*/
|
||||
6
doc/pluginhowto/qtc-project-plugin.qdoc
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
\page qtc-project-plugin.html
|
||||
\title 3. Creating plugins using Qt Creator
|
||||
|
||||
This page needs to be written still!
|
||||
*/
|
||||
25
doc/pluginhowto/qtcreator-compile.qdoc
Normal 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}.
|
||||
|
||||
*/
|
||||
28
doc/pluginhowto/qtcreator-pluginhowto.qdoc
Normal 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
|
||||
*/
|
||||
221
doc/pluginhowto/qtcreator-pluginhowto.qdocconf
Normal 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 = "å"
|
||||
macro.Auml.HTML = "Ä"
|
||||
macro.author = "\\bold{Author:}"
|
||||
macro.br.HTML = "<br />"
|
||||
macro.BR.HTML = "<br />"
|
||||
macro.aacute.HTML = "á"
|
||||
macro.eacute.HTML = "é"
|
||||
macro.iacute.HTML = "í"
|
||||
macro.gui = "\\bold"
|
||||
macro.hr.HTML = "<hr />"
|
||||
macro.key = "\\bold"
|
||||
macro.menu = "\\bold"
|
||||
macro.note = "\\bold{Note:}"
|
||||
macro.oslash.HTML = "ø"
|
||||
macro.ouml.HTML = "ö"
|
||||
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>®</sup>"
|
||||
macro.return = "Returns"
|
||||
macro.starslash = "\\c{*/}"
|
||||
macro.uuml.HTML = "ü"
|
||||
macro.mdash.HTML = "—"
|
||||
|
||||
## 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\"> </td>" \
|
||||
"<td class=\"postheader\" valign=\"center\">" \
|
||||
"<a href=\"index.html\">" \
|
||||
"<font color=\"#004faf\">Home</font></a> ·" \
|
||||
" <a href=\"namespaces.html\">" \
|
||||
"<font color=\"#004faf\">All Namespaces</font></a> ·" \
|
||||
" <a href=\"classes.html\">" \
|
||||
"<font color=\"#004faf\">All Classes</font></a> ·" \
|
||||
" <a href=\"mainclasses.html\">" \
|
||||
"<font color=\"#004faf\">Main Classes</font></a> ·" \
|
||||
" <a href=\"groups.html\">" \
|
||||
"<font color=\"#004faf\">Grouped Classes</font></a> ·" \
|
||||
" <a href=\"modules.html\">" \
|
||||
"<font color=\"#004faf\">Modules</font></a> ·" \
|
||||
" <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 © 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
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
\page vcs.html
|
||||
\title 12.Implementing version control system
|
||||
|
||||
This page needs to be written still!
|
||||
*/
|
||||