Russian Qt Forum
Сентябрь 24, 2024, 21:29 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: spellchecker  (Прочитано 4837 раз)
QuAzI
Гость
« : Декабрь 21, 2011, 20:54 »

Задачка довольно баянистая, но главная заковыка: проверять орфографию нужно в чужих окнах. За сим два вопроса
1) Как отлавливать что юзверь производит ввод в чьём-то TextEdit'е и получить его содержимое (текст).
2) Как hunspell натравить на этот TextEdit, чтобы подсветка синтаксиса велать в нём, а не отдельном окне.
Записан
BuRn
Гость
« Ответ #1 : Декабрь 21, 2011, 21:21 »

в экзамплах есть пример input panel называется , как считать с эдита дальше я думаю разберешься ... если что кину свой код
Записан
QuAzI
Гость
« Ответ #2 : Декабрь 22, 2011, 00:58 »

В чужих окнах. Была бы это своя форма - не возникло бы и вопроса. В данном экземпле идёт работа только с собственной формой.
Записан
andrew.k
Гость
« Ответ #3 : Декабрь 22, 2011, 01:04 »

наверное нужно использовать платформозависимое API.
средствами Qt не получится.
Записан
QuAzI
Гость
« Ответ #4 : Декабрь 24, 2011, 01:10 »

Код:
    #if defined(Q_WS_WIN32)
        char buff[MAX_PATH];

            HWND active = GetForegroundWindow();
            DWORD tid = GetWindowThreadProcessId(active, 0);
            DWORD cid = GetCurrentThreadId();
            AttachThreadInput(cid, tid, true);
            HWND focused = GetFocus();

            //qDebug() << GetWindowText(focused, (LPTSTR)buff, sizeof(buff));

            DWORD res = SendMessage( focused,
                                WM_GETTEXT,
                                sizeof(buff),
                                LPARAM((LPTSTR)buff));

            editor->setText(QString::fromUtf16((ushort*)buff));

            AttachThreadInput(cid, tid, false);
    #endif
Если дёргать через SendMessage, то из блокнота текст выдёргивается, но не проверяется на ошибки (это отдельно надо hunspell долбить). На ошибки проверяются только вновь введённые слова. Ну да пофиг, не сабж.
По сабжу если выбрать окошко посложнее, например окно Qt, то возвращается не содержимое, а тупо заголовок окна, как буд-то выполнен GetWindowText(). Про последний кстати писали что в случае передачи хендла контрола, а не хендла окна, должен возвращать текст, но он даже для блокнота возвращает только заголовок окна.
Есть ещё трезвые мысли, как можно выдёргивать текст из любого нужного эдита?
Записан
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #5 : Декабрь 24, 2011, 10:48 »

По сабжу если выбрать окошко посложнее, например окно Qt
Проблема не в том, что посложнее. Проблема в том, что на Qt, а Qt-шные виджеты не порождаются через WinAPI (кроме собственно окна), а рисуются.
Поэтому для WinAPI окно Qt-приложение - это пустое окно и до такого эдита через WinAPI вы никак не достучитесь.
Записан
QuAzI
Гость
« Ответ #6 : Декабрь 24, 2011, 11:10 »

Мм. Ну допустим (хотя странно). Но на EditPlus оно вообще сыпется при попытке извлечь текст. Как-то уж слишком грустно получается.
Записан
QuAzI
Гость
« Ответ #7 : Декабрь 24, 2011, 11:45 »

Прошёлся по софту
Блокнот, Texter2, консоль Total Commander, Dev C++ - всё отлично.
MS Office - текст "Документ Microsoft Word"
Qt - Текст заголовка окна
QIP - пусто
FlameRobin - текст "stcwindow"
Opera - Заголовок текущего таба
Firefox - Текст заголовка окна
DrWeb - вообще китайская бНОПНЯ, реальные иероглифы, причём они меняются в зависимости от текста введённого в тестовой строке (проверял на списке исключений)
А вот с EditPlus я ошибся - оно падает не при копировании текста как такового, а из-за того что текста было много. И так падает на любом большом тексте даже в блокноте или текстере (которые вроде как работают).
Грубо говоря - вообще нифига не работает.
Записан
QuAzI
Гость
« Ответ #8 : Январь 12, 2012, 08:49 »

Переделал оное на DLL с хуком под клавку (прочитать ввод с клавы, удалить лишнее, вставить правильный текст). Получилось не особо лучше. Qt, офис - хук не отрабатывает.
Второй косяк, не могу отловить где и почему, но после проверки пары-тройки слов перестаёт вообще отрабатывать хук.
Код:
#include "kbdhook.h"

QString kbdbuffer;

QString spell_dic;
Hunspell *pChecker = 0;

bool ownApp;

LRESULT CALLBACK KeyboardProc(int code,WPARAM wParam,LPARAM lParam) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
//  WPARAM The virtual-key code of the key that generated the keystroke message.
//

    // lParam decomposition

    //WORD nRepeat = (WORD) lParam; // bits 0..15
    UINT nScanCode = (UINT) (lParam & 0x00F0);
    //LPARAM bContext = lParam & 0x20000000;
    //LPARAM bPrev = lParam & 0x40000000;

    LPARAM bRelease = lParam & 0x80000000;
    bool isChar = false;
    bool isLetter = false;   

    if (bRelease) // KeyUp
    {
        static BYTE keyState[256];
        GetKeyboardState(keyState);
        static WCHAR inBuffer[4] = {0};

        HWND hWnd = GetForegroundWindow();
        DWORD WinThreadProcId = GetWindowThreadProcessId(hWnd, 0);
        HKL kbLayout = GetKeyboardLayout(WinThreadProcId);

        if (ToUnicodeEx(wParam,
              nScanCode,
              keyState,
              (WCHAR*)&inBuffer,
              4,
              0,
              kbLayout) >= 1)
        {
            isChar = true;
            QChar chr = QString::fromUtf16((const ushort*)&inBuffer, 1)[0];
            if (chr.isLetter())
            {
                isLetter = true;
                kbdbuffer += chr;
            }
        }
       if ((!kbdbuffer.isEmpty()) & (!isLetter))
        {
           if (pChecker == 0)
           {
             spell_dic="ru_RU";
             pChecker = new Hunspell(spell_dic.toLatin1()+".aff",spell_dic.toLatin1()+".dic");
            }

            QByteArray encodedString;
            QString spell_encoding=QString(pChecker->get_dic_encoding());
            QTextCodec *codec = QTextCodec::codecForName(spell_encoding.toLatin1());
            encodedString = codec->fromUnicode(kbdbuffer);
            bool isValid = pChecker->spell(encodedString.data());
            if (!isValid)
            {               
                        char ** wlst;
                        int ns = pChecker->suggest(&wlst,encodedString.data());                       
                        if (ns > 0)
                        {                           
                            QString newtext;
                                    {
                                    QMenu *ckMenu = new QMenu();
                                    for (int i=0; i < ns; i++)
                                    {
                                            ckMenu->addAction( codec->toUnicode(wlst[i]) );
                                            free(wlst[i]);
                                    }
                                    free(wlst);

                                    QAction *act = ckMenu->exec(QCursor::pos());
                                    if (act != 0)
                                        newtext = act->text();
                                    ckMenu->close();
                                    delete ckMenu;
                                    }

                                if (newtext.length()>0)
                                {
                                    int dRepeat = 0;
                                    dRepeat = (kbdbuffer.length());
                                    if (isChar) dRepeat = dRepeat + 1;
                                    for (int ii=0; ii<dRepeat; ii++)
                                    {
                                        keybd_event( VK_BACK,
                                                     0x45,
                                                     KEYEVENTF_EXTENDEDKEY | 0,
                                                     0 );
                                        keybd_event( VK_BACK,
                                                     0x45,
                                                     KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
                                                     0 );
                                    }

                                    for (int uu=0; uu<newtext.length(); uu++)
                                    {
                                        QString substr(newtext.at(uu));
                                        static WCHAR ss;
                                        substr.toWCharArray(&ss);

                                        short vk = VkKeyScanEx(ss, kbLayout);

                                        keybd_event( LOBYTE(vk),
                                                     0,
                                                     KEYEVENTF_EXTENDEDKEY | 0,
                                                     0 );
                                        keybd_event( LOBYTE(vk),
                                                     0,
                                                     KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
                                                     0 );
                                    }
                                }
                        }// if ns >0
                        pChecker->free_list(&wlst, ns);
            }
            kbdbuffer.clear();
        }
       if (!isLetter)
           kbdbuffer.clear();
    }

return CallNextHookEx(0,code,wParam,lParam);
}

BOOL WINAPI DllMain(  HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved  ) {

  if (fdwReason==DLL_PROCESS_ATTACH)
  {
      QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
      QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
      QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

     kbdbuffer = "";


    ownApp = QMfcApp::pluginInstance(hinstDLL);
    QMfcApp::enterModalLoop();
  } else
  if (fdwReason==DLL_PROCESS_DETACH)
  {
      if (pChecker!=0)
      {
        delete pChecker;
        pChecker = 0;
      }
     if (ownApp)
        {
         QMfcApp::exitModalLoop();
         delete qApp;     
        }
  }

  return TRUE;
}
Третий косяк - DLL выполняет DLL_PROCESS_ATTACH при срабатывании хука в каждом новом приложении. Соответственно DLL_PROCESS_DETACH только при его закрытии, пофиг что из своей программы я его давно UnhookWindowsHook, Unload и само приложение закрыл. Не совсем понятно получилось с ресурсами, вроде как переменные между собой не пересекаются (по крайней мере счётчик подключенных/отключенных у меня считает по единице каждый раз, а не накапливается), но глюки при этом ловлю только в путь. Особенно шикарно получается что приложение ловит в WindowsHook собственный вызов keybd_event и пытается его обработать отдельно. Попытка установки флага аки bool isTransaction = true на время ввода и его проверка при перехвате хука ничего не дали.
И ещё, как сделать чтобы API-функции экспортируемые библиотекой именовались по человечески?
Код:
#if defined(KBDHOOK_LIBRARY)
#  define KBDHOOKSHARED_EXPORT Q_DECL_EXPORT
#else
#  define KBDHOOKSHARED_EXPORT Q_DECL_IMPORT
#endif

...

extern "C" {
    KBDHOOKSHARED_EXPORT BOOL WINAPI DllMain(  HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved  );
    KBDHOOKSHARED_EXPORT LRESULT CALLBACK KeyboardProc(int code,WPARAM wParam,LPARAM lParam);
}
В экспорте имею KeyboardProc@12. Если убираю CALLBACK и WINAPI, то в экспорте имена красивые, но в этот момент хук перестаёт вызываться вообще. Т.е. SetWindowsHookEx хочет именно CALLBACK-функцию.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.118 секунд. Запросов: 23.