/* 
 * Copyright (c) 2009, 2015, 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 "wb_live_schema_tree.h"
#include "grtdb/charset_utils.h"
#include "grt/icon_manager.h"

#include "grts/structs.db.query.h"

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

#include "mforms/app.h"
#include <boost/make_shared.hpp>

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

DEFAULT_LOG_DOMAIN("SqlEditorSchemaTree");

const std::string LiveSchemaTree::SCHEMA_TAG = "_SCHEMA_";
const std::string LiveSchemaTree::TABLES_TAG = "_TABLES_";
const std::string LiveSchemaTree::VIEWS_TAG = "_VIEWS_";
const std::string LiveSchemaTree::PROCEDURES_TAG = "_PROCEDURES_";
const std::string LiveSchemaTree::FUNCTIONS_TAG = "_FUNCTIONS_";
const std::string LiveSchemaTree::TABLE_TAG = "_TABLE_";
const std::string LiveSchemaTree::VIEW_TAG = "_VIEW_";
const std::string LiveSchemaTree::ROUTINE_TAG = "_ROUTINE_";
const std::string LiveSchemaTree::COLUMNS_TAG = "_COLUMNS_";
const std::string LiveSchemaTree::INDEXES_TAG = "_INDEXES_";
const std::string LiveSchemaTree::TRIGGERS_TAG = "_TRIGGERS_";
const std::string LiveSchemaTree::FOREIGN_KEYS_TAG = "_FOREIGN_KEYS_";
const std::string LiveSchemaTree::COLUMN_TAG = "_COLUMN_";
const std::string LiveSchemaTree::INDEX_TAG = "_INDEX_";
const std::string LiveSchemaTree::TRIGGER_TAG = "_TRIGGER_";
const std::string LiveSchemaTree::FOREIGN_KEY_TAG = "_FOREIGN_KEY_";

const std::string LiveSchemaTree::FETCHING_CAPTION = _("fetching...");
const std::string LiveSchemaTree::ERROR_FETCHING_CAPTION = _("could not be fetched");
const std::string LiveSchemaTree::TABLES_CAPTION = _("Tables");
const std::string LiveSchemaTree::VIEWS_CAPTION = _("Views");
const std::string LiveSchemaTree::PROCEDURES_CAPTION = _("Stored Procedures");
const std::string LiveSchemaTree::FUNCTIONS_CAPTION = _("Functions");

const std::string LiveSchemaTree::COLUMNS_CAPTION = _("Columns");
const std::string LiveSchemaTree::INDEXES_CAPTION = _("Indexes");
const std::string LiveSchemaTree::TRIGGERS_CAPTION = _("Triggers");
const std::string LiveSchemaTree::FOREIGN_KEYS_CAPTION = _("Foreign Keys");

const std::string LiveSchemaTree::LST_INFO_BOX_DETAIL_ROW = "<tr>"
                                                              "<td style=\"border:none; padding-left: 15px;\">%s</td>"
                                                              "<td style=\"border:none; padding-left: 15px;\"><font color='#717171'>%s</font></td>"
                                                            "</tr>";

const int LiveSchemaTree::TABLES_NODE_INDEX = 0;
const int LiveSchemaTree::VIEWS_NODE_INDEX = 1;
const int LiveSchemaTree::PROCEDURES_NODE_INDEX = 2;
const int LiveSchemaTree::FUNCTIONS_NODE_INDEX = 3;
const int LiveSchemaTree::TABLE_COLUMNS_NODE_INDEX = 0;
const int LiveSchemaTree::TABLE_INDEXES_NODE_INDEX = 1;
const int LiveSchemaTree::TABLE_FOREIGN_KEYS_NODE_INDEX = 2;
const int LiveSchemaTree::TABLE_TRIGGERS_NODE_INDEX = 3;

const char* LiveSchemaTree::_schema_tokens[16] = {"",                                                           // 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", "SPATIAL",                // The index types
                                                  "INSERT", "UPDATE", "DELETE",                                  // Trigger events
                                                  "BEFORE", "AFTER"};                                            // Trigger timing

LiveSchemaTree::LSTData::LSTData()
  : details("")
{
};

void LiveSchemaTree::LSTData::copy(LSTData* other)
{
  this->details = other->details;
}

std::string LiveSchemaTree::LSTData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");

  if (full)
  {
    std::string owner_name = node->get_string(0);

    ret_val = strfmt(_("<b>%s:</b> <font color='#148814'><b>%s</b></font><br><br>"), get_object_name().c_str(), owner_name.c_str());
  }
  else
    ret_val = details;

  return ret_val;
}

void LiveSchemaTree::ColumnData::copy(LSTData* other)
{
  LSTData::copy(other);

  ColumnData* pother = dynamic_cast<ColumnData*>(other);

  if (pother)
  {
    this->name = pother->name;
    this->type = pother->type;
    this->default_value = pother->default_value;
    this->charset_collation = pother->charset_collation;
    this->is_id = pother->is_id;
    this->is_pk = pother->is_pk;
    this->is_fk = pother->is_fk;
    this->is_idx = pother->is_idx;
  }
}

std::string LiveSchemaTree::ColumnData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");
  
  if (details.empty())
  {
    std::string html_name = name;
    if (is_pk || is_idx)
    {
      if (is_pk)
        html_name = "<u>" + html_name + "</u>";
      
      html_name = "<b>" + html_name + "</b>";
    }
    
    std::string html_type = type;
    if (is_pk)
      html_type += " PK";
    
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), html_name.c_str(), html_type.c_str());
  }
  

  if (full)
  {
    ret_val = LSTData::get_details(full, node);
    if (!charset_collation.empty())
    {
      ret_val += _("<b>Collation:</b>  ");
      ret_val += charset_collation;
      ret_val += "<br><br>";
    }
    ret_val += _("<b>Definition:</b><table style=\"border: none; border-collapse: collapse;\">");
    ret_val += details;
    ret_val += "</table><br><br>";
  }
  else
    ret_val = details;
  
  return ret_val;
}

void LiveSchemaTree::FKData::copy(LSTData* other)
{
  LSTData::copy(other);

  FKData* pother = dynamic_cast<FKData*>(other);

  if (pother)
  {
    this->referenced_table = pother->referenced_table;
    this->from_cols = pother->from_cols;
    this->to_cols = pother->to_cols;
    this->update_rule = pother->update_rule;
    this->delete_rule = pother->delete_rule;
  }
}

std::string LiveSchemaTree::FKData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");

  if (details.empty())
  {
    std::string target = strfmt("%s (%s \xE2\x86\x92 %s)", referenced_table.c_str(), from_cols.c_str(), to_cols.c_str());
    details = "<table style=\"border: none; border-collapse: collapse;\">";
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Target", target.c_str());
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "On Update", externalize_token(update_rule).c_str());
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "On Delete", externalize_token(delete_rule).c_str());
    details += "</table>";
  }

  if (full)
  {
    ret_val = LSTData::get_details(full, node);
    ret_val += _("<b>Definition:</b><br>");
    ret_val += details;
  }
  else
    ret_val = details;
  
  return ret_val;
}

void LiveSchemaTree::IndexData::copy(LSTData* other)
{
  LSTData::copy(other);

  IndexData* pother = dynamic_cast<IndexData*>(other);

  if (pother)
  {
    this->columns.assign(pother->columns.begin(), pother->columns.end());
    this->type = pother->type;
    this->unique = pother->unique;
  }
}

std::string LiveSchemaTree::IndexData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");
  
  if (details.empty())
  {
    details = "<table style=\"border: none; border-collapse: collapse;\">";
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Type", externalize_token(type).c_str());
    std::string unique_str = unique ? "Yes" : "No";
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Unique", unique_str.c_str());
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Columns", columns[0].c_str());
    
    for(size_t index = 1; index < columns.size();index++)
      details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "", columns[index].c_str());

    details+="</table>";
  }
  
  if (full)
  {
    ret_val = LSTData::get_details(full, node);
    ret_val += _("<b>Definition:</b><br>");
    ret_val += details;
  }
  else
    ret_val = details;
  
  return ret_val;
}

void LiveSchemaTree::TriggerData::copy(LSTData* other)
{
  LSTData::copy(other);

  TriggerData* pother = dynamic_cast<TriggerData*>(other);

  if (pother)
  {
    this->event_manipulation = pother->event_manipulation;
    this->timing = pother->timing;
  }
}

std::string LiveSchemaTree::TriggerData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val("");
  
  if (details.empty())
  {
    details = "<table style=\"border: none; border-collapse: collapse;\">";
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Event", externalize_token(event_manipulation).c_str());
    details += strfmt(LST_INFO_BOX_DETAIL_ROW.c_str(), "Timing", externalize_token(timing).c_str());
    details+="</table>";
  }
  
  if (full)
  {
    ret_val = LSTData::get_details(full, node);
    ret_val += _("<b>Definition:</b><br>");
    ret_val += details;
  }
  else
    ret_val = details;
  
  return ret_val;
}


void LiveSchemaTree::ObjectData::copy(LSTData* other)
{
  LSTData::copy(other);

  ObjectData* pother = dynamic_cast<ObjectData*>(other);

  if (pother)
  {
    this->fetched = pother->fetched;
    this->fetching = pother->fetching;
  }
}

void LiveSchemaTree::ViewData::copy(LSTData* other)
{
  ObjectData::copy(other);

  ViewData* pother = dynamic_cast<ViewData*>(other);

  if (pother)
  {
    this->_loaded_mask = pother->_loaded_mask;
    this->_loading_mask = pother->_loading_mask;
    this->columns_load_error = pother->columns_load_error;
  }
}

std::string LiveSchemaTree::ViewData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val;
  
  if (full)
    ret_val = LSTData::get_details(full, node);

  if (_loaded_mask & COLUMN_DATA)
  {
    int count = (get_type() == Table) ? node->get_child(TABLE_COLUMNS_NODE_INDEX)->count() : node->count();
    if (count)
    {
      ret_val += _("<b>Columns:</b><table style=\"border: none; border-collapse: collapse;\">");

      for(int index = 0; index < count; index++)
      {
        ColumnData* pdata;
        if (get_type() == Table)
          pdata = dynamic_cast<ColumnData*>(node->get_child(TABLE_COLUMNS_NODE_INDEX)->get_child(index)->get_data());
        else
          pdata = dynamic_cast<ColumnData*>(node->get_child(index)->get_data());

        ret_val += pdata->get_details(false, node);
      }

      ret_val += "</table><br><br>";
    }
  }

  // Appends the error message if needed
  if (columns_load_error)
    ret_val += details;

  return ret_val;
}

std::string LiveSchemaTree::TableData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val = ViewData::get_details(full, node);

  if (_loaded_mask & FK_DATA)
  {
    int count = node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->count();
    if (count)
    {
      ret_val += _("<div><b>Related Tables:</b></div>");

      for(int index = 0; index < count; index++)
      {
        FKData* pdata = dynamic_cast<FKData*>(node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->get_child(index)->get_data());

        ret_val += pdata->get_details(false, node);
      }
    }
  }

  return ret_val;
}

void LiveSchemaTree::SchemaData::copy(LSTData* other)
{
  LSTData::copy(other);

  SchemaData* pother = dynamic_cast<SchemaData*>(other);

  if (pother)
  {
    this->fetched = pother->fetched;
    this->fetching = pother->fetching;
  }
}

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

short LiveSchemaTree::ViewData::get_loaded_mask()
{
  return _loaded_mask;
}

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

void LiveSchemaTree::ViewData::set_unloaded_data(short mask)
{
  // Gets the data that is loaded and needs to be marked as unloaded
  short filter = _loaded_mask & mask;

  _loaded_mask -= filter;
}

void LiveSchemaTree::ViewData::set_loaded_data(short mask)
{
  // Gets the data that is being marked as loaded and was loading
  short filter = mask & _loading_mask;
  _loading_mask -= filter;

  // Now marks the data as loaded
  _loaded_mask |= mask;
}

short LiveSchemaTree::ViewData::get_loading_mask()
{
  return _loading_mask;
}

void LiveSchemaTree::ViewData::set_loading_mask(short mask)
{
  _loading_mask = mask;
}

bool LiveSchemaTree::ViewData::is_data_loaded(short mask)
{
  return (_loaded_mask & mask) == mask;
}

bool LiveSchemaTree::ViewData::is_update_complete()
{
  bool ret_val = false;

  if ( _reload_mask )
  {
    short loaded_mask = get_loaded_mask();

    if ((loaded_mask & _reload_mask) == _reload_mask)
    {
      _reload_mask = 0;
      ret_val = true;
    }
  }

  return ret_val;
}



std::string LiveSchemaTree::ProcedureData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val = ObjectData::get_details(true, node);
  ret_val += ObjectData::get_details(false, node);
  
  return ret_val;
}

std::string LiveSchemaTree::FunctionData::get_details(bool full, const mforms::TreeNodeRef& node)
{
  std::string ret_val = ObjectData::get_details(true, node);
  ret_val += ObjectData::get_details(false, node);
  
  return ret_val;
}
//--------------------------------------------------------------------------------------------------

LiveSchemaTree::LiveSchemaTree(grt::GRT* grt)
: _grt(grt), _model_view(NULL), _case_sensitive_identifiers(false), 
_is_schema_contents_enabled(true), _enabled_events(false), _base(0), _filter_type(Any), _schema_pattern(0), _object_pattern(0)
{
  fill_node_icons();
  
  // Setup the schema node collection skeleton
  mforms::TreeNodeCollectionSkeleton schema_nodes(_icon_paths[Schema]);
  
  mforms::TreeNodeSkeleton tables(TABLES_CAPTION, _icon_paths[TableCollection], TABLES_TAG);
  schema_nodes.children.push_back(tables);

  mforms::TreeNodeSkeleton views(VIEWS_CAPTION, _icon_paths[ViewCollection], VIEWS_TAG);
  schema_nodes.children.push_back(views);

  mforms::TreeNodeSkeleton procedures(PROCEDURES_CAPTION, _icon_paths[ProcedureCollection], PROCEDURES_TAG);
  schema_nodes.children.push_back(procedures);
  
    mforms::TreeNodeSkeleton functions(FUNCTIONS_CAPTION, _icon_paths[FunctionCollection], FUNCTIONS_TAG);
    schema_nodes.children.push_back(functions);
  _node_collections[Schema] = schema_nodes;
  
  // Setup the table node collection skeleton
  mforms::TreeNodeCollectionSkeleton table_nodes(_icon_paths[Table]);

  mforms::TreeNodeSkeleton columns(COLUMNS_CAPTION, _icon_paths[ColumnCollection], COLUMNS_TAG);
  table_nodes.children.push_back(columns);

  mforms::TreeNodeSkeleton indexes(INDEXES_CAPTION, _icon_paths[IndexCollection], INDEXES_TAG);
  table_nodes.children.push_back(indexes);

  mforms::TreeNodeSkeleton foreign_keys(FOREIGN_KEYS_CAPTION, _icon_paths[ForeignKeyCollection], FOREIGN_KEYS_TAG);
  mforms::TreeNodeSkeleton fetching_fk(FETCHING_CAPTION, _icon_paths[ForeignKey], "");
  foreign_keys.children.push_back(fetching_fk);
  table_nodes.children.push_back(foreign_keys);

  mforms::TreeNodeSkeleton triggers(TRIGGERS_CAPTION, _icon_paths[TriggerCollection], TRIGGERS_TAG);
  mforms::TreeNodeSkeleton fetching_trigger(FETCHING_CAPTION, _icon_paths[Trigger], "");
  triggers.children.push_back(fetching_trigger);
  table_nodes.children.push_back(triggers);
  
  _node_collections[Table] = table_nodes;

  // Setup the view node collection skeleton
  mforms::TreeNodeCollectionSkeleton view_nodes(_icon_paths[View]);
  mforms::TreeNodeSkeleton fetching_view(FETCHING_CAPTION, _icon_paths[View], "");
  view_nodes.children.push_back(fetching_view);
  _node_collections[View] = view_nodes;
  
  mforms::TreeNodeCollectionSkeleton procedure_nodes(_icon_paths[Procedure]);
  _node_collections[Procedure] = procedure_nodes;
  
  mforms::TreeNodeCollectionSkeleton function_nodes(_icon_paths[Function]);
  _node_collections[Function] = function_nodes;
  

  mforms::TreeNodeCollectionSkeleton table_column_nodes(_icon_paths[TableColumn]);
  _node_collections[TableColumn] = table_column_nodes;

  mforms::TreeNodeCollectionSkeleton view_column_nodes(_icon_paths[ViewColumn]);
  _node_collections[ViewColumn] = view_column_nodes;


  mforms::TreeNodeCollectionSkeleton index_nodes(_icon_paths[Index]);
  _node_collections[Index] = index_nodes;


  mforms::TreeNodeCollectionSkeleton trigger_nodes(_icon_paths[Trigger]);
  _node_collections[Trigger] = trigger_nodes;


  mforms::TreeNodeCollectionSkeleton fk_nodes(_icon_paths[ForeignKey]);
  _node_collections[ForeignKey] = fk_nodes;
}

LiveSchemaTree::~LiveSchemaTree()
 { 
   clean_filter();
}

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)
{
  return base::string_compare(a, b, _case_sensitive_identifiers) == 0;
}

void LiveSchemaTree::setup_node(mforms::TreeNodeRef node, ObjectType type, mforms::TreeNodeData* pdata, bool ignore_null_data)
{
  switch(type)
  {
  case Schema:
    node->set_data(pdata ? pdata : new SchemaData());
    break;
  case Table:
    node->set_data(pdata ? pdata : new TableData());
    break;
  case View:
    node->set_data(pdata ? pdata : new ViewData());
    break;
  case Procedure:
    node->set_data(pdata ? pdata : new ProcedureData());
    break;
  case Function:
    node->set_data(pdata ? pdata : new FunctionData());
    break;
  case ViewColumn:
    if (pdata || !ignore_null_data)
      node->set_data(pdata ? pdata : new ColumnData(type));
    break;
  case TableColumn:
    node->set_data(pdata ? pdata : new ColumnData(type));
    break;
  case Index:
    node->set_data(pdata ? pdata : new IndexData());
    break;
  case Trigger:
    if (pdata || !ignore_null_data)
      node->set_data(pdata ? pdata : new TriggerData());
    break;
  case ForeignKey:
    if (pdata || !ignore_null_data)
      node->set_data(pdata ? pdata : new FKData());
    break;
  default:
    break;
  }
}

void LiveSchemaTree::fill_node_icons()
{
  _icon_paths[Schema] = get_node_icon_path(Schema);
  _icon_paths[TableCollection] = get_node_icon_path(TableCollection);
  _icon_paths[ViewCollection] = get_node_icon_path(ViewCollection);
  _icon_paths[ProcedureCollection] = get_node_icon_path(ProcedureCollection);
  _icon_paths[FunctionCollection] = get_node_icon_path(FunctionCollection);
  _icon_paths[Table] = get_node_icon_path(Table);
  _icon_paths[View] = get_node_icon_path(View);
  _icon_paths[Procedure] = get_node_icon_path(Procedure);
  _icon_paths[Function] = get_node_icon_path(Function);
  _icon_paths[ColumnCollection] = get_node_icon_path(ColumnCollection);
  _icon_paths[IndexCollection] = get_node_icon_path(IndexCollection);
  _icon_paths[ForeignKeyCollection] = get_node_icon_path(ForeignKeyCollection);
  _icon_paths[TriggerCollection] = get_node_icon_path(TriggerCollection);
  _icon_paths[ViewColumn] = get_node_icon_path(ViewColumn);
  _icon_paths[TableColumn] = get_node_icon_path(TableColumn);
  _icon_paths[Index] = get_node_icon_path(Index);
  _icon_paths[ForeignKey] = get_node_icon_path(ForeignKey);
  _icon_paths[Trigger] = get_node_icon_path(Trigger);
}

std::string LiveSchemaTree::get_node_icon_path(ObjectType type)
{
  bec::IconId icon = get_node_icon(type);
  return bec::IconManager::get_instance()->get_icon_file(icon);
}

bec::IconId LiveSchemaTree::get_node_icon(ObjectType type)
{
  bec::IconId icon;

  switch(type)
  {
  case Schema:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.unloaded.side.$.png", bec::Icon16);
    break;
  case TableCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Table.many.side.$.png", bec::Icon16);
    break;
  case ViewCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.View.many.side.$.png", bec::Icon16);
    break;
  // TODO: Update the Procedure and Function collection icons to the correct value
  case ProcedureCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.many.side.$.png", bec::Icon16);
    break;
  case FunctionCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.many.side.$.png", bec::Icon16);
    break;
  case Table:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Table.side.$.png", bec::Icon16);
    break;
  case View:
    icon = bec::IconManager::get_instance()->get_icon_id("db.View.side.$.png", bec::Icon16);
    break;
  case Procedure:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Routine.side.$.png", bec::Icon16);
    break;
  case Function:
    icon = bec::IconManager::get_instance()->get_icon_id("grt_function.png", bec::Icon16);
    break;
  case ColumnCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Column.many.side.$.png", bec::Icon16);
    break;
  case IndexCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Index.many.side.$.png", bec::Icon16);
    break;
  case ForeignKeyCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.many.side.$.png", bec::Icon16);
    break;
  case TriggerCollection:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Trigger.many.side.$.png", bec::Icon16);
    break;
  case ViewColumn:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Column.side.$.png", bec::Icon16);
    break;
  case TableColumn:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Column.side.$.png", bec::Icon16);
    break;
  case Index:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Index.side.$.png", bec::Icon16);
    break;
  case ForeignKey:
    icon = bec::IconManager::get_instance()->get_icon_id("db.ForeignKey.side.$.png", bec::Icon16);
    break;
  case Trigger:
    icon = bec::IconManager::get_instance()->get_icon_id("db.Trigger.side.$.png", bec::Icon16);
    break;
  default:
    icon = -1;
    break;
  }
  
  return icon;
}

void LiveSchemaTree::update_node_icon(mforms::TreeNodeRef node)
{
  bec::IconId icon = 0;

  LSTData* pnode_data = dynamic_cast<LSTData*> (node->get_data());
  if (pnode_data)
  {
    switch(pnode_data->get_type())
    {
      case Schema:
        {
          SchemaData *pdata = dynamic_cast<SchemaData*> (node->get_data());
          if (pdata->fetched)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.side.$.png", bec::Icon16);
          else if (pdata->fetching)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.loading.side.$.png", bec::Icon16);
          else
            icon = bec::IconManager::get_instance()->get_icon_id("db.Schema.unloaded.side.$.png", bec::Icon16);
        }
        break;

      case View:
        {
          ViewData *pdata = dynamic_cast<ViewData*> (node->get_data());

          if (pdata->columns_load_error)
            icon = bec::IconManager::get_instance()->get_icon_id("db.View.broken.side.$.png", bec::Icon16);
          else
            icon = bec::IconManager::get_instance()->get_icon_id("db.View.side.$.png", bec::Icon16);
        }
        break;
      case TableColumn:
        {
          ColumnData *pdata = dynamic_cast<ColumnData*> (node->get_data());

          if (pdata->is_pk)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Column.pk.side.$.png", bec::Icon16);
          else if (pdata->is_fk)
            icon = bec::IconManager::get_instance()->get_icon_id("db.Column.fk.side.$.png", bec::Icon16);
        }
        break;
      // No other item changes icon
      default:
        break;
    }
    
    if (icon)
      node->set_icon_path(0, bec::IconManager::get_instance()->get_icon_file(icon));
  }
}

/*
 * Function: update_change_data
 * Description: Uses the received information to
 *              - Generate the list of nodes to be removed
 *              - Purge children to let there only the items to be added
 * Parameters:
 *   parent: the tree node for which the children list will be updated
 *   children: the list of names that will be used on the process
 *   type: the type of children that will be affected (some nodes may have children of different types)
 *   to_remove: a vector containing the nodes to be removed from the parent node
 */
void LiveSchemaTree::update_change_data(mforms::TreeNodeRef parent, base::StringListPtr children,
  ObjectType type, std::vector<mforms::TreeNodeRef>& to_remove)
{
  mforms::TreeNodeRef node;

    int total_nodes = parent->count();
    if (total_nodes == 1 && parent->get_child(0)->get_string(0) == FETCHING_CAPTION)
      to_remove.push_back(parent->get_child(0));
    else
    {
      for(int index = 0; index < total_nodes; index++)
      {
        node = parent->get_child(index);
        LSTData *pchild_data = dynamic_cast<LSTData*>(node->get_data());
      
        // Ensure only items of the same type are analyzed
        if ( pchild_data && pchild_data->get_type() == type)
        {
          std::list<std::string>::iterator found_child = std::find(children->begin(), children->end(), node->get_string(0));

          // If not found, the item will be removed
          if ( found_child == children->end())
            to_remove.push_back(node);
          // If found, the item is removed from the incoming list
          // to let only nodes to be added
          else
            children->erase(found_child);
        }
      }
    }
}

/*
 * Function: update_node_children
 * Description: Updates the children of a node based on a received children name list:
 *              Removes children nodes not contained on the received name list (if not just appending)
 *              Adds children nodes for names on the list for which a node is missing
 * Parameters:
 *   parent: the tree node for which the children list will be updated
 *   children: the list of names that will be used on the process
 *   type: the type of children that will be affected (some nodes may have children of different types)
 *   sorted: used to determine whether the nodes should be inserted in the proper position to keep the objects
 *           sorted or if they just need to be appended the way they come.
 *   just_append: suppresses the logic of removing nodes (i.e. when searching nodes with different filters
 *                they are only appended on the base tree)
 * Return Value: a boolean value indicating whether the children list changed (nodes were added or removed)
 * 
 * NOTE : That children may change, so if the original list is needed, the caller needs to have a copy
 */
bool LiveSchemaTree::update_node_children(mforms::TreeNodeRef parent, base::StringListPtr children,
  ObjectType type, bool sorted, bool just_append)
{
  bool ret_val = false;

  if (_base)
  {
    std::vector<std::string> path = get_node_path(parent);
    mforms::TreeNodeRef base_parent = _base->get_node_from_path(path);
    ret_val = _base->update_node_children(base_parent, children, type, sorted, just_append);

    // Filters the arrived data
    GPatternSpec* pattern = NULL;
    if (type == Schema)
      pattern = _schema_pattern;
    else if(is_object_type(SchemaObject, type))
      pattern = _object_pattern;

    filter_children(type, base_parent, parent, pattern);
  }
  else
  {
    mforms::TreeNodeRef node;
    bool added = false;
    bool removed = false;
    std::vector<mforms::TreeNodeRef> childs_to_remove;
    
    //_model_view->freeze_refresh(); cannot be called from a background thread.
    
    // Calculates the nodes to be removed and the new nodes to be created
    update_change_data(parent, children, type, childs_to_remove);
    
    // Removes deleted children if needed...
    if (!just_append && !childs_to_remove.empty())
    {
      for(size_t index = 0; index < childs_to_remove.size(); index++)
        childs_to_remove[index]->remove_from_parent();
      
      removed = true;
    }

    // If at this point there are nodes on the parent, then the new nodes
    // need to be inserted into the right position.
    // If not, as they are sorted, they just get added.
    std::vector<mforms::TreeNodeRef> added_nodes;
    std::vector<mforms::TreeNodeRef> group_added_nodes;
    std::string icon_path = get_node_icon_path(type);

    if (sorted)
      children->sort(boost::bind(base::stl_string_compare, _1, _2, _case_sensitive_identifiers));
    
    if (!children->empty())
    {
      // it and it_end are used on the iteration process
      // start and end are used to determine a group to be inserted
      // which by default is the whole list
      std::list<std::string>::const_iterator   it = children->begin(),
                                               start = it,
                                               it_end = children->end(),
                                               end = it_end;

      // final_group will be used to prevent searching for the position
      // of the children once one has been found to be at the end.
      bool final_group = false;
      int last_position = -1;
      _node_collections[type].captions.clear();

      if (parent->count() > 0 && sorted)
      {
        // Initializes last_position with the position of the first
        // Item to be inserted
        find_child_position(parent, *it, type, last_position);

        end = start;
        
        do
        {
          it++;
          if (it != it_end)
          {
            int position = 0;
            if (!find_child_position(parent, *it, type, position))
            {
              if (position != last_position)
              {
                // Assigns the captions for the group to be inserted
                _node_collections[type].captions.assign(start, it);

                group_added_nodes = parent->add_node_collection(_node_collections[type], last_position);
                added_nodes.insert(added_nodes.end(), group_added_nodes.begin(), group_added_nodes.end());

                // Start is reset to the iterator position
                start = it;

                last_position = position == -1 ? position : position + (int)_node_collections[type].captions.size();

                // Shortcut to quit once an item has been found to be located
                // At the end of the list
                final_group = (last_position == -1);
              }
            }
          }

          // All the time end will advance with the iterator
          end = it;
        } while (it != it_end && !final_group);
      }

      // Inserts the last group of nodes...
      end = children->end();
      _node_collections[type].captions.assign(start, end);
      if (!_node_collections[type].captions.empty())
      {
        // positions the index on the previous position (last in group)
        group_added_nodes = parent->add_node_collection(_node_collections[type], last_position);
        added_nodes.insert(added_nodes.end(), group_added_nodes.begin(), group_added_nodes.end());
      }

      for(size_t index = 0; index < added_nodes.size(); index++)
      {
        setup_node(added_nodes[index], type);
        added = true;
      }
    }
    
    ret_val = (added || removed);

    std::string icon = get_node_icon_path(type);

    //_model_view->thaw_refresh(); Cannot be called from a background thread.
    }

  return ret_val;
}

std::string LiveSchemaTree::get_field_description(const mforms::TreeNodeRef& node)
{
  std::string text("");
  mforms::TreeNodeRef temp_node = node;

  try
  {
    while(temp_node && !text.length())
    {
      LSTData *pdata = dynamic_cast<LSTData*>(temp_node->get_data());

      if (pdata)
      {
        ObjectType type = pdata->get_type();
        
        if (is_object_type(TableOrView, type))
        {
          short fetch_mask = (type == Table) ? COLUMN_DATA|INDEX_DATA : COLUMN_DATA;
        
          load_table_details(type, get_schema_name(node), temp_node->get_string(0), fetch_mask);
        }
        else if (is_object_type(RoutineObject, type))
        {
          load_routine_details(temp_node);
        }

        text = pdata->get_details(true, temp_node);
      }
      else
        temp_node = temp_node->get_parent();
    }
  }
  catch (std::exception &e)
  {
    text = _("Unable to retrieve node description.");
    log_error("Exception retrieving node description : %s\n", strfmt("%s", e.what()).c_str());
  }

  return text;
}

void LiveSchemaTree::set_notify_on_reload(const mforms::TreeNodeRef& node)
{
  mforms::TreeNodeRef temp_node = node;

  LSTData *pdata = NULL;
  while(temp_node && !pdata)
  {
    pdata = dynamic_cast<LSTData*>(temp_node->get_data());

    if (pdata)
      notify_on_reload_data = pdata;
    else
      temp_node = temp_node->get_parent();
  }
}

void LiveSchemaTree::notify_on_reload(const mforms::TreeNodeRef& node)
{
  mforms::TreeNodeRef temp_node = node;

  LSTData *pdata = NULL;
  while(temp_node && !pdata)
  {
    pdata = dynamic_cast<LSTData*>(temp_node->get_data());

    if (pdata && pdata == notify_on_reload_data && pdata->is_update_complete())
      _model_view->changed();
    else
      temp_node = temp_node->get_parent();
  }
}

void LiveSchemaTree::set_active_schema(const std::string &schema)
{
  mforms::TreeNodeTextAttributes attrs;

  if (_model_view)
  {
    mforms::TreeNodeRef current_active = get_child_node(_model_view->root_node(), _active_schema);
    mforms::TreeNodeRef new_active     = get_child_node(_model_view->root_node(), schema);

    if (current_active)
    {
      std::string name = current_active->get_string(0);
      current_active->set_string(0, name);
      current_active->set_attributes(0, attrs);
    }

    if (new_active)
    {
      std::string name = new_active->get_string(0);
      attrs.bold = true;
      new_active->set_string(0, name);
      new_active->set_attributes(0, attrs);
        
      new_active->expand();
    }
  }

  _active_schema = schema;

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

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)
{
  if (_model_view)
  {
    mforms::TreeNodeRef schema_node;
    bool created = old_obj_name.empty() && !new_obj_name.empty();
    bool deleted = !old_obj_name.empty() && new_obj_name.empty();
    bool changed = !old_obj_name.empty() && !new_obj_name.empty();

    if (type == Schema)
    {
      if (created)
        insert_node(_model_view->root_node(), new_obj_name, type);
      else if (deleted)
      {
        schema_node = get_child_node(_model_view->root_node(), old_obj_name);

        if (schema_node)
          schema_node->remove_from_parent();
      }
    }
    else
    {
      schema_node = get_child_node(_model_view->root_node(), schema_name);

      if (schema_node)
      {
        mforms::TreeNodeRef object_node;
        if (!created)
        {
          switch(type)
          {
          case Table:
            object_node = this->get_child_node(schema_node->get_child(TABLES_NODE_INDEX), old_obj_name);
            break;
          case View:
            object_node = this->get_child_node(schema_node->get_child(VIEWS_NODE_INDEX), old_obj_name);
            break;
          case Procedure:
              object_node = this->get_child_node(schema_node->get_child(PROCEDURES_NODE_INDEX), old_obj_name, type);
              break;
          case Function:
            object_node = this->get_child_node(schema_node->get_child(FUNCTIONS_NODE_INDEX), old_obj_name, type);
            break;
          default:
            break;
          }
        }

        if (changed && object_node)
        {
            if (old_obj_name != new_obj_name)
                object_node->set_string(0, new_obj_name);

            // As the object has changed we trigger a reload on
            // Its content
            reload_object_data(object_node);
        }
        else
        {
          if (created)
          {
            mforms::TreeNodeRef parent_node;
            int target_position = 0;
            switch(type)
            {
            case Table:
              if (!find_child_position(schema_node->get_child(TABLES_NODE_INDEX), new_obj_name, type, target_position))
                parent_node = schema_node->get_child(TABLES_NODE_INDEX);
              break;
            case View:
              if (!find_child_position(schema_node->get_child(VIEWS_NODE_INDEX), new_obj_name, type, target_position))
                parent_node = schema_node->get_child(VIEWS_NODE_INDEX);
              break;
            case Procedure:
              if (!find_child_position(schema_node->get_child(PROCEDURES_NODE_INDEX), new_obj_name, type, target_position))
                  parent_node = schema_node->get_child(PROCEDURES_NODE_INDEX);
              break;
            case Function:
              if (!find_child_position(schema_node->get_child(FUNCTIONS_NODE_INDEX), new_obj_name, type, target_position))
                parent_node = schema_node->get_child(FUNCTIONS_NODE_INDEX);
              break;
            default:
              break;
            }
            
            if (parent_node)
              insert_node(parent_node, new_obj_name, type);
          }
          else
          {
            if (object_node)
              object_node->remove_from_parent();
          }
        }
      }
    }
  }
}

void LiveSchemaTree::schema_contents_arrived(const std::string &schema_name,
  base::StringListPtr tables, base::StringListPtr views, base::StringListPtr procedures,
  base::StringListPtr functions, bool just_append)
{
  if (_base)
  {
    _base->schema_contents_arrived(schema_name, tables, views, procedures, functions, just_append);
    filter_data();
  }
  else
  {
    if (_model_view)
    {
      mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name);

      if (schema_node)
      {
        mforms::TreeNodeRef tables_node   = schema_node->get_child(TABLES_NODE_INDEX);
        mforms::TreeNodeRef views_node    = schema_node->get_child(VIEWS_NODE_INDEX);
        mforms::TreeNodeRef procedures_node = schema_node->get_child(PROCEDURES_NODE_INDEX);
        mforms::TreeNodeRef functions_node = schema_node->get_child(FUNCTIONS_NODE_INDEX);
        
        SchemaData *pdata = dynamic_cast<SchemaData*>(schema_node->get_data());

        // When an error occurred all the incoming lists are NULL
        if (tables && views && procedures && functions)
        {
          int old_table_count = tables_node->count();
          int old_view_count = tables_node->count();
          
          //We need to duplicate the data, because it's being changed inside update_node_children
          //and we can't do this because it's shared between threads
          update_node_children(tables_node, boost::make_shared<StringList>(*tables), Table, true, just_append);
          update_node_children(views_node, boost::make_shared<StringList>(*views), View, true, just_append);
          update_node_children(procedures_node, boost::make_shared<StringList>(*procedures), Procedure,true, just_append);
          update_node_children(functions_node, boost::make_shared<StringList>(*functions), Function, true, just_append);
          

          // If there were nodes that means this is a refresh, in such case loaded tables
          // must be reloaded so the changes are displayed
          if (old_table_count)
          {
              for (size_t index = 0; index < (size_t)tables_node->count(); index++)
              {
                mforms::TreeNodeRef pnode = tables_node->get_child((int)index);
                  reload_object_data(pnode);
              }
          }
          
          // If there were nodes that means this is a refresh, in such case loaded tables
          // must be reloaded so the changes are displayed
          if (old_view_count)
          {
              for (size_t index = 0; index < (size_t)views_node->count(); index++)
              {
                mforms::TreeNodeRef pnode = views_node->get_child((int)index);
                  reload_object_data(pnode);
              }
          }

          if (!just_append)
            pdata->fetched = true;

          tables_node->set_string(0, TABLES_CAPTION);
          views_node->set_string(0, VIEWS_CAPTION);
          procedures_node->set_string(0, PROCEDURES_CAPTION);
          functions_node->set_string(0, FUNCTIONS_CAPTION);
        }
        // This section will be reached whenever there´s an exception loading the schema data
        else
        {
          tables_node->set_string(0, TABLES_CAPTION + " " + ERROR_FETCHING_CAPTION);
          views_node->set_string(0, VIEWS_CAPTION + " " + ERROR_FETCHING_CAPTION);
          procedures_node->set_string(0, PROCEDURES_CAPTION + " " + ERROR_FETCHING_CAPTION);
          functions_node->set_string(0, FUNCTIONS_CAPTION + " " + ERROR_FETCHING_CAPTION);
        }
        
        pdata->fetching = false;
        update_node_icon(schema_node);
      }
    }
  }
}

void LiveSchemaTree::load_table_details(ObjectType object_type, const std::string schema_name, const std::string object_name, int fetch_mask)
{
  if (_model_view)
  {
    mforms::TreeNodeRef node;
    
    // If object type is unknown there's no need to pull the
    // object from the tree
    if (object_type != Any)
      node = get_node_for_object(schema_name, object_type, object_name);

    if (node)
      load_table_details(node, fetch_mask);
    else
      fetch_table_details(object_type, schema_name, object_name, fetch_mask);
  }
}

void LiveSchemaTree::load_table_details(mforms::TreeNodeRef& node, int fetch_mask)
{
  ViewData* pdata = dynamic_cast<ViewData*>(node->get_data());

  if (pdata)
  {
    short loaded_mask = pdata->get_loaded_mask();
    short loading_mask = pdata->get_loading_mask();

    // Calculates what needs to be loaded based on what was requested and what is already loaded
    short data_load_flags = (fetch_mask ^ loaded_mask) & fetch_mask;

    // Calculates what needs to be loaded based on what was left on the previous check and what is already being loaded
    data_load_flags = (data_load_flags ^ loading_mask) & data_load_flags;

    if (data_load_flags)
    {
      pdata->set_loading_mask(data_load_flags);
      std::string schema_name = get_schema_name(node);
      fetch_table_details(pdata->get_type(), schema_name, node->get_string(0), data_load_flags);
    }
  }
}


void LiveSchemaTree::fetch_table_details(ObjectType object_type, const std::string schema_name, const std::string object_name, int fetch_mask)
{
    boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock();

    if (delegate)
    {

      delegate->fetch_object_details(schema_name, object_name, object_type, fetch_mask,
                                    boost::bind(&LiveSchemaTree::update_node_children, this, _1, _2, _3, _4, _5));
    }
}

void LiveSchemaTree::load_routine_details(mforms::TreeNodeRef& node)

{
  ObjectData* pdata = dynamic_cast<ObjectData*>(node->get_data());

  if (pdata && !pdata->fetched && !pdata->fetching)
  {
    pdata->fetching = true;

    std::string schema_name = get_schema_name(node);

    boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock();

    if (delegate)
      delegate->fetch_routine_details(schema_name, node->get_string(0), pdata->get_type());
  }
}


void LiveSchemaTree::load_data_for_filter(const std::string& schema_filter, const std::string& object_filter)
{
    if (boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock())
    {
      std::string remote_schema_filter = get_filter_wildcard(schema_filter, RemoteLike);
      std::string remote_object_filter = get_filter_wildcard(object_filter, RemoteLike);
      delegate->fetch_data_for_filter(remote_schema_filter, remote_object_filter, 
                              boost::bind(&LiveSchemaTree::schema_contents_arrived, this, _1, _2, _3, _4, _5, _6));
    }
}

void LiveSchemaTree::load_schema_content(mforms::TreeNodeRef& schema_node)
{
  SchemaData* data = dynamic_cast<SchemaData*>(schema_node->get_data());

  if (!data->fetched && !data->fetching)
  {
    data->fetching = true;
    std::string name = schema_node->get_string(0);

    if (_base)
    {
      mforms::TreeNodeRef base_schema_node = _base->get_node_from_path(get_node_path(schema_node));

      base_schema_node->get_child(TABLES_NODE_INDEX)->set_string(0, TABLES_CAPTION + " " + FETCHING_CAPTION);
      base_schema_node->get_child(VIEWS_NODE_INDEX)->set_string(0, VIEWS_CAPTION + " " + FETCHING_CAPTION);
      base_schema_node->get_child(PROCEDURES_NODE_INDEX)->set_string(0, PROCEDURES_CAPTION + " " + FETCHING_CAPTION);
      base_schema_node->get_child(FUNCTIONS_NODE_INDEX)->set_string(0, FUNCTIONS_CAPTION + " " + FETCHING_CAPTION);
    }

    schema_node->get_child(TABLES_NODE_INDEX)->set_string(0, TABLES_CAPTION + " " + FETCHING_CAPTION);
    schema_node->get_child(VIEWS_NODE_INDEX)->set_string(0, VIEWS_CAPTION + " " + FETCHING_CAPTION);
    schema_node->get_child(PROCEDURES_NODE_INDEX)->set_string(0, PROCEDURES_CAPTION + " " + FETCHING_CAPTION);
    schema_node->get_child(FUNCTIONS_NODE_INDEX)->set_string(0, FUNCTIONS_CAPTION + " " + FETCHING_CAPTION);

    update_node_icon(schema_node);

    if (boost::shared_ptr<FetchDelegate> delegate = _fetch_delegate.lock())
    {
      delegate->fetch_schema_contents(name, 
                              boost::bind(&LiveSchemaTree::schema_contents_arrived, this, _1, _2, _3, _4, _5, _6));
    }
  }
}

mforms::TreeNodeRef LiveSchemaTree::get_node_for_object(const std::string &schema_name, ObjectType type, const std::string& name)
{
  mforms::TreeNodeRef object_node = mforms::TreeNodeRef();

  if (_model_view)
  {
    mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name);

    if (schema_node)
    {
      if (type == Schema)
        object_node = schema_node;
      else
      {
        switch(type)
        {
        case Table:
          object_node = get_child_node(schema_node->get_child(TABLES_NODE_INDEX), name);
          break;
        case View:
          object_node = get_child_node(schema_node->get_child(VIEWS_NODE_INDEX), name);
          break;
        case Procedure:
          object_node = get_child_node(schema_node->get_child(PROCEDURES_NODE_INDEX), name, type);
          break;
        case Function:
          object_node = get_child_node(schema_node->get_child(FUNCTIONS_NODE_INDEX), name, type);
          break;
        default:
          break;
        }
      }
    }
  }

  return object_node;
}


mforms::TreeNodeRef LiveSchemaTree::create_node_for_object(const std::string &schema_name, ObjectType type, const std::string& name)
{
  bool created_schema = false;
  mforms::TreeNodeRef object_node = mforms::TreeNodeRef();
  mforms::TreeNodeRef parent_node = mforms::TreeNodeRef();

  if (_model_view)
  {
    mforms::TreeNodeRef schema_node = get_child_node(_model_view->root_node(), schema_name);

    // Creates the schema if doesnt exist...
    if (!schema_node)
    {
      schema_node = insert_node(_model_view->root_node(), schema_name, Schema);
      created_schema = true;
    }

    switch(type)
    {
    case Table:
      parent_node = schema_node->get_child(TABLES_NODE_INDEX);
      break;
    case View:
      parent_node = schema_node->get_child(VIEWS_NODE_INDEX);
      break;
    case Procedure:
      parent_node = schema_node->get_child(PROCEDURES_NODE_INDEX);
      break;
    case Function:
      parent_node = schema_node->get_child(FUNCTIONS_NODE_INDEX);
      break;
    default:
      break;
    }

    if (parent_node)
      object_node = insert_node(parent_node, name, type);
    else if (created_schema)
      schema_node->remove_from_parent();
  }

  return object_node;
}


grt::BaseListRef LiveSchemaTree::get_selected_objects()
{
  grt::ListRef<db_query_LiveDBObject> selection(_grt);

  if (_model_view)
  {
    std::list<mforms::TreeNodeRef> selnodes(_model_view->get_selection());
    db_query_LiveDBObjectRef table_object;

    std::list<mforms::TreeNodeRef>::const_iterator index, end = selnodes.end();
    for (index = selnodes.begin(); index != end; index++)
    {
      db_query_LiveDBObjectRef obj(_grt);

      mforms::TreeNodeRef node = *index;
      LSTData* pdata = dynamic_cast<LSTData*>(node->get_data());
      ObjectType current_type = Any;

      if (pdata)
        current_type = pdata->get_type();
      else
      {
        if (node->get_tag() == TABLES_TAG)
          current_type = TableCollection;
        else if (node->get_tag() == VIEWS_TAG)
          current_type = ViewCollection;
        else if (node->get_tag() == PROCEDURES_TAG)
          current_type = ProcedureCollection;
        else if (node->get_tag() == FUNCTIONS_TAG)
          current_type = FunctionCollection;
        else if (node->get_tag() == COLUMNS_TAG)
          current_type = ColumnCollection;
        else if (node->get_tag() == INDEXES_TAG)
          current_type = IndexCollection;
        else if (node->get_tag() == TRIGGERS_TAG)
          current_type = TriggerCollection;
        else if (node->get_tag() == FOREIGN_KEYS_TAG)
          current_type = ForeignKeyCollection;
      }

      // the selection list is always sorted top elements first, so we now for sure that
      // if a node and one of its subnodes are selected, the parent node will always come
      // first in the list
      switch (current_type)
      {
        case Any:
        case NoneType:
          break;
        case Schema:
          obj->type("db.Schema");
          obj->schemaName(node->get_string(0));
          obj->name(node->get_string(0));
          break;
        case Table:
          obj->type("db.Table");
          obj->schemaName(node->get_parent()->get_parent()->get_string(0));
          obj->name(node->get_string(0));
          table_object = obj;
          break;
        case TableColumn:
        case Trigger:
        case Index:
        case ForeignKey:
          obj->schemaName(node->get_parent()->get_parent()->get_parent()->get_parent()->get_string(0));
          if (!table_object.is_valid() ||
              table_object->schemaName() != obj->schemaName() ||
              table_object->name() != node->get_parent()->get_parent()->get_string(0))
          {
            table_object = db_query_LiveDBObjectRef(_grt);
            table_object->type("db.Table");
            table_object->schemaName(obj->schemaName());
            table_object->name(node->get_parent()->get_parent()->get_string(0));
          }
          if (current_type == TableColumn)
            obj->type("db.Column");
          else if (current_type == Trigger)
            obj->type("db.Trigger");
          else if (current_type == Index)
            obj->type("db.Index");
          else if (current_type == ForeignKey)
            obj->type("db.ForeignKey");
          obj->owner(table_object);
          obj->name(node->get_string(0));
          break;
        case View:
          obj->type("db.View");
          obj->schemaName(node->get_parent()->get_parent()->get_string(0));
          obj->name(node->get_string(0));
          table_object = obj;
          break;
        case ViewColumn:
          obj->schemaName(node->get_parent()->get_parent()->get_parent()->get_string(0));
          if (!table_object.is_valid() ||
              table_object->schemaName() != obj->schemaName() ||
              table_object->name() != node->get_parent()->get_string(0))
          {
            table_object = db_query_LiveDBObjectRef(_grt);
            table_object->type("db.View");
            table_object->schemaName(obj->schemaName());
            table_object->name(node->get_parent()->get_string(0));
          }
          obj->type("db.Column");
          obj->owner(table_object);
          obj->name(node->get_string(0));
          break;
        case Procedure:
          obj->type("db.StoredProcedure");
          obj->schemaName(node->get_parent()->get_parent()->get_string(0));
          obj->name(node->get_string(0));
          break;
        case Function:
          obj->type("db.Function");
          obj->schemaName(node->get_parent()->get_parent()->get_string(0));
          obj->name(node->get_string(0));
          break;
        case TableCollection:
          obj->schemaName(node->get_parent()->get_string(0));
          obj->type("tables");
          obj->name("");
          break;
        case ViewCollection:
          obj->schemaName(node->get_parent()->get_string(0));
          obj->type("views");
          obj->name("");
          break;
        case ProcedureCollection:
          obj->schemaName(node->get_parent()->get_string(0));
          obj->type("storedProcedures");
          obj->name("");
          break;
        case FunctionCollection:
          obj->schemaName(node->get_parent()->get_string(0));
          obj->type("functions");
          obj->name("");
          break;
        case ColumnCollection:
        case IndexCollection:
        case TriggerCollection:
        case ForeignKeyCollection:
          obj->schemaName(node->get_parent()->get_parent()->get_parent()->get_string(0));
          if (!table_object.is_valid() ||
              table_object->schemaName() != obj->schemaName() ||
              table_object->name() != node->get_parent()->get_parent()->get_string(0))
          {
            table_object = db_query_LiveDBObjectRef(_grt);
            table_object->type("db.Table");
            table_object->schemaName(obj->schemaName());
            table_object->name(node->get_parent()->get_string(0));
          }
          obj->owner(table_object);
          if (current_type == ForeignKeyCollection)
            obj->type("foreignKeys");
          else if (current_type == TriggerCollection)
            obj->type("triggers");
          else if (current_type == IndexCollection)
            obj->type("indexes");
          else if (current_type == ColumnCollection)
            obj->type("columns");
          break;
        case ForeignKeyColumn:
        case IndexColumn:
          break;
      }
      if (obj->type() != "")
        selection.insert(obj);
    }
  }

  return selection;
}


bec::MenuItemList LiveSchemaTree::get_popup_items_for_nodes(const std::list<mforms::TreeNodeRef> &nodes)
{
  bec::MenuItemList items;

  {
    mforms::TreeNodeRef node;
    if (nodes.size())
      node = nodes.front();

    int type = -1;
    if (node)
    {
      LSTData* pdata = dynamic_cast<LSTData*>(node->get_data());

      if (pdata)
        type = pdata->get_type();
    }

    if (type == Schema)
    {
      bec::MenuItem active_schema_item;
      active_schema_item.caption= _("Set as Default Schema");
      active_schema_item.name= "set_active_schema";
      active_schema_item.enabled= nodes.size() == 1;

      bec::MenuItem filter_schema_item;
      filter_schema_item.caption= _("Filter to This Schema");
      filter_schema_item.name= "filter_schema";
      filter_schema_item.enabled= nodes.size() == 1;

      items.push_back(active_schema_item);
      items.push_back(filter_schema_item);
      bec::MenuItem item;
      item.type= MenuSeparator;
      item.name = "builtins_separator"; // this indicates where plugins should start adding their menu items
      items.push_back(item);
    }
    else if (type == Table || type == View || type == TableColumn || type == ViewColumn || type == ViewCollection || type == ColumnCollection)
    {
      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"));
          ssize_t limit_rows_count= options.get_int("SqlEditor:LimitRowsCount");
          if (limit_rows && (limit_rows_count <= 0))
            limit_rows= false;
          if (limit_rows)
            caption += _(" - Limit ") + base::to_string(limit_rows_count);
        }
        view_item.caption= caption;
      }
      view_item.name= "select_data";
      view_item.enabled= !nodes.empty() && (nodes.size() == 1 || (type == TableColumn || type == ViewColumn));
      items.push_back(view_item);
      bec::MenuItem item;
      item.type= MenuSeparator;
      item.name = "builtins_separator"; // this indicates where plugins should start adding their menu items
      items.push_back(item);
    }
  }
  {
    bec::MenuItem item;
    item.type= MenuSeparator;
    item.name = "bottom_plugins_separator"; // this indicates where plugins should start adding their menu items
    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::list<mforms::TreeNodeRef> &unsorted_nodes)
{
  std::vector<ChangeRecord> changes;
  
  mforms::TreeNodeRef pnode;

  std::string schema_name = "";
  std::string object_name = "";
  std::string object_detail = "";

  if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
  {
    if (name == "refresh")
    {
      delegate->tree_refresh();
      return true;
    }
    else if (name == "set_active_schema")
    {
      pnode = unsorted_nodes.front();
      ChangeRecord record = { Schema, "", pnode->get_string(0), "" };
      changes.push_back(record);
      delegate->tree_activate_objects("activate", changes);
      return true;
    }
    else if (name == "filter_schema")
    {
      pnode = unsorted_nodes.front();
      ChangeRecord record = { Schema, "", pnode->get_string(0), "" };
      changes.push_back(record);
      delegate->tree_activate_objects("filter", changes);
      return true;
    }
    else if (name == "select_data")
    {
      std::list<mforms::TreeNodeRef>::const_iterator index, end = unsorted_nodes.end();
      mforms::TreeNodeRef pnode;
      LSTData* pdata;
      std::string schema_name = "";
      std::string object_name = "";
      std::string object_detail = "";
      ObjectType type = Any;
      bool use_columns = false;

      for (index = unsorted_nodes.begin(); index != end; index++)
      {
        pnode = (*index);
        pdata = dynamic_cast<LSTData*>(pnode->get_data());

        if (pdata)
        {
          schema_name = "";
          object_name = "";
          object_detail = "";
          type = pdata->get_type();
          schema_name = get_schema_name(pnode);

          if (is_object_type(SchemaObject, type))
            object_name = pnode->get_string(0);
          else
          {
            if (pdata->get_type() == TableColumn)
            {
              object_name = pnode->get_parent()->get_parent()->get_string(0);
              object_detail = pnode->get_string(0);
              type = Table;
              use_columns = true;
            }
            else if(pdata->get_type() == ViewColumn)
            {
              object_name = pnode->get_parent()->get_string(0);
              object_detail = pnode->get_string(0);
              type = View;
              use_columns = true;
            }
          }

          if (object_name.length() > 0)
          {
            ChangeRecord record = { type, schema_name, object_name, object_detail };
            changes.push_back(record);
          }
        }
        else
        {
          if (pnode->get_tag() == COLUMNS_TAG)
          {
            int size = pnode->count();
            mforms::TreeNodeRef child;

            schema_name = get_schema_name(pnode);
            object_name = pnode->get_parent()->get_string(0);

            for(int index = 0; index < size; index++)
            {
              child = pnode->get_child(index);
              ChangeRecord record = { Table, schema_name, object_name, child->get_string(0) };
              changes.push_back(record);
            }
          }
        }
      }

      if (use_columns)
        delegate->tree_activate_objects(name+"_columns", changes);
      else
        delegate->tree_activate_objects(name, changes);
      return true;
    }
    else
      return delegate->sidebar_action(name);
  }

  return false;
}


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;
}

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

void LiveSchemaTree::set_no_connection()
{
  _model_view->clear();
  mforms::TreeNodeRef node = _model_view->add_node();
  node->set_string(0, "Not connected");
}

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

std::string LiveSchemaTree::get_filter_wildcard(const std::string& filter, FilterType type)
{
  std::string wildcard = filter;
  if (filter.length() == 0)
    wildcard = "*";

  switch(type)
  {
    case LocalRegexp:
    case LocalLike:
    case RemoteRegexp:
      if ('*' != wildcard.at(wildcard.length() - 1))
        wildcard += "*";
      break;
    case RemoteLike:
      base::replace(wildcard, "%","\\%");
      base::replace(wildcard, "_","\\_");
      base::replace(wildcard, "?","_");
      base::replace(wildcard, "*","%");

      if ('%' != wildcard.at(wildcard.length() - 1))
        wildcard += "%";

      break;
  }

  return wildcard;
}

//--------------------------------------------------------------------------------------------------
void LiveSchemaTree::filter_data()
{
  _enabled_events = false;

  // Removes all the objects on the target tree
  _model_view->clear();

  mforms::TreeNodeRef base_root = _base->_model_view->root_node();
  mforms::TreeNodeRef this_root = _model_view->root_node();
  filter_children(Schema, base_root, this_root, _schema_pattern);

  // To keep the active schema on the filtered tree
  set_active_schema(_base->_active_schema);

  _enabled_events = true;
}

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

/*
*  filter_children_collection: will trigger a children copy for the collection nodes of the given source
*                              right now the pattern is only used for nodes on schema collections
*/
void LiveSchemaTree::filter_children_collection(mforms::TreeNodeRef& source, mforms::TreeNodeRef& target)
{
  LSTData* pdata = dynamic_cast<LSTData*>(source->get_data());

  if(pdata)
  {
    mforms::TreeNodeRef source_collection;
    mforms::TreeNodeRef target_collection;

    switch(pdata->get_type())
    {
    case Schema:
      {
        source_collection = source->get_child(TABLES_NODE_INDEX);
        target_collection = target->get_child(TABLES_NODE_INDEX);
        bool found_tables   = filter_children(Table,   source_collection, target_collection, _object_pattern);

        source_collection = source->get_child(VIEWS_NODE_INDEX);
        target_collection = target->get_child(VIEWS_NODE_INDEX);
        bool found_views    = filter_children(View,    source_collection, target_collection, _object_pattern);

        source_collection = source->get_child(PROCEDURES_NODE_INDEX);
        target_collection = target->get_child(PROCEDURES_NODE_INDEX);
        bool found_procedures = filter_children(Procedure, source_collection, target_collection, _object_pattern);

        source_collection = source->get_child(FUNCTIONS_NODE_INDEX);
        target_collection = target->get_child(FUNCTIONS_NODE_INDEX);
        bool found_functions = filter_children(Function, source_collection, target_collection, _object_pattern);

        if (_object_pattern && !(found_tables || found_views || found_procedures || found_functions))
          target->remove_from_parent();
      }
      break;
    case Table:
      source_collection = source->get_child(TABLE_COLUMNS_NODE_INDEX);
      target_collection = target->get_child(TABLE_COLUMNS_NODE_INDEX);
      filter_children(TableColumn, source_collection, target_collection);

      source_collection = source->get_child(TABLE_INDEXES_NODE_INDEX);
      target_collection = target->get_child(TABLE_INDEXES_NODE_INDEX);
      filter_children(Index, source_collection, target_collection);

      source_collection = source->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX);
      target_collection = target->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX);
      filter_children(ForeignKey, source_collection, target_collection);

      source_collection = source->get_child(TABLE_TRIGGERS_NODE_INDEX);
      target_collection = target->get_child(TABLE_TRIGGERS_NODE_INDEX);
      filter_children(Trigger, source_collection, target_collection);
      break;
    case View:
      filter_children(ViewColumn,source, target);
      break;
    default:
      break;
    }
  }
}
/*
*  filter_children: will create duplicate objects in target for the children in source matching the given pattern
*                   if no pattern is specified, all the children will be cuplicated
*/
bool LiveSchemaTree::filter_children(ObjectType type, mforms::TreeNodeRef& source, mforms::TreeNodeRef& target, GPatternSpec* pattern)
{
  // Validation to occur only on schema child objects if a pattern is set
  bool validate = is_object_type(DatabaseObject, type) && pattern;


  // Clears the collection...
  target->remove_children();

  int count = source->count();
  for(int index = 0; index < count; index++)
  {
    mforms::TreeNodeRef source_node = source->get_child(index);
    if (!validate || g_pattern_match_string(pattern, base::toupper(source_node->get_string(0)).c_str()))
    {
      std::vector<mforms::TreeNodeRef> group_added_nodes;
      _node_collections[type].captions.clear();
      _node_collections[type].captions.push_back(source_node->get_string(0));
      group_added_nodes = target->add_node_collection(_node_collections[type]);
      setup_node(group_added_nodes[0], type, source_node->get_data(), true);

      // For each found node, continues with their children...
      if (type == Schema || type == Table || type == View)
        filter_children_collection(source_node, group_added_nodes[0]);

      if (source_node->is_expanded() != group_added_nodes[0]->is_expanded())
      {
        if (group_added_nodes[0]->is_expanded())
          group_added_nodes[0]->expand();
        else
          group_added_nodes[0]->collapse();
      }
    }
  }

  if (source->is_expanded() != target->is_expanded())
  {
    if (source->is_expanded())
      target->expand();
    else
      target->collapse();
  }

  return target->count() > 0;
}

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

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)
{
  // 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 = base::toupper(get_filter_wildcard(filters[0], LocalLike));
    std::string object_filter = base::toupper(get_filter_wildcard(filters.size() > 1 ? filters[1] : "", LocalLike));

    _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());
  }
}

void LiveSchemaTree::set_model_view(mforms::TreeNodeView* target)
{
  _model_view = target;

  if (_model_view)
  {
    scoped_connect(_model_view->signal_expand_toggle(),boost::bind(&LiveSchemaTree::expand_toggled, this, _1, _2));
    scoped_connect(_model_view->signal_node_activated(),boost::bind(&LiveSchemaTree::node_activated, this, _1, _2));

    _model_view->set_row_overlay_handler(boost::bind(&LiveSchemaTree::overlay_icons_for_tree_node, this, _1));
  }
}

/* Function : binary_search_node
 * Description : Search a child on a given node based on it's name, assumes the children
 *               are sorted
 * Parameters : parent, the node where the search is being performed
 *              min, max : usual parameters on the binary search
 *              name : the name of the child node being searched
 *              type : the type of the child node to be searched (not being used)
 *              position : output parameter to store:
 *                        - If the node is found, the position in parent's list
 *                        - If not found, the position where it should be located if it is going to be added
 * Return Value : if found, the child node
 */
mforms::TreeNodeRef LiveSchemaTree::binary_search_node(const mforms::TreeNodeRef& parent, int min, int max, const std::string &name, ObjectType type, int &position)
{
  if (max < min)
    return mforms::TreeNodeRef();
  else
  {
    int middle = (max+min) / 2;
    position = middle;
    
    mforms::TreeNodeRef node = parent->get_child(middle);
    
    int comparison = base::string_compare(node->get_string(0), name, _case_sensitive_identifiers);
    
    if (comparison < 0)
      return binary_search_node(parent, middle + 1, max, name, type, ++position);
    else if (comparison > 0)
      return binary_search_node(parent, min, middle - 1, name, type, position);
    else
      return node;     
  }
}

/* Function : get_child_node
 * Description : Searches a specific child on a given node, supports both sequential search
 *               and binary search. Binary search should be used when searching for schemas,
 *               tables, views and routines which are sorted. Sequential search is there for 
 *               the non sorted nodes.
 */
mforms::TreeNodeRef LiveSchemaTree::get_child_node(const mforms::TreeNodeRef& parent, const std::string& name, ObjectType type, bool binary_search)
{
  int last_position = 0;
  bool found = false;
  mforms::TreeNodeRef child;

  if (binary_search)
  {
    if (parent && parent->count())
      child = binary_search_node(parent, 0, parent->count()-1, name, type, last_position);
      
    if (child)
      found = true;
  }
  else
  {
    if (parent && parent->count())
    {
      for(int index=0; !found && index < parent->count(); index++)
      {
        child = parent->get_child(index);
        
        found = (base::string_compare(child->get_string(0), name, _case_sensitive_identifiers) == 0);
        
        if (found && type != Any)
        {
          LSTData* pdata = dynamic_cast<LSTData*>(child->get_data());
          found = (pdata && type == pdata->get_type());          
        }
      }
    }
  }

  return found ? child : mforms::TreeNodeRef();
}

/* Function : find_child_position
 * Description : given a child name, searches it on the received parent. This function is
 *               intended for usage on sorted collections of nodes
 * Parameters : parent, the node where the search will occur
 *              type, the type of the object being searched
 *              position, output parameter containing:
 *                - If found, the index of the found node
 *                - If not found, the position where the node should be (i.e. to add it there)
 * Return value : boolean value indicating whether the node was found or not
 */
bool LiveSchemaTree::find_child_position(const mforms::TreeNodeRef& parent, const std::string& name, ObjectType type, int &position)
{
  mforms::TreeNodeRef child;
  
  position = 0;
  
  if (parent && parent->count())
    child = binary_search_node(parent, 0, parent->count()-1, name, type, position);
  
  if (parent->count() == position)
    position = -1;

  return child ? true : false;
}

void LiveSchemaTree::update_schemata(base::StringListPtr schema_list)
{
  mforms::TreeNodeRef schema_node;

  if (_model_view)
  {
    mforms::TreeNodeRef root = _model_view->root_node();
    if (root && root->count() > 0 && !root->get_child(0)->get_data())
    {
      // the tree was in no-connection mode
      _model_view->clear();
      root = _model_view->root_node();
    }
    
    schema_list->sort(boost::bind(base::stl_string_compare, _1, _2, _case_sensitive_identifiers));
    
    update_node_children(root, schema_list, Schema, true);

    // Re-sets the active schema at view level
    if (_active_schema.length())
      set_active_schema(_active_schema);

    int total_schemas = root->count();
    for(int index = 0; index < total_schemas; index++)
    {
      schema_node = root->get_child(index);
      SchemaData *data = dynamic_cast<SchemaData*>(schema_node->get_data());

      if (data->fetched)
      {
        data->fetched = false;

        if (schema_node->is_expanded())
          load_schema_content(schema_node);
      }
    }
  }
}

void LiveSchemaTree::expand_toggled(mforms::TreeNodeRef node, bool value)
{
  if (_enabled_events)
  {
    LSTData *node_data = dynamic_cast<LSTData*> (node->get_data());

    if (value)
    {
      if (node_data)
      {
        switch(node_data->get_type())
        {
        case Schema:
          load_schema_content(node);
          break;
        case Table:
          load_table_details(node, COLUMN_DATA|INDEX_DATA);
          break;
        case View:
          {
            load_table_details(node, COLUMN_DATA);

            ViewData *pdata = dynamic_cast<ViewData*>(node->get_data());
            if (pdata->columns_load_error)
            {
              node->remove_children();
              update_node_icon(node);
            }
          }
          break;
        default:
          break;
        }
      }
      else
      {
        std::string node_tag = node->get_tag();
        mforms::TreeNodeRef parent = node->get_parent();

        if (node_tag == TRIGGERS_TAG)
          load_table_details(parent, TRIGGER_DATA);
        else if (node_tag == FOREIGN_KEYS_TAG)
          load_table_details(parent, FK_DATA);
      }
    }

    // If there's a base tree the expansion state needs to be propagated to that tree
    // Events should be disabled there so only the expand state will be propagated
    if (_base)
    {
      std::vector<std::string> path = get_node_path(node);
      mforms::TreeNodeRef base_node = _base->get_node_from_path(path);
      if (value)
        base_node->expand();
      else
        base_node->collapse();
    }
  }
}

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

void LiveSchemaTree::node_activated(mforms::TreeNodeRef node, int column)
{
  LSTData *node_data = dynamic_cast<LSTData*> (node->get_data());

  if (node_data)
  {
    std::string node_name = node->get_string(0);

    switch (node_data->get_type())
    {
    case Schema:
      {
        std::vector<ChangeRecord> changes;

        ChangeRecord record = { Schema, "", node_name, "" };
        changes.push_back(record);

        if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
        {
          switch (column)
          {
          case -1:
            delegate->tree_activate_objects("inspect", changes);
            break;
          case -2:
            delegate->tree_activate_objects("alter", changes);
            break;
          default:
            delegate->tree_activate_objects("activate", changes);
#ifndef _WIN32
            node->toggle();
#endif
            break;
          }
        }
      }
      break;

    case Table:
    case View:
      {
        if (column < 0)
        {
          std::vector<ChangeRecord> changes;
          ChangeRecord record = { node_data->get_type(), get_schema_name(node), node_name, "" };
          changes.push_back(record);

          if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
          {
            switch (column)
            {
            case -1:
              delegate->tree_activate_objects("inspect", changes);
              break;
            case -2:
              delegate->tree_activate_objects("alter", changes);
              break;
            case -3:
              delegate->tree_activate_objects("select_data", changes);
              break;
#ifndef _WIN32
            default:
              node->toggle();
              break;
#endif
            }
          }
          break;
        }
        // else fall through.
      }

    case Procedure:
    case Function:
      {
        if (column < 0)
        {
          std::vector<ChangeRecord> changes;
          ChangeRecord record = { node_data->get_type(), get_schema_name(node), node_name, "" };
          changes.push_back(record);

          if (boost::shared_ptr<Delegate> delegate = _delegate.lock())
          {
            switch (column)
            {
            case -1:
              delegate->tree_activate_objects("alter", changes);
              break;
            case -2:
              delegate->tree_activate_objects("execute", changes);
              break;
            }
          }
          break;
        }
        // else fall through.
      }

    default:
      node_name = base::quote_identifier_if_needed(node_name, '`');
      sql_editor_text_insert_signal(node_name);
      break;

    }
  }
#ifndef _WIN32
  else
    node->toggle();
#endif
}

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

/**
 * Finds the parent schema for a specific node in the tree.
 */
std::string LiveSchemaTree::get_schema_name(const mforms::TreeNodeRef& node)
{
  std::string ret_val;
  mforms::TreeNodeRef temp_node = node;
  mforms::TreeNodeRef parent = temp_node->get_parent();

  // Safety validation, all nodes in the LST should have a parent
  // Even schema nodes which it's parent is root
  if (parent)
  {
    while(parent->get_parent())
    {
      temp_node = parent;
      parent = parent->get_parent();
    }

    ret_val = temp_node->get_string(0);
  }

  return ret_val;
}

/*
* get_node_path: Gets the name path to a node from root
*/
std::vector<std::string> LiveSchemaTree::get_node_path(const mforms::TreeNodeRef& node)
{
  std::vector<std::string> path;

  mforms::TreeNodeRef temp_node = node;
  mforms::TreeNodeRef parent = temp_node->get_parent();

  // Safety validation, all nodes in the LST should have a parent
  // Even schema nodes which it's parent is root
  if (parent)
  {
    path.insert(path.begin(), temp_node->get_string(0));

    while(parent->get_parent())
    {
      temp_node = parent;
      path.insert(path.begin(), temp_node->get_string(0));

      parent = parent->get_parent();
    }
  }

  return path;
}

/*
* get_node_from_path: Finds a node in a tree following a name path
* TODO: This will not work in the case of the routines, as there's no way to know if the path is
*       for a procedure or for a function, on this case a sequential search will be done so
*       procedures will be searched first all the time
*/
mforms::TreeNodeRef LiveSchemaTree::get_node_from_path(std::vector<std::string> path)
{
  mforms::TreeNodeRef temp_node = _model_view->root_node();
  size_t index = 0;
  bool error = false;
  bool use_binary_search = true;
  
  while(!error && index < path.size())
  {
    temp_node = get_child_node(temp_node, path[index], Any, use_binary_search);

    if (temp_node && temp_node->is_valid())
    {
      index++;
      
      // Uses binary search only on the db object collection nodes
      std::string tag = temp_node->get_tag();
      
      use_binary_search = (tag == TABLES_TAG ||
                           tag == VIEWS_TAG );
    }
    else
      error = true;
  }

  return error ? mforms::TreeNodeRef() : temp_node;
}

/*
* is_object_type: Validates that the type of a given object is in a specific group
*/
bool LiveSchemaTree::is_object_type(ObjectTypeValidation validation, ObjectType type)
{
  switch(validation)
  {
  case DatabaseObject:
    return (type == Schema || type == Table || type == View || type == Procedure || type == Function);
    break;
  case SchemaObject:
    return (type == Table || type == View || type == Procedure || type == Function);
    break;
  case TableOrView:
    return (type == Table || type == View);
    break;
  case ColumnObject:
	  return (type == TableColumn || type == ViewColumn);
	  break;
  case RoutineObject:
    return (type == Procedure || type == Function);
    break;
  }

  return false;
}

mforms::TreeNodeRef LiveSchemaTree::insert_node(mforms::TreeNodeRef parent, const std::string& name, ObjectType type)
{
  mforms::TreeNodeRef node;

  int target_position = 0;
  if (!find_child_position(parent, name, type, target_position))
  {
    std::vector<mforms::TreeNodeRef> group_added_nodes;

    _node_collections[type].captions.clear();
    _node_collections[type].captions.push_back(name);
    group_added_nodes = parent->add_node_collection(_node_collections[type], target_position);
    node = group_added_nodes[0];
              
    setup_node(node, type);
  }

  return node;
}

void LiveSchemaTree::reload_object_data(mforms::TreeNodeRef& node)
{
    ViewData* pdata = dynamic_cast<ViewData*>(node->get_data());
    if (pdata)
    {
        short loaded_mask = pdata->get_loaded_mask();
        if (loaded_mask)
        {
            // This was a successful update so in case the error icon was loaded
            // It needs to be reset
            if (pdata->columns_load_error)
            {
              pdata->columns_load_error = false;
              update_node_icon(node);
            }

            // Marks the data that is expected to be loaded on the node
            pdata->set_reload_mask(loaded_mask);

            // Identifies the node expansion state
            bool is_expanded = node->is_expanded();
            int expanded_mask = 0;
            
            if (is_expanded && pdata->get_type() == Table)
            {
                expanded_mask |= node->get_child(TABLE_COLUMNS_NODE_INDEX)->is_expanded() ? COLUMN_DATA : 0;
                expanded_mask |= node->get_child(TABLE_INDEXES_NODE_INDEX)->is_expanded() ? INDEX_DATA : 0;
                expanded_mask |= node->get_child(TABLE_TRIGGERS_NODE_INDEX)->is_expanded() ? TRIGGER_DATA : 0;
                expanded_mask |= node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->is_expanded() ? FK_DATA : 0;
            }
            
            // Invalidates any loaded data on the object
            pdata->set_unloaded_data(loaded_mask);
                
            // Removes any loaded information to allow reload
            discard_object_data(node, loaded_mask);

            // Reloads the previously loaded information if the node is
            // expanded, if not, it will be loaded on expansion
            if (loaded_mask)
            {
                load_table_details(node, loaded_mask);
                
                // Triggers the expansion of the subnodes
                if (is_expanded)
                {
                  node->expand();
                  if (expanded_mask)
                  {
                      if (expanded_mask & COLUMN_DATA)
                          node->get_child(TABLE_COLUMNS_NODE_INDEX)->expand();
                      if (expanded_mask & INDEX_DATA)
                          node->get_child(TABLE_INDEXES_NODE_INDEX)->expand();
                      if (expanded_mask & TRIGGER_DATA)
                          node->get_child(TABLE_TRIGGERS_NODE_INDEX)->expand();
                      if (expanded_mask & FK_DATA)
                          node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX)->expand();
                  }
                }
            }
        }
    }
}

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

void LiveSchemaTree::discard_object_data(mforms::TreeNodeRef& node, int data_mask)
{
    mforms::TreeNodeRef parent_node;
    
    if (data_mask & COLUMN_DATA)
    {
        LSTData* pdata = dynamic_cast<LSTData*>(node->get_data());
        if (pdata->get_type() == Table)
            parent_node = node->get_child(TABLE_COLUMNS_NODE_INDEX);
        else
            parent_node = node;
            
        parent_node->remove_children();
    }
    
    if (data_mask & INDEX_DATA)
    {
        parent_node = node->get_child(TABLE_INDEXES_NODE_INDEX);
        parent_node->remove_children();
    }

    if (data_mask & TRIGGER_DATA)
    {
        parent_node = node->get_child(TABLE_TRIGGERS_NODE_INDEX);
        parent_node->remove_children();
    }
    
    if (data_mask & FK_DATA)
    {
        parent_node = node->get_child(TABLE_FOREIGN_KEYS_NODE_INDEX);
        parent_node->remove_children();
    }
}

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

std::vector<std::string> LiveSchemaTree::overlay_icons_for_tree_node(mforms::TreeNodeRef node)
{
  LSTData *data = dynamic_cast<LSTData*>(node->get_data());

  std::vector<std::string> icons;
  if (data)
  {
    switch (data->get_type())
    {
      case Schema:
        icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_inspector.png"));
        icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_editor.png"));
        break;
      case Table:
      case View:
        icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_inspector.png"));
        icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_editor.png"));
        icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_result.png"));
        break;
      case Procedure:
      case Function:
        icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_editor.png"));
        icons.push_back(mforms::App::get()->get_resource_path("wb_item_overlay_execute.png"));
        break;
      default:
        break;
    }
  }
  return icons;
}

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