Added Guy's last name
[privoxy.git] / w32log.c
1 const char w32log_rcs[] = "$Id: w32log.c,v 1.25.2.1 2002/08/21 17:59:05 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa//current/Attic/w32log.c,v $
5  *
6  * Purpose     :  Functions for creating and destroying the log window,
7  *                ouputting strings, processing messages and so on.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001-2002 members of
10  *                the Privoxy team.  http://www.privoxy.org/
11  *
12  *                Written by and Copyright (C) 1999 Adam Lock
13  *                <locka@iol.ie>
14  *
15  *                This program is free software; you can redistribute it 
16  *                and/or modify it under the terms of the GNU General
17  *                Public License as published by the Free Software
18  *                Foundation; either version 2 of the License, or (at
19  *                your option) any later version.
20  *
21  *                This program is distributed in the hope that it will
22  *                be useful, but WITHOUT ANY WARRANTY; without even the
23  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
24  *                PARTICULAR PURPOSE.  See the GNU General Public
25  *                License for more details.
26  *
27  *                The GNU General Public License should be included with
28  *                this file.  If not, you can view it at
29  *                http://www.gnu.org/copyleft/gpl.html
30  *                or write to the Free Software Foundation, Inc., 59
31  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
32  *
33  * Revisions   :
34  *    $Log: w32log.c,v $
35  *    Revision 1.25.2.1  2002/08/21 17:59:05  oes
36  *     - "Show Privoxy Window" now a toggle
37  *     - Temp kludge to let user and default action file be edited through win32 GUI (FR 592080)
38  *
39  *    Revision 1.25  2002/04/04 00:36:36  gliptak
40  *    always use pcre for matching
41  *
42  *    Revision 1.24  2002/03/31 17:19:00  jongfoster
43  *    Win32 only: Enabling STRICT to fix a VC++ compile warning.
44  *
45  *    Revision 1.23  2002/03/26 22:57:10  jongfoster
46  *    Web server name should begin www.
47  *
48  *    Revision 1.22  2002/03/24 12:48:23  jongfoster
49  *    Fixing doc links
50  *
51  *    Revision 1.21  2002/03/24 12:07:35  jongfoster
52  *    Consistern name for filters file
53  *
54  *    Revision 1.20  2002/03/24 12:03:47  jongfoster
55  *    Name change
56  *
57  *    Revision 1.19  2002/01/17 21:04:17  jongfoster
58  *    Replacing hard references to the URL of the config interface
59  *    with #defines from project.h
60  *
61  *    Revision 1.18  2001/11/30 23:37:24  jongfoster
62  *    Renaming the Win32 config file to config.txt - this is almost the
63  *    same as the corresponding UNIX name "config"
64  *
65  *    Revision 1.17  2001/11/16 00:46:31  jongfoster
66  *    Fixing compiler warnings
67  *
68  *    Revision 1.16  2001/08/01 19:58:12  jongfoster
69  *    Fixing documentation filenames in help menu, and making status
70  *    option work without needing the "Junkbuster Status.URL" file.
71  *
72  *    Revision 1.15  2001/07/30 22:08:36  jongfoster
73  *    Tidying up #defines:
74  *    - All feature #defines are now of the form FEATURE_xxx
75  *    - Permanently turned off WIN_GUI_EDIT
76  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
77  *
78  *    Revision 1.14  2001/07/29 18:47:05  jongfoster
79  *    Adding missing #include "loadcfg.h"
80  *
81  *    Revision 1.13  2001/07/19 19:15:14  haroon
82  *    - Added a FIXME for EditFile but didn't fix :-)
83  *
84  *    Revision 1.12  2001/07/13 14:04:59  oes
85  *    Removed all #ifdef PCRS
86  *
87  *    Revision 1.11  2001/06/07 23:08:12  jongfoster
88  *    Forward and ACL edit options removed.
89  *
90  *    Revision 1.10  2001/05/31 21:37:11  jongfoster
91  *    GUI changes to rename "permissions file" to "actions file".
92  *
93  *    Revision 1.9  2001/05/31 17:33:13  oes
94  *
95  *    CRLF -> LF
96  *
97  *    Revision 1.8  2001/05/29 09:50:24  jongfoster
98  *    Unified blocklist/imagelist/permissionslist.
99  *    File format is still under discussion, but the internal changes
100  *    are (mostly) done.
101  *
102  *    Also modified interceptor behaviour:
103  *    - We now intercept all URLs beginning with one of the following
104  *      prefixes (and *only* these prefixes):
105  *        * http://i.j.b/
106  *        * http://ijbswa.sf.net/config/
107  *        * http://ijbswa.sourceforge.net/config/
108  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
109  *    - Internal changes so that intercepted and fast redirect pages
110  *      are not replaced with an image.
111  *    - Interceptors now have the option to send a binary page direct
112  *      to the client. (i.e. ijb-send-banner uses this)
113  *    - Implemented show-url-info interceptor.  (Which is why I needed
114  *      the above interceptors changes - a typical URL is
115  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
116  *      The previous mechanism would not have intercepted that, and
117  *      if it had been intercepted then it then it would have replaced
118  *      it with an image.)
119  *
120  *    Revision 1.7  2001/05/26 01:26:34  jongfoster
121  *    New #define, WIN_GUI_EDIT, enables the (embryonic) Win32 GUI editor.
122  *    This #define cannot be set from ./configure - there's no point, it
123  *    doesn't work yet.  See feature request # 425722
124  *
125  *    Revision 1.6  2001/05/26 00:31:30  jongfoster
126  *    Fixing compiler warning about comparing signed/unsigned.
127  *
128  *    Revision 1.5  2001/05/26 00:28:36  jongfoster
129  *    Automatic reloading of config file.
130  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
131  *    Most of the global variables have been moved to a new
132  *    struct configuration_spec, accessed through csp->config->globalname
133  *    Most of the globals remaining are used by the Win32 GUI.
134  *
135  *    Revision 1.4  2001/05/22 18:56:28  oes
136  *    CRLF -> LF
137  *
138  *    Revision 1.3  2001/05/20 15:07:54  jongfoster
139  *    File is now ignored if _WIN_CONSOLE is defined.
140  *
141  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
142  *    Version 2.9.4 checkin.
143  *    - Merged popupfile and cookiefile, and added control over PCRS
144  *      filtering, in new "permissionsfile".
145  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
146  *      file error you now get a message box (in the Win32 GUI) rather
147  *      than the program exiting with no explanation.
148  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
149  *      skipping.
150  *    - Removed tabs from "config"
151  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
152  *    - Bumped up version number.
153  *
154  *    Revision 1.1.1.1  2001/05/15 13:59:07  oes
155  *    Initial import of version 2.9.3 source tree
156  *
157  *
158  *********************************************************************/
159 \f
160
161 #include "config.h"
162
163 #include <assert.h>
164 #include <stdio.h>
165
166 #ifndef STRICT
167 #define STRICT
168 #endif
169 #include <windows.h>
170 #include <richedit.h>
171
172 #include "project.h"
173 #include "w32log.h"
174 #include "w32taskbar.h"
175 #include "win32.h"
176 #include "w32res.h"
177 #include "jcc.h"
178 #include "miscutil.h"
179 #include "errlog.h"
180 #include "loadcfg.h"
181
182 const char w32res_h_rcs[] = W32RES_H_VERSION;
183
184 #ifdef __MINGW32__
185 #include "cygwin.h"
186 const char cygwin_h_rcs[] = CYGWIN_H_VERSION;
187 #endif
188
189 const char w32log_h_rcs[] = W32LOG_H_VERSION;
190
191 #ifndef _WIN_CONSOLE /* entire file */
192
193 /*
194  * Timers and the various durations
195  */
196 #define TIMER_ANIM_ID               1
197 #define TIMER_ANIM_TIME             100
198 #define TIMER_ANIMSTOP_ID           2
199 #define TIMER_ANIMSTOP_TIME         1000
200 #define TIMER_CLIPBUFFER_ID         3
201 #define TIMER_CLIPBUFFER_TIME       1000
202 #define TIMER_CLIPBUFFER_FORCE_ID   4
203 #define TIMER_CLIPBUFFER_FORCE_TIME 5000
204
205 /*
206  * Styles of text that can be output
207  */
208 #define STYLE_NONE      0
209 #define STYLE_HIGHLIGHT 1
210 #define STYLE_LINK      2
211 #define STYLE_HEADER    3
212
213 /*
214  * Number of frames of animation in tray activity sequence
215  */
216 #define ANIM_FRAMES 8
217
218 #define DEFAULT_MAX_BUFFER_LINES    200
219 #define DEFAULT_LOG_FONT_NAME       "MS Sans Serif"
220 #define DEFAULT_LOG_FONT_SIZE       8
221
222 /*
223  * These values affect the way the log window behaves, they should be read
224  * from a file but for the moment, they are hardcoded here. Some options are
225  * configurable through the UI.
226  */
227
228 /* Indicates whether task bar shows activity animation */
229 BOOL g_bShowActivityAnimation = 1;
230
231 /* Indicates whether the log window is shown */
232 BOOL g_bShowLogWindow = 1;
233
234 /* Indicates if the log window appears on the task bar */
235 BOOL g_bShowOnTaskBar = 0;
236
237 /* Indicates whether closing the log window really just hides it */
238 BOOL g_bCloseHidesWindow = 1;
239
240 /* Indicates if messages are logged at all */
241 BOOL g_bLogMessages = 1;
242
243 /* Indicates whether log messages are highlighted */
244 BOOL g_bHighlightMessages = 1;
245
246 /* Indicates if buffer is limited in size */
247 BOOL g_bLimitBufferSize = 1;
248
249 /* Maximum number of lines allowed in buffer when limited */
250 int g_nMaxBufferLines = DEFAULT_MAX_BUFFER_LINES;
251
252 /* Font to use */
253 char g_szFontFaceName[255] = DEFAULT_LOG_FONT_NAME;
254
255 /* Size of font to use */
256 int g_nFontSize = DEFAULT_LOG_FONT_SIZE;
257
258
259 /* FIXME: this is a kludge */
260
261 const char * g_default_actions_file = NULL;
262 const char * g_user_actions_file = NULL;
263 const char * g_re_filterfile = NULL;
264 #ifdef FEATURE_TRUST
265 const char * g_trustfile = NULL;
266 #endif /* def FEATURE_TRUST */
267
268 /* FIXME: end kludge */
269
270 /* Regular expression for detected URLs */
271 #define RE_URL "http:[^ \n\r]*"
272
273 /*
274  * Regular expressions that are used to perform highlight in the log window
275  */
276 static struct _Pattern
277 {
278    const char *str;
279    int style;
280    regex_t buffer;
281 } patterns_to_highlight[] =
282 {
283    /* url headers */
284    { RE_URL,                STYLE_LINK },
285 /* { "[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[^ \n\r]*", STYLE_LINK }, */
286    /* interesting text to highlight */
287    { "crunch!",           STYLE_HIGHLIGHT },
288    /* http headers */
289    { "referer:",            STYLE_HEADER },
290    { "proxy-connection:",   STYLE_HEADER },
291    { "proxy-agent:",        STYLE_HEADER },
292    { "user-agent:",         STYLE_HEADER },
293    { "host:",               STYLE_HEADER },
294    { "accept:",             STYLE_HEADER },
295    { "accept-encoding:",    STYLE_HEADER },
296    { "accept-language:",    STYLE_HEADER },
297    { "accept-charset:",     STYLE_HEADER },
298    { "accept-ranges:",      STYLE_HEADER },
299    { "date:",               STYLE_HEADER },
300    { "cache-control:",      STYLE_HEADER },
301    { "cache-last-checked:", STYLE_HEADER },
302    { "connection:",         STYLE_HEADER },
303    { "content-type",        STYLE_HEADER },
304    { "content-length",      STYLE_HEADER },
305    { "cookie",              STYLE_HEADER },
306    { "last-modified:",      STYLE_HEADER },
307    { "pragma:",             STYLE_HEADER },
308    { "server:",             STYLE_HEADER },
309    { "etag:",               STYLE_HEADER },
310    { "expires:",            STYLE_HEADER },
311    { "warning:",            STYLE_HEADER },
312    /* this is the terminator statement - do not delete! */
313    { NULL,                  STYLE_NONE }
314 };
315
316 /*
317  * Public variables
318  */
319 HWND g_hwndLogFrame;
320
321 /*
322  * Private variables
323  */
324 static CRITICAL_SECTION g_criticalsection;
325 static HWND g_hwndTray;
326 static HWND g_hwndLogBox;
327 static WNDPROC g_fnLogBox;
328 static HICON g_hiconAnim[ANIM_FRAMES];
329 static HICON g_hiconIdle;
330 static HICON g_hiconApp;
331 static int g_nAnimFrame;
332 static BOOL g_bClipPending = FALSE;
333 static int g_nRichEditVersion = 0;
334
335 /*
336  * Private functions
337  */
338 static HWND CreateLogWindow(HINSTANCE hInstance, int nCmdShow);
339 static HWND CreateHiddenLogOwnerWindow(HINSTANCE hInstance);
340 static LRESULT CALLBACK LogWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
341 static LRESULT CALLBACK LogOwnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
342 static LRESULT CALLBACK LogRichEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
343 static BOOL InitRichEdit(void);
344 static void LogClipBuffer(void);
345 static void LogCreatePatternMatchingBuffers(void);
346 static void LogDestroyPatternMatchingBuffers(void);
347 static int LogPutStringNoMatch(const char *pszText, int style);
348
349
350 /*********************************************************************
351  *
352  * Function    :  InitLogWindow
353  *
354  * Description :  Initialise the log window.
355  *
356  * Parameters  :  None
357  *
358  * Returns     :  Always TRUE (there should be error checking on the resources).
359  *
360  *********************************************************************/
361 BOOL InitLogWindow(void)
362 {
363    int i;
364
365    /* Load the icons */
366    g_hiconIdle = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_IDLE));
367    for (i = 0; i < ANIM_FRAMES; i++)
368    {
369       g_hiconAnim[i] = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_ANIMATED1 + i));
370    }
371    g_hiconApp = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_MAINICON));
372
373    /* Create the user interface */
374    g_hwndLogFrame = CreateLogWindow(g_hInstance, g_nCmdShow);
375    g_hwndTray = CreateTrayWindow(g_hInstance);
376    TrayAddIcon(g_hwndTray, 1, g_hiconApp, "Privoxy");
377
378    /* Create pattern matching buffers (for highlighting */
379    LogCreatePatternMatchingBuffers();
380
381    /* Create a critical section to protect multi-threaded access to certain things */
382    InitializeCriticalSection(&g_criticalsection);
383
384    return TRUE;
385
386 }
387
388
389 /*********************************************************************
390  *
391  * Function    :  TermLogWindow
392  *
393  * Description :  Cleanup the logwindow.
394  *
395  * Parameters  :  None
396  *
397  * Returns     :  N/A
398  *
399  *********************************************************************/
400 void TermLogWindow(void)
401 {
402    int i;
403
404    LogDestroyPatternMatchingBuffers();
405
406    TrayDeleteIcon(g_hwndTray, 1);
407    DeleteObject(g_hiconApp);
408    DeleteObject(g_hiconIdle);
409    for (i = 0; i < ANIM_FRAMES; i++)
410    {
411       DeleteObject(g_hiconAnim[i]);
412    }
413
414 }
415
416
417 /*********************************************************************
418  *
419  * Function    :  LogCreatePatternMatchingBuffers
420  *
421  * Description :  Compile the pattern matching buffers.
422  *
423  * Parameters  :  None
424  *
425  * Returns     :  N/A
426  *
427  *********************************************************************/
428 void LogCreatePatternMatchingBuffers(void)
429 {
430    int i;
431    for (i = 0; patterns_to_highlight[i].str != NULL; i++)
432    {
433       regcomp(&patterns_to_highlight[i].buffer, patterns_to_highlight[i].str, REG_ICASE);
434    }
435 }
436
437
438 /*********************************************************************
439  *
440  * Function    :  LogDestroyPatternMatchingBuffers
441  *
442  * Description :  Free up the pattern matching buffers.
443  *
444  * Parameters  :  None
445  *
446  * Returns     :  N/A
447  *
448  *********************************************************************/
449 void LogDestroyPatternMatchingBuffers(void)
450 {
451    int i;
452    for (i = 0; patterns_to_highlight[i].str != NULL; i++)
453    {
454       regfree(&patterns_to_highlight[i].buffer);
455    }
456 }
457
458
459 /*********************************************************************
460  *
461  * Function    :  LogGetURLUnderCursor
462  *
463  * Description :  Returns the URL from under the cursor (remember to free it!).
464  *
465  * Parameters  :  None
466  *
467  * Returns     :  NULL or a pointer to an URL string.
468  *
469  *********************************************************************/
470 char *LogGetURLUnderCursor(void)
471 {
472    char *szResult = NULL;
473    regex_t re;
474    POINT ptCursor;
475    POINTL ptl;
476    DWORD nPos;
477    DWORD nWordStart = 0;
478    DWORD nWordEnd = 0;
479
480    regcomp(&re, RE_URL, REG_ICASE);
481
482    /* Get the position of the cursor over the text window */
483    GetCursorPos(&ptCursor);
484    ScreenToClient(g_hwndLogBox, &ptCursor);
485    ptl.x = ptCursor.x;
486    ptl.y = ptCursor.y;
487
488    /* Search backwards and fowards to obtain the word that is highlighted */
489    nPos = LOWORD(SendMessage(g_hwndLogBox, EM_CHARFROMPOS, 0, (LPARAM) &ptl));
490    nWordStart = SendMessage(g_hwndLogBox, EM_FINDWORDBREAK, WB_LEFT, nPos);
491    nWordEnd = SendMessage(g_hwndLogBox, EM_FINDWORDBREAK, WB_RIGHTBREAK, nPos);
492
493    /* Compare the string to the pattern */
494    if (nWordEnd > nWordStart)
495    {
496       TEXTRANGE range;
497       regmatch_t match;
498
499       range.chrg.cpMin = nWordStart;
500       range.chrg.cpMax = nWordEnd;
501       range.lpstrText = (LPSTR)zalloc(nWordEnd - nWordStart + 1);
502       SendMessage(g_hwndLogBox, EM_GETTEXTRANGE, 0, (LPARAM) &range);
503
504       if (regexec(&re, range.lpstrText, 1, &match, 0) == 0)
505       {
506          szResult = range.lpstrText;
507       }
508       else
509       {
510          free(range.lpstrText);
511       }
512
513       regfree(&re);
514    }
515    return szResult;
516
517 }
518
519
520 /*********************************************************************
521  *
522  * Function    :  LogPutString
523  *
524  * Description :  Inserts text into the logging window.  This is really
525  *                a regexp aware wrapper function to `LogPutStringNoMatch'.
526  *
527  * Parameters  :
528  *          1  :  pszText = pointer to string going to the log window
529  *
530  * Returns     :  1 => success, else the return code from `LogPutStringNoMatch'.
531  *                FIXME: this is backwards to the rest of IJB and to common
532  *                programming practice.  Please use 0 => success instead.
533  *
534  *********************************************************************/
535 int LogPutString(const char *pszText)
536 {
537    int i;
538    int result = 0;
539
540    if (pszText == NULL || strlen(pszText) == 0)
541    {
542       return 1;
543    }
544
545    if (!g_bLogMessages)
546    {
547       return 1;
548    }
549
550    /* Critical section stops multiple threads doing nasty interactions that
551     * foul up the highlighting and output.
552     */
553    EnterCriticalSection(&g_criticalsection);
554
555    if (g_bHighlightMessages)
556    {
557       regmatch_t match;
558
559       /* First things first, regexp scan for various things that we would like highlighted */
560       for (i = 0; patterns_to_highlight[i].str != NULL; i++)
561       {
562          if (regexec(&patterns_to_highlight[i].buffer, pszText, 1, &match, 0) == 0)
563          {
564             char *pszBefore = NULL;
565             char *pszMatch = NULL;
566             char *pszAfter = NULL;
567             int nMatchSize;
568
569             /* Split the string up into pieces representing the strings, before
570                at and after the matching pattern
571              */
572             if (match.rm_so > 0)
573             {
574                pszBefore = (char *)malloc((match.rm_so + 1) * sizeof(char));
575                memset(pszBefore, 0, (match.rm_so + 1) * sizeof(char));
576                strncpy(pszBefore, pszText, match.rm_so);
577             }
578             if (match.rm_eo < (regoff_t)strlen(pszText))
579             {
580                pszAfter = strdup(&pszText[match.rm_eo]);
581             }
582             nMatchSize = match.rm_eo - match.rm_so;
583             pszMatch = (char *)malloc(nMatchSize + 1);
584             strncpy(pszMatch, &pszText[match.rm_so], nMatchSize);
585             pszMatch[nMatchSize] = '\0';
586
587             /* Recursively call LogPutString */
588             if (pszBefore)
589             {
590                LogPutString(pszBefore);
591                free(pszBefore);
592             }
593             if (pszMatch)
594             {
595                LogPutStringNoMatch(pszMatch, patterns_to_highlight[i].style);
596                free(pszMatch);
597             }
598             if (pszAfter)
599             {
600                LogPutString(pszAfter);
601                free(pszAfter);
602             }
603
604             result = 1;
605             goto end;
606          }
607       }
608    }
609
610    result = LogPutStringNoMatch(pszText, STYLE_NONE);
611
612 end:
613    LeaveCriticalSection(&g_criticalsection);
614
615    return result;
616
617 }
618
619
620 /*********************************************************************
621  *
622  * Function    :  LogPutStringNoMatch
623  *
624  * Description :  Puts a string into the logging window.
625  *
626  * Parameters  :
627  *          1  :  pszText = pointer to string going to the log window
628  *          2  :  style = STYLE_NONE, STYLE_HEADER, STYLE_HIGHLIGHT, or STYLE_LINK
629  *
630  * Returns     :  Always 1 => success.
631  *                FIXME: this is backwards to the rest of IJB and to common
632  *                programming practice.  Please use 0 => success instead.
633  *
634  *********************************************************************/
635 int LogPutStringNoMatch(const char *pszText, int style)
636 {
637    CHARRANGE range;
638    CHARFORMAT format;
639    int nTextLength;
640
641    assert(g_hwndLogBox);
642    if (g_hwndLogBox == NULL)
643    {
644       return 1;
645    }
646
647    /* TODO preserve existing selection */
648
649    /* Go to the end of the text */
650    nTextLength = GetWindowTextLength(g_hwndLogBox);
651    range.cpMin = nTextLength;
652    range.cpMax = nTextLength;
653    SendMessage(g_hwndLogBox, EM_EXSETSEL, 0, (LPARAM) &range);
654
655    /* Apply a formatting style */
656    memset(&format, 0, sizeof(format));
657    format.cbSize = sizeof(format);
658    format.dwMask = CFM_BOLD | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_ITALIC | CFM_COLOR | CFM_FACE | CFM_SIZE;
659    format.yHeight = (g_nFontSize * 1440) / 72;
660    strcpy(format.szFaceName, g_szFontFaceName);
661    if (style == STYLE_NONE)
662    {
663       /* DO NOTHING */
664       format.dwEffects |= CFE_AUTOCOLOR;
665    }
666    else if (style == STYLE_HEADER)
667    {
668       format.dwEffects |= CFE_AUTOCOLOR | CFE_ITALIC;
669    }
670    else if (style == STYLE_HIGHLIGHT)
671    {
672       format.dwEffects |= CFE_AUTOCOLOR | CFE_BOLD;
673    }
674    else if (style == STYLE_LINK)
675    {
676       format.dwEffects |= CFE_UNDERLINE;
677       format.crTextColor = RGB(0, 0, 255);
678    }
679    SendMessage(g_hwndLogBox, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &format);
680
681    /* Append text to the end */
682    SendMessage(g_hwndLogBox, EM_REPLACESEL, FALSE, (LPARAM) pszText);
683
684    /* TODO Restore the old selection */
685
686    /* Purge buffer */
687    if (strchr(pszText, '\n') != NULL)
688    {
689       SetTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_ID, TIMER_CLIPBUFFER_TIME, NULL);
690       if (!g_bClipPending)
691       {
692          /* Set the force clip timer going. This timer ensures clipping is done
693             intermittently even when there is a sustained burst of logging
694          */
695          SetTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_FORCE_ID, TIMER_CLIPBUFFER_FORCE_TIME, NULL);
696       }
697       g_bClipPending = TRUE;
698    }
699
700    return 1;
701
702 }
703
704
705 /*********************************************************************
706  *
707  * Function    :  LogShowActivity
708  *
709  * Description :  Start the spinner.
710  *
711  * Parameters  :  None
712  *
713  * Returns     :  N/A
714  *
715  *********************************************************************/
716 void LogShowActivity(void)
717 {
718    /* Start some activity timers */
719    if (g_bShowActivityAnimation)
720    {
721       SetTimer(g_hwndLogFrame, TIMER_ANIM_ID, TIMER_ANIM_TIME, NULL);
722       SetTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID, TIMER_ANIMSTOP_TIME, NULL);
723    }
724
725 }
726
727
728 /*********************************************************************
729  *
730  * Function    :  LogClipBuffer
731  *
732  * Description :  Prunes old lines from the log.
733  *
734  * Parameters  :  None
735  *
736  * Returns     :  N/A
737  *
738  *********************************************************************/
739 void LogClipBuffer(void)
740 {
741    int nLines = SendMessage(g_hwndLogBox, EM_GETLINECOUNT, 0, 0);
742    if (g_bLimitBufferSize && nLines > g_nMaxBufferLines)
743    {
744       /* Compute the range representing the lines to be deleted */
745       LONG nLastLineToDelete = nLines - g_nMaxBufferLines;
746       LONG nLastChar = SendMessage(g_hwndLogBox, EM_LINEINDEX, nLastLineToDelete, 0);
747       CHARRANGE range;
748       range.cpMin = 0;
749       range.cpMax = nLastChar;
750
751       /* TODO get current selection */
752
753       /* TODO adjust and clip old selection against range to be deleted */
754
755       /* Select range and erase it (turning off autoscroll to prevent
756          nasty scrolling) */
757       SendMessage(g_hwndLogBox, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL);
758       SendMessage(g_hwndLogBox, EM_EXSETSEL, 0, (LPARAM) &range);
759       SendMessage(g_hwndLogBox, EM_REPLACESEL, FALSE, (LPARAM) "");
760       SendMessage(g_hwndLogBox, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL);
761
762       /* Restore old selection */
763    }
764
765 }
766
767
768 /*********************************************************************
769  *
770  * Function    :  CreateHiddenLogOwnerWindow
771  *
772  * Description :  Creates a hidden owner window that stops the log
773  *                window appearing in the task bar.
774  *
775  * Parameters  :
776  *          1  :  hInstance = application's instance handle
777  *
778  * Returns     :  Handle to newly created window.
779  *
780  *********************************************************************/
781 HWND CreateHiddenLogOwnerWindow(HINSTANCE hInstance)
782 {
783    static const char *szWndName = "PrivoxyLogOwner";
784    WNDCLASS wc;
785    HWND hwnd;
786
787    wc.style          = 0;
788    wc.lpfnWndProc    = LogOwnerWindowProc;
789    wc.cbClsExtra     = 0;
790    wc.cbWndExtra     = 0;
791    wc.hInstance      = hInstance;
792    wc.hIcon          = 0;
793    wc.hCursor        = 0;
794    wc.hbrBackground  = 0;
795    wc.lpszMenuName   = 0;
796    wc.lpszClassName  = szWndName;
797
798    RegisterClass(&wc);
799
800    hwnd = CreateWindow(szWndName, szWndName,
801       WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
802       CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
803
804    return hwnd;
805
806 }
807
808
809 /*********************************************************************
810  *
811  * Function    :  LogOwnerWindowProc
812  *
813  * Description :  Dummy procedure that does nothing special.
814  *
815  * Parameters  :
816  *          1  :  hwnd = window handle
817  *          2  :  uMsg = message number
818  *          3  :  wParam = first param for this message
819  *          4  :  lParam = next param for this message
820  *
821  * Returns     :  Same as `DefWindowProc'.
822  *
823  *********************************************************************/
824 LRESULT CALLBACK LogOwnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
825 {
826    return DefWindowProc(hwnd, uMsg, wParam, lParam);
827
828 }
829
830
831 /*********************************************************************
832  *
833  * Function    :  CreateLogWindow
834  *
835  * Description :  Create the logging window.
836  *
837  * Parameters  :
838  *          1  :  hInstance = application's instance handle
839  *          2  :  nCmdShow = window show value (MIN, MAX, NORMAL, etc...)
840  *
841  * Returns     :  Handle to newly created window.
842  *
843  *********************************************************************/
844 HWND CreateLogWindow(HINSTANCE hInstance, int nCmdShow)
845 {
846    static const char *szWndName = "PrivoxyLogWindow";
847    static const char *szWndTitle = "Privoxy";
848
849    HWND hwnd = NULL;
850    HWND hwndOwner = (g_bShowOnTaskBar) ? NULL : CreateHiddenLogOwnerWindow(hInstance);
851    RECT rcClient;
852    WNDCLASSEX wc;
853
854    memset(&wc, 0, sizeof(wc));
855    wc.cbSize         = sizeof(wc);
856    wc.style          = CS_DBLCLKS;
857    wc.lpfnWndProc    = LogWindowProc;
858    wc.cbClsExtra     = 0;
859    wc.cbWndExtra     = 0;
860    wc.hInstance      = hInstance;
861    wc.hIcon          = g_hiconApp;
862    wc.hCursor        = 0;
863    wc.hbrBackground  = 0;
864    wc.lpszMenuName   = MAKEINTRESOURCE(IDR_LOGVIEW);
865    wc.lpszClassName  = szWndName;
866    wc.hbrBackground  = GetStockObject(WHITE_BRUSH);
867    RegisterClassEx(&wc);
868
869    hwnd = CreateWindowEx(WS_EX_APPWINDOW, szWndName, szWndTitle,
870       WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
871       CW_USEDEFAULT, hwndOwner, NULL, hInstance, NULL);
872
873    /* Now create a child list box */
874    GetClientRect(hwnd, &rcClient);
875
876    /* Create a rich edit control */
877    InitRichEdit();
878    g_hwndLogBox = CreateWindowEx(0, (g_nRichEditVersion == 0x0100) ? "RichEdit" : RICHEDIT_CLASS, "",
879       ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY | ES_NOHIDESEL | WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE,
880       rcClient.left, rcClient.top, rcClient.right, rcClient.bottom,
881       hwnd, NULL, hInstance, NULL);
882 /* SendMessage(g_hwndLogBox, EM_SETWORDWRAPMODE, 0, 0); */
883
884    /* Subclass the control to catch certain messages */
885    g_fnLogBox = (WNDPROC) GetWindowLong(g_hwndLogBox, GWL_WNDPROC);
886    SetWindowLong(g_hwndLogBox, GWL_WNDPROC, (LONG) LogRichEditProc);
887
888    /* Minimizing looks stupid when the log window is not on the task bar, so hide instead */
889    if (!g_bShowOnTaskBar &&
890          (nCmdShow == SW_SHOWMINIMIZED ||
891           nCmdShow == SW_MINIMIZE ||
892           nCmdShow == SW_SHOWMINNOACTIVE))
893    {
894       nCmdShow = SW_HIDE;
895    }
896
897    ShowWindow(hwnd, nCmdShow);
898    UpdateWindow(hwnd);
899
900    GetClientRect(g_hwndLogFrame, &rcClient);
901    SetWindowPos(g_hwndLogBox, NULL, rcClient.left, rcClient.top, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, SWP_NOZORDER);
902
903    return hwnd;
904
905 }
906
907
908 /*********************************************************************
909  *
910  * Function    :  InitRichEdit
911  *
912  * Description :  Initialise the rich edit control library.
913  *
914  * Parameters  :  None
915  *
916  * Returns     :  TRUE => success, FALSE => failure.
917  *                FIXME: this is backwards to the rest of IJB and to common
918  *                programming practice.  Please use 0 => success instead.
919  *
920  *********************************************************************/
921 BOOL InitRichEdit(void)
922 {
923    static HINSTANCE hInstRichEdit;
924    if (hInstRichEdit == NULL)
925    {
926       g_nRichEditVersion = 0;
927       hInstRichEdit = LoadLibraryA("RICHED20.DLL");
928       if (hInstRichEdit)
929       {
930          g_nRichEditVersion = _RICHEDIT_VER;
931       }
932       else
933       {
934          hInstRichEdit = LoadLibraryA("RICHED32.DLL");
935          if (hInstRichEdit)
936          {
937             g_nRichEditVersion = 0x0100;
938          }
939       }
940    }
941    return (hInstRichEdit != NULL) ? TRUE : FALSE;
942
943 }
944
945
946 /*********************************************************************
947  *
948  * Function    :  ShowLogWindow
949  *
950  * Description :  Shows or hides the log window.  We will also raise the
951  *                window on a show command in case it is buried.
952  *
953  * Parameters  :
954  *          1  :  bShow = TRUE to show, FALSE to mimize/hide
955  *
956  * Returns     :  N/A
957  *
958  *********************************************************************/
959 void ShowLogWindow(BOOL bShow)
960 {
961    if (bShow)
962    {
963       SetForegroundWindow(g_hwndLogFrame);
964       SetWindowPos(g_hwndLogFrame, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
965       
966    }
967    else if (g_bShowOnTaskBar)
968    {
969       ShowWindow(g_hwndLogFrame, SW_MINIMIZE);
970    }
971    else
972    {
973       ShowWindow(g_hwndLogFrame, SW_HIDE);
974    }
975 }
976
977
978 /*********************************************************************
979  *
980  * Function    :  EditFile
981  *
982  * Description :  Opens the specified setting file for editing.
983  * FIXME: What if the file has no associated application. Check for return values
984 *        from ShellExecute??
985  *
986  * Parameters  :
987  *          1  :  filename = filename from the config (aka config.txt) file.
988  *
989  * Returns     :  N/A
990  *
991  *********************************************************************/
992 void EditFile(const char *filename)
993 {
994    if (filename)
995    {
996       ShellExecute(g_hwndLogFrame, "open", filename, NULL, NULL, SW_SHOWNORMAL);
997    }
998
999 }
1000
1001
1002 /*--------------------------------------------------------------------------*/
1003 /* Windows message handlers                                                 */
1004 /*--------------------------------------------------------------------------*/
1005
1006
1007 /*********************************************************************
1008  *
1009  * Function    :  OnLogRButtonUp
1010  *
1011  * Description :  Handler for WM_RBUTTONUP messages.
1012  *
1013  * Parameters  :
1014  *          1  :  nModifier = wParam from mouse message (unused)
1015  *          2  :  x = x coordinate of the mouse event
1016  *          3  :  y = y coordinate of the mouse event
1017  *
1018  * Returns     :  N/A
1019  *
1020  *********************************************************************/
1021 void OnLogRButtonUp(int nModifier, int x, int y)
1022 {
1023    HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_POPUP_SELECTION));
1024    if (hMenu != NULL)
1025    {
1026       HMENU hMenuPopup = GetSubMenu(hMenu, 0);
1027
1028       /* Check if there is a selection */
1029       CHARRANGE range;
1030       SendMessage(g_hwndLogBox, EM_EXGETSEL, 0, (LPARAM) &range);
1031       if (range.cpMin == range.cpMax)
1032       {
1033          EnableMenuItem(hMenuPopup, ID_EDIT_COPY, MF_BYCOMMAND | MF_GRAYED);
1034       }
1035       else
1036       {
1037          EnableMenuItem(hMenuPopup, ID_EDIT_COPY, MF_BYCOMMAND | MF_ENABLED);
1038       }
1039
1040       /* Display the popup */
1041       TrackPopupMenu(hMenuPopup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, x, y, 0, g_hwndLogFrame, NULL);
1042       DestroyMenu(hMenu);
1043    }
1044
1045 }
1046
1047
1048 /*********************************************************************
1049  *
1050  * Function    :  OnLogCommand
1051  *
1052  * Description :  Handler for WM_COMMAND messages.
1053  *
1054  * Parameters  :
1055  *          1  :  nCommand = the command portion of the menu selection event
1056  *
1057  * Returns     :  N/A
1058  *
1059  *********************************************************************/
1060 void OnLogCommand(int nCommand)
1061 {
1062    switch (nCommand)
1063    {
1064       case ID_TOGGLE_SHOWWINDOW:
1065          g_bShowLogWindow = !g_bShowLogWindow;
1066
1067          ShowLogWindow(g_bShowLogWindow);
1068          break;
1069
1070       case ID_FILE_EXIT:
1071          PostMessage(g_hwndLogFrame, WM_CLOSE, 0, 0);
1072          break;
1073
1074       case ID_EDIT_COPY:
1075          SendMessage(g_hwndLogBox, WM_COPY, 0, 0);
1076          break;
1077
1078       case ID_VIEW_CLEARLOG:
1079          SendMessage(g_hwndLogBox, WM_SETTEXT, 0, (LPARAM) "");
1080          break;
1081
1082       case ID_VIEW_LOGMESSAGES:
1083          g_bLogMessages = !g_bLogMessages;
1084          /* SaveLogSettings(); */
1085          break;
1086
1087       case ID_VIEW_MESSAGEHIGHLIGHTING:
1088          g_bHighlightMessages = !g_bHighlightMessages;
1089          /* SaveLogSettings(); */
1090          break;
1091
1092       case ID_VIEW_LIMITBUFFERSIZE:
1093          g_bLimitBufferSize = !g_bLimitBufferSize;
1094          /* SaveLogSettings(); */
1095          break;
1096
1097       case ID_VIEW_ACTIVITYANIMATION:
1098          g_bShowActivityAnimation = !g_bShowActivityAnimation;
1099          /* SaveLogSettings(); */
1100          break;
1101
1102 #ifdef FEATURE_TOGGLE
1103       /* by haroon - change toggle to its opposite value */
1104       case ID_TOGGLE_ENABLED:
1105          g_bToggleIJB = !g_bToggleIJB;
1106          if (g_bToggleIJB)
1107          {
1108             log_error(LOG_LEVEL_INFO, "Now toggled ON.");
1109          }
1110          else
1111          {
1112             log_error(LOG_LEVEL_INFO, "Now toggled OFF.");
1113          }
1114          break;
1115 #endif /* def FEATURE_TOGGLE */
1116
1117       case ID_TOOLS_EDITCONFIG:
1118          EditFile(configfile);
1119          break;
1120
1121       case ID_TOOLS_EDITDEFAULTACTIONS:
1122          EditFile(g_default_actions_file);
1123          break;
1124
1125       case ID_TOOLS_EDITUSERACTIONS:
1126          EditFile(g_user_actions_file);
1127          break;
1128
1129       case ID_TOOLS_EDITFILTERS:
1130          EditFile(g_re_filterfile);
1131          break;
1132
1133 #ifdef FEATURE_TRUST
1134       case ID_TOOLS_EDITTRUST:
1135          EditFile(g_trustfile);
1136          break;
1137 #endif /* def FEATURE_TRUST */
1138
1139       case ID_HELP_GPL:
1140          ShellExecute(g_hwndLogFrame, "open", "LICENSE.txt", NULL, NULL, SW_SHOWNORMAL);
1141          break;
1142
1143       case ID_HELP_FAQ:
1144          ShellExecute(g_hwndLogFrame, "open", "doc\\faq\\index.html", NULL, NULL, SW_SHOWNORMAL);
1145          break;
1146
1147       case ID_HELP_MANUAL:
1148          ShellExecute(g_hwndLogFrame, "open", "doc\\user-manual\\index.html", NULL, NULL, SW_SHOWNORMAL);
1149          break;
1150
1151       case ID_HELP_STATUS:
1152          ShellExecute(g_hwndLogFrame, "open", CGI_PREFIX "show-status", NULL, NULL, SW_SHOWNORMAL);
1153          break;
1154
1155       case ID_HELP_ABOUT:
1156          MessageBox(g_hwndLogFrame, win32_blurb, "About Privoxy", MB_OK);
1157          break;
1158
1159       default:
1160          /* DO NOTHING */
1161          break;
1162    }
1163
1164 }
1165
1166
1167 /*********************************************************************
1168  *
1169  * Function    :  OnLogInitMenu
1170  *
1171  * Description :  Handler for WM_INITMENU messages.  Enable, disable,
1172  *                check, and/or uncheck menu options as apropos.
1173  *
1174  * Parameters  :
1175  *          1  :  hmenu = handle to menu to "make current"
1176  *
1177  * Returns     :  N/A
1178  *
1179  *********************************************************************/
1180 void OnLogInitMenu(HMENU hmenu)
1181 {
1182    /* Only enable editors if there is a file to edit */
1183    EnableMenuItem(hmenu, ID_TOOLS_EDITDEFAULTACTIONS, MF_BYCOMMAND | (g_default_actions_file ? MF_ENABLED : MF_GRAYED));
1184    EnableMenuItem(hmenu, ID_TOOLS_EDITUSERACTIONS, MF_BYCOMMAND | (g_user_actions_file ? MF_ENABLED : MF_GRAYED));
1185    EnableMenuItem(hmenu, ID_TOOLS_EDITFILTERS, MF_BYCOMMAND | (g_re_filterfile ? MF_ENABLED : MF_GRAYED));
1186 #ifdef FEATURE_TRUST
1187    EnableMenuItem(hmenu, ID_TOOLS_EDITTRUST, MF_BYCOMMAND | (g_trustfile ? MF_ENABLED : MF_GRAYED));
1188 #endif /* def FEATURE_TRUST */
1189
1190    /* Check/uncheck options */
1191    CheckMenuItem(hmenu, ID_VIEW_LOGMESSAGES, MF_BYCOMMAND | (g_bLogMessages ? MF_CHECKED : MF_UNCHECKED));
1192    CheckMenuItem(hmenu, ID_VIEW_MESSAGEHIGHLIGHTING, MF_BYCOMMAND | (g_bHighlightMessages ? MF_CHECKED : MF_UNCHECKED));
1193    CheckMenuItem(hmenu, ID_VIEW_LIMITBUFFERSIZE, MF_BYCOMMAND | (g_bLimitBufferSize ? MF_CHECKED : MF_UNCHECKED));
1194    CheckMenuItem(hmenu, ID_VIEW_ACTIVITYANIMATION, MF_BYCOMMAND | (g_bShowActivityAnimation ? MF_CHECKED : MF_UNCHECKED));
1195 #ifdef FEATURE_TOGGLE
1196    /* by haroon - menu item for Enable toggle on/off */
1197    CheckMenuItem(hmenu, ID_TOGGLE_ENABLED, MF_BYCOMMAND | (g_bToggleIJB ? MF_CHECKED : MF_UNCHECKED));
1198 #endif /* def FEATURE_TOGGLE */
1199    CheckMenuItem(hmenu, ID_TOGGLE_SHOWWINDOW, MF_BYCOMMAND | (g_bShowLogWindow ? MF_CHECKED : MF_UNCHECKED));
1200
1201 }
1202
1203
1204 /*********************************************************************
1205  *
1206  * Function    :  OnLogTimer
1207  *
1208  * Description :  Handler for WM_TIMER messages.
1209  *
1210  * Parameters  :
1211  *          1  :  nTimer = timer id (animation start/stop or clip buffer)
1212  *
1213  * Returns     :  N/A
1214  *
1215  *********************************************************************/
1216 void OnLogTimer(int nTimer)
1217 {
1218    switch (nTimer)
1219    {
1220       case TIMER_ANIM_ID:
1221          TraySetIcon(g_hwndTray, 1, g_hiconAnim[g_nAnimFrame++ % ANIM_FRAMES]);
1222          break;
1223
1224       case TIMER_ANIMSTOP_ID:
1225          g_nAnimFrame = 0;
1226          TraySetIcon(g_hwndTray, 1, g_hiconIdle);
1227          KillTimer(g_hwndLogFrame, TIMER_ANIM_ID);
1228          KillTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID);
1229          break;
1230
1231       case TIMER_CLIPBUFFER_ID:
1232       case TIMER_CLIPBUFFER_FORCE_ID:
1233          LogClipBuffer();
1234          g_bClipPending = FALSE;
1235          KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_ID);
1236          KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_FORCE_ID);
1237          break;
1238
1239       default:
1240          /* DO NOTHING */
1241          break;
1242    }
1243
1244 }
1245
1246
1247 /*********************************************************************
1248  *
1249  * Function    :  LogRichEditProc
1250  *
1251  * Description :  Window subclass routine handles some events for the rich edit control.
1252  *
1253  * Parameters  :
1254  *          1  :  hwnd = window handle of the rich edit control
1255  *          2  :  uMsg = message number
1256  *          3  :  wParam = first param for this message
1257  *          4  :  lParam = next param for this message
1258  *
1259  * Returns     :  Appropriate M$ window message handler codes.
1260  *
1261  *********************************************************************/
1262 LRESULT CALLBACK LogRichEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1263 {
1264    switch (uMsg)
1265    {
1266       case WM_RBUTTONUP:
1267       {
1268          POINT pt;
1269          pt.x = LOWORD(lParam);
1270          pt.y = HIWORD(lParam);
1271          ClientToScreen(hwnd, &pt);
1272          OnLogRButtonUp(wParam, pt.x, pt.y);
1273       }
1274       return 0;
1275    }
1276    return CallWindowProc(g_fnLogBox, hwnd, uMsg, wParam, lParam);
1277
1278 }
1279
1280
1281 /*********************************************************************
1282  *
1283  * Function    :  LogWindowProc
1284  *
1285  * Description :  Windows call back routine handles events on the log window.
1286  *
1287  * Parameters  :
1288  *          1  :  hwnd = handle of the logging window
1289  *          2  :  uMsg = message number
1290  *          3  :  wParam = first param for this message
1291  *          4  :  lParam = next param for this message
1292  *
1293  * Returns     :  Appropriate M$ window message handler codes.
1294  *
1295  *********************************************************************/
1296 LRESULT CALLBACK LogWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1297 {
1298    switch (uMsg)
1299    {
1300       case WM_CREATE:
1301          return 0;
1302
1303       case WM_CLOSE:
1304          /* This is the end - beautiful friend - the end */
1305          DestroyWindow(g_hwndLogBox);
1306          DestroyWindow(g_hwndLogFrame);
1307          return 0;
1308
1309       case WM_DESTROY:
1310          PostQuitMessage(0);
1311          return 0;
1312
1313       case WM_SHOWWINDOW:
1314          g_bShowLogWindow = wParam;
1315       case WM_SIZE:
1316          /* Resize the logging window to fit the new frame */
1317          if (g_hwndLogBox)
1318          {
1319             RECT rc;
1320             GetClientRect(g_hwndLogFrame, &rc);
1321             SetWindowPos(g_hwndLogBox, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
1322          }
1323          return 0;
1324
1325       case WM_INITMENU:
1326          OnLogInitMenu((HMENU) wParam);
1327          return 0;
1328
1329       case WM_TIMER:
1330          OnLogTimer(wParam);
1331          return 0;
1332
1333       case WM_COMMAND:
1334          OnLogCommand(LOWORD(wParam));
1335          return 0;
1336
1337       case WM_SYSCOMMAND:
1338          switch (wParam)
1339          {
1340             case SC_CLOSE:
1341                if (g_bCloseHidesWindow)
1342                {
1343                   ShowLogWindow(FALSE);
1344                   return 0;
1345                }
1346                break;
1347             case SC_MINIMIZE:
1348                ShowLogWindow(FALSE);
1349                return 0;
1350          }
1351          break;
1352    }
1353
1354    return DefWindowProc(hwnd, uMsg, wParam, lParam);
1355
1356 }
1357
1358 #endif /* ndef _WIN_CONSOLE - entire file */
1359
1360 /*
1361   Local Variables:
1362   tab-width: 3
1363   end:
1364 */