--- /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();
+}
+
+
+
+