GtkClickCounter.cpp   [plain text]


/*
 * Copyright (C) 2010, 2011 Igalia S.L.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "config.h"
#include "GtkClickCounter.h"

#include "GOwnPtrGtk.h"
#include <gtk/gtk.h>

namespace WebCore {

GtkClickCounter::GtkClickCounter()
    : m_currentClickCount(0)
    , m_previousClickButton(0)
    , m_previousClickTime(0)
{
}

void GtkClickCounter::reset()
{
    m_currentClickCount = 0;
    m_previousClickPoint = IntPoint();
    m_previousClickTime = 0;
    m_previousClickButton = 0;
}

bool GtkClickCounter::shouldProcessButtonEvent(GdkEventButton* buttonEvent)
{
    // For double and triple clicks GDK sends both a normal button press event
    // and a specific type (like GDK_2BUTTON_PRESS). If we detect a special press
    // coming up, ignore this event as it certainly generated the double or triple
    // click. The consequence of not eating this event is two DOM button press events
    // are generated.
    GOwnPtr<GdkEvent> nextEvent(gdk_event_peek());
    if (!nextEvent)
        return true;
    if (nextEvent->any.type == GDK_2BUTTON_PRESS || nextEvent->any.type == GDK_3BUTTON_PRESS)
        return false;
    return true;
}

static guint32 getEventTime(GdkEvent* event)
{
    guint32 time = gdk_event_get_time(event);
    if (time)
        return time;

    // Real events always have a non-zero time, but events synthesized
    // by the DRT do not and we must calculate a time manually. This time
    // is not calculated in the DRT, because GTK+ does not work well with
    // anything other than GDK_CURRENT_TIME on synthesized events.
    GTimeVal timeValue;
    g_get_current_time(&timeValue);
    return (timeValue.tv_sec * 1000) + (timeValue.tv_usec / 1000);
}

int GtkClickCounter::clickCountForGdkButtonEvent(GtkWidget* widget, GdkEventButton* buttonEvent)
{
    gint doubleClickDistance = 250;
    gint doubleClickTime = 5;
    GtkSettings* settings = gtk_settings_get_for_screen(gtk_widget_get_screen(widget));
    g_object_get(settings,
                 "gtk-double-click-distance", &doubleClickDistance,
                 "gtk-double-click-time", &doubleClickTime, NULL);

    // GTK+ only counts up to triple clicks, but WebCore wants to know about
    // quadruple clicks, quintuple clicks, ad infinitum. Here, we replicate the
    // GDK logic for counting clicks.
    GdkEvent* event(reinterpret_cast<GdkEvent*>(buttonEvent));
    guint32 eventTime = getEventTime(event);

    if ((event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
        || ((abs(buttonEvent->x - m_previousClickPoint.x()) < doubleClickDistance)
            && (abs(buttonEvent->y - m_previousClickPoint.y()) < doubleClickDistance)
            && (eventTime - m_previousClickTime < static_cast<guint>(doubleClickTime))
            && (buttonEvent->button == m_previousClickButton)))
        m_currentClickCount++;
    else
        m_currentClickCount = 1;

    gdouble x, y;
    gdk_event_get_coords(event, &x, &y);
    m_previousClickPoint = IntPoint(x, y);
    m_previousClickButton = buttonEvent->button;
    m_previousClickTime = eventTime;

    return m_currentClickCount;
}

} // namespace WebCore