[calligra] /: Make calligra compile with clang
Matthew Rezny
mrezny at hexaneinc.com
Sun Apr 22 00:25:36 BST 2012
I had only glanced over this commit yesterday, but today noticed it doesn't include the whole patch that was submitted and thus one compile error remains.
Based on discussion following the original submission, I realize it probably would have been helpful to break it apart a bit as some changes were to correct what appear to be coding errors, some changes were for clang compilation errors, and other changes were just silencing warnings (making it easier to find the rest). So, here is a brief rundown of each part, both committed and not so that its clear what remains to be done for Clang. First will be the parts of the patch that were not committed, with the rest following inlined.
--- ./libs/pigment/KoCompositeOp.h.orig 2012-04-19 10:00:10.930315809 +0200
+++ ./libs/pigment/KoCompositeOp.h 2012-04-19 10:24:10.524306616 +0200
@@ -79,7 +79,7 @@
* @param userVisible define whether or not that composite op should be visible in an user
* interface
*/
- KoCompositeOp(const KoColorSpace * cs, const QString& id, const QString& description, const QString & categoryMisc = categoryMisc());
+ KoCompositeOp(const KoColorSpace * cs, const QString& id, const QString& description, const QString & categoryMisc = KoCompositeOp::categoryMisc());
virtual ~KoCompositeOp();
/**
This change is needed to address a compilation error with Clang. I do not know the exact rule, but it would seem that when specifying default values in function definitions, anything that is a function call needs to be fully qualified with class name even if it is part of this class. I'm not sure why this wasn't committed, but it was one of the actual errors which will stop compilation so it would be helpful to have it included.
--- filters/words/hancomword/pole.cpp.orig 2012-04-20 13:21:24.441302350 +0200
+++ filters/words/hancomword/pole.cpp 2012-04-20 13:24:55.353305032 +0200
@@ -694,7 +694,7 @@
// CLSID, contains a object class GUI if this entry is a storage or root
// storage or all zero if not.
- printf("DirTree::load name=%s type=%i prev=%i next=%i child=%i start=%i size=%i clsid=%i.%i.%i.%i\n",
+ printf("DirTree::load name=%s type=%i prev=%i next=%i child=%i start=%lu size=%lu clsid=%lu.%lu.%lu.%lu\n",
name.c_str(),type,e.prev,e.next,e.child,e.start,e.size,readU32(buffer+0x50+p),readU32(buffer+0x54+p),readU32(buffer+0x58+p),readU32(buffer+0x5C+p));
entries.push_back(e);
@@ -867,8 +867,8 @@
unsigned k = 109;
unsigned mblock = header->mbat_start;
for (unsigned r = 0; r < header->num_mbat; r++) {
- unsigned long r = loadBigBlock(mblock, buffer2, bbat->blockSize);
- if (r != bbat->blockSize) {
+ unsigned long rc = loadBigBlock(mblock, buffer2, bbat->blockSize);
+ if (rc != bbat->blockSize) {
delete[] buffer2;
return;
}
The first change here was just altering the format string to match the types as the compiler warned of possible truncation on type conversion, so nothing terribly important most likely. The second change was another error that stopped compilation as Clang would not allow the redefinition of r. Apparently gcc 4.7 also no longer allows this and thus a comparable change was made in trunk just a few days prior, making this change redundant and so obvious why it was not applied.
--- ./libs/kotext/opendocument/KoTextWriter_p.h.orig 2012-04-19 10:49:53.745306760 +0200
+++ ./libs/kotext/opendocument/KoTextWriter_p.h 2012-04-19 10:50:11.804306223 +0200
@@ -270,7 +270,7 @@
int splitEndBlockNumber;
bool splitRegionOpened;
- bool splitIdCounter;
+ int splitIdCounter;
//For saving of delete-changes that result in a merge between two elements
bool deleteMergeRegionOpened;
This was one of the more interesting changes in that it required some careful thought. I figure that it might not have been applied as it looks intrusive so I'll explain the motive. My attention was called to it by a compiler warning on its use (splitIdCounter++), which doesn't make much sense for a bool. After looking around, I found that it is always initialized with a value of 1, the only change of its value is the aforementioned increment, it's used as a number and it's called a counter. So, all signs point to it should be an int. As a bool, it will always be true (non-zero) and may appear strangely when interpreted as if it were an int. Someone familiar with this section of code should review it.
On 20 Apr, 2012, at 16:40, Boudewijn Rempt wrote:
> Git commit c804712c97b24a3774a53a3825f50762089f1f49 by Boudewijn Rempt.
> Committed on 20/04/2012 at 16:35.
> Pushed by rempt into branch 'master'.
>
> Make calligra compile with clang
>
> Clang has some additional restriction.
>
> if (foo = bar) becomes if ((foo = bar)), while (foo=bar) while ((foo=bar))
> and if ((baz==boz)) if (bas==boz) and so on.
>
Note that these aren't hard restrictions, rather the compiler will generate a warning when this convention is not followed which can help to identify places where there is a mistake. Indeed, in this patch set, there are 10 such instances where such an error was found by the warning and another 10 in which the warning only needed adjustment of parenthesis to confirm that the code executes the intent.
> patch by Matthew Rezny
>
> CCMAIL: mrezny at hexaneinc.com
> CCMAIL: calligra-devel at kde.org
> CCMAIL: kimageshop at kde.org
>
> M +1 -1 filters/libmsooxml/MsooXmlUtils.cpp
> M +2 -3 filters/sheets/opencalc/opencalcexport.cc
> M +2 -2 filters/words/libexport/ProcessDocument.cc
> M +1 -1 filters/words/msword-odf/wv2/src/bookmark.cpp
> M +4 -4 filters/words/rtf/import/rtf-qt/src/UserPropsDestination.cpp
> M +2 -2 karbon/plugins/tools/filterEffectTool/FilterEffectResource.cpp
> M +1 -1 kexi/kexidb/connection.cpp
> M +1 -1 kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp
> M +1 -1 kexi/kexidb/parser/parser_p.cpp
> M +1 -1 kexi/kexiutils/utils.h
> M +1 -1 kexi/main/KexiMainWindow.cpp
> M +1 -1 kexi/migration/mdb/src/mdbtools/libmdb/write.c
> M +2 -2 krita/image/tiles3/kis_tiled_data_manager.cc
> M +1 -1 krita/plugins/formats/psd/psd_colormode_block.cpp
> M +2 -2 krita/plugins/tools/tool_perspectivegrid/kis_tool_perspectivegrid.cc
> M +4 -4 libs/kotext/styles/KoTableStyle.cpp
> M +1 -1 libs/odf/KoElementReference.cpp
> M +1 -1 words/part/KWDocument.cpp
>
> http://commits.kde.org/calligra/c804712c97b24a3774a53a3825f50762089f1f49
>
> diff --git a/filters/libmsooxml/MsooXmlUtils.cpp b/filters/libmsooxml/MsooXmlUtils.cpp
> index 3975c88..657ce05 100644
> --- a/filters/libmsooxml/MsooXmlUtils.cpp
> +++ b/filters/libmsooxml/MsooXmlUtils.cpp
> @@ -1944,7 +1944,7 @@ QString Utils::ParagraphBulletProperties::convertToListProperties(KoGenStyles& m
> }
> //MSPowerPoint: A label does NOT inherit Underline from text-properties
> //of the 1st text chunk. A bullet does NOT inherit {Italics, Bold}.
> - if ((currentFilter == Utils::PptxFilter)) {
> + if (currentFilter == Utils::PptxFilter) {
> if (m_type != ParagraphBulletProperties::NumberType) {
> out.addAttribute("fo:font-style", "normal");
> out.addAttribute("fo:font-weight", "normal");
Silenced a warning, follow style for comparison
> diff --git a/filters/sheets/opencalc/opencalcexport.cc b/filters/sheets/opencalc/opencalcexport.cc
> index 7f34f5b..383d328 100644
> --- a/filters/sheets/opencalc/opencalcexport.cc
> +++ b/filters/sheets/opencalc/opencalcexport.cc
> @@ -449,12 +449,11 @@ bool OpenCalcExport::exportBody(QDomDocument & doc, QDomElement & content, const
> QString name(sheet->sheetName());
>
> int n = name.indexOf(' ');
> - if (n != -1) {
> + if (n > -1) {
> kDebug(30518) << "Sheet name converting:" << name;
> - name[n] == '_';
> + name.replace(' ','_');
> kDebug(30518) << "Sheet name converted:" << name;
> }
> - name = name.replace(' ', "_");
>
> QRect _printRange = sheet->printSettings()->printRegion().lastRange();
> if (_printRange != (QRect(QPoint(1, 1), QPoint(KS_colMax, KS_rowMax)))) {
There was a warning, and the obvious change was to make it an assignment (name[n] = '_';), but that would be redundant due to the following call to replace, so I just re-factored the section for clarity (and now the debug message will be correct). The change of != to > which Steve noted was just personal style, could have been left out.
> diff --git a/filters/words/libexport/ProcessDocument.cc b/filters/words/libexport/ProcessDocument.cc
> index 30a4465..0dad6a7 100644
> --- a/filters/words/libexport/ProcessDocument.cc
> +++ b/filters/words/libexport/ProcessDocument.cc
> @@ -515,7 +515,7 @@ static void SubProcessFormatTwoTag(QDomNode myNode,
> ValueListFormatData *formatDataList, int formatPos, int formatLen,
> KWEFWordsLeader *leader)
> {
> - if ((formatPos == -1)) {
> + if (formatPos == -1) {
> // We have no position defined
> kWarning(30508) << "Missing text image position!";
> return;
> @@ -551,7 +551,7 @@ static void SubProcessFormatThreeTag(QDomNode myNode,
> ValueListFormatData *formatDataList, int formatPos, int /*formatLen*/,
> KWEFWordsLeader *leader)
> {
> - if ((formatPos == -1)) { // formatLen is never there but is 1.
> + if (formatPos == -1) { // formatLen is never there but is 1.
> // We have no position and no length defined
> kWarning(30508) << "Missing variable formatting!";
> return;
Silenced warnings, follow style for comparison
> diff --git a/filters/words/msword-odf/wv2/src/bookmark.cpp b/filters/words/msword-odf/wv2/src/bookmark.cpp
> index 63e8e6a..cf40c67 100644
> --- a/filters/words/msword-odf/wv2/src/bookmark.cpp
> +++ b/filters/words/msword-odf/wv2/src/bookmark.cpp
> @@ -363,7 +363,7 @@ bool Bookmarks::valid(U16 &num, const U32 ccpText)
> //check bookmark names
> for (uint i = 0; i < m_name.size(); i++) {
> if ( (m_name[i] == UString::null) ) {
> - m_name[i] == UString().from(i + 1);
> + m_name[i] = UString().from(i + 1);
> }
> }
> if (m_name.size() < m_start->count()) {
The second comparison should obvious be an assignment, caught by compiler warning.
> diff --git a/filters/words/rtf/import/rtf-qt/src/UserPropsDestination.cpp b/filters/words/rtf/import/rtf-qt/src/UserPropsDestination.cpp
> index 4e6d185..6b9b76e 100644
> --- a/filters/words/rtf/import/rtf-qt/src/UserPropsDestination.cpp
> +++ b/filters/words/rtf/import/rtf-qt/src/UserPropsDestination.cpp
> @@ -38,13 +38,13 @@ namespace RtfReader
> if ( value == 30 ) {
> m_propertyType = QVariant::String;
> } else if ( value == 3 ) {
> - m_propertyType == QVariant::Int;
> + m_propertyType = QVariant::Int;
> } else if ( value == 5 ) {
> - m_propertyType == QVariant::Double;
> + m_propertyType = QVariant::Double;
> } else if ( value == 64 ) {
> - m_propertyType == QVariant::Date;
> + m_propertyType = QVariant::Date;
> } else if ( value == 11 ) {
> - m_propertyType == QVariant::Bool;
> + m_propertyType = QVariant::Bool;
> } else {
> qDebug() << "unhandled value type in UserPropsDestination:" << value;
> }
This sequence repeats the mistake for not actually assigning in any but the first case. Again caught by warnings.
> diff --git a/karbon/plugins/tools/filterEffectTool/FilterEffectResource.cpp b/karbon/plugins/tools/filterEffectTool/FilterEffectResource.cpp
> index 2b0c959..a31afb1 100644
> --- a/karbon/plugins/tools/filterEffectTool/FilterEffectResource.cpp
> +++ b/karbon/plugins/tools/filterEffectTool/FilterEffectResource.cpp
> @@ -119,10 +119,10 @@ KoFilterEffectStack * FilterEffectResource::toFilterStack() const
>
> // only allow obect bounding box units
> if (e.hasAttribute("filterUnits") && e.attribute("filterUnits") != "objectBoundingBox")
> - return false;
> + return 0;
>
> if (e.attribute("primitiveUnits") != "objectBoundingBox")
> - return false;
> + return 0;
>
> // parse filter region rectangle
> QRectF filterRegion;
Silenced warnings, function should return a pointer
> diff --git a/kexi/kexidb/connection.cpp b/kexi/kexidb/connection.cpp
> index d6ccd9c..1eac0b9 100644
> --- a/kexi/kexidb/connection.cpp
> +++ b/kexi/kexidb/connection.cpp
> @@ -3015,7 +3015,7 @@ KexiDB::QuerySchema* Connection::setupQuerySchema(const RecordData &data)
> bool ok = true;
> const int objID = data[0].toInt(&ok);
> if (!ok)
> - return false;
> + return 0;
> QString sqlText;
> if (!loadDataBlock(objID, sqlText, "sql")) {
> setError(ERR_OBJECT_NOT_FOUND,
Silenced warning, function should return a pointer
> diff --git a/kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp b/kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp
> index 5dc1f1d..5c20482 100644
> --- a/kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp
> +++ b/kexi/kexidb/drivers/sqlite/sqlitevacuum.cpp
> @@ -194,7 +194,7 @@ void SQLiteVacuum::sqliteProcessFinished(int exitCode, QProcess::ExitStatus exit
>
> if (0 != KDE::rename(m_tmpFilePath, m_filePath)) {
> kWarning() << "Rename" << m_tmpFilePath << "to" << m_filePath << "failed.";
> - m_result == false;
> + m_result = false;
> }
>
> if (m_result == true) {
Obviously should be assignment, caught by warning.
> diff --git a/kexi/kexidb/parser/parser_p.cpp b/kexi/kexidb/parser/parser_p.cpp
> index b1e8efe..6d69429 100644
> --- a/kexi/kexidb/parser/parser_p.cpp
> +++ b/kexi/kexidb/parser/parser_p.cpp
> @@ -564,7 +564,7 @@ QuerySchema* buildSelectQuery(
> if (!options->whereExpr->validate(parseInfo)) {
> setError(parseInfo.errMsg, parseInfo.errDescr);
> CLEANUP;
> - return false;
> + return 0;
> }
> querySchema->setWhereExpression(options->whereExpr);
> }
Silenced a warning, function should return a pointer
> diff --git a/kexi/kexiutils/utils.h b/kexi/kexiutils/utils.h
> index 4629d66..5d06a2d 100644
> --- a/kexi/kexiutils/utils.h
> +++ b/kexi/kexiutils/utils.h
> @@ -538,7 +538,7 @@ public:
> return QHash<Key, T>::insertMulti(key.toLower(), value);
> }
> const Key key(const T& value, const Key& defaultKey) const {
> - return QHash<Key, T>::key(value, key.toLower());
> + return QHash<Key, T>::key(value, defaultKey.toLower());
> }
> int remove(const Key& key) {
> return QHash<Key, T>::remove(key.toLower());
Interesting error here. The one line function doesn't use one of its arguments but does refer to something else not passed in. Clang gave an error here to the effect that key is undefined for the call toLower. I'm not sure what gcc was doing with this, but my best guess is that it found a match in the function name itself and the call became recursive.
> diff --git a/kexi/main/KexiMainWindow.cpp b/kexi/main/KexiMainWindow.cpp
> index 33f81f5..3bef113 100644
> --- a/kexi/main/KexiMainWindow.cpp
> +++ b/kexi/main/KexiMainWindow.cpp
> @@ -3434,7 +3434,7 @@ KexiMainWindow::openObjectFromNavigator(KexiPart::Item* item, Kexi::ViewMode vie
> return 0;
> }
> if (!d->prj || !item)
> - return false;
> + return 0;
> #ifndef KEXI_NO_PENDING_DIALOGS
> Private::PendingJobType pendingType;
> KexiWindow *window = d->openedWindowFor(item, pendingType);
Silenced a warning, function defined to return a pointer
> diff --git a/kexi/migration/mdb/src/mdbtools/libmdb/write.c b/kexi/migration/mdb/src/mdbtools/libmdb/write.c
> index 8506b71..b1fbb46 100644
> --- a/kexi/migration/mdb/src/mdbtools/libmdb/write.c
> +++ b/kexi/migration/mdb/src/mdbtools/libmdb/write.c
> @@ -170,7 +170,7 @@ mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields)
> }
>
> bitmask_sz = (row_cols + 7) / 8;
> - nullmask = (char*)pg_buf + row_end - bitmask_sz + 1;
> + nullmask = (unsigned char*)pg_buf + row_end - bitmask_sz + 1;
>
> /* read table of variable column locations */
> row_var_cols = IS_JET4(mdb) ?
Cast to the type nullmask is defined as to silence a warning
> diff --git a/krita/image/tiles3/kis_tiled_data_manager.cc b/krita/image/tiles3/kis_tiled_data_manager.cc
> index dacef5e..cac55d7 100644
> --- a/krita/image/tiles3/kis_tiled_data_manager.cc
> +++ b/krita/image/tiles3/kis_tiled_data_manager.cc
> @@ -138,7 +138,7 @@ bool KisTiledDataManager::write(KoStore *store)
> KisAbstractTileCompressorSP compressor =
> KisTileCompressorFactory::create(CURRENT_VERSION);
>
> - while (tile = iter.tile()) {
> + while ((tile = iter.tile())) {
> compressor->writeTile(tile, store);
> ++iter;
> }
> @@ -287,7 +287,7 @@ void KisTiledDataManager::purge(const QRect& area)
> KisTileHashTableIterator iter(m_hashTable);
> KisTileSP tile;
>
> - while (tile = iter.tile()) {
> + while ((tile = iter.tile())) {
> if (tile->extent().intersects(area)) {
> tile->lockForRead();
> if(memcmp(defaultData, tile->data(), tileDataSize) == 0) {
Silenced warnings, follow style for assignment in a comparison
> diff --git a/krita/plugins/formats/psd/psd_colormode_block.cpp b/krita/plugins/formats/psd/psd_colormode_block.cpp
> index 3466725..ccd6099 100644
> --- a/krita/plugins/formats/psd/psd_colormode_block.cpp
> +++ b/krita/plugins/formats/psd/psd_colormode_block.cpp
> @@ -89,7 +89,7 @@ bool PSDColorModeBlock::valid()
> return false;
> }
> if (colormode == DuoTone && blocksize == 0) {
> - error == QString("DuoTone mode, but data block is empty");
> + error = QString("DuoTone mode, but data block is empty");
> return false;
> }
> if ((quint32)data.size() != blocksize) {
That should obviously be an assignment, again caught by compiler warning
> diff --git a/krita/plugins/tools/tool_perspectivegrid/kis_tool_perspectivegrid.cc b/krita/plugins/tools/tool_perspectivegrid/kis_tool_perspectivegrid.cc
> index ba1e1de..c4ae45e 100644
> --- a/krita/plugins/tools/tool_perspectivegrid/kis_tool_perspectivegrid.cc
> +++ b/krita/plugins/tools/tool_perspectivegrid/kis_tool_perspectivegrid.cc
> @@ -138,7 +138,7 @@ void KisToolPerspectiveGrid::mousePressEvent(KoPointerEvent *event)
> KisSubPerspectiveGrid* grid = *it;
> QPointF gridCenter = grid->center();
> dbgKrita << "click at " << event->point << " top left at " << *grid->topLeft();
> - if (m_selectedNode1 = nodeNearPoint(grid, mousep)) {
> + if (m_selectedNode1 == nodeNearPoint(grid, mousep)) {
> m_internalMode = MODE_DRAGING_NODE;
> break;
> } else if (mouseNear(mousep, ((pixelToView(*grid->topLeft()) + pixelToView(*grid->bottomLeft()))*0.5))) {
> @@ -233,7 +233,7 @@ void KisToolPerspectiveGrid::mouseMoveEvent(KoPointerEvent *event)
> KisPerspectiveGrid* pGrid = m_canvas->view()->resourceProvider()->currentImage()->perspectiveGrid();
> for (QList<KisSubPerspectiveGrid*>::const_iterator it = pGrid->begin(); it != pGrid->end(); ++it) {
> KisSubPerspectiveGrid* grid = *it;
> - if (m_higlightedNode = nodeNearPoint(grid, mousep)) {
> + if ((m_higlightedNode = nodeNearPoint(grid, mousep))) {
> if (m_higlightedNode == m_selectedNode1 || m_higlightedNode == m_selectedNode2) {
> m_higlightedNode = 0;
> } else {
This is the fun one that I hoped would be reviewed closely before committing. The second change was just a change of style to silence a warning. The first change took some more consideration. There is a lot going on in this code block and I don't have any reference for what it should be doing in all cases, thus it was my best guess as to the intent. But for both these changes, someone more familiar with this piece of code should take a look to consider if each should be an assignment or just a comparison.
> diff --git a/libs/kotext/styles/KoTableStyle.cpp b/libs/kotext/styles/KoTableStyle.cpp
> index a9ebe62..21bf9af 100644
> --- a/libs/kotext/styles/KoTableStyle.cpp
> +++ b/libs/kotext/styles/KoTableStyle.cpp
> @@ -633,13 +633,13 @@ void KoTableStyle::saveOdf(KoGenStyle &style)
> style.addProperty("fo:background-color", backBrush.color().name(), KoGenStyle::TableType);
> else
> style.addProperty("fo:background-color", "transparent", KoGenStyle::TableType);
> - } else if ((key == QTextFormat::FrameLeftMargin)) {
> + } else if (key == QTextFormat::FrameLeftMargin) {
> style.addPropertyLength("fo:margin-left", propertyLength(QTextFormat::FrameLeftMargin), KoGenStyle::TableType);
> - } else if ((key == QTextFormat::FrameRightMargin)) {
> + } else if (key == QTextFormat::FrameRightMargin) {
> style.addPropertyLength("fo:margin-right", propertyLength(QTextFormat::FrameRightMargin), KoGenStyle::TableType);
> - } else if ((key == QTextFormat::FrameTopMargin)) {
> + } else if (key == QTextFormat::FrameTopMargin) {
> style.addPropertyLength("fo:margin-top", propertyLength(QTextFormat::FrameTopMargin), KoGenStyle::TableType);
> - } else if ((key == QTextFormat::FrameBottomMargin)) {
> + } else if (key == QTextFormat::FrameBottomMargin) {
> style.addPropertyLength("fo:margin-bottom", propertyLength(QTextFormat::FrameBottomMargin), KoGenStyle::TableType);
> } else if (key == KoTableStyle::CollapsingBorders) {
> if (collapsingBorderModel())
Silenced warnings, follow style for comparison
> diff --git a/libs/odf/KoElementReference.cpp b/libs/odf/KoElementReference.cpp
> index 8747574..b51f97f 100644
> --- a/libs/odf/KoElementReference.cpp
> +++ b/libs/odf/KoElementReference.cpp
> @@ -110,5 +110,5 @@ KoElementReference KoElementReference::loadOdf(const KoXmlElement &element)
>
> void KoElementReference::invalidate()
> {
> - d->xmlid == QString::null;
> + d->xmlid = QString::null;
> }
That should obviously be an assignment, found by compiler warning
> diff --git a/words/part/KWDocument.cpp b/words/part/KWDocument.cpp
> index 30ac20a..65d027c 100644
> --- a/words/part/KWDocument.cpp
> +++ b/words/part/KWDocument.cpp
> @@ -24,7 +24,7 @@
> */
>
> #include "KWDocument.h"
> -#include "KWFactory.h"<
> +#include "KWFactory.h"
> #include "KWView.h"
> #include "KWCanvas.h"
> #include "KWCanvasItem.h"
Typo, meh. generated a warning
I hope that makes everything clearer to those who didn't catch the initial talk on IRC. Also, for those parts that were not committed, I don't now if they were pending review or had slipped through the cracks since this was submitted as mishmash. Now by writing this mail I know there's at least a record so the latter doesn't happen. Hopefully I don't have to make a similar patch in the future, but if so I'll be sure to cut it apart into sections that deal with errors and warnings separately so it can be better evaluated and handled.
More information about the calligra-devel
mailing list