src/hbcore/utils/hbtextmeasurementutility.cpp
changeset 21 4633027730f5
child 23 e6ad4ef83b23
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/utils/hbtextmeasurementutility.cpp	Wed Aug 18 10:05:37 2010 +0300
@@ -0,0 +1,498 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at developer.feedback@nokia.com.
+**
+****************************************************************************/
+
+#include "hbtextmeasurementutility_r.h"
+#include "hbtextmeasurementutility_r_p.h"
+#include "hbwidgetbase.h"
+#include "hbfontspec.h"
+#include "hbinstance.h"
+#include "hbfeaturemanager_r.h"
+
+#include <QGraphicsWidget>
+#include <QTextStream>
+#include <QFile>
+#include <QDir>
+#include <QDate>
+#include <QTimer>
+#include <QFont>
+
+#include <QDebug> // for qWarning
+
+#include <qmath.h>
+
+#ifndef Q_OS_SYMBIAN
+#include <iostream>
+#endif
+
+const QChar KCSVSeparator(',');
+const QString KUnknown("UNKNOWN");
+
+class HbTextRecord
+{
+public:
+    HbTextRecord();
+    QString fontLogicalName() const;
+    bool operator<(const HbTextRecord &other)const;
+    static bool recordLessThan(HbTextRecord *rc1, HbTextRecord *rc2);
+    static bool recordFullCompare(HbTextRecord *rc1, HbTextRecord *rc2);
+public:
+    QString mRecordName;  // Record name
+    QSizeF mSize;         // Text item size (in pixels)
+    HbFontSpec mFontSpec; // Font spec
+    int mRowCount;
+};
+
+
+HbTextRecord::HbTextRecord()
+{
+}
+
+QString HbTextRecord::fontLogicalName() const
+{
+    QString logicalName(KUnknown);
+    switch(mFontSpec.role()){
+        case HbFontSpec::Primary:
+            logicalName = "qfn_primary";
+            break;
+        case HbFontSpec::Secondary:
+            logicalName = "qfn_secondary";
+            break;
+        case HbFontSpec::Title:
+            logicalName = "qfn_title";
+            break;
+        case HbFontSpec::PrimarySmall:
+            logicalName = "qfn_primary_small";
+            break;
+        case HbFontSpec::Digital:
+            logicalName = "qfn_digital";
+            break;
+        default:
+            break;
+    }
+    return logicalName;
+}
+
+bool HbTextRecord::operator<(const HbTextRecord& other)const
+{
+    return this->mRecordName < other.mRecordName;
+}
+
+bool HbTextRecord::recordLessThan(HbTextRecord* rc1, HbTextRecord* rc2)
+{
+    return rc1->mRecordName < rc2->mRecordName;
+}
+
+bool HbTextRecord::recordFullCompare(HbTextRecord* rc1, HbTextRecord* rc2)
+{
+    if (!rc1->mRecordName.compare(rc2->mRecordName)) {
+        if (rc1->mFontSpec == rc2->mFontSpec) {
+            if (rc1->mRowCount == rc2->mRowCount) {
+                if (rc1->mSize == rc2->mSize) {
+                    return true;
+                } else {
+                    qDebug() << "HbTextMeasurementUtility::recordFullCompare: Sizes don't match";
+                }
+            } else {
+                qDebug() << "HbTextMeasurementUtility::recordFullCompare: Row counts don't match";
+            }
+        } else {
+            qDebug() << "HbTextMeasurementUtility::recordFullCompare: Fonts don't match";
+        }
+    } else {
+        qDebug() << "HbTextMeasurementUtility::recordFullCompare: Names don't match";
+    }
+    qDebug() << "HbTextMeasurementUtility::recordFullCompare: -- record1:"
+        << rc1->mRecordName << rc1->mFontSpec.role() << rc1->mFontSpec.textHeight() << rc1->mRowCount << rc1->mSize;
+    qDebug() << "HbTextMeasurementUtility::recordFullCompare: -- record2:"
+        << rc2->mRecordName << rc2->mFontSpec.role() << rc2->mFontSpec.textHeight() << rc2->mRowCount << rc2->mSize;
+    return false;
+}
+
+
+/*!
+    Write a report headers to csv file
+    \internal
+*/
+void HbTextMeasurementUtilityPrivate::writeHeaders(QTextStream &csvWriter)
+{
+    csvWriter << "Layout";
+    csvWriter << KCSVSeparator;
+    csvWriter << "Font";
+    csvWriter << KCSVSeparator;
+    csvWriter << "Row height";
+    csvWriter << KCSVSeparator;
+    csvWriter << "Row width";
+    csvWriter << KCSVSeparator;
+    csvWriter << "Max rows";
+    csvWriter << "\n";
+}
+
+/*!
+    Write a text item record to csv file
+    \internal
+*/
+void HbTextMeasurementUtilityPrivate::writeEntry(
+    QTextStream &csvWriter,
+    const HbTextRecord *record)
+{
+    // "Layout"
+    csvWriter << record->mRecordName;
+    csvWriter << KCSVSeparator;
+    // "Font"
+    csvWriter << record->fontLogicalName();
+    csvWriter << KCSVSeparator;
+    // "Row height"
+    csvWriter << qRound(record->mFontSpec.textHeight()-0.5); // Floor.
+    csvWriter << KCSVSeparator;
+    // "Row width"
+    csvWriter << qRound(record->mSize.width()-0.5); // Floor.
+    csvWriter << KCSVSeparator;
+    // "Max rows"
+    csvWriter << record->mRowCount;
+    csvWriter << '\n';
+}
+
+/*!
+    Validate records. Try to remove duplicate items.
+    \internal
+*/
+bool HbTextMeasurementUtilityPrivate::validateRecords(HbDeviceProfile &profile)
+{
+    if (records.isEmpty()) {
+        qDebug() << "HbTextMeasurementUtility::validateRecords: No result entries";
+        return false;
+    }
+    QList<HbTextRecord*> temp;
+    qSort(records.begin(), records.end(), &HbTextRecord::recordLessThan);
+    bool ret = true;
+
+    foreach (HbTextRecord *record, records) {
+
+        bool notFound = true;
+
+        for(int i=0; i<temp.count();i++) {
+            if (!temp[i]->mRecordName.compare(record->mRecordName)) {
+                // duplicate with same data
+                if (HbTextRecord::recordFullCompare(temp[i], record)) {
+                    qDebug() << "HbTextMeasurementUtility::validateRecords: Duplicate removed";
+                    notFound = false;
+
+                // duplicate with same id and correct data
+                } else if ( !temp[i]->fontLogicalName().compare(KUnknown) &&
+                record->fontLogicalName().compare(KUnknown)) {
+                    qDebug() << "HbTextMeasurementUtility::validateRecords: Duplicate overwritten";
+                    notFound = false;
+                    temp[i] = record;
+                // duplicates
+                } else {
+                    qDebug() << "HbTextMeasurementUtility::validateRecords: Duplicate items found";
+                    ret = false;
+                }
+                break;
+            }
+        }
+        if (notFound) {
+            temp.append(record);
+        }
+    }
+
+    records = temp;
+    foreach (const HbTextRecord *record, records) {
+        if ( !record->fontLogicalName().compare(KUnknown) ) {
+            qDebug() << "HbTextMeasurementUtility::validateRecords: Result item" << record->mRecordName << "Fontspec is null";
+            ret = false;
+        }
+
+        if ( record->mSize.width() > profile.logicalSize().width() ) {
+            qDebug() << "HbTextMeasurementUtility::validateRecords: Result item" << record->mRecordName << "width is too wide";
+            qDebug() << "HbTextMeasurementUtility::validateRecords: Profile width: " << profile.logicalSize().width();
+            qDebug() << "HbTextMeasurementUtility::validateRecords: Record width:" << record->mSize.width();
+            ret = false;
+        }
+    }
+    return ret;
+}
+
+/*!
+    \internal
+*/
+void HbTextMeasurementUtilityPrivate::doMeasureItems()
+{
+#ifndef HB_TEXT_MEASUREMENT_UTILITY
+    return;
+#else
+    QList<HbMainWindow*> mainWindows;
+	if (mWindow) {
+		mainWindows.append(mWindow);
+	} else {
+		mainWindows = hbInstance->allMainWindows();
+	}
+    foreach (HbMainWindow* mainWindow, mainWindows ) {
+        QGraphicsScene* scene = mainWindow->scene(); //krazy:exclude=qclasses
+        QList<QGraphicsItem*> sceneItems = scene->items();
+        foreach (QGraphicsItem* sceneItem, sceneItems ) {
+            if ( sceneItem->isWidget() ) {
+                HbWidgetBase* widget = qobject_cast<HbWidgetBase*>(static_cast<QGraphicsWidget*>(sceneItem));
+                QVariant textId = widget
+                    ? widget->property( HbTextMeasurementUtilityNameSpace::textIdPropertyName )
+                    : QVariant();
+                if( ( textId != QVariant::Invalid ) && ( !textId.toString().isEmpty() ) ) {
+                    HbTextRecord *record = new HbTextRecord();
+                    record->mRecordName = textId.toString();
+                    record->mSize = widget->size();
+                    record->mFontSpec = widget->effectiveFontSpec();
+                    if ( record->mFontSpec != widget->fontSpec() ) {
+                        qDebug() << "HbTextMeasurementUtility::measureItems: fontSpec and effectiveFontSpec do not match for item"
+                                 << record->mRecordName;
+                    }
+                    QVariant rowCount = widget->property( HbTextMeasurementUtilityNameSpace::textMaxLines );
+                    record->mRowCount = rowCount.toInt();
+                    if (record->mRowCount <= 0) {
+                        record->mRowCount = -1;
+                    }
+                    records.append(record);
+                }
+            }
+        }
+    }
+#endif
+}
+
+/*!
+    @alpha
+    @hbcore
+    \class HbTextMeasurementUtility
+    \brief HbTextMeasurementUtility is used for measuring available space for localized texts.
+*/
+
+/*!
+    Default constructor.
+*/
+HbTextMeasurementUtility::HbTextMeasurementUtility()
+{
+    d = new HbTextMeasurementUtilityPrivate;
+}
+
+/*!
+    Destructor.
+*/
+HbTextMeasurementUtility::~HbTextMeasurementUtility()
+{
+    qDeleteAll(d->records);
+    delete d;
+}
+
+/*!
+    Returns singleton instance.
+*/
+HbTextMeasurementUtility *HbTextMeasurementUtility::instance()
+{
+    static HbTextMeasurementUtility theUtility;
+    return &theUtility;
+}
+
+/*!
+    Sets the localization test mode to \a enabled.
+*/
+void HbTextMeasurementUtility::setLocTestMode( bool enabled )
+{
+#ifndef HB_TEXT_MEASUREMENT_UTILITY
+    Q_UNUSED( enabled );
+    return;
+#else
+    HbFeatureManager::instance()->setFeatureStatus( HbFeatureManager::TextMeasurement, ( int )enabled );
+#endif
+}
+
+/*!
+    Returns the current localization test mode.
+*/
+bool HbTextMeasurementUtility::locTestMode() const
+{
+#ifndef HB_TEXT_MEASUREMENT_UTILITY
+    return false;
+#else
+    return HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement );
+#endif
+
+}
+
+/*!
+    Measures all currently visible text items.
+    Method is asynchronous if time interval \a after (in milliseconds) is larger than zero.
+    Method is synchronous otherwise. It's possible to give \a window parameter if
+    the measurement is only made for text items of specific HbMainWindow. 
+    The measurement is made for all text items in all HbMainWindows if the parameter is omitted.
+*/
+void HbTextMeasurementUtility::measureItems(int after, HbMainWindow *window)
+{
+    // Store the window pointer, because it would not survive the singleShot timer call.
+    d->mWindow = window;
+    if (after > 0) {
+        // Asynchronous
+        QTimer::singleShot(after, d, SLOT(doMeasureItems()));
+    } else {
+        // Synchronous
+        QCoreApplication::sendPostedEvents();
+        QCoreApplication::sendPostedEvents();
+        QCoreApplication::sendPostedEvents();
+        d->doMeasureItems();
+    }
+}
+
+
+/*!
+    Writes a layout metric report into a file, by default C: drive is used.
+    The report contains metrics data for each text item at the time of last
+    call to the measureItems() method.
+
+    Report is written to a csv (Comma Separated Values) file.
+
+    Report is intended to be used when text strings lengths are decided and
+    localized to different language variants.
+
+    Report contains data for each measured text item such as:
+    - Logical name.
+    - Font type (qfn_primary, qfn_secondary...).
+    - Font size in pixels.
+    - Text width in pixels.
+*/
+bool HbTextMeasurementUtility::writeReport(HbDeviceProfile &profile, const QString &domainName)
+{
+#ifndef HB_TEXT_MEASUREMENT_UTILITY
+    Q_UNUSED( profile );
+    Q_UNUSED( domainName );
+    return false;
+#else
+
+    qDebug() << "HbTextMeasurementUtility::writeReport: Using profile" << profile.name();
+
+#ifdef Q_OS_SYMBIAN
+    const QString KDriveF("F:\\");
+    const QString KDriveC("C:\\");
+    const QString KDirectory("data\\log\\qtestcase\\loc\\"); 
+
+    QString filePath;
+    if (QFile::exists(KDriveF)) {
+        filePath = KDriveF + KDirectory;
+    } else {
+        filePath = KDriveC + KDirectory;
+    }
+#else
+    QString filePath(QDir::tempPath());
+    filePath.append(QDir::separator());
+    filePath.append("loc");
+    filePath.append(QDir::separator());
+    filePath.append(profile.name());
+    filePath.append(QDir::separator());
+#endif
+    filePath = QDir::toNativeSeparators(filePath);
+
+    QDir dir(filePath);
+    if (!dir.exists()) {
+        dir.mkpath(filePath);
+    }
+
+    // Make sure there are no illegal characters in "domainName"
+    QString tempName = domainName;
+    tempName.remove(QRegExp("[^a-zA-Z0-9]"));
+    if (tempName.isEmpty()) {
+        tempName = "unknown";
+    }
+
+    filePath.append(tempName);
+    filePath.append('_');
+    filePath.append(profile.name());
+    filePath.append('_');
+    filePath.append(QString::number(QDate::currentDate().year()));
+    filePath.append("wk");
+    filePath.append(QString::number(QDate::currentDate().weekNumber()));
+    filePath.append(".csv");
+
+    QFile file(filePath);
+    if (!file.open(QFile::WriteOnly | QFile::Text)) {
+#ifndef Q_OS_SYMBIAN
+        std::cerr << "Error: Cannot write file ";
+        std::cerr << qPrintable(filePath);
+        std::cerr << qPrintable(file.errorString()) << std::endl;
+#endif
+        return false;
+    }
+    bool ret = writeReport(profile, &file);
+
+    file.close();
+#ifndef Q_OS_SYMBIAN
+    if (file.error()) {
+        std::cerr << "Error: Cannot write file ";
+        std::cerr << qPrintable(filePath);
+        std::cerr << qPrintable(file.errorString()) << std::endl;
+    }
+#endif
+   return ret;
+#endif
+}
+
+
+/*!
+    Overloaded function provided for convenience. Here you can manually specify QIODevice where to write report.
+*/
+bool HbTextMeasurementUtility::writeReport(HbDeviceProfile &profile, QIODevice *device)
+{
+#ifndef HB_TEXT_MEASUREMENT_UTILITY
+    Q_UNUSED( device );
+    return false;
+#else
+
+    if( device == 0 ) {
+        return false;
+    }
+
+    bool succeed = d->validateRecords(profile);
+    if (succeed) {
+        qDebug() << "HbTextMeasurementUtility::writeReport: Measurements OK";
+    } else {
+        qDebug() << "HbTextMeasurementUtility::writeReport: Measurements NOT OK";
+    }
+
+    QTextStream csvWriter(device);
+    d->writeHeaders(csvWriter);
+    foreach (const HbTextRecord *record, d->records) {
+        d->writeEntry(csvWriter, record);
+    }
+    return succeed;
+#endif
+}
+
+/*!
+    Reset the report.
+*/
+void HbTextMeasurementUtility::reset()
+{
+    d->records.clear();
+}
+
+
+
+