Porting a KControl Module to KF5

Over the last days I ported a few KControl Modules (KCM) to frameworks 5. As it’s a rather simple task I decided to document the needed steps. Yesterday I ported over KInfoCenter with all it’s modules:

Aus 2013-10-15

First some preparation tasks. I highly recommend to configure kdevelop to not stop on the first compile error. This helps to find pattern in the errors during the porting and also allows to start with the most easy tasks and not have to start with a difficult one, which might turn out to be a non-issue once the other errors are corrected. I also recommend to have the KDE5PORTING.html from kdelibs-frameworks open in your browser.

Adjust CMakeLists.txt

First of all you need to re-enable the directory containing the KCM in CMakeLists.txt. Then one should do small adjustments in the KCM’s CMakeList.txt.

Drop any kde4_no_enable_final line – that got dropped:

kde4_no_enable_final(foo)

Remove a few definitions to get less compile errors – especially you don’t want a compile error for each cast from const char* to QString:

remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS)

Search for the target_link_libraries of the KCM and remove all variables with Qt4 and KDE4. Instead just add a few frameworks you can be sure to need:

target_link_libraries(kcm_foo
    KF5::KCMUtils
    KF5::KI18n
    ${KDE4Support_LIBRARIES}
)

This should be enough for getting most of a KCM to compile. Further frameworks should be added once you hit linker errors.

Adjust desktop file

The next step is rather simple. Look for the desktop file of the kcm. Most often called foo.desktop and look for the Exec line and change from kcmshell4 to kcmshell5:

 [Desktop Entry]
Exec=kcmshell5 foo

Common Compile Problems

Now it’s time to start the compile, fix loop till you hit linker errors. I just want to present the most common errors I have hit so far and how to fix them.

Remove QtGui/ from includes

/home/martin/src/kf5/kde-workspace/kcontrol/keys/kglobalshortcutseditor.cpp:37:32: fatal error: QtGui/QStackedWidget: No such file or directory
 #include <QtGui/QStackedWidget>
                                ^
compilation terminated.

This one is rather simple: just remove all QtGui/ or QtCore/ from the #include. There’s also a small helper application in the Qt source tree, but for a small code base it might be easier to just fix manually. Remember to use block selection mode if the includes are all nicely one below the other.

Q_SLOTS

A common problem is that the code uses slots and signals and that should be Q_SLOTS or Q_SIGNALS. A compile error looks like this:

/home/martin/src/kf5/kde-workspace/kcontrol/keys/select_scheme_dialog.h:38:9: error: expected ‘:’ before ‘slots’
 private slots:
         ^
/home/martin/src/kf5/kde-workspace/kcontrol/keys/select_scheme_dialog.h:38:9: error: ‘slots’ does not name a type

Easy to fix: just replace by Q_SLOTS. I recommend to directly recompile after one of those errors as they cause many compile issues.

i18n

In KDELibs4 the include of KLocale was needed for i18n, in KF5 it’s KLocalizedString. So this is bound to fail:

/home/martin/src/kf5/kde-workspace/kcontrol/keys/globalshortcuts.cpp:67:89: error: ‘i18n’ was not declared in this scope
                     i18n("You are about to reset all shortcuts to their default values."),
                                                                                         ^

The fix is really simple. Look for

#include <KLocale>

and replace by

#include <KLocalizedString>

In the uncommon situation that something from KLocale is used you of course need to keep it and add ${KDE4Attic_LIBRARIES} to the target_link_libraries in the CMakeLists.txt. Another possibility is to directly port to QLocale.

KGlobal::config()

In case you get a compile error about missing KGlobal when using KGlobal::config(), do not just add the missing include, but port over to the new way:

KSharedConfig::openConfig();

KComponentData

Each KCM I ported so far failed with the following error in the ctor:

/home/martin/src/kf5/kde-workspace/kcontrol/keys/globalshortcuts.cpp: In constructor ‘GlobalShortcutsModule::GlobalShortcutsModule(QWidget*, const QVariantList&)’:
/home/martin/src/kf5/kde-workspace/kcontrol/keys/globalshortcuts.cpp:37:13: error: ‘componentData’ is not a member of ‘GlobalShortcutsModuleFactory’
  : KCModule(GlobalShortcutsModuleFactory::componentData(), parent, args),
             ^

The solution is again very simple: just drop the call to componentData():

  : KCModule(parent, args)

KAboutData

KAboutData changed in frameworks with the old one being moved to K4AboutData. Luckily the changes are rather simple and a pattern can be used:

  • ki18n -> i18n
  • KLocalizedString() -> QString()
  • 0 -> QString()
  • wrap normal string in QStringLiteral()
    • As an example the old code:

           KAboutData *about =
               new KAboutData(I18N_NOOP("kcmfoo"), 0,
                              ki18n("KDE Foo Module"),
                              0, KLocalizedString(), KAboutData::License_GPL,
                              ki18n("(c) 2013 Bar FooBar, Konqui"));
      
          about->addAuthor(ki18n("Bar FooBar"), KLocalizedString(), "foobar@kde.org");
          about->addAuthor(ki18n("Konqui"), KLocalizedString(), "konqui@kde.org");
      

      becomes:

           KAboutData *about =
              new KAboutData(I18N_NOOP("kcmfoo"), QString(),
                             i18n("KDE Foo Module"),
                             QString(), QString(), KAboutData::License_GPL,
                             i18n("(c) 2013 Bar FooBar, Konqui"));
      
          about->addAuthor(i18n("Bar FooBar"), QString(), QStringLiteral("foobar@kde.org"));
          about->addAuthor(i18n("Konqui"), QString(), QStringLiteral("konqui@kde.org"));
      

      Common other problems

      Usages of KUrl can in most cases just be switched to QUrl and it will work as intended. The same is true for KAction, though if it sets global shortcuts you need to properly port following the steps in the porting guide. A usage of KFileDialog can be changed to QFileDialog – be aware that the order of arguments is different in QFileDialog.

      Linker errors

      If everything went fine you should reach a point where you hit linker errors. Now we need to add the required frameworks to the target link libraries. I try to locate the header file of a class which threw a linker error to get the framework. E.g.

      $ locate kiconloader.h
      /home/martin/src/kf5/kdelibs-frameworks/tier3/kiconthemes/src/kiconloader.h
      

      So the file is part of KIconThemes and thus one needs to add KF5::KIconThemes to target_link_libraries.

      Testing

      Once all linker errors are fixed, install the kcm, and run in your KF5 environment:

      $ kbuildsycoca5
      $ kcmshell5 foo
      

      kcmshell5 is part of kde-runtime. In case you don’t have that one installed you can also use systemsettings for a KCM that is listed there. Otherwise just build kde-runtime.

      And that’s it. Not much work and hardly any code to change.

9 thoughts on “Porting a KControl Module to KF5”

  1. In an only slightly related note, “(c)” is not a proper copyright sign and therefore not recognized anywhere. When you are touching the About code anyway, you should change the copyright message to “Copyright ©”

    1. You are hopefully aware that this belongs into the area of urban legend. If that were the case all of KWin would not be copyrighted. This used to be the case in the US three decades ago.

  2. hi
    surely out of topic, talking about oxygen widget theme..
    today i used the first qt5 based app (transmission-qt) and it looks ugly.
    since oxygen is already been ported to qt5, can we have an oxygen-qt5 release – as we have oxygen-gtk2 and oxygen-gtk3 – until workspaces2 is out?
    maybe it could be useful for debugging purposes.
    thanks

  3. When porting note that there is a guide KDE5Porting.html inside the kdelibs-frameworks source directory. This (should) explain any of changes not listed here.

      1. So it is. I’m going to pretend you sneakily edited it after I made my comment so I feel like less of a fool.

Comments are closed.