/*
 * File Name: test_gtk_source.h
 */

/*
 * This file is part of uds-plugin-pdf.
 *
 * uds-plugin-pdf 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 2 of the License, or
 * (at your option) any later version.
 *
 * uds-plugin-pdf 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 <http://www.gnu.org/licenses/>.
 */

/**
 * Copyright (C) 2008 iRex Technologies B.V.
 * All rights reserved.
 */

#ifndef TEST_GTK_SOURCE_H_
#define TEST_GTK_SOURCE_H_

#include <gtk/gtk.h>
#include <string.h>

namespace gtk
{

    /// @brief Enable caller to handle GTK events without inheriting from any class.
    /// One-one connection. It's better to use template based technology. 
    /// By GtkEventSource caller is able to handle multiple widgets' events.
    template <class T>
    class GtkEventSource 
    {
    public:
        enum EventType 
        {
            EVENT_EXPOSE = 0,
            EVENT_CONFIG,
            EVENT_BUTTON_PRESS,         
            EVENT_BUTTON_RELEASE, 
            EVENT_MOTION_NOTIFY,
            EVENT_KEY_PRESS,
            EVENT_KEY_RELEASE,
            EVENT_DELETE,
            EVENT_FD_INPUT,
            EVENT_UNDEFINED,
        };

    public:
        GtkEventSource(T * r) : receiver(r)
        {
            memset(&callbacks[0], 0, sizeof(callbacks));
        }

        ~GtkEventSource()
        {
        }

        /// @brief Connect the event handler(slot) with the gtk event(signal)
        void connect_event_handlers(GtkWidget * widget)
        {
            // events 
            gtk_widget_set_events (widget,
                GDK_EXPOSURE_MASK | GDK_BUTTON_MOTION_MASK |
                GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);

            // install event handlers
            gtk_signal_connect (GTK_OBJECT (widget), "expose_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);

            gtk_signal_connect (GTK_OBJECT (widget), "configure_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);

            gtk_signal_connect (GTK_OBJECT (widget), "button_press_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);

            gtk_signal_connect (GTK_OBJECT (widget), "button_release_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);

            gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);

            gtk_signal_connect (GTK_OBJECT (widget), "key_press_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);

            gtk_signal_connect (GTK_OBJECT (widget), "key_release_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);

            gtk_signal_connect (GTK_OBJECT (widget), "delete_event",
                GTK_SIGNAL_FUNC (widget_event_handler), this);
        }

        /// @brief Set event handler of File Descriptor
        void set_fd_event_watcher(int fd, gboolean 
                                  (T::*func)(GtkWidget *widget,
                                             GdkEvent * event))
        {
            gdk_input_add(fd, GDK_INPUT_READ,
                          (GdkInputFunction)fd_event_handler,
                          (gpointer)this);

            callbacks[EVENT_FD_INPUT] = func;
        }

        /// @brief Set event handler of gtk event (from event loop)
        inline void set_event_handler(EventType type, 
                                      gboolean (T::*func)(GtkWidget * widget,
                                      GdkEvent * event))
        {
            callbacks[type] = func;
        }
        
    private:
        static gboolean widget_event_handler(GtkWidget * widget, 
                                             GdkEvent * event, 
                                             gpointer user_data)
        {
            GtkEventSource* pThis = (GtkEventSource *)user_data;
            
            if (event == 0)
            {
                return FALSE;
            }

            switch (event->type)
            {
            case GDK_EXPOSE:

                return pThis->invoke_callback(EVENT_EXPOSE, widget, event);
                
            case GDK_CONFIGURE:

                return pThis->invoke_callback(EVENT_CONFIG, widget, event);

            case GDK_BUTTON_PRESS:

                return pThis->invoke_callback(EVENT_BUTTON_PRESS, widget, event);
                
            case GDK_BUTTON_RELEASE:

                return pThis->invoke_callback(EVENT_BUTTON_RELEASE, widget, event);
                
            case GDK_MOTION_NOTIFY:

                return pThis->invoke_callback(EVENT_MOTION_NOTIFY, widget, event);

            case GDK_KEY_PRESS:

                return pThis->invoke_callback(EVENT_KEY_PRESS, widget, event);

            case GDK_KEY_RELEASE:

                return pThis->invoke_callback(EVENT_KEY_RELEASE, widget, event);

            case GDK_DELETE:

                return pThis->invoke_callback(EVENT_DELETE, widget, event);

            default:
                break;
            }

            return FALSE;
        }

        static void fd_event_handler(gpointer user_data, gint *fd,
                                     GdkInputCondition id)
        {
            GtkEventSource *pThis = (GtkEventSource *)user_data;
            pThis->invoke_callback(EVENT_FD_INPUT, 0, 0);
        }

        gboolean invoke_callback(EventType type, 
                                 GtkWidget *widget, 
                                 GdkEvent  *event)
        {
            if (callbacks[type])
            {
                return (receiver->*callbacks[type])(widget, event);
            }
            return FALSE;            
        }

    private:

        T * receiver;
        gboolean (T::*callbacks[EVENT_UNDEFINED])(GtkWidget *widget,
                                                  GdkEvent  *event);

    };

}; //namespace gtk

#endif


