├── CMakeLists.txt ├── CandidateWindow.cpp ├── CandidateWindow.h ├── ComPtr.h ├── Dialog.cpp ├── Dialog.h ├── DisplayAttributeInfo.cpp ├── DisplayAttributeInfo.h ├── DisplayAttributeInfoEnum.cpp ├── DisplayAttributeInfoEnum.h ├── DisplayAttributeProvider.cpp ├── DisplayAttributeProvider.h ├── DrawUtils.cpp ├── DrawUtils.h ├── EditSession.cpp ├── EditSession.h ├── ImeEngine.cpp ├── ImeEngine.h ├── ImeModule.cpp ├── ImeModule.h ├── ImeWindow.cpp ├── ImeWindow.h ├── KeyEvent.cpp ├── KeyEvent.h ├── LICENSE.txt ├── LangBarButton.cpp ├── LangBarButton.h ├── MessageWindow.cpp ├── MessageWindow.h ├── PropertyDialog.cpp ├── PropertyDialog.h ├── PropertyPage.cpp ├── PropertyPage.h ├── TextService.cpp ├── TextService.h ├── Utils.cpp ├── Utils.h ├── Window.cpp ├── Window.h ├── WindowsVersion.h ├── libIME.cpp ├── libIME.h ├── libIME.rc └── resource.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.8) 2 | 3 | project(libIME) 4 | 5 | # http://www.utf8everywhere.org/ 6 | add_definitions( 7 | /D_UNICODE=1 8 | /DUNICODE=1 9 | ) 10 | 11 | add_library(libIME_static STATIC 12 | # Core TSF part 13 | ${PROJECT_SOURCE_DIR}/ImeModule.cpp 14 | ${PROJECT_SOURCE_DIR}/ImeModule.h 15 | ${PROJECT_SOURCE_DIR}/libIME.cpp 16 | ${PROJECT_SOURCE_DIR}/libIME.h 17 | ${PROJECT_SOURCE_DIR}/TextService.cpp 18 | ${PROJECT_SOURCE_DIR}/TextService.h 19 | ${PROJECT_SOURCE_DIR}/KeyEvent.cpp 20 | ${PROJECT_SOURCE_DIR}/KeyEvent.h 21 | ${PROJECT_SOURCE_DIR}/EditSession.cpp 22 | ${PROJECT_SOURCE_DIR}/EditSession.h 23 | ${PROJECT_SOURCE_DIR}/DisplayAttributeInfo.cpp 24 | ${PROJECT_SOURCE_DIR}/DisplayAttributeInfo.h 25 | ${PROJECT_SOURCE_DIR}/DisplayAttributeInfoEnum.cpp 26 | ${PROJECT_SOURCE_DIR}/DisplayAttributeInfoEnum.h 27 | ${PROJECT_SOURCE_DIR}/DisplayAttributeProvider.cpp 28 | ${PROJECT_SOURCE_DIR}/DisplayAttributeProvider.h 29 | ${PROJECT_SOURCE_DIR}/LangBarButton.cpp 30 | ${PROJECT_SOURCE_DIR}/LangBarButton.h 31 | ${PROJECT_SOURCE_DIR}/Utils.cpp 32 | ${PROJECT_SOURCE_DIR}/Utils.h 33 | ${PROJECT_SOURCE_DIR}/ComPtr.h 34 | ${PROJECT_SOURCE_DIR}/WindowsVersion.h 35 | # GUI-related code 36 | ${PROJECT_SOURCE_DIR}/DrawUtils.h 37 | ${PROJECT_SOURCE_DIR}/DrawUtils.cpp 38 | ${PROJECT_SOURCE_DIR}/Window.cpp 39 | ${PROJECT_SOURCE_DIR}/Window.h 40 | # ${PROJECT_SOURCE_DIR}/Dialog.cpp 41 | # ${PROJECT_SOURCE_DIR}/Dialog.h 42 | # ${PROJECT_SOURCE_DIR}/PropertyDialog.cpp 43 | # ${PROJECT_SOURCE_DIR}/PropertyDialog.h 44 | # ${PROJECT_SOURCE_DIR}/PropertyPage.cpp 45 | # ${PROJECT_SOURCE_DIR}/PropertyPage.h 46 | ${PROJECT_SOURCE_DIR}/ImeWindow.cpp 47 | ${PROJECT_SOURCE_DIR}/ImeWindow.h 48 | ${PROJECT_SOURCE_DIR}/MessageWindow.cpp 49 | ${PROJECT_SOURCE_DIR}/MessageWindow.h 50 | ${PROJECT_SOURCE_DIR}/CandidateWindow.h 51 | ${PROJECT_SOURCE_DIR}/CandidateWindow.cpp 52 | ) 53 | 54 | target_link_libraries(libIME_static 55 | shlwapi.lib 56 | ) 57 | -------------------------------------------------------------------------------- /CandidateWindow.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "CandidateWindow.h" 21 | #include "DrawUtils.h" 22 | #include "TextService.h" 23 | #include "EditSession.h" 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | using namespace std; 32 | 33 | namespace Ime { 34 | 35 | CandidateWindow::CandidateWindow(TextService* service, EditSession* session): 36 | ImeWindow(service), 37 | refCount_(1), 38 | shown_(false), 39 | candPerRow_(1), 40 | textWidth_(0), 41 | itemHeight_(0), 42 | currentSel_(0), 43 | hasResult_(false), 44 | useCursor_(true), 45 | selKeyWidth_(0) { 46 | 47 | if(service->isImmersive()) { // windows 8 app mode 48 | margin_ = 10; 49 | rowSpacing_ = 8; 50 | colSpacing_ = 12; 51 | } 52 | else { // desktop mode 53 | margin_ = 5; 54 | rowSpacing_ = 4; 55 | colSpacing_ = 8; 56 | } 57 | 58 | HWND parent = service->compositionWindow(session); 59 | create(parent, WS_POPUP|WS_CLIPCHILDREN, WS_EX_TOOLWINDOW|WS_EX_TOPMOST); 60 | } 61 | 62 | CandidateWindow::~CandidateWindow(void) { 63 | } 64 | 65 | // IUnknown 66 | STDMETHODIMP CandidateWindow::QueryInterface(REFIID riid, void **ppvObj) { 67 | if (!ppvObj) 68 | return E_INVALIDARG; 69 | 70 | if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCandidateListUIElement)) { 71 | *ppvObj = (ITfCandidateListUIElement*)this; 72 | } 73 | else { 74 | *ppvObj = NULL; 75 | } 76 | 77 | if (!*ppvObj) { 78 | return E_NOINTERFACE; 79 | } 80 | 81 | AddRef(); 82 | return S_OK; 83 | } 84 | 85 | STDMETHODIMP_(ULONG) CandidateWindow::AddRef(void) { 86 | return ++refCount_; 87 | } 88 | 89 | STDMETHODIMP_(ULONG) CandidateWindow::Release(void) { 90 | assert(refCount_ > 0); 91 | const ULONG newCount = --refCount_; 92 | if (refCount_ == 0) 93 | delete this; 94 | return newCount; 95 | } 96 | 97 | // ITfUIElement 98 | STDMETHODIMP CandidateWindow::GetDescription(BSTR *pbstrDescription) { 99 | if (!pbstrDescription) 100 | return E_INVALIDARG; 101 | *pbstrDescription = SysAllocString(L"Candidate window~"); 102 | return S_OK; 103 | } 104 | 105 | // {BD7CCC94-57CD-41D3-A789-AF47890CEB29} 106 | STDMETHODIMP CandidateWindow::GetGUID(GUID *pguid) { 107 | if (!pguid) 108 | return E_INVALIDARG; 109 | *pguid = { 0xbd7ccc94, 0x57cd, 0x41d3, { 0xa7, 0x89, 0xaf, 0x47, 0x89, 0xc, 0xeb, 0x29 } }; 110 | return S_OK; 111 | } 112 | 113 | STDMETHODIMP CandidateWindow::Show(BOOL bShow) { 114 | shown_ = bShow; 115 | if (shown_) 116 | show(); 117 | else 118 | hide(); 119 | return S_OK; 120 | } 121 | 122 | STDMETHODIMP CandidateWindow::IsShown(BOOL *pbShow) { 123 | if (!pbShow) 124 | return E_INVALIDARG; 125 | *pbShow = shown_; 126 | return S_OK; 127 | } 128 | 129 | // ITfCandidateListUIElement 130 | STDMETHODIMP CandidateWindow::GetUpdatedFlags(DWORD *pdwFlags) { 131 | if (!pdwFlags) 132 | return E_INVALIDARG; 133 | /// XXX update all!!! 134 | *pdwFlags = TF_CLUIE_DOCUMENTMGR | TF_CLUIE_COUNT | TF_CLUIE_SELECTION | TF_CLUIE_STRING | TF_CLUIE_PAGEINDEX | TF_CLUIE_CURRENTPAGE; 135 | return S_OK; 136 | } 137 | 138 | STDMETHODIMP CandidateWindow::GetDocumentMgr(ITfDocumentMgr **ppdim) { 139 | if (!textService_) 140 | return E_FAIL; 141 | return textService_->currentContext()->GetDocumentMgr(ppdim); 142 | } 143 | 144 | STDMETHODIMP CandidateWindow::GetCount(UINT *puCount) { 145 | if (!puCount) 146 | return E_INVALIDARG; 147 | *puCount = std::min(10, items_.size()); 148 | return S_OK; 149 | } 150 | 151 | STDMETHODIMP CandidateWindow::GetSelection(UINT *puIndex) { 152 | assert(currentSel_ >= 0); 153 | if (!puIndex) 154 | return E_INVALIDARG; 155 | *puIndex = static_cast(currentSel_); 156 | return S_OK; 157 | } 158 | 159 | STDMETHODIMP CandidateWindow::GetString(UINT uIndex, BSTR *pbstr) { 160 | if (!pbstr) 161 | return E_INVALIDARG; 162 | if (uIndex >= items_.size()) 163 | return E_INVALIDARG; 164 | *pbstr = SysAllocString(items_[uIndex].c_str()); 165 | return S_OK; 166 | } 167 | 168 | STDMETHODIMP CandidateWindow::GetPageIndex(UINT *puIndex, UINT uSize, UINT *puPageCnt) { 169 | /// XXX Always return the same single page index. 170 | if (!puPageCnt) 171 | return E_INVALIDARG; 172 | *puPageCnt = 1; 173 | if (puIndex) { 174 | if (uSize < *puPageCnt) { 175 | return E_INVALIDARG; 176 | } 177 | puIndex[0] = 0; 178 | } 179 | return S_OK; 180 | } 181 | 182 | STDMETHODIMP CandidateWindow::SetPageIndex(UINT *puIndex, UINT uPageCnt) { 183 | /// XXX Do not let app set page indices. 184 | if (!puIndex) 185 | return E_INVALIDARG; 186 | return S_OK; 187 | } 188 | 189 | STDMETHODIMP CandidateWindow::GetCurrentPage(UINT *puPage) { 190 | if (!puPage) 191 | return E_INVALIDARG; 192 | *puPage = 0; 193 | return S_OK; 194 | } 195 | 196 | LRESULT CandidateWindow::wndProc(UINT msg, WPARAM wp , LPARAM lp) { 197 | switch (msg) { 198 | case WM_PAINT: 199 | onPaint(wp, lp); 200 | break; 201 | case WM_ERASEBKGND: 202 | return TRUE; 203 | break; 204 | case WM_LBUTTONDOWN: 205 | onLButtonDown(wp, lp); 206 | break; 207 | case WM_MOUSEMOVE: 208 | onMouseMove(wp, lp); 209 | break; 210 | case WM_LBUTTONUP: 211 | onLButtonUp(wp, lp); 212 | break; 213 | case WM_MOUSEACTIVATE: 214 | return MA_NOACTIVATE; 215 | default: 216 | return Window::wndProc(msg, wp, lp); 217 | } 218 | return 0; 219 | } 220 | 221 | void CandidateWindow::onPaint(WPARAM wp, LPARAM lp) { 222 | // TODO: check isImmersive_, and draw the window differently 223 | // in Windows 8 app immersive mode to follow windows 8 UX guidelines 224 | PAINTSTRUCT ps; 225 | BeginPaint(hwnd_, &ps); 226 | HDC hDC = ps.hdc; 227 | HFONT oldFont; 228 | RECT rc; 229 | 230 | oldFont = (HFONT)SelectObject(hDC, font_); 231 | 232 | GetClientRect(hwnd_,&rc); 233 | SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT)); 234 | SetBkColor(hDC, GetSysColor(COLOR_WINDOW)); 235 | 236 | // paint window background and border 237 | // draw a flat black border in Windows 8 app immersive mode 238 | // draw a 3d border in desktop mode 239 | if(isImmersive()) { 240 | HPEN pen = ::CreatePen(PS_SOLID, 3, RGB(0, 0, 0)); 241 | HGDIOBJ oldPen = ::SelectObject(hDC, pen); 242 | ::Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); 243 | ::SelectObject(hDC, oldPen); 244 | ::DeleteObject(pen); 245 | } 246 | else { 247 | // draw a 3d border in desktop mode 248 | ::FillSolidRect(ps.hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, GetSysColor(COLOR_WINDOW)); 249 | ::Draw3DBorder(hDC, &rc, GetSysColor(COLOR_3DFACE), 0); 250 | } 251 | 252 | // paint items 253 | int col = 0; 254 | int x = margin_, y = margin_; 255 | for(int i = 0, n = items_.size(); i < n; ++i) { 256 | paintItem(hDC, i, x, y); 257 | ++col; // go to next column 258 | if(col >= candPerRow_) { 259 | col = 0; 260 | x = margin_; 261 | y += itemHeight_ + rowSpacing_; 262 | } 263 | else { 264 | x += colSpacing_ + selKeyWidth_ + textWidth_; 265 | } 266 | } 267 | SelectObject(hDC, oldFont); 268 | EndPaint(hwnd_, &ps); 269 | } 270 | 271 | void CandidateWindow::recalculateSize() { 272 | if(items_.empty()) { 273 | resize(margin_ * 2, margin_ * 2); 274 | } 275 | 276 | HDC hDC = ::GetWindowDC(hwnd()); 277 | int height = 0; 278 | int width = 0; 279 | selKeyWidth_ = 0; 280 | textWidth_ = 0; 281 | itemHeight_ = 0; 282 | 283 | HGDIOBJ oldFont = ::SelectObject(hDC, font_); 284 | vector::const_iterator it; 285 | for(int i = 0, n = items_.size(); i < n; ++i) { 286 | SIZE selKeySize; 287 | int lineHeight = 0; 288 | // the selection key string 289 | wchar_t selKey[] = L"?. "; 290 | selKey[0] = selKeys_[i]; 291 | ::GetTextExtentPoint32W(hDC, selKey, 3, &selKeySize); 292 | if(selKeySize.cx > selKeyWidth_) 293 | selKeyWidth_ = selKeySize.cx; 294 | 295 | // the candidate string 296 | SIZE candidateSize; 297 | wstring& item = items_.at(i); 298 | ::GetTextExtentPoint32W(hDC, item.c_str(), item.length(), &candidateSize); 299 | if(candidateSize.cx > textWidth_) 300 | textWidth_ = candidateSize.cx; 301 | int itemHeight = max(candidateSize.cy, selKeySize.cy); 302 | if(itemHeight > itemHeight_) 303 | itemHeight_ = itemHeight; 304 | } 305 | ::SelectObject(hDC, oldFont); 306 | ::ReleaseDC(hwnd(), hDC); 307 | 308 | if(items_.size() <= candPerRow_) { 309 | width = items_.size() * (selKeyWidth_ + textWidth_); 310 | width += colSpacing_ * (items_.size() - 1); 311 | width += margin_ * 2; 312 | height = itemHeight_ + margin_ * 2; 313 | } 314 | else { 315 | width = candPerRow_ * (selKeyWidth_ + textWidth_); 316 | width += colSpacing_ * (candPerRow_ - 1); 317 | width += margin_ * 2; 318 | int rowCount = items_.size() / candPerRow_; 319 | if(items_.size() % candPerRow_) 320 | ++rowCount; 321 | height = itemHeight_ * rowCount + rowSpacing_ * (rowCount - 1) + margin_ * 2; 322 | } 323 | resize(width, height); 324 | } 325 | 326 | void CandidateWindow::setCandPerRow(int n) { 327 | if(n != candPerRow_) { 328 | candPerRow_ = n; 329 | recalculateSize(); 330 | } 331 | } 332 | 333 | bool CandidateWindow::filterKeyEvent(KeyEvent& keyEvent) { 334 | // select item with arrow keys 335 | int oldSel = currentSel_; 336 | switch(keyEvent.keyCode()) { 337 | case VK_UP: 338 | if(currentSel_ - candPerRow_ >=0) 339 | currentSel_ -= candPerRow_; 340 | break; 341 | case VK_DOWN: 342 | if(currentSel_ + candPerRow_ < items_.size()) 343 | currentSel_ += candPerRow_; 344 | break; 345 | case VK_LEFT: 346 | if(currentSel_ - 1 >=0) 347 | --currentSel_; 348 | break; 349 | case VK_RIGHT: 350 | if(currentSel_ + 1 < items_.size()) 351 | ++currentSel_; 352 | break; 353 | case VK_RETURN: 354 | hasResult_ = true; 355 | return true; 356 | default: 357 | return false; 358 | } 359 | // if currently selected item is changed, redraw 360 | if(currentSel_ != oldSel) { 361 | // repaint the old and new items 362 | RECT rect; 363 | itemRect(oldSel, rect); 364 | ::InvalidateRect(hwnd_, &rect, TRUE); 365 | itemRect(currentSel_, rect); 366 | ::InvalidateRect(hwnd_, &rect, TRUE); 367 | return true; 368 | } 369 | return false; 370 | } 371 | 372 | void CandidateWindow::setCurrentSel(int sel) { 373 | if(sel >= items_.size()) 374 | sel = 0; 375 | if (currentSel_ != sel) { 376 | currentSel_ = sel; 377 | if (isVisible()) 378 | ::InvalidateRect(hwnd_, NULL, TRUE); 379 | } 380 | } 381 | 382 | void CandidateWindow::clear() { 383 | items_.clear(); 384 | selKeys_.clear(); 385 | currentSel_ = 0; 386 | hasResult_ = false; 387 | } 388 | 389 | void CandidateWindow::setUseCursor(bool use) { 390 | useCursor_ = use; 391 | if(isVisible()) 392 | ::InvalidateRect(hwnd_, NULL, TRUE); 393 | } 394 | 395 | void CandidateWindow::paintItem(HDC hDC, int i, int x, int y) { 396 | RECT textRect = {x, y, 0, y + itemHeight_}; 397 | wchar_t selKey[] = L"?. "; 398 | selKey[0] = selKeys_[i]; 399 | textRect.right = textRect.left + selKeyWidth_; 400 | // FIXME: make the color of strings configurable. 401 | COLORREF selKeyColor = RGB(0, 0, 255); 402 | COLORREF oldColor = ::SetTextColor(hDC, selKeyColor); 403 | // paint the selection key 404 | ::ExtTextOut(hDC, textRect.left, textRect.top, ETO_OPAQUE, &textRect, selKey, 3, NULL); 405 | ::SetTextColor(hDC, oldColor); // restore text color 406 | 407 | // paint the candidate string 408 | wstring& item = items_.at(i); 409 | textRect.left += selKeyWidth_; 410 | textRect.right = textRect.left + textWidth_; 411 | // paint the candidate string 412 | ::ExtTextOut(hDC, textRect.left, textRect.top, ETO_OPAQUE, &textRect, item.c_str(), item.length(), NULL); 413 | 414 | if(useCursor_ && i == currentSel_) { // invert the selected item 415 | int left = textRect.left; // - selKeyWidth_; 416 | int top = textRect.top; 417 | int width = textRect.right - left; 418 | int height = itemHeight_; 419 | ::BitBlt(hDC, left, top, width, itemHeight_, hDC, left, top, NOTSRCCOPY); 420 | } 421 | } 422 | 423 | void CandidateWindow::itemRect(int i, RECT& rect) { 424 | int row, col; 425 | row = i / candPerRow_; 426 | col = i % candPerRow_; 427 | rect.left = margin_ + col * (selKeyWidth_ + textWidth_ + colSpacing_); 428 | rect.top = margin_ + row * (itemHeight_ + rowSpacing_); 429 | rect.right = rect.left + (selKeyWidth_ + textWidth_); 430 | rect.bottom = rect.top + itemHeight_; 431 | } 432 | 433 | 434 | } // namespace Ime 435 | -------------------------------------------------------------------------------- /CandidateWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_CANDIDATE_WINDOW_H 21 | #define IME_CANDIDATE_WINDOW_H 22 | 23 | #include "ImeWindow.h" 24 | #include 25 | #include 26 | 27 | namespace Ime { 28 | 29 | class TextService; 30 | class EditSession; 31 | class KeyEvent; 32 | 33 | // TODO: make the candidate window looks different in immersive mode 34 | class CandidateWindow : 35 | public ImeWindow, 36 | public ITfCandidateListUIElement { 37 | public: 38 | CandidateWindow(TextService* service, EditSession* session); 39 | 40 | // IUnknown 41 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 42 | STDMETHODIMP_(ULONG) AddRef(void); 43 | STDMETHODIMP_(ULONG) Release(void); 44 | 45 | // ITfUIElement 46 | STDMETHODIMP GetDescription(BSTR *pbstrDescription); 47 | STDMETHODIMP GetGUID(GUID *pguid); 48 | STDMETHODIMP Show(BOOL bShow); 49 | STDMETHODIMP IsShown(BOOL *pbShow); 50 | 51 | // ITfCandidateListUIElement 52 | STDMETHODIMP GetUpdatedFlags(DWORD *pdwFlags); 53 | STDMETHODIMP GetDocumentMgr(ITfDocumentMgr **ppdim); 54 | STDMETHODIMP GetCount(UINT *puCount); 55 | STDMETHODIMP GetSelection(UINT *puIndex); 56 | STDMETHODIMP GetString(UINT uIndex, BSTR *pstr); 57 | STDMETHODIMP GetPageIndex(UINT *puIndex, UINT uSize, UINT *puPageCnt); 58 | STDMETHODIMP SetPageIndex(UINT *puIndex, UINT uPageCnt); 59 | STDMETHODIMP GetCurrentPage(UINT *puPage); 60 | 61 | const std::vector& items() const { 62 | return items_; 63 | } 64 | 65 | void setItems(const std::vector& items, const std::vector& sekKeys) { 66 | items_ = items; 67 | selKeys_ = selKeys_; 68 | recalculateSize(); 69 | refresh(); 70 | } 71 | 72 | void add(std::wstring item, wchar_t selKey) { 73 | items_.push_back(item); 74 | selKeys_.push_back(selKey); 75 | } 76 | 77 | void clear(); 78 | 79 | int candPerRow() const { 80 | return candPerRow_; 81 | } 82 | void setCandPerRow(int n); 83 | 84 | virtual void recalculateSize(); 85 | 86 | bool filterKeyEvent(KeyEvent& keyEvent); 87 | 88 | int currentSel() const { 89 | return currentSel_; 90 | } 91 | void setCurrentSel(int sel); 92 | 93 | wchar_t currentSelKey() const { 94 | return selKeys_.at(currentSel_); 95 | } 96 | 97 | bool hasResult() const { 98 | return hasResult_; 99 | } 100 | 101 | bool useCursor() const { 102 | return useCursor_; 103 | } 104 | 105 | void setUseCursor(bool use); 106 | 107 | protected: 108 | LRESULT wndProc(UINT msg, WPARAM wp , LPARAM lp); 109 | void onPaint(WPARAM wp, LPARAM lp); 110 | void paintItem(HDC hDC, int i, int x, int y); 111 | void itemRect(int i, RECT& rect); 112 | 113 | protected: // COM object should not be deleted directly. calling Release() instead. 114 | ~CandidateWindow(void); 115 | 116 | private: 117 | ULONG refCount_; 118 | BOOL shown_; 119 | 120 | int selKeyWidth_; 121 | int textWidth_; 122 | int itemHeight_; 123 | int candPerRow_; 124 | int colSpacing_; 125 | int rowSpacing_; 126 | std::vector selKeys_; 127 | std::vector items_; 128 | int currentSel_; 129 | bool hasResult_; 130 | bool useCursor_; 131 | }; 132 | 133 | } 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /ComPtr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_COM_PTR_H 21 | #define IME_COM_PTR_H 22 | 23 | #include 24 | 25 | // ATL-indepdent smart pointers for COM objects 26 | // very similar to the ones provided by ATL (CComPtr & CComQIPtr). 27 | 28 | namespace Ime { 29 | 30 | // a smart pointer for COM interface/object 31 | // automatic AddRef() on copy and Release() on destruction. 32 | template 33 | class ComPtr { 34 | public: 35 | ComPtr(void): p_(nullptr) { 36 | } 37 | 38 | ComPtr(T* p, bool ref = true): p_(p) { 39 | if (p_ && ref) { 40 | p_->AddRef(); 41 | } 42 | } 43 | 44 | ComPtr(ComPtr&& other) noexcept : p_(other.p_) { 45 | other.p_ = nullptr; 46 | } 47 | 48 | ComPtr(const ComPtr& other): p_(other.p_) { 49 | if (p_) { 50 | p_->AddRef(); 51 | } 52 | } 53 | 54 | ~ComPtr(void) { 55 | if(p_) { 56 | p_->Release(); 57 | } 58 | } 59 | 60 | T& operator * () const { 61 | return *p_; 62 | } 63 | 64 | T** operator & () { 65 | return &p_; 66 | } 67 | 68 | T* operator-> () const { 69 | return p_; 70 | } 71 | 72 | operator T* () const { 73 | return p_; 74 | } 75 | 76 | bool operator !() const { 77 | return !p_; 78 | } 79 | 80 | bool operator == (T* p) const { 81 | return p == p_; 82 | } 83 | 84 | bool operator != (T* p) const { 85 | return p != p_; 86 | } 87 | 88 | bool operator < (T* p) const { 89 | return p_ < p; 90 | } 91 | 92 | ComPtr& operator = (ComPtr&& other) noexcept { 93 | p_ = other.p_; 94 | other.p_ = nullptr; 95 | return *this; 96 | } 97 | 98 | ComPtr& operator = (const ComPtr& other) { 99 | return operator = (other.p_); 100 | } 101 | 102 | ComPtr& operator = (T* p) { 103 | T* old = p_; 104 | p_ = p; 105 | if (p_) { 106 | p_->AddRef(); 107 | } 108 | if (old) { 109 | old->Release(); 110 | } 111 | return *this; 112 | } 113 | 114 | protected: 115 | T* p_; 116 | }; 117 | 118 | 119 | // a smart pointer for COM interface/object with automatic QueryInterface 120 | // QueryInterface() for interface T was done automatically on 121 | // assignment or construction. 122 | // automatic AddRef() on copy and Release() on destruction. 123 | 124 | template 125 | class ComQIPtr: public ComPtr { 126 | 127 | public: 128 | ComQIPtr(void): ComPtr() { 129 | } 130 | 131 | ComQIPtr(T* p): ComPtr(p) { 132 | } 133 | 134 | ComQIPtr(const ComQIPtr& other): ComPtr(other) { 135 | } 136 | 137 | ComQIPtr(ComQIPtr&& other) noexcept : ComPtr(std::move(other)) { 138 | } 139 | 140 | ComQIPtr(IUnknown* p) : ComPtr() { 141 | if(p) { 142 | p->QueryInterface(__uuidof(T), (void**)&p_); 143 | } 144 | } 145 | 146 | ComQIPtr& operator = (IUnknown* p) { 147 | ComPtr::operator = (nullptr); 148 | if(p) { 149 | p->QueryInterface(__uuidof(T), (void**)&p_); 150 | } 151 | return *this; 152 | } 153 | 154 | }; 155 | 156 | } 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /Dialog.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "Dialog.h" 21 | 22 | namespace Ime { 23 | 24 | Dialog::Dialog(void): 25 | Window() { 26 | } 27 | 28 | Dialog::~Dialog(void) { 29 | } 30 | 31 | bool Dialog::Create(HINSTANCE hinstance, UINT dialogId, HWND parent) { // modaless 32 | hwnd_ = ::CreateDialogParam(hinstance, LPCTSTR(dialogId), parent, _dlgProc, (LPARAM)this); 33 | return hwnd_ != NULL; 34 | } 35 | 36 | UINT Dialog::showModal(HINSTANCE hinstance, UINT dialogId, HWND parent) { // modal 37 | return ::DialogBoxParam(hinstance, LPCTSTR(dialogId), parent, _dlgProc, (LPARAM)this); 38 | } 39 | 40 | // static 41 | INT_PTR CALLBACK Dialog::_dlgProc(HWND hwnd , UINT msg, WPARAM wp , LPARAM lp) { 42 | Dialog* pThis = (Dialog*)hwndMap_[hwnd]; 43 | if(pThis) { 44 | INT_PTR result = pThis->wndProc(msg, wp, lp); 45 | if(msg == WM_NCDESTROY) 46 | hwndMap_.erase(hwnd); 47 | return result; 48 | } 49 | else { 50 | if(msg == WM_INITDIALOG) { 51 | Dialog* pThis = (Dialog*)lp; 52 | pThis->hwnd_ = hwnd; 53 | hwndMap_[hwnd] = pThis; 54 | return pThis->wndProc(msg, wp, lp); 55 | } 56 | } 57 | return FALSE; 58 | } 59 | 60 | // virtual 61 | LRESULT Dialog::wndProc(UINT msg, WPARAM wp , LPARAM lp) { 62 | switch(msg) { 63 | case WM_COMMAND: 64 | switch(wp) { 65 | case IDOK: 66 | onOK(); 67 | return TRUE; 68 | case IDCANCEL: 69 | onCancel(); 70 | return TRUE; 71 | } 72 | break; 73 | case WM_INITDIALOG: 74 | return onInitDialog(); 75 | } 76 | return FALSE; 77 | } 78 | 79 | // virtual 80 | bool Dialog::onInitDialog() { 81 | return true; 82 | } 83 | 84 | // virtual 85 | void Dialog::onOK() { 86 | endDialog(IDOK); 87 | } 88 | 89 | // virtual 90 | void Dialog::onCancel() { 91 | endDialog(IDCANCEL); 92 | } 93 | 94 | } // namespace Ime 95 | 96 | -------------------------------------------------------------------------------- /Dialog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_DIALOG_H 21 | #define IME_DIALOG_H 22 | 23 | #include "Window.h" 24 | #include 25 | 26 | namespace Ime { 27 | 28 | class Dialog : public Window { 29 | public: 30 | Dialog(void); 31 | virtual ~Dialog(void); 32 | 33 | bool Create(HINSTANCE hinstance, UINT dialogId, HWND parent = HWND_DESKTOP); // modaless 34 | UINT showModal(HINSTANCE hinstance, UINT dialogId, HWND parent = HWND_DESKTOP); // modal 35 | 36 | void endDialog(UINT result) { 37 | ::EndDialog(hwnd_, result); 38 | } 39 | 40 | protected: 41 | static INT_PTR CALLBACK _dlgProc(HWND hwnd , UINT msg, WPARAM wp , LPARAM lp); 42 | virtual LRESULT wndProc(UINT msg, WPARAM wp , LPARAM lp); 43 | 44 | virtual bool onInitDialog(); 45 | virtual void onOK(); 46 | virtual void onCancel(); 47 | 48 | private: 49 | }; 50 | 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /DisplayAttributeInfo.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "DisplayAttributeInfo.h" 21 | #include 22 | 23 | namespace Ime { 24 | 25 | DisplayAttributeInfo::DisplayAttributeInfo(const GUID& guid): 26 | atom_(0), 27 | guid_(guid), 28 | desc_(NULL), 29 | refCount_(1) { 30 | 31 | Reset(); 32 | } 33 | 34 | DisplayAttributeInfo::~DisplayAttributeInfo(void) { 35 | if(desc_) 36 | free(desc_); 37 | } 38 | 39 | 40 | // COM stuff 41 | 42 | // IUnknown 43 | STDMETHODIMP DisplayAttributeInfo::QueryInterface(REFIID riid, void **ppvObj) { 44 | if (ppvObj == NULL) 45 | return E_INVALIDARG; 46 | 47 | if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfDisplayAttributeInfo)) 48 | *ppvObj = (ITfDisplayAttributeInfo*)this; 49 | else 50 | *ppvObj = NULL; 51 | 52 | if(*ppvObj) { 53 | AddRef(); 54 | return S_OK; 55 | } 56 | return E_NOINTERFACE; 57 | } 58 | 59 | STDMETHODIMP_(ULONG) DisplayAttributeInfo::AddRef(void) { 60 | return ++refCount_; 61 | } 62 | 63 | STDMETHODIMP_(ULONG) DisplayAttributeInfo::Release(void) { 64 | assert(refCount_ > 0); 65 | const ULONG newCount = --refCount_; 66 | if(0 == refCount_) 67 | delete this; 68 | return newCount; 69 | } 70 | 71 | // ITfDisplayAttributeInfo 72 | STDMETHODIMP DisplayAttributeInfo::GetGUID(GUID *pguid) { 73 | *pguid = guid_; 74 | return S_OK; 75 | } 76 | 77 | STDMETHODIMP DisplayAttributeInfo::GetDescription(BSTR *pbstrDesc) { 78 | if(desc_) { 79 | *pbstrDesc = ::SysAllocString(desc_); 80 | return S_OK; 81 | } 82 | *pbstrDesc = NULL; 83 | return E_FAIL; 84 | } 85 | 86 | STDMETHODIMP DisplayAttributeInfo::GetAttributeInfo(TF_DISPLAYATTRIBUTE *ptfDisplayAttr) { 87 | *ptfDisplayAttr = attrib_; 88 | return S_OK; 89 | } 90 | 91 | STDMETHODIMP DisplayAttributeInfo::SetAttributeInfo(const TF_DISPLAYATTRIBUTE *ptfDisplayAttr) { 92 | attrib_ = *ptfDisplayAttr; 93 | return S_OK; 94 | } 95 | 96 | STDMETHODIMP DisplayAttributeInfo::Reset() { 97 | attrib_.bAttr = TF_ATTR_INPUT; 98 | attrib_.crBk.type = TF_CT_NONE; 99 | attrib_.crLine.type = TF_CT_NONE; 100 | attrib_.crText.type = TF_CT_NONE; 101 | attrib_.fBoldLine = FALSE; 102 | attrib_.lsStyle = TF_LS_NONE; 103 | return S_OK; 104 | } 105 | 106 | } // namespace Ime 107 | -------------------------------------------------------------------------------- /DisplayAttributeInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_DISPLAY_ATTRIBUTE_H 21 | #define IME_DISPLAY_ATTRIBUTE_H 22 | 23 | #include 24 | 25 | namespace Ime { 26 | 27 | class DisplayAttributeInfo : public ITfDisplayAttributeInfo { 28 | public: 29 | DisplayAttributeInfo(const GUID& guid); 30 | 31 | // public methods 32 | 33 | void setAtom(TfGuidAtom atom) { 34 | atom_ = atom; 35 | } 36 | 37 | TfGuidAtom atom() const { 38 | return atom_; 39 | } 40 | 41 | void setTextColor(COLORREF color) { 42 | attrib_.crText.type = TF_CT_COLORREF; 43 | attrib_.crText.cr = color; 44 | } 45 | 46 | void setTextColor(int index) { 47 | attrib_.crText.type = TF_CT_SYSCOLOR; 48 | attrib_.crText.nIndex = index; 49 | } 50 | 51 | void setBackgroundColor(COLORREF color) { 52 | attrib_.crBk.type = TF_CT_COLORREF; 53 | attrib_.crBk.cr = color; 54 | } 55 | 56 | void setBackgroundColor(int index) { 57 | attrib_.crBk.type = TF_CT_SYSCOLOR; 58 | attrib_.crBk.nIndex = index; 59 | } 60 | 61 | void setLineColor(COLORREF color) { 62 | attrib_.crLine.type = TF_CT_COLORREF; 63 | attrib_.crLine.cr = color; 64 | } 65 | 66 | void setLineColor(int index) { 67 | attrib_.crLine.type = TF_CT_SYSCOLOR; 68 | attrib_.crLine.nIndex = index; 69 | } 70 | 71 | void setLineStyle(TF_DA_LINESTYLE style) { 72 | attrib_.lsStyle = style; 73 | } 74 | 75 | void setLineBold(bool bold) { 76 | attrib_.fBoldLine = (BOOL)bold; 77 | } 78 | 79 | void setAttrInfo(TF_DA_ATTR_INFO attr) { 80 | attrib_.bAttr = attr; 81 | } 82 | 83 | void setDescription(wchar_t* desc) { 84 | if(desc_) 85 | free(desc_); 86 | desc_ = _wcsdup(desc); 87 | } 88 | 89 | const GUID& guid() const { 90 | return guid_; 91 | } 92 | 93 | // IUnknown 94 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 95 | STDMETHODIMP_(ULONG) AddRef(void); 96 | STDMETHODIMP_(ULONG) Release(void); 97 | 98 | // ITfDisplayAttributeInfo 99 | STDMETHODIMP GetGUID(GUID *pguid); 100 | STDMETHODIMP GetDescription(BSTR *pbstrDesc); 101 | STDMETHODIMP GetAttributeInfo(TF_DISPLAYATTRIBUTE *ptfDisplayAttr); 102 | STDMETHODIMP SetAttributeInfo(const TF_DISPLAYATTRIBUTE *ptfDisplayAttr); 103 | STDMETHODIMP Reset(); 104 | 105 | protected: // COM object should not be deleted directly. calling Release() instead. 106 | virtual ~DisplayAttributeInfo(void); 107 | 108 | private: 109 | int refCount_; 110 | TfGuidAtom atom_; 111 | GUID guid_; 112 | wchar_t* desc_; 113 | TF_DISPLAYATTRIBUTE attrib_; 114 | }; 115 | 116 | } 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /DisplayAttributeInfoEnum.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "DisplayAttributeInfoEnum.h" 21 | #include "DisplayAttributeProvider.h" 22 | #include "ImeModule.h" 23 | #include 24 | 25 | namespace Ime { 26 | 27 | DisplayAttributeInfoEnum::DisplayAttributeInfoEnum(DisplayAttributeProvider* provider): 28 | provider_(provider), 29 | refCount_(1) { 30 | std::list& displayAttrInfos = provider_->imeModule_->displayAttrInfos(); 31 | iterator_ = displayAttrInfos.begin(); 32 | } 33 | 34 | DisplayAttributeInfoEnum::DisplayAttributeInfoEnum(const DisplayAttributeInfoEnum& other): 35 | provider_(other.provider_), 36 | iterator_(other.iterator_) { 37 | } 38 | 39 | DisplayAttributeInfoEnum::~DisplayAttributeInfoEnum(void) { 40 | } 41 | 42 | // IUnknown 43 | STDMETHODIMP DisplayAttributeInfoEnum::QueryInterface(REFIID riid, void **ppvObj) { 44 | if (ppvObj == NULL) 45 | return E_INVALIDARG; 46 | 47 | if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumTfDisplayAttributeInfo)) 48 | *ppvObj = (IEnumTfDisplayAttributeInfo*)this; 49 | else 50 | *ppvObj = NULL; 51 | 52 | if(*ppvObj) { 53 | AddRef(); 54 | return S_OK; 55 | } 56 | return E_NOINTERFACE; 57 | } 58 | 59 | STDMETHODIMP_(ULONG) DisplayAttributeInfoEnum::AddRef(void) { 60 | return ++refCount_; 61 | } 62 | 63 | STDMETHODIMP_(ULONG) DisplayAttributeInfoEnum::Release(void) { 64 | assert(refCount_ > 0); 65 | const ULONG newCount = --refCount_; 66 | if (0 == refCount_) 67 | delete this; 68 | return newCount; 69 | } 70 | 71 | 72 | // IEnumTfDisplayAttributeInfo 73 | STDMETHODIMP DisplayAttributeInfoEnum::Clone(IEnumTfDisplayAttributeInfo **ppEnum) { 74 | *ppEnum = (IEnumTfDisplayAttributeInfo*)new DisplayAttributeInfoEnum(*this); 75 | return S_OK; 76 | } 77 | 78 | STDMETHODIMP DisplayAttributeInfoEnum::Next(ULONG ulCount, ITfDisplayAttributeInfo **rgInfo, ULONG *pcFetched) { 79 | ULONG i = 0; 80 | std::list& displayAttrInfos = provider_->imeModule_->displayAttrInfos(); 81 | for(; i < ulCount; ++i) { 82 | if(iterator_ != displayAttrInfos.end()) { 83 | DisplayAttributeInfo* info = *iterator_; 84 | info->AddRef(); 85 | rgInfo[i] = info; 86 | ++iterator_; 87 | } 88 | else 89 | break; 90 | } 91 | if(pcFetched) 92 | *pcFetched = i; 93 | return S_OK; 94 | } 95 | 96 | STDMETHODIMP DisplayAttributeInfoEnum::Reset() { 97 | std::list& displayAttrInfos = provider_->imeModule_->displayAttrInfos(); 98 | iterator_ = displayAttrInfos.begin(); 99 | return S_OK; 100 | } 101 | 102 | STDMETHODIMP DisplayAttributeInfoEnum::Skip(ULONG ulCount) { 103 | std::list& displayAttrInfos = provider_->imeModule_->displayAttrInfos(); 104 | if(iterator_ != displayAttrInfos.end()) 105 | ++iterator_; 106 | return S_OK; 107 | } 108 | 109 | } // namespace Ime 110 | 111 | -------------------------------------------------------------------------------- /DisplayAttributeInfoEnum.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_DISPLAY_ATTRIBUTE_INFO_ENUM_H 21 | #define IME_DISPLAY_ATTRIBUTE_INFO_ENUM_H 22 | 23 | #include 24 | #include 25 | #include "DisplayAttributeInfo.h" 26 | #include "ComPtr.h" 27 | 28 | namespace Ime { 29 | 30 | class DisplayAttributeProvider; 31 | 32 | class DisplayAttributeInfoEnum : public IEnumTfDisplayAttributeInfo { 33 | public: 34 | DisplayAttributeInfoEnum(DisplayAttributeProvider* provider); 35 | DisplayAttributeInfoEnum(const DisplayAttributeInfoEnum& other); 36 | 37 | // IUnknown 38 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 39 | STDMETHODIMP_(ULONG) AddRef(void); 40 | STDMETHODIMP_(ULONG) Release(void); 41 | 42 | // IEnumTfDisplayAttributeInfo 43 | STDMETHODIMP Clone(IEnumTfDisplayAttributeInfo **ppEnum); 44 | STDMETHODIMP Next(ULONG ulCount, ITfDisplayAttributeInfo **rgInfo, ULONG *pcFetched); 45 | STDMETHODIMP Reset(); 46 | STDMETHODIMP Skip(ULONG ulCount); 47 | 48 | protected: // COM object should not be deleted directly. calling Release() instead. 49 | virtual ~DisplayAttributeInfoEnum(void); 50 | 51 | private: 52 | int refCount_; 53 | std::list::iterator iterator_; 54 | ComPtr provider_; 55 | }; 56 | 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /DisplayAttributeProvider.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "DisplayAttributeProvider.h" 21 | #include "DisplayAttributeInfo.h" 22 | #include "DisplayAttributeInfoEnum.h" 23 | #include "ImeModule.h" 24 | #include 25 | 26 | using namespace std; 27 | 28 | namespace Ime { 29 | 30 | DisplayAttributeProvider::DisplayAttributeProvider(ImeModule* module): 31 | imeModule_(module), 32 | refCount_(1) { 33 | } 34 | 35 | DisplayAttributeProvider::~DisplayAttributeProvider(void) { 36 | } 37 | 38 | 39 | // COM stuff 40 | 41 | // IUnknown 42 | STDMETHODIMP DisplayAttributeProvider::QueryInterface(REFIID riid, void **ppvObj) { 43 | if (ppvObj == NULL) 44 | return E_INVALIDARG; 45 | if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfDisplayAttributeProvider)) 46 | *ppvObj = (ITfDisplayAttributeProvider*)this; 47 | else 48 | *ppvObj = NULL; 49 | 50 | if(*ppvObj) { 51 | AddRef(); 52 | return S_OK; 53 | } 54 | return E_NOINTERFACE; 55 | } 56 | 57 | // IUnknown implementation 58 | STDMETHODIMP_(ULONG) DisplayAttributeProvider::AddRef(void) { 59 | return ++refCount_; 60 | } 61 | 62 | STDMETHODIMP_(ULONG) DisplayAttributeProvider::Release(void) { 63 | assert(refCount_ > 0); 64 | const ULONG newCount = --refCount_; 65 | if (0 == refCount_) 66 | delete this; 67 | return newCount; 68 | } 69 | 70 | 71 | // ITfDisplayAttributeProvider 72 | STDMETHODIMP DisplayAttributeProvider::EnumDisplayAttributeInfo(IEnumTfDisplayAttributeInfo **ppEnum) { 73 | *ppEnum = (IEnumTfDisplayAttributeInfo*)new DisplayAttributeInfoEnum(this); 74 | return S_OK; 75 | } 76 | 77 | STDMETHODIMP DisplayAttributeProvider::GetDisplayAttributeInfo(REFGUID guidInfo, ITfDisplayAttributeInfo **ppInfo) { 78 | list& displayAttrInfos = imeModule_->displayAttrInfos(); 79 | list::iterator it; 80 | for(it = displayAttrInfos.begin(); it != displayAttrInfos.end(); ++it) { 81 | DisplayAttributeInfo* info = *it; 82 | if(::IsEqualGUID(info->guid(), guidInfo)) { 83 | *ppInfo = info; 84 | info->AddRef(); 85 | return S_OK; 86 | } 87 | } 88 | return E_INVALIDARG; 89 | } 90 | 91 | } // namespace Ime 92 | -------------------------------------------------------------------------------- /DisplayAttributeProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_DISPLAY_ATTRIBUTE_PROVIDER_H 21 | #define IME_DISPLAY_ATTRIBUTE_PROVIDER_H 22 | 23 | #include 24 | #include 25 | #include "ComPtr.h" 26 | 27 | namespace Ime { 28 | 29 | class ImeModule; 30 | class DisplayAttributeInfo; 31 | 32 | class DisplayAttributeProvider : public ITfDisplayAttributeProvider { 33 | public: 34 | 35 | friend class DisplayAttributeInfoEnum; 36 | 37 | DisplayAttributeProvider(ImeModule* module); 38 | 39 | // COM stuff 40 | 41 | // IUnknown 42 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 43 | STDMETHODIMP_(ULONG) AddRef(void); 44 | STDMETHODIMP_(ULONG) Release(void); 45 | 46 | // ITfDisplayAttributeProvider 47 | STDMETHODIMP EnumDisplayAttributeInfo(IEnumTfDisplayAttributeInfo **ppEnum); 48 | STDMETHODIMP GetDisplayAttributeInfo(REFGUID guidInfo, ITfDisplayAttributeInfo **ppInfo); 49 | 50 | protected: // COM object should not be deleted directly. calling Release() instead. 51 | virtual ~DisplayAttributeProvider(void); 52 | 53 | private: 54 | int refCount_; 55 | ComPtr imeModule_; 56 | }; 57 | 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /DrawUtils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "DrawUtils.h" 21 | 22 | void FillSolidRect(HDC dc, LPRECT rc, COLORREF color) { 23 | SetBkColor(dc, color); 24 | ::ExtTextOut(dc, 0, 0, ETO_OPAQUE, rc, NULL, 0, NULL); 25 | } 26 | 27 | void FillSolidRect(HDC dc, int l, int t, int w, int h, COLORREF color) { 28 | RECT rc; 29 | rc.left = l; 30 | rc.top = t; 31 | rc.right = rc.left + w; 32 | rc.bottom = rc.top + h; 33 | SetBkColor(dc, color); 34 | ::ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); 35 | } 36 | 37 | void Draw3DBorder(HDC hdc, LPRECT rc, COLORREF light, COLORREF dark, int width) { 38 | MoveToEx(hdc, rc->left, rc->bottom, NULL); 39 | 40 | HPEN light_pen = CreatePen(PS_SOLID|PS_INSIDEFRAME, width, light); 41 | HGDIOBJ oldPen = SelectObject(hdc, light_pen); 42 | LineTo(hdc, rc->left, rc->top); 43 | LineTo(hdc, rc->right-width, rc->top); 44 | SelectObject(hdc, oldPen); 45 | DeleteObject(light_pen); 46 | 47 | HPEN dark_pen = CreatePen(PS_SOLID|PS_INSIDEFRAME, width, dark); 48 | oldPen = SelectObject(hdc, dark_pen); 49 | LineTo(hdc, rc->right-width, rc->bottom-width); 50 | LineTo(hdc, rc->left, rc->bottom-width); 51 | DeleteObject(dark_pen); 52 | SelectObject(hdc, oldPen); 53 | } 54 | 55 | void DrawBitmap(HDC dc, HBITMAP bmp, int x, int y, int w, int h, int srcx, int srcy) { 56 | HDC memdc = CreateCompatibleDC(dc); 57 | HGDIOBJ oldobj = SelectObject(memdc, bmp); 58 | BitBlt(dc, x, y, w, h, memdc, srcx, srcy, SRCCOPY); 59 | SelectObject(memdc, oldobj); 60 | DeleteDC(memdc); 61 | } -------------------------------------------------------------------------------- /DrawUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_DRAW_UTIL_H 21 | #define IME_DRAW_UTIL_H 22 | 23 | #include 24 | 25 | void FillSolidRect( HDC dc, LPRECT rc, COLORREF color ); 26 | void FillSolidRect( HDC dc, int l, int t, int w, int h, COLORREF color ); 27 | void Draw3DBorder(HDC hdc, LPRECT rc, COLORREF light, COLORREF dark, int width = 1); 28 | void DrawBitmap(HDC dc, HBITMAP bmp, int x, int y, int w, int h, int srcx, int srcy ); 29 | 30 | #endif -------------------------------------------------------------------------------- /EditSession.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "EditSession.h" 21 | #include "TextService.h" 22 | #include 23 | 24 | namespace Ime { 25 | 26 | EditSession::EditSession(TextService* service, ITfContext* context): 27 | textService_(service), 28 | context_(context), 29 | editCookie_(0), 30 | refCount_(1) { 31 | if(textService_) 32 | textService_->AddRef(); 33 | if(context_) 34 | context_->AddRef(); 35 | } 36 | 37 | EditSession::~EditSession(void) { 38 | if(textService_) 39 | textService_->Release(); 40 | if(context_) 41 | context_->Release(); 42 | } 43 | 44 | // COM stuff 45 | 46 | // IUnknown 47 | STDMETHODIMP EditSession::QueryInterface(REFIID riid, void **ppvObj) { 48 | if (ppvObj == NULL) 49 | return E_INVALIDARG; 50 | 51 | if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfEditSession)) 52 | *ppvObj = (ITfEditSession*)this; 53 | else 54 | *ppvObj = NULL; 55 | 56 | if(*ppvObj) { 57 | AddRef(); 58 | return S_OK; 59 | } 60 | return E_NOINTERFACE; 61 | } 62 | 63 | // IUnknown implementation 64 | STDMETHODIMP_(ULONG) EditSession::AddRef(void) { 65 | return ++refCount_; 66 | } 67 | 68 | STDMETHODIMP_(ULONG) EditSession::Release(void) { 69 | assert(refCount_ > 0); 70 | const ULONG newCount = --refCount_; 71 | if (0 == refCount_) 72 | delete this; 73 | return newCount; 74 | } 75 | 76 | STDMETHODIMP EditSession::DoEditSession(TfEditCookie ec) { 77 | editCookie_ = ec; 78 | return S_OK; 79 | } 80 | 81 | } // namespace Ime 82 | -------------------------------------------------------------------------------- /EditSession.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_EDIT_SESSION_H 21 | #define IME_EDIT_SESSION_H 22 | 23 | #include 24 | 25 | namespace Ime { 26 | 27 | class TextService; 28 | 29 | class EditSession: public ITfEditSession { 30 | public: 31 | EditSession(TextService* service, ITfContext* context); 32 | 33 | TextService* textService() { 34 | return textService_; 35 | } 36 | 37 | ITfContext* context() { 38 | return context_; 39 | } 40 | 41 | TfEditCookie editCookie() { 42 | return editCookie_; 43 | } 44 | 45 | // COM stuff 46 | 47 | // IUnknown 48 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 49 | STDMETHODIMP_(ULONG) AddRef(void); 50 | STDMETHODIMP_(ULONG) Release(void); 51 | 52 | // ITfEditSession 53 | virtual STDMETHODIMP DoEditSession(TfEditCookie ec); 54 | 55 | protected: // COM object should not be deleted directly. calling Release() instead. 56 | virtual ~EditSession(void); 57 | 58 | protected: 59 | TextService* textService_; 60 | ITfContext* context_; 61 | 62 | private: 63 | long refCount_; 64 | TfEditCookie editCookie_; 65 | }; 66 | 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /ImeEngine.cpp: -------------------------------------------------------------------------------- 1 | #include "ImeEngine.h" 2 | #include "EditSession.h" 3 | #include "CandidateWindow.h" 4 | #include "LangBarButton.h" 5 | #include "DisplayAttributeInfoEnum.h" 6 | #include "ImeModule.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | 14 | namespace Ime { 15 | 16 | ImeEngine::ImeEngine(ImeModule* module): 17 | module_(module), 18 | threadMgr_(NULL), 19 | clientId_(TF_CLIENTID_NULL), 20 | activateFlags_(0), 21 | isKeyboardOpened_(false), 22 | threadMgrEventSinkCookie_(TF_INVALID_COOKIE), 23 | textEditSinkCookie_(TF_INVALID_COOKIE), 24 | compositionSinkCookie_(TF_INVALID_COOKIE), 25 | keyboardOpenEventSinkCookie_(TF_INVALID_COOKIE), 26 | globalCompartmentEventSinkCookie_(TF_INVALID_COOKIE), 27 | langBarSinkCookie_(TF_INVALID_COOKIE), 28 | composition_(NULL), 29 | candidateWindow_(NULL) { 30 | 31 | addCompartmentMonitor(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, false); 32 | } 33 | 34 | ImeEngine::~ImeEngine() { 35 | if(candidateWindow_) 36 | delete candidateWindow_; 37 | 38 | // This should only happen in rare cases 39 | if(!compartmentMonitors_.empty()) { 40 | vector::iterator it; 41 | for(it = compartmentMonitors_.begin(); it != compartmentMonitors_.end(); ++it) { 42 | ComQIPtr source; 43 | if(it->isGlobal) 44 | source = globalCompartment(it->guid); 45 | else 46 | source = threadCompartment(it->guid); 47 | source->UnadviseSink(it->cookie); 48 | } 49 | } 50 | 51 | if(!langBarButtons_.empty()) { 52 | for(vector::iterator it = langBarButtons_.begin(); it != langBarButtons_.end(); ++it) { 53 | LangBarButton* button = *it; 54 | button->Release(); 55 | } 56 | } 57 | if(langBarMgr_) { 58 | langBarMgr_->UnadviseEventSink(langBarSinkCookie_); 59 | } 60 | langBarMgr_ = NULL; 61 | } 62 | 63 | 64 | // public methods 65 | 66 | ImeModule* ImeEngine::imeModule() const { 67 | return module_; 68 | } 69 | 70 | ITfThreadMgr* ImeEngine::threadMgr() const { 71 | return threadMgr_; 72 | } 73 | 74 | TfClientId ImeEngine::clientId() const { 75 | return clientId_; 76 | } 77 | 78 | 79 | // language bar 80 | DWORD ImeEngine::langBarStatus() const { 81 | if(langBarMgr_) { 82 | DWORD status; 83 | if(langBarMgr_->GetShowFloatingStatus(&status) == S_OK) { 84 | return status; 85 | } 86 | } 87 | return 0; 88 | } 89 | 90 | void ImeEngine::addButton(LangBarButton* button) { 91 | if(button) { 92 | langBarButtons_.push_back(button); 93 | button->AddRef(); 94 | if(isActivated()) { 95 | ITfLangBarItemMgr* langBarItemMgr; 96 | if(threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, (void**)&langBarItemMgr) == S_OK) { 97 | langBarItemMgr->AddItem(button); 98 | langBarItemMgr->Release(); 99 | } 100 | } 101 | } 102 | } 103 | 104 | void ImeEngine::removeButton(LangBarButton* button) { 105 | if(button) { 106 | vector::iterator it; 107 | it = find(langBarButtons_.begin(), langBarButtons_.end(), button); 108 | if(it != langBarButtons_.end()) { 109 | if(isActivated()) { 110 | ITfLangBarItemMgr* langBarItemMgr; 111 | if(threadMgr_->QueryInterface(IID_ITfLangBarItemMgr, (void**)&langBarItemMgr) == S_OK) { 112 | langBarItemMgr->RemoveItem(button); 113 | langBarItemMgr->Release(); 114 | } 115 | } 116 | button->Release(); 117 | langBarButtons_.erase(it); 118 | } 119 | } 120 | } 121 | 122 | // preserved key 123 | void ImeEngine::addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid) { 124 | PreservedKey preservedKey; 125 | preservedKey.guid = guid; 126 | preservedKey.uVKey = keyCode; 127 | preservedKey.uModifiers = modifiers; 128 | preservedKeys_.push_back(preservedKey); 129 | if(threadMgr_) { // our text service is activated 130 | ITfKeystrokeMgr *keystrokeMgr; 131 | if (threadMgr_->QueryInterface(IID_ITfKeystrokeMgr, (void **)&keystrokeMgr) == S_OK) { 132 | keystrokeMgr->PreserveKey(clientId_, guid, &preservedKey, NULL, 0); 133 | keystrokeMgr->Release(); 134 | } 135 | } 136 | } 137 | 138 | void ImeEngine::removePreservedKey(const GUID& guid) { 139 | vector::iterator it; 140 | for(it = preservedKeys_.begin(); it != preservedKeys_.end(); ++it) { 141 | PreservedKey& preservedKey = *it; 142 | if(::IsEqualIID(preservedKey.guid, guid)) { 143 | if(threadMgr_) { // our text service is activated 144 | ITfKeystrokeMgr *keystrokeMgr; 145 | if (threadMgr_->QueryInterface(IID_ITfKeystrokeMgr, (void **)&keystrokeMgr) == S_OK) { 146 | keystrokeMgr->UnpreserveKey(preservedKey.guid, &preservedKey); 147 | keystrokeMgr->Release(); 148 | } 149 | } 150 | preservedKeys_.erase(it); 151 | break; 152 | } 153 | } 154 | } 155 | 156 | 157 | // text composition 158 | 159 | bool ImeEngine::isComposing() { 160 | return (composition_ != NULL); 161 | } 162 | 163 | // is keyboard disabled for the context (NULL means current context) 164 | bool ImeEngine::isKeyboardDisabled(ITfContext* context) { 165 | return (contextCompartmentValue(GUID_COMPARTMENT_KEYBOARD_DISABLED, context) 166 | || contextCompartmentValue(GUID_COMPARTMENT_EMPTYCONTEXT, context)); 167 | } 168 | 169 | // is keyboard opened for the whole thread 170 | bool ImeEngine::isKeyboardOpened() { 171 | return isKeyboardOpened_; 172 | } 173 | 174 | void ImeEngine::setKeyboardOpen(bool open) { 175 | if(open != isKeyboardOpened_) { 176 | setThreadCompartmentValue(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, (DWORD)open); 177 | } 178 | } 179 | 180 | 181 | // check if current insertion point is in the range of composition. 182 | // if not in range, insertion is now allowed 183 | bool ImeEngine::isInsertionAllowed(EditSession* session) { 184 | TfEditCookie cookie = session->editCookie(); 185 | TF_SELECTION selection; 186 | ULONG selectionNum; 187 | if(isComposing()) { 188 | if(session->context()->GetSelection(cookie, TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { 189 | ITfRange* compositionRange; 190 | if(composition_->GetRange(&compositionRange) == S_OK) { 191 | bool allowed = false; 192 | // check if current selection is covered by composition range 193 | LONG compareResult1; 194 | LONG compareResult2; 195 | if(selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_START, &compareResult1) == S_OK 196 | && selection.range->CompareStart(cookie, compositionRange, TF_ANCHOR_END, &compareResult2) == S_OK) { 197 | if(compareResult1 == -1 && compareResult2 == +1) 198 | allowed = true; 199 | } 200 | compositionRange->Release(); 201 | } 202 | if(selection.range) 203 | selection.range->Release(); 204 | } 205 | } 206 | return false; 207 | } 208 | 209 | void ImeEngine::startComposition(ITfContext* context) { 210 | assert(context); 211 | HRESULT sessionResult; 212 | StartCompositionEditSession* session = new StartCompositionEditSession(this, context); 213 | context->RequestEditSession(clientId_, session, TF_ES_SYNC|TF_ES_READWRITE, &sessionResult); 214 | session->Release(); 215 | } 216 | 217 | void ImeEngine::endComposition(ITfContext* context) { 218 | assert(context); 219 | HRESULT sessionResult; 220 | EndCompositionEditSession* session = new EndCompositionEditSession(this, context); 221 | context->RequestEditSession(clientId_, session, TF_ES_SYNC|TF_ES_READWRITE, &sessionResult); 222 | session->Release(); 223 | } 224 | 225 | void ImeEngine::setCompositionString(EditSession* session, const wchar_t* str, int len) { 226 | ITfContext* context = session->context(); 227 | if(context) { 228 | TfEditCookie editCookie = session->editCookie(); 229 | TF_SELECTION selection; 230 | ULONG selectionNum; 231 | // get current selection/insertion point 232 | if(context->GetSelection(editCookie, TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { 233 | ComPtr compositionRange; 234 | if(composition_->GetRange(&compositionRange) == S_OK) { 235 | bool selPosInComposition = true; 236 | // if current insertion point is not covered by composition, we cannot insert text here. 237 | if(selPosInComposition) { 238 | // replace context of composion area with the new string. 239 | compositionRange->SetText(editCookie, TF_ST_CORRECTION, str, len); 240 | 241 | // move the insertion point to end of the composition string 242 | selection.range->Collapse(editCookie, TF_ANCHOR_END); 243 | context->SetSelection(editCookie, 1, &selection); 244 | } 245 | 246 | // set display attribute to the composition range 247 | ComPtr dispAttrProp; 248 | if(context->GetProperty(GUID_PROP_ATTRIBUTE, &dispAttrProp) == S_OK) { 249 | VARIANT val; 250 | val.vt = VT_I4; 251 | val.lVal = module_->inputAttrib()->atom(); 252 | dispAttrProp->SetValue(editCookie, compositionRange, &val); 253 | } 254 | } 255 | selection.range->Release(); 256 | } 257 | } 258 | } 259 | 260 | // set cursor position in the composition area 261 | // 0 means the start pos of composition string 262 | void ImeEngine::setCompositionCursor(EditSession* session, int pos) { 263 | TF_SELECTION selection; 264 | ULONG selectionNum; 265 | // get current selection 266 | if(session->context()->GetSelection(session->editCookie(), TF_DEFAULT_SELECTION, 1, &selection, &selectionNum) == S_OK) { 267 | // get composition range 268 | ITfRange* compositionRange; 269 | if(composition_->GetRange(&compositionRange) == S_OK) { 270 | // make the start of selectionRange the same as that of compositionRange 271 | selection.range->ShiftStartToRange(session->editCookie(), compositionRange, TF_ANCHOR_START); 272 | selection.range->Collapse(session->editCookie(), TF_ANCHOR_START); 273 | LONG moved; 274 | // move the start anchor to right 275 | selection.range->ShiftStart(session->editCookie(), (LONG)pos, &moved, NULL); 276 | selection.range->Collapse(session->editCookie(), TF_ANCHOR_START); 277 | // set the new selection to the context 278 | session->context()->SetSelection(session->editCookie(), 1, &selection); 279 | compositionRange->Release(); 280 | } 281 | selection.range->Release(); 282 | } 283 | } 284 | 285 | // compartment handling 286 | ComPtr ImeEngine::globalCompartment(const GUID& key) { 287 | if(threadMgr_) { 288 | ComQIPtr compartmentMgr; 289 | if(threadMgr_->GetGlobalCompartment(&compartmentMgr) == S_OK) { 290 | ComPtr compartment; 291 | compartmentMgr->GetCompartment(key, &compartment); 292 | return compartment; 293 | } 294 | } 295 | return NULL; 296 | } 297 | 298 | ComPtr ImeEngine::threadCompartment(const GUID& key) { 299 | if(threadMgr_) { 300 | ComQIPtr compartmentMgr = threadMgr_; 301 | if(compartmentMgr) { 302 | ComPtr compartment; 303 | compartmentMgr->GetCompartment(key, &compartment); 304 | return compartment; 305 | } 306 | } 307 | return NULL; 308 | } 309 | 310 | ComPtr ImeEngine::contextCompartment(const GUID& key, ITfContext* context) { 311 | ITfContext* curContext = NULL; 312 | if(!context) { 313 | curContext = currentContext(); 314 | context = curContext; 315 | } 316 | if(context) { 317 | ComQIPtr compartmentMgr = context; 318 | if(compartmentMgr) { 319 | ComPtr compartment; 320 | compartmentMgr->GetCompartment(key, &compartment); 321 | return compartment; 322 | } 323 | } 324 | if(curContext) 325 | curContext->Release(); 326 | return NULL; 327 | } 328 | 329 | 330 | DWORD ImeEngine::globalCompartmentValue(const GUID& key) { 331 | ComPtr compartment = globalCompartment(key); 332 | if(compartment) { 333 | VARIANT var; 334 | if(compartment->GetValue(&var) == S_OK && var.vt == VT_I4) { 335 | return (DWORD)var.lVal; 336 | } 337 | } 338 | return 0; 339 | } 340 | 341 | DWORD ImeEngine::threadCompartmentValue(const GUID& key) { 342 | ComPtr compartment = threadCompartment(key); 343 | if(compartment) { 344 | VARIANT var; 345 | ::VariantInit(&var); 346 | HRESULT r = compartment->GetValue(&var); 347 | if(r == S_OK) { 348 | if(var.vt == VT_I4) 349 | return (DWORD)var.lVal; 350 | } 351 | } 352 | return 0; 353 | } 354 | 355 | DWORD ImeEngine::contextCompartmentValue(const GUID& key, ITfContext* context) { 356 | ComPtr compartment = contextCompartment(key, context); 357 | if(compartment) { 358 | VARIANT var; 359 | if(compartment->GetValue(&var) == S_OK && var.vt == VT_I4) { 360 | return (DWORD)var.lVal; 361 | } 362 | } 363 | return 0; 364 | } 365 | 366 | void ImeEngine::setGlobalCompartmentValue(const GUID& key, DWORD value) { 367 | if(threadMgr_) { 368 | ComPtr compartment = globalCompartment(key); 369 | if(compartment) { 370 | VARIANT var; 371 | ::VariantInit(&var); 372 | var.vt = VT_I4; 373 | var.lVal = value; 374 | compartment->SetValue(clientId_, &var); 375 | } 376 | } 377 | else { 378 | // if we don't have a thread manager (this is possible when we try to set 379 | // a global compartment value while the text service is not activated) 380 | ComPtr threadMgr; 381 | if(::CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&threadMgr) == S_OK) { 382 | if(threadMgr) { 383 | ComPtr compartmentMgr; 384 | if(threadMgr->GetGlobalCompartment(&compartmentMgr) == S_OK) { 385 | ComPtr compartment; 386 | if(compartmentMgr->GetCompartment(key, &compartment) == S_OK && compartment) { 387 | TfClientId id; 388 | if(threadMgr->Activate(&id) == S_OK) { 389 | VARIANT var; 390 | ::VariantInit(&var); 391 | var.vt = VT_I4; 392 | var.lVal = value; 393 | compartment->SetValue(id, &var); 394 | threadMgr->Deactivate(); 395 | } 396 | } 397 | } 398 | } 399 | } 400 | } 401 | } 402 | 403 | void ImeEngine::setThreadCompartmentValue(const GUID& key, DWORD value) { 404 | ComPtr compartment = threadCompartment(key); 405 | if(compartment) { 406 | VARIANT var; 407 | ::VariantInit(&var); 408 | var.vt = VT_I4; 409 | var.lVal = value; 410 | compartment->SetValue(clientId_, &var); 411 | } 412 | } 413 | 414 | void ImeEngine::setContextCompartmentValue(const GUID& key, DWORD value, ITfContext* context) { 415 | ComPtr compartment = contextCompartment(key, context); 416 | if(compartment) { 417 | VARIANT var; 418 | ::VariantInit(&var); 419 | var.vt = VT_I4; 420 | var.lVal = value; 421 | compartment->SetValue(clientId_, &var); 422 | } 423 | } 424 | 425 | 426 | void ImeEngine::addCompartmentMonitor(const GUID key, bool isGlobal) { 427 | CompartmentMonitor monitor; 428 | monitor.guid = key; 429 | monitor.cookie = 0; 430 | monitor.isGlobal = isGlobal; 431 | // if the text service is activated 432 | if(threadMgr_) { 433 | ComQIPtr source; 434 | if(isGlobal) 435 | source = globalCompartment(key); 436 | else 437 | source = threadCompartment(key); 438 | if(source) { 439 | source->AdviseSink(IID_ITfCompartmentEventSink, (ITfCompartmentEventSink*)this, &monitor.cookie); 440 | } 441 | } 442 | compartmentMonitors_.push_back(monitor); 443 | } 444 | 445 | void ImeEngine::removeCompartmentMonitor(const GUID key) { 446 | vector::iterator it; 447 | it = find(compartmentMonitors_.begin(), compartmentMonitors_.end(), key); 448 | if(it != compartmentMonitors_.end()) { 449 | if(threadMgr_) { 450 | ComQIPtr source; 451 | if(it->isGlobal) 452 | source = globalCompartment(key); 453 | else 454 | source = threadCompartment(key); 455 | source->UnadviseSink(it->cookie); 456 | } 457 | compartmentMonitors_.erase(it); 458 | } 459 | } 460 | 461 | 462 | // virtual 463 | void ImeEngine::onActivate() { 464 | } 465 | 466 | // virtual 467 | void ImeEngine::onDeactivate() { 468 | } 469 | 470 | // virtual 471 | bool ImeEngine::filterKeyDown(KeyEvent& keyEvent) { 472 | return false; 473 | } 474 | 475 | // virtual 476 | bool ImeEngine::onKeyDown(KeyEvent& keyEvent, EditSession* session) { 477 | return false; 478 | } 479 | 480 | // virtual 481 | bool ImeEngine::filterKeyUp(KeyEvent& keyEvent) { 482 | return false; 483 | } 484 | 485 | // virtual 486 | bool ImeEngine::onKeyUp(KeyEvent& keyEvent, EditSession* session) { 487 | return false; 488 | } 489 | 490 | // virtual 491 | bool ImeEngine::onPreservedKey(const GUID& guid) { 492 | return false; 493 | } 494 | 495 | // virtual 496 | void ImeEngine::onSetFocus() { 497 | } 498 | 499 | // virtual 500 | void ImeEngine::onKillFocus() { 501 | } 502 | 503 | bool ImeEngine::onCommand(UINT id, CommandType type) { 504 | return false; 505 | } 506 | 507 | // virtual 508 | void ImeEngine::onCompartmentChanged(const GUID& key) { 509 | // keyboard status changed, this is threadMgr specific 510 | // See explanations on TSF aware blog: 511 | // http://blogs.msdn.com/b/tsfaware/archive/2007/05/30/what-is-a-keyboard.aspx 512 | if(::IsEqualGUID(key, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) { 513 | isKeyboardOpened_ = threadCompartmentValue(key) ? true : false; 514 | onKeyboardStatusChanged(isKeyboardOpened_); 515 | } 516 | } 517 | 518 | // virtual 519 | void ImeEngine::onLangBarStatusChanged(int newStatus) { 520 | } 521 | 522 | // called when the keyboard is opened or closed 523 | // virtual 524 | void ImeEngine::onKeyboardStatusChanged(bool opened) { 525 | } 526 | 527 | // called just before current composition is terminated for doing cleanup. 528 | // if forced is true, the composition is terminated by others, such as 529 | // the input focus is grabbed by another application. 530 | // if forced is false, the composition is terminated gracefully by endComposition(). 531 | // virtual 532 | void ImeEngine::onCompositionTerminated(bool forced) { 533 | } 534 | 535 | } 536 | -------------------------------------------------------------------------------- /ImeEngine.h: -------------------------------------------------------------------------------- 1 | #ifndef IME_IME_ENGINE_H 2 | #define IME_IME_ENGINE_H 3 | 4 | #include "libIME.h" 5 | #include 6 | #include "EditSession.h" 7 | #include "KeyEvent.h" 8 | #include "ComPtr.h" 9 | 10 | #include 11 | #include 12 | 13 | // for Windows 8 support 14 | #ifndef TF_TMF_IMMERSIVEMODE // this is defined in Win 8 SDK 15 | #define TF_TMF_IMMERSIVEMODE 0x40000000 16 | #endif 17 | 18 | namespace Ime { 19 | 20 | class ImeModule; 21 | class CandidateWindow; 22 | class LangBarButton; 23 | 24 | class ImeEngine { 25 | public: 26 | enum CommandType { // used in onCommand() 27 | COMMAND_LEFT_CLICK, 28 | COMMAND_RIGHT_CLICK, 29 | COMMAND_MENU 30 | }; 31 | 32 | ImeEngine(ImeModule* module); 33 | virtual ~ImeEngine(); 34 | 35 | // public methods 36 | ImeModule* imeModule() const; 37 | 38 | ITfThreadMgr* threadMgr() const; 39 | 40 | TfClientId clientId() const; 41 | 42 | ITfContext* currentContext(); 43 | 44 | bool isActivated() const { 45 | return (threadMgr() != NULL); 46 | } 47 | 48 | DWORD activateFlags() const { 49 | return activateFlags_; 50 | } 51 | 52 | // running in Windows 8 app mode 53 | bool isImmersive() const { 54 | return (activateFlags_ & TF_TMF_IMMERSIVEMODE) != 0; 55 | } 56 | 57 | DWORD langBarStatus() const; 58 | 59 | // language bar buttons 60 | void addButton(LangBarButton* button); 61 | void removeButton(LangBarButton* button); 62 | 63 | // preserved keys 64 | void addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid); 65 | void removePreservedKey(const GUID& guid); 66 | 67 | // text composition handling 68 | bool isComposing(); 69 | 70 | // is keyboard disabled for the context (NULL means current context) 71 | bool isKeyboardDisabled(ITfContext* context = NULL); 72 | 73 | // is keyboard opened for the whole thread 74 | bool isKeyboardOpened(); 75 | void setKeyboardOpen(bool open); 76 | 77 | bool isInsertionAllowed(EditSession* session); 78 | void startComposition(ITfContext* context); 79 | void endComposition(ITfContext* context); 80 | bool compositionRect(EditSession* session, RECT* rect); 81 | bool selectionRect(EditSession* session, RECT* rect); 82 | HWND compositionWindow(EditSession* session); 83 | 84 | void setCompositionString(EditSession* session, const wchar_t* str, int len); 85 | void setCompositionCursor(EditSession* session, int pos); 86 | 87 | // compartment handling 88 | ComPtr globalCompartment(const GUID& key); 89 | ComPtr threadCompartment(const GUID& key); 90 | ComPtr contextCompartment(const GUID& key, ITfContext* context = NULL); 91 | 92 | DWORD globalCompartmentValue(const GUID& key); 93 | DWORD threadCompartmentValue(const GUID& key); 94 | DWORD contextCompartmentValue(const GUID& key, ITfContext* context = NULL); 95 | 96 | void setGlobalCompartmentValue(const GUID& key, DWORD value); 97 | void setThreadCompartmentValue(const GUID& key, DWORD value); 98 | void setContextCompartmentValue(const GUID& key, DWORD value, ITfContext* context = NULL); 99 | 100 | // manage sinks to global or thread compartment (context specific compartment is not used) 101 | void addCompartmentMonitor(const GUID key, bool isGlobal = false); 102 | void removeCompartmentMonitor(const GUID key); 103 | 104 | // virtual functions that IME implementors may need to override 105 | virtual void onActivate(); 106 | virtual void onDeactivate(); 107 | 108 | virtual void onSetFocus(); 109 | virtual void onKillFocus(); 110 | 111 | virtual bool filterKeyDown(KeyEvent& keyEvent); 112 | virtual bool onKeyDown(KeyEvent& keyEvent, EditSession* session); 113 | 114 | virtual bool filterKeyUp(KeyEvent& keyEvent); 115 | virtual bool onKeyUp(KeyEvent& keyEvent, EditSession* session); 116 | 117 | virtual bool onPreservedKey(const GUID& guid); 118 | 119 | // called when a language button or menu item is clicked 120 | virtual bool onCommand(UINT id, CommandType type); 121 | 122 | // called when a value in the global or thread compartment changed. 123 | virtual void onCompartmentChanged(const GUID& key); 124 | 125 | virtual void onLangBarStatusChanged(int newStatus); 126 | 127 | // called when the keyboard is opened or closed 128 | virtual void onKeyboardStatusChanged(bool opened); 129 | 130 | // called just before current composition is terminated for doing cleanup. 131 | // if forced is true, the composition is terminated by others, such as 132 | // the input focus is grabbed by another application. 133 | // if forced is false, the composition is terminated gracefully by endComposition(). 134 | virtual void onCompositionTerminated(bool forced); 135 | 136 | private: 137 | ComPtr module_; 138 | ComPtr threadMgr_; 139 | TfClientId clientId_; 140 | DWORD activateFlags_; 141 | bool isKeyboardOpened_; 142 | 143 | // event sink cookies 144 | DWORD threadMgrEventSinkCookie_; 145 | DWORD textEditSinkCookie_; 146 | DWORD compositionSinkCookie_; 147 | DWORD keyboardOpenEventSinkCookie_; 148 | DWORD globalCompartmentEventSinkCookie_; 149 | DWORD langBarSinkCookie_; 150 | 151 | ITfComposition* composition_; // acquired when starting composition, released when ending composition 152 | CandidateWindow* candidateWindow_; 153 | ComPtr langBarMgr_; 154 | std::vector langBarButtons_; 155 | std::vector preservedKeys_; 156 | std::vector compartmentMonitors_; 157 | }; 158 | 159 | } 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /ImeModule.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "ImeModule.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "Window.h" 30 | #include "TextService.h" 31 | #include "DisplayAttributeProvider.h" 32 | 33 | using namespace std; 34 | 35 | namespace Ime { 36 | 37 | // these values are not defined in older TSF SDK (windows xp) 38 | #ifndef TF_IPP_CAPS_IMMERSIVESUPPORT 39 | // for Windows 8 40 | // GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT {13A016DF-560B-46CD-947A-4C3AF1E0E35D} 41 | static const GUID GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT = 42 | { 0x13A016DF, 0x560B, 0x46CD, { 0x94, 0x7A, 0x4C, 0x3A, 0xF1, 0xE0, 0xE3, 0x5D } }; 43 | // GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT {25504FB4-7BAB-4BC1-9C69-CF81890F0EF5} 44 | static const GUID GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT = 45 | { 0x25504FB4, 0x7BAB, 0x4BC1, { 0x9C, 0x69, 0xCF, 0x81, 0x89, 0x0F, 0x0E, 0xF5 } }; 46 | #endif 47 | 48 | // display attribute GUIDs 49 | 50 | // {05814A20-00B3-4B73-A3D0-2C521EFA8BE5} 51 | static const GUID g_inputDisplayAttributeGuid = 52 | { 0x5814a20, 0xb3, 0x4b73, { 0xa3, 0xd0, 0x2c, 0x52, 0x1e, 0xfa, 0x8b, 0xe5 } }; 53 | 54 | // {E1270AA5-A6B1-4112-9AC7-F5E476C3BD63} 55 | // static const GUID g_convertedDisplayAttributeGuid = 56 | // { 0xe1270aa5, 0xa6b1, 0x4112, { 0x9a, 0xc7, 0xf5, 0xe4, 0x76, 0xc3, 0xbd, 0x63 } }; 57 | 58 | 59 | ImeModule::ImeModule(HMODULE module, const CLSID& textServiceClsid): 60 | hInstance_(HINSTANCE(module)), 61 | textServiceClsid_(textServiceClsid), 62 | refCount_(1) { 63 | 64 | Window::registerClass(hInstance_); 65 | 66 | // regiser default display attributes 67 | inputAttrib_ = new DisplayAttributeInfo(g_inputDisplayAttributeGuid); 68 | inputAttrib_->setTextColor(COLOR_WINDOWTEXT); 69 | inputAttrib_->setLineStyle(TF_LS_DOT); 70 | inputAttrib_->setLineColor(COLOR_WINDOWTEXT); 71 | inputAttrib_->setBackgroundColor(COLOR_WINDOW); 72 | displayAttrInfos_.push_back(inputAttrib_); 73 | // convertedAttrib_ = new DisplayAttributeInfo(g_convertedDisplayAttributeGuid); 74 | // displayAttrInfos_.push_back(convertedAttrib_); 75 | 76 | registerDisplayAttributeInfos(); 77 | } 78 | 79 | ImeModule::~ImeModule(void) { 80 | 81 | // display attributes 82 | if(!displayAttrInfos_.empty()) { 83 | list::iterator it; 84 | for(it = displayAttrInfos_.begin(); it != displayAttrInfos_.end(); ++it) { 85 | DisplayAttributeInfo* info = *it; 86 | info->Release(); 87 | } 88 | } 89 | } 90 | 91 | // Dll entry points implementations 92 | HRESULT ImeModule::canUnloadNow() { 93 | // we own the last reference 94 | return refCount_ <= 1 ? S_OK : S_FALSE; 95 | } 96 | 97 | HRESULT ImeModule::getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj) { 98 | if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) { 99 | // increase reference count 100 | AddRef(); 101 | *ppvObj = (IClassFactory*)this; // our own object implements IClassFactory 102 | return NOERROR; 103 | } 104 | else 105 | *ppvObj = NULL; 106 | return CLASS_E_CLASSNOTAVAILABLE; 107 | } 108 | 109 | #ifndef _WIN64 // only do this for the 32-bit version dll 110 | static void loadDefaultUserRegistry(const wchar_t* defaultUserRegKey) { 111 | // The registry settings of all newly created users are based on the content of 112 | // "C:\Users\Default User\ntuser.dat", so we need to write our settings to this file so 113 | // the HKEY_CURRENT_USER key of newly created users can also contain our settings. 114 | // In order to do this, we need to load the default "hive" to registry first. 115 | // Reference: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms724889(v=vs.85).aspx 116 | wchar_t *userProfilesDir = nullptr; 117 | if (SUCCEEDED(::SHGetKnownFolderPath(FOLDERID_UserProfiles, 0, NULL, &userProfilesDir))) { 118 | // get the path of the default ntuser.dat file 119 | std::wstring defaultRegFile = userProfilesDir; 120 | ::CoTaskMemFree(userProfilesDir); 121 | defaultRegFile += L"\\Default User\\ntuser.dat"; 122 | 123 | // loading registry file requires special privileges SE_RESTORE_NAME and SE_BACKUP_NAME. 124 | // So let's do privilege elevation for our process. 125 | HANDLE processToken = NULL; 126 | ::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &processToken); 127 | DWORD bufLen = sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES); 128 | std::unique_ptr buf(new char[bufLen]); 129 | TOKEN_PRIVILEGES* privileges = reinterpret_cast(buf.get()); 130 | privileges->PrivilegeCount = 2; 131 | ::LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &privileges->Privileges[0].Luid); 132 | privileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 133 | ::LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &privileges->Privileges[1].Luid); 134 | privileges->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; 135 | ::AdjustTokenPrivileges(processToken, FALSE, privileges, bufLen, NULL, NULL); 136 | ::CloseHandle(processToken); 137 | 138 | // load the default registry hive under the specified key name 139 | ::RegLoadKeyW(HKEY_USERS, defaultUserRegKey, defaultRegFile.c_str()); 140 | } 141 | } 142 | #endif // #ifndef _WIN64 143 | 144 | HRESULT ImeModule::registerLangProfiles(LangProfileInfo* langs, int langsCount) { 145 | // register the language profile 146 | ComPtr inputProcessProfiles; 147 | if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) { 148 | for(int i = 0; i < langsCount; ++i) { 149 | LangProfileInfo& lang = langs[i]; 150 | if(inputProcessProfiles->Register(textServiceClsid_) == S_OK) { 151 | LCID lcid = LocaleNameToLCID(lang.locale.c_str(), 0); 152 | if (lcid == 0 && !lang.fallbackLocale.empty()) { // the conversion fails 153 | // The new RFC4646 locale names are not well-supported in Windows 7/Vista, so 154 | // here we provide a fallback locale which uses the deprecated RFC 1766 format instead. 155 | lcid = LocaleNameToLCID(lang.fallbackLocale.c_str(), 0); 156 | } 157 | if (lcid != 0) { 158 | LANGID langId = LANGIDFROMLCID(lcid); 159 | if (inputProcessProfiles->AddLanguageProfile(textServiceClsid_, langId, lang.profileGuid, 160 | lang.name.c_str(), lang.name.length(), lang.iconFile.empty() ? NULL : lang.iconFile.c_str(), 161 | lang.iconFile.length(), lang.iconIndex) != S_OK) { 162 | return E_FAIL; 163 | } 164 | } 165 | else { 166 | return E_FAIL; 167 | } 168 | } 169 | } 170 | } 171 | 172 | // NOTE: For Windows newer than Windows 8, we have to manually write some settings 173 | // to the registry so the input methods can appear in the Windows control panel. 174 | // 175 | // Registry path: "HKEY_CURRENT_USER\Control Panel\International\User Profile\" 176 | // Sub key: ":{text service GUID}{input module GUID}" 177 | // 178 | // Unfortunately, this is not documented officially by Microsoft. 179 | // We found the values with some registry monitor tools: 180 | // These settings are user-specific so they should be written to HKEY_CURRENT_USER of all users. 181 | // This might be achieved by Microsoft Acitve Setup, yet another undocumented feature. 182 | // https://helgeklein.com/blog/2010/04/active-setup-explained/ 183 | // 184 | // However, there is no way to uninstall keys installed with Active Setup. So let's avoid it. 185 | // References: https://support.microsoft.com/en-us/kb/284193 186 | // https://blogs.technet.microsoft.com/deploymentguys/2009/10/29/configuring-default-user-settings-full-update-for-windows-7-and-windows-server-2008-r2/ 187 | #ifndef _WIN64 // only do this for the 32-bit version dll 188 | // The keys under HKCU\Control Panel\ is shared between the x86 and x64 versions and 189 | // are not affected by WOW64 redirection. So doing this inside the 32-bit version is enough. 190 | 191 | if (isWindows8Above()) { 192 | DWORD sidCount = 0; 193 | if (::RegQueryInfoKeyW(HKEY_USERS, NULL, NULL, NULL, &sidCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 194 | return E_FAIL; 195 | wchar_t* textServiceClsIdStr = nullptr; 196 | if (FAILED(::StringFromCLSID(textServiceClsid_, &textServiceClsIdStr))) 197 | return E_FAIL; 198 | 199 | const wchar_t* defaultUserRegKey = L"__PIME_Default_user__"; 200 | loadDefaultUserRegistry(defaultUserRegKey); 201 | 202 | // write the language settings to user-specific registry. 203 | wchar_t sid[256]; 204 | for (DWORD iSid = 0; iSid < sidCount; ++iSid) { 205 | DWORD sidLen = sizeof(sid) / sizeof(wchar_t); 206 | if (::RegEnumKeyExW(HKEY_USERS, iSid, sid, &sidLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { 207 | // write settings of each input module to the user's registry 208 | for (int i = 0; i < langsCount; ++i) { 209 | auto& lang = langs[i]; 210 | std::wstring localeRegPath = sid; 211 | localeRegPath += L"\\Control Panel\\International\\User Profile\\"; 212 | localeRegPath += lang.locale; 213 | HKEY localeRegKey = NULL; 214 | DWORD err = ::RegCreateKeyExW(HKEY_USERS, localeRegPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &localeRegKey, NULL); 215 | if (err == ERROR_SUCCESS) { 216 | LCID lcid = LocaleNameToLCID(lang.locale.c_str(), 0); 217 | if (lcid == 0 && !lang.fallbackLocale.empty()) { // the conversion fails 218 | lcid = LocaleNameToLCID(lang.fallbackLocale.c_str(), 0); // try the fallback locale name 219 | } 220 | wchar_t lcid_hex[16]; 221 | wsprintf(lcid_hex, L"%04x", lcid); 222 | std::wstring valueName = lcid_hex; 223 | valueName += L":"; 224 | valueName += textServiceClsIdStr; 225 | wchar_t* profileClsIdStr = nullptr; 226 | if (SUCCEEDED(::StringFromCLSID(lang.profileGuid, &profileClsIdStr))) { 227 | valueName += profileClsIdStr; 228 | ::CoTaskMemFree(profileClsIdStr); 229 | DWORD profileCount = 1; 230 | if (::RegQueryInfoKeyW(localeRegKey, NULL, NULL, NULL, NULL, NULL, NULL, &profileCount, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { 231 | // ::MessageBoxW(0, std::to_wstring(profileCount).c_str(), 0, 0); 232 | ++profileCount; 233 | } 234 | ::RegSetKeyValueW(localeRegKey, NULL, valueName.c_str(), REG_DWORD, &profileCount, sizeof(DWORD)); 235 | } 236 | ::RegCloseKey(localeRegKey); 237 | } 238 | } 239 | } 240 | } 241 | ::CoTaskMemFree(textServiceClsIdStr); 242 | 243 | // unload the default user registry hive 244 | ::RegUnLoadKeyW(HKEY_USERS, defaultUserRegKey); 245 | } 246 | #endif // #ifndef _WIN64 247 | return S_OK; 248 | } 249 | 250 | HRESULT ImeModule::registerServer(wchar_t* imeName, LangProfileInfo* langs, int count) { 251 | // write info of our COM text service component to the registry 252 | // path: HKEY_CLASS_ROOT\\CLSID\\{xxxx-xxxx-xxxx-xx....} 253 | // This reguires Administrator permimssion to write to the registery 254 | // regsvr32 should be run with Administrator 255 | // For 64 bit dll, it seems that we need to write the key to 256 | // a different path to make it coexist with 32 bit version: 257 | // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{xxx-xxx-...} 258 | // Reference: http://stackoverflow.com/questions/1105031/can-my-32-bit-and-64-bit-com-components-co-reside-on-the-same-machine 259 | 260 | HRESULT result = S_OK; 261 | 262 | // get path of our module 263 | wchar_t modulePath[MAX_PATH]; 264 | DWORD modulePathLen = GetModuleFileNameW(hInstance_, modulePath, MAX_PATH); 265 | 266 | wstring regPath = L"CLSID\\"; 267 | LPOLESTR clsidStr = NULL; 268 | if(StringFromCLSID(textServiceClsid_, &clsidStr) != ERROR_SUCCESS) 269 | return E_FAIL; 270 | regPath += clsidStr; 271 | CoTaskMemFree(clsidStr); 272 | 273 | HKEY hkey = NULL; 274 | if(::RegCreateKeyExW(HKEY_CLASSES_ROOT, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, NULL) == ERROR_SUCCESS) { 275 | // write name of our IME 276 | ::RegSetValueExW(hkey, NULL, 0, REG_SZ, (BYTE*)imeName, sizeof(wchar_t) * (wcslen(imeName) + 1)); 277 | 278 | HKEY inProcServer32Key; 279 | if(::RegCreateKeyExW(hkey, L"InprocServer32", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &inProcServer32Key, NULL) == ERROR_SUCCESS) { 280 | // store the path of our dll module in the registry 281 | ::RegSetValueExW(inProcServer32Key, NULL, 0, REG_SZ, (BYTE*)modulePath, (modulePathLen + 1) * sizeof(wchar_t)); 282 | // write threading model 283 | wchar_t apartmentStr[] = L"Apartment"; 284 | ::RegSetValueExW(inProcServer32Key, L"ThreadingModel", 0, REG_SZ, (BYTE*)apartmentStr, 10 * sizeof(wchar_t)); 285 | ::RegCloseKey(inProcServer32Key); 286 | } 287 | else 288 | result = E_FAIL; 289 | ::RegCloseKey(hkey); 290 | } 291 | else 292 | result = E_FAIL; 293 | 294 | // register language profiles 295 | if(result == S_OK) { 296 | result = registerLangProfiles(langs, count); 297 | } 298 | 299 | // register category 300 | if(result == S_OK) { 301 | ITfCategoryMgr *categoryMgr = NULL; 302 | if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) { 303 | if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_) != S_OK) { 304 | result = E_FAIL; 305 | } 306 | 307 | // register ourself as a display attribute provider 308 | // so later we can set change the look and feels of composition string. 309 | if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, textServiceClsid_) != S_OK) { 310 | result = E_FAIL; 311 | } 312 | 313 | // enable UI less mode 314 | if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, textServiceClsid_) != S_OK || 315 | categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_UIELEMENTENABLED, textServiceClsid_) != S_OK) { 316 | result = E_FAIL; 317 | } 318 | 319 | if(isWindows8Above()) { 320 | // for Windows 8 store app support 321 | // TODO: according to a exhaustive Google search, I found that 322 | // TF_IPP_CAPS_IMMERSIVESUPPORT is required to make the IME work with Windows 8. 323 | // http://social.msdn.microsoft.com/Forums/windowsapps/en-US/4c422cf1-ceb4-413b-8a7c-6881946a4c63/how-to-set-a-flag-indicating-tsf-components-compatibility 324 | // Quote from the page: "To indicate that your IME is compatible with Windows Store apps, call RegisterCategory with GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT." 325 | 326 | // declare supporting immersive mode 327 | if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, textServiceClsid_) != S_OK) { 328 | result = E_FAIL; 329 | } 330 | 331 | // declare compatibility with Windows 8 system tray 332 | if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, textServiceClsid_) != S_OK) { 333 | result = E_FAIL; 334 | } 335 | } 336 | 337 | categoryMgr->Release(); 338 | } 339 | } 340 | return result; 341 | } 342 | 343 | HRESULT ImeModule::unregisterServer() { 344 | // unregister the language profile 345 | ITfInputProcessorProfiles *inputProcessProfiles = NULL; 346 | if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) { 347 | inputProcessProfiles->Unregister(textServiceClsid_); 348 | inputProcessProfiles->Release(); 349 | } 350 | 351 | // unregister categories 352 | ITfCategoryMgr *categoryMgr = NULL; 353 | if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) { 354 | categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_); 355 | categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, textServiceClsid_); 356 | // UI less mode 357 | categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, textServiceClsid_); 358 | 359 | if(isWindows8Above()) { 360 | // Windows 8 support 361 | categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, textServiceClsid_); 362 | categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, textServiceClsid_); 363 | } 364 | 365 | categoryMgr->Release(); 366 | } 367 | 368 | // delete the registry key 369 | wstring regPath = L"CLSID\\"; 370 | LPOLESTR clsidStr = NULL; 371 | if(StringFromCLSID(textServiceClsid_, &clsidStr) == ERROR_SUCCESS) { 372 | regPath += clsidStr; 373 | CoTaskMemFree(clsidStr); 374 | ::SHDeleteKey(HKEY_CLASSES_ROOT, regPath.c_str()); 375 | } 376 | 377 | #ifndef _WIN64 // only do this for the 32-bit version dll 378 | // The keys under HKCU\Control Panel\ is shared between the x86 and x64 versions and 379 | // are not affected by WOW64 redirection. So doing this inside the 32-bit version is enough. 380 | 381 | // delete settings under "HKEY_CURRENT_USER\Control Panel\International\User Profile\" for all users 382 | if (isWindows8Above()) { 383 | DWORD sidCount = 0; 384 | if (::RegQueryInfoKeyW(HKEY_USERS, NULL, NULL, NULL, &sidCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 385 | return E_FAIL; 386 | wchar_t* textServiceClsIdStr = nullptr; 387 | if (FAILED(::StringFromCLSID(textServiceClsid_, &textServiceClsIdStr))) 388 | return E_FAIL; 389 | 390 | const wchar_t* defaultUserRegKey = L"__PIME_Default_user__"; 391 | loadDefaultUserRegistry(defaultUserRegKey); 392 | 393 | // delete the language settings from user-specific registry. 394 | wchar_t sid[256]; 395 | for (DWORD iSid = 0; iSid < sidCount; ++iSid) { 396 | DWORD sidLen = sizeof(sid) / sizeof(wchar_t); 397 | if (::RegEnumKeyExW(HKEY_USERS, iSid, sid, &sidLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { 398 | // remove settings of each input module to the user's registry 399 | std::wstring userRegPath = sid; 400 | userRegPath += L"\\Control Panel\\International\\User Profile"; 401 | HKEY userKey = NULL; 402 | if (::RegOpenKeyExW(HKEY_USERS, userRegPath.c_str(), 0, KEY_READ, &userKey) == ERROR_SUCCESS) { 403 | DWORD localeCount = 0; 404 | if (::RegQueryInfoKeyW(userKey, NULL, NULL, NULL, &localeCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { 405 | // list all locales under this user 406 | wchar_t locale[100]; 407 | for (DWORD iLocale = 0; iLocale < localeCount; ++iLocale) { 408 | DWORD localeLen = sizeof(locale) / sizeof(wchar_t); 409 | if (::RegEnumKeyExW(userKey, iLocale, locale, &localeLen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { 410 | HKEY localeKey = NULL; 411 | if (::RegOpenKeyExW(userKey, locale, 0, KEY_ALL_ACCESS | KEY_READ, &localeKey) == ERROR_SUCCESS) { 412 | DWORD profileCount = 0; 413 | ::RegQueryInfoKeyW(localeKey, NULL, NULL, NULL, NULL, NULL, NULL, &profileCount, NULL, NULL, NULL, NULL); 414 | // list all language profiles under this locale 415 | std::vector profiles; 416 | for (DWORD iProfile = 0; iProfile < profileCount; ++iProfile) { 417 | wchar_t profile[128]; 418 | DWORD profileLen = sizeof(profile) / sizeof(wchar_t); 419 | if (::RegEnumValueW(localeKey, iProfile, profile, &profileLen, 0, NULL, NULL, NULL) == ERROR_SUCCESS) { 420 | if (wcsstr(profile, textServiceClsIdStr)) { // this profile is registered by us 421 | profiles.push_back(profile); 422 | } 423 | } 424 | } 425 | // delete these language profiles beloning to us 426 | for (const auto& profile : profiles) { 427 | ::RegDeleteValueW(localeKey, profile.c_str()); 428 | } 429 | ::RegCloseKey(localeKey); 430 | } 431 | } 432 | } 433 | } 434 | ::RegCloseKey(userKey); 435 | } 436 | } 437 | } 438 | ::CoTaskMemFree(textServiceClsIdStr); 439 | 440 | // unload the default user registry hive 441 | ::RegUnLoadKeyW(HKEY_USERS, defaultUserRegKey); 442 | } 443 | #endif // #ifndef _WIN64 444 | return S_OK; 445 | } 446 | 447 | 448 | // display attributes stuff 449 | bool ImeModule::registerDisplayAttributeInfos() { 450 | 451 | // register display attributes 452 | ComPtr categoryMgr; 453 | if(::CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) { 454 | TfGuidAtom atom; 455 | categoryMgr->RegisterGUID(g_inputDisplayAttributeGuid, &atom); 456 | inputAttrib_->setAtom(atom); 457 | // categoryMgr->RegisterGUID(g_convertedDisplayAttributeGuid, &atom); 458 | // convertedAttrib_->setAtom(atom); 459 | return true; 460 | } 461 | return false; 462 | } 463 | 464 | void ImeModule::removeTextService(TextService* service) { 465 | textServices_.remove(service); 466 | } 467 | 468 | // virtual 469 | bool ImeModule::onConfigure(HWND hwndParent, LANGID langid, REFGUID rguidProfile) { 470 | return true; 471 | } 472 | 473 | 474 | // COM related stuff 475 | 476 | // IUnknown 477 | STDMETHODIMP ImeModule::QueryInterface(REFIID riid, void **ppvObj) { 478 | if (ppvObj == NULL) 479 | return E_INVALIDARG; 480 | 481 | if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) 482 | *ppvObj = (IClassFactory*)this; 483 | else if(IsEqualIID(riid, IID_ITfFnConfigure)) 484 | *ppvObj = (ITfFnConfigure*)this; 485 | else 486 | *ppvObj = NULL; 487 | 488 | if(*ppvObj) { 489 | AddRef(); 490 | return S_OK; 491 | } 492 | return E_NOINTERFACE; 493 | } 494 | 495 | STDMETHODIMP_(ULONG) ImeModule::AddRef(void) { 496 | return ::InterlockedIncrement(&refCount_); 497 | } 498 | 499 | STDMETHODIMP_(ULONG) ImeModule::Release(void) { 500 | // NOTE: I think we do not need to use critical sections to 501 | // protect the operation as M$ did in their TSF samples. 502 | // Our ImeModule will be alive until dll unload so 503 | // it's not possible for an user application and TSF manager 504 | // to free our objecet since we always have refCount == 1. 505 | // The last reference is released in DllMain() when unloading. 506 | // Hence interlocked operations are enough here, I guess. 507 | assert(refCount_ > 0); 508 | if(::InterlockedExchangeSubtract(&refCount_, 1) == 1) { 509 | delete this; 510 | return 0; 511 | } 512 | return refCount_; 513 | } 514 | 515 | // IClassFactory 516 | STDMETHODIMP ImeModule::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) { 517 | *ppvObj = NULL; 518 | if(::IsEqualIID(riid, IID_ITfDisplayAttributeProvider)) { 519 | DisplayAttributeProvider* provider = new DisplayAttributeProvider(this); 520 | if(provider) { 521 | provider->QueryInterface(riid, ppvObj); 522 | provider->Release(); 523 | } 524 | } 525 | else if(::IsEqualIID(riid, IID_ITfFnConfigure)) { 526 | // ourselves implement this interface. 527 | this->QueryInterface(riid, ppvObj); 528 | } 529 | else { 530 | TextService* service = createTextService(); 531 | if(service) { 532 | textServices_.push_back(service); 533 | service->QueryInterface(riid, ppvObj); 534 | service->Release(); 535 | } 536 | } 537 | return *ppvObj ? S_OK : E_NOINTERFACE; 538 | } 539 | 540 | STDMETHODIMP ImeModule::LockServer(BOOL fLock) { 541 | if(fLock) 542 | AddRef(); 543 | else 544 | Release(); 545 | return S_OK; 546 | } 547 | 548 | // ITfFnConfigure 549 | STDMETHODIMP ImeModule::Show(HWND hwndParent, LANGID langid, REFGUID rguidProfile) { 550 | return onConfigure(hwndParent, langid, rguidProfile) ? S_OK : E_FAIL; 551 | } 552 | 553 | // ITfFunction 554 | STDMETHODIMP ImeModule::GetDisplayName(BSTR *pbstrName) { 555 | *pbstrName = ::SysAllocString(L"Configuration"); 556 | return S_OK; 557 | } 558 | 559 | } // namespace Ime 560 | -------------------------------------------------------------------------------- /ImeModule.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_IME_MODULE_H 21 | #define IME_IME_MODULE_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "WindowsVersion.h" 28 | #include 29 | 30 | namespace Ime { 31 | 32 | class TextService; 33 | class DisplayAttributeInfo; 34 | 35 | // language profile info, used to register new language profiles 36 | struct LangProfileInfo { 37 | std::wstring name; // should not exceed 32 chars 38 | GUID profileGuid; 39 | std::wstring locale; // RFC 4646 locale name 40 | std::wstring fallbackLocale; // RFC 1766 locale name or other alternatives if locale does not work 41 | std::wstring iconFile; 42 | int iconIndex; 43 | }; 44 | 45 | 46 | class ImeModule: 47 | public IClassFactory, 48 | public ITfFnConfigure { 49 | public: 50 | ImeModule(HMODULE module, const CLSID& textServiceClsid); 51 | 52 | // public methods 53 | HINSTANCE hInstance() const { 54 | return hInstance_; 55 | } 56 | 57 | const CLSID& textServiceClsid() const { 58 | return textServiceClsid_; 59 | } 60 | 61 | bool isWindows8Above() { 62 | return winVer_.isWindows8Above(); 63 | } 64 | 65 | WindowsVersion windowsVersion() const { 66 | return winVer_; 67 | } 68 | 69 | // Dll entry points implementations 70 | HRESULT canUnloadNow(); 71 | HRESULT getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj); 72 | 73 | HRESULT registerServer(wchar_t* imeName, LangProfileInfo* langs, int count); 74 | HRESULT registerLangProfiles(LangProfileInfo* langs, int count); 75 | HRESULT unregisterServer(); 76 | 77 | // should be override by IME implementors 78 | virtual TextService* createTextService() = 0; 79 | void removeTextService(TextService* service); 80 | 81 | // called when config dialog needs to be launched 82 | virtual bool onConfigure(HWND hwndParent, LANGID langid, REFGUID rguidProfile); 83 | 84 | // display attributes for composition string 85 | std::list& displayAttrInfos() { 86 | return displayAttrInfos_; 87 | } 88 | 89 | bool registerDisplayAttributeInfos(); 90 | 91 | DisplayAttributeInfo* inputAttrib() { 92 | return inputAttrib_; 93 | } 94 | 95 | /* 96 | DisplayAttributeInfo* convertedAttrib() { 97 | return convertedAttrib_; 98 | } 99 | */ 100 | 101 | const std::list& textServices() const { 102 | return textServices_; 103 | } 104 | 105 | // COM-related stuff 106 | 107 | // IUnknown 108 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 109 | STDMETHODIMP_(ULONG) AddRef(void); 110 | STDMETHODIMP_(ULONG) Release(void); 111 | 112 | protected: 113 | // IClassFactory 114 | STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj); 115 | STDMETHODIMP LockServer(BOOL fLock); 116 | 117 | // ITfFunction 118 | STDMETHODIMP GetDisplayName(BSTR *pbstrName); 119 | 120 | // ITfFnConfigure 121 | STDMETHODIMP Show(HWND hwndParent, LANGID langid, REFGUID rguidProfile); 122 | 123 | protected: // COM object should not be deleted directly. calling Release() instead. 124 | virtual ~ImeModule(void); 125 | 126 | private: 127 | volatile unsigned long refCount_; 128 | HINSTANCE hInstance_; 129 | CLSID textServiceClsid_; 130 | wchar_t* tooltip_; 131 | 132 | // display attributes 133 | std::list displayAttrInfos_; // display attribute info 134 | DisplayAttributeInfo* inputAttrib_; 135 | // DisplayAttributeInfo* convertedAttrib_; 136 | 137 | WindowsVersion winVer_; 138 | std::list textServices_; 139 | }; 140 | 141 | } 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /ImeWindow.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "ImeWindow.h" 21 | 22 | namespace Ime { 23 | 24 | ImeWindow::ImeWindow(TextService* service): 25 | textService_(service) { 26 | 27 | if(service->isImmersive()) { // windows 8 app mode 28 | margin_ = 10; 29 | } 30 | else { // desktop mode 31 | margin_ = 5; 32 | } 33 | 34 | font_ = (HFONT)GetStockObject(DEFAULT_GUI_FONT); 35 | /* 36 | LOGFONT lf; 37 | GetObject(font_, sizeof(lf), &lf); 38 | lf.lfHeight = fontSize_; 39 | lf.lfWeight = FW_NORMAL; 40 | font_ = CreateFontIndirect(&lf); 41 | */ 42 | } 43 | 44 | ImeWindow::~ImeWindow(void) { 45 | } 46 | 47 | void ImeWindow::onLButtonDown(WPARAM wp, LPARAM lp) { 48 | oldPos = MAKEPOINTS(lp); 49 | SetCapture(hwnd_); 50 | } 51 | 52 | void ImeWindow::onLButtonUp(WPARAM wp, LPARAM lp) { 53 | ReleaseCapture(); 54 | } 55 | 56 | void ImeWindow::onMouseMove(WPARAM wp, LPARAM lp) { 57 | if(GetCapture() != hwnd_) 58 | return; 59 | POINTS pt = MAKEPOINTS(lp); 60 | RECT rc; 61 | GetWindowRect(hwnd_, &rc); 62 | OffsetRect( &rc, (pt.x - oldPos.x), (pt.y - oldPos.y) ); 63 | move(rc.left, rc.top); 64 | } 65 | 66 | void ImeWindow::move(int x, int y) { 67 | int w, h; 68 | size(&w, &h); 69 | // ensure that the window does not fall outside of the screen. 70 | RECT rc = {x, y, x + w, y + h}; // current window rect 71 | // get the nearest monitor 72 | HMONITOR monitor = ::MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST); 73 | MONITORINFO mi; 74 | mi.cbSize = sizeof(mi); 75 | if(GetMonitorInfo(monitor, &mi)) 76 | rc = mi.rcWork; 77 | if(x < rc.left) 78 | x = rc.left; 79 | else if((x + w) > rc.right) 80 | x = rc.right - w; 81 | 82 | if(y < rc.top) 83 | y = rc.top; 84 | else if((y + h) > rc.bottom) 85 | y = rc.bottom - h; 86 | ::MoveWindow(hwnd_, x, y, w, h, TRUE); 87 | } 88 | 89 | void ImeWindow::setFont(HFONT f) { 90 | if(font_) 91 | ::DeleteObject(font_); 92 | font_ = f; 93 | recalculateSize(); 94 | if(isVisible()) 95 | ::InvalidateRect(hwnd_, NULL, TRUE); 96 | } 97 | 98 | // virtual 99 | void ImeWindow::recalculateSize() { 100 | } 101 | 102 | } // namespace Ime 103 | 104 | -------------------------------------------------------------------------------- /ImeWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_IME_WINDOW_H 21 | #define IME_IME_WINDOW_H 22 | 23 | #include 24 | #include "window.h" 25 | #include "TextService.h" 26 | 27 | namespace Ime { 28 | 29 | class TextService; 30 | 31 | // base class for all IME windows (candidate, tooltip, ...etc) 32 | class ImeWindow: public Window { 33 | public: 34 | ImeWindow(TextService* service); 35 | virtual ~ImeWindow(void); 36 | void move(int x, int y); 37 | bool isImmersive() { 38 | return textService_->isImmersive(); 39 | } 40 | 41 | void setFont(HFONT f); 42 | virtual void recalculateSize(); 43 | 44 | protected: 45 | void onLButtonDown(WPARAM wp, LPARAM lp); 46 | void onLButtonUp(WPARAM wp, LPARAM lp); 47 | void onMouseMove(WPARAM wp, LPARAM lp); 48 | 49 | protected: 50 | TextService* textService_; 51 | POINTS oldPos; 52 | HFONT font_; 53 | int margin_; 54 | }; 55 | 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /KeyEvent.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "KeyEvent.h" 21 | 22 | namespace Ime { 23 | 24 | KeyEvent::KeyEvent(UINT type, WPARAM wp, LPARAM lp): 25 | type_(type), 26 | keyCode_(wp), 27 | lParam_(lp) { 28 | 29 | if(!::GetKeyboardState(keyStates_)) // get state of all keys 30 | ::memset(keyStates_, 0, sizeof(keyStates_)); 31 | 32 | // try to convert the key event to an ASCII character 33 | // ToAscii API tries to convert Ctrl + printable characters to 34 | // ASCII 0x00 - 0x31 non-printable escape characters, which we don't want 35 | // So here is a hack: pretend that Ctrl key is not pressed 36 | WORD result[2] = {0, 0}; 37 | BYTE ctrlState = keyStates_[VK_CONTROL]; 38 | keyStates_[VK_CONTROL] = 0; 39 | if(::ToAscii(keyCode_, scanCode(), keyStates_, result, 0) == 1) 40 | charCode_ = (UINT)result[0]; 41 | else 42 | charCode_ = 0; 43 | keyStates_[VK_CONTROL] = ctrlState; 44 | } 45 | 46 | KeyEvent::KeyEvent(const KeyEvent& other): 47 | type_(other.type_), 48 | keyCode_(other.keyCode_), 49 | charCode_(other.charCode_), 50 | lParam_(other.lParam_) { 51 | 52 | ::memcpy(keyStates_, other.keyStates_, sizeof(keyStates_)); 53 | } 54 | 55 | KeyEvent::~KeyEvent(void) { 56 | } 57 | 58 | } // namespace Ime 59 | -------------------------------------------------------------------------------- /KeyEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_KEY_EVENT_H 21 | #define IME_KEY_EVENT_H 22 | 23 | #include 24 | 25 | namespace Ime { 26 | 27 | class KeyEvent { 28 | public: 29 | KeyEvent(UINT type, WPARAM wp, LPARAM lp); 30 | KeyEvent(const KeyEvent& other); 31 | ~KeyEvent(void); 32 | 33 | UINT type() { 34 | return type_; 35 | } 36 | 37 | UINT keyCode() { 38 | return keyCode_; 39 | } 40 | 41 | UINT charCode() { 42 | return charCode_; 43 | } 44 | 45 | bool isChar() { 46 | return (charCode_ != 0); 47 | } 48 | 49 | LPARAM lParam() { 50 | return lParam_; 51 | } 52 | 53 | unsigned short repeatCount() { 54 | // bits 0-15 55 | return (unsigned short)(lParam_ & 0xffff); 56 | } 57 | 58 | unsigned char scanCode() { 59 | // bits 16-23 60 | return (unsigned char)(lParam_ & 0xff0000); 61 | } 62 | 63 | bool isExtended() { 64 | // bit 24 65 | return (lParam_ & (1<<24)) != 0; 66 | } 67 | 68 | bool isKeyDown(UINT code) const { 69 | return (keyStates_[code] & (1 << 7)) != 0; 70 | } 71 | 72 | bool isKeyToggled(UINT code) const { 73 | return (keyStates_[code] & 1) != 0; 74 | } 75 | 76 | const BYTE* keyStates() const { 77 | return keyStates_; 78 | } 79 | 80 | private: 81 | KeyEvent(void) {} 82 | 83 | private: 84 | UINT type_; 85 | UINT keyCode_; 86 | UINT charCode_; 87 | LPARAM lParam_; 88 | BYTE keyStates_[256]; 89 | }; 90 | 91 | // Try to use KeyEvent::isKeyDown() and KeyEvent::isKeyToggled() whenever possible. 92 | // If a KeyEvent object is not available, then build a KeyState object as an alternative 93 | // to get key states 94 | class KeyState { 95 | public: 96 | KeyState(int keyCode, bool sync = true) { 97 | state_ = ::GetKeyState(keyCode); 98 | } 99 | 100 | bool isDown() { 101 | return ((state_ & (1 << 15)) != 0); 102 | } 103 | 104 | bool isToggled() { 105 | return ((state_ & 1) != 0); 106 | } 107 | 108 | short state() { 109 | return state_; 110 | } 111 | 112 | private: 113 | short state_; 114 | }; 115 | 116 | } 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! -------------------------------------------------------------------------------- /LangBarButton.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "LangBarButton.h" 21 | #include "TextService.h" 22 | #include "ImeModule.h" 23 | #include 24 | #include 25 | #include 26 | 27 | namespace Ime { 28 | 29 | LangBarButton::LangBarButton(TextService* service, const GUID& guid, UINT commandId, const wchar_t* text, DWORD style): 30 | textService_(service), 31 | tooltip_(), 32 | commandId_(commandId), 33 | menu_(NULL), 34 | icon_(NULL), 35 | status_(0), 36 | refCount_(1) { 37 | 38 | assert(service && service->imeModule()); 39 | 40 | textService_->AddRef(); 41 | info_.clsidService = service->imeModule()->textServiceClsid(); 42 | info_.guidItem = guid; 43 | info_.dwStyle = style; 44 | info_.ulSort = 0; 45 | setText(text); 46 | } 47 | 48 | LangBarButton::~LangBarButton(void) { 49 | if(textService_) 50 | textService_->Release(); 51 | if(menu_) 52 | ::DestroyMenu(menu_); 53 | } 54 | 55 | const wchar_t* LangBarButton::text() const { 56 | return info_.szDescription; 57 | } 58 | 59 | void LangBarButton::setText(const wchar_t* text) { 60 | if (text && text[0] != '\0') { 61 | wcsncpy(info_.szDescription, text, TF_LBI_DESC_MAXLEN - 1); 62 | info_.szDescription[TF_LBI_DESC_MAXLEN - 1] = 0; 63 | } 64 | else { 65 | // NOTE: The language button text should NOT be empty. 66 | // Otherwise, when the button status or icon is changed after its creation, 67 | // the button will disappear temporarily in Windows 10 for unknown reason. 68 | // This can be considered a bug of Windows 10 and there does not seem to be a way to fix it. 69 | // So we need to avoid empty button text otherwise the language button won't work properly. 70 | // Here we use a space character to make the text non-empty to workaround the problem. 71 | wcscpy(info_.szDescription, L" "); 72 | } 73 | update(TF_LBI_TEXT); 74 | } 75 | 76 | void LangBarButton::setText(UINT stringId) { 77 | const wchar_t* str; 78 | int len = ::LoadStringW(textService_->imeModule()->hInstance(), stringId, (LPTSTR)&str, 0); 79 | if(str) { 80 | if(len > (TF_LBI_DESC_MAXLEN - 1)) 81 | len = TF_LBI_DESC_MAXLEN - 1; 82 | wcsncpy(info_.szDescription, str, len); 83 | info_.szDescription[len] = 0; 84 | update(TF_LBI_TEXT); 85 | } 86 | } 87 | 88 | // public methods 89 | const wchar_t* LangBarButton::tooltip() const { 90 | return tooltip_.c_str(); 91 | } 92 | 93 | void LangBarButton::setTooltip(const wchar_t* tooltip) { 94 | tooltip_ = tooltip; 95 | update(TF_LBI_TOOLTIP); 96 | } 97 | 98 | void LangBarButton::setTooltip(UINT tooltipId) { 99 | const wchar_t* str; 100 | // If this parameter is 0, then lpBuffer receives a read-only pointer to the resource itself. 101 | auto len = ::LoadStringW(textService_->imeModule()->hInstance(), tooltipId, (LPTSTR)&str, 0); 102 | if(str) { 103 | tooltip_ = std::wstring(str, len); 104 | update(TF_LBI_TOOLTIP); 105 | } 106 | } 107 | 108 | HICON LangBarButton::icon() const { 109 | return icon_; 110 | } 111 | 112 | // The language button does not take owner ship of the icon 113 | // That means, when the button is destroyed, it will not destroy 114 | // the icon automatically. 115 | void LangBarButton::setIcon(HICON icon) { 116 | icon_ = icon; 117 | update(TF_LBI_ICON); 118 | } 119 | 120 | void LangBarButton::setIcon(UINT iconId) { 121 | HICON icon = ::LoadIconW(textService_->imeModule()->hInstance(), (LPCTSTR)iconId); 122 | if(icon) 123 | setIcon(icon); 124 | } 125 | 126 | UINT LangBarButton::commandId() const { 127 | return commandId_; 128 | } 129 | 130 | void LangBarButton::setCommandId(UINT id) { 131 | commandId_ = id; 132 | } 133 | 134 | HMENU LangBarButton::menu() const { 135 | return menu_; 136 | } 137 | 138 | void LangBarButton::setMenu(HMENU menu) { 139 | if(menu_) { 140 | ::DestroyMenu(menu_); 141 | } 142 | menu_ = menu; 143 | // FIXME: how to handle toggle buttons? 144 | if(menu) 145 | info_.dwStyle = TF_LBI_STYLE_BTN_MENU; 146 | else 147 | info_.dwStyle = TF_LBI_STYLE_BTN_BUTTON; 148 | } 149 | 150 | bool LangBarButton::enabled() const { 151 | return !(status_ & TF_LBI_STATUS_DISABLED); 152 | } 153 | 154 | void LangBarButton::setEnabled(bool enable) { 155 | if(enabled() != enable) { 156 | if(enable) 157 | status_ &= ~TF_LBI_STATUS_DISABLED; 158 | else 159 | status_ |= TF_LBI_STATUS_DISABLED; 160 | update(TF_LBI_STATUS); 161 | } 162 | } 163 | 164 | // need to create the button with TF_LBI_STYLE_BTN_TOGGLE style 165 | bool LangBarButton::toggled() const { 166 | return (status_ & TF_LBI_STATUS_BTN_TOGGLED) ? true : false; 167 | } 168 | 169 | void LangBarButton::setToggled(bool toggle) { 170 | if(toggled() != toggle) { 171 | if(toggle) 172 | status_ |= TF_LBI_STATUS_BTN_TOGGLED; 173 | else 174 | status_ &= ~TF_LBI_STATUS_BTN_TOGGLED; 175 | update(TF_LBI_STATUS); 176 | } 177 | } 178 | 179 | 180 | DWORD LangBarButton::style() const { 181 | return info_.dwStyle; 182 | } 183 | 184 | void LangBarButton::setStyle(DWORD style) { 185 | info_.dwStyle = style; 186 | } 187 | 188 | 189 | // COM stuff 190 | 191 | // IUnknown 192 | STDMETHODIMP LangBarButton::QueryInterface(REFIID riid, void **ppvObj) { 193 | if (ppvObj == NULL) 194 | return E_INVALIDARG; 195 | 196 | if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfLangBarItem) || IsEqualIID(riid, IID_ITfLangBarItemButton)) 197 | *ppvObj = (ITfLangBarItemButton*)this; 198 | else if(IsEqualIID(riid, IID_ITfSource)) 199 | *ppvObj = (ITfSource*)this; 200 | else 201 | *ppvObj = NULL; 202 | 203 | if(*ppvObj) { 204 | AddRef(); 205 | return S_OK; 206 | } 207 | return E_NOINTERFACE; 208 | } 209 | 210 | // IUnknown implementation 211 | STDMETHODIMP_(ULONG) LangBarButton::AddRef(void) { 212 | return ++refCount_; 213 | } 214 | 215 | STDMETHODIMP_(ULONG) LangBarButton::Release(void) { 216 | assert(refCount_ > 0); 217 | const ULONG newCount = --refCount_; 218 | if (0 == refCount_) 219 | delete this; 220 | return newCount; 221 | } 222 | 223 | // ITfLangBarItem 224 | STDMETHODIMP LangBarButton::GetInfo(TF_LANGBARITEMINFO *pInfo) { 225 | *pInfo = info_; 226 | return S_OK; 227 | } 228 | 229 | STDMETHODIMP LangBarButton::GetStatus(DWORD *pdwStatus) { 230 | *pdwStatus = status_; 231 | return S_OK; 232 | } 233 | 234 | STDMETHODIMP LangBarButton::Show(BOOL fShow) { 235 | return E_NOTIMPL; 236 | } 237 | 238 | STDMETHODIMP LangBarButton::GetTooltipString(BSTR *pbstrToolTip) { 239 | *pbstrToolTip = ::SysAllocString(tooltip_.c_str()); 240 | return *pbstrToolTip ? S_OK : E_FAIL; 241 | } 242 | 243 | // ITfLangBarItemButton 244 | STDMETHODIMP LangBarButton::OnClick(TfLBIClick click, POINT pt, const RECT *prcArea) { 245 | TextService::CommandType type; 246 | if(click == TF_LBI_CLK_RIGHT) 247 | type = TextService::COMMAND_RIGHT_CLICK; 248 | else 249 | type = TextService::COMMAND_LEFT_CLICK; 250 | textService_->onCommand(commandId_, type); 251 | return S_OK; 252 | } 253 | 254 | STDMETHODIMP LangBarButton::InitMenu(ITfMenu *pMenu) { 255 | if(!menu_) 256 | return E_FAIL; 257 | buildITfMenu(pMenu, menu_); 258 | return S_OK; 259 | } 260 | 261 | STDMETHODIMP LangBarButton::OnMenuSelect(UINT wID) { 262 | textService_->onCommand(wID, TextService::COMMAND_MENU); 263 | return S_OK; 264 | } 265 | 266 | STDMETHODIMP LangBarButton::GetIcon(HICON *phIcon) { 267 | // https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms628718%28v=vs.85%29.aspx 268 | // The caller will delete the icon when it's no longer needed. 269 | // However, we might still need it. So let's return a copy here. 270 | *phIcon = (HICON)CopyImage(icon_, IMAGE_ICON, 0, 0, 0); 271 | return S_OK; 272 | } 273 | 274 | STDMETHODIMP LangBarButton::GetText(BSTR *pbstrText) { 275 | *pbstrText = ::SysAllocString(info_.szDescription); 276 | return *pbstrText ? S_OK : S_FALSE; 277 | } 278 | 279 | // ITfSource 280 | STDMETHODIMP LangBarButton::AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie) { 281 | if(IsEqualIID(riid, IID_ITfLangBarItemSink)) { 282 | ITfLangBarItemSink* langBarItemSink; 283 | if(punk->QueryInterface(IID_ITfLangBarItemSink, (void **)&langBarItemSink) == S_OK) { 284 | *pdwCookie = (DWORD)rand(); 285 | sinks_[*pdwCookie] = langBarItemSink; 286 | return S_OK; 287 | } 288 | else 289 | return E_NOINTERFACE; 290 | } 291 | return CONNECT_E_CANNOTCONNECT; 292 | } 293 | 294 | STDMETHODIMP LangBarButton::UnadviseSink(DWORD dwCookie) { 295 | std::map::iterator it = sinks_.find(dwCookie); 296 | if(it != sinks_.end()) { 297 | ITfLangBarItemSink* langBarItemSink = (ITfLangBarItemSink*)it->second; 298 | langBarItemSink->Release(); 299 | sinks_.erase(it); 300 | return S_OK; 301 | } 302 | return CONNECT_E_NOCONNECTION; 303 | } 304 | 305 | 306 | // build ITfMenu according to the content of HMENU 307 | void LangBarButton::buildITfMenu(ITfMenu* menu, HMENU templ) { 308 | int n = ::GetMenuItemCount(templ); 309 | for(int i = 0; i < n; ++i) { 310 | MENUITEMINFO mi; 311 | wchar_t textBuffer[256]; 312 | memset(&mi, 0, sizeof(mi)); 313 | mi.cbSize = sizeof(mi); 314 | mi.dwTypeData = (LPTSTR)textBuffer; 315 | mi.cch = 255; 316 | mi.fMask = MIIM_FTYPE|MIIM_ID|MIIM_STATE|MIIM_STRING|MIIM_SUBMENU; 317 | if(::GetMenuItemInfoW(templ, i, TRUE, &mi)) { 318 | UINT flags = 0; 319 | wchar_t* text = nullptr; 320 | ULONG textLen = 0; 321 | ITfMenu* subMenu = NULL; 322 | ITfMenu** pSubMenu = NULL; 323 | if(mi.hSubMenu) { // has submenu 324 | pSubMenu = &subMenu; 325 | flags |= TF_LBMENUF_SUBMENU; 326 | } 327 | if(mi.fType == MFT_STRING) { // text item 328 | text = (wchar_t*)mi.dwTypeData; 329 | textLen = mi.cch; 330 | } 331 | else if(mi.fType == MFT_SEPARATOR) { // separator item 332 | flags |= TF_LBMENUF_SEPARATOR; 333 | } 334 | else // other types are not supported 335 | continue; 336 | 337 | if(mi.fState & MFS_CHECKED) // checked 338 | flags |= TF_LBMENUF_CHECKED; 339 | if(mi.fState & (MFS_GRAYED|MFS_DISABLED)) // disabled 340 | flags |= TF_LBMENUF_GRAYED; 341 | 342 | if(menu->AddMenuItem(mi.wID, flags, NULL, 0, text, textLen, pSubMenu) == S_OK) { 343 | if(subMenu) { 344 | buildITfMenu(subMenu, mi.hSubMenu); 345 | subMenu->Release(); 346 | } 347 | } 348 | } 349 | else { 350 | DWORD error = ::GetLastError(); 351 | } 352 | } 353 | } 354 | 355 | // call all sinks to generate update notifications 356 | void LangBarButton::update(DWORD flags) { 357 | if(!sinks_.empty()) { 358 | std::map::iterator it; 359 | for(it = sinks_.begin(); it != sinks_.end(); ++it) { 360 | ITfLangBarItemSink* sink = it->second; 361 | sink->OnUpdate(flags); 362 | } 363 | } 364 | } 365 | 366 | } // namespace Ime 367 | 368 | -------------------------------------------------------------------------------- /LangBarButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_LANGUAGE_BAR_BUTTON_H 21 | #define IME_LANGUAGE_BAR_BUTTON_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace Ime { 30 | 31 | class TextService; 32 | 33 | class LangBarButton: 34 | public ITfLangBarItemButton, 35 | public ITfSource { 36 | public: 37 | LangBarButton(TextService* service, const GUID& guid, UINT commandId = 0, const wchar_t* text = NULL, DWORD style = TF_LBI_STYLE_BTN_BUTTON); 38 | 39 | // public methods 40 | const wchar_t* text() const; 41 | void setText(const wchar_t* text); 42 | void setText(UINT stringId); 43 | 44 | const wchar_t* tooltip() const; 45 | void setTooltip(const wchar_t* tooltip); 46 | void setTooltip(UINT stringId); 47 | 48 | HICON icon() const; 49 | void setIcon(HICON icon); 50 | void setIcon(UINT iconId); 51 | 52 | UINT commandId() const; 53 | void setCommandId(UINT id); 54 | 55 | HMENU menu() const; 56 | void setMenu(HMENU menu); 57 | 58 | bool enabled() const; 59 | void setEnabled(bool enable); 60 | 61 | // need to create the button with TF_LBI_STYLE_BTN_TOGGLE style 62 | bool toggled() const; 63 | void setToggled(bool toggle); 64 | 65 | DWORD style() const; 66 | void setStyle(DWORD style); 67 | 68 | // COM-related stuff 69 | 70 | // IUnknown 71 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 72 | STDMETHODIMP_(ULONG) AddRef(void); 73 | STDMETHODIMP_(ULONG) Release(void); 74 | 75 | // ITfLangBarItem 76 | STDMETHODIMP GetInfo(TF_LANGBARITEMINFO *pInfo); 77 | STDMETHODIMP GetStatus(DWORD *pdwStatus); 78 | STDMETHODIMP Show(BOOL fShow); 79 | STDMETHODIMP GetTooltipString(BSTR *pbstrToolTip); 80 | 81 | // ITfLangBarItemButton 82 | STDMETHODIMP OnClick(TfLBIClick click, POINT pt, const RECT *prcArea); 83 | STDMETHODIMP InitMenu(ITfMenu *pMenu); 84 | STDMETHODIMP OnMenuSelect(UINT wID); 85 | STDMETHODIMP GetIcon(HICON *phIcon); 86 | STDMETHODIMP GetText(BSTR *pbstrText); 87 | 88 | // ITfSource 89 | STDMETHODIMP AdviseSink(REFIID riid, IUnknown *punk, DWORD *pdwCookie); 90 | STDMETHODIMP UnadviseSink(DWORD dwCookie); 91 | 92 | void update(DWORD flags = TF_LBI_BTNALL); 93 | 94 | TextService* textService() const { 95 | return textService_; 96 | }; 97 | 98 | protected: // COM object should not be deleted directly. calling Release() instead. 99 | virtual ~LangBarButton(void); 100 | 101 | private: 102 | void buildITfMenu(ITfMenu* menu, HMENU templ); 103 | 104 | private: 105 | int refCount_; 106 | TextService* textService_; 107 | TF_LANGBARITEMINFO info_; 108 | UINT commandId_; 109 | std::wstring tooltip_; 110 | HICON icon_; 111 | HMENU menu_; 112 | std::map sinks_; 113 | DWORD status_; 114 | }; 115 | 116 | } 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /MessageWindow.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "MessageWindow.h" 21 | #include "TextService.h" 22 | #include "DrawUtils.h" 23 | 24 | namespace Ime { 25 | 26 | MessageWindow::MessageWindow(TextService* service, EditSession* session): 27 | ImeWindow(service) { 28 | 29 | HWND parent = service->compositionWindow(session); 30 | create(parent, WS_POPUP|WS_CLIPCHILDREN, WS_EX_TOOLWINDOW|WS_EX_TOPMOST); 31 | } 32 | 33 | MessageWindow::~MessageWindow(void) { 34 | } 35 | 36 | // virtual 37 | void MessageWindow::recalculateSize() { 38 | SIZE size = {0}; 39 | HDC dc = GetDC(hwnd_); 40 | HGDIOBJ old_font = SelectObject(dc, font_); 41 | GetTextExtentPointW(dc, text_.c_str(), text_.length(), &size); 42 | SelectObject(dc, old_font); 43 | ReleaseDC(hwnd_, dc); 44 | 45 | SetWindowPos(hwnd_, HWND_TOPMOST, 0, 0, 46 | size.cx + margin_ * 2, size.cy + margin_ * 2, SWP_NOACTIVATE|SWP_NOMOVE); 47 | } 48 | 49 | void MessageWindow::setText(std::wstring text) { 50 | // FIXMEl: use different appearance under immersive mode 51 | text_ = text; 52 | recalculateSize(); 53 | if(IsWindowVisible(hwnd_)) 54 | InvalidateRect(hwnd_, NULL, TRUE); 55 | } 56 | 57 | LRESULT MessageWindow::wndProc(UINT msg, WPARAM wp, LPARAM lp) { 58 | switch(msg) { 59 | case WM_PAINT: { 60 | PAINTSTRUCT ps; 61 | BeginPaint(hwnd_, &ps); 62 | onPaint(ps); 63 | EndPaint(hwnd_, &ps); 64 | } 65 | break; 66 | case WM_MOUSEACTIVATE: 67 | return MA_NOACTIVATE; 68 | default: 69 | return ImeWindow::wndProc(msg, wp, lp); 70 | } 71 | return 0; 72 | } 73 | 74 | void MessageWindow::onPaint(PAINTSTRUCT& ps) { 75 | int len = text_.length(); 76 | RECT rc, textrc = {0}; 77 | GetClientRect(hwnd_, &rc); 78 | 79 | // draw a flat black border in Windows 8 app immersive mode 80 | // draw a 3d border in desktop mode 81 | HDC hDC = ps.hdc; 82 | HFONT oldFont = (HFONT)SelectObject(hDC, font_); 83 | 84 | SetBkMode(hDC, TRANSPARENT); 85 | if(isImmersive()) { 86 | SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT)); 87 | SetBkColor(hDC, GetSysColor(COLOR_WINDOW)); 88 | HPEN pen = ::CreatePen(PS_SOLID, 3, RGB(0, 0, 0)); 89 | HGDIOBJ oldPen = ::SelectObject(hDC, pen); 90 | ::Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom); 91 | ::SelectObject(hDC, oldPen); 92 | ::DeleteObject(pen); 93 | } 94 | else { 95 | SetTextColor(hDC, GetSysColor(COLOR_INFOTEXT)); 96 | SetBkColor(hDC, GetSysColor(COLOR_INFOBK)); 97 | // draw a 3d border in desktop mode 98 | ::FillSolidRect(hDC, &rc, ::GetSysColor(COLOR_INFOBK)); 99 | ::Draw3DBorder(hDC, &rc, GetSysColor(COLOR_3DFACE), 0); 100 | } 101 | 102 | SIZE size; 103 | GetTextExtentPoint32W(hDC, text_.c_str(), len, &size); 104 | rc.top += (rc.bottom - size.cy)/2; 105 | rc.left += (rc.right - size.cx)/2; 106 | ExtTextOutW(hDC, rc.left, rc.top, 0, &textrc, text_.c_str(), len, NULL); 107 | 108 | SelectObject(hDC, oldFont); 109 | } 110 | 111 | } // namespace Ime 112 | -------------------------------------------------------------------------------- /MessageWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_MESSAGE_WINDOW_H 21 | #define IME_MESSAGE_WINDOW_H 22 | 23 | #include "ImeWindow.h" 24 | #include "EditSession.h" 25 | #include 26 | 27 | namespace Ime { 28 | 29 | class TextService; 30 | 31 | class MessageWindow : public ImeWindow { 32 | public: 33 | MessageWindow(TextService* service, EditSession* session = NULL); 34 | virtual ~MessageWindow(void); 35 | 36 | std::wstring text() { 37 | return text_; 38 | } 39 | void setText(std::wstring text); 40 | 41 | TextService* textService() { 42 | return textService_; 43 | } 44 | 45 | virtual void recalculateSize(); 46 | protected: 47 | LRESULT wndProc(UINT msg, WPARAM wp, LPARAM lp); 48 | void onPaint(PAINTSTRUCT& ps); 49 | 50 | private: 51 | std::wstring text_; 52 | }; 53 | 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /PropertyDialog.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "PropertyDialog.h" 21 | #include "PropertyPage.h" 22 | #include 23 | #include 24 | 25 | using namespace std; 26 | 27 | namespace Ime { 28 | 29 | //typedef INT_PTR (WINAPI *PropertySheetFunc)(LPCPROPSHEETHEADER lppsph); 30 | //static PropertySheetFunc g_PropertySheetW = NULL; 31 | 32 | PropertyDialog::PropertyDialog(void) { 33 | } 34 | 35 | PropertyDialog::~PropertyDialog(void) { 36 | if(!pages_.empty()) { 37 | for(vector::iterator it = pages_.begin(); it != pages_.end(); ++it) { 38 | PropertyPage* page = *it; 39 | delete page; 40 | } 41 | } 42 | } 43 | 44 | INT_PTR PropertyDialog::showModal(HINSTANCE hInstance, LPCTSTR captionId, LPCTSTR iconId, HWND parent, DWORD flags) { 45 | assert(pages_.size() > 0); 46 | 47 | // Windows 8 does not allow linking to comctl32.dll 48 | // when running in app containers. 49 | // We use LoadLibrary here, but this should only be used in desktop app mode. 50 | // if(!g_PropertySheetW) { 51 | // HMODULE mod = ::LoadLibraryW(L"comctl32.dll"); 52 | // g_PropertySheetW = (PropertySheetFunc)::GetProcAddress(mod, "PropertySheetW"); 53 | // } 54 | 55 | PROPSHEETPAGE* pages = new PROPSHEETPAGE[pages_.size()]; 56 | for(size_t i = 0; i < pages_.size(); ++i) { 57 | pages_[i]->setup(pages[i]); 58 | pages[i].hInstance = hInstance; 59 | } 60 | 61 | PROPSHEETHEADER psh = {0}; 62 | psh.dwFlags = flags; 63 | psh.dwSize = sizeof(PROPSHEETHEADER); 64 | psh.hInstance = hInstance; 65 | psh.hwndParent = parent; 66 | psh.pszIcon = (LPCTSTR)iconId; 67 | psh.nPages = pages_.size(); 68 | psh.ppsp = pages; 69 | psh.pszCaption = (LPCTSTR)captionId; 70 | 71 | // INT_PTR result = g_PropertySheetW(&psh); 72 | INT_PTR result =::PropertySheetW(&psh); 73 | delete []pages; 74 | 75 | return result; 76 | } 77 | 78 | void PropertyDialog::addPage(PropertyPage* page) { 79 | pages_.push_back(page); 80 | } 81 | 82 | void PropertyDialog::removePage(PropertyPage* page) { 83 | vector::iterator it = std::find(pages_.begin(), pages_.end(), page); 84 | if(it != pages_.end()) 85 | pages_.erase(it); 86 | } 87 | 88 | } // namespace Ime 89 | -------------------------------------------------------------------------------- /PropertyDialog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_PROPERTY_DIALOG_H 21 | #define IME_PROPERTY_DIALOG_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include "PropertyPage.h" 27 | 28 | namespace Ime { 29 | 30 | class PropertyPage; 31 | 32 | // Create a property sheet which contains tabbed interface 33 | // for a configuration dialog 34 | // This class should only be used in desktop app mode. 35 | // Otherwise, your IME might be blocked by Windows 8. (not sure?) 36 | 37 | class PropertyDialog { 38 | public: 39 | PropertyDialog(void); 40 | virtual ~PropertyDialog(void); 41 | 42 | void addPage(PropertyPage* page); 43 | void removePage(PropertyPage* page); 44 | INT_PTR showModal(HINSTANCE hInstance, LPCTSTR captionId = 0, LPCTSTR iconId = 0, HWND parent = HWND_DESKTOP, DWORD flags = PSH_NOAPPLYNOW | PSH_USEICONID | PSH_PROPSHEETPAGE); 45 | 46 | private: 47 | std::vector pages_; 48 | }; 49 | 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /PropertyPage.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "PropertyPage.h" 21 | 22 | namespace Ime { 23 | 24 | PropertyPage::PropertyPage(LPCTSTR dialogId): 25 | Dialog(), 26 | dialogId_(dialogId) { 27 | } 28 | 29 | PropertyPage::~PropertyPage(void) { 30 | } 31 | 32 | void PropertyPage::setup(PROPSHEETPAGE& page) { 33 | memset(&page, 0, sizeof(PROPSHEETPAGE)); 34 | page.dwSize = sizeof(PROPSHEETPAGE); 35 | page.dwFlags = PSP_DEFAULT; 36 | 37 | page.pszTemplate = (LPCTSTR)dialogId_; 38 | page.pfnDlgProc = (DLGPROC)_dlgProc; 39 | page.lParam = (LPARAM)this; 40 | } 41 | 42 | // static 43 | INT_PTR CALLBACK PropertyPage::_dlgProc(HWND hwnd , UINT msg, WPARAM wp , LPARAM lp) { 44 | if(msg == WM_INITDIALOG) { 45 | PROPSHEETPAGE* page = (PROPSHEETPAGE*)lp; 46 | PropertyPage* pThis = (PropertyPage*)page->lParam; 47 | pThis->hwnd_ = hwnd; 48 | hwndMap_[hwnd] = pThis; 49 | pThis->wndProc(msg, wp, lp); 50 | return TRUE; 51 | } 52 | return Dialog::_dlgProc(hwnd, msg, wp, lp); 53 | } 54 | 55 | // virtual 56 | LRESULT PropertyPage::wndProc(UINT msg, WPARAM wp, LPARAM lp) { 57 | switch(msg) { 58 | case WM_NOTIFY: 59 | switch(LPNMHDR(lp)->code) { 60 | case PSN_APPLY: 61 | onOK(); 62 | return TRUE; 63 | case PSN_QUERYCANCEL: 64 | onCancel(); 65 | return TRUE; 66 | } 67 | } 68 | return Dialog::wndProc(msg, wp, lp); 69 | } 70 | 71 | // virtual 72 | void PropertyPage::onOK() { 73 | ::SetWindowLongPtr(hwnd_, DWLP_MSGRESULT, PSNRET_NOERROR); 74 | } 75 | 76 | // virtual 77 | void PropertyPage::onCancel() { 78 | ::SetWindowLongPtr(hwnd_, DWLP_MSGRESULT, PSNRET_NOERROR); 79 | } 80 | 81 | } // namespace Ime 82 | -------------------------------------------------------------------------------- /PropertyPage.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_PROPERTY_PAGE_H 21 | #define IME_PROPERTY_PAGE_H 22 | 23 | #include "Dialog.h" 24 | #include 25 | 26 | namespace Ime { 27 | 28 | class PropertyPage: public Dialog { 29 | public: 30 | friend class PropertyDialog; 31 | 32 | PropertyPage(LPCTSTR dialogId); 33 | virtual ~PropertyPage(void); 34 | 35 | protected: 36 | void setup(PROPSHEETPAGE& page); 37 | static INT_PTR CALLBACK _dlgProc(HWND hwnd , UINT msg, WPARAM wp , LPARAM lp); 38 | virtual LRESULT wndProc(UINT msg, WPARAM wp, LPARAM lp); 39 | virtual void onOK(); 40 | virtual void onCancel(); 41 | 42 | private: 43 | LPCTSTR dialogId_; 44 | }; 45 | 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /TextService.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_TEXT_SERVICE_H 21 | #define IME_TEXT_SERVICE_H 22 | 23 | #include "libIME.h" 24 | #include 25 | #include "EditSession.h" 26 | #include "KeyEvent.h" 27 | #include "ComPtr.h" 28 | #include "DisplayAttributeInfo.h" 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | // for Windows 8 support 35 | #ifndef TF_TMF_IMMERSIVEMODE // this is defined in Win 8 SDK 36 | #define TF_TMF_IMMERSIVEMODE 0x40000000 37 | #endif 38 | 39 | namespace Ime { 40 | 41 | class ImeModule; 42 | class LangBarButton; 43 | 44 | class TextService: 45 | // TSF interfaces 46 | public ITfTextInputProcessorEx, 47 | // event sinks 48 | public ITfThreadMgrEventSink, 49 | public ITfTextEditSink, 50 | public ITfKeyEventSink, 51 | public ITfCompositionSink, 52 | public ITfCompartmentEventSink, 53 | public ITfLangBarEventSink, 54 | public ITfActiveLanguageProfileNotifySink { 55 | public: 56 | 57 | enum CommandType { // used in onCommand() 58 | COMMAND_LEFT_CLICK, 59 | COMMAND_RIGHT_CLICK, 60 | COMMAND_MENU 61 | }; 62 | 63 | TextService(ImeModule* module); 64 | 65 | // public methods 66 | ImeModule* imeModule() const; 67 | 68 | ITfThreadMgr* threadMgr() const; 69 | 70 | TfClientId clientId() const; 71 | 72 | ITfContext* currentContext(); 73 | 74 | bool isActivated() const { 75 | return (threadMgr() != NULL); 76 | } 77 | 78 | DWORD activateFlags() const { 79 | return activateFlags_; 80 | } 81 | 82 | // running in Windows 8 app mode 83 | bool isImmersive() const { 84 | return (activateFlags_ & TF_TMF_IMMERSIVEMODE) != 0; 85 | } 86 | 87 | // alias of isImmersive() 88 | bool isMetroApp() const { 89 | return isImmersive(); 90 | } 91 | 92 | // UI less mode is enabled (for ex: in fullscreen games) 93 | bool isUiLess() const { 94 | return (activateFlags_ & TF_TMF_UIELEMENTENABLEDONLY) != 0; 95 | } 96 | 97 | // is in console mode 98 | bool isConsole() const { 99 | return (activateFlags_ & TF_TMF_CONSOLE) != 0; 100 | } 101 | 102 | DWORD langBarStatus() const; 103 | 104 | // language bar buttons 105 | void addButton(LangBarButton* button); 106 | void removeButton(LangBarButton* button); 107 | 108 | // preserved keys 109 | void addPreservedKey(UINT keyCode, UINT modifiers, const GUID& guid); 110 | void removePreservedKey(const GUID& guid); 111 | 112 | // text composition handling 113 | bool isComposing(); 114 | 115 | // is keyboard disabled for the context (NULL means current context) 116 | bool isKeyboardDisabled(ITfContext* context = NULL); 117 | 118 | // is keyboard opened for the whole thread 119 | bool isKeyboardOpened(); 120 | void setKeyboardOpen(bool open); 121 | 122 | bool isInsertionAllowed(EditSession* session); 123 | void startComposition(ITfContext* context); 124 | void endComposition(ITfContext* context); 125 | bool compositionRect(EditSession* session, RECT* rect); 126 | bool selectionRect(EditSession* session, RECT* rect); 127 | HWND compositionWindow(EditSession* session); 128 | 129 | std::wstring compositionString(EditSession* session); 130 | void setCompositionString(EditSession* session, const wchar_t* str, int len); 131 | void setCompositionCursor(EditSession* session, int pos); 132 | 133 | // compartment handling 134 | ComPtr globalCompartment(const GUID& key); 135 | ComPtr threadCompartment(const GUID& key); 136 | ComPtr contextCompartment(const GUID& key, ITfContext* context = NULL); 137 | 138 | DWORD globalCompartmentValue(const GUID& key); 139 | DWORD threadCompartmentValue(const GUID& key); 140 | DWORD contextCompartmentValue(const GUID& key, ITfContext* context = NULL); 141 | 142 | void setGlobalCompartmentValue(const GUID& key, DWORD value); 143 | void setThreadCompartmentValue(const GUID& key, DWORD value); 144 | void setContextCompartmentValue(const GUID& key, DWORD value, ITfContext* context = NULL); 145 | 146 | // manage sinks to global or thread compartment (context specific compartment is not used) 147 | void addCompartmentMonitor(const GUID key, bool isGlobal = false); 148 | void removeCompartmentMonitor(const GUID key); 149 | 150 | // virtual functions that IME implementors may need to override 151 | virtual void onActivate(); 152 | virtual void onDeactivate(); 153 | 154 | virtual void onSetFocus(); 155 | virtual void onKillFocus(); 156 | 157 | virtual bool filterKeyDown(KeyEvent& keyEvent); 158 | virtual bool onKeyDown(KeyEvent& keyEvent, EditSession* session); 159 | 160 | virtual bool filterKeyUp(KeyEvent& keyEvent); 161 | virtual bool onKeyUp(KeyEvent& keyEvent, EditSession* session); 162 | 163 | virtual bool onPreservedKey(const GUID& guid); 164 | 165 | // called when a language button or menu item is clicked 166 | virtual bool onCommand(UINT id, CommandType type); 167 | 168 | // called when a value in the global or thread compartment changed. 169 | virtual void onCompartmentChanged(const GUID& key); 170 | 171 | virtual void onLangBarStatusChanged(int newStatus); 172 | 173 | // called when the keyboard is opened or closed 174 | virtual void onKeyboardStatusChanged(bool opened); 175 | 176 | // called just before current composition is terminated for doing cleanup. 177 | // if forced is true, the composition is terminated by others, such as 178 | // the input focus is grabbed by another application. 179 | // if forced is false, the composition is terminated gracefully by endComposition(). 180 | virtual void onCompositionTerminated(bool forced); 181 | 182 | // called when a language profile is activated (only useful for text services that supports multiple language profiles) 183 | virtual void onLangProfileActivated(REFGUID guidProfile); 184 | 185 | // called when a language profile is deactivated 186 | virtual void onLangProfileDeactivated(REFGUID guidProfile); 187 | 188 | // COM related stuff 189 | public: 190 | friend class DisplayAttributeInfoEnum; 191 | 192 | // IUnknown 193 | STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); 194 | STDMETHODIMP_(ULONG) AddRef(void); 195 | STDMETHODIMP_(ULONG) Release(void); 196 | 197 | // ITfTextInputProcessor 198 | STDMETHODIMP Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClientId); 199 | STDMETHODIMP Deactivate(); 200 | 201 | // ITfTextInputProcessorEx 202 | STDMETHODIMP ActivateEx(ITfThreadMgr *ptim, TfClientId tid, DWORD dwFlags); 203 | 204 | // ITfThreadMgrEventSink 205 | STDMETHODIMP OnInitDocumentMgr(ITfDocumentMgr *pDocMgr); 206 | STDMETHODIMP OnUninitDocumentMgr(ITfDocumentMgr *pDocMgr); 207 | STDMETHODIMP OnSetFocus(ITfDocumentMgr *pDocMgrFocus, ITfDocumentMgr *pDocMgrPrevFocus); 208 | STDMETHODIMP OnPushContext(ITfContext *pContext); 209 | STDMETHODIMP OnPopContext(ITfContext *pContext); 210 | 211 | // ITfTextEditSink 212 | STDMETHODIMP OnEndEdit(ITfContext *pContext, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord); 213 | 214 | // ITfKeyEventSink 215 | STDMETHODIMP OnSetFocus(BOOL fForeground); 216 | STDMETHODIMP OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten); 217 | STDMETHODIMP OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten); 218 | STDMETHODIMP OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten); 219 | STDMETHODIMP OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten); 220 | STDMETHODIMP OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pfEaten); 221 | 222 | // ITfCompositionSink 223 | STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition); 224 | 225 | // ITfCompartmentEventSink 226 | STDMETHODIMP OnChange(REFGUID rguid); 227 | 228 | // ITfLangBarEventSink 229 | STDMETHODIMP OnSetFocus(DWORD dwThreadId); 230 | STDMETHODIMP OnThreadTerminate(DWORD dwThreadId); 231 | STDMETHODIMP OnThreadItemChange(DWORD dwThreadId); 232 | STDMETHODIMP OnModalInput(DWORD dwThreadId, UINT uMsg, WPARAM wParam, LPARAM lParam); 233 | STDMETHODIMP ShowFloating(DWORD dwFlags); 234 | STDMETHODIMP GetItemFloatingRect(DWORD dwThreadId, REFGUID rguid, RECT *prc); 235 | 236 | // ITfActiveLanguageProfileNotifySink 237 | STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL fActivated); 238 | 239 | protected: 240 | // edit session classes, used with TSF 241 | class KeyEditSession: public EditSession { 242 | public: 243 | KeyEditSession(TextService* service, ITfContext* context, KeyEvent& keyEvent): 244 | EditSession(service, context), 245 | keyEvent_(keyEvent), 246 | result_(false) { 247 | } 248 | STDMETHODIMP DoEditSession(TfEditCookie ec); 249 | 250 | KeyEvent keyEvent_; 251 | bool result_; 252 | }; 253 | 254 | class StartCompositionEditSession: public EditSession { 255 | public: 256 | StartCompositionEditSession(TextService* service, ITfContext* context): 257 | EditSession(service, context){ 258 | } 259 | STDMETHODIMP DoEditSession(TfEditCookie ec); 260 | }; 261 | 262 | class EndCompositionEditSession: public EditSession { 263 | public: 264 | EndCompositionEditSession(TextService* service, ITfContext* context): 265 | EditSession(service, context){ 266 | } 267 | STDMETHODIMP DoEditSession(TfEditCookie ec); 268 | }; 269 | 270 | HRESULT doKeyEditSession(TfEditCookie cookie, KeyEditSession* session); 271 | HRESULT doStartCompositionEditSession(TfEditCookie cookie, StartCompositionEditSession* session); 272 | HRESULT doEndCompositionEditSession(TfEditCookie cookie, EndCompositionEditSession* session); 273 | 274 | struct PreservedKey : public TF_PRESERVEDKEY { 275 | GUID guid; 276 | }; 277 | 278 | struct CompartmentMonitor { 279 | GUID guid; 280 | DWORD cookie; 281 | bool isGlobal; 282 | 283 | bool operator == (const GUID& other) const { 284 | return bool(::IsEqualGUID(guid, other)); 285 | } 286 | }; 287 | 288 | protected: // COM object should not be deleted directly. calling Release() instead. 289 | virtual ~TextService(void); 290 | 291 | private: 292 | ComPtr module_; 293 | ComPtr threadMgr_; 294 | TfClientId clientId_; 295 | DWORD activateFlags_; 296 | bool isKeyboardOpened_; 297 | 298 | // event sink cookies 299 | DWORD threadMgrEventSinkCookie_; 300 | DWORD textEditSinkCookie_; 301 | DWORD compositionSinkCookie_; 302 | DWORD keyboardOpenEventSinkCookie_; 303 | DWORD globalCompartmentEventSinkCookie_; 304 | DWORD langBarSinkCookie_; 305 | DWORD activateLanguageProfileNotifySinkCookie_; 306 | 307 | ITfComposition* composition_; // acquired when starting composition, released when ending composition 308 | ComPtr langBarMgr_; 309 | std::vector> langBarButtons_; 310 | std::vector preservedKeys_; 311 | std::vector compartmentMonitors_; 312 | 313 | long refCount_; // reference counting 314 | }; 315 | 316 | } 317 | 318 | #endif 319 | -------------------------------------------------------------------------------- /Utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "Utils.h" 21 | #include 22 | #include 23 | 24 | std::wstring utf8ToUtf16(const char* text) { 25 | std::wstring wtext; 26 | int wlen = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); 27 | if(wlen > 1) { // length includes terminating null 28 | wtext.resize(wlen - 1); 29 | // well, this is a little bit dirty, but it works! 30 | wlen = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, &wtext[0], wlen); 31 | } 32 | return wtext; 33 | } 34 | 35 | std::string utf16ToUtf8(const wchar_t* wtext) { 36 | std::string text; 37 | int len = ::WideCharToMultiByte(CP_UTF8, 0, wtext, -1, NULL, 0, NULL, NULL); 38 | if(len > 1) { // length includes terminating null 39 | text.resize(len - 1); 40 | // well, this is a little bit dirty, but it works! 41 | len = ::WideCharToMultiByte(CP_UTF8, 0, wtext, -1, &text[0], len, NULL, NULL); 42 | } 43 | return text; 44 | } 45 | 46 | std::wstring tradToSimpChinese(const std::wstring& trad) { 47 | int len = ::LCMapStringW(0x0404, LCMAP_SIMPLIFIED_CHINESE, trad.c_str(), trad.length(), NULL, 0); 48 | std::wstring simp; 49 | simp.resize(len); 50 | if(::LCMapStringW(0x0404, LCMAP_SIMPLIFIED_CHINESE, trad.c_str(), trad.length(), &simp[0], len)) 51 | return simp; 52 | return trad; 53 | } 54 | -------------------------------------------------------------------------------- /Utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef IME_UTILS_H 21 | #define IME_UTILS_H 22 | #pragma once 23 | 24 | #include 25 | 26 | std::wstring utf8ToUtf16(const char* text); 27 | 28 | std::string utf16ToUtf8(const wchar_t* wtext); 29 | 30 | // convert traditional Chinese to simplified Chinese 31 | std::wstring tradToSimpChinese(const std::wstring& trad); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Window.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #include "Window.h" 21 | 22 | namespace Ime { 23 | 24 | static TCHAR g_imeWindowClassName[] = _T("LibImeWindow"); 25 | static HINSTANCE g_hinstance = NULL; 26 | 27 | std::map Window::hwndMap_; 28 | 29 | Window::Window(): 30 | hwnd_(NULL) { 31 | } 32 | 33 | Window::~Window() { 34 | if(hwnd_) 35 | DestroyWindow(hwnd_); 36 | } 37 | 38 | bool Window::create(HWND parent, DWORD style, DWORD exStyle) { 39 | hwnd_ = CreateWindowEx(exStyle, g_imeWindowClassName, NULL, style, 40 | 0, 0, 0, 0, parent, NULL, g_hinstance, NULL); 41 | if(hwnd_) { 42 | // associate this object with the hwnd 43 | hwndMap_[hwnd_] = this; 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | void Window::destroy(void) { 50 | if( hwnd_ ) 51 | DestroyWindow(hwnd_); 52 | hwnd_ = NULL; 53 | } 54 | 55 | // static 56 | LRESULT Window::_wndProc(HWND hwnd , UINT msg, WPARAM wp , LPARAM lp) { 57 | // get object pointer from the hwnd 58 | Window* window = (Window*)hwndMap_[hwnd]; 59 | // FIXME: we cannot handle WM_CREATE in Window::wndProc member function 60 | // because the message is sent before CreateWindow returns, and 61 | // we do SetWindowLongPtr() only after CreateWindow(). 62 | if(window) { 63 | LRESULT result = window->wndProc(msg, wp, lp); 64 | if(msg == WM_NCDESTROY) 65 | hwndMap_.erase(hwnd); 66 | return result; 67 | } 68 | return ::DefWindowProc(hwnd, msg, wp, lp); 69 | } 70 | 71 | LRESULT Window::wndProc(UINT msg, WPARAM wp , LPARAM lp) { 72 | return ::DefWindowProc(hwnd_, msg, wp, lp); 73 | } 74 | 75 | bool Window::registerClass(HINSTANCE hinstance) { 76 | 77 | if(!g_hinstance) 78 | g_hinstance = hinstance; 79 | 80 | WNDCLASSEX wc; 81 | wc.cbSize = sizeof(WNDCLASSEX); 82 | wc.style = CS_IME; // FIXME: is this needed? 83 | wc.lpfnWndProc = (WNDPROC)Window::_wndProc; 84 | wc.cbClsExtra = 0; 85 | wc.cbWndExtra = 0; 86 | wc.hInstance = hinstance; 87 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 88 | wc.hIcon = NULL; 89 | wc.lpszMenuName = (LPTSTR)NULL; 90 | wc.lpszClassName = g_imeWindowClassName; 91 | wc.hbrBackground = NULL; 92 | wc.hIconSm = NULL; 93 | 94 | if(!::RegisterClassEx((LPWNDCLASSEX)&wc)) 95 | return false; 96 | return true; 97 | } 98 | 99 | } // namespace Ime 100 | -------------------------------------------------------------------------------- /Window.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #if !defined(AFX_WINDOW_H__86D89A4E_5040_4FF8_B991_0C7D6502119D__INCLUDED_) 21 | #define AFX_WINDOW_H__86D89A4E_5040_4FF8_B991_0C7D6502119D__INCLUDED_ 22 | 23 | #if _MSC_VER > 1000 24 | #pragma once 25 | #endif // _MSC_VER > 1000 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | namespace Ime { 32 | 33 | class Window { 34 | public: 35 | 36 | Window(); 37 | virtual ~Window(); 38 | 39 | HWND hwnd(){ return hwnd_; } 40 | 41 | bool create(HWND parent, DWORD style, DWORD exStyle = 0); 42 | void destroy(void); 43 | 44 | bool isVisible(){ 45 | return !!IsWindowVisible(hwnd_); 46 | } 47 | 48 | bool isWindow(){ 49 | return !!IsWindow(hwnd_); 50 | } 51 | 52 | void size(int* width, int* height) { 53 | RECT rc; 54 | rect(&rc); 55 | *width = (rc.right - rc.left); 56 | *height = (rc.bottom - rc.top); 57 | } 58 | 59 | void resize(int width, int height) { 60 | ::SetWindowPos(hwnd_, HWND_TOP, 0, 0, width, height, SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE); 61 | } 62 | 63 | void clientRect(RECT* rect) { 64 | ::GetClientRect(hwnd_, rect); 65 | } 66 | 67 | void rect(RECT* rect) { 68 | ::GetWindowRect(hwnd_, rect); 69 | } 70 | 71 | void show() { 72 | if( hwnd_ ) 73 | ShowWindow(hwnd_, SW_SHOWNA); 74 | } 75 | 76 | void hide(){ ShowWindow(hwnd_, SW_HIDE); } 77 | 78 | void refresh() { InvalidateRect( hwnd_, NULL, FALSE ); } 79 | 80 | static bool registerClass(HINSTANCE hinstance); 81 | 82 | static Window* fromHwnd(HWND hwnd) { 83 | std::map::iterator it = hwndMap_.find(hwnd); 84 | return it != hwndMap_.end() ? it->second : NULL; 85 | } 86 | 87 | protected: 88 | static LRESULT _wndProc(HWND hwnd , UINT msg, WPARAM wp , LPARAM lp); 89 | virtual LRESULT wndProc(UINT msg, WPARAM wp , LPARAM lp); 90 | 91 | protected: 92 | HWND hwnd_; 93 | static std::map hwndMap_; 94 | }; 95 | 96 | } 97 | 98 | #endif // !defined(AFX_WINDOW_H__86D89A4E_5040_4FF8_B991_0C7D6502119D__INCLUDED_) 99 | -------------------------------------------------------------------------------- /WindowsVersion.h: -------------------------------------------------------------------------------- 1 | #ifndef IME_WINDOWS_VERSION_H 2 | #define IME_WINDOWS_VERSION_H 3 | #pragma once 4 | 5 | #include 6 | 7 | namespace Ime { 8 | 9 | // Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724832(v=vs.85).aspx 10 | 11 | class WindowsVersion { 12 | public: 13 | WindowsVersion(void) { 14 | // check Windows version (windows 8 is 6.2, and 7 is 6.1) 15 | DWORD winVer = ::GetVersion(); 16 | major_ = (DWORD)(LOBYTE(LOWORD(winVer))); 17 | minor_ = (DWORD)(HIBYTE(LOWORD(winVer))); 18 | } 19 | 20 | ~WindowsVersion(void) { 21 | } 22 | 23 | bool isWindows8Above() const { 24 | // Windows 8: 6.2 25 | return (major_ > 6 || (major_ == 6 && minor_ >= 2)); 26 | } 27 | 28 | bool isWindows7() const { 29 | // Windows 7: 6.1 30 | // Windows server 2008 R2: 6.1 31 | return (major_ == 6 && minor_ == 1); 32 | } 33 | 34 | bool isWindowsVista() const { 35 | // Windows Vista: 6.0 36 | // Windows server 2008: 6.0 37 | return (major_ == 6 && minor_ == 0); 38 | } 39 | 40 | bool isWindowsXp() const { 41 | // Windows xp: 5.1 42 | // Windows xp 64 bit, server 2003: 5.2 43 | return (major_ == 5 && (minor_ == 1 || minor_ == 2)); 44 | } 45 | 46 | DWORD major() const { 47 | return major_; 48 | } 49 | 50 | DWORD minor() const { 51 | return minor_; 52 | } 53 | 54 | private: 55 | DWORD major_; 56 | DWORD minor_; 57 | }; 58 | 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /libIME.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | // libIME.cpp : Defines the exported functions for the DLL application. 21 | 22 | #include "libIME.h" 23 | -------------------------------------------------------------------------------- /libIME.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Hong Jen Yee (PCMan) 3 | // 4 | // This library is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU Library General Public 6 | // License as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | // Library General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Library General Public 15 | // License along with this library; if not, write to the 16 | // Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 17 | // Boston, MA 02110-1301, USA. 18 | // 19 | 20 | #ifndef LIB_IME_H 21 | #define LIB_IME_H 22 | 23 | // The following ifdef block is the standard way of creating macros which make exporting 24 | // from a DLL simpler. All files within this DLL are compiled with the LIBIME_EXPORTS 25 | // symbol defined on the command line. This symbol should not be defined on any project 26 | // that uses this DLL. This way any other project whose source files include this file see 27 | // LIBIME_API functions as being imported from a DLL, whereas this DLL sees symbols 28 | // defined with this macro as being exported. 29 | #ifdef LIBIME_EXPORTS 30 | #define LIBIME_API __declspec(dllexport) 31 | #else 32 | #define LIBIME_API __declspec(dllimport) 33 | #endif 34 | 35 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 36 | // Windows Header Files: 37 | #include 38 | 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /libIME.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EasyIME/libIME/d347cb63747c510a599df08602b7aeb060726dc3/libIME.rc -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by libIME.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | --------------------------------------------------------------------------------