/* 
 * Copyright (c) 2011, 2014, 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 <cmath>

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

#include "base/string_utilities.h"
#include "base/geometry.h"
#include "base/drawing.h"
#include "base/log.h"
#include "base/notifications.h"

#include "mforms/imagebox.h"
#include "mforms/button.h"
#include "mforms/utilities.h"

using namespace base;

#include "advanced_sidebar.h"

#include <math.h>

using namespace std;

using namespace wb;
using namespace mforms;


#ifdef _WIN32
  #define SIDEBAR_FONT "Segoe UI"
  #define SIDEBAR_TITLE_FONT_SIZE 11
  #define SIDEBAR_ENTRY_FONT_SIZE 11
#else
#ifdef __APPLE__
  #define SIDEBAR_FONT "Lucida Grande"
  #define SIDEBAR_TITLE_FONT_SIZE 11
  #define SIDEBAR_ENTRY_FONT_SIZE 11
#else
  #define SIDEBAR_FONT "Tahoma"
  #define SIDEBAR_TITLE_FONT_SIZE 11
  #define SIDEBAR_ENTRY_FONT_SIZE 11
#endif
#endif

#if !defined(__APPLE__) && !defined(_WIN32)
#define BACK_COLOR ""
#else
#define BACK_COLOR "#d9e2ef"
#endif


#define SECTION_ENTRY_HEIGHT 20  // Height of a single section entry.
#define SECTION_ENTRY_SPACING 0  // Vertical distance between two section entries.
#define SECTION_ENTRY_INDENT 13  // Horizontal distance from the left section border to the entry icon.
#define SECTION_ENTRY_ICON_SPACING 6  // Horizontal distance between entry icon and text.


//----------------- SidebarSection -----------------------------------------------------------------

SidebarEntry::SidebarEntry(const string& name, const string& title, const string& icon, TaskEntryType type)
{
  _name = name;
  _title = title;
  if (!icon.empty())
    _icon = Utilities::load_icon(icon, true);
  else
    _icon = NULL;
  _type = type;
  _enabled = true;
}

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

SidebarEntry::~SidebarEntry()
{
  if (_icon!= NULL)
    cairo_surface_destroy(_icon);
}


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

void SidebarEntry::set_title(const std::string &title)
{
  _title = title;
}

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

void SidebarEntry::set_icon(const std::string &icon)
{
  if (_icon)
    cairo_surface_destroy(_icon);
  _icon = Utilities::load_icon(icon, true);
}

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

void SidebarEntry::set_enabled(bool flag)
{
  _enabled = flag;
}

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

void SidebarEntry::paint(cairo_t *cr, base::Rect bounds, bool hot, bool active, const Color& selection_color)
{
  _bounds = bounds;

  // Fill background if the item is active.
  if (active)
  {
    cairo_set_source_rgb(cr, selection_color.red, selection_color.green, selection_color.blue);
    cairo_rectangle(cr, 2, bounds.top(), bounds.left() + bounds.width() - 4, bounds.height());
    cairo_fill(cr);
  }

  cairo_move_to(cr, bounds.left(), bounds.top());

  double offset;
  if (_icon != NULL)
  {
    int width, height;
    mforms::Utilities::get_icon_size(_icon, width, height);
    offset = (bounds.height() - height) / 2;
    mforms::Utilities::paint_icon(cr, _icon, bounds.left(), bounds.top() + offset);
    cairo_rel_move_to(cr, width + SECTION_ENTRY_ICON_SPACING, 0);
  }

  cairo_select_font_face(cr, SIDEBAR_FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size(cr, SIDEBAR_ENTRY_FONT_SIZE);

  if (active)
    cairo_set_source_rgb(cr, 1, 1, 1);
  else if (_enabled)
    cairo_set_source_rgb(cr, 0, 0, 0);
  else
    cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
  cairo_rel_move_to(cr, 0, (bounds.height() + SIDEBAR_ENTRY_FONT_SIZE) / 2 - 2);
  cairo_show_text(cr, _title.c_str());

  if (hot)
  {
    // Use the color we set for the text.
    cairo_set_line_width(cr, 1);

    cairo_text_extents_t extents;
    cairo_text_extents(cr, _title.c_str(), &extents);

    double width = ceil(extents.width);
    cairo_rel_move_to(cr, -width, 2);
    cairo_rel_line_to(cr, width, 0);
    cairo_stroke(cr);
  }
}

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

bool SidebarEntry::contains(double x, double y)
{
  return false;
}


//----------------- SidebarSection -----------------------------------------------------------------

SidebarSection::Button::Button(const std::string &accessible_name, const std::string &icon_name, const std::string &alt_icon_name)
: icon(NULL), alt_icon(NULL), hot(false), down(false), state(false)
{
  _icon_name = icon_name;
  _alt_icon_name = alt_icon_name;

  _name = accessible_name;
  move(0, 0);

  if (!icon_name.empty())
    icon = Utilities::load_icon(icon_name, true);
  if (!alt_icon_name.empty())
    alt_icon = Utilities::load_icon(alt_icon_name, true);

  width = 0;
  height = 0;
  if (icon)
  {
    Utilities::get_icon_size(icon, width, height);
  }
  else if (alt_icon)
  {
    Utilities::get_icon_size(alt_icon, width, height);
  }
  bounds_width = width + 4;
  bounds_height = height + 4;
}


SidebarSection::Button::~Button()
{
  if (icon)
    cairo_surface_destroy(icon);
  if (alt_icon)
    cairo_surface_destroy(alt_icon);
}


void SidebarSection::Button::draw(cairo_t *cr)
{
  if (Utilities::icon_needs_reload(icon))
  {
    if (icon)
      cairo_surface_destroy(icon);
    if (alt_icon)
      cairo_surface_destroy(alt_icon);
    icon = Utilities::load_icon(_icon_name, true);
    alt_icon = Utilities::load_icon(_alt_icon_name, true);
  }

  cairo_surface_t *image = (state && alt_icon != NULL) ? alt_icon : icon;

  if (image)
  {
    double image_left = x;
    double image_top = y;
    if (hot || down)
    {
      // Draw a bezel shape under the icon.
      double radius = 3;
      cairo_new_sub_path(cr);
      cairo_arc(cr, image_left + bounds_width - radius, image_top + radius, radius, -M_PI / 2, 0);
      cairo_arc(cr, image_left + bounds_width - radius, image_top + bounds_height - radius, radius, 0, M_PI / 2);
      cairo_arc(cr, image_left + radius, image_top + bounds_height - radius, radius, M_PI / 2, M_PI);
      cairo_arc(cr, image_left + radius, image_top + radius, radius, M_PI, 3 * M_PI / 2);
      cairo_close_path(cr);

      cairo_set_line_width(cr, 1);
      if (down)
      {
        cairo_set_source_rgba(cr, 0, 0, 0, 0.1);
        cairo_fill_preserve(cr);
        cairo_set_source_rgba(cr, 0, 0, 0, 0.15);
      }
      else
      {
        cairo_set_source_rgba(cr, 0, 0, 0, 0.05);
        cairo_fill_preserve(cr);
        cairo_set_source_rgba(cr, 0, 0, 0, 0.1);
      }
      cairo_stroke(cr);
    }

    image_left += floor((bounds_width - width) / 2 + 1);
    image_top += floor((bounds_height - height) / 2 + 1);
    Utilities::paint_icon(cr, image, image_left, image_top);
  }
}


bool SidebarSection::Button::check_hit(int x, int y)
{
  return (x >= this->x && x < this->x + bounds_width && y >= this->y && y < this->y + bounds_height);
}


void SidebarSection::Button::move(int x, int y)
{
  this->x = x;
  this->y = y;
}


SidebarSection::SidebarSection(SimpleSidebar* owner, const std::string& name, const std::string& title, mforms::TaskSectionFlags flags)
  : DrawBox()
{
  _owner = owner;
  _name = name;
  _title = title;
  _selected_entry = NULL;
  _hot_entry = NULL;

  _layout_width = 0;
  _layout_height = 0;
  _expanded = true;
  _expandable = (flags & mforms::TaskSectionCollapsible) != 0;
  _expand_text_visible = false;
  _expand_text_width = 0;
  _expand_text_active = false;

  _refresh_button = NULL;
  _toggle_mode_button = NULL;
  _config_button = NULL;

  _layout_surface= NULL;
  _layout_context= NULL;
  _last_width = 0;

  if (flags & mforms::TaskSectionRefreshable)
  {
#ifdef __APPLE__
    _refresh_button = new Button("Refresh", "wb-sidebar-refresh.png", "busy_sidebar_mac.png");
#else
    _refresh_button = new Button("Refresh", "refresh_sidebar.png", "busy_sidebar.png");
#endif
    _enabled_buttons.push_back(_refresh_button);
  }
  else
  {
#ifdef __APPLE__
    _refresh_button = new Button("Busy", "", "busy_sidebar_mac.png");
#else
    _refresh_button = new Button("Busy", "", "busy_sidebar.png");
#endif
  }

  if (flags & mforms::TaskSectionToggleModeButton)
  {
    _toggle_mode_button = new Button("Toggle Mode", "wb-sidebar-collapse.png", "wb-sidebar-expand.png");
    _enabled_buttons.push_back(_toggle_mode_button);
    if (flags & TaskSectionToggleModeButtonPreSelected)
      _toggle_mode_button->state = true;
  }

  if (flags & mforms::TaskSectionShowConfigButton)
  {
    _config_button = new Button("Configuration", "wb_perform_config.png", "");
    _enabled_buttons.push_back(_config_button);
  }
}

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

SidebarSection::~SidebarSection()
{
  clear();
  delete _refresh_button;
  delete _toggle_mode_button;
  delete _config_button;

  if (_layout_surface != NULL)
    cairo_surface_destroy(_layout_surface);

  if (_layout_context != NULL)
    cairo_destroy(_layout_context);
}

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

void SidebarSection::set_selected(SidebarEntry* entry)
{
  if (entry)
    _owner->clear_selection();

  if (_selected_entry != entry)
  {
    _selected_entry = entry;
    set_needs_repaint();
  }
}

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

/**
 * Creates a cairo context on a small image surface, to be used for layouting.
 */
void SidebarSection::create_context_for_layout()
{
  if (_layout_surface == NULL)
    _layout_surface= cairo_image_surface_create(CAIRO_FORMAT_ARGB32, get_width(), get_height());
  if (_layout_context == NULL)
    _layout_context= cairo_create(_layout_surface);
}

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

#define SECTION_TOP_SPACING     4 // Vertical distance from the top border to the heading.
#define SECTION_SIDE_SPACING    6 // Horizontal spacing between border and content.
#define SECTION_BOTTOM_SPACING  7 // Vertical distance from the content to the bottom.
#define SECTION_HEADER_SPACING  6 // Vertical distance between the heading bottom line and the content top.
#define SECTION_HEADER_HEIGHT  12 // Height of the section heading.
#define SECTION_ICON_SPACING    4 // Horizontal distance between buttons and/or text.

void SidebarSection::layout(cairo_t* cr)
{
  set_layout_dirty(false);

  _layout_height = SECTION_TOP_SPACING + SECTION_HEADER_HEIGHT;

  if (_expanded)
  {
    if (_entries.size() > 0)
      _layout_height += SECTION_HEADER_SPACING;
    _layout_width = SECTION_SIDE_SPACING;

    if (_entries.size() > 0)
      _layout_height += _entries.size() * SECTION_ENTRY_HEIGHT + (_entries.size() - 1) * SECTION_ENTRY_SPACING;
  }

  _layout_height += SECTION_BOTTOM_SPACING;

  // Precompute size of the hide/show text for hit tests.
  std::string expand_text = _expanded ? _("Hide") : _("Show");
  cairo_text_extents_t extents;

  cairo_select_font_face(cr, SIDEBAR_FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
  cairo_set_font_size(cr, SIDEBAR_TITLE_FONT_SIZE);
  cairo_text_extents(cr, expand_text.c_str(), &extents);
  _expand_text_width = (int) ceil(extents.x_advance);

  double width = get_width();

  Rect bounds(SECTION_SIDE_SPACING, SECTION_TOP_SPACING + SIDEBAR_TITLE_FONT_SIZE,
              width - SECTION_SIDE_SPACING, SECTION_HEADER_HEIGHT);
  
  if (_config_button != NULL)
  {
    cairo_select_font_face(cr, SIDEBAR_FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    cairo_text_extents(cr, _title.c_str(), &extents);
    int title_width = (int)ceil(extents.x_advance);

    _config_button->move((int)bounds.left() + title_width + SECTION_ICON_SPACING, SECTION_TOP_SPACING / 2);
    _config_button->bounds_width = SECTION_HEADER_HEIGHT + SECTION_TOP_SPACING;
    _config_button->bounds_height = SECTION_HEADER_HEIGHT + SECTION_TOP_SPACING;
  }

  if (_toggle_mode_button)
  {
    _toggle_mode_button->move((int)bounds.size.width - _toggle_mode_button->bounds_width - SECTION_SIDE_SPACING, SECTION_TOP_SPACING);
    _toggle_mode_button->bounds_width = SECTION_HEADER_HEIGHT;
    _toggle_mode_button->bounds_height = SECTION_HEADER_HEIGHT;
  }

  if (_refresh_button)
  {
    if (_toggle_mode_button)
      _refresh_button->move(_toggle_mode_button->x -_refresh_button->bounds_width - SECTION_ICON_SPACING, SECTION_TOP_SPACING);
    else
      _refresh_button->move((int)bounds.size.width - _refresh_button->bounds_width - SECTION_SIDE_SPACING, SECTION_TOP_SPACING);
    _refresh_button->bounds_width = SECTION_HEADER_HEIGHT;
    _refresh_button->bounds_height = SECTION_HEADER_HEIGHT;
  }
}

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

SidebarEntry* SidebarSection::entry_from_point(double x, double y)
{
  if (x < 0 || y < SECTION_TOP_SPACING + SECTION_HEADER_HEIGHT + SECTION_HEADER_SPACING 
    || x > get_width() || y > get_height() || _entries.size() == 0)
    return NULL;

  y -= SECTION_TOP_SPACING + SECTION_HEADER_HEIGHT + SECTION_HEADER_SPACING;

  int index = (int) y / (SECTION_ENTRY_HEIGHT + SECTION_ENTRY_SPACING);
  if (index < (int) _entries.size())
    return _entries[index];
  else
    return NULL;
}

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

/**
 * Find an entry with the given title and return its index or -1 if there is none.
 */
int SidebarSection::find_entry(const std::string& name)
{
  for (size_t i = 0; i < _entries.size(); i++)
  {
    if (_entries[i]->name() == name)
      return (int)i;
  }

  return -1;
}

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

void SidebarSection::toggle_expand()
{
  _expanded = !_expanded;
  set_layout_dirty(true);
  set_needs_repaint();
  relayout();

  _expanded_changed(this);
}

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

void SidebarSection::update_mode_button(bool active)
{
  if (_toggle_mode_button != NULL)
  {
    _toggle_mode_button->state = active;
    set_needs_repaint();
  }
}

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

int SidebarSection::add_entry(const std::string& name, const std::string& title, const std::string& icon, TaskEntryType type)
{
  int result = find_entry(name);
  if (result > -1)
    return result;

  SidebarEntry* entry = new SidebarEntry(name, title, icon, type);
  _entries.push_back(entry);
  set_layout_dirty(true);

  return (int)_entries.size() - 1;
}

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

void SidebarSection::set_entry_text(int index, const std::string& title)
{
  if (index >= 0 && index < (int)_entries.size())
    _entries[index]->set_title(title);
}

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

void SidebarSection::set_entry_icon(int index, const std::string& icon)
{
  if (index >= 0 && index < (int)_entries.size())
    _entries[index]->set_icon(icon);
}

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

void SidebarSection::set_entry_enabled(int index, bool flag)
{
  if (index >= 0 && index < (int)_entries.size())
    _entries[index]->set_enabled(flag);
}

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

void SidebarSection::mark_busy(bool busy)
{
  if (_refresh_button)
    _refresh_button->state = busy;
  set_needs_repaint();
}

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

void SidebarSection::remove_entry(const std::string& entry)
{
  int index = find_entry(entry);
  if (index < 0)
    return;

  delete _entries[index];
  _entries.erase(_entries.begin() + index);

  set_layout_dirty(true);
}

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

void SidebarSection::clear()
{
  for (size_t i = 0; i < _entries.size(); i++)
    delete _entries[i];
  _entries.clear();

  set_layout_dirty(true);
}

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

bool SidebarSection::select(const std::string& name)
{
  const int index = find_entry(name);
  if (index >= 0 && index < (int)_entries.size())
  {
    set_selected(_entries[index]);
    return false;
  }
  else
  {
    set_selected(NULL);
    return false;
  }
}

//------------------------------------------------------------------------------------------------
int SidebarSection::get_acc_child_count()
{ 
  return (int)(_entries.size() + _enabled_buttons.size());
}

//------------------------------------------------------------------------------------------------
Accessible* SidebarSection::get_acc_child(int index)
{ 
  mforms::Accessible* accessible = NULL;

  if ((size_t)index < _enabled_buttons.size())
    accessible = _enabled_buttons[index];
  else
    accessible = _entries[index - _enabled_buttons.size()];
  
  return accessible;
}


//------------------------------------------------------------------------------------------------
mforms::Accessible* SidebarSection::hit_test(int x, int y)
{ 
  mforms::Accessible* accessible = NULL;

  //if (_expand_text_active)
  if (_config_button && _config_button->check_hit(x, y))
    accessible = _config_button;
  else if (_refresh_button && _refresh_button->check_hit(x, y))
    accessible = _refresh_button;
  else if (_toggle_mode_button && _toggle_mode_button->check_hit(x, y))
    accessible = _toggle_mode_button;
  else
    accessible = entry_from_point(x, y);

  return accessible;
}

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

void draw_header_text(cairo_t* cr, Rect &bounds, const std::string& text, bool active)
{
  // Draw heading twice. Once like a white shadow and once normal.
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_move_to(cr, bounds.left(), bounds.top() + 1);
  cairo_show_text(cr, text.c_str());
  cairo_stroke(cr);

  if (active)
    cairo_set_source_rgb(cr, 0x04 / 255.0, 0x7c / 255.0, 0xf2 / 255.0);
  else
    cairo_set_source_rgb(cr, 0x61 / 255.0, 0x70 / 255.0, 0x80 / 255.0);
  cairo_move_to(cr, bounds.left(), bounds.top());
  cairo_show_text(cr, text.c_str());
  cairo_stroke(cr);
}

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

void SidebarSection::repaint(cairo_t *cr, int areax, int areay, int areaw, int areah)
{
  double width = get_width();
  if (_last_width != width)
  {
    _last_width = width;
    layout(cr);
  }

  Rect bounds(SECTION_SIDE_SPACING, SECTION_TOP_SPACING + SIDEBAR_TITLE_FONT_SIZE,
    width - SECTION_SIDE_SPACING, SECTION_HEADER_HEIGHT);

  cairo_select_font_face(cr, SIDEBAR_FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
  cairo_set_font_size(cr, SIDEBAR_TITLE_FONT_SIZE);
  draw_header_text(cr, bounds, _title, false);

  if (_config_button)
    _config_button->draw(cr);

  if (_refresh_button)
    _refresh_button->draw(cr);

  if (_toggle_mode_button)
    _toggle_mode_button->draw(cr);

  if (_expand_text_visible)
  {
    cairo_select_font_face(cr, SIDEBAR_FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
    cairo_set_font_size(cr, SIDEBAR_TITLE_FONT_SIZE);

    std::string expand_text = _expanded ? _("Hide") : _("Show");
    Rect text_bounds = bounds;
    text_bounds.pos.x = width - _expand_text_width - SECTION_SIDE_SPACING;
    text_bounds.size.width = _expand_text_width;
    draw_header_text(cr, text_bounds, expand_text, _expand_text_active);
  }

  if (_expanded)
  {
    bounds.pos.x += SECTION_ENTRY_INDENT;
    bounds.size.width -= SECTION_ENTRY_INDENT;
    bounds.pos.y += SECTION_HEADER_SPACING;
    bounds.size.height = SECTION_ENTRY_HEIGHT;
    const Color& selection_color = _owner->selection_color();
    for (std::vector<SidebarEntry*>::const_iterator iterator= _entries.begin(); iterator != _entries.end(); iterator++)
    {
      (*iterator)->paint(cr, bounds, *iterator == _hot_entry, (*iterator == _selected_entry), selection_color);
      bounds.pos.y += SECTION_ENTRY_HEIGHT + SECTION_ENTRY_SPACING;
    }
  }
}

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

bool SidebarSection::mouse_leave()
{
  if (DrawBox::mouse_leave())
    return true;

  if (_hot_entry != NULL || _expand_text_visible || _expand_text_active ||
      (_config_button && _config_button->hot) ||
      (_refresh_button && _refresh_button->hot) ||
      (_toggle_mode_button && _toggle_mode_button->hot))
  {
    _hot_entry= NULL;
    _expand_text_visible = false;
    _expand_text_active = false;

    if (_config_button)
    {
      _config_button->down = false;
      _config_button->hot = false;
    }

    if (_refresh_button)
    {
      _refresh_button->down = false;
      _refresh_button->hot = false;
    }

    if (_toggle_mode_button)
    {
      _toggle_mode_button->down = false;
      _toggle_mode_button->hot = false;
    }
    set_needs_repaint();
    return true;
  }

  return false;
}

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

bool SidebarSection::mouse_move(mforms::MouseButton button, int x, int y)
{
  if (DrawBox::mouse_move(button, x, y))
    return true;

  bool need_refresh = false;
  
  if (y < SECTION_TOP_SPACING + SECTION_HEADER_HEIGHT)
  {
    // Header area.
    if (_expandable && !_expand_text_visible)
    {
      need_refresh = true;
      _expand_text_visible = true;
    }
    if (_hot_entry != NULL)
    {
      need_refresh = true;
      _hot_entry= NULL;
    }

    // Check buttons.
    if (_config_button)
    {
      bool isHot = _config_button->check_hit(x, y);
      if (isHot != _config_button->hot)
      {
        _config_button->hot = isHot;
        need_refresh = true;
      }
    }

    if (_refresh_button)
    {
      bool isHot = _refresh_button->check_hit(x, y);
      if (isHot != _refresh_button->hot)
      {
        _refresh_button->hot = isHot;
        need_refresh = true;
      }
    }

    if (_toggle_mode_button)
    {
      bool isHot = _toggle_mode_button->check_hit(x, y);
      if (isHot != _toggle_mode_button->hot)
      {
        _toggle_mode_button->hot = isHot;
        need_refresh = true;
      }
    }
  }
  else
  {
    if (_expand_text_visible ||
        (_config_button && _config_button->hot) ||
        (_refresh_button && _refresh_button->hot) ||
        (_toggle_mode_button && _toggle_mode_button->hot))
    {
      _expand_text_visible = false;
      if (_config_button)
        _config_button->hot = false;
      if (_refresh_button)
        _refresh_button->hot = false;
      if (_toggle_mode_button)
      _toggle_mode_button->hot = false;
      need_refresh = true;
    }

    SidebarEntry* entry = entry_from_point(x, y);
    if (entry != _hot_entry)
    {
      if (_hot_entry != NULL || (entry != NULL && (entry->type() == mforms::TaskEntryLink || entry->type() == mforms::TaskEntryAlwaysActiveLink)))
        need_refresh = true;

      if (entry != NULL && (entry->type() == mforms::TaskEntryLink || entry->type() == mforms::TaskEntryAlwaysActiveLink))
        _hot_entry = entry; // Only links can appear as hot entries.
      else
        _hot_entry = NULL;
    }
  }

  if (need_refresh)
    set_needs_repaint();

  return need_refresh;
}

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

bool SidebarSection::mouse_down(mforms::MouseButton button, int x, int y)
{
  if (DrawBox::mouse_down(button, x, y))
    return true;

  bool result = false;
  if (button == MouseButtonLeft)
  {
    if (_config_button && _config_button->hot)
    {
      _config_button->down = true;
      set_needs_repaint();
      result = true;
    }
    else if (_refresh_button && _refresh_button->hot)
    {
      _refresh_button->down = true;
      set_needs_repaint();
      result = true;
    }
    else if (_toggle_mode_button && _toggle_mode_button->hot)
    {
      _toggle_mode_button->down = true;
      set_needs_repaint();
      result = true;
    }
    else if (_expand_text_visible)
    {
      _expand_text_active = true;
      set_needs_repaint();
      result = true;
    }
    else
    {
      SidebarEntry* entry = entry_from_point(x, y);
      if (entry && entry->enabled() && entry->type() == mforms::TaskEntrySelectableItem)
      {
        set_selected(entry);
        result = true;
      }
    }
  }

  return result;
}

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

bool SidebarSection::mouse_click(mforms::MouseButton button, int x, int y)
{
  if (DrawBox::mouse_click(button, x, y))
    return true;

  bool handled = false;

  switch (button)
  {
    case MouseButtonLeft:
    {
      if (_expand_text_active)
      {
        handled = true;
        toggle_expand();
        _expand_text_active = false;
        set_needs_repaint();
      }
      else if (_config_button != NULL && _config_button->down)
      {
        handled = true;
        (*_owner->on_section_command())("configure");
      }
      else if (_refresh_button != NULL && _refresh_button->down && _refresh_button->icon)
      {
        handled = true;
        AdvancedSidebar *aSidebar;
        if ((aSidebar = dynamic_cast<AdvancedSidebar*>(_owner)) != NULL)
          aSidebar->tool_action_clicked("refresh");
      }
      else if (_toggle_mode_button != NULL && _toggle_mode_button->down)
      {
        handled = true;
        bool new_state = !_toggle_mode_button->state;
        if (new_state)
          (*_owner->on_section_command())("switch_mode_on");
        else
          (*_owner->on_section_command())("switch_mode_off");
        _owner->update_mode_buttons(new_state);
        _toggle_mode_button->down = false;
        _toggle_mode_button->hot = false;
      }

      if (!handled)
      {
        SidebarEntry* entry = entry_from_point(x, y);
        if (entry && (entry->enabled() || entry->type() == mforms::TaskEntryAlwaysActiveLink) && ((entry == _hot_entry) || (entry == _selected_entry)))
          (*_owner->on_section_command())(entry->name());
      }
    }
      break;

    case MouseButtonRight:
      /*
       if (_context_menu != NULL && _selected_link != NULL)
       _context_menu->popup_at(this, x + 5, y + 5);
       */
      break;
      
    default:
      break;
  }

  return handled;
}

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

bool SidebarSection::mouse_up(mforms::MouseButton button, int x, int y)
{
  if (DrawBox::mouse_up(button, x, y))
    return true;

  bool result = false;

  switch (button)
  {
    case MouseButtonLeft:
      if (_config_button && _config_button->down)
      {
        _config_button->down = false;
        result = true;
      }

      if (_refresh_button && _refresh_button->down)
      {
        _refresh_button->down = false;
        result = true;
      }

      if (_toggle_mode_button && _toggle_mode_button->down)
      {
        _toggle_mode_button->down = false;
        result = true;
      }
      break;

    default:
      break;
  }
  
  if (result)
    set_needs_repaint();

  return result;
}

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

void SidebarSection::get_layout_size(int* w, int* h)
{
  if (is_layout_dirty())
  {
    create_context_for_layout();
    layout(_layout_context);
  }

  *w = (int)_layout_width;
  *h = (int)_layout_height;
}

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

void SidebarSection::clear_selection()
{
  const bool had_selection = _selected_entry != 0;

  set_selected(0);

  if (had_selection)
    set_needs_repaint();
}

//----------------- SimpleSidebar ------------------------------------------------------------------

// implicitly initialize the adv. sidebar
bool SimpleSidebar::__init = init_factory_method();

bool SimpleSidebar::init_factory_method()
{
 // log_debug3("Initializing SimpleSidebar factory method\n");
  register_factory("Simple", &create_instance);
  return true;
}

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

SimpleSidebar::SimpleSidebar()
{
  switch (Color::get_active_scheme())
  {
  case base::ColorSchemeHighContrast:
  case base::ColorSchemeStandardWin7:
  case base::ColorSchemeStandardWin8:
  case base::ColorSchemeStandardWin8Alternate:
    set_back_color(base::Color::get_application_color_as_string(AppColorPanelContentArea, false));
    break;
  default:
    set_back_color(BACK_COLOR);
    break;
  }
}

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

mforms::TaskSidebar* SimpleSidebar::create_instance()
{
  return new SimpleSidebar();
}

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

SimpleSidebar::~SimpleSidebar()
{
  for (size_t i = 0; i < _sections.size(); i++)
    delete _sections[i];
}


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

/**
 * Find a section with the given name and return its index or -1 if there is none.
 */
int SimpleSidebar::find_section(const std::string& name)
{
  for (size_t i = 0; i < _sections.size(); i++)
  {
    if (_sections[i]->name() == name)
      return (int)i;
  }

  return -1;
}

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

int SimpleSidebar::add_section(const std::string &name, const string& title, mforms::TaskSectionFlags flags)
{
  int result = find_section(title);
  if (result > -1)
    return result;

  SidebarSection* box = new SidebarSection(this, name, title, flags);
  box->set_back_color(get_back_color());
  _sections.push_back(box);
  add(box, false, true);

  return (int)_sections.size() - 1;
}

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

void SimpleSidebar::remove_section(const std::string& section_name)
{
  int index = find_section(section_name);
  if (index < 0)
    return;

  remove(_sections[index]);
  delete _sections[index];
  _sections.erase(_sections.begin() + index);
}

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

int SimpleSidebar::add_section_entry(const std::string& section_name, const std::string &name, const std::string& title,
                                     const std::string& icon, TaskEntryType type)
{
  int index = find_section(section_name);
  if (index < 0)
    return - 1;

  return _sections[index]->add_entry(name, title, icon, type);
}

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

void SimpleSidebar::set_section_entry_text(const std::string& section_entry, const std::string& title)
{
  for (std::vector<SidebarSection*>::const_iterator section = _sections.begin(); section != _sections.end(); ++section)
  {
    int entry_index = (*section)->find_entry(section_entry);
    if (entry_index >= 0 && entry_index < (*section)->entry_count())
    {
      (*section)->set_entry_text(entry_index, title);
      break;
    }
  }
}

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

void SimpleSidebar::set_section_entry_icon(const std::string& section_entry, const std::string& icon)
{
  for (std::vector<SidebarSection*>::const_iterator section = _sections.begin(); section != _sections.end(); ++section)
  {
    int entry_index = (*section)->find_entry(section_entry);
    if (entry_index >= 0 && entry_index < (*section)->entry_count())
    {
      (*section)->set_entry_icon(entry_index, icon);
      break;
    }
  }
}

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

void SimpleSidebar::set_section_entry_enabled(const std::string& section_entry, bool enabled)
{
  for (std::vector<SidebarSection*>::const_iterator section = _sections.begin(); section != _sections.end(); ++section)
  {
    int entry_index = (*section)->find_entry(section_entry);
    if (entry_index >= 0 && entry_index < (*section)->entry_count())
    {
      (*section)->set_entry_enabled(entry_index, enabled);
      break;
    }
  }
}


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

void SimpleSidebar::mark_section_busy(const std::string& section, bool busy)
{
  int index = find_section(section);
  if (index < 0)
    return;

  _sections[index]->mark_busy(busy);
}

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

void SimpleSidebar::remove_section_entry(const std::string& entry_name)
{
  for (std::vector<SidebarSection*>::const_iterator section = _sections.begin(); section != _sections.end(); ++section)
  {
    int entry_index = (*section)->find_entry(entry_name);
    if (entry_index >= 0 && entry_index < (*section)->entry_count())
    {
      (*section)->remove_entry(entry_name);
      break;
    }
  }
}

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

void SimpleSidebar::set_collapse_states(const std::string& data)
{
  std::vector<std::string> collapsed_sections = base::split(data, ",");
  for (std::vector<std::string>::const_iterator iter = collapsed_sections.begin();
       iter != collapsed_sections.end(); ++iter)
  {
    int section;
    int state;
    const char *ptr = strrchr(iter->c_str(), '=');
    if (ptr)
    {
      section = find_section(iter->substr(0, ptr-iter->c_str()));
      if (section < 0)
        continue;

      state = base::atoi<int>(ptr + 1, 0);
    }
    else
      continue;

    if ((state != 0) == _sections[section]->expanded())
      _sections[section]->toggle_expand();
  }
}

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

std::string SimpleSidebar::get_collapse_states()
{
  std::string states;
  for (int i = 0; i < (int)_sections.size(); i++)
  {
    if (i > 0)
      states.append(",");
    states.append(base::strfmt("%s=%i", _sections[i]->name().c_str(), !_sections[i]->expanded()));
  }
  return states;
}

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

void SimpleSidebar::clear_sections()
{
  for (size_t i = 0; i < _sections.size(); i++)
    delete _sections[i];
  _sections.clear();

  relayout();
}

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

void SimpleSidebar::clear_section(const std::string& section)
{
  int index = find_section(section);
  if (index > -1)
  {
    delete _sections[index];
    _sections.erase(_sections.begin() + index);

    relayout();
  }
}

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

void SimpleSidebar::set_selection_color(const std::string& color)
{
  _selection_color = Color::parse(color);
  set_needs_repaint();
}

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

void SimpleSidebar::set_selection_color(const mforms::SystemColor color)
{
  mforms::App* app = mforms::App::get();
  if (app)
    set_selection_color(app->get_system_color(color).to_html());
}

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

int SimpleSidebar::select_entry(const std::string& entry_name)
{
  int was_selected = 0;

  for (std::vector<SidebarSection*>::const_iterator section = _sections.begin(); section != _sections.end(); ++section)
  {
    int entry_index = (*section)->find_entry(entry_name);
    if (entry_index >= 0 && entry_index < (*section)->entry_count())
    {
      if ((*section)->select(entry_name))
        was_selected = 1;
      break;
    }
  }

  return was_selected;
}

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

std::string SimpleSidebar::selected_entry()
{
  for (std::vector<SidebarSection*>::const_iterator section = _sections.begin(); section != _sections.end(); ++section)
  {
    SidebarEntry *entry = (*section)->selected();
    if (entry)
      return entry->name();
  }
  return "";
}

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

void SimpleSidebar::clear_selection()
{
  for (size_t i = 0; i < _sections.size(); i++)
    _sections[i]->clear_selection();
}

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

void SimpleSidebar::update_mode_buttons(bool active)
{
  for (size_t i = 0; i < _sections.size(); i++)
    _sections[i]->update_mode_button(active);
}

//----------------- AdvancedSidebar ----------------------------------------------------------------

// implicitly initialize the adv. sidebar
bool AdvancedSidebar::__init = init_factory_method();

bool AdvancedSidebar::init_factory_method()
{
//  log_debug3("Initializing AdvancedSidebar factory method\n");
  register_factory("SchemaTree", &create_instance);
  return true;
}

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

AdvancedSidebar::AdvancedSidebar()
: _new_schema_tree(TreeNoColumns | TreeNoBorder | TreeSidebar | TreeNoHeader | TreeCanBeDragSource),
_filtered_schema_tree(TreeNoColumns | TreeNoBorder | TreeSidebar | TreeNoHeader | TreeCanBeDragSource),
_schema_search_box(true),
_schema_search_text(mforms::SmallSearchEntry),
_remote_search_enabled(false),
_schema_search_warning(_("Showing loaded schemas only")),
_schema_model(0),
_base_model(0),
_is_model_owner(false),
_schema_box(false)
{
  _remote_search.set_text(_("Search on Server..."));

  _schema_search_warning.set_style(mforms::SmallHelpTextStyle);
  _schema_search_warning.set_text_align(mforms::MiddleCenter);

  NotificationCenter::get()->add_observer(this, "GNColorsChanged");

  setup_schema_tree();
}

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

AdvancedSidebar::~AdvancedSidebar()
{
  NotificationCenter::get()->remove_observer(this);

  if (_is_model_owner)
    delete _schema_model;
}

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

void AdvancedSidebar::mark_section_busy(const std::string& section, bool busy)
{
  if (section.empty())
  {
//    _schema_tree_heading->mark_busy(busy);
    return;
  }
  SimpleSidebar::mark_section_busy(section, busy);
}

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

/**
 * Factory method for this control to be used by mforms to create the instance.
 */
mforms::TaskSidebar* AdvancedSidebar::create_instance()
{
  return new AdvancedSidebar();
}

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

/**
 * Do all the necessary setup for the schema tree (colors, columns and other visual stuff).
 */
void AdvancedSidebar::setup_schema_tree()
{
  std::string background_color; // Empty, to indicate default color.
  std::string toolbar_background_color = BACK_COLOR;
  switch (Color::get_active_scheme())
  {
  case base::ColorSchemeHighContrast: // Don't touch the tree back color. We use the one provided by the OS.
    toolbar_background_color = base::Color::get_application_color_as_string(AppColorPanelToolbar, false);
    break;

  case base::ColorSchemeStandardWin7:
  case base::ColorSchemeStandardWin8:
  case base::ColorSchemeStandardWin8Alternate:
    background_color = base::Color::get_application_color_as_string(AppColorPanelContentArea, false);
    toolbar_background_color = base::Color::get_application_color_as_string(AppColorPanelToolbar, false);
    break;

    default:
      background_color = BACK_COLOR;
      break;
  }

  _new_schema_tree.add_column(mforms::IconStringColumnType, _("Schema"), 100, false, true);
  _new_schema_tree.set_selection_mode(mforms::TreeSelectMultiple);
  _new_schema_tree.set_back_color(background_color);
  scoped_connect(_new_schema_tree.signal_changed(), boost::bind(&AdvancedSidebar::on_tree_node_selected, this));

  _filtered_schema_tree.add_column(mforms::IconStringColumnType, _("Schema"), 100, false, true);
  _filtered_schema_tree.set_selection_mode(mforms::TreeSelectMultiple);
  _filtered_schema_tree.set_back_color(background_color);
  scoped_connect(_filtered_schema_tree.signal_changed(), boost::bind(&AdvancedSidebar::on_tree_node_selected, this));

  _new_schema_tree.set_context_menu(&_tree_context_menu);
  _new_schema_tree.end_columns();

  _filtered_schema_tree.set_context_menu(&_tree_context_menu);
  _filtered_schema_tree.end_columns();

  scoped_connect(_tree_context_menu.signal_will_show(),boost::bind(&AdvancedSidebar::on_show_menu, this, _1));

  _schema_search_box.set_back_color(background_color);
  _schema_search_box.set_name("schema-search-box");
  _schema_search_box.set_spacing(5);
#ifdef _WIN32
  _schema_search_box.set_padding(4, 0, 6, 5);
  _schema_search_box.set_size(-1, 17); // Height excluding the padding, which adds extra pixels.
#else
  _schema_search_box.set_padding(8, 1, 8, 5);
#endif

#ifdef _WIN32
  mforms::ImageBox *image = mforms::manage(new mforms::ImageBox, false);
  image->set_image("search_sidebar.png");
  image->set_image_align(mforms::MiddleCenter);
  _schema_search_box.add(image, false, false);
#endif

  _schema_search_text.set_placeholder_text("Filter objects");
  _schema_search_text.set_tooltip("You can use wildcards to search for objects in multiple schemas.\n"
      "* - a substitue for zero or more characters\n"
      "? - a substitue for single character\n"
      "Search is possible only through already loaded schemas."
      );
  _schema_search_box.add(&_schema_search_text, true, true);
  scoped_connect(_schema_search_text.signal_changed(), boost::bind(&AdvancedSidebar::on_search_text_changed, this));
  scoped_connect(_remote_search.signal_clicked(), boost::bind(&AdvancedSidebar::on_remote_search_clicked, this));

  // Add the tree itself and its section caption to the container.
  _schema_box.set_back_color(background_color);
  _schema_box.add(&_schema_search_box, false, true);
  _schema_box.add(&_new_schema_tree, true, true);
  _schema_box.show(false);
  add_end(&_schema_box, true, true);

}

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

void AdvancedSidebar::handle_notification(const std::string &name, void *sender, NotificationInfo &info)
{
  if (name == "GNColorsChanged")
  {
    std::string background_color; // Empty to indicate default color.
    std::string toolbar_background_color = BACK_COLOR;
    switch (Color::get_active_scheme())
    {
    case base::ColorSchemeHighContrast:
      toolbar_background_color = base::Color::get_application_color_as_string(AppColorPanelToolbar, false);
      break;

    case base::ColorSchemeStandardWin7:
    case base::ColorSchemeStandardWin8:
    case base::ColorSchemeStandardWin8Alternate:
      background_color = base::Color::get_application_color_as_string(AppColorPanelContentArea, false);
      toolbar_background_color = base::Color::get_application_color_as_string(AppColorPanelToolbar, false);
      break;

      default:
        background_color = BACK_COLOR;
        break;
    }

    _new_schema_tree.set_back_color(background_color);
    _filtered_schema_tree.set_back_color(background_color);
    _schema_box.set_back_color(background_color);

    _schema_search_box.set_back_color(toolbar_background_color);
  }
}

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

void AdvancedSidebar::on_search_text_changed()
{
  std::string filter = _schema_search_text.get_string_value();

  // Updates the current schema model to filtered/base
  // based on the content of filter.
  if (filter.length() > 0)
  {
    _filtered_schema_model->set_filter(_schema_search_text.get_string_value());
    _filtered_schema_model->filter_data();

    if (_schema_model == _base_model)
    {
      //_base_model->filter_data(LiveSchemaTree::Any, _schema_search_text.get_string_value(), *_filtered_schema_model);
      _schema_box.remove(&_new_schema_tree);
      set_schema_model(_filtered_schema_model);
      _schema_box.add(&_filtered_schema_tree, true, true);

      if (_remote_search_enabled)
        _schema_box.add(&_remote_search, false, true);
      else
        _schema_box.add(&_schema_search_warning, false, true);
        
      _base_model->enable_events(false);
      _filtered_schema_model->enable_events(true);
    }
  }
  else
  {
    _schema_box.remove(&_filtered_schema_tree);
    
    if (_remote_search_enabled)
      _schema_box.remove(&_remote_search);
    else
      _schema_box.remove(&_schema_search_warning);
      
    set_schema_model(_base_model);
    _schema_box.add(&_new_schema_tree, true, true);
    _base_model->enable_events(true);
    _filtered_schema_model->enable_events(false);
  }

  // Raises a signal indicating the filter has changed
  _search_box_changed_signal(filter);
}

void AdvancedSidebar::on_tree_node_selected()
{
  _tree_node_selected();
}

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

void AdvancedSidebar::on_remote_search_clicked()
{
  std::vector<std::string> filter = base::split(_schema_search_text.get_string_value(), ".", 2);
  std::string schema_filter = filter[0];
  std::string object_filter = "";
  if (filter.size() == 2 )
    object_filter = filter[1];

  _filtered_schema_model->load_data_for_filter(schema_filter, object_filter);
}

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

void AdvancedSidebar::add_items_from_list(mforms::MenuBase &menu, const bec::MenuItemList &items)
{
  for (bec::MenuItemList::const_iterator item = items.begin(); item != items.end(); ++item)
  {
    if (item->type == bec::MenuAction)
    {
      mforms::MenuItem *mitem = menu.add_item_with_title(item->caption, boost::bind(&AdvancedSidebar::handle_menu_command, this, item->name));
      mitem->set_name(item->name);
      mitem->set_enabled(item->enabled);
    }
    else if (item->type == bec::MenuSeparator)
    {
      mforms::MenuItem *mitem = mforms::manage(new mforms::MenuItem("", mforms::SeparatorMenuItem));
      mitem->set_name(item->name);
      menu.add_item(mitem);
    }
    else if (item->type == bec::MenuCascade)
    {
      mforms::MenuItem *submenu = mforms::manage(new mforms::MenuItem(item->caption));
      submenu->set_name(item->name);
      add_items_from_list(*submenu, item->subitems);
      menu.add_submenu(submenu);
      submenu->set_enabled(item->enabled);
    }
  }
}

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

void AdvancedSidebar::on_show_menu(mforms::MenuItem *parent_item)
{
  if (!parent_item)
  {
    _tree_context_menu.remove_all();

    std::list<mforms::TreeNodeRef> nodes = _schema_model == _base_model ? _new_schema_tree.get_selection() : _filtered_schema_tree.get_selection();

    bec::MenuItemList items = _schema_model->get_popup_items_for_nodes(nodes);
    add_items_from_list(_tree_context_menu, items);
  }
}

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

void AdvancedSidebar::handle_menu_command(const std::string& command)
{
  std::list<mforms::TreeNodeRef> nodes = _schema_model == _base_model ? _new_schema_tree.get_selection() : _filtered_schema_tree.get_selection();
  
  _schema_model->activate_popup_item_for_nodes(command, nodes);
}

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

void AdvancedSidebar::tool_action_clicked(const std::string &action)
{
  std::list<mforms::TreeNodeRef> nodes;
  _schema_model->activate_popup_item_for_nodes(action, nodes);
}

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

void AdvancedSidebar::set_schema_model(LiveSchemaTree* model)
{
  // Sets the given model as the base model if none assigned already
  if(!_base_model)
  {
    _base_model = model;
    _base_model->set_model_view(&_new_schema_tree);
    _base_model->enable_events(true);
  }

  if (_is_model_owner)
  {
    delete _schema_model;
    _is_model_owner = false;
  }
  _schema_model = model;
  _schema_box.show(_schema_model != NULL);
}

void AdvancedSidebar::set_filtered_schema_model(wb::LiveSchemaTree* model) 
{
  _filtered_schema_model = model;
  _filtered_schema_model->set_model_view(&_filtered_schema_tree);
}

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

/**
 * Expands the schema node with the given index. A schema node is a top level node.
 */
void AdvancedSidebar::expand_schema(int schema_index)
{
  // TODO: _new_schema_tree
  //_schema_tree.set_expanded(schema_index, true);
}
