ewk_contextmenu.cpp   [plain text]


/*
    Copyright (C) 2010 ProFUSION embedded systems
    Copyright (C) 2010 Samsung Electronics

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "config.h"
#include "ewk_contextmenu.h"

#include "ContextMenu.h"
#include "ContextMenuController.h"
#include "ContextMenuItem.h"
#include "EWebKit.h"
#include "ewk_private.h"

#include <Eina.h>
#include <eina_safety_checks.h>
#include <wtf/text/CString.h>

/**
 * \struct  _Ewk_Context_Menu
 * @brief   Contains the context menu data.
 */
struct _Ewk_Context_Menu {
    unsigned int __ref; /**< the reference count of the object */
#if ENABLE(CONTEXT_MENUS)
    WebCore::ContextMenuController* controller; /**< the WebCore's object which is responsible for the context menu */
#endif
    Evas_Object* view; /**< the view object */

    Eina_List* items; /**< the list of items */
};

/**
 * \struct  _Ewk_Context_Menu_Item
 * @brief   Represents one item of the context menu object.
 */
struct _Ewk_Context_Menu_Item {
    Ewk_Context_Menu_Item_Type type; /**< contains the type of the item */
    Ewk_Context_Menu_Action action; /**< contains the action of the item */

    const char* title; /**< contains the title of the item */
    Ewk_Context_Menu* submenu; /**< contains the pointer to the submenu of the item */

    Eina_Bool checked:1;
    Eina_Bool enabled:1;
};

/**
 * Increases the reference count of the given object.
 *
 * @param menu the context menu object to increase the reference count
 */
void ewk_context_menu_ref(Ewk_Context_Menu* menu)
{
    EINA_SAFETY_ON_NULL_RETURN(menu);
    menu->__ref++;
}

/**
 * Decreases the reference count of the given object, possibly freeing it.
 *
 * When the reference count it's reached 0, the menu with all items are freed.
 *
 * @param menu the context menu object to decrease the reference count
 */
void ewk_context_menu_unref(Ewk_Context_Menu* menu)
{
    EINA_SAFETY_ON_NULL_RETURN(menu);
    void* item;

    if (--menu->__ref)
        return;

    EINA_LIST_FREE(menu->items, item)
        ewk_context_menu_item_free(static_cast<Ewk_Context_Menu_Item*>(item));

    free(menu);
}

/**
 * Destroys the context menu object.
 *
 * @param menu the context menu object to destroy
 * @return @c EINA_TRUE on success, @c EINA_FALSE on failure
 *
 * @see ewk_context_menu_item_free
 */
Eina_Bool ewk_context_menu_destroy(Ewk_Context_Menu* menu)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(menu, EINA_FALSE);
#if ENABLE(CONTEXT_MENUS)
    EINA_SAFETY_ON_NULL_RETURN_VAL(menu->controller, EINA_FALSE);

    menu->controller->clearContextMenu();
#endif

    return EINA_TRUE;
}

/**
 * Gets the list of items.
 *
 * @param o the context menu object to get list of the items
 * @return the list of the items on success or @c 0 on failure
 */
const Eina_List* ewk_context_menu_item_list_get(Ewk_Context_Menu* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, 0);

    return o->items;
}

/**
 * Creates a new item of the context menu.
 *
 * @param type specifies a type of the item
 * @param action specifies a action of the item
 * @param submenu specifies a submenu of the item
 * @param title specifies a title of the item
 * @param checked
 * @param enabled @c EINA_TRUE to enable the item or @c EINA_FALSE to disable
 * @return the pointer to the new item on success or @c 0 on failure
 *
 * @note The return value @b should @b be freed after use.
 */
Ewk_Context_Menu_Item* ewk_context_menu_item_new(Ewk_Context_Menu_Item_Type type,
        Ewk_Context_Menu_Action action, Ewk_Context_Menu* submenu,
        const char* title, Eina_Bool checked, Eina_Bool enabled)
{
    Ewk_Context_Menu_Item* item = (Ewk_Context_Menu_Item*) malloc(sizeof(*item));
    if (!item)
        return 0;

    item->type = type;
    item->action = action;
    item->title = eina_stringshare_add(title);
    item->submenu = submenu;
    item->checked = checked;
    item->enabled = enabled;

    return item;
}

/**
 * Selects the item from the context menu object.
 *
 * @param menu the context menu object
 * @param item the item is selected
 * @return @c EINA_TRUE on success or @c EINA_FALSE on failure
 */
Eina_Bool ewk_context_menu_item_select(Ewk_Context_Menu* menu, Ewk_Context_Menu_Item* item)
{
#if ENABLE(CONTEXT_MENUS)
    EINA_SAFETY_ON_NULL_RETURN_VAL(menu, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(item, EINA_FALSE);
    WebCore::ContextMenuAction action = static_cast<WebCore::ContextMenuAction>(item->action);
    WebCore::ContextMenuItemType type = static_cast<WebCore::ContextMenuItemType>(item->type);

    // Don't care about title and submenu as they're not used after this point.
    WebCore::ContextMenuItem core(type, action, WTF::String());
    menu->controller->contextMenuItemSelected(&core);
    return EINA_TRUE;
#else
    return EINA_FALSE;
#endif
}

/**
 * Destroys the item of the context menu object.
 *
 * @param item the item to destroy
 *
 * @see ewk_context_menu_destroy
 * @see ewk_context_menu_unref
 */
void ewk_context_menu_item_free(Ewk_Context_Menu_Item* item)
{
    EINA_SAFETY_ON_NULL_RETURN(item);

    eina_stringshare_del(item->title);
    free(item);
}

/**
 * Gets type of the item.
 *
 * @param o the item to get the type
 * @return type of the item on success or @c EWK_ACTION_TYPE on failure
 *
 * @see ewk_context_menu_item_type_set
 */
Ewk_Context_Menu_Item_Type ewk_context_menu_item_type_get(Ewk_Context_Menu_Item* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EWK_ACTION_TYPE);
    return o->type;
}

/**
 * Sets the type of item.
 *
 * @param o the item to set the type
 * @param type a new type for the item object
 * @return @c EINA_TRUE on success, or @c EINA_FALSE on failure
 *
 * @see ewk_context_menu_item_type_get
 */
Eina_Bool ewk_context_menu_item_type_set(Ewk_Context_Menu_Item* o, Ewk_Context_Menu_Item_Type type)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EINA_FALSE);
    o->type = type;
    return EINA_TRUE;
}

/**
 * Gets an action of the item.
 *
 * @param o the item to get the action
 * @return an action of the item on success or @c EWK_CONTEXT_MENU_ITEM_TAG_NO_ACTION on failure
 *
 * @see ewk_context_menu_item_action_set
 */
Ewk_Context_Menu_Action ewk_context_menu_item_action_get(Ewk_Context_Menu_Item* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EWK_CONTEXT_MENU_ITEM_TAG_NO_ACTION);
    return o->action;
}

/**
 * Sets an action of the item.
 *
 * @param o the item to set the action
 * @param action a new action for the item object
 * @return @c EINA_TRUE on success, or @c EINA_FALSE on failure
 *
 * @see ewk_context_menu_item_action_get
 */
Eina_Bool ewk_context_menu_item_action_set(Ewk_Context_Menu_Item* o, Ewk_Context_Menu_Action action)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EINA_FALSE);
    o->action = action;
    return EINA_TRUE;
}

/**
 * Gets a title of the item.
 *
 * @param o the item to get the title
 * @return a title of the item on success, or @c 0 on failure
 *
 * @see ewk_context_menu_item_title_set
 */
const char* ewk_context_menu_item_title_get(Ewk_Context_Menu_Item* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, 0);
    return o->title;
}

/**
 * Sets a title of the item.
 *
 * @param o the item to set the title
 * @param title a new title for the item object
 * @return a new title of the item on success or @c 0 on failure
 *
 * @see ewk_context_menu_item_title_get
 */
const char* ewk_context_menu_item_title_set(Ewk_Context_Menu_Item* o, const char* title)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, 0);
    eina_stringshare_replace(&o->title, title);
    return o->title;
}

Eina_Bool ewk_context_menu_item_checked_get(Ewk_Context_Menu_Item* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EINA_FALSE);
    return o->checked;
}

Eina_Bool ewk_context_menu_item_checked_set(Ewk_Context_Menu_Item* o, Eina_Bool checked)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EINA_FALSE);
    o->checked = checked;
    return EINA_TRUE;
}

/**
 * Gets if the item is enabled.
 *
 * @param o the item to get enabled state
 * @return @c EINA_TRUE if it's enabled, @c EINA_FALSE if not or on failure
 *
 * @see ewk_context_menu_item_enabled_set
 */
Eina_Bool ewk_context_menu_item_enabled_get(Ewk_Context_Menu_Item* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EINA_FALSE);
    return o->enabled;
}

/**
 * Enables/disables the item.
 *
 * @param o the item to enable/disable
 * @param enabled @c EINA_TRUE to enable the item or @c EINA_FALSE to disable
 * @return @c EINA_TRUE on success, or @c EINA_FALSE on failure
 *
 * @see ewk_context_menu_item_enabled_get
 */
Eina_Bool ewk_context_menu_item_enabled_set(Ewk_Context_Menu_Item *o, Eina_Bool enabled)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EINA_FALSE);
    o->enabled = enabled;
    return EINA_TRUE;
}


/* internal methods ****************************************************/

#if ENABLE(CONTEXT_MENUS)
/**
 * @internal
 *
 * Creates an empty context menu on view.
 *
 * @param view the view object
 * @param controller the WebCore's context menu controller
 * @return newly allocated the context menu on success or @c 0 on errors
 *
 * @note emits a signal "contextmenu,new"
 */
Ewk_Context_Menu* ewk_context_menu_new(Evas_Object* view, WebCore::ContextMenuController* controller)
{
    Ewk_Context_Menu* menu;
    EINA_SAFETY_ON_NULL_RETURN_VAL(view, 0);
    EINA_SAFETY_ON_NULL_RETURN_VAL(controller, 0);

    menu = static_cast<Ewk_Context_Menu*>(malloc(sizeof(*menu)));
    if (!menu) {
        CRITICAL("Could not allocate context menu memory.");
        return 0;
    }

    menu->__ref = 1;
    menu->view = view;
    menu->controller = controller;
    menu->items = 0;
    evas_object_smart_callback_call(menu->view, "contextmenu,new", menu);

    return menu;
}

/**
 * @internal
 *
 * Frees the context menu.
 *
 * @param o the view object
 * @return @c EINA_TRUE on success, or @c EINA_FALSE on failure
 *
 * @note emits a signal "contextmenu,free"
 *
 * @see ewk_context_menu_unref
 * @see ewk_context_menu_destroy
 */
Eina_Bool ewk_context_menu_free(Ewk_Context_Menu* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, EINA_FALSE);
    evas_object_smart_callback_call(o->view, "contextmenu,free", o);
    ewk_context_menu_unref(o);
    return EINA_TRUE;
}

/**
 * @internal
 *
 * Appends the WebCore's item to the context menu object.
 *
 * @param o the context menu object
 * @param core the WebCore's context menu item that will be added to the context menu
 * @note emits a signal "contextmenu,item,appended"
 *
 * @see ewk_context_menu_item_new
 */
void ewk_context_menu_item_append(Ewk_Context_Menu* o, WebCore::ContextMenuItem& core)
{
    Ewk_Context_Menu_Item_Type type = static_cast<Ewk_Context_Menu_Item_Type>(core.type());
    Ewk_Context_Menu_Action action = static_cast<Ewk_Context_Menu_Action>(core.action());

    Ewk_Context_Menu_Item* menu_item = ewk_context_menu_item_new
        (type, action, 0, core.title().utf8().data(), core.checked(),
         core.enabled());
    EINA_SAFETY_ON_NULL_RETURN(menu_item);

    o->items = eina_list_append(o->items, menu_item);
    evas_object_smart_callback_call(o->view, "contextmenu,item,appended", o);
}

/**
 * @internal
 *
 * Emits a signal with the items of the context menu.
 *
 * @param o the context menu object
 * @return the same context menu object that was given through parameter
 *
 * @note emits a signal "contextmenu,customize"
 *
 * @see ewk_context_menu_item_list_get
 */
Ewk_Context_Menu* ewk_context_menu_custom_get(Ewk_Context_Menu* o)
{
    EINA_SAFETY_ON_NULL_RETURN_VAL(o, 0);

    evas_object_smart_callback_call(o->view, "contextmenu,customize", o->items);
    return o;
}

/**
 * @internal
 *
 * Emits a signal "contextmenu,show"
 *
 * @param o the context menu object
 */
void ewk_context_menu_show(Ewk_Context_Menu* o)
{
    EINA_SAFETY_ON_NULL_RETURN(o);

    evas_object_smart_callback_call(o->view, "contextmenu,show", o);
}

#endif