/* 
 * Copyright (c) 2007, 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 "connection_figure.h"
#include "table_figure.h"
#include "mdc_box_side_magnet.h"

using namespace std;
using namespace wbfig;
using namespace mdc;
using namespace base;

#define SPLIT_LINE_LENGTH 30


ConnectionLineLayouter::ConnectionLineLayouter(mdc::Connector *sconn, mdc::Connector *econn)
: mdc::OrthogonalLineLayouter(sconn, econn)
{
  _type= NormalLine;
}


std::vector<mdc::ItemHandle*> ConnectionLineLayouter::create_handles(mdc::Line *line, 
                                                                     mdc::InteractionLayer *ilayer)
{
  if (_type == ZLine)
    return mdc::LineLayouter::create_handles(line, ilayer);

  return super::create_handles(line, ilayer);
}


bool ConnectionLineLayouter::handle_dragged(mdc::Line *line, mdc::ItemHandle *handle, 
                                            const Point &pos, bool dragging)
{
  /*
  if (handle->get_tag() == 1)
  {
  }
*/
  return super::handle_dragged(line, handle, pos, dragging);
}


static void set_connector_side(mdc::BoxSideMagnet *magnet, mdc::Connector *conn, double angle)
{
  if (magnet == NULL)
    return;

  if (angle == 0)
    magnet->set_connector_side(conn, mdc::BoxSideMagnet::Right);
  else if (angle == 180)
    magnet->set_connector_side(conn, mdc::BoxSideMagnet::Left);
  else if (angle == 90)
    magnet->set_connector_side(conn, mdc::BoxSideMagnet::Top);
  else 
    magnet->set_connector_side(conn, mdc::BoxSideMagnet::Bottom);
}


bool ConnectionLineLayouter::update_start_point()
{
  mdc::CanvasItem *start_item= _linfo.start_connector()->get_connected_item();

  if (_type == ZLine)
  {
    if (start_item)
    {
      
    }
  }
  else if (start_item && dynamic_cast<mdc::BoxSideMagnet*>(_linfo.start_connector()->get_connected_magnet()))
  {
    double angle;
    
    angle= angle_of_intersection_with_rect(start_item->get_root_bounds(),
      _linfo.subline_end_point(_linfo.start_subline()));

    set_connector_side(dynamic_cast<mdc::BoxSideMagnet*>(_linfo.start_connector()->get_connected_magnet()), 
      _linfo.start_connector(), angle);
  }
  return super::update_start_point();
}


bool ConnectionLineLayouter::update_end_point()
{
  mdc::CanvasItem *end_item= _linfo.end_connector()->get_connected_item();

  if (_type == ZLine)
  {
    if (end_item)
    {

    }
  }
  else if (end_item && dynamic_cast<mdc::BoxSideMagnet*>(_linfo.end_connector()->get_connected_magnet()))
  {
    double angle;
    
    angle= angle_of_intersection_with_rect(end_item->get_root_bounds(),
      _linfo.subline_start_point(_linfo.end_subline()));

    set_connector_side(dynamic_cast<mdc::BoxSideMagnet*>(_linfo.end_connector()->get_connected_magnet()), 
      _linfo.end_connector(), angle);
  }

  return super::update_end_point();
}


std::vector<Point> ConnectionLineLayouter::get_points_for_subline(int subline)
{
  if (_type == ZLine)
  {
    if (subline == _linfo.start_subline())
    {
      std::vector<Point> points;
      Point p;

      p= _linfo.subline_start_point(subline).round();
      points.push_back(p);
      if (_linfo.subline_start_angle(subline) == 0)
        points.push_back(Point(p.x+15, p.y));
      else
        points.push_back(Point(p.x-15, p.y));

      p= _linfo.subline_end_point(subline).round();
      if (_linfo.subline_end_angle(subline) == 0)
        points.push_back(Point(p.x+15, p.y));
      else
        points.push_back(Point(p.x-15, p.y));
      points.push_back(p);

      return points;
    }
  }

  return super::get_points_for_subline(subline);
}


void ConnectionLineLayouter::set_type(ConnectionLineLayouter::Type type)
{
  _type= type;
  update();
}

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



Connection::Connection(mdc::Layer *layer, FigureEventHub *hub, model_Object *represented_object)
: mdc::Line(layer), _represented_object(represented_object), _hub(hub)
{
  set_cache_toplevel_contents(false);
  set_accepts_selection(true);
  set_draggable(false);

  _split= false;
  _center_captions= false;

  _start_dashed= false;
  _end_dashed= false;

  _diamond= None;

  _start_figure= 0;
  _end_figure= 0;
}


void Connection::set_start_figure(mdc::CanvasItem *item)
{
  _start_figure= item;
  
  update_layouter();
}


void Connection::set_end_figure(mdc::CanvasItem *item)
{
  _end_figure= item;

  update_layouter();
}


void Connection::update_layouter()
{
  if (_start_figure && _end_figure)
  {
    if (!get_layouter())
    {
      mdc::Connector *sc, *ec;
      TableColumnItem *item;

      sc= new mdc::Connector(this);
      sc->set_draggable(false);
      item= dynamic_cast<TableColumnItem*>(_start_figure);
      if (item)
        sc->connect(item->get_item_magnet());
      else
      {
        wbfig::Table *table= dynamic_cast<wbfig::Table*>(_start_figure);
        sc->connect(table->get_sides_magnet());
      }
      ec= new mdc::Connector(this);
      ec->set_draggable(false);
      item= dynamic_cast<TableColumnItem*>(_end_figure);
      if (item)
        ec->connect(item->get_item_magnet());
      else
      {
        wbfig::Table *table = dynamic_cast<wbfig::Table*>(_end_figure);
        if (table != NULL)
          ec->connect(table->get_sides_magnet());
      }

      ConnectionLineLayouter *layouter= new ConnectionLineLayouter(sc, ec);
      set_layouter(layouter);
    }
    else
      get_layouter()->update();
  }
}


void Connection::set_diamond_type(DiamondType type)
{
  _diamond= type;
  set_needs_render();
}


void Connection::set_splitted(bool flag)
{
  _split= flag;
  set_needs_render();
}


double Connection::get_segment_offset(int subline)
{
  ConnectionLineLayouter* l= dynamic_cast<ConnectionLineLayouter*>(get_layouter());
  if (l)
    return l->get_segment_offset(subline);
  return 0.0;
}


void Connection::set_segment_offset(int subline, double offset)
{
  ConnectionLineLayouter* l= dynamic_cast<ConnectionLineLayouter*>(get_layouter());
  if (l)
    l->set_segment_offset(subline, offset);
}


bool Connection::contains_point(const Point &point) const
{
  if (!super::contains_point(point))
    return false;

  if (_split)
  {
    Point v1, v2;
    Rect bounds;

    v1= convert_point_to(_segments[0].pos, get_parent());
    v2= convert_point_to(_segments[1].pos, get_parent());
    points_reorder(v1, v2);

    if (v2.y == v1.y)
    {
      v1.y -= 2;
      v2.y += 2;
      if (v2.x > v1.x)
        v2.x= v1.x + 20;
      else
        v2.x= v1.x - 20;
      bounds= Rect(v1, v2);
    }
    else
    {
      v1.x -= 2;
      v2.x += 2;
      if (v2.y > v1.y)
        v2.y= v1.y + 20;
      else
        v2.y= v1.y - 20;
      bounds= Rect(v1, v2);
    }

    if (bounds_contain_point(bounds, point.x, point.y))
      return true;

    v1= convert_point_to(_segments[_segments.size()-1].pos, get_parent());
    v2= convert_point_to(_segments[_segments.size()-2].pos, get_parent());
    points_reorder(v1, v2);

    if (v2.y == v1.y)
    {
      v1.y -= 2;
      v2.y += 2;
      if (v2.x > v1.x)
        v2.x= v1.x + 20;
      else
        v2.x= v1.x - 20;
      bounds= Rect(v1, v2);
    }
    else
    {
      v1.x -= 2;
      v2.x += 2;
      if (v2.y > v1.y)
        v2.y= v1.y + 20;
      else
        v2.y= v1.y - 20;
      bounds= Rect(v1, v2);
    }

    if (bounds_contain_point(bounds, point.x, point.y))
      return true;
    return false;
  }
  return true;
}

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

void Connection::stroke_outline(mdc::CairoCtx *cr, float offset) const
{
  if (!_split || _segments.size() < 2)
    mdc::Line::stroke_outline(cr, offset);
  else // Draw a split connection (only show start and end).
  {
    Point v1, v2;
    {
      v1= _segments[0].pos;
      v2= _segments[1].pos;

      if (v2.y == v1.y)
      {
        cr->move_to(v1);
        if (v2.x > v1.x)
          cr->line_to(v1.x + 20, v2.y);
        else
          cr->line_to(v1.x - 20, v2.y);
      }
      else
      {
        cr->move_to(v1);
        if (v2.y > v1.y)
          cr->line_to(v1.x, v1.y + 20);
        else
          cr->line_to(v1.x, v1.y - 20);
      }
    }

    {
      v1= _segments[_segments.size()-1].pos;
      v2= _segments[_segments.size()-2].pos;

      if (v2.y == v1.y)
      {
        cr->move_to(v1);
        if (v2.x > v1.x)
          cr->line_to(v1.x + 20, v2.y);
        else
          cr->line_to(v1.x - 20, v2.y);
      }
      else
      {
        cr->move_to(v1);
        if (v2.y > v1.y)
          cr->line_to(v1.x, v1.y + 20);
        else
          cr->line_to(v1.x, v1.y - 20);
      }
    }
  }
}

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

void Connection::stroke_outline_gl(float offset)
{
  if (!_split || _segments.size() < 2)
    mdc::Line::stroke_outline_gl(offset);
  else 
  {
    // Draw a split connection (only show start and end).
    glTranslated(offset, offset, 0);
    glBegin(GL_LINES);

    Point v1, v2;

    // Begin of the connection.
    v1= _segments[0].pos;
    v2= _segments[1].pos;

    if (v2.y == v1.y)
    {
      glVertex2d(v1.x, v1.y);
      if (v2.x > v1.x)
        glVertex2d(v1.x + 20 + offset, v2.y);
      else
        glVertex2d(v1.x - 20, v2.y);
    }
    else
    {
      glVertex2d(v1.x, v1.y);
      if (v2.y > v1.y)
        glVertex2d(v1.x, v1.y + 20);
      else
        glVertex2d(v1.x, v1.y - 20);
    }

    // End of the connection.
    v1= _segments[_segments.size() - 1].pos;
    v2= _segments[_segments.size() - 2].pos;

    if (v2.y == v1.y)
    {
      glVertex2d(v1.x, v1.y);
      if (v2.x > v1.x)
        glVertex2d(v1.x + 20, v2.y);
      else
        glVertex2d(v1.x - 20, v2.y);
    }
    else
    {
      glVertex2d(v1.x, v1.y);
      if (v2.y > v1.y)
        glVertex2d(v1.x, v1.y + 20);
      else
        glVertex2d(v1.x, v1.y - 20);
    }

    glEnd();
  }
}

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

Point Connection::get_middle_caption_pos(const Size &size, CaptionPos pos)
{
  if (_segments.size() >= 2)
  {
    Point midpoint;
    Point p1, p2;

    if (_segments.size() == 2)
    {
      p1= _segments.front().pos;
      p2= _segments.back().pos;

      if (fabs(p1.x - p2.x) < fabs(p1.y - p2.y)) // vertical
      {
        midpoint.y= (p1.y+p2.y)/2 - size.height/2;        
        if (pos == Above && !_center_captions)
          midpoint.x= (p1.x+p2.x)/2 - size.width - 10;
        else if (pos == Below && !_center_captions)
          midpoint.x= (p1.x+p2.x)/2 + 10;
        else
          midpoint.x= (p1.x+p2.x)/2;
      }
      else
      {
        if (!_center_captions)
          midpoint.x= (p1.x+p2.x)/2 - size.width/2;
        else
          midpoint.x= (p1.x+p2.x)/2;

        if (pos == Above)
          midpoint.y= (p1.y+p2.y)/2 - size.height - 10;
        else if (pos == Below)
          midpoint.y= (p1.y+p2.y)/2 + 10;
        else
          midpoint.y= (p1.y+p2.y)/2;
      }
    }
    else if ((_segments.size() & 1) == 1)
    {
      size_t i= _segments.size()/2;
      std::vector<SegmentPoint>::const_iterator iter= _segments.begin();
      while (i-- > 0)
        ++iter;
      midpoint= iter->pos;

      //XXX
//      throw std::logic_error("not handled yet");
    }
    else
    {
      size_t i= _segments.size()/2;

      std::vector<SegmentPoint>::const_iterator iter= _segments.begin();
      
      while (--i > 0)
        ++iter;
      p1= iter->pos;
      ++iter;
      p2= iter->pos;
      midpoint= (p1 + p2);
      midpoint.x/=2;
      midpoint.y/=2;

      if (fabs(p1.x - p2.x) <= fabs(p1.y - p2.y) && !_center_captions) // verticalish line
      {
        if (fabs(p1.y - p2.y) < (size.height+2))
        {
          if (pos == Above)
            midpoint.y= min(p1.y, p2.y) - 10 - size.height;
          else if (pos == Below)
            midpoint.y= max(p1.y, p2.y) + 10;

          midpoint.x-= size.width/2;
        }
        else
        {
          if (pos == Above)
            midpoint.x-= size.width + 10;
          else if (pos == Below)
            midpoint.x+= 10;

          midpoint.y-= size.height/2;
        }
      }
      else // horizontal-ish line
      {
        if (fabs(p1.x - p2.x) < (size.width+2) && !_center_captions)
        {
          if (pos == Above)
            midpoint.x= min(p1.x, p2.x) - 10 - size.width;
          else if (pos == Below)
            midpoint.x= max(p1.x, p2.x) + 10;

          midpoint.y-= size.height/2;
        }
        else
        {
          if (pos == Above)
            midpoint.y-= size.height + 10;
          else if (pos == Below)
            midpoint.y+= 10;

          midpoint.x-= size.width/2;
        }
      }
    }

    return convert_point_to(midpoint, 0);
  }
  return get_position();
}


double Connection::get_middle_segment_angle()
{
  if (_segments.size() == 2)
    return mdc::angle_of_line(_segments.front().pos, _segments.back().pos);
  else if (_segments.size() > 2)
  {
    size_t i= _segments.size()/2;

    std::vector<SegmentPoint>::const_iterator iter= _segments.begin();
      
    while (--i > 0)
      ++iter;

    Point pos= iter->pos;
    ++iter;

    return mdc::angle_of_line(pos, iter->pos);
  }
  return 0.0;
}


Point Connection::get_start_caption_pos(const Size &size)
{
  Point p= _segments.front().pos;
  Point next_point= (++_segments.begin())->pos;
  
  if (fabs(next_point.x-p.x) <= fabs(next_point.y-p.y))
  {
    if (next_point.y < p.y)
      p.y-= size.height + 10;
    else
      p.y+= 10;
    p.x-= size.width + 5;
  }
  else
  {
    p.y-= size.height + 5;
    if (next_point.x < p.x)
      p.x-= size.width + 10;
    else
      p.x+= 10;
  }
  
  return convert_point_to(p, 0);
}


Point Connection::get_end_caption_pos(const Size &size)
{
  Point p= _segments.back().pos;
  Point next_point= (++_segments.rbegin())->pos;

  if (fabs(next_point.x-p.x) <= fabs(next_point.y-p.y))
  {
    if (next_point.y < p.y)
      p.y-= size.height + 10;
    else
      p.y+= 10;
    p.x-= size.width + 5;
  }
  else
  {
    p.y-= size.height + 5;
    if (next_point.x < p.x)
      p.x-= size.width + 10;
    else
      p.x+= 10;
  }
  
  return convert_point_to(p, 0);
}


void Connection::set_start_dashed(bool flag)
{
  _start_dashed= flag;
}


void Connection::set_end_dashed(bool flag)
{
  _end_dashed= flag;
}


void Connection::set_center_captions(bool flag)
{
  _center_captions= flag;
}

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

void Connection::render(mdc::CairoCtx *cr)
{
  if (_segments.empty())
    return;

  // sanity/debugging check
  //if (_content_cache)
  //  throw std::logic_error("connection figure is caching its contents");
    
  //mdc::Line::render(cr);

  draw_state(cr);

  cr->translate(get_position());

  cr->set_line_width(_line_width);
  cr->set_color(_pen_color);

  /*
  if (_start_dashed || _end_dashed)
  {
    stroke_outline(cr, 0, true);
    if (_start_dashed)
      set_line_pattern(cr, mdc::Dashed1Pattern);
    else
      set_line_pattern(cr, _line_pattern);
    cr->stroke();

    stroke_outline(cr, 0, false);
    if (_end_dashed)
      set_line_pattern(cr, mdc::Dashed1Pattern);
    else
      set_line_pattern(cr, _line_pattern);
    cr->stroke();
  }
  else*/
  {
    stroke_outline(cr);
    set_line_pattern(cr, _line_pattern);
    cr->stroke();
  }

  cr->set_dash(0, 0, 0);

  draw_line_ends(cr);

  cr->save();
  cr->translate(get_middle_caption_pos(Size(1,1), Middle) - get_position());

  double angle= get_middle_segment_angle();

  // for some reason the angle gets flipped in some cases
  if (angle == 90 || angle == 270)
    angle-= 180;

  cr->rotate(M_PI*angle/180.0);
  switch (_diamond)
  {
  case None:
    break;
  case Filled:
    cr->new_path();
    cr->move_to(0, -10);
    cr->line_to(10, 0);
    cr->line_to(0, 10);
    cr->line_to(-10, 0);
    cr->line_to(0, -10);
    cr->close_path();
    cr->set_color(Color::Black());
    cr->fill();
    break;
  case LeftEmpty:
    cr->new_path();
    cr->move_to(0, -10);
    cr->line_to(10, 0);
    cr->line_to(0, 10);
    cr->line_to(-10, 0);
    cr->line_to(0, -10);
    cr->close_path();
    cr->set_color(Color::White());
    cr->fill_preserve();
    cr->set_color(Color::Black());
    cr->stroke();
    cr->new_path();
    cr->move_to(0, -10);
    cr->line_to(10, 0);
    cr->line_to(0, 10);
    cr->line_to(0, -10);
    cr->close_path();
    cr->fill();
    break;
  case RightEmpty:
    cr->new_path();
    cr->move_to(0, -10);
    cr->line_to(10, 0);
    cr->line_to(0, 10);
    cr->line_to(-10, 0);
    cr->line_to(0, -10);
    cr->close_path();
    cr->set_color(Color::White());
    cr->fill_preserve();
    cr->set_color(Color::Black());
    cr->stroke();
    cr->new_path();
    cr->move_to(0, -10);
    cr->line_to(-10, 0);
    cr->line_to(0, 10);
    cr->line_to(0, -10);
    cr->close_path();
    cr->fill();
    break;
  case Empty:
    cr->new_path();
    cr->move_to(0, -10);
    cr->line_to(10, 0);
    cr->line_to(0, 10);
    cr->line_to(-10, 0);
    cr->line_to(0, -10);
    cr->close_path();
    cr->set_color(Color::White());
    cr->fill_preserve();
    cr->set_color(Color::Black());
    cr->stroke();
    break;
  }
  cr->restore();
}


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

void Connection::render_gl(mdc::CairoCtx *cr)
{
  if (_segments.empty())
    return;

  // sanity/debugging check
  if (_content_cache)
    throw std::logic_error("Connection figure is caching its contents in OpenGL mode.");
    
  draw_state_gl();

  glPushMatrix(); 
  glTranslated(get_position().x, get_position().y, 0); 
  glLineWidth(_line_width);
  glEnable(GL_LINE_SMOOTH);
  gl_setcolor(_pen_color);

  GLushort gl_pattern= get_gl_pattern(_line_pattern);
  if (gl_pattern == 0xFFFF)
    glDisable(GL_LINE_STIPPLE);
  else
  {
    glLineStipple(1, gl_pattern);
    glEnable(GL_LINE_STIPPLE);
  }
  stroke_outline_gl();

  glDisable(GL_LINE_STIPPLE);
  draw_line_ends_gl();

  Point effective_position= get_middle_caption_pos(Size(1, 1), Middle) - get_position();
  glTranslated(effective_position.x, effective_position.y, 0);

  double angle= get_middle_segment_angle();

  // For some reason the angle gets flipped in some cases.
  if (angle == 90 || angle == 270)
    angle-= 180;

  glRotated(angle, 0, 0, 1);
  switch (_diamond)
  {
  case None:
    break;
  case Filled:
    {
      Point vertices[]=
      {
        Point(0, -10),
        Point(10, 0),
        Point(0, 10),
        Point(-10, 0),
        Point(0, -10),
      };
      gl_setcolor(Color::Black());
      gl_polygon(vertices, 5, true);
      break;
    }

  case LeftEmpty:
    {
      Point vertices[]=
      {
        Point(0, -10),
        Point(10, 0),
        Point(0, 10),
        Point(-10, 0),
        Point(0, -10),
      };
      gl_setcolor(Color::Black());
      gl_polygon(vertices, 5, Color::Black(), Color::White());
    }

    {
      Point vertices[]=
      {
        Point(0, -10),
        Point(10, 0),
        Point(0, 10),
        Point(0, -10),
      };
      gl_polygon(vertices, 4, Color::Black(), Color::White());
    }
    break;

  case RightEmpty:
    {
      Point vertices[]=
      {
        Point(0, -10),
        Point(10, 0),
        Point(0, 10),
        Point(-10, 0),
        Point(0, -10),
      };
      gl_polygon(vertices, 5, Color::Black(), Color::White());
    }

    {
      Point vertices[]=
      {
        Point(0, -10),
        Point(10, 0),
        Point(0, 10),
        Point(0, -10),
      };
      gl_setcolor(Color::Black());
      gl_polygon(vertices, 4, true);
    }

    break;
  case Empty:
    {
      Point vertices[]=
      {
        Point(0, -10),
        Point(10, 0),
        Point(0, 10),
        Point(-10, 0),
        Point(0, -10),
      };
      gl_polygon(vertices, 5, Color::Black(), Color::White());
    }
    break;
  }

  glPopMatrix();
}

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

bool Connection::on_click(mdc::CanvasItem *target, const Point &point, mdc::MouseButton button,
                          mdc::EventState state)
{
  if (!_hub->figure_click(_represented_object, target, point, button, state))
    return super::on_click(target, point, button, state);
  return false;
}

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

bool Connection::on_double_click(mdc::CanvasItem *target, const Point &point, mdc::MouseButton button,
                          mdc::EventState state)
{
  if (!_hub->figure_double_click(_represented_object, target, point, button, state))
    return super::on_double_click(target, point, button, state);
  return false;
}

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

bool Connection::on_enter(mdc::CanvasItem *target, const Point &point)
{
  if (!_hub->figure_enter(_represented_object, target, point))
    return super::on_enter(target, point);
  return false;
}


bool Connection::on_leave(mdc::CanvasItem *target, const Point &point)
{
  if (!_hub->figure_leave(_represented_object, target, point))
    return super::on_leave(target, point);
  return false;
}


bool Connection::on_button_press(mdc::CanvasItem *target, const Point &point, mdc::MouseButton button, mdc::EventState state)
{
  if (!_hub->figure_button_press(_represented_object, target, point, button, state))
    return super::on_button_press(target, point, button, state);
  return false;
}


bool Connection::on_button_release(mdc::CanvasItem *target, const Point &point, mdc::MouseButton button, mdc::EventState state)
{
  if (!_hub->figure_button_release(_represented_object, target, point, button, state))
    return super::on_button_release(target, point, button, state);
  return false;
}



void Connection::mark_crossings(mdc::Line *line)
{
  ConnectionLineLayouter *cLineLayouter = dynamic_cast<ConnectionLineLayouter*>(_layouter);
  if (cLineLayouter != NULL &&
      cLineLayouter->get_type() == ConnectionLineLayouter::ZLine)
    return;

  if (_split || !_visible)
    return;

  Connection *conn= dynamic_cast<Connection*>(line);
  if (conn != NULL && conn->_split)
    return;

  super::mark_crossings(line);
}

