#include "stdafx.h"

#include "wb_context_ui.h"
#include "wb_command_ui.h"
#include "grt/common.h"
#include "grt/clipboard.h"

#include "wb_overview.h"
#include "wb_model_diagram_form.h"

#include "wb_component.h"

using namespace bec;
using namespace wb;



CommandUI::CommandUI(WBContext *wb)
  : _wb(wb)
{
}



static void save_menu_items(std::map<std::string, app_MenuItemRef> &map, const grt::ListRef<app_MenuItem> &items)
{
  for (size_t c= items.count(), i= 0; i < c; i++)
  {
    map[items[i]->name()]= items[i];

    save_menu_items(map, items[i]->subItems());
  }
}


void CommandUI::load_data()
{
  grt::GRT *grt= _wb->get_grt();
 
  // load menu and toolbar definitions
  grt::ListRef<app_MenuItem> items(grt::ListRef<app_MenuItem>::cast_from(
                   grt->unserialize(make_path(_wb->get_datadir(), "data/main_menu.xml"))));
  
  for (size_t c= items.count(), i= 0; i < c; i++)
  {
    _menu_items[items[i]->name()]= items[i];
    _main_menus.push_back(items.get(i));
    
    save_menu_items(_menu_items, items[i]->subItems());
  }
  
  
  _shortcuts= grt::ListRef<app_ShortcutItem>::cast_from(
                   grt->unserialize(make_path(_wb->get_datadir(), "data/shortcuts.xml")));
}


static std::string get_command_type(const std::string &command)
{
  std::vector<std::string> parts= split_string(command, ":");
  if (parts.size()>1)
    return parts[0];
  return "";
}



//--------------------------------------------------------------------------------
static std::string get_command_without_type(const std::string &command)
{
  std::vector<std::string> parts= split_string(command, ":", 1);
  if (parts.size()>1)
    return parts[1];
  return "";
}


static std::string get_command_name(const std::string &command)
{
  std::vector<std::string> parts= split_string(command, ":");
  if (parts.size()>1)
    return parts[1];
  return "";
}

static std::string get_command_arg(const std::string &command)
{
  std::vector<std::string> parts= split_string(command, ":", 2);
  if (parts.size()>2)
    return parts[2];
  return "";
}


static bool filter_context_platform(const app_CommandItemRef &item, const std::string &context)
{
  std::vector<std::string> plats(split_string(item->platform(), ","));

  if (!plats.empty())
  {
    std::string platform;
#ifdef _WIN32
    platform= "windows";
#elif defined(__APPLE__)
    platform= "macosx";
#else
    platform= "linux";
#endif

    if (std::find(plats.begin(), plats.end(), platform) == plats.end())
      return false;
  }

  if (item->context() == "" || item->context() == WB_CONTEXT_GLOBAL)
    return true;

  if (item->context() == context)
    return true;

  return false;
}

bool CommandUI::validate_command_item(const app_CommandItemRef &item)
{
  std::string name(item->name());
  std::string command(item->command());

  if (name == "exit_application")
    return true;

  if (command == "")
    return true;

  std::string cmdtype= get_command_type(command);
  std::string cmdname= get_command_name(command);
  std::string cmdarg= get_command_arg(command);
  
  ModelDiagramForm *active_view= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());
  bec::UIForm *active_form= _wb->get_active_form();

  if (has_prefix(command, "plugin:wb.export.export"))
  {
    if (cmdname == "wb.export.exportPNG" ||
      cmdname == "wb.export.exportPDF" ||
      cmdname == "wb.export.exportSVG" ||
      cmdname == "wb.export.exportPS")
      return active_view != 0;
  }
  else if (has_prefix(command, "plugin:wb.edit"))
  {
    if (cmdname == "wb.edit.goToNextSelected" || cmdname == "wb.edit.goToPreviousSelected")
      return active_view!=0 && active_view->has_selection() && active_view == active_form;
    else if (cmdname == "wb.edit.selectSimilar" || cmdname == "wb.edit.selectConnected")
      return active_view!=0 && active_view->get_selection().count()==1 && active_view == active_form;
  }
  else if (has_prefix(command, "plugin:wb.view"))
  {
    if (has_prefix(cmdname, "wb.view.setMarker"))
      return active_view != 0;
    else if (has_prefix(cmdname, "wb.view.goToMarker"))
    {
      model_ModelRef model;

      if (active_view)
        model= active_view->get_model_diagram()->owner();
      else if (dynamic_cast<OverviewBE*>(_wb->get_active_main_form()))
        model= dynamic_cast<OverviewBE*>(_wb->get_active_main_form())->get_model();

      if (model.is_valid() && model->markers().is_valid())
      {
        for (size_t c= model->markers().count(), i= 0; i < c; i++)
        {
          if (*model->markers().get(i)->name() == cmdarg)
            return true;
        }
      }
      return false;
    }
  }
  else if (cmdtype == "builtin")
    return validate_builtin_command(cmdname);

  if (cmdtype == "plugin")
  {
    app_PluginRef plugin(_wb->get_plugin_manager()->get_plugin(cmdname));

    if (plugin.is_valid())
      return _wb->check_plugin_runnable(plugin, cmdarg);
  }

  return true;
}


bool CommandUI::get_command_item_state(const app_CommandItemRef &item)
{
  std::string name(item->name());
  std::string command(item->command());

  std::string cmdtype= get_command_type(command);
  std::string cmdname= get_command_without_type(command);
  std::string cmdarg= get_command_arg(command);

  ModelDiagramForm *active_view= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());
//unused  bec::UIForm *active_form= get_active_form();

  if (cmdtype == "plugin")
  {
    if (cmdname == "wb.edit.toggleGridAlign")
      return active_view && active_view->get_diagram_options().get_int("AlignToGrid", 0);
    else if (cmdname == "wb.edit.toggleGrid")
      return active_view && active_view->get_diagram_options().get_int("ShowGrid", 1);
    else if (cmdname == "wb.edit.togglePageGrid")
      return active_view && active_view->get_diagram_options().get_int("ShowPageGrid", 1);
    else if (has_prefix(cmdname, "wb.view.setMarker:"))
    {
      if (active_view)
      {
        model_ModelRef model(active_view->get_model_diagram()->owner());
        std::string name= command.substr(strlen("plugin:wb.view.setMarker:"));
        for (size_t c= model->markers().count(), i= 0; i < c; i++)
        {
          if (*model->markers().get(i)->name() == cmdarg)
            return true;
        }
      }
      return false;
    }
    else if (has_prefix(cmdname, "wb.view.setFigureNotation"))
    {
      return active_view && workbench_physical_ModelRef::cast_from(active_view->get_model_diagram()->owner())->figureNotation() == cmdarg;
    }
    else if (has_prefix(cmdname, "wb.view.setRelationshipNotation"))
    {      
      return active_view && workbench_physical_ModelRef::cast_from(active_view->get_model_diagram()->owner())->connectionNotation() == cmdarg;
    }
  } 
  else if (cmdtype == "tool")
  {
    std::string tool= cmdname;

    if (active_view && tool == active_view->get_tool())
      return true;
  }
  else if (cmdtype == "option")
  {
    std::list<std::string> results;

    _wb->foreach_component(sigc::compose(
      sigc::mem_fun(results, &std::list<std::string>::push_back),
      sigc::bind<std::string>(sigc::mem_fun(&WBComponent::get_command_option_value), cmdname)));

    for (std::list<std::string>::iterator i= results.begin(); i != results.end(); ++i)
    {
      if (!i->empty())
        return *i == "1";
    }

    /*
    FOREACH_COMPONENT(_wb->_components, iter)
    {
      std::string value= (*iter)->get_command_option_value(cmdname);
      if (!value.empty())
        return value=="1";
    }*/
  }

  return false;
}


std::string CommandUI::get_command_item_caption(const app_CommandItemRef &item)
{
  std::string name(item->name());
  std::string command(item->command());

  if (command == "builtin:undo")
  {
    std::string descr(_wb->get_grt()->get_undo_manager()->undo_description());
    if (!descr.empty())
      return strfmt(_("Undo %s"), descr.c_str());
  }
  else if (command == "builtin:redo")
  {
    std::string descr(_wb->get_grt()->get_undo_manager()->redo_description());
    if (!descr.empty())
      return strfmt(_("Redo %s"), descr.c_str());
  }
  else if (command == "builtin:delete")
  {
    std::string descr;

    UIForm *form= _wb->get_active_form();
    if (form && !form->get_edit_target_name().empty())
      return strfmt(_("Delete %s"), form->get_edit_target_name().c_str());
    else
      return _("Delete");
  }
  else if (command == "builtin:copy")
  {
    std::string descr;

    UIForm *form= _wb->get_active_form();
    if (form && !form->get_edit_target_name().empty())
      return strfmt(_("Copy %s"), form->get_edit_target_name().c_str());
    else
      return _("Copy");
  }
  else if (command == "builtin:cut")
  {
    std::string descr;

    UIForm *form= _wb->get_active_form();
    if (form && !form->get_edit_target_name().empty())
      return strfmt(_("Cut %s"), form->get_edit_target_name().c_str());
    else
      return _("Cut");
  }
  else if (command == "builtin:paste")
  {
    std::string descr;
    Clipboard *clip= _wb->get_clipboard();

    if (clip && _wb->get_active_form() && !clip->get_content_description().empty())
      return strfmt(_("Paste %s"), clip->get_content_description().c_str());
    else
      return _("Paste");
  }
  return std::string();
}


//--------------------------------------------------------------------------------
// Keyboard and Shortcut Handling

static bool parse_key(const std::string &key, mdc::KeyInfo &info)
{
  static struct {
    mdc::KeyCode code;
    std::string name;
  } keys[]= {
    {mdc::KShift,    "Shift"},
    {mdc::KAlt,      "Alt"},
    {mdc::KControl,  "Control"},
    {mdc::KOption,   "Option"},
    {mdc::KCommand,  "Command"},
#ifdef __APPLE__
    {mdc::KCommand,  "Modifier"},
#else
    {mdc::KControl,  "Modifier"},
#endif

    {mdc::KF1,       "F1"},
    {mdc::KF2,       "F2"},
    {mdc::KF3,       "F3"},
    {mdc::KF4,       "F4"},
    {mdc::KF5,       "F5"},
    {mdc::KF6,       "F6"},
    {mdc::KF7,       "F7"},
    {mdc::KF8,       "F8"},
    {mdc::KF9,       "F9"},
    {mdc::KF10,      "F10"},
    {mdc::KF11,      "F11"},
    {mdc::KF12,      "F12"},

    {mdc::KLeft,     "Left"},
    {mdc::KRight,    "Right"},
    {mdc::KUp,       "Up"},
    {mdc::KDown,     "Down"},
    {mdc::KHome,     "Home"},
    {mdc::KEnd,      "End"},
    {mdc::KPageUp,   "PageUp"},
    {mdc::KPageDown, "PageDown"},

    {mdc::KInsert,   "Insert"},
    {mdc::KDelete,   "Delete"},
    {mdc::KBackspace,"Backspace"},
    {mdc::KBackspace,"BackSpace"},

    {mdc::KEscape,   "Escape"},
    {mdc::KTab,      "Tab"},
    {mdc::KEnter,    "Enter"},
    {mdc::KNone,         ""}
  };

  info.keycode= mdc::KNone;

  if (key.empty())
    return false;

  for (size_t i= 0; !keys[i].name.empty(); ++i)
    if (keys[i].name == key)
    {
      info.keycode= keys[i].code;
      break;
    }

  info.string= key;
#ifndef _WIN32
  if (info.string.length() == 1)
    info.string= tolower(info.string[0]);
#endif

  return true;
}

/* unused
static std::string format_key(const mdc::KeyInfo &key)
{
  static struct {
    mdc::KeyCode code;
    std::string name;
  } keys[]= {
    {mdc::KShift,    "Shift"},
    {mdc::KAlt,      "Alt"},
    {mdc::KControl,  "Control"},
    {mdc::KOption,   "Option"},
    {mdc::KCommand,  "Command"},

    {mdc::KF1,       "F1"},
    {mdc::KF2,       "F2"},
    {mdc::KF3,       "F3"},
    {mdc::KF4,       "F4"},
    {mdc::KF5,       "F5"},
    {mdc::KF6,       "F6"},
    {mdc::KF7,       "F7"},
    {mdc::KF8,       "F8"},
    {mdc::KF9,       "F9"},
    {mdc::KF10,      "F10"},
    {mdc::KF11,      "F11"},
    {mdc::KF12,      "F12"},

    {mdc::KLeft,     "Left"},
    {mdc::KRight,    "Right"},
    {mdc::KUp,       "Up"},
    {mdc::KDown,     "Down"},
    {mdc::KHome,     "Home"},
    {mdc::KEnd,      "End"},
    {mdc::KPageUp,   "PgUp"},
    {mdc::KPageDown, "PgDown"},

    {mdc::KInsert,   "Ins"},
    {mdc::KDelete,   "Del"},
    {mdc::KBackspace,"BackSpace"},

    {mdc::KEscape,   "Esc"},
    {mdc::KTab,      "Tab"},
    {mdc::KEnter,    "Enter"},
    {mdc::KNone,         ""}
  };

  std::string label;
  if (key.keycode != 0)
  {
    for (size_t i= 0; !keys[i].name.empty(); ++i)
      if (keys[i].code == key.keycode)
      {
        label= keys[i].name;
        break;
      }
  }
  else
  {
    gchar *tmp= g_utf8_strup(key.string.c_str(), (gssize)key.string.size());
    label= (label.empty()?"":"+")+std::string(tmp);
    g_free(tmp);
  }
  return label;
}
*/


static bool parse_shortcut(const std::string &shortcut, mdc::KeyInfo &key, mdc::EventState &mods)
{
  if (shortcut.empty())
    return false;

  std::vector<std::string> parts= split_string(shortcut, "+");
  mods= mdc::SNone;

  for (size_t c= parts.size()-1, i= 0; i < c; i++)
  {
    std::string mod= parts[i];
    if (mod == "control")
      mods= mods | mdc::SControlMask;
    else if (mod == "alt")
      mods= mods | mdc::SAltMask;
    else if (mod == "shift")
      mods= mods | mdc::SShiftMask;
    else if (mod == "option")
      mods= mods | mdc::SOptionMask;
    else if (mod == "command")
      mods= mods | mdc::SCommandMask;
#ifdef __APPLE__
    else if (mod == "modifier")
      mods= mods | mdc::SCommandMask;
#else
    else if (mod == "modifier")
      mods= mods | mdc::SControlMask;
#endif
    else
      return false;
  }

  if (!parse_key(parts.back(), key))
    return false;

  return true;
}


void CommandUI::append_shortcut_items(const grt::ListRef<app_ShortcutItem> &plist, const std::string &context,
      std::vector<WBShortcut> *items)
{
  std::string platform;

  if (!plist.is_valid())
    return;


  for (size_t c= plist.count(), i= 0; i < c; i++)
  {
    app_ShortcutItemRef shortcut(plist[i]);
    std::string item_context;

    if (!filter_context_platform(shortcut, context))
      continue;

    // filter by context
    if (shortcut->context().is_valid())
      item_context= shortcut->context();
    if (item_context.empty())
      item_context= WB_CONTEXT_GLOBAL;

    if (item_context == WB_CONTEXT_GLOBAL)
    {
       // if item is global, skip if there's another item with same name for current context
      if (context != WB_CONTEXT_GLOBAL)
      {
        bool dupe= false;

        for (size_t j= 0; j < c; j++)
        {
          if (j != i && *shortcut->platform() == *plist[j]->platform() &&
            *shortcut->name()!="" && *plist[j]->name() == *shortcut->name())
          {
            dupe= true;
            break;
          }
        }
        if (dupe)
          continue;
      }
    }

    WBShortcut item;

    item.name= shortcut->name();
    item.command= shortcut->command();
    item.shortcut= shortcut->shortcut();

    if (!parse_shortcut(shortcut->shortcut(), item.key, item.modifiers))
    {
      item.key.keycode= mdc::KNone;
      item.modifiers= mdc::SNone;
    }

    items->push_back(item);
  }
}


std::vector<WBShortcut> CommandUI::get_shortcuts_for_context(const std::string &context)
{
  std::vector<WBShortcut> shortcuts;

  append_shortcut_items(_shortcuts, context, &shortcuts);

  // view specific contextual shortcuts
  if (context == WB_CONTEXT_MODEL)
  {
    grt::ListRef<app_ShortcutItem> model_items;

    _wb->foreach_component(sigc::compose(
      sigc::bind<std::string, std::vector<WBShortcut>* >(sigc::mem_fun(this, &CommandUI::append_shortcut_items),
                                                        context, &shortcuts),
      sigc::mem_fun(&WBComponent::get_shortcut_items)));
  }

  return shortcuts;
}



//--------------------------------------------------------------------------------
// Menu Management


static MenuItemType get_menu_item_type(const std::string &type)
{
  if (type == "action")
    return MenuAction;
  else if (type == "separator")
    return MenuSeparator;
  else if (type == "radio")
    return MenuRadio;
  else if (type == "check")
    return MenuCheck;
  else if (type == "cascade")
    return MenuCascade;
  return MenuAction;
}


void CommandUI::append_menu_items(const grt::ListRef<app_MenuItem> &plist, const std::string &context,
                                  std::vector<bec::MenuItem> *items)
{
#ifdef EDITION_OSS
  bool hide_missing= true;
  //_wb->get_wb_options().get_int("workbench:OSSHideMissing", 0);
#endif

  for (size_t c= plist.count(), i= 0; i < c; i++)
  {
    bec::MenuItem item;
    app_MenuItemRef mitem= plist[i];
    std::string item_context;
    std::string name= mitem->name();
    std::string command= mitem->command();
    
    if (!filter_context_platform(mitem, context))
      continue;

    // filter by context
    if (mitem->context().is_valid())
      item_context= mitem->context();
    if (item_context.empty())
      item_context= WB_CONTEXT_GLOBAL;

    item.oid= mitem.id();
    item.type= get_menu_item_type(mitem->itemType());

    if (item_context == WB_CONTEXT_GLOBAL)
    {
       // if item is global, skip if there's another item with same name for current context
      if (context != WB_CONTEXT_GLOBAL && item.type != MenuSeparator)
      {
        bool dupe= false;

        for (size_t j= 0; j < c; j++)
        {
          if (!filter_context_platform(plist[j], context))
            continue;
          
          if (j != i && *plist[j]->name() == name && //filter_context_platform(plist[j], context))
            (*plist[j]->context() == context || *plist[j]->context() == WB_CONTEXT_GLOBAL))
          {
            dupe= true;
            break;
          }
        }
        if (dupe)
          continue;
      }
    }
    else if (context != item_context) 
      continue; // skip items in different context

    app_PluginRef plugin;
    
    item.shortcut= mitem->shortcut();

    if (item.type == MenuCascade)
    {
      item.caption= mitem->caption();
      item.name= name;
      item.enabled= validate_command_item(mitem);
    }
    else if (item.type != MenuSeparator)
    {
      item.name= command;
      item.caption= get_command_item_caption(mitem);
      if (item.caption.empty())
      {
        if (mitem->caption().is_valid() && *mitem->caption() != "")
          item.caption= mitem->caption();
      }

      if (get_command_type(command) == "plugin")
      {
        plugin= _wb->get_plugin_manager()->get_plugin(get_command_name(command));
        if (item.caption.empty() && plugin.is_valid())
          item.caption= plugin->caption();
        if (item.caption.empty())
          item.caption= command;

        // skip if the plugin is invalid
        if (!plugin.is_valid())
        {
          //continue;
          item.enabled= false;
        }
        else
          item.enabled= validate_command_item(mitem);
      }
      else if (get_command_type(command) == "builtin" || get_command_type(command) == "tool")
      {
        item.enabled= validate_command_item(mitem);
      }
      else
      {
        _wb->get_grt_manager()->get_grt()->send_warning("Invalid menu item command: "+command+"\n");
        continue;
      }
    }

    if (item.type == MenuCheck || item.type == MenuRadio)
      item.checked= get_command_item_state(mitem);
    else
      item.checked= false;

#ifdef EDITION_OSS
    if (bec::has_suffix(mitem.id(), "/SE"))
    {
      if (hide_missing)
        continue;
      if (item.type != MenuSeparator)
      {
        item.enabled= false;
        item.type= MenuUnavailable;
      }
    }
#endif

#ifdef _WIN32
    // turn mnemonic indicator from _ into & for windows
    item.caption= replace_string(item.caption, "_", "&");
#elif defined(__APPLE__)
    // remove _ in osx
    item.caption= replace_string(item.caption, "_", "");
#endif
    
    items->push_back(item);
  }
}



std::vector<bec::MenuItem> CommandUI::get_main_menus()
{
  std::vector<bec::MenuItem> items;
  std::string context= _wb->get_ui()->get_active_context();

  for (size_t c= _main_menus.size(), i= 0; i < c; i++)
  {
    bec::MenuItem item;
    app_MenuItemRef mitem(_main_menus[i]);
    std::string item_context;

    if (mitem->context().is_valid())
      item_context= mitem->context();
    if (item_context.empty())
      item_context= WB_CONTEXT_GLOBAL;

    if (item_context != context && item_context != WB_CONTEXT_GLOBAL)
      continue;

    item.oid= mitem.id();
    
#ifdef EDITION_OSS
    if (bec::has_suffix(mitem.id(), "/SE"))
    {
      //if (hide_missing)
        continue;
    }
#endif
    
    item.type= MenuCascade;
    item.caption= mitem->caption();
    item.name= mitem->name();

#ifdef _WIN32
    // turn mnemonic indicator from _ into & for windows
    item.caption= replace_string(item.caption, "_", "&");
#elif defined(__APPLE__)
    // remove _ in osx
    item.caption= replace_string(item.caption, "_", "");
#endif
    
    items.push_back(item);
  }
  return items;
}


std::vector<bec::MenuItem> CommandUI::get_menu_items(const std::string &menu)
{
  // special menu handling
  if (menu == "open_recent")
  {
    std::vector<bec::MenuItem> items;
    grt::StringListRef strlist(_wb->get_root()->options()->recentFiles());

    bec::MenuItem item;
    for (size_t c= std::min(strlist.count(), (size_t)10), i= 0; i < c; i++)
    {
      item.name= strfmt("plugin:wb.file.openRecentModel:%li", i+1);
      item.oid= item.name;
      if (i < 9)
      {
        item.caption= strfmt("%li %s", i+1, strlist.get(i).c_str());
#if !defined(_WIN32) && !defined(__APPLE__)
        item.caption= "_"+replace_string(item.caption, "_", "__");
#endif
      }
      else if (i == 9)
      {
        item.caption= strfmt("0 %s", strlist.get(i).c_str());
#if !defined(_WIN32) && !defined(__APPLE__)
        item.caption= "_"+replace_string(item.caption, "_", "__");
#endif
      }
      else
      {
        item.caption= strfmt(" %s", strlist.get(i).c_str());
#if !defined(_WIN32) && !defined(__APPLE__)
        item.caption= replace_string(item.caption, "_", "__");
#endif
      }
      item.type= MenuAction;
      items.push_back(item);
    }
    return items;
  }

  // standard menu handling
  app_MenuItemRef item(_menu_items[menu]);
  std::vector<bec::MenuItem> items;

  if (item.is_valid())
  {
    append_menu_items(item->subItems(), _wb->get_ui()->get_active_context(), &items);

    if (menu == "plugins")
    {
      // get sub-groups for the plugins menu

      grt::ListRef<app_PluginGroup> groups(_wb->get_root()->registry()->pluginGroups());

      for (size_t c= groups.count(), i= 0; i < c; i++)
      {
        app_PluginGroupRef group(groups.get(i));
        if (g_str_has_prefix(group->name().c_str(), "Menu/") && group->plugins().count() > 0)
        {
          bec::MenuItem item;
          item.name= std::string("plugins:").append(group->name().c_str());
          item.oid= item.name;
          item.caption= group->name().c_str()+5;
          item.enabled= 1;
          item.type= MenuCascade;
          items.push_back(item);
        }
      }
      if (items.empty())
      {
        bec::MenuItem item;
        item.oid= "placeholderforplugins";
        item.caption= "No Extra Plugins";
        item.enabled= false;
        item.type= MenuAction;
        items.push_back(item);
      }
    }
    return items;
  }
  else if (g_str_has_prefix(menu.c_str(), "plugins:"))
  {
    std::vector<app_PluginRef> plugins(_wb->get_plugin_manager()->get_plugins_for_group(menu.substr(8)));

    for (std::vector<app_PluginRef>::const_iterator iter= plugins.begin(); iter != plugins.end(); ++iter)
    {
      bec::MenuItem item;
      item.name= std::string("plugin:").append(*(*iter)->name());
      item.oid= item.name;
      item.caption= (*iter)->caption();
      item.enabled= _wb->check_plugin_runnable(*iter, "");
      item.type= MenuAction;
      items.push_back(item);
    }
    return items;
  }

  return std::vector<bec::MenuItem>();
}

//--------------------------------------------------------------------------------
// Toolbar Management

static ToolbarItemType get_toolbar_item_type(const std::string &type)
{
  if (type == "action")
    return ToolbarAction;
  else if (type == "separator")
    return ToolbarSeparator;
  else if (type == "toggle")
    return ToolbarToggle;
  else if (type == "radio")
    return ToolbarRadio;
  else if (type == "dropdown")
    return ToolbarDropDown;
  else if (type == "label")
    return ToolbarLabel;
  else if (type == "check")
    return ToolbarCheck;
  else if (type == "search")
    return ToolbarSearch;
  else
    throw std::runtime_error("invalid toolbar item type "+type);
  return ToolbarAction;
}


void CommandUI::append_toolbar_items(const grt::ListRef<app_ToolbarItem> &plist, const std::string &context,
                                     std::vector<WBToolbarItem> *items)
{
  if (!plist.is_valid())
    return;

  std::vector<WBShortcut> shortcuts(get_shortcuts_for_context(context));

  for (size_t c= plist.count(), i= 0; i < c; i++)
  {
    WBToolbarItem item;
    app_ToolbarItemRef titem(plist[i]);
    std::string item_context;

    if (!filter_context_platform(titem, context))
      continue;

    // filter by context
    if (titem->context().is_valid())
      item_context= titem->context();
    if (item_context.empty())
      item_context= WB_CONTEXT_GLOBAL;
    item.type= get_toolbar_item_type(titem->itemType());

    if (item_context == WB_CONTEXT_GLOBAL)
    {
       // if item is global, skip if there's another item with same name for current context
      if (context != WB_CONTEXT_GLOBAL && item.type != ToolbarSeparator)
      {
        bool dupe= false;

        for (size_t j= 0; j < c; j++)
        {
          if (j != i && *titem->name()!="" && *plist[j]->name() == *titem->name())
          {
            dupe= true;
            break;
          }
        }
        if (dupe)
          continue;
      }
    }
    
    item.name= titem->name();
    item.command= titem->command();

    std::string shortcut;
    for (std::vector<WBShortcut>::const_iterator iter= shortcuts.begin(); iter != shortcuts.end(); ++iter)
    {
      if (iter->command == item.command)
      {
        shortcut= iter->shortcut;
        break;
      }
    }

    std::string tooltip= get_command_item_caption(titem);
    if (tooltip.empty())
      tooltip= titem->tooltip();
    if (shortcut.empty())
      item.tooltip= tooltip;
    else
      item.tooltip= strfmt("%s (%s)", tooltip.c_str(), shortcut.c_str());

    if (item.type == ToolbarLabel)
    {
      // nothing
    }
    else if (item.type == ToolbarDropDown)
    {
      item.enabled= validate_command_item(titem);
    }
    else if (item.type != ToolbarSeparator)
    {
      if (item.type == ToolbarAction || item.type == ToolbarRadio || item.type == ToolbarToggle)
      {
        item.icon= IconManager::get_instance()->get_icon_id(*titem->icon());
        if (titem->altIcon().is_valid() && *titem->altIcon()!="")
          item.alt_icon= IconManager::get_instance()->get_icon_id(*titem->altIcon());
        else
          item.alt_icon= 0;
      }
      else if (item.type == ToolbarCheck)
        item.caption= titem->icon();

      if (get_command_type(titem->command()) == "plugin")
      {
        app_PluginRef plugin(_wb->get_plugin_manager()->get_plugin(get_command_name(titem->command())));

        // skip if the plugin is invalid
        if (!plugin.is_valid())
        {
          //continue;
          item.enabled= false;
        }
        else
        {
          item.enabled= validate_command_item(titem);
          if (item.tooltip.empty())
            item.tooltip= plugin->description();
        }
      }
      else if (get_command_type(titem->command()) == "builtin" || get_command_type(titem->command()) == "tool")
      {
        item.enabled= validate_command_item(titem);
      }
      else if (get_command_type(titem->command()) == "option")
        ;
      else
      {
        _wb->get_grt_manager()->get_grt()->send_warning("Invalid toolbar item command: "+*titem->command()+"\n");
        continue;
      }
    }

    if (item.type == ToolbarRadio || item.type == ToolbarToggle || item.type == ToolbarCheck)
      item.checked= get_command_item_state(titem);
    else
      item.checked= false;

    items->push_back(item);
  }
}


std::vector<WBToolbarItem> CommandUI::get_toolbar_items(const std::string &toolbar)
{
  std::vector<WBToolbarItem> items;

  // standard toolbar handling
  grt::ListRef<app_ToolbarItem> tbar_items;
  
  tbar_items= _wb->get_model_context()->get_toolbar_items(toolbar);
  
  if (tbar_items.is_valid())
    append_toolbar_items(tbar_items, _wb->get_ui()->get_active_context(), &items);

  return items;
}


std::vector<std::string> CommandUI::get_dropdown_items(const std::string &name, const std::string &option, std::string &selected)
{
  std::vector<std::string> items;
  WBComponent *compo;

  compo= _wb->get_component_named(split_string(name, "/")[0]);
  if (compo)
  {
    items= compo->get_command_dropdown_items(get_command_without_type(option));
    selected= compo->get_command_option_value(get_command_without_type(option));
  }
  return items;
}


void CommandUI::select_dropdown_item(const std::string &name, const std::string &option, const std::string &item)
{
  WBComponent *compo;

  compo= _wb->get_component_named(split_string(name, "/")[0]);
  if (compo)
    compo->set_command_option_value(get_command_without_type(option), item);
}


void CommandUI::toggle_checkbox_item(const std::string &name, const std::string &option, bool state)
{
  WBComponent *compo;

  compo= _wb->get_component_named(split_string(name, "/")[0]);
  if (compo)
    compo->set_command_option_value(get_command_without_type(option), state?"1":"0"); 
}




//--------------------------------------------------------------------------------


static bool has_active_view(WBContext *wb)
{
  return dynamic_cast<ModelDiagramForm*>(wb->get_active_form()) != 0;
}


void CommandUI::add_frontend_commands(const std::list<std::string> &commands)
{
  for (std::list<std::string>::const_iterator iter= commands.begin();
    iter != commands.end(); ++iter)
  {
    // hack
    if (iter->compare("diagram_size")==0)
      add_builtin_command(*iter, 
        sigc::bind<std::string>(_wb->perform_command, *iter),
        sigc::bind<WBContext*>(sigc::ptr_fun(has_active_view), _wb));
    else
      add_builtin_command(*iter, sigc::bind<std::string>(_wb->perform_command, *iter));
  }
}


void CommandUI::add_builtin_command(const std::string &name,
                                      const sigc::slot<void> &slot,
                                      const sigc::slot<bool> &validate)
{
  BuiltinCommand cmd;

  cmd.execute= slot;
  cmd.validate= validate;

  if (_builtin_commands.find(name) != _builtin_commands.end())
    g_message("%s built-in command is being overwritten", name.c_str());
  
  _builtin_commands[name]= cmd;
}


void CommandUI::add_builtin_form_command(const std::string &name, 
      const sigc::slot<void,bec::UIForm*> &slot, 
      const sigc::slot<bool,bec::UIForm*> &validate,
      bool main_form)
{
  add_builtin_command(name, 
    sigc::bind<sigc::slot<void,bec::UIForm*>, bool>(
          sigc::mem_fun(this, &CommandUI::execute_active_form_command), slot, main_form),
    sigc::bind<sigc::slot<bool,bec::UIForm*>, bool>(
          sigc::mem_fun(this, &CommandUI::validate_active_form_command), validate, main_form));
}


void CommandUI::execute_active_form_command(const sigc::slot<void,bec::UIForm*> &slot, bool main_form)
{
  bec::UIForm *form= main_form ? _wb->get_active_main_form() : _wb->get_active_form();

  if (form)
    slot(form);
}


bool CommandUI::validate_active_form_command(const sigc::slot<bool,bec::UIForm*> &slot, bool main_form)
{
  bec::UIForm *form= main_form ? _wb->get_active_main_form() : _wb->get_active_form();

  if (form)
    return slot(form);
  return false;
}


bool CommandUI::is_builtin_command(const std::string &name)
{
  return (_builtin_commands.find(name) != _builtin_commands.end());
}


bool CommandUI::execute_builtin_command(const std::string &name)
{
  if (_builtin_commands.find(name) != _builtin_commands.end())
  {
    _builtin_commands[name].execute();
    return true;
  }
  return false;
}


bool CommandUI::validate_builtin_command(const std::string &name)
{
  if (_builtin_commands.find(name) != _builtin_commands.end())
  {
    if (_builtin_commands[name].validate)
      return _builtin_commands[name].validate();
    return true;
  }
  return false;
}



//--------------------------------------------------------------------------------


void CommandUI::activate_command_for_object(const GrtObjectRef &object, const std::string &command)
{
 try
  {
    std::vector<std::string> parts= split_string(command, ":");
    std::string type= parts[0];
    std::string cmdname= parts[1];

//    bec::UIForm *active_form= get_active_form();

    if (type == "plugin")
    {
      grt::BaseListRef args(_wb->get_grt_manager()->get_grt());

      if (parts.size() > 2)
      {  
        args.ginsert(grt::StringRef(parts[2]));
        args.ginsert(object);
        _wb->execute_plugin(cmdname, args);
      }
      else
      {
        args.ginsert(object);
        _wb->execute_plugin(cmdname, args);
      }
    }
    else if (type == "builtin")
    {
      execute_builtin_command(cmdname);
    }
    else
      throw std::runtime_error("Unhandled command type "+type);
  }
  catch (grt::grt_runtime_error &error)
  {
    _wb->show_exception(command, error);
  }
}


void CommandUI::activate_command(const std::string &command)
{
  if (command.empty() || !_wb->user_interaction_allowed())
    return;

  if (command.find(':') == std::string::npos)
    return;
  
  try
  {
    std::vector<std::string> parts= split_string(command, ":");
    std::string type= parts[0];
    std::string cmdname= parts[1];

//    bec::UIForm *active_form= get_active_form();

    if (type == "tool")
    {
      ModelDiagramForm *active_view= dynamic_cast<ModelDiagramForm*>(_wb->get_active_main_form());
      if (active_view)
        active_view->set_tool(cmdname);
    }
    else if (type == "builtin")
    {
      if (!execute_builtin_command(cmdname))
        throw std::runtime_error(strfmt("Unrecognized command %s", cmdname.c_str()));
    }
    else if (type == "plugin")
    {
      if (parts.size() > 2)
      {
        grt::BaseListRef args(_wb->get_grt_manager()->get_grt());
        args.ginsert(grt::StringRef(parts[2]));
        _wb->execute_plugin(cmdname, args);
      }
      else
      {
        _wb->execute_plugin(cmdname);
      }
    }
  }
  catch (grt::grt_runtime_error &error)
  {
    _wb->show_exception(command, error);
  }
  catch (const std::exception& e)
  {
    _wb->show_exception(command, e);
  }
}

