diff options
Diffstat (limited to 'linden/indra/llwindow/llwindowmacosx.cpp')
-rw-r--r-- | linden/indra/llwindow/llwindowmacosx.cpp | 428 |
1 files changed, 378 insertions, 50 deletions
diff --git a/linden/indra/llwindow/llwindowmacosx.cpp b/linden/indra/llwindow/llwindowmacosx.cpp index 93ef46b..f522abb 100644 --- a/linden/indra/llwindow/llwindowmacosx.cpp +++ b/linden/indra/llwindow/llwindowmacosx.cpp | |||
@@ -12,12 +12,12 @@ | |||
12 | * ("GPL"), unless you have obtained a separate licensing agreement | 12 | * ("GPL"), unless you have obtained a separate licensing agreement |
13 | * ("Other License"), formally executed by you and Linden Lab. Terms of | 13 | * ("Other License"), formally executed by you and Linden Lab. Terms of |
14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | 14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or |
15 | * online at http://secondlife.com/developers/opensource/gplv2 | 15 | * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 |
16 | * | 16 | * |
17 | * There are special exceptions to the terms and conditions of the GPL as | 17 | * There are special exceptions to the terms and conditions of the GPL as |
18 | * it is applied to this Source Code. View the full text of the exception | 18 | * it is applied to this Source Code. View the full text of the exception |
19 | * in the file doc/FLOSS-exception.txt in this software distribution, or | 19 | * in the file doc/FLOSS-exception.txt in this software distribution, or |
20 | * online at http://secondlife.com/developers/opensource/flossexception | 20 | * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception |
21 | * | 21 | * |
22 | * By copying, modifying or distributing this software, you acknowledge | 22 | * By copying, modifying or distributing this software, you acknowledge |
23 | * that you have read and understood your obligations described above, | 23 | * that you have read and understood your obligations described above, |
@@ -48,6 +48,7 @@ | |||
48 | #include "indra_constants.h" | 48 | #include "indra_constants.h" |
49 | 49 | ||
50 | #include "llwindowmacosx-objc.h" | 50 | #include "llwindowmacosx-objc.h" |
51 | #include "llpreeditor.h" | ||
51 | 52 | ||
52 | extern BOOL gDebugWindowProc; | 53 | extern BOOL gDebugWindowProc; |
53 | 54 | ||
@@ -69,7 +70,6 @@ const S32 MAX_NUM_RESOLUTIONS = 32; | |||
69 | void show_window_creation_error(const char* title) | 70 | void show_window_creation_error(const char* title) |
70 | { | 71 | { |
71 | llwarns << title << llendl; | 72 | llwarns << title << llendl; |
72 | shell_open( "help/window_creation_error.html"); | ||
73 | /* | 73 | /* |
74 | OSMessageBox( | 74 | OSMessageBox( |
75 | "Second Life is unable to run because it can't set up your display.\n" | 75 | "Second Life is unable to run because it can't set up your display.\n" |
@@ -172,8 +172,22 @@ static EventTypeSpec WindowHandlerEventList[] = | |||
172 | { kEventClassKeyboard, kEventRawKeyModifiersChanged }, | 172 | { kEventClassKeyboard, kEventRawKeyModifiersChanged }, |
173 | 173 | ||
174 | // Text input events | 174 | // Text input events |
175 | { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } | 175 | { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, |
176 | 176 | { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }, | |
177 | { kEventClassTextInput, kEventTextInputOffsetToPos }, | ||
178 | { kEventClassTextInput, kEventTextInputPosToOffset }, | ||
179 | { kEventClassTextInput, kEventTextInputShowHideBottomWindow }, | ||
180 | { kEventClassTextInput, kEventTextInputGetSelectedText }, | ||
181 | { kEventClassTextInput, kEventTextInputFilterText }, | ||
182 | |||
183 | // TSM Document Access events (advanced input method support) | ||
184 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength }, | ||
185 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange }, | ||
186 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters }, | ||
187 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont }, | ||
188 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo }, | ||
189 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument }, | ||
190 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument } | ||
177 | }; | 191 | }; |
178 | 192 | ||
179 | static EventTypeSpec GlobalHandlerEventList[] = | 193 | static EventTypeSpec GlobalHandlerEventList[] = |
@@ -195,7 +209,22 @@ static EventTypeSpec GlobalHandlerEventList[] = | |||
195 | { kEventClassKeyboard, kEventRawKeyModifiersChanged }, | 209 | { kEventClassKeyboard, kEventRawKeyModifiersChanged }, |
196 | 210 | ||
197 | // Text input events | 211 | // Text input events |
198 | { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } | 212 | { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }, |
213 | { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, | ||
214 | { kEventClassTextInput, kEventTextInputOffsetToPos }, | ||
215 | { kEventClassTextInput, kEventTextInputPosToOffset }, | ||
216 | { kEventClassTextInput, kEventTextInputShowHideBottomWindow }, | ||
217 | { kEventClassTextInput, kEventTextInputGetSelectedText }, | ||
218 | { kEventClassTextInput, kEventTextInputFilterText }, | ||
219 | |||
220 | // TSM Document Access events (advanced input method support) | ||
221 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetLength }, | ||
222 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetSelectedRange }, | ||
223 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetCharacters }, | ||
224 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetFont }, | ||
225 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessGetGlyphInfo }, | ||
226 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessLockDocument }, | ||
227 | { kEventClassTSMDocumentAccess, kEventTSMDocumentAccessUnlockDocument } | ||
199 | }; | 228 | }; |
200 | 229 | ||
201 | static EventTypeSpec CommandHandlerEventList[] = | 230 | static EventTypeSpec CommandHandlerEventList[] = |
@@ -246,6 +275,7 @@ LLWindowMacOSX::LLWindowMacOSX(char *title, char *name, S32 x, S32 y, S32 width, | |||
246 | mLanguageTextInputAllowed = FALSE; | 275 | mLanguageTextInputAllowed = FALSE; |
247 | mTSMScriptCode = 0; | 276 | mTSMScriptCode = 0; |
248 | mTSMLangCode = 0; | 277 | mTSMLangCode = 0; |
278 | mPreeditor = NULL; | ||
249 | 279 | ||
250 | // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state. | 280 | // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state. |
251 | // Since the started state of this one is used to track whether the NMRec has been installed, it wants to start out in the "stopped" state. | 281 | // Since the started state of this one is used to track whether the NMRec has been installed, it wants to start out in the "stopped" state. |
@@ -497,15 +527,16 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits | |||
497 | mTSMDocument = NULL; | 527 | mTSMDocument = NULL; |
498 | } | 528 | } |
499 | static InterfaceTypeList types = { kUnicodeDocument }; | 529 | static InterfaceTypeList types = { kUnicodeDocument }; |
500 | OSErr err = NewTSMDocument(1, types, &mTSMDocument, 0); | 530 | err = NewTSMDocument(1, types, &mTSMDocument, 0); |
501 | if (err != noErr) | 531 | if (err != noErr) |
502 | { | 532 | { |
503 | llwarns << "createContext: couldn't create a TSMDocument (" << err << ")" << llendl; | 533 | llwarns << "createContext: couldn't create a TSMDocument (" << err << ")" << llendl; |
504 | } | 534 | } |
505 | if (mTSMDocument) | 535 | if (mTSMDocument) |
506 | { | 536 | { |
507 | UseInputWindow(mTSMDocument, TRUE); | ||
508 | ActivateTSMDocument(mTSMDocument); | 537 | ActivateTSMDocument(mTSMDocument); |
538 | UseInputWindow(mTSMDocument, FALSE); | ||
539 | allowLanguageTextInput(NULL, FALSE); | ||
509 | } | 540 | } |
510 | } | 541 | } |
511 | 542 | ||
@@ -703,7 +734,6 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits | |||
703 | if (check_for_card(RENDERER, CARD_LIST[i])) | 734 | if (check_for_card(RENDERER, CARD_LIST[i])) |
704 | { | 735 | { |
705 | close(); | 736 | close(); |
706 | shell_open( "help/unsupported_card.html" ); | ||
707 | return FALSE; | 737 | return FALSE; |
708 | } | 738 | } |
709 | } | 739 | } |
@@ -1949,6 +1979,141 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e | |||
1949 | { | 1979 | { |
1950 | switch (evtKind) | 1980 | switch (evtKind) |
1951 | { | 1981 | { |
1982 | case kEventTextInputUpdateActiveInputArea: | ||
1983 | { | ||
1984 | EventParamType param_type; | ||
1985 | |||
1986 | long fix_len; | ||
1987 | UInt32 text_len; | ||
1988 | if (mPreeditor | ||
1989 | && (result = GetEventParameter(event, kEventParamTextInputSendFixLen, | ||
1990 | typeLongInteger, ¶m_type, sizeof(fix_len), NULL, &fix_len)) == noErr | ||
1991 | && typeLongInteger == param_type | ||
1992 | && (result = GetEventParameter(event, kEventParamTextInputSendText, | ||
1993 | typeUnicodeText, ¶m_type, 0, &text_len, NULL)) == noErr | ||
1994 | && typeUnicodeText == param_type) | ||
1995 | { | ||
1996 | // Handle an optional (but essential to facilitate TSMDA) ReplaceRange param. | ||
1997 | CFRange range; | ||
1998 | if (GetEventParameter(event, kEventParamTextInputSendReplaceRange, | ||
1999 | typeCFRange, ¶m_type, sizeof(range), NULL, &range) == noErr | ||
2000 | && typeCFRange == param_type) | ||
2001 | { | ||
2002 | // Although the spec. is unclear, replace range should | ||
2003 | // not present when there is an active preedit. We just | ||
2004 | // ignore the case. markAsPreedit will detect the case and warn it. | ||
2005 | const LLWString & text = mPreeditor->getWText(); | ||
2006 | const S32 location = wstring_wstring_length_from_utf16_length(text, 0, range.location); | ||
2007 | const S32 length = wstring_wstring_length_from_utf16_length(text, location, range.length); | ||
2008 | mPreeditor->markAsPreedit(location, length); | ||
2009 | } | ||
2010 | mPreeditor->resetPreedit(); | ||
2011 | |||
2012 | // Receive the text from input method. | ||
2013 | U16 *const text = new U16[text_len / sizeof(U16)]; | ||
2014 | GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText, NULL, text_len, NULL, text); | ||
2015 | if (fix_len < 0) | ||
2016 | { | ||
2017 | // Do we still need this? Seems obsolete... | ||
2018 | fix_len = text_len; | ||
2019 | } | ||
2020 | const LLWString fix_string | ||
2021 | = utf16str_to_wstring(llutf16string(text, fix_len / sizeof(U16))); | ||
2022 | const LLWString preedit_string | ||
2023 | = utf16str_to_wstring(llutf16string(text + fix_len / sizeof(U16), (text_len - fix_len) / sizeof(U16))); | ||
2024 | delete[] text; | ||
2025 | |||
2026 | // Handle fixed (comitted) string. | ||
2027 | if (fix_string.length() > 0) | ||
2028 | { | ||
2029 | for (LLWString::const_iterator i = fix_string.begin(); i != fix_string.end(); i++) | ||
2030 | { | ||
2031 | mPreeditor->handleUnicodeCharHere(*i, FALSE); | ||
2032 | } | ||
2033 | } | ||
2034 | |||
2035 | // Receive the segment info and caret position. | ||
2036 | LLPreeditor::segment_lengths_t preedit_segment_lengths; | ||
2037 | LLPreeditor::standouts_t preedit_standouts; | ||
2038 | S32 caret_position = preedit_string.length(); | ||
2039 | UInt32 text_range_array_size; | ||
2040 | if (GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, | ||
2041 | ¶m_type, 0, &text_range_array_size, NULL) == noErr | ||
2042 | && typeTextRangeArray == param_type | ||
2043 | && text_range_array_size > sizeof(TextRangeArray)) | ||
2044 | { | ||
2045 | // TextRangeArray is a variable-length struct. | ||
2046 | TextRangeArray * const text_range_array = (TextRangeArray *) new char[text_range_array_size]; | ||
2047 | GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, | ||
2048 | NULL, text_range_array_size, NULL, text_range_array); | ||
2049 | |||
2050 | // WARNING: We assume ranges are in ascending order, | ||
2051 | // although the condition is undocumented. It seems | ||
2052 | // OK to assume this. I also assumed | ||
2053 | // the ranges are contiguous in previous versions, but I | ||
2054 | // have heard a rumore that older versions os ATOK may | ||
2055 | // return ranges with some _gap_. I don't know whether | ||
2056 | // it is true, but I'm preparing my code for the case. | ||
2057 | |||
2058 | const S32 ranges = text_range_array->fNumOfRanges; | ||
2059 | preedit_segment_lengths.reserve(ranges); | ||
2060 | preedit_standouts.reserve(ranges); | ||
2061 | |||
2062 | S32 last_bytes = 0; | ||
2063 | S32 last_utf32 = 0; | ||
2064 | for (S32 i = 0; i < ranges; i++) | ||
2065 | { | ||
2066 | const TextRange &range = text_range_array->fRange[i]; | ||
2067 | if (range.fStart > last_bytes) | ||
2068 | { | ||
2069 | const S32 length_utf16 = (range.fStart - last_bytes) / sizeof(U16); | ||
2070 | const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16); | ||
2071 | preedit_segment_lengths.push_back(length_utf32); | ||
2072 | preedit_standouts.push_back(FALSE); | ||
2073 | last_utf32 += length_utf32; | ||
2074 | } | ||
2075 | if (range.fEnd > range.fStart) | ||
2076 | { | ||
2077 | const S32 length_utf16 = (range.fEnd - range.fStart) / sizeof(U16); | ||
2078 | const S32 length_utf32 = wstring_wstring_length_from_utf16_length(preedit_string, last_utf32, length_utf16); | ||
2079 | preedit_segment_lengths.push_back(length_utf32); | ||
2080 | preedit_standouts.push_back( | ||
2081 | kTSMHiliteSelectedRawText == range.fHiliteStyle | ||
2082 | || kTSMHiliteSelectedConvertedText == range.fHiliteStyle | ||
2083 | || kTSMHiliteSelectedText == range.fHiliteStyle); | ||
2084 | last_utf32 += length_utf32; | ||
2085 | } | ||
2086 | if (kTSMHiliteCaretPosition == range.fHiliteStyle) | ||
2087 | { | ||
2088 | caret_position = last_utf32; | ||
2089 | } | ||
2090 | last_bytes = range.fEnd; | ||
2091 | } | ||
2092 | if (preedit_string.length() > last_utf32) | ||
2093 | { | ||
2094 | preedit_segment_lengths.push_back(preedit_string.length() - last_utf32); | ||
2095 | preedit_standouts.push_back(FALSE); | ||
2096 | } | ||
2097 | |||
2098 | delete[] (char *) text_range_array; | ||
2099 | } | ||
2100 | |||
2101 | // Handle preedit string. | ||
2102 | if (preedit_string.length() > 0) | ||
2103 | { | ||
2104 | if (preedit_segment_lengths.size() == 0) | ||
2105 | { | ||
2106 | preedit_segment_lengths.push_back(preedit_string.length()); | ||
2107 | preedit_standouts.push_back(FALSE); | ||
2108 | } | ||
2109 | mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position); | ||
2110 | } | ||
2111 | |||
2112 | result = noErr; | ||
2113 | } | ||
2114 | } | ||
2115 | break; | ||
2116 | |||
1952 | case kEventTextInputUnicodeForKeyEvent: | 2117 | case kEventTextInputUnicodeForKeyEvent: |
1953 | { | 2118 | { |
1954 | UInt32 modifiers = 0; | 2119 | UInt32 modifiers = 0; |
@@ -2021,6 +2186,63 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e | |||
2021 | result = err; | 2186 | result = err; |
2022 | } | 2187 | } |
2023 | break; | 2188 | break; |
2189 | |||
2190 | case kEventTextInputOffsetToPos: | ||
2191 | { | ||
2192 | EventParamType param_type; | ||
2193 | long offset; | ||
2194 | if (mPreeditor | ||
2195 | && GetEventParameter(event, kEventParamTextInputSendTextOffset, typeLongInteger, | ||
2196 | ¶m_type, sizeof(offset), NULL, &offset) == noErr | ||
2197 | && typeLongInteger == param_type) | ||
2198 | { | ||
2199 | S32 preedit, preedit_length; | ||
2200 | mPreeditor->getPreeditRange(&preedit, &preedit_length); | ||
2201 | const LLWString & text = mPreeditor->getWText(); | ||
2202 | |||
2203 | LLCoordGL caret_coord; | ||
2204 | LLRect preedit_bounds; | ||
2205 | if (0 <= offset | ||
2206 | && mPreeditor->getPreeditLocation(wstring_wstring_length_from_utf16_length(text, preedit, offset / sizeof(U16)), | ||
2207 | &caret_coord, &preedit_bounds, NULL)) | ||
2208 | { | ||
2209 | LLCoordGL caret_base_coord(caret_coord.mX, preedit_bounds.mBottom); | ||
2210 | LLCoordScreen caret_base_coord_screen; | ||
2211 | convertCoords(caret_base_coord, &caret_base_coord_screen); | ||
2212 | Point qd_point; | ||
2213 | qd_point.h = caret_base_coord_screen.mX; | ||
2214 | qd_point.v = caret_base_coord_screen.mY; | ||
2215 | SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint, sizeof(qd_point), &qd_point); | ||
2216 | |||
2217 | short line_height = (short) preedit_bounds.getHeight(); | ||
2218 | SetEventParameter(event, kEventParamTextInputReplyLineHeight, typeShortInteger, sizeof(line_height), &line_height); | ||
2219 | |||
2220 | result = noErr; | ||
2221 | } | ||
2222 | else | ||
2223 | { | ||
2224 | result = errOffsetInvalid; | ||
2225 | } | ||
2226 | } | ||
2227 | } | ||
2228 | break; | ||
2229 | |||
2230 | case kEventTextInputGetSelectedText: | ||
2231 | { | ||
2232 | if (mPreeditor) | ||
2233 | { | ||
2234 | S32 selection, selection_length; | ||
2235 | mPreeditor->getSelectionRange(&selection, &selection_length); | ||
2236 | if (selection_length) | ||
2237 | { | ||
2238 | const LLWString text = mPreeditor->getWText().substr(selection, selection_length); | ||
2239 | const llutf16string text_utf16 = wstring_to_utf16str(text); | ||
2240 | result = SetEventParameter(event, kEventParamTextInputReplyText, typeUnicodeText, | ||
2241 | text_utf16.length() * sizeof(U16), text_utf16.c_str()); | ||
2242 | } | ||
2243 | } | ||
2244 | } | ||
2245 | break; | ||
2024 | } | 2246 | } |
2025 | } | 2247 | } |
2026 | break; | 2248 | break; |
@@ -2194,6 +2416,13 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e | |||
2194 | switch (evtKind) | 2416 | switch (evtKind) |
2195 | { | 2417 | { |
2196 | case kEventMouseDown: | 2418 | case kEventMouseDown: |
2419 | if (mLanguageTextInputAllowed) | ||
2420 | { | ||
2421 | // We need to interrupt before handling mouse events, | ||
2422 | // so that the fixed string from IM are delivered to | ||
2423 | // the currently focused UI component. | ||
2424 | interruptLanguageTextInput(); | ||
2425 | } | ||
2197 | switch(button) | 2426 | switch(button) |
2198 | { | 2427 | { |
2199 | case kEventMouseButtonPrimary: | 2428 | case kEventMouseButtonPrimary: |
@@ -2287,6 +2516,10 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e | |||
2287 | mCallbacks->handleFocus(this); | 2516 | mCallbacks->handleFocus(this); |
2288 | break; | 2517 | break; |
2289 | case kEventWindowDeactivated: | 2518 | case kEventWindowDeactivated: |
2519 | if (mTSMDocument) | ||
2520 | { | ||
2521 | DeactivateTSMDocument(mTSMDocument); | ||
2522 | } | ||
2290 | mCallbacks->handleFocusLost(this); | 2523 | mCallbacks->handleFocusLost(this); |
2291 | break; | 2524 | break; |
2292 | case kEventWindowBoundsChanging: | 2525 | case kEventWindowBoundsChanging: |
@@ -2359,6 +2592,109 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e | |||
2359 | // BringToFront(mWindow); | 2592 | // BringToFront(mWindow); |
2360 | // result = noErr; | 2593 | // result = noErr; |
2361 | break; | 2594 | break; |
2595 | |||
2596 | } | ||
2597 | break; | ||
2598 | |||
2599 | case kEventClassTSMDocumentAccess: | ||
2600 | if (mPreeditor) | ||
2601 | { | ||
2602 | switch(evtKind) | ||
2603 | { | ||
2604 | |||
2605 | case kEventTSMDocumentAccessGetLength: | ||
2606 | { | ||
2607 | // Return the number of UTF-16 units in the text, excluding those for preedit. | ||
2608 | |||
2609 | S32 preedit, preedit_length; | ||
2610 | mPreeditor->getPreeditRange(&preedit, &preedit_length); | ||
2611 | const LLWString & text = mPreeditor->getWText(); | ||
2612 | const CFIndex length = wstring_utf16_length(text, 0, preedit) | ||
2613 | + wstring_utf16_length(text, preedit + preedit_length, text.length()); | ||
2614 | result = SetEventParameter(event, kEventParamTSMDocAccessCharacterCount, typeCFIndex, sizeof(length), &length); | ||
2615 | } | ||
2616 | break; | ||
2617 | |||
2618 | case kEventTSMDocumentAccessGetSelectedRange: | ||
2619 | { | ||
2620 | // Return the selected range, excluding preedit. | ||
2621 | // In our preeditor, preedit and selection are exclusive, so, | ||
2622 | // when it has a preedit, there is no selection and the | ||
2623 | // insertion point is on the preedit that corrupses into the | ||
2624 | // beginning of the preedit when the preedit was removed. | ||
2625 | |||
2626 | S32 preedit, preedit_length; | ||
2627 | mPreeditor->getPreeditRange(&preedit, &preedit_length); | ||
2628 | const LLWString & text = mPreeditor->getWText(); | ||
2629 | |||
2630 | CFRange range; | ||
2631 | if (preedit_length) | ||
2632 | { | ||
2633 | range.location = wstring_utf16_length(text, 0, preedit); | ||
2634 | range.length = 0; | ||
2635 | } | ||
2636 | else | ||
2637 | { | ||
2638 | S32 selection, selection_length; | ||
2639 | mPreeditor->getSelectionRange(&selection, &selection_length); | ||
2640 | range.location = wstring_utf16_length(text, 0, selection); | ||
2641 | range.length = wstring_utf16_length(text, selection, selection_length); | ||
2642 | } | ||
2643 | |||
2644 | result = SetEventParameter(event, kEventParamTSMDocAccessReplyCharacterRange, typeCFRange, sizeof(range), &range); | ||
2645 | } | ||
2646 | break; | ||
2647 | |||
2648 | case kEventTSMDocumentAccessGetCharacters: | ||
2649 | { | ||
2650 | UniChar *target_pointer; | ||
2651 | CFRange range; | ||
2652 | EventParamType param_type; | ||
2653 | if ((result = GetEventParameter(event, kEventParamTSMDocAccessSendCharacterRange, | ||
2654 | typeCFRange, ¶m_type, sizeof(range), NULL, &range)) == noErr | ||
2655 | && typeCFRange == param_type | ||
2656 | && (result = GetEventParameter(event, kEventParamTSMDocAccessSendCharactersPtr, | ||
2657 | typePtr, ¶m_type, sizeof(target_pointer), NULL, &target_pointer)) == noErr | ||
2658 | && typePtr == param_type) | ||
2659 | { | ||
2660 | S32 preedit, preedit_length; | ||
2661 | mPreeditor->getPreeditRange(&preedit, &preedit_length); | ||
2662 | const LLWString & text = mPreeditor->getWText(); | ||
2663 | |||
2664 | // The GetCharacters event of TSMDA has a fundamental flaw; | ||
2665 | // An input method need to decide the starting offset and length | ||
2666 | // *before* it actually see the contents, so it is impossible | ||
2667 | // to guarantee the character-aligned access. The event reply | ||
2668 | // has no way to indicate a condition something like "Request | ||
2669 | // was not fulfilled due to unaligned access. Please retry." | ||
2670 | // Any error sent back to the input method stops use of TSMDA | ||
2671 | // entirely during the session... | ||
2672 | // We need to simulate very strictly the behaviour as if the | ||
2673 | // underlying *text engine* holds the contents in UTF-16. | ||
2674 | // I guess this is the reason why Apple repeats saying "all | ||
2675 | // text handling application should use UTF-16." They are | ||
2676 | // trying to _fix_ the flaw by changing the appliations... | ||
2677 | // ... or, domination of UTF-16 in the industry may be a part | ||
2678 | // of the company vision, and Apple is trying to force third | ||
2679 | // party developers to obey their vision. Remember that use | ||
2680 | // of 16 bits per _a_character_ was one of the very fundamental | ||
2681 | // Unicode design policy on its early days (during late 80s) | ||
2682 | // and the original Unicode design was by two Apple employees... | ||
2683 | |||
2684 | const llutf16string text_utf16 | ||
2685 | = wstring_to_utf16str(text, preedit) | ||
2686 | + wstring_to_utf16str(text.substr(preedit + preedit_length)); | ||
2687 | |||
2688 | llassert_always(sizeof(U16) == sizeof(UniChar)); | ||
2689 | llassert(0 <= range.location && 0 <= range.length && range.location + range.length <= text_utf16.length()); | ||
2690 | memcpy(target_pointer, text_utf16.c_str() + range.location, range.length * sizeof(UniChar)); | ||
2691 | |||
2692 | // Note that result has already been set above. | ||
2693 | } | ||
2694 | } | ||
2695 | break; | ||
2696 | |||
2697 | } | ||
2362 | } | 2698 | } |
2363 | break; | 2699 | break; |
2364 | } | 2700 | } |
@@ -2862,46 +3198,6 @@ void spawn_web_browser(const char* escaped_url) | |||
2862 | } | 3198 | } |
2863 | } | 3199 | } |
2864 | 3200 | ||
2865 | void shell_open( const char* file_path ) | ||
2866 | { | ||
2867 | OSStatus result = noErr; | ||
2868 | |||
2869 | llinfos << "Opening " << file_path << llendl; | ||
2870 | CFURLRef urlRef = NULL; | ||
2871 | |||
2872 | CFStringRef stringRef = CFStringCreateWithCString(NULL, file_path, kCFStringEncodingUTF8); | ||
2873 | if (stringRef) | ||
2874 | { | ||
2875 | // This will succeed if the string is a full URL, including the http:// | ||
2876 | // Note that URLs specified this way need to be properly percent-escaped. | ||
2877 | urlRef = CFURLCreateWithString(NULL, stringRef, NULL); | ||
2878 | |||
2879 | if(urlRef == NULL) | ||
2880 | { | ||
2881 | // This will succeed if the string is a full or partial posix path. | ||
2882 | // This will work even if the path contains characters that would need to be percent-escaped | ||
2883 | // in the URL (such as spaces). | ||
2884 | urlRef = CFURLCreateWithFileSystemPath(NULL, stringRef, kCFURLPOSIXPathStyle, false); | ||
2885 | } | ||
2886 | |||
2887 | CFRelease(stringRef); | ||
2888 | } | ||
2889 | |||
2890 | if (urlRef) | ||
2891 | { | ||
2892 | result = LSOpenCFURLRef(urlRef, NULL); | ||
2893 | |||
2894 | if (result != noErr) | ||
2895 | { | ||
2896 | llinfos << "Error " << result << " on open." << llendl; | ||
2897 | } | ||
2898 | CFRelease(urlRef); | ||
2899 | } | ||
2900 | else | ||
2901 | { | ||
2902 | llinfos << "Error: couldn't create URL." << llendl; | ||
2903 | } | ||
2904 | } | ||
2905 | 3201 | ||
2906 | BOOL LLWindowMacOSX::dialog_color_picker ( F32 *r, F32 *g, F32 *b) | 3202 | BOOL LLWindowMacOSX::dialog_color_picker ( F32 *r, F32 *g, F32 *b) |
2907 | { | 3203 | { |
@@ -2995,10 +3291,34 @@ static long getDictLong (CFDictionaryRef refDict, CFStringRef key) | |||
2995 | return int_value; // otherwise return the long value | 3291 | return int_value; // otherwise return the long value |
2996 | } | 3292 | } |
2997 | 3293 | ||
2998 | void LLWindowMacOSX::allowLanguageTextInput(BOOL b) | 3294 | void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b) |
2999 | { | 3295 | { |
3000 | ScriptLanguageRecord script_language; | 3296 | ScriptLanguageRecord script_language; |
3001 | 3297 | ||
3298 | if (preeditor != mPreeditor && !b) | ||
3299 | { | ||
3300 | // This condition may occur by a call to | ||
3301 | // setEnabled(BOOL) against LLTextEditor or LLLineEditor | ||
3302 | // when the control is not focused. | ||
3303 | // We need to silently ignore the case so that | ||
3304 | // the language input status of the focused control | ||
3305 | // is not disturbed. | ||
3306 | return; | ||
3307 | } | ||
3308 | |||
3309 | // Take care of old and new preeditors. | ||
3310 | if (preeditor != mPreeditor || !b) | ||
3311 | { | ||
3312 | // We need to interrupt before updating mPreeditor, | ||
3313 | // so that the fix string from input method goes to | ||
3314 | // the old preeditor. | ||
3315 | if (mLanguageTextInputAllowed) | ||
3316 | { | ||
3317 | interruptLanguageTextInput(); | ||
3318 | } | ||
3319 | mPreeditor = (b ? preeditor : NULL); | ||
3320 | } | ||
3321 | |||
3002 | if (b == mLanguageTextInputAllowed) | 3322 | if (b == mLanguageTextInputAllowed) |
3003 | { | 3323 | { |
3004 | return; | 3324 | return; |
@@ -3028,4 +3348,12 @@ void LLWindowMacOSX::allowLanguageTextInput(BOOL b) | |||
3028 | } | 3348 | } |
3029 | } | 3349 | } |
3030 | 3350 | ||
3351 | void LLWindowMacOSX::interruptLanguageTextInput() | ||
3352 | { | ||
3353 | if (mTSMDocument) | ||
3354 | { | ||
3355 | FixTSMDocument(mTSMDocument); | ||
3356 | } | ||
3357 | } | ||
3358 | |||
3031 | #endif // LL_DARWIN | 3359 | #endif // LL_DARWIN |