Grantlee  5.1.0
Handling custom QTextObjects

It is possible using the QTextObjectInterface API to construct QTextDocuments containing custom text objects. If processing a QTextDocument containing custom objects through the MarkupDirector and AbstractMarkupBuilder classes, it is necessary to implement support for the custom types in Grantlee.

In this example, we create a builder with a new method addAudioTag. As it may be desired to implement multiple builders with support for the addAudioTag method, it makes sense to put that call into an interface.

class AbstractAudioBuilder : virtual public Grantlee::AbstractMarkupBuilder
{
public:
virtual void addAudioTag( const QString &source ) = 0;
};

We create a small interface for supporting the addition of audio markup. The interface should inherit virtually from the AbstractMarkupBuilder interface, so that functionality from through that interface is also available.

Customized builders inheriting from an existing builder and the new interface can then be created.

class AudioTextHtmlBuilder : public Grantlee::TextHTMLBuilder, public AbstractAudioBuilder
{
public:
AudioTextHtmlBuilder();
/* reimp */ void addAudioTag( const QString &source );
};
class AudioPlainTextBuilder : public Grantlee::PlainTextMarkupBuilder, public AbstractAudioBuilder
{
public:
AudioPlainTextBuilder();
/* reimp */ void addAudioTag( const QString &source );
};

The implementation of each concrete builder is appropriate to the type of markup being created.

void AudioTextHtmlBuilder::addAudioTag( const QString &source )
{
appendRawText( QString( "<audio src=\"%1\" />").arg( source ) );
}
void AudioPlainTextBuilder::addAudioTag(const QString& source)
{
int ref = addReference( source );
appendRawText( QString( "[%1]" ).arg( ref ) );
}

The final part to be implemented is support for the custom type in a MarkupDirector. The MarkupDirector::processCustomFragment method is called for any QTextFragments with custom QTextFormat types. This method can be implemented to instruct the builder to create appropriate output for the type.

class AudioTextDocumentDirector : public Grantlee::MarkupDirector
{
public:
AudioTextDocumentDirector(AbstractAudioBuilder* builder);
/* reimp */ void processCustomFragment( const QTextFragment& fragment, const QTextDocument* doc);
private:
AbstractAudioBuilder *m_builder;
};

We create a subclass of Grantlee::MarkupDirector to take our new AbstractAudioBuilder interface and implement the processCustomFragment method to handle QTextFragments with our custom format type. The name of the audio file referenced in the document is then extracted and used as a parameter in the addAudioTag method.

AudioTextDocumentDirector::AudioTextDocumentDirector(AbstractAudioBuilder* builder)
: MarkupDirector(builder), m_builder(builder)
{
}
void AudioTextDocumentDirector::processCustomFragment(const QTextFragment& fragment, const QTextDocument* doc)
{
if ( fragment.charFormat().objectType() != AudioType )
QString name = fragment.charFormat().property( AudioProperty ).toString();
m_builder->addAudioTag( name );
}

The custom AudioTextDocumentDirector and builders can then be used to create output including markup for custom types.

AudioTextHtmlBuilder *builder = new AudioTextHtmlBuilder();
AudioTextDocumentDirector *director = new AudioTextDocumentDirector(builder);
director->processDocument(document);
QString result = builder->getResult();

In a simple case, this could create output such as:

  <p>Here is a paragraph with an inline audio element: <audio src="http://www.example.com/mysound.ogg" />.

  <p>And another paragraph.

or if the AudioPlainTextBuilder was used instead:

  Here is a paragraph with an inline audio element: [1].

  And another paragraph.

  ----
  [1] http://www.example.com/mysound.ogg

The textedit example in the Grantlee source distribution implements this feature.