/////////////////////////////////////////////////////////////////////////////
// Name: gestureclick.cpp
// Purpose:
// Author: Cesar Mauri Loba (cesar at crea-si dot com)
// Modified by:
// Created:
// Copyright: (C) 2008-12 Cesar Mauri Loba - CREA Software Systems
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
/////////////////////////////////////////////////////////////////////////////
#include "gestureclick.h"
#include "mousecontrol.h"
#include
#undef ENABLE_EXPERIMENTAL_KEY_ACTIONS
CGestureClick::CGestureClick(CMouseControl& mc)
: m_enabled(false)
, m_isLeftPressed(false)
, m_xIniGesture(0)
, m_yIniGesture(0)
, m_state(DWELL_TIME)
, m_mouseControl(&mc)
, m_visualAlertsEnabled(false)
, m_dwellToleranceArea(0)
, m_actionLeft(DISABLE)
, m_actionRight(DISABLE)
, m_actionTop(DISABLE)
, m_actionBottom(DISABLE)
, m_fastGestureAction(false)
{
InitKeyboardCodes();
InitDefaults ();
}
CGestureClick::~CGestureClick ()
{
SetEnabled(false);
}
// Configuration methods
void CGestureClick::InitDefaults()
{
// General attributes
//SetConsecutiveClicksAllowed(false);
EnableVisualAlerts(true);
SetDwellTime (10);
SetDwellToleranceArea (3);
// Gesture click
SetActionLeft(SINGLE);
SetActionRight(SECONDARY);
SetActionTop(DOUBLE);
SetActionBottom(DRAG);
SetFastGestureAction(false);
}
void CGestureClick::WriteProfileData(wxConfigBase* pConfObj)
{
pConfObj->SetPath (_T("gestureClick"));
pConfObj->Write(_T("dwellTime"), (int) GetDwellTime());
pConfObj->Write(_T("dwellToleranceArea"), (int) GetDwellToleranceArea());
pConfObj->Write(_T("actionTop"), (int) GetActionTop());
pConfObj->Write(_T("actionLeft"), (int) GetActionLeft());
pConfObj->Write(_T("actionRight"), (int) GetActionRight());
pConfObj->Write(_T("actionBottom"), (int) GetActionBottom());
pConfObj->Write(_T("visualAlertsEnabled"), (bool) AreVisualAlertsEnabled());
pConfObj->Write(_T("fastAction"), (bool) GetFastGestureAction());
pConfObj->SetPath (_T(".."));
}
void CGestureClick::ReadProfileData(wxConfigBase* pConfObj)
{
int vall;
bool valb;
pConfObj->SetPath (_T("gestureClick"));
if (pConfObj->Read(_T("dwellTime"), &vall)) SetDwellTime(vall);
if (pConfObj->Read(_T("dwellToleranceArea"), &vall))
SetDwellToleranceArea((unsigned int) vall);
if (pConfObj->Read(_T("actionTop"), &vall)) SetActionTop((EAction) vall);
if (pConfObj->Read(_T("actionLeft"), &vall)) SetActionLeft((EAction) vall);
if (pConfObj->Read(_T("actionRight"), &vall)) SetActionRight((EAction) vall);
if (pConfObj->Read(_T("actionBottom"), &vall)) SetActionBottom((EAction) vall);
if (pConfObj->Read(_T("visualAlertsEnabled"), &valb)) EnableVisualAlerts(valb);
if (pConfObj->Read(_T("fastAction"), &valb)) SetFastGestureAction(valb);
pConfObj->SetPath (_T(".."));
}
void CGestureClick::InitKeyboardCodes()
{
#if defined(ENABLE_EXPERIMENTAL_KEY_ACTIONS)
// TODO: add useful entries here
m_keyboardCodes.push_back(CKeyboardCode('a'));
m_keyboardCodes.push_back(CKeyboardCode('b'));
m_keyboardCodes.push_back(CKeyboardCode('c'));
m_keyboardCodes.push_back(CKeyboardCode('d'));
m_keyboardCodes.push_back(CKeyboardCode('e'));
m_keyboardCodes.push_back(CKeyboardCode('f'));
m_keyboardCodes.push_back(CKeyboardCode('g'));
m_keyboardCodes.push_back(CKeyboardCode('h'));
m_keyboardCodes.push_back(CKeyboardCode('i'));
m_keyboardCodes.push_back(CKeyboardCode('j'));
m_keyboardCodes.push_back(CKeyboardCode('K'));
m_keyboardCodes.push_back(CKeyboardCode('L'));
m_keyboardCodes.push_back(CKeyboardCode('M'));
m_keyboardCodes.push_back(CKeyboardCode('N'));
m_keyboardCodes.push_back(CKeyboardCode('O'));
m_keyboardCodes.push_back(CKeyboardCode('P'));
m_keyboardCodes.push_back(CKeyboardCode('Q'));
m_keyboardCodes.push_back(CKeyboardCode('R'));
m_keyboardCodes.push_back(CKeyboardCode('S'));
m_keyboardCodes.push_back(CKeyboardCode('T'));
m_keyboardCodes.push_back(CKeyboardCode('U'));
m_keyboardCodes.push_back(CKeyboardCode('V'));
m_keyboardCodes.push_back(CKeyboardCode('W'));
m_keyboardCodes.push_back(CKeyboardCode('X'));
m_keyboardCodes.push_back(CKeyboardCode('Y'));
m_keyboardCodes.push_back(CKeyboardCode('Z'));
#endif
}
mousecmd::mousecmd CGestureClick::ProcessMotion (int dxPix, int dyPix, unsigned int xCurr, unsigned int yCurr)
{
if (!m_enabled) return mousecmd::CMD_NO_CLICK;
mousecmd::mousecmd retval= mousecmd::CMD_NO_CLICK;
// Do move.
float despl= sqrtf ((float) (dxPix*dxPix + dyPix*dyPix));
// Gesture click
switch (m_state) {
case DWELL_TIME:
// This state waits until the dwell time has expired
if (despl> m_dwellToleranceArea) {
// Pointer moving. Reset countdown and remove
// visual alerts
if (m_visualAlertsEnabled) m_progressVisualAlert.End();
m_dwellCountdown.Reset();
}
else {
// Pointer stopped
if (m_dwellCountdown.HasExpired()) {
if (m_visualAlertsEnabled) m_progressVisualAlert.End();
m_dwellCountdown.Reset();
m_xIniGesture = xCurr;
m_yIniGesture = yCurr;
m_state = COMPUTE_DIRECTION;
}
else if (m_visualAlertsEnabled)
m_progressVisualAlert.Update
(xCurr, yCurr, m_dwellCountdown.PercentagePassed());
}
break;
case COMPUTE_DIRECTION:
if (despl> m_dwellToleranceArea) {
// Pointer moving
if (m_visualAlertsEnabled) {
m_progressVisualAlert.End();
m_gestureVisualAlert.Update(xCurr, yCurr);
}
if (!m_fastGestureAction) m_dwellCountdown.Reset();
}
else {
// Pointer static
if (m_dwellCountdown.HasExpired()) {
// Countdown expired
// Remove visual alerts
if (m_visualAlertsEnabled) {
m_gestureVisualAlert.End();
m_progressVisualAlert.End();
}
// Compute motion from iniGesture point
int dxPixels= xCurr - m_xIniGesture;
int dyPixels= yCurr - m_yIniGesture;
if (sqrtf((float)(dxPixels*dxPixels + dyPixels*dyPixels))> m_dwellToleranceArea) {
// Is far enough from iniGeture, do action
if (abs(dxPixels)> abs(dyPixels)) {
if (dxPixels < 0) retval= DoAction(m_actionLeft);
else retval= DoAction(m_actionRight);
}
else {
if (dyPixels < 0) retval= DoAction(m_actionTop);
else retval= DoAction(m_actionBottom);
}
}
// Next state. Ignores consecutive clicks allowed because for
// gesture click mode it is difficult to control
//if (m_consecutiveClicksAllowed) m_state = DWELL_TIME;
//else m_state = WAIT_DWELL;
m_state = WAIT_DWELL;
}
else {
if (m_visualAlertsEnabled) {
m_progressVisualAlert.Update(xCurr, yCurr, m_dwellCountdown.PercentagePassed());
m_gestureVisualAlert.Update(xCurr, yCurr);
}
}
}
break;
case WAIT_DWELL:
// Wait to restart geture action until pointer is moved
if (despl> m_dwellToleranceArea) {
// Pointer moving. Next state
m_dwellCountdown.Reset();
m_state= DWELL_TIME;
}
break;
default:
assert (false);
break;
}
return retval;
}
mousecmd::mousecmd CGestureClick::DoAction (EAction action)
{
mousecmd::mousecmd retval= mousecmd::CMD_NO_CLICK;
// Return pointer to the position where gesture started
m_mouseControl->DoMovePointerAbs(m_xIniGesture, m_yIniGesture);
if (m_isLeftPressed) {
// Previous action started a drag & drop operation,
// the new action finishes it
m_isLeftPressed = false;
m_mouseControl->LeftUp();
retval= mousecmd::CMD_LEFT_UP;
if (action== DRAG) return retval;
}
switch (action) {
case DISABLE:
break;
case SINGLE:
m_mouseControl->LeftClick();
retval= mousecmd::CMD_LEFT_CLICK;
break;
case DOUBLE:
m_mouseControl->LeftDblClick();
retval= mousecmd::CMD_DOUBLE_CLICK;
break;
case SECONDARY:
m_mouseControl->RightClick();
retval= mousecmd::CMD_RIGHT_CLICK;
break;
case THIRD:
m_mouseControl->MiddleClick();
retval= mousecmd::CMD_MIDDLE_CLICK;
break;
case DRAG:
m_mouseControl->LeftDown();
retval= mousecmd::CMD_LEFT_DOWN;
m_isLeftPressed = true;
break;
default:
// Keyboard event
m_keyboardCodes[action - MOUSE_EVENTS_COUNT].SendKey();
break;
}
return retval;
}
void CGestureClick::Reset()
{
m_state = DWELL_TIME;
m_dwellCountdown.Reset();
if (m_visualAlertsEnabled) {
m_progressVisualAlert.End();
m_gestureVisualAlert.End();
}
if (m_isLeftPressed) {
m_mouseControl->LeftUp();
m_isLeftPressed = false;
}
// Unnecessary
//m_xIniGesture = 0;
//m_yIniGesture = 0;
}
void CGestureClick::SetEnabled(bool value)
{
// TODO: sync
if (value!= m_enabled) {
if (value) {
// Enable
Reset();
m_enabled= true;
}
else {
// Disable
m_enabled= false;
Reset();
}
}
}
void CGestureClick::EnableVisualAlerts(bool value)
{
// TODO: sync
if (value!= m_visualAlertsEnabled) {
if (value) {
m_progressVisualAlert.End();
m_gestureVisualAlert.End();
m_visualAlertsEnabled= true;
}
else {
m_visualAlertsEnabled= false;
m_progressVisualAlert.End();
m_gestureVisualAlert.End();
}
}
}