/////////////////////////////////////////////////////////////////////////
// File:        src/gtk/taskbar.cpp
// Purpose:     wxTaskBarIcon
// Author:      Vaclav Slavik
// Modified by: Paul Cornett
// Created:     2004/05/29
// Copyright:   (c) Vaclav Slavik, 2004
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#if wxUSE_TASKBARICON

#include "wx/taskbar.h"

#ifndef __WXGTK4__

#ifndef WX_PRECOMP
    #include "wx/toplevel.h"
    #include "wx/menu.h"
    #include "wx/icon.h"
#endif

#include "wx/gtk/private/wrapgtk.h"
#include "wx/gtk/private/backend.h"
#ifdef GDK_WINDOWING_X11
    #include <gdk/gdkx.h>
#endif
#ifndef __WXGTK3__
    #include "eggtrayicon.h"
#endif

wxGCC_WARNING_SUPPRESS(deprecated-declarations)

#if !GTK_CHECK_VERSION(2,10,0)
    typedef struct _GtkStatusIcon GtkStatusIcon;
#endif

GdkWindow* wxGetTopLevelGDK();

class wxTaskBarIcon::Private
{
public:
    Private(wxTaskBarIcon* taskBarIcon);
    ~Private();
    void SetIcon();
    void size_allocate(int width, int height);

    // owning wxTaskBarIcon
    wxTaskBarIcon* m_taskBarIcon;
    // used when GTK+ >= 2.10
    GtkStatusIcon* m_statusIcon;
    // for PopupMenu
    wxWindow* m_win;
    wxBitmapBundle m_bitmap;
    wxString m_tipText;
#ifndef __WXGTK3__
    // used when GTK+ < 2.10
    GtkWidget* m_eggTrayIcon;
    // for tooltip when GTK+ < 2.10
    GtkTooltips* m_tooltips;
    // width and height of available space, only used when GTK+ < 2.10
    int m_size;
#endif
};
//-----------------------------------------------------------------------------

extern "C" {
#ifndef __WXGTK3__
static void
icon_size_allocate(GtkWidget*, GtkAllocation* alloc, wxTaskBarIcon::Private* priv)
{
    priv->size_allocate(alloc->width, alloc->height);
}

static void
icon_destroy(GtkWidget*, wxTaskBarIcon::Private* priv)
{
    // Icon window destroyed, probably because tray program has died.
    // Recreate icon so it will appear if tray program is restarted.
    priv->m_eggTrayIcon = nullptr;
    priv->SetIcon();
}
#endif

static void
icon_activate(void*, wxTaskBarIcon* taskBarIcon)
{
    // activate occurs from single click with GTK+
    wxTaskBarIconEvent event(wxEVT_TASKBAR_LEFT_DOWN, taskBarIcon);
    if (!taskBarIcon->SafelyProcessEvent(event))
    {
        // if single click not handled, send double click for compatibility
        event.SetEventType(wxEVT_TASKBAR_LEFT_DCLICK);
        taskBarIcon->SafelyProcessEvent(event);
    }
}

static gboolean
icon_popup_menu(GtkWidget*, wxTaskBarIcon* taskBarIcon)
{
    wxTaskBarIconEvent event(wxEVT_TASKBAR_CLICK, taskBarIcon);
    taskBarIcon->SafelyProcessEvent(event);
    return true;
}

#ifndef __WXGTK3__
static gboolean
icon_button_press_event(GtkWidget*, GdkEventButton* event, wxTaskBarIcon* taskBarIcon)
{
    if (event->type == GDK_BUTTON_PRESS)
    {
        if (event->button == 1)
            icon_activate(nullptr, taskBarIcon);
        else if (event->button == 3)
            icon_popup_menu(nullptr, taskBarIcon);
    }
    return false;
}
#endif

#if GTK_CHECK_VERSION(2,10,0)
static void
status_icon_popup_menu(GtkStatusIcon*, guint, guint, wxTaskBarIcon* taskBarIcon)
{
    icon_popup_menu(nullptr, taskBarIcon);
}
#endif
} // extern "C"
//-----------------------------------------------------------------------------

bool wxTaskBarIconBase::IsAvailable()
{
#ifdef GDK_WINDOWING_X11
#ifdef __WXGTK3__
    if (!wxGTKImpl::IsX11(nullptr))
        return false;
#endif

    char name[32];
    g_snprintf(name, sizeof(name), "_NET_SYSTEM_TRAY_S%d",
        gdk_x11_get_default_screen());
    Atom atom = gdk_x11_get_xatom_by_name(name);

    Window manager = XGetSelectionOwner(gdk_x11_get_default_xdisplay(), atom);

    return manager != None;
#else
    return true;
#endif
}
//-----------------------------------------------------------------------------

wxTaskBarIcon::Private::Private(wxTaskBarIcon* taskBarIcon)
{
    m_taskBarIcon = taskBarIcon;
    m_statusIcon = nullptr;
    m_win = nullptr;
#ifndef __WXGTK3__
    m_eggTrayIcon = nullptr;
    m_tooltips = nullptr;
    m_size = 0;
#endif
}

wxTaskBarIcon::Private::~Private()
{
    if (m_statusIcon)
        g_object_unref(m_statusIcon);
#ifndef __WXGTK3__
    else if (m_eggTrayIcon)
    {
        g_signal_handlers_disconnect_by_func(m_eggTrayIcon, (void*)icon_destroy, this);
        gtk_widget_destroy(m_eggTrayIcon);
    }
#endif
    if (m_win)
    {
        m_win->PopEventHandler();
        m_win->Destroy();
    }
#ifndef __WXGTK3__
    if (m_tooltips)
    {
        gtk_object_destroy(GTK_OBJECT(m_tooltips));
        g_object_unref(m_tooltips);
    }
#endif
}

void wxTaskBarIcon::Private::SetIcon()
{
#if GTK_CHECK_VERSION(2,10,0)
    if (wx_is_at_least_gtk2(10))
    {
        int scale = 1;
#if GTK_CHECK_VERSION(3,10,0)
        if (gtk_check_version(3,10,0) == nullptr)
        {
            scale = gdk_window_get_scale_factor(wxGetTopLevelGDK());
        }
#endif
        const wxSize size(m_bitmap.GetPreferredBitmapSizeAtScale(scale));
        const wxBitmap bmp(m_bitmap.GetBitmap(size));
        if (m_statusIcon)
            gtk_status_icon_set_from_pixbuf(m_statusIcon, bmp.GetPixbuf());
        else
        {
            m_statusIcon = gtk_status_icon_new_from_pixbuf(bmp.GetPixbuf());
            g_signal_connect(m_statusIcon, "activate",
                G_CALLBACK(icon_activate), m_taskBarIcon);
            g_signal_connect(m_statusIcon, "popup_menu",
                G_CALLBACK(status_icon_popup_menu), m_taskBarIcon);
        }
    }
    else
#endif
    {
#ifndef __WXGTK3__
        m_size = 0;
        const wxBitmap bmp(m_bitmap.GetBitmap(wxDefaultSize));
        if (m_eggTrayIcon)
        {
            GtkWidget* image = gtk_bin_get_child(GTK_BIN(m_eggTrayIcon));
            gtk_image_set_from_pixbuf(GTK_IMAGE(image), bmp.GetPixbuf());
        }
        else
        {
            m_eggTrayIcon = GTK_WIDGET(egg_tray_icon_new("wxTaskBarIcon"));
            gtk_widget_add_events(m_eggTrayIcon, GDK_BUTTON_PRESS_MASK);
            g_signal_connect(m_eggTrayIcon, "size_allocate",
                G_CALLBACK(icon_size_allocate), this);
            g_signal_connect(m_eggTrayIcon, "destroy",
                G_CALLBACK(icon_destroy), this);
            g_signal_connect(m_eggTrayIcon, "button_press_event",
                G_CALLBACK(icon_button_press_event), m_taskBarIcon);
            g_signal_connect(m_eggTrayIcon, "popup_menu",
                G_CALLBACK(icon_popup_menu), m_taskBarIcon);
            GtkWidget* image = gtk_image_new_from_pixbuf(bmp.GetPixbuf());
            gtk_container_add(GTK_CONTAINER(m_eggTrayIcon), image);
            gtk_widget_show_all(m_eggTrayIcon);
        }
#endif
    }
#if wxUSE_TOOLTIPS
    const char *tip_text = nullptr;
    if (!m_tipText.empty())
        tip_text = m_tipText.utf8_str();

#if GTK_CHECK_VERSION(2,10,0)
    if (m_statusIcon)
    {
#if GTK_CHECK_VERSION(2,16,0)
        if (wx_is_at_least_gtk2(16))
            gtk_status_icon_set_tooltip_text(m_statusIcon, tip_text);
        else
#endif
        {
#ifndef __WXGTK3__
            gtk_status_icon_set_tooltip(m_statusIcon, tip_text);
#endif
        }
    }
    else
#endif // GTK_CHECK_VERSION(2,10,0)
    {
#ifndef __WXGTK3__
        if (tip_text && m_tooltips == nullptr)
        {
            m_tooltips = gtk_tooltips_new();
            g_object_ref(m_tooltips);
            gtk_object_sink(GTK_OBJECT(m_tooltips));
        }
        if (m_tooltips)
            gtk_tooltips_set_tip(m_tooltips, m_eggTrayIcon, tip_text, "");
#endif
    }
#endif // wxUSE_TOOLTIPS
}

#ifndef __WXGTK3__
void wxTaskBarIcon::Private::size_allocate(int width, int height)
{
    int size = height;
    EggTrayIcon* icon = EGG_TRAY_ICON(m_eggTrayIcon);
    if (egg_tray_icon_get_orientation(icon) == GTK_ORIENTATION_VERTICAL)
        size = width;
    if (m_size == size)
        return;
    m_size = size;
    const wxBitmap bmp(m_bitmap.GetBitmap(wxDefaultSize));
    int w = bmp.GetLogicalWidth();
    int h = bmp.GetLogicalHeight();
    if (w > size || h > size)
    {
        if (w > size) w = size;
        if (h > size) h = size;
        GdkPixbuf* pixbuf =
            gdk_pixbuf_scale_simple(bmp.GetPixbuf(), w, h, GDK_INTERP_BILINEAR);
        GtkImage* image = GTK_IMAGE(gtk_bin_get_child(GTK_BIN(m_eggTrayIcon)));
        gtk_image_set_from_pixbuf(image, pixbuf);
        g_object_unref(pixbuf);
    }
}
#endif
//-----------------------------------------------------------------------------

wxIMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler);

wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType WXUNUSED(iconType))
{
    m_priv = new Private(this);
}

wxTaskBarIcon::~wxTaskBarIcon()
{
    delete m_priv;
}

bool wxTaskBarIcon::SetIcon(const wxBitmapBundle& icon, const wxString& tooltip)
{
    m_priv->m_bitmap = icon;
    m_priv->m_tipText = tooltip;
    m_priv->SetIcon();
    return true;
}

bool wxTaskBarIcon::RemoveIcon()
{
    delete m_priv;
    m_priv = new Private(this);
    return true;
}

bool wxTaskBarIcon::IsIconInstalled() const
{
#ifdef __WXGTK3__
    return m_priv->m_statusIcon != nullptr;
#else
    return m_priv->m_statusIcon || m_priv->m_eggTrayIcon;
#endif
}

bool wxTaskBarIcon::PopupMenu(wxMenu* menu)
{
#if wxUSE_MENUS
    if (m_priv->m_win == nullptr)
    {
        m_priv->m_win = new wxTopLevelWindow(
            nullptr, wxID_ANY, wxString(), wxDefaultPosition, wxDefaultSize, 0);
        m_priv->m_win->PushEventHandler(this);
    }
    wxPoint point(-1, -1);
#ifdef __WXUNIVERSAL__
    point = wxGetMousePosition();
#endif
    m_priv->m_win->PopupMenu(menu, point);
#endif // wxUSE_MENUS
    return true;
}

#else
wxIMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler);

wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType)
{
    m_priv = nullptr;
}

wxTaskBarIcon::~wxTaskBarIcon()
{
}

bool wxTaskBarIcon::SetIcon(const wxIcon&, const wxString&)
{
    return false;
}

bool wxTaskBarIcon::RemoveIcon()
{
    return false;
}

bool wxTaskBarIcon::IsIconInstalled() const
{
    return false;
}

bool wxTaskBarIcon::PopupMenu(wxMenu*)
{
    return false;
}
#endif // __WXGTK4__
#endif // wxUSE_TASKBARICON
