main.c 12.5 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4 5 6 7 8 9
/*
 * Clock
 *
 * Copyright 1998 Marcel Baur <mbaur@g26.ethz.ch>
 *
 * Clock is partially based on
 * - Program Manager by Ulrich Schmied
 * - rolex.c by Jim Peterson
 *
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
24 25 26
 */

#include <stdio.h>
Richard Cohen's avatar
Richard Cohen committed
27

Alexandre Julliard's avatar
Alexandre Julliard committed
28
#include "windows.h"
Richard Cohen's avatar
Richard Cohen committed
29
#include "commdlg.h"
30
#include "shellapi.h"
Richard Cohen's avatar
Richard Cohen committed
31

Alexandre Julliard's avatar
Alexandre Julliard committed
32 33 34
#include "main.h"
#include "winclock.h"

35 36 37
#define INITIAL_WINDOW_SIZE 200
#define TIMER_ID 1

Alexandre Julliard's avatar
Alexandre Julliard committed
38 39
CLOCK_GLOBALS Globals;

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
static VOID CLOCK_UpdateMenuCheckmarks(VOID)
{
    HMENU hPropertiesMenu;
    hPropertiesMenu = GetSubMenu(Globals.hMainMenu, 0);
    if (!hPropertiesMenu)
	return;

    if(Globals.bAnalog) {

        /* analog clock */
        CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_ANALOG, MF_CHECKED);
        EnableMenuItem(hPropertiesMenu, IDM_FONT, MF_GRAYED);
    }
    else
    {
        /* digital clock */
        CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_DIGITAL, MF_CHECKED);
        EnableMenuItem(hPropertiesMenu, IDM_FONT, 0);
    }

    CheckMenuItem(hPropertiesMenu, IDM_NOTITLE, (Globals.bWithoutTitle ? MF_CHECKED : MF_UNCHECKED));

    CheckMenuItem(hPropertiesMenu, IDM_ONTOP, (Globals.bAlwaysOnTop ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hPropertiesMenu, IDM_SECONDS, (Globals.bSeconds ? MF_CHECKED : MF_UNCHECKED));
    CheckMenuItem(hPropertiesMenu, IDM_DATE, (Globals.bDate ? MF_CHECKED : MF_UNCHECKED));
}

static VOID CLOCK_UpdateWindowCaption(VOID)
{
69
    WCHAR szCaption[MAX_STRING_LEN];
70 71 72 73
    int chars = 0;

    /* Set frame caption */
    if (Globals.bDate) {
74 75
	chars = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL,
                               szCaption, sizeof(szCaption)/sizeof(WCHAR));
76 77 78 79 80 81 82 83
        if (chars) {
	    --chars;
	    szCaption[chars++] = ' ';
	    szCaption[chars++] = '-';
	    szCaption[chars++] = ' ';
	    szCaption[chars] = '\0';
	}
    }
84 85
    LoadStringW(0, IDS_CLOCK, szCaption + chars, MAX_STRING_LEN - chars);
    SetWindowTextW(Globals.hMainWnd, szCaption);
86 87
}

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
/***********************************************************************
 *
 *           CLOCK_ResetTimer
 */
static BOOL CLOCK_ResetTimer(void)
{
    UINT period; /* milliseconds */

    KillTimer(Globals.hMainWnd, TIMER_ID);

    if (Globals.bSeconds)
	if (Globals.bAnalog)
	    period = 50;
	else
	    period = 500;
    else
	period = 1000;

    if (!SetTimer (Globals.hMainWnd, TIMER_ID, period, NULL)) {
107 108 109 110
        static const WCHAR notimersW[] = {'N','o',' ','a','v','a','i','l','a','b','l','e',' ','t','i','m','e','r','s',0};
        WCHAR szApp[MAX_STRING_LEN];
        LoadStringW(Globals.hInstance, IDS_CLOCK, szApp, MAX_STRING_LEN);
        MessageBoxW(0, notimersW, szApp, MB_ICONEXCLAMATION | MB_OK);
111 112 113 114 115
        return FALSE;
    }
    return TRUE;
}

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
/***********************************************************************
 *
 *           CLOCK_ResetFont
 */
static VOID CLOCK_ResetFont(VOID)
{
    HFONT newfont;
    HDC dc = GetDC(Globals.hMainWnd);
    newfont = SizeFont(dc, Globals.MaxX, Globals.MaxY, Globals.bSeconds, &Globals.logfont);
    if (newfont) {
	DeleteObject(Globals.hFont);
	Globals.hFont = newfont;
    }
	
    ReleaseDC(Globals.hMainWnd, dc);
}


/***********************************************************************
 *
 *           CLOCK_ChooseFont
 */
static VOID CLOCK_ChooseFont(VOID)
{
140 141
    LOGFONTW lf;
    CHOOSEFONTW cf;
142 143 144 145 146 147
    memset(&cf, 0, sizeof(cf));
    lf = Globals.logfont;
    cf.lStructSize = sizeof(cf);
    cf.hwndOwner = Globals.hMainWnd;
    cf.lpLogFont = &lf;
    cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT;
148
    if (ChooseFontW(&cf)) {
149 150 151 152 153 154 155 156 157 158 159 160
	Globals.logfont = lf;
	CLOCK_ResetFont();
    }
}

/***********************************************************************
 *
 *           CLOCK_ToggleTitle
 */
static VOID CLOCK_ToggleTitle(VOID)
{
    /* Also shows/hides the menu */
161
    LONG style = GetWindowLongW(Globals.hMainWnd, GWL_STYLE);
162 163 164 165 166 167 168
    if ((Globals.bWithoutTitle = !Globals.bWithoutTitle)) {
	style = (style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP|WS_THICKFRAME;
	SetMenu(Globals.hMainWnd, 0);
    }
    else {
	style = (style & ~(WS_POPUP|WS_THICKFRAME)) | WS_OVERLAPPEDWINDOW;
        SetMenu(Globals.hMainWnd, Globals.hMainMenu);
169
        SetWindowRgn(Globals.hMainWnd, 0, TRUE);
170
    }
171
    SetWindowLongW(Globals.hMainWnd, GWL_STYLE, style);
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    SetWindowPos(Globals.hMainWnd, 0,0,0,0,0, 
		 SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
    
    CLOCK_UpdateMenuCheckmarks();
    CLOCK_UpdateWindowCaption();
}

/***********************************************************************
 *
 *           CLOCK_ToggleOnTop
 */
static VOID CLOCK_ToggleOnTop(VOID)
{
    if ((Globals.bAlwaysOnTop = !Globals.bAlwaysOnTop)) {
	SetWindowPos(Globals.hMainWnd, HWND_TOPMOST, 0,0,0,0,
		     SWP_NOMOVE|SWP_NOSIZE);
    }
    else {
	SetWindowPos(Globals.hMainWnd, HWND_NOTOPMOST, 0,0,0,0,
		     SWP_NOMOVE|SWP_NOSIZE);
    }
    CLOCK_UpdateMenuCheckmarks();
}
Alexandre Julliard's avatar
Alexandre Julliard committed
195 196 197 198 199 200 201
/***********************************************************************
 *
 *           CLOCK_MenuCommand
 *
 *  All handling of main menu events
 */

202
static int CLOCK_MenuCommand (WPARAM wParam)
203
{
204 205
    WCHAR szApp[MAX_STRING_LEN];
    WCHAR szAppRelease[MAX_STRING_LEN];
206 207
    switch (wParam) {
        /* switch to analog */
208
        case IDM_ANALOG: {
209
            Globals.bAnalog = TRUE;
210
            CLOCK_UpdateMenuCheckmarks();
211 212
	    CLOCK_ResetTimer();
	    InvalidateRect(Globals.hMainWnd, NULL, FALSE);
213 214 215
            break;
        }
            /* switch to digital */
216
        case IDM_DIGITAL: {
217
            Globals.bAnalog = FALSE;
218
            CLOCK_UpdateMenuCheckmarks();
219
	    CLOCK_ResetTimer();
220
	    CLOCK_ResetFont();
221
	    InvalidateRect(Globals.hMainWnd, NULL, FALSE);
222 223 224
            break;
        }
            /* change font */
225
        case IDM_FONT: {
226
            CLOCK_ChooseFont();
227 228 229
            break;
        }
            /* hide title bar */
230
        case IDM_NOTITLE: {
231
	    CLOCK_ToggleTitle();
232 233 234
            break;
        }
            /* always on top */
235
        case IDM_ONTOP: {
236
	    CLOCK_ToggleOnTop();
237 238 239
            break;
        }
            /* show or hide seconds */
240
        case IDM_SECONDS: {
241
            Globals.bSeconds = !Globals.bSeconds;
242
            CLOCK_UpdateMenuCheckmarks();
243
	    CLOCK_ResetTimer();
244 245
	    if (!Globals.bAnalog)
		CLOCK_ResetFont();
246
	    InvalidateRect(Globals.hMainWnd, NULL, FALSE);
247 248 249
            break;
        }
            /* show or hide date */
250
        case IDM_DATE: {
251
            Globals.bDate = !Globals.bDate;
252 253
            CLOCK_UpdateMenuCheckmarks();
            CLOCK_UpdateWindowCaption();
254 255 256
            break;
        }
            /* show "about" box */
257
        case IDM_ABOUT: {
258 259 260
            LoadStringW(Globals.hInstance, IDS_CLOCK, szApp, sizeof(szApp)/sizeof(WCHAR));
            lstrcpyW(szAppRelease,szApp);
            ShellAboutW(Globals.hMainWnd, szApp, szAppRelease, 0);
261 262 263 264 265
            break;
        }
    }
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
266

267 268 269 270 271
/***********************************************************************
 *
 *           CLOCK_Paint
 */
static VOID CLOCK_Paint(HWND hWnd)
272
{
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
    PAINTSTRUCT ps;
    HDC dcMem, dc;
    HBITMAP bmMem, bmOld;

    dc = BeginPaint(hWnd, &ps);

    /* Use an offscreen dc to avoid flicker */
    dcMem = CreateCompatibleDC(dc);
    bmMem = CreateCompatibleBitmap(dc, ps.rcPaint.right - ps.rcPaint.left,
				    ps.rcPaint.bottom - ps.rcPaint.top);

    bmOld = SelectObject(dcMem, bmMem);

    SetViewportOrgEx(dcMem, -ps.rcPaint.left, -ps.rcPaint.top, NULL);
    /* Erase the background */
288
    FillRect(dcMem, &ps.rcPaint, GetSysColorBrush(COLOR_3DFACE));
289 290

    if(Globals.bAnalog)
291
	AnalogClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.bWithoutTitle);
292 293 294 295 296 297 298 299 300 301 302 303 304 305
    else
	DigitalClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.hFont);

    /* Blit the changes to the screen */
    BitBlt(dc, 
	   ps.rcPaint.left, ps.rcPaint.top,
	   ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
           dcMem,
	   ps.rcPaint.left, ps.rcPaint.top,
           SRCCOPY);

    SelectObject(dcMem, bmOld);
    DeleteObject(bmMem);
    DeleteDC(dcMem);
306
    
307
    EndPaint(hWnd, &ps);
Alexandre Julliard's avatar
Alexandre Julliard committed
308
}
Alexandre Julliard's avatar
Alexandre Julliard committed
309 310 311 312 313 314

/***********************************************************************
 *
 *           CLOCK_WndProc
 */

315
static LRESULT WINAPI CLOCK_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
316 317
{
    switch (msg) {
318 319
	/* L button drag moves the window */
        case WM_NCHITTEST: {
320
	    LRESULT ret = DefWindowProcW(hWnd, msg, wParam, lParam);
321 322 323 324
	    if (ret == HTCLIENT)
		ret = HTCAPTION;
            return ret;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
325

326 327 328
        case WM_NCLBUTTONDBLCLK:
        case WM_LBUTTONDBLCLK: {
	    CLOCK_ToggleTitle();
Alexandre Julliard's avatar
Alexandre Julliard committed
329 330 331
            break;
        }

332
        case WM_PAINT: {
333
	    CLOCK_Paint(hWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
334
            break;
335

Alexandre Julliard's avatar
Alexandre Julliard committed
336 337 338
        }

        case WM_SIZE: {
339 340
            Globals.MaxX = LOWORD(lParam);
            Globals.MaxY = HIWORD(lParam);
341 342 343 344 345 346 347 348 349 350 351 352 353
            if (Globals.bAnalog && Globals.bWithoutTitle)
            {
                RECT rect;
                INT diameter = min( Globals.MaxX, Globals.MaxY );
                HRGN hrgn = CreateEllipticRgn( (Globals.MaxX - diameter) / 2,
                                               (Globals.MaxY - diameter) / 2,
                                               (Globals.MaxX + diameter) / 2,
                                               (Globals.MaxY + diameter) / 2 );
                GetWindowRect( hWnd, &rect );
                MapWindowPoints( 0, hWnd, (LPPOINT)&rect, 2 );
                OffsetRgn( hrgn, -rect.left, -rect.top );
                SetWindowRgn( Globals.hMainWnd, hrgn, TRUE );
            }
354
	    CLOCK_ResetFont();
355
            break;
Alexandre Julliard's avatar
Alexandre Julliard committed
356
        }
357

Alexandre Julliard's avatar
Alexandre Julliard committed
358 359 360
        case WM_COMMAND: {
            CLOCK_MenuCommand(wParam);
            break;
361
        }
362
            
363
        case WM_TIMER: {
364
            /* Could just invalidate what has changed,
Richard Cohen's avatar
Richard Cohen committed
365 366 367 368
             * but it doesn't really seem worth the effort
             */
	    InvalidateRect(Globals.hMainWnd, NULL, FALSE);
	    break;
369
        }
370

Alexandre Julliard's avatar
Alexandre Julliard committed
371 372 373 374 375 376
        case WM_DESTROY: {
            PostQuitMessage (0);
            break;
        }

        default:
377
            return DefWindowProcW(hWnd, msg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
378
    }
379
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
380 381 382 383 384 385 386 387
}


/***********************************************************************
 *
 *           WinMain
 */

388
int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
Alexandre Julliard's avatar
Alexandre Julliard committed
389 390
{
    MSG      msg;
391
    WNDCLASSW class;
392

393 394
    static const WCHAR szClassName[] = {'C','L','C','l','a','s','s',0};
    static const WCHAR szWinName[]   = {'C','l','o','c','k',0};
395

Alexandre Julliard's avatar
Alexandre Julliard committed
396
    /* Setup Globals */
397
    memset(&Globals.hFont, 0, sizeof (Globals.hFont));
398
    Globals.bAnalog         = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
399
    Globals.bSeconds        = TRUE;
400
    
Alexandre Julliard's avatar
Alexandre Julliard committed
401
    if (!prev){
402
        class.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
403 404 405
        class.lpfnWndProc   = CLOCK_WndProc;
        class.cbClsExtra    = 0;
        class.cbWndExtra    = 0;
406
        class.hInstance     = hInstance;
407 408
        class.hIcon         = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
        class.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
409
        class.hbrBackground = 0;
410 411
        class.lpszMenuName  = 0;
        class.lpszClassName = szClassName;
Alexandre Julliard's avatar
Alexandre Julliard committed
412
    }
413
    
414
    if (!RegisterClassW(&class)) return FALSE;
415
    
416
    Globals.MaxX = Globals.MaxY = INITIAL_WINDOW_SIZE;
417
    Globals.hMainWnd = CreateWindowW(szClassName, szWinName, WS_OVERLAPPEDWINDOW,
418 419
                                     CW_USEDEFAULT, CW_USEDEFAULT,
                                     Globals.MaxX, Globals.MaxY, 0,
420
                                     0, hInstance, 0);
421

422
    if (!CLOCK_ResetTimer())
423
        return FALSE;
424

425
    Globals.hMainMenu = LoadMenuW(0, MAKEINTRESOURCEW(MAIN_MENU));
Alexandre Julliard's avatar
Alexandre Julliard committed
426
    SetMenu(Globals.hMainWnd, Globals.hMainMenu);
427 428
    CLOCK_UpdateMenuCheckmarks();
    CLOCK_UpdateWindowCaption();
429
    
Alexandre Julliard's avatar
Alexandre Julliard committed
430 431
    ShowWindow (Globals.hMainWnd, show);
    UpdateWindow (Globals.hMainWnd);
432
    
433
    while (GetMessageW(&msg, 0, 0, 0)) {
434
        TranslateMessage(&msg);
435
        DispatchMessageW(&msg);
Alexandre Julliard's avatar
Alexandre Julliard committed
436 437
    }

438
    KillTimer(Globals.hMainWnd, TIMER_ID);
439
    DeleteObject(Globals.hFont);
440

441
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
442
}