443 lines
12 KiB
C
443 lines
12 KiB
C
/*
|
|
* Clock
|
|
*
|
|
* Copyright 1998 Marcel Baur <mbaur@g26.ethz.ch>
|
|
*
|
|
* Clock is partially based on
|
|
* - Program Manager by Ulrich Schmied
|
|
* - rolex.c by Jim Peterson
|
|
*
|
|
*
|
|
* 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
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "windows.h"
|
|
#include "commdlg.h"
|
|
#include "shellapi.h"
|
|
|
|
#include "main.h"
|
|
#include "winclock.h"
|
|
|
|
#define INITIAL_WINDOW_SIZE 200
|
|
#define TIMER_ID 1
|
|
|
|
CLOCK_GLOBALS Globals;
|
|
|
|
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)
|
|
{
|
|
WCHAR szCaption[MAX_STRING_LEN];
|
|
int chars = 0;
|
|
|
|
/* Set frame caption */
|
|
if (Globals.bDate) {
|
|
chars = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL,
|
|
szCaption, sizeof(szCaption)/sizeof(WCHAR));
|
|
if (chars) {
|
|
--chars;
|
|
szCaption[chars++] = ' ';
|
|
szCaption[chars++] = '-';
|
|
szCaption[chars++] = ' ';
|
|
szCaption[chars] = '\0';
|
|
}
|
|
}
|
|
LoadStringW(0, IDS_CLOCK, szCaption + chars, MAX_STRING_LEN - chars);
|
|
SetWindowTextW(Globals.hMainWnd, szCaption);
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* 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)) {
|
|
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);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* 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)
|
|
{
|
|
LOGFONTW lf;
|
|
CHOOSEFONTW cf;
|
|
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;
|
|
if (ChooseFontW(&cf)) {
|
|
Globals.logfont = lf;
|
|
CLOCK_ResetFont();
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* CLOCK_ToggleTitle
|
|
*/
|
|
static VOID CLOCK_ToggleTitle(VOID)
|
|
{
|
|
/* Also shows/hides the menu */
|
|
LONG style = GetWindowLongW(Globals.hMainWnd, GWL_STYLE);
|
|
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);
|
|
SetWindowRgn(Globals.hMainWnd, 0, TRUE);
|
|
}
|
|
SetWindowLongW(Globals.hMainWnd, GWL_STYLE, style);
|
|
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();
|
|
}
|
|
/***********************************************************************
|
|
*
|
|
* CLOCK_MenuCommand
|
|
*
|
|
* All handling of main menu events
|
|
*/
|
|
|
|
static int CLOCK_MenuCommand (WPARAM wParam)
|
|
{
|
|
WCHAR szApp[MAX_STRING_LEN];
|
|
WCHAR szAppRelease[MAX_STRING_LEN];
|
|
switch (wParam) {
|
|
/* switch to analog */
|
|
case IDM_ANALOG: {
|
|
Globals.bAnalog = TRUE;
|
|
CLOCK_UpdateMenuCheckmarks();
|
|
CLOCK_ResetTimer();
|
|
InvalidateRect(Globals.hMainWnd, NULL, FALSE);
|
|
break;
|
|
}
|
|
/* switch to digital */
|
|
case IDM_DIGITAL: {
|
|
Globals.bAnalog = FALSE;
|
|
CLOCK_UpdateMenuCheckmarks();
|
|
CLOCK_ResetTimer();
|
|
CLOCK_ResetFont();
|
|
InvalidateRect(Globals.hMainWnd, NULL, FALSE);
|
|
break;
|
|
}
|
|
/* change font */
|
|
case IDM_FONT: {
|
|
CLOCK_ChooseFont();
|
|
break;
|
|
}
|
|
/* hide title bar */
|
|
case IDM_NOTITLE: {
|
|
CLOCK_ToggleTitle();
|
|
break;
|
|
}
|
|
/* always on top */
|
|
case IDM_ONTOP: {
|
|
CLOCK_ToggleOnTop();
|
|
break;
|
|
}
|
|
/* show or hide seconds */
|
|
case IDM_SECONDS: {
|
|
Globals.bSeconds = !Globals.bSeconds;
|
|
CLOCK_UpdateMenuCheckmarks();
|
|
CLOCK_ResetTimer();
|
|
if (!Globals.bAnalog)
|
|
CLOCK_ResetFont();
|
|
InvalidateRect(Globals.hMainWnd, NULL, FALSE);
|
|
break;
|
|
}
|
|
/* show or hide date */
|
|
case IDM_DATE: {
|
|
Globals.bDate = !Globals.bDate;
|
|
CLOCK_UpdateMenuCheckmarks();
|
|
CLOCK_UpdateWindowCaption();
|
|
break;
|
|
}
|
|
/* show "about" box */
|
|
case IDM_ABOUT: {
|
|
LoadStringW(Globals.hInstance, IDS_CLOCK, szApp, sizeof(szApp)/sizeof(WCHAR));
|
|
lstrcpyW(szAppRelease,szApp);
|
|
ShellAboutW(Globals.hMainWnd, szApp, szAppRelease, 0);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* CLOCK_Paint
|
|
*/
|
|
static VOID CLOCK_Paint(HWND hWnd)
|
|
{
|
|
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 */
|
|
FillRect(dcMem, &ps.rcPaint, GetSysColorBrush(COLOR_3DFACE));
|
|
|
|
if(Globals.bAnalog)
|
|
AnalogClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.bWithoutTitle);
|
|
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);
|
|
|
|
EndPaint(hWnd, &ps);
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* CLOCK_WndProc
|
|
*/
|
|
|
|
static LRESULT WINAPI CLOCK_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg) {
|
|
/* L button drag moves the window */
|
|
case WM_NCHITTEST: {
|
|
LRESULT ret = DefWindowProcW(hWnd, msg, wParam, lParam);
|
|
if (ret == HTCLIENT)
|
|
ret = HTCAPTION;
|
|
return ret;
|
|
}
|
|
|
|
case WM_NCLBUTTONDBLCLK:
|
|
case WM_LBUTTONDBLCLK: {
|
|
CLOCK_ToggleTitle();
|
|
break;
|
|
}
|
|
|
|
case WM_PAINT: {
|
|
CLOCK_Paint(hWnd);
|
|
break;
|
|
|
|
}
|
|
|
|
case WM_SIZE: {
|
|
Globals.MaxX = LOWORD(lParam);
|
|
Globals.MaxY = HIWORD(lParam);
|
|
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 );
|
|
}
|
|
CLOCK_ResetFont();
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND: {
|
|
CLOCK_MenuCommand(wParam);
|
|
break;
|
|
}
|
|
|
|
case WM_TIMER: {
|
|
/* Could just invalidate what has changed,
|
|
* but it doesn't really seem worth the effort
|
|
*/
|
|
InvalidateRect(Globals.hMainWnd, NULL, FALSE);
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY: {
|
|
PostQuitMessage (0);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* WinMain
|
|
*/
|
|
|
|
int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
|
|
{
|
|
MSG msg;
|
|
WNDCLASSW class;
|
|
|
|
static const WCHAR szClassName[] = {'C','L','C','l','a','s','s',0};
|
|
static const WCHAR szWinName[] = {'C','l','o','c','k',0};
|
|
|
|
/* Setup Globals */
|
|
memset(&Globals.hFont, 0, sizeof (Globals.hFont));
|
|
Globals.bAnalog = TRUE;
|
|
Globals.bSeconds = TRUE;
|
|
|
|
if (!prev){
|
|
class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
|
|
class.lpfnWndProc = CLOCK_WndProc;
|
|
class.cbClsExtra = 0;
|
|
class.cbWndExtra = 0;
|
|
class.hInstance = hInstance;
|
|
class.hIcon = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
|
|
class.hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
|
|
class.hbrBackground = 0;
|
|
class.lpszMenuName = 0;
|
|
class.lpszClassName = szClassName;
|
|
}
|
|
|
|
if (!RegisterClassW(&class)) return FALSE;
|
|
|
|
Globals.MaxX = Globals.MaxY = INITIAL_WINDOW_SIZE;
|
|
Globals.hMainWnd = CreateWindowW(szClassName, szWinName, WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
Globals.MaxX, Globals.MaxY, 0,
|
|
0, hInstance, 0);
|
|
|
|
if (!CLOCK_ResetTimer())
|
|
return FALSE;
|
|
|
|
Globals.hMainMenu = LoadMenuW(0, MAKEINTRESOURCEW(MAIN_MENU));
|
|
SetMenu(Globals.hMainWnd, Globals.hMainMenu);
|
|
CLOCK_UpdateMenuCheckmarks();
|
|
CLOCK_UpdateWindowCaption();
|
|
|
|
ShowWindow (Globals.hMainWnd, show);
|
|
UpdateWindow (Globals.hMainWnd);
|
|
|
|
while (GetMessageW(&msg, 0, 0, 0)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
|
|
KillTimer(Globals.hMainWnd, TIMER_ID);
|
|
DeleteObject(Globals.hFont);
|
|
|
|
return 0;
|
|
}
|