📄 feedview.cpp.svn-base
字号:
/**
* FeedView.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 "prssr.h"
#include "FeedView.h"
#include "Appearance.h"
#include "Feed.h"
#include "Site.h"
#include "Config.h"
#include "misc.h"
#include "MainFrm.h"
#include "../share/date.h"
#ifdef MYDEBUG
#undef THIS_FILE
static TCHAR THIS_FILE[] = _T(__FILE__);
#include "debug\crtdbg.h"
#define new MYDEBUG_NEW
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define CLASS_NAME _T("FeedViewClass")
#define SCROLL_SLEEP 10
#define SCROLL_SPEED 1
#define SCROLL_MAXSPEED 60
const int CFeedView::LEFT_SKIP = 20;
const int CFeedView::PADDING_TOP = 2;
const int CFeedView::PADDING_RIGHT = 4;
const int CFeedView::PADDING_BOTTOM = 1;
const int CFeedView::ITEM_MARGIN = 22;
const int CFeedView::DATE_HEIGHT = 18;
BOOL CFeedView::Register() {
// Register your unique class name that you wish to use
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL
// defaults
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
//you can specify your own window procedure
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wndcls.lpszMenuName = NULL;
// Specify your own class name for using FindWindow later
wndcls.lpszClassName = CLASS_NAME;
// Register the new class and exit if it fails
if (!AfxRegisterClass(&wndcls)) {
TRACE(_T("Class Registration Failed\n"));
return FALSE;
}
return TRUE;
}
void CFeedView::Unregister() {
}
/////////////////////////////////////////////////////////////////////////////
// CFeedView
CFeedView::CFeedView() {
m_nTotalHeight = 0;
m_nViewTop = 0;
m_nClientHeight = 0;
m_nClientWidth = 0;
m_nSelectStart = m_nSelectEnd = -1; // none
m_nSelectFirst = -1;
m_bSelecting = FALSE;
m_bScrolling = FALSE;
CtxMenuTimer = 1;
m_bOpenCtxMenu = FALSE;
SiteItem = NULL;
}
CFeedView::~CFeedView() {
}
BEGIN_MESSAGE_MAP(CFeedView, CWnd)
//{{AFX_MSG_MAP(CFeedView)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_VSCROLL()
ON_WM_SIZE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_WM_KEYDOWN()
ON_WM_KEYUP()
//}}AFX_MSG_MAP
ON_COMMAND(ID_ITEM_OPEN, OnItemOpen)
ON_COMMAND(ID_ITEM_MARKASREAD, OnItemMarkRead)
ON_COMMAND(ID_ITEM_MARKASUNREAD, OnItemMarkUnread)
ON_COMMAND(ID_ITEM_MARKASNEW, OnItemMarkNew)
ON_COMMAND(ID_ITEM_DELETE, OnItemDelete)
ON_UPDATE_COMMAND_UI(ID_ITEM_DELETE, OnUpdateItemSelected)
ON_UPDATE_COMMAND_UI(ID_ITEM_MARKASREAD, OnUpdateItemSelected)
ON_UPDATE_COMMAND_UI(ID_ITEM_MARKASUNREAD, OnUpdateItemSelected)
ON_UPDATE_COMMAND_UI(ID_ITEM_MARKASNEW, OnUpdateItemSelected)
ON_COMMAND(ID_SELECT_ALL, OnSelectAll)
ON_COMMAND(ID_ITEM_FLAG, OnItemFlag)
ON_UPDATE_COMMAND_UI(ID_ITEM_FLAG, OnUpdateItemFlag)
ON_COMMAND(ID_COPY_URL, OnCopyUrl)
ON_COMMAND(ID_SEND_BY_EMAIL, OnSendByEmail)
ON_COMMAND(ID_VIEW_HIDEREADITEMS, OnViewHideReadItems)
ON_UPDATE_COMMAND_UI(ID_VIEW_HIDEREADITEMS, OnUpdateViewHideReadItems)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CFeedView message handlers
void CFeedView::CreateFonts() {
m_fntSmall.DeleteObject();
m_fntBase.DeleteObject();
m_fntBold.DeleteObject();
HGDIOBJ hSysFont = ::GetStockObject(SYSTEM_FONT);
LOGFONT lf;
::GetObject(hSysFont, sizeof(LOGFONT), (LPVOID) &lf);
lf.lfHeight = SCALEY(11) + 1;
m_fntSmall.CreateFontIndirect(&lf);
wcscpy(lf.lfFaceName, Appearance.FeedViewFontCfg.FontFace);
CDC *pDC = GetDC();
lf.lfHeight = ((Appearance.FeedViewFontCfg.Size * pDC->GetDeviceCaps(LOGPIXELSY)) / 72) + 1;
m_fntBase.CreateFontIndirect(&lf);
lf.lfWeight = FW_BOLD;
m_fntBold.CreateFontIndirect(&lf);
ReleaseDC(pDC);
}
int CFeedView::OnCreate(LPCREATESTRUCT lpCreateStruct) {
LOG1(3, "CFeedView::OnCreate(%p)", lpCreateStruct);
BOOL ret = CWnd::OnCreate(lpCreateStruct);
if (ret == -1)
return -1;
CreateFonts();
// icons
AfxSetResourceHandle(theApp.GetDPISpecificInstanceHandle());
m_oIcons.Create(IDB_NEWS_ITEMS, SCALEX(16), 0, RGB(255, 0, 255));
AfxSetResourceHandle(AfxGetInstanceHandle());
// scroll bar
CRect rcVert;
m_oVScrollBar.Create(SBS_VERT | WS_CHILD, rcVert, this, 1);
UpdateScrollBars();
return ret;
}
void CFeedView::SelectAll() {
LOG0(3, "CFeedView::SelectAll()");
m_nSelectFirst = m_nSelectStart = 0;
m_nSelectEnd = m_oItems.GetSize();
Invalidate(FALSE);
}
void CFeedView::SelectItem(int item) {
if (item >= 0 && item < m_oItems.GetSize()) {
m_nSelectFirst = m_nSelectStart = m_nSelectEnd = item;
Invalidate(FALSE);
}
}
void CFeedView::DeleteItem(int idx) {
LOG1(1, "CFeedView::DeleteItem(%d)", idx);
if (idx >= 0 && idx < m_oItems.GetSize()) {
m_oItems.RemoveAt(idx);
if (idx <= m_nSelectStart) m_nSelectStart--;
if (idx <= m_nSelectEnd) m_nSelectEnd--;
if (idx <= m_nSelectFirst) m_nSelectFirst--;
AdjustViewTop();
UpdateItemHeights();
UpdateScrollBars();
Invalidate();
}
}
void CFeedView::DeleteAllItems() {
LOG0(1, "CFeedView::DeleteAllItems()");
m_oItems.RemoveAll();
m_oItemHeight.RemoveAll();
Invalidate();
m_nTotalHeight = 0;
m_nViewTop = 0;
m_nSelectStart = m_nSelectEnd = m_nSelectFirst = -1;
}
BOOL CFeedView::InsertItem(int i, CFeedItem *item) {
LOG2(1, "CFeedView::InsertItem(%d, %p)", i, item);
if (item->IsDeleted())
return FALSE;
else {
m_oItems.SetAtGrow(i, item);
int ht = CalcItemHeight(i);
m_nTotalHeight += ht;
m_oItemHeight.SetAtGrow(i, m_nTotalHeight);
return TRUE;
}
}
void CFeedView::EnsureVisible(int item) {
LOG1(1, "CFeedView::EnsureVisible(%d)", item);
int y;
if (item > 0) y = m_oItemHeight[item - 1];
else y = 0;
if (m_nViewTop <= y && m_oItemHeight[item] <= m_nViewTop + m_nClientHeight) {
}
else {
m_nViewTop = y;
AdjustViewTop();
UpdateScrollBars();
}
}
void CFeedView::DeselectAllItems() {
LOG0(1, "CFeedView::DeselectAllItems()");
m_nSelectStart = m_nSelectEnd = m_nSelectFirst = -1;
Invalidate();
}
void CFeedView::MarkItem(int item, DWORD mask) {
CFeedItem *fi = m_oItems.GetAt(item);
fi->SetFlags(mask, MESSAGE_READ_STATE);
CMainFrame *frame = (CMainFrame *) AfxGetMainWnd();
frame->SyncItem(fi);
}
void CFeedView::MarkAllRead() {
LOG0(1, "CFeedView::MarkAllRead()");
for (int i = 0; i < m_oItems.GetSize(); i++)
MarkItem(i, MESSAGE_READ);
if (SiteItem != NULL) {
if (SiteItem->Type == CSiteItem::VFolder && SiteItem->FlagMask == MESSAGE_READ_STATE)
DeleteAllItems();
else if (SiteItem->Type != CSiteItem::VFolder && Config.HideReadItems)
DeleteAllItems();
}
AdjustViewTop();
DeselectAllItems();
SortItems();
UpdateScrollBars();
Invalidate(TRUE);
}
void CFeedView::MarkAllUnread() {
LOG0(1, "CFeedView::MarkAllUnread()");
for (int i = 0; i < m_oItems.GetSize(); i++)
MarkItem(i, MESSAGE_UNREAD);
AdjustViewTop();
DeselectAllItems();
SortItems();
UpdateScrollBars();
Invalidate(TRUE);
}
void CFeedView::MarkAllUnflagged() {
LOG0(1, "CFeedView::MarkAllUnflagged()");
for (int i = 0; i < m_oItems.GetSize(); i++)
UnflagItem(i);
AdjustViewTop();
DeselectAllItems();
SortItems();
UpdateScrollBars();
Invalidate(TRUE);
}
//
//
//
void CFeedView::AdjustViewTop() {
if (m_nViewTop < 0 || m_nTotalHeight < m_nClientHeight)
m_nViewTop = 0;
else if (m_nTotalHeight > m_nClientHeight) {
int max = m_nTotalHeight - m_nClientHeight;
if (m_nViewTop > max)
m_nViewTop = max;
}
}
void CFeedView::DrawIcon(CDC &dc, int icon, BOOL selected, int xofs/* = 0*/, int yofs/* = 0*/) {
IMAGELISTDRAWPARAMS drawing;
drawing.cbSize = sizeof(IMAGELISTDRAWPARAMS);
drawing.himl = m_oIcons.GetSafeHandle();
drawing.i = icon;
drawing.hdcDst = dc.GetSafeHdc();
drawing.x = SCALEX(1) + SCALEX(xofs);
drawing.y = SCALEY(2) + SCALEX(yofs);
drawing.cx = SCALEX(16);
drawing.cy = SCALEY(16);
drawing.xBitmap = 0;
drawing.yBitmap = 0;
drawing.rgbBk = CLR_NONE;
drawing.rgbFg = CLR_DEFAULT;
if (selected)
drawing.fStyle = ILD_BLEND50;
else
drawing.fStyle = ILD_NORMAL;
drawing.dwRop = SRCCOPY;
ImageList_DrawIndirect(&drawing);
}
void CFeedView::DrawItem(CDC &dc, CRect &rc, int idx) {
CFeedItem *item = m_oItems[idx];
BOOL selected = (m_nSelectStart <= idx) && (idx <= m_nSelectEnd);
COLORREF clrBg, clrFg, clrOld;
if (selected) {
clrBg = ::GetSysColor(COLOR_HIGHLIGHT);
clrFg = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
}
else {
clrBg = Appearance.ClrFeedViewBg;
if (item->IsNew() || item->IsUnread())
clrFg = Appearance.ClrFeedViewFg;
else
clrFg = ::GetSysColor(COLOR_3DSHADOW);
}
// background
dc.FillSolidRect(rc, clrBg);
int oldBkMode = dc.SetBkMode(TRANSPARENT);
// icon
if (item->IsNew())
DrawIcon(dc, NEW_FEED_ICON, selected);
else if (item->IsUnread())
DrawIcon(dc, UNREAD_FEED_ICON, selected);
else
DrawIcon(dc, READ_FEED_ICON, selected);
// has html cached
if (IsHTMLCached(item->Link, TRUE)) {
if (item->IsNew() || item->IsUnread())
DrawIcon(dc, CACHED_ITEM_ICON, selected);
else
DrawIcon(dc, NOT_CACHED_ITEM_ICON, selected);
}
if (item->HasEnclosure()) {
CEnclosureItem *ei = item->Enclosures.GetHead();
if (IsEnclosureCached(ei->URL))
DrawIcon(dc, CACHED_ITEM_ICON, selected);
else
DrawIcon(dc, NOT_CACHED_ITEM_ICON, selected);
}
// keyword
if (item->HasKeywordMatch())
DrawIcon(dc, KEYWORD_ICON, selected);
// flagged?
if (item->IsFlagged())
DrawIcon(dc, FLAG_ICON, selected);
// title
CFont *pOldFont;
pOldFont = dc.SelectObject(&m_fntBold);
clrOld = dc.SetTextColor(clrFg);
CRect rcTitle = rc;
rcTitle.DeflateRect(SCALEX(LEFT_SKIP), SCALEY(PADDING_TOP), SCALEX(PADDING_RIGHT), SCALEY(PADDING_BOTTOM));
CString strTitle = item->Title;
ReplaceHTMLEntities(strTitle);
if (m_bWrapTitles) {
dc.DrawText(strTitle, &rcTitle, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK);
}
else
DrawTextEndEllipsis(dc, strTitle, rcTitle, DT_LEFT | DT_TOP | DT_NOPREFIX);
dc.SetTextColor(clrOld);
dc.SelectObject(pOldFont);
// date
if (item->PubDate.wYear != 0) {
pOldFont = dc.SelectObject(&m_fntSmall);
CString sTime;
SYSTEMTIME st = TimeToTimeZone(&item->PubDate); // convert to local time zone
FormatDateTime(sTime, st, Config.ShowRelativeDates);
CRect rcTime = rc;
rcTime.left = rcTitle.left;
rcTime.bottom -= SCALEY(3);
rcTime.right -= SCALEX(3);
if (selected)
clrOld = dc.SetTextColor(clrFg);
else
clrOld = dc.SetTextColor(Appearance.ClrDate);
dc.DrawText(sTime, &rcTime, DT_LEFT | DT_BOTTOM | DT_NOPREFIX);
dc.SetTextColor(clrOld);
dc.SelectObject(pOldFont);
}
// draw separator
CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW));
CPen *oldPen = dc.SelectObject(&pen);
dc.MoveTo(rc.left, rc.bottom - 1);
dc.LineTo(rc.right, rc.bottom - 1);
dc.SelectObject(oldPen);
dc.SetBkMode(oldBkMode);
}
void CFeedView::InvalidateItem(int idx, BOOL erase/* = TRUE*/) {
if (idx >= 0 && idx < m_oItems.GetSize()) {
CRect rcClient;
GetClientRect(rcClient);
int top;
if (idx > 0) top = m_oItemHeight[idx - 1];
else top = 0;
CRect rc(rcClient.left, top - m_nViewTop, rcClient.right, m_oItemHeight[idx] - m_nViewTop);
InvalidateRect(rc, erase);
}
}
void CFeedView::OnDraw(CDC *pDC) {
}
void CFeedView::OnPaint() {
LOG0(5, "CFeedView::OnPaint()");
CPaintDC dc(this); // device context for painting
int nSavedDC = dc.SaveDC();
CRect rc;
GetClientRect(rc);
if (m_oItems.GetSize() > 0) {
CRect rcClip;
dc.GetClipBox(rcClip);
if (m_oVScrollBar.IsWindowVisible()) {
CRect rcVScroll;
m_oVScrollBar.GetWindowRect(rcVScroll);
rc.right -= rcVScroll.Width() - SCALEX(1);
}
int y = rc.top;
int first = 0;
for (; first < m_oItems.GetSize(); first++)
if (m_oItemHeight[first] > m_nViewTop)
break;
int ofsY;
if (first > 0) ofsY = m_nViewTop - m_oItemHeight[first - 1];
else ofsY = m_nViewTop;
for (int idx = first; idx < m_oItems.GetSize() && y < rc.bottom; idx++) {
int wd = rc.Width();
int ht;
if (idx > 0) ht = m_oItemHeight[idx] - m_oItemHeight[idx - 1];
else ht = m_oItemHeight[idx];
CBitmap bmp;
bmp.CreateCompatibleBitmap(&dc, wd, ht);
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap *saveBmp = memDC.SelectObject(&bmp);
CRect rcPhys(rc.left, y, rc.right, y + ht);
CRect rcDraw;
if (rcDraw.IntersectRect(rcClip, rcPhys)) {
CRect rcMsg(rc.left, 0, rc.right, ht);
DrawItem(memDC, rcMsg, idx);
// copy to screen
dc.BitBlt(0, y, wd, ht - ofsY, &memDC, 0, ofsY, SRCCOPY);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -