diff --git a/CMakeLists.txt b/CMakeLists.txt index ba08a43..14def74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,92 +1,92 @@ cmake_minimum_required( VERSION 3.5 ) # KDE Application Version, managed by release script set(KDE_APPLICATIONS_VERSION_MAJOR "20") set(KDE_APPLICATIONS_VERSION_MINOR "03") set(KDE_APPLICATIONS_VERSION_MICRO "70") set(KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") #Name the project project(kiten VERSION ${KDE_APPLICATIONS_VERSION}) #ECM setup set(KF5_MIN_VERSION "5.46.0") set(QT_REQUIRED_VERSION "5.11.0") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) find_package(Qt5 ${QT_REQUIRED_VERSION} REQUIRED COMPONENTS Core Widgets) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Archive - Completion - Config - ConfigWidgets + Completion + Config + ConfigWidgets CoreAddons Crash - DocTools - I18n - KHtml + DocTools + I18n + KIO Notifications XmlGui) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) include(CheckIncludeFiles) include(ECMGenerateHeaders) include(ECMAddAppIcon) include(ECMMarkNonGuiExecutable) include(GenerateExportHeader) include(ECMSetupVersion) ## Make it easy for radselect and kiten to find libkiten headers include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib) include_directories(${CMAKE_CURRENT_BINARY_DIR}) ## Find mman.h on Windows if(WIN32) find_path(MMAN_INCLUDE_PATH NAMES sys/mman.h) if(NOT MMAN_INCLUDE_PATH) message(FATAL_ERROR "On Windows, you need to install mman-win32 headers.") endif() message(STATUS "Found mman-win32 include: ${MMAN_INCLUDE_PATH}") find_library(MMAN_LIBRARY NAMES mman) if(NOT MMAN_LIBRARY) message(FATAL_ERROR "On Windows, you need to install mman-win32 library.") endif() message(STATUS "Found mman-win32 library: ${MMAN_LIBRARY}") endif(WIN32) ## Generate config.h check_include_files(stdint.h HAVE_STDINT_H) check_include_files(inttypes.h HAVE_INTTYPES_H) configure_file( config-kiten.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kiten.h ) add_definitions(-DTRANSLATION_DOMAIN=\"kiten\") if (EXISTS "${CMAKE_SOURCE_DIR}/.git") add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000) endif() ## Build each subdir add_subdirectory( app ) add_subdirectory( data/font ) add_subdirectory( data/pics ) add_subdirectory( doc ) add_subdirectory( kanjibrowser ) add_subdirectory( lib ) add_subdirectory( radselect ) ## Build kitengen, a small index generator used by the kiten app set(kitengen_SRCS xjdxgen.c) add_executable(kitengen ${kitengen_SRCS}) ecm_mark_nongui_executable(kitengen) target_link_libraries(kitengen ${QT_QTCORE_LIBRARY}) install( TARGETS kitengen ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES org.kde.kiten.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) ## Install our datafiles install( FILES data/edict data/kanjidic data/radkfile data/vconj data/romkana.cnv DESTINATION ${DATA_INSTALL_DIR}/kiten ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index c5d021e..e6804aa 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,47 +1,48 @@ ############## kiten binary ############# ecm_setup_version(${KDE_APPLICATIONS_VERSION} VARIABLE_PREFIX KITEN VERSION_HEADER kiten_version.h) set(kiten_bin_SRCS configdictionaryselector.cpp configsortingpage.cpp configuredialog.cpp entrylistmodel.cpp entrylistview.cpp kiten.cpp main.cpp resultsview.cpp searchstringinput.cpp dictionaryupdatemanager.cpp wordtype.cpp ) ki18n_wrap_ui(kiten_bin_SRCS - configdictselect.ui - configfont.ui - configlearn.ui - configsearching.ui - configsorting.ui - ) + configdictselect.ui + configfont.ui + configlearn.ui + configsearching.ui + configsorting.ui + ) kconfig_add_kcfg_files( kiten_bin_SRCS kitenconfig.kcfgc) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/../data/pics/*-apps-kiten.png") ecm_add_app_icon(kiten_bin_SRCS ICONS ${ICONS_SRCS}) add_executable(kiten_bin ${kiten_bin_SRCS}) set_target_properties(kiten_bin PROPERTIES OUTPUT_NAME kiten IMPORT_PREFIX bin_) -target_link_libraries(kiten_bin +target_link_libraries(kiten_bin KF5::Archive KF5::ConfigWidgets KF5::Crash KF5::I18n - KF5::KHtml + KF5::KIOCore KF5::Notifications + KF5::XmlGui kiten) install( TARGETS kiten_bin ${INSTALL_TARGETS_DEFAULT_ARGS} ) ############ install files ############# install( PROGRAMS org.kde.kiten.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install( FILES kiten.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install( FILES kitenui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/kiten ) diff --git a/app/kiten.cpp b/app/kiten.cpp index c31665c..a85726f 100644 --- a/app/kiten.cpp +++ b/app/kiten.cpp @@ -1,818 +1,817 @@ /***************************************************************************** * This file is part of Kiten, a KDE Japanese Reference Tool... * * Copyright (C) 2001 Jason Katz-Brown * * Copyright (C) 2005 Paul Temple * * Copyright (C) 2006 Joseph Kerian * * Copyright (C) 2006 Eric Kjeldergaard * * Copyright (C) 2011 Daniel E. Moctezuma * * * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * * USA * *****************************************************************************/ #include "kiten.h" #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configuredialog.h" #include "dictionaryupdatemanager.h" #include "dictquery.h" #include "entrylist.h" #include "entrylistmodel.h" #include "entrylistview.h" #include "historyptrlist.h" #include "kitenconfig.h" #include "resultsview.h" #include "searchstringinput.h" /* Separating Learn */ //#include "learn.h" Kiten::Kiten( QWidget *parent, const char *name ) : KXmlGuiWindow( parent ) , _lastQuery( DictQuery() ) , _radselect_proc( new KProcess( this ) ) , _kanjibrowser_proc( new KProcess( this ) ) { _radselect_proc->setProgram( QStandardPaths::findExecutable( QStringLiteral("kitenradselect") ) ); _kanjibrowser_proc->setProgram( QStandardPaths::findExecutable( QStringLiteral("kitenkanjibrowser") ) ); setStandardToolBarMenuEnabled( true ); setObjectName( QLatin1String( name ) ); /* Set up the config */ _config = KitenConfigSkeleton::self(); _config->load(); /* Set up hot keys */ //KDE4 TODO /* Accel = new KGlobalAccel( this ); (void) Accel->insert( "Lookup English/Japanese word" , i18n("Lookup English/Japanese Word") , i18n( "Looks up current text on clipboard in the same way as if you used Kiten's regular search." ) , Qt::CTRL + Qt::ALT + Qt::Key_S , Qt::CTRL + Qt::ALT + Qt::Key_S , this , SLOT(searchClipboardContents()) ); Accel->readSettings( KSharedConfig::openConfig() ); Accel->updateConnections(); */ /* ResultsView is our main widget, displaying the results of a search */ _mainView = new ResultsView( this, "mainView" ); /* Create the export list */ // setupExportListDock(); /* TODO: have a look at this idea detachedView = new ResultsView( NULL, "detachedView" ); */ - setCentralWidget( _mainView->widget() ); + setCentralWidget( _mainView ); setupActions(); // Be sure to create this manager before creating the GUI // as it needs to add a KAction to it. _dictionaryUpdateManager = new DictionaryUpdateManager( this ); createGUI(); _statusBar = statusBar(); _optionDialog = nullptr; /* Start the system tray icon. */ _sysTrayIcon = new KStatusNotifierItem(this); _sysTrayIcon->setStandardActionsEnabled(true); _sysTrayIcon->setAssociatedWidget(this); _sysTrayIcon->setIconByName(QStringLiteral("kiten")); /* Set things as they were (as told in the config) */ _autoSearchToggle->setChecked( _config->autosearch() ); _inputManager->setDefaultsFromConfig(); updateConfiguration(); applyMainWindowSettings( KSharedConfig::openConfig()->group( "kitenWindow" ) ); /* What happens when links are clicked or things are selected in the clipboard */ connect(_mainView, &ResultsView::urlClicked, this, &Kiten::searchText); connect( QApplication::clipboard(), &QClipboard::selectionChanged, this, &Kiten::searchClipboard ); connect(_inputManager, &SearchStringInput::search, this, &Kiten::searchFromEdit); - connect( _mainView->view()->verticalScrollBar(), &QAbstractSlider::valueChanged, + connect( _mainView->verticalScrollBar(), &QAbstractSlider::valueChanged, this, &Kiten::setCurrentScrollValue ); /* We need to know when to reload our dictionaries if the user updated them. */ connect(_dictionaryUpdateManager, &DictionaryUpdateManager::updateFinished, this, &Kiten::loadDictionaries); /* See below for what else needs to be done */ QTimer::singleShot( 10, this, &Kiten::finishInit ); } // Destructor to clean up the little bits Kiten::~Kiten() { if( _radselect_proc->state() != QProcess::NotRunning ) { _radselect_proc->kill(); } if( _kanjibrowser_proc->state() != QProcess::NotRunning ) { _kanjibrowser_proc->kill(); } _dictionaryManager.removeAllDictionaries(); delete _optionDialog; _optionDialog = nullptr; } KitenConfigSkeleton* Kiten::getConfig() { return _config; } void Kiten::setupActions() { /* Add the basic quit/print/prefs actions, use the gui factory for keybindings */ (void) KStandardAction::quit( this, SLOT(close()), actionCollection() ); //Why the heck is KSA:print adding it's own toolbar!? // (void) KStandardAction::print(this, SLOT(print()), actionCollection()); (void) KStandardAction::preferences( this, SLOT(slotConfigure()), actionCollection() ); //old style cast seems needed here, (const QObject*) KStandardAction::keyBindings( (const QObject*)guiFactory() , SLOT(configureShortcuts()) , actionCollection() ); /* Setup the Go-to-learn-mode actions */ /* TODO: put back when Dictionary Editor is reorganised */ // (void) new KAction( i18n( "&Dictionary Editor..." ) // , "document-properties" // , 0 // , this // , SLOT(createEEdit()) // , actionCollection() // , "dict_editor"); QAction *radselect = actionCollection()->addAction( QStringLiteral("radselect") ); radselect->setText( i18n( "Radical Selector" ) ); // radselect->setIcon( "edit-find" ); actionCollection()->setDefaultShortcut(radselect, Qt::CTRL+Qt::Key_R ); connect(radselect, &QAction::triggered, this, &Kiten::radicalSearch); QAction *kanjibrowser = actionCollection()->addAction( QStringLiteral("kanjibrowser") ); kanjibrowser->setText( i18n( "Kanji Browser" ) ); actionCollection()->setDefaultShortcut(kanjibrowser, Qt::CTRL+Qt::Key_K ); connect(kanjibrowser, &QAction::triggered, this, &Kiten::kanjiBrowserSearch); /* Setup the Search Actions and our custom Edit Box */ _inputManager = new SearchStringInput( this ); QAction *searchButton = actionCollection()->addAction( QStringLiteral("search") ); searchButton->setText( i18n( "S&earch" ) ); // Set the search button to search connect(searchButton, &QAction::triggered, this, &Kiten::searchFromEdit); searchButton->setIcon( KStandardGuiItem::find().icon() ); // That's not it, that's "find as you type"... // connect( Edit, SIGNAL(completion(QString)), // this, SLOT(searchFromEdit()) ); /* Setup our widgets that handle preferences */ // deinfCB = new KToggleAction( i18n( "&Deinflect Verbs in Regular Search" ) // , 0 // , this // , SLOT(kanjiDictChange()) // , actionCollection() // , "deinf_toggle" ); _autoSearchToggle = actionCollection()->add( QStringLiteral("autosearch_toggle") ); _autoSearchToggle->setText( i18n( "&Automatically Search Clipboard Selections" ) ); _irAction = actionCollection()->add( QStringLiteral("search_in_results") ); _irAction->setText( i18n( "Search &in Results" ) ); connect(_irAction, &QAction::triggered, this, &Kiten::searchInResults); QAction *actionFocusResultsView; actionFocusResultsView = actionCollection()->addAction( QStringLiteral("focusresultview") , this , SLOT(focusResultsView()) ); actionCollection()->setDefaultShortcut(actionFocusResultsView, Qt::Key_Escape ); actionFocusResultsView->setText( i18n( "Focus result view" ) ); (void) KStandardAction::configureToolbars( this , SLOT(configureToolBars()) , actionCollection() ); //TODO: this should probably be a standard action /* globalShortcutsAction = actionCollection()->addAction( "options_configure_global_keybinding" ); globalShortcutsAction->setText( i18n( "Configure &Global Shortcuts..." ) ); connect( globalShortcutsAction, SIGNAL(triggered()), this, SLOT(configureGlobalKeys()) ); */ //TODO: implement this //_globalSearchAction = actionCollection()->add( "search_on_the_spot" ); //_globalSearchAction->setText( i18n( "On The Spo&t Search" ) ); //KAction *temp = qobject_cast( _globalSearchAction ); //KShortcut shrt( "Ctrl+Alt+S" ); //globalSearchAction->setGlobalShortcut(shrt); //FIXME: Why does this take ~50 seconds to return!? //connect(globalSearchAction, SIGNAL(triggered()), this, SLOT(searchOnTheSpot())); _backAction = KStandardAction::back( this, SLOT(back()), actionCollection() ); _forwardAction = KStandardAction::forward( this, SLOT(forward()), actionCollection() ); _backAction->setEnabled( false ); _forwardAction->setEnabled( false ); } void Kiten::setupExportListDock() { _exportListDock = new QDockWidget( i18n( "Export List" ), this ); _exportListDockContents = new QWidget( _exportListDock ); _exportListDock->setWidget( _exportListDockContents ); addDockWidget( Qt::RightDockWidgetArea, _exportListDock ); QVBoxLayout *layout = new QVBoxLayout( _exportListDockContents ); _exportListDockContents->setLayout( layout ); _exportList = new EntryListView( _exportListDockContents ); layout->addWidget( _exportList ); _exportList->setModel( new EntryListModel( EntryList() ) ); } void Kiten::addExportListEntry( int index ) { EntryListModel *model = qobject_cast( _exportList->model() ); if ( ! model ) return; EntryList list = model->entryList(); list << _historyList.current()->at( index )->clone(); model->setEntryList( list ); } // This is the latter part of the initialization void Kiten::finishInit() { _statusBar->showMessage( i18n( "Initializing Dictionaries" ) ); // if it's the application's first time starting, // the app group won't exist and we show demo if ( _config->initialSearch() ) { if ( ! KConfigGui::sessionConfig()->hasGroup( "app" ) ) { searchTextAndRaise( QStringLiteral( "辞書" ) ); //Note to future tinkerers... DO NOT EDIT OR TRANSLATE THAT //it's an embedded unicode search string to find "dictionary" in japanese } } // Edit->Completion()->clear(); // make sure the edit is focused initially _statusBar->showMessage( i18n( "Welcome to Kiten" ) ); setCaption( QString() ); } void Kiten::focusResultsView() { - _mainView->view()->setFocus(); + _mainView->verticalScrollBar()->setFocus(); } // This function is run on program window close. // It saves the settings in the config. bool Kiten::queryClose() { _config->setAutosearch( _autoSearchToggle->isChecked() ); _config->save(); KConfigGroup configGroup = KSharedConfig::openConfig()->group( "kitenWindow" ); saveMainWindowSettings( configGroup ); return true; } ////////////////////////////////////////////////////////////////////////////// // SEARCHING METHODS ////////////////////////////////////////////////////////////////////////////// /** * This function searches for the contents of the Edit field in the mainwindow. * Any gui choices will also be included here. */ void Kiten::searchFromEdit() { qDebug() << "SEARCH FROM EDIT CALLED"; DictQuery query = _inputManager->getSearchQuery(); if( query != _lastQuery ) { _lastQuery = query; searchAndDisplay( query ); } } /** * This function is called when a kanji is clicked in the result view * or any other time in which we want to search for something that didn't * come from the input box. */ void Kiten::searchText( const QString &text ) { searchAndDisplay( DictQuery( text ) ); } /** * This should change the Edit text to be appropriate and then begin a search * of the dictionaries' entries. */ void Kiten::searchTextAndRaise( const QString &str ) { /* Do the search */ searchText( str ); /* get the window as we'd like it */ raise(); } /** * This function will search things as they are put in the clipboard. * It checks each time to see if the autoSearchToggle is set. * This function searches for the contents of the clipboard * filter out long selections and selections that are contained in our * current search (alleviates problem where research occurs because of * X's auto-add-to-clipboard-on-select feature. */ void Kiten::searchClipboard() { if ( _autoSearchToggle->isChecked() ) { QString clipboard = QApplication::clipboard()->text( QClipboard::Selection ).simplified(); //Only search if the clipboard selection was less than 20 characters wide if ( clipboard.length() < 20 ) { DictQuery potentialQuery( clipboard ); //Make sure that we're not looking for a substring of the current string (needed?) if( ! ( potentialQuery < _inputManager->getSearchQuery() ) ) { searchTextAndRaise(clipboard); } qDebug() << "Clipboard item is a substring (rejected)"; } else { qDebug() << "Clipboard entry too long"; } } } /** * This method performs the search and displays * the result to the screen. */ void Kiten::searchAndDisplay( const DictQuery &query ) { /* keep the user informed of what we are doing */ _statusBar->showMessage( i18n( "Searching..." ) ); /* This gorgeous incantation is all that's necessary to fill a DictQuery with a query and an Entrylist with all of the results form all of the requested dictionaries */ EntryList *results = _dictionaryManager.doSearch( query ); /* if there are no results */ if ( results->size() == 0 ) //TODO: check here if the user actually prefers this { //create a modifiable copy of the original query DictQuery newQuery( query ); bool tryAgain = false; do { //by default we don't try again tryAgain = false; //but if the matchtype is changed we try again if ( newQuery.getMatchType() == DictQuery::Exact ) { newQuery.setMatchType( DictQuery::Beginning ); tryAgain = true; } else if ( newQuery.getMatchType() == DictQuery::Beginning ) { newQuery.setMatchType( DictQuery::Anywhere ); tryAgain = true; } //try another search if ( tryAgain ) { delete results; results = _dictionaryManager.doSearch( newQuery ); //results means all is ok; don't try again if ( results->size() > 0 ) { tryAgain = false; } } } while ( tryAgain ); } /* synchronize the history (and store this pointer there) */ addHistory( results ); /* Add the current search to our drop down list */ _inputManager->setSearchQuery( results->getQuery() ); /* suppose it's about time to show the users the results. */ displayResults( results ); } /** * Search in the previous results, identical to * searchAndDisplay except for the one call. */ void Kiten::searchInResults() { _statusBar->showMessage( i18n( "Searching..." ) ); DictQuery searchQuery = _inputManager->getSearchQuery(); EntryList *results = _dictionaryManager.doSearchInList( searchQuery ,_historyList.current() ); addHistory( results ); _inputManager->setSearchQuery( searchQuery ); displayResults( results ); } /** * Given a set of Search Results items, this function does all that's needed * to put the interface into an appropriate state for those searchResults. */ void Kiten::displayResults( EntryList *results ) { QString infoStr; /* synchronize the statusbar */ if( results->count() > 0 ) { infoStr = i18np( "Found 1 result", "Found %1 results", results->count() ); } else { infoStr = i18n( "No results found" ); } _statusBar->showMessage( infoStr ); setCaption( infoStr ); /* sort the results */ /* synchronize the results window */ if( results->count() > 0 ) { QStringList dictSort; QStringList fieldSort = _config->field_sortlist(); if( _config->dictionary_enable() == QLatin1String("true") ) { dictSort = _config->dictionary_sortlist(); } results->sort( fieldSort, dictSort ); _mainView->setContents( results->toHTML() ); } else { _mainView->setContents( "" + infoStr + "" ); } _mainView->setLaterScrollValue( results->scrollValue() ); /* //Debuggery: to print the html results to file: QFile file( "/tmp/lala" ); file.open( QIODevice::WriteOnly ); file.write( results->toHTML().toUtf8() ); file.close(); */ } /* void Kiten::searchOnTheSpot() { qDebug() << "On the spot search!\n"; } */ void Kiten::radicalSearch() { // Radselect is a KUniqueApplication, so we don't // need to worry about opening more than one copy _radselect_proc->start(); } void Kiten::kanjiBrowserSearch() { // KanjiBrowser is a KUniqueApplication, so we don't // need to worry about opening more than one copy _kanjibrowser_proc->start(); } ////////////////////////////////////////////////////////////////////////////// // PREFERENCES RELATED METHODS ////////////////////////////////////////////////////////////////////////////// void Kiten::slotConfigure() { //If the settings dialog is open and obscured/hidden/cached, show it if ( ConfigureDialog::showDialog( QStringLiteral("settings") ) ) { return; } //ConfigureDialog didn't find an instance of this dialog, so lets create it : _optionDialog = new ConfigureDialog( this, _config ); connect( _optionDialog, SIGNAL(hidden()), this, SLOT(slotConfigureHide()) ); connect(_optionDialog, &ConfigureDialog::settingsChanged, this, &Kiten::updateConfiguration); _optionDialog->show(); } /** * This function just queues up slotConfigureDestroy() to get around the * SIGSEGV if you try to delete yourself if you are in the stack. */ void Kiten::slotConfigureHide() { QTimer::singleShot(0, this, &Kiten::slotConfigureDestroy); } /** * This function actually tears down the optionDialog */ void Kiten::slotConfigureDestroy() { if ( _optionDialog != nullptr && _optionDialog->isVisible() == 0 ) { delete _optionDialog; _optionDialog = nullptr; } } /* TODO: reimplement something very much like this void Kiten::createEEdit() { eEdit *_eEdit = new eEdit( PERSONALDictionaryLocation( "data" ), this ); _eEdit->show(); } */ void Kiten::configureToolBars() { KConfigGroup configGroup = KSharedConfig::openConfig()->group( "kitenWindow" ); saveMainWindowSettings( configGroup ); KEditToolBar dlg( actionCollection() ); connect(&dlg, &KEditToolBar::newToolBarConfig, this, &Kiten::newToolBarConfig); dlg.exec(); } void Kiten::newToolBarConfig() { createGUI( QStringLiteral("kitenui.rc") ); KConfigGroup configGroup = KSharedConfig::openConfig()->group( "kitenWindow" ); applyMainWindowSettings( configGroup ); } /** Opens the dialog for configuring the global accelerator keys. */ void Kiten::configureGlobalKeys() { //KDE4 TODO: done? KShortcutsDialog::configure( actionCollection() , KShortcutsEditor::LetterShortcutsAllowed , this ); } /** * This function, as the name says, updates the configuration file. * It should be called in EVERY case where the configuration files change. */ void Kiten::updateConfiguration() { loadDictionaries(); //Update the HTML/CSS for our fonts displayHistoryItem(); _inputManager->updateFontFromConfig(); // Reset the content of the last query. This is because in case the user adds // new dictionaries and wants to execute the same last search, the output // will contain results of the new dictionary/dictionaries added. _lastQuery = DictQuery(); /*: TODO: have a look at this as well detachedView->updateFont(); */ } /** * Loads the dictionaries, their settings and updates general * options for the display manager. */ void Kiten::loadDictionaries() { // Avoid duplicates (this makes it easy when we need to reload the dictionaries). _dictionaryManager.removeAllDictionaries(); //Load the dictionaries of each type that we can adjust in prefs foreach( const QString &it, _config->dictionary_list() ) { loadDictConfig( it ); } //Load settings for each dictionary type foreach( const QString &it, _dictionaryManager.listDictFileTypes() ) { _dictionaryManager.loadDictSettings( it, _config ); } //Update general options for the display manager (sorting by dict, etc) _dictionaryManager.loadSettings( *_config->config() ); qDebug() << "Dictionaries loaded!" << endl; } /** * This function loads the dictionaries from the config file for the program * to use via the dictionaryManager object. */ void Kiten::loadDictConfig( const QString &dictType ) { KConfigGroup group = _config->config()->group( "dicts_" + dictType.toLower() ); //A list of QPair's Name->Path QList< QPair > dictionariesToLoad; //If we need to load the global if( group.readEntry( "__useGlobal", true ) ) { QString dictionary = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral( "kiten/" ) + dictType.toLower()); dictionariesToLoad.append( qMakePair( dictType, dictionary ) ); } QStringList dictNames = group.readEntry( "__NAMES", QStringList() ); foreach( const QString &name, dictNames ) { QString dictPath = group.readEntry( name,QString() ); if( ! dictPath.isEmpty() && ! name.isEmpty() ) { dictionariesToLoad.append( qMakePair( name, dictPath ) ); } } QStringList loadedDictionaries = _dictionaryManager.listDictionariesOfType( dictType.toLower() ); typedef QPair __dictName_t; //Can't have commas in a foreach foreach( const __dictName_t &it, dictionariesToLoad ) { //Remove from the loadedDictionaries list all the dicts that we are supposed to load //This will leave only those that need to be unloaded at the end if( loadedDictionaries.removeAll( it.first ) == 0 ) { _dictionaryManager.addDictionary( it.second, it.first, dictType.toLower() ); } } foreach( const QString &it, loadedDictionaries ) { _dictionaryManager.removeDictionary( it ); } /* #define PERSONALDictionaryLocation( __X ) KGlobal::dirs()->saveLocation(__X, \ "kiten/dictionaries/",true).append("personal") if(!isKanji) { //Don't load personal dicts as kanji dicts QString personalDict(PERSONALDictionaryLocation("data")); if (QFile::exists(personalDict) && loadedDicts.find(personalDict) == loadedDicts.end()) { dictionaryManager.addDictionary(personalDict,"Personal",dictType.lower()); } } */ } /** * This function allows one to print out the currently displayed result */ void Kiten::print() { // _ResultsView->toHTML(); } /****************************************************************************** HISTORY HANDLING FUNCTIONS ******************************************************************************/ /** * Given one Search Result, it adds it to the history list the logic in it * exists to maintain the history list of a certain size. Note that this method * does not display the EntryList it is given... so you can add something to the * history and display it separately. */ void Kiten::addHistory( EntryList *result ) { _historyList.addItem( result ); enableHistoryButtons(); } /** * This goes back one item in the history and displays */ void Kiten::back( void ) { _historyList.prev(); displayHistoryItem(); } /** * This goes forward one item in the history and displays */ void Kiten::forward( void ) { _historyList.next(); displayHistoryItem(); } /** * This method just sets the current element in the list and triggers the display */ void Kiten::goInHistory( int index ) { _historyList.setCurrent( index ); displayHistoryItem(); } void Kiten::displayHistoryItem() { if ( _historyList.current() == nullptr ) return; _inputManager->setSearchQuery( _historyList.current()->getQuery() ); enableHistoryButtons(); displayResults( _historyList.current() ); } /** * This function determines whether the forward and back buttons should be enabled. * It is currently done independently of what action has just occurred. */ void Kiten::enableHistoryButtons() { _backAction->setEnabled( _historyList.index() > 0 ); _forwardAction->setEnabled( _historyList.index() + 1 < _historyList.count() ); } void Kiten::setCurrentScrollValue( int value ) { if ( _historyList.current() == nullptr ) return; _historyList.current()->setScrollValue( value ); } diff --git a/app/resultsview.cpp b/app/resultsview.cpp index 8709c2f..90d8dcb 100644 --- a/app/resultsview.cpp +++ b/app/resultsview.cpp @@ -1,229 +1,177 @@ /***************************************************************************** * This file is part of Kiten, a KDE Japanese Reference Tool... * * Copyright (C) 2001 Jason Katz-Brown * * Copyright (C) 2005 Paul Temple * * Copyright (C) 2006 Joseph Kerian * * Copyright (C) 2006 Eric Kjeldergaard * * * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * * USA * *****************************************************************************/ #include "resultsview.h" - #include "kitenconfig.h" - #include -#include - -/* Needed by ResultsView only */ #include ResultsView::ResultsView( QWidget *parent, const char *name ) -: KHTMLPart( parent, parent ) +: QTextBrowser( parent ) , _scrollValue( 0 ) { - ////////setReadOnly(true); - /* TODO: configurably underlined links */ -// setLinkUnderline(false); //KDE4 CHANGE - ////////basicMode = false; - - // don't let ktextbrowser internally handle link clicks - ////////setNotifyClick(true); - connect( view(), &KHTMLView::finishedLayout, - this, &ResultsView::doScroll ); + setOpenLinks( false ); + connect(this, &QTextBrowser::anchorClicked, this, [=](const QUrl &url){ + emit urlClicked( url.toString() ); + }); } /** * As the name implies, it appends @param text to the printText */ void ResultsView::append( const QString &text ) { _printText.append( text ); } /** * This clears the printText */ void ResultsView::clear() { - _printText = QLatin1String(""); -} - -QString ResultsView::deLinkify( const DOM::Node &node ) -{ - //TODO: make this function more flexible (ie, accept non-link-content as - //well.) - QString word; - for ( long unsigned int i = 0; i < node.childNodes().length(); ++i ) - { - if ( node.childNodes().item(i).nodeName() != "a" ) - { - return QString(); - } - if ( ! node.childNodes().item(i).hasChildNodes() ) - { - return QString(); - } - - word += node.childNodes().item(i).childNodes().item( 0 ).nodeValue().string(); - } - return word; + _printText.clear(); } void ResultsView::doScroll() { - view()->verticalScrollBar()->setValue(_scrollValue); + verticalScrollBar()->setValue(_scrollValue); } /** * Flushes the printText to screen */ void ResultsView::flush() { - begin(); - setUserStyleSheet( generateCSS() ); - write( _printText ); - end(); -//KDE4 CHANGE setCursorPosition( 0, 0 ); - ////////ensureCursorVisible(); + const QString content = QString("%2").arg(generateCSS()).arg(_printText); + setHtml( content ); } QString ResultsView::generateCSS() { KColorScheme scheme( QPalette::Active ); QFont font = KitenConfigSkeleton::self()->font(); return QString( ".Word { font-size: %9px }" ".Entry { font-size: %8px; color: %1; font-family: \"%7\"; }" //"div.Entry {display: inline; }" ".DictionaryHeader { color: %2; border-bottom: solid %3 }" ".CommonHeader { color: %2; text-align:center }" ".UncommonHeader { color: %2; text-align:center }" "a{ text-decoration: none; }" "a:link { color: %4; }" "a:visited {color: %5} " "a:hover {color: %6 } " "a:active {color: %6}" - ".Entry:hover { background-color: %10 }" + ".odd { background-color: %10 }" "query { color: %6 }" ) .arg( scheme.foreground().color().name() ) .arg( scheme.foreground( KColorScheme::InactiveText ).color().name() ) .arg( scheme.shade( KColorScheme::MidlightShade ).name() ) .arg( scheme.foreground( KColorScheme::LinkText ).color().name() ) .arg( scheme.foreground( KColorScheme::VisitedText ).color().name() ) .arg( scheme.foreground( KColorScheme::ActiveText ).color().name() ) .arg( font.family() ) .arg( font.pointSize() ) // the text size .arg( font.pointSize() + 10 ) // a larger size for kanji .arg( scheme.background( KColorScheme::AlternateBackground ).color().name() ); } /** * Prints the contents of the resultview to a * printer with @param title as the title. */ void ResultsView::print( const QString &title ) { /* KDE4 CHANGE KPrinter printer; printer.setFullPage(true); if (printer.setup(this, i18n("Print Japanese Reference"))) { QPainter p(&printer); //Q3PaintDeviceMetrics metrics(p.device()); int dpix = metrics.logicalDpiX(); int dpiy = metrics.logicalDpiY(); const int margin = 72; // pt QRect body(dpix, dpiy, metrics.width() - margin * dpix / margin * 2, metrics.height() - margin * dpiy / margin * 2); //Q3SimpleRichText richText(title.isNull()? printText : i18n("

Search for \"%1\"

",title) + printText, font(), context(), styleSheet(), mimeSourceFactory(), body.height(), Qt::black, false); richText.setWidth(&p, body.width()); QRect view(body); int page = 1; QColorGroup goodColorGroup = QColorGroup(colorGroup()); goodColorGroup.setColor(QColorGroup::Link, Qt::black); do { richText.draw(&p, body.left(), body.top(), view, goodColorGroup); view.moveBy(0, body.height()); p.translate(0, -body.height()); QFont myFont(font()); myFont.setPointSize(9); p.setFont(myFont); QString footer(QString("%1 - Kiten").arg(QString::number(page))); p.drawText(view.right() - p.fontMetrics().width(footer), view.bottom() + p.fontMetrics().ascent() + 5, footer); if (view.top() >= richText.height()) break; printer.newPage(); page++; qApp->processEvents(); } while (true); } */ } void ResultsView::setBasicMode( bool yes ) { _basicMode = yes; } /** * Non-buffered write of contents to screen */ void ResultsView::setContents( const QString &text ) { - begin(); - setUserStyleSheet( generateCSS() ); - write( text ); - end(); - - qDebug() << "Set CSS to " << generateCSS(); -//KDE4 CHANGE setCursorPosition( 0,0 ); - ////////ensureCursorVisible(); + const QString content = QString("%2").arg(generateCSS()).arg(text); + setHtml( content ); +// qDebug() << "Set HTML to " << content; } void ResultsView::setLaterScrollValue( int scrollValue ) { this->_scrollValue = scrollValue; } -bool ResultsView::urlSelected( const QString &url, - int button, - int state, - const QString &_target, - const KParts::OpenUrlArguments& args, - const KParts::BrowserArguments& browserArgs ) -{ - //qDebug() << nodeUnderMouse().parentNode().parentNode().parentNode().toHTML(); - emit urlClicked( url ); - return KHTMLPart::urlSelected( url, button, state, _target, args, browserArgs ); -} - diff --git a/app/resultsview.h b/app/resultsview.h index 87eca94..58a1983 100644 --- a/app/resultsview.h +++ b/app/resultsview.h @@ -1,79 +1,72 @@ /***************************************************************************** * This file is part of Kiten, a KDE Japanese Reference Tool... * * Copyright (C) 2001 Jason Katz-Brown * * Copyright (C) 2006 Joseph Kerian * * Copyright (C) 2006 Eric Kjeldergaard * * * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * * USA * *****************************************************************************/ #ifndef RESULTSVIEW_H #define RESULTSVIEW_H #include -#include +#include #include "entry.h" class KActionCollection; class KActionMenu; -class ResultsView : public KHTMLPart +class ResultsView : public QTextBrowser { Q_OBJECT public: explicit ResultsView( QWidget *parent = nullptr, const char *name = nullptr ); void addResult( Entry *result, bool common = false ); void addKanjiResult( Entry*, bool common = false ); void setLaterScrollValue( int scrollValue ); public slots: void append( const QString &text ); void clear(); void flush(); void print( const QString &title ); void setBasicMode( bool yes ); void setContents( const QString &text ); signals: void entrySpecifiedForExport( int index ); void urlClicked( const QString& ); protected: - QString deLinkify( const DOM::Node &); QString generateCSS(); - bool urlSelected( const QString &url - , int button - , int state - , const QString &_target - , const KParts::OpenUrlArguments& args = KParts::OpenUrlArguments() - , const KParts::BrowserArguments& browserArgs = KParts::BrowserArguments() ) override; private slots: void doScroll(); private: QAction *_addToExportListAction = nullptr; bool _basicMode; KActionCollection *_popupActions = nullptr; KActionMenu *_popupMenu = nullptr; QString _printText; int _scrollValue; }; #endif diff --git a/lib/entrylist.cpp b/lib/entrylist.cpp index 8b0abc4..6021172 100644 --- a/lib/entrylist.cpp +++ b/lib/entrylist.cpp @@ -1,328 +1,329 @@ /***************************************************************************** * This file is part of Kiten, a KDE Japanese Reference Tool * * Copyright (C) 2001 Jason Katz-Brown * * Copyright (C) 2006 Joseph Kerian * * Copyright (C) 2006 Eric Kjeldergaard * * Copyright (C) 2011 Daniel E. Moctezuma * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *****************************************************************************/ #include "entrylist.h" #include #include #include "DictEdict/dictfileedict.h" #include "DictEdict/entryedict.h" #include "kitenmacros.h" class EntryList::Private { public: Private() : storedScrollValue( 0 ) , sorted( false ) , sortedByDictionary( false ) {} Private( const Private &old ) : storedScrollValue( old.storedScrollValue ) , sorted( old.sorted ) , sortedByDictionary( old.sortedByDictionary ) , query( old.query ) {} int storedScrollValue; bool sorted; bool sortedByDictionary; DictQuery query; }; /* sorts the EntryList in a C++ish, thread-safe manner. */ class SortFunctor { public: QStringList *dictionary_order; QStringList *sort_order; bool operator()( const Entry *n1, const Entry *n2 ) const { return n1->sort( *n2, *dictionary_order, *sort_order ); } }; EntryList::EntryList() : QList() , d( new Private ) { } EntryList::EntryList( const EntryList &old ) : QList ( old ) , d( new Private( *( old.d ) ) ) { } EntryList::~EntryList() { delete d; // kdDebug() << "A copy of EntryList is being deleted... watch your memory!" << endl; } int EntryList::scrollValue() const { return d->storedScrollValue; } void EntryList::setScrollValue( int val ) { d->storedScrollValue = val; } void EntryList::deleteAll() { while( ! this->isEmpty() ) { delete this->takeFirst(); } d->sorted = false; } /* Returns the EntryList as HTML */ //TODO: Some intelligent decision making regarding when to print what when AutoPrinting is on QString EntryList::toHTML( unsigned int start, unsigned int length ) const { unsigned int max = count(); if( start > max ) { return QString(); } if( start + length > max ) { length = max-start; } QString result; QString temp; QString &lastDictionary = temp; const QString fromDictionary = i18n( "From Dictionary:" ); QString query( getQuery() ); bool firstTimeDeinflection = true; bool firstTimeCommon = true; bool firstTimeUncommon = true; for( unsigned int i = 0; i < max; ++i ) { Entry *entry = at( i ); if( d->sortedByDictionary ) { const QString &newDictionary = entry->getDictName(); if( firstTimeDeinflection && newDictionary == EDICT && DictFileEdict::deinflectionLabel ) { const QString &label = *DictFileEdict::deinflectionLabel; const QString &type = *DictFileEdict::wordType; const QString &message = i18nc( "%1 is a word type (verb or adjective)." " %2 is a verb or adjective tense." " Example: 'Entered verb in past tense'." , "Entered %1 in %2 form" , type , label ); result += QStringLiteral( "
%1
" ).arg( message ); firstTimeDeinflection = false; } if( lastDictionary != newDictionary ) { lastDictionary = newDictionary; result += QStringLiteral( "
%1 %2
" ) .arg( fromDictionary ) .arg( newDictionary ); firstTimeCommon = true; firstTimeUncommon = true; } } if( getQuery().getFilterType() == DictQuery::CommonUncommon ) { if( entry->getDictionaryType() == EDICT ) { EntryEdict *entryEdict = static_cast( entry ); if( entryEdict->isCommon() && firstTimeCommon ) { result += QStringLiteral( "
%1
" ).arg( i18n( "Common" ) ); firstTimeCommon = false; } else if( ! entryEdict->isCommon() && firstTimeUncommon ) { result += QStringLiteral( "
%1
" ).arg( i18n( "Uncommon" ) ); firstTimeUncommon = false; } } } if( length-- > 0 ) { - result += QStringLiteral( "
%3
" ) + result += QStringLiteral( "
%4
" ) + .arg( i % 2 == 0 ? "Entry" : "Entry odd" ) .arg( QString::number( i ) ) .arg( entry->getDictName() ) .arg( entry->toHTML() ); } else { break; } } //result.replace( query, "" + query + "" ); return result; } QString EntryList::toKVTML( unsigned int start, unsigned int length ) const { unsigned int max = count(); if( start > max ) { return QString(); } if( start + length > max ) { length = max - start; } QString result = "\n\n" "\n"; foreach( Entry *it, *this ) { if( length-- > 0 ) { result = result + it->toKVTML() + '\n'; } else { break; } } return result + "\n"; } QString EntryList::toHTML() const { return toHTML( 0, count() ); } /* Returns the EntryList as HTML */ //TODO: Some intelligent decision making... regarding the output format (differ for //different types of searches? QString EntryList::toString( unsigned int start, unsigned int length ) const { unsigned int max = count(); if( start > max ) { return QString(); } if( start + length > max ) { length = max-start; } QString result; foreach( Entry *it, *this ) { if( length-- > 0 ) { result = result + it->toString(); } else { break; } } return result; } QString EntryList::toString() const { return toString( 0, count() ); } void EntryList::sort( QStringList &sortOrder, QStringList &dictionaryOrder ) { //Don't shortcut sorting, unless we start to keep track of the last sorting order, //Otherwise we won't respond when the user changes the sorting order SortFunctor sorter; sorter.dictionary_order = &dictionaryOrder; sorter.sort_order = &sortOrder; std::stable_sort( this->begin(), this->end(), sorter ); d->sorted = true; d->sortedByDictionary = dictionaryOrder.size() > 0; } const EntryList& EntryList::operator+=( const EntryList &other ) { foreach( Entry *it, other ) { this->append( it ); } if( other.size() > 0 ) { d->sorted = false; } return *this; } const EntryList& EntryList::operator=( const EntryList &other ) { QList::operator=( other ); *d = *( other.d ); return *this; } void EntryList::appendList( const EntryList *other ) { foreach( Entry *it, *other ) { append( it ); } if( other->size() > 0 ) { d->sorted = false; } } /** * This method retrieves an earlier saved query in the EntryList, * this should be the query that generated the list. */ DictQuery EntryList::getQuery() const { return d->query; } /** * This allows us to save a query in the EntryList for later retrieval */ void EntryList::setQuery( const DictQuery &newQuery ) { d->query = newQuery; }