// MainGtkMenu.cc --- Menus using Gtk+
//
// Copyright (C) 2001 - 2012 Rob Caelers & Raymond Penners
// All rights reserved.
//
// 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 <http://www.gnu.org/licenses/>.
//

#include "preinclude.h"

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "nls.h"
#include "debug.hh"

#include "GUI.hh"
#include "MainGtkMenu.hh"

#include <string>

#include <gdkmm.h>
#include <gtkmm.h>

#include "MainWindow.hh"
#include "Menus.hh"
#include "GtkUtil.hh"

using namespace std;

//! Constructor.
MainGtkMenu::MainGtkMenu(bool show_open)
  : popup_menu(NULL)
  , show_open(show_open)
{
}

//! Destructor.
MainGtkMenu::~MainGtkMenu() {}

void
MainGtkMenu::add_stock_item(const Glib::RefPtr<Gtk::IconFactory> &factory,
                            const std::string &path,
                            const Glib::ustring &icon_id,
                            const Glib::ustring &label)
{
  Gtk::IconSource source;
#ifdef HAVE_GTK3
  Glib::RefPtr<Gtk::IconSet> icon_set = Gtk::IconSet::create();
#else
  Gtk::IconSet icon_set;
#endif

  source.set_pixbuf(GtkUtil::create_pixbuf(path));
  source.set_size(Gtk::ICON_SIZE_SMALL_TOOLBAR);
  source.set_size_wildcarded();

#ifdef HAVE_GTK3
  icon_set->add_source(source);
#else
  icon_set.add_source(source);
#endif

  const Gtk::StockID stock_id(icon_id);
  factory->add(stock_id, icon_set);
  Gtk::Stock::add(Gtk::StockItem(stock_id, label));
}

void
MainGtkMenu::register_stock_items()
{
  Glib::RefPtr<Gtk::IconFactory> factory = Gtk::IconFactory::create();

  add_stock_item(factory, "timer-rest-break.png", "restbreak", _("_Rest break"));

  factory->add_default();
}

void
MainGtkMenu::init()
{
  if (popup_menu == NULL)
    {
      register_stock_items();
      create_actions();
      create_ui();
      post_init();
    }
  else
    {
      // Re-init.
      ui_manager->remove_action_group(action_group);
      create_actions();
      ui_manager->insert_action_group(action_group);
    }

  IGUI *gui = GUI::get_instance();
  MainWindow *main_window = gui->get_main_window();
  main_window->add_accel_group(ui_manager->get_accel_group());
}

void
MainGtkMenu::create_actions()
{
  IGUI *gui = GUI::get_instance();
  Menus *menus = gui->get_menus();

  action_group = Gtk::ActionGroup::create();

  action_group->add(Gtk::Action::create("Main", "_Tools"));

  // Mode menu
  Gtk::RadioAction::Group group_mode;
  action_group->add(Gtk::Action::create("Mode", _("_Mode")));

  action_group->add(Gtk::RadioAction::create(group_mode, "Normal", _("_Normal")),
                    sigc::mem_fun(*this, &MainGtkMenu::on_menu_normal));

  action_group->add(Gtk::RadioAction::create(group_mode, "Quiet", _("Q_uiet")), sigc::mem_fun(*this, &MainGtkMenu::on_menu_quiet));

  action_group->add(Gtk::RadioAction::create(group_mode, "Suspended", _("_Suspended")),
                    sigc::mem_fun(*this, &MainGtkMenu::on_menu_suspend));

  action_group->add(Gtk::ToggleAction::create("Reading", _("_Reading mode")), sigc::mem_fun(*this, &MainGtkMenu::on_menu_reading));

  // Networking menu
#ifdef HAVE_DISTRIBUTION
  action_group->add(Gtk::Action::create("Network", _("_Network")));
  action_group->add(Gtk::Action::create("Join", _("_Connect")), sigc::mem_fun(*menus, &Menus::on_menu_network_join));
  action_group->add(Gtk::Action::create("Disconnect", _("_Disconnect")), sigc::mem_fun(*menus, &Menus::on_menu_network_leave));
  action_group->add(Gtk::Action::create("Reconnect", _("_Reconnect")), sigc::mem_fun(*menus, &Menus::on_menu_network_reconnect));
  action_group->add(Gtk::ToggleAction::create("ShowLog", _("Show _log")), sigc::mem_fun(*this, &MainGtkMenu::on_menu_network_log));
#endif

  // Open
  action_group->add(Gtk::Action::create("Open", Gtk::Stock::OPEN), sigc::mem_fun(*menus, &Menus::on_menu_open_main_window));

  // Restbreak now
  // Gtk::AccelKey("<control>r"),
  action_group->add(Gtk::Action::create("Restbreak", Gtk::StockID("restbreak"), _("_Rest break")),
                    sigc::mem_fun(*menus, &Menus::on_menu_restbreak_now));

  // Preferences
  action_group->add(Gtk::Action::create("Preferences", Gtk::Stock::PREFERENCES, _("_Preferences")),
                    sigc::mem_fun(*menus, &Menus::on_menu_preferences));

#ifdef HAVE_EXERCISES
  action_group->add(Gtk::Action::create("Exercises", _("Exercises")), sigc::mem_fun(*menus, &Menus::on_menu_exercises));

#endif
  action_group->add(Gtk::Action::create("Statistics", _("Statistics")), sigc::mem_fun(*menus, &Menus::on_menu_statistics));

  action_group->add(Gtk::Action::create("About", Gtk::Stock::ABOUT, _("_About")), sigc::mem_fun(*menus, &Menus::on_menu_about));

  action_group->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT, _("_Quit")), sigc::mem_fun(*menus, &Menus::on_menu_quit));
}

void
MainGtkMenu::create_ui()
{
  Glib::ustring open_ui_info;

  if (show_open)
    {
      open_ui_info = "    <menuitem action='Open'/>";
    }

  // Layout the actions in a menubar and toolbar:
  Glib::ustring ui_info =
    "<ui>"
    "  <popup name='Menu'>"
    + open_ui_info +
    "    <separator/>" +
    "    <menuitem action='Restbreak'/>"
#ifdef HAVE_EXERCISES
    "    <menuitem action='Exercises'/>"
#endif
    "    <menuitem action='Statistics'/>"
    "    <menu action='Mode'>"
    "      <menuitem action='Normal'/>"
    "      <menuitem action='Quiet'/>"
    "      <menuitem action='Suspended'/>"
    "    </menu>"
#ifdef HAVE_DISTRIBUTION
    "    <menu action='Network'>"
    "      <menuitem action='Join'/>"
    "      <menuitem action='Disconnect'/>"
    "      <menuitem action='Reconnect'/>"
    "      <menuitem action='ShowLog'/>"
    "    </menu>"
#endif
    "    <menuitem action='Reading'/>"
    "    <separator/>"
    "    <menuitem action='Preferences'/>"
    "    <menuitem action='About'/>"
    "    <menuitem action='Quit'/>"
    "  </popup>"
    "</ui>";

  ui_manager = Gtk::UIManager::create();
  ui_manager->insert_action_group(action_group);

  try
    {
      ui_manager->add_ui_from_string(ui_info);
    }
  catch (const Glib::Error &)
    {
    }

  popup_menu = dynamic_cast<Gtk::Menu *>(ui_manager->get_widget("/Menu"));

#ifdef PLATFORM_OS_MACOS
  macos_popup_hack_connect(popup_menu);
#endif
}

void
MainGtkMenu::popup(const guint button, const guint activate_time)
{
  (void)button;

  if (popup_menu != NULL)
    {
      popup_menu->popup(button, activate_time);
    }
}

void
MainGtkMenu::resync(OperationMode mode, UsageMode usage, bool show_log)
{
  Gtk::CheckMenuItem *item = NULL;
  const char *menu_name = NULL;

  switch (mode)
    {
    case OPERATION_MODE_NORMAL:
      menu_name = "/Menu/Mode/Normal";
      break;

    case OPERATION_MODE_SUSPENDED:
      menu_name = "/Menu/Mode/Suspended";
      break;

    case OPERATION_MODE_QUIET:
      menu_name = "/Menu/Mode/Quiet";
      break;

    default:
      break;
    }

  if (menu_name)
    {
      item = dynamic_cast<Gtk::CheckMenuItem *>(ui_manager->get_widget(menu_name));
      if (item && !item->get_active())
        {
          item->set_active();
        }
    }

  item = dynamic_cast<Gtk::CheckMenuItem *>(ui_manager->get_widget("/Menu/Network/ShowLog"));
  if (item != NULL)
    {
      item->set_active(show_log);
    }

  item = dynamic_cast<Gtk::CheckMenuItem *>(ui_manager->get_widget("/Menu/Reading"));
  if (item)
    {
      bool reading = (usage == USAGE_MODE_READING);

      if (reading != item->get_active())
        item->set_active(reading);
    }
}

void
MainGtkMenu::on_menu_network_log()
{
  Glib::RefPtr<Gtk::Action> act = ui_manager->get_action("/Menu/Network/ShowLog");
  Glib::RefPtr<Gtk::ToggleAction> ract = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic(act);

  if (ract)
    {
      bool active = ract->get_active();
      IGUI *gui = GUI::get_instance();
      Menus *menus = gui->get_menus();
      menus->on_menu_network_log(active);
    }
}

void
MainGtkMenu::on_menu_normal()
{
  Glib::RefPtr<Gtk::Action> act = ui_manager->get_action("/Menu/Mode/Normal");
  Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic(act);

  if (ract)
    {
      bool active = ract->get_active();
      if (active)
        {
          IGUI *gui = GUI::get_instance();
          Menus *menus = gui->get_menus();
          menus->on_menu_normal();
        }
    }
}

void
MainGtkMenu::on_menu_suspend()
{
  Glib::RefPtr<Gtk::Action> act = ui_manager->get_action("/Menu/Mode/Suspended");
  Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic(act);

  if (ract)
    {
      bool active = ract->get_active();
      if (active)
        {
          IGUI *gui = GUI::get_instance();
          Menus *menus = gui->get_menus();

          menus->on_menu_suspend();
        }
    }
}

void
MainGtkMenu::on_menu_quiet()
{
  Glib::RefPtr<Gtk::Action> act = ui_manager->get_action("/Menu/Mode/Quiet");
  Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic(act);

  if (ract)
    {
      bool active = ract->get_active();
      if (active)
        {
          IGUI *gui = GUI::get_instance();
          Menus *menus = gui->get_menus();

          menus->on_menu_quiet();
        }
    }
}

void
MainGtkMenu::on_menu_reading()
{
  Glib::RefPtr<Gtk::Action> act = ui_manager->get_action("/Menu/Reading");
  Glib::RefPtr<Gtk::ToggleAction> ract = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic(act);

  if (ract)
    {
      bool active = ract->get_active();
      IGUI *gui = GUI::get_instance();
      Menus *menus = gui->get_menus();

      menus->on_menu_reading(active);
    }
}

#ifdef PLATFORM_OS_MACOS
// /* Taken from Gaim. needs to be gtkmm-ified. */
// /* This is a workaround for a bug in windows GTK+. Clicking outside of the
//    menu does not get rid of it, so instead we get rid of it as soon as the
//    pointer leaves the menu. */

void
MainGtkMenu::macos_popup_hack_connect(Gtk::Menu *menu)
{
  TRACE_ENTER("W32TrayMenu::macos_popup_hack_connect");

  GtkWidget *widget = (GtkWidget *)menu->gobj();
  g_signal_connect(widget, "leave-notify-event", G_CALLBACK(macos_popup_hack_leave_enter), NULL);
  g_signal_connect(widget, "enter-notify-event", G_CALLBACK(macos_popup_hack_leave_enter), NULL);

  TRACE_EXIT();
}

gboolean
MainGtkMenu::macos_popup_hack_hide(gpointer data)
{
  TRACE_ENTER("W32TrayMenu::macos_popup_hack_hide");
  if (data != NULL)
    {
      gtk_menu_popdown(GTK_MENU(data));
    }
  TRACE_EXIT();
  return FALSE;
}

gboolean
MainGtkMenu::macos_popup_hack_leave_enter(GtkWidget *menu, GdkEventCrossing *event, void *data)
{
  TRACE_ENTER("W32TrayMenu::macos_popup_hack_leave_enter");

  TRACE_MSG(event->type << " " << event->detail);

  (void)data;
  static guint hide_docklet_timer = 0;
  if (event->type == GDK_LEAVE_NOTIFY
      /* RC: it seems gtk now generate a GDK_NOTIFY_UNKNOWN when the menu if left...*/
      && (event->detail == GDK_NOTIFY_ANCESTOR || event->detail == GDK_NOTIFY_UNKNOWN))
    {
      /* Add some slop so that the menu doesn't annoyingly disappear
         when mousing around */
      TRACE_MSG("leave " << hide_docklet_timer);
      if (hide_docklet_timer == 0)
        {
          hide_docklet_timer = g_timeout_add(500, macos_popup_hack_hide, menu);
        }
    }
  else if (event->type == GDK_ENTER_NOTIFY && event->detail == GDK_NOTIFY_ANCESTOR)
    {

      TRACE_MSG("enter " << hide_docklet_timer);

      if (hide_docklet_timer != 0)
        {
          /* Cancel the hiding if we reenter */

          g_source_remove(hide_docklet_timer);
          hide_docklet_timer = 0;
        }
    }
  TRACE_EXIT();
  return FALSE;
}
#endif
