ToolTips in a View and TTN_NEEDTEXT
From: Rick Lee (Rick_at_Custom-Made-SoftwareNOSPAM.com)
Date: 11/19/04
- Next message: txemuki: "Positioning a window"
- Previous message: Gary Chang[MSFT]: "RE: VC 7.1: Preview DC"
- Next in thread: Bill Thompson: "Re: ToolTips in a View and TTN_NEEDTEXT"
- Reply: Bill Thompson: "Re: ToolTips in a View and TTN_NEEDTEXT"
- Messages sorted by: [ date ] [ thread ]
Date: Thu, 18 Nov 2004 23:42:25 -0800
I have been trying to reuse code for ToolTips in a View ("Data Tips")
that I found in the
April 97 issue of MSJ:
http://www.microsoft.com/msj/0497/tooltip/tooltip.aspx
Most of the required functions and portions of functions --
OnMouseMove(), OnInitialUpdate(),
PreTranslateMessage() HitTest() -- are called correctly. But for some
reason the TTN_NEEDTEXT
message appears never to be sent because OnToolTipNeedText() is never
called.
Note that the only value of pMsg->message that is relayed to the ToolTip
is 512, and I have
no idea if that's right. But it does seem to be the case in the Sample
Project I downloaded.
(Click tooltips.exe at the top of the article for the self-extracting
archive. The Data Tips
is in the DTTest directory.)
Thanks,
Rick Lee
I know it's a mess, but here is my code if you want to look:
////////////////////////////////////////////
//
// DayView.cpp
//
////////////////////////////////////////////
// DayView.cpp : implementation file
//
#include "stdafx.h"
#include "clockwatcher.h"
#include "ClockWatcherDoc.h"
#include "QueryWorkDaysSet.h"
#include "PayPeriodView.h"
#include "DayView.h"
#include "WorkDayParams.h"
#include "SortableArray.h"
#include "ProjectColor.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
extern CWorkDayParams g_workDayParams;
extern CArrayEx <CSingleProject, CSingleProject> g_projectList;
extern CProjectColorList g_projectColorList;
extern CArray<CQWorkPeriodRecord, CQWorkPeriodRecord> g_workPeriodSet;
extern int g_minWorkPeriod;
CArray<CQWorkPeriodRecord, CQWorkPeriodRecord> g_workPeriodSet;
static CString dayNames[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat"
};
static int numberOfDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31,
30, 31};
/////////////////////////////////////////////////////////////////////////////
// CDayView
IMPLEMENT_DYNCREATE(CDayView, CScrollView)
CDayView::CDayView()
{
xOrigin = 50;
rightMargin = xOrigin;
yOrigin = 2;
bottomMargin = yOrigin;
dayHeight = 68;
hourWidth = 80;
hourHeight = 60;
taskWidth = 400;
totalDayWidth = hourWidth + taskWidth;
legendLeft = xOrigin + totalDayWidth + rightMargin;
legendWidth = 250;
legendBoxWidth = 40;
legendBoxHeight = 20;
// Set button parameters
centerBtnHeight = 23;
arrowBtnHeight = 41;
yButton = yOrigin + (dayHeight-arrowBtnHeight)/2;
arrowWidth = 66;
centerWidth = 140;
groupWidth = 2*arrowWidth + centerWidth + 6;
xBorder = (totalDayWidth-groupWidth) / 2;
m_pRectHit = NULL;
}
CDayView::~CDayView()
{
delete m_dayArrowLeft;
delete m_daySelect;
delete m_dayArrowRight;
m_brushBlackHatched.DeleteObject();
m_brushYellowSolid.DeleteObject();
for (int i=0; i<CMS_BRIGHT_COLORS; i++) {
m_fillBrush[i].DeleteObject();
}
m_penBlackSolid.DeleteObject();
m_penBlackThin.DeleteObject();
m_penWhiteDotted.DeleteObject();
m_penWhiteSolid.DeleteObject();
m_fontArialTitle.DeleteObject();
m_fontArialBody.DeleteObject();
}
BEGIN_MESSAGE_MAP(CDayView, CScrollView)
ON_BN_CLICKED(IDC_CALENDAR_MONTH, OnClickDaySelect)
ON_BN_CLICKED(IDC_CALENDAR_LEFT, OnClickLeftArrow)
ON_BN_DOUBLECLICKED(IDC_CALENDAR_LEFT, OnDblClickLeftArrow)
ON_BN_CLICKED(IDC_CALENDAR_RIGHT, OnClickRightArrow)
ON_BN_DOUBLECLICKED(IDC_CALENDAR_RIGHT, OnDblClickRightArrow)
ON_COMMAND(ID_DAYLOG_SUN, OnDayLog_Sun)
ON_COMMAND(ID_DAYLOG_MON, OnDayLog_Mon)
ON_COMMAND(ID_DAYLOG_TUE, OnDayLog_Tue)
ON_COMMAND(ID_DAYLOG_WED, OnDayLog_Wed)
ON_COMMAND(ID_DAYLOG_THU, OnDayLog_Thu)
ON_COMMAND(ID_DAYLOG_FRI, OnDayLog_Fri)
ON_COMMAND(ID_DAYLOG_SAT, OnDayLog_Sat)
ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipNeedText)
//{{AFX_MSG_MAP(CDayView)
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CDayView::CreateAllFonts()
{
LOGFONT fontSpec;
CString message = "Resources are critically low.\n";
message += "Close other programs in order to continue.";
// Create Title font.
memset(&fontSpec, 0, sizeof(LOGFONT)); // clear out structure.
strcpy(fontSpec.lfFaceName, "Arial");
fontSpec.lfHeight = 120;
fontSpec.lfWeight = FW_BOLD;
if (!m_fontArialTitle.CreatePointFontIndirect(&fontSpec, NULL)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
// Create Body font.
memset(&fontSpec, 0, sizeof(LOGFONT)); // clear out structure.
strcpy(fontSpec.lfFaceName, "Arial");
fontSpec.lfHeight = 90;
fontSpec.lfWeight = FW_BOLD;
if (!m_fontArialBody.CreatePointFontIndirect(&fontSpec, NULL)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
}
void CDayView::CreateAllPens()
{
CString message = "Resources are critically low.\n";
message += "Close other programs in order to continue.";
if (!m_penWhiteSolid.CreatePen(PS_SOLID, 3, CMS_WHITE)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
if (!m_penWhiteDotted.CreatePen(PS_DOT, 0, CMS_WHITE)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
if (!m_penBlackSolid.CreatePen(PS_SOLID, 3, CMS_BLACK)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
if (!m_penBlackThin.CreatePen(PS_SOLID, 1, CMS_BLACK)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
}
void CDayView::CreateAllBrushes()
{
CString message = "Resources are critically low.\n";
message += "Close other programs in order to continue.";
for (int i=0; i<CMS_BRIGHT_COLORS; i++) {
COLORREF color = g_projectColorList.GetColor(i);
if (!m_fillBrush[i].CreateSolidBrush(color)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
}
if (!m_brushYellowSolid.CreateSolidBrush(CMS_YELLOW)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
if (!m_brushBlackHatched.CreateHatchBrush(HS_FDIAGONAL, CMS_BLACK)) {
// Alert the user that resources are low
AfxMessageBox(message);
return;
}
}
/////////////////////////////////////////////////////////////////////////////
// CDayView drawing
void CDayView::OnDraw(CDC* pDC)
{
CDocument* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code here
int oldMapMode = pDC->GetMapMode();
pDC->SetMapMode(MM_TEXT);
// Draw the day's worklog
int numHours = this->CalcNumHours();
totalHeight = dayHeight + 2 +
CMS_NINT(numHours*hourHeight);
this->DrawDayLog(pDC);
pDC->SetMapMode(oldMapMode);
}
void CDayView::DrawDayLog(CDC* pDC)
{
// Top-level drawing routine.
// Add major steps here.
this->DrawDayLogBackground(pDC);
this->DrawDayLogBorders(pDC);
this->WriteDateHeader(pDC);
COLORREF oldBkColor = pDC->SetBkColor(CMS_BKGND_GRAY);
this->DrawDayLogHours(pDC);
this->PlaceHourArrows(pDC);
this->DrawWorkPeriods(pDC);
pDC->SetBkColor(oldBkColor);
}
void CDayView::DrawDayLogBackground(CDC* pDC)
{
int x = xOrigin; int y = yOrigin;
int xExtent = totalDayWidth + 2;
int yExtent = totalHeight;
pDC->FillSolidRect(x, y, xExtent, yExtent, CMS_BKGND_GRAY);
pDC->SetBkColor(CMS_WHITE);
}
void CDayView::DrawDayLogBorders(CDC* pDC)
{
// Select the solid black pen into the device context
// Save the old pen at the same time
CPen* pOldPen = pDC->SelectObject(&m_penBlackSolid);
// Draw Day header box of Log.
pDC->MoveTo(xOrigin, yOrigin);
pDC->LineTo(xOrigin+totalDayWidth, yOrigin);
pDC->LineTo(xOrigin+totalDayWidth, yOrigin+dayHeight);
pDC->LineTo(xOrigin, yOrigin+dayHeight);
pDC->LineTo(xOrigin, yOrigin);
// Draw border around rest of display.
pDC->MoveTo(xOrigin+totalDayWidth, dayHeight);
pDC->LineTo(xOrigin+totalDayWidth, totalHeight);
pDC->LineTo(xOrigin+hourWidth, totalHeight);
// Draw one column of black hour boxes.
int x = xOrigin; int y = yOrigin+dayHeight;
pDC->FillSolidRect(x-1, y, hourWidth+3, totalHeight-dayHeight,
CMS_BLACK);
pDC->SetBkColor(CMS_WHITE);
// Draw solid white lines to divide whole hours.
pDC->SelectObject(&m_penWhiteSolid);
int numHours = this->CalcNumHours();
for (int i=1; i<numHours; i++) {
pDC->MoveTo(xOrigin, yOrigin+dayHeight+i*hourHeight);
pDC->LineTo(xOrigin+hourWidth, yOrigin+dayHeight+i*hourHeight);
}
// Draw dotted white lines to divide half hours.
int oldBkMode = pDC->SetBkColor(TRANSPARENT);
pDC->SelectObject(&m_penWhiteDotted);
for (int j=1; j<=numHours; j++) {
pDC->MoveTo(xOrigin,
yOrigin+2+CMS_NINT(0.5*dayHeight)+j*hourHeight);
pDC->LineTo(xOrigin+hourWidth+1,
yOrigin+2+CMS_NINT(0.5*dayHeight)+j*hourHeight);
}
pDC->SetBkMode(oldBkMode);
// Restore the old pen to the device context
pDC->SelectObject(pOldPen);
}
void CDayView::WriteDateHeader(CDC* pDC)
{
char term = '\0';
char ss[32];
COleDateTime today(currentYear, currentMonth, currentDay, 0, 0, 0);
CString out = dayNames[today.GetDayOfWeek()-1];
out += ", ";
sprintf(ss, "%d/%d/%d%c", currentMonth, currentDay, currentYear, term);
out += ss;
CPoint pt(xOrigin+xBorder+arrowWidth+1, yButton+centerBtnHeight+3);
CSize sz(centerWidth, arrowBtnHeight-centerBtnHeight-3);
CRect rc(pt,sz);
pDC->DrawText(out, rc, DT_CENTER|DT_VCENTER);
}
void CDayView::DrawDayLogHours(CDC* pDC)
{
COLORREF oldBkColor = pDC->SetBkColor(CMS_BLACK);
COLORREF oldTextColor = pDC->SetTextColor(CMS_WHITE);
// We may need to display a start time
// earlier than the actual time.
int iStartTime = this->AdjustedStartTime();
// For text drawing, the size of the rectangle is constant.
int halfHour = CMS_NINT(0.5*(float)hourHeight) - 3;
CSize sz(hourWidth, halfHour);
int numHours = this->CalcNumHours();
char hrStr[6];
char term = '\0';
// Draw full hours
for (int i=0; i<numHours; i++) {
sprintf(hrStr, "%d:00%c", iStartTime+i, term);
CPoint pt(xOrigin, yOrigin+dayHeight+i*hourHeight+8);
CRect rc(pt,sz);
pDC->DrawText(hrStr, rc, DT_CENTER|DT_VCENTER);
}
// Draw half hours.
for (int j=0; j<numHours; j++) {
sprintf(hrStr, "%d:30%c", iStartTime+j, term);
CPoint pt(xOrigin, yOrigin+dayHeight+j*hourHeight+halfHour+8);
CRect rc(pt,sz);
pDC->DrawText(hrStr, rc, DT_CENTER|DT_VCENTER);
}
pDC->SetBkColor(oldBkColor);
pDC->SetTextColor(oldTextColor);
}
int CDayView::CalcNumHours()
{
// We may need to display a start time
// earlier than the actual time.
int iStartTime = this->AdjustedStartTime();
// We may need to display a stop time
// later than the actual time.
int iStopTime = this->AdjustedStopTime();
return(iStopTime - iStartTime);
}
int CDayView::AdjustedStartTime()
{
// We may need to display a start time
// earlier than the actual time.
double dStartTime = g_workDayParams.GetStartTimeDouble();
int iStartTime = CMS_NINT(dStartTime);
if (dStartTime < iStartTime) {
iStartTime--;
}
return iStartTime;
}
int CDayView::AdjustedStopTime()
{
// We may need to display a stop time
// later than the actual time.
double dStopTime = g_workDayParams.GetStopTimeDouble();
int iStopTime = CMS_NINT(dStopTime);
if (dStopTime > iStopTime) {
iStopTime++;
}
return iStopTime;
}
void CDayView::PlaceHourArrows(CDC* pDC)
{
// The x-coord of the right vertex is the same for both triangles.
int x = xOrigin;
int y;
// Calculate the position of the right-hand vertex
// of the START Time.
double dStartTime = g_workDayParams.GetStartTimeDouble();
this->TimeToY(dStartTime, &y);
CPoint pt(x,y);
this->DrawHourTriangle(pDC, pt);
// Calculate the position of the right-hand vertex
// of the STOP Time.
double dStopTime = g_workDayParams.GetStopTimeDouble();
this->TimeToY(dStopTime, &y);
pt.y = y;
this->DrawHourTriangle(pDC, pt);
}
BOOL CDayView::TimeToY(double time, int* pY)
{
int yBase = yOrigin + dayHeight;
// Subtract 2 times
int timeHr = (int)time;
int timeMin = (int)((time - (double)timeHr) * 100.0);
double fractionMin = (double)timeMin / 60.0;
double fractionTime = timeHr + fractionMin;
double startDiff = fractionTime - (double)adjStartTime;
double stopDiff = (double)adjStopTime - fractionTime;
double yOffset;
if (startDiff < 0.0) {
*pY = yBase;
return FALSE;
} else if (stopDiff < 0.0) {
startDiff = adjStopTime - adjStartTime;
yOffset = startDiff * (double)hourHeight;
*pY = yBase + yOffset;
return FALSE;
} else {
yOffset = startDiff * (double)hourHeight;
*pY = yBase + CMS_NINT(yOffset);
return TRUE;
}
}
void CDayView::DrawHourTriangle(CDC* pDC, CPoint vertex)
{
CPoint vertices[3];
vertices[0] = vertex;
vertices[1].x = vertices[2].x = vertex.x - 25;
vertices[1].y = vertex.y + 7;
vertices[2].y = vertex.y - 7;
CPen* pOldPen = pDC->SelectObject(&m_penBlackThin);
CBrush* pOldBrush = pDC->SelectObject(&m_brushYellowSolid);
pDC->Polygon(vertices, 3);
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
}
void CDayView::DrawWorkPeriods(CDC* pDC)
{
int brush; // Choice of brush
int i, j;
// char message[100];
// char m2[100];
m_allProjects.RemoveAll();
int numProjects = 0;
m_allWorkPeriods.RemoveAll();
// Loop browses through the records of the array.
// Add up the times for each unique project.
int n = g_workPeriodSet.GetSize();
if (n>0) {
for (i=0; i<n; i++) {
m_startTime = g_workPeriodSet[i].startTime;
m_endTime = g_workPeriodSet[i].endTime;
m_workDayNumber = g_workPeriodSet[i].workDayNumber;
m_workPeriodNumber = g_workPeriodSet[i].workPeriodNumber;
m_projectNumber = g_workPeriodSet[i].projectNumber;
numProjects = m_allProjects.GetSize();
if (numProjects > 0) {
BOOL foundProject = FALSE;
for (j=0; j<numProjects; j++) {
if (m_projectNumber ==
m_allProjects[j].m_projectNumber) {
foundProject = TRUE;
m_allProjects[j].m_totalTime
+=
(m_endTime - m_startTime);
break;
}
}
if (!foundProject) {
m_allProjects.Add(COneProjectTotal(m_projectNumber));
m_allProjects[numProjects].m_totalTime = m_endTime -
m_startTime;
}
} else {
m_allProjects.Add(COneProjectTotal(m_projectNumber));
m_allProjects[0].m_totalTime = m_endTime - m_startTime;
}
brush = this->ChooseProjectBrush(m_projectNumber);
m_allWorkPeriods.Add(CWorkPeriodRect());
m_allWorkPeriods[i].Initialize(this,
&(m_fillBrush[brush]), &m_brushBlackHatched, &m_penBlackThin,
m_startTime, m_endTime);
m_allWorkPeriods[i].Draw(pDC);
// sprintf(message, "Work Day: %d; Period: %d; Project: %d.",
// m_workDayNumber, m_workPeriodNumber, m_projectNumber);
}
// sprintf(m2, "There are %d work periods(s) today:\n", n);
// strcat(m2, message);
// AfxMessageBox(m2);
} else {
// AfxMessageBox("No work periods today.");
}
this->DrawWorkPeriodLegends(pDC);
}
void CDayView::DrawWorkPeriodLegends(CDC *pDC)
{
CString out;
TEXTMETRIC textMetrics;
RECT textRect;
int textHeight;
int x = legendLeft;
int y = 10;
pDC->SelectObject(&m_fontArialTitle);
pDC->GetTextMetrics(&textMetrics);
textHeight = textMetrics.tmHeight;
textRect.left = x;
textRect.top = y;
textRect.right = x + legendWidth;
textRect.bottom = y + textHeight;
out = "Daily Totals";
pDC->DrawText(out, &textRect, DT_CENTER|DT_TOP);
y += textHeight + 10;
pDC->SelectObject(&m_fontArialBody);
pDC->GetTextMetrics(&textMetrics);
textHeight = textMetrics.tmHeight;
int n = m_allProjects.GetSize();
if (n == 0) {
textRect.top = y;
textRect.bottom = y + 2*textHeight;
out = "No work periods recorded.";
pDC->DrawText(out, &textRect, DT_CENTER|DT_TOP);
return;
}
textRect.left += legendBoxWidth + 2;
for (int i=0; i<n; i++) {
int pn = m_allProjects[i].m_projectNumber;
int brush = this->ChooseProjectBrush(pn);
this->DrawLegendBoxRect(pDC, &(m_fillBrush[brush]), y);
textRect.top = y;
textRect.bottom = y + 2*textHeight;
this->DrawLegendProjectInfo(pDC, i, textRect);
y += 40;
}
out = "All times are rounded\nto the nearest";
out += this->GetRoundingFactor();
out += "hour";
pDC->SelectObject(&m_fontArialBody);
pDC->GetTextMetrics(&textMetrics);
textHeight = textMetrics.tmHeight;
textRect.left = x;
textRect.top = y;
textRect.right = x + legendWidth;
textRect.bottom = y + 2*textHeight;
pDC->DrawText(out, &textRect, DT_CENTER|DT_TOP);
}
void CDayView::DrawLegendBoxRect(CDC* pDC, CBrush* pBrush, int top)
{
int left = legendLeft;
int right = left + legendBoxWidth;
int bottom = top + legendBoxHeight;
CPoint vertices[4];
vertices[0].x = left;
vertices[0].y = top;
vertices[1].x = right;
vertices[1].y = top;
vertices[2].x = right;
vertices[2].y = bottom;
vertices[3].x = left;
vertices[3].y = bottom;
CPen* pOldPen = pDC->SelectObject(&m_penBlackThin);
CBrush* pOldBrush = pDC->SelectObject(pBrush);
pDC->Polygon(vertices, 4);
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
}
void CDayView::DrawLegendProjectInfo(CDC* pDC, int projectIndex, RECT
textRect)
{
char buff[100];
CString out, rounded;
BOOL found = FALSE;
CString ID, name;
int projectNumber = m_allProjects[projectIndex].m_projectNumber;
int numProjects = g_projectList.GetSize();
for (int i=0; i< numProjects; i++) {
int pn = g_projectList[i].GetProjectNumber();
if (projectNumber == pn) {
found = TRUE;
ID = g_projectList[i].GetIndexedMember(1);
name = g_projectList[i].GetIndexedMember(2);
break;
}
}
ASSERT(found);
COleDateTimeSpan elapsed = m_allProjects[projectIndex].m_totalTime;
double time = elapsed.GetTotalSeconds() / 3600.0;
CPayPeriodView::RoundWorkTime(time, &time, rounded);
sprintf (buff, "%s, %s\n%s hour(s)", ID, name, rounded);
out = buff;
pDC->DrawText(out, &textRect, DT_CENTER|DT_TOP);
}
CString CDayView::GetRoundingFactor()
{
CString out;
switch(g_minWorkPeriod) {
case MIN_WORK_PERIOD_SIX: // Rounded to 1/10s of an hour
out = " 0.1 ";
break;
case MIN_WORK_PERIOD_TEN: // Rounded to 1/6s of an hour
out = " 1/6 ";
break;
case MIN_WORK_PERIOD_FIFTEEN: // Rounded to 1/4s of an hour
out = " 0.25 ";
break;
case MIN_WORK_PERIOD_THIRTY: // Rounded to 1/2s of an hour
out = " 0.5 ";
break;
default:
out = " ? ";
break;
}
return out;
}
int CDayView::ChooseProjectBrush(int projectNum)
{
int n = g_projectList.GetSize();
for (int i=0; i<n; i++) {
if (g_projectList[i].GetProjectNumber() == projectNum) {
return g_projectList[i].GetColorIndex();
}
}
return -1; // ERROR
}
int CDayView::Get_X_Origin()
{
return xOrigin;
}
int CDayView::GetHourWidth()
{
return hourWidth;
}
int CDayView::GetTotalDayWidth()
{
return totalDayWidth;
}
/////////////////////////////////////////////////////////////////////////////
// CDayView diagnostics
#ifdef _DEBUG
void CDayView::AssertValid() const
{
CScrollView::AssertValid();
}
void CDayView::Dump(CDumpContext& dc) const
{
CScrollView::Dump(dc);
}
CClockWatcherDoc* CDayView::GetDocument()
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CClockWatcherDoc)));
return (CClockWatcherDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CDayView message handlers
BOOL CDayView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Add your specialized code here and/or call the base class
cs.style |= WS_BORDER | WS_VSCROLL | WS_HSCROLL;
return CScrollView::PreCreateWindow(cs);
}
void CDayView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (::IsWindow(m_tooltip.m_hWnd))
{
const CWorkPeriodRect* pRectHit = this->HitTest(point);
if (!pRectHit || (pRectHit!=m_pRectHit))
{
// Use Activate() to hide the tooltip.
m_tooltip.Activate(FALSE);
}
if (pRectHit)
{
m_tooltip.Activate(TRUE);
m_pRectHit = pRectHit;
}
}
// Pass on to the default handler.
CScrollView::OnMouseMove(nFlags, point);
}
BOOL CDayView::OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT *
pResult)
{
BOOL bHandledNotify = FALSE;
CPoint CursorPos;
VERIFY(::GetCursorPos(&CursorPos));
ScreenToClient(&CursorPos);
CRect ClientRect;
GetClientRect(ClientRect);
// Make certain that the cursor is in the client rect, because the
// mainframe also wants these messages to provide tooltips for the
// toolbar.
if (ClientRect.PtInRect(CursorPos))
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
m_pRectHit = this->HitTest(CursorPos);
if (m_pRectHit)
{
// Adjust the text by filling in TOOLTIPTEXT
CString strTip;
strTip.Format("Hello");
// strTip.Format("Center: (%d, %d)\nRadius: %d\nColor: (%d, %d,
%d)",
// Center.x, Center.y, m_pCircleHit->GetRadius(),
// (int)GetRValue(Color), (int)GetGValue(Color),
(int)GetBValue(Color));
ASSERT(strTip.GetLength() < sizeof(pTTT->szText));
::strcpy(pTTT->szText, strTip);
}
else
{
pTTT->szText[0] = 0;
}
bHandledNotify = TRUE;
}
return bHandledNotify;
}
const CWorkPeriodRect* CDayView::HitTest(const CPoint& point)
{
// Check in reverse order to deal with clipping.
const CWorkPeriodRect* pRectHit = NULL;
int numPeriods = m_allWorkPeriods.GetSize();
if (numPeriods > 0) {
for (int n=numPeriods-1; ((n>=0) && (pRectHit==NULL)); n--)
{
if (m_allWorkPeriods[n].HitTest(point))
{
pRectHit = &m_allWorkPeriods[n];
}
}
}
return pRectHit;
}
BOOL CDayView::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if (::IsWindow(m_tooltip.m_hWnd) && pMsg->hwnd == m_hWnd)
{
switch(pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
m_tooltip.RelayEvent(pMsg);
break;
}
}
return CScrollView::PreTranslateMessage(pMsg);
}
void CDayView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
this->CreateAllFonts();
this->CreateAllPens();
this->CreateAllBrushes();
// TODO: calculate the total size of this view
GetParentFrame()->RecalcLayout();
// Initialize the calendar buttons.
CPoint ptLeft(xOrigin+xBorder, yButton);
CSize szLeft(arrowWidth, arrowBtnHeight);
CRect rcLeft(ptLeft, szLeft);
VERIFY(m_dayArrowLeft.Create(NULL,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW,
rcLeft, this, IDC_CALENDAR_LEFT));
m_dayArrowLeft.LoadBitmaps
(IDB_CALENDAR_UP_LT, IDB_CALENDAR_DN_LT, 0, 0);
CPoint ptDay(xOrigin+xBorder+arrowWidth+3, yButton);
CSize szDay(centerWidth, centerBtnHeight);
CRect rcDay(ptDay, szDay);
VERIFY(m_daySelect.Create(NULL,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW,
rcDay, this, IDC_CALENDAR_MONTH));
m_daySelect.LoadBitmaps
(IDB_CALENDAR_UP_CTR, IDB_CALENDAR_DN_CTR, 0, 0);
CPoint ptRight(xOrigin+xBorder+arrowWidth+centerWidth+6, yButton);
CSize szRight(arrowWidth, arrowBtnHeight);
CRect rcRight(ptRight, szRight);
VERIFY(m_dayArrowRight.Create(NULL,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_OWNERDRAW,
rcRight, this, IDC_CALENDAR_RIGHT));
m_dayArrowRight.LoadBitmaps
(IDB_CALENDAR_UP_RT, IDB_CALENDAR_DN_RT, 0, 0);
this->SetScrollBars();
this->RebuildWorkPeriodSet();
CRect ClientRect(0, 0, 1000, 1000);
if (m_tooltip.Create(this, TTS_ALWAYSTIP) && m_tooltip.AddTool(this))
{
m_tooltip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
m_tooltip.SendMessage(TTM_SETDELAYTIME, TTDT_AUTOPOP, SHRT_MAX);
m_tooltip.SendMessage(TTM_SETDELAYTIME, TTDT_INITIAL, 200);
m_tooltip.SendMessage(TTM_SETDELAYTIME, TTDT_RESHOW, 200);
}
else
{
TRACE("Error in creating ToolTip");
}
}
void CDayView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint)
{
currentDay = this->GetDocument()->GetCurrentDay();
currentMonth = this->GetDocument()->GetCurrentMonth();
currentYear = this->GetDocument()->GetCurrentYear();
adjStartTime = this->AdjustedStartTime();
adjStopTime = this->AdjustedStopTime();
this->SetScrollBars();
this->RebuildWorkPeriodSet();
CScrollView::OnUpdate(pSender, lHint, pHint);
}
void CDayView::SetScrollBars()
{
CSize total = this->GetDrawingExtents();
CSize page(total.cx/10, total.cy/10);
CSize line( page.cx/10, page.cy/10);
SetScrollSizes(MM_TEXT, total, page, line);
}
CSize CDayView::GetDrawingExtents()
{
CSize extents;
extents.cx = xOrigin + totalDayWidth + rightMargin + legendWidth;
int numHrs = this->CalcNumHours();
extents.cy = dayHeight + 10 +
CMS_NINT(numHrs*hourHeight) + bottomMargin;
return extents;
}
void CDayView::OnClickLeftArrow()
{
this->DecrementDay();
this->RefreshAllViews();
}
void CDayView::OnDblClickLeftArrow()
{
this->OnClickLeftArrow();
}
void CDayView::OnClickRightArrow()
{
this->IncrementDay();
this->RefreshAllViews();
}
void CDayView::OnDblClickRightArrow()
{
this->OnClickRightArrow();
}
void CDayView::OnClickDaySelect()
{
CMenu thePopup;
thePopup.LoadMenu(IDR_DAYLOG_POPUP); // returns 1 (success)
CMenu *pSubPopup = thePopup.GetSubMenu(0);
ASSERT (pSubPopup);
CPoint pt;
GetCursorPos(&pt);
pSubPopup->TrackPopupMenu(TPM_CENTERALIGN|TPM_LEFTBUTTON,
pt.x,pt.y,this,NULL); // "this" is this view
}
int CDayView::FebruaryDays(int theYear)
{
if (theYear%4 == 0) {
if (theYear%100 == 0) {
if (theYear%1000 == 0) {
return 29;
} else {
return 28;
}
} else {
return 29;
}
} else {
return 28;
}
}
void CDayView::OnDayLog_Sun()
{
this->ProcessWeekday(1);
}
void CDayView::OnDayLog_Mon()
{
this->ProcessWeekday(2);
}
void CDayView::OnDayLog_Tue()
{
this->ProcessWeekday(3);
}
void CDayView::OnDayLog_Wed()
{
this->ProcessWeekday(4);
}
void CDayView::OnDayLog_Thu()
{
this->ProcessWeekday(5);
}
void CDayView::OnDayLog_Fri()
{
this->ProcessWeekday(6);
}
void CDayView::OnDayLog_Sat()
{
this->ProcessWeekday(7);
}
void CDayView::ProcessWeekday(int menuDay)
{
COleDateTime currentDate(currentYear, currentMonth, currentDay, 0, 0,
0);
int currentWeekday = currentDate.GetDayOfWeek();
int diff = menuDay - currentWeekday;
if (diff != 0) {
if (diff > 0) {
for (int i=0; i<diff; i++) {
this->IncrementDay();
}
} else {
for (int j=0; j<abs(diff); j++) {
this->DecrementDay();
}
}
}
this->RefreshAllViews();
}
void CDayView::IncrementDay()
{
BOOL endOfMonth;
currentDay++;
if (currentMonth == 2) {
if (currentDay > this->FebruaryDays(currentYear)) {
endOfMonth = TRUE;
} else {
endOfMonth = FALSE;
}
} else {
if (currentDay > numberOfDays[currentMonth-1]) {
endOfMonth = TRUE;
} else {
endOfMonth = FALSE;
}
}
if (endOfMonth) {
currentMonth++;
currentDay = 1;
if (currentMonth > 12) {
currentMonth = 1;
currentYear++;
}
}
}
void CDayView::DecrementDay()
{
currentDay--;
if (currentDay<1) {
currentMonth--;
if (currentMonth < 1) {
currentMonth = 12;
currentYear--;
}
if (currentMonth == 2) {
currentDay = this->FebruaryDays(currentYear);
} else {
currentDay = numberOfDays[currentMonth-1];
}
}
}
void CDayView::RefreshAllViews()
{
this->GetDocument()->SetCurrentDay(currentDay);
this->GetDocument()->SetCurrentMonth(currentMonth);
this->GetDocument()->SetCurrentYear(currentYear);
// this->RebuildWorkPeriodSet();
this->GetDocument()->SetModifiedFlag();
this->GetDocument()->UpdateAllViews(NULL);
}
void CDayView::RebuildWorkPeriodSet()
{
m_pQueryWkDays = this->GetDocument()->GetQueryWorkDaysSet();
m_pQueryWkDays->RefreshWorkDay(currentYear, currentMonth, currentDay);
m_pQueryWkDays->RebuidDayViewArray();
}
//////////////////////////////////////////////////////////////////////
// COneProjectTotal Class
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
COneProjectTotal::COneProjectTotal()
{
m_projectNumber = -1;
m_totalTime = 0;
}
COneProjectTotal::COneProjectTotal(int projectNumber)
{
m_projectNumber = projectNumber;
m_totalTime = 0;
}
COneProjectTotal::COneProjectTotal(int projectNumber, double totalTime)
{
m_projectNumber = projectNumber;
m_totalTime = totalTime;
}
COneProjectTotal::~COneProjectTotal()
{
}
//////////////////////////////////////////////////////////////////////
// CWorkPeriodRect Class
//////////////////////////////////////////////////////////////////////
CWorkPeriodRect::CWorkPeriodRect()
{
}
CWorkPeriodRect::~CWorkPeriodRect()
{
}
void CWorkPeriodRect::Initialize(CDayView* pParent,
CBrush* pBrush, CBrush*
pHatched, CPen* pThin,
COleDateTime startTime,
COleDateTime stopTime)
{
int xOrigin = pParent->Get_X_Origin();
int hourWidth = pParent->GetHourWidth();
int totalDayWidth = pParent->GetTotalDayWidth();
m_startTime = CWorkDayParams::COleDateTime2Double(startTime);
m_endTime = CWorkDayParams::COleDateTime2Double(stopTime);
int top;
int bottom;
int left = xOrigin + hourWidth + 2;
int right = xOrigin + totalDayWidth;
BOOL m_solidTop = pParent->TimeToY(m_startTime, &top);
BOOL m_solidBottom = pParent->TimeToY(m_endTime, &bottom);
m_rect = CRect(left, top, right, bottom);
m_pBrush = pBrush;
m_pPenBlackThin = pThin;
m_pBrushBlackHatched = pHatched;
}
void CWorkPeriodRect::Draw(CDC* pDC) const
{
CPoint vertices[4];
vertices[0].x = m_rect.left;
vertices[0].y = m_rect.top;
vertices[1].x = m_rect.right;
vertices[1].y = m_rect.top;
vertices[2].x = m_rect.right;
vertices[2].y = m_rect.bottom;
vertices[3].x = m_rect.left;
vertices[3].y = m_rect.bottom;
CPen* pOldPen = pDC->SelectObject(m_pPenBlackThin);
CBrush* pOldBrush = pDC->SelectObject(m_pBrush);
pDC->Polygon(vertices, 4);
if (!m_solidTop || !m_solidBottom) {
pDC->SelectObject(m_pBrushBlackHatched);
pDC->Polygon(vertices, 4);
}
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldBrush);
};
BOOL CWorkPeriodRect::HitTest(const CPoint& point) const
{
return m_rect.PtInRect(point);
};
////////////////////////////////////////////
//
// DayView.h
//
////////////////////////////////////////////
#if
!defined(AFX_DAYVIEW_H__41F27CF8_10AC_4E78_8490_5A65A7EA2CAB__INCLUDED_)
#define AFX_DAYVIEW_H__41F27CF8_10AC_4E78_8490_5A65A7EA2CAB__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// DayView.h : header file
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// COneProjectTotal class
class COneProjectTotal
{
friend class CDayView;
public:
COneProjectTotal();
COneProjectTotal(int projectNumber);
COneProjectTotal(int projectNumber, double totalTime);
virtual ~COneProjectTotal();
private:
COleDateTimeSpan m_totalTime;
int m_projectNumber;
};
/////////////////////////////////////////////////////////////////////////////
// CWorkPeriodRect class
class CWorkPeriodRect
{
public:
void Initialize (CDayView* pParent,
CBrush* brush, CBrush* pHatched, CPen* pThin,
COleDateTime startTime, COleDateTime stopTime);
CWorkPeriodRect();
virtual ~CWorkPeriodRect();
void Draw(CDC* pDC) const;
BOOL HitTest(const CPoint& point) const;
private:
CBrush* m_pBrush;
CBrush* m_pBrushBlackHatched;
CPen* m_pPenBlackThin;
CRect m_rect;
double m_endTime;
double m_startTime;
BOOL m_solidTop;
BOOL m_solidBottom;
};
/////////////////////////////////////////////////////////////////////////////
// CDayView class
class CDayView : public CScrollView
{
protected:
DECLARE_DYNCREATE(CDayView)
// Attributes
public:
// Operations
public:
CDayView(); // protected constructor used by dynamic creation
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDayView)
public:
virtual void OnInitialUpdate();
virtual BOOL PreTranslateMessage(MSG* pMsg);
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//}}AFX_VIRTUAL
// Implementation
public:
CClockWatcherDoc* GetDocument();
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual ~CDayView();
int GetTotalDayWidth();
int GetHourWidth();
int Get_X_Origin();
BOOL TimeToY(double time, int* pY);
CBrush m_brushBlackHatched;
CPen m_penBlackThin;
protected:
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
// Generated message map functions
protected:
const CWorkPeriodRect* HitTest(const CPoint& point);
const CWorkPeriodRect* m_pRectHit;
CToolTipCtrl m_tooltip;
void OnDayLog_Sat();
void OnDayLog_Fri();
void OnDayLog_Thu();
void OnDayLog_Wed();
void OnDayLog_Tue();
void OnDayLog_Mon();
void OnDayLog_Sun();
void OnClickDaySelect();
void OnDblClickRightArrow();
void OnClickRightArrow();
void OnDblClickLeftArrow();
void OnClickLeftArrow();
void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint);
//{{AFX_MSG(CDayView)
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
//}}AFX_MSG
BOOL OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult);
DECLARE_MESSAGE_MAP()
private:
CString GetRoundingFactor();
void DrawLegendProjectInfo(CDC* pDC, int projectIndex, RECT textRect);
int legendWidth;
CFont m_fontArialBody;
CFont m_fontArialTitle;
void CreateAllFonts();
int legendBoxHeight;
int legendBoxWidth;
int legendLeft;
void DrawLegendBoxRect(CDC *pDC, CBrush* pBrush, int top);
void DrawWorkPeriodLegends(CDC *pDC);
int bottomMargin;
int rightMargin;
CSize GetDrawingExtents();
void SetScrollBars();
int ChooseProjectBrush(int projectNum);
CPen m_penBlackSolid;
CPen m_penWhiteSolid;
CPen m_penWhiteDotted;
CBrush m_fillBrush[CMS_BRIGHT_COLORS];
CBrush m_brushYellowSolid;
void RebuildWorkPeriodSet();
COleDateTime m_endTime;
COleDateTime m_startTime;
long m_projectNumber;
CArray<COneProjectTotal,COneProjectTotal> m_allProjects;
CArray<CWorkPeriodRect,CWorkPeriodRect> m_allWorkPeriods;
long m_workPeriodNumber;
long m_workDayNumber;
CQueryWorkDaysSet* m_pQueryWkDays;
void DrawWorkPeriods(CDC* pDC);
int adjStopTime;
int adjStartTime;
void RefreshAllViews();
void DecrementDay();
void IncrementDay();
void ProcessWeekday(int menuDay);
int FebruaryDays(int theYear);
void WriteDateHeader(CDC* pDC);
int AdjustedStopTime();
int AdjustedStartTime();
void CreateAllBrushes();
void DrawHourTriangle(CDC* pDC, CPoint vertex);
void PlaceHourArrows(CDC* pDC);
int CalcNumHours();
void CreateAllPens();
void DrawDayLogHours(CDC* pDC);
void DrawDayLogBackground(CDC* pDC);
void DrawDayLogBorders(CDC* pDC);
void DrawDayLog(CDC* pDC);
int hourWidth;
int hourHeight;
int taskWidth;
int xBorder;
int totalHeight;
int totalDayWidth;
int groupWidth;
int centerWidth;
int arrowWidth;
int yButton;
int arrowBtnHeight;
int centerBtnHeight;
int dayHeight;
int xOrigin;
int yOrigin;
CBitmapButton m_dayArrowLeft;
CBitmapButton m_daySelect;
CBitmapButton m_dayArrowRight;
int currentDay;
int currentMonth;
int currentYear;
};
#ifndef _DEBUG // debug version in ClockWatcherView.cpp
inline CClockWatcherDoc* CDayView::GetDocument()
{ return (CClockWatcherDoc*)m_pDocument; }
#endif
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately
before the previous
line.
#endif //
!defined(AFX_DAYVIEW_H__41F27CF8_10AC_4E78_8490_5A65A7EA2CAB__INCLUDED_)
- Next message: txemuki: "Positioning a window"
- Previous message: Gary Chang[MSFT]: "RE: VC 7.1: Preview DC"
- Next in thread: Bill Thompson: "Re: ToolTips in a View and TTN_NEEDTEXT"
- Reply: Bill Thompson: "Re: ToolTips in a View and TTN_NEEDTEXT"
- Messages sorted by: [ date ] [ thread ]