Merge pull request #856 from xiaoyifang/fix/macos-ooi

fix: refactor editDictionary logic
This commit is contained in:
xiaoyifang 2023-06-23 12:23:23 +08:00 committed by GitHub
commit 2ee2271e91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 625 additions and 338 deletions

View file

@ -0,0 +1,239 @@
name: macos-homebrew-nobreakpad
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
push:
branches:
- dev
- master
# - staged
paths-ignore:
- 'docs/**'
- ".github/**"
- "howto/**"
- "*.md"
- ".clang-format"
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-12,macos-13]
qt_ver: [ 6.5.1 ]
qt_arch: [clang_64]
env:
targetName: GoldenDict
version: 23.06.02
version-suffix: alpha
prerelease: true
steps:
# macos 11.0 默认环境变了,要指定
- name: prepare env
if: ${{ matrix.os == 'macos-11' }}
run: |
softwareupdate --all --install --force
sudo xcode-select --print-path
sudo xcode-select --switch /Library/Developer/CommandLineTools
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- name: Set outputs
id: githash
run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: install deps on macos
run: |
brew install pcre2 harfbuzz freetype
brew install cmake ninja python
brew install automake
brew install autoconf
brew install libtool
brew install opencc
brew install speex
brew install wavpack
brew install automake fdk-aac git lame libass libtool libvorbis libvpx opus sdl shtool texi2html theora wget x264 x265 xvid nasm
brew install speex
brew tap homebrew-ffmpeg/ffmpeg
brew install homebrew-ffmpeg/ffmpeg/ffmpeg --with-speex
brew install libiconv
brew install lzo bzip2
brew install libogg
brew install zstd lzip
brew install libvorbis
brew install hunspell
git clone https://github.com/xiaoyifang/eb.git
cd eb && ./configure && make -j 8 && sudo make install && cd ..
brew install xz lzo
brew install pkg-config
brew install create-dmg
brew install xapian
brew install libzim
# brew reinstall icu4c
brew install dylibbundler
find /opt -name libicudata.72.dylib
find /usr/local -name libicudata.72.dylib
- name: version-file
shell: bash
run: |
current_tag=$(git tag --sort=-creatordate | grep "v.*" | sed -n 1p |cut -c 2-)
echo "$current_tag">version.txt
- name: vcpkg install
shell: bash
run: |
vcpkg install breakpad
- name: copy vcpkg packages into winlibs
shell: bash
run: |
ls -al /usr/local/share/vcpkg/packages/breakpad*
cp -R /usr/local/share/vcpkg/packages/breakpad*/* thirdparty/breakpad
ls -al thirdparty/breakpad/lib
- uses: actions/setup-python@v3
with:
python-version: '3.9'
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: qtwebengine qtwebchannel qtpositioning qt5compat qtmultimedia qtimageformats qtspeech
setup-python: 'false'
- name: compile
run: |
qmake CONFIG+=release CONFIG+=no_macos_universal CONFIG+=zim_support CONFIG+=use_xapian
make -j8
- name: package
run: |
macdeployqt ${targetName}.app -qmldir=. -verbose=1
otool -L GoldenDict.app/Contents/MacOS/GoldenDict
ls -al GoldenDict.app/Contents/Frameworks
otool -L GoldenDict.app/Contents/Frameworks/libicu*.dylib
cp -r /usr/local/Cellar/icu4c/7*/lib/libicudata.*.dylib GoldenDict.app/Contents/Frameworks
codesign --force --deep -s - GoldenDict.app
ls -al GoldenDict.app/Contents/Frameworks
mkdir tmp
mv ${targetName}.app ./tmp
# --background "installer_background.png"
create-dmg --volname "${targetName} Installer" --volicon "icons/macicon.icns" --window-pos 200 120 --window-size 800 400 --icon-size 100 --icon "${targetName}.app" 200 190 --hide-extension "${targetName}.app" --app-drop-link 600 185 --skip-jenkins "${targetName}.dmg" tmp/
- name: changelog
id: changelog
run: |
previousTag=$(git tag --sort=-creatordate | grep "v.*" | sed -n 2p)
echo "previousTag : $previousTag"
CHANGELOG="$(git log --oneline --no-decorate $previousTag..HEAD)"
CHANGELOG="${CHANGELOG//'%'/'%25'}"
CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
CHANGELOG="${CHANGELOG//'\"'/'%22'}"
CHANGELOG="${CHANGELOG//"'"/ }"
echo "::set-output name=changelog::$(echo "$CHANGELOG")"
echo "::set-output name=prev_tag::$previousTag"
echo "::set-output name=curr_tag::$(git tag --sort=-creatordate | grep "v.*" | sed -n 1p)"
- name: Set outputs
id: vars
run: |
echo "::set-output name=sha_short::$(git rev-parse --short=8 HEAD)"
echo "::set-output name=release_date::$(date +'%Y%m%d')"
echo "::set-output name=release_time::$(date +'%H%M%S')"
echo "::set-output name=release_time_clock::$(date +'%H:%M:%S')"
echo "::set-output name=release_hm::$(date +'%y%m%d')"
# tag 上传Release
- name: "Build Changelog"
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v3
with:
commitMode: false
fromTag: ${{ steps.changelog.outputs.prev_tag }}
toTag: "${{ steps.changelog.outputs.curr_tag }}"
configurationJson: |
{
"template": "#{{CHANGELOG}}\n\n<details>\n<summary>🔴 Uncategorized</summary>\n\n#{{UNCATEGORIZED}}\n</details>",
"categories": [
{
"title": "## 🚀 Features",
"labels": ["feature","feat","opt"]
},
{
"title": "## 🐛 Fixes",
"labels": ["fix","bug"]
}
,
{
"title": "## 🤖 Github action",
"labels": ["action"]
}
,
{
"title": "## 🧼 Clean Code",
"labels": ["clean"]
}
],
"label_extractor": [
{
"pattern": "([^:]*):.*",
"target": "$1",
"on_property": "title",
"flags": "gu"
}
]
}
- name: uploadRelease
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.targetName }}.dmg
asset_name: ${{ matrix.qt_ver }}-${{ env.targetName }}_${{ matrix.os }}_homebrew_${{steps.vars.outputs.release_date}}.dmg
overwrite: true
release_name: GoldenDict-ng-v${{env.version}}-${{env.version-suffix}}.${{ steps.vars.outputs.release_hm }}.${{ steps.vars.outputs.sha_short }}
prerelease: ${{env.prerelease}}
body: |
release on date: ${{steps.vars.outputs.release_date}} time: ${{steps.vars.outputs.release_time_clock}}
branch: ${{ github.ref_name }}
commit: ${{ steps.vars.outputs.sha_short }}
Qt version: Qt5.15.2, Qt6.X
Windows built with: msvc64, Visual studio 2019
## goldendict.exe can not be used alone
if you have a previous version. replace this maybe ok. if not ,download the whole bundle.
AppImage built with: Ubuntu-20.04 ,latest gcc
macos built with: macos-10.15,macos-11.0,clang_64 x86_64
Qt6.X(homebrew build)
Qt5.15.2(Intel Kind)
auto built by github action. use on your on risk:-)
**recommend version**:Qt6.X (with the latest bug fixes and performance enhancements)
Filename pattern: **[Qt version]-GoldenDict-[OS]-[release-date].[ext]**
[xapian](https://xapian.org/) is enabled by default which offers 10X~20X performance
------------------------------
文件名的模式: **[Qt version]-GoldenDict-[OS]-[release-date].[ext]**
[xapian](https://xapian.org/) 用于全文索引的创建,提供更快的全文索引创建、搜索支持
比如:
6.4.3-GoldenDict.exe_windows-2022_20230502.zip
表示基于qt6.4.3windows-2022, 于20230502日创建的版本。
CHANGES:
${{ steps.changelog.outputs.changelog }}
----------------
${{steps.build_changelog.outputs.changelog}}

View file

@ -1,28 +1,28 @@
name: macos-homebrew-xapian
name: macos-homebrew-breakpad
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
push:
branches:
- dev
- master
# - staged
paths-ignore:
- 'docs/**'
- ".github/**"
- "howto/**"
- "*.md"
- ".clang-format"
#push:
# branches:
# - dev
# - master
# # - staged
# paths-ignore:
# - 'docs/**'
# - ".github/**"
# - "howto/**"
# - "*.md"
# - ".clang-format"
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-11,macos-12]
qt_ver: [ 6.5.1 ]
os: [macos-13]
qt_ver: [ 6.4.3,6.5.1 ]
qt_arch: [clang_64]
env:
targetName: GoldenDict
@ -37,18 +37,7 @@ jobs:
softwareupdate --all --install --force
sudo xcode-select --print-path
sudo xcode-select --switch /Library/Developer/CommandLineTools
- uses: actions/setup-python@v3
with:
python-version: '3.9'
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: qtwebengine qtwebchannel qtpositioning qt5compat qtmultimedia qtimageformats qtspeech
setup-python: 'false'
- uses: actions/checkout@v3
with:
fetch-depth: 0
@ -102,15 +91,38 @@ jobs:
echo "$VAR_VERSION-$VAR_SUFFIX.$release_date.$current_tag">version.txt
cat version.txt
echo "$version"
- name: vcpkg install
shell: bash
run: |
vcpkg install breakpad
- name: copy vcpkg packages into winlibs
shell: bash
run: |
ls -al /usr/local/share/vcpkg/packages/breakpad*
cp -R /usr/local/share/vcpkg/packages/breakpad*/* thirdparty/breakpad
ls -al thirdparty/breakpad/lib
- uses: actions/setup-python@v3
with:
python-version: '3.9'
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: qtwebengine qtwebchannel qtpositioning qt5compat qtmultimedia qtimageformats qtspeech
setup-python: 'false'
- name: compile
run: |
qmake CONFIG+=release CONFIG+=no_macos_universal CONFIG+=zim_support CONFIG+=use_xapian
qmake CONFIG+=release CONFIG+=no_macos_universal CONFIG+=zim_support CONFIG+=use_xapian CONFIG+=use_breakpad
make -j8
- name: package
run: |
macdeployqt ${targetName}.app -qmldir=. -verbose=1
macdeployqt ${targetName}.app -no-strip -qmldir=. -verbose=1
otool -L GoldenDict.app/Contents/MacOS/GoldenDict
ls -al GoldenDict.app/Contents/Frameworks
otool -L GoldenDict.app/Contents/Frameworks/libicu*.dylib
@ -195,7 +207,7 @@ jobs:
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.targetName }}.dmg
asset_name: ${{ matrix.qt_ver }}-${{ env.targetName }}_${{ matrix.os }}_homebrew_${{steps.vars.outputs.release_date}}.dmg
asset_name: ${{ matrix.qt_ver }}-${{ env.targetName }}_${{ matrix.os }}_homebrew_breakpad_${{steps.vars.outputs.release_date}}.dmg
tag: tag-${{env.version-suffix}}.${{ steps.vars.outputs.sha_short }}
overwrite: true

View file

@ -83,8 +83,8 @@ win32{
CONFIG( use_breakpad ) {
DEFINES += USE_BREAKPAD
win32: LIBS += -L$$PWD/thirdparty/breakpad/lib/ -llibbreakpad -llibbreakpad_client
else:unix: LIBS += -L$$PWD/thirdparty/breakpad/lib/ -llibbreakpa
LIBS += -L$$PWD/thirdparty/breakpad/lib/ -llibbreakpad -llibbreakpad_client
INCLUDEPATH += $$PWD/thirdparty/breakpad/include
DEPENDPATH += $$PWD/thirdparty/breakpad/include

View file

@ -147,6 +147,7 @@ Class::Class( string const & id_, vector< string > const & dictionaryFiles_ ):
void Class::deferredInit()
{
//base method.
}
sptr< WordSearchRequest > Class::stemmedMatch( wstring const & /*str*/,
@ -510,18 +511,14 @@ string makeDictionaryId( vector< string > const & dictionaryFiles ) noexcept
// For portable version, we use relative paths
sortedList.reserve( dictionaryFiles.size() );
QDir dictionariesDir( Config::getPortableVersionDictionaryDir() );
for( unsigned x = 0; x < dictionaryFiles.size(); ++x )
{
string const & full( dictionaryFiles[ x ] );
const QDir dictionariesDir( Config::getPortableVersionDictionaryDir() );
for ( const auto & full : dictionaryFiles ) {
QFileInfo fileInfo( QString::fromStdString( full ) );
if ( fileInfo.isAbsolute() )
sortedList.push_back( dictionariesDir.relativeFilePath( fileInfo.filePath() ).toStdString() );
else
{
else {
// Well, it's relative. We don't technically support those, but
// what the heck
sortedList.push_back( full );
@ -535,9 +532,9 @@ string makeDictionaryId( vector< string > const & dictionaryFiles ) noexcept
QCryptographicHash hash( QCryptographicHash::Md5 );
for( std::vector< string >::const_iterator i = sortedList.begin();
i != sortedList.end(); ++i )
hash.addData( i->c_str(), i->size() + 1 );
for ( const auto & i : sortedList ) {
hash.addData( i.c_str(), i.size() + 1 );
}
return hash.result().toHex().data();
}
@ -551,20 +548,17 @@ bool needToRebuildIndex( vector< string > const & dictionaryFiles,
{
unsigned long lastModified = 0;
for( std::vector< string >::const_iterator i = dictionaryFiles.begin();
i != dictionaryFiles.end(); ++i )
{
QString name = QString::fromUtf8( i->c_str() );
for ( const auto & dictionaryFile : dictionaryFiles ) {
QString name = QString::fromUtf8( dictionaryFile.c_str() );
QFileInfo fileInfo( name );
unsigned long ts;
if( fileInfo.isDir() )
if ( fileInfo.isDir() )
continue;
if( name.toLower().endsWith( ".zip" ) )
{
if ( name.toLower().endsWith( ".zip" ) ) {
ZipFile::SplitZipFile zf( name );
if( !zf.exists() )
if ( !zf.exists() )
return true;
ts = zf.lastModified().toSecsSinceEpoch();
}
@ -605,8 +599,9 @@ QMap< std::string, sptr< Dictionary::Class > >
dictToMap( std::vector< sptr< Dictionary::Class > > const & dicts )
{
QMap< std::string, sptr< Dictionary::Class > > dictMap;
for( auto dict : dicts )
{
for ( auto & dict : dicts ) {
if ( !dict )
continue;
dictMap.insert( dict.get()->getId(), dict );
}
return dictMap;

View file

@ -145,7 +145,7 @@ Q_OBJECT
Q_PROPERTY(int type READ getType WRITE setType USER true)
public:
ProgramTypeEditor( QWidget * widget = 0 );
explicit ProgramTypeEditor( QWidget * widget = nullptr );
// Returns localized name for the given program type
static QString getNameForType( int );

View file

@ -130,7 +130,9 @@ public:
}
void clearDictionaries()
{ dictionaries.clear(); }
{
dictionaries.clear();
}
/// Start dictionaries indexing for full-text search
void doIndexing();
@ -140,7 +142,7 @@ public:
QString nowIndexingName();
protected:
private:
QAtomicInt isCancelled;
QSemaphore indexingExited;
std::vector< sptr< Dictionary::Class > > dictionaries;

View file

@ -33,10 +33,17 @@
#include <QMutex>
#if defined(USE_BREAKPAD)
#include "client/windows/handler/exception_handler.h"
#if defined( Q_OS_MAC )
#include "client/mac/handler/exception_handler.h"
#elif defined( Q_OS_LINUX )
#include "client/linux/handler/exception_handler.h"
#elif defined( Q_OS_WIN32 )
#include "client/windows/handler/exception_handler.h"
#endif
#endif
#if defined(USE_BREAKPAD)
#ifdef Q_OS_WIN32
bool callback(const wchar_t* dump_path, const wchar_t* id,
void* context, EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
@ -48,6 +55,31 @@ bool callback(const wchar_t* dump_path, const wchar_t* id,
}
return succeeded;
}
#endif
#ifdef Q_OS_LINUX
bool callback( const google_breakpad::MinidumpDescriptor & descriptor, void * context, bool succeeded )
{
if ( succeeded ) {
qDebug() << "Create dump file success";
}
else {
qDebug() << "Create dump file failed";
}
return succeeded;
}
#endif
#ifdef Q_OS_MAC
bool callback( const char * dump_dir, const char * minidump_id, void * context, bool succeeded )
{
if ( succeeded ) {
qDebug() << "Create dump file success";
}
else {
qDebug() << "Create dump file failed";
}
return succeeded;
}
#endif
#endif
QMutex logMutex;
@ -55,9 +87,7 @@ QMutex logMutex;
void gdMessageHandler( QtMsgType type, const QMessageLogContext &context, const QString &mess )
{
QString strTime = QDateTime::currentDateTime().toString( "MM-dd hh:mm:ss" );
QString message = QString( "%1 %2\r\n" )
.arg( strTime )
.arg( mess );
QString message = QString( "%1 %2\r\n" ).arg( strTime, mess );
if ( ( logFilePtr != nullptr ) && logFilePtr->isOpen() ) {
//without the lock ,on multithread,there would be assert error.
@ -292,18 +322,32 @@ int main( int argc, char ** argv )
QHotkeyApplication::setWindowIcon( QIcon( ":/icons/programicon.png" ) );
#if defined(USE_BREAKPAD)
QString appDirPath = QCoreApplication::applicationDirPath() + "/crash";
QString appDirPath = Config::getConfigDir() + "crash";
QDir dir;
if ( !dir.exists( appDirPath ) ) {
dir.mkpath( appDirPath );
}
#ifdef Q_OS_WIN32
google_breakpad::ExceptionHandler eh(
appDirPath.toStdWString(), NULL, callback, NULL,
google_breakpad::ExceptionHandler::HANDLER_ALL);
#elif defined( Q_OS_MAC )
google_breakpad::ExceptionHandler eh( appDirPath.toStdString(), 0, callback, 0, true, NULL );
#else
google_breakpad::ExceptionHandler eh( google_breakpad::MinidumpDescriptor( appDirPath.toStdString() ),
/*FilterCallback*/ 0,
callback,
/*context*/ 0,
true,
-1 );
#endif
#endif
GDOptions gdcl{};
@ -411,6 +455,7 @@ int main( int argc, char ** argv )
if ( gdcl.notts ) {
cfg.notts = true;
cfg.voiceEngines.clear();
}
cfg.resetState = gdcl.resetState;

View file

@ -49,7 +49,7 @@ SpeechClient::Engines SpeechClient::availableEngines()
bool SpeechClient::tell( QString const & text, int volume, int rate ) const
{
if( internalData->sp->state() != QTextToSpeech::Ready )
if ( !internalData || !internalData->sp || internalData->sp->state() != QTextToSpeech::Ready )
return false;
internalData->sp->setVolume( volume / 100.0 );

View file

@ -5,6 +5,7 @@
#include "config.hh"
#include <QTextToSpeech>
#include <memory>
#include <QDebug>
class SpeechClient: public QObject
{
@ -40,10 +41,18 @@ public:
sp( std::make_unique< QTextToSpeech >( e.engine_name ) ),
engine( e )
{
qDebug() << QStringLiteral( "initialize tts" ) << e.engine_name;
#if ( QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) )
if ( !sp || sp->state() == QTextToSpeech::Error )
return;
#else
if ( !sp || sp->state() == QTextToSpeech::BackendError )
return;
#endif
sp->setLocale( e.locale );
auto voices = sp->availableVoices();
for( const auto & voice : voices ) {
if( voice.name() == e.voice_name ) {
for ( const auto & voice : voices ) {
if ( voice.name() == e.voice_name ) {
sp->setVoice( voice );
break;
@ -67,10 +76,6 @@ public:
bool tell( QString const & text, int volume, int rate ) const;
bool tell( QString const & text ) const;
signals:
void started( bool ok );
void finished();
private:
std::unique_ptr< InternalData > internalData;
};

View file

@ -4,11 +4,13 @@
#include "editdictionaries.hh"
#include "dict/loaddictionaries.hh"
#include "help.hh"
#include <QTabWidget>
#include <QMessageBox>
using std::vector;
EditDictionaries::EditDictionaries( QWidget * parent, Config::Class & cfg_,
EditDictionaries::EditDictionaries( QWidget * parent,
Config::Class & cfg_,
vector< sptr< Dictionary::Class > > & dictionaries_,
Instances::Groups & groupInstances_,
QNetworkAccessManager & dictNetMgr_ ):
@ -19,12 +21,10 @@ EditDictionaries::EditDictionaries( QWidget * parent, Config::Class & cfg_,
dictNetMgr( dictNetMgr_ ),
origCfg( cfg ),
sources( this, cfg ),
orderAndProps( new OrderAndProps( this, cfg.dictionaryOrder, cfg.inactiveDictionaries,
dictionaries ) ),
orderAndProps( new OrderAndProps( this, cfg.dictionaryOrder, cfg.inactiveDictionaries, dictionaries ) ),
groups( new Groups( this, dictionaries, cfg.groups, orderAndProps->getCurrentDictionaryOrder() ) ),
dictionariesChanged( false ),
groupsChanged( false ),
lastCurrentTab( 0 ),
helpAction( this )
{
// Some groups may have contained links to non-existnent dictionaries. We
@ -32,35 +32,32 @@ EditDictionaries::EditDictionaries( QWidget * parent, Config::Class & cfg_,
// the initial group readings so that if no edits were really done, we won't
// be changing groups.
origCfg.groups = groups->getGroups();
origCfg.dictionaryOrder = orderAndProps->getCurrentDictionaryOrder();
origCfg.dictionaryOrder = orderAndProps->getCurrentDictionaryOrder();
origCfg.inactiveDictionaries = orderAndProps->getCurrentInactiveDictionaries();
ui.setupUi( this );
setWindowIcon( QIcon(":/icons/dictionary.svg") );
setWindowIcon( QIcon( ":/icons/dictionary.svg" ) );
ui.tabs->clear();
ui.tabs->addTab( &sources, QIcon(":/icons/sources.png"), tr( "&Sources" ) );
ui.tabs->addTab( orderAndProps, QIcon(":/icons/book.svg"), tr( "&Dictionaries" ) );
ui.tabs->addTab( groups.get(), QIcon(":/icons/bookcase.svg"), tr( "&Groups" ) );
ui.tabs->addTab( &sources, QIcon( ":/icons/sources.png" ), tr( "&Sources" ) );
ui.tabs->addTab( orderAndProps, QIcon( ":/icons/book.svg" ), tr( "&Dictionaries" ) );
ui.tabs->addTab( groups, QIcon( ":/icons/bookcase.svg" ), tr( "&Groups" ) );
connect( ui.buttons, &QDialogButtonBox::clicked, this, &EditDictionaries::buttonBoxClicked );
connect( &sources, &Sources::rescan, this, &EditDictionaries::rescanSources );
connect( groups.get(), &Groups::showDictionaryInfo, this, &EditDictionaries::showDictionaryInfo );
connect( groups, &Groups::showDictionaryInfo, this, &EditDictionaries::showDictionaryInfo );
connect( orderAndProps.data(),
&OrderAndProps::showDictionaryHeadwords,
this,
&EditDictionaries::showDictionaryHeadwords );
connect( orderAndProps, &OrderAndProps::showDictionaryHeadwords, this, &EditDictionaries::showDictionaryHeadwords );
helpAction.setShortcut( QKeySequence( "F1" ) );
helpAction.setShortcutContext( Qt::WidgetWithChildrenShortcut );
connect( &helpAction, &QAction::triggered, [ this ]() {
if ( ui.tabs->currentWidget() == this->groups.get() ) {
if ( ui.tabs->currentWidget() == this->groups ) {
Help::openHelpWebpage( Help::section::manage_groups );
}
else {
@ -71,6 +68,7 @@ EditDictionaries::EditDictionaries( QWidget * parent, Config::Class & cfg_,
addAction( &helpAction );
connect( ui.tabs, &QTabWidget::currentChanged, this, &EditDictionaries::currentChanged );
}
void EditDictionaries::editGroup( unsigned id )
@ -90,19 +88,18 @@ void EditDictionaries::editGroup( unsigned id )
void EditDictionaries::save( bool rebuildGroups )
{
Config::Groups newGroups = groups->getGroups();
Config::Group newOrder = orderAndProps->getCurrentDictionaryOrder();
Config::Group newInactive = orderAndProps->getCurrentInactiveDictionaries();
const Config::Groups newGroups = groups->getGroups();
const Config::Group newOrder = orderAndProps->getCurrentDictionaryOrder();
const Config::Group newInactive = orderAndProps->getCurrentInactiveDictionaries();
if( isSourcesChanged() )
if ( isSourcesChanged() )
acceptChangedSources( rebuildGroups );
if ( origCfg.groups != newGroups || origCfg.dictionaryOrder != newOrder ||
origCfg.inactiveDictionaries != newInactive )
{
groupsChanged = true;
cfg.groups = newGroups;
cfg.dictionaryOrder = newOrder;
if ( origCfg.groups != newGroups || origCfg.dictionaryOrder != newOrder
|| origCfg.inactiveDictionaries != newInactive ) {
groupsChanged = true;
cfg.groups = newGroups;
cfg.dictionaryOrder = newOrder;
cfg.inactiveDictionaries = newInactive;
}
}
@ -113,54 +110,49 @@ void EditDictionaries::accept()
QDialog::accept();
}
void EditDictionaries::on_tabs_currentChanged( int index )
void EditDictionaries::currentChanged( int index )
{
if ( index == -1 || !isVisible() )
return; // Sent upon the construction/destruction
if ( !lastCurrentTab && index )
{
qDebug() << ui.tabs->currentWidget()->objectName();
if ( lastTabName.isEmpty() || lastTabName == "Sources" ) {
// We're switching away from the Sources tab -- if its contents were
// changed, we need to either apply or reject now.
if ( isSourcesChanged() )
{
ui.tabs->setCurrentIndex( 0 );
QMessageBox question( QMessageBox::Question, tr( "Sources changed" ),
if ( isSourcesChanged() ) {
QMessageBox question( QMessageBox::Question,
tr( "Sources changed" ),
tr( "Some sources were changed. Would you like to accept the changes?" ),
QMessageBox::NoButton, this );
QMessageBox::NoButton,
this );
QPushButton * accept = question.addButton( tr( "Accept" ), QMessageBox::AcceptRole );
const QPushButton * accept = question.addButton( tr( "Accept" ), QMessageBox::AcceptRole );
question.addButton( tr( "Cancel" ), QMessageBox::RejectRole );
question.exec();
if ( question.clickedButton() == accept )
{
//When accept the changes ,the second and third tab will be recreated. which means the current Index tab will be changed.
if ( question.clickedButton() == accept ) {
disconnect( ui.tabs, &QTabWidget::currentChanged, this, &EditDictionaries::currentChanged );
acceptChangedSources( true );
lastCurrentTab = index;
ui.tabs->setCurrentIndex( index );
connect( ui.tabs, &QTabWidget::currentChanged, this, &EditDictionaries::currentChanged );
}
else
{
else {
// Prevent tab from switching
lastCurrentTab = 0;
return;
// return;
}
}
}
else
if ( lastCurrentTab == 1 && index != 1 )
{
else if ( lastTabName == "OrderAndProps" ) {
// When switching from the dictionary order, we need to propagate any
// changes to the groups.
groups->updateDictionaryOrder( orderAndProps->getCurrentDictionaryOrder() );
}
lastCurrentTab = index;
lastTabName = ui.tabs->currentWidget()->objectName();
}
void EditDictionaries::rescanSources()
@ -194,64 +186,64 @@ void EditDictionaries::acceptChangedSources( bool rebuildGroups )
{
dictionariesChanged = true;
Config::Groups savedGroups = groups->getGroups();
Config::Group savedOrder = orderAndProps->getCurrentDictionaryOrder();
Config::Groups savedGroups = groups->getGroups();
Config::Group savedOrder = orderAndProps->getCurrentDictionaryOrder();
Config::Group savedInactive = orderAndProps->getCurrentInactiveDictionaries();
cfg.paths = sources.getPaths();
cfg.soundDirs = sources.getSoundDirs();
cfg.hunspell = sources.getHunspell();
cfg.paths = sources.getPaths();
cfg.soundDirs = sources.getSoundDirs();
cfg.hunspell = sources.getHunspell();
cfg.transliteration = sources.getTransliteration();
cfg.lingua = sources.getLingua();
cfg.forvo = sources.getForvo();
cfg.mediawikis = sources.getMediaWikis();
cfg.webSites = sources.getWebSites();
cfg.dictServers = sources.getDictServers();
cfg.programs = sources.getPrograms();
cfg.voiceEngines = sources.getVoiceEngines();
cfg.lingua = sources.getLingua();
cfg.forvo = sources.getForvo();
cfg.mediawikis = sources.getMediaWikis();
cfg.webSites = sources.getWebSites();
cfg.dictServers = sources.getDictServers();
cfg.programs = sources.getPrograms();
cfg.voiceEngines = sources.getVoiceEngines();
groupInstances.clear(); // Those hold pointers to dictionaries, we need to
// free them.
ui.tabs->setUpdatesEnabled( false );
// Those hold pointers to dictionaries, we need to free them.
groupInstances.clear();
groups.reset();
groups.clear();
orderAndProps.clear();
loadDictionaries( this, true, cfg, dictionaries, dictNetMgr );
// If no changes to groups were made, update the original data
bool noGroupEdits = ( origCfg.groups == savedGroups );
const bool noGroupEdits = ( origCfg.groups == savedGroups );
if ( noGroupEdits )
savedGroups = cfg.groups;
Instances::updateNames( savedGroups, dictionaries );
bool noOrderEdits = ( origCfg.dictionaryOrder == savedOrder );
const bool noOrderEdits = ( origCfg.dictionaryOrder == savedOrder );
if ( noOrderEdits )
savedOrder = cfg.dictionaryOrder;
Instances::updateNames( savedOrder, dictionaries );
bool noInactiveEdits = ( origCfg.inactiveDictionaries == savedInactive );
const bool noInactiveEdits = ( origCfg.inactiveDictionaries == savedInactive );
if ( noInactiveEdits )
savedInactive = cfg.inactiveDictionaries;
Instances::updateNames( savedInactive, dictionaries );
if ( rebuildGroups )
{
if ( rebuildGroups ) {
ui.tabs->removeTab( 1 );
ui.tabs->removeTab( 1 );
orderAndProps = new OrderAndProps( this, savedOrder, savedInactive, dictionaries );
groups = std::make_shared<Groups>( this, dictionaries, savedGroups, orderAndProps->getCurrentDictionaryOrder() );
ui.tabs->removeTab( 1 );
ui.tabs->removeTab( 1 );
ui.tabs->insertTab( 1, orderAndProps, QIcon(":/icons/book.svg"), tr( "&Dictionaries" ) );
ui.tabs->insertTab( 2, groups.get(), QIcon(":/icons/bookcase.svg"), tr( "&Groups" ) );
groups = new Groups( this, dictionaries, savedGroups, orderAndProps->getCurrentDictionaryOrder() );
ui.tabs->insertTab( 1, orderAndProps, QIcon( ":/icons/book.svg" ), tr( "&Dictionaries" ) );
ui.tabs->insertTab( 2, groups, QIcon( ":/icons/bookcase.svg" ), tr( "&Groups" ) );
connect( groups, &Groups::showDictionaryInfo, this, &EditDictionaries::showDictionaryInfo );
connect( orderAndProps, &OrderAndProps::showDictionaryHeadwords, this, &EditDictionaries::showDictionaryHeadwords );
if ( noGroupEdits )
origCfg.groups = groups->getGroups();
@ -263,5 +255,8 @@ void EditDictionaries::acceptChangedSources( bool rebuildGroups )
origCfg.inactiveDictionaries = orderAndProps->getCurrentInactiveDictionaries();
}
ui.tabs->setUpdatesEnabled( true );
}
EditDictionaries::~EditDictionaries()
{
disconnect( ui.tabs, &QTabWidget::currentChanged, this, &EditDictionaries::currentChanged );
}

View file

@ -26,7 +26,7 @@ public:
Instances::Groups & groupInstances, // We only clear those on rescan
QNetworkAccessManager & dictNetMgr );
~EditDictionaries() = default;
~EditDictionaries();
/// Instructs the dialog to position itself on editing the given group.
void editGroup( unsigned id );
@ -45,7 +45,7 @@ protected:
private slots:
void on_tabs_currentChanged( int index );
void currentChanged( int index );
void buttonBoxClicked( QAbstractButton * button );
@ -72,19 +72,19 @@ private:
std::vector< sptr< Dictionary::Class > > & dictionaries;
Instances::Groups & groupInstances;
QNetworkAccessManager & dictNetMgr;
// Backed up to decide later if something was changed or not
Config::Class origCfg;
Ui::EditDictionaries ui;
Sources sources;
QPointer<OrderAndProps> orderAndProps;
sptr< Groups > groups;
QPointer< OrderAndProps > orderAndProps;
QPointer< Groups > groups;
bool dictionariesChanged;
bool groupsChanged;
int lastCurrentTab;
QString lastTabName;
QAction helpAction;
};

View file

@ -84,7 +84,7 @@ void Groups::updateDictionaryOrder( Config::Group const & order )
if( ui.dictionaries->getCurrentDictionaries() != newOrder.dictionaries )
{
// Repopulate
ui.dictionaries->populate( Instances::Group( order, dicts, Config::Group() ).dictionaries, dicts );
ui.dictionaries->populate( newOrder.dictionaries, dicts );
}
}

View file

@ -44,7 +44,7 @@ DictGroupWidget::DictGroupWidget( QWidget * parent,
ui.groupIcon->addItem( tr( "None" ), "" );
bool usesIconData = !group.iconData.isEmpty();
const bool usesIconData = !group.iconData.isEmpty();
if ( !usesIconData )
ui.groupIcon->addItem( tr( "From file..." ), "" );
@ -80,35 +80,32 @@ DictGroupWidget::DictGroupWidget( QWidget * parent,
void DictGroupWidget::groupIconActivated( int index )
{
if ( index == 1 )
{
if ( index == 1 ) {
QList< QByteArray > supImageFormats = QImageReader::supportedImageFormats();
QString formatList( " (" );
for( int x = 0; x < supImageFormats.size(); ++x )
formatList += "*." + QString::fromLatin1( supImageFormats[ x ] ) + " ";
for ( const auto & supImageFormat : supImageFormats )
formatList += "*." + QString::fromLatin1( supImageFormat ) + " ";
formatList.chop( 1 );
formatList.append( ")" );
QString chosenFile =
QFileDialog::getOpenFileName( this, tr( "Choose a file to use as group icon" ),
QString(),
tr( "Images" ) + formatList + ";;" +
tr( "All files" ) + " (*.*)" );
const QString chosenFile =
QFileDialog::getOpenFileName( this,
tr( "Choose a file to use as group icon" ),
QString(),
tr( "Images" ) + formatList + ";;" + tr( "All files" ) + " (*.*)" );
if ( !chosenFile.isEmpty() )
{
QIcon icon( chosenFile );
if ( !chosenFile.isEmpty() ) {
const QIcon icon( chosenFile );
if ( icon.isNull() )
QMessageBox::critical( this, tr( "Error" ), tr( "Can't read the specified image file." ) );
else
{
else {
ui.groupIcon->setItemIcon( 1, icon );
QString baseName = QFileInfo( chosenFile ).completeBaseName();
const QString baseName = QFileInfo( chosenFile ).completeBaseName();
ui.groupIcon->setItemText( 1, baseName );
ui.groupIcon->setItemData( 1, baseName );
}
@ -124,7 +121,7 @@ Config::Group DictGroupWidget::makeGroup() const
g.dictionaries = ui.dictionaries->getCurrentDictionaries();
int currentIndex = ui.groupIcon->currentIndex();
const int currentIndex = ui.groupIcon->currentIndex();
if ( currentIndex == 1 ) // File
g.iconData = ui.groupIcon->itemIcon( currentIndex );
@ -140,7 +137,7 @@ Config::Group DictGroupWidget::makeGroup() const
void DictGroupWidget::showDictInfo( QPoint const & pos )
{
QVariant data = ui.dictionaries->getModel()->data( ui.dictionaries->indexAt( pos ), Qt::EditRole );
const QVariant data = ui.dictionaries->getModel()->data( ui.dictionaries->indexAt( pos ), Qt::EditRole );
QString id;
if( data.canConvert< QString >() )
id = data.toString();
@ -197,7 +194,7 @@ std::vector< sptr< Dictionary::Class > > const &
Qt::ItemFlags DictListModel::flags( QModelIndex const & index ) const
{
Qt::ItemFlags defaultFlags = QAbstractListModel::flags( index );
const Qt::ItemFlags defaultFlags = QAbstractListModel::flags( index );
if (index.isValid())
return Qt::ItemIsDragEnabled | defaultFlags;
@ -222,14 +219,12 @@ QVariant DictListModel::data( QModelIndex const & index, int role ) const
switch ( role )
{
case Qt::ToolTipRole:
{
case Qt::ToolTipRole: {
QString tt = "<b>" + QString::fromUtf8( item->getName().c_str() ) + "</b>";
QString lfrom( Language::localizedNameForId( item->getLangFrom() ) );
QString lto( Language::localizedNameForId( item->getLangTo() ) );
if ( !lfrom.isEmpty() )
{
const QString lfrom( Language::localizedNameForId( item->getLangFrom() ) );
const QString lto( Language::localizedNameForId( item->getLangTo() ) );
if ( !lfrom.isEmpty() ) {
if ( lfrom == lto )
tt += "<br>" + lfrom;
else
@ -285,14 +280,13 @@ bool DictListModel::insertRows( int row, int count, const QModelIndex & parent )
void DictListModel::addRow(const QModelIndex & parent, sptr< Dictionary::Class > dict)
{
for (unsigned i = 0; i < dictionaries.size(); i++)
{
if (dictionaries[i]->getId() == dict->getId())
for ( const auto & dictionary : dictionaries ) {
if ( dictionary->getId() == dict->getId() )
return;
}
beginInsertRows( parent, dictionaries.size(), dictionaries.size()+1 );
dictionaries.push_back(dict);
beginInsertRows( parent, dictionaries.size(), dictionaries.size() + 1 );
dictionaries.push_back( dict );
endInsertRows();
emit contentChanged();
}
@ -330,7 +324,7 @@ bool DictListModel::setData( QModelIndex const & index, const QVariant & value,
g.dictionaries.push_back( Config::DictionaryRef( value.toString(), QString() ) );
Instances::Group i( g, *allDicts, Config::Group() );
const Instances::Group i( g, *allDicts, Config::Group() );
if ( i.dictionaries.size() == 1 )
{
@ -356,7 +350,7 @@ void DictListModel::removeSelectedRows( QItemSelectionModel * source )
if ( !source )
return;
QModelIndexList rows = source->selectedRows();
const QModelIndexList rows = source->selectedRows();
if ( !rows.count() )
return;
@ -376,7 +370,7 @@ void DictListModel::addSelectedUniqueFromModel( QItemSelectionModel * source )
if ( !source )
return;
QModelIndexList rows = source->selectedRows();
const QModelIndexList rows = source->selectedRows();
if ( !rows.count() )
return;
@ -389,9 +383,8 @@ void DictListModel::addSelectedUniqueFromModel( QItemSelectionModel * source )
{
baseModel = dynamic_cast< const DictListModel * > ( proxyModel->sourceModel() );
}
else
{
baseModel = dynamic_cast< const DictListModel * > ( source->model() );
else {
baseModel = dynamic_cast< const DictListModel * >( source->model() );
}
if ( !baseModel )
@ -399,13 +392,12 @@ void DictListModel::addSelectedUniqueFromModel( QItemSelectionModel * source )
QVector< std::string > list;
QVector< std::string > dicts;
for ( unsigned i = 0; i < dictionaries.size(); i++ )
dicts.append( dictionaries.at( i )->getId() );
for ( const auto & dictionarie : dictionaries )
dicts.append( dictionarie->getId() );
for ( int i = 0; i < rows.count(); i++ )
{
QModelIndex idx = proxyModel ? proxyModel->mapToSource(rows.at( i )) : rows.at( i );
std::string id = baseModel->dictionaries.at( idx.row() )->getId();
for ( int i = 0; i < rows.count(); i++ ) {
QModelIndex idx = proxyModel ? proxyModel->mapToSource( rows.at( i ) ) : rows.at( i );
std::string id = baseModel->dictionaries.at( idx.row() )->getId();
if ( !dicts.contains( id ) )
list.append( id );
@ -414,13 +406,10 @@ void DictListModel::addSelectedUniqueFromModel( QItemSelectionModel * source )
if ( list.empty() )
return;
for ( int j = 0; j < list.size(); j++ )
{
for ( unsigned i = 0; i < allDicts->size(); i++ )
{
if ( allDicts->at( i )->getId() == list.at( j ) )
{
dictionaries.push_back( allDicts->at( i ) );
for ( const auto & j : list ) {
for ( const auto & allDict : *allDicts ) {
if ( allDict->getId() == j ) {
dictionaries.push_back( allDict );
break;
}
}
@ -472,11 +461,6 @@ DictListWidget::DictListWidget( QWidget * parent ): QListView( parent ),
setDropIndicatorShown( true );
}
DictListWidget::~DictListWidget()
{
setModel( 0 );
}
void DictListWidget::populate(
std::vector< sptr< Dictionary::Class > > const & active,
std::vector< sptr< Dictionary::Class > > const & available )
@ -504,7 +488,7 @@ std::vector< sptr< Dictionary::Class > > const &
void DictListWidget::dropEvent( QDropEvent * event )
{
DictListWidget * sourceList = dynamic_cast< DictListWidget * > ( event->source() );
const auto sourceList = dynamic_cast< DictListWidget * >( event->source() );
QListView::dropEvent( event );
@ -532,12 +516,10 @@ void DictListWidget::rowsAboutToBeRemoved( QModelIndex const & parent, int start
{
// When removing rows, if the current row is among the removed ones, select
// an item just before the first row to be removed, if there's one.
QModelIndex current = currentIndex();
if ( current.isValid() && current.row() &&
current.row() >= start && current.row() <= end )
selectionModel()->setCurrentIndex( model.index( current.row() - 1, 0, parent ),
QItemSelectionModel::NoUpdate );
if ( const QModelIndex current = currentIndex();
current.isValid() && current.row() && current.row() >= start && current.row() <= end )
selectionModel()->setCurrentIndex( model.index( current.row() - 1, 0, parent ), QItemSelectionModel::NoUpdate );
QListView::rowsAboutToBeRemoved( parent, start, end );
}
@ -546,7 +528,10 @@ void DictListWidget::rowsAboutToBeRemoved( QModelIndex const & parent, int start
// DictGroupsWidget
DictGroupsWidget::DictGroupsWidget( QWidget * parent ):
QTabWidget( parent ), nextId( 1 ), allDicts( 0 ), activeDicts( 0 )
QTabWidget( parent ),
nextId( 1 ),
allDicts( nullptr ),
activeDicts( nullptr )
{
setMovable( true );
setContextMenuPolicy( Qt::CustomContextMenu );
@ -585,14 +570,13 @@ void DictGroupsWidget::populate( Config::Groups const & groups,
for( int x = 0; x < groups.size(); ++x )
{
DictGroupWidget *gr = new DictGroupWidget( this, *allDicts, groups[ x ] );
const auto gr = new DictGroupWidget( this, *allDicts, groups[ x ] );
addTab( gr, escapeAmps( groups[ x ].name ) );
connect( gr, &DictGroupWidget::showDictionaryInfo,this, &DictGroupsWidget::showDictionaryInfo );
connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
setCurrentIndex( x );
QString toolTipStr = "\"" + tabText( x ) + "\"\n" + tr( "Dictionaries: " )
+ QString::number( getCurrentModel()->getCurrentDictionaries().size() );
QString toolTipStr =
"\"" + tabText( x ) + "\"\n" + tr( "Dictionaries: " ) + QString::number( getDictionaryCountAt( x ) );
setTabToolTip( x, toolTipStr );
}
@ -619,53 +603,78 @@ Config::Groups DictGroupsWidget::makeGroups() const
DictListModel * DictGroupsWidget::getCurrentModel() const
{
int current = currentIndex();
const int current = currentIndex();
if ( current >= 0 )
{
DictGroupWidget * w = ( DictGroupWidget * ) widget( current );
const auto w = (DictGroupWidget *)widget( current );
return w->getModel();
}
return 0;
return nullptr;
}
DictListModel * DictGroupsWidget::getModelAt( int current ) const
{
if ( current >= 0 && current < count() ) {
const auto w = static_cast< DictGroupWidget * >( widget( current ) );
if ( !w )
return nullptr;
return w->getModel();
}
return nullptr;
}
int DictGroupsWidget::getDictionaryCountAt( int current ) const
{
const auto model = getModelAt( current );
if ( !model )
return 0;
return model->getCurrentDictionaries().size();
}
std::vector< sptr< Dictionary::Class > > DictGroupsWidget::getDictionaryAt( int current ) const
{
const auto model = getModelAt( current );
if ( !model )
return {};
return model->getCurrentDictionaries();
}
QItemSelectionModel * DictGroupsWidget::getCurrentSelectionModel() const
{
int current = currentIndex();
const int current = currentIndex();
if ( current >= 0 )
{
DictGroupWidget * w = ( DictGroupWidget * ) widget( current );
const auto w = (DictGroupWidget *)widget( current );
return w->getSelectionModel();
}
return 0;
return nullptr;
}
void DictGroupsWidget::addNewGroup( QString const & name )
int DictGroupsWidget::addNewGroup( QString const & name )
{
if ( !allDicts )
return;
int idx = currentIndex() + 1;
return 0;
Config::Group newGroup;
newGroup.id = nextId++;
DictGroupWidget *gr = new DictGroupWidget( this, *allDicts, newGroup );
insertTab( idx, gr, escapeAmps( name ) );
const auto gr = new DictGroupWidget( this, *allDicts, newGroup );
const int idx = insertTab( currentIndex() + 1, gr, escapeAmps( name ) );
connect( gr, &DictGroupWidget::showDictionaryInfo, this, &DictGroupsWidget::showDictionaryInfo );
setCurrentIndex( idx );
connect( gr->getModel(), &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
QString toolTipStr = "\"" + tabText( idx ) + "\"\n" + tr( "Dictionaries: " )
+ QString::number( getCurrentModel()->getCurrentDictionaries().size() );
const QString toolTipStr =
"\"" + tabText( idx ) + "\"\n" + tr( "Dictionaries: " ) + QString::number( getDictionaryCountAt( idx ) );
setTabToolTip( idx, toolTipStr );
return idx;
}
int DictGroupsWidget::addUniqueGroup( const QString & name )
@ -673,12 +682,11 @@ int DictGroupsWidget::addUniqueGroup( const QString & name )
for( int n = 0; n < count(); n++ )
if( tabText( n ) == name )
{
setCurrentIndex( n );
// setCurrentIndex( n );
return n;
}
addNewGroup( name );
return currentIndex();
return addNewGroup( name );
}
void DictGroupsWidget::addAutoGroups()
@ -697,17 +705,14 @@ void DictGroupsWidget::addAutoGroups()
// Put active dictionaries into lists
for ( unsigned i = 0; i < activeDicts->size(); i++ )
{
sptr<Dictionary::Class> dict = activeDicts->at( i );
for ( const auto & dict : *activeDicts ) {
int idFrom = dict->getLangFrom();
int idTo = dict->getLangTo();
if( idFrom == 0)
{
// Attempt to find language pair in dictionary name
QPair<quint32,quint32> ids = LangCoder::findIdsForName( QString::fromUtf8( dict->getName().c_str() ) );
const QPair< quint32, quint32 > ids = LangCoder::findIdsForName( QString::fromUtf8( dict->getName().c_str() ) );
idFrom = ids.first;
idTo = ids.second;
}
@ -737,34 +742,30 @@ void DictGroupsWidget::addAutoGroups()
dictMap[ name ].push_back( dict );
}
QStringList groupList = dictMap.keys();
QStringList groupList = dictMap.keys();
QStringList morphoList = morphoMap.keys();
// Insert morphology dictionaries into corresponding lists
for( QStringList::ConstIterator ln = morphoList.begin(); ln != morphoList.end(); ++ln )
{
for( QStringList::ConstIterator gr = groupList.begin(); gr != groupList.end(); ++gr )
if( ln->compare( gr->left( 2 ), Qt::CaseInsensitive ) == 0 )
{
QVector<sptr<Dictionary::Class> > vdg = dictMap[ *gr ];
vdg += morphoMap[ *ln ];
dictMap[ *gr ] = vdg;
for ( const auto & ln : morphoList ) {
for ( const auto & gr : groupList )
if ( ln.compare( gr.left( 2 ), Qt::CaseInsensitive ) == 0 ) {
QVector< sptr< Dictionary::Class > > vdg = dictMap[ gr ];
vdg += morphoMap[ ln ];
dictMap[ gr ] = vdg;
}
}
// Make groups
for( QStringList::ConstIterator gr = groupList.begin(); gr != groupList.end(); ++gr )
{
if( count() )
setCurrentIndex( count() - 1 );
addUniqueGroup( *gr );
for ( const auto & gr : groupList ) {
const auto idx = addUniqueGroup( gr );
// add dictionaries into the current group
QVector< sptr<Dictionary::Class> > vd = dictMap[ *gr ];
DictListModel *model = getCurrentModel();
QVector< sptr< Dictionary::Class > > vd = dictMap[ gr ];
DictListModel * model = getModelAt( idx );
if ( !model )
continue;
for( int i = 0; i < vd.count(); i++ )
model->addRow(QModelIndex(), vd.at( i ) );
}
@ -866,13 +867,12 @@ void DictGroupsWidget::addAutoGroupsByFolders()
void DictGroupsWidget::addGroupBasedOnMap( const QMultiMap<QString, sptr<Dictionary::Class>> & groupToDicts )
{
for ( const auto & group : groupToDicts.uniqueKeys() ) {
if ( count() != 0 ) {
setCurrentIndex( count() - 1 );
const auto idx = addUniqueGroup( group );
DictListModel * model = getModelAt( idx );
if ( !model ) {
continue;
}
addUniqueGroup( group );
DictListModel * model = getCurrentModel();
for ( const auto & dict : groupToDicts.values( group ) ) {
model->addRow( QModelIndex(), dict );
}
@ -902,7 +902,7 @@ void DictGroupsWidget::groupsByMetadata()
auto filePath = Utils::Path::combine( baseDir, "metadata.toml" );
auto dictMetaData = Metadata::load( filePath.toStdString() );
const auto dictMetaData = Metadata::load( filePath.toStdString() );
if ( dictMetaData && dictMetaData->categories ) {
for ( const auto & category : dictMetaData->categories.value() ) {
auto group = QString::fromStdString( category ).trimmed();
@ -922,7 +922,7 @@ void DictGroupsWidget::groupsByMetadata()
QString DictGroupsWidget::getCurrentGroupName() const
{
int current = currentIndex();
const int current = currentIndex();
if ( current >= 0 )
return unescapeAmps( tabText( current ) );
@ -932,7 +932,7 @@ QString DictGroupsWidget::getCurrentGroupName() const
void DictGroupsWidget::renameCurrentGroup( QString const & name )
{
int current = currentIndex();
const int current = currentIndex();
if ( current >= 0 )
setTabText( current, escapeAmps( name ) );
@ -940,13 +940,10 @@ void DictGroupsWidget::renameCurrentGroup( QString const & name )
void DictGroupsWidget::removeCurrentGroup()
{
int current = currentIndex();
const int current = currentIndex();
if ( current >= 0 )
{
QWidget * w = widget( current );
if ( current >= 0 ) {
removeTab( current );
delete w;
}
}
@ -954,7 +951,7 @@ void DictGroupsWidget::removeAllGroups()
{
while ( count() )
{
QWidget * w = widget( 0 );
const QWidget * w = widget( 0 );
removeTab( 0 );
delete w;
}
@ -965,37 +962,39 @@ void DictGroupsWidget::combineGroups( int source, int target )
if( source < 0 || source >= count() || target < 0 || target >= count() )
return;
setCurrentIndex( source );
vector< sptr< Dictionary::Class > > const & dicts = getCurrentModel()->getCurrentDictionaries();
vector< sptr< Dictionary::Class > > const & dicts = getDictionaryAt( source );
setCurrentIndex( target );
DictListModel *model = getCurrentModel();
const auto model = getModelAt( target );
if ( !model )
return;
disconnect( model, &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
for( unsigned i = 0; i < dicts.size(); i++ )
model->addRow( QModelIndex(), dicts[ i ] );
for ( const auto & dict : dicts ) {
model->addRow( QModelIndex(), dict );
}
connect( model, &DictListModel::contentChanged, this, &DictGroupsWidget::tabDataChanged );
QString toolTipStr = "\"" + tabText( target ) + "\"\n" + tr( "Dictionaries: " )
+ QString::number( model->getCurrentDictionaries().size() );
const QString toolTipStr = "\"" + tabText( target ) + "\"\n" + tr( "Dictionaries: " )
+ QString::number( model->getCurrentDictionaries().size() );
setTabToolTip( target, toolTipStr );
}
void DictGroupsWidget::contextMenu( QPoint const & pos )
{
int clickedGroup = tabBar()->tabAt( pos );
const int clickedGroup = tabBar()->tabAt( pos );
if( clickedGroup < 0 )
return;
QString name = tabText( clickedGroup );
const QString name = tabText( clickedGroup );
if( name.length() != 7 || name.mid( 2, 3 ) != " - " )
return;
QMenu menu( this );
QAction *combineSourceAction = new QAction( QString( tr( "Combine groups by source language to \"%1->\"" ) )
.arg( name.left( 2 ) ), &menu );
const auto combineSourceAction =
new QAction( QString( tr( "Combine groups by source language to \"%1->\"" ) ).arg( name.left( 2 ) ), &menu );
combineSourceAction->setEnabled( false );
QString grLeft = name.left( 2 );
@ -1011,8 +1010,8 @@ void DictGroupsWidget::contextMenu( QPoint const & pos )
}
menu.addAction( combineSourceAction );
QAction *combineTargetAction = new QAction( QString( tr( "Combine groups by target language to \"->%1\"" ) )
.arg( name.right( 2 ) ), &menu );
const auto combineTargetAction =
new QAction( QString( tr( "Combine groups by target language to \"->%1\"" ) ).arg( name.right( 2 ) ), &menu );
combineTargetAction->setEnabled( false );
for( int i = 0; i < count(); i++ )
@ -1026,15 +1025,15 @@ void DictGroupsWidget::contextMenu( QPoint const & pos )
}
menu.addAction( combineTargetAction );
QAction *combineTwoSidedAction = NULL;
QAction * combineTwoSidedAction = nullptr;
if( grLeft != grRight )
{
combineTwoSidedAction = new QAction( QString( tr( "Make two-side translate group \"%1-%2-%1\"" ) )
.arg( grLeft ).arg( grRight ), &menu );
combineTwoSidedAction =
new QAction( QString( tr( "Make two-side translate group \"%1-%2-%1\"" ) ).arg( grLeft, grRight ), &menu );
combineTwoSidedAction->setEnabled( false );
QString str = grRight + " - " + grLeft;
const QString str = grRight + " - " + grLeft;
for( int i = 0; i < count(); i++ )
{
if( str == tabText( i ) )
@ -1047,8 +1046,7 @@ void DictGroupsWidget::contextMenu( QPoint const & pos )
menu.addAction( combineTwoSidedAction );
}
QAction *combineFirstAction = new QAction( QString( tr( "Combine groups with \"%1\"" ) )
.arg( grLeft ), &menu );
const auto combineFirstAction = new QAction( QString( tr( "Combine groups with \"%1\"" ) ).arg( grLeft ), &menu );
combineFirstAction->setEnabled( false );
for( int i = 0; i < count(); i++ )
{
@ -1062,7 +1060,7 @@ void DictGroupsWidget::contextMenu( QPoint const & pos )
}
menu.addAction( combineFirstAction );
QAction *combineSecondAction = NULL;
QAction * combineSecondAction = nullptr;
if( grLeft != grRight )
{
@ -1083,58 +1081,45 @@ void DictGroupsWidget::contextMenu( QPoint const & pos )
menu.addAction( combineSecondAction );
}
QAction *result = menu.exec( mapToGlobal( pos ) );
const QAction * result = menu.exec( mapToGlobal( pos ) );
setUpdatesEnabled( false );
int targetGroup;
if( result && result == combineSourceAction )
{
setCurrentIndex( clickedGroup );
if ( result && result == combineSourceAction ) {
targetGroup = addUniqueGroup( grLeft + "->" );
for( int i = 0; i < count(); i++ )
{
for ( int i = 0; i < count(); i++ ) {
QString str = tabText( i );
if( str.length() == 7 && str.mid( 2, 3 ) == " - " && str.startsWith( grLeft ) )
if ( str.length() == 7 && str.mid( 2, 3 ) == " - " && str.startsWith( grLeft ) )
combineGroups( i, targetGroup );
}
setCurrentIndex( targetGroup );
}
else
if( result && result == combineTargetAction )
{
setCurrentIndex( clickedGroup );
else if ( result && result == combineTargetAction ) {
targetGroup = addUniqueGroup( "->" + grRight );
for( int i = 0; i < count(); i++ )
{
for ( int i = 0; i < count(); i++ ) {
QString str = tabText( i );
if( str.length() == 7 && str.mid( 2, 3 ) == " - " && str.endsWith( grRight ) )
if ( str.length() == 7 && str.mid( 2, 3 ) == " - " && str.endsWith( grRight ) )
combineGroups( i, targetGroup );
}
setCurrentIndex( targetGroup );
}
else
if( result && result == combineTwoSidedAction )
{
setCurrentIndex( clickedGroup );
targetGroup = addUniqueGroup( name + " - " + grLeft );
QString str = grRight + " - " + grLeft;
else if ( result && result == combineTwoSidedAction ) {
targetGroup = addUniqueGroup( name + " - " + grLeft );
const QString str = grRight + " - " + grLeft;
for( int i = 0; i < count(); i++ )
if( tabText( i ) == name || tabText( i ) == str )
for ( int i = 0; i < count(); i++ )
if ( tabText( i ) == name || tabText( i ) == str )
combineGroups( i, targetGroup );
setCurrentIndex( targetGroup );
}
else
if( result && ( result == combineFirstAction || result == combineSecondAction ) )
{
else if ( result && ( result == combineFirstAction || result == combineSecondAction ) ) {
QString const & grBase = result == combineFirstAction ? grLeft : grRight;
setCurrentIndex( clickedGroup );
targetGroup = addUniqueGroup( grBase );
for( int i = 0; i < count(); i++ )
@ -1153,8 +1138,8 @@ void DictGroupsWidget::contextMenu( QPoint const & pos )
void DictGroupsWidget::tabDataChanged()
{
QString toolTipStr = "\"" + tabText( currentIndex() ) + "\"\n" + tr( "Dictionaries: " )
+ QString::number( getCurrentModel()->getCurrentDictionaries().size() );
const QString toolTipStr = "\"" + tabText( currentIndex() ) + "\"\n" + tr( "Dictionaries: " )
+ QString::number( getCurrentModel()->getCurrentDictionaries().size() );
setTabToolTip( currentIndex(), toolTipStr );
}

View file

@ -35,7 +35,10 @@ public:
/// Marks that this model is used as an immutable dictionary source
void setAsSource();
bool sourceModel() const { return isSource; }
bool sourceModel() const
{
return isSource;
}
/// Returns the dictionaries the model currently has listed
std::vector< sptr< Dictionary::Class > > const & getCurrentDictionaries() const;
@ -43,16 +46,16 @@ public:
void removeSelectedRows( QItemSelectionModel * source );
void addSelectedUniqueFromModel( QItemSelectionModel * source );
Qt::ItemFlags flags( QModelIndex const &index ) const;
int rowCount( QModelIndex const & parent ) const;
QVariant data( QModelIndex const & index, int role ) const;
bool insertRows( int row, int count, const QModelIndex & parent );
bool removeRows( int row, int count, const QModelIndex & parent );
bool setData( QModelIndex const & index, const QVariant & value, int role );
Qt::ItemFlags flags( QModelIndex const & index ) const override;
int rowCount( QModelIndex const & parent ) const override;
QVariant data( QModelIndex const & index, int role ) const override;
bool insertRows( int row, int count, const QModelIndex & parent ) override;
bool removeRows( int row, int count, const QModelIndex & parent ) override;
bool setData( QModelIndex const & index, const QVariant & value, int role ) override;
void addRow(const QModelIndex & parent, sptr< Dictionary::Class > dict);
void addRow( const QModelIndex & parent, sptr< Dictionary::Class > dict );
Qt::DropActions supportedDropActions() const;
Qt::DropActions supportedDropActions() const override;
void filterDuplicates();
@ -73,7 +76,7 @@ class DictListWidget: public QListView
Q_OBJECT
public:
DictListWidget( QWidget * parent );
~DictListWidget();
~DictListWidget() override = default;
/// Populates the current list with the given dictionaries.
void populate( std::vector< sptr< Dictionary::Class > > const & active,
@ -87,18 +90,20 @@ public:
std::vector< sptr< Dictionary::Class > > const & getCurrentDictionaries() const;
DictListModel * getModel()
{ return & model; }
{
return &model;
}
signals:
void gotFocus();
protected:
virtual void dropEvent( QDropEvent * event );
virtual void focusInEvent(QFocusEvent *);
void dropEvent( QDropEvent * event ) override;
void focusInEvent( QFocusEvent * ) override;
// We need these to to handle drag-and-drop focus issues
virtual void rowsInserted( QModelIndex const & parent, int start, int end );
virtual void rowsAboutToBeRemoved( QModelIndex const & parent, int start, int end );
void rowsInserted( QModelIndex const & parent, int start, int end ) override;
void rowsAboutToBeRemoved( QModelIndex const & parent, int start, int end ) override;
private:
DictListModel model;
@ -156,7 +161,7 @@ public:
std::vector< sptr< Dictionary::Class > > const & activeDicts );
/// Creates new empty group with the given name
void addNewGroup( QString const & );
int addNewGroup( QString const & );
/// Creates new empty group with the given name if no such group
/// and return it index
@ -187,6 +192,10 @@ public:
DictListModel * getCurrentModel() const;
DictListModel * getModelAt( int current ) const;
int getDictionaryCountAt( int current ) const;
std::vector< sptr< Dictionary::Class > > getDictionaryAt( int current ) const;
QItemSelectionModel * getCurrentSelectionModel() const;
private:
@ -213,7 +222,7 @@ class QuickFilterLine: public QLineEdit
public:
QuickFilterLine( QWidget * parent );
~QuickFilterLine();
~QuickFilterLine() override;
/// Sets the source view to filter
void applyTo( QAbstractItemView * source );
@ -223,7 +232,7 @@ public:
QModelIndex mapToSource( QModelIndex const & idx );
protected:
virtual void keyPressEvent( QKeyEvent * event );
void keyPressEvent( QKeyEvent * event ) override;
private:
QSortFilterProxyModel m_proxyModel;