comparison src/video/win32/SDL_win32keyboard.c @ 4913:6b89d83b0b5a

Windows candidate list support. Candidate list should now be drawn and function normally. Tested in XP and 7.
author Daniel Wyatt <Daniel.Wyatt@gmail.com>
date Tue, 23 Nov 2010 17:46:47 -0500
parents ac19d74e2a47
children cc7ac6aaac5d
comparison
equal deleted inserted replaced
4912:37576cdf6751 4913:6b89d83b0b5a
98 data->ime_hwnd_current = 0; 98 data->ime_hwnd_current = 0;
99 data->ime_himc = 0; 99 data->ime_himc = 0;
100 data->ime_composition[0] = 0; 100 data->ime_composition[0] = 0;
101 data->ime_readingstring[0] = 0; 101 data->ime_readingstring[0] = 0;
102 data->ime_cursor = 0; 102 data->ime_cursor = 0;
103
104 data->ime_candlist = SDL_FALSE;
105 SDL_memset(data->ime_candidates, 0, sizeof(data->ime_candidates));
106 data->ime_candcount = 0;
107 data->ime_candref = 0;
108 data->ime_candsel = 0;
109 data->ime_candpgsize = 0;
110 data->ime_candlistindexbase = 0;
111 data->ime_candvertical = SDL_TRUE;
112
113 data->ime_candtex = NULL;
114 data->ime_dirty = SDL_FALSE;
115 SDL_memset(&data->ime_rect, 0, sizeof(data->ime_rect));
116 SDL_memset(&data->ime_candlistrect, 0, sizeof(data->ime_candlistrect));
117 data->ime_winwidth = 0;
118 data->ime_winheight = 0;
119
103 data->ime_hkl = 0; 120 data->ime_hkl = 0;
104 data->ime_himm32 = 0; 121 data->ime_himm32 = 0;
105 data->GetReadingString = 0; 122 data->GetReadingString = 0;
106 data->ShowReadingWindow = 0; 123 data->ShowReadingWindow = 0;
107 data->ImmLockIMC = 0; 124 data->ImmLockIMC = 0;
163 { 180 {
164 SDL_Window *window = SDL_GetKeyboardFocus(); 181 SDL_Window *window = SDL_GetKeyboardFocus();
165 if (window) { 182 if (window) {
166 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd; 183 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
167 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata; 184 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
185 SDL_GetWindowSize(window, &videodata->ime_winwidth, &videodata->ime_winheight);
168 IME_Init(videodata, hwnd); 186 IME_Init(videodata, hwnd);
169 IME_Enable(videodata, hwnd); 187 IME_Enable(videodata, hwnd);
170 } 188 }
171 } 189 }
172 190
183 } 201 }
184 202
185 void 203 void
186 WIN_SetTextInputRect(_THIS, SDL_Rect *rect) 204 WIN_SetTextInputRect(_THIS, SDL_Rect *rect)
187 { 205 {
188 206 SDL_VideoData *videodata = (SDL_VideoData *)_this->driverdata;
207 videodata->ime_rect = *rect;
189 } 208 }
190 209
191 #ifdef __GNUC__ 210 #ifdef __GNUC__
192 #undef DEFINE_GUID 211 #undef DEFINE_GUID
193 #define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} 212 #define DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
194 DEFINE_GUID(IID_ITfInputProcessorProfileActivationSink, 0x71C6E74E,0x0F28,0x11D8,0xA8,0x2A,0x00,0x06,0x5B,0x84,0x43,0x5C); 213 DEFINE_GUID(IID_ITfInputProcessorProfileActivationSink, 0x71C6E74E,0x0F28,0x11D8,0xA8,0x2A,0x00,0x06,0x5B,0x84,0x43,0x5C);
195 DEFINE_GUID(IID_ITfUIElementSink, 0xEA1EA136,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C); 214 DEFINE_GUID(IID_ITfUIElementSink, 0xEA1EA136,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
196 DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD, 0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31); 215 DEFINE_GUID(GUID_TFCAT_TIP_KEYBOARD, 0x34745C63,0xB2F0,0x4784,0x8B,0x67,0x5E,0x12,0xC8,0x70,0x1A,0x31);
197 DEFINE_GUID(IID_ITfSource, 0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7); 216 DEFINE_GUID(IID_ITfSource, 0x4EA48A35,0x60AE,0x446F,0x8F,0xD6,0xE6,0xA8,0xD8,0x24,0x59,0xF7);
198 DEFINE_GUID(IID_ITfUIElementMgr, 0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C); 217 DEFINE_GUID(IID_ITfUIElementMgr, 0xEA1EA135,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
218 DEFINE_GUID(IID_ITfCandidateListUIElement, 0xEA1EA138,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
199 DEFINE_GUID(IID_ITfReadingInformationUIElement, 0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C); 219 DEFINE_GUID(IID_ITfReadingInformationUIElement, 0xEA1EA139,0x19DF,0x11D7,0xA6,0xD2,0x00,0x06,0x5B,0x84,0x43,0x5C);
200 DEFINE_GUID(IID_ITfThreadMgr, 0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E); 220 DEFINE_GUID(IID_ITfThreadMgr, 0xAA80E801,0x2021,0x11D2,0x93,0xE0,0x00,0x60,0xB0,0x67,0xB8,0x6E);
201 DEFINE_GUID(CLSID_TF_ThreadMgr, 0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50); 221 DEFINE_GUID(CLSID_TF_ThreadMgr, 0x529A9E6B,0x6587,0x4F23,0xAB,0x9E,0x9C,0x7D,0x68,0x3E,0x3C,0x50);
202 DEFINE_GUID(IID_ITfThreadMgrEx, 0x3E90ADE3,0x7594,0x4CB0,0xBB,0x58,0x69,0x62,0x8F,0x5F,0x45,0x8C); 222 DEFINE_GUID(IID_ITfThreadMgrEx, 0x3E90ADE3,0x7594,0x4CB0,0xBB,0x58,0x69,0x62,0x8F,0x5F,0x45,0x8C);
203 #endif 223 #endif
241 static void IME_ClearComposition(SDL_VideoData *videodata); 261 static void IME_ClearComposition(SDL_VideoData *videodata);
242 static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd); 262 static void IME_SetWindow(SDL_VideoData* videodata, HWND hwnd);
243 static void IME_SetupAPI(SDL_VideoData *videodata); 263 static void IME_SetupAPI(SDL_VideoData *videodata);
244 static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex); 264 static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex);
245 static void IME_SendEditingEvent(SDL_VideoData *videodata); 265 static void IME_SendEditingEvent(SDL_VideoData *videodata);
266 static void IME_DestroyTextures(SDL_VideoData *videodata);
267
246 #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2) 268 #define SDL_IsEqualIID(riid1, riid2) SDL_IsEqualGUID(riid1, riid2)
247 #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID))) 269 #define SDL_IsEqualGUID(rguid1, rguid2) (!SDL_memcmp(rguid1, rguid2, sizeof(GUID)))
248 270
249 static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata); 271 static SDL_bool UILess_SetupSinks(SDL_VideoData *videodata);
250 static void UILess_ReleaseSinks(SDL_VideoData *videodata); 272 static void UILess_ReleaseSinks(SDL_VideoData *videodata);
343 } 365 }
344 if (videodata->ime_com_initialized) { 366 if (videodata->ime_com_initialized) {
345 CoUninitialize(); 367 CoUninitialize();
346 videodata->ime_com_initialized = SDL_FALSE; 368 videodata->ime_com_initialized = SDL_FALSE;
347 } 369 }
370 IME_DestroyTextures(videodata);
348 videodata->ime_initialized = SDL_FALSE; 371 videodata->ime_initialized = SDL_FALSE;
349 } 372 }
350 373
351 static void 374 static void
352 IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd) 375 IME_GetReadingString(SDL_VideoData *videodata, HWND hwnd)
451 474
452 static void 475 static void
453 IME_InputLangChanged(SDL_VideoData *videodata) 476 IME_InputLangChanged(SDL_VideoData *videodata)
454 { 477 {
455 UINT lang = PRIMLANG(); 478 UINT lang = PRIMLANG();
456 HWND hwndime = 0;
457 IME_UpdateInputLocale(videodata); 479 IME_UpdateInputLocale(videodata);
480 if (!videodata->ime_uiless)
481 videodata->ime_candlistindexbase = (videodata->ime_hkl == CHT_HKL_DAYI) ? 0 : 1;
482
458 IME_SetupAPI(videodata); 483 IME_SetupAPI(videodata);
459 if (lang != PRIMLANG()) { 484 if (lang != PRIMLANG()) {
460 IME_ClearComposition(videodata); 485 IME_ClearComposition(videodata);
461 }
462 hwndime = ImmGetDefaultIMEWnd(videodata->ime_hwnd_current);
463 if (hwndime) {
464 SendMessageA(hwndime, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0);
465 SendMessageA(hwndime, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0);
466 } 486 }
467 } 487 }
468 488
469 static DWORD 489 static DWORD
470 IME_GetId(SDL_VideoData *videodata, UINT uIndex) 490 IME_GetId(SDL_VideoData *videodata, UINT uIndex)
608 videodata->ime_hkl = GetKeyboardLayout(0); 628 videodata->ime_hkl = GetKeyboardLayout(0);
609 if (hklprev == videodata->ime_hkl) 629 if (hklprev == videodata->ime_hkl)
610 return; 630 return;
611 631
612 hklprev = videodata->ime_hkl; 632 hklprev = videodata->ime_hkl;
633 switch (PRIMLANG())
634 {
635 case LANG_CHINESE:
636 videodata->ime_candvertical = SDL_TRUE;
637 if (SUBLANG() == SUBLANG_CHINESE_SIMPLIFIED)
638 videodata->ime_candvertical = SDL_FALSE;
639
640 break;
641 case LANG_JAPANESE:
642 videodata->ime_candvertical = SDL_TRUE;
643 break;
644 case LANG_KOREAN:
645 videodata->ime_candvertical = SDL_FALSE;
646 break;
647 }
613 } 648 }
614 649
615 static void 650 static void
616 IME_ClearComposition(SDL_VideoData *videodata) 651 IME_ClearComposition(SDL_VideoData *videodata)
617 { 652 {
628 ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR)); 663 ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR));
629 664
630 ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0); 665 ImmNotifyIME(himc, NI_CLOSECANDIDATE, 0, 0);
631 ImmReleaseContext(videodata->ime_hwnd_current, himc); 666 ImmReleaseContext(videodata->ime_hwnd_current, himc);
632 SDL_SendEditingText("", 0, 0); 667 SDL_SendEditingText("", 0, 0);
633 }
634
635 static void
636 IME_ClearEditing(SDL_VideoData *videodata)
637 {
638
639 } 668 }
640 669
641 static void 670 static void
642 IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string) 671 IME_GetCompositionString(SDL_VideoData *videodata, HIMC himc, DWORD string)
643 { 672 {
688 s = WIN_StringToUTF8(buffer); 717 s = WIN_StringToUTF8(buffer);
689 SDL_SendEditingText(s, videodata->ime_cursor + SDL_wcslen(videodata->ime_readingstring), 0); 718 SDL_SendEditingText(s, videodata->ime_cursor + SDL_wcslen(videodata->ime_readingstring), 0);
690 SDL_free(s); 719 SDL_free(s);
691 } 720 }
692 721
722 static void
723 IME_AddCandidate(SDL_VideoData *videodata, UINT i, LPCWSTR candidate)
724 {
725 LPWSTR dst = videodata->ime_candidates[i];
726 *dst++ = (WCHAR)(TEXT('0') + ((i + videodata->ime_candlistindexbase) % 10));
727 if (videodata->ime_candvertical)
728 *dst++ = TEXT(' ');
729
730 while (*candidate && (SDL_arraysize(videodata->ime_candidates[i]) > (dst - videodata->ime_candidates[i])))
731 *dst++ = *candidate++;
732
733 *dst = (WCHAR)'\0';
734 }
735
736 static void
737 IME_GetCandidateList(HIMC himc, SDL_VideoData *videodata)
738 {
739 LPCANDIDATELIST cand_list = 0;
740 DWORD size = ImmGetCandidateListW(himc, 0, 0, 0);
741 if (size)
742 {
743 cand_list = (LPCANDIDATELIST)SDL_malloc(size);
744 if (cand_list)
745 {
746 size = ImmGetCandidateListW(himc, 0, cand_list, size);
747 if (size)
748 {
749 int i = 0;
750 int j = 0;
751 int page_start = 0;
752 videodata->ime_candsel = cand_list->dwSelection;
753 videodata->ime_candcount = cand_list->dwCount;
754
755 if (LANG() == LANG_CHS && IME_GetId(videodata, 0))
756 {
757 const UINT maxcandchar = 18;
758 UINT i = 0;
759 UINT cchars = 0;
760
761 for (; i < videodata->ime_candcount; ++i)
762 {
763 UINT len = SDL_wcslen((LPWSTR)((DWORD)cand_list + cand_list->dwOffset[i])) + 1;
764 if (len + cchars > maxcandchar)
765 {
766 if (i > cand_list->dwSelection)
767 break;
768
769 page_start = i;
770 cchars = len;
771 }
772 else
773 {
774 cchars += len;
775 }
776 }
777 videodata->ime_candpgsize = i - page_start;
778 }
779 else
780 {
781 videodata->ime_candpgsize = SDL_min(cand_list->dwPageSize, MAX_CANDLIST);
782 page_start = (cand_list->dwSelection / videodata->ime_candpgsize) * videodata->ime_candpgsize;
783 }
784 SDL_memset(&videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
785 for (i = page_start, j = 0; (DWORD)i < cand_list->dwCount && j < (int)videodata->ime_candpgsize; i++, j++)
786 {
787 LPCWSTR candidate = (LPCWSTR)((DWORD)cand_list + cand_list->dwOffset[i]);
788 IME_AddCandidate(videodata, j, candidate);
789 }
790 if (PRIMLANG() == LANG_KOREAN || (PRIMLANG() == LANG_CHT && !IME_GetId(videodata, 0)))
791 videodata->ime_candsel = -1;
792
793 }
794 SDL_free(cand_list);
795 }
796 }
797 }
798
799 static void
800 IME_ShowCandidateList(SDL_VideoData *videodata)
801 {
802 videodata->ime_dirty = SDL_TRUE;
803 videodata->ime_candlist = SDL_TRUE;
804 IME_DestroyTextures(videodata);
805 IME_SendEditingEvent(videodata);
806 }
807
808 static void
809 IME_HideCandidateList(SDL_VideoData *videodata)
810 {
811 videodata->ime_dirty = SDL_FALSE;
812 videodata->ime_candlist = SDL_FALSE;
813 IME_DestroyTextures(videodata);
814 IME_SendEditingEvent(videodata);
815 }
816
693 SDL_bool 817 SDL_bool
694 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata) 818 IME_HandleMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam, SDL_VideoData *videodata)
695 { 819 {
696 SDL_bool trap = SDL_FALSE; 820 SDL_bool trap = SDL_FALSE;
697 HIMC himc = 0; 821 HIMC himc = 0;
699 return SDL_FALSE; 823 return SDL_FALSE;
700 824
701 switch (msg) 825 switch (msg)
702 { 826 {
703 case WM_INPUTLANGCHANGE: 827 case WM_INPUTLANGCHANGE:
704 //IME_InputLangChanged(videodata); 828 IME_InputLangChanged(videodata);
705 break; 829 break;
706 case WM_IME_SETCONTEXT: 830 case WM_IME_SETCONTEXT:
707 *lParam = 0; 831 *lParam = 0;
708 break; 832 break;
709 case WM_IME_STARTCOMPOSITION: 833 case WM_IME_STARTCOMPOSITION:
738 case IMN_SETOPENSTATUS: 862 case IMN_SETOPENSTATUS:
739 IME_UpdateInputLocale(videodata); 863 IME_UpdateInputLocale(videodata);
740 break; 864 break;
741 case IMN_OPENCANDIDATE: 865 case IMN_OPENCANDIDATE:
742 case IMN_CHANGECANDIDATE: 866 case IMN_CHANGECANDIDATE:
867 if (videodata->ime_uiless)
868 break;
869
743 trap = SDL_TRUE; 870 trap = SDL_TRUE;
871 IME_ShowCandidateList(videodata);
872 himc = ImmGetContext(hwnd);
873 if (!himc)
874 break;
875
876 IME_GetCandidateList(himc, videodata);
877 ImmReleaseContext(hwnd, himc);
744 break; 878 break;
745 case IMN_CLOSECANDIDATE: 879 case IMN_CLOSECANDIDATE:
746 trap = SDL_TRUE; 880 trap = SDL_TRUE;
881 IME_HideCandidateList(videodata);
747 break; 882 break;
748 case IMN_PRIVATE: 883 case IMN_PRIVATE:
749 { 884 {
750 DWORD dwId = IME_GetId(videodata, 0); 885 DWORD dwId = IME_GetId(videodata, 0);
751 IME_GetReadingString(videodata, hwnd); 886 IME_GetReadingString(videodata, hwnd);
782 break; 917 break;
783 } 918 }
784 return trap; 919 return trap;
785 } 920 }
786 921
922 static void
923 IME_CloseCandidateList(SDL_VideoData *videodata)
924 {
925 IME_HideCandidateList(videodata);
926 videodata->ime_candcount = 0;
927 SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
928 }
929
930 static void
931 UILess_GetCandidateList(SDL_VideoData *videodata, ITfCandidateListUIElement *pcandlist)
932 {
933 UINT selection = 0;
934 UINT count = 0;
935 UINT page = 0;
936 UINT pgcount = 0;
937 DWORD pgstart = 0;
938 DWORD pgsize = 0;
939 UINT i, j;
940 pcandlist->lpVtbl->GetSelection(pcandlist, &selection);
941 pcandlist->lpVtbl->GetCount(pcandlist, &count);
942 pcandlist->lpVtbl->GetCurrentPage(pcandlist, &page);
943
944 videodata->ime_candsel = selection;
945 videodata->ime_candcount = count;
946 IME_ShowCandidateList(videodata);
947
948 pcandlist->lpVtbl->GetPageIndex(pcandlist, 0, 0, &pgcount);
949 if (pgcount > 0)
950 {
951 UINT *idxlist = SDL_malloc(sizeof(UINT) * pgcount);
952 if (idxlist)
953 {
954 pcandlist->lpVtbl->GetPageIndex(pcandlist, idxlist, pgcount, &pgcount);
955 pgstart = idxlist[page];
956 if (page < pgcount - 1)
957 pgsize = SDL_min(count, idxlist[page + 1]) - pgstart;
958 else
959 pgsize = count - pgstart;
960
961 SDL_free(idxlist);
962 }
963 }
964 videodata->ime_candpgsize = SDL_min(pgsize, MAX_CANDLIST);
965 videodata->ime_candsel = videodata->ime_candsel - pgstart;
966
967 SDL_memset(videodata->ime_candidates, 0, sizeof(videodata->ime_candidates));
968 for (i = pgstart, j = 0; (DWORD)i < count && j < videodata->ime_candpgsize; i++, j++)
969 {
970 BSTR bstr;
971 if (SUCCEEDED(pcandlist->lpVtbl->GetString(pcandlist, i, &bstr)))
972 {
973 if (bstr)
974 {
975 IME_AddCandidate(videodata, j, bstr);
976 SysFreeString(bstr);
977 }
978 }
979 }
980 if (PRIMLANG() == LANG_KOREAN)
981 videodata->ime_candsel = -1;
982 }
983
787 STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink) 984 STDMETHODIMP_(ULONG) TSFSink_AddRef(TSFSink *sink)
788 { 985 {
789 return ++sink->refcount; 986 return ++sink->refcount;
790 } 987 }
791 988
833 1030
834 STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow) 1031 STDMETHODIMP UIElementSink_BeginUIElement(TSFSink *sink, DWORD dwUIElementId, BOOL *pbShow)
835 { 1032 {
836 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); 1033 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
837 ITfReadingInformationUIElement *preading = 0; 1034 ITfReadingInformationUIElement *preading = 0;
1035 ITfCandidateListUIElement *pcandlist = 0;
838 SDL_VideoData *videodata = (SDL_VideoData *)sink->data; 1036 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
839 if (!element) 1037 if (!element)
840 return E_INVALIDARG; 1038 return E_INVALIDARG;
841 1039
842 *pbShow = FALSE; 1040 *pbShow = FALSE;
846 WCHAR *s = (WCHAR *)bstr; 1044 WCHAR *s = (WCHAR *)bstr;
847 SysFreeString(bstr); 1045 SysFreeString(bstr);
848 } 1046 }
849 preading->lpVtbl->Release(preading); 1047 preading->lpVtbl->Release(preading);
850 } 1048 }
1049 else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1050 videodata->ime_candref++;
1051 UILess_GetCandidateList(videodata, pcandlist);
1052 pcandlist->lpVtbl->Release(pcandlist);
1053 }
851 return S_OK; 1054 return S_OK;
852 } 1055 }
853 1056
854 STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId) 1057 STDMETHODIMP UIElementSink_UpdateUIElement(TSFSink *sink, DWORD dwUIElementId)
855 { 1058 {
856 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); 1059 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
857 ITfReadingInformationUIElement *preading = 0; 1060 ITfReadingInformationUIElement *preading = 0;
1061 ITfCandidateListUIElement *pcandlist = 0;
858 SDL_VideoData *videodata = (SDL_VideoData *)sink->data; 1062 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
859 if (!element) 1063 if (!element)
860 return E_INVALIDARG; 1064 return E_INVALIDARG;
861 1065
862 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) { 1066 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
867 IME_SendEditingEvent(videodata); 1071 IME_SendEditingEvent(videodata);
868 SysFreeString(bstr); 1072 SysFreeString(bstr);
869 } 1073 }
870 preading->lpVtbl->Release(preading); 1074 preading->lpVtbl->Release(preading);
871 } 1075 }
1076 else if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1077 UILess_GetCandidateList(videodata, pcandlist);
1078 pcandlist->lpVtbl->Release(pcandlist);
1079 }
872 return S_OK; 1080 return S_OK;
873 } 1081 }
874 1082
875 STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId) 1083 STDMETHODIMP UIElementSink_EndUIElement(TSFSink *sink, DWORD dwUIElementId)
876 { 1084 {
877 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId); 1085 ITfUIElement *element = UILess_GetUIElement((SDL_VideoData *)sink->data, dwUIElementId);
878 ITfReadingInformationUIElement *preading = 0; 1086 ITfReadingInformationUIElement *preading = 0;
1087 ITfCandidateListUIElement *pcandlist = 0;
879 SDL_VideoData *videodata = (SDL_VideoData *)sink->data; 1088 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
880 if (!element) 1089 if (!element)
881 return E_INVALIDARG; 1090 return E_INVALIDARG;
882 1091
883 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) { 1092 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfReadingInformationUIElement, (LPVOID *)&preading))) {
884 videodata->ime_readingstring[0] = 0; 1093 videodata->ime_readingstring[0] = 0;
885 IME_SendEditingEvent(videodata); 1094 IME_SendEditingEvent(videodata);
886 preading->lpVtbl->Release(preading); 1095 preading->lpVtbl->Release(preading);
1096 }
1097 if (SUCCEEDED(element->lpVtbl->QueryInterface(element, &IID_ITfCandidateListUIElement, (LPVOID *)&pcandlist))) {
1098 videodata->ime_candref--;
1099 if (videodata->ime_candref == 0)
1100 IME_CloseCandidateList(videodata);
1101
1102 pcandlist->lpVtbl->Release(pcandlist);
887 } 1103 }
888 return S_OK; 1104 return S_OK;
889 } 1105 }
890 1106
891 STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv) 1107 STDMETHODIMP IPPASink_QueryInterface(TSFSink *sink, REFIID riid, PVOID *ppv)
906 return E_NOINTERFACE; 1122 return E_NOINTERFACE;
907 } 1123 }
908 1124
909 STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags) 1125 STDMETHODIMP IPPASink_OnActivated(TSFSink *sink, DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags)
910 { 1126 {
1127 static GUID TF_PROFILE_DAYI = {0x037B2C25, 0x480C, 0x4D7F, 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A};
1128 SDL_VideoData *videodata = (SDL_VideoData *)sink->data;
1129 videodata->ime_candlistindexbase = SDL_IsEqualGUID(&TF_PROFILE_DAYI, guidProfile) ? 0 : 1;
911 if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE)) 1130 if (SDL_IsEqualIID(catid, &GUID_TFCAT_TIP_KEYBOARD) && (dwFlags & TF_IPSINK_FLAG_ACTIVE))
912 IME_InputLangChanged((SDL_VideoData *)sink->data); 1131 IME_InputLangChanged((SDL_VideoData *)sink->data);
913 1132
1133 IME_HideCandidateList(videodata);
914 return S_OK; 1134 return S_OK;
915 } 1135 }
916 1136
917 static void *vtUIElementSink[] = { 1137 static void *vtUIElementSink[] = {
918 (void *)(UIElementSink_QueryInterface), 1138 (void *)(UIElementSink_QueryInterface),
1014 TSFSink_Release(videodata->ime_ippasink); 1234 TSFSink_Release(videodata->ime_ippasink);
1015 videodata->ime_ippasink = 0; 1235 videodata->ime_ippasink = 0;
1016 } 1236 }
1017 } 1237 }
1018 1238
1239 static void *
1240 StartDrawToBitmap(HDC hdc, HBITMAP *hhbm, int width, int height)
1241 {
1242 BITMAPINFO info = {0};
1243 BITMAPINFOHEADER *infoHeader = &info.bmiHeader;
1244 BYTE *bits = NULL;
1245 if (hhbm)
1246 {
1247 infoHeader->biSize = sizeof(BITMAPINFOHEADER);
1248 infoHeader->biWidth = width;
1249 infoHeader->biHeight = -1 * SDL_abs(height);
1250 infoHeader->biPlanes = 1;
1251 infoHeader->biBitCount = 32;
1252 infoHeader->biCompression = BI_RGB;
1253 *hhbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, (void **)&bits, 0, 0);
1254 if (*hhbm)
1255 SelectObject(hdc, *hhbm);
1256 }
1257 return bits;
1258 }
1259
1260 static void
1261 StopDrawToBitmap(HDC hdc, HBITMAP *hhbm)
1262 {
1263 if (hhbm && *hhbm)
1264 {
1265 DeleteObject(*hhbm);
1266 *hhbm = NULL;
1267 }
1268 }
1269
1270 static void
1271 BitmapToTexture(HBITMAP hbm, BYTE *bits, int width, int height, SDL_Texture **texture)
1272 {
1273 SDL_Surface *surface = NULL;
1274 BITMAP bm = {0};
1275
1276 if (GetObject(hbm, sizeof(bm), &bm) == 0)
1277 return;
1278
1279 if (bits && texture)
1280 {
1281 /*
1282 For transparency:
1283
1284 const Uint8 alpha = 130;
1285 unsigned long *p = (unsigned long *)bits;
1286 unsigned long *end = (unsigned long *)(bits + (bm.bmWidthBytes * bm.bmHeight));
1287 while (p < end)
1288 {
1289 *p = RGB(GetRValue(*p), GetGValue(*p), GetBValue(*p)) | (alpha << 24);
1290 ++p;
1291 }
1292 surface = SDL_CreateRGBSurfaceFrom(bits, width, height, bm.bmBitsPixel, bm.bmWidthBytes, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
1293 */
1294 surface = SDL_CreateRGBSurfaceFrom(bits, width, height, bm.bmBitsPixel, bm.bmWidthBytes, 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
1295 if (surface)
1296 {
1297 *texture = SDL_CreateTextureFromSurface(0, surface);
1298 SDL_FreeSurface(surface);
1299 }
1300 }
1301 }
1302
1303 /* This draws only within the specified area and fills the entire region. */
1304 static void
1305 DrawRect(HDC hdc, int left, int top, int right, int bottom, int pensize)
1306 {
1307 /* The case of no pen (PenSize = 0) is automatically taken care of. */
1308 const int penadjust = (int)SDL_floor(pensize / 2.0f - 0.5f);
1309 left += pensize / 2;
1310 top += pensize / 2;
1311 right -= penadjust;
1312 bottom -= penadjust;
1313 Rectangle(hdc, left, top, right, bottom);
1314 }
1315
1316 static void
1317 DestroyTexture(SDL_Texture **texture)
1318 {
1319 if (texture && *texture)
1320 {
1321 SDL_DestroyTexture(*texture);
1322 *texture = NULL;
1323 }
1324 }
1325
1326 static void
1327 IME_DestroyTextures(SDL_VideoData *videodata)
1328 {
1329 DestroyTexture(&videodata->ime_candtex);
1330 }
1331
1332 #define SDL_swap(a,b) { \
1333 int c = (a); \
1334 (a) = (b); \
1335 (b) = c; \
1336 }
1337
1338 static void
1339 IME_PositionCandidateList(SDL_VideoData *videodata, SIZE size)
1340 {
1341 int left, top, right, bottom;
1342 SDL_bool ok = SDL_FALSE;
1343 int winw = videodata->ime_winwidth;
1344 int winh = videodata->ime_winheight;
1345
1346 /* Bottom */
1347 left = videodata->ime_rect.x;
1348 top = videodata->ime_rect.y + videodata->ime_rect.h;
1349 right = left + size.cx;
1350 bottom = top + size.cy;
1351 if (right >= winw)
1352 {
1353 left -= right - winw;
1354 right = winw;
1355 }
1356 if (bottom < winh)
1357 ok = SDL_TRUE;
1358
1359 /* Top */
1360 if (!ok)
1361 {
1362 left = videodata->ime_rect.x;
1363 top = videodata->ime_rect.y - size.cy;
1364 right = left + size.cx;
1365 bottom = videodata->ime_rect.y;
1366 if (right >= winw)
1367 {
1368 left -= right - winw;
1369 right = winw;
1370 }
1371 if (top >= 0)
1372 ok = SDL_TRUE;
1373 }
1374
1375 /* Right */
1376 if (!ok)
1377 {
1378 left = videodata->ime_rect.x + size.cx;
1379 top = 0;
1380 right = left + size.cx;
1381 bottom = size.cy;
1382 if (right < winw)
1383 ok = SDL_TRUE;
1384 }
1385
1386 /* Left */
1387 if (!ok)
1388 {
1389 left = videodata->ime_rect.x - size.cx;
1390 top = 0;
1391 right = videodata->ime_rect.x;
1392 bottom = size.cy;
1393 if (right >= 0)
1394 ok = SDL_TRUE;
1395 }
1396
1397 /* Window too small, show at (0,0) */
1398 if (!ok)
1399 {
1400 left = 0;
1401 top = 0;
1402 right = size.cx;
1403 bottom = size.cy;
1404 }
1405
1406 videodata->ime_candlistrect.x = left;
1407 videodata->ime_candlistrect.y = top;
1408 videodata->ime_candlistrect.w = right - left;
1409 videodata->ime_candlistrect.h = bottom - top;
1410 }
1411
1412 static void
1413 IME_RenderCandidateList(SDL_VideoData *videodata, HDC hdc)
1414 {
1415 int i = 0;
1416 SIZE size = {0};
1417 SIZE maxcandsize = {0};
1418 HBITMAP hbm = NULL;
1419 BYTE *bits = NULL;
1420 const int candcount = SDL_min(SDL_min(MAX_CANDLIST, videodata->ime_candcount), videodata->ime_candpgsize);
1421 SDL_bool vertical = videodata->ime_candvertical;
1422
1423 const int listborder = 1;
1424 const int listpadding = 0;
1425 const int listbordercolor = RGB(0xB4, 0xC7, 0xAA);
1426 const int listfillcolor = RGB(255, 255, 255);
1427
1428 const int candborder = 1;
1429 const int candpadding = 0;
1430 const int candmargin = 1;
1431 const COLORREF candbordercolor = RGB(255, 255, 255);
1432 const COLORREF candfillcolor = RGB(255, 255, 255);
1433 const COLORREF candtextcolor = RGB(0, 0, 0);
1434 const COLORREF selbordercolor = RGB(0x84, 0xAC, 0xDD);
1435 const COLORREF selfillcolor = RGB(0xD2, 0xE6, 0xFF);
1436 const COLORREF seltextcolor = RGB(0, 0, 0);
1437
1438 HPEN listpen = listborder != 0 ? CreatePen(PS_SOLID, listborder, listbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1439 HBRUSH listbrush = CreateSolidBrush(listfillcolor);
1440 HPEN candpen = candborder != 0 ? CreatePen(PS_SOLID, candborder, candbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1441 HBRUSH candbrush = CreateSolidBrush(candfillcolor);
1442 HPEN selpen = candborder != 0 ? CreatePen(PS_DOT, candborder, selbordercolor) : (HPEN)GetStockObject(NULL_PEN);
1443 HBRUSH selbrush = CreateSolidBrush(selfillcolor);
1444 HFONT font = CreateFont((int)(1 + videodata->ime_rect.h * 0.75f), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH | FF_SWISS, TEXT("Microsoft Sans Serif"));
1445
1446 SetBkMode(hdc, TRANSPARENT);
1447 SelectObject(hdc, font);
1448
1449 for (i = 0; i < candcount; ++i)
1450 {
1451 const WCHAR *s = videodata->ime_candidates[i];
1452 if (!*s)
1453 break;
1454
1455 GetTextExtentPoint32W(hdc, s, SDL_wcslen(s), &size);
1456 maxcandsize.cx = SDL_max(maxcandsize.cx, size.cx);
1457 maxcandsize.cy = SDL_max(maxcandsize.cy, size.cy);
1458
1459 }
1460 if (!vertical)
1461 SDL_swap(maxcandsize.cx, maxcandsize.cy);
1462
1463 size.cx =
1464 (listborder * 2) +
1465 (listpadding * 2) +
1466 (candmargin * 2) +
1467 (candborder * 2) +
1468 (candpadding * 2) +
1469 (maxcandsize.cx)
1470 ;
1471 size.cy =
1472 (listborder * 2) +
1473 (listpadding * 2) +
1474 ((candcount + 1) * candmargin) +
1475 (candcount * candborder * 2) +
1476 (candcount * candpadding * 2) +
1477 (candcount * maxcandsize.cy)
1478 ;
1479 if (!vertical)
1480 SDL_swap(size.cx, size.cy);
1481
1482 bits = StartDrawToBitmap(hdc, &hbm, size.cx, size.cy);
1483
1484 SelectObject(hdc, listpen);
1485 SelectObject(hdc, listbrush);
1486 DrawRect(hdc, 0, 0, size.cx, size.cy, listborder);
1487
1488 SelectObject(hdc, candpen);
1489 SelectObject(hdc, candbrush);
1490 SetTextColor(hdc, candtextcolor);
1491 SetBkMode(hdc, TRANSPARENT);
1492
1493 for (i = 0; i < candcount; ++i)
1494 {
1495 const WCHAR *s = videodata->ime_candidates[i];
1496 int left, top, right, bottom;
1497 if (!*s)
1498 break;
1499
1500 left = listborder + listpadding + candmargin;
1501 top = listborder + listpadding + (i * candborder * 2) + (i * candpadding * 2) + ((i + 1) * candmargin) + (i * maxcandsize.cy);
1502 if (!vertical)
1503 SDL_swap(size.cx, size.cy);
1504
1505 right = size.cx - listborder - listpadding - candmargin;
1506 if (!vertical)
1507 SDL_swap(size.cx, size.cy);
1508
1509 bottom = top + maxcandsize.cy + (candpadding * 2) + (candborder * 2);
1510 if (!vertical)
1511 {
1512 SDL_swap(left, top);
1513 SDL_swap(right, bottom);
1514 }
1515
1516 if (i == videodata->ime_candsel)
1517 {
1518 SelectObject(hdc, selpen);
1519 SelectObject(hdc, selbrush);
1520 SetTextColor(hdc, seltextcolor);
1521 }
1522 else
1523 {
1524 SelectObject(hdc, candpen);
1525 SelectObject(hdc, candbrush);
1526 SetTextColor(hdc, candtextcolor);
1527 }
1528
1529 DrawRect(hdc, left, top, right, bottom, candborder);
1530 ExtTextOutW(hdc, left + candborder + candpadding, top + candborder + candpadding, 0, NULL, s, SDL_wcslen(s), NULL);
1531 }
1532 BitmapToTexture(hbm, bits, size.cx, size.cy, &videodata->ime_candtex);
1533 StopDrawToBitmap(hdc, &hbm);
1534
1535 DeleteObject(listpen);
1536 DeleteObject(listbrush);
1537 DeleteObject(candpen);
1538 DeleteObject(candbrush);
1539 DeleteObject(selpen);
1540 DeleteObject(selbrush);
1541 DeleteObject(font);
1542
1543 IME_PositionCandidateList(videodata, size);
1544 }
1545
1546 static void
1547 IME_Render(SDL_VideoData *videodata)
1548 {
1549 HDC hdc = CreateCompatibleDC(NULL);
1550
1551 if (videodata->ime_candlist)
1552 IME_RenderCandidateList(videodata, hdc);
1553
1554 DeleteDC(hdc);
1555
1556 videodata->ime_dirty = SDL_FALSE;
1557 }
1558
1559 void IME_Present(SDL_VideoData *videodata)
1560 {
1561 if (videodata->ime_dirty)
1562 IME_Render(videodata);
1563
1564 SDL_RenderCopy(videodata->ime_candtex, NULL, &videodata->ime_candlistrect);
1565 }
1566
1019 /* vi: set ts=4 sw=4 expandtab: */ 1567 /* vi: set ts=4 sw=4 expandtab: */