de_win.c raw
1 /*
2 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
3 *
4 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
5 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
6 *
7 * Permission is hereby granted to use or copy this program
8 * for any purpose, provided the above notices are retained on all copies.
9 * Permission to modify the code and to distribute modified code is granted,
10 * provided the above notices are retained, and a notice that the code was
11 * modified is included with the above copyright notice.
12 */
13
14 /*
15 * The Windows-specific part of `de`. This has been started as the generic
16 * Windows application template but its significant parts did not survive
17 * to the final version.
18 */
19
20 #if defined(__CYGWIN__) || defined(__MINGW32__) \
21 || (defined(__NT__) && defined(__386__)) || defined(_WIN32) \
22 || defined(WIN32)
23
24 # ifndef WIN32_LEAN_AND_MEAN
25 # define WIN32_LEAN_AND_MEAN 1
26 # endif
27 # define NOSERVICE
28 # include <windows.h>
29
30 # include <ctype.h>
31
32 # include "gc.h"
33 # include "gc/cord.h"
34
35 # include "de_win.h"
36
37 int LINES = 0;
38 int COLS = 0;
39
40 # define szAppName TEXT("DE")
41
42 static HWND hwnd;
43
44 void
45 de_error(const char *s)
46 {
47 (void)MessageBoxA(hwnd, s, "Demonstration Editor",
48 MB_ICONINFORMATION | MB_OK);
49 InvalidateRect(hwnd, NULL, TRUE);
50 }
51
52 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,
53 LPARAM lParam);
54
55 int APIENTRY
56 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR command_line,
57 int nCmdShow)
58 {
59 MSG msg;
60 WNDCLASS wndclass;
61 HACCEL hAccel;
62
63 GC_set_find_leak(0);
64 GC_INIT();
65 # ifndef NO_INCREMENTAL
66 GC_enable_incremental();
67 # endif
68 # if defined(CPPCHECK)
69 GC_noop1((GC_word)(GC_uintptr_t)(&WinMain));
70 # endif
71
72 if (!hPrevInstance) {
73 wndclass.style = CS_HREDRAW | CS_VREDRAW;
74 wndclass.lpfnWndProc = WndProc;
75 wndclass.cbClsExtra = 0;
76 wndclass.cbWndExtra = DLGWINDOWEXTRA;
77 wndclass.hInstance = hInstance;
78 wndclass.hIcon = LoadIcon(hInstance, szAppName);
79 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
80 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
81 wndclass.lpszMenuName = TEXT("DE");
82 wndclass.lpszClassName = szAppName;
83
84 if (RegisterClass(&wndclass) == 0) {
85 de_error("RegisterClass error");
86 return 0;
87 }
88 }
89
90 /* Empirically, the command line does not include the command name... */
91 if (command_line == 0 || *command_line == 0) {
92 de_error("File name argument required");
93 return 0;
94 } else {
95 char *p = command_line;
96
97 while (*p != 0 && !isspace(*(unsigned char *)p))
98 p++;
99 arg_file_name
100 = CORD_to_char_star(CORD_substr(command_line, 0, p - command_line));
101 }
102
103 hwnd = CreateWindow(
104 szAppName, TEXT("Demonstration Editor") /* `lpWindowName` */,
105 WS_OVERLAPPEDWINDOW | WS_CAPTION /* `dwStyle` */,
106 CW_USEDEFAULT /* `x` */, 0 /* `y` */, CW_USEDEFAULT /* `nWidth` */,
107 0 /* `nHeight` */, NULL /* `hWndParent` */, NULL /* `hMenu` */,
108 hInstance, NULL /* `lpParam` */);
109 if (NULL == hwnd) {
110 de_error("CreateWindow error");
111 return 0;
112 }
113
114 ShowWindow(hwnd, nCmdShow);
115
116 hAccel = LoadAccelerators(hInstance, szAppName);
117
118 while (GetMessage(&msg, NULL, 0, 0)) {
119 if (!TranslateAccelerator(hwnd, hAccel, &msg)) {
120 TranslateMessage(&msg);
121 DispatchMessage(&msg);
122 }
123 }
124 return (int)msg.wParam;
125 }
126
127 /* Return the argument with all control characters replaced by blanks. */
128 static char *
129 plain_chars(const char *text, size_t len)
130 {
131 char *result = (char *)GC_MALLOC_ATOMIC(len + 1);
132 size_t i;
133
134 if (NULL == result)
135 return NULL;
136 for (i = 0; i < len; i++) {
137 if (iscntrl(((unsigned char *)text)[i])) {
138 result[i] = ' ';
139 } else {
140 result[i] = text[i];
141 }
142 }
143 result[len] = '\0';
144 return result;
145 }
146
147 /*
148 * Return the argument with all non-control-characters replaced by blank,
149 * and all control characters `c` replaced by `c + 64`.
150 */
151 static char *
152 control_chars(const char *text, size_t len)
153 {
154 char *result = (char *)GC_MALLOC_ATOMIC(len + 1);
155 size_t i;
156
157 if (NULL == result)
158 return NULL;
159 for (i = 0; i < len; i++) {
160 if (iscntrl(((unsigned char *)text)[i])) {
161 result[i] = (char)(text[i] + 0x40);
162 } else {
163 result[i] = ' ';
164 }
165 }
166 result[len] = '\0';
167 return result;
168 }
169
170 static int char_width;
171 static int char_height;
172
173 static void
174 get_line_rect(int line_arg, int win_width, RECT *rectp)
175 {
176 rectp->top = line_arg * (LONG)char_height;
177 rectp->bottom = rectp->top + char_height;
178 rectp->left = 0;
179 rectp->right = win_width;
180 }
181
182 /* A flag whether the caret is currently visible. */
183 static int caret_visible = 0;
184
185 /* A flag whether the screen has been painted at least once. */
186 static int screen_was_painted = 0;
187
188 static void update_cursor(void);
189
190 static INT_PTR CALLBACK
191 AboutBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
192 {
193 (void)lParam;
194 switch (message) {
195 case WM_INITDIALOG:
196 SetFocus(GetDlgItem(hDlg, IDOK));
197 break;
198
199 case WM_COMMAND:
200 switch (wParam) {
201 case IDOK:
202 EndDialog(hDlg, TRUE);
203 break;
204 }
205 break;
206
207 case WM_CLOSE:
208 EndDialog(hDlg, TRUE);
209 return TRUE;
210 }
211 return FALSE;
212 }
213
214 static LRESULT CALLBACK
215 WndProc(HWND hwnd_arg, UINT message, WPARAM wParam, LPARAM lParam)
216 {
217 static HINSTANCE hInstance;
218 HDC dc;
219 PAINTSTRUCT ps;
220 RECT client_area;
221 RECT this_line;
222 RECT dummy;
223 TEXTMETRIC tm;
224 int i;
225 int id;
226
227 switch (message) {
228 case WM_CREATE:
229 hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
230 dc = GetDC(hwnd_arg);
231 SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT));
232 GetTextMetrics(dc, &tm);
233 ReleaseDC(hwnd_arg, dc);
234 char_width = tm.tmAveCharWidth;
235 char_height = tm.tmHeight + tm.tmExternalLeading;
236 GetClientRect(hwnd_arg, &client_area);
237 COLS = (client_area.right - client_area.left) / char_width;
238 LINES = (client_area.bottom - client_area.top) / char_height;
239 generic_init();
240 return 0;
241
242 case WM_CHAR:
243 if (wParam == QUIT) {
244 SendMessage(hwnd_arg, WM_CLOSE, 0, 0L);
245 } else {
246 do_command((int)wParam);
247 }
248 return 0;
249
250 case WM_SETFOCUS:
251 CreateCaret(hwnd_arg, NULL, char_width, char_height);
252 ShowCaret(hwnd_arg);
253 caret_visible = 1;
254 update_cursor();
255 return 0;
256
257 case WM_KILLFOCUS:
258 HideCaret(hwnd_arg);
259 DestroyCaret();
260 caret_visible = 0;
261 return 0;
262
263 case WM_LBUTTONUP:
264 {
265 unsigned xpos = LOWORD(lParam); /*< from left */
266 unsigned ypos = HIWORD(lParam); /*< from top */
267
268 set_position(xpos / (unsigned)char_width, ypos / (unsigned)char_height);
269 }
270 return 0;
271
272 case WM_COMMAND:
273 id = LOWORD(wParam);
274 if (id & EDIT_CMD_FLAG) {
275 if (id & REPEAT_FLAG)
276 do_command(REPEAT);
277 do_command(CHAR_CMD(id));
278 return 0;
279 }
280 switch (id) {
281 case IDM_FILEEXIT:
282 SendMessage(hwnd_arg, WM_CLOSE, 0, 0L);
283 return 0;
284 case IDM_HELPABOUT:
285 if (DialogBox(hInstance, TEXT("ABOUTBOX"), hwnd_arg, AboutBoxCallback)) {
286 InvalidateRect(hwnd_arg, NULL, TRUE);
287 }
288 return 0;
289 case IDM_HELPCONTENTS:
290 de_error("Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n"
291 "Undo: ^U Write: ^W Quit:^D Repeat count: ^R[n]\n"
292 "Top: ^T Locate (search, find): ^L text ^L\n");
293 return 0;
294 }
295 break;
296
297 case WM_CLOSE:
298 DestroyWindow(hwnd_arg);
299 return 0;
300
301 case WM_DESTROY:
302 PostQuitMessage(0);
303 GC_win32_free_heap();
304 return 0;
305
306 case WM_PAINT:
307 dc = BeginPaint(hwnd_arg, &ps);
308 GetClientRect(hwnd_arg, &client_area);
309 COLS = (client_area.right - client_area.left) / char_width;
310 LINES = (client_area.bottom - client_area.top) / char_height;
311 SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT));
312 for (i = 0; i < LINES; i++) {
313 get_line_rect(i, client_area.right, &this_line);
314 if (IntersectRect(&dummy, &this_line, &ps.rcPaint)) {
315 CORD raw_line = (CORD)retrieve_screen_line(i);
316 size_t len = CORD_len(raw_line);
317 const char *text = CORD_to_char_star(raw_line);
318 /* May contain embedded NUL characters. */
319 char *plain = plain_chars(text, len);
320 char *blanks = CORD_to_char_star(CORD_chars(' ', COLS - len));
321 char *control = control_chars(text, len);
322 if (NULL == plain || NULL == control)
323 de_error("Out of memory!");
324
325 # define RED RGB(255, 0, 0)
326
327 SetBkMode(dc, OPAQUE);
328 SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT));
329
330 if (plain != NULL)
331 TextOutA(dc, this_line.left, this_line.top, plain, (int)len);
332 TextOutA(dc, this_line.left + (int)len * char_width, this_line.top,
333 blanks, (int)(COLS - len));
334 SetBkMode(dc, TRANSPARENT);
335 SetTextColor(dc, RED);
336 if (control != NULL)
337 TextOutA(dc, this_line.left, this_line.top, control,
338 (int)strlen(control));
339 }
340 }
341 EndPaint(hwnd_arg, &ps);
342 screen_was_painted = 1;
343 return 0;
344 }
345 return DefWindowProc(hwnd_arg, message, wParam, lParam);
346 }
347
348 static int last_col;
349 static int last_line;
350
351 void
352 move_cursor(int c, int l)
353 {
354 last_col = c;
355 last_line = l;
356
357 if (caret_visible)
358 update_cursor();
359 }
360
361 static void
362 update_cursor(void)
363 {
364 SetCaretPos(last_col * char_width, last_line * char_height);
365 ShowCaret(hwnd);
366 }
367
368 void
369 invalidate_line(int i)
370 {
371 RECT line_r;
372
373 if (!screen_was_painted) {
374 /*
375 * Invalidating a rectangle before painting seems result in a major
376 * performance problem.
377 */
378 return;
379 }
380 get_line_rect(i, COLS * char_width, &line_r);
381 InvalidateRect(hwnd, &line_r, FALSE);
382 }
383
384 #else
385
386 /*
387 * ANSI C does not allow translation units to be empty.
388 * So we guarantee this one is nonempty.
389 */
390 extern int GC_quiet;
391
392 #endif /* !WIN32 */
393