/**************************************************************************** * @doc INTERNAL PROPEDIT * * @module PropEdit.cpp | Source file for the * class used to implement behavior of a single property to be displayed * in a property page. ***************************************************************************/ #include "Precomp.h" /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc void | CPropertyEditor | CPropertyEditor | This * method is the constructor for property objects. * * @parm HWND | hDlg | Specifies a handle to the parent property page. * * @parm ULONG | IDLabel | Specifies a label ID for the property. * * @parm ULONG | IDMinControl | Specifies a label ID for the associated * property edit control where the Minimum value of the property appears. * * @parm ULONG | IDMaxControl | Specifies a label ID for the associated * property edit control where the Maximum value of the property appears. * * @parm ULONG | IDDefaultControl | Specifies a label ID for the associated * property edit control where the Default value of the property appears. * * @parm ULONG | IDStepControl | Specifies a label ID for the associated * property edit control where the Stepping Delta value of the property appears. * * @parm ULONG | IDEditControl | Specifies a label ID for the associated * property edit control where the value of the property appears. * * @parm ULONG | IDTrackbarControl | Specifies a label ID for the associated * property slide bar. * * @parm ULONG | IDProgressControl | Specifies a label ID for the associated * progress bar. * * @parm ULONG | IDProperty | Specifies the ID of the property. * * @rdesc Nada. ***************************************************************************/ CPropertyEditor::CPropertyEditor(HWND hDlg, ULONG IDLabel, ULONG IDMinControl, ULONG IDMaxControl, ULONG IDDefaultControl, ULONG IDStepControl, ULONG IDEditControl, ULONG IDTrackbarControl, ULONG IDProgressControl, ULONG IDProperty, ULONG IDAutoControl) : m_hDlg (hDlg), m_hWndMin (NULL), m_hWndMax (NULL), m_hWndDefault (NULL), m_hWndStep (NULL), m_hWndEdit (NULL), m_hWndTrackbar (NULL), m_hWndProgress (NULL), m_IDLabel (IDLabel), m_hWndAuto (NULL), m_IDAutoControl (IDAutoControl) , m_IDMinControl (IDMinControl), m_IDMaxControl (IDMaxControl), m_IDDefaultControl (IDDefaultControl), m_IDStepControl (IDStepControl), m_IDTrackbarControl (IDTrackbarControl), m_IDProgressControl (IDProgressControl) , m_IDEditControl (IDEditControl), m_IDProperty (IDProperty), m_Active (FALSE), m_Min (0), m_Max (0), m_DefaultValue (0), m_DefaultFlags (0), m_SteppingDelta (0), m_CurrentValue (0), m_TrackbarOffset (0), m_ProgressOffset (0), m_fCheckBox (0) , m_CurrentFlags (0), m_CanAutoControl (FALSE) { FX_ENTRY("CPropertyEditor::CPropertyEditor") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | Init | This initializes the controls. * * @rdesc TRUE on success, FALSE otherwise. ***************************************************************************/ BOOL CPropertyEditor::Init() { HRESULT Hr = NOERROR; BOOL fRes = TRUE; FX_ENTRY("CPropertyEditor::Init") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); // For now disable all controls, and re-enable only the ones that make sense // at the end of this initialization function // Those GetDlgItem calls 'd better not fail ;) if (m_IDLabel) EnableWindow(GetDlgItem(m_hDlg, m_IDLabel), FALSE); if (m_IDMinControl) EnableWindow(m_hWndMin = GetDlgItem(m_hDlg, m_IDMinControl), FALSE); if (m_IDMaxControl) EnableWindow(m_hWndMax = GetDlgItem(m_hDlg, m_IDMaxControl), FALSE); if (m_IDDefaultControl) EnableWindow(m_hWndDefault = GetDlgItem(m_hDlg, m_IDDefaultControl), FALSE); if (m_IDStepControl) EnableWindow(m_hWndStep = GetDlgItem(m_hDlg, m_IDStepControl), FALSE); if (m_IDEditControl) EnableWindow(m_hWndEdit = GetDlgItem(m_hDlg, m_IDEditControl), FALSE); if (m_IDTrackbarControl) EnableWindow(m_hWndTrackbar = GetDlgItem(m_hDlg, m_IDTrackbarControl), FALSE); if (m_IDProgressControl) EnableWindow(m_hWndProgress = GetDlgItem(m_hDlg, m_IDProgressControl), FALSE); if (m_IDAutoControl) EnableWindow(m_hWndAuto = GetDlgItem(m_hDlg, m_IDAutoControl), FALSE); // Default values if the property isn't supported m_CurrentFlags = TAPIControl_Flags_Manual; m_CurrentValue = 0L; // Only enable the control if we can read the current value if (FAILED(Hr = GetValue())) { fRes = FALSE; goto MyExit; } // Save original value in case user clicks Cancel m_OriginalValue = m_CurrentValue; m_OriginalFlags = m_CurrentFlags; // Get the range, stepping, default, and capabilities if (FAILED(Hr = GetRange())) { // Special case, if no trackbar and no edit box, treat the // autocheck box as a boolean to control the property if (m_hWndTrackbar || m_hWndEdit || m_hWndProgress) { fRes = FALSE; goto MyExit; } } else { if (m_Min > m_Max || m_CurrentValue > m_Max || m_CurrentValue < m_Min || m_DefaultValue > m_Max || m_DefaultValue < m_Min) { DbgLog((LOG_ERROR, DBG_LEVEL_TRACE_FAILURES, TEXT("%s: ERROR: Invalid range or current value"), _fx_)); fRes = FALSE; goto MyExit; } if (m_Min == 0 && m_Max == 1 && m_SteppingDelta == 1) m_fCheckBox = TRUE; } // We're ready to rock & roll m_Active = TRUE; // Re-enable appropriate controls if (m_IDLabel) { EnableWindow(GetDlgItem(m_hDlg, m_IDLabel), TRUE); } if (m_hWndMin) { SetDlgItemInt(m_hDlg, m_IDMinControl, m_Min, TRUE); EnableWindow(m_hWndMin, TRUE); } if (m_hWndMax) { SetDlgItemInt(m_hDlg, m_IDMaxControl, m_Max, TRUE); EnableWindow(m_hWndMax, TRUE); } if (m_hWndDefault) { SetDlgItemInt(m_hDlg, m_IDDefaultControl, m_DefaultValue, TRUE); EnableWindow(m_hWndDefault, TRUE); } if (m_hWndStep) { SetDlgItemInt(m_hDlg, m_IDStepControl, m_SteppingDelta, TRUE); EnableWindow(m_hWndStep, TRUE); } if (m_hWndEdit) { UpdateEditBox(); EnableWindow(m_hWndEdit, TRUE); } if (m_hWndTrackbar) { EnableWindow(m_hWndTrackbar, TRUE); // Trackbars don't handle negative values, so slide everything positive if (m_Min < 0) m_TrackbarOffset = -m_Min; SendMessage(m_hWndTrackbar, TBM_SETRANGEMAX, FALSE, m_Max + m_TrackbarOffset); SendMessage(m_hWndTrackbar, TBM_SETRANGEMIN, FALSE, m_Min + m_TrackbarOffset); // Have fun with the keyboards Page Up, Page Down, and arrows SendMessage(m_hWndTrackbar, TBM_SETLINESIZE, FALSE, (LPARAM) m_SteppingDelta); SendMessage(m_hWndTrackbar, TBM_SETPAGESIZE, FALSE, (LPARAM) m_SteppingDelta); UpdateTrackbar(); } if (m_hWndProgress) { EnableWindow(m_hWndProgress, TRUE); // Progress controls don't handle negative values, so slide everything positive if (m_Min < 0) m_ProgressOffset = -m_Min; SendMessage(m_hWndProgress, PBM_SETRANGE32, m_Min + m_ProgressOffset, m_Max + m_ProgressOffset); UpdateProgress(); // Set a timer to update the progress regularly SetTimer(m_hDlg, 123456, 250, NULL); } if (m_hWndAuto) { // If the control has an auto setting, enable the auto checkbox m_CanAutoControl = CanAutoControl(); EnableWindow (m_hWndAuto, m_CanAutoControl); if (m_CanAutoControl) { Button_SetCheck (m_hWndAuto, GetAuto ()); } } MyExit: DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return fRes; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc void | CPropertyEditor | ~CPropertyEditor | Destructor for this class. * * @rdesc Nada. ***************************************************************************/ CPropertyEditor::~CPropertyEditor() { FX_ENTRY("CPropertyEditor::~CPropertyEditor") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); // Kill timer if we have a progress bar if (m_hWndProgress) KillTimer(m_hDlg, 123456); DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc void | CPropertyEditor | OnApply | This member function is * called by the framework when the user chooses the OK or the Apply Now * button. When the framework calls this member function, changes made on * all property pages in the property sheet are accepted, the property * sheet retains focus. * * @rdesc Returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::OnApply() { int nCurrentValue; FX_ENTRY("CPropertyEditor::OnApply") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); // Make sure the value is a multiple of the stepping delta if (m_SteppingDelta) { nCurrentValue = m_CurrentValue; m_CurrentValue = m_CurrentValue / m_SteppingDelta * m_SteppingDelta; if (m_CurrentValue != nCurrentValue) { UpdateEditBox(); UpdateTrackbar(); } } // Backup current value in order to only apply changes if something has really changed m_OriginalValue = m_CurrentValue; m_OriginalFlags = m_CurrentFlags; // Set the value on the device SetValue(); DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return TRUE; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc void | CPropertyEditor | HasChanged | This member tests for a * change in value. * * @rdesc Returns TRUE if value has changed. ***************************************************************************/ BOOL CPropertyEditor::HasChanged() { FX_ENTRY("CPropertyEditor::HasChanged") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return (m_CurrentValue != m_OriginalValue); } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | OnDefault | Resets the position of the * slide bar and updates the content of the Target windows after the user * pressed the Default button. * * @rdesc Returns TRUE if Active, FALSE otherwise. ***************************************************************************/ BOOL CPropertyEditor::OnDefault() { BOOL fRes = TRUE; FX_ENTRY("CPropertyEditor::OnDefault") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); if (!m_Active) { DbgLog((LOG_ERROR, DBG_LEVEL_TRACE_FAILURES, TEXT("%s: WARNING: Control not active yet!"), _fx_)); fRes = FALSE; goto MyExit; } // Backup value in case user goes for the Cancel button m_CurrentValue = m_DefaultValue; m_CurrentFlags = m_DefaultFlags; // Update appropriate controls UpdateEditBox(); UpdateTrackbar(); UpdateAuto(); MyExit: DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return fRes; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | OnScroll | Reads the position of the * slide bar and updates the content of the Target windows after the user * has messed with the slide bar. * * @rdesc Returns TRUE if Active, FALSE otherwise. ***************************************************************************/ BOOL CPropertyEditor::OnScroll(ULONG nCommand, WPARAM wParam, LPARAM lParam) { int pos; int command = LOWORD(wParam); BOOL fRes = TRUE; FX_ENTRY("CPropertyEditor::OnScroll") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); // Validate input params if (command != TB_ENDTRACK && command != TB_THUMBTRACK && command != TB_LINEDOWN && command != TB_LINEUP && command != TB_PAGEUP && command != TB_PAGEDOWN) { DbgLog((LOG_ERROR, DBG_LEVEL_TRACE_FAILURES, TEXT("%s: ERROR: Invalid input parameter!"), _fx_)); fRes = FALSE; goto MyExit; } if (!m_Active) { DbgLog((LOG_ERROR, DBG_LEVEL_TRACE_FAILURES, TEXT("%s: WARNING: Control not active yet!"), _fx_)); fRes = FALSE; goto MyExit; } // Retrieve position in slide bar pos = (int)SendMessage((HWND) lParam, TBM_GETPOS, 0, 0L); // Make sure the value is a multiple of the stepping delta if (m_SteppingDelta) m_CurrentValue = (pos - m_TrackbarOffset - m_Min) / m_SteppingDelta * m_SteppingDelta + m_Min; else m_CurrentValue = pos - m_TrackbarOffset; // Sync edit box to the slide bar UpdateEditBox(); MyExit: DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return fRes; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | OnEdit | Reads the content of the * Target window and updates the postion of the slider after the user * has messed with the Target edit control. * * @rdesc Returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::OnEdit(ULONG nCommand, WPARAM wParam, LPARAM lParam) { BOOL fTranslated; int nCurrentValue; FX_ENTRY("CPropertyEditor::OnEdit") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); // We get called even before init has been done -> test for m_Active if (m_Active) { if (!m_fCheckBox) { // Read the value from the control if (m_hWndEdit) nCurrentValue = GetDlgItemInt(m_hDlg, m_IDEditControl, &fTranslated, TRUE); // Is the value garbage? if (fTranslated) { if (nCurrentValue > m_Max) { // The value is already large than its max -> clamp it and update the control m_CurrentValue = m_Max; UpdateEditBox(); } else if (nCurrentValue < m_Min) { // The value is already smaller than its min -> clamp it and update the control m_CurrentValue = m_Min; UpdateEditBox(); } else m_CurrentValue = nCurrentValue; } else { // It's garbage -> Reset the control to its minimum value m_CurrentValue = m_Min; UpdateEditBox(); } // Sync slide bar to edit box UpdateTrackbar(); } else { // Read the value from the control if (m_hWndEdit) m_CurrentValue = Button_GetCheck(m_hWndEdit); } } DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return TRUE; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | OnAuto | Gets the status of the * checkbox. * * @rdesc Returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::OnAuto(ULONG nCommand, WPARAM wParam, LPARAM lParam) { SetAuto(Button_GetCheck(m_hWndAuto)); return TRUE; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc HWND | CPropertyEditor | GetTrackbarHWnd | Helper method to allow * the property page code to access the slide bar window (private member) of * a property. * * @rdesc Returns a handle to the slide bar window. ***************************************************************************/ HWND CPropertyEditor::GetTrackbarHWnd() { return m_hWndTrackbar; }; /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc HWND | CPropertyEditor | GetProgressHWnd | Helper method to allow * the property page code to access the progress bar window (private member) of * a property. * * @rdesc Returns a handle to the progress window. ***************************************************************************/ HWND CPropertyEditor::GetProgressHWnd() { return m_hWndProgress; }; /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc HWND | CPropertyEditor | GetEditHWnd | Helper method to allow * the property page code to access the Target window (private member) of * a property. * * @rdesc Returns a handle to the Target window. ***************************************************************************/ HWND CPropertyEditor::GetEditHWnd() { return m_hWndEdit; }; /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc HWND | CPropertyEditor | GetAutoHWnd | Helper method to allow * the property page code to access the auto window (private member) of * a property. * * @rdesc Returns a handle to the auto window. ***************************************************************************/ HWND CPropertyEditor::GetAutoHWnd() { return m_hWndAuto; }; /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | UpdateEditBox | Updates the content of * the Target window after user has moved the slide bar. * * @rdesc Returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::UpdateEditBox() { if (m_hWndEdit) { if (!m_fCheckBox) SetDlgItemInt(m_hDlg, m_IDEditControl, m_CurrentValue, TRUE); else Button_SetCheck(m_hWndEdit, m_CurrentValue); } return TRUE; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | UpdateTrackbar | Updates the position of * the slide bar after user has messed with the Target window. * * @rdesc Returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::UpdateTrackbar() { if (m_hWndTrackbar) SendMessage(m_hWndTrackbar, TBM_SETPOS, TRUE, (LPARAM) m_CurrentValue + m_TrackbarOffset); return TRUE; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | UpdateProgress | Updates the position of * the progress bar. * * @rdesc Returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::UpdateProgress() { // Get current value from the device GetValue(); if (m_hWndProgress) SendMessage(m_hWndProgress, PBM_SETPOS, (WPARAM) m_CurrentValue + m_ProgressOffset, 0); UpdateEditBox(); return TRUE; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc BOOL | CPropertyEditor | UpdateAuto | Updates the auto checkbox * * @rdesc Returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::UpdateAuto() { if (m_hWndAuto && CanAutoControl()) { m_CanAutoControl = GetAuto(); } return TRUE; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc HRESULT | CPropertyEditor | CanAutoControl | This method * retrieves the automatic control capabilities for a property. * * @rdesc This method returns TRUE if automatic control is supported, FALSE * otherwise. ***************************************************************************/ BOOL CPropertyEditor::CanAutoControl(void) { FX_ENTRY("CPropertyEditor::CanAutoControl") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return m_CapsFlags & TAPIControl_Flags_Auto; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc HRESULT | CPropertyEditor | GetAuto | This method * retrieves the current automatic control mode of a property. * * @rdesc This method returns TRUE if automatic control is supported, FALSE * otherwise. ***************************************************************************/ BOOL CPropertyEditor::GetAuto(void) { FX_ENTRY("CPropertyEditor::GetAuto") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); GetValue(); DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return m_CurrentFlags & TAPIControl_Flags_Auto; } /**************************************************************************** * @doc INTERNAL CPROPEDITMETHOD * * @mfunc HRESULT | CPropertyEditor | SetAuto | This method * sets the automatic control mode of a property. * * @parm BOOL | fAuto | Specifies the automatic control mode. * * @rdesc This method returns TRUE. ***************************************************************************/ BOOL CPropertyEditor::SetAuto(BOOL fAuto) { FX_ENTRY("CPropertyEditor::SetAuto") DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: begin"), _fx_)); m_CurrentFlags = (fAuto ? TAPIControl_Flags_Auto : (m_CapsFlags & TAPIControl_Flags_Manual) ? TAPIControl_Flags_Manual : TAPIControl_Flags_None); SetValue(); DbgLog((LOG_TRACE, DBG_LEVEL_TRACE_DETAILS, TEXT("%s: end"), _fx_)); return TRUE; }