📄 todaypluginwnd.cpp.svn-base
字号:
/**
* TodayPluginWnd.cpp
*
* Copyright (C) 2008 David Andrs <pda@jasnapaka.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "StdAfx.h"
#include "prssrtoday.h"
#include "TodayPluginWnd.h"
#include "todaycmn.h"
#include "Config.h"
#include "../prssr/ctrls/CePropertySheet.h"
#include "OptChannelsPg.h"
#include "OptAppearancePg.h"
#include "OptModePg.h"
#include "../prssr/xml/FeedFile.h"
#include "../share/helpers.h"
#include "../share/defs.h"
#include "../share/reg.h"
#include "../share/wnd.h"
#include "../share/date.h"
#include "../share/fs.h"
#ifdef MYDEBUG
#undef THIS_FILE
static TCHAR THIS_FILE[] = _T(__FILE__);
#include "../prssr/debug/crtdbg.h"
#define new MYDEBUG_NEW
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define FILE_CHANGE_NOT_EXISTS 0
#define FILE_CHANGE_NO_CHANGE 1
#define FILE_CHANGE_UDPATED 2
#define CONFIG_DEFAULT_FONT_SIZE 9
#define LINE_SPACING 2
//int CyclingSpeed[] = { 1000, 2500, 5000, 7500, 10000 };
UINT WM_SH_UIMETRIC_CHANGE;
UINT ReadConfigMessage;
UINT CheckFeedsMessage;
UINT UpdateFeedFlagsMessage;
UINT ReloadSubscriptionsMessage;
CTodayPluginWnd TodayPluginWnd;
BOOL APIENTRY CustomItemOptionsDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam);
DWORD WINAPI ThreadStubProc(LPVOID lpPar) {
CTodayPluginWnd *plugin = (CTodayPluginWnd *) lpPar;
return plugin->Thread();
}
/////////////////////////////////////////////////////////////////////////////
// CTodayPluginWnd
CTodayPluginWnd::CTodayPluginWnd() {
ItemIdx = 0;
SiteIdx = -1;
HThread = NULL;
CycleTimer = 1;
TapAndHoldTimer = 2;
FileCheckIdx = 0;
InitializeCriticalSection(&CSSiteList);
HTerminate = CreateEvent(NULL, FALSE, FALSE, NULL);
HReloadSubscriptions = CreateEvent(NULL, FALSE, FALSE, NULL);
HCheckFeeds = CreateEvent(NULL, FALSE, FALSE, NULL);
HUpdateFeedFlags = CreateEvent(NULL, FALSE, FALSE, NULL);
}
CTodayPluginWnd::~CTodayPluginWnd() {
SiteList.Destroy();
DeleteCriticalSection(&CSSiteList);
CloseHandle(HTerminate);
CloseHandle(HReloadSubscriptions);
CloseHandle(HCheckFeeds);
CloseHandle(HUpdateFeedFlags);
}
BEGIN_MESSAGE_MAP(CTodayPluginWnd, CWnd)
//{{AFX_MSG_MAP(CTodayPluginWnd)
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_TIMER()
ON_WM_DESTROY()
ON_WM_SIZE()
ON_COMMAND(ID_TODAY_BRIEF, OnTodayBrief)
ON_COMMAND(ID_TODAY_CYCLING, OnTodayCycling)
ON_COMMAND(ID_TODAY_UPDATEALL, OnTodayUpdateall)
ON_COMMAND(ID_TODAY_SETTINGS, OnTodaySettings)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTodayPluginWnd message handlers
BOOL CTodayPluginWnd::Register() {
Unregister();
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) ::DefWindowProc;
wc.hInstance = AfxGetInstanceHandle();
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpszClassName = TODAY_CLASS_NAME;
// register our window
if (!AfxRegisterClass(&wc)) {
TRACE(_T("Class Registration Failed\n"));
return FALSE;
}
WM_SH_UIMETRIC_CHANGE = RegisterWindowMessage(SH_UIMETRIC_CHANGE);
ReadConfigMessage = RegisterWindowMessage(READ_CONFIG_MESSAGE);
ReloadSubscriptionsMessage = RegisterWindowMessage(RELOAD_SUBSCRIPTIONS_MESSAGE);
CheckFeedsMessage = RegisterWindowMessage(CHECK_FEEDS_MESSAGE);
UpdateFeedFlagsMessage = RegisterWindowMessage(UPDATE_FEED_FLAGS_MESSAGE);
return TRUE;
}
BOOL CTodayPluginWnd::Unregister() {
UnregisterClass(TODAY_CLASS_NAME, AfxGetInstanceHandle());
return TRUE;
}
void CTodayPluginWnd::LoadFonts() {
LOG0(5, "CTodayPluginWnd::LoadFonts()");
m_fntNormal.DeleteObject();
m_fntBold.DeleteObject();
LONG dwFontSize;
if (Config.FontSize == -1) {
dwFontSize = SCALEY(CONFIG_DEFAULT_FONT_SIZE);
CDC *pDC = GetDC();
SHGetUIMetrics(SHUIM_FONTSIZE_PIXEL, &dwFontSize, sizeof(dwFontSize), NULL);
ReleaseDC(pDC);
}
else {
dwFontSize = SCALEY(Config.FontSize);
}
HGDIOBJ hObj = ::GetStockObject(SYSTEM_FONT);
LOGFONT lf;
::GetObject(hObj, sizeof(LOGFONT), (LPVOID) &lf);
lf.lfHeight = -dwFontSize;
m_fntNormal.CreateFontIndirect(&lf);
RowHeight = (int) (1.2 * dwFontSize);
lf.lfWeight = FW_BOLD;
m_fntBold.CreateFontIndirect(&lf);
}
BOOL CTodayPluginWnd::Create(HWND hwndParent) {
LOG1(5, "CTodayPluginWnd::Create(%x)", hwndParent);
m_hWndParent = hwndParent;
BOOL ret = CreateEx(0, TODAY_CLASS_NAME, TODAY_WINDOW_NAME, WS_VISIBLE | WS_CHILD,
0, 0, GetSystemMetrics(SM_CXSCREEN), 0, hwndParent, (HMENU) 0x1234);
// load the font
LoadFonts();
HIcon = (HICON) ::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_DISPLAYICON), IMAGE_ICON, SCALEX(16), SCALEX(16), 0);
EnterCriticalSection(&CSSiteList);
// LoadSiteList(SiteList);
if (SiteList.GetCount() > 0)
SiteIdx = 0;
else
SiteIdx = -1;
LeaveCriticalSection(&CSSiteList);
// SetTimer(CycleTimer, CyclingSpeed[Config.CyclingSpeedIdx], NULL);
SetTimer(CycleTimer, Config.CyclingSpeed * 1000, NULL);
return TRUE;
}
void CTodayPluginWnd::OnDestroy() {
LOG0(5, "CTodayPluginWnd::OnDestroy()");
SetEvent(HTerminate);
if (WaitForSingleObject(HThread, 4000) == WAIT_OBJECT_0) {
}
CWnd::OnDestroy();
KillTimer(CycleTimer);
KillTimer(TapAndHoldTimer);
DestroyIcon(HIcon); HIcon = NULL;
m_fntNormal.DeleteObject();
m_fntBold.DeleteObject();
}
BOOL CTodayPluginWnd::OnEraseBkgnd(CDC* pDC) {
LOG1(5, "CTodayPluginWnd::OnEraseBkgnd(%x)", pDC);
TODAYDRAWWATERMARKINFO dwi;
dwi.hwnd = GetSafeHwnd();
dwi.hdc = pDC->GetSafeHdc();
GetClientRect(&dwi.rc);
::SendMessage(m_hWndParent, TODAYM_DRAWWATERMARK, 0, (LPARAM) &dwi);
return TRUE;
}
int CTodayPluginWnd::DrawSiteTitle(CDC &dc, CSiteItem *si, CRect *rc, UINT uFormat) {
LOG2(5, "CTodayPluginWnd::DrawSiteTitle(, %p, , %d)", si, uFormat);
int nNewCount = si->GetUnreadCount();
CString strTitle = si->Name;
ReplaceHTMLEntities(strTitle);
CString buffer;
if (nNewCount > 0)
buffer.Format(_T("%s (%d)"), strTitle, nNewCount);
else
buffer.Format(_T("%s"), strTitle);
int nWidth = rc->Width();
CRect rcTemp = *rc;
dc.DrawText(buffer, &rcTemp, uFormat | DT_CALCRECT);
if (rcTemp.Width() > nWidth) {
// Text doesn't fit in rect. We have to truncate it and add ellipsis to the end.
for (int i = strTitle.GetLength(); i >= 0; i--) {
CString strName = strTitle.Left(i);
if (nNewCount > 0)
buffer.Format(_T("%s... (%d)"), strName, nNewCount);
else
buffer.Format(_T("%s..."), strName);
rcTemp = *rc;
dc.DrawText(buffer, &rcTemp, uFormat | DT_CALCRECT);
if (rcTemp.Width() < nWidth) {
// Gotcha!
break;
}
}
return dc.DrawText(buffer, rc, uFormat);
}
return dc.DrawText(buffer, rc, uFormat);
}
void CTodayPluginWnd::DrawMessage(CDC &dc, CRect &rect, UINT nID) {
// no news
CString strNoNews;
strNoNews.LoadString(nID);
dc.SelectObject(m_fntNormal);
dc.DrawText(strNoNews, &rect, DT_LEFT | DT_TOP);
}
void CTodayPluginWnd::DrawBrief(CDC &dc, CRect &rcLabel) {
LOG0(5, "CTodayPluginWnd::DrawBrief()");
// only brief view
if (NewItems == 0 && UnreadItems == 0) {
DrawMessage(dc, rcLabel, IDS_NO_NEWS);
}
else {
CString strText;
if (NewItems > 0) {
if (UnreadItems > 0)
strText.Format(IDS_NEW_UNREAD_ITEMS, NewItems, UnreadItems);
else
strText.Format(IDS_NEW_ITEMS, NewItems);
}
else
strText.Format(IDS_UNREAD_ITEMS, UnreadItems);
dc.SelectObject(m_fntBold);
dc.DrawText(strText, &rcLabel, DT_LEFT | DT_TOP);
}
}
void CTodayPluginWnd::DrawCycling(CDC &dc, CRect &rcLabel) {
dc.SelectObject(m_fntBold);
CSiteItem *si = SiteList.GetAt(SiteIdx);
switch (Config.CyclingSubMode) {
case CYCLING_SUBMODE_SITES:
DrawSiteTitle(dc, si, &rcLabel, DT_LEFT | DT_TOP);
break;
case CYCLING_SUBMODE_FEED_ITEMS:
// show site name
if (Config.ShowSiteName) {
DrawSiteTitle(dc, si, &rcLabel, DT_LEFT | DT_TOP);
CPen pen(PS_SOLID, 1, RGB(255, 255, 255));
dc.SelectObject(pen);
POINT ln[2] = {
{ rcLabel.left, rcLabel.top + RowHeight + SCALEY(2) },
{ rcLabel.right, rcLabel.top + RowHeight + SCALEY(2) }
};
dc.Polyline(ln, 2);
rcLabel.top += RowHeight + SCALEY(2) + SCALEY(2);
rcLabel.bottom += RowHeight + SCALEY(2) + SCALEY(2);
}
dc.SelectObject(m_fntNormal);
// show feed item
if (si->Feed != NULL &&
ItemIdx >= 0 && ItemIdx < si->Feed->GetItemCount())
{
CFeedItem *fi = si->Feed->GetItem(ItemIdx);
dc.SelectObject(m_fntNormal);
CString sFeedTitle = fi->Title;
ReplaceHTMLEntities(sFeedTitle);
DrawTextEndEllipsis(dc, sFeedTitle, rcLabel, DT_LEFT | DT_TOP | DT_NOPREFIX);
if (Config.ShowDateTime) {
rcLabel.top += RowHeight;
rcLabel.bottom += RowHeight;
CString strDateTime;
SYSTEMTIME st = { 0 };
if (fi->PubDate.wYear != 0) {
// if (Config.GMTTimes)
// st = fi->PubDate; // leave at GMT
// else
st = TimeToTimeZone(&fi->PubDate); // convert to local time zone
}
if (!FormatDateTime(strDateTime, st, Config.ShowRelativeDates))
strDateTime.Empty();
dc.DrawText(strDateTime, &rcLabel, DT_LEFT | DT_TOP);
}
}
else {
DrawMessage(dc, rcLabel, IDS_NO_NEWS);
}
break;
default:
DrawMessage(dc, rcLabel, IDS_NO_NEWS);
break;
}
}
void CTodayPluginWnd::OnPaint() {
LOG0(5, "CTodayPluginWnd::OnPaint()");
CPaintDC dc(this); // device context for painting
int saveDC = dc.SaveDC();
CRect rcClient;
GetClientRect(&rcClient);
// Get appropriate text color from shell
COLORREF crText;
if (Selected) {
COLORREF clrBkgnd = GetParent()->SendMessage(TODAYM_GETCOLOR, (WPARAM) TODAYCOLOR_HIGHLIGHT, NULL);
CBrush brush(clrBkgnd);
dc.FillRect(&rcClient, &brush);
crText = GetParent()->SendMessage(TODAYM_GETCOLOR, (WPARAM) TODAYCOLOR_HIGHLIGHTEDTEXT, NULL);
}
else {
crText = GetParent()->SendMessage(TODAYM_GETCOLOR, (WPARAM) TODAYCOLOR_TEXT, NULL);
}
// Important if you want the watermark behind the text
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(crText);
CRect rcLabel = rcClient;
rcLabel.top += SCALEY(Config.VOffset);
rcLabel.left += SCALEX(4);
rcLabel.right -= SCALEX(4);
if (Loading) {
if (Config.DisplayIcon) {
DrawIconEx(dc.GetSafeHdc(), SCALEX(2), SCALEY(2), HIcon, SCALEX(16), SCALEY(16), 0, NULL, DI_NORMAL);
rcLabel.left += SCALEX(24);
}
DrawMessage(dc, rcLabel, IDS_LOADING);
}
else {
BOOL bNewsAvailable = NewsAvailable();
if (bNewsAvailable || !Config.HidePlugin) {
if (Config.DisplayIcon) {
DrawIconEx(dc.GetSafeHdc(), SCALEX(2), SCALEY(2), HIcon, SCALEX(16), SCALEY(16), 0, NULL, DI_NORMAL);
rcLabel.left += SCALEX(24);
}
if (Config.Mode == MODE_BRIEF)
DrawBrief(dc, rcLabel);
else if (Config.Mode == MODE_CYCLING) {
if (SiteIdx != -1 && bNewsAvailable)
DrawCycling(dc, rcLabel);
else
DrawMessage(dc, rcLabel, IDS_NO_NEWS);
}
}
}
ValidateRect(rcClient);
dc.RestoreDC(saveDC);
// Do not call CWnd::OnPaint() for painting messages
}
void CTodayPluginWnd::OnAction() {
LOG0(1, "CTodayPluginWnd::OnAction()");
switch (Config.Mode) {
case MODE_BRIEF:
OpenReader();
break;
case MODE_CYCLING:
if (SiteIdx != -1) {
if (Config.CyclingSubMode == CYCLING_SUBMODE_FEED_ITEMS)
OpenReaderFeedItem(SiteIdx, ItemIdx);
else
OpenReaderSite(SiteIdx);
}
else
OpenReader();
break;
}
}
BOOL CTodayPluginWnd::OnQueryRefreshCache(TODAYLISTITEM *ptli) {
LOG1(5, "CTodayPluginWnd::OnQueryRefreshCache(%p)", ptli);
if (ptli == NULL)
return FALSE;
Config.LoadSharedConfig();
if (HThread == NULL) {
HThread = CreateThread(NULL, 0, ThreadStubProc, this, 0, NULL);
}
BOOL fRet = FALSE;
// calc height
RECT rcClient;
GetClientRect(&rcClient);
UINT nMinHeight = (UINT) SCALEY(20);
UINT nHeight = nMinHeight;
BOOL bNewsAvailable = NewsAvailable();
switch (Config.Mode) {
case MODE_BRIEF:
nHeight = (UINT) SCALEY(2 * Config.VOffset) + RowHeight;
break;
case MODE_CYCLING:
nHeight = (UINT) SCALEY(2 * Config.VOffset) + RowHeight;
if (bNewsAvailable) {
if (Config.CyclingSubMode == CYCLING_SUBMODE_FEED_ITEMS) {
if (Config.ShowSiteName)
nHeight += RowHeight + SCALEY(2) + SCALEY(2) + SCALEY(1);
if (Config.ShowDateTime)
nHeight += RowHeight;
}
}
break;
}
// ensure minimal height
if (nHeight < nMinHeight)
nHeight = nMinHeight;
// don't show the plugin if there are no news items
if (!bNewsAvailable && Config.HidePlugin)
nHeight = -1;
if (nHeight != ptli->cyp) {
ptli->cyp = nHeight;
fRet = TRUE;
}
return fRet;
}
void CTodayPluginWnd::OnClearCache(TODAYLISTITEM *ptli) {
LOG1(5, "CTodayPluginWnd::OnClearCache(%p)", ptli);
InvalidateRect(NULL, TRUE);
}
LRESULT CTodayPluginWnd::OnUIMetricChange(WPARAM wParam, LPARAM lParam) {
LOG0(5, "CTodayPluginWnd::OnUIMetricChange()");
LoadFonts();
InvalidateRect(NULL, TRUE);
return 0;
}
void CTodayPluginWnd::OnReadConfig() {
LOG0(5, "CTodayPluginWnd::OnReadConfig()");
Config.LoadSharedConfig();
}
void CTodayPluginWnd::OnLButtonDown(UINT nFlags, CPoint point) {
LOG0(5, "CTodayPluginWnd::OnLButtonDown()");
SetCapture();
// only on VGA displays
CDC *pDC = GetDC();
if (pDC->GetDeviceCaps(LOGPIXELSX) > 130)
Selected = TRUE;
ReleaseDC(pDC);
GetParent()->PostMessage(TODAYM_TOOKSELECTION, (WPARAM) GetSafeHwnd(), 0);
LastCursorPos = point;
KillTimer(TapAndHoldTimer); // for sure
SetTimer(TapAndHoldTimer, 100, NULL);
// CWnd::OnLButtonDown(nFlags, point);
}
void CTodayPluginWnd::OnLButtonUp(UINT nFlags, CPoint point) {
LOG0(5, "CTodayPluginWnd::OnLButtonUp()");
Selected = FALSE;
ReleaseCapture();
KillTimer(TapAndHoldTimer);
CRect rc;
GetClientRect(&rc);
if (rc.PtInRect(point)) {
// if user taps icon -> open config
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -