// P4PaneView.cpp : implementation file
//

#include "stdafx.h"
#include "p4win.h"
#include "P4PaneView.h"
#include "P4PaneContent.h"
#include "MainFrm.h"
#include "ImageList.h"
#include <afxpriv.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CP4PaneView

const int ID_CAPTION = 222;

IMPLEMENT_DYNCREATE(CP4PaneView, CView)

BEGIN_MESSAGE_MAP(CP4PaneView, CView)
	ON_WM_SETFOCUS()
	ON_WM_SIZE()
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
	ON_WM_CREATE()
	ON_MESSAGE( WM_FINDPATTERN, OnFindPattern )
	ON_BN_CLICKED( ID_CAPTION, LButtonClk )
	ON_BN_DOUBLECLICKED( ID_CAPTION, LButtonDblClk )
	ON_WM_SYSCOLORCHANGE()
	ON_MESSAGE( WM_ACTIVATEMODELESS, OnActivateModeless )
END_MESSAGE_MAP()

CP4PaneView::CP4PaneView() 
	: m_content(0)
{
}

CP4PaneView::~CP4PaneView() {}

void CP4PaneView::SetBranchWnd(CWnd *wnd) { m_content->SetBranchWnd(wnd); }
void CP4PaneView::SetChangeWnd(CWnd *wnd) { m_content->SetChangeWnd(wnd); }
void CP4PaneView::SetClientWnd(CWnd *wnd) { m_content->SetClientWnd(wnd); }
void CP4PaneView::SetDepotWnd(CWnd *wnd) { m_content->SetDepotWnd(wnd); }
void CP4PaneView::SetJobWnd(CWnd *wnd) { m_content->SetJobWnd(wnd); }
void CP4PaneView::SetLabelWnd(CWnd *wnd) { m_content->SetLabelWnd(wnd); }
void CP4PaneView::SetOldChgWnd(CWnd *wnd) { m_content->SetOldChgWnd(wnd); }
void CP4PaneView::SetUserWnd(CWnd *wnd) { m_content->SetUserWnd(wnd); }

void CP4PaneView::OnInitialUpdate()
{
	CView::OnInitialUpdate();

	// for some reason, list views end up with client edge style set
	// and we don't want it for any views
	ModifyStyleEx(WS_EX_CLIENTEDGE, 0);

	// want tooltips for caption
	m_reBar.SetBarStyle(CBRS_ALIGN_TOP |CBRS_TOOLTIPS);
	// don't want separator bar between caption and toolbar bands
	m_reBar.ModifyStyle(RBS_BANDBORDERS, 0);

	// put dropdown arrows on dropdown buttons
	m_toolBar.GetToolBarCtrl().SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);
	// set owner to mainframe so tooltips status bar messages will work
	m_toolBar.SetOwner(AfxGetApp()->m_pMainWnd); 
	// make it flat and transparent so it looks good on XP
	m_toolBar.ModifyStyle(0, TBSTYLE_FLAT|TBSTYLE_TRANSPARENT);
	// get rid of extra space around buttons
	m_toolBar.SetBorders();
	// set style to get tooltips working
	m_toolBar.SetBarStyle(CBRS_ALIGN_TOP | CBRS_TOOLTIPS | CBRS_FLYBY);
	// created non-visible so styles could be set before showing, so show now
	m_toolBar.ShowWindow(SW_SHOW);

	// set image list(s) for the toolbar
	m_toolBar.GetToolBarCtrl().SetImageList(
		TheApp()->GetToolBarImageList());
	m_toolBar.GetToolBarCtrl().SetDisabledImageList(
		TheApp()->GetToolBarImageList()->GetDisabled());

	// must set button size before adding buttons
	CSize sizeImage(18, 16);
	CSize sizeButton(18 + 7, 16 + 7);
	m_toolBar.SetSizes(sizeButton, sizeImage);

	// add the buttons to the toolbar
	SetToolBarButtons();

	// make the caption control transparent so the rebar will
	// draw the background under it
	m_captionCtrl.ModifyStyleEx(0, WS_EX_TRANSPARENT);

	// put the bands into the rebar
	// the ' ' text for the caption provides a little space on the left
	m_reBar.AddBar(&m_captionCtrl, _T(" "), NULL, RBBS_NOGRIPPER);
	m_reBar.AddBar(&m_toolBar, NULL, NULL, RBBS_NOGRIPPER|RBBS_FIXEDBMP);

	// determine how tall the bar should be to fit the taller of
	// the caption band or the toolbar band
	CRect r;
	r.SetRectEmpty();
	m_reBar.CalcInsideRect(r, TRUE);
	m_barHeight = max(16 + 7, GetSystemMetrics(SM_CYCAPTION));
	m_barHeight -= r.Height();
	m_contentTop = m_barHeight + GetSystemMetrics(SM_CYEDGE);

	// set up min/max sizes and ideal sizes for pieces of the rebar
	REBARBANDINFO rbbi;
	rbbi.cbSize = sizeof(rbbi);

	// set title band so it will be as wide as possible
	// and with a min size of one, it will never disappear
	// since the caption control will use the standard caption
	// font, it should be as tall as a standard caption.
	rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE;
	rbbi.cxMinChild = 1;
	rbbi.cyMinChild = GetSystemMetrics(SM_CYCAPTION);
	rbbi.cx = rbbi.cxIdeal = 20000;	// want to be really big!
	m_reBar.GetReBarCtrl().SetBandInfo(0, &rbbi);

	// set max size for toolbar band to it's actual size
	// and min size to same so it won't be crowded out by
	// caption.
	m_toolBar.GetWindowRect(&r);
	rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE;
	rbbi.cxMinChild = r.Width();
	rbbi.cyMinChild = 16 + 7;
	rbbi.cx = rbbi.cxIdeal = r.Width();
	m_reBar.GetReBarCtrl().SetBandInfo(1, &rbbi);

	// reposition the bars, now that their sizes have been set
	GetClientRect(&r);
	r.bottom = m_barHeight;
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 1,
		reposDefault, NULL, &r);
	// and reposition the content, allowing space for the rebar edge
	GetContentWnd()->MoveWindow(0,m_contentTop,
		r.Width(),r.Height() - m_contentTop,TRUE);

	EnableToolTips();

	if (GET_P4REGPTR( )->SwapButtonPosition())
		SwapButtonPosition();
}

void CP4PaneView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);

	// don't do other sizing until created
	if(!IsWindow(m_toolBar.m_hWnd) || !m_reBar.GetReBarCtrl().GetBandCount())
		return;

	// put toolbar at top, list below it
	CRect r;
	r.SetRect(0, 0, cx, m_barHeight);
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 1,
		reposDefault, NULL, &r);
	// RepositionBars won't leave space between the rebar and the content
	// so we need to re-move the content to make space needed for the border
	GetContentWnd()->MoveWindow(0, m_contentTop,	cx, cy - m_contentTop, TRUE);
}

void CP4PaneView::OnDraw(CDC* pDC) 
{
	// just draw an edget between the toolbar and the content
	CRect r;
	GetClientRect(&r);
	r.bottom = r.top + m_contentTop;
	pDC->DrawEdge(&r, EDGE_RAISED, BF_RECT);
}

void CP4PaneView::OnSetFocus(CWnd* pOldWnd)
{
	CView::OnSetFocus(pOldWnd);

	if(GetContentWnd() && IsWindow(GetContentWnd()->m_hWnd))
		GetContentWnd()->SetFocus();
}

BOOL CP4PaneView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
	AFX_CMDHANDLERINFO* pHandlerInfo)
{
	if(GetContentWnd()->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
		return TRUE;

	return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

BOOL CP4PaneView::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
	// first, check for special case of caption control
	// and handle it here
    TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;    
    UINT nID =pNMHDR->idFrom;
    if (pTTT->uFlags & TTF_IDISHWND)    
    {
        // idFrom is actually the HWND of the tool
        nID = ::GetDlgCtrlID((HWND)nID);
		if(nID == ID_CAPTION)
		{
			m_captionCtrl.GetWindowText(m_captionTxt);
			pTTT->lpszText = (LPTSTR)(LPCTSTR)m_captionTxt;
            pTTT->hinst = NULL;
			return TRUE;
		}
	}
	// for other cases, just pass it along to mainframe
	CMainFrame *mainFrame = (CMainFrame *) AfxGetApp()->m_pMainWnd;
    return mainFrame->OnToolTipText(id, pNMHDR, pResult);
}

int CP4PaneView::GetSelectedItem()
{
	CP4ListCtrl * pList = DYNAMIC_DOWNCAST(CP4ListCtrl, GetContentWnd());
	ASSERT(pList);
	if(pList)
		return pList->GetSelectedItem();
	return 0;
}

void CP4PaneView::OnEditPaste( const CString &Name )
{
	CP4ListCtrl * pList = DYNAMIC_DOWNCAST(CP4ListCtrl, GetContentWnd());
	ASSERT(pList);
	if(pList)
		pList->OnEditPaste(Name);
}

CString CP4PaneView::GetSelectedItemText( )
{
	CP4ListCtrl * pList = DYNAMIC_DOWNCAST(CP4ListCtrl, GetContentWnd());
	ASSERT(pList);
	if(pList)
		return pList->GetSelectedItemText();
	return "";
}

HTREEITEM CP4PaneView::GetSelectedItem(int index)
{
	CMultiSelTreeCtrl * pTree = DYNAMIC_DOWNCAST(CMultiSelTreeCtrl, GetContentWnd());
	ASSERT(pTree);
	if(pTree)
		return pTree->GetSelectedItem(index);
	return 0;
}

CString CP4PaneView::GetItemText(HTREEITEM curr_item)
{
	CMultiSelTreeCtrl * pTree = DYNAMIC_DOWNCAST(CMultiSelTreeCtrl, GetContentWnd());
	ASSERT(pTree);
	if(pTree)
		return pTree->GetItemText(curr_item);
	return "";
}

void CP4PaneView::SetCaption()
{
	m_captionCtrl.SetWindowText(m_content->GetCaption());

	// Getting this updated is a bit complicated since the caption
	// control is transparent (to allow the rebar background to
	// show through).  First, redraw the rebar, then the caption control.
	m_reBar.InvalidateRect(NULL);
	m_reBar.UpdateWindow();
	m_captionCtrl.InvalidateRect(NULL);
	m_captionCtrl.UpdateWindow();
}

int CP4PaneView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	if(!m_reBar.Create(this))
		return -1;
	if(!m_toolBar.Create(this,WS_CHILD | CBRS_ALIGN_TOP))
		return -1;
	if(!m_captionCtrl.Create(_T(""),WS_CHILD|WS_VISIBLE|BS_OWNERDRAW|BS_NOTIFY,
		CRect(0,0,1,1),this,ID_CAPTION))
		return -1;
	if(!CreateContent())
		return -1;

	LOGFONT lf;
	m_reBar.GetFont()->GetLogFont(&lf);
	m_inactiveFont.CreateFontIndirect(&lf);
	lf.lfWeight = FW_BOLD;
	m_activeFont.CreateFontIndirect(&lf);

	// Register that we accept Exploder files
	try
	{
		m_DropTarget.Register(this);
	}
	catch(...)
	{
		return 0;
	}

	return 0;
}

void CP4PaneView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
	if(GetContentWnd() && IsWindow(GetContentWnd()->m_hWnd))
	{
		// force UI update right away, so if activation is caused by
		// click on toolbar dropdown, it can be enabled before the
		// toolbar gets the click message.  Without this, it takes
		// one click to activate and another to drop down.
		m_toolBar.SendMessage(WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0);

		if(bActivate)
		{
			m_captionCtrl.SetFont(&m_activeFont);
		}
		else
		{
			m_captionCtrl.SetFont(&m_inactiveFont);
		}
		// The size of the caption text will change with the font change
		// so force an update
		SetCaption();
	}

	CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}

DROPEFFECT CP4PaneView::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
	MapWindowPoints(GetContentWnd(), &point, 1);
	return m_content->OnDragEnter(pDataObject, dwKeyState, point);
}

void CP4PaneView::OnDragLeave()
{
	m_content->OnDragLeave();
}

DROPEFFECT CP4PaneView::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
	MapWindowPoints(GetContentWnd(), &point, 1);
	return m_content->OnDragOver(pDataObject, dwKeyState, point);
}

BOOL CP4PaneView::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
	MapWindowPoints(GetContentWnd(), &point, 1);
	return m_content->OnDrop(pDataObject, dropEffect, point);
}

void CP4PaneView::SwapButtonPosition()
{
	// swap the bands
	m_reBar.GetReBarCtrl().MoveBand(1, 0);
	// for some inexplicable reason, MoveBand doesn't update the bar
	// immediately.  It takes some other action to make it actually update
	// and this seems like the least disruptive thing to do:
	m_reBar.GetReBarCtrl().ShowBand(0, FALSE);
	m_reBar.GetReBarCtrl().ShowBand(0, TRUE);
}

// This is required in order to make the LButtonDblClk() work!
void CP4PaneView::LButtonClk()
{
	SetFocus();
}

void CP4PaneView::LButtonDblClk()
{
	// by default, do nothing
}

LRESULT CP4PaneView::OnFindPattern(WPARAM wParam, LPARAM lParam)
{
	CP4ListCtrl * pList = DYNAMIC_DOWNCAST(CP4ListCtrl, GetContentWnd());
	ASSERT(pList);
	if(pList)
		return pList->OnFindPattern(wParam, lParam);
	return Default();
}

IMPLEMENT_DYNCREATE(CCaptionTextControl, CButton)

BEGIN_MESSAGE_MAP(CCaptionTextControl, CButton)
	ON_WM_CTLCOLOR_REFLECT()
END_MESSAGE_MAP()

HBRUSH CCaptionTextControl::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
	// we don't want any background drawn; we're supposed to be transparent
	pDC->SetBkMode(TRANSPARENT);
	return (HBRUSH)GetStockObject(NULL_BRUSH);
}

void CCaptionTextControl::DrawItem(LPDRAWITEMSTRUCT di) 
{
	// Just draw the text.  If it's too long, it will be shortened, since
	// we're using the DT_END_ELLIPSIS style.  This also means the string
	// passed to DrawText may be modified, so we use a temp copy.
	CDC dc;
	dc.Attach(di->hDC);
	const int maxCaption = 256;
	TCHAR txt[maxCaption];
	GetWindowText(txt,maxCaption);
	txt[maxCaption-1] = 0;
	TCHAR mtxt[maxCaption];
	lstrcpy(mtxt,txt);
	dc.DrawText(mtxt, lstrlen(mtxt), &di->rcItem, DT_END_ELLIPSIS | DT_SINGLELINE|DT_VCENTER | DT_MODIFYSTRING);
	m_textTruncated = lstrcmp(mtxt,txt) != 0;
	dc.Detach();
}

int CCaptionTextControl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
	// CWnd implementation won't do us any good, so we have to handle
	// this.
	// watch out!  sometimes get called with null pointer
	if(pTI == 0)
		return -1;

	// don't want to show tooltip if nothing is hidden
	// the tooltip is only there to show truncated text
	if(!m_textTruncated)
		return -1;

	// we want to show a tooltip, so set up for the normal callback
	pTI->hwnd = GetParent()->GetParent()->m_hWnd;	// our parent
	pTI->uId = (UINT_PTR)m_hWnd;		// us
	pTI->uFlags |= TTF_IDISHWND;		// uId is HWND, not ID
	pTI->lpszText = LPSTR_TEXTCALLBACK;

	return 1;
}

DROPEFFECT CP4OleDropTarget::OnDragScroll(CWnd* pWnd, DWORD dwKeyState,
	CPoint point)
{
	// We need special handling of autoscrolling because we want
	// it to behave as though the CP4PaneView's content window is
	// doing the autoscroll.  Without this, it looks like you are
	// dragging over the caption to autoscroll the content.

	// This code is only slightly modified from the base implementation,
	// as needed to get the correct window's client rect for determining
	// the autoscroll inset region.  Also, code for synchronized
	// scrolling of splitters has been stripped out.

	ASSERT_VALID(this);
	ASSERT_VALID(pWnd);

	// if it's not a CP4PaneView, just use the default handler
	CP4PaneView * pView = DYNAMIC_DOWNCAST(CP4PaneView,pWnd);
	if(!pView)
		return COleDropTarget::OnDragScroll(pWnd, dwKeyState, point);

	DROPEFFECT dropEffect = pView->OnDragScroll(dwKeyState, point);

	// DROPEFFECT_SCROLL means do the default
	if (dropEffect != DROPEFFECT_SCROLL)
		return dropEffect;

	// get client rectangle of destination window
	CRect rectClient;
	pView->GetContentWnd()->GetClientRect(&rectClient);
	pView->GetContentWnd()->MapWindowPoints(pView, &rectClient);
	CRect rect = rectClient;

	// hit-test against inset region
	UINT nTimerID = 0xffff;
	rect.InflateRect(-nScrollInset, -nScrollInset);
	if (rectClient.PtInRect(point) && !rect.PtInRect(point))
	{
		// determine which way to scroll along both X & Y axis
		if (point.x < rect.left)
			nTimerID = MAKEWORD(SB_LINEUP, HIBYTE(nTimerID));
		else if (point.x >= rect.right)
			nTimerID = MAKEWORD(SB_LINEDOWN, HIBYTE(nTimerID));
		if (point.y < rect.top)
			nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEUP);
		else if (point.y >= rect.bottom)
			nTimerID = MAKEWORD(LOBYTE(nTimerID), SB_LINEDOWN);
		ASSERT(nTimerID != 0xffff);

		// we don't do synchronized splitter scrolling, so this part is
		// somewhat simplified
		BOOL bEnableScroll = pView->OnScroll(nTimerID, 0, FALSE);
		if (!bEnableScroll)
			nTimerID = 0xffff;
	}

	if (nTimerID == 0xffff)
	{
		if (m_nTimerID != 0xffff)
		{
			// send fake OnDragEnter when transition from scroll->normal
			COleDataObject dataObject;
			dataObject.Attach(m_lpDataObject, FALSE);
			OnDragEnter(pWnd, &dataObject, dwKeyState, point);
			m_nTimerID = 0xffff;
		}
		return DROPEFFECT_NONE;
	}

	// save tick count when timer ID changes
	DWORD dwTick = GetTickCount();
	if (nTimerID != m_nTimerID)
	{
		m_dwLastTick = dwTick;
		m_nScrollDelay = nScrollDelay;
	}

	// scroll if necessary
	if (dwTick - m_dwLastTick > m_nScrollDelay)
	{
		pView->OnScroll(nTimerID, 0, TRUE);
		m_dwLastTick = dwTick;
		m_nScrollDelay = nScrollInterval;
	}
	if (m_nTimerID == 0xffff)
	{
		// send fake OnDragLeave when transitioning from normal->scroll
		OnDragLeave(pWnd);
	}

	m_nTimerID = nTimerID;
	// check for force link
	if ((dwKeyState & (MK_CONTROL|MK_SHIFT)) == (MK_CONTROL|MK_SHIFT))
		dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_LINK;
	// check for force copy
	else if ((dwKeyState & MK_CONTROL) == MK_CONTROL)
		dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_COPY;
	// check for force move
	else if ((dwKeyState & MK_ALT) == MK_ALT ||
		(dwKeyState & MK_SHIFT) == MK_SHIFT)
		dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;
	// default -- recommended action is move
	else
		dropEffect = DROPEFFECT_SCROLL|DROPEFFECT_MOVE;
	return dropEffect;
}

void CP4PaneView::OnSysColorChange()
{
	CView::OnSysColorChange();

	m_toolBar.GetToolBarCtrl().SetImageList(
		TheApp()->GetToolBarImageList());
	m_toolBar.GetToolBarCtrl().SetDisabledImageList(
		TheApp()->GetToolBarImageList()->GetDisabled());

	CP4ListCtrl * pList = DYNAMIC_DOWNCAST(CP4ListCtrl, GetContentWnd());
	if(pList)
		pList->SetImageList(TheApp()->GetViewImageList(), LVSIL_SMALL);
	CMultiSelTreeCtrl * pTree = DYNAMIC_DOWNCAST(CMultiSelTreeCtrl, GetContentWnd());
	if(pTree)
		pTree->SetImageList(TheApp()->GetViewImageList(), TVSIL_NORMAL);
}

LRESULT CP4PaneView::OnActivateModeless(WPARAM wParam, LPARAM lParam)
{
	CP4ListCtrl * pList = DYNAMIC_DOWNCAST(CP4ListCtrl, GetContentWnd());
	if(pList)
		pList->SendMessage(WM_ACTIVATEMODELESS, WA_ACTIVE, NULL);
	else
	{
		CMultiSelTreeCtrl * pTree = DYNAMIC_DOWNCAST(CMultiSelTreeCtrl, GetContentWnd());
		if(pTree)
			pTree->SendMessage(WM_ACTIVATEMODELESS, WA_ACTIVE, NULL);
	}
	return 0;
}