[CRT] Remove useless #undef abort from process.h
[reactos.git] / dll / win32 / lpk / lpk.c
1 /*
2 * PROJECT: ReactOS LPK
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Language Pack DLL.
5 * PROGRAMMERS: Magnus Olsen (greatlrd)
6 * Baruch Rutman (peterooch at gmail dot com)
7 */
8
9 #include "ros_lpk.h"
10
11 WINE_DEFAULT_DEBUG_CHANNEL(bidi);
12
13 LPK_LPEDITCONTROL_LIST LpkEditControl = {EditCreate, EditIchToXY, EditMouseToIch, EditCchInWidth,
14 EditGetLineWidth, EditDrawText, EditHScroll, EditMoveSelection,
15 EditVerifyText, EditNextWord, EditSetMenu, EditProcessMenu,
16 EditCreateCaret, EditAdjustCaret};
17
18 #define PREFIX 38
19 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
20 #define KANA_PREFIX 31 /* Win16: Katakana prefix */
21
22 static int PSM_FindLastPrefix(LPCWSTR str, int count)
23 {
24 int i, prefix_count = 0, index = -1;
25
26 for (i = 0; i < count - 1; i++)
27 {
28 if (str[i] == PREFIX && str[i + 1] != PREFIX)
29 {
30 index = i - prefix_count;
31 prefix_count++;
32 }
33 else if (str[i] == PREFIX && str[i + 1] == PREFIX)
34 {
35 i++;
36 }
37 }
38 return index;
39 }
40
41 static void PSM_PrepareToDraw(LPCWSTR str, INT count, LPWSTR new_str, LPINT new_count)
42 {
43 int len, i = 0, j = 0;
44
45 while (i < count)
46 {
47 if (str[i] == PREFIX || (iswspace(str[i]) && str[i] != L' '))
48 {
49 if (i < count - 1 && str[i + 1] == PREFIX)
50 new_str[j++] = str[i++];
51 else
52 i++;
53 }
54 else
55 {
56 new_str[j++] = str[i++];
57 }
58 }
59
60 new_str[j] = L'\0';
61 len = wcslen(new_str);
62 *new_count = len;
63 }
64
65 /* Can be used with also LpkDrawTextEx() if it will be implemented */
66 static void LPK_DrawUnderscore(HDC hdc, int x, int y, LPCWSTR str, int count, int offset)
67 {
68 SCRIPT_STRING_ANALYSIS ssa;
69 DWORD dwSSAFlags = SSA_GLYPHS;
70 int prefix_x;
71 int prefix_end;
72 int pos;
73 SIZE size;
74 HPEN hpen;
75 HPEN oldPen;
76 HRESULT hr = S_FALSE;
77
78 if (offset == -1)
79 return;
80
81 if (ScriptIsComplex(str, count, SIC_COMPLEX) == S_OK)
82 {
83 if (GetLayout(hdc) & LAYOUT_RTL || GetTextAlign(hdc) & TA_RTLREADING)
84 dwSSAFlags |= SSA_RTL;
85
86 hr = ScriptStringAnalyse(hdc, str, count, (3 * count / 2 + 16),
87 -1, dwSSAFlags, -1, NULL, NULL, NULL, NULL, NULL, &ssa);
88 }
89
90 if (hr == S_OK)
91 {
92 ScriptStringCPtoX(ssa, offset, FALSE, &pos);
93 prefix_x = x + pos;
94 ScriptStringCPtoX(ssa, offset, TRUE, &pos);
95 prefix_end = x + pos;
96 ScriptStringFree(&ssa);
97 }
98 else
99 {
100 GetTextExtentPointW(hdc, str, offset, &size);
101 prefix_x = x + size.cx;
102 GetTextExtentPointW(hdc, str, offset + 1, &size);
103 prefix_end = x + size.cx - 1;
104 }
105 hpen = CreatePen(PS_SOLID, 1, GetTextColor(hdc));
106 oldPen = SelectObject(hdc, hpen);
107 MoveToEx(hdc, prefix_x, y, NULL);
108 LineTo(hdc, prefix_end, y);
109 SelectObject(hdc, oldPen);
110 DeleteObject(hpen);
111 }
112
113 /* Code taken from the GetProcessDefaultLayout() function from Wine's user32
114 * Wine version 3.17
115 *
116 * This function should be called from LpkInitialize(),
117 * which is in turn called by GdiInitializeLanguagePack() (from gdi32).
118 * TODO: Move call from LpkDllInitialize() to LpkInitialize() when latter
119 * function is implemented.
120 */
121 static void LPK_ApplyMirroring()
122 {
123 static const WCHAR translationW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
124 '\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
125 static const WCHAR filedescW[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
126 '\\','%','0','4','x','%','0','4','x',
127 '\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 };
128 WCHAR *str, buffer[MAX_PATH];
129 #ifdef __REACTOS__
130 DWORD i, version_layout = 0;
131 UINT len;
132 #else
133 DWORD i, len, version_layout = 0;
134 #endif
135 DWORD user_lang = GetUserDefaultLangID();
136 DWORD *languages;
137 void *data = NULL;
138
139 GetModuleFileNameW( 0, buffer, MAX_PATH );
140 if (!(len = GetFileVersionInfoSizeW( buffer, NULL ))) goto done;
141 if (!(data = HeapAlloc( GetProcessHeap(), 0, len ))) goto done;
142 if (!GetFileVersionInfoW( buffer, 0, len, data )) goto done;
143 if (!VerQueryValueW( data, translationW, (void **)&languages, &len ) || !len) goto done;
144
145 len /= sizeof(DWORD);
146 for (i = 0; i < len; i++) if (LOWORD(languages[i]) == user_lang) break;
147 if (i == len) /* try neutral language */
148 for (i = 0; i < len; i++)
149 if (LOWORD(languages[i]) == MAKELANGID( PRIMARYLANGID(user_lang), SUBLANG_NEUTRAL )) break;
150 if (i == len) i = 0; /* default to the first one */
151
152 sprintfW( buffer, filedescW, LOWORD(languages[i]), HIWORD(languages[i]) );
153 if (!VerQueryValueW( data, buffer, (void **)&str, &len )) goto done;
154 TRACE( "found description %s\n", debugstr_w( str ));
155 if (str[0] == 0x200e && str[1] == 0x200e) version_layout = LAYOUT_RTL;
156
157 done:
158 HeapFree( GetProcessHeap(), 0, data );
159 SetProcessDefaultLayout(version_layout);
160 }
161
162 BOOL
163 WINAPI
164 LpkDllInitialize(
165 _In_ HANDLE hDll,
166 _In_ ULONG dwReason,
167 _In_opt_ PVOID pReserved)
168 {
169 UNREFERENCED_PARAMETER(pReserved);
170
171 switch (dwReason)
172 {
173 case DLL_PROCESS_ATTACH:
174 DisableThreadLibraryCalls(hDll);
175 /* Tell usp10 it is activated usp10 */
176 //LpkPresent();
177 LPK_ApplyMirroring();
178 break;
179
180 default:
181 break;
182 }
183
184 return TRUE;
185 }
186
187
188 /*
189 * @implemented
190 */
191 BOOL
192 WINAPI
193 LpkExtTextOut(
194 HDC hdc,
195 int x,
196 int y,
197 UINT fuOptions,
198 const RECT *lprc,
199 LPCWSTR lpString,
200 UINT uCount,
201 const INT *lpDx,
202 INT unknown)
203 {
204 LPWORD glyphs = NULL;
205 LPWSTR reordered_str = NULL;
206 INT cGlyphs;
207 DWORD dwSICFlags = SIC_COMPLEX;
208 BOOL bResult, bReorder;
209
210 UNREFERENCED_PARAMETER(unknown);
211
212 fuOptions |= ETO_IGNORELANGUAGE;
213
214 /* Check text direction */
215 if ((GetLayout(hdc) & LAYOUT_RTL) || (GetTextAlign(hdc) & TA_RTLREADING))
216 fuOptions |= ETO_RTLREADING;
217
218 /* If text direction is RTL change flag to account neutral characters */
219 if (fuOptions & ETO_RTLREADING)
220 dwSICFlags |= SIC_NEUTRAL;
221
222 /* Check if the string requires complex script processing and not a "glyph indices" array */
223 if (ScriptIsComplex(lpString, uCount, dwSICFlags) == S_OK && !(fuOptions & ETO_GLYPH_INDEX))
224 {
225 /* reordered_str is used as fallback in case the glyphs array fails to generate,
226 BIDI_Reorder() doesn't attempt to write into reordered_str if memory allocation fails */
227 reordered_str = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WCHAR));
228
229 bReorder = BIDI_Reorder(hdc, lpString, uCount, GCP_REORDER,
230 (fuOptions & ETO_RTLREADING) ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR,
231 reordered_str, uCount, NULL, &glyphs, &cGlyphs);
232
233 /* Now display the reordered text if any of the arrays is valid and if BIDI_Reorder() succeeded */
234 if ((glyphs || reordered_str) && bReorder)
235 {
236 if (glyphs)
237 {
238 fuOptions |= ETO_GLYPH_INDEX;
239 uCount = cGlyphs;
240 }
241
242 bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc,
243 glyphs ? (LPWSTR)glyphs : reordered_str, uCount, lpDx);
244 }
245 else
246 {
247 WARN("BIDI_Reorder failed, falling back to original string.\n");
248 bResult = ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
249 }
250
251 HeapFree(GetProcessHeap(), 0, glyphs);
252 HeapFree(GetProcessHeap(), 0, reordered_str);
253
254 return bResult;
255 }
256
257 return ExtTextOutW(hdc, x, y, fuOptions, lprc, lpString, uCount, lpDx);
258 }
259
260 /*
261 * @implemented
262 */
263 DWORD
264 WINAPI
265 LpkGetCharacterPlacement(
266 HDC hdc,
267 LPCWSTR lpString,
268 INT uCount,
269 INT nMaxExtent,
270 LPGCP_RESULTSW lpResults,
271 DWORD dwFlags,
272 DWORD dwUnused)
273 {
274 DWORD ret = 0;
275 HRESULT hr;
276 SCRIPT_STRING_ANALYSIS ssa;
277 LPWORD lpGlyphs = NULL;
278 SIZE size;
279 UINT nSet, i;
280 INT cGlyphs;
281
282 UNREFERENCED_PARAMETER(dwUnused);
283
284 /* Sanity check (most likely a direct call) */
285 if (!(dwFlags & GCP_REORDER))
286 return GetCharacterPlacementW(hdc, lpString, uCount, nMaxExtent, lpResults, dwFlags);
287
288 nSet = (UINT)uCount;
289 if (nSet > lpResults->nGlyphs)
290 nSet = lpResults->nGlyphs;
291
292 BIDI_Reorder(hdc, lpString, uCount, dwFlags, WINE_GCPW_FORCE_LTR, lpResults->lpOutString,
293 nSet, lpResults->lpOrder, &lpGlyphs, &cGlyphs);
294
295 lpResults->nGlyphs = (UINT)cGlyphs;
296
297 if (lpResults->lpGlyphs)
298 {
299 if (lpGlyphs)
300 StringCchCopyW(lpResults->lpGlyphs, cGlyphs, lpGlyphs);
301 else if (lpResults->lpOutString)
302 GetGlyphIndicesW(hdc, lpResults->lpOutString, nSet, lpResults->lpGlyphs, 0);
303 }
304
305 if (lpResults->lpDx)
306 {
307 int c;
308
309 /* If glyph shaping was requested */
310 if (dwFlags & GCP_GLYPHSHAPE)
311 {
312 if (lpResults->lpGlyphs)
313 {
314 for (i = 0; i < lpResults->nGlyphs; i++)
315 {
316 if (GetCharWidthI(hdc, 0, 1, (WORD *)&lpResults->lpGlyphs[i], &c))
317 lpResults->lpDx[i] = c;
318 }
319 }
320 }
321
322 else
323 {
324 for (i = 0; i < nSet; i++)
325 {
326 if (GetCharWidth32W(hdc, lpResults->lpOutString[i], lpResults->lpOutString[i], &c))
327 lpResults->lpDx[i] = c;
328 }
329 }
330 }
331
332 if (lpResults->lpCaretPos)
333 {
334 int pos = 0;
335
336 hr = ScriptStringAnalyse(hdc, lpString, nSet, (3 * nSet / 2 + 16), -1, SSA_GLYPHS, -1,
337 NULL, NULL, NULL, NULL, NULL, &ssa);
338 if (hr == S_OK)
339 {
340 for (i = 0; i < nSet; i++)
341 {
342 if (ScriptStringCPtoX(ssa, i, FALSE, &pos) == S_OK)
343 lpResults->lpCaretPos[i] = pos;
344 }
345 ScriptStringFree(&ssa);
346 }
347 else
348 {
349 lpResults->lpCaretPos[0] = 0;
350 for (i = 1; i < nSet; i++)
351 {
352 if (GetTextExtentPoint32W(hdc, &(lpString[i - 1]), 1, &size))
353 lpResults->lpCaretPos[i] = (pos += size.cx);
354 }
355 }
356 }
357
358 if (GetTextExtentPoint32W(hdc, lpString, uCount, &size))
359 ret = MAKELONG(size.cx, size.cy);
360
361 HeapFree(GetProcessHeap(), 0, lpGlyphs);
362
363 return ret;
364 }
365
366 /* Stripped down version of DrawText(), can only draw single line text and Prefix underscore
367 * (only on the last found amperstand).
368 * Only flags to be found to be of use in testing:
369 *
370 * DT_NOPREFIX - Draw the string as is without removal of the amperstands and without underscore
371 * DT_HIDEPREFIX - Draw the string without underscore
372 * DT_PREFIXONLY - Draw only the underscore
373 *
374 * Without any of these flags the behavior is the string being drawn without the amperstands and
375 * with the underscore.
376 * user32 has an equivalent function - UserLpkPSMTextOut().
377 *
378 * Note: lpString does not need to be null terminated.
379 */
380 INT WINAPI LpkPSMTextOut(HDC hdc, int x, int y, LPCWSTR lpString, int cString, DWORD dwFlags)
381 {
382 SIZE size;
383 TEXTMETRICW tm;
384 int prefix_offset, len;
385 LPWSTR display_str = NULL;
386
387 if (!lpString || cString <= 0)
388 return 0;
389
390 if (dwFlags & DT_NOPREFIX)
391 {
392 LpkExtTextOut(hdc, x, y, 0, NULL, lpString, cString, NULL, 0);
393 GetTextExtentPointW(hdc, lpString, cString, &size);
394 return size.cx;
395 }
396
397 display_str = HeapAlloc(GetProcessHeap(), 0, (cString + 1) * sizeof(WCHAR));
398
399 if (!display_str)
400 return 0;
401
402 PSM_PrepareToDraw(lpString, cString, display_str, &len);
403
404 if (!(dwFlags & DT_PREFIXONLY))
405 LpkExtTextOut(hdc, x, y, 0, NULL, display_str, len, NULL, 0);
406
407 if (!(dwFlags & DT_HIDEPREFIX))
408 {
409 prefix_offset = PSM_FindLastPrefix(lpString, cString);
410 GetTextMetricsW(hdc, &tm);
411 LPK_DrawUnderscore(hdc, x, y + tm.tmAscent + 1, display_str, len, prefix_offset);
412 }
413
414 GetTextExtentPointW(hdc, display_str, len + 1, &size);
415 HeapFree(GetProcessHeap(), 0, display_str);
416
417 return size.cx;
418 }
419
420 /*
421 * @implemented
422 */
423 BOOL
424 WINAPI
425 LpkGetTextExtentExPoint(
426 HDC hdc,
427 LPCWSTR lpString,
428 INT cString,
429 INT nMaxExtent,
430 LPINT lpnFit,
431 LPINT lpnDx,
432 LPSIZE lpSize,
433 DWORD dwUnused,
434 int unknown)
435 {
436 SCRIPT_STRING_ANALYSIS ssa;
437 HRESULT hr;
438 INT i, extent, *Dx;
439 TEXTMETRICW tm;
440
441 UNREFERENCED_PARAMETER(dwUnused);
442 UNREFERENCED_PARAMETER(unknown);
443
444 if (cString < 0 || !lpSize)
445 return FALSE;
446
447 if (cString == 0 || !lpString)
448 {
449 lpSize->cx = 0;
450 lpSize->cy = 0;
451 return TRUE;
452 }
453
454 /* Check if any processing is required */
455 if (ScriptIsComplex(lpString, cString, SIC_COMPLEX) != S_OK)
456 goto fallback;
457
458 hr = ScriptStringAnalyse(hdc, lpString, cString, 3 * cString / 2 + 16, -1,
459 SSA_GLYPHS, 0, NULL, NULL, NULL, NULL, NULL, &ssa);
460 if (hr != S_OK)
461 goto fallback;
462
463 /* Use logic from TextIntGetTextExtentPoint() */
464 Dx = HeapAlloc(GetProcessHeap(), 0, cString * sizeof(INT));
465 if (!Dx)
466 {
467 ScriptStringFree(&ssa);
468 goto fallback;
469 }
470
471 if (lpnFit)
472 *lpnFit = 0;
473
474 ScriptStringGetLogicalWidths(ssa, Dx);
475
476 for (i = 0, extent = 0; i < cString; i++)
477 {
478 extent += Dx[i];
479
480 if (extent <= nMaxExtent && lpnFit)
481 *lpnFit = i + 1;
482
483 if (lpnDx)
484 lpnDx[i] = extent;
485 }
486
487 HeapFree(GetProcessHeap(), 0, Dx);
488 ScriptStringFree(&ssa);
489
490 if (!GetTextMetricsW(hdc, &tm))
491 return GetTextExtentExPointWPri(hdc, lpString, cString, 0, NULL, NULL, lpSize);
492
493 lpSize->cx = extent;
494 lpSize->cy = tm.tmHeight;
495
496 return TRUE;
497
498 fallback:
499 return GetTextExtentExPointWPri(hdc, lpString, cString, nMaxExtent, lpnFit, lpnDx, lpSize);
500 }