/* 
 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. 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; version 2 of the
 * License.
 * 
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "stdafx.h"

#include "wb_live_schema_tree.h"
#include "grtdb/charset_utils.h"

#include "base/string_utilities.h"
#include "base/sqlstring.h"

using namespace wb;
using namespace bec;
using namespace base;
using namespace grt;

#define TABLES_NODE_INDEX 0
#define VIEWS_NODE_INDEX 1
#define ROUTINES_NODE_INDEX 2

#define TABLE_COLUMNS_NODE_INDEX 0
#define TABLE_INDEXES_NODE_INDEX 1
#define TABLE_FOREIGN_KEYS_NODE_INDEX 2
#define TABLE_TRIGGERS_NODE_INDEX 3


const char* LiveSchemaTree::_schema_tokens[15] = {"",                                                           // Empty item to use 0 as not found
                                                  "CASCADE", "SET NULL", "SET DEFAULT", "RESTRICT", "NO ACTION", // The update/delete rules on foreign keys
                                                  "BTREE", "FULLTEXT", "HASH", "RTREE",                          // The index types
                                                  "INSERT", "UPDATE", "DELETE",                                  // Trigger events
                                                  "BEFORE", "AFTER"};                                            // Trigger timing

static bool compare_schema_node(const LiveSchemaTree::SchemaNode &a, const LiveSchemaTree::SchemaNode &b)
{
  return a.name < b.name;
}

LiveSchemaTree::ObjectNode::ObjectNode():
  fetched(false), 
  fetching(false),
  expanded(false)
{
}

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

void LiveSchemaTree::ObjectNode::copy(const ObjectNode &node, bool full)
{
  name = node.name;

  if (full || (node.details.length() > 0))
    details = node.details;

  fetched = node.fetched;
  fetching = node.fetching;
  expanded = node.expanded;
}

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

LiveSchemaTree::ObjectNode& LiveSchemaTree::ObjectNode::operator=(const ObjectNode &node)
{
  copy(node, true);
  return *this;
}

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

LiveSchemaTree::ViewNode::ViewNode():
  ObjectNode(), 
  columns_load_error(false),
  columns_loaded(false), 
  columns_expanded(false) 
{
}

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

void LiveSchemaTree::ViewNode::copy(const ObjectNode &node, bool full)
{
  ObjectNode::copy(node, full);

  const ViewNode *pnode = dynamic_cast<const ViewNode*>(&node);

  if (pnode)
  {
    if (full || pnode->columns_loaded)
    {
      columns = pnode->columns;
      columns_loaded = pnode->columns_loaded;
      columns_expanded = pnode->columns_expanded;
      columns_load_error = pnode->columns_load_error;
    }
  }
}

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

LiveSchemaTree::ObjectNode& LiveSchemaTree::ViewNode::operator=(const ObjectNode &node)
{
  copy(node, true);
  return *this;
}

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

LiveSchemaTree::ViewNode& LiveSchemaTree::ViewNode::operator=(const ViewNode &node)
{
  operator=(dynamic_cast<const ObjectNode&>(node));
  return *this;
}

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

LiveSchemaTree::TableNode::TableNode():
  ViewNode(), 
  triggers_loaded(false), 
  indexes_loaded(false), 
  foreign_keys_loaded(false),
  triggers_expanded(false), 
  indexes_expanded(false), 
  foreign_keys_expanded(false)
{
}

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

int LiveSchemaTree::TableNode::get_loaded_mask()
{
  int loaded_mask = ViewNode::get_loaded_mask();

  loaded_mask |= foreign_keys_loaded ? FK_DATA : 0;
  loaded_mask |= triggers_loaded ? TRIGGER_DATA : 0;
  loaded_mask |= indexes_loaded ? INDEX_DATA : 0;

  return loaded_mask;
}

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

void LiveSchemaTree::TableNode::copy(const ObjectNode &node, bool full)
{
  ViewNode::copy(node, full);

  const TableNode *pnode = dynamic_cast<const TableNode*>(&node);

  if (pnode)
  {
    if (full || pnode->triggers_loaded)
    {
      triggers = pnode->triggers;
      triggers_loaded = pnode->triggers_loaded;
      triggers_expanded = pnode->triggers_expanded;
    }

    if(full || pnode->indexes_loaded)
    {
      indexes = pnode->indexes;
      indexes_loaded = pnode->indexes_loaded;
      indexes_expanded = pnode->indexes_expanded;
    }

    if(full || pnode->foreign_keys_loaded)
    {
      foreign_keys = pnode->foreign_keys;
      foreign_keys_loaded = pnode->foreign_keys_loaded;
      foreign_keys_expanded = pnode->foreign_keys_expanded;
    }
  }
}

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

LiveSchemaTree::ObjectNode& LiveSchemaTree::TableNode::operator=(const ObjectNode &node)
{
  copy(node, true);
  return *this;
}

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

LiveSchemaTree::TableNode& LiveSchemaTree::TableNode::operator=(const TableNode &node)
{
  operator=(dynamic_cast<const ObjectNode&>(node));
  return *this;
}

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

LiveSchemaTree::IndexNode* LiveSchemaTree::TableNode::get_index(const std::string &name)
{
  IndexNode* found_index = NULL;
  std::vector<IndexNode>::iterator end = indexes.end(),
                                    index = indexes.begin();

  while(!found_index && index != end)
  {
    if(index->name == name)
      found_index = &(*index);
    else
      index++;
  }

  return found_index;
}

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

LiveSchemaTree::FKNode* LiveSchemaTree::TableNode::get_foreign_key(const std::string &name)
{
  FKNode* found_fk = NULL;
  std::vector<FKNode>::iterator end = foreign_keys.end(),
                                index = foreign_keys.begin();

  while(!found_fk && index != end)
  {
    if(index->name == name)
      found_fk = &(*index);
    else
      index++;
  }

  return found_fk;
}

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

LiveSchemaTree::SchemaNode::SchemaNode():
  fetched(false), 
  fetching(false), 
  expanded(false), 
  tables_expanded(false), 
  views_expanded(false), 
  routines_expanded(false) 
{
}

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

void LiveSchemaTree::SchemaNode::add_node(ObjectType type, std::string name)
{
  switch(type)
  {
  case Any:
  case Schema:
    break;
  case Table:
    {
      TableNode tnode;
      tnode.name = name;
      tables.push_back(tnode);
    }
    break;
  case View:
    {
      ViewNode  vnode;
      vnode.name = name;
      views.push_back(vnode);
    }
    break;
  case Routine:
    {
      ObjectNode onode;
      onode.name = name;
      routines.push_back(onode);
    }
    break;
  }

};

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

void LiveSchemaTree::SchemaNode::delete_node(std::string name, ObjectType type)
{
  bool found = false;

  switch(type)
  {
  case Any:
  case Schema:
    break;
  case Table:
    for(size_t index = 0; !found && (index < tables.size()); index++)
    {
      if(name == tables[index].name)
      {
        found = true;
        tables.erase(tables.begin() + index);
        break;
      }
    }
    break;
  case View:
    for(size_t index = 0; !found && (index < views.size()); index++)
    {
      if(name == views[index].name)
      {
        found = true;
        views.erase(views.begin() + index);
        break;
      }
    }
    break;
  case Routine:
    for(size_t index = 0; !found && (index < routines.size()); index++)
    {
      if(name == routines[index].name)
      {
        found = true;
        routines.erase(routines.begin() + index);
        break;
      }
    }
    break;
  }
};

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

void LiveSchemaTree::SchemaNode::sort_nodes(ObjectType type)
{
  switch(type)
  {
  case Any:
  case Schema:
    break;
  case Table:
    std::sort(tables.begin(), tables.end());
    break;
  case View:
    std::sort(views.begin(), views.end());
    break;
  case Routine:
    std::sort(routines.begin(), routines.end());
    break;
  }
}

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

bool LiveSchemaTree::SchemaNode::filter_data(ObjectType object_type, GPatternSpec* schema_pattern, GPatternSpec* object_pattern, SchemaNode& target) const
{
  bool ret_val = false;
        
  if (fetched || (schema_pattern && !object_pattern))
  {
    if (g_pattern_match_string(schema_pattern, name.c_str()))
    {
      if (object_type == Schema || !object_pattern)
      {
        ret_val = true;
        target = *this;
      }
      else
      {
        SchemaNode schema;
        schema.name = name;
        schema.expanded = expanded;
        schema.fetched = fetched;
        schema.fetching = fetching;
        schema.tables_expanded = tables_expanded;
        schema.views_expanded = views_expanded;
        schema.routines_expanded = routines_expanded;

        if(object_type == Table || object_type == Any)
        {
            for(size_t index = 0; index < tables.size(); index++)
            {
              if (g_pattern_match_string(object_pattern, tables[index].name.c_str()))
                schema.tables.push_back(tables[index]);
            }

            if(schema.tables.size())
              ret_val = true;
        }
            
        if(object_type == View || object_type == Any)
        {
            for(size_t index = 0; index < views.size(); index++)
            {
              if (g_pattern_match_string(object_pattern, views[index].name.c_str()))
                schema.views.push_back(views[index]);
            }

            if(schema.views.size())
              ret_val = true;
        }
            
        if(object_type == Routine || object_type == Any)
        {
            for(size_t index = 0; index < routines.size(); index++)
            {
              if (g_pattern_match_string(object_pattern, routines[index].name.c_str()))
                schema.routines.push_back(routines[index]);
            }

            if(schema.routines.size())
              ret_val = true;
        }

        target = schema;
      }
    }
  }

  return ret_val;
}

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

LiveSchemaTree::LiveSchemaTree(grt::GRT* grt)
: _grt(grt), _schemata_vector_serial(0), _rebuilding(false), _case_sensitive_identifiers(false), 
_is_schema_contents_enabled(true), _auto_fetch_columns(false), _base(0), _filter_type(Any), _schema_pattern(0), _object_pattern(0)
{
  // TODO: Autofetch columns should is disabled for now
  //       Should be enabled when autocompletion is implemented
}

void LiveSchemaTree::set_fetch_delegate(boost::shared_ptr<FetchDelegate> delegate)
{
  _fetch_delegate= delegate;
}

void LiveSchemaTree::set_delegate(boost::shared_ptr<Delegate> delegate)
{
  _delegate= delegate;
}

unsigned char LiveSchemaTree::internalize_token(const std::string& token)
{
  unsigned char found_index = 0;
  for(unsigned char index =1; !found_index && (index < (sizeof(_schema_tokens)/sizeof(char*))); index++)
  {
    if (token == _schema_tokens[index])
      found_index = index;
  }

  return found_index;
}

std::string LiveSchemaTree::externalize_token(unsigned char c)
{
  return (c > 0 && c < (sizeof(_schema_tokens)/sizeof(char*))) ? _schema_tokens[c] : "";
}

void LiveSchemaTree::set_case_sensitive_identifiers(bool flag)
{
  _case_sensitive_identifiers = flag;
}

bool LiveSchemaTree::identifiers_equal(const std::string &a, const std::string &b)
{
  if (_case_sensitive_identifiers)
    return a == b;
  return g_strcasecmp(a.c_str(), b.c_str()) == 0;
}

LiveSchemaTree::ObjectNode * LiveSchemaTree::get_object_node_by_index(const bec::NodeId &index_node, ObjectType *object_type)
{
  if(_schemata.empty())
      return NULL;
  ObjectNode* obj_node = NULL;
  ObjectType obj_type;
  SchemaNode &snode(_schemata[index_node[0]]);

  switch (index_node[1])
  {
    case TABLES_NODE_INDEX:
      obj_type= Table;
      if(index_node[2] < (int)snode.tables.size())
        obj_node = &snode.tables[index_node[2]];
      break;
    case VIEWS_NODE_INDEX:
      obj_type= View;
      if(index_node[2] < (int)snode.views.size())
        obj_node = &snode.views[index_node[2]];
      break;
    case ROUTINES_NODE_INDEX:
      obj_type= Routine;
      if(index_node[2] < (int)snode.routines.size())
        obj_node = &snode.routines[index_node[2]];
      break;
  }

  if(obj_node && object_type)
    *object_type = obj_type;

  return obj_node;
}

LiveSchemaTree::ObjectNode * LiveSchemaTree::get_object_node_by_type_and_name(const std::string &schema_name, ObjectType obj_type, std::string name, bec::NodeId *node_ret)
{
  int index = get_index_of_schema(schema_name);
  if (index >= 0 && index < (int) _schemata.size())
  {
    SchemaNode &snode(_schemata[index]);
    if (node_ret)
      *node_ret = bec::NodeId(index);
    return get_object_node_by_type_and_name(&snode, obj_type, name, node_ret);
  }
  return 0;
}

LiveSchemaTree::ObjectNode * LiveSchemaTree::get_object_node_by_type_and_name(SchemaNode *schema_node, ObjectType obj_type, std::string name, bec::NodeId *node_ret)
{
  ObjectNode *obj_node = NULL;

  if (node_ret && node_ret->depth() == 0)
    *node_ret = bec::NodeId(std::find(_schemata.begin(), _schemata.end(), *schema_node) - _schemata.begin());

  switch (obj_type)
  {
    case Any:
    case Schema:
      break;
    case Table: 
      if (node_ret)
        node_ret->append(0); 
      for(size_t index = 0; index < schema_node->tables.size() && !obj_node; index++)
      {
        if(schema_node->tables[index].name == name)
        {
          obj_node = &schema_node->tables[index];
          if (node_ret) node_ret->append(index);
        }
      }
      break;
    case View: 
      if (node_ret)
        node_ret->append(1); 
      for(size_t index = 0; index < schema_node->views.size() && !obj_node; index++)
      {
        if(schema_node->views[index].name == name)
        {
          obj_node = &schema_node->views[index];
          if (node_ret) node_ret->append(index);
        }
      }
      break;
    case Routine: 
      if (node_ret)
        node_ret->append(2); 
      for(size_t index = 0; index < schema_node->routines.size() && !obj_node; index++)
      {
        if(schema_node->routines[index].name == name)
        {
          obj_node = &schema_node->routines[index];
          if (node_ret) node_ret->append(index);
        }
      }
      break;
  }

  return obj_node;
}

LiveSchemaTree::ObjectNode * LiveSchemaTree::get_object_node_by_type_and_index(SchemaNode *schema_node, ObjectType obj_type, size_t index)
{
  ObjectNode *obj_node = NULL;

  switch (obj_type)
  {
    case Any:
    case Schema:
      break;
    case Table: 
      if (index < schema_node->tables.size())
        obj_node = &schema_node->tables[index];
      break;
    case View: 
      if (index < schema_node->views.size())
        obj_node = &schema_node->views[index];
      break;
    case Routine: 
      if(index < schema_node->routines.size())
        obj_node = &schema_node->routines[index];
      break;
  }

  return obj_node;
}

void LiveSchemaTree::refresh()
{
  refresh_ex(false);
}


void LiveSchemaTree::refresh_ex(bool hard_refresh)
{
  std::list<std::string> schema_list;
  bool refresh_schema_list = true;

  if (hard_refresh)
  {
    // Gets the schema list from the database...
    if (boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock())
      schema_list = delegate->fetch_schema_list();
    else
      refresh_schema_list = false;
  }
  else
  {
    // Uses the same schema list...
    if (_base)
      for(size_t index = 0; index < _base->_schemata.size(); index++)
        schema_list.push_back(_base->_schemata[index].name);
    else
      for(size_t index = 0; index < _schemata.size(); index++)
        schema_list.push_back(_schemata[index].name);
  }

  if(refresh_schema_list)
    refresh_ex(schema_list, hard_refresh);
}


void LiveSchemaTree::refresh_ex(const std::list<std::string> &schema_list, bool hard_refresh)
{
  std::list<std::string> current_schema_list;

  if(_base)
  {
    _base->refresh_ex(schema_list, hard_refresh);
    _base->filter_data(*this);
  }
  else
  {
    std::map<std::string, SchemaNode> old_schemata; // or expanded nodes, if it's a hard_refresh

    if (!hard_refresh)
    {
      // save old data in a map so that we can reuse the contents
      for (std::vector<SchemaNode>::const_iterator iter= _schemata.begin();
           iter != _schemata.end(); ++iter)
      {
        old_schemata[iter->name]= *iter;
      }
    }
    else
    {
      // save state of the expanded schema nodes
      for (std::vector<SchemaNode>::iterator iter= _schemata.begin();
           iter != _schemata.end(); ++iter)
      {
        if (iter->expanded)
        {
           iter->fetched= false;
           iter->fetching= false;
           iter->tables.clear();
           iter->views.clear();
           iter->routines.clear();
           old_schemata[iter->name]= *iter;
        }
      }
    }
    _schemata.clear();
    _schemata.reserve(schema_list.size());
    for (std::list<std::string>::const_iterator iter= schema_list.begin();
         iter != schema_list.end(); ++iter)
    {
      if (old_schemata.find(*iter) == old_schemata.end())
      {
        SchemaNode node;
        node.name= *iter;
        node.fetched= false;
        node.fetching= false;
        node.expanded= false;
        _schemata.push_back(node);
      }
      else
        _schemata.push_back(old_schemata[*iter]);
    }
  
    std::sort(_schemata.begin(), _schemata.end(), compare_schema_node);
  }

  _schemata_vector_serial++;

  if (hard_refresh)
    tree_changed();
}


bool LiveSchemaTree::is_expanded(const bec::NodeId &node)
{
  if (node.depth() && node[0] >= 0 && node[0] < (int)_schemata.size())
  {
    if (node.depth() == 1)
        return _schemata[node[0]].expanded;
    else if (node.depth() == 2)
    {
      switch (node[1])
      {
        case TABLES_NODE_INDEX: return _schemata[node[0]].tables_expanded;
        case VIEWS_NODE_INDEX: return _schemata[node[0]].views_expanded;
        case ROUTINES_NODE_INDEX: return _schemata[node[0]].routines_expanded;
      }
    }
    else if (node.depth() == 3)
    {
      if (node[1] == TABLES_NODE_INDEX || node[1] == VIEWS_NODE_INDEX)
      {
        ViewNode *object = dynamic_cast<ViewNode*>(get_object_node_by_index(node));
        return object->expanded;
      }
    }
    else if (node.depth() == 4)
    {
      if (node[1] == TABLES_NODE_INDEX) // Load needed stuff for tables
      {
        TableNode *table = dynamic_cast<TableNode*>(get_object_node_by_index(node));

        if (table)
        {
          switch (node[3])
          {
            case TABLE_COLUMNS_NODE_INDEX: return table->columns_expanded; break;
            case TABLE_INDEXES_NODE_INDEX: return table->indexes_expanded; break;
            case TABLE_FOREIGN_KEYS_NODE_INDEX: return table->foreign_keys_expanded; break;
            case TABLE_TRIGGERS_NODE_INDEX: return table->triggers_expanded; break;
          }
        }
      }
    }
  }
  return false;
}


int LiveSchemaTree::count_children(const bec::NodeId &node)
{
  if (node.depth() == 0)
    return _schemata.size();
  else if (!_is_schema_contents_enabled)
    return 0;
  else if (node.depth() == 1)
  {
    return 3;
  }
  else if (node.depth() == 2)
  {
    SchemaNode &snode(_schemata[node[0]]);
    switch (node[1])
    {
      case TABLES_NODE_INDEX: return snode.tables.size();
      case VIEWS_NODE_INDEX: return snode.views.size();
      case ROUTINES_NODE_INDEX: return snode.routines.size();
    }
    return 0;
  }
  else if (node.depth() == 3)
  {
    switch (node[1])
    {
    case TABLES_NODE_INDEX: 
        return 4; // tables
    case VIEWS_NODE_INDEX:
      {
        ViewNode *view_node = dynamic_cast<ViewNode*>(get_object_node_by_index(node));
        if (view_node)
          return view_node->columns.size();
      }
    }
    return 0;
  }
  else if (node.depth() == 4)
  {
    int ret_val = 0;
    ObjectNode *obj_node= get_object_node_by_index(node);
    ViewNode *view_node = dynamic_cast<ViewNode*>(obj_node);
    TableNode *table_node = dynamic_cast<TableNode*>(obj_node);

    switch (node[3])
    {
      case TABLE_COLUMNS_NODE_INDEX:
        if (view_node)
          ret_val = view_node->columns.size();
        break;
      case TABLE_INDEXES_NODE_INDEX:
        if (table_node)
          ret_val = table_node->indexes.size();
        break;
      case TABLE_FOREIGN_KEYS_NODE_INDEX:
        if (table_node)
          ret_val = table_node->foreign_keys.size();
        break;
      case TABLE_TRIGGERS_NODE_INDEX:
        if (table_node)
          ret_val = table_node->triggers.size();
        break;        
    }
    return ret_val;
  }
  return 0;
}


bool LiveSchemaTree::get_field(const bec::NodeId &node, int column, std::string &value)
{
  if (_schemata.empty())
  {
    return false;
  }
  if ((node.depth() > 0) && ((int)_schemata.size() <= node[0]))
    return false;

  if (node.depth() == 1)
  {
    value= _schemata[node[0]].name;
    if (column == 4242) // hack to return formatted column text in linux
    {
      if (identifiers_equal(value, _active_schema))
        value = "<b>"+value+"</b>";
    }
#if defined(_WIN32)
    SchemaNode &snode(_schemata[node[0]]);
    if (snode.fetching)
      value+= " - fetching...";
#endif
    return true;
  }
  else if (node.depth() == 2)
  {
    SchemaNode &snode(_schemata[node[0]]);
    if (snode.fetching)
    {
      value= _("fetching...");
      return true;
    }
    else
    {
      switch (node[1])
      {
        case TABLES_NODE_INDEX: value= _("Tables"); return true;
        case VIEWS_NODE_INDEX: value= _("Views"); return true;
        case ROUTINES_NODE_INDEX: value= _("Routines"); return true;
      }
    }
  }
  else if (node.depth() == 3)
  {
    if (ObjectNode *obj_node= get_object_node_by_index(node))
    {
      value= obj_node->name;
      return true;
    }
  }
  else if (node.depth() == 4)
  {
    if (node[1] == 0)
    {
      ObjectNode *obj_node = get_object_node_by_index(node);
      if (obj_node && obj_node->fetching)
      {
        value = _("fetching...");
        return true;
      }
      else
      {
        switch (node[3])
        {
          case TABLE_COLUMNS_NODE_INDEX: value= _("Columns"); return true;
          case TABLE_INDEXES_NODE_INDEX: value= _("Indexes"); return true;
          case TABLE_FOREIGN_KEYS_NODE_INDEX: value= _("Foreign Keys"); return true;
          case TABLE_TRIGGERS_NODE_INDEX: value= _("Triggers"); return true;
        }
      }
    }
    else
    {
      ViewNode *view_node = dynamic_cast<ViewNode*>(get_object_node_by_index(node));
      if (view_node)
      {
        if (node[3] < (int)view_node->columns.size())
        {
          value= view_node->columns[node[3]].name;
          return true;
        }
      }
    }
  }
  else if (node.depth() == 5)
  {
    ViewNode *view_node = dynamic_cast<ViewNode*>(get_object_node_by_index(node));
    TableNode *table_node = dynamic_cast<TableNode*>(get_object_node_by_index(node));

    switch(node[3])
    {
    case TABLE_COLUMNS_NODE_INDEX:
      if (view_node)
      {
        if (node[4] < (int)view_node->columns.size())
        {
          value= view_node->columns[node[4]].name;
          return true;
        }
      }
      break;
    case TABLE_INDEXES_NODE_INDEX:
      if (table_node)
      {
        if (node[4] < (int)table_node->indexes.size())      
        {
          value= table_node->indexes[node[4]].name;
          return true;
        }
      }
      break;
    case TABLE_FOREIGN_KEYS_NODE_INDEX:
      if (table_node)
      {
        if (node[4] < (int)table_node->foreign_keys.size())
        {
          value= table_node->foreign_keys[node[4]].name;
          return true;
        }
      }
      break;
    case TABLE_TRIGGERS_NODE_INDEX:
      if (table_node)
      {
        if (node[4] < (int)table_node->triggers.size())
        {
          value= table_node->triggers[node[4]].name;
          return true;
        }
      }
      break;        
    }
  }
  return false;
}


std::string LiveSchemaTree::get_field_description(const bec::NodeId &node, int column)
{
  std::string text;
  if (node.depth() == 3 || node.depth() == 4)
  {
    ObjectType obj_type;
    ObjectNode *obj_node= get_object_node_by_index(node, &obj_type);
    if (!obj_node)
      return "";

    switch (obj_type)
    {
      case Any:
      case Schema:
        break;
      case Table:
        if (TableNode *table = dynamic_cast<TableNode*>(obj_node))
        { // for tables, we need the column data and the foreign key data available
          if (!table->columns_loaded || !table->foreign_keys_loaded)
            load_table_details(node, COLUMN_DATA|FK_DATA);

          for (std::vector<FKNode>::const_iterator fk = table->foreign_keys.begin();
               fk != table->foreign_keys.end(); ++fk)
          {
            std::string from, to;
          
            for (std::vector<FKColumnNode>::const_iterator fkc = fk->columns.begin(); fkc != fk->columns.end(); ++fkc)
            {
              if (from.empty())
                from = fkc->name;
              else
                from.append(", ").append(fkc->name);
              if (to.empty())
                to = fkc->referenced_column;
              else
                to.append(", ").append(fkc->referenced_column);
            }
            text.append(strfmt("<div style=\"color: #1a6cb9; padding-left: 15px\">%s (%s \xE2\x86\x92 %s)</div>", fk->referenced_table.c_str(), from.c_str(), to.c_str()));
          }
          
          if (text.empty())
            text = table->details;
          else
            text = table->details+"<div><b>Related Tables:</b><div>" + text;
          break;
        }
      case View:
        if (ViewNode *view = dynamic_cast<ViewNode*>(obj_node))
        { // for tables, we need the column data and the foreign key data available
          if (!view->columns_loaded)
            load_table_details(node, COLUMN_DATA);
          text = view->details;
          break;
        }
        break;
      case Routine:
        break;
    }
  }
  return text;
}


bec::IconId LiveSchemaTree::get_field_icon(const bec::NodeId &node, int column, bec::IconSize size)
{
  std::string suffix;
#ifdef _WIN32
  suffix = "win.$.png";
#else
  suffix = "$.png";
#endif

  if (node.depth() == 1)
  {
    if (_schemata[node[0]].fetched)
      return bec::IconManager::get_instance()->get_icon_id("db.Schema.side." + suffix, bec::Icon16);
    else if (_schemata[node[0]].fetching)
      return bec::IconManager::get_instance()->get_icon_id("db.Schema.loading.side." + suffix, bec::Icon16);
    else
      return bec::IconManager::get_instance()->get_icon_id("db.Schema.unloaded.side." + suffix, bec::Icon16);
  }
  else if (node.depth() == 2)
  {
    switch (node[1])
    {
      case TABLES_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.Table.many.side." + suffix, bec::Icon16);
      case VIEWS_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.View.many.side." + suffix, bec::Icon16);
      case ROUTINES_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.Routine.many.side." + suffix, bec::Icon16);
    }
    return 0;
  }
  else if (node.depth() == 3)
  {
    switch (node[1])
    {
      case TABLES_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.Table.side." + suffix, bec::Icon16);
      case VIEWS_NODE_INDEX:
        {
          ViewNode *view_node = dynamic_cast<ViewNode*>(get_object_node_by_index(node));

          if (view_node && view_node->columns_load_error)
            return bec::IconManager::get_instance()->get_icon_id("db.View.broken.side." + suffix, bec::Icon16);
          else
            return bec::IconManager::get_instance()->get_icon_id("db.View.side." + suffix, bec::Icon16);
        }
      case ROUTINES_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.Routine.side." + suffix, bec::Icon16);
    }
  }
  else if (node.depth() == 4)
  {
    if (node[1] == TABLES_NODE_INDEX) // tables
    {
      switch (node[3])
      {
        case TABLE_COLUMNS_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.Column.many.side." + suffix, bec::Icon16);
        case TABLE_INDEXES_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.Index.many.side." + suffix, bec::Icon16);
        case TABLE_FOREIGN_KEYS_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.many.side." + suffix, bec::Icon16);
        case TABLE_TRIGGERS_NODE_INDEX: return bec::IconManager::get_instance()->get_icon_id("db.Trigger.many.side." + suffix, bec::Icon16);
      }
    }
    else if (node[1] == VIEWS_NODE_INDEX) // views
      return bec::IconManager::get_instance()->get_icon_id("db.Column.side." + suffix, bec::Icon16);
  }
  else if (node.depth() == 5)
  {
    ViewNode *view_node = dynamic_cast<ViewNode*>(get_object_node_by_index(node));

    switch(node[3])
    {
    case TABLE_COLUMNS_NODE_INDEX:
      if (view_node)
      {
        if (view_node->columns[node[4]].is_pk)
          return bec::IconManager::get_instance()->get_icon_id("db.Column.pk.side." + suffix, bec::Icon16);
        //else if (obj_node->columns[node[4]].is_fk)
        //  return bec::IconManager::get_instance()->get_icon_id("db.Column.fk.side." + suffix, bec::Icon16);
        else
          return bec::IconManager::get_instance()->get_icon_id("db.Column.side." + suffix, bec::Icon16);
      }

    case TABLE_INDEXES_NODE_INDEX:
      return bec::IconManager::get_instance()->get_icon_id("db.Index.side." + suffix, bec::Icon16);

    case TABLE_FOREIGN_KEYS_NODE_INDEX:
      return bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.side." + suffix, bec::Icon16);

    case TABLE_TRIGGERS_NODE_INDEX:
      return bec::IconManager::get_instance()->get_icon_id("db.Trigger.side." + suffix, bec::Icon16);
    }
  }
  return 0;
}

void LiveSchemaTree::set_active_schema(const std::string &schema)
{
  _active_schema = schema;

  if(_base)
    _base->set_active_schema(schema);
}

#if !defined(__APPLE__)
bool LiveSchemaTree::get_field(const bec::NodeId &node, int column, int &value)
{
  value = 0;
  if (node.depth() == 1)
  {
    if (identifiers_equal(_active_schema.c_str(), _schemata[node[0]].name.c_str()))
      value = 1;
  }
  return true;
}
#endif

bec::NodeId LiveSchemaTree::get_child(const bec::NodeId &node, int index)
{
  return bec::NodeId(node).append(index);
}

void LiveSchemaTree::update_live_object_state(ObjectType type, const std::string &schema_name, const std::string &old_obj_name, const std::string &new_obj_name)
{
  bool is_obj_created= old_obj_name.empty();
  bool is_obj_dropped= new_obj_name.empty();
  
  // Rename occurs when both names are set and they are different.
  bool is_obj_renamed= !is_obj_created && !is_obj_dropped && (old_obj_name != new_obj_name);

  if (old_obj_name == new_obj_name)
  {
    is_obj_created= false;
    is_obj_dropped= false;
  }

  SchemaNode *snode= NULL;
  if (!schema_name.empty())
  {
    NodeId schema_node_id= get_node_for_schema(schema_name);
    snode= &_schemata[schema_node_id[0]];
  }

  if (type == Schema)
  {
    bool is_new_obj_found= false;
    bool was_expanded= false;
    for (int n= _schemata.size()-1; n >= 0; --n)
    {
      if (_schemata[n].name == new_obj_name)
        is_new_obj_found= true;
      if (is_obj_dropped && (_schemata[n].name == old_obj_name))
      {
        was_expanded = _schemata[n].expanded;
        _schemata.erase(_schemata.begin()+n);
      }
    }
    if (is_obj_created && !is_new_obj_found)
    {
      SchemaNode new_schema_node;
      new_schema_node.name= new_obj_name;
      new_schema_node.fetched= false;
      new_schema_node.fetching= false;
      new_schema_node.expanded= was_expanded;
      _schemata.push_back(new_schema_node);
      std::sort(_schemata.begin(), _schemata.end(), compare_schema_node);
    }
  }
  else
  {
    bool is_new_obj_found= false;
    ObjectNode* new_object = this->get_object_node_by_type_and_name(snode, type, new_obj_name);
    ObjectNode* old_object = this->get_object_node_by_type_and_name(snode, type, old_obj_name);


    if (new_object)
      is_new_obj_found= true;

    if (old_object)
    {
      if (is_obj_dropped)
        snode->delete_node(old_obj_name, type);
      else if (is_obj_renamed)
        old_object->name = new_obj_name;
    }

    if (is_obj_created && !is_new_obj_found)
    {
      snode->add_node(type, new_obj_name);
      snode->sort_nodes(type);
    }

    // Triggers a reload in case of tables and views
    if (!is_obj_created && !is_obj_dropped && type != Routine)
    {
      int loaded_data = old_object->get_loaded_mask();

      ViewNode* v_node = dynamic_cast<ViewNode*> (new_object);
      TableNode* t_node = dynamic_cast<TableNode*> (new_object);

      if (v_node)
        v_node->columns_loaded = false;

      if (t_node)
      {
        t_node->foreign_keys_loaded = false;
        t_node->indexes_loaded = false;
        t_node->triggers_loaded = false;
      }

      old_object->fetched = false;
      
      NodeId object_node = get_node_for_object(schema_name, type, new_obj_name);

      // Reloads whatever was loaded before
      load_table_details(object_node, loaded_data);
    }
  }
}


void LiveSchemaTree::schema_contents_arrived(const SchemaNode &node, int index, int serial)
{
  // If set, calls the _base method so the object gets updated there too
  if (_base)
    _base->schema_contents_arrived(node, -1, -1);

  if (serial != _schemata_vector_serial)
  {
    for (std::vector<SchemaNode>::iterator iter= _schemata.begin(); iter != _schemata.end(); ++iter)
    {
      if (iter->name == node.name)
      {
        if (_filter.length() > 0)
          node.filter_data(_filter_type, _schema_pattern, _object_pattern, *iter);
        else
        {
          iter->tables= node.tables;
          iter->views= node.views;
          iter->routines= node.routines;
        }
        iter->fetched= true;
        iter->fetching= false;
        break;
      }
    }
    // unknown schema, just ignore it
  }
  else
  {
    SchemaNode &snode(_schemata[index]);

    snode.fetched= true;
    snode.fetching= false;

    if (_filter.length() > 0)
      node.filter_data(_filter_type, _schema_pattern, _object_pattern, snode);
    else
    {
      snode.tables= node.tables;
      snode.views= node.views;
      snode.routines= node.routines;
    }
  }
}


void LiveSchemaTree::object_details_arrived(const SchemaNode &schema_node, const ObjectNode *obj_node, ObjectType obj_type, int schema_index, int obj_index, int serial)
{
  bec::NodeId obj_nodeid;

  // If set, calls the _base method so the object gets updated there too
  if (_base)
    _base->object_details_arrived(schema_node, obj_node, obj_type, -1, -1, -1);

  if (serial != _schemata_vector_serial)
  {
    for (std::vector<SchemaNode>::iterator iter= _schemata.begin(); iter != _schemata.end(); ++iter)
    {
      if (iter->name == schema_node.name)
      {
        ObjectNode* found_node = this->get_object_node_by_type_and_name(&(*iter), obj_type, obj_node->name, &obj_nodeid);

        if (found_node)
        {
          bool expanded = found_node->expanded;
          found_node->copy(*obj_node, false);
          found_node->fetched = true;
          found_node->fetching = false;
          found_node->expanded = expanded;

        }
        break;
      }
    }
  }
  else
  {
    SchemaNode *snode(&_schemata[schema_index]);
    ObjectNode *onode = get_object_node_by_type_and_index(snode, obj_type, obj_index);

    if (obj_index >= 0)
    {
      switch (obj_type)
      { 
        case Table:
          obj_nodeid.append(schema_index).append(0).append(obj_index);
          break;
        case View:
          obj_nodeid.append(schema_index).append(1).append(obj_index);
          break;
        case Routine:
          obj_nodeid.append(schema_index).append(2).append(obj_index);
          break;
        case Any: 
        case Schema: 
        break;
      }
    }
    if (onode)
    {
      bool expanded = onode->expanded;
      onode->copy(*obj_node, false);
      onode->expanded = expanded;

    }

    onode->fetched= true;
    onode->fetching= false;
  }
#if !defined(__APPLE__) && !defined(_WIN32)
  if (obj_type == Table && obj_nodeid.is_valid())
    tree_changed(424242, obj_nodeid);
#endif
}


bool LiveSchemaTree::is_expandable(const bec::NodeId &node)
{
  if (!_is_schema_contents_enabled)
  {
    return false;
  }
  else if (node.depth() < 3)
  {
    return true;
  }
  else if (node.depth() == 3)
  {
    return (node[1] < 2);
  }
  else if (node.depth() == 4)
  {
    return (node[1] == 0);
  }
  return false;
}


bool LiveSchemaTree::expand_node(const bec::NodeId &node)
{
  return do_expand_node(node, false);
}


bool LiveSchemaTree::do_expand_node(const bec::NodeId &node, bool dont_fetch)
{
  ObjectType  object_type;
  std::string object_name = "";
  bool ret_val = false;

  if (!_is_schema_contents_enabled || (node.depth() <= 0))
     ret_val = true;

  else if (node[0] >= 0 && node[0] < (int)_schemata.size())
  {
    SchemaNode &snode(_schemata[node[0]]);

    if (node.depth() == 1) // Schema node
    {
      if (!snode.fetched && !snode.fetching && !dont_fetch)
      {
        snode.fetching= true;
        if (boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock())
        {
          delegate->fetch_schema_contents(node, snode.name, _auto_fetch_columns,
                                 boost::bind(&LiveSchemaTree::schema_contents_arrived, this, _1,
                                             node[0], _schemata_vector_serial));
        }
      }
      
      snode.expanded= true;

      ret_val = true;
    }
    else if (node.depth() == 2) // Schema object group node
    {
      switch (node[1])
      {
        case TABLES_NODE_INDEX: _schemata[node[0]].tables_expanded = true; break;
        case VIEWS_NODE_INDEX: _schemata[node[0]].views_expanded = true; break;
        case ROUTINES_NODE_INDEX: _schemata[node[0]].routines_expanded = true; break;
      }

      ret_val = true;
    }
    else if (node.depth() == 3) // Object node
    {
      if (node[1] == TABLES_NODE_INDEX || node[1] == VIEWS_NODE_INDEX) // for tables we only pre-fetch column data, if it isn't loaded already
      {
        ViewNode *object = dynamic_cast<ViewNode*>(get_object_node_by_index(node));

        if (object)
        {
          object->expanded = true;
          object_name = object->name;

          // with gtk, we need to preload everything, because it's not possible to expand the indexes
          // and triggers group nodes, when they have no children yet 
//#if !defined(_WIN32) && !defined(__APPLE__)
          TableNode *table = dynamic_cast<TableNode*>(object);
          if ((!object->columns_loaded || (table && (!table->indexes_loaded || !table->triggers_loaded))) && !dont_fetch)
            load_table_details(node, COLUMN_DATA|FK_DATA|TRIGGER_DATA|INDEX_DATA);
            
/*#else
          if (!object->columns_loaded && !dont_fetch)
            load_table_details(node, COLUMN_DATA|FK_DATA);
          else
            object->expanded = true;
#endif*/
        }
      }

      ret_val = true;
    }
    else if (node.depth() == 4) // Object content node
    {
      if (node[1] == TABLES_NODE_INDEX) // Load needed stuff for tables
      {
        TableNode *table = dynamic_cast<TableNode*>(get_object_node_by_index(node));

        if (table)
        {
          object_name = table->name;

          switch (node[3])
          {
            case TABLE_COLUMNS_NODE_INDEX:
              if (!table->columns_loaded && !dont_fetch)
                load_table_details(node, COLUMN_DATA);

              table->columns_expanded = true;
              break;
            case TABLE_INDEXES_NODE_INDEX:
              if (!table->indexes_loaded && !dont_fetch)
                load_table_details(node, INDEX_DATA);
                
              table->indexes_expanded = true;
              break;          
            case TABLE_FOREIGN_KEYS_NODE_INDEX:
              if (!table->foreign_keys_loaded && !dont_fetch)
                load_table_details(node, FK_DATA);

              table->foreign_keys_expanded = true;
              break;          
            case TABLE_TRIGGERS_NODE_INDEX:
              if (!table->triggers_loaded && !dont_fetch)
                load_table_details(node, TRIGGER_DATA);

              table->triggers_expanded = true;
              break;
          }
        }

        ret_val = true;
      }
    }

    if (_base)
    {
      // Get the schema NodeId on the base LST
      bec::NodeId base_node (_base->get_node_for_schema(snode.name));

      if (node.depth() > 1)
      {
        base_node.append(node[1]);

        // If the node belongs to an object
        if(node.depth() > 2)
        {
          if (object_name.length() > 0)
            base_node = bec::NodeId();

          switch(node[1])
          {
            case TABLES_NODE_INDEX: object_type = Table; break;
            case VIEWS_NODE_INDEX: object_type = View; break;
            case ROUTINES_NODE_INDEX: object_type = Routine; break;
          }

          base_node = _base->get_node_for_object(snode.name, object_type, object_name);

          if (node.depth() > 3)
            base_node.append(node[3]);
        }
      }

      // mark the node as expanded, but prevent duplicate fetching
      if ( base_node.depth() > 0 )
        _base->do_expand_node(base_node, true);
    }
  }
  return ret_val;
}


void LiveSchemaTree::collapse_node(const bec::NodeId &node)
{
  std::string object_name;
  ObjectType object_type;

  if (node.depth() && node[0] >= 0 && node[0] < (int)_schemata.size())
  {
    SchemaNode &snode(_schemata[node[0]]);
    bec::NodeId base_node (_base ? _base->get_node_for_schema(snode.name): bec::NodeId() );

    if (node.depth() == 1)
      snode.expanded= false;
    else if (node.depth() == 2)
    {
      switch (node[1])
      {
        case TABLES_NODE_INDEX: _schemata[node[0]].tables_expanded = false; break;
        case VIEWS_NODE_INDEX: _schemata[node[0]].views_expanded = false; break;
        case ROUTINES_NODE_INDEX: _schemata[node[0]].routines_expanded = false; break;
      }
    }
    else if (node.depth() == 3) // Object node
    {
      if (node[1] == TABLES_NODE_INDEX || node[1] == VIEWS_NODE_INDEX) // for tables we only pre-fetch column data, if it isn't loaded already
      {
        ViewNode *object = dynamic_cast<ViewNode*>(get_object_node_by_index(node));

        object_name = object->name;

        if (object)
          object->expanded = false;
      }
    }
    else if (node.depth() == 4) // Object content node
    {
      if (node[1] == TABLES_NODE_INDEX) // Load needed stuff for tables
      {
        TableNode *table = dynamic_cast<TableNode*>(get_object_node_by_index(node));

        object_name = table->name;

        if (table)
        {
          switch (node[3])
          {
            case TABLE_COLUMNS_NODE_INDEX: table->columns_expanded = false; break;
            case TABLE_INDEXES_NODE_INDEX: table->indexes_expanded = true; break;          
            case TABLE_FOREIGN_KEYS_NODE_INDEX: table->foreign_keys_expanded = true; break;          
            case TABLE_TRIGGERS_NODE_INDEX: table->triggers_expanded = true; break;
          }
        }
      }
    }

    if (_base)
    {
      // Get the schema NodeId on the base LST
      bec::NodeId base_node (_base->get_node_for_schema(snode.name));

      if (node.depth() > 1)
      {
        base_node.append(node[1]);

        // If the node belongs to an object
        if(node.depth() > 2)
        {
          switch(node[1])
          {
            case TABLES_NODE_INDEX: object_type = Table; break;
            case VIEWS_NODE_INDEX: object_type = View; break;
            case ROUTINES_NODE_INDEX: object_type = Routine; break;
          }

          base_node = _base->get_node_for_object( snode.name, object_type, object_name);

          if(node.depth() > 3)
            base_node.append(node[3]);
        }
      }

      _base->collapse_node(base_node);
    }
  }
}


void LiveSchemaTree::load_table_details(const NodeId &index_node, int fetch_mask)
{
  ObjectType obj_type;
  short data_load_flags = fetch_mask;
  
  ObjectNode *obj_node= get_object_node_by_index(index_node, &obj_type);
  
  ViewNode *view_node = dynamic_cast<ViewNode*>(obj_node);
  TableNode *table_node = dynamic_cast<TableNode*>(obj_node);
  
  if (view_node)
  {
    if (view_node->columns_loaded)
      data_load_flags &= ~COLUMN_DATA;
    
    if (table_node)
    {
      if(table_node->triggers_loaded)
        data_load_flags &= ~TRIGGER_DATA;
      
      if(table_node->indexes_loaded)
        data_load_flags &= ~INDEX_DATA;
      
      if(table_node->foreign_keys_loaded)
        data_load_flags &= ~FK_DATA;
    }
    else
      data_load_flags &= ~(TRIGGER_DATA|INDEX_DATA|FK_DATA);
  }
  
  if (!obj_node || obj_node->fetching || !data_load_flags)
    return;
  
  obj_node->fetching= true;
  if (boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock())
  {
    delegate->fetch_object_details(obj_type, _schemata[index_node[0]].name, obj_node->name, data_load_flags,
                                   boost::bind(&LiveSchemaTree::object_details_arrived, this, _1, _2, _3,
                                               index_node[0], index_node[2], _schemata_vector_serial));
    if (obj_node->details.empty())
      obj_node->details= " "; // means object's details are fetched
  }
  return;
}


bec::NodeId LiveSchemaTree::get_node_for_schema(const std::string &name)
{
  int index= 0;
  for (std::vector<SchemaNode>::iterator iter= _schemata.begin(); iter != _schemata.end(); ++iter, ++index)
  {
    if (iter->name == name)
      return bec::NodeId(index);
  }
  return bec::NodeId();
}

bec::NodeId LiveSchemaTree::get_node_for_object(const std::string &schema_name, ObjectType type, const std::string& name)
{
  bec::NodeId schema_node = get_node_for_schema(schema_name);
  bec::NodeId object_node;
  bool found = false;

  if (schema_node.is_valid())
  {
    // TODO: This call is to force schema loading, however that is asynchronous
    //       Some mechanism should be implemented to wait till the data gets loaded
    //       For now if the schema is not loaded, retrieving the node will fail
  //  expand_node(schema_node);

    object_node.append(schema_node[0]);

    switch(type)
    {
    case Any:
      break;
    case Schema:
      object_node = schema_node;
      found = true;
      break;
    case Table:
      object_node.append(TABLES_NODE_INDEX);
      for(size_t index = 0; !found && index < _schemata[schema_node[0]].tables.size(); index++)
      {
        if (_schemata[schema_node[0]].tables[index].name == name)
        {
          found = true;
          object_node.append(index);
        }
      }
      break;
    case View:
      object_node.append(VIEWS_NODE_INDEX);
      for(size_t index = 0; !found && index < _schemata[schema_node[0]].views.size(); index++)
      {
        if (_schemata[schema_node[0]].views[index].name == name)
        {
          found = true;
          object_node.append(index);
        }
      }
      break;
    case Routine:
      object_node.append(ROUTINES_NODE_INDEX);
      for(size_t index = 0; !found && index < _schemata[schema_node[0]].routines.size(); index++)
      {
        if (_schemata[schema_node[0]].routines[index].name == name)
        {
          found = true;
          object_node.append(index);
        }
      }
      break;
    }
  }

  return found ? object_node : bec::NodeId();
}


bool LiveSchemaTree::activate_node(const bec::NodeId &node)
{
  switch (node.depth())
  {
  case 1:
    {
      if (node[0] < (int)_schemata.size())
      {
        std::vector<ChangeRecord> changes;
        ChangeRecord record = { Schema, "", _schemata[node[0]].name };
        changes.push_back(record);
        if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
          delegate->tree_activate_objects("activate", changes);
        return true;
      }
    }
    break;
  case 3:
    {
      ObjectType type;
      ObjectNode *obj_node = get_object_node_by_index(node, &type);
      if (!obj_node)
        return false;

      /* XXX Temporarily switching back to the old behavior.
      SchemaNode *snode(&_schemata[node[0]]);

      if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
      {
        std::vector<ChangeRecord> changes;
        ChangeRecord record = { type, snode->name, obj_node->name };
        changes.push_back(record);
        delegate->tree_activate_objects("edit_data", changes);
      }
      */

      std::string object_name = base::quote_identifier_if_needed(obj_node->name, '`');
      sql_editor_text_insert_signal(object_name);

      return true;
    }
    break;
  case 4:
    {
      ObjectType type;
      ViewNode* view_node = dynamic_cast<ViewNode*>(get_object_node_by_index(node, &type));
      if (view_node && type == View)
      {
        if (node[3] < (int)view_node->columns.size())
        {
          std::string column_name= view_node->columns[node[3]].name;
          column_name= base::quote_identifier_if_needed(column_name, '`');
          sql_editor_text_insert_signal(column_name);
          return true;
        }
      }
    }
    break;
  case 5:
    {
      TableNode* table_node = dynamic_cast<TableNode*>(get_object_node_by_index(node));
      if (table_node)
      {
        std::string object_name;
        bool ret_val = false;
        switch(node[3])
        {
        case TABLE_COLUMNS_NODE_INDEX:
          if (node[4] < (int)table_node->columns.size())
          {
            object_name = table_node->columns[node[4]].name;
            ret_val = true;
          }
          break;
        case TABLE_INDEXES_NODE_INDEX:
          if (node[4] < (int)table_node->indexes.size())
          {
            object_name = table_node->indexes[node[4]].name;
            ret_val = true;
          }
          break;
        case TABLE_FOREIGN_KEYS_NODE_INDEX:
            if (node[4] < (int)table_node->foreign_keys.size())
            {
              object_name = table_node->foreign_keys[node[4]].name;
              ret_val = true;
            }
            break;     
        case TABLE_TRIGGERS_NODE_INDEX:
          if (node[4] < (int)table_node->triggers.size())
          {
            object_name = table_node->triggers[node[4]].name;
            ret_val = true;
          }
          break;
        }
        if(ret_val)
        {
          object_name= base::quote_identifier_if_needed(object_name, '`');
          sql_editor_text_insert_signal(object_name);
          return true;
        }
      }
    }
  }
  return false;
}


bec::MenuItemList LiveSchemaTree::get_popup_items_for_nodes(const std::vector<bec::NodeId> &nodes)
{
  bec::MenuItemList items;

  {
    bec::MenuItem item;
    item.type = MenuAction;

    bec::MenuItem edit_item;
    edit_item.caption= _("Edit Table Data");
    edit_item.name= "edit_data";
    edit_item.enabled= false;
        
    bec::MenuItem view_item;
    {
      std::string caption = _("Select Rows");
      {
        DictRef options= DictRef::cast_from(_grt->get("/wb/options/options"));
        bool limit_rows= (0 != options.get_int("SqlEditor:LimitRows"));
        int limit_rows_count= options.get_int("SqlEditor:LimitRowsCount");
        if (limit_rows && (limit_rows_count <= 0))
          limit_rows= false;
        if (limit_rows)
          caption += strfmt(_(" - Limit %i"), limit_rows_count);
      }
      view_item.caption= caption;
    }
    view_item.name= "select_data";
    view_item.enabled= false;

    
    bec::MenuItem active_schema_item;
    active_schema_item.caption= _("Set as Default Schema");
    active_schema_item.name= "set_active_schema";
    active_schema_item.enabled= false;

    bec::MenuItem filter_schema_item;
    filter_schema_item.caption= _("Filter to This Schema");
    filter_schema_item.name= "filter_schema";
    filter_schema_item.enabled= false;
    
    bec::MenuItem clipboard_copy_item;
    clipboard_copy_item.caption= _("Copy to Clipboard");
    clipboard_copy_item.name= "clipboard_copy";
    clipboard_copy_item.type= MenuCascade;
    clipboard_copy_item.enabled= false;

    bec::MenuItem editor_insert_item;
    editor_insert_item.caption= _("Send to SQL Editor");
    editor_insert_item.name= "editor_insert";
    editor_insert_item.type= MenuCascade;
    editor_insert_item.enabled= false;

    bec::MenuItem object_name_short_item;
    object_name_short_item.caption= _("Name (short)");
    object_name_short_item.name= "object_name_short";
    object_name_short_item.enabled= false;

    bec::MenuItem object_name_long_item;
    object_name_long_item.caption= _("Name (long)");
    object_name_long_item.name= "object_name_long";
    object_name_long_item.enabled= false;

    bec::MenuItem select_statement_item;
    select_statement_item.caption= _("Select All Statement");
    select_statement_item.name= "table_select_statement";
    select_statement_item.enabled= false;

    bec::MenuItem insert_statement_item;
    insert_statement_item.caption= _("Insert Statement");
    insert_statement_item.name= "table_insert_statement";
    insert_statement_item.enabled= false;

    bec::MenuItem update_statement_item;
    update_statement_item.caption= _("Update Statement");
    update_statement_item.name= "table_update_statement";
    update_statement_item.enabled= false;

    bec::MenuItem delete_statement_item;
    delete_statement_item.caption= _("Delete Statement");
    delete_statement_item.name= "table_delete_statement";
    delete_statement_item.enabled= false;

    bec::MenuItem create_statement_item;
    create_statement_item.caption= _("Create Statement");
    create_statement_item.name= "table_create_statement";
    create_statement_item.enabled= false;
    
    bec::MenuItem create_item;
    create_item.caption= _("Create...");
    create_item.name= "create";
    create_item.enabled= false;

    bec::MenuItem alter_item;
    alter_item.caption= _("Alter...");
    alter_item.name= "alter";
    alter_item.enabled= false;

    bec::MenuItem drop_item;
    drop_item.caption= _("Drop...");
    drop_item.name= "drop";
    drop_item.enabled= false;

    if (nodes.size() > 1)
    {
      // Go through all nodes and see if they belong to depths 1 or 3 (the schema and object node levels).
      // Only then we can do object actions.
      int type = -1; // -1 = no object, 0 = table, 1 = view, 2 = routine, 3 = schema, 4 = mixed, 10 = column, 11 = view column
      int count = 0;
      for (size_t i = 0; i < nodes.size(); i++)
      {
        switch (nodes[i].depth())
        {
          case 1:
            count++;
            if (type == -1)
              type = 3;
            else if (type != 3)
              type = 4;
            break;

          case 3:
            count++;
            if (type == -1)
              type = nodes[i][1];
            else if (type != nodes[i][1])
              type = 4;
            break;

          case 4:
            if (nodes[i][1] == VIEWS_NODE_INDEX) // views
            {
              if (type == -1 || type == 10)
                type = 11;
              else if (type != 11)
                type = 4;
            }
            break;
            
          case 5:
            if (nodes[i][1] == TABLES_NODE_INDEX) // table
            {
              if (nodes[i][3] == TABLE_COLUMNS_NODE_INDEX) // columns
              {
                if (type == -1)
                  type = 10;
                else if (type != 10)
                  type = 4;
              }
            }
            break;
            
          default:
            // No object actions for group nodes.
            type = -1;
            break;
        }
      }
      
      if (type > -1)
      {
        clipboard_copy_item.enabled= true;
        editor_insert_item.enabled= true;
        object_name_long_item.enabled= true;
        drop_item.enabled = true;
        create_statement_item.enabled= true;
        alter_item.enabled= true;

        switch (type)
        {
        case 0:
          drop_item.caption= base::strfmt(_("Drop %i Tables..."), count);
          view_item.enabled = true;
          create_item.enabled = true;
          edit_item.enabled = true;
          create_item.caption= _("Create Table...");
          alter_item.caption = base::strfmt(_("Alter %i Tables..."), count);
          object_name_short_item.enabled= true;
          select_statement_item.enabled= true;
          insert_statement_item.enabled= true;
          update_statement_item.enabled= true;
          delete_statement_item.enabled= true;
          create_statement_item.enabled= true;
          break;
        case 10:
          view_item.enabled = true;
          view_item.name += "_columns";
          create_item.enabled = false;
          edit_item.enabled = true;
          edit_item.name += "_columns";
          select_statement_item.caption = _("Select Columns Statement");
          object_name_short_item.enabled= true;
          object_name_short_item.name += "_columns";
          object_name_long_item.enabled= true;
          object_name_long_item.name += "_columns";
          select_statement_item.enabled= true;
          select_statement_item.name += "_columns";
          insert_statement_item.enabled= true;
          insert_statement_item.name += "_columns";
          update_statement_item.enabled= true;
          update_statement_item.name += "_columns";
          delete_statement_item.enabled= false;
          create_statement_item.enabled= false;
          alter_item.enabled = false;
          drop_item.enabled = false;
          break;
        case 11:
          view_item.enabled = true;
          view_item.name += "_columns";
          create_item.enabled = false;
          edit_item.enabled = false;
          select_statement_item.caption = _("Select Columns Statement");
          object_name_short_item.enabled= true;
          object_name_short_item.name += "_columns";
          object_name_long_item.enabled= true;
          object_name_long_item.name += "_columns";
          select_statement_item.enabled= true;
          select_statement_item.name += "_columns";
          insert_statement_item.enabled= false;
          update_statement_item.enabled= false;
          delete_statement_item.enabled= false;
          create_statement_item.enabled= false;
          alter_item.enabled = false;
          drop_item.enabled = false;
          break;
        case 1:
          drop_item.caption= base::strfmt(_("Drop %i Views..."), count);
          create_item.enabled = true;
          create_item.caption= _("Create View...");
          alter_item.caption = base::strfmt(_("Alter %i Views..."), count);
          object_name_short_item.enabled= true;
          select_statement_item.enabled= true;
          insert_statement_item.enabled= true;
          update_statement_item.enabled= true;
          delete_statement_item.enabled= true;
          create_statement_item.enabled= true;
          break;
        case 2:
          drop_item.caption= base::strfmt(_("Drop %i Routines..."), count);
          create_item.enabled = true;
          create_item.caption= _("Create Routine...");
          alter_item.caption = base::strfmt(_("Alter %i Routines..."), count);
          object_name_short_item.enabled= true;
          create_statement_item.enabled= true;
          break;
        case 3:
          drop_item.caption = base::strfmt(_("Drop %i Schemas..."), count);
          create_item.enabled = true;
          create_item.caption = _("Create Schema...");
          alter_item.caption = base::strfmt(_("Alter %i Schemas..."), count);
          object_name_long_item.caption = _("Name");
          break;
        case 4:
          drop_item.caption= base::strfmt(_("Drop %i Objects..."), count);
          alter_item.caption = base::strfmt(_("Alter %i Objects..."), count);
          object_name_short_item.enabled= true;
          break;
        }
      }
    }
    else // nodes.size() <= 1
    {
      bec::NodeId node= nodes.empty() ? bec::NodeId() : nodes[0];
      switch (node.depth())
      {
        case 1:
          object_name_long_item.caption= _("Name");
          object_name_long_item.enabled= true;
          active_schema_item.enabled= true;
          filter_schema_item.enabled= true;
          clipboard_copy_item.enabled= true;
          editor_insert_item.enabled= true;
          create_item.enabled= true;
          create_item.caption= _("Create Schema...");
          alter_item.caption= _("Alter Schema...");
          drop_item.caption= _("Drop Schema...");
          alter_item.enabled= true;
          drop_item.enabled= true;
          create_statement_item.enabled= true;
          break;
        case 3:
          {
            switch (node[1])
            {
            case TABLES_NODE_INDEX: // Table
              alter_item.caption= _("Alter Table...");
              drop_item.caption= _("Drop Table...");
              edit_item.enabled= true;
              view_item.enabled= true;
              select_statement_item.enabled= true;
              insert_statement_item.enabled= true;
              update_statement_item.enabled= true;
              delete_statement_item.enabled= true;
              create_statement_item.enabled= true;
              break;
            case VIEWS_NODE_INDEX: // View
              alter_item.caption= _("Alter View...");
              drop_item.caption= _("Drop View...");
              view_item.enabled= true;
              select_statement_item.enabled= true;
              insert_statement_item.enabled= true;
              update_statement_item.enabled= true;
              delete_statement_item.enabled= true;
              create_statement_item.enabled= true;
              break;
            case ROUTINES_NODE_INDEX: // SP
              alter_item.caption= _("Alter Routine...");
              drop_item.caption= _("Drop Routine...");
              break;                  
            }
            clipboard_copy_item.enabled= true;
            editor_insert_item.enabled= true;
            object_name_short_item.enabled= true;
            object_name_long_item.enabled= true;
            create_statement_item.enabled= true;
            alter_item.enabled= true;
            drop_item.enabled= true;
          }
          //break;
        case 2:
          {
            switch (node[1])
            {
            case TABLES_NODE_INDEX: // Table
              create_item.caption= _("Create Table...");
              break;
            case VIEWS_NODE_INDEX: // View
              create_item.caption= _("Create View...");
              break;
            case ROUTINES_NODE_INDEX: // SP
              create_item.caption= _("Create Routine...");
              break;                  
            }
            create_item.enabled= true;
          }
          break;
        case 4: // if the Columns group node of a table is selected...
        case 5:
          if ((node[1] == TABLES_NODE_INDEX && node[3] == 0) || node[1] == VIEWS_NODE_INDEX)
          {
            clipboard_copy_item.enabled= true;
            editor_insert_item.enabled= true;
            if (node[1] == TABLES_NODE_INDEX)
            {
              view_item.enabled = true;
              view_item.name += "_columns";
              create_item.enabled = false;
              edit_item.enabled = true;
              edit_item.name += "_columns";
              select_statement_item.caption = _("Select Columns Statement");
              object_name_short_item.enabled= true;
              object_name_short_item.name += "_columns";
              object_name_long_item.enabled= true;
              object_name_long_item.name += "_columns";
              select_statement_item.enabled= true;
              select_statement_item.name += "_columns";
              insert_statement_item.enabled= true;
              insert_statement_item.name += "_columns";
              update_statement_item.enabled= true;
              update_statement_item.name += "_columns";
              delete_statement_item.enabled= false;
              create_statement_item.enabled= false;
              alter_item.enabled = false;
              drop_item.enabled = false;
            }
            else
            {
              view_item.enabled = true;
              view_item.name += "_columns";
              create_item.enabled = false;
              edit_item.enabled = false;
              select_statement_item.caption = _("Select Columns Statement");
              object_name_short_item.enabled= true;
              object_name_short_item.name += "_columns";
              object_name_long_item.enabled= true;
              object_name_long_item.name += "_columns";
              select_statement_item.enabled= true;
              select_statement_item.name += "_columns";
              insert_statement_item.enabled= false;
              update_statement_item.enabled= false;
              delete_statement_item.enabled= false;
              create_statement_item.enabled= false;
              alter_item.enabled = false;
              drop_item.enabled = false;
            }              
          }
          break;
        default:
          break;
      }
    }
    
    if (view_item.enabled)
      items.push_back(view_item);
    if (edit_item.enabled)
      items.push_back(edit_item);
    if (active_schema_item.enabled)
    {
      items.push_back(active_schema_item);
      items.push_back(filter_schema_item);
      bec::MenuItem item;
      item.type= MenuSeparator;
      items.push_back(item);
    }
    if (clipboard_copy_item.enabled)
    {
      MenuItemList subitems;
      subitems.push_back(object_name_short_item);
      subitems.push_back(object_name_long_item);
      subitems.push_back(select_statement_item);
      subitems.push_back(insert_statement_item);
      subitems.push_back(update_statement_item);
      subitems.push_back(delete_statement_item);
      subitems.push_back(create_statement_item);
      for (MenuItemList::iterator i= subitems.begin(), end= subitems.end(); i != end; ++i)
      {
        if (!i->enabled)
          continue;
        i->name= "clipboard_" + i->name;
        clipboard_copy_item.subitems.push_back(*i);
      }
      items.push_back(clipboard_copy_item);
    }
    if (editor_insert_item.enabled)
    {
      MenuItemList subitems;
      subitems.push_back(object_name_short_item);
      subitems.push_back(object_name_long_item);
      subitems.push_back(select_statement_item);
      subitems.push_back(insert_statement_item);
      subitems.push_back(update_statement_item);
      subitems.push_back(delete_statement_item);
      subitems.push_back(create_statement_item);
      for (MenuItemList::iterator i= subitems.begin(), end= subitems.end(); i != end; ++i)
      {
        if (!i->enabled)
          continue;
        i->name= "editor_" + i->name;
        editor_insert_item.subitems.push_back(*i);
      }
      items.push_back(editor_insert_item);
    }
    if (!items.empty() && (alter_item.enabled || create_item.enabled || drop_item.enabled))
    {
      item.enabled= false;
      item.type = MenuSeparator;
      items.push_back(item);
    }
    if (alter_item.enabled)
      items.push_back(alter_item);
    if (create_item.enabled)
      items.push_back(create_item);
    if (drop_item.enabled)
      items.push_back(drop_item);
  }

  try
  {
    bec::MenuItem item;

    std::string schema;
    std::string object;
    std::string type;
    bec::NodeId node= nodes.empty() ? bec::NodeId() : nodes[0];
    switch (node.depth())
    {
      case 1:
        schema= _schemata[node[0]].name;
        object= "";
        type= "schema";
        break;
      case 3:
      {
        schema= _schemata[node[0]].name;
        switch (node[1])
        {
          case TABLES_NODE_INDEX: // Table
            type= "table";
            object= _schemata[node[0]].tables[node[2]].name;
            break;
          case VIEWS_NODE_INDEX: // View
            type= "view";
            object= _schemata[node[0]].views[node[2]].name;
            break;
          case ROUTINES_NODE_INDEX: // SP
            type= "routine";
            object= _schemata[node[0]].routines[node[2]].name;
            break;
        }
      }
    }
    
    if (!type.empty())
    {
      // plugin items
      bec::MenuItemList plugin_items(object_plugin_items_slot(schema, object, type));

      if (!plugin_items.empty())
      {
        bec::MenuItem item;
        item.type = MenuSeparator;
        items.push_back(item);    
        items.insert(items.end(), plugin_items.begin(), plugin_items.end());
      }
    }
  }
  catch (const std::exception &exc)
  {
    g_message("exception fetching context menu items for live tree: %s", exc.what());
  }
  
  {
    bec::MenuItem item;

    if (!items.empty())
    {
      item.type = MenuSeparator;
      items.push_back(item);    
    }
    item.type = MenuAction;
    item.caption= _("Refresh All");
    item.name= "refresh";
    items.push_back(item);
  }

  return items;
}


bool LiveSchemaTree::activate_popup_item_for_nodes(const std::string &name, const std::vector<bec::NodeId> &unsorted_nodes)
{
  if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
  {
    if (name == "refresh")
    {
      delegate->tree_refresh();
      return true;
    }
        
    if (name == "alter")
    {
      std::vector<ChangeRecord> changes;
      
      for (size_t i = 0; i < unsorted_nodes.size(); i++)
      {
        switch (unsorted_nodes[i].depth())
        {
          case 1:
          {
            ChangeRecord record = { Schema, "", _schemata[unsorted_nodes[i][0]].name, "" };
            changes.push_back(record);
          }
          break;
            
          case 3:
          {
            ObjectType type;
            ObjectNode *obj_node= get_object_node_by_index(unsorted_nodes[i], &type);
            if (obj_node)
            {
              SchemaNode *snode(&_schemata[unsorted_nodes[i][0]]);
              ChangeRecord record = { type, snode->name, obj_node->name, "" };
              changes.push_back(record);
            }
          }
            break;
        }
      }
      delegate->tree_alter_objects(changes);
      
      return true;
    }
    
    if (name == "drop")
    {
      std::vector<ChangeRecord> changes;
      
      for (size_t i = 0; i < unsorted_nodes.size(); i++)
      {
        switch (unsorted_nodes[i].depth())
        {
          case 1:
          {
            ChangeRecord record = { Schema, "", _schemata[unsorted_nodes[i][0]].name, "" };
            changes.push_back(record);
          }
            break;
          case 3:
          {
            ObjectType type;
            ObjectNode *obj_node= get_object_node_by_index(unsorted_nodes[i], &type);
            if (obj_node)
            {
              SchemaNode *snode(&_schemata[unsorted_nodes[i][0]]);
              ChangeRecord record = { type, snode->name, obj_node->name, "" };
              changes.push_back(record);
            }
            
            break;
          }
        }
      }
      
      delegate->tree_drop_objects(changes);
      
      return true;
    }
    
    if ((0 == name.find("select_data")) || (0 == name.find("edit_data")) ||
        (0 == name.find("clipboard_")) || (0 == name.find("editor_")))
    {
      std::vector<ChangeRecord> changes;
      for (size_t i = 0; i < unsorted_nodes.size(); i++)
      {
        switch (unsorted_nodes[i].depth())
        {
          case 1:
          {
            ChangeRecord record = { Schema, "", _schemata[unsorted_nodes[i][0]].name, "" };
            changes.push_back(record);
          }
          break;
            
          case 3:
          {
            ObjectType type;
            ObjectNode *obj_node= get_object_node_by_index(unsorted_nodes[i], &type);
            if (obj_node)
            {
              SchemaNode *snode(&_schemata[unsorted_nodes[i][0]]);
              ChangeRecord record = { type, snode->name, obj_node->name, "" };
              changes.push_back(record);
            }
          }
          break;
            
          case 4: // group node selected, if it's a table add all columns
          {
            ObjectType type;
            ViewNode *view_node= dynamic_cast<ViewNode*>(get_object_node_by_index(unsorted_nodes[i], &type));
            if (view_node)
            {
              SchemaNode *snode(&_schemata[unsorted_nodes[i][0]]);
              if (type == Table)
              {
                for (std::vector<ColumnNode>::const_iterator iter = view_node->columns.begin();
                     iter != view_node->columns.end(); ++iter)
                {
                  ChangeRecord record = { type, snode->name, view_node->name, iter->name };
                  changes.push_back(record);
                }
              }
              else
              {
                ChangeRecord record = { type, snode->name, view_node->name, view_node->columns[unsorted_nodes[i][3]].name };
                changes.push_back(record);
              }
            }
          } 
          break;

          case 5:
          {
            ObjectType type;
            TableNode *table_node= dynamic_cast<TableNode*>(get_object_node_by_index(unsorted_nodes[i], &type));
            if (table_node && unsorted_nodes[i][3] == TABLE_COLUMNS_NODE_INDEX) // Columns group
            {
              SchemaNode *snode(&_schemata[unsorted_nodes[i][0]]);
              ChangeRecord record = { type, snode->name, table_node->name, table_node->columns[unsorted_nodes[i][4]].name };
              changes.push_back(record);
            }
          }
          break;
        }
      }
      delegate->tree_activate_objects(name, changes);
      
      return true;    
    }
    
    if (name == "create")
    {
      if (unsorted_nodes.size() == 0)
      {
        delegate->tree_create_object(Schema, "", "");
      }
      else
      {
        bec::NodeId node= unsorted_nodes[0];
        switch (node.depth())
        {
          case 1:
            delegate->tree_create_object(Schema, "", "");
            break;
            
          case 2:
          case 3:
          {
            SchemaNode &snode(_schemata[node[0]]);
            switch (node[1])
            {
              case TABLES_NODE_INDEX: delegate->tree_create_object(Table, snode.name, ""); break;
              case VIEWS_NODE_INDEX: delegate->tree_create_object(View, snode.name, ""); break;
              case ROUTINES_NODE_INDEX: delegate->tree_create_object(Routine, snode.name, ""); break;
            }
          }
            break;
        }
      }
      return true;
    }
    
    if (name == "set_active_schema")
    {
      bec::NodeId node= unsorted_nodes[0];
      ChangeRecord record = { Schema, "", _schemata[node[0]].name };
      std::vector<ChangeRecord> actions;
      actions.push_back(record);
      delegate->tree_activate_objects("activate", actions);
      return true;
    }
    if (name == "filter_schema")
    {
      bec::NodeId node= unsorted_nodes[0];
      ChangeRecord record = { Schema, "", _schemata[node[0]].name };
      std::vector<ChangeRecord> actions;
      actions.push_back(record);
      delegate->tree_activate_objects("filter", actions);
      return true;
    }
    
    try
    {
      bec::MenuItem item;
      
      std::string schema;
      std::string object;
      std::string type;
      bec::NodeId node= unsorted_nodes.empty() ? bec::NodeId() : unsorted_nodes[0];
      switch (node.depth())
      {
        case 1:
          schema= _schemata[node[0]].name;
          object= "";
          type= "schema";
          break;
        case 3:
        {
          schema= _schemata[node[0]].name;
          switch (node[1])
          {
            case TABLES_NODE_INDEX: // Table
              type= "table";
              object= _schemata[node[0]].tables[node[2]].name;
              break;
            case VIEWS_NODE_INDEX: // View
              type= "view";
              object= _schemata[node[0]].views[node[2]].name;
              break;
            case ROUTINES_NODE_INDEX: // SP
              type= "routine";
              object= _schemata[node[0]].routines[node[2]].name;
              break;
          }
        }
      }
      
      if (!type.empty())
        return call_object_plugin_items_slot(name, schema, object, type);
    }
    catch (const std::exception &exc)
    {
      g_message("exception fetching context menu items for live tree: %s", exc.what());
    }
  }

  return false;
}


int LiveSchemaTree::get_index_of_schema(const std::string &schema_name) const
{
  for (int n= 0, count= _schemata.size(); n < count; ++n)
    if (schema_name == _schemata[n].name)
      return n;
  std::string upper_schema_name= toupper(schema_name);
  for (int n= 0, count= _schemata.size(); n < count; ++n)
    if (upper_schema_name == toupper(_schemata[n].name))
      return n;
  return -1;
}


bool LiveSchemaTree::is_schema_contents_enabled() const
{
  return _is_schema_contents_enabled;
}


void LiveSchemaTree::is_schema_contents_enabled(bool value)
{
  _is_schema_contents_enabled= value;
}

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

/**
 * Returns true if the given node corresponds to the currently active schema.
 */
bool LiveSchemaTree::is_highlighted(const NodeId& node)
{
  if (node.depth() != 1)
    return false;

  if (node[0] > -1 && node[0] < (int) _schemata.size())
    if (identifiers_equal(_active_schema.c_str(), _schemata[node[0]].name))
      return true;

  return false;
}

std::string LiveSchemaTree::get_filter_wildcard(const std::string& filter)
{
  std::string wildcard = filter;
  if (filter.length() == 0)
    wildcard = "*";
  else
  {
    //if ('*' != filter.at(0))
    //  wildcard = "*" + wildcard;

    if ('*' != filter.at(filter.length() - 1))
      wildcard += "*";
  }

  return wildcard;
}

//--------------------------------------------------------------------------------------------------
bool LiveSchemaTree::filter_data(LiveSchemaTree& target)
{
  bool ret_val = false;

  target._schemata.clear();
  target._active_schema = _active_schema;

  for(size_t index = 0; index < _schemata.size(); index++)
  {
    SchemaNode target_schema;

    // Uses the target filter patterns to filter the data
    if(_schemata[index].filter_data(target._filter_type, target._schema_pattern, target._object_pattern, target_schema))
    {
      ret_val = true;
      target._schemata.push_back(target_schema);
    }
  }

  return ret_val;
}

bool LiveSchemaTree::filter_data(ObjectType object_type, const std::string& filter, LiveSchemaTree& target)
{
  // Sets the filter on the target LST
  target.set_filter(filter, object_type);
  
  return filter_data(target);
}

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

void LiveSchemaTree::clean_filter()
{
  if (_filter.length() > 0)
  {
    _filter_type = Any;
    _filter = "";

    g_pattern_spec_free(_schema_pattern);
    _schema_pattern = NULL;
  
    if(_object_pattern)
    {
      g_pattern_spec_free(_object_pattern);
      _object_pattern = NULL;
    }
  }
}

void LiveSchemaTree::set_filter(std::string filter, ObjectType type)
{
  // Cleans the previous filter if any...
  clean_filter();

  if(filter.length() > 0)
  {
    _filter = filter;

    std::vector<std::string> filters = base::split(_filter, ".", 2);

    // Gets the filter wildcard strings
    std::string schema_filter = get_filter_wildcard(filters[0]);
    std::string object_filter = get_filter_wildcard(filters.size() > 1 ? filters[1] : "" );

    _schema_pattern = g_pattern_spec_new(schema_filter.c_str());

    if (filters.size() > 1 && object_filter != "*")
        _object_pattern = g_pattern_spec_new(object_filter.c_str());
  }
}
