mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-24 00:14:06 +00:00
Initial support for mouseover feature in Windows added.
This commit is contained in:
parent
3f245cb58a
commit
626934c0cc
|
@ -64,7 +64,8 @@ HEADERS += folding.hh \
|
||||||
wordfinder.hh \
|
wordfinder.hh \
|
||||||
groupcombobox.hh \
|
groupcombobox.hh \
|
||||||
griparea.hh \
|
griparea.hh \
|
||||||
keyboardstate.hh
|
keyboardstate.hh \
|
||||||
|
mouseover.hh
|
||||||
|
|
||||||
|
|
||||||
FORMS += groups.ui dictgroupwidget.ui mainwindow.ui sources.ui initializing.ui\
|
FORMS += groups.ui dictgroupwidget.ui mainwindow.ui sources.ui initializing.ui\
|
||||||
|
@ -77,6 +78,11 @@ SOURCES += folding.cc main.cc dictionary.cc md5.c config.cc sources.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
|
groupcombobox.cc griparea.cc keyboardstate.cc mouseover.cc
|
||||||
|
|
||||||
|
win32 {
|
||||||
|
SOURCES += mouseover_win32/ThTypes.c
|
||||||
|
HEADERS += mouseover_win32/ThTypes.h
|
||||||
|
}
|
||||||
|
|
||||||
RESOURCES += resources.qrc flags.qrc
|
RESOURCES += resources.qrc flags.qrc
|
||||||
|
|
190
src/mouseover.cc
Normal file
190
src/mouseover.cc
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
#include "mouseover.hh"
|
||||||
|
#include "utf8.hh"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
#include "mouseover_win32/ThTypes.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MouseOver & MouseOver::instance()
|
||||||
|
{
|
||||||
|
static MouseOver m;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
const UINT WM_MY_SHOW_TRANSLATION = WM_USER + 301;
|
||||||
|
static wchar_t className[] = L"GoldenDictMouseover";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MouseOver::MouseOver()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
|
||||||
|
ThTypes_Init();
|
||||||
|
memset( GlobalData, 0, sizeof( TGlobalDLLData ) );
|
||||||
|
strcpy( GlobalData->LibName,
|
||||||
|
QDir::toNativeSeparators( QDir( QCoreApplication::applicationDirPath() ).filePath( "GdTextOutHook.dll" ) ).toLocal8Bit().data() );
|
||||||
|
|
||||||
|
// Create the window to recive spying results to
|
||||||
|
|
||||||
|
WNDCLASSEX wcex;
|
||||||
|
|
||||||
|
wcex.cbSize = sizeof( WNDCLASSEX );
|
||||||
|
|
||||||
|
wcex.style = 0;
|
||||||
|
wcex.lpfnWndProc = ( WNDPROC ) eventHandler;
|
||||||
|
wcex.cbClsExtra = 0;
|
||||||
|
wcex.cbWndExtra = 0;
|
||||||
|
wcex.hInstance = GetModuleHandle( 0 );
|
||||||
|
wcex.hIcon = NULL;
|
||||||
|
wcex.hCursor = NULL,
|
||||||
|
wcex.hbrBackground = NULL;
|
||||||
|
wcex.lpszMenuName = NULL;
|
||||||
|
wcex.lpszClassName = className;
|
||||||
|
wcex.hIconSm = NULL;
|
||||||
|
|
||||||
|
RegisterClassEx( &wcex );
|
||||||
|
|
||||||
|
GlobalData->ServerWND = CreateWindow( className, L"", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, GetModuleHandle( 0 ), 0 );
|
||||||
|
|
||||||
|
spyDll = LoadLibrary( QDir::toNativeSeparators( QDir( QCoreApplication::applicationDirPath() ).filePath( "GdTextOutSpy.dll" ) ).toStdWString().c_str() );
|
||||||
|
|
||||||
|
if ( spyDll )
|
||||||
|
{
|
||||||
|
activateSpyFn = ( ActivateSpyFn ) GetProcAddress( spyDll, "ActivateTextOutSpying" );
|
||||||
|
|
||||||
|
if ( activateSpyFn )
|
||||||
|
activateSpyFn( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
|
||||||
|
LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg,
|
||||||
|
WPARAM wparam, LPARAM lparam )
|
||||||
|
{
|
||||||
|
if ( msg == WM_MY_SHOW_TRANSLATION )
|
||||||
|
{
|
||||||
|
int wordSeqPos;
|
||||||
|
QString wordSeq;
|
||||||
|
|
||||||
|
// Is the string in utf8 or in locale encoding?
|
||||||
|
|
||||||
|
wchar_t testBuf[ 256 ];
|
||||||
|
|
||||||
|
long result = Utf8::decode( GlobalData->CurMod.MatchedWord,
|
||||||
|
strlen( GlobalData->CurMod.MatchedWord ),
|
||||||
|
testBuf );
|
||||||
|
|
||||||
|
if ( result >= 0 )
|
||||||
|
{
|
||||||
|
// It seems to be
|
||||||
|
QString begin = QString::fromUtf8( GlobalData->CurMod.MatchedWord,
|
||||||
|
GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C );
|
||||||
|
|
||||||
|
QString end = QString::fromUtf8( GlobalData->CurMod.MatchedWord +
|
||||||
|
GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C );
|
||||||
|
|
||||||
|
wordSeq = begin + end;
|
||||||
|
wordSeqPos = begin.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// It's not, so interpret it as in local encoding
|
||||||
|
QString begin = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord,
|
||||||
|
GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C );
|
||||||
|
|
||||||
|
QString end = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord +
|
||||||
|
GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C );
|
||||||
|
|
||||||
|
wordSeq = begin + end;
|
||||||
|
wordSeqPos = begin.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now locate the word inside the sequence
|
||||||
|
|
||||||
|
QString word;
|
||||||
|
|
||||||
|
if ( wordSeq[ wordSeqPos ].isSpace() )
|
||||||
|
{
|
||||||
|
// Currently we ignore such cases
|
||||||
|
return DefWindowProc( hwnd, msg, wparam, lparam );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if ( !wordSeq[ wordSeqPos ].isLetterOrNumber() )
|
||||||
|
{
|
||||||
|
// Special case: the cursor points to something which doesn't look like a
|
||||||
|
// middle of the word -- assume that it's something that joins two words
|
||||||
|
// together.
|
||||||
|
|
||||||
|
int begin = wordSeqPos;
|
||||||
|
|
||||||
|
for( ; begin; --begin )
|
||||||
|
if ( !wordSeq[ begin - 1 ].isLetterOrNumber() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
int end = wordSeqPos;
|
||||||
|
|
||||||
|
while( ++end < wordSeq.size() )
|
||||||
|
if ( !wordSeq[ end ].isLetterOrNumber() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ( end - begin == 1 )
|
||||||
|
{
|
||||||
|
// Well, turns out it was just a single non-letter char, discard it
|
||||||
|
return DefWindowProc( hwnd, msg, wparam, lparam );
|
||||||
|
}
|
||||||
|
|
||||||
|
word = wordSeq.mid( begin, end - begin );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Cursor points to a letter -- cut the word it points to
|
||||||
|
|
||||||
|
int begin = wordSeqPos;
|
||||||
|
|
||||||
|
for( ; begin; --begin )
|
||||||
|
if ( !wordSeq[ begin - 1 ].isLetterOrNumber() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
int end = wordSeqPos;
|
||||||
|
|
||||||
|
while( ++end < wordSeq.size() )
|
||||||
|
{
|
||||||
|
if ( !wordSeq[ end ].isLetterOrNumber() )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
word = wordSeq.mid( begin, end - begin );
|
||||||
|
}
|
||||||
|
|
||||||
|
emit instance().hovered( word );
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc( hwnd, msg, wparam, lparam );
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MouseOver::~MouseOver()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
|
||||||
|
if ( activateSpyFn )
|
||||||
|
activateSpyFn( false );
|
||||||
|
|
||||||
|
FreeLibrary( spyDll );
|
||||||
|
|
||||||
|
DestroyWindow( GlobalData->ServerWND );
|
||||||
|
|
||||||
|
UnregisterClass( className, GetModuleHandle( 0 ) );
|
||||||
|
|
||||||
|
Thtypes_End();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
50
src/mouseover.hh
Normal file
50
src/mouseover.hh
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef __MOUSEOVER_HH_INCLUDED__
|
||||||
|
#define __MOUSEOVER_HH_INCLUDED__
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// This is a mouseover feature interface, where you can point your mouse at
|
||||||
|
/// any word in any window and wait a little, and it would provide that word
|
||||||
|
/// for the translation.
|
||||||
|
/// This interface always exists, even on platforms that don't support that
|
||||||
|
/// feature -- it just remains dormant on them.
|
||||||
|
///
|
||||||
|
/// The Windows platform is the only one supported; it works with the help of
|
||||||
|
/// two external .dll files,
|
||||||
|
class MouseOver: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// The class is a singleton.
|
||||||
|
static MouseOver & instance();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
/// Emitted when there was some text under cursor which was hovered over.
|
||||||
|
void hovered( QString const & );
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
MouseOver();
|
||||||
|
~MouseOver();
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
|
||||||
|
static LRESULT CALLBACK eventHandler( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );
|
||||||
|
|
||||||
|
typedef void ( *ActivateSpyFn )( bool );
|
||||||
|
ActivateSpyFn activateSpyFn;
|
||||||
|
HINSTANCE spyDll;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "scanpopup.hh"
|
#include "scanpopup.hh"
|
||||||
#include "folding.hh"
|
#include "folding.hh"
|
||||||
|
#include "mouseover.hh"
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
@ -59,6 +60,9 @@ ScanPopup::ScanPopup( QWidget * parent,
|
||||||
|
|
||||||
connect( QApplication::clipboard(), SIGNAL( changed( QClipboard::Mode ) ),
|
connect( QApplication::clipboard(), SIGNAL( changed( QClipboard::Mode ) ),
|
||||||
this, SLOT( clipboardChanged( QClipboard::Mode ) ) );
|
this, SLOT( clipboardChanged( QClipboard::Mode ) ) );
|
||||||
|
|
||||||
|
connect( &MouseOver::instance(), SIGNAL( hovered( QString const & ) ),
|
||||||
|
this, SLOT( mouseHovered( QString const & ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScanPopup::clipboardChanged( QClipboard::Mode m )
|
void ScanPopup::clipboardChanged( QClipboard::Mode m )
|
||||||
|
@ -72,7 +76,17 @@ void ScanPopup::clipboardChanged( QClipboard::Mode m )
|
||||||
|
|
||||||
QString subtype = "plain";
|
QString subtype = "plain";
|
||||||
|
|
||||||
inputWord = QApplication::clipboard()->text( subtype, m ).trimmed();
|
handleInputWord( QApplication::clipboard()->text( subtype, m ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanPopup::mouseHovered( QString const & str )
|
||||||
|
{
|
||||||
|
handleInputWord( str );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScanPopup::handleInputWord( QString const & str )
|
||||||
|
{
|
||||||
|
inputWord = str.trimmed();
|
||||||
|
|
||||||
if ( !inputWord.size() )
|
if ( !inputWord.size() )
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -36,6 +36,7 @@ private:
|
||||||
|
|
||||||
vector< QString > diacriticMatches, prefixMatches;
|
vector< QString > diacriticMatches, prefixMatches;
|
||||||
|
|
||||||
|
void handleInputWord( QString const & );
|
||||||
void initiateTranslation();
|
void initiateTranslation();
|
||||||
|
|
||||||
vector< sptr< Dictionary::Class > > const & getActiveDicts();
|
vector< sptr< Dictionary::Class > > const & getActiveDicts();
|
||||||
|
@ -47,6 +48,7 @@ private:
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void clipboardChanged( QClipboard::Mode );
|
void clipboardChanged( QClipboard::Mode );
|
||||||
|
void mouseHovered( QString const & );
|
||||||
void currentGroupChanged( QString const & );
|
void currentGroupChanged( QString const & );
|
||||||
void prefixMatchComplete( WordFinderResults r );
|
void prefixMatchComplete( WordFinderResults r );
|
||||||
void diacriticButtonClicked();
|
void diacriticButtonClicked();
|
||||||
|
|
Loading…
Reference in a new issue