875 lines
26 KiB
C++
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());
|
|
}
|