ok() does not support '%S'. Store the Ansi version, convert to Unicode
[wine] / programs / wineconsole / user.c
1 /*
2  * a GUI application for displaying a console
3  *      USER32 back end
4  * Copyright 2001 Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include "winecon_user.h"
24
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(wineconsole);
28 WINE_DECLARE_DEBUG_CHANNEL(wc_font);
29
30 /* mapping console colors to RGB values */
31 COLORREF        WCUSER_ColorMap[16] =
32 {
33     RGB(0x00, 0x00, 0x00), RGB(0x00, 0x00, 0x80), RGB(0x00, 0x80, 0x00), RGB(0x00, 0x80, 0x80),
34     RGB(0x80, 0x00, 0x00), RGB(0x80, 0x00, 0x80), RGB(0x80, 0x80, 0x00), RGB(0x80, 0x80, 0x80),
35     RGB(0xC0, 0xC0, 0xC0), RGB(0x00, 0x00, 0xFF), RGB(0x00, 0xFF, 0x00), RGB(0x00, 0xFF, 0xFF),
36     RGB(0xFF, 0x00, 0x00), RGB(0xFF, 0x00, 0xFF), RGB(0xFF, 0xFF, 0x00), RGB(0xFF, 0xFF, 0xFF),
37 };
38
39 static BOOL WCUSER_SetFont(struct inner_data* data, const LOGFONT* font);
40
41 /******************************************************************
42  *              WCUSER_FillMemDC
43  *
44  * Fills the Mem DC with current cells values
45  */
46 static void WCUSER_FillMemDC(const struct inner_data* data, int upd_tp, int upd_bm)
47 {
48     unsigned            i, j, k;
49     CHAR_INFO*          cell;
50     HFONT               hOldFont;
51     WORD                attr;
52     WCHAR*              line;
53
54     /* no font has been set up yet, don't worry about filling the bitmap,
55      * we'll do it once a font is chosen
56      */
57     if (!PRIVATE(data)->hFont) return;
58
59     /* FIXME: could set up a mechanism to reuse the line between different
60      * calls to this function
61      */
62     if (!(line = HeapAlloc(GetProcessHeap(), 0, data->curcfg.sb_width * sizeof(WCHAR))))
63         WINECON_Fatal("OOM\n");
64
65     hOldFont = SelectObject(PRIVATE(data)->hMemDC, PRIVATE(data)->hFont);
66     for (j = upd_tp; j <= upd_bm; j++)
67     {
68         cell = &data->cells[j * data->curcfg.sb_width];
69         for (i = 0; i < data->curcfg.sb_width; i++)
70         {
71             attr = cell[i].Attributes;
72             SetBkColor(PRIVATE(data)->hMemDC, WCUSER_ColorMap[(attr>>4)&0x0F]);
73             SetTextColor(PRIVATE(data)->hMemDC, WCUSER_ColorMap[attr&0x0F]);
74             for (k = i; k < data->curcfg.sb_width && cell[k].Attributes == attr; k++)
75             {
76                 line[k - i] = cell[k].Char.UnicodeChar;
77             }
78             TextOut(PRIVATE(data)->hMemDC, i * data->curcfg.cell_width, j * data->curcfg.cell_height,
79                     line, k - i);
80             i = k - 1;
81         }
82     }
83     SelectObject(PRIVATE(data)->hMemDC, hOldFont);
84     HeapFree(GetProcessHeap(), 0, line);
85 }
86
87 /******************************************************************
88  *              WCUSER_NewBitmap
89  *
90  * Either the font geometry or the sb geometry has changed. we need
91  * to recreate the bitmap geometry.
92  */
93 static void WCUSER_NewBitmap(struct inner_data* data)
94 {
95     HDC         hDC;
96     HBITMAP     hnew, hold;
97
98     if (!data->curcfg.sb_width || !data->curcfg.sb_height ||
99         !PRIVATE(data)->hFont || !(hDC = GetDC(PRIVATE(data)->hWnd)))
100         return;
101     hnew = CreateCompatibleBitmap(hDC,
102                                   data->curcfg.sb_width  * data->curcfg.cell_width,
103                                   data->curcfg.sb_height * data->curcfg.cell_height);
104     ReleaseDC(PRIVATE(data)->hWnd, hDC);
105     hold = SelectObject(PRIVATE(data)->hMemDC, hnew);
106
107     if (PRIVATE(data)->hBitmap)
108     {
109         if (hold == PRIVATE(data)->hBitmap)
110             DeleteObject(PRIVATE(data)->hBitmap);
111         else
112             WINE_FIXME("leak\n");
113     }
114     PRIVATE(data)->hBitmap = hnew;
115     WCUSER_FillMemDC(data, 0, data->curcfg.sb_height - 1);
116 }
117
118 /******************************************************************
119  *              WCUSER_ResizeScreenBuffer
120  *
121  *
122  */
123 static void WCUSER_ResizeScreenBuffer(struct inner_data* data)
124 {
125     WCUSER_NewBitmap(data);
126 }
127
128 /******************************************************************
129  *              WCUSER_PosCursor
130  *
131  * Set a new position for the cursor
132  */
133 static void     WCUSER_PosCursor(const struct inner_data* data)
134 {
135     if (PRIVATE(data)->hWnd != GetFocus() || !data->curcfg.cursor_visible) return;
136
137     SetCaretPos((data->cursor.X - data->curcfg.win_pos.X) * data->curcfg.cell_width,
138                 (data->cursor.Y - data->curcfg.win_pos.Y) * data->curcfg.cell_height);
139     ShowCaret(PRIVATE(data)->hWnd);
140 }
141
142 /******************************************************************
143  *              WCUSER_ShapeCursor
144  *
145  * Sets a new shape for the cursor
146  */
147 static void     WCUSER_ShapeCursor(struct inner_data* data, int size, int vis, BOOL force)
148 {
149     if (force || size != data->curcfg.cursor_size)
150     {
151         if (data->curcfg.cursor_visible && PRIVATE(data)->hWnd == GetFocus()) DestroyCaret();
152         if (PRIVATE(data)->cursor_bitmap) DeleteObject(PRIVATE(data)->cursor_bitmap);
153         PRIVATE(data)->cursor_bitmap = NULL;
154         if (size != 100)
155         {
156             int         w16b; /* number of bytes per row, aligned on word size */
157             BYTE*       ptr;
158             int         i, j, nbl;
159
160             w16b = ((data->curcfg.cell_width + 15) & ~15) / 8;
161             ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, w16b * data->curcfg.cell_height);
162             if (!ptr) WINECON_Fatal("OOM");
163             nbl = max((data->curcfg.cell_height * size) / 100, 1);
164             for (j = data->curcfg.cell_height - nbl; j < data->curcfg.cell_height; j++)
165             {
166                 for (i = 0; i < data->curcfg.cell_width; i++)
167                 {
168                     ptr[w16b * j + (i / 8)] |= 0x80 >> (i & 7);
169                 }
170             }
171             PRIVATE(data)->cursor_bitmap = CreateBitmap(data->curcfg.cell_width,
172                                                data->curcfg.cell_height, 1, 1, ptr);
173             HeapFree(GetProcessHeap(), 0, ptr);
174         }
175         data->curcfg.cursor_size = size;
176         data->curcfg.cursor_visible = -1;
177     }
178
179     vis = (vis) ? TRUE : FALSE;
180     if (force || vis != data->curcfg.cursor_visible)
181     {
182         data->curcfg.cursor_visible = vis;
183         if (PRIVATE(data)->hWnd == GetFocus())
184         {
185             if (vis)
186             {
187                 CreateCaret(PRIVATE(data)->hWnd, PRIVATE(data)->cursor_bitmap,
188                             data->curcfg.cell_width, data->curcfg.cell_height);
189                 WCUSER_PosCursor(data);
190             }
191             else
192             {
193                 DestroyCaret();
194             }
195         }
196     }
197     WINECON_DumpConfig("crsr", &data->curcfg);
198 }
199
200 /******************************************************************
201  *              WCUSER_ComputePositions
202  *
203  * Recomputes all the components (mainly scroll bars) positions
204  */
205 void    WCUSER_ComputePositions(struct inner_data* data)
206 {
207     RECT                r;
208     int                 dx, dy;
209
210     /* compute window size from desired client size */
211     r.left = r.top = 0;
212     r.right = data->curcfg.win_width * data->curcfg.cell_width;
213     r.bottom = data->curcfg.win_height * data->curcfg.cell_height;
214
215     if (IsRectEmpty(&r)) return;
216
217     AdjustWindowRect(&r, GetWindowLong(PRIVATE(data)->hWnd, GWL_STYLE), FALSE);
218
219     dx = dy = 0;
220     if (data->curcfg.sb_width > data->curcfg.win_width)
221     {
222         dy = GetSystemMetrics(SM_CYHSCROLL);
223         SetScrollRange(PRIVATE(data)->hWnd, SB_HORZ, 0,
224                        data->curcfg.sb_width - data->curcfg.win_width, FALSE);
225         SetScrollPos(PRIVATE(data)->hWnd, SB_HORZ, 0, FALSE); /* FIXME */
226         ShowScrollBar(PRIVATE(data)->hWnd, SB_HORZ, TRUE);
227     }
228     else
229     {
230         ShowScrollBar(PRIVATE(data)->hWnd, SB_HORZ, FALSE);
231     }
232
233     if (data->curcfg.sb_height > data->curcfg.win_height)
234     {
235         dx = GetSystemMetrics(SM_CXVSCROLL);
236         SetScrollRange(PRIVATE(data)->hWnd, SB_VERT, 0,
237                        data->curcfg.sb_height - data->curcfg.win_height, FALSE);
238         SetScrollPos(PRIVATE(data)->hWnd, SB_VERT, 0, FALSE); /* FIXME */
239         ShowScrollBar(PRIVATE(data)->hWnd, SB_VERT, TRUE);
240     }
241     else
242     {
243         ShowScrollBar(PRIVATE(data)->hWnd, SB_VERT, FALSE);
244     }
245
246     SetWindowPos(PRIVATE(data)->hWnd, 0, 0, 0, r.right - r.left + dx, r.bottom - r.top + dy,
247                  SWP_NOMOVE|SWP_NOZORDER);
248     WCUSER_ShapeCursor(data, data->curcfg.cursor_size, data->curcfg.cursor_visible, TRUE);
249     WCUSER_PosCursor(data);
250 }
251
252 /******************************************************************
253  *              WCUSER_SetTitle
254  *
255  * Sets the title to the wine console
256  */
257 static void     WCUSER_SetTitle(const struct inner_data* data)
258 {
259     WCHAR       buffer[256];
260
261     if (WINECON_GetConsoleTitle(data->hConIn, buffer, sizeof(buffer)))
262         SetWindowText(PRIVATE(data)->hWnd, buffer);
263 }
264
265 void WCUSER_DumpLogFont(const char* pfx, const LOGFONT* lf, DWORD ft)
266 {
267     WINE_TRACE_(wc_font)("%s %s%s%s%s\n"
268                          "\tlf.lfHeight=%ld lf.lfWidth=%ld lf.lfEscapement=%ld lf.lfOrientation=%ld\n"
269                          "\tlf.lfWeight=%ld lf.lfItalic=%u lf.lfUnderline=%u lf.lfStrikeOut=%u\n"
270                          "\tlf.lfCharSet=%u lf.lfOutPrecision=%u lf.lfClipPrecision=%u lf.lfQuality=%u\n"
271                          "\tlf->lfPitchAndFamily=%u lf.lfFaceName=%s\n",
272                          pfx,
273                          (ft & RASTER_FONTTYPE) ? "raster" : "",
274                          (ft & TRUETYPE_FONTTYPE) ? "truetype" : "",
275                          ((ft & (RASTER_FONTTYPE|TRUETYPE_FONTTYPE)) == 0) ? "vector" : "",
276                          (ft & DEVICE_FONTTYPE) ? "|device" : "",
277                          lf->lfHeight, lf->lfWidth, lf->lfEscapement, lf->lfOrientation,
278                          lf->lfWeight, lf->lfItalic, lf->lfUnderline, lf->lfStrikeOut, lf->lfCharSet,
279                          lf->lfOutPrecision, lf->lfClipPrecision, lf->lfQuality, lf->lfPitchAndFamily,
280                          wine_dbgstr_w(lf->lfFaceName));
281 }
282
283 void WCUSER_DumpTextMetric(const TEXTMETRIC* tm, DWORD ft)
284 {
285     WINE_TRACE_(wc_font)("%s%s%s%s\n"
286                          "\ttmHeight=%ld tmAscent=%ld tmDescent=%ld tmInternalLeading=%ld tmExternalLeading=%ld\n"
287                          "\ttmAveCharWidth=%ld tmMaxCharWidth=%ld tmWeight=%ld tmOverhang=%ld\n"
288                          "\ttmDigitizedAspectX=%ld tmDigitizedAspectY=%ld\n"
289                          "\ttmFirstChar=%d tmLastChar=%d tmDefaultChar=%d tmBreakChar=%d\n"
290                          "\ttmItalic=%u tmUnderlined=%u tmStruckOut=%u tmPitchAndFamily=%u tmCharSet=%u\n",
291                          (ft & RASTER_FONTTYPE) ? "raster" : "",
292                          (ft & TRUETYPE_FONTTYPE) ? "truetype" : "",
293                          ((ft & (RASTER_FONTTYPE|TRUETYPE_FONTTYPE)) == 0) ? "vector" : "",
294                          (ft & DEVICE_FONTTYPE) ? "|device" : "",
295                          tm->tmHeight, tm->tmAscent, tm->tmDescent, tm->tmInternalLeading, tm->tmExternalLeading, tm->tmAveCharWidth,
296                          tm->tmMaxCharWidth, tm->tmWeight, tm->tmOverhang, tm->tmDigitizedAspectX, tm->tmDigitizedAspectY,
297                          tm->tmFirstChar, tm->tmLastChar, tm->tmDefaultChar, tm->tmBreakChar, tm->tmItalic, tm->tmUnderlined, tm->tmStruckOut,
298                          tm->tmPitchAndFamily, tm->tmCharSet);
299 }
300
301 /******************************************************************
302  *              WCUSER_AreFontsEqual
303  *
304  *
305  */
306 BOOL WCUSER_AreFontsEqual(const struct config_data* config, const LOGFONT* lf)
307 {
308     return lf->lfHeight == config->cell_height &&
309         lf->lfWeight == config->font_weight &&
310         !lf->lfItalic && !lf->lfUnderline && !lf->lfStrikeOut &&
311         !lstrcmp(lf->lfFaceName, config->face_name);
312 }
313
314 struct font_chooser
315 {
316     struct inner_data*  data;
317     int                 done;
318 };
319
320 /******************************************************************
321  *              WCUSER_ValidateFontMetric
322  *
323  * Returns true if the font described in tm is usable as a font for the renderer
324  */
325 BOOL    WCUSER_ValidateFontMetric(const struct inner_data* data, const TEXTMETRIC* tm, DWORD fontType)
326 {
327     BOOL        ret = TRUE;
328
329     if (fontType & RASTER_FONTTYPE)
330         ret = (tm->tmMaxCharWidth * data->curcfg.win_width < GetSystemMetrics(SM_CXSCREEN) &&
331                tm->tmHeight * data->curcfg.win_height < GetSystemMetrics(SM_CYSCREEN));
332     return ret && !tm->tmItalic && !tm->tmUnderlined && !tm->tmStruckOut &&
333         (tm->tmCharSet == DEFAULT_CHARSET || tm->tmCharSet == ANSI_CHARSET);
334 }
335
336 /******************************************************************
337  *              WCUSER_ValidateFont
338  *
339  * Returns true if the font family described in lf is usable as a font for the renderer
340  */
341 BOOL    WCUSER_ValidateFont(const struct inner_data* data, const LOGFONT* lf)
342 {
343     return (lf->lfPitchAndFamily & 3) == FIXED_PITCH &&
344         /* (lf->lfPitchAndFamily & 0xF0) == FF_MODERN && */
345         (lf->lfCharSet == DEFAULT_CHARSET || lf->lfCharSet == ANSI_CHARSET);
346 }
347
348 /******************************************************************
349  *              get_first_font_enum_2
350  *              get_first_font_enum
351  *
352  * Helper functions to get a decent font for the renderer
353  */
354 static int CALLBACK get_first_font_enum_2(const LOGFONT* lf, const TEXTMETRIC* tm,
355                                           DWORD FontType, LPARAM lParam)
356 {
357     struct font_chooser*        fc = (struct font_chooser*)lParam;
358
359     WCUSER_DumpTextMetric(tm, FontType);
360     if (WCUSER_ValidateFontMetric(fc->data, tm, FontType))
361     {
362         LOGFONT mlf = *lf;
363
364         /* Use the default sizes for the font (this is needed, especially for
365          * TrueType fonts, so that we get a decent size, not the max size)
366          */
367         mlf.lfWidth  = fc->data->curcfg.cell_width;
368         mlf.lfHeight = fc->data->curcfg.cell_height;
369         if (WCUSER_SetFont(fc->data, &mlf))
370         {
371             struct      config_data     defcfg;
372
373             WCUSER_DumpLogFont("InitChoosing: ", &mlf, FontType);
374             fc->done = 1;
375             /* since we've modified the current config with new font information,
376              * set this information as the new default.
377              */
378             WINECON_RegLoad(NULL, &defcfg);
379             defcfg.cell_width = fc->data->curcfg.cell_width;
380             defcfg.cell_height = fc->data->curcfg.cell_height;
381             lstrcpyW(defcfg.face_name, fc->data->curcfg.face_name);
382             /* Force also its writing back to the registry so that we can get it
383              * the next time.
384              */
385             WINECON_RegSave(&defcfg);
386             return 0;
387         }
388     }
389     return 1;
390 }
391
392 static int CALLBACK get_first_font_enum(const LOGFONT* lf, const TEXTMETRIC* tm,
393                                         DWORD FontType, LPARAM lParam)
394 {
395     struct font_chooser*        fc = (struct font_chooser*)lParam;
396
397     WCUSER_DumpLogFont("InitFamily: ", lf, FontType);
398     if (WCUSER_ValidateFont(fc->data, lf))
399     {
400         EnumFontFamilies(PRIVATE(fc->data)->hMemDC, lf->lfFaceName,
401                          get_first_font_enum_2, lParam);
402         return !fc->done; /* we just need the first matching one... */
403     }
404     return 1;
405 }
406
407 /******************************************************************
408  *              WCUSER_CopyFont
409  *
410  * get the relevant information from the font described in lf and store them
411  * in config
412  */
413 HFONT WCUSER_CopyFont(struct config_data* config, HWND hWnd, const LOGFONT* lf)
414 {
415     TEXTMETRIC  tm;
416     HDC         hDC;
417     HFONT       hFont, hOldFont;
418     int         w, i, buf[256];
419
420     if (!(hDC = GetDC(hWnd))) return NULL;
421     if (!(hFont = CreateFontIndirect(lf))) goto err1;
422
423     hOldFont = SelectObject(hDC, hFont);
424     GetTextMetrics(hDC, &tm);
425
426     /* FIXME:
427      * the current freetype engine (at least 2.0.x with x <= 8) and its implementation
428      * in Wine don't return adequate values for fixed fonts
429      * In Windows, those fonts are expected to return the same value for
430      *  - the average width
431      *  - the largest width
432      *  - the width of all characters in the font
433      * This isn't true in Wine. As a temporary workaround, we get as the width of the
434      * cell, the width of the first character in the font, after checking that all
435      * characters in the font have the same width (I hear paranoïa coming)
436      * when this gets fixed, the code should be using tm.tmAveCharWidth
437      * or tm.tmMaxCharWidth as the cell width.
438      */
439     GetCharWidth32(hDC, tm.tmFirstChar, tm.tmFirstChar, &w);
440     for (i = tm.tmFirstChar + 1; i <= tm.tmLastChar; i += sizeof(buf) / sizeof(buf[0]))
441     {
442         int j, l;
443
444         l = min(tm.tmLastChar - i, sizeof(buf) / sizeof(buf[0]) - 1);
445         GetCharWidth32(hDC, i, i + l, buf);
446         for (j = 0; j <= l; j++)
447         {
448             if (buf[j] != w)
449             {
450                 WINE_WARN("Non uniform cell width: [%d]=%d [%d]=%d\n"
451                           "This may be caused by old freetype libraries, >= 2.0.8 is recommended\n",
452                           i + j, buf[j], tm.tmFirstChar, w);
453                 goto err;
454             }
455         }
456     }
457     SelectObject(hDC, hOldFont);
458     ReleaseDC(hWnd, hDC);
459
460     config->cell_width  = w;
461     config->cell_height = tm.tmHeight + tm.tmExternalLeading;
462     config->font_weight = tm.tmWeight;
463     lstrcpy(config->face_name, lf->lfFaceName);
464
465     return hFont;
466  err:
467     if (hDC && hOldFont) SelectObject(hDC, hOldFont);
468     if (hFont) DeleteObject(hFont);
469  err1:
470     if (hDC) ReleaseDC(hWnd, hDC);
471
472     return NULL;
473 }
474
475 /******************************************************************
476  *              WCUSER_FillLogFont
477  *
478  *
479  */
480 void    WCUSER_FillLogFont(LOGFONT* lf, const WCHAR* name, UINT height, UINT weight)
481 {
482     lf->lfHeight        = height;
483     lf->lfWidth         = 0;
484     lf->lfEscapement    = 0;
485     lf->lfOrientation   = 0;
486     lf->lfWeight        = weight;
487     lf->lfItalic        = FALSE;
488     lf->lfUnderline     = FALSE;
489     lf->lfStrikeOut     = FALSE;
490     lf->lfCharSet       = DEFAULT_CHARSET;
491     lf->lfOutPrecision  = OUT_DEFAULT_PRECIS;
492     lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
493     lf->lfQuality       = DEFAULT_QUALITY;
494     lf->lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
495     lstrcpy(lf->lfFaceName, name);
496 }
497
498 /******************************************************************
499  *              WCUSER_SetFont
500  *
501  * sets logfont as the new font for the console
502  */
503 BOOL    WCUSER_SetFont(struct inner_data* data, const LOGFONT* logfont)
504 {
505     HFONT       hFont;
506
507     if (PRIVATE(data)->hFont != 0 && WCUSER_AreFontsEqual(&data->curcfg, logfont))
508         return TRUE;
509
510     hFont = WCUSER_CopyFont(&data->curcfg, PRIVATE(data)->hWnd, logfont);
511     if (!hFont) {WINE_ERR("wrong font\n"); return FALSE;}
512
513     if (PRIVATE(data)->hFont) DeleteObject(PRIVATE(data)->hFont);
514     PRIVATE(data)->hFont = hFont;
515
516     WCUSER_ComputePositions(data);
517     WCUSER_NewBitmap(data);
518     InvalidateRect(PRIVATE(data)->hWnd, NULL, FALSE);
519     UpdateWindow(PRIVATE(data)->hWnd);
520
521     return TRUE;
522 }
523
524 /******************************************************************
525  *              WCUSER_SetFontPmt
526  *
527  * Sets a new font for the console.
528  * In fact a wrapper for WCUSER_SetFont
529  */
530 static void     WCUSER_SetFontPmt(struct inner_data* data, const WCHAR* font,
531                                   unsigned height, unsigned weight)
532 {
533     LOGFONT             lf;
534     struct font_chooser fc;
535
536     WINE_TRACE_(wc_font)("=> %s h=%u w=%u\n",
537                          wine_dbgstr_wn(font, -1), height, weight);
538
539     if (font[0] != '\0' && height != 0 && weight != 0)
540     {
541         WCUSER_FillLogFont(&lf, font, height, weight);
542         if (WCUSER_SetFont(data, &lf))
543         {
544             WCUSER_DumpLogFont("InitReuses: ", &lf, 0);
545             return;
546         }
547     }
548
549     /* try to find an acceptable font */
550     WINE_WARN("Couldn't match the font from registry... trying to find one\n");
551     fc.data = data;
552     fc.done = 0;
553     EnumFontFamilies(PRIVATE(data)->hMemDC, NULL, get_first_font_enum, (LPARAM)&fc);
554     if (!fc.done) WINECON_Fatal("Couldn't find a decent font, aborting\n");
555 }
556
557 /******************************************************************
558  *              WCUSER_GetCell
559  *
560  * Get a cell from a relative coordinate in window (takes into
561  * account the scrolling)
562  */
563 static COORD    WCUSER_GetCell(const struct inner_data* data, LPARAM lParam)
564 {
565     COORD       c;
566
567     c.X = data->curcfg.win_pos.X + (short)LOWORD(lParam) / data->curcfg.cell_width;
568     c.Y = data->curcfg.win_pos.Y + (short)HIWORD(lParam) / data->curcfg.cell_height;
569
570     return c;
571 }
572
573 /******************************************************************
574  *              WCUSER_GetSelectionRect
575  *
576  * Get the selection rectangle
577  */
578 static void     WCUSER_GetSelectionRect(const struct inner_data* data, LPRECT r)
579 {
580     r->left   = (min(PRIVATE(data)->selectPt1.X, PRIVATE(data)->selectPt2.X)     - data->curcfg.win_pos.X) * data->curcfg.cell_width;
581     r->top    = (min(PRIVATE(data)->selectPt1.Y, PRIVATE(data)->selectPt2.Y)     - data->curcfg.win_pos.Y) * data->curcfg.cell_height;
582     r->right  = (max(PRIVATE(data)->selectPt1.X, PRIVATE(data)->selectPt2.X) + 1 - data->curcfg.win_pos.X) * data->curcfg.cell_width;
583     r->bottom = (max(PRIVATE(data)->selectPt1.Y, PRIVATE(data)->selectPt2.Y) + 1 - data->curcfg.win_pos.Y) * data->curcfg.cell_height;
584 }
585
586 /******************************************************************
587  *              WCUSER_SetSelection
588  *
589  *
590  */
591 static void     WCUSER_SetSelection(const struct inner_data* data, HDC hRefDC)
592 {
593     HDC         hDC;
594     RECT        r;
595
596     WCUSER_GetSelectionRect(data, &r);
597     hDC = hRefDC ? hRefDC : GetDC(PRIVATE(data)->hWnd);
598     if (hDC)
599     {
600         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
601             HideCaret(PRIVATE(data)->hWnd);
602         InvertRect(hDC, &r);
603         if (hDC != hRefDC)
604             ReleaseDC(PRIVATE(data)->hWnd, hDC);
605         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
606             ShowCaret(PRIVATE(data)->hWnd);
607     }
608 }
609
610 /******************************************************************
611  *              WCUSER_MoveSelection
612  *
613  *
614  */
615 static void     WCUSER_MoveSelection(struct inner_data* data, COORD c1, COORD c2, BOOL final)
616 {
617     RECT        r;
618     HDC         hDC;
619
620     WCUSER_GetSelectionRect(data, &r);
621     hDC = GetDC(PRIVATE(data)->hWnd);
622     if (hDC)
623     {
624         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
625             HideCaret(PRIVATE(data)->hWnd);
626         InvertRect(hDC, &r);
627     }
628     PRIVATE(data)->selectPt1 = c1;
629     PRIVATE(data)->selectPt2 = c2;
630     if (hDC)
631     {
632         WCUSER_GetSelectionRect(data, &r);
633         InvertRect(hDC, &r);
634         ReleaseDC(PRIVATE(data)->hWnd, hDC);
635         if (PRIVATE(data)->hWnd == GetFocus() && data->curcfg.cursor_visible)
636             ShowCaret(PRIVATE(data)->hWnd);
637     }
638     if (final)
639     {
640         ReleaseCapture();
641         PRIVATE(data)->has_selection = TRUE;
642     }
643 }
644
645 /******************************************************************
646  *              WCUSER_CopySelectionToClipboard
647  *
648  * Copies the current selection into the clipboard
649  */
650 static void     WCUSER_CopySelectionToClipboard(const struct inner_data* data)
651 {
652     HANDLE      hMem;
653     LPWSTR      p;
654     unsigned    w, h;
655
656     w = abs(PRIVATE(data)->selectPt1.X - PRIVATE(data)->selectPt2.X) + 2;
657     h = abs(PRIVATE(data)->selectPt1.Y - PRIVATE(data)->selectPt2.Y) + 1;
658
659     if (!OpenClipboard(PRIVATE(data)->hWnd)) return;
660     EmptyClipboard();
661
662     hMem = GlobalAlloc(GMEM_MOVEABLE, (w * h) * sizeof(WCHAR));
663     if (hMem && (p = GlobalLock(hMem)))
664     {
665         COORD   c;
666         int     y;
667
668         c.X = min(PRIVATE(data)->selectPt1.X, PRIVATE(data)->selectPt2.X);
669         c.Y = min(PRIVATE(data)->selectPt1.Y, PRIVATE(data)->selectPt2.Y);
670
671         for (y = 0; y < h; y++, c.Y++)
672         {
673             ReadConsoleOutputCharacter(data->hConOut, &p[y * w], w - 1, c, NULL);
674             p[y * w + w - 1] = (y < h - 1) ? '\n' : '\0';
675         }
676         GlobalUnlock(hMem);
677         SetClipboardData(CF_UNICODETEXT, hMem);
678     }
679     CloseClipboard();
680 }
681
682 /******************************************************************
683  *              WCUSER_PasteFromClipboard
684  *
685  *
686  */
687 static void     WCUSER_PasteFromClipboard(struct inner_data* data)
688 {
689     HANDLE      h;
690     WCHAR*      ptr;
691
692     if (!OpenClipboard(PRIVATE(data)->hWnd)) return;
693     h = GetClipboardData(CF_UNICODETEXT);
694     if (h && (ptr = GlobalLock(h)))
695     {
696         int             i, len = GlobalSize(h) / sizeof(WCHAR);
697         INPUT_RECORD    ir[2];
698         DWORD           n;
699         SHORT           sh;
700
701         ir[0].EventType = KEY_EVENT;
702         ir[0].Event.KeyEvent.wRepeatCount = 0;
703         ir[0].Event.KeyEvent.dwControlKeyState = 0;
704         ir[0].Event.KeyEvent.bKeyDown = TRUE;
705
706         /* generate the corresponding input records */
707         for (i = 0; i < len; i++)
708         {
709             /* FIXME: the modifying keys are not generated (shift, ctrl...) */
710             sh = VkKeyScan(ptr[i]);
711             ir[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(sh);
712             ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(LOBYTE(sh), 0);
713             ir[0].Event.KeyEvent.uChar.UnicodeChar = ptr[i];
714
715             ir[1] = ir[0];
716             ir[1].Event.KeyEvent.bKeyDown = FALSE;
717
718             WriteConsoleInput(data->hConIn, ir, 2, &n);
719         }
720         GlobalUnlock(h);
721     }
722     CloseClipboard();
723 }
724
725 /******************************************************************
726  *              WCUSER_Refresh
727  *
728  *
729  */
730 static void WCUSER_Refresh(const struct inner_data* data, int tp, int bm)
731 {
732     WCUSER_FillMemDC(data, tp, bm);
733     if (data->curcfg.win_pos.Y <= bm && data->curcfg.win_pos.Y + data->curcfg.win_height >= tp)
734     {
735         RECT    r;
736
737         r.left   = 0;
738         r.right  = data->curcfg.win_width * data->curcfg.cell_width;
739         r.top    = (tp - data->curcfg.win_pos.Y) * data->curcfg.cell_height;
740         r.bottom = (bm - data->curcfg.win_pos.Y + 1) * data->curcfg.cell_height;
741         InvalidateRect(PRIVATE(data)->hWnd, &r, FALSE);
742         UpdateWindow(PRIVATE(data)->hWnd);
743     }
744 }
745
746 /******************************************************************
747  *              WCUSER_Paint
748  *
749  *
750  */
751 static void     WCUSER_Paint(const struct inner_data* data)
752 {
753     PAINTSTRUCT         ps;
754
755     BeginPaint(PRIVATE(data)->hWnd, &ps);
756     BitBlt(ps.hdc, 0, 0,
757            data->curcfg.win_width * data->curcfg.cell_width,
758            data->curcfg.win_height * data->curcfg.cell_height,
759            PRIVATE(data)->hMemDC,
760            data->curcfg.win_pos.X * data->curcfg.cell_width,
761            data->curcfg.win_pos.Y * data->curcfg.cell_height,
762            SRCCOPY);
763     if (PRIVATE(data)->has_selection)
764         WCUSER_SetSelection(data, ps.hdc);
765     EndPaint(PRIVATE(data)->hWnd, &ps);
766 }
767
768 /******************************************************************
769  *              WCUSER_Scroll
770  *
771  *
772  */
773 static void WCUSER_Scroll(struct inner_data* data, int pos, BOOL horz)
774 {
775     if (horz)
776     {
777         SetScrollPos(PRIVATE(data)->hWnd, SB_HORZ, pos, TRUE);
778         data->curcfg.win_pos.X = pos;
779     }
780     else
781     {
782         SetScrollPos(PRIVATE(data)->hWnd, SB_VERT, pos, TRUE);
783         data->curcfg.win_pos.Y = pos;
784     }
785     InvalidateRect(PRIVATE(data)->hWnd, NULL, FALSE);
786 }
787
788 /******************************************************************
789  *              WCUSER_FillMenu
790  *
791  *
792  */
793 static BOOL WCUSER_FillMenu(HMENU hMenu, BOOL sep)
794 {
795     HMENU               hSubMenu;
796     HINSTANCE           hInstance = GetModuleHandle(NULL);
797     WCHAR               buff[256];
798
799     if (!hMenu) return FALSE;
800
801     /* FIXME: error handling & memory cleanup */
802     hSubMenu = CreateMenu();
803     if (!hSubMenu) return FALSE;
804
805     LoadString(hInstance, IDS_MARK, buff, sizeof(buff) / sizeof(WCHAR));
806     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_MARK, buff);
807     LoadString(hInstance, IDS_COPY, buff, sizeof(buff) / sizeof(WCHAR));
808     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_COPY, buff);
809     LoadString(hInstance, IDS_PASTE, buff, sizeof(buff) / sizeof(WCHAR));
810     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PASTE, buff);
811     LoadString(hInstance, IDS_SELECTALL, buff, sizeof(buff) / sizeof(WCHAR));
812     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SELECTALL, buff);
813     LoadString(hInstance, IDS_SCROLL, buff, sizeof(buff) / sizeof(WCHAR));
814     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SCROLL, buff);
815     LoadString(hInstance, IDS_SEARCH, buff, sizeof(buff) / sizeof(WCHAR));
816     InsertMenu(hSubMenu, -1, MF_BYPOSITION|MF_STRING, IDS_SEARCH, buff);
817
818     if (sep) InsertMenu(hMenu, -1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL);
819     LoadString(hInstance, IDS_EDIT, buff, sizeof(buff) / sizeof(WCHAR));
820     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING|MF_POPUP, (UINT_PTR)hSubMenu, buff);
821     LoadString(hInstance, IDS_DEFAULT, buff, sizeof(buff) / sizeof(WCHAR));
822     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_DEFAULT, buff);
823     LoadString(hInstance, IDS_PROPERTIES, buff, sizeof(buff) / sizeof(WCHAR));
824     InsertMenu(hMenu, -1, MF_BYPOSITION|MF_STRING, IDS_PROPERTIES, buff);
825
826     return TRUE;
827 }
828
829 /******************************************************************
830  *              WCUSER_SetMenuDetails
831  *
832  * Grays / ungrays the menu items according to their state
833  */
834 static void     WCUSER_SetMenuDetails(const struct inner_data* data, HMENU hMenu)
835 {
836     if (!hMenu) {WINE_ERR("Issue in getting menu bits\n");return;}
837
838     EnableMenuItem(hMenu, IDS_COPY,
839                    MF_BYCOMMAND|(PRIVATE(data)->has_selection ? MF_ENABLED : MF_GRAYED));
840     EnableMenuItem(hMenu, IDS_PASTE,
841                    MF_BYCOMMAND|(IsClipboardFormatAvailable(CF_UNICODETEXT)
842                                  ? MF_ENABLED : MF_GRAYED));
843     EnableMenuItem(hMenu, IDS_SCROLL, MF_BYCOMMAND|MF_GRAYED);
844     EnableMenuItem(hMenu, IDS_SEARCH, MF_BYCOMMAND|MF_GRAYED);
845 }
846
847 /******************************************************************
848  *              WCUSER_Create
849  *
850  * Creates the window for the rendering
851  */
852 static LRESULT WCUSER_Create(HWND hWnd, LPCREATESTRUCT lpcs)
853 {
854     struct inner_data*  data;
855     HMENU               hSysMenu;
856
857     data = lpcs->lpCreateParams;
858     SetWindowLong(hWnd, 0L, (DWORD)data);
859     PRIVATE(data)->hWnd = hWnd;
860
861     hSysMenu = GetSystemMenu(hWnd, FALSE);
862     if (!hSysMenu) return 0;
863     PRIVATE(data)->hPopMenu = CreatePopupMenu();
864     if (!PRIVATE(data)->hPopMenu) return 0;
865
866     WCUSER_FillMenu(hSysMenu, TRUE);
867     WCUSER_FillMenu(PRIVATE(data)->hPopMenu, FALSE);
868
869     PRIVATE(data)->hMemDC = CreateCompatibleDC(0);
870     if (!PRIVATE(data)->hMemDC) {WINE_ERR("no mem dc\n");return 0;}
871
872     data->curcfg.quick_edit = FALSE;
873     return 0;
874 }
875
876 /******************************************************************
877  *              WCUSER_GetCtrlKeyState
878  *
879  * Get the console bit mask equivalent to the VK_ status in keyState
880  */
881 static DWORD    WCUSER_GetCtrlKeyState(BYTE* keyState)
882 {
883     DWORD               ret = 0;
884
885     GetKeyboardState(keyState);
886     if (keyState[VK_SHIFT]    & 0x80)   ret |= SHIFT_PRESSED;
887     if (keyState[VK_LCONTROL] & 0x80)   ret |= LEFT_CTRL_PRESSED;
888     if (keyState[VK_RCONTROL] & 0x80)   ret |= RIGHT_CTRL_PRESSED;
889     if (keyState[VK_LMENU]    & 0x80)   ret |= LEFT_ALT_PRESSED;
890     if (keyState[VK_RMENU]    & 0x80)   ret |= RIGHT_ALT_PRESSED;
891     if (keyState[VK_CAPITAL]  & 0x01)   ret |= CAPSLOCK_ON;
892     if (keyState[VK_NUMLOCK]  & 0x01)   ret |= NUMLOCK_ON;
893     if (keyState[VK_SCROLL]   & 0x01)   ret |= SCROLLLOCK_ON;
894
895     return ret;
896 }
897
898 /******************************************************************
899  *              WCUSER_HandleSelectionKey
900  *
901  * Handles keys while selecting an area
902  */
903 static void WCUSER_HandleSelectionKey(struct inner_data* data, BOOL down,
904                                       WPARAM wParam, LPARAM lParam)
905 {
906     BYTE        keyState[256];
907     DWORD       state = WCUSER_GetCtrlKeyState(keyState) & ~(CAPSLOCK_ON|NUMLOCK_ON|SCROLLLOCK_ON);
908     COORD       c1, c2;
909
910     if (down) return;
911
912     switch (state)
913     {
914     case 0:
915         switch (wParam)
916         {
917         case VK_RETURN:
918             PRIVATE(data)->has_selection = FALSE;
919             WCUSER_SetSelection(data, 0);
920             WCUSER_CopySelectionToClipboard(data);
921             break;
922         case VK_RIGHT:
923             c1 = PRIVATE(data)->selectPt1;
924             c2 = PRIVATE(data)->selectPt2;
925             c1.X++; c2.X++;
926             if (c1.X < data->curcfg.sb_width && c2.X < data->curcfg.sb_width)
927             {
928                 WCUSER_MoveSelection(data, c1, c2, FALSE);
929             }
930             break;
931         case VK_LEFT:
932             c1 = PRIVATE(data)->selectPt1;
933             c2 = PRIVATE(data)->selectPt2;
934             c1.X--; c2.X--;
935             if (c1.X >= 0 && c2.X >= 0)
936             {
937                 WCUSER_MoveSelection(data, c1, c2, FALSE);
938             }
939             break;
940         case VK_UP:
941             c1 = PRIVATE(data)->selectPt1;
942             c2 = PRIVATE(data)->selectPt2;
943             c1.Y--; c2.Y--;
944             if (c1.Y >= 0 && c2.Y >= 0)
945             {
946                 WCUSER_MoveSelection(data, c1, c2, FALSE);
947             }
948             break;
949         case VK_DOWN:
950             c1 = PRIVATE(data)->selectPt1;
951             c2 = PRIVATE(data)->selectPt2;
952             c1.Y++; c2.Y++;
953             if (c1.X < data->curcfg.sb_height && c2.X < data->curcfg.sb_height)
954             {
955                 WCUSER_MoveSelection(data, c1, c2, FALSE);
956             }
957             break;
958         }
959         break;
960     case SHIFT_PRESSED:
961         switch (wParam)
962         {
963         case VK_RIGHT:
964             c1 = PRIVATE(data)->selectPt1;
965             c2 = PRIVATE(data)->selectPt2;
966             c2.X++;
967             if (c2.X < data->curcfg.sb_width)
968             {
969                 WCUSER_MoveSelection(data, c1, c2, FALSE);
970             }
971             break;
972         case VK_LEFT:
973             c1 = PRIVATE(data)->selectPt1;
974             c2 = PRIVATE(data)->selectPt2;
975             c2.X--;
976             if (c2.X >= c1.X)
977             {
978                 WCUSER_MoveSelection(data, c1, c2, FALSE);
979             }
980             break;
981         case VK_UP:
982             c1 = PRIVATE(data)->selectPt1;
983             c2 = PRIVATE(data)->selectPt2;
984             c2.Y--;
985             if (c2.Y >= c1.Y)
986             {
987                 WCUSER_MoveSelection(data, c1, c2, FALSE);
988             }
989             break;
990         case VK_DOWN:
991             c1 = PRIVATE(data)->selectPt1;
992             c2 = PRIVATE(data)->selectPt2;
993             c2.Y++;
994             if (c2.X < data->curcfg.sb_height)
995             {
996                 WCUSER_MoveSelection(data, c1, c2, FALSE);
997             }
998             break;
999         }
1000         break;
1001     }
1002 }
1003
1004 /******************************************************************
1005  *              WCUSER_GenerateKeyInputRecord
1006  *
1007  * generates input_record from windows WM_KEYUP/WM_KEYDOWN messages
1008  */
1009 static void    WCUSER_GenerateKeyInputRecord(struct inner_data* data, BOOL down,
1010                                              WPARAM wParam, LPARAM lParam, BOOL sys)
1011 {
1012     INPUT_RECORD        ir;
1013     DWORD               n;
1014     WCHAR               buf[2];
1015     static      WCHAR   last; /* keep last char seen as feed for key up message */
1016     BYTE                keyState[256];
1017
1018     ir.EventType = KEY_EVENT;
1019     ir.Event.KeyEvent.bKeyDown = down;
1020     ir.Event.KeyEvent.wRepeatCount = LOWORD(lParam);
1021     ir.Event.KeyEvent.wVirtualKeyCode = wParam;
1022
1023     ir.Event.KeyEvent.wVirtualScanCode = HIWORD(lParam) & 0xFF;
1024
1025     ir.Event.KeyEvent.uChar.UnicodeChar = 0;
1026     ir.Event.KeyEvent.dwControlKeyState = WCUSER_GetCtrlKeyState(keyState);
1027     if (lParam & (1L << 24))            ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY;
1028     if (sys)                            ir.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; /* FIXME: gotta choose one */
1029
1030     if (!(ir.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
1031     {
1032         if (down)
1033         {
1034             switch (ToUnicode(wParam, HIWORD(lParam), keyState, buf, 2, 0))
1035             {
1036             case 2:
1037                 /* FIXME... should generate two events... */
1038                 /* fall thru */
1039             case 1:
1040                 last = buf[0];
1041                 break;
1042             default:
1043                 last = 0;
1044                 break;
1045             }
1046         }
1047         ir.Event.KeyEvent.uChar.UnicodeChar = last; /* FIXME HACKY... and buggy 'coz it should be a stack, not a single value */
1048         if (!down) last = 0;
1049     }
1050
1051     WriteConsoleInput(data->hConIn, &ir, 1, &n);
1052 }
1053
1054 /******************************************************************
1055  *              WCUSER_GenerateMouseInputRecord
1056  *
1057  *
1058  */
1059 static void    WCUSER_GenerateMouseInputRecord(struct inner_data* data, COORD c,
1060                                                WPARAM wParam, DWORD event)
1061 {
1062     INPUT_RECORD        ir;
1063     BYTE                keyState[256];
1064     DWORD               mode, n;
1065
1066     /* MOUSE_EVENTs shouldn't be sent unless ENABLE_MOUSE_INPUT is active */
1067     if (!GetConsoleMode(data->hConIn, &mode) || !(mode & ENABLE_MOUSE_INPUT))
1068         return;
1069
1070     ir.EventType = MOUSE_EVENT;
1071     ir.Event.MouseEvent.dwMousePosition = c;
1072     ir.Event.MouseEvent.dwButtonState = 0;
1073     if (wParam & MK_LBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_1ST_BUTTON_PRESSED;
1074     if (wParam & MK_MBUTTON) ir.Event.MouseEvent.dwButtonState |= FROM_LEFT_2ND_BUTTON_PRESSED;
1075     if (wParam & MK_RBUTTON) ir.Event.MouseEvent.dwButtonState |= RIGHTMOST_BUTTON_PRESSED;
1076     ir.Event.MouseEvent.dwControlKeyState = WCUSER_GetCtrlKeyState(keyState);
1077     ir.Event.MouseEvent.dwEventFlags = event;
1078
1079     WriteConsoleInput(data->hConIn, &ir, 1, &n);
1080 }
1081
1082 /******************************************************************
1083  *              WCUSER_Proc
1084  *
1085  *
1086  */
1087 static LRESULT CALLBACK WCUSER_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1088 {
1089     struct inner_data*  data = (struct inner_data*)GetWindowLong(hWnd, 0);
1090
1091     switch (uMsg)
1092     {
1093     case WM_CREATE:
1094         return WCUSER_Create(hWnd, (LPCREATESTRUCT)lParam);
1095     case WM_DESTROY:
1096         PRIVATE(data)->hWnd = 0;
1097         PostQuitMessage(0);
1098         break;
1099     case WM_PAINT:
1100         WCUSER_Paint(data);
1101         break;
1102     case WM_KEYDOWN:
1103     case WM_KEYUP:
1104         if (PRIVATE(data)->has_selection)
1105             WCUSER_HandleSelectionKey(data, uMsg == WM_KEYDOWN, wParam, lParam);
1106         else
1107             WCUSER_GenerateKeyInputRecord(data, uMsg == WM_KEYDOWN, wParam, lParam, FALSE);
1108         break;
1109     case WM_SYSKEYDOWN:
1110     case WM_SYSKEYUP:
1111         WCUSER_GenerateKeyInputRecord(data, uMsg == WM_SYSKEYDOWN, wParam, lParam, TRUE);
1112         break;
1113     case WM_LBUTTONDOWN:
1114         if (data->curcfg.quick_edit)
1115         {
1116             if (PRIVATE(data)->has_selection)
1117             {
1118                 PRIVATE(data)->has_selection = FALSE;
1119                 WCUSER_SetSelection(data, 0);
1120             }
1121             else
1122             {
1123                 PRIVATE(data)->selectPt1 = PRIVATE(data)->selectPt2 = WCUSER_GetCell(data, lParam);
1124                 SetCapture(PRIVATE(data)->hWnd);
1125                 WCUSER_SetSelection(data, 0);
1126                 PRIVATE(data)->has_selection = TRUE;
1127             }
1128         }
1129         else
1130         {
1131             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1132         }
1133         break;
1134     case WM_MOUSEMOVE:
1135         if (data->curcfg.quick_edit)
1136         {
1137             if (GetCapture() == PRIVATE(data)->hWnd && PRIVATE(data)->has_selection &&
1138                 (wParam & MK_LBUTTON))
1139             {
1140                 WCUSER_MoveSelection(data, PRIVATE(data)->selectPt1, WCUSER_GetCell(data, lParam), FALSE);
1141             }
1142         }
1143         else
1144         {
1145             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, MOUSE_MOVED);
1146         }
1147         break;
1148     case WM_LBUTTONUP:
1149         if (data->curcfg.quick_edit)
1150         {
1151             if (GetCapture() == PRIVATE(data)->hWnd && PRIVATE(data)->has_selection &&
1152                 (wParam& MK_LBUTTON))
1153             {
1154                 WCUSER_MoveSelection(data, PRIVATE(data)->selectPt1, WCUSER_GetCell(data, lParam), TRUE);
1155             }
1156         }
1157         else
1158         {
1159             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1160         }
1161         break;
1162     case WM_RBUTTONDOWN:
1163         if ((wParam & (MK_CONTROL|MK_SHIFT)) == data->curcfg.menu_mask)
1164         {
1165             RECT        r;
1166
1167             GetWindowRect(hWnd, &r);
1168             WCUSER_SetMenuDetails(data, PRIVATE(data)->hPopMenu);
1169             TrackPopupMenu(PRIVATE(data)->hPopMenu, TPM_LEFTALIGN|TPM_TOPALIGN,
1170                            r.left + LOWORD(lParam), r.top + HIWORD(lParam), 0, hWnd, NULL);
1171         }
1172         else
1173         {
1174             WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1175         }
1176         break;
1177     case WM_RBUTTONUP:
1178         /* no need to track for rbutton up when opening the popup... the event will be
1179          * swallowed by TrackPopupMenu */
1180         WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, 0);
1181         break;
1182     case WM_MOUSEWHEEL:
1183         /* FIXME: should we scroll too ? */
1184         WCUSER_GenerateMouseInputRecord(data, WCUSER_GetCell(data, lParam), wParam, MOUSE_WHEELED);
1185         break;
1186     case WM_SETFOCUS:
1187         if (data->curcfg.cursor_visible)
1188         {
1189             CreateCaret(PRIVATE(data)->hWnd, PRIVATE(data)->cursor_bitmap,
1190                         data->curcfg.cell_width, data->curcfg.cell_height);
1191             WCUSER_PosCursor(data);
1192         }
1193         break;
1194     case WM_KILLFOCUS:
1195         if (data->curcfg.cursor_visible)
1196             DestroyCaret();
1197         break;
1198     case WM_HSCROLL:
1199         {
1200             int pos = data->curcfg.win_pos.X;
1201
1202             switch (LOWORD(wParam))
1203             {
1204             case SB_PAGEUP:     pos -= 8;               break;
1205             case SB_PAGEDOWN:   pos += 8;               break;
1206             case SB_LINEUP:     pos--;                  break;
1207             case SB_LINEDOWN:   pos++;                  break;
1208             case SB_THUMBTRACK: pos = HIWORD(wParam);   break;
1209             default:                                    break;
1210             }
1211             if (pos < 0) pos = 0;
1212             if (pos > data->curcfg.sb_width - data->curcfg.win_width)
1213                 pos = data->curcfg.sb_width - data->curcfg.win_width;
1214             if (pos != data->curcfg.win_pos.X)
1215             {
1216                 ScrollWindow(hWnd, (data->curcfg.win_pos.X - pos) * data->curcfg.cell_width, 0,
1217                              NULL, NULL);
1218                 data->curcfg.win_pos.X = pos;
1219                 SetScrollPos(hWnd, SB_HORZ, pos, TRUE);
1220                 UpdateWindow(hWnd);
1221                 WCUSER_PosCursor(data);
1222                 WINECON_NotifyWindowChange(data);
1223             }
1224         }
1225         break;
1226     case WM_VSCROLL:
1227         {
1228             int pos = data->curcfg.win_pos.Y;
1229
1230             switch (LOWORD(wParam))
1231             {
1232             case SB_PAGEUP:     pos -= 8;               break;
1233             case SB_PAGEDOWN:   pos += 8;               break;
1234             case SB_LINEUP:     pos--;                  break;
1235             case SB_LINEDOWN:   pos++;                  break;
1236             case SB_THUMBTRACK: pos = HIWORD(wParam);   break;
1237             default:                                    break;
1238             }
1239             if (pos < 0) pos = 0;
1240             if (pos > data->curcfg.sb_height - data->curcfg.win_height)
1241                 pos = data->curcfg.sb_height - data->curcfg.win_height;
1242             if (pos != data->curcfg.win_pos.Y)
1243             {
1244                 ScrollWindow(hWnd, 0, (data->curcfg.win_pos.Y - pos) * data->curcfg.cell_height,
1245                              NULL, NULL);
1246                 data->curcfg.win_pos.Y = pos;
1247                 SetScrollPos(hWnd, SB_VERT, pos, TRUE);
1248                 UpdateWindow(hWnd);
1249                 WCUSER_PosCursor(data);
1250                 WINECON_NotifyWindowChange(data);
1251             }
1252
1253         }
1254     case WM_SYSCOMMAND:
1255         switch (wParam)
1256         {
1257         case IDS_DEFAULT:
1258             WCUSER_GetProperties(data, FALSE);
1259             break;
1260         case IDS_PROPERTIES:
1261             WCUSER_GetProperties(data, TRUE);
1262             break;
1263         default:
1264             return DefWindowProc(hWnd, uMsg, wParam, lParam);
1265         }
1266         break;
1267     case WM_COMMAND:
1268         switch (wParam)
1269         {
1270         case IDS_DEFAULT:
1271             WCUSER_GetProperties(data, FALSE);
1272             break;
1273         case IDS_PROPERTIES:
1274             WCUSER_GetProperties(data, TRUE);
1275             break;
1276         case IDS_MARK:
1277             PRIVATE(data)->selectPt1.X = PRIVATE(data)->selectPt1.Y = 0;
1278             PRIVATE(data)->selectPt2.X = PRIVATE(data)->selectPt2.Y = 0;
1279             WCUSER_SetSelection(data, 0);
1280             PRIVATE(data)->has_selection = TRUE;
1281             break;
1282         case IDS_COPY:
1283             if (PRIVATE(data)->has_selection)
1284             {
1285                 PRIVATE(data)->has_selection = FALSE;
1286                 WCUSER_SetSelection(data, 0);
1287                 WCUSER_CopySelectionToClipboard(data);
1288             }
1289             break;
1290         case IDS_PASTE:
1291             WCUSER_PasteFromClipboard(data);
1292             break;
1293         case IDS_SELECTALL:
1294             PRIVATE(data)->selectPt1.X = PRIVATE(data)->selectPt1.Y = 0;
1295             PRIVATE(data)->selectPt2.X = (data->curcfg.sb_width - 1) * data->curcfg.cell_width;
1296             PRIVATE(data)->selectPt2.Y = (data->curcfg.sb_height - 1) * data->curcfg.cell_height;
1297             WCUSER_SetSelection(data, 0);
1298             PRIVATE(data)->has_selection = TRUE;
1299             break;
1300         case IDS_SCROLL:
1301         case IDS_SEARCH:
1302             WINE_FIXME("Unhandled yet command: %x\n", wParam);
1303             break;
1304         default:
1305             return DefWindowProc(hWnd, uMsg, wParam, lParam);
1306         }
1307         break;
1308     case WM_INITMENUPOPUP:
1309         if (!HIWORD(lParam))    return DefWindowProc(hWnd, uMsg, wParam, lParam);
1310         WCUSER_SetMenuDetails(data, GetSystemMenu(PRIVATE(data)->hWnd, FALSE));
1311         break;
1312     default:
1313         return DefWindowProc(hWnd, uMsg, wParam, lParam);
1314     }
1315     return 0;
1316 }
1317
1318 /******************************************************************
1319  *              WCUSER_DeleteBackend
1320  *
1321  *
1322  */
1323 void WCUSER_DeleteBackend(struct inner_data* data)
1324 {
1325     if (!PRIVATE(data)) return;
1326     if (PRIVATE(data)->hMemDC)          DeleteDC(PRIVATE(data)->hMemDC);
1327     if (PRIVATE(data)->hWnd)            DestroyWindow(PRIVATE(data)->hWnd);
1328     if (PRIVATE(data)->hFont)           DeleteObject(PRIVATE(data)->hFont);
1329     if (PRIVATE(data)->cursor_bitmap)   DeleteObject(PRIVATE(data)->cursor_bitmap);
1330     if (PRIVATE(data)->hBitmap)         DeleteObject(PRIVATE(data)->hBitmap);
1331     HeapFree(GetProcessHeap(), 0, PRIVATE(data));
1332 }
1333
1334 /******************************************************************
1335  *              WCUSER_MainLoop
1336  *
1337  *
1338  */
1339 static int WCUSER_MainLoop(struct inner_data* data)
1340 {
1341     MSG         msg;
1342
1343     ShowWindow(PRIVATE(data)->hWnd, SW_SHOW);
1344     for (;;)
1345     {
1346         switch (MsgWaitForMultipleObjects(1, &data->hSynchro, FALSE, INFINITE, QS_ALLINPUT))
1347         {
1348         case WAIT_OBJECT_0:
1349             if (!WINECON_GrabChanges(data) && data->curcfg.exit_on_die)
1350                 PostQuitMessage(0);
1351             break;
1352         case WAIT_OBJECT_0+1:
1353             /* need to use PeekMessage loop instead of simple GetMessage:
1354              * multiple messages might have arrived in between,
1355              * so GetMessage would lead to delayed processing */
1356             while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
1357             {
1358                 if (msg.message == WM_QUIT) return 0;
1359                 WINE_TRACE("dispatching msg %04x\n", msg.message);
1360                 DispatchMessage(&msg);
1361             }
1362             break;
1363         default:
1364             WINE_ERR("got pb\n");
1365             /* err */
1366             break;
1367         }
1368     }
1369 }
1370
1371 /******************************************************************
1372  *              WCUSER_InitBackend
1373  *
1374  * Initialisation part II: creation of window.
1375  *
1376  */
1377 BOOL WCUSER_InitBackend(struct inner_data* data)
1378 {
1379     static WCHAR wClassName[] = {'W','i','n','e','C','o','n','s','o','l','e','C','l','a','s','s',0};
1380
1381     WNDCLASS            wndclass;
1382
1383     data->private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct inner_data_user));
1384     if (!data->private) return FALSE;
1385
1386     data->fnMainLoop = WCUSER_MainLoop;
1387     data->fnPosCursor = WCUSER_PosCursor;
1388     data->fnShapeCursor = WCUSER_ShapeCursor;
1389     data->fnComputePositions = WCUSER_ComputePositions;
1390     data->fnRefresh = WCUSER_Refresh;
1391     data->fnResizeScreenBuffer = WCUSER_ResizeScreenBuffer;
1392     data->fnSetTitle = WCUSER_SetTitle;
1393     data->fnSetFont = WCUSER_SetFontPmt;
1394     data->fnScroll = WCUSER_Scroll;
1395     data->fnDeleteBackend = WCUSER_DeleteBackend;
1396
1397     wndclass.style         = 0;
1398     wndclass.lpfnWndProc   = WCUSER_Proc;
1399     wndclass.cbClsExtra    = 0;
1400     wndclass.cbWndExtra    = sizeof(DWORD);
1401     wndclass.hInstance     = GetModuleHandle(NULL);
1402     wndclass.hIcon         = LoadIcon(0, IDI_WINLOGO);
1403     wndclass.hCursor       = LoadCursor(0, IDC_ARROW);
1404     wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1405     wndclass.lpszMenuName  = NULL;
1406     wndclass.lpszClassName = wClassName;
1407
1408     RegisterClass(&wndclass);
1409
1410     CreateWindow(wndclass.lpszClassName, NULL,
1411                  WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_HSCROLL|WS_VSCROLL,
1412                  CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, 0, wndclass.hInstance, data);
1413     if (!PRIVATE(data)->hWnd) return FALSE;
1414
1415     return TRUE;
1416 }