Click here to Skip to main content
13,260,383 members (44,443 online)
Click here to Skip to main content
Add your own
alternative version

Stats

3.7K views
171 downloads
1 bookmarked
Posted 12 Apr 2017

An AutoRepeat Button Class

, 12 Apr 2017
Rate this:
Please Sign up or sign in to vote.
This is an alternative for "An AutoRepeat Button Class"

Introduction

I am in need of an autorepeat button. A button that when the user presses and holds it down will fire multiple BN_CLICKED notifications. I did a search and found Joseph M. Newcomer's article "An AutoRepeat Button Class". While the code presented in the article worked exactly as advertised it had one major flaw, it did not work when the user pressed the space bar, it only worked with the left mouse button. The comments attached to the article provided some ideas on how to fix it but none were ideal. I took the ideas presented in the article and some of the comments and came up with the code I am sharing here.

Using the code

The best way to get the button control to do what I needed it to do was to subclass the CButton control and override it's OnLButtonDownOnLButtonUpOnKeyDown, and OnKeyUp message handlers. I added four bool member variables that act as flags to control what has  happened and what has to happen next when the control is used. The first two variables are KeyPress and MousePress that  control whether it is the mouse button or the space bar that started the timer. The next one is TimerActive that prevents the timer from being restarted when the key press auto-repeats. The last one is MessageSent that controls whether a BN_CLICK message should be sent when the button is released.

void CAutoRepeatButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (!MousePress)    // Only if not already activated by the mouse
    {
        if (VK_SPACE == nChar && !TimerActive)
        {
            SetTimer(TIMERID, InitialTime, NULL);
            TimerActive = true;
            KeyPress = true;
        }

        CButton::OnKeyDown(nChar, nRepCnt, nFlags);
    }
}

void CAutoRepeatButton::OnLButtonDown(UINT nFlags, CPoint point)
{
    if (!KeyPress)  // Only if not already activated with the space bar
    {
        if (!TimerActive)
        {
            SetTimer(TIMERID, InitialTime, NULL);
            TimerActive = true;
            MousePress = true;
        }

        CButton::OnLButtonDown(nFlags, point);
    }
}

When the button is pressed with either the left mouse button or the space bar we first check that the button is not already activated by the other one. Then we check if the timer is not already started and start it with the initial delay time if it was not. Finally place a call to the base class handler to do the default handler which captures the mouse and draws the button as depressed.

void CAutoRepeatButton::OnTimer(UINT_PTR nIDEvent)
{
    if (nIDEvent == TIMERID)
    {
        if (BST_PUSHED == (BST_PUSHED & GetState()))
        {
            if (!MessageSent)
            {
                SetTimer(TIMERID, RepeatTime, NULL);
                MessageSent = true;
            }
            Parent->SendMessage(WM_COMMAND, MAKELPARAM(GetDlgCtrlID(), BN_CLICKED), (WPARAM)m_hWnd);
        }
    }
    else
    {
        CButton::OnTimer(nIDEvent);
    }
}

When the timer fires we first check the state of the button. If the mouse was used to start the timer and then it is moved off of the button, the button will no longer be pushed so we will not want the BN_CLICKED messages to be sent. But we do want them to continue if the mouse moves back onto the button. After the initial time delay the timer is reset to the repeat time and a BN_CLICKED message is sent.

void CAutoRepeatButton::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (VK_SPACE == nChar && KeyPress)
    {
        KillTimer(TIMERID);
        if (MessageSent)
        {
            ReleaseCapture();   // CButton::OnKeyDown captures the mouse
            SetState(0);        // Redraw button as not pushed
        }
        else
        {
            CButton::OnKeyUp(nChar, nRepCnt, nFlags);
        }
        TimerActive = false;
        KeyPress = false;
        MessageSent = false;
    }
}

void CAutoRepeatButton::OnLButtonUp(UINT nFlags, CPoint point)
{
    if (MousePress)
    {
        KillTimer(TIMERID);
        if (MessageSent)
        {
            ReleaseCapture();
            SetState(0);
        }
        else
        {
            CButton::OnLButtonUp(nFlags, point);
        }
        TimerActive = false;
        MousePress = false;
        MessageSent = false;
    }
} 

When the button is released the timer is stopped. And then, depending on whether the button was held down long enough to generate a BN_CLICKED message we either simply release the mouse capture and draw the button as not pressed or we call the base class handler to fire off a BN_CLICKED message. Then the flags are reset for the next time the button is pressed.

void CAutoRepeatButton::OnLButtonDblClk(UINT nFlags, CPoint point)
{
    OnLButtonDown(nFlags, point);
}

The last thing to handle is if the user double clicks and then holds the button down. I simply call OnLButtonDown to treat the double click as a second single click. If this is not done then the timer is not started.

void CAutoRepeatButton::SetTimes(UINT Initial, UINT Repeat)
{
    InitialTime = Initial;
    RepeatTime = Repeat;
}

Finally, as a convience function, I added the ability to set the initial and repeating delay time interval.

History

April 13, 2017 - published on CodeProject.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

PJ Arends
President
Canada Canada
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionSeeking help with CFileEditCtrl.cpp Pin
Member 1332065220-Jul-17 14:37
memberMember 1332065220-Jul-17 14:37 
AnswerRe: Seeking help with CFileEditCtrl.cpp Pin
PJ Arends2-Aug-17 8:39
professionalPJ Arends2-Aug-17 8:39 
GeneralMy vote of 5 Pin
chaz-chance18-Apr-17 3:46
memberchaz-chance18-Apr-17 3:46 
GeneralRe: My vote of 5 Pin
PJ Arends18-Apr-17 12:48
professionalPJ Arends18-Apr-17 12:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171114.1 | Last Updated 13 Apr 2017
Article Copyright 2017 by PJ Arends
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid