This repository has been archived on 2024-03-20. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
ContactBook/MainFrm.cpp
2024-03-20 09:28:18 -05:00

875 lines
26 KiB
C++

// MainFrm.cpp : implementation of the CMainFrame class
//
#include "stdafx.h"
#include "CB32.h"
#include "ComboBar.h"
#include "MainFrm.h"
#include "CB32Doc.h"
#include "CB32View.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMainFrame
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_MESSAGE(MYWM_NOTIFYICON, On_MYWM_NOTIFYICON)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_CLOSE()
ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar)
ON_COMMAND(ID_EDIT_FIND_COMBO, OnEditFindCombo)
ON_WM_NCPAINT()
ON_WM_NCACTIVATE()
ON_WM_SYSCOLORCHANGE()
ON_MESSAGE(WM_SETTEXT, OnSetText)
ON_MESSAGE(WM_SETTINGCHANGE, OnSettingChange)
ON_COMMAND(ID_VIEW_WNDPOS, OnViewWndpos)
ON_WM_SYSCOMMAND()
ON_COMMAND(ID_TRAY_SHOW, OnTrayShow)
ON_COMMAND(ID_TRAY_EXIT, OnTrayExit)
ON_WM_DESTROY()
ON_COMMAND(ID_VIEW_FLATBAR, OnViewFlatbar)
ON_UPDATE_COMMAND_UI(ID_VIEW_FLATBAR, OnUpdateViewFlatbar)
//}}AFX_MSG_MAP
ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR, OnUpdateViewStatusBar)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_CLOCK, OnUpdateClock)
END_MESSAGE_MAP()
// toolbar buttons - IDs are command buttons
static UINT BASED_CODE buttons[] =
{
// same order as in the bitmap 'toolbar.bmp'
ID_FILE_NEW,
ID_FILE_OPEN,
ID_FILE_SAVE,
ID_SEPARATOR,
ID_ENTRY_ADD,
ID_ENTRY_EDIT,
ID_ENTRY_DELETE,
ID_ENTRY_COPY,
ID_SEPARATOR,
ID_SEPARATOR, //(10) CComboBox location in the toolbar
ID_EDIT_FIND,
ID_SEPARATOR,
ID_TBAR_DIAL,
ID_TBAR_EMAIL,
ID_TBAR_WEB,
ID_APP_PREFS,
ID_SEPARATOR,
ID_HELP_INDEX,
};
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
ID_INDICATOR_CLOCK,
};
/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction
CMainFrame::CMainFrame()
{
m_bActive = -1; // invalid
// init tray icon tracker
m_inTray = FALSE;
}
CMainFrame::~CMainFrame()
{
}
/////////////////////////////////////////////////////////////////////////////
// Helpers for saving/restoring window state
BOOL CMainFrame::ReadWindowPlacement(LPWINDOWPLACEMENT pwp)
{
CString strBuffer = theApp.GetProfileString(_T("Preferences"),
_T("WindowPos"));
if (strBuffer.IsEmpty())
return FALSE;
WINDOWPLACEMENT wp;
int nRead = _stscanf(strBuffer, _T("%u,%u,%d,%d,%d,%d,%d,%d,%d,%d"),
&wp.flags, &wp.showCmd,
&wp.ptMinPosition.x, &wp.ptMinPosition.y,
&wp.ptMaxPosition.x, &wp.ptMaxPosition.y,
&wp.rcNormalPosition.left, &wp.rcNormalPosition.top,
&wp.rcNormalPosition.right, &wp.rcNormalPosition.bottom);
if (nRead != 10)
return FALSE;
if (m_inTray)
wp.showCmd = SW_HIDE;
wp.length = sizeof wp;
*pwp = wp;
return TRUE;
}
void CMainFrame::WriteWindowPlacement(LPWINDOWPLACEMENT pwp)
{
TCHAR szBuffer[sizeof("-32767")*8 + sizeof("65535")*2];
wsprintf(szBuffer, _T("%u,%u,%d,%d,%d,%d,%d,%d,%d,%d"),
pwp->flags, pwp->showCmd,
pwp->ptMinPosition.x, pwp->ptMinPosition.y,
pwp->ptMaxPosition.x, pwp->ptMaxPosition.y,
pwp->rcNormalPosition.left, pwp->rcNormalPosition.top,
pwp->rcNormalPosition.right, pwp->rcNormalPosition.bottom);
theApp.WriteProfileString(_T("Preferences"), _T("WindowPos"),
szBuffer);
}
void CMainFrame::SaveWndSize()
{
WINDOWPLACEMENT wp;
wp.length = sizeof wp;
if (GetWindowPlacement(&wp))
{
wp.flags = 0;
if (IsZoomed())
wp.flags |= WPF_RESTORETOMAXIMIZED;
if (!IsIconic())
WriteWindowPlacement(&wp);
}
}
/////////////////////////////////////////////////////////////////////////////
// Create main window with toolbar, etc
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (theApp.GetProfileInt(_T("Preferences"),_T("StartMinimized"),0))
{
TrayMessage(NIM_ADD,IDR_MAINFRAME,
AfxGetApp()->LoadIcon(IDR_MAINFRAME),
m_strTitle + " " + GetDocTitle());
m_inTray = TRUE;
}
if (theApp.GetProfileInt(_T("Preferences"),_T("RememberWin"),1))
{
WINDOWPLACEMENT wp;
if (ReadWindowPlacement(&wp))
SetWindowPlacement(&wp);
}
if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadBitmap(IDR_MAINFRAME) ||
!m_wndToolBar.SetButtons(buttons,
sizeof(buttons)/sizeof(UINT)))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!CreateComboBox())
{
TRACE0("Failed to create combobox in toolbar\n");
return -1; // fail to create
}
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.SetWindowText(_T("ContactBook ToolBar"));
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
m_wndToolBar.SetFlatLook(theApp.GetProfileInt(_T("Preferences"),_T("Flatbar"),1));
if (!m_wndStatusBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM , AFX_IDW_STATUS_BAR) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// create the timer to update the status bar clock
if(!SetTimer(TIMER_ID, 2000, &CMainFrame::UpdateTime))
return -1;
CMainFrame::UpdateTime(m_hWnd, 0, 0, 0); // update the time now
return 0;
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CFrameWnd::PreCreateWindow(cs);
}
void CMainFrame::OnClose()
{
KillTimer(TIMER_ID);
if (theApp.GetProfileInt(_T("Preferences"),_T("RememberWin"),1))
SaveWndSize();
DeleteTrayIcon();
CFrameWnd::OnClose();
}
/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
// Private MFC function only sets the title if it's different
//
extern void AFXAPI AfxSetWindowText(HWND, LPCTSTR);
//////////////////
// Override to change title--app name first, then doc name
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
AfxSetWindowText(m_hWnd, m_strTitle + " " + GetDocTitle());
}
//////////////////
// Get doc title: I use full path name or "untitled"
CString CMainFrame::GetDocTitle()
{
CString s = "";
CDocument* pDoc = GetActiveDocument();
if (pDoc != NULL)
s = pDoc->GetPathName();
return s.IsEmpty() ? "- [Untitled]" : "- ["+s+"]";
}
////////////////
// Mini-class to get the caption rectangle of a window in window coords.
// This is the area of the title bar inside the window frame,
// including the icon but not the min/max/close buttons.
class CCaptionRect : public CRect {
public:
CCaptionRect(const CWnd& wnd); // use reference to deny NULL ptr
};
/////////////////
// Paint non-client area: First let Windows paint, then I'll paint over it.
//
// I use the relatively obscure and poorly documented update region feature
// to prevent Windows from painting the title text. This is to get totally
// flicker-free painting when the user sizes the window. Note that Windows
// sometimes uses WPARAM=1.
void CMainFrame::OnNcPaint()
{
MSG& msg = AfxGetThreadState()->m_lastSentMsg;
HRGN hRgn = (HRGN)msg.wParam;
CCaptionRect rc(*this); // caption rectangle in window coords
CRect rcWin; // window rect
GetWindowRect(&rcWin); // .. get window rect
rc += rcWin.TopLeft(); // convert caption rect to screen coords
// Don't bother painting if the caption doesn't lie within the region.
if (msg.wParam > 1 && !::RectInRegion((HRGN)msg.wParam, &rc)) {
CFrameWnd::OnNcPaint(); // just do default thing
return; // and quit
}
// Exclude caption from update region
HRGN hRgnCaption = ::CreateRectRgnIndirect(&rc);
HRGN hRgnNew = ::CreateRectRgnIndirect(&rc);
if (msg.wParam > 1) {
// wParam is a valid region: subtract caption from it
::CombineRgn(hRgnNew, hRgn, hRgnCaption, RGN_DIFF);
} else {
// wParam is not a valid region: create one that's the whole
// window minus the caption bar
HRGN hRgnAll = ::CreateRectRgnIndirect(&rcWin);
CombineRgn(hRgnNew, hRgnAll, hRgnCaption, RGN_DIFF);
DeleteObject(hRgnAll);
}
// Call Windows to do WM_NCPAINT with altered update region
WPARAM savewp = msg.wParam; // save original wParam
msg.wParam = (WPARAM)hRgnNew; // set new region for DefWindowProc
CFrameWnd::OnNcPaint(); // call it
DeleteObject(hRgnCaption); // clean up
DeleteObject(hRgnNew); // ...
msg.wParam = savewp; // restore original wParam
PaintCaption(); // Now paint my special caption
}
//////////////////
// Non-client area (de)activated: paint it
BOOL CMainFrame::OnNcActivate(BOOL bActive)
{
// Mimic MFC kludge to stay active if WF_STAYACTIVE bit is on
if (m_nFlags & WF_STAYACTIVE)
bActive = TRUE;
if (!IsWindowEnabled()) // but not if disabled
bActive = FALSE;
if (bActive==m_bActive)
return TRUE; // nothing to do
// Turn WS_VISIBLE off before callinf DefWindowProc,
// so DefWindowProc won't paint and thereby cause flicker.
DWORD dwStyle = GetStyle();
if (dwStyle & WS_VISIBLE)
SetWindowLong(m_hWnd, GWL_STYLE, (dwStyle & ~ WS_VISIBLE));
// do not call the base class because it will call Default()
// and we may have changed bActive.
DefWindowProc(WM_NCACTIVATE, bActive, 0L);
if (dwStyle & WS_VISIBLE)
SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
// At this point, nothing has happened (since WS_VISIBLE was off).
// Now it's time to paint.
m_bActive = bActive; // update state
SendMessage(WM_NCPAINT); // paint non-client area (frame too)
return TRUE; // done OK
}
//////////////////
// Someone called SetWindowText: paint new text.
LRESULT CMainFrame::OnSetText(WPARAM wParam, LPARAM lParam)
{
// Turn WS_VISIBLE style off before calling Windows to
// set the text, then turn it back on again after.
DWORD dwStyle = GetStyle();
if (dwStyle & WS_VISIBLE)
SetWindowLong(m_hWnd, GWL_STYLE, dwStyle & ~ WS_VISIBLE);
LRESULT lRet = DefWindowProc(WM_SETTEXT, wParam, lParam);
if (dwStyle & WS_VISIBLE)
SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
m_rcCaption.SetRectEmpty(); // invalidate caption
SendMessage(WM_NCPAINT); // paint non-client area (frame too)
PaintCaption(); // ..and paint it
return lRet;
}
//////////////////
// User changed system settings (possibly font) or colors.
// Invalidate everything to generate anew.
LRESULT CMainFrame::OnSettingChange(WPARAM, LPARAM)
{
OnSysColorChange();
return 0;
}
void CMainFrame::OnSysColorChange()
{
m_fontCaption.DeleteObject(); // generate new fonts
m_fontAcme.DeleteObject(); // ...
m_rcCaption.SetRectEmpty(); // will force new bitmap to be created
}
//////////////////
// Paint custom caption. Flag tells whether active or not.
// Just blast the bitmap to the title bar, but not iif minimized (iconic).
void CMainFrame::PaintCaption(BOOL bActive)
{
if (!IsIconic()) {
CWindowDC dcWin(this);
CDC dc;
dc.CreateCompatibleDC(&dcWin);
CBitmap* pOldBitmap = dc.SelectObject(GetCaptionBitmap(bActive));
const CRect& rc = m_rcCaption;
dcWin.BitBlt(rc.left,rc.top,rc.Width(),rc.Height(),&dc,0,0,SRCCOPY);
}
}
//////////////////
// Helper function to compute the luminosity for an RGB color.
// Measures how bright the color is. I use this so I can draw the caption
// text using the user's chosen color, unless it's too dark. See MSDN for
// definition of luminosity and how to compute it.
static int GetLuminosity(COLORREF color)
{
#define HLSMAX 240 // This is what Display Properties uses
#define RGBMAX 255 // max r/g/b value is 255
int r = GetRValue(color);
int g = GetGValue(color);
int b = GetBValue(color);
int rgbMax = max( max(r,g), b);
int rgbMin = min( min(r,g), b);
return (((rgbMax+rgbMin) * HLSMAX) + RGBMAX ) / (2*RGBMAX);
}
#define COLOR_WHITE RGB(255,255,255)
#define COLOR_BLACK RGB(0,0,0)
#define NCOLORSHADES 64 // this many shades in gradient
//////////////////
// Helper to paint rectangle with a color.
static void PaintRect(CDC& dc, int x, int y, int w, int h, COLORREF color)
{
CBrush brush(color);
CBrush* pOldBrush = dc.SelectObject(&brush);
dc.PatBlt(x, y, w, h, PATCOPY);
dc.SelectObject(pOldBrush);
}
//////////////////
// Get the appropriate bitmap whether active/inactive. If the size of
// the caption rect has changed since the last time it was generated,
// generate it again.
//
// This is the function that actually does the shading. It creates a
// bitmap that's used to paint the caption. It looks horrible, but it's
// just a lot of bit-twiddling GDI stuff.
CBitmap* CMainFrame::GetCaptionBitmap(BOOL bActive)
{
CBitmap& bm = m_bmCaption[bActive!=0]; // one of two bitmaps
CCaptionRect rcCap(*this); // caption rectangle in win coords
if (rcCap != m_rcCaption) { // if changed since last time:
m_bmCaption[0].DeleteObject(); // delete both bitmaps,
m_bmCaption[1].DeleteObject(); // ..they are bad
m_rcCaption = rcCap; // note for next time
}
if (bm.m_hObject)
return &bm; // already have bitmap; return it.
// Either the size changed or the bitmap was never created. Create it.
rcCap -= rcCap.TopLeft(); // convert caption rectangle origin to (0,0)
int w = rcCap.Width();
int h = rcCap.Height();
int cxIcon = GetSystemMetrics(SM_CXSIZE);
int cyIcon = GetSystemMetrics(SM_CYSIZE);
// Create bitmap same size as caption area and select into memory DC
CWindowDC dcWin(this);
CDC dc;
dc.CreateCompatibleDC(&dcWin);
bm.DeleteObject();
bm.CreateCompatibleBitmap(&dcWin, w, h);
CBitmap* pOldBitmap = dc.SelectObject(&bm);
// Paint shaded background. Note all painting is into memory DC,
// not window, so there's no flicker.
if (!bActive) {
// Inactive caption: don't do shading, just fill w/bg color
PaintRect(dc, 0, 0, w, h, GetSysColor(COLOR_INACTIVECAPTION));
} else {
// Active caption: do shading
COLORREF clrBG = GetSysColor(COLOR_ACTIVECAPTION); // background color
int r = GetRValue(clrBG); // red..
int g = GetGValue(clrBG); // ..green
int b = GetBValue(clrBG); // ..blue color vals
int x = 5*rcCap.right/6; // start 5/6 of the way right
int w = x - rcCap.left; // width of area to shade
int xDelta= max(w/NCOLORSHADES,1); // width of one shade band
// Paint far right 1/6 of caption the background color
PaintRect(dc, x, 0, rcCap.right-x, h, clrBG);
// Compute new color brush for each band from x to x + xDelta.
// Excel uses a linear algorithm from black to normal, i.e.
//
// color = CaptionColor * r
//
// where r is the ratio x/w, which ranges from 0 (x=0, left)
// to 1 (x=w, right). This results in a mostly black title bar,
// since we humans don't distinguish dark colors as well as light
// ones. So instead, I use the formula
//
// color = CaptionColor * [1-(1-r)^2]
//
// which still equals black when r=0 and CaptionColor when r=1,
// but spends more time near CaptionColor. For example, when r=.5,
// the multiplier is [1-(1-.5)^2] = .75, closer to 1 than .5.
// I leave the algebra to the reader to verify that the above formula
// is equivalent to
//
// color = CaptionColor - (CaptionColor*(w-x)*(w-x))/(w*w)
//
// The computation looks horrendous, but it's only done once each
// time the caption changes size; thereafter BitBlt'ed to the screen.
//
while (x > xDelta) { // paint bands right to left
x -= xDelta; // next band
int wmx2 = (w-x)*(w-x); // w minus x squared
int w2 = w*w; // w squared
PaintRect(dc, x, 0, xDelta, h,
RGB(r-(r*wmx2)/w2, g-(g*wmx2)/w2, b-(b*wmx2)/w2));
}
PaintRect(dc,0,0,x,h,COLOR_BLACK); // whatever's left ==> black
}
// Draw icon and caption buttons.
// These are all drawn inside a rectangle of dimensions SM_C[XY]SIZE.
CRect rcButn(0,0,cxIcon,cyIcon);
// Within the basic button rectangle, Windows 95 uses a 1 or 2 pixel border
// Icon has 2 pixel border on left, 1 pixel on top/bottom, 0 right
rcButn.DeflateRect(0,1);
rcButn.left += 2;
DrawIconEx(dc.m_hDC, rcButn.left, rcButn.top,
(HICON)GetClassLong(m_hWnd, GCL_HICONSM),
rcButn.Width(), rcButn.Height(), 0, NULL, DI_NORMAL);
// Close box has a 2 pixel border on all sides but left, which is zero
rcButn.DeflateRect(0,1); // shrink top/bottom 1 more
rcButn += CPoint(w-cxIcon-2,0); // move right
dc.DrawFrameControl(&rcButn, DFC_CAPTION, DFCS_CAPTIONCLOSE);
DWORD dwStyle = GetStyle();
// Max/restore button is like close box; just shift rectangle left
if (dwStyle & WS_MAXIMIZEBOX) {
rcButn -= CPoint(cxIcon,0);
dc.DrawFrameControl(&rcButn, DFC_CAPTION,
IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX);
}
// Minimize button has 2 pixel border on all sides but right.
if (dwStyle & WS_MINIMIZEBOX) {
rcButn -= CPoint(cxIcon-2,0);
dc.DrawFrameControl(&rcButn, DFC_CAPTION, DFCS_CAPTIONMIN);
}
// Now draw text. First Create fonts if needed
if (!m_fontCaption.m_hObject)
CreateFonts();
// Paint "ACME TEXT" using ACME font, always white
//CString s = m_strTitle + " "; // app title
CRect rcText = rcCap; // caption rectangle
rcText.left += cxIcon+2; // start after icon
rcText.right = rcButn.left-2; // don't draw past buttons
dc.SetBkMode(TRANSPARENT); // draw on top of our shading
//dc.SetTextColor(COLOR_WHITE); // always white
CFont* pOldFont = dc.SelectObject(&m_fontAcme);
//dc.DrawText(s, &rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
// Now paint window title (caption)
//rcText.left += dc.GetTextExtent(s).cx; // move past "ACME EDIT "
if (rcText.right > rcText.left) { // if still room:
COLORREF clrText; // text color
if (bActive) {
// Excel always uses white for title color, but I use the user's
// selected color--unless it's too dark, then I use white.
clrText = GetSysColor(COLOR_CAPTIONTEXT);
if (GetLuminosity(clrText) < 90) // good from trial & error
clrText = COLOR_WHITE;
} else
clrText = GetSysColor(COLOR_INACTIVECAPTIONTEXT);
// Paint the text. Use DT_END_ELLIPSIS to draw ellipsis if text
// won't fit. Win32 sure is friendly!
COLORREF clrColor;
clrColor = dc.SetTextColor(clrText);
dc.SelectObject(&m_fontCaption);
dc.DrawText(m_strTitle + " " + GetDocTitle(), &rcText,
DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
dc.SetTextColor(clrColor);
}
// Restore DC
dc.SelectObject(pOldFont);
dc.SelectObject(pOldBitmap);
return &bm; // return bitmap to caller--whew!
}
//////////////////
// Helper function to build the fonts I need.
//
void CMainFrame::CreateFonts()
{
// Get current system caption font, just to get its size
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0));
m_fontCaption.CreateFontIndirect(&ncm.lfCaptionFont);
// Create "ACME" font same size as caption font, but use Book Antiqua
m_fontAcme.CreatePointFont(80, _T("MS Sans Serif")); // 12 pt for now
LOGFONT lf;
m_fontAcme.GetLogFont(&lf); // get font info
m_fontAcme.DeleteObject(); // I don't really want 12 pt
lf.lfWeight|=FW_BOLD; // make bold
lf.lfHeight = ncm.lfCaptionFont.lfHeight; // same height as caption font
m_fontAcme.CreateFontIndirect(&lf); // create font
}
//////////////////
// CCaptionRect: Constructor conputes caption rectangle in window coords.
CCaptionRect::CCaptionRect(const CWnd& wnd)
{
// Get size of frame around window
DWORD dwStyle = wnd.GetStyle();
CSize szFrame = (dwStyle & WS_THICKFRAME) ?
CSize(GetSystemMetrics(SM_CXSIZEFRAME),
GetSystemMetrics(SM_CYSIZEFRAME)) :
CSize(GetSystemMetrics(SM_CXFIXEDFRAME),
GetSystemMetrics(SM_CYFIXEDFRAME));
int cxIcon = GetSystemMetrics(SM_CXSIZE); // width of caption icon/button
// Compute rectangle
wnd.GetWindowRect(this); // window rect in screen coords
*this -= CPoint(left, top); // shift origin to (0,0)
left += szFrame.cx; // frame
right -= szFrame.cx; // frame
top += szFrame.cy; // top = end of frame
bottom = top + GetSystemMetrics(SM_CYCAPTION) // height of caption
- GetSystemMetrics(SM_CYBORDER); // minus gray shadow border
}
///////////////////////////////////////
// time/combobar
int CMainFrame::CreateComboBox()
{
CRect rect, comboRect;
m_wndToolBar.SetButtonInfo(9, IDW_COMBO, TBBS_SEPARATOR, COMBO_WIDTH);
m_wndToolBar.GetItemRect(9, &rect);
rect.bottom += COMBO_HEIGHT;
if (!m_wndToolBar.m_toolBarCombo.Create(CBS_DROPDOWN|WS_VISIBLE|WS_TABSTOP|CBS_AUTOHSCROLL,
rect, &m_wndToolBar, IDW_COMBO))
{
return FALSE;
}
// center combo box edit control vertically within tool bar
rect.bottom -= COMBO_HEIGHT;
m_wndToolBar.m_toolBarCombo.GetWindowRect(&comboRect);
m_wndToolBar.m_toolBarCombo.ScreenToClient(&comboRect);
m_wndToolBar.m_toolBarCombo.SetWindowPos(&m_wndToolBar.m_toolBarCombo, rect.left,
rect.top + (rect.Height() - comboRect.Height())/2+1, 0, 0,
SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
return TRUE;
}
void CMainFrame::OnViewStatusBar()
{
CWnd* pStatusBar;
if (pStatusBar = GetDescendantWindow(AFX_IDW_STATUS_BAR))
{
pStatusBar->ShowWindow(!(pStatusBar->GetStyle() & WS_VISIBLE));
RecalcLayout();
}
}
void CMainFrame::OnUpdateViewStatusBar(CCmdUI* pCmdUI)
{
CWnd* pStatusBar;
if (pStatusBar = GetDescendantWindow(AFX_IDW_STATUS_BAR))
pCmdUI->SetCheck((pStatusBar->GetStyle() & WS_VISIBLE));
}
void CMainFrame::OnViewWndpos()
{
SaveWndSize();
AfxMessageBox(IDS_WINDSAVE, MB_ICONINFORMATION);
}
void CMainFrame::OnUpdateClock(CCmdUI* pCmdUI)
{
pCmdUI->Enable(TRUE);
}
void CMainFrame::OnEditFindCombo()
{
m_wndToolBar.m_toolBarCombo.SetFocus();
}
void CALLBACK EXPORT CMainFrame::UpdateTime(HWND hWnd,UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
CString time;
BOOL bPM = TRUE;
SYSTEMTIME tm;
CMainFrame* pFrame = (CMainFrame*)CWnd::FromHandle(hWnd);
CStatusBar* pStatusBar = (CStatusBar*) pFrame->GetDescendantWindow(AFX_IDW_STATUS_BAR);
if (pStatusBar == NULL)
return;
GetLocalTime(&tm);
if (tm.wHour >= 0 && tm.wHour < 12)
bPM = FALSE;
// convert 24 hour clock
if (tm.wHour > 12) // 13 - 23
tm.wHour -= 12;
else if (tm.wHour == 0) // midnight
tm.wHour = 12;
time.Format(_T(" %d:%02.2d %s "), tm.wHour, tm.wMinute, ((bPM) ? _T("PM"):_T("AM")));
// get current pane font information and update time
UINT nID, nStyle;
int nWidth, nIndex = pStatusBar->CommandToIndex(ID_INDICATOR_CLOCK);
CClientDC dc(pStatusBar);
CFont* pOldFont = dc.SelectObject(pStatusBar->GetFont());
CSize szExtent = dc.GetTextExtent(time, time.GetLength());
dc.SelectObject(pOldFont);
pStatusBar->GetPaneInfo(nIndex, nID, nStyle, nWidth);
pStatusBar->SetPaneInfo(nIndex, nID, nStyle, szExtent.cx);
pStatusBar->SetPaneText(nIndex, time);
}
void CMainFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID == SC_MINIMIZE) && theApp.GetProfileInt(_T("Preferences"),_T("RememberTray"),1))
{
TrayMessage(NIM_ADD,IDR_MAINFRAME,
AfxGetApp()->LoadIcon(IDR_MAINFRAME),
m_strTitle + " " + GetDocTitle());
m_inTray = TRUE;
ShowWindow(SW_HIDE);
}
else
CFrameWnd::OnSysCommand(nID, lParam);
}
BOOL CMainFrame::TrayMessage(DWORD dwMessage, UINT uID, HICON hIcon,
CString pszTip)
{
BOOL res;
tnd.cbSize = sizeof(NOTIFYICONDATA);
tnd.hWnd = m_hWnd;
tnd.uID = uID;
tnd.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
tnd.uCallbackMessage = MYWM_NOTIFYICON;
tnd.hIcon = hIcon;
if (!pszTip.IsEmpty())
{
lstrcpyn(tnd.szTip, pszTip, sizeof(tnd.szTip));
}
else
{
tnd.szTip[0] = '\0';
}
res = Shell_NotifyIcon(dwMessage, &tnd);
if (hIcon)
DestroyIcon(hIcon);
return res;
}
LONG CMainFrame::On_MYWM_NOTIFYICON(UINT wParam, LONG lParam)
{
UINT uMouseMsg;
uMouseMsg = (UINT) lParam;
switch (uMouseMsg)
{
case WM_LBUTTONUP:
{
ShowWindow(SW_SHOW);
DeleteTrayIcon();
break;
}
case WM_RBUTTONUP:
{
CMenu menu;
CMenu* pSubMenu = NULL;
if (menu.LoadMenu(IDR_TRAYMENU))
pSubMenu = menu.GetSubMenu(0);
if (pSubMenu)
{
::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);
CPoint ptCur;
::GetCursorPos(&ptCur);
::SetForegroundWindow(tnd.hWnd);
::TrackPopupMenu(pSubMenu->m_hMenu, 0, ptCur.x, ptCur.y, 0, tnd.hWnd, NULL);
}
}
}
return 0;
}
void CMainFrame::OnTrayShow()
{
ShowWindow(SW_SHOW);
DeleteTrayIcon();
}
void CMainFrame::OnTrayExit()
{
DeleteTrayIcon();
theApp.HideApplication();
CDocument* pDoc = GetActiveDocument();
if (pDoc != NULL)
pDoc->SaveModified();
theApp.CloseAllDocuments(FALSE);
PostQuitMessage(0);
}
void CMainFrame::DeleteTrayIcon()
{
if (m_inTray)
{
TrayMessage(NIM_DELETE,IDR_MAINFRAME,0,"");
m_inTray = FALSE;
}
}
void CMainFrame::OnDestroy()
{
CFrameWnd::OnDestroy();
DeleteTrayIcon();
}
void CMainFrame::OnViewFlatbar()
{
m_wndToolBar.SetFlatLook(!m_wndToolBar.IsFlatLook());
theApp.WriteProfileInt(_T("Preferences"),_T("Flatbar"),
m_wndToolBar.IsFlatLook());
}
void CMainFrame::OnUpdateViewFlatbar(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_wndToolBar.IsFlatLook());
}