diff --git a/.clang-tidy b/.clang-tidy index 2ef34ab55..6b2fd3721 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -27,6 +27,7 @@ Checks: > -google-readability-casting, -hicpp-deprecated-headers, -misc-const-correctness, + -misc-include-cleaner, -misc-non-private-member-variables-in-classes, -modernize-avoid-c-arrays, -modernize-deprecated-headers, diff --git a/.github/workflows/PR-check-cmake.yml b/.github/workflows/PR-check-cmake.yml index f2614626b..b42bc7943 100644 --- a/.github/workflows/PR-check-cmake.yml +++ b/.github/workflows/PR-check-cmake.yml @@ -45,7 +45,7 @@ jobs: cmake --build ./build_dir job_macos_build_check: name: macos Build and analyze - runs-on: macos-12 + runs-on: macos-13 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/release-macos-homebrew.yml b/.github/workflows/release-macos-homebrew.yml index dea137b92..ab6907d2e 100644 --- a/.github/workflows/release-macos-homebrew.yml +++ b/.github/workflows/release-macos-homebrew.yml @@ -22,7 +22,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-12,macos-14] + os: [macos-13,macos-14] qt_ver: [ 6.7.3,6.6.3 ] qt_arch: [clang_64] env: diff --git a/src/config.cc b/src/config.cc index 045d8130a..c523d83a4 100644 --- a/src/config.cc +++ b/src/config.cc @@ -1157,8 +1157,10 @@ Class load() c.showingDictBarNames = ( root.namedItem( "showingDictBarNames" ).toElement().text() == "1" ); - c.usingToolbarsIconSize = - ( static_cast< ToolbarsIconSize >( root.namedItem( "usingToolbarsIconSize" ).toElement().text().toInt() ) ); + QDomNode usingToolbarsIconSize = root.namedItem( "usingToolbarsIconSize" ); + if ( !usingToolbarsIconSize.isNull() ) { + c.usingToolbarsIconSize = static_cast< ToolbarsIconSize >( usingToolbarsIconSize.toElement().text().toInt() ); + } if ( !root.namedItem( "historyExportPath" ).isNull() ) c.historyExportPath = root.namedItem( "historyExportPath" ).toElement().text(); @@ -1169,9 +1171,6 @@ Class load() if ( !root.namedItem( "articleSavePath" ).isNull() ) c.articleSavePath = root.namedItem( "articleSavePath" ).toElement().text(); - if ( !root.namedItem( "editDictionaryCommandLine" ).isNull() ) - c.editDictionaryCommandLine = root.namedItem( "editDictionaryCommandLine" ).toElement().text(); - if ( !root.namedItem( "maxHeadwordSize" ).isNull() ) { unsigned int value = root.namedItem( "maxHeadwordSize" ).toElement().text().toUInt(); if ( value != 0 ) // 0 is invalid value for our purposes @@ -2174,10 +2173,6 @@ void save( Class const & c ) root.appendChild( opt ); } - opt = dd.createElement( "editDictionaryCommandLine" ); - opt.appendChild( dd.createTextNode( c.editDictionaryCommandLine ) ); - root.appendChild( opt ); - opt = dd.createElement( "maxHeadwordSize" ); opt.appendChild( dd.createTextNode( QString::number( c.maxHeadwordSize ) ) ); root.appendChild( opt ); diff --git a/src/config.hh b/src/config.hh index 6c870fb17..2dc1568c4 100644 --- a/src/config.hh +++ b/src/config.hh @@ -885,8 +885,6 @@ struct Class HeadwordsDialog headwordsDialog; - QString editDictionaryCommandLine; // Command line to call external editor for dictionary - Class(): lastMainGroupId( 0 ), lastPopupGroupId( 0 ), diff --git a/src/dict/dictdfiles.cc b/src/dict/dictdfiles.cc index 2199d8a17..b7869dc27 100644 --- a/src/dict/dictdfiles.cc +++ b/src/dict/dictdfiles.cc @@ -426,8 +426,7 @@ QString const & DictdDictionary::getDescription() sptr< Dictionary::DataRequest > req = getArticle( U"00databaseinfo", vector< wstring >(), wstring(), false ); if ( req->dataSize() > 0 ) { - dictionaryDescription = Html::unescape( QString::fromUtf8( req->getFullData().data(), req->getFullData().size() ), - Html::HtmlOption::Keep ); + dictionaryDescription = QString::fromUtf8( req->getFullData().data(), req->getFullData().size() ); } else { dictionaryDescription = "NONE"; diff --git a/src/dict/dictionary.cc b/src/dict/dictionary.cc index 1cd646f87..2e3f6fbd4 100644 --- a/src/dict/dictionary.cc +++ b/src/dict/dictionary.cc @@ -516,7 +516,8 @@ string makeDictionaryId( vector< string > const & dictionaryFiles ) noexcept QCryptographicHash hash( QCryptographicHash::Md5 ); for ( const auto & i : sortedList ) { - hash.addData( i.c_str(), i.size() + 1 ); + // Note: a null byte at the end is a must + hash.addData( { i.c_str(), static_cast< qsizetype >( i.size() + 1 ) } ); } return hash.result().toHex().data(); diff --git a/src/dict/dsl_details.cc b/src/dict/dsl_details.cc index c09eb8e14..3c1976915 100644 --- a/src/dict/dsl_details.cc +++ b/src/dict/dsl_details.cc @@ -807,7 +807,7 @@ bool ArticleDom::atSignFirstInLine() /////////////// DslScanner DslScanner::DslScanner( string const & fileName ): - encoding( Utf8::Windows1252 ), + encoding( Utf8::Utf8 ), readBufferPtr( readBuffer ), readBufferLeft( 0 ), linesRead( 0 ) @@ -819,11 +819,12 @@ DslScanner::DslScanner( string const & fileName ): if ( !f ) throw exCantOpen( fileName ); - // Now try guessing the encoding by reading the first two bytes + // Now try guessing the encoding - unsigned char firstBytes[ 50 ]; + constexpr size_t firstBytesSize = 50; + unsigned char firstBytes[ firstBytesSize ]; - if ( gzread( f, firstBytes, sizeof( firstBytes ) ) != sizeof( firstBytes ) ) { + if ( gzread( f, firstBytes, firstBytesSize ) != firstBytesSize ) { // Apparently the file's too short gzclose( f ); throw exMalformedDslFile( fileName ); @@ -831,37 +832,33 @@ DslScanner::DslScanner( string const & fileName ): bool needExactEncoding = false; - QByteArray ba = QByteArray::fromRawData( (const char *)firstBytes, 50 ); - codec = QTextCodec::codecForUtfText( ba, nullptr ); - if ( !codec ) { - // the encoding has no bom. - // check the first char # (0x23). - auto hashTag = 0x0023; - - auto uci = qFromUnaligned< uint32_t >( firstBytes ); - if ( uci == qToBigEndian( hashTag ) ) { - codec = QTextCodec::codecForMib( 1018 ); // utf-32 be - } - else if ( uci == qToLittleEndian( hashTag ) ) { - codec = QTextCodec::codecForMib( 1019 ); // utf-32 le - } - else { - auto uc = qFromUnaligned< uint16_t >( firstBytes ); - if ( uc == qToBigEndian( uint16_t( hashTag ) ) ) { - codec = QTextCodec::codecForMib( 1013 ); // utf16 be - } - else if ( uc == qToLittleEndian( uint16_t( hashTag ) ) ) { - codec = QTextCodec::codecForMib( 1014 ); // utf16 le - } - else { - //default encoding - codec = QTextCodec::codecForName( "UTF-8" ); - } + // Note that .dsl format always starts with "#NAME" + if ( auto guessedEncoding = QStringConverter::encodingForData( { firstBytes, firstBytesSize }, '#' ); + guessedEncoding.has_value() ) { + switch ( guessedEncoding.value() ) { + case QStringConverter::Utf8: + encoding = Utf8::Utf8; + break; + case QStringConverter::Utf16LE: + encoding = Utf8::Utf16LE; + break; + case QStringConverter::Utf16BE: + encoding = Utf8::Utf16BE; + break; + case QStringConverter::Utf32LE: + encoding = Utf8::Utf16LE; + break; + case QStringConverter::Utf32BE: + encoding = Utf8::Utf32BE; + break; + default: + break; } } - encoding = Utf8::getEncodingForName( codec->name() ); - qDebug() << codec->name(); + codec = QTextCodec::codecForName( getEncodingNameFor( encoding ) ); + + qDebug() << "DSL encoding ->" << codec->name(); if ( gzrewind( f ) ) { gzclose( f ); diff --git a/src/ui/articleview.cc b/src/ui/articleview.cc index 45722dce1..0a99ad6f2 100644 --- a/src/ui/articleview.cc +++ b/src/ui/articleview.cc @@ -1389,7 +1389,7 @@ void ArticleView::playSound() return link;})(); )"; webview->page()->runJavaScript( variable, [ this ]( const QVariant & result ) { - if ( result.type() == QVariant::String ) { + if ( result.typeId() == qMetaTypeId< QString >() ) { QString soundScript = result.toString(); if ( !soundScript.isEmpty() ) openLink( QUrl::fromEncoded( soundScript.toUtf8() ), webview->url() ); diff --git a/src/ui/dictinfo.cc b/src/ui/dictinfo.cc index 73b06cf63..5b0490522 100644 --- a/src/ui/dictinfo.cc +++ b/src/ui/dictinfo.cc @@ -27,9 +27,6 @@ void DictInfo::showInfo( sptr< Dictionary::Class > dict ) ui.dictionaryTranslatesTo->setText( Language::localizedStringForId( dict->getLangTo() ) ); ui.openFolder->setVisible( dict->isLocalDictionary() ); - ui.editDictionary->setVisible( dict->isLocalDictionary() && !dict->getMainFilename().isEmpty() - && !cfg.editDictionaryCommandLine.isEmpty() ); - ui.editDictionary->setToolTip( tr( "Edit the dictionary via command:\n%1" ).arg( cfg.editDictionaryCommandLine ) ); if ( dict->getWordCount() == 0 ) ui.headwordsButton->setVisible( false ); @@ -63,11 +60,6 @@ void DictInfo::savePos( int ) cfg.dictInfoGeometry = saveGeometry(); } -void DictInfo::on_editDictionary_clicked() -{ - done( EDIT_DICTIONARY ); -} - void DictInfo::on_openFolder_clicked() { done( OPEN_FOLDER ); diff --git a/src/ui/dictinfo.hh b/src/ui/dictinfo.hh index 74a15b6af..13a7e205e 100644 --- a/src/ui/dictinfo.hh +++ b/src/ui/dictinfo.hh @@ -16,7 +16,6 @@ public: REJECTED, ACCEPTED, OPEN_FOLDER, - EDIT_DICTIONARY, SHOW_HEADWORDS }; @@ -28,7 +27,6 @@ private: Config::Class & cfg; private slots: void savePos( int ); - void on_editDictionary_clicked(); void on_openFolder_clicked(); void on_OKButton_clicked(); void on_headwordsButton_clicked(); diff --git a/src/ui/dictinfo.ui b/src/ui/dictinfo.ui index 1e73f5dd4..d799a6f9b 100644 --- a/src/ui/dictinfo.ui +++ b/src/ui/dictinfo.ui @@ -23,16 +23,6 @@ - - - - true - - - Edit dictionary - - - diff --git a/src/ui/dictionarybar.cc b/src/ui/dictionarybar.cc index 52bd13d71..1230fada9 100644 --- a/src/ui/dictionarybar.cc +++ b/src/ui/dictionarybar.cc @@ -11,12 +11,10 @@ using std::vector; DictionaryBar::DictionaryBar( QWidget * parent, Config::Events & events, - QString const & _editDictionaryCommand, unsigned short const & maxDictionaryRefsInContextMenu_ ): QToolBar( tr( "&Dictionary Bar" ), parent ), mutedDictionaries( nullptr ), configEvents( events ), - editDictionaryCommand( _editDictionaryCommand ), maxDictionaryRefsInContextMenu( maxDictionaryRefsInContextMenu_ ) { normalIconSize = { this->iconSize().height(), this->iconSize().height() }; @@ -115,7 +113,6 @@ void DictionaryBar::showContextMenu( QContextMenuEvent * event, bool extended ) const QAction * infoAction = nullptr; const QAction * headwordsAction = nullptr; - const QAction * editDictAction = nullptr; const QAction * openDictFolderAction = nullptr; QString dictFilename; @@ -138,13 +135,6 @@ void DictionaryBar::showContextMenu( QContextMenuEvent * event, bool extended ) headwordsAction = menu.addAction( tr( "Dictionary headwords" ) ); openDictFolderAction = menu.addAction( tr( "Open dictionary folder" ) ); - - if ( !editDictionaryCommand.isEmpty() ) { - if ( !pDict->getMainFilename().isEmpty() ) { - dictFilename = pDict->getMainFilename(); - editDictAction = menu.addAction( tr( "Edit dictionary" ) ); - } - } } } } @@ -201,13 +191,6 @@ void DictionaryBar::showContextMenu( QContextMenuEvent * event, bool extended ) return; } - if ( result && result == editDictAction ) { - QString command( editDictionaryCommand ); - command.replace( "%GDDICT%", QString( R"("%1")" ).arg( dictFilename ) ); - if ( !QProcess::startDetached( command, QStringList() ) ) - QApplication::beep(); - } - if ( result && result == maxDictionaryRefsAction ) { showContextMenu( event, true ); } diff --git a/src/ui/dictionarybar.hh b/src/ui/dictionarybar.hh index 8a8ebe4fc..9f38f942b 100644 --- a/src/ui/dictionarybar.hh +++ b/src/ui/dictionarybar.hh @@ -18,10 +18,7 @@ class DictionaryBar: public QToolBar public: /// Constructs an empty dictionary bar - DictionaryBar( QWidget * parent, - Config::Events &, - QString const & _editDictionaryCommand, - unsigned short const & maxDictionaryRefsInContextMenu_ ); + DictionaryBar( QWidget * parent, Config::Events &, unsigned short const & maxDictionaryRefsInContextMenu_ ); /// Sets dictionaries to be displayed in the bar. Their statuses (enabled/ /// disabled) are taken from the configuration data. @@ -68,7 +65,7 @@ private: Config::MutedDictionaries storedMutedSet; bool enterSoloMode = false; - QString editDictionaryCommand; + // how many dictionaries should be shown in the context menu: unsigned short const & maxDictionaryRefsInContextMenu; std::vector< sptr< Dictionary::Class > > allDictionaries; diff --git a/src/ui/mainwindow.cc b/src/ui/mainwindow.cc index 0ccdc5120..a1639c6e7 100644 --- a/src/ui/mainwindow.cc +++ b/src/ui/mainwindow.cc @@ -155,7 +155,7 @@ MainWindow::MainWindow( Config::Class & cfg_ ): addTab( this ), cfg( cfg_ ), history( History::Load(), cfg_.preferences.maxStringsInHistory, cfg_.maxHeadwordSize ), - dictionaryBar( this, configEvents, cfg.editDictionaryCommandLine, cfg.preferences.maxDictionaryRefsInContextMenu ), + dictionaryBar( this, configEvents, cfg.preferences.maxDictionaryRefsInContextMenu ), articleMaker( dictionaries, groupInstances, cfg.preferences ), articleNetMgr( this, dictionaries, @@ -4021,9 +4021,6 @@ void MainWindow::showDictionaryInfo( const QString & id ) if ( result == DictInfo::OPEN_FOLDER ) { openDictionaryFolder( id ); } - else if ( result == DictInfo::EDIT_DICTIONARY ) { - editDictionary( dictionaries[ x ].get() ); - } else if ( result == DictInfo::SHOW_HEADWORDS ) { showDictionaryHeadwords( dictionaries[ x ].get() ); } @@ -4100,21 +4097,6 @@ void MainWindow::stopAudio() } } -void MainWindow::editDictionary( Dictionary::Class * dict ) -{ - QString dictFilename = dict->getMainFilename(); - if ( !cfg.editDictionaryCommandLine.isEmpty() && !dictFilename.isEmpty() ) { - QString command( cfg.editDictionaryCommandLine ); - command.replace( "%GDDICT%", "\"" + dictFilename + "\"" ); - if ( command.contains( "%GDWORD%" ) ) { - QString headword = unescapeTabHeader( ui.tabWidget->tabText( ui.tabWidget->currentIndex() ) ); - command.replace( "%GDWORD%", headword ); - } - if ( !QProcess::startDetached( command, QStringList() ) ) - QApplication::beep(); - } -} - void MainWindow::openDictionaryFolder( const QString & id ) { for ( unsigned x = 0; x < dictionaries.size(); x++ ) { @@ -4161,12 +4143,6 @@ void MainWindow::foundDictsContextMenuRequested( const QPoint & pos ) QAction * openDictFolderAction = menu.addAction( tr( "Open dictionary folder" ) ); - QAction * editAction = nullptr; - - QString dictFilename = pDict->getMainFilename(); - if ( !cfg.editDictionaryCommandLine.isEmpty() && !dictFilename.isEmpty() ) - editAction = menu.addAction( tr( "Edit dictionary" ) ); - QAction * result = menu.exec( ui.dictsList->mapToGlobal( pos ) ); if ( result && result == infoAction ) { @@ -4186,9 +4162,6 @@ void MainWindow::foundDictsContextMenuRequested( const QPoint & pos ) else if ( result && result == openDictFolderAction ) { openDictionaryFolder( id ); } - else if ( result && result == editAction ) { - editDictionary( pDict ); - } } } } diff --git a/src/ui/mainwindow.hh b/src/ui/mainwindow.hh index 4be69adbe..9e6aa2fa8 100644 --- a/src/ui/mainwindow.hh +++ b/src/ui/mainwindow.hh @@ -290,8 +290,6 @@ private slots: void openDictionaryFolder( QString const & id ); - void editDictionary( Dictionary::Class * dict ); - void showFTSIndexingName( QString const & name ); void handleAddToFavoritesButton(); diff --git a/src/ui/scanpopup.cc b/src/ui/scanpopup.cc index f88d872cd..cb5158f35 100644 --- a/src/ui/scanpopup.cc +++ b/src/ui/scanpopup.cc @@ -80,7 +80,7 @@ ScanPopup::ScanPopup( QWidget * parent, stopAudioAction( this ), openSearchAction( this ), wordFinder( this ), - dictionaryBar( this, configEvents, cfg.editDictionaryCommandLine, cfg.preferences.maxDictionaryRefsInContextMenu ), + dictionaryBar( this, configEvents, cfg.preferences.maxDictionaryRefsInContextMenu ), hideTimer( this ) { ui.setupUi( this );