src/hbcore/utils/hbtextmeasurementutility.cpp
changeset 21 4633027730f5
child 23 e6ad4ef83b23
equal deleted inserted replaced
7:923ff622b8b9 21:4633027730f5
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbCore module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 
       
    26 #include "hbtextmeasurementutility_r.h"
       
    27 #include "hbtextmeasurementutility_r_p.h"
       
    28 #include "hbwidgetbase.h"
       
    29 #include "hbfontspec.h"
       
    30 #include "hbinstance.h"
       
    31 #include "hbfeaturemanager_r.h"
       
    32 
       
    33 #include <QGraphicsWidget>
       
    34 #include <QTextStream>
       
    35 #include <QFile>
       
    36 #include <QDir>
       
    37 #include <QDate>
       
    38 #include <QTimer>
       
    39 #include <QFont>
       
    40 
       
    41 #include <QDebug> // for qWarning
       
    42 
       
    43 #include <qmath.h>
       
    44 
       
    45 #ifndef Q_OS_SYMBIAN
       
    46 #include <iostream>
       
    47 #endif
       
    48 
       
    49 const QChar KCSVSeparator(',');
       
    50 const QString KUnknown("UNKNOWN");
       
    51 
       
    52 class HbTextRecord
       
    53 {
       
    54 public:
       
    55     HbTextRecord();
       
    56     QString fontLogicalName() const;
       
    57     bool operator<(const HbTextRecord &other)const;
       
    58     static bool recordLessThan(HbTextRecord *rc1, HbTextRecord *rc2);
       
    59     static bool recordFullCompare(HbTextRecord *rc1, HbTextRecord *rc2);
       
    60 public:
       
    61     QString mRecordName;  // Record name
       
    62     QSizeF mSize;         // Text item size (in pixels)
       
    63     HbFontSpec mFontSpec; // Font spec
       
    64     int mRowCount;
       
    65 };
       
    66 
       
    67 
       
    68 HbTextRecord::HbTextRecord()
       
    69 {
       
    70 }
       
    71 
       
    72 QString HbTextRecord::fontLogicalName() const
       
    73 {
       
    74     QString logicalName(KUnknown);
       
    75     switch(mFontSpec.role()){
       
    76         case HbFontSpec::Primary:
       
    77             logicalName = "qfn_primary";
       
    78             break;
       
    79         case HbFontSpec::Secondary:
       
    80             logicalName = "qfn_secondary";
       
    81             break;
       
    82         case HbFontSpec::Title:
       
    83             logicalName = "qfn_title";
       
    84             break;
       
    85         case HbFontSpec::PrimarySmall:
       
    86             logicalName = "qfn_primary_small";
       
    87             break;
       
    88         case HbFontSpec::Digital:
       
    89             logicalName = "qfn_digital";
       
    90             break;
       
    91         default:
       
    92             break;
       
    93     }
       
    94     return logicalName;
       
    95 }
       
    96 
       
    97 bool HbTextRecord::operator<(const HbTextRecord& other)const
       
    98 {
       
    99     return this->mRecordName < other.mRecordName;
       
   100 }
       
   101 
       
   102 bool HbTextRecord::recordLessThan(HbTextRecord* rc1, HbTextRecord* rc2)
       
   103 {
       
   104     return rc1->mRecordName < rc2->mRecordName;
       
   105 }
       
   106 
       
   107 bool HbTextRecord::recordFullCompare(HbTextRecord* rc1, HbTextRecord* rc2)
       
   108 {
       
   109     if (!rc1->mRecordName.compare(rc2->mRecordName)) {
       
   110         if (rc1->mFontSpec == rc2->mFontSpec) {
       
   111             if (rc1->mRowCount == rc2->mRowCount) {
       
   112                 if (rc1->mSize == rc2->mSize) {
       
   113                     return true;
       
   114                 } else {
       
   115                     qDebug() << "HbTextMeasurementUtility::recordFullCompare: Sizes don't match";
       
   116                 }
       
   117             } else {
       
   118                 qDebug() << "HbTextMeasurementUtility::recordFullCompare: Row counts don't match";
       
   119             }
       
   120         } else {
       
   121             qDebug() << "HbTextMeasurementUtility::recordFullCompare: Fonts don't match";
       
   122         }
       
   123     } else {
       
   124         qDebug() << "HbTextMeasurementUtility::recordFullCompare: Names don't match";
       
   125     }
       
   126     qDebug() << "HbTextMeasurementUtility::recordFullCompare: -- record1:"
       
   127         << rc1->mRecordName << rc1->mFontSpec.role() << rc1->mFontSpec.textHeight() << rc1->mRowCount << rc1->mSize;
       
   128     qDebug() << "HbTextMeasurementUtility::recordFullCompare: -- record2:"
       
   129         << rc2->mRecordName << rc2->mFontSpec.role() << rc2->mFontSpec.textHeight() << rc2->mRowCount << rc2->mSize;
       
   130     return false;
       
   131 }
       
   132 
       
   133 
       
   134 /*!
       
   135     Write a report headers to csv file
       
   136     \internal
       
   137 */
       
   138 void HbTextMeasurementUtilityPrivate::writeHeaders(QTextStream &csvWriter)
       
   139 {
       
   140     csvWriter << "Layout";
       
   141     csvWriter << KCSVSeparator;
       
   142     csvWriter << "Font";
       
   143     csvWriter << KCSVSeparator;
       
   144     csvWriter << "Row height";
       
   145     csvWriter << KCSVSeparator;
       
   146     csvWriter << "Row width";
       
   147     csvWriter << KCSVSeparator;
       
   148     csvWriter << "Max rows";
       
   149     csvWriter << "\n";
       
   150 }
       
   151 
       
   152 /*!
       
   153     Write a text item record to csv file
       
   154     \internal
       
   155 */
       
   156 void HbTextMeasurementUtilityPrivate::writeEntry(
       
   157     QTextStream &csvWriter,
       
   158     const HbTextRecord *record)
       
   159 {
       
   160     // "Layout"
       
   161     csvWriter << record->mRecordName;
       
   162     csvWriter << KCSVSeparator;
       
   163     // "Font"
       
   164     csvWriter << record->fontLogicalName();
       
   165     csvWriter << KCSVSeparator;
       
   166     // "Row height"
       
   167     csvWriter << qRound(record->mFontSpec.textHeight()-0.5); // Floor.
       
   168     csvWriter << KCSVSeparator;
       
   169     // "Row width"
       
   170     csvWriter << qRound(record->mSize.width()-0.5); // Floor.
       
   171     csvWriter << KCSVSeparator;
       
   172     // "Max rows"
       
   173     csvWriter << record->mRowCount;
       
   174     csvWriter << '\n';
       
   175 }
       
   176 
       
   177 /*!
       
   178     Validate records. Try to remove duplicate items.
       
   179     \internal
       
   180 */
       
   181 bool HbTextMeasurementUtilityPrivate::validateRecords(HbDeviceProfile &profile)
       
   182 {
       
   183     if (records.isEmpty()) {
       
   184         qDebug() << "HbTextMeasurementUtility::validateRecords: No result entries";
       
   185         return false;
       
   186     }
       
   187     QList<HbTextRecord*> temp;
       
   188     qSort(records.begin(), records.end(), &HbTextRecord::recordLessThan);
       
   189     bool ret = true;
       
   190 
       
   191     foreach (HbTextRecord *record, records) {
       
   192 
       
   193         bool notFound = true;
       
   194 
       
   195         for(int i=0; i<temp.count();i++) {
       
   196             if (!temp[i]->mRecordName.compare(record->mRecordName)) {
       
   197                 // duplicate with same data
       
   198                 if (HbTextRecord::recordFullCompare(temp[i], record)) {
       
   199                     qDebug() << "HbTextMeasurementUtility::validateRecords: Duplicate removed";
       
   200                     notFound = false;
       
   201 
       
   202                 // duplicate with same id and correct data
       
   203                 } else if ( !temp[i]->fontLogicalName().compare(KUnknown) &&
       
   204                 record->fontLogicalName().compare(KUnknown)) {
       
   205                     qDebug() << "HbTextMeasurementUtility::validateRecords: Duplicate overwritten";
       
   206                     notFound = false;
       
   207                     temp[i] = record;
       
   208                 // duplicates
       
   209                 } else {
       
   210                     qDebug() << "HbTextMeasurementUtility::validateRecords: Duplicate items found";
       
   211                     ret = false;
       
   212                 }
       
   213                 break;
       
   214             }
       
   215         }
       
   216         if (notFound) {
       
   217             temp.append(record);
       
   218         }
       
   219     }
       
   220 
       
   221     records = temp;
       
   222     foreach (const HbTextRecord *record, records) {
       
   223         if ( !record->fontLogicalName().compare(KUnknown) ) {
       
   224             qDebug() << "HbTextMeasurementUtility::validateRecords: Result item" << record->mRecordName << "Fontspec is null";
       
   225             ret = false;
       
   226         }
       
   227 
       
   228         if ( record->mSize.width() > profile.logicalSize().width() ) {
       
   229             qDebug() << "HbTextMeasurementUtility::validateRecords: Result item" << record->mRecordName << "width is too wide";
       
   230             qDebug() << "HbTextMeasurementUtility::validateRecords: Profile width: " << profile.logicalSize().width();
       
   231             qDebug() << "HbTextMeasurementUtility::validateRecords: Record width:" << record->mSize.width();
       
   232             ret = false;
       
   233         }
       
   234     }
       
   235     return ret;
       
   236 }
       
   237 
       
   238 /*!
       
   239     \internal
       
   240 */
       
   241 void HbTextMeasurementUtilityPrivate::doMeasureItems()
       
   242 {
       
   243 #ifndef HB_TEXT_MEASUREMENT_UTILITY
       
   244     return;
       
   245 #else
       
   246     QList<HbMainWindow*> mainWindows;
       
   247 	if (mWindow) {
       
   248 		mainWindows.append(mWindow);
       
   249 	} else {
       
   250 		mainWindows = hbInstance->allMainWindows();
       
   251 	}
       
   252     foreach (HbMainWindow* mainWindow, mainWindows ) {
       
   253         QGraphicsScene* scene = mainWindow->scene(); //krazy:exclude=qclasses
       
   254         QList<QGraphicsItem*> sceneItems = scene->items();
       
   255         foreach (QGraphicsItem* sceneItem, sceneItems ) {
       
   256             if ( sceneItem->isWidget() ) {
       
   257                 HbWidgetBase* widget = qobject_cast<HbWidgetBase*>(static_cast<QGraphicsWidget*>(sceneItem));
       
   258                 QVariant textId = widget
       
   259                     ? widget->property( HbTextMeasurementUtilityNameSpace::textIdPropertyName )
       
   260                     : QVariant();
       
   261                 if( ( textId != QVariant::Invalid ) && ( !textId.toString().isEmpty() ) ) {
       
   262                     HbTextRecord *record = new HbTextRecord();
       
   263                     record->mRecordName = textId.toString();
       
   264                     record->mSize = widget->size();
       
   265                     record->mFontSpec = widget->effectiveFontSpec();
       
   266                     if ( record->mFontSpec != widget->fontSpec() ) {
       
   267                         qDebug() << "HbTextMeasurementUtility::measureItems: fontSpec and effectiveFontSpec do not match for item"
       
   268                                  << record->mRecordName;
       
   269                     }
       
   270                     QVariant rowCount = widget->property( HbTextMeasurementUtilityNameSpace::textMaxLines );
       
   271                     record->mRowCount = rowCount.toInt();
       
   272                     if (record->mRowCount <= 0) {
       
   273                         record->mRowCount = -1;
       
   274                     }
       
   275                     records.append(record);
       
   276                 }
       
   277             }
       
   278         }
       
   279     }
       
   280 #endif
       
   281 }
       
   282 
       
   283 /*!
       
   284     @alpha
       
   285     @hbcore
       
   286     \class HbTextMeasurementUtility
       
   287     \brief HbTextMeasurementUtility is used for measuring available space for localized texts.
       
   288 */
       
   289 
       
   290 /*!
       
   291     Default constructor.
       
   292 */
       
   293 HbTextMeasurementUtility::HbTextMeasurementUtility()
       
   294 {
       
   295     d = new HbTextMeasurementUtilityPrivate;
       
   296 }
       
   297 
       
   298 /*!
       
   299     Destructor.
       
   300 */
       
   301 HbTextMeasurementUtility::~HbTextMeasurementUtility()
       
   302 {
       
   303     qDeleteAll(d->records);
       
   304     delete d;
       
   305 }
       
   306 
       
   307 /*!
       
   308     Returns singleton instance.
       
   309 */
       
   310 HbTextMeasurementUtility *HbTextMeasurementUtility::instance()
       
   311 {
       
   312     static HbTextMeasurementUtility theUtility;
       
   313     return &theUtility;
       
   314 }
       
   315 
       
   316 /*!
       
   317     Sets the localization test mode to \a enabled.
       
   318 */
       
   319 void HbTextMeasurementUtility::setLocTestMode( bool enabled )
       
   320 {
       
   321 #ifndef HB_TEXT_MEASUREMENT_UTILITY
       
   322     Q_UNUSED( enabled );
       
   323     return;
       
   324 #else
       
   325     HbFeatureManager::instance()->setFeatureStatus( HbFeatureManager::TextMeasurement, ( int )enabled );
       
   326 #endif
       
   327 }
       
   328 
       
   329 /*!
       
   330     Returns the current localization test mode.
       
   331 */
       
   332 bool HbTextMeasurementUtility::locTestMode() const
       
   333 {
       
   334 #ifndef HB_TEXT_MEASUREMENT_UTILITY
       
   335     return false;
       
   336 #else
       
   337     return HbFeatureManager::instance()->featureStatus( HbFeatureManager::TextMeasurement );
       
   338 #endif
       
   339 
       
   340 }
       
   341 
       
   342 /*!
       
   343     Measures all currently visible text items.
       
   344     Method is asynchronous if time interval \a after (in milliseconds) is larger than zero.
       
   345     Method is synchronous otherwise. It's possible to give \a window parameter if
       
   346     the measurement is only made for text items of specific HbMainWindow. 
       
   347     The measurement is made for all text items in all HbMainWindows if the parameter is omitted.
       
   348 */
       
   349 void HbTextMeasurementUtility::measureItems(int after, HbMainWindow *window)
       
   350 {
       
   351     // Store the window pointer, because it would not survive the singleShot timer call.
       
   352     d->mWindow = window;
       
   353     if (after > 0) {
       
   354         // Asynchronous
       
   355         QTimer::singleShot(after, d, SLOT(doMeasureItems()));
       
   356     } else {
       
   357         // Synchronous
       
   358         QCoreApplication::sendPostedEvents();
       
   359         QCoreApplication::sendPostedEvents();
       
   360         QCoreApplication::sendPostedEvents();
       
   361         d->doMeasureItems();
       
   362     }
       
   363 }
       
   364 
       
   365 
       
   366 /*!
       
   367     Writes a layout metric report into a file, by default C: drive is used.
       
   368     The report contains metrics data for each text item at the time of last
       
   369     call to the measureItems() method.
       
   370 
       
   371     Report is written to a csv (Comma Separated Values) file.
       
   372 
       
   373     Report is intended to be used when text strings lengths are decided and
       
   374     localized to different language variants.
       
   375 
       
   376     Report contains data for each measured text item such as:
       
   377     - Logical name.
       
   378     - Font type (qfn_primary, qfn_secondary...).
       
   379     - Font size in pixels.
       
   380     - Text width in pixels.
       
   381 */
       
   382 bool HbTextMeasurementUtility::writeReport(HbDeviceProfile &profile, const QString &domainName)
       
   383 {
       
   384 #ifndef HB_TEXT_MEASUREMENT_UTILITY
       
   385     Q_UNUSED( profile );
       
   386     Q_UNUSED( domainName );
       
   387     return false;
       
   388 #else
       
   389 
       
   390     qDebug() << "HbTextMeasurementUtility::writeReport: Using profile" << profile.name();
       
   391 
       
   392 #ifdef Q_OS_SYMBIAN
       
   393     const QString KDriveF("F:\\");
       
   394     const QString KDriveC("C:\\");
       
   395     const QString KDirectory("data\\log\\qtestcase\\loc\\"); 
       
   396 
       
   397     QString filePath;
       
   398     if (QFile::exists(KDriveF)) {
       
   399         filePath = KDriveF + KDirectory;
       
   400     } else {
       
   401         filePath = KDriveC + KDirectory;
       
   402     }
       
   403 #else
       
   404     QString filePath(QDir::tempPath());
       
   405     filePath.append(QDir::separator());
       
   406     filePath.append("loc");
       
   407     filePath.append(QDir::separator());
       
   408     filePath.append(profile.name());
       
   409     filePath.append(QDir::separator());
       
   410 #endif
       
   411     filePath = QDir::toNativeSeparators(filePath);
       
   412 
       
   413     QDir dir(filePath);
       
   414     if (!dir.exists()) {
       
   415         dir.mkpath(filePath);
       
   416     }
       
   417 
       
   418     // Make sure there are no illegal characters in "domainName"
       
   419     QString tempName = domainName;
       
   420     tempName.remove(QRegExp("[^a-zA-Z0-9]"));
       
   421     if (tempName.isEmpty()) {
       
   422         tempName = "unknown";
       
   423     }
       
   424 
       
   425     filePath.append(tempName);
       
   426     filePath.append('_');
       
   427     filePath.append(profile.name());
       
   428     filePath.append('_');
       
   429     filePath.append(QString::number(QDate::currentDate().year()));
       
   430     filePath.append("wk");
       
   431     filePath.append(QString::number(QDate::currentDate().weekNumber()));
       
   432     filePath.append(".csv");
       
   433 
       
   434     QFile file(filePath);
       
   435     if (!file.open(QFile::WriteOnly | QFile::Text)) {
       
   436 #ifndef Q_OS_SYMBIAN
       
   437         std::cerr << "Error: Cannot write file ";
       
   438         std::cerr << qPrintable(filePath);
       
   439         std::cerr << qPrintable(file.errorString()) << std::endl;
       
   440 #endif
       
   441         return false;
       
   442     }
       
   443     bool ret = writeReport(profile, &file);
       
   444 
       
   445     file.close();
       
   446 #ifndef Q_OS_SYMBIAN
       
   447     if (file.error()) {
       
   448         std::cerr << "Error: Cannot write file ";
       
   449         std::cerr << qPrintable(filePath);
       
   450         std::cerr << qPrintable(file.errorString()) << std::endl;
       
   451     }
       
   452 #endif
       
   453    return ret;
       
   454 #endif
       
   455 }
       
   456 
       
   457 
       
   458 /*!
       
   459     Overloaded function provided for convenience. Here you can manually specify QIODevice where to write report.
       
   460 */
       
   461 bool HbTextMeasurementUtility::writeReport(HbDeviceProfile &profile, QIODevice *device)
       
   462 {
       
   463 #ifndef HB_TEXT_MEASUREMENT_UTILITY
       
   464     Q_UNUSED( device );
       
   465     return false;
       
   466 #else
       
   467 
       
   468     if( device == 0 ) {
       
   469         return false;
       
   470     }
       
   471 
       
   472     bool succeed = d->validateRecords(profile);
       
   473     if (succeed) {
       
   474         qDebug() << "HbTextMeasurementUtility::writeReport: Measurements OK";
       
   475     } else {
       
   476         qDebug() << "HbTextMeasurementUtility::writeReport: Measurements NOT OK";
       
   477     }
       
   478 
       
   479     QTextStream csvWriter(device);
       
   480     d->writeHeaders(csvWriter);
       
   481     foreach (const HbTextRecord *record, d->records) {
       
   482         d->writeEntry(csvWriter, record);
       
   483     }
       
   484     return succeed;
       
   485 #endif
       
   486 }
       
   487 
       
   488 /*!
       
   489     Reset the report.
       
   490 */
       
   491 void HbTextMeasurementUtility::reset()
       
   492 {
       
   493     d->records.clear();
       
   494 }
       
   495 
       
   496 
       
   497 
       
   498