/*
 * common - Common user interface services
 */

#include "tweakui.h"

/*****************************************************************************
 *
 *  Common_SetDirty
 *
 *	Make a property sheet dirty.
 *
 *****************************************************************************/

void NEAR PASCAL
Common_SetDirty(HWND hdlg)
{
    PropSheet_Changed(GetParent(hdlg), hdlg);
}

/*****************************************************************************
 *
 *  Common_NeedLogoff
 *
 *	Indicate that you need to log off for the changes to take effect.
 *
 *****************************************************************************/

void NEAR PASCAL
Common_NeedLogoff(HWND hdlg)
{
    PropSheet_RestartWindows(GetParent(hdlg));
}

/*****************************************************************************
 *
 *  Common_OnHelp
 *
 *	Respond to the F1 key.
 *
 *****************************************************************************/

void PASCAL
Common_OnHelp(LPARAM lp, const DWORD CODESEG *pdwHelp)
{
    WinHelp((HWND) ((LPHELPINFO) lp)->hItemHandle, c_tszMyHelp,
            HELP_WM_HELP, (DWORD_PTR) pdwHelp);
}

/*****************************************************************************
 *
 *  Common_OnContextMenu
 *
 *	Respond to a right-click.
 *
 *****************************************************************************/

void PASCAL
Common_OnContextMenu(WPARAM wp, const DWORD CODESEG *pdwHelp)
{
    WinHelp((HWND) wp, c_tszMyHelp, HELP_CONTEXTMENU, (DWORD_PTR) pdwHelp);
}

/*****************************************************************************
 *
 *  Common_OnLvContextMenu
 *
 *	The context menu shall appear in the listview.
 *
 *	Have the callback generate the menu, then pop it up and let the
 *	window procedure's WM_COMMAND do the rest.
 *
 *****************************************************************************/

void PASCAL
Common_OnLvContextMenu(HWND hwnd, POINT pt, CMCALLBACK pfn)
{
    int iItem = Misc_LV_GetCurSel(hwnd);
    if (iItem != -1) {
	ClientToScreen(hwnd, &pt);	/* Make it screen coordinates */
	TrackPopupMenuEx(pfn(hwnd, iItem),
			 TPM_RIGHTBUTTON | TPM_VERTICAL |
			 TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y,
			 GetParent(hwnd), 0);
    }
}

/*****************************************************************************
 *
 *  Common_LV_OnContextMenu
 *
 *	If the context menu came from the listview, figure out which
 *	item got clicked on.  If we find an item, pop up its context
 *	menu.  Otherwise, just do the standard help thing.
 *
 *	NOTE!  We don't use LVHT_ONITEM because ListView is broken!
 *	Watch:
 *
 *	    #define LVHT_ONITEMSTATEICON 0x0008
 *	    #define LVHT_ABOVE          0x0008
 *
 *	Oops.  This means that clicks above the item are treated as
 *	clicks on the state icon.
 *
 *	Fortunately, we reside completely in report view, so you can't
 *	legally click above the item.  The only way it can happen is
 *	if the coordinates to OnContextMenu are out of range, so we
 *	catch that up front and munge it accordingly.
 *
 *****************************************************************************/

void PASCAL
Common_LV_OnContextMenu(HWND hdlg, HWND hwnd, LPARAM lp,
		        CMCALLBACK pfn, const DWORD CODESEG *pdwHelp)
{
    if (GetDlgCtrlID(hwnd) == IDC_LISTVIEW) {
	LV_HITTESTINFO hti;
	if (lp == (LPARAM)0xFFFFFFFF) {
	    /* Pretend it was on the center of the small icon */
	    ListView_GetItemPosition(hwnd, Misc_LV_GetCurSel(hwnd),
				     &hti.pt);
	    hti.pt.x += GetSystemMetrics(SM_CXSMICON) / 2;
	    hti.pt.y += GetSystemMetrics(SM_CYSMICON) / 2;
	    Common_OnLvContextMenu(hwnd, hti.pt, pfn);
	} else {
	    Misc_LV_HitTest(hwnd, &hti, lp);
	    if ((hti.flags & LVHT_ONITEM)) {
		/* Because LV sometimes forgets to move the focus... */
		Misc_LV_SetCurSel(hwnd, hti.iItem);
		Common_OnLvContextMenu(hwnd, hti.pt, pfn);
	    } else {
		Common_OnContextMenu((WPARAM)hwnd, pdwHelp);
	    }
	}
    } else {
	Common_OnContextMenu((WPARAM)hwnd, pdwHelp);
    }
}


/*****************************************************************************
 *
 *  Common_OnLvCommand
 *
 *	Handle a WM_COMMAND that might be for the listview.
 *
 *****************************************************************************/

void PASCAL
Common_OnLvCommand(HWND hdlg, int idCmd, PLVCI rglvci, int clvci)
{
    do {
	if (idCmd == rglvci[0].id) {
	    HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
	    int iItem = Misc_LV_GetCurSel(hwnd);
	    if (iItem != -1) {
		rglvci[0].pfn(hwnd, iItem);
	    }
	    break;
	}
	rglvci++;
    } while (--clvci);
}

/*****************************************************************************
 *
 *  EnableDlgItem
 *
 *	A simple wrapper.
 *
 *****************************************************************************/

void NEAR PASCAL
EnableDlgItem(HWND hdlg, UINT idc, BOOL f)
{
    EnableWindow(GetDlgItem(hdlg, idc), f);
}

/*****************************************************************************
 *
 *  EnableDlgItems
 *
 *****************************************************************************/

void NEAR PASCAL
EnableDlgItems(HWND hdlg, UINT idcFirst, UINT idcLast, BOOL f)
{
    for (; idcFirst <= idcLast; idcFirst++) {
        EnableWindow(GetDlgItem(hdlg, idcFirst), f);
    }
}

/*****************************************************************************
 *
 *  AdjustDlgItems
 *
 *****************************************************************************/

void NEAR PASCAL
AdjustDlgItems(HWND hdlg, UINT idcFirst, UINT idcLast, UINT adi)
{
    for (UINT idc = idcFirst; idc <= idcLast; idc++) {
        HWND hwnd = GetDlgItem(hdlg, idc);
        if (adi & (ADI_DISABLE | ADI_ENABLE)) {
            EnableWindow(hwnd, (adi & ADI_ENABLE) ? TRUE : FALSE);
        }
        if (adi & (ADI_HIDE | ADI_SHOW)) {
            ShowWindow(hwnd, (adi & ADI_SHOW) ? SW_SHOW : SW_HIDE);
        }
    }
}

/*****************************************************************************
 *
 *  SetDlgItemTextLimit
 *
 *	Set the dialog item text as well as limiting its size.
 *
 *****************************************************************************/

void NEAR PASCAL
SetDlgItemTextLimit(HWND hdlg, UINT id, LPCTSTR ptsz, UINT ctch)
{
    HWND hwnd = GetDlgItem(hdlg, id);
    SetWindowText(hwnd, ptsz);
    Edit_LimitText(hwnd, ctch - 1);
}

/*****************************************************************************
 *
 *  DestroyDlgItems
 *
 *****************************************************************************/

void NEAR PASCAL
DestroyDlgItems(HWND hdlg, UINT idcFirst, UINT idcLast)
{
    UINT idc;
    for (idc = idcFirst; idc <= idcLast; idc++) {
        DestroyWindow(GetDlgItem(hdlg, idc));
    }
}

/*****************************************************************************
 *
 *  GetDlgItemRect
 *
 *      Get the rect of a dialog item in client coordinates.
 *
 *****************************************************************************/

void PASCAL
GetDlgItemRect(HWND hdlg, UINT idc, LPRECT prc)
{
    GetWindowRect(GetDlgItem(hdlg, idc), prc);
    MapWindowRect(NULL, hdlg, prc);
}

/*****************************************************************************
 *
 *  MoveDlgItems
 *
 *****************************************************************************/

void PASCAL
MoveDlgItems(HWND hdlg, UINT idcTarget, UINT idcFirst, UINT idcLast)
{
    RECT rcFrom, rcTo;

    GetDlgItemRect(hdlg, idcTarget, &rcTo);
    GetDlgItemRect(hdlg, idcFirst, &rcFrom);
    int dy = rcTo.top - rcFrom.top;
    HDWP hdwp = BeginDeferWindowPos(idcLast - idcFirst + 1);
    for (UINT idc = idcFirst; idc <= idcLast; idc++) {
        GetDlgItemRect(hdlg, idc, &rcFrom);
        HWND hwnd = GetDlgItem(hdlg, idc);
        hdwp = DeferWindowPos(hdwp, hwnd, NULL,
                              rcFrom.left, rcFrom.top + dy, 0, 0,
                              SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
    }
    EndDeferWindowPos(hdwp);
}
