#include "stdafx.h"

#include "wb_model_diagram_form.h"
#include "wb_context.h"
#include "wb_component.h"
#include "canvas_floater.h"

#include "mdc_back_layer.h"

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

#include "wbcanvas/model_figure_impl.h"
#include "wbcanvas/model_layer_impl.h"
#include "wbcanvas/model_diagram_impl.h"
#include "wbcanvas/model_connection_impl.h"

#include "wb_context_ui.h"

#include "wb_physical_model_diagram_features.h"

#define DEFAULT_TOOL "basic/select"


using namespace wb;
using namespace bec;



static const double zoom_steps[]= {
2.0,
1.5,
1.2,
1.0,
0.95,
0.9,
0.85,
0.8,
0.7,
0.6,
0.5,
0.4,
0.3,
0.2,
0.1
};




ModelDiagramForm::ModelDiagramForm(WBComponent *owner, const model_DiagramRef &view)
: _view(0), _owner(owner), _model_diagram(view)
{
  _drag_panning= false;
  _space_panning= false;
  _inline_edit_context= 0;

  _current_mouse_x= -1;
  _current_mouse_y= -1;
  
  _main_layer= 0;
  _floater_layer= 0;
  _badge_layer= 0;
  
  _tool= DEFAULT_TOOL;

  _paste_offset= 0;
  _shortcuts= owner->get_wb()->get_ui()->get_command_ui()->get_shortcuts_for_context(WB_CONTEXT_MODEL);

  owner->get_wb()->get_clipboard()->signal_changed().connect(sigc::mem_fun(this, &ModelDiagramForm::clipboard_changed));
  
  _features= new PhysicalModelDiagramFeatures(this);
}


ModelDiagramForm::~ModelDiagramForm()
{
}


void ModelDiagramForm::attach_canvas_view(mdc::CanvasView *cview)
{
  _view= cview;

  cview->set_tag(_model_diagram.id());
  cview->set_grid_snapping(_model_diagram->options().get_int("AlignToGrid", 0)!=0);
  cview->get_background_layer()->set_grid_visible(_model_diagram->options().get_int("ShowGrid", 1)!=0);
  cview->get_background_layer()->set_paper_visible(_model_diagram->options().get_int("ShowPageGrid", 1)!=0);

  cview->get_selection()->signal_begin_dragging().connect(sigc::mem_fun(this, &ModelDiagramForm::begin_selection_drag));
  cview->get_selection()->signal_end_dragging().connect(sigc::mem_fun(this, &ModelDiagramForm::end_selection_drag));

  _main_layer= _view->get_current_layer();
  _badge_layer= _view->new_layer("badges");
  _floater_layer= _view->new_layer("floater");
}


void ModelDiagramForm::set_closed(bool flag)
{
  _model_diagram->closed(flag!=0);
}


bool ModelDiagramForm::is_closed()
{
  return *_model_diagram->closed() != 0;
}


void ModelDiagramForm::set_button_callback(const sigc::slot<bool, ModelDiagramForm*, mdc::MouseButton, bool, mdc::Point, mdc::EventState> &cb)
{
  _handle_button= cb;
}


void ModelDiagramForm::set_motion_callback(const sigc::slot<bool, ModelDiagramForm*, mdc::Point, mdc::EventState> &cb)
{
  _handle_motion= cb;	
}


void ModelDiagramForm::set_reset_tool_callback(const sigc::slot<void, ModelDiagramForm*> &cb)
{
  _reset_tool= cb;
}


void ModelDiagramForm::enable_panning(bool flag)
{
  if (flag)
  {
    _old_tool= _tool;
    _old_reset_tool= _reset_tool;
    _old_handle_button= _handle_button;
    _old_handle_motion= _handle_motion;
    _old_cursor= _cursor;
    _tool= "basic/hand";
    WBComponent *compo= _owner->get_wb()->get_component_named("basic");
    compo->setup_canvas_tool(this, _tool);

    _owner->get_wb()->tool_changed(_view);
  }
  else
  {
    _reset_tool(this);
    _tool= _old_tool;
    _cursor= _old_cursor;
    _reset_tool= _old_reset_tool;
    _handle_button= _old_handle_button;
    _handle_motion= _old_handle_motion;
    _owner->get_wb()->tool_changed(_view);
  }
}


void ModelDiagramForm::begin_selection_drag()
{
  mdc::Selection::ContentType selection(_view->get_selection()->get_contents());
  mdc::AreaGroup *root= _view->get_current_layer()->get_root_area_group();

  // save current position of everything before dragging around objects
  // so that we can store that for undo 
  {
    _old_positions.clear();
    
    grt::ListRef<model_Object> sel(_model_diagram->selection());
    for (size_t c= sel.count(), i= 0; i < c; i++)
    {
      if (sel[i]->is_instance(model_Figure::static_class_name()))
      {
        model_FigureRef figure(model_FigureRef::cast_from(sel[i]));

        _old_positions[figure.valueptr()].pos= mdc::Point(figure->left(), figure->top());
        _old_positions[figure.valueptr()].layer_id= figure->layer().id();
      }
      else if (sel[i]->is_instance(model_Layer::static_class_name()))
      {
        model_LayerRef layer(model_LayerRef::cast_from(sel[i]));

        _old_positions[layer.valueptr()].pos= mdc::Point(layer->left(), layer->top());
      }
    }
  }

  // remove objects from respective layers so they can be freely dragged
  for (mdc::Selection::ContentType::const_iterator iter= selection.begin();
    iter != selection.end(); ++iter)
  {
    // if the layer of the figure is also selected, then leave the figure alone
    mdc::Point rpos= (*iter)->get_root_position();

    if (root != (*iter)->get_parent() && !(*iter)->get_parent()->get_selected())
    {
      root->add(*iter);
      (*iter)->move_to(rpos);
    }
  }
}


void ModelDiagramForm::end_selection_drag()
{
  std::string name;
  bool moved= false;
  int count= 0;

  grt::UndoManager *um= _owner->get_grt()->get_undo_manager();

  um->begin_undo_group();
  
  // save original position of objects into undo record
  grt::ListRef<model_Object> sel(_model_diagram->selection());
  for (size_t c= sel.count(), i= 0; i < c; i++)
  {
    if (sel[i]->is_instance(model_Figure::static_class_name()))
    {
      model_FigureRef figure(model_FigureRef::cast_from(sel[i]));
      {
        mdc::Point p= _old_positions[figure.valueptr()].pos;
        std::string layer_id= _old_positions[figure.valueptr()].layer_id;

        if (*figure->left() != p.x || *figure->top() != p.y || layer_id != figure->layer().id())
        {
          moved= true;
          um->add_undo(new grt::UndoObjectChangeAction(figure, "left", grt::DoubleRef(p.x)));
          um->add_undo(new grt::UndoObjectChangeAction(figure, "top", grt::DoubleRef(p.y)));
        }
      }
      name= figure->name();
      count++;
    }
    else if (sel[i]->is_instance(model_Layer::static_class_name()))
    {
      model_LayerRef layer(model_LayerRef::cast_from(sel[i]));
      {
        mdc::Point p= _old_positions[layer.valueptr()].pos;

        if (*layer->left() != p.x || *layer->top() != p.y)
        {
          moved= true;
          um->add_undo(new grt::UndoObjectChangeAction(layer, "left", grt::DoubleRef(p.x)));
          um->add_undo(new grt::UndoObjectChangeAction(layer, "top", grt::DoubleRef(p.y)));
        }
      }
      name= layer->name();
      count++;
    }
  }
  
  // change the layer of moved stuff
  {
    grt::AutoUndo undo(_owner->get_grt());
    if (relocate_figures())
    {
      moved= true;
      undo.end("Update Object Layers");
    }
    else
      undo.cancel();
  }  
  
  if (!moved)
  {
    um->cancel_undo_group();
    return;
  }

  if (!name.empty() && count == 1)
    um->end_undo_group(strfmt(_("Move %s"), name.c_str()));
  else
    um->end_undo_group(_("Move Objects"));
}


bool ModelDiagramForm::current_mouse_position(int &x, int &y)
{
  int w, h;
  
  _view->get_view_size(w, h);
  x= _current_mouse_x;
  y= _current_mouse_y;
  
  if (x < 0 || y < 0 || x >= w || y >= h)
    return false;
  return true;
}


bool ModelDiagramForm::current_mouse_position(mdc::Point &pos)
{
  int x, y;
  bool inside= current_mouse_position(x, y);
  
  pos= _view->window_to_canvas(x, y);
  
  return inside;
}


mdc::CanvasItem *ModelDiagramForm::get_leaf_item_at(const mdc::Point &pos)
{
  return _view->get_leaf_item_at(pos);
}


void ModelDiagramForm::handle_mouse_button(mdc::MouseButton button, bool press, int x, int y, mdc::EventState state)
{
  stop_editing();
  
  mdc::Point pos(_view->window_to_canvas(x, y));
  
  if (button == mdc::ButtonRight && !press)
  {
    model_ObjectRef object= get_object_at(pos);

    bec::MenuItem item;
    bec::MenuItemList items;

    // if the user clicks in a selected object or in the view just show popup 
    // if user clicks in an unseleced object, select it and show popup

    if (object.is_valid() && _model_diagram->selection().get_index(object) == grt::BaseListRef::npos)
      _view->get_selection()->set(_view->get_item_at(pos));

    {
      std::list<std::string> groups;

      groups.push_back("Catalog/*");
      groups.push_back("Model/*");

      _owner->get_wb()->get_object_list_popup_items(this, grt::ListRef<GrtObject>::cast_from(_model_diagram->selection()),
        get_edit_target_name(), groups, items);
    }
    //else
      //_owner->get_model_popup_items(items);

    if (!items.empty())
    {
      int x, y;
      _view->canvas_to_window(pos, x, y);
      _owner->get_wb()->popup_menu(&items, x, y);
    }
    return;
  }

  // temporarily select Hand tool when middle-button-dragging
  if (button == mdc::ButtonMiddle)
  {
    if (press && !_drag_panning && !_space_panning)
    {
      _drag_panning= true;
      enable_panning(true);
    }
    else if (!press && _drag_panning)
    {
      _drag_panning= false;
      enable_panning(false);
    }
  }

  if (button != mdc::ButtonLeft && button != mdc::ButtonMiddle)
    return;

  if (_handle_button && _handle_button(this, button, press, pos, state))
    return;

  
  _view->handle_mouse_button(button, press, x, y, state);
}


void ModelDiagramForm::handle_mouse_move(int x, int y, mdc::EventState state)
{
  mdc::Point pos(_view->window_to_canvas(x, y));
  
  _features->mouse_moved(x, y, state);
  
  _current_mouse_x= x;
  _current_mouse_y= y;

  if (_handle_motion && _handle_motion(this, pos, state))
    return;

  _view->handle_mouse_move(x, y, state);
}


void ModelDiagramForm::handle_key(const mdc::KeyInfo &key, bool press, mdc::EventState state)
{
  if (press)
  {    
    for (std::vector<WBShortcut>::const_iterator iter= _shortcuts.begin();
      iter != _shortcuts.end(); ++iter)
    {
      if (iter->modifiers == state && iter->key == key)
      {
        _owner->get_wb()->get_ui()->get_command_ui()->activate_command(iter->command);
        return;
      }
    }
  }
  if (key.keycode == mdc::KSpace)
  {    
    if (press && !_drag_panning && !_space_panning)
    {
      _space_panning= true;
      enable_panning(true);
    }
    else if (!press && _space_panning)
    {
      _space_panning= false;
      enable_panning(false);
    }
  }

  _view->handle_key(key, press, state);
}


void ModelDiagramForm::set_tool(const std::string &tool)
{
  reset_tool(false);

  _tool= tool;

  WBComponent *compo= _owner->get_wb()->get_component_named(split_string(tool, "/")[0]);
  if (compo)
    compo->setup_canvas_tool(this, tool);
  else
    throw std::runtime_error("Invalid tool "+tool);

  _owner->get_wb()->tool_changed(_view);
}


void ModelDiagramForm::reset_tool(bool notify)
{
 // _tool_args.clear();

  _tool= DEFAULT_TOOL;

  if (_reset_tool)
    _reset_tool(this);

  _cursor= "";
  _handle_button.disconnect();
  _handle_motion.disconnect();
  _reset_tool.disconnect();

  if (notify)
    _owner->get_wb()->tool_changed(_view);
}


std::string ModelDiagramForm::get_tool_argument(const std::string &option)
{
  return _tool_args[option];
}


/** 
 ****************************************************************************
 * @brief Sets options for the selected tool
 *
 * Change the value of an option for the currently selected tool.
 *
 * @param option name of the option
 * @param value value of the option
 *****************************************************************************/
void ModelDiagramForm::set_tool_argument(const std::string &option, const std::string &value)
{
  _tool_args[option]= value;

  _tool_argument_changed.emit(option);
}


void ModelDiagramForm::set_zoom(double zoom)
{
  _model_diagram->zoom(zoom);
}


double ModelDiagramForm::get_zoom()
{
  return _model_diagram->zoom();
}


bec::Clipboard *ModelDiagramForm::get_clipboard()
{
  return _owner->get_grt_manager()->get_clipboard();
}



bool ModelDiagramForm::can_copy()
{
  return get_copiable_selection().count() > 0;
}


bool ModelDiagramForm::can_select_all()
{
  return true;
}

static void check_if_can_paste(WBComponent *compo, const grt::ObjectRef &object, bool *result)
{
  if (compo->can_paste_non_model_object(object))
    *result= true;
}


bool ModelDiagramForm::can_paste()
{
  std::list<grt::ObjectRef> data(get_clipboard()->get_data());
  WBContext *wb= _owner->get_wb();

  for (std::list<grt::ObjectRef>::iterator iter= data.begin();
      iter != data.end(); ++iter)
  {
    if (model_ObjectRef::can_wrap(*iter))
    {
      if (!wb->get_component_handling(model_ObjectRef::cast_from(*iter)))
        return false;
    }
    else
    {
      bool result= false;
      wb->foreach_component(sigc::bind<grt::ObjectRef,bool*>(sigc::ptr_fun(check_if_can_paste), *iter, &result));
      if (!result)
        return false;
    }
  }
  return !get_clipboard()->empty();
}


bool ModelDiagramForm::can_delete()
{
  return has_selection();
}


std::string ModelDiagramForm::get_edit_target_name()
{
  grt::ListRef<model_Object> sel(get_copiable_selection());

  if (sel.count()==0)
    return "";

  if (sel.count() == 1)
  {
    std::string name;

    name= sel[0]->name().c_str();
    if (name.empty() && sel[0]->has_member("caption"))
      name= sel[0]->get_string_member("caption");

    return strfmt("'%s'", name.c_str());
  }
  else
    return strfmt(_("%i Selected Figures"), sel.count());
}



void ModelDiagramForm::cut()
{
  grt::UndoManager *um= _owner->get_grt()->get_undo_manager();

  std::string edit_target_name= get_edit_target_name();

  um->begin_undo_group();
  copy();
  int count= delete_selection(true);
  um->end_undo_group();
  um->set_action_description(strfmt(_("Cut %s"), edit_target_name.c_str()));

  _owner->get_wb()->show_status_text(strfmt(_("%i object(s) cut."), count));
}


void ModelDiagramForm::copy()
{
  grt::ListRef<model_Object> selection(get_copiable_selection());
  bec::Clipboard *clip= get_clipboard();
  model_LayerRef target_layer(get_model_diagram()->rootLayer());
  int count= 0;
  grt::CopyContext copy_context(_owner->get_grt());

  clip->clear();
  for (size_t c= selection.count(), i= 0; i < c; i++)
  {
    WBComponent *compo= _owner->get_wb()->get_component_handling(selection.get(i));
    if (compo)
    {
      model_ObjectRef copy(compo->clone_object(selection.get(i), target_layer, copy_context, true));
      clip->append_data(copy);
      count++;
    }
  }
  clip->set_content_description(get_edit_target_name());

  copy_context.finish();
  clip->changed();

  _owner->get_wb()->show_status_text(strfmt(_("%i object(s) copied."), count));
}


void ModelDiagramForm::clipboard_changed()
{
  _paste_offset= 0;
}


static void get_component_that_can_paste(WBComponent *compo, 
                                         const grt::ObjectRef &object, 
                                         WBComponent **result)
{
  if (compo->can_paste_non_model_object(object))
    *result= compo;
}


void ModelDiagramForm::paste()
{
  WBContext *wb= _owner->get_wb();
  int count= 0;

  _paste_offset+= 10;

  grt::CopyContext context(wb->get_grt());

  grt::AutoUndo undo(wb->get_grt());

  _model_diagram->unselectAll();

  std::list<grt::ObjectRef> data(get_clipboard()->get_data());
  for (std::list<grt::ObjectRef>::iterator iter= data.begin();
      iter != data.end(); ++iter)
  {
    model_LayerRef layer= _model_diagram->rootLayer();
    if (model_ObjectRef::can_wrap(*iter))
    {
      WBComponent *compo= wb->get_component_handling(model_ObjectRef::cast_from(*iter));

      if (!compo) continue;

      if (model_ObjectRef::can_wrap(*iter))
      {
        model_ObjectRef object(compo->clone_object(model_ObjectRef::cast_from(*iter), layer, context, false));
        if (object.is_valid())
        {
          if (object.is_instance(model_Figure::static_class_name()))
          {
            model_FigureRef figure(model_FigureRef::cast_from(object));
            figure->top(*figure->top() + _paste_offset);
            figure->left(*figure->left() + _paste_offset);
          }
          else if (object.is_instance(model_Layer::static_class_name()))
          {
            model_LayerRef layer(model_LayerRef::cast_from(object));
            layer->top(*layer->top() + _paste_offset);
            layer->left(*layer->left() + _paste_offset);
          }
          count++;

          _model_diagram->selectObject(object);
        }
      }
    }
    else
    {
      WBComponent *compo= 0;
      wb->foreach_component(
        sigc::bind<grt::ObjectRef,WBComponent**>(sigc::ptr_fun(get_component_that_can_paste),
          *iter, &compo)); 
      if (compo)
      {
        compo->paste_non_model_object(this, *iter);
        count++;
      }
    }
  }
  context.finish();
  undo.end(strfmt(_("Paste %s"), get_clipboard()->get_content_description().c_str()));

  _owner->get_wb()->show_status_text(strfmt(_("%i object(s) pasted."), count));
}


void ModelDiagramForm::select_all()
{
  for (size_t c= get_model_diagram()->figures().count(), i= 0; i < c; i++)      
    get_model_diagram()->selectObject(get_model_diagram()->figures().get(i)); 
  
  for (size_t c= get_model_diagram()->layers().count(), i= 0; i < c; i++)      
    get_model_diagram()->selectObject(get_model_diagram()->layers().get(i)); 
}


void ModelDiagramForm::delete_selection()
{
  int count= delete_selection(false);
  _owner->get_wb()->show_status_text(strfmt(_("%i object(s) deleted."), count));
}


int ModelDiagramForm::delete_selection(bool noquestions)
{
  grt::UndoManager *um= _owner->get_grt()->get_undo_manager();
  grt::ListRef<model_Object> selection= get_selection();

  std::vector<model_ObjectRef> objects;
  std::string edit_target_name= get_edit_target_name();

  um->begin_undo_group();

  for (size_t c= selection.count(), i= 0; i < c; i++)
  {
    if (selection.get(i).is_instance(model_Object::static_class_name()))
      objects.push_back(model_ObjectRef::cast_from(selection.get(i)));
  }

  std::map<std::string, bool> options;

  if (noquestions)
    options["deletedbobjects"]= true;
  else
  {
    std::string option;

    option= _owner->get_wb()->get_wb_options().get_string("workbench.physical:DeleteObjectConfirmation", "ask");
    if (option == "delete")
      options["deletedbobjects"]= true;
    else if (option == "keep")
      options["deletedbobjects"]= false;
  }
  for (size_t c= objects.size(), i= 0; i < c; i++)
    _owner->get_wb()->delete_object(objects[i], options);

  um->end_undo_group();
  um->set_action_description(strfmt(_("Delete %s"), edit_target_name.c_str()));

  return objects.size();
}


std::string ModelDiagramForm::get_diagram_info_text()
{
  if (_model_diagram.is_valid())
    return strfmt("%i x %i mm", (int)*_model_diagram->width(), (int)*_model_diagram->height());
  return "";
}


grt::ListRef<model_Object> ModelDiagramForm::get_selection()
{
  return _model_diagram->selection();
}


grt::ListRef<model_Object> ModelDiagramForm::get_copiable_selection()
{
  grt::ListRef<model_Object> sel(_model_diagram->selection());
  grt::ListRef<model_Object> copiable(_owner->get_grt());

  for (size_t c= sel.count(), i= 0; i < c; i++)
  {
    if (!sel.get(i).is_instance(model_Connection::static_class_name()))
      copiable.insert(sel[i]);
  }
  return copiable;
}


bool ModelDiagramForm::has_selection()
{
  return _model_diagram->selection().count() > 0;
}


bool ModelDiagramForm::is_visible(const model_ObjectRef &object, bool partially)
{
  mdc::CanvasItem *item= 0;
  
  if (object.is_instance(model_Figure::static_class_name()))
  {
    item= model_FigureRef::cast_from(object)->get_data()->get_canvas_item();
  }
  else if (object.is_instance(model_Connection::static_class_name()))
  {
    item= model_ConnectionRef::cast_from(object)->get_data()->get_canvas_item();
  }
  else if (object.is_instance(model_Layer::static_class_name()))
  {
    item= model_LayerRef::cast_from(object)->get_data()->get_area_group();
  }
  else
  {
    g_warning("unhandled");
    return false;
  }

  if (!item) return false;

  mdc::Rect bounds= item->get_root_bounds();
  mdc::Rect viewport= _view->get_viewport();
  
  if (partially)
    return mdc::bounds_intersect(viewport, bounds);    
  else
    return mdc::bounds_contain_bounds(viewport, bounds);
}

void ModelDiagramForm::focus_and_make_visible(const model_ObjectRef &object, bool select)
{
  mdc::CanvasItem *item= 0;

  if (object.is_instance(model_Figure::static_class_name()))
  {
    item= model_FigureRef::cast_from(object)->get_data()->get_canvas_item();
  }
  else if (object.is_instance(model_Connection::static_class_name()))
  {
    item= model_ConnectionRef::cast_from(object)->get_data()->get_canvas_item();
  }
  else if (object.is_instance(model_Layer::static_class_name()))
  {
    item= model_LayerRef::cast_from(object)->get_data()->get_area_group();
  }

  if (item)
  {
    mdc::CanvasView *view= item->get_view();
    mdc::Rect bounds= item->get_root_bounds();
    mdc::Rect viewport= view->get_viewport();

    if (!mdc::bounds_contain_bounds(viewport, bounds))
    {
      mdc::Point offset= viewport.pos;

      // get the offset that will move the viewport the least to bring the item inside it

      if (bounds.xmin() < viewport.xmin())
        offset.x= bounds.xmin() - 20;
      else if (bounds.xmax() > viewport.xmax())
        offset.x= bounds.xmax() - viewport.width();

      if (bounds.ymin() < viewport.ymin())
        offset.y= bounds.ymin() - 20;
      else if (bounds.ymax() > viewport.ymax())
        offset.y= bounds.ymax() - viewport.height();

      view->set_offset(offset);
    }
    view->focus_item(item);
    if (select)
      view->get_selection()->set(item);
  }
}


static model_ObjectRef search_object_list(const grt::ListRef<model_Object> &objects,
                                          size_t starting_index,
                                          const std::string &text)
{
  for (size_t count= objects.count(), i= starting_index; i < count; i++)
  {
    model_ObjectRef object(objects[i]);
    
    if (strstr(object->name().c_str(), text.c_str()))
      return object;
  }
  
  return model_ObjectRef();
}



bool ModelDiagramForm::search_and_focus_object(const std::string &text)
{
  grt::ListRef<model_Object> selection(get_selection());
  model_ObjectRef selected, found_object;
  
  if (selection.count() > 0)
    selected= selection[0];

  if (!selected.is_valid() || selected.is_instance(model_Figure::static_class_name()))
  {
    size_t index= 0;
    
    if (selected.is_valid())
    {
      index = _model_diagram->figures().get_index(selected);
      if (index == grt::BaseListRef::npos)
        index= 0;
      else
        index++;
    }
    found_object= search_object_list(_model_diagram->figures(), index, text);    
  }
  
  if (!found_object.is_valid() && selected.is_instance(model_Connection::static_class_name()))
  {
    size_t index= 0;
    if (selected.is_valid())
    {
      index = _model_diagram->connections().get_index(selected);
      if (index == grt::BaseListRef::npos)
        index= 0;
      else
        index++;
    }
    found_object= search_object_list(_model_diagram->connections(), index, text);
  }
  
  if (!found_object.is_valid() && selected.is_instance(model_Layer::static_class_name()))
  {
    size_t index= 0;
    if (selected.is_valid())
    {
      index = _model_diagram->layers().get_index(selected);
      if (index == grt::BaseListRef::npos)
        index= 0;
      else
        index++;
    }
    found_object= search_object_list(_model_diagram->layers(), index, text);
  }

  
  if (found_object.is_valid())
  {
    _owner->get_grt_manager()->replace_status_text(strfmt(_("Found %s '%s'"),
                                                          found_object.get_metaclass()->get_attribute("caption").c_str(),
                                                          found_object->name().c_str()));
    
    focus_and_make_visible(found_object, true);

    return true;
  }
  else
  {
    if (_model_diagram->selection().count() > 0)
      _owner->get_grt_manager()->replace_status_text(_("No more matches"));
    else
      _owner->get_grt_manager()->replace_status_text(_("No match found"));
  }

  _model_diagram->selection().remove_all();
  
  return false;
}


void ModelDiagramForm::set_cursor(const std::string &cursor)
{
  _cursor= cursor;
}



model_LayerRef ModelDiagramForm::get_layer_at(const mdc::Point &pos, mdc::Point &offset)
{
  model_LayerRef layer;
  mdc::AreaGroup *ag= 0;

  mdc::CanvasItem *item= _view->get_item_at(pos);

  if (!item)
  {
    offset= pos;
    return _model_diagram->rootLayer();
  }
  item= item->get_toplevel();


  while (item && (ag= dynamic_cast<mdc::AreaGroup*>(item))==0)
    item= item->get_parent();

  if (ag)
  {
    for (size_t c= _model_diagram->layers().count(), i= 0; i < c; i++)
    {
      layer= _model_diagram->layers().get(i);

      if (ag == layer->get_data()->get_area_group())
      {
        offset= ag->convert_point_from(pos, 0);

        return layer;
      }
    }
  }
  
  offset= pos;

  return _model_diagram->rootLayer();
}


model_LayerRef ModelDiagramForm::get_layer_bounding(const mdc::Rect &rect, mdc::Point &offset)
{
  grt::ListRef<model_Layer> layers(_model_diagram->layers());
  
  for (grt::ListRef<model_Layer>::const_reverse_iterator layer= layers.rbegin(); 
       layer != layers.rend(); ++layer)
  {
    if ((*layer)->get_data()->get_area_group() &&
      mdc::bounds_contain_bounds((*layer)->get_data()->get_area_group()->get_root_bounds(), rect))
    {
      return *layer;
    }
  }
  return model_LayerRef();
}


model_ObjectRef ModelDiagramForm::get_object_at(const mdc::Point &pos)
{
  mdc::CanvasItem *item= _view->get_item_at(pos);

  if (!item)
    return model_ObjectRef();

  std::string id= item->get_tag();
  if (id.empty())
    return model_ObjectRef();

  model_ObjectRef object;

  object= grt::find_object_in_list(_model_diagram->figures(), id);
  if (object.is_valid())
    return object;

  object= grt::find_object_in_list(_model_diagram->layers(), id);
  if (object.is_valid())
    return object;

  object= grt::find_object_in_list(_model_diagram->connections(), id);
  if (object.is_valid())
    return object;

  return model_ObjectRef();
}

grt::UndoManager *ModelDiagramForm::get_undo_manager()
{
  return _owner->get_grt()->get_undo_manager(); 
}


bool ModelDiagramForm::relocate_figures()
{
  bool relocated= false;
  grt::ListRef<model_Figure> figures(_model_diagram->figures());

  // relocate all figures in the diagram to the layers they're inside
  for (size_t c= figures.count(), i= 0; i < c; i++)
  {
    model_FigureRef figure(figures[i]);

    relocated|= _model_diagram->get_data()->update_layer_of_figure(figure);
  }

  return relocated;
}


std::vector<std::string> ModelDiagramForm::get_accepted_drop_types()
{
  std::vector<std::string> types= _owner->get_accepted_drop_types();

  return types;
}


bool ModelDiagramForm::accepts_drop(int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects)
{
  return _owner->accepts_drop(this, x, y, type, objects);
}


bool ModelDiagramForm::accepts_drop(int x, int y, const std::string &type, const std::string &text)
{
  return _owner->accepts_drop(this, x, y, type, text);
}


bool ModelDiagramForm::perform_drop(int x, int y, const std::string &type, const std::list<GrtObjectRef> &objects)
{
  return _owner->perform_drop(this, x, y, type, objects);
}

bool ModelDiagramForm::perform_drop(int x, int y, const std::string &type, const std::string &text)
{
  return _owner->perform_drop(this, x, y, type, text);
}




mdc::Layer *ModelDiagramForm::get_floater_layer()
{
  return _floater_layer;
}


void ModelDiagramForm::add_floater(Floater *floater)
{
  mdc::Point pos;

  pos.x= _view->get_viewport().xmax() - 200;
  pos.y= _view->get_viewport().ymin() + 20;

  floater->move_to(pos);
  _floater_layer->add_item(floater);
}


/* unused
void ModelDiagramForm::hover_in_figure(mdc::CanvasItem *item, mdc::Point pos)
{
}



void ModelDiagramForm::hover_out_figure(mdc::CanvasItem *item)
{
}
*/



// inline editing

void ModelDiagramForm::begin_editing(const mdc::Rect &rect, const std::string &text, float text_size, bool multiline)
{
  if (_inline_edit_context)
  {
    int x, y;
    int width, height;

    _inline_edit_context->set_font_size(text_size);
    _inline_edit_context->set_multiline(multiline);

    _view->canvas_to_window(rect, x, y, width, height);

    _inline_edit_context->begin_editing(x, y, width, height, text);
  }
}


void ModelDiagramForm::stop_editing()
{
  if (_inline_edit_context)
    _inline_edit_context->end_editing();
}


static void forward_edit_finished(const std::string &text, EditFinishReason reason,
                                  ModelDiagramForm *form)
{
  form->signal_editing_done().emit(text, reason);
}


void ModelDiagramForm::set_inline_editor_context(InlineEditContext *context)
{
  _inline_edit_context= context;
  
  _inline_edit_context->signal_edit_finished().
    connect(sigc::bind(sigc::ptr_fun(forward_edit_finished), this));
}



void ModelDiagramForm::zoom_in()
{
  model_DiagramRef view(get_model_diagram());
  double zoom= *view->zoom();
  
  for (size_t i= 0; i < sizeof(zoom_steps)/sizeof(*zoom_steps); i++)
  {
    if (zoom >= zoom_steps[i])
    {
      if (i > 0)
        view->zoom(zoom_steps[i-1]);
      return;
    }
  }
  view->zoom(zoom_steps[0]);  
}


void ModelDiagramForm::zoom_out()
{
  model_DiagramRef view(get_model_diagram());
  double zoom= *view->zoom();
  
  for (size_t i= 0; i < sizeof(zoom_steps)/sizeof(*zoom_steps); i++)
  {
    if (zoom >= zoom_steps[i])
    {
      if (i+1 < sizeof(zoom_steps)/sizeof(*zoom_steps))
        view->zoom(zoom_steps[i+1]);
      return;
    }
  }
  view->zoom(zoom_steps[sizeof(zoom_steps)/sizeof(*zoom_steps)-1]);
}
