浏览代码

Merge branch '280-ctkLanguageComboBox-filename-error'

* 280-ctkLanguageComboBox-filename-error:
  Fix language inconsistencies in ctkLanguageComboBox
  Add ctkLanguageComboBoxTest
  Add US language country flag
Julien Finet 12 年之前
父节点
当前提交
6d52d91db4

二进制
Libs/Widgets/Resources/Icons/Languages/us.png


+ 1 - 0
Libs/Widgets/Resources/ctkWidgets.qrc

@@ -234,6 +234,7 @@
         <file>Icons/Languages/ua.png</file>
         <file>Icons/Languages/ug.png</file>
         <file>Icons/Languages/um.png</file>
+        <file>Icons/Languages/us.png</file>
         <file>Icons/Languages/uy.png</file>
         <file>Icons/Languages/uz.png</file>
         <file>Icons/Languages/va.png</file>

+ 3 - 0
Libs/Widgets/Testing/Cpp/CMakeLists.txt

@@ -117,6 +117,7 @@ if(CTK_USE_QTTESTING)
     ctkExpandButtonEventTranslatorPlayerTest1.cpp
     ctkFileDialogEventTranslatorPlayerTest1.cpp
     ctkFontButtonEventTranslatorPlayerTest1.cpp
+    ctkLanguageComboBoxTest.cpp
     ctkMaterialPropertyWidgetEventTranslatorPlayerTest1.cpp
     ctkMatrixWidgetEventTranslatorPlayerTest1.cpp
     ctkMenuButtonEventTranslatorPlayerTest1.cpp
@@ -177,6 +178,7 @@ QT4_GENERATE_MOCS(
   ctkCoordinatesWidgetTest.cpp
   ctkFlatProxyModelTest.cpp
   ctkFontButtonTest.cpp
+  ctkLanguageComboBoxTest.cpp
   ctkMessageBoxDontShowAgainTest.cpp
   ctkPathListWidgetTest.cpp
   ctkPathListWidgetWithButtonsTest.cpp
@@ -238,6 +240,7 @@ SIMPLE_TEST( ctkFileDialogTest1 )
 SIMPLE_TEST( ctkFittedTextBrowserTest1 )
 SIMPLE_TEST( ctkFlowLayoutTest1 )
 SIMPLE_TEST( ctkFontButtonTest )
+SIMPLE_TEST( ctkLanguageComboBoxTest )
 SIMPLE_TEST( ctkLayoutManagerTest1 )
 SIMPLE_TEST( ctkMaterialPropertyPreviewLabelTest1 )
 SIMPLE_TEST( ctkMaterialPropertyWidgetTest1 )

+ 104 - 0
Libs/Widgets/Testing/Cpp/ctkLanguageComboBoxTest.cpp

@@ -0,0 +1,104 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QSignalSpy>
+#include <QTimer>
+#include <QVBoxLayout>
+
+// CTK includes
+#include "ctkLanguageComboBox.h"
+#include "ctkTest.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+// ----------------------------------------------------------------------------
+class ctkLanguageComboBoxTester: public QObject
+{
+  Q_OBJECT
+private slots:
+
+  void testDefaults();
+
+  void testSetDefaultLanguage();
+  void testSetDefaultLanguage_data();
+};
+
+// ----------------------------------------------------------------------------
+void ctkLanguageComboBoxTester::testDefaults()
+{
+  ctkLanguageComboBox languageComboBox;
+  QCOMPARE(languageComboBox.currentLanguage(), languageComboBox.defaultLanguage());
+  QCOMPARE(languageComboBox.defaultLanguage(), QString());
+  QCOMPARE(languageComboBox.directory(), QString());
+}
+
+// ----------------------------------------------------------------------------
+void ctkLanguageComboBoxTester::testSetDefaultLanguage()
+{
+  ctkLanguageComboBox languageComboBox;
+  QFETCH(QString, defaultLanguage);
+  languageComboBox.setDefaultLanguage(defaultLanguage);
+
+  // Default language
+  QFETCH(QString, expectedDefaultLanguage);
+  QCOMPARE(languageComboBox.defaultLanguage(), expectedDefaultLanguage);
+  if (!expectedDefaultLanguage.isEmpty())
+    {
+    QCOMPARE(languageComboBox.count(), 1);
+    QCOMPARE(languageComboBox.itemData(0).toString(), expectedDefaultLanguage);
+    QCOMPARE(languageComboBox.itemText(0),
+             QLocale::languageToString(QLocale(expectedDefaultLanguage).language()));
+    }
+
+  // Current language
+  QFETCH(QString, expectedCurrentLanguage);
+  QCOMPARE(languageComboBox.currentLanguage(), expectedCurrentLanguage);
+  QCOMPARE(languageComboBox.currentIndex(), expectedCurrentLanguage.isEmpty() ? -1 : 0);
+}
+
+// ----------------------------------------------------------------------------
+void ctkLanguageComboBoxTester::testSetDefaultLanguage_data()
+{
+  QTest::addColumn<QString>("defaultLanguage");
+  QTest::addColumn<QString>("expectedDefaultLanguage");
+  QTest::addColumn<QString>("expectedCurrentLanguage");
+
+  QTest::newRow("invalid") << QString() << QString() << QString();
+  QTest::newRow("empty") << QString("") << QString("") << QString("");
+  QTest::newRow("en") << QString("en") << QString("en_US") << QString("en_US");
+  // ISO_3166 says that The United Kingdom is GB not UK
+  QTest::newRow("en_UK") << QString("en_UK") << QString("en_US") << QString("en_US");
+  QTest::newRow("en_GB") << QString("en_GB") << QString("en_GB") << QString("en_GB");
+  QTest::newRow("fr") << QString("fr") << QString("fr_FR") << QString("fr_FR");
+  QTest::newRow("de_CH") << QString("de_CH") << QString("de_CH") << QString("de_CH");
+  QTest::newRow("de_ch") << QString("de_ch") << QString("de_DE") << QString("de_DE");
+  QTest::newRow("xx") << QString("xx") << QString() << QString();
+  QTest::newRow("xx_yy") << QString("xx_yy") << QString() << QString();
+  QTest::newRow("_en") << QString("_en") << QString() << QString();
+  QTest::newRow("fr_") << QString("fr_") << QString("fr_FR") << QString("fr_FR");
+}
+
+// ----------------------------------------------------------------------------
+CTK_TEST_MAIN(ctkLanguageComboBoxTest)
+#include "moc_ctkLanguageComboBoxTest.cpp"

+ 122 - 40
Libs/Widgets/ctkLanguageComboBox.cpp

@@ -34,11 +34,14 @@ protected:
 public:
   ctkLanguageComboBoxPrivate(ctkLanguageComboBox& object);
   void init();
-  void addLanguages(const QStringList& languages);
-  void addLanguage(const QString& language);
-
-  QString   DefaultLanguage;
-  QString   Dir;
+  void addLanguageFiles(const QStringList& fileNames);
+  bool addLanguage(const QString& language);
+  bool insertLanguage(int index, const QString& language);
+  bool languageItem(const QString& language,
+                    QIcon& icon, QString& text,QVariant& data);
+
+  QString DefaultLanguage;
+  QString LanguageDirectory;
 };
 
 // ----------------------------------------------------------------------------
@@ -51,68 +54,103 @@ ctkLanguageComboBoxPrivate::ctkLanguageComboBoxPrivate(ctkLanguageComboBox &obje
 void ctkLanguageComboBoxPrivate::init()
 {
   Q_Q(ctkLanguageComboBox);
-  /// Recover all the translation file from the directory Translations
-  QDir translationDir = QDir(this->Dir);
 
-  QStringList languages = translationDir.entryList(QStringList("*.qm"));
+  QObject::connect(q, SIGNAL(currentIndexChanged(int)),
+                   q, SLOT(onLanguageChanged(int)));
 
-  /// Add default language.
-  if (!this->DefaultLanguage.isEmpty())
+  /// Add default language if any
+  if (this->DefaultLanguage.isEmpty())
     {
     this->addLanguage(this->DefaultLanguage);
     }
-  /// Add all the languages availables
-  this->addLanguages(languages);
-
-  QObject::connect(q, SIGNAL(currentIndexChanged(int)),
-                   q, SLOT(onLanguageChanged(int)));
 }
 
 // ----------------------------------------------------------------------------
-void ctkLanguageComboBoxPrivate::addLanguages(const QStringList& languages)
+void ctkLanguageComboBoxPrivate::addLanguageFiles(const QStringList& fileNames)
 {
-  foreach(QString language, languages)
+  foreach(QString fileName, fileNames)
     {
+    QFileInfo file(fileName);
+    if (!file.exists())
+      {
+      qWarning() << "File " << file.absoluteFilePath() << " doesn't exist.";
+      }
+    // language is "de_ch" for a file named "/abc/def_de_ch.qm"
+    QString language = file.completeBaseName();
     language.remove(0,language.indexOf('_') + 1);
-    language.chop(3);
     this->addLanguage(language);
     }
 }
 
 // ----------------------------------------------------------------------------
-void ctkLanguageComboBoxPrivate::addLanguage(const QString& language)
+bool ctkLanguageComboBoxPrivate::addLanguage(const QString& language)
 {
   Q_Q(ctkLanguageComboBox);
-  QLocale lang(language);
-  QString icon = ":Icons/Languages/";
-  icon += language;
-  icon += ".png";
-  q->addItem(QIcon(icon), QLocale::languageToString(lang.language()), language);
+  return this->insertLanguage(q->count(), language);
 }
 
 // ----------------------------------------------------------------------------
-ctkLanguageComboBox::ctkLanguageComboBox(QWidget* _parent)
-  : QComboBox(_parent)
-  , d_ptr(new ctkLanguageComboBoxPrivate(*this))
+bool ctkLanguageComboBoxPrivate::insertLanguage(int index, const QString& language)
 {
+  Q_Q(ctkLanguageComboBox);
+  QIcon icon;
+  QString text;
+  QVariant data;
+  bool res =this->languageItem(language, icon, text, data);
+  if (res)
+    {
+    q->insertItem(index, icon, text, data);
+    }
+  return res;
 }
 
 // ----------------------------------------------------------------------------
-ctkLanguageComboBox::~ctkLanguageComboBox()
+bool ctkLanguageComboBoxPrivate::languageItem(const QString& language,
+                                              QIcon& icon,
+                                              QString& text,
+                                              QVariant& data)
 {
+  QLocale locale(language);
+  if (language.isEmpty() ||
+      locale.name() == "C")
+    {
+    icon = QIcon();
+    text = QString();
+    data = QVariant();
+    return false;
+    }
+  QString countryFlag = locale.name();
+  countryFlag.remove(0, countryFlag.lastIndexOf('_') + 1);
+  countryFlag = countryFlag.toLower();
+  icon = QIcon(QString(":Icons/Languages/%1.png").arg(countryFlag));
+  text = QLocale::languageToString(locale.language());
+  data = locale.name();
+  return true;
 }
 
 // ----------------------------------------------------------------------------
-QString ctkLanguageComboBox::currentLanguage() const
+ctkLanguageComboBox::ctkLanguageComboBox(QWidget* _parent)
+  : QComboBox(_parent)
+  , d_ptr(new ctkLanguageComboBoxPrivate(*this))
 {
-  return this->itemData(this->currentIndex()).toString();
+  Q_D(ctkLanguageComboBox);
+  d->init();
 }
 
 // ----------------------------------------------------------------------------
-void ctkLanguageComboBox::setCurrentLanguage(const QString &language)
+ctkLanguageComboBox::ctkLanguageComboBox(const QString& defaultLanguage,
+                                         QWidget* _parent)
+  : QComboBox(_parent)
+  , d_ptr(new ctkLanguageComboBoxPrivate(*this))
+{
+  Q_D(ctkLanguageComboBox);
+  d->DefaultLanguage = defaultLanguage;
+  d->init();
+}
+
+// ----------------------------------------------------------------------------
+ctkLanguageComboBox::~ctkLanguageComboBox()
 {
-  int index = this->findData(QVariant(language));
-  this->setCurrentIndex(index);
 }
 
 // ----------------------------------------------------------------------------
@@ -126,29 +164,73 @@ QString ctkLanguageComboBox::defaultLanguage() const
 void ctkLanguageComboBox::setDefaultLanguage(const QString& language)
 {
   Q_D(ctkLanguageComboBox);
-  d->DefaultLanguage = language;
+  bool isValid = false;
+  if (!d->DefaultLanguage.isEmpty())
+    {
+    QIcon icon;
+    QString text;
+    QVariant data;
+    isValid = d->languageItem(language, icon, text, data);
+    if (isValid)
+      {
+      // Replace the default language
+      this->setItemIcon(0, icon);
+      this->setItemText(0, text);
+      this->setItemData(0, data);
+      }
+    else
+      {
+      this->removeItem(0);
+      }
+    }
+  else
+    {
+    isValid = d->insertLanguage(0, language);
+    }
+  d->DefaultLanguage = isValid ? this->itemData(0).toString() : QString();
+}
+
+// ----------------------------------------------------------------------------
+QString ctkLanguageComboBox::currentLanguage() const
+{
+  return this->itemData(this->currentIndex()).toString();
+}
+
+// ----------------------------------------------------------------------------
+void ctkLanguageComboBox::setCurrentLanguage(const QString &language)
+{
+  int index = this->findData(QVariant(language));
+  this->setCurrentIndex(index);
 }
 
 // ----------------------------------------------------------------------------
 QString ctkLanguageComboBox::directory() const
 {
   Q_D(const ctkLanguageComboBox);
-  return d->Dir;
+  return d->LanguageDirectory;
 }
 
 // ----------------------------------------------------------------------------
 void ctkLanguageComboBox::setDirectory(const QString& dir)
 {
   Q_D(ctkLanguageComboBox);
-  d->Dir = dir;
-  d->init();
+
+  d->LanguageDirectory = dir;
+
+  /// Recover all the translation file from the directory Translations
+  QDir translationDir = QDir(d->LanguageDirectory);
+  QStringList languages = translationDir.entryList(QStringList("*.qm"));
+
+  /// Add all the languages availables
+  d->addLanguageFiles(languages);
+
   this->update();
 }
 
 // ----------------------------------------------------------------------------
 void ctkLanguageComboBox::onLanguageChanged(int index)
 {
-  QVariant lang = this->itemData(index);
-  //QLocale locale(lang.toString());
-  emit currentLanguageNameChanged(lang.toString());
+  Q_UNUSED(index);
+  QString currentLanguage = this->currentLanguage();
+  emit currentLanguageNameChanged(currentLanguage);
 }

+ 47 - 16
Libs/Widgets/ctkLanguageComboBox.h

@@ -26,56 +26,87 @@
 
 // CTK includes
 #include "ctkWidgetsExport.h"
+class ctkLanguageComboBoxPrivate;
 
 /// ctkLanguageComboBox is a simple QComboBox to select the language
 /// of your application.
-
 /// You have to set the default language of your application and then
 /// set the directory to allow the comboBox to find the translation files.
-/// ctkLanguageComboBox automaically recognizes the language of the
-/// translation file by the suffix _en or _fr and add the associated
+/// ctkLanguageComboBox automatically recognizes the language of the
+/// translation file by the suffix "_en" or "_fr" and add the associated
 /// language to the comboBox.
-
 /// \note:
 /// Translation files names need to finish with the suffix of the
 /// country.
 /// Example: for a french traduction, xxxx_fr.ts
-
-class ctkLanguageComboBoxPrivate;
-
+/// \warning Please don't use QComboBox methods when using this class.
 class CTK_WIDGETS_EXPORT ctkLanguageComboBox : public QComboBox
 {
   Q_OBJECT
-  Q_PROPERTY(QString currentLanguage READ currentLanguage WRITE setCurrentLanguage)
+  /// \brief This property controls the default language of the application.
+  ///
+  /// The default language is the language in which all the texts in GUI
+  /// elements are written.
+  /// As the application doesn't have translation file for the default
+  /// language, this property adds an item to the combobox.
+  /// The language format is a lowercase, two-letter, ISO 639 language code.
+  /// For example: "fr", "en" or "de_ch".
+  /// If empty, there is no default language, and there is no entry added.
+  /// By default, there is no default language.
+  /// \sa defaultLanguage(), setDefaultLanguage(), QLocale::setDefault()
+  Q_PROPERTY(QString defaultLanguage READ defaultLanguage WRITE setDefaultLanguage)
+
+  /// This property controls the directory where the translation files are
+  /// located.
+  /// \sa directory(), setDirectory()
   Q_PROPERTY(QString directory READ directory WRITE setDirectory)
 
+  /// This property controls the current language of the combobox.
+  /// The \a defaultLanguage by default.
+  /// \sa currentLanguage(), setCurrentLanguage()
+  Q_PROPERTY(QString currentLanguage READ currentLanguage WRITE setCurrentLanguage NOTIFY currentLanguageNameChanged USER true)
+
 public:
   typedef QComboBox Superclass;
+  /// Constructor of ctkLanguageComboBox
   ctkLanguageComboBox(QWidget *parent = 0);
+  /// Constructor that specifies a default language.
+  /// \sa defaultLanguage
+  ctkLanguageComboBox(const QString& defaultLanguage, QWidget *parent = 0);
   virtual ~ctkLanguageComboBox();
 
-  /// Return the currentLanguage of the combobox.
-  QString currentLanguage()const;
-  void setCurrentLanguage(const QString& language);
-
-  /// Set the default language of your application.
-  /// As the application doesn't have translation file for the default
-  /// language, it's very important to set this variable. Otherwise,
-  /// the default language is not added to the ComboBox.
+  /// Return the default language.
+  /// \sa defaultLanguage, setDefaultLanguage()
   QString defaultLanguage()const;
+
+  /// Set the default language. The previous default language is removed and
+  /// replaced with the new default language.
+  /// \sa defaultLanguage, defaultLanguage()
   void setDefaultLanguage(const QString& language);
 
   /// Set the \a directory with all the translation files.
   /// The list of available languages will be populated based on
   /// the discovered translation files.
+  /// The default language will still be the first item in the menu.
+  /// Empty by default.
   QString directory()const;
   void setDirectory(const QString& dir);
 
+  /// Return the currently selected language of the combobox.
+  /// \sa currentLanguage, setCurrentLanguage()
+  QString currentLanguage()const;
+
+public Q_SLOTS:
+  /// Set the current language
+  /// \sa currentLanguage, currentLanguage()
+  void setCurrentLanguage(const QString& language);
+
 protected slots:
   void onLanguageChanged(int index);
 
 signals:
   /// Signals emitted when the current language changed.
+  /// \sa QLocale::name()
   void currentLanguageNameChanged(const QString&);
 
 protected: