
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2016 Matthias Koefferlein

  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; either version 2 of the License, or
  (at your option) any later version.

  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 <iostream>
#include <fstream>
#include <vector>

#include "QtCore/QTimer"
#include "QtGui/QSpinBox"
#include "QtGui/QPainter"
#include "QtGui/QPaintEvent"
#include "QtGui/QComboBox"
#include "QtGui/QDialog"
#include "QtGui/QImageWriter"
#include "QtGui/QInputDialog"
#include "QtGui/QKeyEvent"
#include "QtGui/QMessageBox"

#include "tlInternational.h"
#include "tlExpression.h"
#include "tlTimer.h"
#include "tlString.h"
#include "tlLog.h"
#include "tlAssert.h"
#include "layApplication.h"
#include "layViewOp.h"
#include "layViewObject.h"
#include "layLayoutViewConfigPages.h"
#include "layConfig.h"
#include "layConverters.h"
#include "layGridNet.h"
#include "layMove.h"
#include "layDialogs.h"
#include "layZoomBox.h"
#include "layMouseTracker.h"
#include "layTipDialog.h"
#include "layEditable.h"
#include "layFixedFont.h"
#include "laySelector.h"
#include "layLayoutCanvas.h"
#include "layLayerControlPanel.h"
#include "layHierarchyControlPanel.h"
#include "layMainWindow.h"
#include "layBrowser.h"
#include "layRedrawThread.h"
#include "layRedrawThreadWorker.h"
#include "layParsedLayerSource.h"
#include "layBookmarkManagementForm.h"
#include "dbLayout.h"
#include "dbLayoutUtils.h"
#include "dbRecursiveShapeIterator.h"
#include "dbManager.h"
#include "dbEdgeProcessor.h"
#include "rdb.h"
#include "rdbMarkerBrowserDialog.h"
#include "antService.h"
#include "antPlugin.h"
#include "imgPlugin.h"
#include "tlXMLParser.h"
#include "gsi.h"

#include "FlattenInstOptionsDialog.h"

#include <limits>

namespace lay
{

//  factor for "zoom in & out"
const double zoom_factor = 0.7;

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

struct OpHideShowCell 
  : public db::Op
{
  OpHideShowCell (lay::CellView::cell_index_type ci, int cv_index, bool show)
    : m_cell_index (ci), m_cellview_index (cv_index), m_show (show)
  { }

  lay::CellView::cell_index_type m_cell_index;
  int m_cellview_index;
  bool m_show;
};

struct OpSetDitherPattern
  : public db::Op 
{
  OpSetDitherPattern (const lay::DitherPattern &o, const lay::DitherPattern &n)
    : db::Op (), m_old (o), m_new (n)
  { 
    //  nothing yet.
  }

  lay::DitherPattern m_old, m_new;
};

struct OpSetLayerProps 
  : public db::Op
{
  OpSetLayerProps (unsigned int li, unsigned int i, const lay::LayerProperties &o, const lay::LayerProperties &n)
    : m_list_index (li), m_index (i), m_old (o), m_new (n)
  { 
    //  .. nothing yet ..
  }
  
  unsigned int m_list_index;
  size_t m_index;
  lay::LayerProperties m_old, m_new;
};

struct OpSetLayerPropsNode 
  : public db::Op
{
  OpSetLayerPropsNode (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &o, const lay::LayerPropertiesNode &n)
    : m_list_index (li), m_index (i), m_old (o), m_new (n)
  { 
    //  .. nothing yet ..
  }
  
  unsigned int m_list_index;
  size_t m_index;
  lay::LayerPropertiesNode m_old, m_new;
};

struct OpDeleteLayerList 
  : public db::Op
{
  OpDeleteLayerList (unsigned int li, const lay::LayerPropertiesList &o)
    : m_list_index (li), m_old (o)
  { 
    //  .. nothing yet ..
  }
  
  unsigned int m_list_index;
  lay::LayerPropertiesList m_old;
};

struct OpInsertLayerList 
  : public db::Op
{
  OpInsertLayerList (unsigned int li, const lay::LayerPropertiesList &n)
    : m_list_index (li), m_new (n)
  { 
    //  .. nothing yet ..
  }
  
  unsigned int m_list_index;
  lay::LayerPropertiesList m_new;
};

struct OpRenameProps 
  : public db::Op
{
  OpRenameProps (unsigned int li, const std::string &old_name, const std::string &new_name)
    : m_list_index (li), m_old (old_name), m_new (new_name)
  { 
    //  .. nothing yet ..
  }
  
  unsigned int m_list_index;
  std::string m_old, m_new;
};

struct OpSetAllProps 
  : public db::Op
{
  OpSetAllProps (unsigned int li, const lay::LayerPropertiesList &o, const lay::LayerPropertiesList &n)
    : m_list_index (li), m_old (o), m_new (n)
  { 
    //  .. nothing yet ..
  }
  
  unsigned int m_list_index;
  lay::LayerPropertiesList m_old, m_new;
};

struct OpLayerList
  : public db::Op
{
  enum Mode { Delete, Insert };

  OpLayerList (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &n, Mode m)
    : m_list_index (li), m_index (i), m_mode (m), m_node (n)
  { }

  unsigned int m_list_index;
  size_t m_index;
  Mode m_mode;
  lay::LayerPropertiesNode m_node;
};

struct OpInsertLayerProps 
  : public OpLayerList
{
  OpInsertLayerProps (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &n)
    : OpLayerList (li, i, n, Insert)
  { 
    // .. nothing yet ..
  }
};

struct OpDeleteLayerProps 
  : public OpLayerList
{
  OpDeleteLayerProps (unsigned int li, unsigned int i, const lay::LayerPropertiesNode &n)
    : OpLayerList (li, i, n, Delete)
  { 
    // .. nothing yet ..
  }
};

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

const int timer_interval = 500;

LayoutView::LayoutView (db::Manager *manager, lay::MainWindow *main_window, QWidget *parent, const char *name, unsigned int options)
  : QFrame (parent), 
    lay::Plugin (main_window), 
    m_options (options),
    m_annotation_shapes (manager),
    m_hier_changed_observer (this, &LayoutView::signal_hier_changed),
    m_bboxes_changed_observer (this, &LayoutView::signal_bboxes_changed),
    m_prop_ids_changed_observer (this, &LayoutView::signal_prop_ids_changed),
    m_layer_properties_changed_observer (this, &LayoutView::signal_layer_properties_changed),
    m_cell_name_changed_observer (this, &LayoutView::signal_cell_name_changed),
    m_annotations_changed_observer (this, &LayoutView::signal_annotations_changed),
    m_plugin_enabled_changed_observer (this, &LayoutView::signal_plugin_enabled_changed),
    m_apply_technology_observer (this, &LayoutView::signal_apply_technology),
    dm_prop_changed (this, &LayoutView::do_prop_changed)
{
  setObjectName (QString::fromAscii (name));
  init (manager, main_window, parent);
}

LayoutView::LayoutView (lay::LayoutView *source, db::Manager *manager, lay::MainWindow *main_window, QWidget *parent, const char *name, unsigned int options)
  : QFrame (parent), 
    lay::Plugin (main_window),
    m_options (options),
    m_annotation_shapes (manager),
    m_hier_changed_observer (this, &LayoutView::signal_hier_changed),
    m_bboxes_changed_observer (this, &LayoutView::signal_bboxes_changed),
    m_prop_ids_changed_observer (this, &LayoutView::signal_prop_ids_changed),
    m_layer_properties_changed_observer (this, &LayoutView::signal_layer_properties_changed),
    m_cell_name_changed_observer (this, &LayoutView::signal_cell_name_changed),
    m_annotations_changed_observer (this, &LayoutView::signal_annotations_changed),
    m_plugin_enabled_changed_observer (this, &LayoutView::signal_plugin_enabled_changed),
    m_apply_technology_observer (this, &LayoutView::signal_apply_technology),
    dm_prop_changed (this, &LayoutView::do_prop_changed)
{
  setObjectName (QString::fromAscii (name));

  m_annotation_shapes = source->m_annotation_shapes;

  init (manager, main_window, parent);

  //  set the handle reference and clear all cell related stuff 
  m_cellviews = source->cellview_vector ();
  m_hidden_cells = source->m_hidden_cells;

  //  clear the history, store path and zoom box
  m_display_states.clear ();
  m_display_state_ptr = 0;
  m_synchronous = source->synchronous ();
  m_drawing_workers = source->drawing_workers ();

  //  duplicate the layer properties
  for (size_t i = 0; i < source->m_layer_properties_lists.size (); ++i) {
    if (i >= m_layer_properties_lists.size ()) {
      m_layer_properties_lists.push_back (new lay::LayerPropertiesList (*source->m_layer_properties_lists [i]));
    } else {
      *m_layer_properties_lists [i] = *source->m_layer_properties_lists [i];
    }
    m_layer_properties_lists [i]->attach_view (this);
  }

  if (! m_layer_properties_lists.empty ()) {
    mp_canvas->set_dither_pattern (m_layer_properties_lists [0]->dither_pattern ()); 
  }

  bookmarks (source->bookmarks ());

  set_active_cellview_index (source->active_cellview_index ());

  //  copy the title
  m_title = source->m_title;

  m_layer_list_observers.signal_observers (3);
  m_layer_list_observers.signal_observers ();

  finish_cellviews_changed ();
}

bool 
LayoutView::eventFilter(QObject *obj, QEvent *event)
{
  if (obj == mp_min_hier_spbx || obj == mp_max_hier_spbx) {

    //  Makes the min/max spin boxes accept only numeric and some control keys ..
    QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);
    if (keyEvent && 
        keyEvent->key () != Qt::Key_Home &&
        keyEvent->key () != Qt::Key_End &&
        keyEvent->key () != Qt::Key_Delete &&
        keyEvent->key () != Qt::Key_Backspace &&
        (keyEvent->key () < Qt::Key_0 || keyEvent->key () > Qt::Key_9)) {
      return true;
    } else {
      return false;
    }

  } else {
    return QFrame::eventFilter (obj, event);
  }
}

void
LayoutView::init (db::Manager *mgr, lay::MainWindow *main_window, QWidget *parent)
{
  manager (mgr);

  m_annotation_shapes.manager (mgr);

  m_visibility_changed = false;

  m_synchronous = false;
  m_drawing_workers = 1;
  mp_control_panel = 0;
  mp_control_frame = 0;
  mp_hierarchy_panel = 0;
  mp_hierarchy_frame = 0;
  mp_min_hier_spbx = 0;
  mp_max_hier_spbx = 0;
  mp_coord_tracker = main_window;
  m_from_level = 0;
  m_pan_distance = 0.15;
  m_wheel_mode = 0;
  m_paste_display_mode = 2;
  m_guiding_shape_visible = true;
  m_guiding_shape_line_width = 1;
  m_guiding_shape_color = QColor ();
  m_guiding_shape_vertex_size = 5;
  m_to_level = 0;
  m_ctx_dimming = 50;
  m_ctx_hollow = false;
  m_child_ctx_dimming = 50;
  m_child_ctx_hollow = false;
  m_child_ctx_enabled = false;
  m_abstract_mode_width = 10.0;
  m_abstract_mode_enabled = false;
  m_box_text_transform = true;
  m_box_font = 0;
  m_min_size_for_label = 16;
  m_cell_box_visible = true;
  m_text_visible = true;
  m_default_font_size = lay::FixedFont::default_font_size ();
  m_text_lazy_rendering = true;
  m_bitmap_caching = true;
  m_show_properties = false;
  m_apply_text_trans = true;
  m_default_text_size = 0.1;
  m_text_font = 0;
  m_no_stipples = false;
  m_stipple_offset = true;
  m_fit_new_cell = true;
  m_full_hier_new_cell = true;
  m_clear_ruler_new_cell = false;
  m_dbu_coordinates = false;
  m_absolute_coordinates = false;
  m_drop_small_cells = false;
  m_drop_small_cells_value = 10;
  m_drop_small_cells_cond = DSC_Max;
  m_draw_array_border_instances = false;
  m_activated = true;
  m_animated = false;
  m_phase = 0;
  m_palette = lay::ColorPalette::default_palette ();
  m_stipple_palette = lay::StipplePalette::default_palette ();
  m_display_state_ptr = 0;
  m_mode = std::numeric_limits<int>::min (); // nothing selected yet.
  mp_tracker = 0;
  mp_zoom_service = 0;
  mp_selection_service = 0;
  mp_move_service = 0;
  m_marker_line_width = 0;
  m_marker_vertex_size = 0;
  m_marker_dither_pattern = 1;
  m_marker_halo = true;
  m_transient_selection_mode = true;
  m_sel_inside_pcells = false;
  m_move_to_origin_mode_x = 0;
  m_move_to_origin_mode_y = 0;
  m_align_cell_origin_mode_x = -1;
  m_align_cell_origin_mode_y = -1;
  m_align_cell_origin_visible_layers = false;
  m_align_cell_adjust_parents = true;
  m_del_cell_mode = 0;
  m_layer_hier_mode = 0;
  m_add_other_layers = false;
  m_always_show_source = false;
  m_always_show_ld = true;
  m_always_show_layout_index = false;
  m_duplicate_hier_mode = 2;
  m_clear_before = true;
  m_copy_cva = -1;
  m_copy_cvr = -1;
  m_copy_layera = -1;
  m_copy_layerr = -1;

  m_layer_properties_lists.push_back (new LayerPropertiesList ());
  m_layer_properties_lists.back ()->attach_view (this);
  m_current_layer_list = 0;

  QVBoxLayout *vbl = new QVBoxLayout (this);
  vbl->setMargin (0);
  vbl->setSpacing (0);

  if ((m_options & LV_NoHierarchyPanel) == 0 && (m_options & LV_Naked) == 0) {

    QFrame *hierarchy_frame = new QFrame (0);
    hierarchy_frame->setObjectName (QString::fromAscii ("left"));
    mp_hierarchy_frame = hierarchy_frame;
    QVBoxLayout *left_frame_ly = new QVBoxLayout (hierarchy_frame);
    left_frame_ly->setMargin (0);
    left_frame_ly->setSpacing (0);

    mp_hierarchy_panel = new lay::HierarchyControlPanel (this, hierarchy_frame, "hcp");
    left_frame_ly->addWidget (mp_hierarchy_panel, 1 /*stretch*/);

    connect (mp_hierarchy_panel, SIGNAL (cell_selected (cell_path_type, int)), this, SLOT (select_cell_dispatch (cell_path_type, int)));
    connect (mp_hierarchy_panel, SIGNAL (active_cellview_changed (int)), this, SLOT (active_cellview_changed (int)));

    QFrame *levels_frame = new QFrame (hierarchy_frame);
    levels_frame->setObjectName (QString::fromAscii ("lvl_frame"));
    left_frame_ly->addWidget (levels_frame);
    QHBoxLayout *levels_frame_ly = new QHBoxLayout (levels_frame);
    levels_frame_ly->setMargin (1);
    QLabel *level_l1 = new QLabel (tl::to_qstring (" " + tl::to_string (QObject::tr ("Levels"))), levels_frame);
    levels_frame_ly->addWidget (level_l1);
    mp_min_hier_spbx = new QSpinBox (levels_frame);
    mp_min_hier_spbx->setObjectName (QString::fromAscii ("min_lvl"));
    levels_frame_ly->addWidget (mp_min_hier_spbx);
    QLabel *level_l2 = new QLabel (QString::fromAscii (".."), levels_frame);
    levels_frame_ly->addWidget (level_l2);
    mp_max_hier_spbx = new QSpinBox (levels_frame);
    mp_max_hier_spbx->setObjectName (QString::fromAscii ("max_lvl"));
    levels_frame_ly->addWidget (mp_max_hier_spbx);

    mp_min_hier_spbx->installEventFilter (this);
    mp_max_hier_spbx->installEventFilter (this);

    mp_min_hier_spbx->setMaximum (0);
    mp_min_hier_spbx->setMinimum (-1000);
    mp_min_hier_spbx->setValue (0);
    mp_max_hier_spbx->setMaximum (999);
    mp_max_hier_spbx->setValue (0);
    mp_max_hier_spbx->setMinimum (-1000);

    connect (mp_min_hier_spbx, SIGNAL (valueChanged (int)), this, SLOT (min_hier_changed (int)));
    connect (mp_max_hier_spbx, SIGNAL (valueChanged (int)), this, SLOT (max_hier_changed (int)));

  }

  mp_canvas = new lay::LayoutCanvas (this, this);
  vbl->addWidget (mp_canvas);
  connect (mp_canvas, SIGNAL (left_arrow_key_pressed ()), this, SLOT (pan_left ()));
  connect (mp_canvas, SIGNAL (up_arrow_key_pressed ()), this, SLOT (pan_up ()));
  connect (mp_canvas, SIGNAL (right_arrow_key_pressed ()), this, SLOT (pan_right ()));
  connect (mp_canvas, SIGNAL (down_arrow_key_pressed ()), this, SLOT (pan_down ()));

  //  occupy services and editables:
  //  these services get deleted by the canvas destructor automatically:
  if ((m_options & LV_NoTracker) == 0) {
    mp_tracker = new lay::MouseTracker (this);
  }
  if ((m_options & LV_NoZoom) == 0) {
    mp_zoom_service = new lay::ZoomService (this);
  }
  if ((m_options & LV_NoSelection) == 0) {
    mp_selection_service = new lay::SelectionService (this);
  }
  if ((m_options & LV_NoMove) == 0) {
    mp_move_service = new lay::MoveService (this);
  }

  if ((m_options & LV_NoLayers) == 0 && (m_options & LV_Naked) == 0) {

    mp_control_panel = new lay::LayerControlPanel (this, manager (), 0, "lcp");
    mp_control_frame = mp_control_panel;

    connect (mp_control_panel, SIGNAL (tab_changed ()), this, SLOT (layer_tab_changed ()));
    connect (mp_control_panel, SIGNAL (order_changed ()), this, SLOT (layer_order_changed ()));
    /*
    connect (mp_control_panel, SIGNAL (marked_changed ()), this, SLOT (prop_changed ()));
    connect (mp_control_panel, SIGNAL (width_changed ()), this, SLOT (prop_changed ()));
    connect (mp_control_panel, SIGNAL (animation_changed ()), this, SLOT (prop_changed ()));
    connect (mp_control_panel, SIGNAL (visibility_changed ()), this, SLOT (visibility_changed ()));
    connect (mp_control_panel, SIGNAL (transparency_changed ()), this, SLOT (prop_changed ()));
    connect (mp_control_panel, SIGNAL (stipple_changed ()), this, SLOT (prop_changed ()));
    connect (mp_control_panel, SIGNAL (color_changed ()), this, SLOT (prop_changed ()));
    */

  }
  
  mp_timer = new QTimer (this);
  connect (mp_timer, SIGNAL (timeout ()), this, SLOT (timer ()));
  mp_timer->start (timer_interval);

  create_plugins (main_window);

  m_new_layer_props.layer = 1;
  m_new_layer_props.datatype = 0;

  config_setup ();
}

LayoutView::~LayoutView ()
{
  //  detach all observers
  //  This is to prevent signals to partially destroyed observers that own a LayoutView
  m_layer_list_observers.remove_observers ();
  m_layer_list_deleted_observers.remove_observers ();
  m_layer_list_inserted_observers.remove_observers ();
  m_current_layer_list_changed_observers.remove_observers ();
  m_cell_visibility_observers.remove_observers ();
  m_cellviews_about_to_change_observers.remove_observers ();
  m_cellview_about_to_change_observers.remove_observers ();
  m_cellviews_changed_observers.remove_observers ();
  m_cellview_changed_observers.remove_observers ();
  m_rdb_list_changed_observers.remove_observers ();
  m_file_open_observers.remove_observers ();
  m_hier_changed_observers.remove_observers ();
  m_geom_changed_observers.remove_observers ();
  m_annotations_changed_observers.remove_observers ();

  //  detach ourselves from any observed objects to prevent signals while destroying
  m_hier_changed_observer.detach_all ();
  m_bboxes_changed_observer.detach_all ();
  m_prop_ids_changed_observer.detach_all ();
  m_layer_properties_changed_observer.detach_all ();
  m_cell_name_changed_observer.detach_all ();
  m_annotations_changed_observer.detach_all ();

  //  remove all rdb's
  while (num_rdbs () > 0) {
    remove_rdb (0);
  }

  //  delete layer lists
  std::vector<LayerPropertiesList *> layer_properties_lists;
  layer_properties_lists.swap (m_layer_properties_lists);
  for (std::vector<LayerPropertiesList *>::iterator l = layer_properties_lists.begin (); l !=  layer_properties_lists.end (); ++l) {
    if (*l) {
      delete *l;
    }
  }

  //  delete all plugins
  std::vector<lay::Plugin *> plugins;
  plugins.swap (mp_plugins);
  for (std::vector<lay::Plugin *>::iterator p = plugins.begin (); p != plugins.end (); ++p) {
    delete *p;
  }

  //  detach from the manager, so we can safely delete the manager 
  manager (0);

  stop ();

  //  because LayoutView and LayoutCanvas both control lifetimes of
  //  ruler objects for example, it is safer to explictly delete the
  //  LayoutCanvas object here:
  delete mp_canvas;
  mp_canvas = 0;

  if (mp_control_frame) {
    delete mp_control_frame;
  }
  mp_control_panel = 0;
  mp_control_frame = 0;

  if (mp_hierarchy_frame) {
    delete mp_hierarchy_frame;
  }
  mp_hierarchy_frame = 0;
  mp_hierarchy_panel = 0;
}

lay::Plugin *LayoutView::create_plugin (lay::MainWindow *main_window, const lay::PluginDeclaration *cls)
{
  lay::Plugin *p = cls->create_plugin (manager (), main_window, this);
  if (p) {

    //  unhook the plugin from the script side if created there (prevent GC from destroying it)
    p->keep ();

    mp_plugins.push_back (p);
    p->set_plugin_declaration (cls);
  
    //  enable editable functionality
    if (p->editable_interface ()) {
      enable (p->editable_interface (), cls->editable_enabled ());
    }

    //  TODO: get rid of the const_cast hack
    const_cast<lay::PluginDeclaration *> (cls)->add_editable_enabled_changed_observer (m_plugin_enabled_changed_observer);

  }
  return p;
}

void LayoutView::create_plugins (lay::MainWindow *main_window, const lay::PluginDeclaration *except_this)
{
  for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
    delete *p;
  }
  mp_plugins.clear ();

  //  create the plugins
  if ((m_options & LV_NoPlugins) == 0) {

    for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
      if (&*cls != except_this) {
        create_plugin (main_window, &*cls);
      }
    }

  } else {

    if ((m_options & LV_NoGrid) == 0) {
      create_plugin<lay::GridNetPluginDeclaration> (main_window);
    }

    create_plugin<img::PluginDeclaration> (main_window);
    create_plugin<ant::PluginDeclaration> (main_window);

  }

  mode (default_mode ());
}

void 
LayoutView::init_menu (lay::AbstractMenu &menu)
{
  lay::LayerControlPanel::init_menu (menu);
  lay::HierarchyControlPanel::init_menu (menu);
}

void
LayoutView::set_drawing_workers (int workers)
{
  m_drawing_workers = std::max (0, std::min (100, workers));
}

void
LayoutView::set_synchronous (bool s)
{
  m_synchronous = s;
}

std::string
LayoutView::title () const
{
  if (! m_title.empty ()) {
    return m_title;
  } else if (cellviews () == 0) {
    return tl::to_string (QObject::tr ("<empty>"));
  } else {
    std::string t (cellview (0)->name ());
    if (cellviews () > 1) {
      t += " ...";
    }
    return t;
  }
}

void 
LayoutView::set_title (const std::string &t)
{
  if (m_title != t) {
    m_title = t;
    emit title_changed ();
  }
}

void 
LayoutView::reset_title ()
{
  if (! m_title.empty ()) {
    m_title = "";
    emit title_changed ();
  }
}

bool 
LayoutView::configure (const std::string &name, const std::string &value)
{
  if (mp_move_service && mp_move_service->configure (name, value)) {
    return true;
  }

  if (name == cfg_default_lyp_file) {

    m_def_lyp_file = value;
    return false; // not taken - let others set it too.

  } else if (name == cfg_default_add_other_layers) {

    tl::from_string (value, m_add_other_layers);
    return false; // not taken - let others set it too.

  } else if (name == cfg_layers_always_show_source) {

    bool a = false;
    tl::from_string (value, a);
    if (a != m_always_show_source) {
      m_always_show_source = a;
      m_layer_list_observers.signal_observers (4);
      m_layer_list_observers.signal_observers ();
    }

    return true;

  } else if (name == cfg_layers_always_show_ld) {

    tl::from_string (value, m_always_show_ld);
    update_content ();
    return true;

  } else if (name == cfg_layers_always_show_layout_index) {

    tl::from_string (value, m_always_show_layout_index);
    update_content ();
    return true;

  } else if (name == cfg_flat_cell_list) {

    bool f;
    tl::from_string (value, f);
    if (mp_hierarchy_panel) {
      mp_hierarchy_panel->set_flat (f);
    }
    return true;

  } else if (name == cfg_split_cell_list) {

    bool f;
    tl::from_string (value, f);
    if (mp_hierarchy_panel) {
      mp_hierarchy_panel->set_split_mode (f);
    }
    return true;

  } else if (name == cfg_cell_list_sorting) {

    if (mp_hierarchy_panel) {
      if (value == "by-name") {
        mp_hierarchy_panel->set_sorting (CellTreeModel::ByName);
      } else if (value == "by-area") {
        mp_hierarchy_panel->set_sorting (CellTreeModel::ByArea);
      } else if (value == "by-area-reverse") {
        mp_hierarchy_panel->set_sorting (CellTreeModel::ByAreaReverse);
      }
    }
    return true;

  } else if (name == cfg_hide_empty_layers) {

    bool f;
    tl::from_string (value, f);
    if (mp_control_panel) {
      mp_control_panel->set_hide_empty_layers (f);
    }
    return true;

  } else if (name == cfg_test_shapes_in_view) {

    bool f;
    tl::from_string (value, f);
    if (mp_control_panel) {
      mp_control_panel->set_test_shapes_in_view (f);
    }
    return true;

  } else if (name == cfg_background_color) {

    QColor color;
    ColorConverter ().from_string (value, color);
    background_color (color);
    //  do not take - let others receive the background color events as well
    return false;

  } else if (name == cfg_default_font_size) {

    int df = 0;
    tl::from_string (value, df);
    if (m_default_font_size != df) {
      //  keep a shadow state to correctly issue the redraw call
      m_default_font_size = df;
      lay::FixedFont::set_default_font_size (df);
      redraw ();
    }
    //  do not take - let others have the event for the redraw call
    return false;

  } else if (name == cfg_bitmap_oversampling) {

    int os = 1;
    tl::from_string (value, os);
    mp_canvas->set_oversampling (os);
    return true;

  } else if (name == cfg_image_cache_size) {

    int sz = 0;
    tl::from_string (value, sz);
    mp_canvas->set_image_cache_size (size_t (sz));
    return true;

  } else if (name == cfg_global_trans) {

    tl::Extractor ex (value.c_str ());
    try {
      db::DCplxTrans t;
      ex.read (t);
      set_global_trans (t);
    } catch (...) { }
    return true;

  } else if (name == cfg_ctx_color) {

    QColor color;
    ColorConverter ().from_string (value, color);
    ctx_color (color);
    return true;

  } else if (name == cfg_ctx_dimming) {

    int n;
    tl::from_string (value, n);
    ctx_dimming (n);
    return true;

  } else if (name == cfg_ctx_hollow) {

    bool h;
    tl::from_string (value, h);
    ctx_hollow (h);
    return true;

  } else if (name == cfg_child_ctx_color) {

    QColor color;
    ColorConverter ().from_string (value, color);
    child_ctx_color (color);
    return true;

  } else if (name == cfg_child_ctx_dimming) {

    int n;
    tl::from_string (value, n);
    child_ctx_dimming (n);
    return true;

  } else if (name == cfg_child_ctx_hollow) {

    bool h;
    tl::from_string (value, h);
    child_ctx_hollow (h);
    return true;

  } else if (name == cfg_child_ctx_enabled) {

    bool h;
    tl::from_string (value, h);
    child_ctx_enabled (h);
    return true;

  } else if (name == cfg_abstract_mode_enabled) {

    bool e;
    tl::from_string (value, e);
    abstract_mode_enabled (e);
    return true;

  } else if (name == cfg_abstract_mode_width) {

    double w;
    tl::from_string (value, w);
    abstract_mode_width (w);
    return true;

  } else if (name == cfg_min_inst_label_size) {

    int n;
    tl::from_string (value, n);
    min_inst_label_size (n);
    return true;

  } else if (name == cfg_cell_box_text_font) {

    int n;
    tl::from_string (value, n);
    cell_box_text_font (n);
    return true;

  } else if (name == cfg_cell_box_text_transform) {

    bool flag;
    tl::from_string (value, flag);
    cell_box_text_transform (flag);
    return true;

  } else if (name == cfg_cell_box_visible) {

    bool flag;
    tl::from_string (value, flag);
    cell_box_visible (flag);
    return true;

  } else if (name == cfg_cell_box_color) {

    QColor color;
    ColorConverter ().from_string (value, color);
    cell_box_color (color);
    return true;

  } else if (name == cfg_text_color) {

    QColor color;
    ColorConverter ().from_string (value, color);
    text_color (color);
    return true;

  } else if (name == cfg_text_visible) {

    bool flag;
    tl::from_string (value, flag);
    text_visible (flag);
    return true;

  } else if (name == cfg_bitmap_caching) {

    bool flag;
    tl::from_string (value, flag);
    bitmap_caching (flag);
    return true;

  } else if (name == cfg_text_lazy_rendering) {

    bool flag;
    tl::from_string (value, flag);
    text_lazy_rendering (flag);
    return true;

  } else if (name == cfg_show_properties) {

    bool flag;
    tl::from_string (value, flag);
    show_properties_as_text (flag);
    return true;

  } else if (name == cfg_apply_text_trans) {

    bool flag;
    tl::from_string (value, flag);
    apply_text_trans (flag);
    return true;

  } else if (name == cfg_no_stipple) {

    bool flag;
    tl::from_string (value, flag);
    no_stipples (flag);
    return true;

  } else if (name == cfg_stipple_offset) {

    bool flag;
    tl::from_string (value, flag);
    offset_stipples (flag);
    return true;

  } else if (name == cfg_default_text_size) {

    double sz;
    tl::from_string (value, sz);
    default_text_size (sz);
    return true;

  } else if (name == cfg_text_font) {

    int n;
    tl::from_string (value, n);
    text_font (n);
    return true;

  } else if (name == cfg_full_hier_new_cell) {

    bool flag;
    tl::from_string (value, flag);
    full_hier_new_cell (flag);
    return true;

  } else if (name == cfg_fit_new_cell) {

    bool flag;
    tl::from_string (value, flag);
    fit_new_cell (flag);
    return true;

  } else if (name == cfg_clear_ruler_new_cell) {

    bool flag;
    tl::from_string (value, flag);
    clear_ruler_new_cell (flag);
    return true;

  } else if (name == cfg_abs_units) {

    bool flag;
    tl::from_string (value, flag);
    absolute_coordinates (flag);
    return true;

  } else if (name == cfg_guiding_shape_visible) {

    bool v = false;
    tl::from_string (value, v);
    guiding_shapes_visible (v);
    return true;

  } else if (name == cfg_guiding_shape_line_width) {

    int v = 0;
    tl::from_string (value, v);
    guiding_shapes_line_width (v);
    return true;

  } else if (name == cfg_guiding_shape_color) {

    QColor color;
    ColorConverter ().from_string (value, color);
    guiding_shapes_color (color);
    return true;

  } else if (name == cfg_guiding_shape_vertex_size) {

    int v = 0;
    tl::from_string (value, v);
    guiding_shapes_vertex_size (v);
    return true;

  } else if (name == cfg_paste_display_mode) {

    tl::from_string (value, m_paste_display_mode);
    return true;

  } else if (name == cfg_mouse_wheel_mode) {

    tl::from_string (value, m_wheel_mode);
    return true;

  } else if (name == cfg_pan_distance) {

    double pd;
    tl::from_string (value, pd);
    pan_distance (pd);
    return true;

  } else if (name == cfg_drawing_workers) {

    int workers;
    tl::from_string (value, workers);
    set_drawing_workers (workers);
    return true;

  } else if (name == cfg_drop_small_cells) {

    bool flag;
    tl::from_string (value, flag);
    drop_small_cells (flag);
    return true;

  } else if (name == cfg_drop_small_cells_cond) {

    unsigned int n;
    tl::from_string (value, n);
    drop_small_cells_cond (drop_small_cells_cond_type (n));
    return true;

  } else if (name == cfg_drop_small_cells_value) {

    unsigned int n;
    tl::from_string (value, n);
    drop_small_cells_value (n);
    return true;

  } else if (name == cfg_array_border_instances) {

    bool f;
    tl::from_string (value, f);
    draw_array_border_instances (f);
    return true;

  } else if (name == cfg_dbu_units) {

    bool flag;
    tl::from_string (value, flag);
    dbu_coordinates (flag);
    return true;

  } else if (name == cfg_stipple_palette) {

    lay::StipplePalette palette = lay::StipplePalette::default_palette ();

    try {
      //  empty string means: default palette
      if (! value.empty ()) {
        palette.from_string (value);
      }
    } catch (...) {
      //  ignore errors: just reset the palette 
      palette = lay::StipplePalette::default_palette ();
    }

    set_palette (palette);

    // others need this property too ..
    return false;

  } else if (name == cfg_color_palette) {

    lay::ColorPalette palette = lay::ColorPalette::default_palette ();

    try {
      //  empty string means: default palette
      if (! value.empty ()) {
        palette.from_string (value);
      }
    } catch (...) {
      //  ignore errors: just reset the palette 
      palette = lay::ColorPalette::default_palette ();
    }

    set_palette (palette);

    // others need this property too ..
    return false;

  } else if (name == cfg_sel_inside_pcells_mode) {

    bool flag;
    tl::from_string (value, flag);

    if (m_sel_inside_pcells != flag) {
      m_sel_inside_pcells = flag;
      clear_selection ();
    }

    return true;

  } else if (name == cfg_sel_transient_mode) {

    bool flag;
    tl::from_string (value, flag);
    m_transient_selection_mode = flag;

    if (! m_transient_selection_mode) {
      clear_transient_selection ();
    }

    //  do not take - let others receive this configuration as well
    return false;

  } else if (name == cfg_sel_color) {

    QColor color;
    lay::ColorConverter ().from_string (value, color);

    //  Change the color
    if (lay::test_and_set (m_marker_color, color)) {
      mp_canvas->update_image ();
    }

    //  do not take - let others receive this configuration as well
    return false;

  } else if (name == cfg_sel_line_width) {

    int lw = 0;
    tl::from_string (value, lw);

    //  Change the line width
    if (lay::test_and_set (m_marker_line_width, lw)) {
      mp_canvas->update_image ();
    }

    //  do not take - let others receive this configuration as well
    return false;

  } else if (name == cfg_sel_dither_pattern) {

    int dp = 0;
    tl::from_string (value, dp);

    //  Change the vertex_size
    if (lay::test_and_set (m_marker_dither_pattern, dp)) {
      mp_canvas->update_image ();
    }

    //  do not take - let others receive this configuration as well
    return false;

  } else if (name == cfg_sel_vertex_size) {

    int vs = 0;
    tl::from_string (value, vs);

    //  Change the vertex_size
    if (lay::test_and_set (m_marker_vertex_size, vs)) {
      mp_canvas->update_image ();
    }

    //  do not take - let others receive this configuration as well
    return false;

  } else if (name == cfg_sel_halo) {

    bool halo = 0;
    tl::from_string (value, halo);

    //  Change the vertex_size
    if (lay::test_and_set (m_marker_halo, halo)) {
      mp_canvas->update_image ();
    }

    //  do not take - let others receive this configuration as well
    return false;

  } else {
    return false;
  }
}

void 
LayoutView::enable_edits (bool enable)
{
  //  enable or disable these services:
  if (mp_selection_service) {
    mp_selection_service->enable (enable);
  }
  if (mp_move_service) {
    mp_move_service->enable (enable);
  }

  //  enable or disable the services that implement "lay::ViewService"
  for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
    lay::ViewService *svc = (*p)->view_service_interface ();
    if (svc) {
      svc->enable (enable);
    }
  }
}

void 
LayoutView::set_current_layer (unsigned int cv_index, const db::LayerProperties &lp) 
{
  //  rename the ones that got shifted.
  lay::LayerPropertiesConstIterator l = begin_layers ();
  while (! l.at_end ()) {
    if (l->source (true).cv_index () == int (cv_index) && l->source (true).layer_props ().log_equal (lp)) {
      set_current_layer (l);
      break;
    }
    ++l;
  }
}

void 
LayoutView::set_current_layer (const lay::LayerPropertiesConstIterator &l) 
{
  if (mp_control_panel) {
    mp_control_panel->set_current_layer (l);
  }
}

lay::LayerPropertiesConstIterator 
LayoutView::current_layer () const
{
  if (mp_control_panel) {
    return mp_control_panel->current_layer ();
  } else {
    return lay::LayerPropertiesConstIterator ();
  }
}

std::vector<lay::LayerPropertiesConstIterator> 
LayoutView::selected_layers () const
{
  if (mp_control_panel) {
    return mp_control_panel->selected_layers ();
  } else {
    return std::vector<lay::LayerPropertiesConstIterator> ();
  }
}

void 
LayoutView::set_selected_layers (const std::vector<lay::LayerPropertiesConstIterator> &sel) 
{
  if (mp_control_panel) {
    mp_control_panel->set_selection (sel);
  }
}

void
LayoutView::set_dither_pattern (const lay::DitherPattern &pattern)
{
  if (mp_canvas->dither_pattern () != pattern) {

    if (transacting ()) {
      manager ()->queue (this, new OpSetDitherPattern (mp_canvas->dither_pattern (), pattern));
    } else if (manager () && ! replaying ()) {
      manager ()->clear ();
    }
    mp_canvas->set_dither_pattern (pattern); 

    for (unsigned int i = 0; i < layer_lists (); ++i) {
      m_layer_properties_lists [i]->set_dither_pattern (pattern);
    }

    m_layer_list_observers.signal_observers (1);
    m_layer_list_observers.signal_observers ();

  }
}

const LayerPropertiesList &
LayoutView::get_properties (unsigned int index) const
{
  if (index >= layer_lists ()) {
    static lay::LayerPropertiesList empty;
    return empty;
  } else {
    return *m_layer_properties_lists [index];
  }
}

void
LayoutView::set_current_layer_list (unsigned int index)
{
  if (index != m_current_layer_list && index < layer_lists ()) {
    m_current_layer_list = index;
    m_current_layer_list_changed_observers.signal_observers ();
    redraw ();
  }
}

void 
LayoutView::insert_layer_list (unsigned index, const LayerPropertiesList &props)
{
  if (index > layer_lists ()) {
    return;
  }

  if (transacting ()) {
    manager ()->queue (this, new OpInsertLayerList (index, props));
  } else if (manager () && ! replaying ()) {
    manager ()->clear ();
  }

  m_layer_properties_lists.insert (m_layer_properties_lists.begin () + index, new LayerPropertiesList (props));
  m_layer_properties_lists [index]->attach_view (this);
  merge_dither_pattern (*m_layer_properties_lists [index]);

  m_current_layer_list = index;
  m_current_layer_list_changed_observers.signal_observers ();

  m_layer_list_inserted_observers.signal_observers (index);

  redraw ();
}

void 
LayoutView::delete_layer_list (unsigned index)
{
  if (index >= layer_lists ()) {
    return;
  }

  if (transacting ()) {
    manager ()->queue (this, new OpDeleteLayerList (index, *m_layer_properties_lists [index]));
  } else if (manager () && ! replaying ()) {
    manager ()->clear ();
  }

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

  if (m_current_layer_list > index) {

    --m_current_layer_list;
    m_current_layer_list_changed_observers.signal_observers ();

    //  don't tell the other observers because effectively nothing has changed.

  } else if (m_current_layer_list == index) {

    if (m_current_layer_list > 0) {
      --m_current_layer_list;
    }

    m_current_layer_list_changed_observers.signal_observers ();

    //  the current list has been deleted.
    m_layer_list_observers.signal_observers (3);
    m_layer_list_observers.signal_observers ();

    redraw ();

  }

  m_layer_list_deleted_observers.signal_observers (index);
}

void 
LayoutView::rename_properties (unsigned int index, const std::string &new_name)
{
  if (index >= layer_lists ()) {
    return;
  }

  if (transacting ()) {
    manager ()->queue (this, new OpRenameProps (index, m_layer_properties_lists [index]->name (), new_name));
  } else if (manager () && ! replaying ()) {
    manager ()->clear ();
  }

  m_layer_properties_lists [index]->set_name (new_name);

  m_layer_list_observers.signal_observers (4);
  m_layer_list_observers.signal_observers ();
}

void
LayoutView::merge_dither_pattern (lay::LayerPropertiesList &props)
{
  lay::DitherPattern dp (dither_pattern ());

  std::map <unsigned int, unsigned int> index_map;
  dp.merge (props.dither_pattern (), index_map);

  //  remap the dither pattern index
  for (lay::LayerPropertiesIterator l = props.begin_recursive (); l != props.end_recursive (); ++l) {
    int dpi = l->dither_pattern (false /*local*/);
    std::map <unsigned int, unsigned int>::iterator m = index_map.find ((unsigned int) dpi);
    if (m != index_map.end ()) {
      l->set_dither_pattern (int (m->second));
    }
  }

  //  install the new custom pattern table
  if (mp_canvas->dither_pattern () != dp) {
    mp_canvas->set_dither_pattern (dp); 
    for (unsigned int i = 0; i < layer_lists (); ++i) {
      m_layer_properties_lists [i]->set_dither_pattern (dp);
    }
  }
}

void 
LayoutView::set_properties (unsigned int index, const LayerPropertiesList &props)
{
  //  If index is not a valid tab index, don't do anythign except for the case of
  //  index 0 in which the first entry is created (this can happen as a result of
  //  delete_properties).
  if (index >= layer_lists ()) {
    if (index > 0) {
      return;
    } else {
      m_layer_properties_lists.push_back (new LayerPropertiesList ());
      m_layer_properties_lists.back ()->attach_view (this);
    }
  }

  //  HINT: this method is quite frequently used in an imperative way. 
  //  Since it has some desired side effects such as forcing a recomputation of the internals, 
  //  it should be executed in any case, even if props == get_properties ().

  if (transacting ()) {
    manager ()->queue (this, new OpSetAllProps (index, get_properties (), props));
  } else if (manager () && ! replaying ()) {
    manager ()->clear ();
  }

  if (mp_control_panel && index == current_layer_list ()) {
    mp_control_panel->begin_updates ();
  }

  *m_layer_properties_lists [index] = props;
  m_layer_properties_lists [index]->attach_view (this);

  merge_dither_pattern (*m_layer_properties_lists [index]);

  if (index == current_layer_list ()) {

    m_layer_list_observers.signal_observers (3);
    m_layer_list_observers.signal_observers ();

    redraw ();

    dm_prop_changed ();

  }
}

void 
LayoutView::expand_properties ()
{
  expand_properties (std::map<int, int> (), false);
}
  
void 
LayoutView::expand_properties (unsigned int index)
{
  expand_properties (index, std::map<int, int> (), false);
}

void 
LayoutView::expand_properties (const std::map<int, int> &map_cv_index, bool add_default)
{
  for (unsigned int i = 0; i < cellviews (); ++i) {
    expand_properties (i, map_cv_index, add_default);
  }
}

void 
LayoutView::expand_properties (unsigned int index, const std::map<int, int> &map_cv_index, bool add_default)
{
  if (index < m_layer_properties_lists.size ()) {
    m_layer_properties_lists [index]->expand (this, map_cv_index, add_default);
  }
}

void
LayoutView::replace_layer_node (unsigned int index, const LayerPropertiesConstIterator &iter, const LayerPropertiesNode &node)
{
  //  if the source specification changed, a redraw is required
  if (*iter != node) {

    if (transacting ()) {
      manager ()->queue (this, new OpSetLayerPropsNode (index, iter.uint (), *iter, node));
    } else if (manager () && ! replaying ()) {
      manager ()->clear ();
    }

    if (mp_control_panel && index == current_layer_list ()) {
      mp_control_panel->begin_updates ();
    }

    LayerPropertiesIterator non_const_iter (get_properties (index), iter.uint ());
    *non_const_iter = node;
    non_const_iter->attach_view (this);

    if (index == current_layer_list ()) {

      m_layer_list_observers.signal_observers (2);
      m_layer_list_observers.signal_observers ();

      //  TODO: check, if redraw is actually necessary (this is complex!)
      redraw ();

      dm_prop_changed ();

    }
  }
}

void 
LayoutView::set_properties (unsigned int index, const LayerPropertiesConstIterator &iter, const LayerProperties &props)
{
  //  if the source specification changed, a redraw is required
  const LayerProperties &l = *iter;
  if (l != props) {

    if (transacting ()) {
      manager ()->queue (this, new OpSetLayerProps (index, iter.uint (), l, props));
    } else if (manager () && ! replaying ()) {
      manager ()->clear ();
    }

    bool need_redraw = (l.source (false /*local*/) != props.source (false /*local*/));
    bool visible_changed = (l.visible (true /*real*/) != props.visible (true /*real*/));

    LayerPropertiesIterator non_const_iter (get_properties (index), iter.uint ());
    *non_const_iter = props;

    if (index == current_layer_list ()) {

      m_layer_list_observers.signal_observers (1);
      m_layer_list_observers.signal_observers ();

      if (need_redraw) {
        redraw ();
      } 

      if (visible_changed) {
        m_visibility_changed = true;
      }

      //  perform the callbacks asynchronously to collect the necessary calls instead
      //  of executing them immediately.
      dm_prop_changed ();

    }
  }
}

const LayerPropertiesNode &
LayoutView::insert_layer (unsigned int index, const LayerPropertiesConstIterator &before, const LayerPropertiesNode &node)
{
  if (transacting ()) {
    manager ()->queue (this, new OpInsertLayerProps (index, before.uint (), node));
  } else if (manager () && ! replaying ()) {
    manager ()->clear ();
  }
  
  if (mp_control_panel && index == current_layer_list ()) {
    mp_control_panel->begin_updates ();
  }

  const LayerPropertiesNode &ret = m_layer_properties_lists [index]->insert (LayerPropertiesIterator (*m_layer_properties_lists [index], before.uint ()), node, this);

  //  signal to the observers that something has changed
  if (index == current_layer_list ()) {

    m_layer_list_observers.signal_observers (2);
    m_layer_list_observers.signal_observers ();

    redraw ();

  }

  return ret;
}

void 
LayoutView::delete_layer (unsigned int index, LayerPropertiesConstIterator &iter)
{
  lay::LayerPropertiesNode orig = *iter;

  if (mp_control_panel && index == current_layer_list ()) {
    mp_control_panel->begin_updates ();
  }

  //  delete the element
  m_layer_properties_lists [index]->erase (LayerPropertiesIterator (*m_layer_properties_lists [index], iter.uint ()));

  if (transacting ()) {
    manager ()->queue (this, new OpDeleteLayerProps (index, iter.uint (), orig));
  } else if (manager () && ! replaying ()) {
    manager ()->clear ();
  }

  //  signal to the observers that something has changed
  if (index == current_layer_list ()) {

    m_layer_list_observers.signal_observers (2);
    m_layer_list_observers.signal_observers ();

    redraw ();

  }

  //  invalidate the iterator so it can be used to refer to the next element
  iter.invalidate ();
}

void
LayoutView::signal_selection_changed ()
{
  if (selection_size () > 1) {
    message (tl::sprintf (tl::to_string (QObject::tr ("selected: %ld objects")), selection_size ()));
  }

  lay::Editables::signal_selection_changed ();
}

void 
LayoutView::save_as (unsigned int index, const std::string &filename, tl::OutputStream::OutputStreamMode om, const db::SaveLayoutOptions &options, bool update)
{
  tl_assert (index < cellviews ());

  tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Saving")));
  cellview (index)->save_as (filename, om, options, update);

  //  because the file name for the cellviews has changed we issue that event:
  m_cellview_changed_observers.signal_observers ();
  m_cellview_changed_observers.signal_observers (index);

  if (m_title.empty ()) {
    emit title_changed ();
  }
}

void
LayoutView::redo (db::Op *op)
{
  tl_assert (! transacting ());

  OpSetLayerProps *sop = dynamic_cast <OpSetLayerProps *> (op);
  if (sop) {
    if (sop->m_list_index < m_layer_properties_lists.size ()) {
      set_properties (sop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [sop->m_list_index], sop->m_index), sop->m_new);
    }
    return;
  }

  OpSetLayerPropsNode *snop = dynamic_cast <OpSetLayerPropsNode *> (op);
  if (snop) {
    if (snop->m_list_index < m_layer_properties_lists.size ()) {
      replace_layer_node (snop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [snop->m_list_index], snop->m_index), snop->m_new);
    }
    return;
  }

  OpInsertLayerList *ilop = dynamic_cast <OpInsertLayerList *> (op);
  if (ilop) {
    if (ilop->m_list_index <= m_layer_properties_lists.size ()) {
      insert_layer_list (ilop->m_list_index, ilop->m_new);
    }
    return;
  }

  OpDeleteLayerList *dlop = dynamic_cast <OpDeleteLayerList *> (op);
  if (dlop) {
    if (dlop->m_list_index < m_layer_properties_lists.size ()) {
      delete_layer_list (dlop->m_list_index);
    }
    return;
  }

  OpSetAllProps *saop = dynamic_cast <OpSetAllProps *> (op);
  if (saop) {
    if (saop->m_list_index < m_layer_properties_lists.size ()) {
      set_properties (saop->m_list_index, saop->m_new);
    }
    return;
  }

  OpRenameProps *rnop = dynamic_cast <OpRenameProps *> (op);
  if (rnop) {
    if (rnop->m_list_index < m_layer_properties_lists.size ()) {
      rename_properties (rnop->m_list_index, rnop->m_new);
    }
    return;
  }

  OpLayerList *lop = dynamic_cast <OpLayerList *> (op);
  if (lop) {
    if (lop->m_list_index < m_layer_properties_lists.size ()) {
      if (lop->m_mode == OpLayerList::Insert) {
        insert_layer (lop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [lop->m_list_index], lop->m_index), lop->m_node);
      } else {
        lay::LayerPropertiesConstIterator iter (*m_layer_properties_lists [lop->m_list_index], lop->m_index);
        delete_layer (lop->m_list_index, iter);
      }
    }
    return;
  } 

  OpSetDitherPattern *stpop = dynamic_cast <OpSetDitherPattern *> (op);
  if (stpop) {
    set_dither_pattern (stpop->m_new);
    return;
  }

  OpHideShowCell *hscop = dynamic_cast <OpHideShowCell *> (op);
  if (hscop) {
    if (hscop->m_show) {
      show_cell (hscop->m_cell_index, hscop->m_cellview_index);
    } else {
      hide_cell (hscop->m_cell_index, hscop->m_cellview_index);
    }
    return;
  }

  db::Object::redo (op);
}

void 
LayoutView::undo (db::Op *op)
{
  tl_assert (! transacting ());

  OpSetLayerProps *sop = dynamic_cast <OpSetLayerProps *> (op);
  if (sop) {
    if (sop->m_list_index < m_layer_properties_lists.size ()) {
      set_properties (sop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [sop->m_list_index], sop->m_index), sop->m_old);
    }
    return;
  }

  OpSetLayerPropsNode *snop = dynamic_cast <OpSetLayerPropsNode *> (op);
  if (snop) {
    if (snop->m_list_index < m_layer_properties_lists.size ()) {
      replace_layer_node (snop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [snop->m_list_index], snop->m_index), snop->m_old);
    }
    return;
  }

  OpInsertLayerList *ilop = dynamic_cast <OpInsertLayerList *> (op);
  if (ilop) {
    if (ilop->m_list_index <= m_layer_properties_lists.size ()) {
      delete_layer_list (ilop->m_list_index);
    }
    return;
  }

  OpDeleteLayerList *dlop = dynamic_cast <OpDeleteLayerList *> (op);
  if (dlop) {
    if (dlop->m_list_index < m_layer_properties_lists.size ()) {
      insert_layer_list (dlop->m_list_index, dlop->m_old);
    }
    return;
  }

  OpSetAllProps *saop = dynamic_cast <OpSetAllProps *> (op);
  if (saop) {
    if (saop->m_list_index < m_layer_properties_lists.size ()) {
      set_properties (saop->m_list_index, saop->m_old);
    }
    return;
  }

  OpRenameProps *rnop = dynamic_cast <OpRenameProps *> (op);
  if (rnop) {
    if (rnop->m_list_index < m_layer_properties_lists.size ()) {
      rename_properties (rnop->m_list_index, rnop->m_old);
    }
    return;
  }

  OpLayerList *lop = dynamic_cast <OpLayerList *> (op);
  if (lop) {
    if (lop->m_list_index < m_layer_properties_lists.size ()) {
      if (lop->m_mode == OpLayerList::Insert) {
        lay::LayerPropertiesConstIterator iter (*m_layer_properties_lists [lop->m_list_index], lop->m_index);
        delete_layer (lop->m_list_index, iter);
      } else {
        insert_layer (lop->m_list_index, lay::LayerPropertiesConstIterator (*m_layer_properties_lists [lop->m_list_index], lop->m_index), lop->m_node);
      }
    }
    return;
  } 

  OpHideShowCell *hscop = dynamic_cast <OpHideShowCell *> (op);
  if (hscop) {
    
    if (hscop->m_show) {
      hide_cell (hscop->m_cell_index, hscop->m_cellview_index);
    } else {
      show_cell (hscop->m_cell_index, hscop->m_cellview_index);
    }

    return;

  }

  OpSetDitherPattern *stpop = dynamic_cast <OpSetDitherPattern *> (op);
  if (stpop) {
    set_dither_pattern (stpop->m_old);
    return;
  }

  db::Object::undo (op);
}

void
LayoutView::signal_hier_changed ()
{
  //  schedule a redraw request for all layers
  redraw ();
  //  forward this event to our observers
  m_hier_changed_observers.signal_observers ();
}

void
LayoutView::signal_bboxes_changed ()
{
  //  schedule a redraw request for all layers
  //  HINT: it could be optimized if we knew which layer to redraw. However, this 
  //  is somewhat difficult to find out: first, we had to identify the layer we need to
  //  redraw, the the layout which to redraw and then the layer views which need to be
  //  redrawn ..
  redraw ();
  //  forward this event to our observers
  m_geom_changed_observers.signal_observers ();
}

void
LayoutView::signal_cell_name_changed ()
{
  m_cell_visibility_observers.signal_observers (); // HINT: that is not what actually is intended, but it serves the function ...
  redraw ();  //  needs redraw
}

void
LayoutView::signal_layer_properties_changed ()
{
  //  recompute the source 
  //  TODO: this is a side effect of this method - provide a special method for this purpose
  for (unsigned int i = 0; i < layer_lists (); ++i) {
    m_layer_properties_lists [i]->attach_view (this);
  }

  //  schedule a redraw request - since the layer views might not have changed, this is necessary
  redraw ();
}

void
LayoutView::signal_prop_ids_changed ()
{
  //  inform the layer list observers that they need to recompute the property selectors
  m_layer_list_observers.signal_observers (1);
  m_layer_list_observers.signal_observers ();

  //  recompute the source 
  //  TODO: this is a side effect of this method - provide a special method for this purpose
  for (unsigned int i = 0; i < layer_lists (); ++i) {
    m_layer_properties_lists [i]->attach_view (this);
  }
}

void
LayoutView::signal_plugin_enabled_changed ()
{
  for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
    if ((*p)->editable_interface ()) {
      enable ((*p)->editable_interface (), (*p)->plugin_declaration ()->editable_enabled ());
    }
  }
}

void
LayoutView::signal_annotations_changed ()
{
  //  schedule a redraw request for the annotation shapes
  redraw_deco_layer ();
  //  forward this event to our observers
  m_annotations_changed_observers.signal_observers ();
}

void 
LayoutView::finish_cellviews_changed ()
{
  m_hier_changed_observer.detach_all ();
  m_bboxes_changed_observer.detach_all ();
  m_prop_ids_changed_observer.detach_all ();
  m_layer_properties_changed_observer.detach_all ();
  m_annotations_changed_observer.detach_all ();
  m_cell_name_changed_observer.detach_all ();
  m_apply_technology_observer.detach_all ();

  for (unsigned int i = 0; i < cellviews (); ++i) {
    cellview (i)->layout ().add_hier_observer (m_hier_changed_observer);
    cellview (i)->layout ().add_bboxes_observer (m_bboxes_changed_observer);
    cellview (i)->layout ().add_dbu_changed_observer (m_bboxes_changed_observer);
    cellview (i)->layout ().add_prop_ids_observer (m_prop_ids_changed_observer);
    cellview (i)->layout ().add_layer_properties_observer (m_layer_properties_changed_observer);
    cellview (i)->layout ().add_cell_name_observer (m_cell_name_changed_observer);
    cellview (i)->add_apply_technology_observer (m_apply_technology_observer);
  }

  annotation_shapes ().add_bboxes_observer (m_annotations_changed_observer);

  m_cellviews_changed_observers.signal_observers ();

  redraw ();
}

void
LayoutView::erase_cellview (unsigned int index)
{
  if (index >= m_cellviews.size ()) {
    return;
  }

  cancel (); 

  //  issue to event that signals a change in the cellviews
  m_cellviews_about_to_change_observers.signal_observers ();

  //  no undo available - clear all transactions
  if (manager ()) {
    manager ()->clear ();
  }

  if (mp_control_panel) {
    mp_control_panel->begin_updates ();
  }

  m_cellviews.erase (m_cellviews.begin () + index);

  for (unsigned int lindex = 0; lindex < layer_lists (); ++lindex) {

    //  remove all references to the cellview
    m_layer_properties_lists [lindex]->remove_cv_references (index);

    //  rename the ones that got shifted.
    lay::LayerPropertiesConstIterator l = begin_layers (lindex);
    while (! l.at_end ()) {
      lay::ParsedLayerSource source (l->source (false));
      if (source.cv_index () >= int (index)) {
        lay::LayerProperties new_props (*l);
        source.cv_index (source.cv_index () == int (index) ? -1 : source.cv_index () - 1);
        new_props.set_source (source);
        LayerPropertiesIterator non_const_iter (*m_layer_properties_lists [lindex], l.uint ());
        *non_const_iter = new_props;
      }
      ++l;
    }

  }

  //  clear the history
  m_display_states.clear ();
  m_display_state_ptr = 0;

  //  signal to the observers that something has changed
  m_layer_list_observers.signal_observers (3);
  m_layer_list_observers.signal_observers ();

  finish_cellviews_changed ();

  update_content ();

  if (m_title.empty ()) {
    emit title_changed ();
  }
}

void
LayoutView::clear_cellviews ()
{
  //  issue to event that signals a change in the cellviews
  m_cellviews_about_to_change_observers.signal_observers ();

  //  no undo available - clear all transactions
  if (manager ()) {
    manager ()->clear ();
  }

  //  clear the layer lists and cellviews 
  while (layer_lists () > 0) {
    delete_layer_list (layer_lists () - 1);
  }
  set_properties (lay::LayerPropertiesList ());
  m_cellviews.clear ();

  //  clear the history, store path and zoom box
  m_display_states.clear ();
  m_display_state_ptr = 0;

  finish_cellviews_changed ();

  if (m_title.empty ()) {
    emit title_changed ();
  }
}

const CellView &
LayoutView::cellview (unsigned int index) const
{
  static const CellView empty;
  if (index >= m_cellviews.size ()) {
    return empty;
  } else {
    return m_cellviews [index];
  }
}

void
LayoutView::set_layout (const lay::CellView &cv, unsigned int cvindex) 
{
  //  issue to event that signals a change in the cellviews
  m_cellviews_about_to_change_observers.signal_observers ();

  //  no undo available - clear all transactions
  if (manager ()) {
    manager ()->clear ();
  }

  //  signal the change of layer properties to the observer
  m_layer_list_observers.signal_observers (3);
  m_layer_list_observers.signal_observers ();

  //  create a new cellview if required
  while (m_cellviews.size () <= cvindex) {
    m_cellviews.push_back (lay::CellView ());
  }

  //  set the handle reference and clear all cell related stuff 
  m_cellviews [cvindex] = cv;

  //  clear the history, store path and zoom box
  m_display_states.clear ();
  m_display_state_ptr = 0;

  finish_cellviews_changed ();

  //  since the hierarchy panel may hold cellviews, we explicitly request an initialization
  //  of the tree. This will release such references. This way, set_layout guarantees that
  //  the layouts are released as far as possible. This is important for reload () for example.
  if (mp_hierarchy_panel) {
    mp_hierarchy_panel->do_update_content (cvindex);
  }

  if (m_title.empty ()) {
    emit title_changed ();
  }
}

void
LayoutView::signal_apply_technology (void *layout_handle)
{
  //  find the cellview which issued the event
  for (unsigned int i = 0; i < cellviews (); ++i) {

    if (cellview (i).handle () == layout_handle) {

      std::string lyp_file;
      const lay::Technology *tech = lay::Technologies::instance ()->technology_by_name (cellview (i)->tech_name ());
      if (tech && ! tech->eff_layer_properties_file ().empty ()) {
        lyp_file = tech->eff_layer_properties_file ();
      }

      if (! lyp_file.empty ()) {

        //  interpolate the layout properties file name
        tl::Eval expr;
        expr.set_var ("layoutfile", cellview (i)->filename ());
        lyp_file = expr.interpolate (lyp_file);

        //  remove all references to the cellview in the layer properties
        for (unsigned int lindex = 0; lindex < layer_lists (); ++lindex) {
          m_layer_properties_lists [lindex]->remove_cv_references (i);
        }

        //  if a layer properties file is set, create the layer properties now
        create_initial_layer_props (i, lyp_file, tech->add_other_layers ());

      }

    }

  }
}

void 
LayoutView::load_layer_props (const std::string &fn)
{
  do_load_layer_props (fn, false, -1, false);
}

void 
LayoutView::load_layer_props (const std::string &fn, bool add_default)
{
  do_load_layer_props (fn, false, -1, add_default);
}

void 
LayoutView::load_layer_props (const std::string &fn, int cv_index, bool add_default)
{
  do_load_layer_props (fn, true, cv_index, add_default);
}

void 
LayoutView::do_load_layer_props (const std::string &fn, bool map_cv, int cv_index, bool add_default)
{
  std::vector<lay::LayerPropertiesList> props;
  bool single_list = false;

  //  read the layer properties from the file
  try {
    tl::XMLFileSource in (fn);
    props.push_back (lay::LayerPropertiesList ());
    props.back ().load (in);
    single_list = true;
  } catch (...) {
    props.clear ();
    tl::XMLFileSource in (fn);
    lay::LayerPropertiesList::load (in, props);
  }

  //  expand the wildcards and map to the target cv.
  for (std::vector<lay::LayerPropertiesList>::iterator p = props.begin (); p != props.end (); ++p) {
    std::map <int, int> cv_map;
    if (map_cv) {
      cv_map.insert (std::make_pair (-1, cv_index));
    }
    p->attach_view (this);
    p->expand (this, cv_map, add_default);
  }

  if (manager ()) {
    manager ()->transaction (tl::to_string (QObject::tr ("Load layer properties"))); 
  }

  if (single_list) {

    //  a single list will only replace the current tab
    if (map_cv && cv_index >= 0) {
      lay::LayerPropertiesList new_props (get_properties ());
      new_props.remove_cv_references (cv_index);
      new_props.append (props [0]);
      set_properties (new_props);
    } else {
      set_properties (props [0]);
    }

  } else {

    for (unsigned int i = 0; i < props.size (); ++i) {

      if (i < layer_lists ()) {

        if (map_cv && cv_index >= 0) {
          lay::LayerPropertiesList new_props (get_properties (i));
          new_props.remove_cv_references (cv_index);
          new_props.append (props [i]);
          set_properties (i, new_props);
        } else {
          set_properties (i, props [i]);
        }

      } else {
        insert_layer_list (i, props [i]);
      }

    }

    while (layer_lists () > props.size () && layer_lists () > 1) {
      delete_layer_list (layer_lists () - 1);
    }

  }

  if (manager ()) {
    manager ()->commit ();
  }

  update_content ();

  tl::log << "Loaded layer properties from " << fn;
}

void 
LayoutView::save_layer_props (const std::string &fn)
{
  tl::OutputStream os (fn, tl::OutputStream::OM_Plain);

  if (layer_lists () == 1) {

    //  a single list is written in the traditional format
    get_properties ().save (os);

  } else {

    //  multiple tabs are written in the multi-tab format
    std::vector<lay::LayerPropertiesList> props;
    for (unsigned int i = 0; i < layer_lists (); ++i) {
      props.push_back (get_properties (i));
    }

    lay::LayerPropertiesList::save (os, props); 

  }

  tl::log << "Saved layer properties to " << fn;
}

void 
LayoutView::add_new_layers (const std::vector <unsigned int> &layer_ids, int cv_index)
{
  if (cv_index >= 0 && cv_index < int (cellviews ())) {

    const lay::CellView &cv = cellview (cv_index);

    //  create the layers and do a basic recoloring ..
    lay::LayerPropertiesList new_props (get_properties ());

    //  don't create new layers for those, for which there are layers already: compute a 
    //  set of layers alreay present
    std::set <db::LayerProperties, db::LPLogicalLessFunc> present_layers;
    for (LayerPropertiesConstIterator lay_iter = begin_layers (); ! lay_iter.at_end (); ++lay_iter) {
      if (! lay_iter->has_children () && lay_iter->cellview_index () == cv_index) {
        present_layers.insert (lay_iter->source (true /*real*/).layer_props ());
      }
    }

    //  determine layers which are new and need to be created
    std::vector <db::LayerProperties> new_layers;
    for (std::vector <unsigned int>::const_iterator l = layer_ids.begin (); l != layer_ids.end (); ++l) {
      const db::LayerProperties &lp = cv->layout ().get_properties (*l);
      if (present_layers.find (lp) == present_layers.end ()) {
        new_layers.push_back (lp);
      }
    }

    //  create them in the sorting order provided by db::LayerProperties
    std::sort (new_layers.begin (), new_layers.end (), db::LPLogicalLessFunc ());

    //  and actually create them
    for (std::vector <db::LayerProperties>::const_iterator l = new_layers.begin (); l != new_layers.end (); ++l) {
      lay::LayerProperties p;
      p.set_source (lay::ParsedLayerSource (*l, cv_index));
      init_layer_properties (p, new_props);
      new_props.push_back (p);
      new_props.back ().attach_view (this);
    }

    set_properties (new_props);

  }
}

void 
LayoutView::init_layer_properties (LayerProperties &p) const
{
  init_layer_properties (p, get_properties ());
}

void 
LayoutView::init_layer_properties (LayerProperties &p, const LayerPropertiesList &lp_list) const
{
  lay::color_t c = 0;
  if (m_palette.luminous_colors () > 0) {
    c = m_palette.luminous_color_by_index (p.source (true /*real*/).color_index ());
  }

  p.set_dither_pattern (m_stipple_palette.standard_stipple_by_index (lp_list.end_const () - lp_list.begin_const ()));
  p.set_fill_color (c);
  p.set_frame_color (c);
  p.set_fill_brightness (0);
  p.set_frame_brightness (0);
  p.set_frame_brightness (0);
  p.set_transparent (false);  // :TODO: make variable
  p.set_visible (true);
  p.set_width (1); 
  p.set_animation (0);
  p.set_marked (false);
}

QImage 
LayoutView::get_screenshot ()
{
  tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save screenshot")));

  //  process events to ensure that all deferred methods are executed
  lay::Application::instance ()->process_events ();
  
  return mp_canvas->screenshot ();
}

void 
LayoutView::save_screenshot (const std::string &fn)
{
  tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save screenshot")));

  QImageWriter writer (tl::to_qstring (fn), QByteArray ("PNG"));

  //  Unfortunately the PNG writer does not allow to write long strings.
  //  We separate the description into a set of keys:

  for (unsigned int i = 0; i < cellviews (); ++i) {
    if (cellview (i).is_valid ()) {
      std::string name = cellview (i)->layout ().cell_name (cellview (i).cell_index ());
      writer.setText (tl::to_qstring ("Cell" + tl::to_string (int (i) + 1)), tl::to_qstring (name));
    }
  }

  db::DBox b (box ());
  std::string desc;
  desc += tl::micron_to_string (b.left ()) + "," + tl::micron_to_string (b.bottom ());
  desc += "/";
  desc += tl::micron_to_string (b.right ()) + "," + tl::micron_to_string (b.top ());
  writer.setText (QString::fromAscii ("Rect"), tl::to_qstring (desc));

  //  process events to ensure that all deferred methods are executed
  lay::Application::instance ()->process_events ();
  
  if (! writer.write (mp_canvas->screenshot ())) {
    throw tl::Exception (tl::to_string (QObject::tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
  }

  tl::log << "Saved screen shot to " << fn;
}

QImage 
LayoutView::get_image (unsigned int width, unsigned int height)
{
  tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save image")));

  //  process events to ensure that all deferred methods are executed
  lay::Application::instance ()->process_events ();
  
  return mp_canvas->image (width, height);
}

QImage 
LayoutView::get_image_with_options (unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, 
                                    QColor background, QColor foreground, QColor active, const db::DBox &target_box, bool monochrome)
{
  tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save image")));

  //  process events to ensure that all deferred methods are executed
  lay::Application::instance ()->process_events ();
  
  return mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box, monochrome);
}

void 
LayoutView::save_image (const std::string &fn, unsigned int width, unsigned int height)
{
  tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save image")));

  QImageWriter writer (tl::to_qstring (fn), QByteArray ("PNG"));

  //  Unfortunately the PNG writer does not allow to write long strings.
  //  We separate the description into a set of keys:

  for (unsigned int i = 0; i < cellviews (); ++i) {
    if (cellview (i).is_valid ()) {
      std::string name = cellview (i)->layout ().cell_name (cellview (i).cell_index ());
      writer.setText (tl::to_qstring ("Cell" + tl::to_string (int (i) + 1)), tl::to_qstring (name));
    }
  }

  lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
  writer.setText (QString::fromAscii ("Rect"), tl::to_qstring (vp.box ().to_string ()));
  
  //  process events to ensure that all deferred methods are executed
  lay::Application::instance ()->process_events ();
  
  if (! writer.write (mp_canvas->image (width, height))) {
    throw tl::Exception (tl::to_string (QObject::tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
  }

  tl::log << "Saved screen shot to " << fn;
}

void 
LayoutView::save_image_with_options (const std::string &fn, 
                                     unsigned int width, unsigned int height, int linewidth, int oversampling, double resolution, 
                                     QColor background, QColor foreground, QColor active, const db::DBox &target_box, bool monochrome)
{
  tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Save image")));

  QImageWriter writer (tl::to_qstring (fn), QByteArray ("PNG"));

  //  Unfortunately the PNG writer does not allow to write long strings.
  //  We separate the description into a set of keys:

  for (unsigned int i = 0; i < cellviews (); ++i) {
    if (cellview (i).is_valid ()) {
      std::string name = cellview (i)->layout ().cell_name (cellview (i).cell_index ());
      writer.setText (tl::to_qstring ("Cell" + tl::to_string (int (i) + 1)), tl::to_qstring (name));
    }
  }

  lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ());
  writer.setText (QString::fromAscii ("Rect"), tl::to_qstring (vp.box ().to_string ()));
  
  //  process events to ensure that all deferred methods are executed
  lay::Application::instance ()->process_events ();
  
  if (! writer.write (mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box, monochrome))) {
    throw tl::Exception (tl::to_string (QObject::tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ()));
  }

  tl::log << "Saved screen shot to " << fn;
}

void
LayoutView::reload_layout (unsigned int cv_index)
{
  stop ();
  cancel (); 

  //  save the current view state
  lay::DisplayState state;
  save_view (state);

  //  this is the cellview at the given index (use a copy since the original is overwritten)
  CellView cvorg = cellview (cv_index);

  //  obtain the original filename  
  std::string filename = cvorg->filename ();
  std::string technology = cvorg->tech_name ();
  std::string name = cvorg->name ();

  //  recreate hidden cells by doing a name referencing
  std::vector <std::string> hidden_cells;
  if (m_hidden_cells.size () > cv_index) {
    hidden_cells.reserve (m_hidden_cells [cv_index].size ());
    for (std::set <cell_index_type>::const_iterator ci = m_hidden_cells [cv_index].begin (); ci != m_hidden_cells [cv_index].end (); ++ci) {
      hidden_cells.push_back (std::string (cvorg->layout ().cell_name (*ci)));
    }
  }

  //  Set up a list of present layers
  std::set <db::LayerProperties, db::LPLogicalLessFunc> present_layers;
  for (LayerPropertiesConstIterator lay_iter = begin_layers (); ! lay_iter.at_end (); ++lay_iter) {
    if (! lay_iter->has_children ()) {
      present_layers.insert (lay_iter->source (true /*real*/).layer_props ());
    }
  }

  std::map <unsigned int, db::LayerProperties> org_layers;

  db::LayerMap lmap (cvorg->load_options ().layer_map ());

  for (unsigned int i = 0; i < cvorg->layout ().layers (); ++i) {
    if (cvorg->layout ().is_valid_layer (i)) {
      const db::LayerProperties &p = cvorg->layout ().get_properties (i);
      if (! p.log_equal (db::LayerProperties ())) {
        if (cvorg->load_options ().layer_map ().mapping (i) == db::LayerProperties ()) {
          lmap.map (p, i);
        } 
        org_layers.insert (std::make_pair (i, p));
      }
    }
  }

  lay::LayoutHandle *handle;

  //  reset the layout: create a dummy handle and install this in between
  //  this will clear the original layout if not further referenced.
  //  Since the dummy layout will act as a placeholder if something goes wrong
  //  when reading the file, it must have the layers created as well
  lay::CellView cv_empty;

  handle = new lay::LayoutHandle (new db::Layout (manager ()), filename);
  handle->set_tech_name (technology);
  cv_empty.set (handle);

  for (std::map <unsigned int, db::LayerProperties>::const_iterator ol = org_layers.begin (); ol != org_layers.end (); ++ol) {
    cv_empty->layout ().insert_layer (ol->first, ol->second);
  }
  cv_empty->rename (name, true);

  set_layout (cv_empty, cv_index);

  //  create a new handle
  lay::CellView cv;
  handle = new lay::LayoutHandle (new db::Layout (manager ()), filename);
  handle->set_tech_name (technology);
  cv.set (handle);

  try {

    //  re-create the layers required
    for (std::map <unsigned int, db::LayerProperties>::const_iterator ol = org_layers.begin (); ol != org_layers.end (); ++ol) {
      cv->layout ().insert_layer (ol->first, ol->second);
    }
    
    {
      tl::log << tl::to_string (QObject::tr ("Loading file: ")) << filename;
      tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading")));

      //  Load with the current options but the new layer map.
      //  Don't change the "create_other_layers" option, since this will preserve
      //  the "confined layer selection" feature.
      db::LoadLayoutOptions options (cvorg->load_options ());
      options.set_layer_map (lmap, options.create_other_layers ());
      cv->load (options);
    }

    //  sort the layout explicitly here. Otherwise it would be done
    //  implicitly at some other time. This may throw an exception
    //  if the operation was cancelled.
    {
      tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Sorting")));
      cv->layout ().update ();
    }

    //  print the memory statistics now.
    if (tl::verbosity () >= 31) {
      db::MemStatistics m;
      cv->layout ().collect_mem_stat (m);
      m.print ();
    }

    //  this is required to release every reference to the cv_empty layout
    cv_empty = lay::CellView ();

    //  install the new layout
    cv->rename (name, true);
    set_layout (cv, cv_index);

  } catch (...) {
    update_content ();
    throw;
  }

  //  recreate the hidden cell indices from the names
  if (m_hidden_cells.size () > cv_index) {
    m_hidden_cells [cv_index].clear ();
    for (std::vector <std::string>::const_iterator cn = hidden_cells.begin (); cn != hidden_cells.end (); ++cn) {
      std::pair<bool, cell_index_type> cid = cv->layout ().cell_by_name (cn->c_str ());
      if (cid.first) {
        m_hidden_cells [cv_index].insert (cid.second);
      }
    }
  }

  //  Determine which layers to create as new layers. New layer need to be created
  //  if these have not been present in the original layout and there are no layer views
  //  referring to them.
  std::vector <db::LayerProperties> new_layers;
  if (cv->load_options ().create_other_layers ()) {
    for (unsigned int i = 0; i < cv->layout ().layers (); ++i) {
      if (cv->layout ().is_valid_layer (i)) {
        std::map <unsigned int, db::LayerProperties>::iterator ol = org_layers.find (i);
        if (ol == org_layers.end () && present_layers.find (cv->layout ().get_properties (i)) == present_layers.end ()) {
          new_layers.push_back (cv->layout ().get_properties (i));
        }
      }
    }
  }

  std::sort (new_layers.begin (), new_layers.end (), db::LPLogicalLessFunc ());

  //  create the layers and do a basic recoloring ..
  lay::LayerPropertiesList new_props (get_properties ());

  for (std::vector <db::LayerProperties>::const_iterator l = new_layers.begin (); l != new_layers.end (); ++l) {
    lay::LayerProperties p;
    p.set_source (lay::ParsedLayerSource (*l, int (cv_index)));
    init_layer_properties (p, new_props);
    new_props.push_back (p);
    new_props.back ().attach_view (this);
  }
  set_properties (new_props);

  goto_view (state);
  
}

unsigned int 
LayoutView::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, bool initialize_layers)
{
  stop ();
  
  bool set_max_hier = (m_full_hier_new_cell || has_max_hier ());

  lay::CellView cv;
  unsigned int cv_index;

  if (! add_cellview) {
    clear_cellviews ();
  }

  cv.set (layout_handle);

  cv->layout ().update ();

  //  select the cell with the largest area as the first top cell
  db::Layout::top_down_const_iterator top = cv->layout ().begin_top_down ();
  for (db::Layout::top_down_const_iterator t = cv->layout ().begin_top_down (); t != cv->layout ().end_top_cells (); ++t) {
    if (cv->layout ().cell (*t).bbox ().area () > cv->layout ().cell (*top).bbox ().area ()) {
      top = t;
    }
  }

  if (top != cv->layout ().end_top_down ()) {
    std::vector <db::cell_index_type> p;
    p.push_back (*top);
    cv.set_unspecific_path (p);
  }

  cv_index = cellviews ();
  set_layout (cv, cv_index);

  if (top != cv->layout ().end_top_cells ()) {
    std::vector <db::cell_index_type> p;
    p.push_back (*top);
    select_cell (p, cv_index);
  } else {
    //  even if there is no cell, select the cellview item
    //  to support applications with an active cellview (that is however invalid)
    set_active_cellview_index (cv_index);
  }
  
  if (initialize_layers) {

    bool add_other_layers = m_add_other_layers;

    //  Use the "layer-properties-file" meta info from the handle to get the layer properties file.
    //  If no such file is present, use the default file or the technology specific file.
    std::string lyp_file = m_def_lyp_file;
    const lay::Technology *tech = lay::Technologies::instance ()->technology_by_name (layout_handle->tech_name ());
    if (tech && ! tech->eff_layer_properties_file ().empty ()) {
      lyp_file = tech->eff_layer_properties_file ();
      add_other_layers = tech->add_other_layers ();
    }

    //  Give the layout object a chance to specify a certain layer property file
    for (lay::LayoutHandle::meta_info_iterator meta = cv->begin_meta (); meta != cv->end_meta (); ++meta) {
      if (meta->name == "layer-properties-file") {
        lyp_file = meta->value;
      }
      if (meta->name == "layer-properties-add-other-layers") {
        try {
          tl::from_string (meta->value, add_other_layers);
        } catch (...) {
        }
      }
    }

    //  interpolate the layout properties file name
    tl::Eval expr;
    expr.set_var ("layoutfile", layout_handle->filename ());
    lyp_file = expr.interpolate (lyp_file);

    //  create the initial layer properties
    create_initial_layer_props (cv_index, lyp_file, add_other_layers);

  }

  //  signal to any observers
  m_file_open_observers.signal_observers ();

  if (cv->layout ().begin_top_down () != cv->layout ().end_top_down ()) {

    //  do a fit and update layer lists etc.
    zoom_fit ();
    if (set_max_hier) {
      max_hier ();
    }
    update_content ();

  } else {
    //  even if there is no cell, select the cellview item
    //  to support applications with an active cellview (that is however invalid)
    set_active_cellview_index (cv_index);
  }

  //  this event may not be generated otherwise:
  active_cellview_changed (cv_index);

  return cv_index;
}

unsigned int 
LayoutView::create_layout (const std::string &technology, bool add_cellview, bool initialize_layers)
{
  lay::LayoutHandle *handle = new lay::LayoutHandle (new db::Layout (manager ()), "");
  handle->set_tech_name (technology);
  return add_layout (handle, add_cellview, initialize_layers);
}

unsigned int 
LayoutView::load_layout (const std::string &filename, const std::string &technology, bool add_cellview)
{
  return load_layout (filename, db::LoadLayoutOptions (), technology, add_cellview);
}

unsigned int 
LayoutView::load_layout (const std::string &filename, const db::LoadLayoutOptions &options, const std::string &technology, bool add_cellview)
{
  stop ();
  
  bool set_max_hier = (m_full_hier_new_cell || has_max_hier ());

  const lay::Technology *tech = lay::Technologies::instance ()->technology_by_name (technology);

  //  create a new layout handle 
  lay::CellView cv;
  lay::LayoutHandle *handle = new lay::LayoutHandle (new db::Layout (manager ()), filename);
  handle->set_tech_name (technology);
  cv.set (handle);

  unsigned int cv_index;
  db::LayerMap lmap;

  try {

    //  load the file
    {
      tl::log << tl::to_string (QObject::tr ("Loading file: ")) << filename << tl::to_string (QObject::tr (" with technology: ")) << technology;
      tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Loading")));

      if (tech && ! tech->layer_map ().is_empty ()) {

        db::LoadLayoutOptions options_with_layer_map (options);
        options_with_layer_map.set_layer_map (tech->layer_map (), tech->create_other_layers ());
        lmap = cv->load (options_with_layer_map);

      } else {

        lmap = cv->load (options);

      }
    }

    //  sort the layout explicitly here. Otherwise it would be done
    //  implicitly at some other time. This may throw an exception
    //  if the operation was cancelled.
    {
      tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Sorting")));
      cv->layout ().update ();
    }

    //  print the memory statistics now.
    if (tl::verbosity () >= 31) {
      db::MemStatistics m;
      cv->layout ().collect_mem_stat (m);
      m.print ();
    }

    //  clear the cellviews if required
    if (! add_cellview) {
      clear_cellviews ();
    }

    //  set the new layout as the layout for the last cellview
    cv_index = cellviews ();
    set_layout (cv, cv_index);

  } catch (...) {

    update_content ();
    throw;

  }

  //  select the cell with the largest area as the first top cell
  db::Layout::top_down_const_iterator top = cv->layout ().begin_top_down ();
  for (db::Layout::top_down_const_iterator t = cv->layout ().begin_top_down (); t != cv->layout ().end_top_cells (); ++t) {
    if (cv->layout ().cell (*t).bbox ().area () > cv->layout ().cell (*top).bbox ().area ()) {
      top = t;
    }
  }
  if (top != cv->layout ().end_top_cells ()) {
    std::vector <db::cell_index_type> p;
    p.push_back (*top);
    select_cell (p, cv_index);
  } else {
    //  even if there is no cell, select the cellview item
    //  to support applications with an active cellview (that is however invalid)
    set_active_cellview_index (cv_index);
  }
  
  bool add_other_layers = m_add_other_layers;

  //  Use the "layer-properties-file" meta info from the handle to get the layer properties file.
  //  If no such file is present, use the default file or the technology specific file.
  std::string lyp_file = m_def_lyp_file;
  if (tech && ! tech->eff_layer_properties_file ().empty ()) {
    lyp_file = tech->eff_layer_properties_file ();
    add_other_layers = tech->add_other_layers ();
  }

  //  Give the layout object a chance to specify a certain layer property file
  for (lay::LayoutHandle::meta_info_iterator meta = handle->begin_meta (); meta != handle->end_meta (); ++meta) {
    if (meta->name == "layer-properties-file") {
      lyp_file = meta->value;
    }
    if (meta->name == "layer-properties-add-other-layers") {
      try {
        tl::from_string (meta->value, add_other_layers);
      } catch (...) {
      }
    }
  }

  //  interpolate the layout properties file name
  tl::Eval expr;
  expr.set_var ("layoutfile", filename);
  lyp_file = expr.interpolate (lyp_file);

  //  create the initial layer properties
  create_initial_layer_props (cv_index, lyp_file, add_other_layers);

  //  signal to any observers
  m_file_open_observers.signal_observers ();

  //  do a fit and update layer lists etc.
  zoom_fit ();
  if (set_max_hier) {
    max_hier ();
  }
  update_content ();

  return cv_index;
}

void 
LayoutView::create_initial_layer_props (int cv_index, const std::string &lyp_file, bool add_missing)
{
  std::vector<lay::LayerPropertiesList> props;
  bool loaded = false;

  if (! lyp_file.empty ()) {

    //  read the layer properties from the file
    try { 

      try {
        tl::XMLFileSource in (lyp_file);
        props.push_back (lay::LayerPropertiesList ());
        props.back ().load (in);
        loaded = true;
      } catch (...) {
        props.clear ();
        tl::XMLFileSource in (lyp_file);
        tl::log << tl::to_string (QObject::tr ("Loading layer properties file: ")) << lyp_file;
        lay::LayerPropertiesList::load (in, props);
        loaded = true;
      }

    } catch (tl::Exception &ex) {
      tl::warn << tl::to_string (QObject::tr ("Initialization of layers failed: ")) << ex.msg ();
    } catch (...) {
      tl::warn << tl::to_string (QObject::tr ("Initialization of layers failed: unspecific error"));
    }

  }

  std::map <int, int> cv_map;
  cv_map.insert (std::make_pair (-1, cv_index));

  if (! loaded) {

    props.clear ();
    props.push_back (lay::LayerPropertiesList ());

  } else {

    //  do't map cv's if the input file is a multi-cv one.
    std::set <int> cv;
    for (std::vector<lay::LayerPropertiesList>::const_iterator p = props.begin (); p != props.end (); ++p) {
      for (lay::LayerPropertiesConstIterator lp = p->begin_const_recursive (); ! lp.at_end (); ++lp) {
        if (! lp->has_children ()) {
          cv.insert (lp->source (true).cv_index ());
          if (cv.size () >= 2) {
            cv_map.clear ();
            cv_map.insert (std::make_pair (cv_index, cv_index));
            //  erase the others:
            cv_map.insert (std::make_pair (-1, -2));
            break;
          }
        }
      }
    }

  }

  //  expand the wildcards and map to the target cv.
  for (std::vector<lay::LayerPropertiesList>::iterator p = props.begin (); p != props.end (); ++p) {
    p->attach_view (this);
    p->expand (this, cv_map, add_missing || !loaded);
  }

  merge_layer_props (props);
}

void 
LayoutView::merge_layer_props (const std::vector<lay::LayerPropertiesList> &props)
{
  lay::LayerPropertiesList p0;
  if (layer_lists () > 0) {
    p0 = get_properties (0);
  }

  //  merge the new layer views into the present ones
  //  If the specific list is a single list (no tabs), it is merged into every tab present. 
  if (props.size () == 1) {

    for (size_t n = 0; n < layer_lists () || n == 0; ++n) {

      std::vector<lay::LayerPropertiesList>::const_iterator p = props.begin ();

      if (n < layer_lists ()) {
        lay::LayerPropertiesList new_props (get_properties (n));
        new_props.append (*p);
        if (! p->name ().empty ()) {
          new_props.set_name (p->name ());
        }
        set_properties (n, new_props);
      } else {

        lay::LayerPropertiesList new_props = p0;
        new_props.append (*p);
        if (! p->name ().empty ()) {
          new_props.set_name (p->name ());
        }
        insert_layer_list (n, new_props);
      }

    }

  } else {

    size_t n = 0;
    for (std::vector<lay::LayerPropertiesList>::const_iterator p = props.begin (); p != props.end (); ++p, ++n) {

      if (n < layer_lists ()) {
        lay::LayerPropertiesList new_props (get_properties (n));
        new_props.append (*p);
        if (! p->name ().empty ()) {
          new_props.set_name (p->name ());
        }
        set_properties (n, new_props);
      } else {
        lay::LayerPropertiesList new_props = p0;
        new_props.append (*p);
        if (! p->name ().empty ()) {
          new_props.set_name (p->name ());
        }
        insert_layer_list (n, new_props);
      }

    }

  }
}

void
LayoutView::store_state ()
{
  //  erase all states after the current position
  if (m_display_state_ptr + 1 < m_display_states.size ()) {
    m_display_states.erase (m_display_states.begin () + m_display_state_ptr + 1, m_display_states.end ());
  }

  //  save the state
  DisplayState state (box (), get_min_hier_levels (), get_max_hier_levels (), m_cellviews);
  m_display_states.push_back (state);

  m_display_state_ptr = m_display_states.size () - 1;
}

db::DBox 
LayoutView::box () const
{
  return mp_canvas->viewport ().box ();
}

void
LayoutView::timer ()
{
  if (m_animated) {
    set_view_ops ();
    if (mp_control_panel) {
      mp_control_panel->set_phase (int (m_phase));
    }
    if (m_animated) {
      ++m_phase;
    }
  }  
}

bool
LayoutView::layer_model_updated ()
{
  //  because check_updated is called in the initialization phase, we check if the pointers
  //  to the widgets are non-null:
  if (mp_control_panel) {
    return mp_control_panel->model_updated ();
  } else {
    return false;
  }
}

void
LayoutView::force_update_content ()
{
  set_view_ops ();
}

void
LayoutView::update_content ()
{
  if (m_activated) {
    set_view_ops ();
  }
}

void
LayoutView::zoom_fit_sel ()
{
  db::DBox bbox = selection_bbox ();
  if (! bbox.empty ()) {
    bbox = db::DBox (bbox.left () - 0.025 * bbox.width (), bbox.bottom () - 0.025 * bbox.height (),
                     bbox.right () + 0.025 * bbox.width (), bbox.top () + 0.025 * bbox.height ());
    zoom_box (bbox);
  }
}

db::DBox
LayoutView::full_box () const
{
  //  compute the bounding box over all layers
  //  this will trigger the update procedures of the layout objects if not done yet ..

  db::DBox bbox;

  for (LayerPropertiesConstIterator l = get_properties ().begin_const_recursive (); ! l.at_end (); ++l) {
    bbox += l->bbox ();
  }

  for (lay::AnnotationShapes::iterator a = annotation_shapes ().begin (); ! a.at_end (); ++a) {
    bbox += a->box ();
  }

  if (bbox.empty ()) {
    bbox = db::DBox (0, 0, 0, 0); // default box
  } else {
    bbox = db::DBox (bbox.left () - 0.025 * bbox.width (), bbox.bottom () - 0.025 * bbox.height (),
                     bbox.right () + 0.025 * bbox.width (), bbox.top () + 0.025 * bbox.height ());
  }

  return bbox;
}

void
LayoutView::zoom_fit ()
{
  mp_canvas->zoom_box (full_box (), true /*precious*/);
  store_state ();
}

void
LayoutView::ensure_selection_visible ()
{
  ensure_visible (selection_bbox ());
}

void
LayoutView::ensure_visible (const db::DBox &bbox)
{
  db::DBox new_box = bbox + viewport ().box ();
  mp_canvas->zoom_box (new_box);
  store_state ();
}

void
LayoutView::zoom_box (const db::DBox &bbox)
{
  mp_canvas->zoom_box (bbox);
  store_state ();
}

void
LayoutView::set_global_trans (const db::DCplxTrans &trans)
{
  mp_canvas->set_global_trans (trans);
  store_state ();
}

void 
LayoutView::zoom_trans (const db::DCplxTrans &trans)
{
  mp_canvas->zoom_trans (trans);
  store_state ();
}

void
LayoutView::pan_left ()
{
  shift_window (1.0, -m_pan_distance, 0.0);
}

void
LayoutView::pan_right ()
{
  shift_window (1.0, m_pan_distance, 0.0);
}

void
LayoutView::pan_up ()
{
  shift_window (1.0, 0.0, m_pan_distance);
}

void
LayoutView::pan_down ()
{
  shift_window (1.0, 0.0, -m_pan_distance);
}

void
LayoutView::pan_center (const db::DPoint &p)
{
  db::DBox b = mp_canvas->viewport ().box ();
  db::DPoint d (b.width () * 0.5, b.height () * 0.5);
  zoom_box (db::DBox (p - d, p + d));
}

void
LayoutView::zoom_in ()
{
  shift_window (zoom_factor, 0.0, 0.0);
}

void
LayoutView::zoom_out ()
{
  shift_window (1.0 / zoom_factor, 0.0, 0.0);
}

void
LayoutView::shift_window (double f, double dx, double dy)
{
  db::DBox b = mp_canvas->viewport ().box ();

  db::DPoint s = mp_canvas->viewport ().global_trans ().inverted () * db::DPoint (dx, dy);
  db::DPoint c = b.center () + db::DPoint (b.width () * s.x (), b.height () * s.y ());

  double w = b.width () * f;
  double h = b.height () * f;

  db::DPoint d (w * 0.5, h * 0.5);

  zoom_box (db::DBox (c - d, c + d));
}

void
LayoutView::goto_window (const db::DPoint &p, double s)
{
  if (s > 1e-6) {
    db::DBox b (p.x () - s * 0.5, p.y () - s * 0.5, p.x () + s * 0.5, p.y () + s * 0.5);
    zoom_box (b);
  } else {
    db::DBox b (box ());
    b.move (p - b.center ());
    zoom_box (b);
  }
}

void 
LayoutView::redraw_layer (unsigned int index)
{
  do_redraw (index);
}

void
LayoutView::redraw_cell_boxes ()
{
  do_redraw (lay::draw_boxes_queue_entry);
}

void
LayoutView::redraw_deco_layer ()
{
  //  redraw background annotations (images etc.)
  mp_canvas->touch_bg ();

  //  redraw other annotations:
  do_redraw (lay::draw_custom_queue_entry);
}

void
LayoutView::redraw ()
{
  std::vector <lay::RedrawLayerInfo> layers;

  size_t nlayers = 0;
  for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
    if (! l->has_children ()) {
      ++nlayers;
    }
  }
  layers.reserve (nlayers);

  for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
    if (! l->has_children ()) {
      layers.push_back (RedrawLayerInfo (*l));
    }
  }

  mp_canvas->redraw_new (layers);
}

void
LayoutView::cancel_edits ()
{
  //  cancel all drag and pending edit operations such as move operations.
  mp_canvas->drag_cancel ();
  lay::Editables::cancel_edits ();
}

void
LayoutView::cancel ()
{
  //  cancel all drags and pending edit operations such as move operations.
  cancel_edits ();
  //  and clear the selection
  clear_selection ();
}

void
LayoutView::manage_bookmarks ()
{
  BookmarkManagementForm dialog (this, "bookmark_form", bookmarks ());
  if (dialog.exec ()) {
    bookmarks (dialog.bookmarks ());
  }
}

void 
LayoutView::bookmarks (const BookmarkList &b)
{
  m_bookmarks = b;
}

void
LayoutView::bookmark_view (const std::string &name)
{
  DisplayState state (box (), get_min_hier_levels (), get_max_hier_levels (), m_cellviews);
  m_bookmarks.add (name, state);
}

void
LayoutView::goto_view (const DisplayState &state)
{
  mp_canvas->zoom_box (state.box ());

  std::vector <lay::CellView> cellviews;
  cellviews.reserve (m_cellviews.size ());
  for (unsigned int i = 0; i < m_cellviews.size (); ++i) {
    cellviews.push_back (state.cellview (i, m_cellviews [i].operator-> ()));
  }

  select_cellviews (cellviews);

  if (state.min_hier () <= state.max_hier ()) {
    set_hier_levels (std::make_pair (state.min_hier (), state.max_hier ()));
  }

  update_content ();
}

void
LayoutView::save_view (DisplayState &state) const
{
  state = DisplayState (box (), get_min_hier_levels (), get_max_hier_levels (), m_cellviews);
}

void
LayoutView::do_redraw (int layer)
{
  std::vector<int> layers;
  layers.push_back (layer);

  mp_canvas->redraw_selected (layers);
}

void 
LayoutView::do_prop_changed ()
{
  if (m_visibility_changed) {

    // change visibility and redraw exposed layers
    std::vector<bool> visibility; 
    for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
      if (! l->has_children ()) {
        visibility.push_back (l->visible (true /*real*/));
      }
    }
    mp_canvas->change_visibility (visibility);

    m_visibility_changed = false;

  }

  update_content ();
}

void 
LayoutView::layer_tab_changed ()
{
  update_content ();
}

void 
LayoutView::layer_order_changed ()
{
  update_content ();
}

void
LayoutView::set_view_ops ()
{
  bool bright_background = (mp_canvas->background_color ().green () > 128);
  int brightness_for_context = ((bright_background ? m_ctx_dimming : -m_ctx_dimming) * 256) / 100;
  int brightness_for_child_context = ((bright_background ? m_child_ctx_dimming : -m_child_ctx_dimming) * 256) / 100;

  //  count the layers to be able to reserve the number of view_ops
  size_t nlayers = 0;
  for (LayerPropertiesConstIterator lp = get_properties ().begin_const_recursive (); !lp.at_end (); ++lp) {
    if (! lp->has_children ()) {
      ++nlayers;
    }
  }

  std::vector <lay::ViewOp> view_ops;
  view_ops.reserve (nlayers * planes_per_layer + special_planes_before + special_planes_after);

  lay::color_t box_color;
  if (! m_box_color.isValid ()) {
    box_color = mp_canvas->foreground_color ().rgb ();
  } else {
    box_color = m_box_color.rgb ();
  }

  //  cell boxes
  if (m_cell_box_visible) {

    lay::ViewOp vop;

    //  context level
    if (m_ctx_color.isValid ()) {
      vop = lay::ViewOp (m_ctx_color.rgb (), lay::ViewOp::Copy, 0);
    } else {
      vop = lay::ViewOp (lay::LayerProperties::brighter (box_color, brightness_for_context), lay::ViewOp::Copy, 0);
    }

    //  fill, frame, text, vertex
    view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));
    view_ops.push_back (vop);
    view_ops.push_back (vop);
    view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));

    //  child level
    if (m_child_ctx_color.isValid ()) {
      vop = lay::ViewOp (m_child_ctx_color.rgb (), lay::ViewOp::Copy, 0);
    } else {
      vop = lay::ViewOp (lay::LayerProperties::brighter (box_color, brightness_for_context), lay::ViewOp::Copy, 0);
    }

    //  fill, frame, text, vertex
    view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));
    view_ops.push_back (vop);
    view_ops.push_back (vop);
    view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));

    //  current level
    vop = lay::ViewOp (box_color, lay::ViewOp::Copy, 0);

    //  fill, frame, text, vertex
    view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));
    view_ops.push_back (vop);
    view_ops.push_back (vop);
    view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));

  } else {
    //  invisible
    for (unsigned int i = 0; i < (unsigned int) planes_per_layer; ++i) {  //  frame, fill, vertex, text
      view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));
    }
  }

  //  sanity check: number of planes defined in layRedrawThreadWorker must match to view_ops layout
  tl_assert (view_ops.size () == (size_t)cell_box_planes);

  //  produce the ViewOps for the guiding shapes

  color_t gs_color = box_color;
  if (m_guiding_shape_color.isValid ()) {
    gs_color = m_guiding_shape_color.rgb ();
  }

  for (int ctx = 0; ctx < 3; ++ctx) { // 0 (context), 1 (child), 2 (current)

    unsigned int di_off = 0;

    lay::ViewOp::Mode mode = lay::ViewOp::Copy;

    color_t fill_color, frame_color, text_color;
    int dp = 1; // no stipples for guiding shapes 

    if (ctx == 0) {

      //  context planes
      if (m_ctx_color.isValid ()) {
        frame_color = text_color = fill_color = m_ctx_color.rgb ();
      } else {
        frame_color = text_color = fill_color = lay::LayerProperties::brighter (gs_color, brightness_for_context);
      }

      if (m_ctx_hollow) {
        dp = 1;
      }

    } else if (ctx == 1) {

      //  child level planes (if used)
      if (m_child_ctx_color.isValid ()) {
        frame_color = text_color = fill_color = m_child_ctx_color.rgb ();
      } else {
        frame_color = text_color = fill_color = lay::LayerProperties::brighter (gs_color, brightness_for_child_context);
      }

      if (m_child_ctx_hollow) {
        dp = 1;
      }

    } else {

      //  current level planes
      frame_color = text_color = fill_color = gs_color;

    }

    if (m_guiding_shape_visible) {

      //  fill 
      view_ops.push_back (lay::ViewOp (fill_color, mode, dp | di_off)); // fill

      //  frame 
      view_ops.push_back (lay::ViewOp (frame_color, mode, 0, lay::ViewOp::Rect, m_guiding_shape_line_width));

      //  text 
      if (m_text_visible) {
        view_ops.push_back (lay::ViewOp (text_color, mode, 0));
      } else {
        view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));
      }

      // vertex 
      view_ops.push_back (lay::ViewOp (frame_color, mode, 0, lay::ViewOp::Rect, m_guiding_shape_vertex_size /*mark size*/)); // vertex

    } else {
      view_ops.push_back (lay::ViewOp ());
      view_ops.push_back (lay::ViewOp ());
      view_ops.push_back (lay::ViewOp ());
      view_ops.push_back (lay::ViewOp ());
    }

  }

  //  sanity check: number of planes defined in layRedrawThreadWorker must match to view_ops layout
  tl_assert (view_ops.size () == (size_t)special_planes_before);

  bool animated = false;

  for (int ctx = 0; ctx < 3; ++ctx) { // 0 (context), 1 (child), 2 (current)

    unsigned int ilayer = 0;
    for (LayerPropertiesConstIterator lp = get_properties ().begin_const_recursive (); !lp.at_end (); ++lp, ++ilayer) {

      //  because accessing the LayerPropertiesNode with lp->... is not quite efficient, we get the pointer here:
      const lay::LayerPropertiesNode *l = &*lp;

      if (l->has_children ()) {
        continue;
      }

      bool animate_visible = true;
      unsigned int di_off = m_stipple_offset ? ((ilayer % 32) << 16) : 0;

      if (l->animation (true /*real*/)) {

        animated = true;
        if (! m_animated) {
          m_animated = true;
          m_phase = 0;
        }

        if (l->animation (true /*real*/) == 1) {
          // scrolling 
          di_off = ((m_phase % 32) << 16);
        } else if (l->animation (true /*real*/) == 2) {
          // blinking
          animate_visible = ((m_phase & 1) == 0);
        } else {
          // inversly blinking
          animate_visible = ((m_phase & 1) != 0);
        }

      }

      if (l->visible (true /*real*/) && animate_visible) {

        lay::ViewOp::Mode mode = lay::ViewOp::Copy;
        if (l->transparent (true /*real*/)) {
          if (bright_background) {
            mode = lay::ViewOp::And;
          } else {
            mode = lay::ViewOp::Or;
          }
        }

        color_t fill_color, frame_color, text_color;
        int dp = m_no_stipples ? 1 : l->dither_pattern (true /*real*/);

        if (ctx == 0) {

          //  context planes
          if (m_ctx_color.isValid ()) {
            frame_color = text_color = fill_color = m_ctx_color.rgb ();
          } else {
            fill_color = l->eff_fill_color_brighter (true /*real*/, brightness_for_context);
            frame_color = l->eff_frame_color_brighter (true /*real*/, brightness_for_context);
            if (m_text_color.isValid ()) {
              text_color = lay::LayerProperties::brighter (m_text_color.rgb (), brightness_for_context);
            } else {
              text_color = frame_color;
            }
          }

          if (m_ctx_hollow) {
            dp = 1;
          }

        } else if (ctx == 1) {

          //  child level planes (if used)
          if (m_child_ctx_color.isValid ()) {
            frame_color = text_color = fill_color = m_child_ctx_color.rgb ();
          } else {
            fill_color = l->eff_fill_color_brighter (true /*real*/, brightness_for_child_context);
            frame_color = l->eff_frame_color_brighter (true /*real*/, brightness_for_child_context);
            if (m_text_color.isValid ()) {
              text_color = lay::LayerProperties::brighter (m_text_color.rgb (), brightness_for_child_context);
            } else {
              text_color = frame_color;
            }
          }

          if (m_child_ctx_hollow) {
            dp = 1;
          }

        } else {

          //  current level planes
          fill_color = l->eff_fill_color (true /*real*/);
          frame_color = l->eff_frame_color (true /*real*/);
          if (m_text_color.isValid ()) {
            text_color = m_text_color.rgb ();
          } else {
            text_color = frame_color;
          }

        }

        //  fill 
        view_ops.push_back (lay::ViewOp (fill_color, mode, dp | di_off)); // fill

        //  frame 
        int lw = l->width (true /*real*/);
        if (lw < 0) {
          //  default line width is 0 for parents and 1 for leafs
          lw = l->has_children () ? 0 : 1;
        }
        view_ops.push_back (lay::ViewOp (frame_color, mode, 0, lay::ViewOp::Rect, lw));

        //  text 
        if (m_text_visible) {
          view_ops.push_back (lay::ViewOp (text_color, mode, 0));
        } else {
          view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));
        }
        // vertex 
        view_ops.push_back (lay::ViewOp (frame_color, mode, 0, lay::ViewOp::Cross, l->marked (true /*real*/) ? 9/*mark size*/ : 0)); // vertex

      } else {
        for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) {
          view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0));
        }
      }

    }

  }

  if (! animated) {
    m_animated = false;
    m_phase = 0;
  }

  mp_canvas->set_view_ops (view_ops);
}

void
LayoutView::guiding_shapes_visible (bool v)
{
  if (v != m_guiding_shape_visible) {
    m_guiding_shape_visible = v;
    update_content ();
  }
}

void
LayoutView::guiding_shapes_color (QColor c)
{
  if (c != m_guiding_shape_color) {
    m_guiding_shape_color = c;
    update_content ();
  }
}

void
LayoutView::guiding_shapes_line_width (int v)
{
  if (v != m_guiding_shape_line_width) {
    m_guiding_shape_line_width = v;
    update_content ();
  }
}

void
LayoutView::guiding_shapes_vertex_size (int v)
{
  if (v != m_guiding_shape_vertex_size) {
    m_guiding_shape_vertex_size = v;
    update_content ();
  }
}

void 
LayoutView::draw_array_border_instances (bool m)
{
  if (m != m_draw_array_border_instances) {
    m_draw_array_border_instances = m;
    redraw ();
  }
}

void 
LayoutView::drop_small_cells (bool m)
{
  if (m != m_drop_small_cells) {
    m_drop_small_cells = m;
    redraw ();
  }
}

void 
LayoutView::drop_small_cells_value (unsigned int s)
{
  if (s != m_drop_small_cells_value) {
    m_drop_small_cells_value = s;
    redraw ();
  }
}

void 
LayoutView::drop_small_cells_cond (drop_small_cells_cond_type t)
{
  if (t != m_drop_small_cells_cond) {
    m_drop_small_cells_cond = t;
    redraw ();
  }
}

void 
LayoutView::cell_box_color (QColor c)
{
  if (c != m_box_color) {
    m_box_color = c;
    update_content ();
  }
}

void 
LayoutView::cell_box_text_transform (bool xform)
{
  if (xform != m_box_text_transform) {
    m_box_text_transform = xform;
    redraw ();
  } 
}

void 
LayoutView::cell_box_text_font (unsigned int f)
{
  if (f != m_box_font) {
    m_box_font = f;
    redraw ();
  } 
}

void 
LayoutView::set_hier_levels (std::pair<int, int> l)
{
  if (l != get_hier_levels ()) {

    if (mp_min_hier_spbx) {
      mp_min_hier_spbx->setValue (l.first);
      mp_min_hier_spbx->setMaximum (l.second);
    }
    if (mp_max_hier_spbx) {
      mp_max_hier_spbx->setValue (l.second);
      mp_max_hier_spbx->setMinimum (l.first);
    }

    m_from_level = l.first;
    m_to_level = l.second;

    //  notify all connected observers
    m_hier_levels_changed_observers.signal_observers ();

    redraw ();

  } 
}

std::pair<int, int> 
LayoutView::get_hier_levels () const
{
  return std::make_pair (m_from_level, m_to_level);
}

void 
LayoutView::min_hier_changed (int i)
{
  mp_max_hier_spbx->setMinimum (i);
  set_hier_levels (std::make_pair (i, m_to_level));
}

void 
LayoutView::max_hier_changed (int i)
{
  mp_min_hier_spbx->setMaximum (i);
  set_hier_levels (std::make_pair (m_from_level, i));
}

/**
 *  @brief set the maximum hierachy level to the number of levels available
 */
void 
LayoutView::max_hier ()
{
  //  determine the maximum level of hierarchies
  int max_level = max_hier_level ();

  //  and set the levels
  if (max_level > 0) {
    set_hier_levels (std::make_pair (std::min (m_from_level, max_level), max_level));
  }
}

/**
 *  @brief determine the maximum hierarchy level
 */
int
LayoutView::max_hier_level () const
{
  int max_level = 0;
  for (std::vector <CellView>::const_iterator cv = m_cellviews.begin (); cv != m_cellviews.end (); ++cv) {
    if (cv->is_valid ()) {
      int nl = cv->ctx_cell ()->hierarchy_levels () + 1;
      if (nl > max_level) {
        max_level = nl;
      }
    }
  }
  return max_level;
}

/**
 *  @brief Returns a value indicating whether the maximum level is shown
 */
bool 
LayoutView::has_max_hier () const
{
  int ml = max_hier_level ();
  return ml > 0 && m_to_level >= ml;
}

void
LayoutView::set_palette (const lay::ColorPalette &p)
{
  m_palette = p;
}

void
LayoutView::set_palette (const lay::StipplePalette &p)
{
  m_stipple_palette = p;
}

void
LayoutView::ctx_color (QColor c)
{
  if (c != m_ctx_color) {
    m_ctx_color = c;
    update_content ();
  }
}

void
LayoutView::ctx_dimming (int d)
{
  if (d != m_ctx_dimming) {
    m_ctx_dimming = d;
    update_content ();
  }
}

void
LayoutView::ctx_hollow (bool h)
{
  if (h != m_ctx_hollow) {
    m_ctx_hollow = h;
    update_content ();
  }
}

void
LayoutView::child_ctx_color (QColor c)
{
  if (c != m_child_ctx_color) {
    m_child_ctx_color = c;
    update_content ();
  }
}

void
LayoutView::child_ctx_dimming (int d)
{
  if (d != m_child_ctx_dimming) {
    m_child_ctx_dimming = d;
    update_content ();
  }
}

void
LayoutView::child_ctx_hollow (bool h)
{
  if (h != m_child_ctx_hollow) {
    m_child_ctx_hollow = h;
    update_content ();
  }
}

void
LayoutView::child_ctx_enabled (bool f)
{
  if (f != m_child_ctx_enabled) {
    m_child_ctx_enabled = f;
    update_content ();
    redraw ();
  }
}

void
LayoutView::abstract_mode_width (double w)
{
  if (fabs (w - m_abstract_mode_width) > 1e-6) {
    m_abstract_mode_width = w;
    if (m_abstract_mode_enabled) {
      redraw ();
    }
  }
}

void
LayoutView::abstract_mode_enabled (bool e)
{
  if (e != m_abstract_mode_enabled) {
    m_abstract_mode_enabled = e;
    redraw ();
  }
}

void 
LayoutView::background_color (QColor c)
{
  if (c == mp_canvas->background_color ()) {
    return;
  }

  //  replace by "real" background color if required
  if (! c.isValid ()) {
    c = palette ().color (QPalette::Normal, QPalette::Base);
  }

  QColor contrast;
  if (c.green () > 128) {
    contrast = QColor (0, 0, 0);
  } else {
    contrast = QColor (255, 255, 255);
  }

  if (mp_control_panel) {
    mp_control_panel->set_background_color (c);
    mp_control_panel->set_text_color (contrast);
  }

  if (mp_hierarchy_panel) {
    mp_hierarchy_panel->set_background_color (c);
    mp_hierarchy_panel->set_text_color (contrast);
  }

  if (mp_selection_service) {
    mp_selection_service->set_colors (c, contrast);
  }
  if (mp_zoom_service) {
    mp_zoom_service->set_colors (c, contrast);
  }

  //  Set the color for all ViewService interfaces
  for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
    lay::ViewService *svc = (*p)->view_service_interface ();
    if (svc) {
      svc->set_colors (c, contrast);
    }
  }

  mp_canvas->set_colors (c, contrast, mp_canvas->active_color ());

  update_content ();
}

void 
LayoutView::dbu_coordinates (bool f) 
{
  m_dbu_coordinates = f;
}

void 
LayoutView::absolute_coordinates (bool f) 
{
  m_absolute_coordinates = f;
}

void 
LayoutView::select_cellviews_fit (const std::vector <CellView> &cvs)
{
  if (m_cellviews != cvs) {

    m_cellview_about_to_change_observers.signal_observers ();
    for (int index = 0; index < int (m_cellviews.size ()); ++index) {
      m_cellview_about_to_change_observers.signal_observers (index);
    }
    m_cellviews_about_to_change_observers.signal_observers ();

    set_min_hier_levels (0);
    cancel (); 
    m_cellviews = cvs;
    zoom_fit ();
    finish_cellviews_changed ();

    m_cellview_changed_observers.signal_observers ();
    for (int index = 0; index < int (m_cellviews.size ()); ++index) {
      m_cellview_changed_observers.signal_observers (index);
    }

    update_content ();

  } else {
    zoom_fit ();
  }
}

void 
LayoutView::select_cell_dispatch (const cell_path_type &path, int cellview_index)
{
  bool set_max_hier = (m_full_hier_new_cell || has_max_hier ());
  if (m_clear_ruler_new_cell) {
    ant::Service *ant_service = get_plugin <ant::Service> ();
    if (ant_service) {
      ant_service->clear_rulers ();
    }
  }
  if (m_fit_new_cell) {
    select_cell_fit (path, cellview_index);
  } else {
    select_cell (path, cellview_index);
  }
  set_current_cell_path (cellview_index, path);
  if (set_max_hier) {
    max_hier ();
  }
}

void 
LayoutView::select_cell_fit (const cell_path_type &path, int index)
{ 
  if (index >= 0 && int (m_cellviews.size ()) > index && (m_cellviews [index].specific_path ().size () > 0 || m_cellviews [index].unspecific_path () != path)) {  

    m_cellview_about_to_change_observers.signal_observers ();
    m_cellview_about_to_change_observers.signal_observers (index);

    set_min_hier_levels (0);
    cancel (); 
    m_cellviews [index].set_unspecific_path (path);
    set_active_cellview_index (index);
    redraw ();
    zoom_fit ();

    m_cellview_changed_observers.signal_observers ();
    m_cellview_changed_observers.signal_observers (index);

    update_content ();

  }
}

void 
LayoutView::select_cell_fit (cell_index_type cell_index, int index)
{ 
  if (index >= 0 && int (m_cellviews.size ()) > index && m_cellviews [index].cell_index () != cell_index) {  

    m_cellview_about_to_change_observers.signal_observers ();
    m_cellview_about_to_change_observers.signal_observers (index);

    set_min_hier_levels (0);
    cancel (); 
    m_cellviews [index].set_cell (cell_index);
    set_active_cellview_index (index);
    redraw ();
    zoom_fit ();

    m_cellview_changed_observers.signal_observers ();
    m_cellview_changed_observers.signal_observers (index);

    update_content ();

  }
}

void 
LayoutView::select_cellviews (const std::vector <CellView> &cvs)
{
  if (m_cellviews != cvs) {

    m_cellview_about_to_change_observers.signal_observers ();
    for (int index = 0; index < int (m_cellviews.size ()); ++index) {
      m_cellview_about_to_change_observers.signal_observers (index);
    }
    m_cellviews_about_to_change_observers.signal_observers ();

    set_min_hier_levels (0);
    cancel (); 
    m_cellviews = cvs;
    redraw ();

    m_cellviews_changed_observers.signal_observers ();
    m_cellview_changed_observers.signal_observers ();
    for (int index = 0; index < int (m_cellviews.size ()); ++index) {
      m_cellview_changed_observers.signal_observers (index);
    }

    update_content ();

  }
}

void 
LayoutView::select_cell (const cell_path_type &path, int index)
{ 
  if (index >= 0 && int (m_cellviews.size ()) > index && (m_cellviews [index].specific_path ().size () > 0 || m_cellviews [index].unspecific_path () != path)) {  

    m_cellview_about_to_change_observers.signal_observers ();
    m_cellview_about_to_change_observers.signal_observers (index);

    set_min_hier_levels (0);
    cancel (); 
    m_cellviews [index].set_unspecific_path (path);
    set_active_cellview_index (index);
    redraw ();

    m_cellview_changed_observers.signal_observers ();
    m_cellview_changed_observers.signal_observers (index);

    update_content ();

  }
}

void 
LayoutView::select_cell (cell_index_type cell_index, int index)
{ 
  if (index >= 0 && int (m_cellviews.size ()) > index && (! m_cellviews [index].is_valid () || m_cellviews [index].cell_index () != cell_index)) {  

    m_cellview_about_to_change_observers.signal_observers ();
    m_cellview_about_to_change_observers.signal_observers (index);

    set_min_hier_levels (0);
    cancel (); 
    m_cellviews [index].set_cell (cell_index);
    set_active_cellview_index (index);
    redraw ();

    m_cellview_changed_observers.signal_observers ();
    m_cellview_changed_observers.signal_observers (index);

    update_content ();

  }
}

bool
LayoutView::is_cell_hidden (cell_index_type ci, int cellview_index) const
{
  if (int (m_hidden_cells.size ()) > cellview_index) {
    return m_hidden_cells [cellview_index].find (ci) != m_hidden_cells [cellview_index].end ();
  } else {
    return false;
  }
}

const std::set<LayoutView::cell_index_type> &
LayoutView::hidden_cells (int cellview_index) const
{
  if (int (m_hidden_cells.size ()) > cellview_index && cellview_index >= 0) {
    return m_hidden_cells[cellview_index];
  } else {
    static std::set<cell_index_type> empty_set;
    return empty_set;
  }
}

void 
LayoutView::hide_cell (cell_index_type ci, int cellview_index)
{
  while (int (m_hidden_cells.size ()) <= cellview_index) {
    m_hidden_cells.push_back (std::set <cell_index_type> ());
  }
  if (m_hidden_cells [cellview_index].insert (ci).second) {
    if (transacting ()) {
      manager ()->queue (this, new OpHideShowCell (ci, cellview_index, false /*=hide*/));
    } else if (manager () && ! replaying ()) {
      manager ()->clear ();
    }
    m_cell_visibility_observers.signal_observers ();
    redraw ();  //  needs redraw
  }
}

void 
LayoutView::show_cell (cell_index_type ci, int cellview_index)
{
  if (int (m_hidden_cells.size ()) > cellview_index) {
    if (m_hidden_cells [cellview_index].erase (ci) > 0) {
      if (transacting ()) {
        manager ()->queue (this, new OpHideShowCell (ci, cellview_index, true /*=show*/));
      } else if (manager () && ! replaying ()) {
        manager ()->clear ();
      }
      m_cell_visibility_observers.signal_observers ();
      redraw ();  //  needs redraw
    }
  }
}

void 
LayoutView::show_all_cells ()
{
  for (unsigned int i = 0; i < m_hidden_cells.size (); ++i) {
    if (! m_hidden_cells [i].empty ()) {
      if (transacting ()) {
        for (std::set<cell_index_type>::const_iterator ci = m_hidden_cells [i].begin (); ci != m_hidden_cells [i].end (); ++ci) {
          manager ()->queue (this, new OpHideShowCell (*ci, i, true /*=show*/));
        }
      } else if (manager () && ! replaying ()) {
        manager ()->clear ();
      }
      m_hidden_cells [i].clear ();
      m_cell_visibility_observers.signal_observers ();
      redraw ();  //  needs redraw
      return;
    }
  }
}

void 
LayoutView::min_inst_label_size (int px)
{
  if (m_min_size_for_label != px) {
    m_min_size_for_label = px;
    redraw ();
  }
}

void 
LayoutView::text_visible (bool vis)
{
  if (m_text_visible != vis) {
    m_text_visible = vis;
    update_content ();
    redraw (); //  required because we do some optimizations is text is not visible ..
  }
}

void 
LayoutView::show_properties_as_text (bool sp)
{
  if (m_show_properties != sp) {
    m_show_properties = sp;
    redraw (); //  required because we do some optimizations is text is not visible ..
  }
}

void 
LayoutView::bitmap_caching (bool l)
{
  if (m_bitmap_caching != l) {
    m_bitmap_caching = l;
    redraw (); //  required because we do some optimizations is text is not visible ..
  }
}

void 
LayoutView::text_lazy_rendering (bool l)
{
  if (m_text_lazy_rendering != l) {
    m_text_lazy_rendering = l;
    redraw (); //  required because we do some optimizations is text is not visible ..
  }
}

void 
LayoutView::cell_box_visible (bool vis)
{
  if (m_cell_box_visible != vis) {
    m_cell_box_visible = vis;
    update_content ();
  }
}

void 
LayoutView::text_font (unsigned int f)
{
  if (m_text_font != f) {
    m_text_font = f;
    redraw ();
  }
}

void 
LayoutView::default_text_size (double fs)
{
  if (m_default_text_size != fs) {
    m_default_text_size = fs;
    redraw ();
  }
}

void 
LayoutView::clear_ruler_new_cell (bool f)
{
  m_clear_ruler_new_cell = f;
}
  
void 
LayoutView::full_hier_new_cell (bool f)
{
  m_full_hier_new_cell = f;
}

double
LayoutView::pan_distance () const
{
  return m_pan_distance;
}

void
LayoutView::pan_distance (double pd) 
{
  m_pan_distance = pd;
}

void 
LayoutView::fit_new_cell (bool f)
{
  m_fit_new_cell = f;
}
  
void 
LayoutView::apply_text_trans (bool f)
{
  if (m_apply_text_trans != f) {
    m_apply_text_trans = f;
    redraw ();
  }
}
  
void 
LayoutView::offset_stipples (bool f)
{
  if (m_stipple_offset != f) {
    m_stipple_offset = f;
    update_content ();
  }
}
  
void 
LayoutView::no_stipples (bool f)
{
  if (m_no_stipples != f) {
    m_no_stipples = f;
    if (mp_control_panel) {
      mp_control_panel->set_no_stipples (m_no_stipples);
    }
    update_content ();
  }
}
  
void 
LayoutView::text_color (QColor c)
{
  if (m_text_color != c) {
    m_text_color = c;
    update_content ();
  }
}

bool
LayoutView::has_selection ()
{
  if (mp_control_panel && mp_control_panel->has_focus ()) {
    return mp_control_panel->has_selection ();
  } else if (mp_hierarchy_panel && mp_hierarchy_panel->has_focus ()) {
    return mp_hierarchy_panel->has_selection ();
  } else {
    return lay::Editables::selection_size () > 0;
  }
}

void
LayoutView::paste ()
{
  clear_selection ();

  {
    db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste")));

    //  let the receivers sort out who is pasting what ..
    if (mp_hierarchy_panel) {
      mp_hierarchy_panel->paste ();
    }
    if (mp_control_panel) {
      mp_control_panel->paste ();
    }
    lay::Editables::paste ();
  }

  //  if we change the state, save it before
  store_state ();

  db::DBox sel_bbox = selection_bbox ();
  if (! sel_bbox.empty ()) {
    if (m_paste_display_mode == 1) {
      // just make selection visible, i.e. shift window somewhat
      pan_center (sel_bbox.center ());
    } else if (m_paste_display_mode == 2) {
      // or: make selection fit into the screen
      zoom_fit_sel ();
    }
  }
}

void
LayoutView::copy ()
{
  if (mp_hierarchy_panel && mp_hierarchy_panel->has_focus ()) {
    mp_hierarchy_panel->copy ();
  } else if (mp_control_panel && mp_control_panel->has_focus ()) {
    mp_control_panel->copy ();
  } else {
    lay::Editables::copy ();
  }
}

void
LayoutView::cut ()
{
  if (mp_hierarchy_panel && mp_hierarchy_panel->has_focus ()) {
    //  TODO: currently the hierarchy panel's cut function does it's own transaction handling.
    //  Otherwise the cut function is not working propertly.
    mp_hierarchy_panel->cut ();
  } else if (mp_control_panel && mp_control_panel->has_focus ()) {
    db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut Layers")));
    mp_control_panel->cut ();
  } else {
    db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut")));
    lay::Editables::cut ();
  }
}

void 
LayoutView::cm_align_cell_origin ()
{
  int cv_index = active_cellview_index ();
  if (cv_index >= 0) {

    const db::Cell *cell = cellview (cv_index).cell ();
    if (! cell) {
      return;
    }
    if (cell->is_proxy ()) {
      throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell")));
    }

    lay::AlignCellOptionsDialog dialog (this);
    if (dialog.exec (m_align_cell_origin_mode_x, m_align_cell_origin_mode_y, m_align_cell_origin_visible_layers, m_align_cell_adjust_parents)) {

      clear_selection ();

      if (manager ()) {
        manager ()->transaction (tl::to_string (QObject::tr ("Align cell origin"))); 
      }

      db::Box bbox; 

      if (m_align_cell_origin_visible_layers) {
        for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
          if (! l->has_children () && l->layer_index () >= 0 && l->cellview_index () == cv_index && l->visible (true /*real*/)) {
            bbox += cell->bbox (l->layer_index ());
          }
        }
      } else {
        bbox = cell->bbox ();
      }

      db::Coord refx, refy;
      switch (m_align_cell_origin_mode_x) {
      case -1:
        refx = bbox.left ();
        break;
      case 1:
        refx = bbox.right ();
        break;
      default:
        refx = bbox.center ().x ();
        break;
      }
      switch (m_align_cell_origin_mode_y) {
      case -1:
        refy = bbox.bottom ();
        break;
      case 1:
        refy = bbox.top ();
        break;
      default:
        refy = bbox.center ().y ();
        break;
      }

      db::Trans t (db::Vector (-refx, -refy));
      db::Layout &layout = cellview (cv_index)->layout ();
      db::Cell &nc_cell = layout.cell (cell->cell_index ());

      for (unsigned int i = 0; i < layout.layers (); ++i) {
        if (layout.is_valid_layer (i)) {
          db::Shapes &shapes = nc_cell.shapes (i);
          for (db::Shapes::shape_iterator s = shapes.begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
            shapes.transform (*s, t);
          }
        }
      }

      for (db::Cell::const_iterator inst = nc_cell.begin (); ! inst.at_end (); ++inst) {
        nc_cell.transform (*inst, t);
      }

      if (m_align_cell_adjust_parents) {

        std::vector<std::pair<db::Cell *, db::Instance> > insts_to_modify;
        for (db::Cell::parent_inst_iterator pi = nc_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
          insts_to_modify.push_back (std::make_pair (& layout.cell (pi->parent_cell_index ()), pi->child_inst ()));
        }

        db::Trans ti (db::Vector (refx, refy));
        for (std::vector<std::pair<db::Cell *, db::Instance> >::const_iterator im = insts_to_modify.begin (); im != insts_to_modify.end (); ++im) {
          im->first->transform (im->second, db::Trans (db::Vector::from_double (im->second.complex_trans ().trans (db::Vector (refx, refy)))));
        }

      }

      if (manager ()) {
        manager ()->commit ();
      }

    }

  }
}

void
LayoutView::cm_cell_user_properties ()
{
  if (! mp_hierarchy_panel) {
    return;
  }

  int cv_index = active_cellview_index ();
  cell_path_type path;
  mp_hierarchy_panel->current_cell (cv_index, path);

  if (cv_index >= 0 && path.size () > 0) {

    db::Layout &layout = cellview (cv_index)->layout ();
    db::Cell &cell = layout.cell (path.back ());
    db::properties_id_type prop_id = cell.prop_id ();

    lay::UserPropertiesForm props_form (this);
    if (props_form.show (this, cv_index, prop_id)) {

      if (manager ()) {
        manager ()->transaction (tl::to_string (QObject::tr ("Edit cell's user propertes"))); 
        cell.prop_id (prop_id);
        manager ()->commit ();
      } else {
        cell.prop_id (prop_id);
      }

    }

  }
}

void
LayoutView::cm_cell_replace ()
{
  if (! mp_hierarchy_panel) {
    return;
  }

  int cv_index = active_cellview_index ();
  std::vector<cell_path_type> paths;
  mp_hierarchy_panel->selected_cells (cv_index, paths);

  if (cv_index >= 0 && paths.size () > 0) {

    if (paths.size () > 1) {
      throw tl::Exception (tl::to_string (QObject::tr ("Replace cell cannot be used when multiple cells are selected")));
    }

    db::Layout &layout = cellview (cv_index)->layout ();

    bool needs_to_ask = false;
    for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) {
      if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) {
        needs_to_ask = true;
      }
    }


    lay::ReplaceCellOptionsDialog mode_dialog (this);

    db::cell_index_type with_cell = paths.front ().back ();
    int mode = needs_to_ask ? m_del_cell_mode : 0;

    if (mode_dialog.exec (cellview (cv_index), mode, with_cell)) {

      if (needs_to_ask) {
        m_del_cell_mode = mode;
      }

      if (with_cell != paths.front ().back ()) {

        //  remember the current path
        cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ());

        clear_selection ();

        manager ()->transaction (tl::to_string (QObject::tr ("Replace cells"))); 

        //  replace instances of the target cell with the new cell 

        db::Cell &target_cell = layout.cell (paths.front ().back ());

        std::vector<std::pair<db::cell_index_type, db::Instance> > parents;
        for (db::Cell::parent_inst_iterator pi = target_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
          parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ()));
        }

        for (std::vector<std::pair<db::cell_index_type, db::Instance> >::const_iterator p = parents.begin (); p != parents.end (); ++p) {
          db::CellInstArray ia = p->second.cell_inst ();
          ia.object ().cell_index (with_cell);
          layout.cell (p->first).replace (p->second, ia);
        }

        std::set<db::cell_index_type> cells_to_delete;
        for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
          if (! p->empty () && layout.is_valid_cell_index (p->back ())) {
            cells_to_delete.insert (p->back ());
            if (mode == 2) {
              layout.cell (p->back ()).collect_called_cells (cells_to_delete);
            }
          }
        }

        //  support a propagation use case:
        std::set<db::cell_index_type> cells_below_replacement_cell;
        cells_below_replacement_cell.insert (with_cell);
        layout.cell (with_cell).collect_called_cells (cells_below_replacement_cell);
        for (std::set<db::cell_index_type>::const_iterator c = cells_below_replacement_cell.begin (); c != cells_below_replacement_cell.end (); ++c) {
          cells_to_delete.erase (*c);
        }

        if (mode == 0 || mode == 2) {
          layout.delete_cells (cells_to_delete);
        } else if (mode == 1) {
          layout.prune_cells (cells_to_delete);
        }

        layout.cleanup ();

        manager ()->commit ();

        //  If one of the cells in the path was deleted, establish a valid path

        bool needs_update = false;
        for (size_t i = cell_path.size (); i > 0; ) {
          --i;
          if (! layout.is_valid_cell_index (cell_path [i])) {
            cell_path.erase (cell_path.begin () + i, cell_path.end ());
            needs_update = true;
          }
        }

        if (needs_update) {
          select_cell (cell_path, cv_index);
        }

      }

    }

  }
}

void
LayoutView::cm_lay_convert_to_static ()
{
  //  end move operations, cancel edit operations
  cancel_edits ();
  clear_selection ();

  int cv_index = active_cellview_index ();
  if (cv_index >= 0) {

    db::Layout &layout = cellview (cv_index)->layout ();

    manager ()->transaction (tl::to_string (QObject::tr ("Convert all cells to static"))); 

    std::vector<db::cell_index_type> cells;
    for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) {
      cells.push_back (c->cell_index ());
    }

    std::map<db::cell_index_type, db::cell_index_type> cell_map;
    for (std::vector<db::cell_index_type>::const_iterator c = cells.begin (); c != cells.end (); ++c) {
      if (layout.is_valid_cell_index (*c)) {
        db::cell_index_type new_cell = layout.convert_cell_to_static (*c);
        if (new_cell != *c) {
          cell_map.insert (std::make_pair (*c, new_cell));
        }
      }
    }

    //  rewrite instances
    for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
      for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) {
        std::map<db::cell_index_type, db::cell_index_type>::const_iterator cm = cell_map.find (i->cell_index ());
        if (cm != cell_map.end ()) {
          db::CellInstArray ci = i->cell_inst ();
          ci.object ().cell_index (cm->second);
          c->replace (*i, ci);
        }
      }
    }

    layout.cleanup ();

    manager ()->commit ();

  }
}

void
LayoutView::cm_cell_convert_to_static ()
{
  if (! mp_hierarchy_panel) {
    return;
  }

  int cv_index = active_cellview_index ();
  std::vector<cell_path_type> paths;
  mp_hierarchy_panel->selected_cells (cv_index, paths);

  if (cv_index >= 0 && paths.size () > 0) {

    db::Layout &layout = cellview (cv_index)->layout ();

    //  remember the current path
    cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ());

    clear_selection ();

    manager ()->transaction (tl::to_string (QObject::tr ("Convert cells to static"))); 

    std::map<db::cell_index_type, db::cell_index_type> cell_map;

    for (std::vector<cell_path_type>::iterator p = paths.begin (); p != paths.end (); ++p) {
      if (! p->empty () && layout.is_valid_cell_index (p->back ())) {
        db::cell_index_type new_cell = layout.convert_cell_to_static (p->back ());
        if (new_cell != p->back ()) {
          cell_map.insert (std::make_pair (p->back (), new_cell));
          p->back () = new_cell;
        }
      }
    }

    //  rewrite instances
    for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
      for (db::Cell::const_iterator i = c->begin (); ! i.at_end (); ++i) {
        std::map<db::cell_index_type, db::cell_index_type>::const_iterator cm = cell_map.find (i->cell_index ());
        if (cm != cell_map.end ()) {
          db::CellInstArray ci = i->cell_inst ();
          ci.object ().cell_index (cm->second);
          c->replace (*i, ci);
        }
      }
    }

    layout.cleanup ();

    manager ()->commit ();

    //  If one of the cells in the path was deleted, establish a valid path

    bool needs_update = false;
    for (size_t i = cell_path.size (); i > 0; ) {
      --i;
      if (! layout.is_valid_cell_index (cell_path [i])) {
        cell_path.erase (cell_path.begin () + i, cell_path.end ());
        needs_update = true;
      }
    }

    if (needs_update) {
      select_cell (cell_path, cv_index);
    }

  }
}

void
LayoutView::cm_cell_delete ()
{
  if (! mp_hierarchy_panel) {
    return;
  }

  int cv_index = active_cellview_index ();
  std::vector<cell_path_type> paths;
  mp_hierarchy_panel->selected_cells (cv_index, paths);

  if (cv_index >= 0 && paths.size () > 0) {

    db::Layout &layout = cellview (cv_index)->layout ();

    bool needs_to_ask = false;
    for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end () && ! needs_to_ask; ++p) {
      if (layout.is_valid_cell_index (p->back ()) && ! layout.cell (p->back ()).is_leaf ()) {
        needs_to_ask = true;
      }
    }

    int mode = m_del_cell_mode;
    if (! needs_to_ask) {
      mode = 0;
    }

    lay::DeleteCellModeDialog mode_dialog (this);
    if (! needs_to_ask || mode_dialog.exec (mode)) {

      if (needs_to_ask) {
        m_del_cell_mode = mode;
      }

      //  remember the current path
      cell_path_type cell_path (cellview (cv_index).combined_unspecific_path ());

      clear_selection ();

      std::set<db::cell_index_type> cells_to_delete;
      for (std::vector<cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
        if (! p->empty () && layout.is_valid_cell_index (p->back ())) {
          cells_to_delete.insert (p->back ());
          if (mode == 2) {
            layout.cell (p->back ()).collect_called_cells (cells_to_delete);
          }
        }
      }

      manager ()->transaction (tl::to_string (QObject::tr ("Delete cells"))); 

      if (mode == 0 || mode == 2) {
        layout.delete_cells (cells_to_delete);
      } else if (mode == 1) {
        layout.prune_cells (cells_to_delete);
      }

      layout.cleanup ();

      manager ()->commit ();

      //  If one of the cells in the path was deleted, establish a valid path

      bool needs_update = false;
      for (size_t i = cell_path.size (); i > 0; ) {
        --i;
        if (! layout.is_valid_cell_index (cell_path [i])) {
          cell_path.erase (cell_path.begin () + i, cell_path.end ());
          needs_update = true;
        }
      }

      if (needs_update) {
        select_cell (cell_path, cv_index);
      }

    }

  }
}

void 
LayoutView::cm_layer_copy ()
{
  if (mp_control_panel) {
    mp_control_panel->copy ();
  }
}

void 
LayoutView::cm_layer_cut ()
{
  if (mp_control_panel) {
    db::Transaction trans (manager (), tl::to_string (QObject::tr ("Cut Layers"))); 
    mp_control_panel->cut ();
  }
}

void 
LayoutView::cm_layer_paste ()
{
  if (mp_control_panel) {
    db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Layers"))); 
    mp_control_panel->paste ();
  }
}

void
LayoutView::cm_cell_cut ()
{
  if (mp_hierarchy_panel) {
    //  TODO: currently the hierarchy panel's cut function does it's own transaction handling.
    //  Otherwise the cut function is not working propertly.
    mp_hierarchy_panel->cut ();
  }
}

void
LayoutView::cm_cell_paste ()
{
  if (mp_hierarchy_panel) {
    db::Transaction trans (manager (), tl::to_string (QObject::tr ("Paste Cells"))); 
    mp_hierarchy_panel->paste ();
  }
}

void
LayoutView::cm_cell_copy ()
{
  if (mp_hierarchy_panel) {
    mp_hierarchy_panel->copy ();
  }
}

void
LayoutView::cm_cell_flatten ()
{
  if (! mp_hierarchy_panel) {
    return;
  }

  tl_assert (is_editable ());

  int cv_index = active_cellview_index ();
  if (cv_index >= 0) {

    const lay::CellView &cv = cellview (cv_index);
    if (cv.is_valid ()) {

      std::vector<HierarchyControlPanel::cell_path_type> paths;
      mp_hierarchy_panel->selected_cells (cv_index, paths);
      if (paths.empty ()) {
        throw tl::Exception (tl::to_string (QObject::tr ("No cells selected for flattening")));
      }

      for (std::vector<HierarchyControlPanel::cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
        if (p->size () > 0 && cv->layout ().cell (p->back ()).is_proxy ()) {
          throw tl::Exception (tl::to_string (QObject::tr ("Cannot use this function on a PCell or library cell")));
        }
      }

      FlattenInstOptionsDialog options_dialog (this);

      int flatten_insts_levels = -1;
      bool prune = true;
      if (options_dialog.exec (flatten_insts_levels, prune) && flatten_insts_levels != 0) {

        bool supports_undo = true;

        if (db::transactions_enabled ()) {

          lay::TipDialog td (lay::MainWindow::instance (), 
                             tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), 
                             "flatten-undo-buffering",
                             lay::TipDialog::yesnocancel_buttons);

          lay::TipDialog::button_type button = lay::TipDialog::null_button;
          td.exec_dialog (button);
          if (button == lay::TipDialog::cancel_button) {
            return;
          }

          supports_undo = (button == lay::TipDialog::yes_button);

        } else {
          supports_undo = false;
        }

        cancel_edits ();
        clear_selection ();

        if (manager ()) {
          if (! supports_undo) {
            manager ()->clear ();
          } else {
            manager ()->transaction (tl::to_string (QObject::tr ("Flatten cell")));
          }
        }

        db::Layout &layout = cv->layout ();

        std::set<db::cell_index_type> child_cells;
        for (std::vector<HierarchyControlPanel::cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
          if (p->size () > 0) {
            layout.cell (p->back ()).collect_called_cells (child_cells);
          }
        }

        //  don't flatten cells which are child cells of the cells to flatten
        std::set<db::cell_index_type> cells_to_flatten;
        for (std::vector<HierarchyControlPanel::cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
          if (p->size () > 0 && child_cells.find (p->back ()) == child_cells.end ()) {
            cells_to_flatten.insert (p->back ());
          }
        }

        for (std::set<db::cell_index_type>::const_iterator c = cells_to_flatten.begin (); c != cells_to_flatten.end (); ++c) {
          db::Cell &target_cell = layout.cell (*c);
          layout.flatten (target_cell, flatten_insts_levels, prune);
        }

        layout.cleanup ();

        if (supports_undo && manager ()) {
          manager ()->commit ();
        }

      }

    }

  }
}

void
LayoutView::cm_cell_rename ()
{
  if (! mp_hierarchy_panel) {
    return;
  }

  int cv_index = active_cellview_index ();
  cell_path_type path;
  mp_hierarchy_panel->current_cell (cv_index, path);

  if (cv_index >= 0 && path.size () > 0) {

    lay::RenameCellDialog name_dialog (this);

    db::Layout &layout = cellview (cv_index)->layout ();
    std::string name (layout.cell_name (path.back ()));
    if (name_dialog.exec (layout, name)) {

      if (manager ()) {
        manager ()->transaction (tl::to_string (QObject::tr ("Rename cell"))); 
      }

      layout.rename_cell (path.back (), name.c_str ());

      if (manager ()) {
        manager ()->commit ();
      }

    }

  }
}

void
LayoutView::cm_cell_select ()
{
  if (mp_hierarchy_panel) {
    mp_hierarchy_panel->cm_cell_select ();
  }
}

void
LayoutView::cm_open_current_cell ()
{
  set_current_cell_path (active_cellview_index (), cellview (active_cellview_index ()).combined_unspecific_path ());
}

void
LayoutView::cm_cell_hide ()
{
  if (mp_hierarchy_panel) {

    std::vector<HierarchyControlPanel::cell_path_type> paths;
    mp_hierarchy_panel->selected_cells (active_cellview_index (), paths);

    manager ()->transaction (tl::to_string (QObject::tr ("Hide cell"))); 

    for (std::vector<HierarchyControlPanel::cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
      if (! p->empty ()) {
        hide_cell (p->back (), active_cellview_index ());
      }
    }

    manager ()->commit ();

  }
}

void
LayoutView::cm_cell_show ()
{
  if (mp_hierarchy_panel) {

    std::vector<HierarchyControlPanel::cell_path_type> paths;
    mp_hierarchy_panel->selected_cells (active_cellview_index (), paths);

    manager ()->transaction (tl::to_string (QObject::tr ("Hide cell"))); 

    for (std::vector<HierarchyControlPanel::cell_path_type>::const_iterator p = paths.begin (); p != paths.end (); ++p) {
      if (! p->empty ()) {
        show_cell (p->back (), active_cellview_index ());
      }
    }

    manager ()->commit ();

  }
}

void
LayoutView::cm_cell_show_all ()
{
  if (mp_hierarchy_panel) {
    manager ()->transaction (tl::to_string (QObject::tr ("Show all cells"))); 
    show_all_cells ();
    manager ()->commit ();
  }
}

void
LayoutView::cm_select_all ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_select_all ();
  }
}

void
LayoutView::cm_new_tab ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_new_tab ();
  }
}

void
LayoutView::cm_remove_tab ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_remove_tab ();
  }
}

void
LayoutView::cm_rename_tab ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_rename_tab ();
  }
}

void
LayoutView::cm_make_invalid ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_make_invalid ();
  }
}

void
LayoutView::cm_make_valid ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_make_valid ();
  }
}

void
LayoutView::cm_hide ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_hide ();
  }
}

void
LayoutView::cm_hide_all ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_hide_all ();
  }
}

void
LayoutView::cm_show_only ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_show_only ();
  }
}

void
LayoutView::cm_show_all ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_show_all ();
  }
}

void
LayoutView::cm_show ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_show ();
  }
}

void
LayoutView::cm_rename ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_rename ();
  }
}

void
LayoutView::cm_delete ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_delete ();
  }
}

void
LayoutView::cm_insert ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_insert ();
  }
}

void
LayoutView::cm_group ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_group ();
  }
}

void
LayoutView::cm_ungroup ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_ungroup ();
  }
}

void
LayoutView::cm_source ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_source ();
  }
}

void
LayoutView::cm_sort_by_name ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_sort_by_name ();
  }
}

void
LayoutView::cm_sort_by_ild ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_sort_by_ild ();
  }
}

void
LayoutView::cm_sort_by_idl ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_sort_by_idl ();
  }
}

void
LayoutView::cm_sort_by_ldi ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_sort_by_ldi ();
  }
}

void
LayoutView::cm_sort_by_dli ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_sort_by_dli ();
  }
}

void
LayoutView::cm_regroup_by_index ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_regroup_by_index ();
  }
}

void
LayoutView::cm_regroup_by_datatype ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_regroup_by_datatype ();
  }
}

void
LayoutView::cm_regroup_by_layer ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_regroup_by_layer ();
  }
}

void
LayoutView::cm_regroup_flatten ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_regroup_flatten ();
  }
}

void
LayoutView::cm_expand_all ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_expand_all ();
  }
}

void
LayoutView::cm_add_missing ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_add_missing ();
  }
}

void 
LayoutView::add_missing_layers ()
{
  std::set <lay::ParsedLayerSource> present;
  LayerPropertiesConstIterator l = begin_layers ();
  while (! l.at_end ()) {
    if (! l->has_children ()) {
      present.insert (l->source (true /*real*/));
    }
    ++l;
  }

  std::vector <lay::ParsedLayerSource> actual;
  for (unsigned int cv = 0; cv < cellviews (); ++cv) {
    const db::Layout &layout = cellview (cv)->layout ();
    for (unsigned int l = 0; l < layout.layers (); ++l) {
      if (layout.is_valid_layer (l)) {
        actual.push_back (lay::ParsedLayerSource (layout.get_properties (l), cv));
      }
    }
  }

  std::sort (actual.begin (), actual.end ());

  for (std::vector <lay::ParsedLayerSource>::const_iterator a = actual.begin (); a != actual.end (); ++a) {
    if (present.find (*a) == present.end ()) {
      lay::LayerPropertiesNode node;
      node.attach_view (this); 
      node.set_source (*a);
      init_layer_properties (node);
      insert_layer (end_layers (), node);
    }
  }

  emit layer_order_changed ();
}

LayerState 
LayoutView::layer_snapshot () const
{
  LayerState state;
  LayerPropertiesConstIterator l = begin_layers ();
  while (! l.at_end ()) {
    if (! l->has_children ()) {
      state.present.insert (l->source (true /*real*/));
    }
    ++l;
  }
  return state;
}

void 
LayoutView::add_new_layers (const LayerState &state)
{
  std::vector <lay::ParsedLayerSource> actual;
  for (unsigned int cv = 0; cv < cellviews (); ++cv) {
    const db::Layout &layout = cellview (cv)->layout ();
    for (unsigned int l = 0; l < layout.layers (); ++l) {
      if (layout.is_valid_layer (l)) {
        actual.push_back (lay::ParsedLayerSource (layout.get_properties (l), cv));
      }
    }
  }

  std::sort (actual.begin (), actual.end ());

  bool needs_update = false;

  for (std::vector <lay::ParsedLayerSource>::const_iterator a = actual.begin (); a != actual.end (); ++a) {
    if (state.present.find (*a) == state.present.end ()) {
      needs_update = true;
      lay::LayerPropertiesNode node;
      node.attach_view (this); 
      node.set_source (*a);
      //  HINT: in editable mode it is desireable to present all layers because otherwise they cannot be
      //  made visible to populate them.
      if (is_editable () || ! node.bbox ().empty ()) {
        init_layer_properties (node);
        insert_layer (end_layers (), node);
      }
    }
  }

  if (needs_update) {
    emit layer_order_changed ();
  }
}

void
LayoutView::cm_remove_unused ()
{
  remove_unused_layers ();
}

void 
LayoutView::remove_unused_layers ()
{
  if (mp_control_panel) {
    mp_control_panel->cm_remove_unused ();
  }
}

void 
LayoutView::last_display_state ()
{
  if (m_display_state_ptr > 0) {
    m_display_state_ptr--;
    goto_view (m_display_states [m_display_state_ptr]);
  }
}

bool 
LayoutView::has_last_display_state ()
{
  return m_display_state_ptr > 0;
}

void 
LayoutView::next_display_state ()
{
  if (m_display_state_ptr + 1 < m_display_states.size ()) {
    m_display_state_ptr++;
    goto_view (m_display_states [m_display_state_ptr]);
  }
}

bool 
LayoutView::has_next_display_state ()
{
  return m_display_state_ptr + 1 < m_display_states.size ();
}

const lay::CellView &
LayoutView::active_cellview () const
{
  return cellview ((unsigned int) active_cellview_index ());
}

int 
LayoutView::active_cellview_index () const
{
  return mp_hierarchy_panel->active ();
}

void 
LayoutView::set_active_cellview_index (int index) 
{
  if (index >= 0 && index < int (m_cellviews.size ())) {
    mp_hierarchy_panel->select_active (index);
  }
}

void 
LayoutView::selected_cells_paths (int cv_index, std::vector<cell_path_type> &paths) const
{
  mp_hierarchy_panel->selected_cells (cv_index, paths);
}

void
LayoutView::current_cell_path (int cv_index, cell_path_type &path) const
{
  mp_hierarchy_panel->current_cell (cv_index, path);
}

void
LayoutView::set_current_cell_path (int cv_index, const cell_path_type &path) 
{
  mp_hierarchy_panel->set_current_cell (cv_index, path);
}

void
LayoutView::activate ()
{
  if (! m_activated) {
    for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
      if ((*p)->browser_interface () && (*p)->browser_interface ()->active ()) {
        (*p)->browser_interface ()->show ();
      }
    }
    mp_timer->start (timer_interval);
    m_activated = true;
    update_content ();
  }
}

void
LayoutView::deactivate ()
{
  for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
    if ((*p)->browser_interface ()) {
      (*p)->browser_interface ()->hide ();
    }
  }

  mp_coord_tracker->clear_current_pos ();
  mp_canvas->free_resources ();
  mp_timer->stop ();
  m_activated = false;
}

void
LayoutView::deactivate_all_browsers ()
{
  for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
    if ((*p)->browser_interface ()) {
      (*p)->browser_interface ()->deactivate ();
    }
  }
}

void
LayoutView::current_pos (double x, double y)
{
  if (m_activated) {
    if (dbu_coordinates ()) {
      double dx = 0.0, dy = 0.0;
      if (active_cellview_index () >= 0) {
        double dbu = cellview (active_cellview_index ())->layout ().dbu ();
        dx = x / dbu;
        dy = y / dbu;
      }
      mp_coord_tracker->current_pos (dx, dy, true);
    } else {
      mp_coord_tracker->current_pos (x, y, false);
    }
  }
}

void
LayoutView::stop_redraw ()
{
  mp_canvas->stop_redraw ();
}

void
LayoutView::stop ()
{
  stop_redraw ();
  deactivate_all_browsers ();
}

void 
LayoutView::mode (int m)
{
  if (m != m_mode) {

    m_mode = m;

    if (m > 0) {

      for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
        if ((*p)->plugin_declaration ()->id () == m) {
          mp_canvas->activate ((*p)->view_service_interface ());
          break;
        }
      }

    } else if (m == 0 && mp_selection_service) {
      mp_canvas->activate (mp_selection_service);
    } else if (m == -1 && mp_move_service) {
      mp_canvas->activate (mp_move_service);
    }

  }
}

bool 
LayoutView::is_move_mode () const
{
  return m_mode == -1;
}

bool 
LayoutView::is_selection_mode () const
{
  return m_mode == 0;
}
  
unsigned int 
LayoutView::intrinsic_mouse_modes (std::vector<std::string> *descriptions)
{
  if (descriptions) {
    descriptions->push_back ("select\t" + tl::to_string (QObject::tr ("Select")) + "<:select.png>");
    descriptions->push_back ("move\t" + tl::to_string (QObject::tr ("Move")) + "<:move.png>");
  }
  return 2;
}

int 
LayoutView::default_mode ()
{
  return 0; // TODO: any generic scheme? is select, should be ruler..
}

void 
LayoutView::menu_activated (const std::string &symbol)
{
  //  distribute the menu on the plugins - one should take it.
  for (std::vector<lay::Plugin *>::iterator p = mp_plugins.begin (); p != mp_plugins.end (); ++p) {
    (*p)->menu_activated (symbol);
  }
}

void 
LayoutView::rename_cellview (const std::string &name, int cellview_index)
{
  if (cellview_index >= 0 && cellview_index < int (m_cellviews.size ())) {
    if (m_cellviews [cellview_index]->name () != name) {
      m_cellviews [cellview_index]->rename (name);
      mp_hierarchy_panel->do_update_content (cellview_index);
      if (m_title.empty ()) {
        emit title_changed ();
      }
    }
  }
}

std::vector<db::CplxTrans> 
LayoutView::cv_transform_variants (int cv_index) const
{
  std::set<db::CplxTrans> trns_variants;
  for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
    if (! l->has_children ()) {
      int cvi = l->cellview_index () >= 0 ? l->cellview_index () : 0;
      if (cv_index < int (cellviews ()) && cvi == cv_index) {
        trns_variants.insert (l->trans ().begin (), l->trans ().end ());
      }
    }
  }
  return std::vector<db::CplxTrans> (trns_variants.begin (), trns_variants.end ());
}

std::vector<db::CplxTrans> 
LayoutView::cv_transform_variants (int cv_index, unsigned int layer) const
{
  if (cellview (cv_index)->layout ().is_valid_layer (layer)) {
    std::set<db::CplxTrans> trns_variants;
    for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
      if (! l->has_children () && l->layer_index () == int (layer)) {
        int cvi = l->cellview_index () >= 0 ? l->cellview_index () : 0;
        if (cv_index < int (cellviews ()) && cvi == cv_index) {
          trns_variants.insert (l->trans ().begin (), l->trans ().end ());
        }
      }
    }
    return std::vector<db::CplxTrans> (trns_variants.begin (), trns_variants.end ());
  } else {
    //  may happen if the layer is a guiding shape layer for example
    return cv_transform_variants (cv_index);
  }
}

std::map<unsigned int, std::vector<db::CplxTrans> > 
LayoutView::cv_transform_variants_by_layer (int cv_index) const
{
  std::map<unsigned int, std::vector<db::CplxTrans> > tv_map;

  for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
    if (! l->has_children () && l->layer_index () >= 0) {
      int cvi = l->cellview_index () >= 0 ? l->cellview_index () : 0;
      if (cv_index < int (cellviews ()) && cvi == cv_index) {
        std::vector<db::CplxTrans> &v = tv_map.insert (std::make_pair (l->layer_index (), std::vector<db::CplxTrans> ())).first->second;
        v.insert (v.end (), l->trans ().begin (), l->trans ().end ());
      }
    }
  }

  for (std::map<unsigned int, std::vector<db::CplxTrans> >::iterator m = tv_map.begin (); m != tv_map.end (); ++m) {
    std::sort (m->second.begin (), m->second.end ());
    m->second.erase (std::unique (m->second.begin (), m->second.end ()), m->second.end ());
  }

  return tv_map;
}

std::set< std::pair<db::CplxTrans, int> > 
LayoutView::cv_transform_variants () const
{
  std::set< std::pair<db::CplxTrans, int> > box_variants;
  for (lay::LayerPropertiesConstIterator l = begin_layers (); !l.at_end (); ++l) {
    if (! l->has_children ()) {
      unsigned int cv_index = l->cellview_index () >= 0 ? (unsigned int) l->cellview_index () : 0;
      if (cv_index < cellviews ()) {
        for (std::vector<db::CplxTrans>::const_iterator t = l->trans ().begin (); t != l->trans ().end (); ++t) {
          box_variants.insert (std::make_pair (*t, cv_index));
        }
      }
    }
  }
  return box_variants;
}

db::InstElement 
LayoutView::ascend (int index)
{
  tl_assert (int (m_cellviews.size ()) > index && m_cellviews [index].is_valid ());

  m_cellview_about_to_change_observers.signal_observers ();
  m_cellview_about_to_change_observers.signal_observers (index);

  lay::CellView::specific_cell_path_type spath (m_cellviews [index].specific_path ());
  if (spath.empty ()) {
    return db::InstElement ();
  } else {

    cancel (); 
    db::InstElement ret = spath.back ();
    spath.pop_back ();
    m_cellviews [index].set_specific_path (spath);

    store_state ();
    redraw ();

    m_cellview_changed_observers.signal_observers ();
    m_cellview_changed_observers.signal_observers (index);

    update_content ();

    return ret;

  }
}

void 
LayoutView::descend (const std::vector<db::InstElement> &path, int index)
{
  if (! path.empty () && index >= 0 && int (m_cellviews.size ()) > index && m_cellviews [index].is_valid ()) {  

    m_cellview_about_to_change_observers.signal_observers ();
    m_cellview_about_to_change_observers.signal_observers (index);

    cancel (); 

    lay::CellView::specific_cell_path_type spath (m_cellviews [index].specific_path ());
    spath.insert (spath.end (), path.begin (), path.end ());
    m_cellviews [index].set_specific_path (spath);

    store_state ();
    redraw ();

    m_cellview_changed_observers.signal_observers ();
    m_cellview_changed_observers.signal_observers (index);

    update_content ();

  }
}

bool 
LayoutView::is_editable (unsigned int /*cv_index*/) const
{
  return lay::Application::instance ()->is_editable ();
}

bool 
LayoutView::is_editable () const
{
  return lay::Application::instance ()->is_editable ();
}

void
LayoutView::message (const std::string &s, int timeout)
{
  if (lay::MainWindow::instance ()) {
    lay::MainWindow::instance ()->message (s, timeout * 1000);
  }
}

db::cell_index_type
LayoutView::new_cell (int cv_index, const std::string &cell_name) 
{
  db::cell_index_type new_ci (0);

  if (cv_index >= 0 && int (m_cellviews.size ()) > cv_index) {

    db::Layout &layout = cellview (cv_index)->layout ();
    if (! cell_name.empty () && layout.cell_by_name (cell_name.c_str ()).first) {
      throw tl::Exception (tl::to_string (QObject::tr ("A cell with that name already exists: %s")), cell_name);
    }

    if (manager ()) {
      manager ()->transaction (tl::to_string (QObject::tr ("New cell"))); 
    }

    new_ci = layout.add_cell (cell_name.empty () ? 0 : cell_name.c_str ());

    if (manager ()) {
      manager ()->commit ();
    }

  }

  return new_ci;
}

void
LayoutView::do_transform (const db::DCplxTrans &tr)
{
  //  end move operations, cancel edit operations
  cancel_edits ();
  lay::Editables::transform (tr);
}

void
LayoutView::transform_layout (const db::DCplxTrans &tr_mic)
{
  //  end move operations, cancel edit operations
  cancel_edits ();
  clear_selection ();

  int cv_index = active_cellview_index ();
  if (cv_index >= 0) {

    db::Layout &layout = cellview (cv_index)->layout ();

    db::ICplxTrans tr (db::DCplxTrans (1.0 / layout.dbu ()) * tr_mic * db::DCplxTrans (layout.dbu ()));

    bool has_proxy = false;
    for (db::Layout::const_iterator c = layout.begin (); ! has_proxy && c != layout.end (); ++c) {
      has_proxy = c->is_proxy ();
    }

    if (has_proxy && 
        QMessageBox::question (this, 
                               QObject::tr ("Transforming PCells Or Library Cells"), 
                               QObject::tr ("The layout contains PCells or library cells or both.\n"
                                            "Any changes to such cells may be lost when their layout is refreshed later.\n"
                                            "Consider using 'Convert all cells to static' before transforming the layout.\n"
                                            "\n"
                                            "Would you like to continue?\n"
                                            "Choose 'Yes' to continue anyway. Choose 'No' to cancel."), 
                               QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
      return;
    }

    if (manager ()) {
      manager ()->transaction (tl::to_string (QObject::tr ("Transform layout"))); 
      layout.transform (tr);
      manager ()->commit ();
    } else {
      active_cellview ()->layout ().transform (tr);
    }

  }
}

void 
LayoutView::cm_lay_flip_x ()
{
  transform_layout (db::DCplxTrans (db::FTrans::m90));
}

void 
LayoutView::cm_lay_flip_y ()
{
  transform_layout (db::DCplxTrans (db::FTrans::m0));
}

void 
LayoutView::cm_lay_rot_ccw ()
{
  db::DCplxTrans tr (db::DFTrans::r90);
  transform_layout (db::DCplxTrans (db::FTrans::r90));
}

void 
LayoutView::cm_lay_rot_cw ()
{
  transform_layout (db::DCplxTrans (db::FTrans::r270));
}

void 
LayoutView::cm_lay_free_rot ()
{
  bool ok = false;
  QString s = QInputDialog::getText (lay::MainWindow::instance (), 
                                     QObject::tr ("Free rotation"), 
                                     QObject::tr ("Rotation angle in degree (counterclockwise)"), 
                                     QLineEdit::Normal, QString::fromAscii ("0.0"), 
                                     &ok);

  if (ok) {

    double angle = 0.0;
    tl::from_string (tl::to_string (s), angle);

    transform_layout (db::DCplxTrans (1.0, angle, false, db::Point ()));

  }
}

void 
LayoutView::cm_lay_scale ()
{
  bool ok = false;
  QString s = QInputDialog::getText (lay::MainWindow::instance (), 
                                     QObject::tr ("Scaling"), 
                                     QObject::tr ("Scaling factor"), 
                                     QLineEdit::Normal, QString::fromAscii ("1.0"), 
                                     &ok);

  if (ok) {

    double scale = 0.0;
    tl::from_string (tl::to_string (s), scale);

    transform_layout (db::DCplxTrans (scale));

  }
}

void 
LayoutView::cm_lay_move ()
{
  lay::MoveOptionsDialog options (this);
  if (options.exec (m_move_dist)) {
    transform_layout (db::DCplxTrans (m_move_dist));
  }
}

void 
LayoutView::cm_sel_flip_x ()
{
  db::DCplxTrans tr (db::DFTrans::m90);
  db::DBox sel_bbox (lay::Editables::selection_bbox ());
  if (! sel_bbox.empty ()) {
    tr = db::DCplxTrans (sel_bbox.center ()) * tr * db::DCplxTrans (-sel_bbox.center ());
  }
  do_transform (tr);
}

void 
LayoutView::cm_sel_flip_y ()
{
  db::DCplxTrans tr (db::DFTrans::m0);
  db::DBox sel_bbox (lay::Editables::selection_bbox ());
  if (! sel_bbox.empty ()) {
    tr = db::DCplxTrans (sel_bbox.center ()) * tr * db::DCplxTrans (-sel_bbox.center ());
  }
  do_transform (tr);
}

void 
LayoutView::cm_sel_rot_ccw ()
{
  db::DCplxTrans tr (db::DFTrans::r90);
  db::DBox sel_bbox (lay::Editables::selection_bbox ());
  if (! sel_bbox.empty ()) {
    tr = db::DCplxTrans (sel_bbox.center ()) * tr * db::DCplxTrans (-sel_bbox.center ());
  }
  do_transform (tr);
}

void 
LayoutView::cm_sel_rot_cw ()
{
  db::DCplxTrans tr (db::DFTrans::r270);
  db::DBox sel_bbox (lay::Editables::selection_bbox ());
  if (! sel_bbox.empty ()) {
    tr = db::DCplxTrans (sel_bbox.center ()) * tr * db::DCplxTrans (-sel_bbox.center ());
  }
  do_transform (tr);
}

void 
LayoutView::cm_sel_free_rot ()
{
  bool ok = false;
  QString s = QInputDialog::getText (lay::MainWindow::instance (), 
                                     QObject::tr ("Free rotation"), 
                                     QObject::tr ("Rotation angle in degree (counterclockwise)"), 
                                     QLineEdit::Normal, QString::fromAscii ("0.0"), 
                                     &ok);

  if (ok) {

    double angle = 0.0;
    tl::from_string (tl::to_string (s), angle);

    db::DCplxTrans tr = db::DCplxTrans (1.0, angle, false, db::DPoint ());
    db::DBox sel_bbox (lay::Editables::selection_bbox ());
    if (! sel_bbox.empty ()) {
      tr = db::DCplxTrans (sel_bbox.center ()) * tr * db::DCplxTrans (-sel_bbox.center ());
    }
    do_transform (tr);

  }
}

void 
LayoutView::cm_sel_scale ()
{
  bool ok = false;
  QString s = QInputDialog::getText (lay::MainWindow::instance (), 
                                     QObject::tr ("Scaling"), 
                                     QObject::tr ("Scaling factor"), 
                                     QLineEdit::Normal, QString::fromAscii ("1.0"), 
                                     &ok);

  if (ok) {

    double scale = 0.0;
    tl::from_string (tl::to_string (s), scale);

    db::DCplxTrans tr = db::DCplxTrans (scale);
    db::DBox sel_bbox (lay::Editables::selection_bbox ());
    if (! sel_bbox.empty ()) {
      tr = db::DCplxTrans (sel_bbox.center ()) * tr * db::DCplxTrans (-sel_bbox.center ());
    }
    do_transform (tr);

  }
}

void 
LayoutView::cm_sel_move_to ()
{
  db::DBox sel_bbox (lay::Editables::selection_bbox ());
  if (sel_bbox.empty ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("Nothing selected to move")));
  }

  double x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5);
  double y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5);
  db::DPoint move_target (x, y);

  lay::MoveToOptionsDialog options (this);
  if (options.exec (m_move_to_origin_mode_x, m_move_to_origin_mode_y, move_target)) {

    x = sel_bbox.left () + (sel_bbox.width () * (1 + m_move_to_origin_mode_x) * 0.5);
    y = sel_bbox.bottom () + (sel_bbox.height () * (1 + m_move_to_origin_mode_y) * 0.5);

    do_transform (db::DCplxTrans (move_target - db::DPoint (x, y)));

  }
}

void 
LayoutView::cm_sel_move ()
{
  lay::MoveOptionsDialog options (this);
  if (options.exec (m_move_dist)) {
    do_transform (db::DCplxTrans (m_move_dist));
  }
}

void
LayoutView::cm_copy_layer () 
{
  struct { int *cv; int *layer; } specs [] = {
    { &m_copy_cva, &m_copy_layera },
    { &m_copy_cvr, &m_copy_layerr }
  };

  for (unsigned int i = 0; i < sizeof (specs) / sizeof (specs[0]); ++i) {

    int &cv = *(specs[i].cv);
    int &layer = *(specs[i].layer);

    if (cv >= int (m_cellviews.size ())) {
      cv = -1;
    }

    int index = active_cellview_index ();
    if (cv < 0) {
      cv = index;
    }

    if (cv < 0 || ! m_cellviews [cv]->layout ().is_valid_layer ((unsigned int) layer)) {
      layer = -1;
    }

  }

  lay::DuplicateLayerDialog dialog (this);
  if (dialog.exec (this, m_copy_cva, m_copy_layera, m_copy_cvr, m_copy_layerr, m_duplicate_hier_mode, m_clear_before)) {

    bool supports_undo = true;

    if (db::transactions_enabled ()) {

      lay::TipDialog td (lay::MainWindow::instance (), 
                         tl::to_string (QObject::tr ("Undo buffering for the following operation can be memory and time consuming.\nChoose \"Yes\" to use undo buffering or \"No\" for no undo buffering. Warning: in the latter case, the undo history will be lost.\n\nChoose undo buffering?")), 
                         "copy-layer-undo-buffering",
                         lay::TipDialog::yesnocancel_buttons);

      lay::TipDialog::button_type button = lay::TipDialog::null_button;
      td.exec_dialog (button);
      if (button == lay::TipDialog::cancel_button) {
        return;
      }

      supports_undo = (button == lay::TipDialog::yes_button);

    } else {
      supports_undo = false;
    }

    cancel ();

    if (manager ()) {
      if (! supports_undo) {
        manager ()->clear ();
      } else {
        manager ()->transaction (tl::to_string (QObject::tr ("Duplicate layer"))); 
      }
    }

    try {

      bool same_layout = (&cellview (m_copy_cvr)->layout () == &cellview (m_copy_cva)->layout ());
      if (same_layout && m_copy_layera == m_copy_layerr) {
        throw tl::Exception (tl::to_string (QObject::tr ("Source and target layer must not be identical for duplicate operation")));
      }

      if (m_duplicate_hier_mode == 0) {

        //  clear the result layer for all called cells in flat mode
        if (m_clear_before) {
          std::set<db::cell_index_type> called_cells;
          called_cells.insert (cellview (m_copy_cvr).cell_index ());
          cellview (m_copy_cvr).cell ()->collect_called_cells (called_cells);
          for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
            cellview (m_copy_cvr)->layout ().cell (*c).shapes (m_copy_layerr).clear ();
          }
        }

        db::Cell &target_cell = *cellview (m_copy_cvr).cell ();

        if (! same_layout) {

          //  flat mode (different layouts)
          db::PropertyMapper pm (cellview (m_copy_cvr)->layout (), cellview (m_copy_cva)->layout ());
          for (db::RecursiveShapeIterator si (cellview (m_copy_cva)->layout (), *cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) {
            target_cell.shapes (m_copy_layerr).insert (*si, si.itrans (), pm);
          }

        } else {

          //  flat mode (same layouts)
          tl::ident_map<db::Layout::properties_id_type> pm1;
          db::Shapes &res = target_cell.shapes (m_copy_layerr);
          
          db::Layout &layout = cellview (m_copy_cvr)->layout ();
          try {

            //  using update/start_layout and end_changes improves the performance since changing the 
            //  shapes collection will invalidate the layout and cause updates inside the RecursiveShapeIerator
            layout.update ();
            layout.start_changes ();
            for (db::RecursiveShapeIterator si (cellview (m_copy_cva)->layout (), *cellview (m_copy_cva).cell (), m_copy_layera); ! si.at_end (); ++si) {
              res.insert (*si, si.itrans (), pm1);
            }
            layout.end_changes ();

          } catch (...) {
            layout.end_changes ();
            throw;
          }

        }
        
      } else if (m_duplicate_hier_mode == 1) {

        db::Cell &cell = *cellview (m_copy_cva).cell ();
        db::Cell &target_cell = *cellview (m_copy_cvr).cell ();

        if (m_clear_before) {
          target_cell.clear (m_copy_layerr);
        } 

        if (m_copy_cvr == m_copy_cva) {

          //  current cell only mode: identical cell
          cell.copy (m_copy_layera, m_copy_layerr);

        } else if (! same_layout) {

          //  current cell only mode (different layouts)
          db::PropertyMapper pm (cellview (m_copy_cvr)->layout (), cellview (m_copy_cva)->layout ());
          for (db::Shapes::shape_iterator si = cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
            target_cell.shapes (m_copy_layerr).insert (*si, pm);
          }

        } else {

          //  current cell only mode (same layouts, but different cells)
          for (db::Shapes::shape_iterator si = cellview (m_copy_cva).cell ()->shapes (m_copy_layera).begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
            target_cell.shapes (m_copy_layerr).insert (*si);
          }

        }
        
      } else if (m_duplicate_hier_mode == 2) {

        //  subcells cell by cell - source and target layout must be identical
        std::set<db::cell_index_type> called_cells;
        cellview (m_copy_cva).cell ()->collect_called_cells (called_cells);
        called_cells.insert (cellview (m_copy_cva).cell_index ());

        db::Layout &layout = cellview (m_copy_cva)->layout ();
        for (std::set<db::cell_index_type>::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) {
          db::Cell &cell = layout.cell (*c);
          if (m_clear_before) {
            cell.clear (m_copy_layerr);
          } 
          cell.copy (m_copy_layera, m_copy_layerr);
        }

      }

      if (manager () && supports_undo) {
        manager ()->commit ();
      }

    } catch (...) {
      if (manager () && supports_undo) {
        manager ()->commit ();
      }
      throw;
    }

  }
}

void
LayoutView::cm_new_layer () 
{
  int index = active_cellview_index ();

  if (index >= 0 && int (m_cellviews.size ()) > index) {

    const lay::CellView &cv = cellview (index);

    lay::NewLayerPropertiesDialog prop_dia (this);
    if (prop_dia.exec (cv, m_new_layer_props)) {

      for (unsigned int l = 0; l < cv->layout ().layers (); ++l) {
        if (cv->layout ().is_valid_layer (l) && cv->layout ().get_properties (l).log_equal (m_new_layer_props)) {
          throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + m_new_layer_props.to_string ());
        }
      }

      if (manager ()) {
        manager ()->transaction (tl::to_string (QObject::tr ("New layer"))); 
      }

      unsigned int l = cv->layout ().insert_layer (m_new_layer_props);
      std::vector <unsigned int> nl;
      nl.push_back (l);
      add_new_layers (nl, index);
      update_content ();

      if (manager ()) {
        manager ()->commit ();
      }

    }

  }
}

void
LayoutView::cm_edit_layer () 
{
  lay::LayerPropertiesConstIterator sel = current_layer ();
  if (sel.is_null ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("No layer selected for editing it's properties")));
  }

  int index = sel->cellview_index ();
  if (sel->has_children () || index < 0 || int (m_cellviews.size ()) <= index || sel->layer_index () < 0) {
    throw tl::Exception (tl::to_string (QObject::tr ("No valid layer selected for editing it's properties")));
  }

  const lay::CellView &cv = cellview (index);

  db::LayerProperties layer_props = cv->layout ().get_properties ((unsigned int) sel->layer_index ());

  lay::NewLayerPropertiesDialog prop_dia (this);
  if (prop_dia.exec (cv, layer_props)) {

    for (unsigned int l = 0; l < cv->layout ().layers (); ++l) {
      if (cv->layout ().is_valid_layer (l) && int (l) != sel->layer_index () && cv->layout ().get_properties (l).log_equal (layer_props)) {
        throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + layer_props.to_string ());
      }
    }

    if (manager ()) {
      manager ()->transaction (tl::to_string (QObject::tr ("Edit layer"))); 
    }

    cv->layout ().set_properties (sel->layer_index (), layer_props);

    lay::LayerProperties lp (*sel);
    lay::ParsedLayerSource s = lp.source (false);
    s.layer (layer_props.layer);
    s.datatype (layer_props.datatype);
    if (! layer_props.name.empty ()) {
      s.name (layer_props.name);
    } else {
      s.clear_name ();
    }
    lp.set_source (s);
    set_properties (sel, lp);

    update_content ();

    if (manager ()) {
      manager ()->commit ();
    }

  }
}

void
LayoutView::cm_delete_layer () 
{
  std::vector<lay::LayerPropertiesConstIterator> sel = selected_layers ();
  std::sort (sel.begin (), sel.end (), CompareLayerIteratorBottomUp ());

  //  collect valid layers
  std::vector<lay::LayerPropertiesConstIterator> valid_sel;
  std::set<std::pair<db::Layout *, unsigned int> > valid_layers;
  for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator si = sel.begin (); si != sel.end (); ++si) {
    int cv_index = (*si)->cellview_index ();
    const lay::CellView &cv = cellview (cv_index);
    if (!(*si)->has_children () && cv_index >= 0 && int (m_cellviews.size ()) > cv_index && (*si)->layer_index () >= 0 && cv.is_valid ()) {
      valid_sel.push_back (*si);
      valid_layers.insert (std::make_pair (&cv->layout (), (*si)->layer_index ()));
    }
  }

  if (valid_sel.empty ()) {
    throw tl::Exception (tl::to_string (QObject::tr ("No or no valid layer selected for deleting them")));
  }

  cancel_edits ();
  clear_selection ();

  if (manager ()) {
    manager ()->transaction (tl::to_string (QObject::tr ("Delete layers"))); 
  }

  //  Hint: delete_layer must come before the layers are actually deleted in because
  //  for undo this must be the last thing to do (otherwise the layout is not propertly set up)

  for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator si = valid_sel.begin (); si != valid_sel.end (); ++si) {
    lay::LayerPropertiesConstIterator lp = *si;
    delete_layer (lp);
  }

  for (std::set<std::pair<db::Layout *, unsigned int> >::const_iterator li = valid_layers.begin (); li != valid_layers.end(); ++li) {

    unsigned int layer_index = li->second;
    db::Layout *layout = li->first;

    for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) {
      c->shapes (layer_index).clear ();
    }

    layout->delete_layer (layer_index);

  }

  update_content ();

  if (manager ()) {
    manager ()->commit ();
  }
}

void
LayoutView::cm_clear_layer () 
{
  int layer_index = -1;
  int cv_index = -1;

  lay::LayerPropertiesConstIterator sel = current_layer ();
  if (! sel.is_null ()) {
    if (!sel->has_children () && sel->layer_index () >= 0 && cellview (sel->cellview_index ()).is_valid ()) {
      layer_index = int (sel->layer_index ());
      cv_index = int (sel->cellview_index ());
    }
  }

  const lay::CellView &cv = cellview (cv_index);
  if (! cv.is_valid ()) {
    return;
  }

  lay::ClearLayerModeDialog mode_dialog (this);
  if (mode_dialog.exec (m_layer_hier_mode, cv)) {

    cancel_edits ();
    clear_selection ();

    if (manager ()) {
      manager ()->transaction (tl::to_string (QObject::tr ("Clear layer"))); 
    }

    if (m_layer_hier_mode == 0) {
      cv.cell ()->clear ((unsigned int) layer_index);
    } else if (m_layer_hier_mode == 1) {

      cv.cell ()->clear ((unsigned int) layer_index);

      std::set <db::cell_index_type> called_cells;
      cv.cell ()->collect_called_cells (called_cells);
      for (std::set <db::cell_index_type>::const_iterator cc = called_cells.begin (); cc != called_cells.end (); ++cc) {
        cv->layout ().cell (*cc).clear ((unsigned int) layer_index);
      }

    } else {
      cv->layout ().clear_layer ((unsigned int) layer_index);
    }

    if (manager ()) {
      manager ()->commit ();
    }

  }
}

unsigned int 
LayoutView::add_rdb (rdb::Database *rdb)
{
  //  make the name unique 

  std::string n (rdb->name ());
  int nn = 0;

  do {

    bool found = false;
    for (unsigned int i = 0; i < num_rdbs () && !found; ++i) {
      if (get_rdb (i)->name () == n) {
        found = true;
      }
    }

    if (! found) {
      break;
    }

    n = rdb->name () + tl::sprintf ("[%d]", ++nn);

  } while (1);

  rdb->set_name (n);

  m_rdbs.push_back (rdb);

  m_rdb_list_changed_observers.signal_observers ();

  return (unsigned int)(m_rdbs.size () - 1);
}

rdb::Database *
LayoutView::get_rdb (int index)
{
  if (index >= 0 && index < int (m_rdbs.size ())) {
    return m_rdbs [index];
  } else {
    return 0;
  }
}

void 
LayoutView::open_rdb_browser (int rdb_index, int cv_index)
{
  rdb::MarkerBrowserDialog *rdb_browser = get_plugin <rdb::MarkerBrowserDialog> ();
  if (rdb_browser) {
    rdb_browser->open (rdb_index, cv_index);
  }
}

const rdb::Database *
LayoutView::get_rdb (int index) const
{
  if (index >= 0 && index < int (m_rdbs.size ())) {
    return m_rdbs [index];
  } else {
    return 0;
  }
}

void 
LayoutView::remove_rdb (unsigned int index)
{
  if (index < (unsigned int) (m_rdbs.size ())) {
    delete m_rdbs [index];
    m_rdbs.erase (m_rdbs.begin () + index);
    m_rdb_list_changed_observers.signal_observers ();
  }
}

QSize 
LayoutView::sizeHint () const
{
  if ((m_options & LV_Naked) != 0) {
    return QSize (200, 200);
  } else if ((m_options & LV_NoLayers) != 0 || (m_options & LV_NoHierarchyPanel) != 0) {
    return QSize (400, 200);
  } else {
    return QSize (600, 200);
  }
}

} // namespace lay

