1 /*
2  * PROJECT:         ReactOS Tests
3  * FILE:            rostests/win32/user32/biditext/biditext.c
4  * PURPOSE:         Demonstrates how ExtTextOut and GetCharacterPlacement
5  *                  handle bidirectional text strings via certain selection
6  *                  of flags provided to them.
7  *
8  * PROGRAMMER:      Program skeleton: https://github.com/TransmissionZero/MinGW-Win32-Application
9  *                  Test code by Baruch Rutman
10  */
11 
12 #include "biditext.h"
13 
14 /* Prototypes */
15 DWORD WINAPI LpkGetCharacterPlacement(HDC hdc, LPCWSTR lpString, INT uCount, INT nMaxExtent,
16                                       GCP_RESULTSW *lpResults, DWORD dwFlags, DWORD dwUnused);
17 
18 BOOL WINAPI LpkExtTextOut(HDC hdc, int x, int y,
19                           UINT fuOptions, const RECT *lprc, LPCWSTR lpString,
20                           UINT uCount , const INT *lpDx, INT unknown);
21 
22 /* Global instance handle */
23 HINSTANCE g_hInstance = NULL;
24 
25 /* Our application entry point */
26 int WINAPI
27 wWinMain(HINSTANCE hInstance,
28           HINSTANCE hPrevInstance,
29           LPTSTR lpszCmdLine,
30           int nCmdShow)
31 {
32   INITCOMMONCONTROLSEX icc;
33   HWND hWnd;
34   HACCEL hAccelerators;
35   MSG msg;
36 
37   /* Assign global HINSTANCE */
38   g_hInstance = hInstance;
39 
40   /* Initialise common controls */
41   icc.dwSize = sizeof(icc);
42   icc.dwICC = ICC_WIN95_CLASSES;
43   InitCommonControlsEx(&icc);
44 
45   /* Register our main window class, or error */
46   if (!RegisterMainWindowClass())
47   {
48     MessageBox(NULL, TEXT("Error registering main window class."), TEXT("Error"), MB_ICONERROR | MB_OK);
49     return 0;
50   }
51 
52   /* Create our main window, or error */
53   if (!(hWnd = CreateMainWindow()))
54   {
55     MessageBox(NULL, TEXT("Error creating main window."), TEXT("Error"), MB_ICONERROR | MB_OK);
56     return 0;
57   }
58 
59   /* Load accelerators */
60   hAccelerators = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR));
61 
62   /* Show main window and force a paint */
63   ShowWindow(hWnd, nCmdShow | SW_MAXIMIZE);
64   UpdateWindow(hWnd);
65 
66   /* Main message loop */
67   while (GetMessage(&msg, NULL, 0, 0) > 0)
68   {
69     if (!TranslateAccelerator(hWnd, hAccelerators, &msg))
70     {
71       TranslateMessage(&msg);
72       DispatchMessage(&msg);
73     }
74   }
75 
76   return (int)msg.wParam;
77 }
78 
79 /* Dialog procedure for our "about" dialog */
80 INT_PTR CALLBACK AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
81 {
82   switch (uMsg)
83   {
84     case WM_COMMAND:
85     {
86       WORD id = LOWORD(wParam);
87 
88       switch (id)
89       {
90         case IDOK:
91         case IDCANCEL:
92         {
93           EndDialog(hwndDlg, (INT_PTR)id);
94           return (INT_PTR)TRUE;
95         }
96       }
97       break;
98     }
99 
100     case WM_INITDIALOG:
101     {
102       return (INT_PTR)TRUE;
103     }
104   }
105 
106   return (INT_PTR)FALSE;
107 }
108 
109 /* Show our "about" dialog */
110 void ShowAboutDialog(HWND owner)
111 {
112   DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ABOUTDIALOG), owner, &AboutDialogProc);
113 }
114 
115 /* Main window class and title */
116 static LPCTSTR MainWndClass = TEXT("BiDi Test");
117 
118 /* Window procedure for our main window */
119 LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
120 {
121   switch (msg)
122   {
123     case WM_COMMAND:
124     {
125       WORD id = LOWORD(wParam);
126 
127       switch (id)
128       {
129         case ID_HELP_ABOUT:
130         {
131           ShowAboutDialog(hWnd);
132           return 0;
133         }
134 
135         case ID_FILE_EXIT:
136         {
137           DestroyWindow(hWnd);
138           return 0;
139         }
140       }
141       break;
142     }
143 
144     case WM_GETMINMAXINFO:
145     {
146       /* Prevent our window from being sized too small */
147       MINMAXINFO *minMax = (MINMAXINFO*)lParam;
148       minMax->ptMinTrackSize.x = 220;
149       minMax->ptMinTrackSize.y = 110;
150 
151       return 0;
152     }
153 
154     /* Item from system menu has been invoked */
155     case WM_SYSCOMMAND:
156     {
157       WORD id = LOWORD(wParam);
158 
159       switch (id)
160       {
161         /* Show "about" dialog on about system menu item */
162         case ID_HELP_ABOUT:
163         {
164           ShowAboutDialog(hWnd);
165           return 0;
166         }
167       }
168       break;
169     }
170 
171     case WM_PAINT:
172         {
173             PAINTSTRUCT ps;
174 
175             HDC hdc = BeginPaint(hWnd, &ps);
176 
177             enum
178             {
179                 ALEF = 0x5D0,
180                 BET,
181                 GIMEL,
182                 DALET,
183                 HEY,
184                 VAV,
185                 ZAYIN,
186                 HET,
187                 TET,
188                 YUD
189             };
190 
191             const WCHAR szString[] = {ALEF, BET, GIMEL, DALET, HEY, 'A', 'B', 'C', 'D', VAV, ZAYIN, HET, TET, YUD, 0};
192             const WCHAR szReversedString[] = {HEY, DALET, GIMEL, BET, ALEF, 'A', 'B', 'C', 'D', YUD, TET, HET, ZAYIN, VAV, 0};
193             int Len = lstrlenW(szString);
194             int i, xpos, tempLength;
195             WCHAR tempString[20] = { 0 };
196             WCHAR Glyphs[100] = { 0 };
197             WCHAR OutString[100] = { 0 };
198             INT lpCaretPos[100] = { 0 };
199             UINT lpOrder[100] = { 0 };
200             GCP_RESULTSW Results = { 0 };
201 
202             Results.lStructSize = sizeof(Results);
203             Results.lpOutString = OutString;
204             Results.lpGlyphs = Glyphs;
205             Results.nGlyphs = 100;
206             Results.lpCaretPos = lpCaretPos;
207             Results.lpOrder = lpOrder;
208 
209             SetBkMode(hdc, TRANSPARENT);
210 
211             TextOutW(hdc, 10, 10, L"Proper (string being used):", 27);
212             TextOutW(hdc, 200, 10, szString, 14);
213             TextOutW(hdc, 10, 30, L"Reversed (example):", 19);
214             TextOutW(hdc, 200, 30, szReversedString, 14);
215 
216             TextOutW(hdc, 10, 50, L"String with NULL LpkETO call (not reversed):", 44);
217             LpkExtTextOut(hdc, 10, 70, 0, NULL, szString, Len, NULL, 0);
218 
219             TextOutW(hdc, 10, 90, L"String with ETO_IGNORELANGUAGE LpkETO call (not reversed):", 58);
220             LpkExtTextOut(hdc, 10, 110, ETO_IGNORELANGUAGE, NULL, szString, Len, NULL, 0);
221 
222             TextOutW(hdc, 10, 130, L"String with GCP_REORDER and ETO_GLYPH_INDEX LpkGCP call (not reversed):", 71);
223             LpkGetCharacterPlacement(hdc, szString, Len, 0, &Results, GCP_REORDER, 0);
224             LpkExtTextOut(hdc, 10, 150, ETO_GLYPH_INDEX, NULL, Glyphs, Results.nGlyphs, NULL, 0);
225             TextOutW(hdc, 10, 170, L"String with GCP_REORDER and ETO_IGNORELANGUAGE LpkGCP call (not reversed, lpOutString):", 87);
226             ExtTextOutW(hdc, 10, 190, ETO_IGNORELANGUAGE, NULL, OutString, Results.nGlyphs, NULL);
227 
228             TextOutW(hdc, 10, 210, L"String without GCP_REORDER and ETO_GLYPH_INDEX LpkGCP call (reversed):", 70);
229             LpkGetCharacterPlacement(hdc, szString, Len, 0, &Results, 0, 0);
230             LpkExtTextOut(hdc, 10, 230, ETO_GLYPH_INDEX, NULL, Glyphs, Results.nGlyphs, NULL, 0);
231             TextOutW(hdc, 10, 250, L"String without GCP_REORDER and ETO_IGNORELANGUAGE LpkGCP call (reversed, lpOutString):", 86);
232             ExtTextOutW(hdc, 10, 270, ETO_IGNORELANGUAGE, NULL, OutString, Len, NULL);
233 
234             TextOutW(hdc, 10, 290, L"String with ETO_IGNORELANGUAGE ETO call (reversed, not Lpk direct call!):", 73);
235             ExtTextOutW(hdc, 10, 310, ETO_IGNORELANGUAGE, NULL, szString, Len, NULL);
236 
237             TextOutW(hdc, 10, 330, L"String with ETO_RTLREADING LpkETO call (slight order change)", 60);
238             LpkExtTextOut(hdc, 10, 350, ETO_RTLREADING, NULL, szString, Len, NULL, 0);
239 
240             TextOutW(hdc, 10, 370, L"String with ETO_RTLREADING ETO call (slight order change)", 57);
241             ExtTextOutW(hdc, 10, 390, ETO_RTLREADING, NULL, szString, Len, NULL);
242 
243             GetCharacterPlacementW(hdc, szString, Len, 0, &Results, GCP_REORDER);
244             TextOutW(hdc, 10, 410, L"Glyph positions with GCP_REORDER flag", 37);
245 
246             /* Prints per column the location of the character in the string, reordered location, its position and the character itself */
247             for (i = 0, xpos = 10; i < Len; i++, xpos += 30)
248             {
249                 StringCchPrintfW(tempString, 20, L"%d", i);
250                 tempLength = lstrlenW(tempString);
251                 TextOutW(hdc, xpos, 430, tempString, tempLength);
252 
253                 StringCchPrintfW(tempString, 20, L"%d", lpOrder[i]);
254                 tempLength = lstrlenW(tempString);
255                 TextOutW(hdc, xpos, 450, tempString, tempLength);
256 
257                 StringCchPrintfW(tempString, 20, L"%d", lpCaretPos[i]);
258                 tempLength = lstrlenW(tempString);
259                 TextOutW(hdc, xpos, 470, tempString, tempLength);
260 
261                 TextOutW(hdc, xpos, 490, &szString[i], 1);
262             }
263             TextOutW(hdc, xpos, 430, L"Character location", 18);
264             TextOutW(hdc, xpos, 450, L"lpOrder[i]", 10);
265             TextOutW(hdc, xpos, 470, L"lpCaretPos[i]", 13);
266             TextOutW(hdc, xpos, 490, L"String[i]", 9);
267 
268             GetCharacterPlacementW(hdc, szString, Len, 0, &Results, 0);
269             TextOutW(hdc, 10, 510, L"Glyph positions without GCP_REORDER flag", 40);
270 
271             for (i = 0, xpos = 10; i < Len; i++, xpos += 30)
272             {
273                 StringCchPrintfW(tempString, 20, L"%d", i);
274                 tempLength = lstrlenW(tempString);
275                 TextOutW(hdc, xpos, 530, tempString, tempLength);
276 
277                 StringCchPrintfW(tempString, 20, L"%d", lpOrder[i]);
278                 tempLength = lstrlenW(tempString);
279                 TextOutW(hdc, xpos, 550, tempString, tempLength);
280 
281                 StringCchPrintfW(tempString, 20, L"%d", lpCaretPos[i]);
282                 tempLength = lstrlenW(tempString);
283                 TextOutW(hdc, xpos, 570, tempString, tempLength);
284 
285                 TextOutW(hdc, xpos, 590, &szString[i], 1);
286             }
287             TextOutW(hdc, xpos, 530, L"Character location", 18);
288             TextOutW(hdc, xpos, 550, L"lpOrder[i]", 10);
289             TextOutW(hdc, xpos, 570, L"lpCaretPos[i]", 13);
290             TextOutW(hdc, xpos, 590, L"String[i]", 9);
291 
292             EndPaint(hWnd, &ps);
293             break;
294         }
295 
296     case WM_DESTROY:
297     {
298       PostQuitMessage(0);
299       return 0;
300     }
301   }
302 
303   return DefWindowProc(hWnd, msg, wParam, lParam);
304 }
305 
306 /* Register a class for our main window */
307 BOOL RegisterMainWindowClass()
308 {
309   WNDCLASSEX wc;
310 
311   /* Class for our main window */
312   wc.cbSize        = sizeof(wc);
313   wc.style         = 0;
314   wc.lpfnWndProc   = &MainWndProc;
315   wc.cbClsExtra    = 0;
316   wc.cbWndExtra    = 0;
317   wc.hInstance     = g_hInstance;
318   wc.hIcon         = (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE |
319                                       LR_DEFAULTCOLOR | LR_SHARED);
320   wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
321   wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
322   wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MAINMENU);
323   wc.lpszClassName = MainWndClass;
324   wc.hIconSm       = (HICON)LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
325 
326   return (RegisterClassEx(&wc)) ? TRUE : FALSE;
327 }
328 
329 /* Create an instance of our main window */
330 HWND CreateMainWindow()
331 {
332   /* Create instance of main window */
333   HWND hWnd = CreateWindowEx(0, MainWndClass, MainWndClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
334                              NULL, NULL, g_hInstance, NULL);
335 
336   if (hWnd)
337   {
338     /* Add "about" to the system menu */
339     HMENU hSysMenu = GetSystemMenu(hWnd, FALSE);
340     InsertMenu(hSysMenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
341     InsertMenu(hSysMenu, 6, MF_BYPOSITION, ID_HELP_ABOUT, TEXT("About"));
342   }
343 
344   return hWnd;
345 }
346