Introduction
For a long time, I wanted to write a clock for my machine. I dislike the feel of digital clocks, days to days, years to years, work work and work... May be, I am AN OLD ANTIQUE! I just like the freedom in the fresh blue sky!
A transparent clock? Should it appear like a piece of glass? Or some pixels block on the desktop, and transparency around the hour hand and minute hand? How can the second hand go around the center point and hold the transparency around it obviously?
Well, I decide to write it under Windows 2000, because I can use the transparent layer attributes. To realize it, I have to fulfill these:
First, in the OnCreate
section, register a hot key for turning it on when you double click on the hour hand or minute hand, and force it disappear. Next, change the window style to WS_EX_TOOLWINDOW
, thus, make it disappear from the task bar. Last, setting the WS_EX_LAYERED
attributes, and it shows me the result. Following is the code in OnCreate
section.
int CClockDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
m_bHotkeyReg=RegisterHotKey(GetSafeHwnd(),
theApp.m_hatomHotKey,MOD_ALT|MOD_CONTROL|MOD_SHIFT,VK_F12);
CRect rc;
GetWindowRect(&rc);
HDC hdc=::GetDC(GetSafeHwnd());
CSize size(GetDeviceCaps(hdc,LOGPIXELSX),GetDeviceCaps(hdc,LOGPIXELSY));
int w=(size.cx/size.cy)*rc.Height();
SetWindowPos(NULL,0,0,w,rc.Height(),
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
::ReleaseDC(GetSafeHwnd(),hdc);
m_size=CSize(w,rc.Height());
SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,WS_EX_TOOLWINDOW);
DWORD Style = ::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE);
Style |= WS_EX_LAYERED;
::SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,Style);
::SetLayeredWindowAttributes( GetSafeHwnd(),
m_ColorBackground,
(BYTE)(255.f * m_fTransPercent/100.f),LWA_ALPHA|LWA_COLORKEY);
return 0;
}
Is it easy? If you don't know about the comments of the code, that is apprehensible, because I am a Chinese man, and my English so poor, but I'll try my best to make it clear for you.
From the APP file, you can see two procedures SayInChinese
and PlayWaveSound
. I ignore those here because I made them work in my early project (it can speak in Chinese words). So, you can make it speak in English by the next step.
In the first step, before you can use these codes, you should do the following things:
- Copy clock.mdb file to ./Debug sub folder and ./Release folder.
- Rebuild it and run.
Use one class named CClockRSet
. It is derived from CDaoRecordset
. I use it to record information of attendance. Use two procedures to perform it:
BOOL GetTodayID(UINT& uID)
{
SYSTEMTIME st;
GetLocalTime(&st);
CString sql;
sql.Format("select * from [KaoQin] where (YEAR(RiQi)=%d
and MONTH(RiQi)=%d and DAY(RiQi)=%d)",
st.wYear,st.wMonth,st.wDay);
CClockRSet rs;
rs.Open(AFX_DB_USE_DEFAULT_TYPE,sql,CRecordset::readOnly);
ASSERT(rs.IsOpen());
if(!rs.IsOpen()) return FALSE;
BOOL bRet=FALSE;
if(rs.GetRecordCount()>0)
{
rs.MoveFirst();
uID=rs.m_ID;
bRet=TRUE;
}
if(rs.IsOpen()) rs.Close();
return bRet;
}
It checks for the first record while checking in today, and returns the ID. Returns zero for failure.
if(!GetTodayID(m_uTodayID))
{
if(FillClock(BAODAO,m_uTodayID))
{
VERIFY(GetTodayID(m_uTodayID));
ASSERT(m_uTodayID!=0);
}
}
An entry making one record today:
BOOL CClockDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_ESCAPE)
{
ShowWindow(SW_MINIMIZE);
ShowWindow(SW_HIDE);
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Perform hiding your clock by pressing ESC key.
void CClockDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CDialog::OnLButtonDown(nFlags, point);
CRect rcTest;
for(int i=1;i<=12;i++)
{
rcTest=GetHourRect(i);
if(rcTest.PtInRect(point))
{
if(i==12)
{
if(m_uTodayID!=0)
FillClock(WUFAN,m_uTodayID);
}
else if(i==1)
{
if(m_uTodayID!=0)
FillClock(PMBAODAO,m_uTodayID);
}
else if(i==6)
{
if(m_uTodayID!=0)
FillClock(XIABAN,m_uTodayID);
}
else if(i==7)
{
if(m_uTodayID!=0)
FillClock(NIGHTBAODAO,m_uTodayID);
}
DWORD dwFlag=SND_RESOURCE;
::PlaySound(MAKEINTRESOURCE(IDR_WAVE_DIDA),
AfxGetResourceHandle(),dwFlag);
if(m_uDiDaTimer!=0)
{
KillTimer(m_uDiDaTimer);
m_uDiDaTimer=0;
}
m_uDiDaCount+=i;
const int OKPASSWORD=1+2+3+4+5+6;
TRACE("%d m_uDiDaCount=%d/%d\n",i,m_uDiDaCount,OKPASSWORD);
if(m_uDiDaCount!=OKPASSWORD)
{
m_uDiDaTimer=SetTimer(2,1000*10,NULL);
}
else
{
::PlaySound(MAKEINTRESOURCE(IDR_WAVE_DONE),
AfxGetResourceHandle(),dwFlag);
DoLoginAction();
m_uDiDaCount=0;
}
return;
}
}
PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y));
}
In the OnTimer
procedure, it does something to make it speak Chinese. It can say something like :"Good morning sir, it'is XXXX clock now.", but it says in Chinese. You can download the Chinese speaking API next time.
CString ss;
if(st.wHour>=0 && st.wHour<3) ss="凌晨";
if(st.wHour>=3 && st.wHour<6) ss="清晨";
if(st.wHour>=6 && st.wHour<9) ss="早晨";
if(st.wHour>=9 && st.wHour<12) ss="中午";
if(st.wHour>=12 && st.wHour<15) ss="下午";
if(st.wHour>=15 && st.wHour<18) ss="傍晚";
if(st.wHour>=18 && st.wHour<21) ss="晚上";
if(st.wHour>=21 && st.wHour<=23) ss="深夜";
if(st.wMinute!=m_nLastMinute)
{
if(st.wMinute==30)
{
if(theApp.GetProfileInt("settings","CanAlarmHalfHour",FALSE))
{
CString sFile=theApp.GetProfileString("settings",
"HalfHourAlarmFile","");
if(!sFile.IsEmpty() && PathFileExists(sFile))
{
CString sExt=PathFindExtension(sFile);
if(sExt.CompareNoCase(".wav")==0)
{
theApp.PlayWaveSound(sFile);
}
}
if(theApp.GetProfileInt("settings",
"UseChineseVoiceInAlarm",TRUE))
{
CString s;
s.Format("%s %d点%d分",
ss,st.wHour,st.wMinute);
theApp.SayInChinese(s);
}
}
}
}
if(m_nLastHour != st.wHour)
{
if(theApp.GetProfileInt("settings","CanAlarmHour",TRUE))
{
CString sFile=theApp.GetProfileString("settings",
"HourAlarmFile","");
if(!sFile.IsEmpty() && PathFileExists(sFile))
{
CString sExt=PathFindExtension(sFile);
if(sExt.CompareNoCase(".wav")==0)
{
theApp.PlayWaveSound(sFile);
}
}
if(theApp.GetProfileInt("settings",
"UseChineseVoiceInAlarm",TRUE))
{
CString sE;
sE="正";
if(st.wMinute!=0)
sE.Format("%d分",st.wMinute);
CString s;
s.Format("%s %d点%s",ss,st.wHour,sE);
theApp.SayInChinese(s);
}
}
}
Make it more transparent while you right click it.
void CClockDlg::OnRButtonDown(UINT nFlags, CPoint point)
{
CDialog::OnRButtonDown(nFlags, point);
::SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,
(LONG)(::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE) & ~WS_EX_LAYERED));
::SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,
(LONG)(::GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE) | WS_EX_LAYERED));
if(m_fTransPercent>0.f)
m_fTransPercent-=5.f;
if(m_fTransPercent<10.f)
m_fTransPercent=10.f;
::SetLayeredWindowAttributes( GetSafeHwnd(),
m_ColorBackground,
(BYTE)(255.f * m_fTransPercent/100.f),LWA_ALPHA|LWA_COLORKEY);
BLENDFUNCTION bl={AC_SRC_OVER,0,
(BYTE)(255.f * m_fTransPercent/100.f),AC_SRC_ALPHA};
::UpdateLayeredWindow(GetSafeHwnd(),
NULL,
NULL,NULL,
NULL,
NULL,
m_ColorBackground,
&bl,
ULW_ALPHA|ULW_COLORKEY);
theApp.WriteProfileInt("Settings","ClockTransparent",(int)m_fTransPercent);
}
void CClockDlg::OnHotKey(WPARAM wParam, LPARAM lParam)
{
WORD wL,wH;
wL=LOWORD(lParam);
wH=HIWORD(lParam);
if((wL & MOD_ALT) && (wL & MOD_CONTROL) && (wL & MOD_SHIFT))
{
if(wH==VK_F12)
{
if(!::IsWindowVisible(GetSafeHwnd()))
{
ShowWindow(SW_RESTORE);
ShowWindow(SW_SHOW);
}
}
}
}
The hotkey for showing it again.
What can I say more? It is obvious and clear to you, and I will upload the Chinese speaking API (CSAPI) next week.