From 574126895e619f98fabeaad501cef4627b48af19 Mon Sep 17 00:00:00 2001 From: trialuser02 Date: Thu, 1 Jan 2009 14:12:32 +0000 Subject: mplayer plugin: added details dialog and Russsian translation git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@724 90c681e8-e032-0410-971d-27865f9a5e38 --- src/plugins/Input/mplayer/CMakeLists.txt | 9 +- .../Input/mplayer/decodermplayerfactory.cpp | 20 +- src/plugins/Input/mplayer/detailsdialog.cpp | 106 ++++++ src/plugins/Input/mplayer/detailsdialog.h | 43 +++ src/plugins/Input/mplayer/detailsdialog.ui | 390 +++++++++++++++++++++ src/plugins/Input/mplayer/mplayer.pro | 21 +- .../mplayer/translations/mplayer_plugin_cs.ts | 134 +++++++ .../mplayer/translations/mplayer_plugin_de.ts | 134 +++++++ .../mplayer/translations/mplayer_plugin_ru.ts | 134 +++++++ .../mplayer/translations/mplayer_plugin_uk_UA.ts | 134 +++++++ .../mplayer/translations/mplayer_plugin_zh_CN.ts | 134 +++++++ .../mplayer/translations/mplayer_plugin_zh_TW.ts | 134 +++++++ .../Input/mplayer/translations/translations.qrc | 11 + 13 files changed, 1384 insertions(+), 20 deletions(-) create mode 100644 src/plugins/Input/mplayer/detailsdialog.cpp create mode 100644 src/plugins/Input/mplayer/detailsdialog.h create mode 100644 src/plugins/Input/mplayer/detailsdialog.ui create mode 100644 src/plugins/Input/mplayer/translations/mplayer_plugin_cs.ts create mode 100644 src/plugins/Input/mplayer/translations/mplayer_plugin_de.ts create mode 100644 src/plugins/Input/mplayer/translations/mplayer_plugin_ru.ts create mode 100644 src/plugins/Input/mplayer/translations/mplayer_plugin_uk_UA.ts create mode 100644 src/plugins/Input/mplayer/translations/mplayer_plugin_zh_CN.ts create mode 100644 src/plugins/Input/mplayer/translations/mplayer_plugin_zh_TW.ts create mode 100644 src/plugins/Input/mplayer/translations/translations.qrc (limited to 'src/plugins/Input/mplayer') diff --git a/src/plugins/Input/mplayer/CMakeLists.txt b/src/plugins/Input/mplayer/CMakeLists.txt index 870b06ef1..3cb2bc956 100644 --- a/src/plugins/Input/mplayer/CMakeLists.txt +++ b/src/plugins/Input/mplayer/CMakeLists.txt @@ -30,16 +30,18 @@ link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../qmmp) SET(libmplayer_SRCS decoder_mplayer.cpp decodermplayerfactory.cpp + detailsdialog.cpp # settingsdialog.cpp ) SET(libmplayer_MOC_HDRS decodermplayerfactory.h decoder_mplayer.h + detailsdialog.h # settingsdialog.h ) -#SET(libmplayer_RCCS translations/translations.qrc) +SET(libmplayer_RCCS translations/translations.qrc) QT4_ADD_RESOURCES(libmplayer_RCC_SRCS ${libmplayer_RCCS}) @@ -48,9 +50,10 @@ QT4_WRAP_CPP(libmplayer_MOC_SRCS ${libmplayer_MOC_HDRS}) # user interface -#SET(libmplayer_UIS +SET(libmplayer_UIS # settingsdialog.ui -#) + detailsdialog.ui +) QT4_WRAP_UI(libmplayer_UIS_H ${libmplayer_UIS}) # Don't forget to include output directory, otherwise diff --git a/src/plugins/Input/mplayer/decodermplayerfactory.cpp b/src/plugins/Input/mplayer/decodermplayerfactory.cpp index b274cdb07..acabeda40 100644 --- a/src/plugins/Input/mplayer/decodermplayerfactory.cpp +++ b/src/plugins/Input/mplayer/decodermplayerfactory.cpp @@ -19,6 +19,7 @@ ***************************************************************************/ #include +#include "detailsdialog.h" #include "decoder_mplayer.h" #include "decodermplayerfactory.h" @@ -59,7 +60,7 @@ const DecoderProperties DecoderMplayerFactory::properties() const } Decoder *DecoderMplayerFactory::create(QObject *parent, QIODevice *input, - Output *output, const QString &url) + Output *output, const QString &url) { Q_UNUSED(input); Q_UNUSED(output); @@ -74,21 +75,24 @@ QList DecoderMplayerFactory::createPlayList(const QString &fileName, return info; } -QObject* DecoderMplayerFactory::showDetails(QWidget *, const QString &) +QObject* DecoderMplayerFactory::showDetails(QWidget *parent, const QString &path) { - return 0; + DetailsDialog *d = new DetailsDialog(path, parent); + d->show(); + return d; } void DecoderMplayerFactory::showSettings(QWidget *) { - /*SettingsDialog *s = new SettingsDialog(parent); - s->show();*/ + /*SettingsDialog *s = new SettingsDialog(parent); + s->show();*/ } void DecoderMplayerFactory::showAbout(QWidget *parent) { - QMessageBox::about (parent, tr("About Mplayer Audio Plugin"), - tr("Qmmp Mplayer Audio Plugin")+"\n"+ + QMessageBox::about (parent, tr("About MPlayer Plugin"), + tr("Qmmp MPlayer Plugin")+"\n"+ + tr("This plugin uses MPlayer as backend")+"\n"+ tr("Writen by: Ilya Kotov ")); } @@ -96,7 +100,7 @@ QTranslator *DecoderMplayerFactory::createTranslator(QObject *parent) { QTranslator *translator = new QTranslator(parent); QString locale = QLocale::system().name(); - translator->load(QString(":/phonon_plugin_") + locale); + translator->load(QString(":/mplayer_plugin_") + locale); return translator; } diff --git a/src/plugins/Input/mplayer/detailsdialog.cpp b/src/plugins/Input/mplayer/detailsdialog.cpp new file mode 100644 index 000000000..b24fed9dc --- /dev/null +++ b/src/plugins/Input/mplayer/detailsdialog.cpp @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2009 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include +#include + +#include "detailsdialog.h" + +DetailsDialog::DetailsDialog(const QString &path, QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + ui.pathLineEdit->setText(path); + ui.sizeLabel->setText(QString("%1 ").arg(QFileInfo(path).size ()/1024)+tr("KB")); + //regular expressions + QRegExp rx_id_length("^ID_LENGTH=([0-9,.]+)*"); + QRegExp rx_id_demuxer("^ID_DEMUXER=(.*)"); + QRegExp rx_id_video_bitrate("^ID_VIDEO_BITRATE=([0-9,.]+)*"); + QRegExp rx_id_width("^ID_VIDEO_WIDTH=([0-9,.]+)*"); + QRegExp rx_id_height("^ID_VIDEO_HEIGHT=([0-9,.]+)*"); + QRegExp rx_id_video_format("^ID_VIDEO_FORMAT=(.*)"); + QRegExp rx_id_video_fps("^ID_VIDEO_FPS=([0-9,.]+)*"); + QRegExp rx_id_video_codec("^ID_VIDEO_CODEC=(.*)"); + QRegExp rx_id_video_aspect("^ID_VIDEO_ASPECT=([0-9,.]+)*"); + QRegExp rx_id_audio_bitrate("^ID_AUDIO_BITRATE=([0-9,.]+)*"); + QRegExp rx_id_audio_rate("^ID_AUDIO_RATE=([0-9,.]+)*"); + QRegExp rx_id_audio_nch("^ID_AUDIO_NCH=([0-9,.]+)*"); + QRegExp rx_id_audio_codec("^ID_AUDIO_CODEC=(.*)"); + //prepare and start mplayer process + QStringList args; + args << "-slave"; + args << "-identify"; + args << "-frames"; + args << "0"; + args << "-vo"; + args << "null"; + args << "-ao"; + args << "null"; + args << path; + QProcess mplayer_process; + mplayer_process.start("mplayer", args); + mplayer_process.waitForFinished(); + QString str = QString::fromLocal8Bit(mplayer_process.readAll()).trimmed(); + QStringList lines = str.split("\n"); + int height = 0, width = 0; + //mplayer std output parsing + foreach(QString line, lines) + { + //general info + if (rx_id_length.indexIn(line) > -1) + ui.lengthLabel->setText(rx_id_length.cap(1)); //TODO use hh:mm:ss format + else if (rx_id_demuxer.indexIn(line) > -1) + ui.demuxerLabel->setText(rx_id_demuxer.cap(1)); + //video info + else if (rx_id_video_format.indexIn(line) > -1) + ui.videoFormatLabel->setText(rx_id_video_format.cap(1)); + else if (rx_id_video_fps.indexIn(line) > -1) + ui.fpsLabel->setText(rx_id_video_fps.cap(1)); + else if (rx_id_video_codec.indexIn(line) > -1) + ui.videoCodecLabel->setText(rx_id_video_codec.cap(1)); + else if (rx_id_video_aspect.indexIn(line) > -1) + ui. ratioLabel->setText(rx_id_video_aspect.cap(1)); + else if (rx_id_video_bitrate.indexIn(line) > -1) + ui.videoBitrateLabel->setText(rx_id_video_bitrate.cap(1)); + else if (rx_id_width.indexIn(line) > -1) + width = rx_id_width.cap(1).toInt(); + else if (rx_id_height.indexIn(line) > -1) + height = rx_id_height.cap(1).toInt(); + //audio info + else if (rx_id_audio_codec.indexIn(line) > -1) + ui.audioCodecLabel->setText(rx_id_audio_codec.cap(1)); + else if (rx_id_audio_rate.indexIn(line) > -1) + ui.sampleRateLabel->setText(rx_id_audio_rate.cap(1)); + else if (rx_id_audio_bitrate.indexIn(line) > -1) + ui.audioBitrateLabel->setText(rx_id_audio_bitrate.cap(1)); + else if (rx_id_audio_nch.indexIn(line) > -1) + ui.channelsLabel->setText(rx_id_audio_nch.cap(1)); + } + ui.resolutionLabel->setText(QString("%1x%2").arg(width).arg(height)); +} + + +DetailsDialog::~DetailsDialog() +{ +} + + diff --git a/src/plugins/Input/mplayer/detailsdialog.h b/src/plugins/Input/mplayer/detailsdialog.h new file mode 100644 index 000000000..0d4af20a7 --- /dev/null +++ b/src/plugins/Input/mplayer/detailsdialog.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2009 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef DETAILSDIALOG_H +#define DETAILSDIALOG_H + +#include + +#include "ui_detailsdialog.h" + +/** + @author Ilya Kotov +*/ +class DetailsDialog : public QDialog +{ + Q_OBJECT +public: + DetailsDialog(const QString &path, QWidget *parent = 0); + + ~DetailsDialog(); + +private: + Ui::DetailsDialog ui; + +}; + +#endif diff --git a/src/plugins/Input/mplayer/detailsdialog.ui b/src/plugins/Input/mplayer/detailsdialog.ui new file mode 100644 index 000000000..37358df2b --- /dev/null +++ b/src/plugins/Input/mplayer/detailsdialog.ui @@ -0,0 +1,390 @@ + + DetailsDialog + + + + 0 + 0 + 415 + 351 + + + + Details + + + + 5 + + + 9 + + + 5 + + + 5 + + + + + General information + + + + + + File path: + + + + + + + true + + + + + + + Size: + + + + + + + - + + + + + + + Demuxer: + + + + + + + - + + + + + + + Length: + + + + + + + - + + + + + + + + + + + 0 + 0 + + + + Video + + + + + + + 0 + 0 + + + + Resolution: + + + + + + + + 0 + 0 + + + + - + + + + + + + + 0 + 0 + + + + Bitrate: + + + + + + + - + + + + + + + + 0 + 0 + + + + Format: + + + + + + + - + + + + + + + + 0 + 0 + + + + FPS: + + + + + + + - + + + + + + + + 0 + 0 + + + + Codec: + + + + + + + - + + + + + + + + 0 + 0 + + + + Aspect ratio: + + + + + + + - + + + + + + + + + + Audio + + + + + + + 0 + 0 + + + + Codec: + + + + + + + + 0 + 0 + + + + - + + + + + + + + 0 + 0 + + + + Sample rate: + + + + + + + - + + + + + + + + 0 + 0 + + + + Bitrate: + + + + + + + - + + + + + + + + 0 + 0 + + + + Channels: + + + + + + + - + + + + + + + Qt::Vertical + + + + 20 + 37 + + + + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 330 + 24 + + + + + + + + Close + + + + + + + + + + + closeButton + clicked() + DetailsDialog + accept() + + + 381 + 334 + + + 23 + 324 + + + + + diff --git a/src/plugins/Input/mplayer/mplayer.pro b/src/plugins/Input/mplayer/mplayer.pro index c3472ebd8..c94d7a388 100644 --- a/src/plugins/Input/mplayer/mplayer.pro +++ b/src/plugins/Input/mplayer/mplayer.pro @@ -1,10 +1,12 @@ include(../../plugins.pri) HEADERS += decodermplayerfactory.h \ - decoder_mplayer.h + decoder_mplayer.h \ + detailsdialog.h SOURCES += decoder_mplayer.cpp \ - decodermplayerfactory.cpp + decodermplayerfactory.cpp \ + detailsdialog.cpp TARGET =$$PLUGINS_PREFIX/Input/mplayer QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libmplayer.so @@ -19,13 +21,13 @@ TEMPLATE = lib QMAKE_LIBDIR += ../../../../lib LIBS += -lqmmp -L/usr/lib -#TRANSLATIONS = translations/mplayer_plugin_ru.ts -# translations/mplayer_plugin_uk_UA.ts -# translations/mplayer_plugin_zh_CN.ts -# translations/mplayer_plugin_zh_TW.ts -# translations/mplayer_plugin_cs.ts -# translations/mplayer_plugin_de.ts -#RESOURCES = translations/translations.qrc +TRANSLATIONS = translations/mplayer_plugin_ru.ts \ + translations/mplayer_plugin_uk_UA.ts \ + translations/mplayer_plugin_zh_CN.ts \ + translations/mplayer_plugin_zh_TW.ts \ + translations/mplayer_plugin_cs.ts \ + translations/mplayer_plugin_de.ts +RESOURCES = translations/translations.qrc isEmpty(LIB_DIR){ LIB_DIR = /lib @@ -33,3 +35,4 @@ isEmpty(LIB_DIR){ target.path = $$LIB_DIR/qmmp/Input INSTALLS += target +FORMS += detailsdialog.ui diff --git a/src/plugins/Input/mplayer/translations/mplayer_plugin_cs.ts b/src/plugins/Input/mplayer/translations/mplayer_plugin_cs.ts new file mode 100644 index 000000000..3fffc818b --- /dev/null +++ b/src/plugins/Input/mplayer/translations/mplayer_plugin_cs.ts @@ -0,0 +1,134 @@ + + + + DecoderMplayerFactory + + + Mplayer Plugin + + + + + Video Files + + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + Autor: Ilja Kotov <forkotov02@hotmail.ru> + + + + About MPlayer Plugin + + + + + Qmmp MPlayer Plugin + + + + + This plugin uses MPlayer as backend + + + + + DetailsDialog + + + KB + KB + + + + - + - + + + + Sample rate: + Vzorkovací frekvence: + + + + Close + Zavřít + + + + File path: + Cesta k souboru: + + + + Size: + + + + + Demuxer: + + + + + Length: + + + + + Video + + + + + Resolution: + + + + + Bitrate: + + + + + Format: + + + + + FPS: + + + + + Codec: + + + + + Aspect ratio: + + + + + Audio + + + + + Channels: + + + + + Details + + + + + General information + + + + diff --git a/src/plugins/Input/mplayer/translations/mplayer_plugin_de.ts b/src/plugins/Input/mplayer/translations/mplayer_plugin_de.ts new file mode 100644 index 000000000..2b9c5b903 --- /dev/null +++ b/src/plugins/Input/mplayer/translations/mplayer_plugin_de.ts @@ -0,0 +1,134 @@ + + + + DecoderMplayerFactory + + + Mplayer Plugin + + + + + Video Files + + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + Autor: Ilja Kotov <forkotov02@hotmail.ru> + + + + About MPlayer Plugin + + + + + Qmmp MPlayer Plugin + + + + + This plugin uses MPlayer as backend + + + + + DetailsDialog + + + KB + KB + + + + - + - + + + + Sample rate: + Abtastrate: + + + + Close + Schließen + + + + File path: + Dateipfad: + + + + Size: + + + + + Demuxer: + + + + + Length: + + + + + Video + + + + + Resolution: + + + + + Bitrate: + + + + + Format: + + + + + FPS: + + + + + Codec: + + + + + Aspect ratio: + + + + + Audio + + + + + Channels: + + + + + Details + + + + + General information + + + + diff --git a/src/plugins/Input/mplayer/translations/mplayer_plugin_ru.ts b/src/plugins/Input/mplayer/translations/mplayer_plugin_ru.ts new file mode 100644 index 000000000..3033a6cb5 --- /dev/null +++ b/src/plugins/Input/mplayer/translations/mplayer_plugin_ru.ts @@ -0,0 +1,134 @@ + + + + DecoderMplayerFactory + + + Mplayer Plugin + Модуль MPlayer + + + + Video Files + Файлы видео + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + Разработчик: Илья Котов <forkotov02@hotmail.ru> + + + + About MPlayer Plugin + О модуле MPlayer для Qmmp + + + + Qmmp MPlayer Plugin + Модуль поддержки MPlayer для Qmmp + + + + This plugin uses MPlayer as backend + В этом модуле для воспроизведения используется Mplayer + + + + DetailsDialog + + + Close + Закрыть + + + + - + - + + + + Sample rate: + Дискретизация: + + + + File path: + Путь к файлу: + + + + KB + Кб + + + + Size: + Размер: + + + + Demuxer: + Демультиплексор: + + + + Length: + Длительность: + + + + Video + Видео + + + + Resolution: + Разрешение: + + + + Bitrate: + Битовая частота: + + + + Format: + Формат: + + + + FPS: + Частота кадров: + + + + Codec: + Кодек: + + + + Aspect ratio: + Соотношение сторон: + + + + Audio + Аудио + + + + Channels: + Каналов: + + + + Details + Информация + + + + General information + Общая информация + + + diff --git a/src/plugins/Input/mplayer/translations/mplayer_plugin_uk_UA.ts b/src/plugins/Input/mplayer/translations/mplayer_plugin_uk_UA.ts new file mode 100644 index 000000000..c6786e80c --- /dev/null +++ b/src/plugins/Input/mplayer/translations/mplayer_plugin_uk_UA.ts @@ -0,0 +1,134 @@ + + + + DecoderMplayerFactory + + + Mplayer Plugin + + + + + Video Files + + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + Розробник: Ілля Котов <forkotov02@hotmail.ru> + + + + About MPlayer Plugin + + + + + Qmmp MPlayer Plugin + + + + + This plugin uses MPlayer as backend + + + + + DetailsDialog + + + Close + Закрити + + + + - + - + + + + Sample rate: + Дискретизація: + + + + File path: + Шлях до файлу: + + + + KB + Кб + + + + Size: + + + + + Demuxer: + + + + + Length: + + + + + Video + + + + + Resolution: + + + + + Bitrate: + + + + + Format: + + + + + FPS: + + + + + Codec: + + + + + Aspect ratio: + + + + + Audio + + + + + Channels: + + + + + Details + + + + + General information + + + + diff --git a/src/plugins/Input/mplayer/translations/mplayer_plugin_zh_CN.ts b/src/plugins/Input/mplayer/translations/mplayer_plugin_zh_CN.ts new file mode 100644 index 000000000..b8fe3a013 --- /dev/null +++ b/src/plugins/Input/mplayer/translations/mplayer_plugin_zh_CN.ts @@ -0,0 +1,134 @@ + + + + DecoderMplayerFactory + + + Mplayer Plugin + + + + + Video Files + + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + 作者:Ilya Kotov <forkotov02@hotmail.ru> + + + + About MPlayer Plugin + + + + + Qmmp MPlayer Plugin + + + + + This plugin uses MPlayer as backend + + + + + DetailsDialog + + + Close + 关闭 + + + + - + - + + + + Sample rate: + 取样率: + + + + File path: + 文件路径: + + + + KB + KB + + + + Size: + + + + + Demuxer: + + + + + Length: + + + + + Video + + + + + Resolution: + + + + + Bitrate: + + + + + Format: + + + + + FPS: + + + + + Codec: + + + + + Aspect ratio: + + + + + Audio + + + + + Channels: + + + + + Details + + + + + General information + + + + diff --git a/src/plugins/Input/mplayer/translations/mplayer_plugin_zh_TW.ts b/src/plugins/Input/mplayer/translations/mplayer_plugin_zh_TW.ts new file mode 100644 index 000000000..6599b8d0d --- /dev/null +++ b/src/plugins/Input/mplayer/translations/mplayer_plugin_zh_TW.ts @@ -0,0 +1,134 @@ + + + + DecoderMplayerFactory + + + Mplayer Plugin + + + + + Video Files + + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + 作者:Ilya Kotov <forkotov02@hotmail.ru> + + + + About MPlayer Plugin + + + + + Qmmp MPlayer Plugin + + + + + This plugin uses MPlayer as backend + + + + + DetailsDialog + + + Close + 關閉 + + + + - + - + + + + Sample rate: + 取樣率: + + + + File path: + 檔案路徑: + + + + KB + KB + + + + Size: + + + + + Demuxer: + + + + + Length: + + + + + Video + + + + + Resolution: + + + + + Bitrate: + + + + + Format: + + + + + FPS: + + + + + Codec: + + + + + Aspect ratio: + + + + + Audio + + + + + Channels: + + + + + Details + + + + + General information + + + + diff --git a/src/plugins/Input/mplayer/translations/translations.qrc b/src/plugins/Input/mplayer/translations/translations.qrc new file mode 100644 index 000000000..bcf0a6b3e --- /dev/null +++ b/src/plugins/Input/mplayer/translations/translations.qrc @@ -0,0 +1,11 @@ + + + + mplayer_plugin_ru.qm + mplayer_plugin_uk_UA.qm + mplayer_plugin_zh_CN.qm + mplayer_plugin_zh_TW.qm + mplayer_plugin_cs.qm + mplayer_plugin_de.qm + + -- cgit v1.2.3-13-gbd6f