Scan popup functionality implemented, among with other small improvements.

For now, the modifier key is hardcoded to be Ctrl.
This commit is contained in:
Konstantin Isakov 2009-02-01 00:08:08 +00:00
parent 2e8cb955c8
commit 2be1c2b375
25 changed files with 763 additions and 201 deletions

View file

@ -22,13 +22,9 @@ ArticleMaker::ArticleMaker( vector< sptr< Dictionary::Class > > const & dictiona
{ {
} }
string ArticleMaker::makeDefinitionFor( QString const & inWord, std::string ArticleMaker::makeHtmlHeader( QString const & word,
QString const & group ) const QString const & icon )
{ {
printf( "group = %ls\n", group.toStdWString().c_str() );
wstring word = inWord.trimmed().toStdWString();
string result = string result =
"<html><head>" "<html><head>"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"; "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">";
@ -42,7 +38,24 @@ string ArticleMaker::makeDefinitionFor( QString const & inWord,
result += "</style>\n"; result += "</style>\n";
} }
result += "<title>" + Html::escape( Utf8::encode( word ) ) + "</title>"; result += "<title>" + Html::escape( Utf8::encode( word.toStdWString() ) ) + "</title>";
// This doesn't seem to be much of influence right now, but we'll keep
// it anyway.
if ( icon.size() )
result += "<link rel=\"icon\" type=\"image/png\" href=\"qrcx://localhost/flags/" + Html::escape( icon.toUtf8().data() ) + "\" />\n";
result += "</head><body>";
return result;
}
string ArticleMaker::makeDefinitionFor( QString const & inWord,
QString const & group ) const
{
printf( "group = %ls\n", group.toStdWString().c_str() );
wstring word = inWord.trimmed().toStdWString();
// Find the given group // Find the given group
@ -60,14 +73,9 @@ string ArticleMaker::makeDefinitionFor( QString const & inWord,
std::vector< sptr< Dictionary::Class > > const & activeDicts = std::vector< sptr< Dictionary::Class > > const & activeDicts =
activeGroup ? activeGroup->dictionaries : dictionaries; activeGroup ? activeGroup->dictionaries : dictionaries;
if ( activeGroup && activeGroup->icon.size() ) string result = makeHtmlHeader( inWord.trimmed(),
{ activeGroup && activeGroup->icon.size() ?
// This doesn't seem to be much of influence right now, but we'll keep activeGroup->icon : QString() );
// it anyway.
result += "<link rel=\"icon\" type=\"image/png\" href=\"qrcx://localhost/flags/" + Html::escape( activeGroup->icon.toUtf8().data() ) + "\" />\n";
}
result += "</head><body>";
DictLock _; DictLock _;
@ -101,7 +109,9 @@ string ArticleMaker::makeDefinitionFor( QString const & inWord,
printf( "From %s: %s\n", activeDicts[ x ]->getName().c_str(), body.c_str() ); printf( "From %s: %s\n", activeDicts[ x ]->getName().c_str(), body.c_str() );
result += "<div class=\"gddictname\">From " + Html::escape( activeDicts[ x ]->getName() ) + "</div>" + body; result += string( "<div class=\"gddictname\">" ) +
tr( "From " ).toUtf8().data() +
Html::escape( activeDicts[ x ]->getName() ) + "</div>" + body;
} }
catch( Dictionary::exNoSuchWord & ) catch( Dictionary::exNoSuchWord & )
{ {
@ -114,6 +124,15 @@ string ArticleMaker::makeDefinitionFor( QString const & inWord,
return result; return result;
} }
string ArticleMaker::makeNotFoundTextFor( QString const & word,
QString const & group ) const
{
return makeHtmlHeader( word, QString() ) +
"<div class=\"gdnotfound\"><p>" +
tr( "No translation for <b>%1</b> was found in group <b>%2</b>." ).
arg( QString::fromUtf8( Html::escape( word.toUtf8().data() ).c_str() ) ).
arg( QString::fromUtf8(Html::escape( group.toUtf8().data() ).c_str() ) ).
toUtf8().data()
+"</p></div>"
"</body></html>";
}

View file

@ -4,12 +4,15 @@
#ifndef __ARTICLE_MAKER_HH_INCLUDED__ #ifndef __ARTICLE_MAKER_HH_INCLUDED__
#define __ARTICLE_MAKER_HH_INCLUDED__ #define __ARTICLE_MAKER_HH_INCLUDED__
#include <QObject>
#include "dictionary.hh" #include "dictionary.hh"
#include "instances.hh" #include "instances.hh"
/// This class generates the article's body for the given lookup request /// This class generates the article's body for the given lookup request
class ArticleMaker class ArticleMaker: public QObject
{ {
Q_OBJECT // We make it QObject to use tr() conveniently
std::vector< sptr< Dictionary::Class > > const & dictionaries; std::vector< sptr< Dictionary::Class > > const & dictionaries;
std::vector< Instances::Group > const & groups; std::vector< Instances::Group > const & groups;
@ -25,6 +28,16 @@ public:
/// Looks up the given word within the given group, and creates a full html /// Looks up the given word within the given group, and creates a full html
/// page text containing its definition. /// page text containing its definition.
std::string makeDefinitionFor( QString const & word, QString const & group ) const; std::string makeDefinitionFor( QString const & word, QString const & group ) const;
/// Makes up a text which states that no translation for the given word
/// was found. Sometimes it's better to call this directly when it's already
/// known that there's no translation.
std::string makeNotFoundTextFor( QString const & word, QString const & group ) const;
private:
/// Makes everything up to and including the opening body tag.
static std::string makeHtmlHeader( QString const & word, QString const & icon );
}; };
#endif #endif

View file

@ -47,8 +47,12 @@ bool ArticleNetworkAccessManager::getResource( QUrl const & url,
if ( url.scheme() == "gdlookup" ) if ( url.scheme() == "gdlookup" )
{ {
string result = articleMaker.makeDefinitionFor( url.queryItemValue( "word" ), QString word = url.queryItemValue( "word" );
url.queryItemValue( "group" ) ); QString group = url.queryItemValue( "group" );
string result = ( url.queryItemValue( "notfound" ) != "1" ) ?
articleMaker.makeDefinitionFor( word, group ) :
articleMaker.makeNotFoundTextFor( word, group );
data.resize( result.size() ); data.resize( result.size() );

View file

@ -6,6 +6,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QWebHitTestResult> #include <QWebHitTestResult>
#include <QMenu> #include <QMenu>
#include <QDesktopServices>
ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm,
@ -48,6 +49,19 @@ void ArticleView::showDefinition( QString const & word, QString const & group )
ui.definition->load( req ); ui.definition->load( req );
} }
void ArticleView::showNotFound( QString const & word, QString const & group )
{
QUrl req;
req.setScheme( "gdlookup" );
req.setHost( "localhost" );
req.addQueryItem( "word", word );
req.addQueryItem( "group", group );
req.addQueryItem( "notfound", "1" );
ui.definition->load( req );
}
void ArticleView::handleTitleChanged( QString const & title ) void ArticleView::handleTitleChanged( QString const & title )
{ {
emit titleChanged( this, title ); emit titleChanged( this, title );
@ -162,6 +176,13 @@ void ArticleView::linkClicked( QUrl const & url )
printf( "%s\n", e.what() ); printf( "%s\n", e.what() );
} }
} }
else
if ( url.scheme() == "http" || url.scheme() == "https" ||
url.scheme() == "ftp" || url.scheme() == "mailto" )
{
// Use the system handler for the conventional internet links
QDesktopServices::openUrl( url );
}
} }
void ArticleView::contextMenuRequested( QPoint const & pos ) void ArticleView::contextMenuRequested( QPoint const & pos )

View file

@ -34,6 +34,9 @@ public:
/// Shows the definition of the given word with the given group /// Shows the definition of the given word with the given group
void showDefinition( QString const & word, QString const & group ); void showDefinition( QString const & word, QString const & group );
/// Shows the page stating that the given word could not be found.
void showNotFound( QString const & word, QString const & group );
/// Goes back in history /// Goes back in history
void back() void back()
{ ui.definition->back(); } { ui.definition->back(); }

View file

@ -200,4 +200,9 @@ QString getUserCssFileName() throw( exError )
return getHomeDir().filePath( "style.css" ); return getHomeDir().filePath( "style.css" );
} }
QString getUserQtCssFileName() throw( exError )
{
return getHomeDir().filePath( "qt-style.css" );
}
} }

View file

@ -51,6 +51,9 @@ QString getIndexDir() throw( exError );
/// Returns the user .css file name. /// Returns the user .css file name.
QString getUserCssFileName() throw( exError ); QString getUserCssFileName() throw( exError );
/// Returns the user .css file name for the Qt interface customization.
QString getUserQtCssFileName() throw( exError );
} }
#endif #endif

View file

@ -55,7 +55,10 @@ HEADERS += folding.hh \
articleview.hh \ articleview.hh \
externalviewer.hh \ externalviewer.hh \
dictlock.hh \ dictlock.hh \
wordfinder.hh wordfinder.hh \
groupcombobox.hh \
griparea.hh \
keyboardstate.hh
FORMS += groups.ui dictgroupwidget.ui mainwindow.ui sources.ui initializing.ui\ FORMS += groups.ui dictgroupwidget.ui mainwindow.ui sources.ui initializing.ui\
@ -67,6 +70,7 @@ SOURCES += folding.cc main.cc dictionary.cc md5.c config.cc sources.cc \
chunkedstorage.cc xdxf2html.cc iconv.cc lsa.cc htmlescape.cc \ chunkedstorage.cc xdxf2html.cc iconv.cc lsa.cc htmlescape.cc \
dsl.cc dsl_details.cc filetype.cc fsencoding.cc groups.cc \ dsl.cc dsl_details.cc filetype.cc fsencoding.cc groups.cc \
groups_widgets.cc instances.cc article_maker.cc scanpopup.cc \ groups_widgets.cc instances.cc article_maker.cc scanpopup.cc \
articleview.cc externalviewer.cc dictlock.cc wordfinder.cc articleview.cc externalviewer.cc dictlock.cc wordfinder.cc \
groupcombobox.cc griparea.cc keyboardstate.cc
RESOURCES += resources.qrc flags.qrc RESOURCES += resources.qrc flags.qrc

53
src/griparea.cc Normal file
View file

@ -0,0 +1,53 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.sf.net>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "griparea.hh"
#include <QMouseEvent>
GripArea::GripArea( QWidget * parent ): QWidget( parent )
{
setCursor( Qt::OpenHandCursor );
}
void GripArea::paintEvent( QPaintEvent * )
{
if ( isEnabled() )
{
QStylePainter p( this );
QStyleOptionDockWidgetV2 opt;
opt.initFrom( this );
p.drawControl( QStyle::CE_DockWidgetTitle, opt );
}
}
void GripArea::mousePressEvent( QMouseEvent * ev )
{
startPos = ev->globalPos();
setCursor( Qt::ClosedHandCursor );
}
void GripArea::mouseMoveEvent( QMouseEvent * ev )
{
QPoint newPos = ev->globalPos();
QPoint delta = newPos - startPos;
startPos = newPos;
// Find a top-level window
QWidget * w = this;
while( w && !w->isWindow() && w->windowType() != Qt::SubWindow )
w = w->parentWidget();
w->move( w->pos() + delta );
}
void GripArea::mouseReleaseEvent( QMouseEvent * )
{
setCursor( Qt::OpenHandCursor );
}

32
src/griparea.hh Normal file
View file

@ -0,0 +1,32 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.sf.net>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifndef __GRIPAREA_HH_INCLUDED__
#define __GRIPAREA_HH_INCLUDED__
#include <QWidget>
#include <QStylePainter>
#include <QStyleOptionDockWidget>
/// A grip area to move a window, looking like a dock widget's title area.
class GripArea: public QWidget
{
Q_OBJECT
public:
GripArea( QWidget * parent );
protected:
virtual void paintEvent( QPaintEvent * );
virtual void mousePressEvent( QMouseEvent * );
virtual void mouseMoveEvent( QMouseEvent * );
virtual void mouseReleaseEvent( QMouseEvent * );
private:
QPoint startPos;
};
#endif

28
src/groupcombobox.cc Normal file
View file

@ -0,0 +1,28 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.sf.net>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "groupcombobox.hh"
GroupComboBox::GroupComboBox( QWidget * parent ): QComboBox( parent )
{
setSizeAdjustPolicy( AdjustToContents );
}
void GroupComboBox::fill( Instances::Groups const & groups )
{
QString prev = currentText();
clear();
for( unsigned x = 0; x < groups.size(); ++x )
{
QIcon icon = groups[ x ].icon.size() ?
QIcon( ":/flags/" + groups[ x ].icon ) : QIcon();
addItem( icon, groups[ x ].name );
if ( prev == groups[ x ].name )
setCurrentIndex( x );
}
}

24
src/groupcombobox.hh Normal file
View file

@ -0,0 +1,24 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.sf.net>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifndef __GROUPCOMBOBOX_HH_INCLUDED__
#define __GROUPCOMBOBOX_HH_INCLUDED__
#include <QComboBox>
#include "instances.hh"
/// This is a combo box which is for choosing the dictionary group
class GroupComboBox: public QComboBox
{
Q_OBJECT
public:
GroupComboBox( QWidget * parent );
/// Fills combo-box with the given groups
void fill( Instances::Groups const & );
};
#endif

BIN
src/icons/accents.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

BIN
src/icons/prefix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

BIN
src/icons/pushpin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

21
src/keyboardstate.cc Normal file
View file

@ -0,0 +1,21 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.sf.net>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "keyboardstate.hh"
#include <QX11Info>
#include <X11/X.h>
#include <X11/XKBlib.h>
bool KeyboardState::checkModifiersPressed( int mask )
{
XkbStateRec state;
XkbGetState( QX11Info::display(), XkbUseCoreKbd, &state );
return !(
( mask & Alt && !( state.base_mods & Mod1Mask ) ) ||
( mask & Ctrl && !( state.base_mods & ControlMask ) ) ||
( mask & Shift && !( state.base_mods & ShiftMask ) ) ||
( mask & Win && !( state.base_mods & Mod4Mask ) ) );
}

27
src/keyboardstate.hh Normal file
View file

@ -0,0 +1,27 @@
/* This file is (c) 2008-2009 Konstantin Isakov <ikm@users.sf.net>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifndef __KEYBOARDSTATE_HH_INCLUDED__
#define __KEYBOARDSTATE_HH_INCLUDED__
/// Since Qt doesn't provide a way to test for keyboard modifiers state
/// when the app isn't in focus, we have to implement this separately for
/// each platform.
class KeyboardState
{
public:
enum
{
Alt = 1,
Ctrl = 2,
Shift = 4,
Win = 8
} Modifier;
/// Returns true if all Modifiers present within the given mask are pressed
/// right now.
bool checkModifiersPressed( int mask );
};
#endif

View file

@ -3,12 +3,22 @@
#include <QApplication> #include <QApplication>
#include "mainwindow.hh" #include "mainwindow.hh"
#include "dictionary.hh" #include "config.hh"
int main( int argc, char ** argv ) int main( int argc, char ** argv )
{ {
QApplication app( argc, argv ); QApplication app( argc, argv );
// Try loading a style sheet if there's one
#if 1
QFile cssFile( Config::getUserQtCssFileName() );
if ( cssFile.open( QFile::ReadOnly ) )
app.setStyleSheet( cssFile.readAll() );
#endif
MainWindow m; MainWindow m;
m.show(); m.show();

View file

@ -27,7 +27,6 @@ MainWindow::MainWindow():
articleMaker( dictionaries, groupInstances ), articleMaker( dictionaries, groupInstances ),
articleNetMgr( this, dictionaries, articleMaker ), articleNetMgr( this, dictionaries, articleMaker ),
wordFinder( this ), wordFinder( this ),
scanPopup( 0, articleNetMgr ),
initializing( 0 ) initializing( 0 )
{ {
ui.setupUi( this ); ui.setupUi( this );
@ -214,6 +213,7 @@ void MainWindow::makeDictionaries()
updateStatusLine(); updateStatusLine();
updateGroupList(); updateGroupList();
makeScanPopup();
} }
void MainWindow::updateStatusLine() void MainWindow::updateStatusLine()
@ -239,24 +239,23 @@ void MainWindow::updateGroupList()
ui.groupLabel->setText( haveGroups ? tr( "Look up in:" ) : tr( "Look up:" ) ); ui.groupLabel->setText( haveGroups ? tr( "Look up in:" ) : tr( "Look up:" ) );
ui.groupList->clear();
groupInstances.clear();
DictLock _;
for( unsigned x = 0; x < cfg.groups.size(); ++x )
{ {
groupInstances.push_back( Instances::Group( cfg.groups[ x ], dictionaries ) ); DictLock _;
QIcon icon = cfg.groups[ x ].icon.size() ? groupInstances.clear();
QIcon( ":/flags/" + cfg.groups[ x ].icon ) : QIcon();
ui.groupList->addItem( icon, cfg.groups[ x ].name ); for( unsigned x = 0; x < cfg.groups.size(); ++x )
groupInstances.push_back( Instances::Group( cfg.groups[ x ], dictionaries ) );
} }
if ( haveGroups ) ui.groupList->fill( groupInstances );
ui.groupList->setCurrentIndex( 0 ); }
void MainWindow::makeScanPopup()
{
scanPopup.reset();
scanPopup = new ScanPopup( 0, articleNetMgr, dictionaries, groupInstances );
} }
vector< sptr< Dictionary::Class > > const & MainWindow::getActiveDicts() vector< sptr< Dictionary::Class > > const & MainWindow::getActiveDicts()
@ -377,6 +376,7 @@ void MainWindow::editGroups()
} }
updateGroupList(); updateGroupList();
makeScanPopup();
} }
void MainWindow::translateInputChanged( QString const & newValue ) void MainWindow::translateInputChanged( QString const & newValue )

View file

@ -70,13 +70,14 @@ private:
WordFinder wordFinder; WordFinder wordFinder;
ScanPopup scanPopup; sptr< ScanPopup > scanPopup;
::Initializing * initializing; ::Initializing * initializing;
void makeDictionaries(); void makeDictionaries();
void updateStatusLine(); void updateStatusLine();
void updateGroupList(); void updateGroupList();
void makeScanPopup();
/// Returns the reference to dictionaries stored in the currently active /// Returns the reference to dictionaries stored in the currently active
/// group, or to all dictionaries if there are no groups. /// group, or to all dictionaries if there are no groups.

View file

@ -1,7 +1,8 @@
<ui version="4.0" > <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class> <class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" > <widget class="QMainWindow" name="MainWindow">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
@ -9,44 +10,44 @@
<height>538</height> <height>538</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle">
<string>GoldenDict</string> <string>GoldenDict</string>
</property> </property>
<widget class="QWidget" name="centralwidget" > <widget class="QWidget" name="centralWidget">
<layout class="QHBoxLayout" name="horizontalLayout_2" > <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_2" > <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" > <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QLabel" name="groupLabel" > <widget class="QLabel" name="groupLabel">
<property name="text" > <property name="text">
<string>Look up in:</string> <string>Look up in:</string>
</property> </property>
<property name="alignment" > <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="groupList" > <widget class="GroupComboBox" name="groupList">
<property name="sizeAdjustPolicy" > <property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum> <enum>QComboBox::AdjustToContents</enum>
</property> </property>
<property name="duplicatesEnabled" > <property name="duplicatesEnabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="frame" > <property name="frame">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="horizontalSpacer" > <spacer name="horizontalSpacer">
<property name="orientation" > <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0" > <property name="sizeHint" stdset="0">
<size> <size>
<width>40</width> <width>40</width>
<height>20</height> <height>20</height>
@ -57,25 +58,25 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QLineEdit" name="translateLine" > <widget class="QLineEdit" name="translateLine">
<property name="minimumSize" > <property name="minimumSize">
<size> <size>
<width>200</width> <width>200</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="baseSize" > <property name="baseSize">
<size> <size>
<width>0</width> <width>0</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="palette" > <property name="palette">
<palette> <palette>
<active> <active>
<colorrole role="Base" > <colorrole role="Base">
<brush brushstyle="SolidPattern" > <brush brushstyle="SolidPattern">
<color alpha="255" > <color alpha="255">
<red>254</red> <red>254</red>
<green>253</green> <green>253</green>
<blue>235</blue> <blue>235</blue>
@ -84,9 +85,9 @@
</colorrole> </colorrole>
</active> </active>
<inactive> <inactive>
<colorrole role="Base" > <colorrole role="Base">
<brush brushstyle="SolidPattern" > <brush brushstyle="SolidPattern">
<color alpha="255" > <color alpha="255">
<red>254</red> <red>254</red>
<green>253</green> <green>253</green>
<blue>235</blue> <blue>235</blue>
@ -95,9 +96,9 @@
</colorrole> </colorrole>
</inactive> </inactive>
<disabled> <disabled>
<colorrole role="Base" > <colorrole role="Base">
<brush brushstyle="SolidPattern" > <brush brushstyle="SolidPattern">
<color alpha="255" > <color alpha="255">
<red>255</red> <red>255</red>
<green>255</green> <green>255</green>
<blue>255</blue> <blue>255</blue>
@ -107,32 +108,32 @@
</disabled> </disabled>
</palette> </palette>
</property> </property>
<property name="frame" > <property name="frame">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QStackedWidget" name="stackedWidget" > <widget class="QStackedWidget" name="stackedWidget">
<property name="lineWidth" > <property name="lineWidth">
<number>0</number> <number>0</number>
</property> </property>
<property name="currentIndex" > <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="page" > <widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_3" > <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin" > <property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QListWidget" name="wordList" > <widget class="QListWidget" name="wordList">
<property name="palette" > <property name="palette">
<palette> <palette>
<active> <active>
<colorrole role="Base" > <colorrole role="Base">
<brush brushstyle="SolidPattern" > <brush brushstyle="SolidPattern">
<color alpha="255" > <color alpha="255">
<red>254</red> <red>254</red>
<green>253</green> <green>253</green>
<blue>235</blue> <blue>235</blue>
@ -141,9 +142,9 @@
</colorrole> </colorrole>
</active> </active>
<inactive> <inactive>
<colorrole role="Base" > <colorrole role="Base">
<brush brushstyle="SolidPattern" > <brush brushstyle="SolidPattern">
<color alpha="255" > <color alpha="255">
<red>254</red> <red>254</red>
<green>253</green> <green>253</green>
<blue>235</blue> <blue>235</blue>
@ -152,9 +153,9 @@
</colorrole> </colorrole>
</inactive> </inactive>
<disabled> <disabled>
<colorrole role="Base" > <colorrole role="Base">
<brush brushstyle="SolidPattern" > <brush brushstyle="SolidPattern">
<color alpha="255" > <color alpha="255">
<red>255</red> <red>255</red>
<green>255</green> <green>255</green>
<blue>255</blue> <blue>255</blue>
@ -168,31 +169,31 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="page_2" > <widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout_4" > <layout class="QVBoxLayout" name="verticalLayout_4">
<property name="margin" > <property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QTabWidget" name="tabWidget_2" > <widget class="QTabWidget" name="tabWidget_2">
<property name="tabPosition" > <property name="tabPosition">
<enum>QTabWidget::West</enum> <enum>QTabWidget::West</enum>
</property> </property>
<widget class="QWidget" name="tab_2" > <widget class="QWidget" name="tab_2">
<attribute name="title" > <attribute name="title">
<string>Tab 1</string> <string>Tab 1</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_5" > <layout class="QVBoxLayout" name="verticalLayout_5">
<property name="margin" > <property name="margin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QListWidget" name="listWidget" /> <widget class="QListWidget" name="listWidget"/>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_3" > <widget class="QWidget" name="tab_3">
<attribute name="title" > <attribute name="title">
<string>Tab 2</string> <string>Tab 2</string>
</attribute> </attribute>
</widget> </widget>
@ -205,25 +206,25 @@
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QTabWidget" name="tabWidget" > <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex" > <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<property name="iconSize" > <property name="iconSize">
<size> <size>
<width>16</width> <width>16</width>
<height>16</height> <height>16</height>
</size> </size>
</property> </property>
<property name="elideMode" > <property name="elideMode">
<enum>Qt::ElideRight</enum> <enum>Qt::ElideRight</enum>
</property> </property>
<widget class="QWidget" name="tab" > <widget class="QWidget" name="tab">
<attribute name="title" > <attribute name="title">
<string>Welcome!</string> <string>Welcome!</string>
</attribute> </attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4" > <layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="margin" > <property name="margin">
<number>0</number> <number>0</number>
</property> </property>
</layout> </layout>
@ -231,56 +232,61 @@
</widget> </widget>
</item> </item>
</layout> </layout>
<zorder>tabWidget</zorder>
<zorder>view</zorder>
</widget> </widget>
<widget class="QMenuBar" name="menubar" > <widget class="QMenuBar" name="menubar">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>653</width> <width>653</width>
<height>29</height> <height>26</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile" > <widget class="QMenu" name="menuFile">
<property name="title" > <property name="title">
<string>&amp;File</string> <string>&amp;File</string>
</property> </property>
</widget> </widget>
<widget class="QMenu" name="menu_Edit" > <widget class="QMenu" name="menu_Edit">
<property name="title" > <property name="title">
<string>&amp;Edit</string> <string>&amp;Edit</string>
</property> </property>
<addaction name="sources" /> <addaction name="sources"/>
<addaction name="groups" /> <addaction name="groups"/>
</widget> </widget>
<addaction name="menuFile" /> <addaction name="menuFile"/>
<addaction name="menu_Edit" /> <addaction name="menu_Edit"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar" /> <widget class="QStatusBar" name="statusbar"/>
<action name="action_Preferences" > <action name="action_Preferences">
<property name="text" > <property name="text">
<string>&amp;Preferences...</string> <string>&amp;Preferences...</string>
</property> </property>
</action> </action>
<action name="sources" > <action name="sources">
<property name="text" > <property name="text">
<string>&amp;Sources...</string> <string>&amp;Sources...</string>
</property> </property>
</action> </action>
<action name="groups" > <action name="groups">
<property name="text" > <property name="text">
<string>&amp;Groups...</string> <string>&amp;Groups...</string>
</property> </property>
</action> </action>
</widget> </widget>
<customwidgets>
<customwidget>
<class>GroupComboBox</class>
<extends>QComboBox</extends>
<header>groupcombobox.hh</header>
</customwidget>
</customwidgets>
<tabstops> <tabstops>
<tabstop>translateLine</tabstop> <tabstop>translateLine</tabstop>
<tabstop>tabWidget</tabstop> <tabstop>tabWidget</tabstop>
</tabstops> </tabstops>
<resources> <resources>
<include location="resources.qrc" /> <include location="resources.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View file

@ -1,5 +1,8 @@
<RCC> <RCC>
<qresource> <qresource>
<file>icons/accents.png</file>
<file>icons/prefix.png</file>
<file>icons/pushpin.png</file>
<file>icons/playsound.png</file> <file>icons/playsound.png</file>
<file>icons/closetab.png</file> <file>icons/closetab.png</file>
<file>icons/addtab.png</file> <file>icons/addtab.png</file>

View file

@ -2,49 +2,229 @@
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */ * Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "scanpopup.hh" #include "scanpopup.hh"
#include "folding.hh"
#include <QUrl> #include <QUrl>
#include <QCursor>
#include <QPixmap>
#include <QBitmap>
#include <QMenu>
using std::wstring;
ScanPopup::ScanPopup( QWidget * parent, ScanPopup::ScanPopup( QWidget * parent,
ArticleNetworkAccessManager & articleNetMgr_ ): ArticleNetworkAccessManager & articleNetMgr,
QDialog( parent ), articleNetMgr( articleNetMgr_ ) std::vector< sptr< Dictionary::Class > > const & allDictionaries_,
Instances::Groups const & groups_ ):
QDialog( parent ),
allDictionaries( allDictionaries_ ),
groups( groups_ ),
wordFinder( this )
{ {
ui.setupUi( this ); ui.setupUi( this );
definition = new ArticleView( ui.outerFrame, articleNetMgr, groups, true ),
ui.mainLayout->addWidget( definition );
ui.diacriticButton->hide();
ui.prefixButton->hide();
ui.groupList->fill( groups );
//setWindowFlags( Qt::Tool ); //setWindowFlags( Qt::Tool );
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint | Qt::Tool );
ui.definition->page()->setNetworkAccessManager( &articleNetMgr ); #if 0 // Experimental code to give window a non-rectangular shape (i.e.
// balloon) using a colorkey mask.
QPixmap pixMask( size() );
render( &pixMask );
setMask( pixMask.createMaskFromColor( QColor( 255, 0, 0 ) ) );
// This helps against flickering
setAttribute( Qt::WA_NoSystemBackground );
#endif
connect( ui.groupList, SIGNAL( currentIndexChanged( QString const & ) ),
this, SLOT( currentGroupChanged( QString const & ) ) );
connect( wordFinder.qobject(), SIGNAL( prefixMatchComplete( WordFinderResults ) ),
this, SLOT( prefixMatchComplete( WordFinderResults ) ) );
connect( ui.word, SIGNAL( clicked() ),
this, SLOT( initialWordClicked() ) );
connect( ui.diacriticButton, SIGNAL( clicked() ),
this, SLOT( diacriticButtonClicked() ) );
connect( ui.prefixButton, SIGNAL( clicked() ),
this, SLOT( prefixButtonClicked() ) );
connect( ui.pinButton, SIGNAL( clicked( bool ) ),
this, SLOT( pinButtonClicked( bool ) ) );
#if 0 // Since this is unconditional this bugs a lot
connect( QApplication::clipboard(), SIGNAL( changed( QClipboard::Mode ) ), connect( QApplication::clipboard(), SIGNAL( changed( QClipboard::Mode ) ),
this, SLOT( clipboardChanged( QClipboard::Mode ) ) ); this, SLOT( clipboardChanged( QClipboard::Mode ) ) );
#endif
} }
void ScanPopup::clipboardChanged( QClipboard::Mode m ) void ScanPopup::clipboardChanged( QClipboard::Mode m )
{ {
printf( "clipboard changed\n" ); printf( "clipboard changed\n" );
QString subtype = "plain"; // Check key modifiers
QString inWord = QApplication::clipboard()->text( subtype, m ); if ( !checkModifiersPressed( Ctrl ) )
if ( !inWord.size() )
return; return;
setWindowTitle( inWord ); QString subtype = "plain";
show(); inputWord = QApplication::clipboard()->text( subtype, m ).trimmed();
activateWindow();
//raise();
QUrl req; if ( !inputWord.size() )
return;
req.setScheme( "gdlookup" ); setWindowTitle( inputWord );
req.setHost( "localhost" ); ui.word->setText( inputWord );
req.addQueryItem( "word", inWord );
req.addQueryItem( "group",
"whatever" );
ui.definition->load( req ); if ( !isVisible() )
{
QPoint currentPos = QCursor::pos();
move( currentPos.x() + 4, currentPos.y() + 10 );
show();
activateWindow();
QApplication::processEvents(); // Make window appear immediately no matter what
}
initiateTranslation();
} }
void ScanPopup::currentGroupChanged( QString const & )
{
if ( isVisible() )
initiateTranslation();
}
void ScanPopup::initiateTranslation()
{
wordFinder.prefixMatch( inputWord, &getActiveDicts() );
}
vector< sptr< Dictionary::Class > > const & ScanPopup::getActiveDicts()
{
int currentGroup = ui.groupList->currentIndex();
return
currentGroup < 0 || currentGroup >= (int)groups.size() ? allDictionaries :
groups[ currentGroup ].dictionaries;
}
void ScanPopup::leaveEvent( QEvent * event )
{
QDialog::leaveEvent( event );
// We hide the popup when the mouse leaves it. So in order to close it
// without any clicking the cursor has to get inside and then to leave.
// Combo-boxes seem to generate leave events for their parents when
// unfolded, so we check coordinates as well.
// If the dialog is pinned, we don't hide the popup
if ( !ui.pinButton->isChecked() && !geometry().contains( QCursor::pos() ) )
hide();
}
void ScanPopup::prefixMatchComplete( WordFinderResults r )
{
// Check that the request wasn't already overridden by another one and
// that there's a window there at all
if ( isVisible() && r.requestStr == inputWord &&
r.requestDicts == &getActiveDicts() )
{
// Find the matches that aren't prefix. If there're more than one,
// show the diacritic toolbutton. If there are prefix matches, show
// the prefix toolbutton.
diacriticMatches.clear();
prefixMatches.clear();
wstring foldedInputWord = Folding::apply( inputWord.toStdWString() );
for( unsigned x = 0; x < r.results.size(); ++x )
{
if ( Folding::apply( r.results[ x ].toStdWString() ) == foldedInputWord )
diacriticMatches.push_back( r.results[ x ] );
else
prefixMatches.push_back( r.results[ x ] );
}
if ( diacriticMatches.size() > 1 )
{
ui.diacriticButton->setToolTip( tr( "%1 results differing in diacritic marks" ).arg( diacriticMatches.size() ) );
ui.diacriticButton->show();
}
else
ui.diacriticButton->hide();
if ( prefixMatches.size() )
{
ui.prefixButton->setToolTip( tr( "%1 result(s) beginning with the search word" ).arg( prefixMatches.size() ) );
ui.prefixButton->show();
}
else
ui.prefixButton->hide();
if ( diacriticMatches.size() )
definition->showDefinition( diacriticMatches[ 0 ], ui.groupList->currentText() );
else
{
// No matches
definition->showNotFound( inputWord, ui.groupList->currentText() );
}
}
}
void ScanPopup::diacriticButtonClicked()
{
popupWordlist( diacriticMatches, ui.diacriticButton );
}
void ScanPopup::prefixButtonClicked()
{
popupWordlist( prefixMatches, ui.prefixButton );
}
void ScanPopup::popupWordlist( vector< QString > const & words, QToolButton * button )
{
if ( !isVisible() )
return;
if ( words.empty() )
return;
QMenu menu( this );
for( unsigned x = 0; x < words.size(); ++x )
menu.addAction( words[ x ] );
QAction * result = menu.exec( mapToGlobal( button->pos() ) +
QPoint( 0, button->height() ) );
if ( result )
definition->showDefinition( result->text(), ui.groupList->currentText() );
}
void ScanPopup::initialWordClicked()
{
if ( isVisible() && diacriticMatches.size() )
definition->showDefinition( diacriticMatches[ 0 ], ui.groupList->currentText() );
}
void ScanPopup::pinButtonClicked( bool checked )
{
if ( checked )
setWindowFlags( Qt::Dialog );
else
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint | Qt::Tool );
// Should we disable grip? I like it with the grip better.
//ui.gripArea->setDisabled( checked );
show();
}

View file

@ -5,26 +5,54 @@
#define __SCANPOPUP_HH_INCLUDED__ #define __SCANPOPUP_HH_INCLUDED__
#include "article_netmgr.hh" #include "article_netmgr.hh"
#include "articleview.hh"
#include "wordfinder.hh"
#include "keyboardstate.hh"
#include "ui_scanpopup.h" #include "ui_scanpopup.h"
#include <QDialog> #include <QDialog>
#include <QClipboard> #include <QClipboard>
class ScanPopup: public QDialog /// This is a popup dialog to show translations when clipboard scanning mode
/// is enabled.
class ScanPopup: public QDialog, KeyboardState
{ {
Q_OBJECT Q_OBJECT
public: public:
ScanPopup( QWidget * parent, ArticleNetworkAccessManager & articleNetMgr ); ScanPopup( QWidget * parent,
ArticleNetworkAccessManager &,
std::vector< sptr< Dictionary::Class > > const & allDictionaries,
Instances::Groups const & );
private: private:
ArticleNetworkAccessManager & articleNetMgr; std::vector< sptr< Dictionary::Class > > const & allDictionaries;
Instances::Groups const & groups;
Ui::ScanPopup ui; Ui::ScanPopup ui;
ArticleView * definition;
QString inputWord;
WordFinder wordFinder;
vector< QString > diacriticMatches, prefixMatches;
void initiateTranslation();
vector< sptr< Dictionary::Class > > const & getActiveDicts();
virtual void leaveEvent( QEvent * event );
void popupWordlist( vector< QString > const &, QToolButton * button );
private slots: private slots:
void clipboardChanged( QClipboard::Mode ); void clipboardChanged( QClipboard::Mode );
void currentGroupChanged( QString const & );
void prefixMatchComplete( WordFinderResults r );
void diacriticButtonClicked();
void prefixButtonClicked();
void initialWordClicked();
void pinButtonClicked( bool checked );
}; };
#endif #endif

View file

@ -1,67 +1,144 @@
<ui version="4.0" > <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScanPopup</class> <class>ScanPopup</class>
<widget class="QDialog" name="ScanPopup" > <widget class="QDialog" name="ScanPopup">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>557</width>
<height>300</height> <height>403</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle">
<string>Dialog</string> <string>Dialog</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout" > <layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>0</number>
</property>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" > <widget class="QFrame" name="outerFrame">
<item> <property name="frameShape">
<widget class="QToolButton" name="toolButton" > <enum>QFrame::NoFrame</enum>
<property name="text" >
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton_2" >
<property name="text" >
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QWebView" name="definition" >
<property name="url" >
<url>
<string>about:blank</string>
</url>
</property> </property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QVBoxLayout" name="mainLayout">
<property name="margin">
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="GroupComboBox" name="groupList"/>
</item>
<item>
<widget class="QToolButton" name="word">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>word</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="diacriticButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/accents.png</normaloff>:/icons/accents.png</iconset>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>16</height>
</size>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="prefixButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/prefix.png</normaloff>:/icons/prefix.png</iconset>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="GripArea" name="gripArea" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pinButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/pushpin.png</normaloff>:/icons/pushpin.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
<class>QWebView</class> <class>GroupComboBox</class>
<extends>QComboBox</extends>
<header>groupcombobox.hh</header>
</customwidget>
<customwidget>
<class>GripArea</class>
<extends>QWidget</extends> <extends>QWidget</extends>
<header>QtWebKit/QWebView</header> <header>griparea.hh</header>
<container>1</container>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources>
<include location="resources.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>