+ History of search queries implemented.

This commit is contained in:
Konstantin Isakov 2009-10-21 19:37:07 +00:00
parent 2c46854ab1
commit 9ee8210c1f
8 changed files with 292 additions and 10 deletions

View file

@ -119,7 +119,9 @@ HEADERS += folding.hh \
orderandprops.hh \
language.hh \
dictionarybar.hh \
broken_xrecord.hh
broken_xrecord.hh \
history.hh \
atomic_rename.hh
FORMS += groups.ui \
dictgroupwidget.ui \
mainwindow.ui \
@ -190,7 +192,9 @@ SOURCES += folding.cc \
orderandprops.cc \
language.cc \
dictionarybar.cc \
broken_xrecord.cc
broken_xrecord.cc \
history.cc \
atomic_rename.cc
win32 {
SOURCES += mouseover_win32/ThTypes.c
HEADERS += mouseover_win32/ThTypes.h
@ -201,7 +205,8 @@ TRANSLATIONS += locale/ru_RU.ts \
locale/zh_CN.ts \
locale/cs_CZ.ts \
locale/de_DE.ts \
locale/el_GR.ts
locale/el_GR.ts \
locale/bg_BG.ts
# This makes qmake generate translations
isEmpty(QMAKE_LRELEASE):QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease

101
src/history.cc Normal file
View file

@ -0,0 +1,101 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.berlios.de>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "history.hh"
#include "config.hh"
#include "atomic_rename.hh"
#include <QFile>
History::History( unsigned size ): maxSize( size )
{
}
History::History( Load, unsigned size ): maxSize( size )
{
QFile file( Config::getHistoryFileName() );
if ( !file.open( QFile::ReadOnly | QIODevice::Text ) )
return; // No file -- no history
for( unsigned count = 0 ; count < maxSize; ++count )
{
QByteArray lineUtf8 = file.readLine( 4096 );
if ( lineUtf8.isEmpty() )
break;
if ( lineUtf8.endsWith( '\n' ) )
lineUtf8.chop( 1 );
QString line = QString::fromUtf8( lineUtf8 );
int firstSpace = line.indexOf( ' ' );
if ( firstSpace < 0 )
// No spaces? Bad line. End this.
break;
bool isNumber;
unsigned groupId = line.left( firstSpace ).toUInt( &isNumber, 10 );
if ( !isNumber )
break; // That's not right
items.push_back( Item( groupId, line.right( line.size() - firstSpace - 1 ) ) );
}
}
void History::addItem( Item const & item )
{
if ( items.contains( item ) )
items.removeAll( item );
// Special case: if this items differs from the previous one only by group,
// remove it too.
if ( items.size() && items.first().word == item.word )
items.pop_front();
items.push_front( item );
while( items.size() > (int)maxSize )
items.pop_back();
emit itemsChanged();
}
bool History::save() const
{
QFile file( Config::getHistoryFileName() + ".tmp" );
if ( !file.open( QFile::WriteOnly | QIODevice::Text ) )
return false;
for( QList< Item >::const_iterator i = items.constBegin();
i != items.constEnd(); ++i )
{
QByteArray line = i->word.toUtf8();
// Those could ruin our format, so we replace them by spaces. They shouldn't
// be there anyway.
line.replace( '\n', ' ' );
line.replace( '\r', ' ' );
line = QByteArray::number( i->groupId ) + " " + line + "\n";
if ( file.write( line ) != line.size() )
return false;
}
file.close();
return renameAtomically( file.fileName(), Config::getHistoryFileName() );
}
void History::clear()
{
items.clear();
emit itemsChanged();
}

78
src/history.hh Normal file
View file

@ -0,0 +1,78 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.berlios.de>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifndef __HISTORY_HH_INCLUDED__
#define __HISTORY_HH_INCLUDED__
#include <QObject>
#include <QList>
#include <QString>
/// Search history
class History: public QObject
{
Q_OBJECT
public:
/// An item in history
struct Item
{
/// Group the search was perfomed in
unsigned groupId;
/// The word that was searched
QString word;
Item(): groupId( 0 )
{}
Item( unsigned groupId_, QString const & word_ ):
groupId( groupId_ ), word( word_ )
{}
bool operator == ( Item const & other ) const
{ return word == other.word && groupId == other.groupId; }
bool operator != ( Item const & other ) const
{ return ! operator == ( other ); }
};
/// Indicates an intention to load -- see the relevant History constructor.
struct Load {};
/// Constructs an empty history which can hold at most "size" items.
History( unsigned size = 20 );
/// Loads history from its file. If load fails, the result would be an empty
/// history. The size parameter is same as in other constructor.
History( Load, unsigned size = 20 );
/// Adds new item. The item is always added at the beginning of the list.
/// If there was such an item already somewhere on the list, it gets removed
/// from there. If otherwise the resulting list gets too large, the oldest
/// item gets removed from the end of the list.
void addItem( Item const & );
/// Attempts saving history. Returns true if succeeded - false otherwise.
/// Since history isn't really that valuable, failures can be ignored.
bool save() const;
/// Clears history.
void clear();
/// Gets the current items. The first one is the newest one on the list.
QList< Item > const & getItems() const
{ return items; }
signals:
/// Signals the changes in items in response to addItem() or clear().
void itemsChanged();
private:
QList< Item > items;
unsigned maxSize;
};
#endif

View file

@ -34,6 +34,7 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
trayIconMenu( this ),
addTab( this ),
cfg( cfg_ ),
history( History::Load() ),
dictionaryBar( this, cfg.mutedDictionaries, configEvents ),
articleMaker( dictionaries, groupInstances, cfg.preferences.displayStyle ),
articleNetMgr( this, dictionaries, articleMaker,
@ -181,6 +182,11 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
connect( dictionaryBar.toggleViewAction(), SIGNAL(toggled(bool)),
this, SLOT(dictionaryBarToggled(bool)) );
// History
connect( &history, SIGNAL( itemsChanged() ),
this, SLOT( historyChanged() ) );
// Show tray icon early so the user would be happy. It won't be functional
// though until the program inits fully.
@ -274,6 +280,9 @@ MainWindow::MainWindow( Config::Class & cfg_ ):
makeDictionaries();
// After we have dictionaries and groups, we can populate history
historyChanged();
addNewTab();
// Show the initial welcome text
@ -569,7 +578,8 @@ void MainWindow::makeScanPopup()
!cfg.preferences.enableClipboardHotkey )
return;
scanPopup = new ScanPopup( 0, cfg, articleNetMgr, dictionaries, groupInstances );
scanPopup = new ScanPopup( 0, cfg, articleNetMgr, dictionaries, groupInstances,
history );
scanPopup->setStyleSheet( styleSheet() );
@ -1111,18 +1121,28 @@ void MainWindow::mutedDictionariesChanged()
applyMutedDictionariesState();
}
void MainWindow::showTranslationFor( QString const & inWord )
void MainWindow::showTranslationFor( QString const & inWord,
int inGroup )
{
ArticleView & view =
dynamic_cast< ArticleView & >( *( ui.tabWidget->currentWidget() ) );
navPronounce->setEnabled( false );
view.showDefinition( inWord, groupInstances.empty() ? 0 :
groupInstances[ groupList.currentIndex() ].id );
unsigned group = inGroup < 0 ?
( groupInstances.empty() ? 0 :
groupInstances[ groupList.currentIndex() ].id ):
inGroup;
view.showDefinition( inWord, group );
updatePronounceAvailability();
// Add to history
history.addItem( History::Item( group, inWord.trimmed() ) );
history.save();
#if 0
QUrl req;
@ -1483,6 +1503,56 @@ void MainWindow::showDictBarNamesTriggered()
cfg.showingDictBarNames = show;
}
void MainWindow::historyChanged()
{
// Rebuild history menu
ui.menuHistory->clear();
QList< History::Item > const & items = history.getItems();
for( int x = 0; x < items.size(); ++x )
{
History::Item const * i = &items[ x ];
Instances::Group const * group = groupInstances.findGroup( i->groupId );
QIcon icon = group ? group->makeIcon() : QIcon();
QAction * action = ui.menuHistory->addAction( icon, i->word );
action->setData( x );
}
ui.menuHistory->addSeparator();
ui.menuHistory->addAction( ui.clearHistory );
ui.clearHistory->setEnabled( items.size() );
}
void MainWindow::on_menuHistory_triggered( QAction * action )
{
if ( action->data().type() != QVariant::Int )
return; // Not the item we added
int index = action->data().toInt();
QList< History::Item > const & items = history.getItems();
if ( index < 0 || index >= items.size() )
return; // Something's not right
History::Item const & item = items[ index ];
showTranslationFor( item.word, item.groupId );
}
void MainWindow::on_clearHistory_activated()
{
history.clear();
}
void MainWindow::setAutostart(bool autostart)
{
#ifdef Q_OS_WIN32

View file

@ -21,6 +21,7 @@
#include "wordfinder.hh"
#include "hotkeywrapper.hh"
#include "dictionarybar.hh"
#include "history.hh"
using std::string;
using std::vector;
@ -56,6 +57,7 @@ private:
QToolButton addTab;
Config::Class & cfg;
Config::Events configEvents;
History history;
DictionaryBar dictionaryBar;
vector< sptr< Dictionary::Class > > dictionaries;
/// Here we store unmuted dictionaries when the dictionary bar is active
@ -200,7 +202,7 @@ private slots:
void mutedDictionariesChanged();
void showTranslationFor( QString const & );
void showTranslationFor( QString const &, int inGroup = -1 );
void trayIconActivated( QSystemTrayIcon::ActivationReason );
@ -216,6 +218,10 @@ private slots:
void showDictBarNamesTriggered();
void historyChanged();
void on_menuHistory_triggered( QAction * );
void on_clearHistory_activated();
void on_actionCloseToTray_activated();
void on_pageSetup_activated();

View file

@ -97,9 +97,17 @@
<string>&amp;View</string>
</property>
</widget>
<widget class="QMenu" name="menuHistory">
<property name="title">
<string>&amp;History</string>
</property>
<addaction name="separator"/>
<addaction name="clearHistory"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuView"/>
<addaction name="menu_Edit"/>
<addaction name="menuHistory"/>
<addaction name="menu_Help"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
@ -354,6 +362,11 @@
<string>Ctrl+F5</string>
</property>
</action>
<action name="clearHistory">
<property name="text">
<string>&amp;Clear</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View file

@ -19,12 +19,14 @@ ScanPopup::ScanPopup( QWidget * parent,
Config::Class & cfg_,
ArticleNetworkAccessManager & articleNetMgr,
std::vector< sptr< Dictionary::Class > > const & allDictionaries_,
Instances::Groups const & groups_ ):
Instances::Groups const & groups_,
History & history_ ):
QDialog( parent ),
cfg( cfg_ ),
isScanningEnabled( false ),
allDictionaries( allDictionaries_ ),
groups( groups_ ),
history( history_ ),
escapeAction( this ),
wordFinder( this ),
mouseEnteredOnce( false ),
@ -294,6 +296,10 @@ void ScanPopup::initiateTranslation()
definition->showDefinition( inputWord, ui.groupList->getCurrentGroup() );
wordFinder.prefixMatch( inputWord, getActiveDicts() );
history.addItem( History::Item( ui.groupList->getCurrentGroup(),
inputWord.trimmed() ) );
history.save();
}
vector< sptr< Dictionary::Class > > const & ScanPopup::getActiveDicts()

View file

@ -12,6 +12,7 @@
#include "ui_scanpopup.h"
#include <QDialog>
#include <QClipboard>
#include "history.hh"
/// This is a popup dialog to show translations when clipboard scanning mode
/// is enabled.
@ -25,7 +26,8 @@ public:
Config::Class & cfg,
ArticleNetworkAccessManager &,
std::vector< sptr< Dictionary::Class > > const & allDictionaries,
Instances::Groups const & );
Instances::Groups const &,
History & );
~ScanPopup();
@ -55,6 +57,7 @@ private:
bool isScanningEnabled;
std::vector< sptr< Dictionary::Class > > const & allDictionaries;
Instances::Groups const & groups;
History & history;
Ui::ScanPopup ui;
ArticleView * definition;
QAction escapeAction;