#include <QFile>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <list>
#include <sstream>
#include <functional>
#include <cassert>

#include <gr.h>

#include <QInputDialog>
#include <QtWidgets>
#include <QFileDialog>
#include <QMessageBox>
#include <cfloat>
#include <QtGlobal>
#include <QApplication>
#include <QTimer>
#include <QEvent>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QWindow>
#include <QCompleter>
#include <QCursor>
#include <QStyle>
#include <QIcon>

#include "GRPlotWidget.hxx"
#include "Threadpool.hxx"
#include "Util.hxx"

#include "gredit/AddElementWidget.hxx"

#include "grm/utilcpp_int.hxx"
#include <grm/dom_render/casts.hxx>
#include <grm/dom_render/render_util.hxx>

#include <gks.h>
#include <time.h>


#ifndef GR_UNUSED
#define GR_UNUSED(x) (void)(x)
#endif

struct HighlightMask
{
  int id;
  int x, y;
  int width, height;
  std::vector<unsigned char> image;
  HighlightMask(int id, int x, int y, int width, int height, std::vector<unsigned char> image)
      : id(id), x(x), y(y), width(width), height(height), image(image) {};
};

struct ThreadAttr
{
  int x_start, y_start, x_end, y_end;
  int x_diff, y_diff;
  int width, height, new_width;
  std::array<uint32_t, 3> smoothed_colors;
  const unsigned int *pixels;
  std::vector<uint32_t> *image;
};

QStringList axis_type_list{
    "x",
    "y",
};
QStringList clip_region_list{
    "rectangular",
    "elliptic",
};
QStringList orientation_list{
    "vertical",
    "horizontal",
};
QStringList algorithm_marginal_heatmap_list{
    "sum",
    "max",
};
QStringList error_bar_style_list{
    "line",
    "area",
};
QStringList marginal_heatmap_kind_list{
    "all",
    "line",
};
QStringList norm_list{
    "count", "countdensity", "pdf", "probability", "cumcount", "cdf",
};
QStringList plot_type_list{
    "2d",
    "3d",
    "polar",
};
QStringList resample_method_list{
    "default",
    "nearest",
    "linear",
    "lanczos",
};
QStringList size_type_list{
    "double",
    "int",
};
QStringList style_list{
    "default",
    "lined",
    "stacked",
};
QStringList text_encoding_list{
    "latin1",
    "utf8",
};
QStringList org_pos_list{
    "low",
    "high",
};
QStringList scientific_format_list{
    "textex",
    "mathtex",
};
QStringList tick_orientation_list{
    "up",
    "down",
};
QStringList side_region_location_list{"top", "right", "bottom", "left"};
QStringList step_where_list{
    "pre",
    "post",
    "mid",
};
QStringList element_type_list{"text", "image"};
QStringList world_coordinates_list{"ndc", "wc"};
QStringList label_orientation_list{"up", "down"};

static std::string file_export;
static QFile *test_commands_file = nullptr;
static QTextStream *test_commands_stream = nullptr;
static Qt::KeyboardModifiers modifiers = Qt::NoModifier;
static bool disable_movable_xform = false;
static bool ctrl_key_mode = false;
static bool mouse_move_triggered = false;
static std::weak_ptr<GRM::Element> previous_active_plot;
static bool active_plot_changed = false;
static bool draw_called_at_least_once = false;
static std::weak_ptr<GRM::Element> prev_selection;
static const char *grm_tmp_dir = nullptr;
static int history_count = 0;
static int history_forward_count = 0;
static std::shared_ptr<GRM::Element> prev_highlighted_tick_group_elem;
static std::list<HighlightMask> mask_highlights;
static std::list<HighlightMask> referred_elements_highlights_cache;
static std::list<HighlightMask> mask_highlights_cache;
static uint32_t color = 0xFF0202F0;
static bool move_to_plot = false;
static std::string plot_id = "";
static std::string input_data_name = "";

void getMousePos(QMouseEvent *event, int *x, int *y)
{
#if QT_VERSION >= 0x060000
  x[0] = static_cast<int>(event->position().x());
  y[0] = static_cast<int>(event->position().y());
#else
  x[0] = static_cast<int>(event->pos().x());
  y[0] = static_cast<int>(event->pos().y());
#endif
}

void getWheelPos(QWheelEvent *event, int *x, int *y)
{
#if QT_VERSION >= 0x060000
  x[0] = static_cast<int>(event->position().x());
  y[0] = static_cast<int>(event->position().y());
#else
  x[0] = static_cast<int>(event->pos().x());
  y[0] = static_cast<int>(event->pos().y());
#endif
}

std::function<void(const grm_event_t *)> size_callback;
extern "C" void sizeCallbackWrapper(const grm_event_t *cb)
{
  size_callback(cb);
}

std::function<void(const grm_request_event_t *)> cmd_callback;
extern "C" void cmdCallbackWrapper(const grm_event_t *event)
{
  cmd_callback(reinterpret_cast<const grm_request_event_t *>(event));
}

GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv, bool listen_mode, int listen_port,
                           bool test_mode, const QString &test_commands)
    : QWidget(parent), pixmap(), redraw_pixmap(RedrawType::NONE), args_(nullptr), rubber_band(nullptr)
{
  args_ = grm_args_new();

  enable_editor = false;
  enable_advanced_editor = false;
  bounding_logic = new BoundingLogic();
  current_selection = nullptr;
  tree_widget = new TreeWidget(this);
  tree_widget->hide();
  table_widget = new TableWidget(this);
  table_widget->hide();
  color_picker_rgb = new ColorPickerRGB(this);
  color_picker_rgb->hide();
  color_picker_rgb->setModal(Qt::ApplicationModal);
  edit_element_widget = new EditElementWidget(this);
  edit_element_widget->hide();
  selected_parent = nullptr;
  preview_text_widget = new PreviewTextWidget(this);
  preview_text_widget->hide();
  selection_list_widget = new SelectionListWidget(this);
  selection_list_widget->hide();
  csr = new QCursor(Qt::ArrowCursor);
  setCursor(*csr);

  combo_box_attr = QStringList{
      "algorithm",
      "axis_type",
      "clip_region",
      "color_model",
      "color_scheme",
      "colormap",
      "element_type",
      "error_bar_style",
      "fill_int_style",
      "fill_style",
      "font",
      "font_precision",
      "kind",
      "label_orientation",
      "line_type",
      "location",
      "marginal_heatmap_kind",
      "marker_type",
      "move_to_plot",
      "norm",
      "orientation",
      "plot_type",
      "radius_kind",
      "ref_x_axis_location",
      "ref_y_axis_location",
      "resample_method",
      "scientific_format",
      "size_x_type",
      "size_y_type",
      "size_x_unit",
      "size_y_unit",
      "world_coordinates",
      "step_where",
      "style",
      "text_encoding",
      "text_align_horizontal",
      "text_align_vertical",
      "tick_orientation",
      "transformation",
      "x_origin_pos",
      "y_origin_pos",
      "z_origin_pos",
  };
  check_box_attr = QStringList{
      "active",
      "adjust_x_lim",
      "adjust_y_lim",
      "adjust_z_lim",
      "clip_negative",
      "colored",
      "colormap_inverted",
      "consecutive_colorbars",
      "disable_x_trans",
      "disable_y_trans",
      "draw_grid",
      "flip_col_and_row",
      "fit_parents_height",
      "fit_parents_width",
      "hide",
      "hidden",
      "is_major",
      "is_mirrored",
      "keep_aspect_ratio",
      "keep_radii_axes",
      "keep_size_if_swapped",
      "keep_window",
      "marginal_heatmap_side_plot",
      "mirrored_axis",
      "movable",
      "only_square_aspect_ratio",
      "polar_with_pan",
      "r_log",
      "set_text_color_for_background",
      "show_mesh",
      "show_unit_cell",
      "spin_style",
      "stairs",
      "text_is_title",
      "theta_flip",
      "trim_col",
      "trim_row",
      "unified_connection_radius",
      "use_gr3",
      "use_grplot_changes",
      "x_flip",
      "x_grid",
      "x_log",
      "y_flip",
      "y_grid",
      "y_line",
      "y_log",
      "z_flip",
      "z_grid",
      "z_log",
  };
  color_ind_attr = QStringList{
      "background_color", "border_color_ind", "fill_color_ind", "line_color_ind", "marker_color_ind", "text_color_ind",
  };
  color_rgb_attr = QStringList{"line_color_rgb", "fill_color_rgb", "color_rgb"};
  slider_attr = QStringList{
      "ambient",
      "diffuse",
      "height_abs",
      "specular",
      "transparency",
      "viewport_height_abs",
      "viewport_height_rel",
      "viewport_normalized_x_max",
      "viewport_normalized_x_min",
      "viewport_normalized_y_max",
      "viewport_normalized_y_min",
      "viewport_width_abs",
      "viewport_width_rel",
      "width_abs",
      "x_max_shift_ndc",
      "x_min_shift_ndc",
      "x_shift_ndc",
      "y_max_shift_ndc",
      "y_min_shift_ndc",
      "y_shift_ndc",
  };

  // add context attributes to combobox list
  auto context_attributes = GRM::getContextAttributes();
  for (const auto &attr : context_attributes)
    {
      if (attr == "line_color_rgb" || attr == "fill_color_rgb" || attr == "color_rgb") continue;
      combo_box_attr.push_back(attr.c_str());
    }

#ifdef _WIN32
  putenv("GKS_WSTYPE=381");
  putenv("GKS_DOUBLE_BUF=True");
#else
  setenv("GKS_WSTYPE", "381", 1);
  setenv("GKS_DOUBLE_BUF", "True", 1);
#endif

  rubber_band = new QRubberBand(QRubberBand::Rectangle, this);
  setFocusPolicy(Qt::FocusPolicy::StrongFocus); // needed to receive key press events
  setMouseTracking(true);
  mouse_state.mode = MouseState::Mode::NORMAL;
  mouse_state.pressed = {0, 0};
  mouse_state.anchor = {0, 0};

  pdf_act = new QAction(tr("&PDF"), this);
  connect(pdf_act, &QAction::triggered, this, &GRPlotWidget::pdf);
  png_act = new QAction(tr("&PNG"), this);
  connect(png_act, &QAction::triggered, this, &GRPlotWidget::png);
  jpeg_act = new QAction(tr("&JPEG"), this);
  connect(jpeg_act, &QAction::triggered, this, &GRPlotWidget::jpeg);
  svg_act = new QAction(tr("&SVG"), this);
  connect(svg_act, &QAction::triggered, this, &GRPlotWidget::svg);

  if (listen_mode)
    {
      in_listen_mode = true;
      qRegisterMetaType<ArgsWrapper>("ArgsWrapper");
      receiver = new Receiver(listen_port);
      QObject::connect(receiver, SIGNAL(resultReady(ArgsWrapper)), this, SLOT(received(ArgsWrapper)),
                       Qt::QueuedConnection);
      QObject::connect(this, SIGNAL(pixmapRedrawn()), receiver, SLOT(dataProcessed()), Qt::QueuedConnection);
      receiver->start();

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
      ::size_callback = [this](auto &&tmp) { sizeCallback(std::forward<decltype(tmp)>(tmp)); };
      ::cmd_callback = [this](auto &&tmp) { cmdCallback(std::forward<decltype(tmp)>(tmp)); };
#else
      ::size_callback = std::bind(&GRPlotWidget::sizeCallback, this, std::placeholders::_1);
      ::cmd_callback = std::bind(&GRPlotWidget::cmdCallback, this, std::placeholders::_1);
#endif

      grm_register(GRM_EVENT_SIZE, sizeCallbackWrapper);
      grm_register(GRM_EVENT_REQUEST, cmdCallbackWrapper);
      ArgsWrapper configuration;
      configuration.setWrapper(grm_args_new());
      grm_args_push(configuration.getWrapper(), "hold_plots", "i", 0);
      grm_merge(configuration.getWrapper());
      grm_args_delete(configuration.getWrapper());
    }
  else
    {
      grm_args_push(args_, "keep_aspect_ratio", "i", 1);

      std::string file_name, optional_file;
      for (int i = 1; i < argc; i++)
        {
          std::string token = argv[i];

          /* parameter needed for import.cxx are handled differently than grm-parameters */
          if (util::startsWith(token, "file:"))
            {
              file_name = token.substr(5, token.length() - 1);
              break;
            }
          else if (i == 1 && (token.find(":") == std::string::npos || (token.find(":") == 1 && token.find('/') == 2)))
            {
              optional_file = token; /* it's only used, if no "file:" keyword was found */
            }
        }
      if (file_name.empty()) file_name = optional_file;

      if (util::endsWith(file_name, ".xml.png") || util::endsWith(file_name, ".xml.pdf") ||
          util::endsWith(file_name, ".xml.svg") || util::endsWith(file_name, ".xml"))
        {
#ifndef NO_XERCES_C
          auto file = fopen(file_name.c_str(), "rb");
          if (!file)
            {
              std::cerr << "Unable to open file \"" << file_name << "\"." << std::endl;
              exit(1);
            }
          grm_load_graphics_tree(file);
          grm_get_render()->setAutoUpdate(true);
          redraw();
#else
          std::stringstream text_stream;
          text_stream << "XML support not compiled in. Please recompile GRPlot with xerces support.";
          QMessageBox::critical(this, "File open not possible", QString::fromStdString(text_stream.str()));
          return;
#endif
        }
      else
        {
          if (!grm_interactive_plot_from_file(args_, argc, argv)) exit(0);

          std::string token;
          int j = 1;
          for (int i = 1; i < argc; i++)
            {
              token = argv[i];
              if (token == "--plot") continue;
              if (util::startsWith(token, "file:"))
                {
                  input_data_name = token.substr(5, token.length() - 1);
                  break;
                }
              else if (j == 1 && (token.find(":") == std::string::npos ||
                                  // Check if is an absolute Windows path, like `C:\Users\...`
                                  (token.length() > 2 && isalpha(token[0]) && token[1] == ':' &&
                                   (token[2] == '/' || token[2] == '\\'))))
                {
                  optional_file = token; /* it's only used, if no "file:" keyword was found */
                  break;
                }
              j += 1;
            }
          if (input_data_name.empty() && !optional_file.empty()) input_data_name = optional_file;
          auto dir_sep_pos = input_data_name.find_last_of("/\\");
          dir_sep_pos = dir_sep_pos != std::string::npos ? dir_sep_pos + 1 : 0;
          input_data_name = input_data_name.substr(dir_sep_pos, input_data_name.find_last_of(".") + 1 - dir_sep_pos);
        }
      if (test_mode)
        {
          test_commands_file = new QFile(test_commands);
          if (test_commands_file->open(QIODevice::ReadOnly))
            {
              test_commands_stream = new QTextStream(test_commands_file);
            }
          else
            {
              std::cerr << "Unable to open test commands file" << std::endl;
              exit(1);
            }
        }
    }

  heatmap_act = new QAction(tr("&Heatmap"), this);
  connect(heatmap_act, &QAction::triggered, this, &GRPlotWidget::heatmap);
  marginal_heatmap_all_act = new QAction(tr("&Type 1 all"), this);
  connect(marginal_heatmap_all_act, &QAction::triggered, this, &GRPlotWidget::marginalHeatmapAll);
  marginal_heatmap_line_act = new QAction(tr("&Type 2 line"), this);
  connect(marginal_heatmap_line_act, &QAction::triggered, this, &GRPlotWidget::marginalHeatmapLine);
  surface_act = new QAction(tr("&Surface"), this);
  connect(surface_act, &QAction::triggered, this, &GRPlotWidget::surface);
  wireframe_act = new QAction(tr("&Wireframe"), this);
  connect(wireframe_act, &QAction::triggered, this, &GRPlotWidget::wireframe);
  contour_act = new QAction(tr("&Contour"), this);
  connect(contour_act, &QAction::triggered, this, &GRPlotWidget::contour);
  imshow_act = new QAction(tr("&Imshow"), this);
  connect(imshow_act, &QAction::triggered, this, &GRPlotWidget::imshow);
  sum_act = new QAction(tr("&Sum"), this);
  connect(sum_act, &QAction::triggered, this, &GRPlotWidget::sumAlgorithm);
  sum_act->setCheckable(true);
  sum_act->setChecked(true);
  max_act = new QAction(tr("&Maximum"), this);
  connect(max_act, &QAction::triggered, this, &GRPlotWidget::maxAlgorithm);
  max_act->setCheckable(true);
  contourf_act = new QAction(tr("&Contourf"), this);
  connect(contourf_act, &QAction::triggered, this, &GRPlotWidget::contourf);
  line_act = new QAction(tr("&Line"), this);
  connect(line_act, &QAction::triggered, this, &GRPlotWidget::line);
  scatter_act = new QAction(tr("&Scatter"), this);
  connect(scatter_act, &QAction::triggered, this, &GRPlotWidget::scatter);
  volume_act = new QAction(tr("&Volume"), this);
  connect(volume_act, &QAction::triggered, this, &GRPlotWidget::volume);
  isosurface_act = new QAction(tr("&Isosurface"), this);
  connect(isosurface_act, &QAction::triggered, this, &GRPlotWidget::isosurface);
  line3_act = new QAction(tr("&Line3"), this);
  connect(line3_act, &QAction::triggered, this, &GRPlotWidget::line3);
  trisurf_act = new QAction(tr("&Trisurface"), this);
  connect(trisurf_act, &QAction::triggered, this, &GRPlotWidget::trisurf);
  tricont_act = new QAction(tr("&Tricontour"), this);
  connect(tricont_act, &QAction::triggered, this, &GRPlotWidget::tricont);
  scatter3_act = new QAction(tr("&Scatter3"), this);
  connect(scatter3_act, &QAction::triggered, this, &GRPlotWidget::scatter3);
  barplot_act = new QAction(tr("&Barplot"), this);
  connect(barplot_act, &QAction::triggered, this, &GRPlotWidget::barplot);
  stairs_act = new QAction(tr("&Stairs"), this);
  connect(stairs_act, &QAction::triggered, this, &GRPlotWidget::stairs);
  stem_act = new QAction(tr("&Stem"), this);
  connect(stem_act, &QAction::triggered, this, &GRPlotWidget::stem);
  shade_act = new QAction(tr("&Shade"), this);
  connect(shade_act, &QAction::triggered, this, &GRPlotWidget::shade);
  hexbin_act = new QAction(tr("&Hexbin"), this);
  connect(hexbin_act, &QAction::triggered, this, &GRPlotWidget::hexbin);
  polar_line_act = new QAction(tr("&Polar Line"), this);
  connect(polar_line_act, &QAction::triggered, this, &GRPlotWidget::polarLine);
  polar_scatter_act = new QAction(tr("&Polar Scatter"), this);
  connect(polar_scatter_act, &QAction::triggered, this, &GRPlotWidget::polarScatter);

  hidePlotTypeMenuElements();
  moveable_mode_act = new QAction(tr("&Disable movable transformation"), this);
  connect(moveable_mode_act, &QAction::triggered, this, &GRPlotWidget::moveableMode);
  moveable_mode_act->setCheckable(true);

  x_log_act = new QAction(tr("&X Log"), this);
  connect(x_log_act, &QAction::triggered, this, &GRPlotWidget::xLogSlot);
  x_log_act->setCheckable(true);
  y_log_act = new QAction(tr("&Y Log"), this);
  connect(y_log_act, &QAction::triggered, this, &GRPlotWidget::yLogSlot);
  y_log_act->setCheckable(true);
  z_log_act = new QAction(tr("&Z Log"), this);
  connect(z_log_act, &QAction::triggered, this, &GRPlotWidget::zLogSlot);
  z_log_act->setCheckable(true);
  r_log_act = new QAction(tr("&R Log"), this);
  connect(r_log_act, &QAction::triggered, this, &GRPlotWidget::rLogSlot);
  r_log_act->setCheckable(true);
  x_flip_act = new QAction(tr("&X Flip"), this);
  connect(x_flip_act, &QAction::triggered, this, &GRPlotWidget::xFlipSlot);
  x_flip_act->setCheckable(true);
  y_flip_act = new QAction(tr("&Y Flip"), this);
  connect(y_flip_act, &QAction::triggered, this, &GRPlotWidget::yFlipSlot);
  y_flip_act->setCheckable(true);
  z_flip_act = new QAction(tr("&Z Flip"), this);
  connect(z_flip_act, &QAction::triggered, this, &GRPlotWidget::zFlipSlot);
  z_flip_act->setCheckable(true);
  theta_flip_act = new QAction(tr("&Theta Flip"), this);
  connect(theta_flip_act, &QAction::triggered, this, &GRPlotWidget::thetaFlipSlot);
  theta_flip_act->setCheckable(true);

  use_gr3_act = new QAction(tr("&Use GR3"), this);
  connect(use_gr3_act, &QAction::triggered, this, &GRPlotWidget::useGR3Slot);
  polar_with_pan_act = new QAction(tr("&Polar Pan"), this);
  connect(polar_with_pan_act, &QAction::triggered, this, &GRPlotWidget::polarWithPanSlot);
  keep_window_act = new QAction(tr("&Keep Window"), this);
  connect(keep_window_act, &QAction::triggered, this, &GRPlotWidget::keepWindowSlot);
  keep_window_act->setCheckable(true);
  keep_window_act->setChecked(false);
  colormap_act = new QAction(tr(""), this);
  auto colormap = QPixmap(":/preview_images/colormaps/viridis.png");
  colormap_act->setIcon(colormap.scaled(20, 20));
  connect(colormap_act, &QAction::triggered, this, &GRPlotWidget::colormapSlot);
  text_color_ind_act = new QAction(tr("Text Color Ind"), this);
  connect(text_color_ind_act, &QAction::triggered, this, &GRPlotWidget::colorIndexSlot);
  text_scale_act = new QAction(tr("Text Scale"), this);
  connect(text_scale_act, &QAction::triggered, this, &GRPlotWidget::textScaleSlot);
  disable_grid_act = new QAction(tr(""), this);
  connect(disable_grid_act, &QAction::triggered, this, &GRPlotWidget::disableGridSlot);
  consecutive_colorbars_act = new QAction(tr("&Consecutive Colorbars"), this);
  connect(consecutive_colorbars_act, &QAction::triggered, this, &GRPlotWidget::consecutiveColorbarsSlot);
  consecutive_colorbars_act->setCheckable(true);
  consecutive_colorbars_act->setChecked(false);
  flip_layout_act = new QAction(tr("&Flip Layout"), this);
  connect(flip_layout_act, &QAction::triggered, this, &GRPlotWidget::flipLayoutSlot);

  vertical_orientation_act = new QAction(tr("&Vertical"), this);
  connect(vertical_orientation_act, &QAction::triggered, this, &GRPlotWidget::verticalOrientationSlot);
  vertical_orientation_act->setCheckable(true);
  vertical_orientation_act->setChecked(false);
  horizontal_orientation_act = new QAction(tr("&Horizontal"), this);
  connect(horizontal_orientation_act, &QAction::triggered, this, &GRPlotWidget::horizontalOrientationSlot);
  horizontal_orientation_act->setCheckable(true);
  horizontal_orientation_act->setChecked(true);
  keep_aspect_ratio_act = new QAction(tr("&Keep Aspectratio"), this);
  connect(keep_aspect_ratio_act, &QAction::triggered, this, &GRPlotWidget::keepAspectRatioSlot);
  keep_aspect_ratio_act->setCheckable(true);
  keep_aspect_ratio_act->setChecked(true);
  only_square_aspect_ratio_act = new QAction(tr("&Square Aspect Ratio"), this);
  connect(only_square_aspect_ratio_act, &QAction::triggered, this, &GRPlotWidget::onlySquareAspectRatioSlot);
  only_square_aspect_ratio_act->setCheckable(true);
  only_square_aspect_ratio_act->setChecked(false);

  legend_act = new QAction(tr("&Legend"), this);
  connect(legend_act, &QAction::triggered, this, &GRPlotWidget::legendSlot);
  colorbar_act = new QAction(tr("&Colorbar"), this);
  connect(colorbar_act, &QAction::triggered, this, &GRPlotWidget::colorbarSlot);
  left_axis_act = new QAction(tr("&Left Axis"), this);
  connect(left_axis_act, &QAction::triggered, this, &GRPlotWidget::leftAxisSlot);
  right_axis_act = new QAction(tr("&Right Axis"), this);
  connect(right_axis_act, &QAction::triggered, this, &GRPlotWidget::rightAxisSlot);
  bottom_axis_act = new QAction(tr("&Bottom Axis"), this);
  connect(bottom_axis_act, &QAction::triggered, this, &GRPlotWidget::bottomAxisSlot);
  top_axis_act = new QAction(tr("&Top Axis"), this);
  connect(top_axis_act, &QAction::triggered, this, &GRPlotWidget::topAxisSlot);
  twin_x_axis_act = new QAction(tr("&Twin-X Axis"), this);
  connect(twin_x_axis_act, &QAction::triggered, this, &GRPlotWidget::twinXAxisSlot);
  twin_y_axis_act = new QAction(tr("&Twin-Y Axis"), this);
  connect(twin_y_axis_act, &QAction::triggered, this, &GRPlotWidget::twinYAxisSlot);

  x_lim_act = new QAction(tr("&X Lim"), this);
  connect(x_lim_act, &QAction::triggered, this, &GRPlotWidget::xLimSlot);
  y_lim_act = new QAction(tr("&Y Lim"), this);
  connect(y_lim_act, &QAction::triggered, this, &GRPlotWidget::yLimSlot);
  z_lim_act = new QAction(tr("&Z Lim"), this);
  connect(z_lim_act, &QAction::triggered, this, &GRPlotWidget::zLimSlot);

  hide_algo_menu_act = new QAction(this);
  show_algo_menu_act = new QAction(this);
  hide_marginal_sub_menu_act = new QAction(this);
  show_marginal_sub_menu_act = new QAction(this);
  hide_orientation_sub_menu_act = new QAction(this);
  show_orientation_sub_menu_act = new QAction(this);
  hide_aspect_ratio_sub_menu_act = new QAction(this);
  show_aspect_ratio_sub_menu_act = new QAction(this);
  hide_multiplot_sub_menu_act = new QAction(this);
  show_multiplot_sub_menu_act = new QAction(this);
  add_seperator_act = new QAction(this);

  if (!getenv("GRDISPLAY") || (getenv("GRDISPLAY") && strcmp(getenv("GRDISPLAY"), "view") != 0))
    {
#if !defined(NO_XERCES_C)
      schema_tree = grm_load_graphics_tree_schema();
#else
      schema_tree = nullptr;
#endif
      add_element_widget = new AddElementWidget(this);
      add_element_widget->hide();

      editor_action = new QAction(tr("&Enable Editorview"));
      QObject::connect(editor_action, SIGNAL(triggered()), this, SLOT(enableEditorFunctions()));

      load_file_action = new QAction("&Load");
      load_file_action->setShortcut(Qt::CTRL | Qt::Key_O);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
      load_file_action->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpen));
      load_file_action->setIconVisibleInMenu(true);
#endif
      QObject::connect(load_file_action, SIGNAL(triggered()), this, SLOT(loadFileSlot()));

      show_container_action = new QAction(tr("&Show GRM Element Tree"));
      show_container_action->setCheckable(true);
      show_container_action->setShortcut(Qt::CTRL | Qt::Key_C);
      show_container_action->setVisible(false);
      QObject::connect(show_container_action, SIGNAL(triggered()), this, SLOT(showContainerSlot()));

      add_element_action = new QAction("&Add Element");
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
      add_element_action->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ListAdd));
      add_element_action->setIconVisibleInMenu(true);
#endif
      add_element_action->setCheckable(true);
      add_element_action->setShortcut(Qt::CTRL | Qt::Key_Plus);
      QObject::connect(add_element_action, SIGNAL(triggered()), this, SLOT(addElementSlot()));
      add_element_action->setVisible(false);

      undo_action = new QAction("&Undo");
      undo_action->setShortcut(Qt::CTRL | Qt::Key_Z);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
      undo_action->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditUndo));
      undo_action->setIconVisibleInMenu(true);
#endif
      QObject::connect(undo_action, SIGNAL(triggered()), this, SLOT(undoSlot()));
      undo_action->setVisible(false);

      redo_action = new QAction("&Redo");
      redo_action->setShortcut(Qt::CTRL | Qt::Key_Y);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
      redo_action->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditRedo));
      redo_action->setIconVisibleInMenu(true);
#endif
      QObject::connect(redo_action, SIGNAL(triggered()), this, SLOT(redoSlot()));
      redo_action->setVisible(false);

      show_context_action = new QAction(tr("&Display Data-Context"));
      show_context_action->setCheckable(true);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
      show_context_action->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DialogInformation));
      show_context_action->setIconVisibleInMenu(true);
#endif
      QObject::connect(show_context_action, SIGNAL(triggered()), this, SLOT(showContextSlot()));
      add_context_action = new QAction(tr("&Column files"));
      QObject::connect(add_context_action, SIGNAL(triggered()), this, SLOT(addContextSlot()));
      add_grplot_data_context = new QAction(tr("&Interpret matrix as 1 column data"));
      QObject::connect(add_grplot_data_context, SIGNAL(triggered()), this, SLOT(addGRPlotDataContextSlot()));
      generate_linear_context_action = new QAction(tr("&Generate linear Data-Context"));
      QObject::connect(generate_linear_context_action, SIGNAL(triggered()), this, SLOT(generateLinearContextSlot()));

      selectable_grid_act = new QAction(tr("&Selectable Grid"));
      QObject::connect(selectable_grid_act, SIGNAL(triggered()), this, SLOT(selectableGridSlot()));
      selectable_grid_act->setCheckable(true);
      selectable_grid_act->setChecked(false);

      advanced_editor_act = new QAction(tr("&Enable Advanced Editor"));
      QObject::connect(advanced_editor_act, SIGNAL(triggered()), this, SLOT(advancedEditorSlot()));
      advanced_editor_act->setVisible(false);

      add_text_act = new QAction(tr("&Add Text"));
      QObject::connect(add_text_act, SIGNAL(triggered()), this, SLOT(addTextSlot()));
      add_overlay_menu = new QMenu(this);
      add_overlay_menu->addAction(add_text_act);

      add_image_act = new QAction(tr("&Add Image"));
      QObject::connect(add_image_act, SIGNAL(triggered()), this, SLOT(addImageSlot()));
      add_overlay_menu->addAction(add_image_act);

      hide_location_sub_menu_act = new QAction(this);
      show_location_sub_menu_act = new QAction(this);
      hide_lim_sub_menu_act = new QAction(this);
      show_lim_sub_menu_act = new QAction(this);
      hide_log_sub_menu_act = new QAction(this);
      show_log_sub_menu_act = new QAction(this);
      hide_flip_sub_menu_act = new QAction(this);
      show_flip_sub_menu_act = new QAction(this);
      hide_plot_type_sub_menu_act = new QAction(this);
      show_plot_type_sub_menu_act = new QAction(this);
      show_edit_element_act = new QAction();
      show_tree_widget_act = new QAction();
      show_table_widget_act = new QAction();
      show_preview_text_act = new QAction();
      show_selection_list_widget_act = new QAction();
      show_icon_bar_widget_act = new QAction();
      show_add_element_act = new QAction();
      hide_edit_element_act = new QAction();
      hide_tree_widget_act = new QAction();
      hide_table_widget_act = new QAction();
      hide_preview_text_act = new QAction();
      hide_selection_list_widget_act = new QAction();
      hide_icon_bar_widget_act = new QAction();
      hide_add_element_act = new QAction();
      update_edit_element_title_act = new QAction();

      connect(selection_list_widget, SIGNAL(itemChanged(QListWidgetItem *)), this,
              SLOT(listItemCheckStatusChanged(QListWidgetItem *)));
      connect(selection_list_widget, SIGNAL(itemPressed(QListWidgetItem *)), this,
              SLOT(listItemPressed(QListWidgetItem *)));

      possible_elems_menu = new QMenu(this);

      icon_bar_widget = new IconBarWidget(this);
      icon_bar_widget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
      icon_bar_widget->setMinimumWidth(0);

      icon_bar_act = new QAction(tr("&Icon bar"));
      icon_bar_act->setCheckable(true);
      icon_bar_act->setChecked(true);
      QObject::connect(icon_bar_act, SIGNAL(triggered()), this, SLOT(showIconBarSlot()));
    }
}

GRPlotWidget::GRPlotWidget(QMainWindow *parent, grm_args_t *args)
    : QWidget(parent), pixmap(), redraw_pixmap(RedrawType::NONE), args_(nullptr), rubber_band(nullptr)
{
  args_ = args;

  enable_editor = false;
  enable_advanced_editor = false;
  bounding_logic = new BoundingLogic();
  current_selection = nullptr;
  selected_parent = nullptr;
  tree_widget = nullptr;
  table_widget = nullptr;
  add_element_widget = nullptr;
  edit_element_widget = nullptr;
  selection_list_widget = nullptr;
  icon_bar_widget = nullptr;
  csr = new QCursor(Qt::ArrowCursor);
  setCursor(*csr);

#ifdef _WIN32
  putenv("GKS_WSTYPE=381");
  putenv("GKS_DOUBLE_BUF=True");
#else
  setenv("GKS_WSTYPE", "381", 1);
  setenv("GKS_DOUBLE_BUF", "True", 1);
#endif

  rubber_band = new QRubberBand(QRubberBand::Rectangle, this);
  setFocusPolicy(Qt::FocusPolicy::StrongFocus); // needed to receive key press events
  setMouseTracking(true);
  mouse_state.mode = MouseState::Mode::NORMAL;
  mouse_state.pressed = {0, 0};
  mouse_state.anchor = {0, 0};

  pdf_act = new QAction(tr("&PDF"), this);
  connect(pdf_act, &QAction::triggered, this, &GRPlotWidget::pdf);
  png_act = new QAction(tr("&PNG"), this);
  connect(png_act, &QAction::triggered, this, &GRPlotWidget::png);
  jpeg_act = new QAction(tr("&JPEG"), this);
  connect(jpeg_act, &QAction::triggered, this, &GRPlotWidget::jpeg);
  svg_act = new QAction(tr("&SVG"), this);
  connect(svg_act, &QAction::triggered, this, &GRPlotWidget::svg);
}

GRPlotWidget::~GRPlotWidget()
{
  /*
   * TODO: Delete the receiver. Currently, this is not possible, since the underlying network thread is caught in
   * blocking networking routines and cannot be signaled to exit.
   * if (receiver != nullptr)
   *   {
   *     delete receiver;
   *   }
   */
  grm_args_delete(args_);
  grm_finalize();
}

static void dilatationForImagePart(void *arg)
{
  constexpr uint32_t no_color = 0xFFFFFFFF;
  constexpr int box_size = 11, inner_box_size = 5;
  auto attr = static_cast<struct ThreadAttr *>(arg);

  for (unsigned int j = attr->y_start; j < attr->y_end; j++)
    {
      for (unsigned int i = attr->x_start; i < attr->x_end; i++)
        {
          auto found_pixel_distance_from_center = -1;
          for (int box_offset_y = 0; box_offset_y <= box_size / 2; box_offset_y++)
            {
              int pixel_y_0 = j + box_offset_y - attr->y_diff;
              int pixel_y_1 = j - box_offset_y - attr->y_diff;
              if (pixel_y_0 < 0 || pixel_y_0 >= attr->height) pixel_y_0 = -1;
              if (pixel_y_1 < 0 || pixel_y_1 >= attr->height) pixel_y_1 = -1;
              if (pixel_y_0 == -1 && pixel_y_1 == -1) continue;
              for (int box_offset_x = 0; box_offset_x <= box_size / 2; box_offset_x++)
                {
                  int pixel_x_0 = i + box_offset_x - attr->x_diff;
                  int pixel_x_1 = i - box_offset_x - attr->x_diff;
                  if (pixel_x_0 >= 0 && pixel_x_0 < attr->width)
                    {
                      if ((pixel_y_0 != -1 && attr->pixels[pixel_y_0 * attr->width + pixel_x_0] != no_color) ||
                          (pixel_y_1 != -1 && attr->pixels[pixel_y_1 * attr->width + pixel_x_0] != no_color))
                        {
                          found_pixel_distance_from_center = std::min<int>(box_offset_x, box_offset_y);
                          break;
                        }
                    }
                  if (pixel_x_1 >= 0 && pixel_x_1 < attr->width)
                    {
                      if ((pixel_y_0 != -1 && attr->pixels[pixel_y_0 * attr->width + pixel_x_1] != no_color) ||
                          (pixel_y_1 != -1 && attr->pixels[pixel_y_1 * attr->width + pixel_x_1] != no_color))
                        {
                          found_pixel_distance_from_center = std::min<int>(box_offset_x, box_offset_y);
                          break;
                        }
                    }
                }
              if (found_pixel_distance_from_center != -1) break;
            }
          if (found_pixel_distance_from_center >= 0)
            {
              (*attr->image)[j * attr->new_width + i] = color;
              (found_pixel_distance_from_center <= inner_box_size / 2)
                  ? color
                  : attr->smoothed_colors[found_pixel_distance_from_center - inner_box_size / 2 - 1];
            }
        }
    }
}

static int systemProcessorCount(void)
{
#ifdef _WIN32
#ifndef _SC_NPROCESSORS_ONLN
  SYSTEM_INFO info;
  GetSystemInfo(&info);
#define sysconf(a) info.dwNumberOfProcessors
#define _SC_NPROCESSORS_ONLN
#endif
#endif
  return static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN));
}

static std::vector<uint32_t> dilatationForImage(unsigned int &x, unsigned int &y, unsigned int &width,
                                                unsigned int &height, const unsigned int *pixels)
{
  constexpr uint32_t no_color = 0xFFFFFFFF;
  constexpr int box_size = 11, inner_box_size = 5;
  constexpr int smoothed_colors_count = std::max(box_size / 2 - inner_box_size / 2, 0);
  std::vector<uint32_t> image;
  std::array<uint32_t, smoothed_colors_count> smoothed_colors;
  auto no_color_rgba = reinterpret_cast<const uint8_t *>(&no_color);
  auto color_rgba = reinterpret_cast<const uint8_t *>(&color);
  ThreadAttr *jobs;
  int x_start = 0, y_start = 0, x_end, y_end;
  int thread_num;
#ifndef NO_THREADS
  ThreadpoolT *thread_pool;
#endif

  for (int i = 0; i < smoothed_colors_count; i++)
    {
      auto smoothed_colors_rgba = reinterpret_cast<uint8_t *>(&smoothed_colors[i]);
      for (int j = 0; j < 4; j++)
        {
          smoothed_colors_rgba[j] =
              (no_color_rgba[j] * (i + 1) + color_rgba[j] * (smoothed_colors_count - i - 1)) / smoothed_colors_count;
        }
    }

  auto active_figure = grm_get_document_root()->querySelectors("figure[active=\"1\"]");
  auto figure_size_x = static_cast<int>(active_figure->getAttribute("size_x"));
  auto figure_size_y = static_cast<int>(active_figure->getAttribute("size_y"));

  // Reserve some more space for the dilatation at the image boundary
  auto new_x = std::max<int>(0, x - box_size / 2);
  auto new_y = std::max<int>(0, y - box_size / 2);
  unsigned int new_width = std::min<int>(x + width + box_size / 2, figure_size_x * 2) - new_x;
  unsigned int new_height = std::min<int>(y + height + box_size / 2, figure_size_y * 2) - new_y;

  image.resize(new_width * new_height, 0x00FFFFFF);

  /* creates the threadpool */
#ifndef NO_THREADS
  thread_pool = static_cast<ThreadpoolT *>(calloc(1, sizeof(*thread_pool)));
  if (thread_pool == 0)
    {
      fprintf(stderr, "Can't allocate memory\n");
      return image;
    }
  thread_num = (systemProcessorCount() - 1) < 256 ? systemProcessorCount() - 1 : 256;
  threadpoolCreate(thread_pool, thread_num, dilatationForImagePart);
#endif

  int thread_size = ceil((new_width + new_height) / (2.0 * thread_num));
  int x_size = ceil(new_width / (1.0 * thread_size)), y_size = ceil(new_height / (1.0 * thread_size));
  jobs = static_cast<ThreadAttr *>(malloc(x_size * y_size * sizeof(ThreadAttr)));

  for (int i = 0; i < x_size; i++)
    {
      x_end = static_cast<int>(std::min((i + 1) * thread_size, static_cast<int>(new_width)));
      for (int j = 0; j < y_size; j++)
        {
          /* transfer data for each thread */
          y_end = static_cast<int>(std::min((j + 1) * thread_size, static_cast<int>(new_height)));
          jobs[i + j * x_size].x_start = x_start;
          jobs[i + j * x_size].y_start = y_start;
          jobs[i + j * x_size].x_end = x_end;
          jobs[i + j * x_size].y_end = y_end;
          jobs[i + j * x_size].x_diff = x - new_x;
          jobs[i + j * x_size].y_diff = y - new_y;
          jobs[i + j * x_size].width = width;
          jobs[i + j * x_size].height = height;
          jobs[i + j * x_size].new_width = new_width;
          jobs[i + j * x_size].image = &image;
          jobs[i + j * x_size].pixels = pixels;
          jobs[i + j * x_size].smoothed_colors = smoothed_colors;

#ifndef NO_THREADS
          threadpoolAddWork(thread_pool, jobs + i + j * x_size);
#endif
          y_start = y_end;
        }
      x_start = x_end;
      y_start = 0;
    }
#ifndef NO_THREADS
  threadpoolDestroy(thread_pool);
#endif

  x = new_x;
  y = new_y;
  width = new_width;
  height = new_height;

  return image;
}

static void processPartialDrawing(int id, unsigned int x, unsigned int y, unsigned int width, unsigned int height,
                                  unsigned int *pixels)
{
  auto processed_image = dilatationForImage(x, y, width, height, pixels);
  std::vector<unsigned char> image;
  for (unsigned int j = 0; j < height; j++)
    {
      for (unsigned int i = 0; i < width; i++)
        {
          const auto pixel = reinterpret_cast<uint8_t *>(processed_image.data() + j * width + i);
          image.push_back(pixel[0]);
          image.push_back(pixel[1]);
          image.push_back(pixel[2]);
          image.push_back(pixel[3]);
        }
    }
  std::free(pixels);
  bool contains_id = false;
  for (const auto &mask : mask_highlights)
    {
      if (mask.id == id)
        {
          contains_id = true;
          break;
        }
    }

  auto mask = HighlightMask(id, x, y, width, height, image);
  if (!contains_id) mask_highlights.emplace_back(mask);
  if (color == 0xFF0202F0)
    mask_highlights_cache.emplace_back(mask);
  else
    referred_elements_highlights_cache.emplace_back(mask);
}

void GRPlotWidget::attributeComboBoxHandler(const std::string &cur_attr_name, std::string cur_elem_name,
                                            QWidget **line_edit)
{
  QStringList size_unit_list, colormap_list, font_list, font_precision_list, line_type_list, location_list,
      x_axis_location_list, y_axis_location_list, marker_type_list, move_to_plot_list, text_align_horizontal_list,
      text_align_vertical_list, algorithm_volume_list, color_model_list, context_attr_list, fill_style_list,
      fill_int_style_list, transformation_list, color_scheme_list, radius_kind_list;
  auto size_unit_vec = GRM::getSizeUnits();
  size_unit_list.reserve(static_cast<int>(size_unit_vec.size()));
  for (auto &i : size_unit_vec) size_unit_list.push_back(i.c_str());
  auto colormap_vec = GRM::getColormaps();
  colormap_list.reserve(static_cast<int>(colormap_vec.size()));
  for (auto &i : colormap_vec) colormap_list.push_back(i.c_str());
  auto font_vec = GRM::getFonts();
  font_list.reserve(static_cast<int>(font_vec.size()));
  for (auto &i : font_vec) font_list.push_back(i.c_str());
  auto font_precision_vec = GRM::getFontPrecisions();
  font_precision_list.reserve(static_cast<int>(font_precision_vec.size()));
  for (auto &i : font_precision_vec) font_precision_list.push_back(i.c_str());
  auto line_type_vec = GRM::getLineTypes();
  line_type_list.reserve(static_cast<int>(line_type_vec.size()));
  for (auto &i : line_type_vec) line_type_list.push_back(i.c_str());
  auto location_vec = GRM::getLocations();
  location_list.reserve(static_cast<int>(location_vec.size()));
  for (auto &i : location_vec) location_list.push_back(i.c_str());
  auto x_axis_location_vec = GRM::getXAxisLocations();
  x_axis_location_list.reserve(static_cast<int>(x_axis_location_vec.size()));
  for (auto &i : x_axis_location_vec) x_axis_location_list.push_back(i.c_str());
  auto y_axis_location_vec = GRM::getYAxisLocations();
  y_axis_location_list.reserve(static_cast<int>(y_axis_location_vec.size()));
  for (auto &i : y_axis_location_vec) y_axis_location_list.push_back(i.c_str());
  auto marker_type_vec = GRM::getMarkerTypes();
  marker_type_list.reserve(static_cast<int>(marker_type_vec.size()));
  for (auto &i : marker_type_vec) marker_type_list.push_back(i.c_str());
  auto text_align_horizontal_vec = GRM::getTextAlignHorizontal();
  text_align_horizontal_list.reserve(static_cast<int>(text_align_horizontal_vec.size()));
  for (auto &i : text_align_horizontal_vec) text_align_horizontal_list.push_back(i.c_str());
  auto text_align_vertical_vec = GRM::getTextAlignVertical();
  text_align_vertical_list.reserve(static_cast<int>(text_align_vertical_vec.size()));
  for (auto &i : text_align_vertical_vec) text_align_vertical_list.push_back(i.c_str());
  auto algorithm_volume_vec = GRM::getAlgorithm();
  algorithm_volume_list.reserve(static_cast<int>(algorithm_volume_vec.size()));
  for (auto &i : algorithm_volume_vec) algorithm_volume_list.push_back(i.c_str());
  auto color_model_vec = GRM::getColorModel();
  color_model_list.reserve(static_cast<int>(color_model_vec.size()));
  for (auto &i : color_model_vec) color_model_list.push_back(i.c_str());
  auto fill_style_vec = GRM::getFillStyles();
  fill_style_list.reserve(static_cast<int>(fill_style_vec.size()));
  for (auto &i : fill_style_vec) fill_style_list.push_back(i.c_str());
  auto fill_int_style_vec = GRM::getFillIntStyles();
  fill_int_style_list.reserve(static_cast<int>(fill_int_style_vec.size()));
  for (auto &i : fill_int_style_vec) fill_int_style_list.push_back(i.c_str());
  auto transformation_vec = GRM::getTransformation();
  transformation_list.reserve(static_cast<int>(transformation_vec.size()));
  for (auto &i : transformation_vec) transformation_list.push_back(i.c_str());
  table_widget->extractContextNames(grm_get_render()->getContext());
  auto context_attr_vec = table_widget->getContextNames();
  context_attr_list.reserve(static_cast<int>(context_attr_vec.size()));
  for (auto &i : context_attr_vec) context_attr_list.push_back(i.c_str());
  auto color_scheme_vec = GRM::getColorScheme();
  color_scheme_list.reserve(static_cast<int>(color_scheme_vec.size()));
  for (auto &i : color_scheme_vec) color_scheme_list.push_back(i.c_str());
  auto radius_kind_vec = GRM::getRadiusKind();
  radius_kind_list.reserve(static_cast<int>(radius_kind_vec.size()));
  for (auto &i : radius_kind_vec) radius_kind_list.push_back(i.c_str());
  std::vector<std::string> move_to_plot_vec;
  for (const auto &elem : grm_get_document_root()->querySelectorsAll("plot"))
    {
      move_to_plot_vec.push_back(static_cast<std::string>(elem->getAttribute("plot_id")));
    }
  move_to_plot_vec.push_back("new_plot"); // to move a series into a new plot
  move_to_plot_list.reserve(static_cast<int>(move_to_plot_vec.size()));
  for (auto &i : move_to_plot_vec) move_to_plot_list.push_back(i.c_str());

  std::map<std::string, QStringList> attribute_to_list{
      {"axis_type", axis_type_list},
      {"error_bar_style", error_bar_style_list},
      {"element_type", element_type_list},
      {"clip_region", clip_region_list},
      {"size_x_unit", size_unit_list},
      {"size_y_unit", size_unit_list},
      {"colormap", colormap_list},
      {"fill_style", fill_style_list},
      {"fill_int_style", fill_int_style_list},
      {"font", font_list},
      {"font_precision", font_precision_list},
      {"label_orientation", label_orientation_list},
      {"line_type", line_type_list},
      {"marker_type", marker_type_list},
      {"move_to_plot", move_to_plot_list},
      {"text_align_vertical", text_align_vertical_list},
      {"text_align_horizontal", text_align_horizontal_list},
      {"orientation", orientation_list},
      {"marginal_heatmap_kind", marginal_heatmap_kind_list},
      {"color_model", color_model_list},
      {"norm", norm_list},
      {"plot_type", plot_type_list},
      {"ref_x_axis_location", x_axis_location_list},
      {"ref_y_axis_location", y_axis_location_list},
      {"resample_method", resample_method_list},
      {"scientific_format", scientific_format_list},
      {"size_x_type", size_type_list},
      {"size_y_type", size_type_list},
      {"world_coordinates", world_coordinates_list},
      {"step_where", step_where_list},
      {"style", style_list},
      {"text_encoding", text_encoding_list},
      {"x_origin_pos", org_pos_list},
      {"y_origin_pos", org_pos_list},
      {"z_origin_pos", org_pos_list},
      {"tick_orientation", tick_orientation_list},
      {"transformation", transformation_list},
      {"color_scheme", color_scheme_list},
      {"radius_kind", radius_kind_list},
  };
  // add for all context attributes all possible values
  for (const auto &attr : GRM::getContextAttributes())
    {
      if (attribute_to_list.count(attr) >= 1)
        attribute_to_list[attr] = context_attr_list;
      else
        attribute_to_list.emplace(attr, context_attr_list);
    }

  static_cast<QComboBox *>(*line_edit)->setEditable(true);
  if (attribute_to_list.count(cur_attr_name))
    {
      int cnt = 0;
      QStringList list = attribute_to_list[cur_attr_name];
      for (const auto &elem : list)
        {
          static_cast<QComboBox *>(*line_edit)->addItem(elem.toStdString().c_str());
          if (cur_attr_name == "colormap")
            {
              const auto global_root = grm_get_document_root();
              const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
              const auto figure_elem =
                  (layout_grid != nullptr && layout_grid->querySelectors("[_selected_for_menu]") != nullptr)
                      ? layout_grid->querySelectors("[_selected_for_menu]")
                      : global_root->querySelectors("figure[active=1]");
              const auto plot_elems = figure_elem->querySelectorsAll("plot");
              std::shared_ptr<GRM::Element> plot_elem;

              if (plot_elems.size() > 1)
                {
                  plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
                }
              else
                {
                  plot_elem = plot_elems[0];
                }

              auto colormap = QPixmap((":/preview_images/colormaps/" + elem.toStdString() + ".png").c_str());
              auto colormap_inverted = plot_elem->hasAttribute("colormap_inverted") &&
                                       static_cast<int>(plot_elem->getAttribute("colormap_inverted"));
              if (!colormap.isNull())
                {
                  colormap.setDevicePixelRatio(15);
                  if (colormap_inverted) colormap = colormap.transformed(QTransform().rotate(180));
                  static_cast<QComboBox *>(*line_edit)->setItemData(cnt++, colormap, Qt::DecorationRole);
                }
            }
          else if (cur_attr_name == "marker_type")
            {
              if (auto marker_type = QPixmap((":/preview_images/marker_types/" + elem.toStdString() + ".png").c_str());
                  !marker_type.isNull())
                {
                  marker_type.setDevicePixelRatio(15);
                  static_cast<QComboBox *>(*line_edit)->setItemData(cnt++, marker_type, Qt::DecorationRole);
                }
            }
          else if (cur_attr_name == "line_type")
            {
              if (auto line_type = QPixmap(
                      (":/preview_images/line_types/" + elem.toStdString() + (isDarkMode() ? "_dark" : "") + ".png")
                          .c_str());
                  !line_type.isNull())
                {
                  line_type.setDevicePixelRatio(15);
                  static_cast<QComboBox *>(*line_edit)->setItemData(cnt++, line_type, Qt::DecorationRole);
                }
            }
          else if (cur_attr_name == "fill_style")
            {
              if (auto fill_style = QPixmap(
                      (":/preview_images/fill_styles/" + elem.toStdString() + (isDarkMode() ? "_dark" : "") + ".png")
                          .c_str());
                  !fill_style.isNull())
                {
                  fill_style = fill_style.scaled(30, 30, Qt::KeepAspectRatio);
                  static_cast<QComboBox *>(*line_edit)->setItemData(cnt++, fill_style, Qt::DecorationRole);
                }
            }
          else if (cur_attr_name == "font_precision")
            {
              if (auto font_precision = QPixmap((":/preview_images/font_precisions/" + elem.toStdString() +
                                                 (isDarkMode() ? "_dark" : "") + ".png")
                                                    .c_str());
                  !font_precision.isNull())
                {
                  font_precision.setDevicePixelRatio(15);
                  static_cast<QComboBox *>(*line_edit)->setItemData(cnt++, font_precision, Qt::DecorationRole);
                }
            }
          else if (cur_attr_name == "font")
            {
              if (auto font =
                      QPixmap((":/preview_images/fonts/" + elem.toStdString() + (isDarkMode() ? "_dark" : "") + ".png")
                                  .c_str());
                  !font.isNull())
                {
                  font.setDevicePixelRatio(15);
                  static_cast<QComboBox *>(*line_edit)->setItemData(cnt++, font, Qt::DecorationRole);
                }
            }
        }
      auto *completer = new QCompleter(list, this);
      completer->setCaseSensitivity(Qt::CaseInsensitive);
      static_cast<QComboBox *>(*line_edit)->setCompleter(completer);

      if (cur_attr_name == "move_to_plot")
        QObject::connect(static_cast<QComboBox *>(*line_edit), SIGNAL(highlighted(int)), this,
                         SLOT(highlightMoveToPlotSlot(int)));
    }
  else if (cur_attr_name == "algorithm" && cur_elem_name == "series_volume")
    {
      for (const auto &elem : algorithm_volume_list)
        {
          static_cast<QComboBox *>(*line_edit)->addItem(elem.toStdString().c_str());
        }
      auto *completer = new QCompleter(algorithm_volume_list, this);
      completer->setCaseSensitivity(Qt::CaseInsensitive);
      static_cast<QComboBox *>(*line_edit)->setCompleter(completer);
    }
  else if (cur_attr_name == "algorithm" && cur_elem_name == "marginal_heatmap_plot")
    {
      for (const auto &elem : algorithm_marginal_heatmap_list)
        {
          static_cast<QComboBox *>(*line_edit)->addItem(elem.toStdString().c_str());
        }
      auto *completer = new QCompleter(algorithm_marginal_heatmap_list, this);
      completer->setCaseSensitivity(Qt::CaseInsensitive);
      static_cast<QComboBox *>(*line_edit)->setCompleter(completer);
    }
  else if (cur_attr_name == "kind")
    {
      QStringList list;
      QStringList line_group = {"line", "scatter"};
      QStringList heatmap_group = {"contour",          "contourf", "heatmap",  "imshow",
                                   "marginal_heatmap", "surface",  "wireframe"};
      QStringList isosurface_group = {"isosurface", "volume"};
      QStringList line3_group = {"line3", "scatter", "scatter3", "tricontour", "trisurface"};
      QStringList barplot_group = {"barplot", "stem", "stairs"};
      QStringList hexbin_group = {"hexbin", "shade"};
      QStringList polar_line_group = {"polar_line", "polar_scatter"};
      QStringList other_kinds = {"histogram", "pie", "polar_heatmap", "polar_histogram", "quiver", "molecule"};
      std::string kind;

      if (util::startsWith(cur_elem_name, "series_")) kind = cur_elem_name.erase(0, 7);
      if (util::endsWith(cur_elem_name, "_plot"))
        kind = cur_elem_name.erase(cur_elem_name.size() - 6, cur_elem_name.size() - 1);

      if (heatmap_group.contains(kind.c_str()))
        {
          list = heatmap_group;
        }
      else if (kind == "line" || kind == "scatter")
        {
          list = line_group;
        }
      else if (kind == "isosurface" || kind == "volume")
        {
          list = isosurface_group;
        }
      else if (line3_group.contains(kind.c_str()))
        {
          list = line3_group;
        }
      else if (barplot_group.contains(kind.c_str()))
        {
          list = barplot_group;
        }
      else if (other_kinds.contains(kind.c_str()))
        {
          list.push_back(kind.c_str());
        }
      else if (kind == "hexbin" || kind == "shade")
        {
          list = hexbin_group;
        }
      else if (kind == "polar_line" || kind == "polar_scatter")
        {
          list = polar_line_group;
        }

      for (const auto &elem : list)
        {
          static_cast<QComboBox *>(*line_edit)->addItem(elem.toStdString().c_str());
        }
      auto *completer = new QCompleter(list, this);
      completer->setCaseSensitivity(Qt::CaseInsensitive);
      static_cast<QComboBox *>(*line_edit)->setCompleter(completer);
    }
  else if (cur_attr_name == "location" &&
           (cur_elem_name == "side_region" || cur_elem_name == "side_plot_region" || cur_elem_name == "text_region") &&
           cur_elem_name != "axis")
    {
      for (const auto &elem : side_region_location_list)
        {
          static_cast<QComboBox *>(*line_edit)->addItem(elem.toStdString().c_str());
        }
      auto *completer = new QCompleter(side_region_location_list, this);
      completer->setCaseSensitivity(Qt::CaseInsensitive);
      static_cast<QComboBox *>(*line_edit)->setCompleter(completer);
    }
  else if (cur_attr_name == "location" &&
           !(cur_elem_name == "side_region" || cur_elem_name == "side_plot_region" || cur_elem_name == "text_region") &&
           cur_elem_name != "axis")
    {
      for (const auto &elem : location_list)
        {
          static_cast<QComboBox *>(*line_edit)->addItem(elem.toStdString().c_str());
        }
      auto *completer = new QCompleter(location_list, this);
      completer->setCaseSensitivity(Qt::CaseInsensitive);
      static_cast<QComboBox *>(*line_edit)->setCompleter(completer);
    }
  else if (cur_attr_name == "location" &&
           !(cur_elem_name == "side_region" || cur_elem_name == "side_plot_region" || cur_elem_name == "text_region") &&
           cur_elem_name == "axis")
    {
      auto axis_type = static_cast<std::string>(current_selection->getRef()->getAttribute("axis_type"));
      for (const auto &elem : (axis_type == "x" ? x_axis_location_list : y_axis_location_list))
        {
          static_cast<QComboBox *>(*line_edit)->addItem(elem.toStdString().c_str());
        }
      auto *completer = new QCompleter((axis_type == "x" ? x_axis_location_list : y_axis_location_list), this);
      completer->setCaseSensitivity(Qt::CaseInsensitive);
      static_cast<QComboBox *>(*line_edit)->setCompleter(completer);
    }
}

void GRPlotWidget::advancedAttributeComboBoxHandler(const std::string &cur_attr_name, std::string cur_elem_name,
                                                    QWidget **line_edit)
{
  if (cur_attr_name == "kind")
    cur_elem_name = "series_" + static_cast<std::string>(current_selection->getRef()->getAttribute(cur_attr_name));
  attributeComboBoxHandler(cur_attr_name, cur_elem_name, line_edit);
  static_cast<QComboBox *>(*line_edit)->addItem(""); // entry to remove the attribute

  auto current_text = static_cast<std::string>(current_selection->getRef()->getAttribute(cur_attr_name));
  if (cur_attr_name == "text_align_vertical" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::textAlignVerticalIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "text_align_horizontal" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text = GRM::textAlignHorizontalIntToString(
          static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "algorithm" && current_selection->getRef()->localName() == "series_volume" &&
           current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::algorithmIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "color_model" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::colorModelIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "location" &&
           !(current_selection->getRef()->localName() == "side_region" ||
             current_selection->getRef()->localName() == "side_plot_region" ||
             current_selection->getRef()->localName() == "text_region") &&
           current_selection->getRef()->localName() != "axis" &&
           current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::locationIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "location" &&
           !(current_selection->getRef()->localName() == "side_region" ||
             current_selection->getRef()->localName() == "side_plot_region" ||
             current_selection->getRef()->localName() == "text_region") &&
           current_selection->getRef()->localName() == "axis" &&
           current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      if (auto type = static_cast<std::string>(current_selection->getRef()->getAttribute("axis_type")); type == "x")
        current_text =
            GRM::xAxisLocationIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
      else
        current_text =
            GRM::yAxisLocationIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "clip_region" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::clipRegionIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "colormap" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::colormapIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "text_encoding" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::textEncodingIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "marker_type" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::markerTypeIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "font" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text = GRM::fontIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "font_precision" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::fontPrecisionIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "line_type" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::lineTypeIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "resample_method" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::resampleMethodIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "scientific_format" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::scientificFormatIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "tick_orientation" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::tickOrientationIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "error_bar_style" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::errorBarStyleIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "fill_style" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::fillStyleIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "fill_int_style" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::fillIntStyleIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "transformation" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::transformationIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "label_orientation" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::labelOrientationIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "world_coordinates" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::worldCoordinatesIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "color_scheme" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::colorSchemeIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }
  else if (cur_attr_name == "radius_kind" && current_selection->getRef()->getAttribute(cur_attr_name).isInt())
    {
      current_text =
          GRM::radiusKindIntToString(static_cast<int>(current_selection->getRef()->getAttribute(cur_attr_name)));
    }

  int index = static_cast<QComboBox *>(*line_edit)->findText(current_text.c_str());
  if (index == -1) index += static_cast<QComboBox *>(*line_edit)->count();
  static_cast<QComboBox *>(*line_edit)->setCurrentIndex(index);
  if (cur_attr_name == "colormap") static_cast<QComboBox *>(*line_edit)->setIconSize(QSize(90, 40));
  if (cur_attr_name == "line_type") static_cast<QComboBox *>(*line_edit)->setIconSize(QSize(60, 40));
  if (cur_attr_name == "font_precision") static_cast<QComboBox *>(*line_edit)->setIconSize(QSize(60, 40));
  if (cur_attr_name == "font") static_cast<QComboBox *>(*line_edit)->setIconSize(QSize(60, 40));
}

void GRPlotWidget::attributeSetForComboBox(const std::string &attr_type, const std::shared_ptr<GRM::Element> &element,
                                           const std::string &value, const std::string &label)
{
  if (attr_type == "xs:string" && !util::isDigits(value))
    {
      auto context_attributes = GRM::getContextAttributes();
      if (label == "algorithm" && element->localName() == "marginal_heatmap_plot")
        {
          if (algorithm_marginal_heatmap_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "axis_type")
        {
          if (axis_type_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "orientation")
        {
          if (orientation_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "marginal_heatmap_kind")
        {
          if (marginal_heatmap_kind_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "norm")
        {
          if (norm_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "plot_type")
        {
          if (plot_type_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if ((label == "size_x_type" || label == "size_y_type"))
        {
          if (size_type_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "style")
        {
          if (style_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if ((label == "x_origin_pos" || label == "y_origin_pos" || label == "z_origin_pos"))
        {
          if (org_pos_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (std::find(context_attributes.begin(), context_attributes.end(), label) != context_attributes.end())
        {
          QStringList context_attr_list;
          table_widget->extractContextNames(grm_get_render()->getContext());
          auto context_attr_vec = table_widget->getContextNames();
          context_attr_list.reserve(static_cast<int>(context_attr_vec.size()));
          for (auto &i : context_attr_vec) context_attr_list.push_back(i.c_str());

          if (context_attr_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "step_where")
        {
          if (step_where_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else if (label == "element_type")
        {
          if (element_type_list.contains(QString::fromStdString(value)))
            element->setAttribute(label, value);
          else
            fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
      else
        {
          element->setAttribute(label, value);
        }
    }
  else if ((attr_type == "xs:integer" || attr_type == "strint") && !util::isDigits(value))
    {
      try
        {
          if (label == "text_align_vertical")
            {
              element->setAttribute(label, GRM::textAlignVerticalStringToInt(value));
            }
          else if (label == "text_align_horizontal")
            {
              element->setAttribute(label, GRM::textAlignHorizontalStringToInt(value));
            }
          else if (label == "algorithm")
            {
              element->setAttribute(label, GRM::algorithmStringToInt(value));
            }
          else if (label == "color_model")
            {
              element->setAttribute(label, GRM::colorModelStringToInt(value));
            }
          else if (label == "location" && element->localName() != "legend" && element->localName() != "axis")
            {
              if (side_region_location_list.contains(QString::fromStdString(value)))
                element->setAttribute(label, value);
              else
                fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
            }
          else if (label == "location" && element->localName() == "legend")
            {
              element->setAttribute(label, GRM::locationStringToInt(value));
            }
          else if (label == "location" && element->localName() == "axis")
            {
              if (auto type = static_cast<std::string>(current_selection->getRef()->getAttribute("axis_type"));
                  type == "x")
                element->setAttribute(label, GRM::xAxisLocationStringToInt(value));
              else
                element->setAttribute(label, GRM::yAxisLocationStringToInt(value));
            }
          else if (label == "clip_region")
            {
              element->setAttribute(label, GRM::clipRegionStringToInt(value));
            }
          else if (label == "colormap")
            {
              element->setAttribute(label, GRM::colormapStringToInt(value));
            }
          else if (label == "text_encoding")
            {
              element->setAttribute(label, GRM::textEncodingStringToInt(value));
            }
          else if (label == "marker_type")
            {
              element->setAttribute(label, GRM::markerTypeStringToInt(value));
            }
          else if (label == "font")
            {
              element->setAttribute(label, GRM::fontStringToInt(value));
            }
          else if (label == "font_precision")
            {
              element->setAttribute(label, GRM::fontPrecisionStringToInt(value));
            }
          else if (label == "line_type")
            {
              element->setAttribute(label, GRM::lineTypeStringToInt(value));
            }
          else if (label == "resample_method")
            {
              element->setAttribute(label, GRM::resampleMethodStringToInt(value));
            }
          else if (label == "tick_orientation")
            {
              element->setAttribute(label, GRM::tickOrientationStringToInt(value));
            }
          else if (label == "scientific_format")
            {
              element->setAttribute(label, GRM::scientificFormatStringToInt(value));
            }
          else if (label == "error_bar_style")
            {
              element->setAttribute(label, GRM::errorBarStyleStringToInt(value));
            }
          else if (label == "fill_style")
            {
              element->setAttribute(label, GRM::fillStyleStringToInt(value));
            }
          else if (label == "fill_int_style")
            {
              element->setAttribute(label, GRM::fillIntStyleStringToInt(value));
            }
          else if (label == "transformation")
            {
              element->setAttribute(label, GRM::transformationStringToInt(value));
            }
          else if (label == "label_orientation")
            {
              element->setAttribute(label, GRM::labelOrientationStringToInt(value));
            }
          else if (label == "world_coordinates")
            {
              element->setAttribute(label, GRM::worldCoordinatesStringToInt(value));
            }
          else if (label == "color_scheme")
            {
              element->setAttribute(label, GRM::colorSchemeStringToInt(value));
            }
          else if (label == "radius_kind")
            {
              element->setAttribute(label, GRM::radiusKindStringToInt(value));
            }
        }
      catch (std::logic_error &e)
        {
          fprintf(stderr, "Invalid value %s for combobox attribute %s\n", value.c_str(), label.c_str());
        }
    }
}

void GRPlotWidget::attributeEditEvent(bool highlight_location)
{
  if (current_selection != nullptr)
    {
      auto multiple_selections = grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]");
      edit_element_widget->show();
      edit_element_widget->attributeEditEvent(multiple_selections, highlight_location);
      update_edit_element_title_act->trigger();
      show_edit_element_act->trigger();
      ignore_resize = 2;
    }
}

void GRPlotWidget::draw()
{
  if (!file_export.empty())
    {
      grm_export(file_export.c_str(),
                 (util::endsWith(file_export, ".xml.png") || util::endsWith(file_export, ".xml.pdf") ||
                  util::endsWith(file_export, ".xml.svg")));
      file_export.clear();
    }
  else
    {
      bool was_successful;
      if (!draw_called_at_least_once || in_listen_mode)
        {
          /* Call `grm_plot` at least once to initialize the internal argument container structure,
           * but use `grm_render` afterward, so the graphics tree is not deleted every time. */
          try
            {
              was_successful = grm_plot(nullptr);
            }
          catch (std::runtime_error e)
            {
              std::cerr << e.what() << std::endl;
              exit(1);
            }
          catch (NotFoundError e)
            {
              std::cerr << e.what() << std::endl;
              exit(1);
            }
        }
      else
        {
          try
            {
              if (ignore_resize > 0) grm_get_render()->setEnableEditor(false);
              was_successful = grm_render();
              if (ignore_resize > 0) grm_get_render()->setEnableEditor(true);
            }
          catch (NotFoundError e)
            {
              std::cerr << e.what() << std::endl;
              exit(1);
            }
        }
      if (!was_successful)
        {
          if (!draw_called_at_least_once || in_listen_mode)
            {
              if (auto error_code = grm_get_error_code(); error_code == 1)
                fprintf(stderr, "A unspecified GRM error occurred!\n");
              else if (error_code == 2)
                fprintf(stderr, "An internal GRM error occurred!\n");
              else if (error_code == 3)
                fprintf(stderr, "A GRM error occurred while trying to allocate storage!\n");
              else if (error_code == 4)
                fprintf(stderr, "A GRM error caused by an unsupported operation occurred!\n");
              else if (error_code == 5)
                fprintf(stderr, "An unsupported datatype GRM error occurred!\n");
              else if (error_code == 6)
                fprintf(stderr, "An invalid argument GRM error occurred!\n");
              else if (error_code == 7)
                fprintf(stderr, "An args invalid key GRM error occurred!\n");
              else if (error_code == 8)
                fprintf(stderr, "An args increasing non array value GRM error occurred!\n");
              else if (error_code == 9)
                fprintf(stderr, "An args increasing multi dimensional array GRM error occurred!\n");
              else if (error_code == 10)
                fprintf(stderr, "A GRM error occurred while parsing a null value!\n");
              else if (error_code == 11)
                fprintf(stderr, "A GRM error occurred while parsing a bool value!\n");
              else if (error_code == 12)
                fprintf(stderr, "A GRM error occurred while parsing an int value!\n");
              else if (error_code == 13)
                fprintf(stderr, "A GRM error occurred while parsing a double value!\n");
              else if (error_code == 14)
                fprintf(stderr, "A GRM error occurred while parsing a string value!\n");
              else if (error_code == 15)
                fprintf(stderr, "A GRM error occurred while parsing an array value!\n");
              else if (error_code == 16)
                fprintf(stderr, "A GRM error occurred while parsing an object value!\n");
              else if (error_code == 17)
                fprintf(stderr, "A GRM error occurred while parsing an unknown datatype!\n");
              else if (error_code == 18)
                fprintf(stderr, "A GRM error occurred while parsing an invalid delimiter!\n");
              else if (error_code == 19)
                fprintf(stderr, "A GRM error occurred while parsing an incomplete string!\n");
              else if (error_code == 20)
                fprintf(stderr, "A GRM error occurred while parsing a missing object container!\n");
              else if (error_code == 21)
                fprintf(stderr, "A GRM error occurred while parsing a non existing schema file!\n");
              else if (error_code == 22)
                fprintf(stderr, "A GRM error occurred while parsing an invalid schema!\n");
              else if (error_code == 23)
                fprintf(stderr, "Validating the xml schema file failed. A GRM parse error occurred!\n");
              else if (error_code == 24)
                fprintf(stderr, "A GRM error occurred while parsing the xml schema!\n");
              else if (error_code == 25)
                fprintf(stderr, "While initialize a network winsocket a GRM error occurred!\n");
              else if (error_code == 26)
                fprintf(stderr, "While creating a network socket a GRM error occurred!\n");
              else if (error_code == 27)
                fprintf(stderr, "While binding a network socket a GRM error occurred!\n");
              else if (error_code == 28)
                fprintf(stderr, "While listening to the network socket a GRM error occurred!\n");
              else if (error_code == 29)
                fprintf(stderr, "A GRM error occurred after a network connection was accepted!\n");
              else if (error_code == 30)
                fprintf(stderr, "While resolving the network hostname a GRM error occurred!\n");
              else if (error_code == 31)
                fprintf(stderr, "While connecting to the network socket a GRM error occurred!\n");
              else if (error_code == 32)
                fprintf(stderr, "While receiving the network a GRM error occurred!\n");
              else if (error_code == 33)
                fprintf(stderr, "The received network is unsupported. A GRM error occurred!\n");
              else if (error_code == 34)
                fprintf(stderr, "The network received a connection shutdown. A GRM error occurred!\n");
              else if (error_code == 35)
                fprintf(stderr, "While sending the network a GRM error occurred!\n");
              else if (error_code == 36)
                fprintf(stderr, "A GRM error occurred cause of unsupported network send!\n");
              else if (error_code == 37)
                fprintf(stderr, "The network socket is closed. A GRM error occurred!\n");
              else if (error_code == 38)
                fprintf(stderr, "While cleaning the network winsocket up a GRM error occurred!\n");
              else if (error_code == 39)
                fprintf(stderr, "While receiving a custom command a GRM error occurred!\n");
              else if (error_code == 40)
                fprintf(stderr, "While sending a custom command a GRM error occurred!\n");
              else if (error_code == 41)
                fprintf(stderr, "A GRM error in combination with the colormap occurred!\n");
              else if (error_code == 42)
                fprintf(stderr, "A GRM error in combination with the normalization occurred!\n");
              else if (error_code == 43)
                fprintf(stderr, "The key is unknown. A GRM error occurred!\n");
              else if (error_code == 44)
                fprintf(stderr, "A GRM error in combination with an unknown algorithm occurred!\n");
              else if (error_code == 45)
                fprintf(stderr, "A GRM error in combination with a missing algorithm occurred!\n");
              else if (error_code == 46)
                fprintf(stderr, "The kind is unknown. A GRM error occurred!\n");
              else if (error_code == 47)
                fprintf(stderr, "A GRM error in combination with missing data occurred!\n");
              else if (error_code == 48)
                fprintf(stderr, "A GRM error in combination with a length mismatch in the components occurred!\n");
              else if (error_code == 49)
                fprintf(stderr, "A GRM error in combination with missing dimensions occurred!\n");
              else if (error_code == 50)
                fprintf(stderr, "A GRM error in combination with missing labels occurred!\n");
              else if (error_code == 51)
                fprintf(stderr, "The plot id is invalid. A GRM error occurred!\n");
              else if (error_code == 52)
                fprintf(stderr, "A out of range GRM error occurred!\n");
              else if (error_code == 53)
                fprintf(stderr, "A GRM error in combination with incompatible arguments occurred!\n");
              else if (error_code == 54)
                fprintf(stderr, "A GRM error in combination with an invalid request occurred!\n");
              else if (error_code == 55)
                fprintf(stderr, "The base64 block is too short. A GRM error occurred!\n");
              else if (error_code == 56)
                fprintf(stderr, "A base64 invalid character encountered. A GRM error occurred!\n");
              else if (error_code == 57)
                fprintf(stderr, "A GRM error based on a invalid layout index occurred!\n");
              else if (error_code == 58)
                fprintf(stderr, "Layout attributes contradict. A GRM error occurred.\n");
              else if (error_code == 59)
                fprintf(stderr, "Encountered a invalid argument range inside the layout. A GRM error occurred!\n");
              else if (error_code == 60)
                fprintf(stderr, "Encountered a length mismatch inside the layout components. A GRM error occurred!\n");
              else if (error_code == 61)
                fprintf(stderr, "A GRM error occurred while creating a temporary directory!\n");
              else if (error_code == 62)
                fprintf(stderr, "A non implemented GRM error occurred!\n");
            }
          fprintf(stderr, "An error occurred, the application will be closed. Please verify your input is correct and "
                          "try it again\n");
          exit(1);
        }
    }
  draw_called_at_least_once = true;
}

void GRPlotWidget::redraw(bool full_redraw, bool update_tree)
{
  redraw_pixmap = full_redraw ? RedrawType::FULL : RedrawType::PARTIAL;
  tree_update = update_tree;
  if (ignore_resize == 0) // resize triggerd by dockwidgets shouldn't clear the cache since the window stays the same
    {
      mask_highlights_cache.clear();
      referred_elements_highlights_cache.clear();
    }
  else
    {
      ignore_resize -= 1;
    }

  update();
}

void GRPlotWidget::collectTooltips()
{
  QPoint mouse_pos = this->mapFromGlobal(QCursor::pos());
  if (Qt::KeyboardModifiers keyboard_modifiers = GRPlotWidget::queryKeyboardModifiers();
      keyboard_modifiers == Qt::ShiftModifier)
    {
      auto accumulated_tooltip = grm_get_accumulated_tooltip_x(mouse_pos.x(), mouse_pos.y());
      tooltips.clear();
      if (accumulated_tooltip != nullptr) tooltips.emplace_back(accumulated_tooltip);
      redraw();
    }
  else
    {
      auto root = grm_get_document_root();
      bool removed = false;
      for (const auto &coordinate_system : root->querySelectorsAll("coordinate_system"))
        {
          if (coordinate_system->hasAttribute("x_ind"))
            {
              coordinate_system->removeAttribute("x_ind");
              removed = true;
            }
        }
      if (removed) redraw();

      if (keyboard_modifiers != Qt::AltModifier) tooltips.clear();
      if (auto current_tooltip = grm_get_tooltip(mouse_pos.x(), mouse_pos.y()); current_tooltip != nullptr)
        {
          bool found_current_tooltip = false;
          for (const auto &tooltip : tooltips)
            {
              if (tooltip.get<grm_tooltip_info_t>()->x == current_tooltip->x &&
                  tooltip.get<grm_tooltip_info_t>()->y == current_tooltip->y)
                {
                  found_current_tooltip = true;
                  break;
                }
            }
          if (!found_current_tooltip) tooltips.emplace_back(current_tooltip);
        }
    }
}

static const std::string TOOLTIP_STYLE{"\
    .gr-label {\n\
        color: #26aae1;\n\
        font-size: 11px;\n\
        line-height: 0.8;\n\
    }\n\
    .gr-value {\n\
        color: #3c3c3c;\n\
        font-size: 11px;\n\
        line-height: 0.8;\n\
    }"};

static const std::string TOOLTIP_TEMPLATE{"\
    <span class=\"gr-label\">%s</span><br>\n\
    <span class=\"gr-label\">%s: </span>\n\
    <span class=\"gr-value\">%.14g</span><br>\n\
    <span class=\"gr-label\">%s: </span>\n\
    <span class=\"gr-value\">%.14g</span>"};

static const std::string TIME_TOOLTIP_TEMPLATE{"\
    <span class=\"gr-label\">%s</span><br>\n\
    <span class=\"gr-label\">%s: </span>\n\
    <span class=\"gr-value\">%s</span><br>\n\
    <span class=\"gr-label\">%s: </span>\n\
    <span class=\"gr-value\">%.14g</span>"};

static const std::string ACCUMULATED_TOOLTIP_TEMPLATE{"\
    <span class=\"gr-label\">%s: </span>\n\
    <span class=\"gr-value\">%.14g</span>"};

static const std::string ACCUMULATED_TIME_TOOLTIP_TEMPLATE{"\
    <span class=\"gr-label\">%s: </span>\n\
    <span class=\"gr-value\">%s</span>"};

void GRPlotWidget::paintEvent(QPaintEvent *event)
{
  if (!getenv("GRDISPLAY") || (getenv("GRDISPLAY") && strcmp(getenv("GRDISPLAY"), "view") != 0))
    {
      if (table_widget != nullptr && !table_widget->isVisible() && show_context_action->isChecked())
        show_context_action->setChecked(false);
      if (tree_widget != nullptr && !tree_widget->isVisible() && show_container_action->isChecked())
        show_container_action->setChecked(false);
      if (add_element_widget != nullptr && !add_element_widget->isVisible() && add_element_action->isChecked())
        add_element_action->setChecked(false);
    }
  util::unused(event);
  paint(this);
}

void GRPlotWidget::paint(QPaintDevice *paint_device)
{
  QPainter painter;
  std::stringstream addresses;
  std::string kind;

  QSize needed_pixmap_size = QSize((int)(geometry().width() * this->devicePixelRatioF()),
                                   (int)(geometry().height() * this->devicePixelRatioF()));

  if (pixmap.isNull() || pixmap.size() != needed_pixmap_size)
    {
      pixmap = QPixmap(needed_pixmap_size);
      pixmap.setDevicePixelRatio(this->devicePixelRatioF());
      redraw_pixmap = RedrawType::FULL;
    }

#ifdef _WIN32
  addresses << "GKS_CONID=";
#endif
  addresses << static_cast<void *>(this) << "!" << static_cast<void *>(&painter);
#ifdef _WIN32
  putenv(addresses.str().c_str());
#else
  setenv("GKS_CONID", addresses.str().c_str(), 1);
#endif
  if (redraw_pixmap == RedrawType::PARTIAL || redraw_pixmap == RedrawType::FULL)
    {
      QPixmap tmp_pixmap;
      if (file_export.empty())
        {
          painter.begin(&pixmap);
        }
      else
        {
          tmp_pixmap = QPixmap(needed_pixmap_size);
          painter.begin(&tmp_pixmap);
        }
      auto global_root = grm_get_document_root();
      auto active_figure = global_root->querySelectors("figure[active=\"1\"]");
      auto is_multiplot = active_figure != nullptr && active_figure->querySelectors("layout_grid") != nullptr;
      if (is_multiplot)
        {
          if (redraw_pixmap == RedrawType::PARTIAL)
            {
              // A PARTIAL redraw was requested, but if no plot is marked as active, we need to redraw the whole figure
              if ((active_figure->querySelectors("layout_grid") == nullptr ||
                   active_figure->querySelectors("plot[_active=\"1\"]") == nullptr) &&
                  (active_figure->querySelectors("plot[_active_through_update=\"1\"]") == nullptr))
                redraw_pixmap = RedrawType::FULL;
            }
          if (redraw_pixmap == RedrawType::FULL)
            {
              auto render = grm_get_render();
              bool auto_update;
              render->getAutoUpdate(&auto_update);
              render->setAutoUpdate(false);
              for (const auto &plot : active_figure->querySelectorsAll("plot[_active=\"1\"]"))
                {
                  plot->removeAttribute("_active");
                }
              render->setAutoUpdate(auto_update);
              previous_active_plot.reset();
              gr_clearbackground();
            }
          else if (auto active_plot_through_update =
                       active_figure->querySelectors("plot[_active_through_update=\"1\"]");
                   active_plot_through_update != nullptr ||
                   active_figure->querySelectors("plot[_active=\"1\"]") != nullptr)
            {
              double viewport[4];
              int width, height;
              double mwidth, mheight;
              GRM::getFigureSize(&width, &height, &mwidth, &mheight);
              auto aspect_r = mwidth / mheight;

              if (active_plot_through_update != nullptr)
                {
                  GRM::Render::getViewport(active_plot_through_update, &viewport[0], &viewport[1], &viewport[2],
                                           &viewport[3]);
                }
              else
                {
                  auto active_plot = active_figure->querySelectors("plot[_active=\"1\"]");
                  GRM::Render::getViewport(active_plot, &viewport[0], &viewport[1], &viewport[2], &viewport[3]);
                }

              viewport[0] *= width;
              viewport[1] *= width;
              viewport[2] *= height;
              viewport[3] *= height;
              if (aspect_r > 1)
                {
                  viewport[2] *= aspect_r;
                  viewport[3] *= aspect_r;
                }
              else
                {
                  viewport[0] /= aspect_r;
                  viewport[1] /= aspect_r;
                }

              gr_setbackground(viewport[0], viewport[1], std::max(0.0, height - viewport[3]),
                               std::max(0.0, height - viewport[3]) + viewport[3] - viewport[2]);
            }
        }
      active_plot_changed = false;
      draw();

      active_figure = global_root->querySelectors("figure[active=\"1\"]");
      if (auto active_plot_through_update = active_figure->querySelectors("plot[_active_through_update=\"1\"]");
          is_multiplot && active_plot_through_update != nullptr)
        {
          double viewport[4];
          int width, height;
          double mwidth, mheight;
          GRM::getFigureSize(&width, &height, &mwidth, &mheight);
          auto aspect_r = mwidth / mheight;

          GRM::Render::getViewport(active_plot_through_update, &viewport[0], &viewport[1], &viewport[2], &viewport[3]);
          viewport[0] *= width;
          viewport[1] *= width;
          viewport[2] *= height;
          viewport[3] *= height;
          if (aspect_r > 1)
            {
              viewport[2] *= aspect_r;
              viewport[3] *= aspect_r;
            }
          else
            {
              viewport[0] /= aspect_r;
              viewport[1] /= aspect_r;
            }
          gr_setbackground(viewport[0], viewport[1], std::max(0.0, height - viewport[3]),
                           std::max(0.0, height - viewport[3]) + viewport[3] - viewport[2]);

          auto render = grm_get_render();
          bool auto_update;
          render->getAutoUpdate(&auto_update);
          render->setAutoUpdate(false);
          for (const auto &plot : active_figure->querySelectorsAll("plot[_active_through_update=\"1\"]"))
            {
              plot->removeAttribute("_active_through_update");
            }
          render->setAutoUpdate(auto_update);
        }
      painter.end();
      redraw_pixmap = RedrawType::NONE;

      if (tree_widget != nullptr && tree_update && enable_editor) tree_widget->updateData(grm_get_document_root());
      if (mouse_move_triggered) collectTooltips();
      emit pixmapRedrawn();
    }

  painter.begin(paint_device);
  painter.drawPixmap(0, 0, pixmap);
  bounding_logic->clear();
  extractBoundingBoxesFromGRM((QPainter &)painter);

  // get the old selected element if existing and set it as the current_selection
  auto old_selected_elem = grm_get_document_root()->querySelectors("[_highlighted=\"1\"]");
  if (old_selected_elem == nullptr) old_selected_elem = prev_highlighted_tick_group_elem;
  if (old_selected_elem != nullptr)
    {
      auto bbox_id = static_cast<int>(old_selected_elem->getAttribute("_bbox_id"));
      auto bbox_x_min = static_cast<double>(old_selected_elem->getAttribute("_bbox_x_min"));
      auto bbox_x_max = static_cast<double>(old_selected_elem->getAttribute("_bbox_x_max"));
      auto bbox_y_min = static_cast<double>(old_selected_elem->getAttribute("_bbox_y_min"));
      auto bbox_y_max = static_cast<double>(old_selected_elem->getAttribute("_bbox_y_max"));
      auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, old_selected_elem);
      current_selection = bbox;
      if (tree_widget->isVisible()) tree_widget->selectItem(current_selection->getRef());
    }

  highlightCurrentSelection((QPainter &)painter);
  // Todo: only trigger this method in non multiplot case where 1 plot has different series when not all elements has to
  // be processed to figure out which kinds are all used
  if (auto global_root = grm_get_document_root(); (global_root->querySelectors("layout_grid") == nullptr ||
                                                   global_root->querySelectorsAll("layout_grid_element").size() <= 1))
    adjustPlotTypeMenu(global_root->querySelectors("figure[active=1]")->querySelectors("plot"));
  else
    adjustPlotTypeMenu(global_root->querySelectors("figure[active=1]")->querySelectors("plot[_selected_for_menu=1]"));
  if (!tooltips.empty() && !enable_editor)
    {
      for (const auto &tooltip : tooltips)
        {
          if (tooltip.xPx() > 0 && tooltip.yPx() > 0)
            {
              QColor background(224, 224, 224, 128);
              QPainterPath triangle;
              std::string info, x_label = tooltip.xLabel();
              std::shared_ptr<GRM::Element> coordinate_system = nullptr;

              if (util::startsWith(x_label, "$") && util::endsWith(x_label, "$")) x_label = "x";

              if (auto global_root = grm_get_document_root();
                  (global_root->querySelectors("layout_grid") == nullptr ||
                   global_root->querySelectorsAll("layout_grid_element").size() <= 1))
                {
                  auto plot_elem = global_root->querySelectors("figure[active=1]")->querySelectors("plot");
                  if (plot_elem != nullptr) coordinate_system = plot_elem->querySelectors("coordinate_system");
                }
              else
                {
                  auto plot_elem =
                      global_root->querySelectors("figure[active=1]")->querySelectors("plot[_selected_for_menu=1]");
                  if (plot_elem != nullptr) coordinate_system = plot_elem->querySelectors("coordinate_system");
                }
              bool timestamp = coordinate_system != nullptr && coordinate_system->hasAttribute("_time_axis") &&
                               static_cast<int>(coordinate_system->getAttribute("_time_axis"));

              if (tooltip.holdsAlternative<grm_tooltip_info_t>())
                {
                  const auto *single_tooltip = tooltip.get<grm_tooltip_info_t>();
                  std::string y_label = single_tooltip->y_label;

                  if (util::startsWith(y_label, "$") && util::endsWith(y_label, "$")) y_label = "y";

                  if (timestamp)
                    {
                      info = util::stringFormat(TIME_TOOLTIP_TEMPLATE, single_tooltip->label, x_label.c_str(),
                                                single_tooltip->x_time, y_label.c_str(), single_tooltip->y);
                    }
                  else
                    {
                      info = util::stringFormat(TOOLTIP_TEMPLATE, single_tooltip->label, x_label.c_str(),
                                                single_tooltip->x, y_label.c_str(), single_tooltip->y);
                    }
                }
              else
                {
                  const auto *accumulated_tooltip = tooltip.get<grm_accumulated_tooltip_info_t>();
                  std::vector<std::string> y_labels(accumulated_tooltip->y_labels,
                                                    accumulated_tooltip->y_labels + accumulated_tooltip->n);

                  std::vector<std::string> info_parts;
                  if (timestamp)
                    {
                      info_parts.push_back(util::stringFormat(ACCUMULATED_TIME_TOOLTIP_TEMPLATE, x_label.c_str(),
                                                              accumulated_tooltip->x_time));
                    }
                  else
                    {
                      info_parts.push_back(
                          util::stringFormat(ACCUMULATED_TOOLTIP_TEMPLATE, x_label.c_str(), accumulated_tooltip->x));
                    }
                  for (int i = 0; i < y_labels.size(); ++i)
                    {
                      auto &y = accumulated_tooltip->y[i];
                      auto &y_label = y_labels[i];
                      if (util::startsWith(y_label, "$") && util::endsWith(y_label, "$")) y_label = "y";
                      info_parts.emplace_back("<br>\n");
                      info_parts.push_back(util::stringFormat(ACCUMULATED_TOOLTIP_TEMPLATE, y_label.c_str(), y));
                    }
                  std::ostringstream info_stream;
                  std::copy(info_parts.begin(), info_parts.end(), std::ostream_iterator<std::string>(info_stream));
                  info = info_stream.str();
                }
              label.setDefaultStyleSheet(QString::fromStdString(TOOLTIP_STYLE));
              label.setHtml(QString::fromStdString(info));

              /* tooltips for filled plot kinds gets a higher alpha value to make the values more visible */
              int width = this->size().width(), height = this->size().height();
              GRM::getFigureSize(&width, &height, nullptr, nullptr);
              auto max_width_height = std::max(width, height);
              auto x = (double)tooltip.xPx() / max_width_height;
              auto y = (double)(height - tooltip.yPx()) / max_width_height;
              if (auto plot_element = grm_get_subplot_from_ndc_points_using_dom(1, &x, &y);
                  plot_element != nullptr && plot_element->hasAttribute("_kind"))
                kind = static_cast<std::string>(plot_element->getAttribute("_kind"));
              if (kind == "heatmap" || kind == "marginal_heatmap" || kind == "contourf" || kind == "imshow")
                background.setAlpha(224);

              painter.fillRect(tooltip.xPx() + 8, (int)(tooltip.yPx() - label.size().height() / 2),
                               (int)label.size().width(), (int)label.size().height(),
                               QBrush(background, Qt::SolidPattern));

              triangle.moveTo(tooltip.xPx(), tooltip.yPx());
              triangle.lineTo(tooltip.xPx() + 8, tooltip.yPx() + 6);
              triangle.lineTo(tooltip.xPx() + 8, tooltip.yPx() - 6);
              triangle.closeSubpath();
              background.setRgb(128, 128, 128, 128);
              painter.fillPath(triangle, QBrush(background, Qt::SolidPattern));

              painter.save();
              painter.translate(tooltip.xPx() + 8, tooltip.yPx() - label.size().height() / 2);
              label.drawContents(&painter);
              painter.restore();
            }
        }
    }
  if (overlay_element_edit) overlayElementEdit();
  painter.end();
}

void GRPlotWidget::keyPressEvent(QKeyEvent *event)
{
  if (enable_editor)
    {
      if (event->key() == Qt::Key_Escape)
        {
          if (!current_selections.empty()) current_selections.clear();
          for (const auto &selection : grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]"))
            {
              selection->setAttribute("_selected_for_move", 0);
            }
          if (current_selection && current_selection->getRef() != nullptr)
            {
              current_selection->getRef()->removeAttribute("_highlighted");
              prev_highlighted_tick_group_elem.reset();
            }
          ctrl_key_mode = false;
          current_selection = nullptr;
          mouse_move_selections.clear();
          prev_selection.reset();
          tree_widget->updateData(grm_get_document_root());
          edit_element_widget->hide();
          add_element_widget->hide();
          this->cursor().setShape(Qt::ArrowCursor);
          selection_list_widget->hide();
          hide_selection_list_widget_act->trigger();
          ignore_resize = 1;
          redraw();
        }
      else if (event->key() == Qt::Key_Return)
        {
          attributeEditEvent();
        }
      else if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace)
        {
          if (current_selection == nullptr) return;
          // to remove y_line, title, xlabel and ylabel from axis Node
          auto elem_name = static_cast<std::string>(current_selection->getRef()->getAttribute("name"));
          if (current_selection->getRef()->parentElement()->hasAttribute(elem_name))
            {
              if (elem_name == "y_line")
                {
                  current_selection->getRef()->parentElement()->setAttribute(elem_name, false);
                }
              else
                {
                  current_selection->getRef()->parentElement()->removeAttribute(elem_name);
                }
            }
          auto parent = current_selection->getRef()->parentElement();
          current_selection->getRef()->remove();
          while (parent != nullptr && parent->localName() != "root" && parent->childElementCount() <= 1)
            {
              auto tmp_parent = parent->parentElement();
              // to remove x_tick_labels, y_tick_labels from coordinate_system
              elem_name = static_cast<std::string>(parent->getAttribute("name"));
              if (tmp_parent->hasAttribute(elem_name)) tmp_parent->removeAttribute(elem_name);
              parent->remove();
              parent = tmp_parent;
            }
          // to prevent recreation of the tree a new flag is introduced
          if (parent->localName() == "root" && !parent->hasChildNodes())
            parent->setAttribute("_removed_children", true);
          mouse_move_selections.clear();
          resetPixmap();
          edit_element_widget->hide();
          hide_edit_element_act->trigger();
          ignore_resize = 1;
        }
      else if (event->key() == Qt::Key_Shift)
        {
          if (!clicked.empty() && current_selection != nullptr)
            {
              for (int i = 0; i < clicked.size(); i++)
                {
                  if (clicked[i].getId() == current_selection->getId())
                    {
                      if (i + 1 < clicked.size())
                        {
                          current_selection = &clicked[i + 1];
                          break;
                        }
                      current_selection = &clicked[i + 1 - clicked.size()];
                      break;
                    }
                }
            }
          tree_widget->updateData(grm_get_document_root());
          if (current_selection != nullptr) tree_widget->selectItem(current_selection->getRef());
        }
      else if (event->key() == Qt::Key_Control)
        {
          ctrl_key_mode = true;
        }
      else if (event->key() == Qt::Key_R)
        {
          // Todo: maybe reset ndc_shift and ndc_max/min_shift
          resetPixmap();
        }
    }
  else
    {
      if (event->key() == Qt::Key_R)
        {
          grm_args_t *args = grm_args_new();
          QPoint widget_cursor_pos = mapFromGlobal(QCursor::pos());
          grm_args_push(args, "key", "s", "r");
          grm_args_push(args, "x", "i", widget_cursor_pos.x());
          grm_args_push(args, "y", "i", widget_cursor_pos.y());
          grm_input(args);
          grm_args_delete(args);
          redraw();
        }

      collectTooltips();
      update();
    }
}

void GRPlotWidget::keyReleaseEvent(QKeyEvent *event)
{
  GR_UNUSED(event);
  if (enable_editor)
    {
      /*
       * Handling of the Ctrl modifier is broken on Linux/X11. The key code is `0` and the reported **set** modifiers
       * contain `Qt::ControlModifier` although it is released.
       */
      if (event->key() == Qt::Key_Control || (event->key() == 0 && (event->modifiers() & Qt::ControlModifier)))
        {
          ctrl_key_mode = false;
          auto rel_cursor_pos = mapFromGlobal(QCursor::pos());
          cursorHandler(rel_cursor_pos.x(), rel_cursor_pos.y()); // get the correct cursor and sets it
        }
    }
  else
    {
      collectTooltips();
    }
  update();
}

void GRPlotWidget::mouseMoveEvent(QMouseEvent *event)
{
  mouse_move_triggered = true;

  if (enable_editor)
    {
      int x, y;
      getMousePos(event, &x, &y);
      cursorHandler(x, y); // get the correct cursor and sets it
      if (mouse_state.mode == MouseState::Mode::MOVE_SELECTED)
        {
          grm_args_t *args = grm_args_new();

          grm_args_push(args, "x", "i", mouse_state.anchor.x());
          grm_args_push(args, "y", "i", mouse_state.anchor.y());
          grm_args_push(args, "x_shift", "i", x - mouse_state.anchor.x());
          grm_args_push(args, "y_shift", "i", y - mouse_state.anchor.y());
          grm_args_push(args, "move_selection", "i", 1);

          grm_input(args);
          grm_args_delete(args);

          mouse_state.anchor.setX(x);
          mouse_state.anchor.setY(y);
          redraw();
        }
      else if (mouse_state.mode == MouseState::Mode::HORIZONTAL_SCALE_SELECTED ||
               mouse_state.mode == MouseState::Mode::VERTICAL_SCALE_SELECTED ||
               mouse_state.mode == MouseState::Mode::B_DIAGONAL_SCALE_SELECTED ||
               mouse_state.mode == MouseState::Mode::F_DIAGONAL_SCALE_SELECTED)
        {
          if (cursor().shape() == Qt::ArrowCursor) return;
          grm_args_t *args = grm_args_new();

          grm_args_push(args, "x", "i", mouse_state.anchor.x());
          grm_args_push(args, "y", "i", mouse_state.anchor.y());

          if (mouse_state.mode == MouseState::Mode::VERTICAL_SCALE_SELECTED)
            grm_args_push(args, "x_shift", "i", 0);
          else
            grm_args_push(args, "x_shift", "i", x - mouse_state.anchor.x());
          if (mouse_state.mode == MouseState::Mode::HORIZONTAL_SCALE_SELECTED)
            grm_args_push(args, "y_shift", "i", 0);
          else
            grm_args_push(args, "y_shift", "i", y - mouse_state.anchor.y());
          grm_args_push(args, "scale_selection", "i", 1);

          grm_input(args);
          grm_args_delete(args);

          mouse_state.anchor.setX(x);
          mouse_state.anchor.setY(y);
          redraw();
        }
      else
        {
          auto elements_at_pos = bounding_logic->getElementsAtPoint(x, y, hide_grid_bbox, enable_advanced_editor);

          if (current_selection == nullptr)
            {
              auto global_root = grm_get_document_root();
              mouse_move_selections.clear();
              if (!elements_at_pos.empty())
                {
                  for (const auto &set_elem : elements_at_pos)
                    {
                      mouse_move_selections.emplace(set_elem);
                    }
                }
              update();
            }
        }
    }
  else
    {
      int mouse_x, mouse_y;
      double x, y;
      int width = this->size().width(), height = this->size().height();
      getMousePos(event, &mouse_x, &mouse_y);

      GRM::getFigureSize(&width, &height, nullptr, nullptr);
      auto max_width_height = std::max(width, height);
      x = static_cast<double>(mouse_x) / max_width_height;
      y = static_cast<double>(height - mouse_y) / max_width_height;

      auto global_root = grm_get_document_root();
      auto plot_element = grm_get_subplot_from_ndc_points_using_dom(1, &x, &y);
      if (plot_element && global_root->querySelectors("figure[active=\"1\"]")->querySelectors("layout_grid"))
        {
          auto render = grm_get_render();
          bool auto_update;
          render->getAutoUpdate(&auto_update);
          render->setAutoUpdate(false);
          auto previous_active_plot_locked = previous_active_plot.lock();
          if (previous_active_plot_locked == nullptr || previous_active_plot_locked != plot_element)
            {
              plot_element->setAttribute("_active", true);
              active_plot_changed = true;
            }
          if (previous_active_plot_locked != nullptr && previous_active_plot_locked != plot_element)
            {
              previous_active_plot_locked->removeAttribute("_active");
            }
          previous_active_plot = plot_element;
          render->setAutoUpdate(auto_update);
        }
      if (mouse_state.mode == MouseState::Mode::BOXZOOM)
        {
          rubber_band->setGeometry(QRect(mouse_state.pressed, event->pos()).normalized());
        }
      else if (mouse_state.mode == MouseState::Mode::PAN)
        {
          grm_args_t *args = grm_args_new();

          grm_args_push(args, "x", "i", mouse_state.anchor.x());
          grm_args_push(args, "y", "i", mouse_state.anchor.y());
          grm_args_push(args, "x_shift", "i", mouse_x - mouse_state.anchor.x());
          grm_args_push(args, "y_shift", "i", mouse_y - mouse_state.anchor.y());

          grm_input(args);
          grm_args_delete(args);

          mouse_state.anchor.setX(mouse_x);
          mouse_state.anchor.setY(mouse_y);
          redraw();
        }
      else if (mouse_state.mode == MouseState::Mode::MOVABLE_XFORM)
        {
          grm_args_t *args = grm_args_new();

          grm_args_push(args, "x", "i", mouse_state.anchor.x());
          grm_args_push(args, "y", "i", mouse_state.anchor.y());
          grm_args_push(args, "x_shift", "i", mouse_x - mouse_state.anchor.x());
          grm_args_push(args, "y_shift", "i", mouse_y - mouse_state.anchor.y());
          if (disable_movable_xform) grm_args_push(args, "disable_movable_trans", "i", disable_movable_xform);

          /* get the correct cursor and sets it */
          int cursor_state = grm_get_hover_mode(mouse_x, mouse_y, disable_movable_xform);
          grm_args_push(args, "movable_state", "i", cursor_state);

          grm_input(args);
          grm_args_delete(args);

          mouse_state.anchor.setX(mouse_x);
          mouse_state.anchor.setY(mouse_y);
          redraw();
        }
      else
        {
          collectTooltips();
          if (plot_element)
            {
              if (auto kind = static_cast<std::string>(plot_element->getAttribute("_kind")); kind == "marginal_heatmap")
                {
                  grm_args_t *input_args = grm_args_new();

                  grm_args_push(input_args, "x", "i", mouse_x);
                  grm_args_push(input_args, "y", "i", mouse_y);
                  grm_input(input_args);
                  redraw();
                }
            }
          else
            {
              for (const auto &plot_elem : global_root->querySelectorsAll("plot"))
                {
                  if (auto kind = static_cast<std::string>(plot_elem->getAttribute("_kind"));
                      kind == "marginal_heatmap")
                    {
                      grm_args_t *input_args = grm_args_new();

                      grm_args_push(input_args, "x", "i", mouse_x);
                      grm_args_push(input_args, "y", "i", mouse_y);
                      grm_input(input_args);
                      redraw();
                    }
                }
            }

          cursorHandler(mouse_x, mouse_y); // get the correct cursor and sets it
          update();
        }
    }
}

void GRPlotWidget::possibleElementsMenuSlot()
{
  auto act = qobject_cast<QAction *>(sender());
  int bbox_id = act->data().value<int>();

  auto global_root = grm_get_document_root();
  auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(bbox_id) + "\"]");
  auto bbox_x_min = static_cast<double>(elem->getAttribute("_bbox_x_min"));
  auto bbox_x_max = static_cast<double>(elem->getAttribute("_bbox_x_max"));
  auto bbox_y_min = static_cast<double>(elem->getAttribute("_bbox_y_min"));
  auto bbox_y_max = static_cast<double>(elem->getAttribute("_bbox_y_max"));
  auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, elem);

  possible_elems_menu->clear();
  current_selection = bbox;

  // special case if ctrl is used in combination with the possibleElementsMenuWidget
  std::vector<BoundingObject> cur_clicked;
  cur_clicked.emplace_back(*bbox);
  clicked = cur_clicked;
  auto rel_cursor_pos = mapFromGlobal(QCursor::pos());
  cursorHandler(rel_cursor_pos.x(), rel_cursor_pos.y()); // get the correct cursor and sets it

  // without this, elements selected with this menu don't get selected properly inside the tree widget
  for (const auto &e : grm_get_document_root()->querySelectorsAll("[_highlighted=\"1\"]"))
    {
      e->removeAttribute("_highlighted");
    }

  redraw();
  selected_elem_via_menu = true;
}

void GRPlotWidget::disableGridSlot()
{
  auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const auto coordinate_system = plot_elem->querySelectors("coordinate_system");

  bool draw_grid =
      static_cast<int>(coordinate_system->querySelectors("axis[axis_type=\"y\"]")->getAttribute("draw_grid"));
  if (coordinate_system->querySelectors("axis[location=\"twin_x\"]") == nullptr &&
      coordinate_system->querySelectors("axis[location=\"twin_y\"]") == nullptr)
    {
      for (const auto &axis_elem : coordinate_system->querySelectorsAll("axis"))
        {
          auto location = static_cast<std::string>(axis_elem->getAttribute("location"));
          if (location == "x" || location == "y") axis_elem->setAttribute("draw_grid", !draw_grid);
        }
    }

  redraw();
}

void GRPlotWidget::highlightMoveToPlotSlot(int index)
{
  std::vector<std::string> move_to_plot_vec;
  for (const auto &elem : grm_get_document_root()->querySelectorsAll("plot"))
    {
      move_to_plot_vec.push_back(static_cast<std::string>(elem->getAttribute("plot_id")));
    }
  move_to_plot_vec.push_back("new_plot"); // to move a series into a new plot
  move_to_plot_vec.push_back("");

  auto new_plot_id = move_to_plot_vec[index];
  if (new_plot_id != "new_plot" && new_plot_id != "")
    plot_id = new_plot_id;
  else
    plot_id = "";
  redraw();
}

void GRPlotWidget::mousePressEvent(QMouseEvent *event)
{
  mouse_state.pressed = event->pos();
  if (event->button() == Qt::MouseButton::RightButton)
    {
      if (enable_editor)
        {
          auto pos = event->pos();
          add_pos_x = pos.x();
          add_pos_y = pos.y();
          add_overlay_menu->exec(this->mapToGlobal(pos));
        }
      else
        {
          mouse_state.mode = MouseState::Mode::BOXZOOM;
          rubber_band->setGeometry(QRect(mouse_state.pressed, QSize()));
          rubber_band->show();
        }
    }
  else if (event->button() == Qt::MouseButton::LeftButton)
    {
      int x, y;
      BoundingObject *old_current_selection = nullptr;
      getMousePos(event, &x, &y);
      selected_elem_via_menu = false;
      if (current_selection != nullptr &&
          !grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]").empty())
        {
          // current_selection should always be part of current_selections
          std::unique_ptr<BoundingObject> tmp(new BoundingObject(current_selection[0]));
          addCurrentSelection(std::move(tmp));
        }
      if (current_selections.empty())
        {
          if (grm_get_document_root()->querySelectors("[_selected_for_move=\"1\"]"))
            mouse_state.mode = MouseState::Mode::MOVE_SELECTED;
          else
            mouse_state.mode = MouseState::Mode::PAN;
        }
      else if (current_selection != nullptr)
        {
          if (auto elem_name = current_selection->getRef()->localName();
              enable_advanced_editor ||
              (elem_name == "figure" || elem_name == "plot" || elem_name == "layout_grid" ||
               elem_name == "layout_grid_element" || elem_name == "colorbar" || elem_name == "label" ||
               elem_name == "titles_3d" || elem_name == "text" || elem_name == "central_region" ||
               elem_name == "side_region" || elem_name == "marginal_heatmap_plot" || elem_name == "legend" ||
               elem_name == "text_region" || elem_name == "overlay_element"))
            {
              if (cursor().shape() == Qt::SizeBDiagCursor)
                mouse_state.mode = MouseState::Mode::B_DIAGONAL_SCALE_SELECTED;
              else if (cursor().shape() == Qt::SizeHorCursor)
                mouse_state.mode = MouseState::Mode::HORIZONTAL_SCALE_SELECTED;
              else if (cursor().shape() == Qt::SizeVerCursor)
                mouse_state.mode = MouseState::Mode::VERTICAL_SCALE_SELECTED;
              else if (cursor().shape() == Qt::SizeFDiagCursor)
                mouse_state.mode = MouseState::Mode::F_DIAGONAL_SCALE_SELECTED;
              else if (cursor().shape() == Qt::SizeAllCursor)
                mouse_state.mode = MouseState::Mode::MOVE_SELECTED;
              old_current_selection = current_selection;
            }
        }
      mouse_state.anchor.setX(x);
      mouse_state.anchor.setY(y);

      int cursor_state = grm_get_hover_mode(x, y, disable_movable_xform);
      if (!enable_editor && cursor_state != DEFAULT_HOVER_MODE && cursor_state != LEGEND_ELEMENT_HOVER_MODE)
        {
          grm_args_t *args = grm_args_new();
          grm_args_push(args, "clear_locked_state", "i", 1);
          grm_input(args);
          grm_args_delete(args);

          mouse_state.mode = MouseState::Mode::MOVABLE_XFORM;
        }

      if (enable_editor)
        {
          bool set_selected_for_move = false;
          for (const auto &elem : grm_get_document_root()->querySelectorsAll("[_highlighted=\"1\"]"))
            {
              elem->removeAttribute("_highlighted");
            }
          prev_highlighted_tick_group_elem.reset();
          auto elements_at_pos = bounding_logic->getElementsAtPoint(x, y, hide_grid_bbox, enable_advanced_editor);
          if (!elements_at_pos.empty())
            {
              std::vector<BoundingObject> cur_clicked;
              possible_elems_menu->clear();
              for (const auto &set_elem : elements_at_pos)
                {
                  auto global_root = grm_get_document_root();
                  auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string((int)(set_elem)) + "\"]");
                  if (elem == nullptr || !elem->hasAttribute("_bbox_x_min")) continue;
                  auto bbox_x_min = static_cast<double>(elem->getAttribute("_bbox_x_min"));
                  auto bbox_x_max = static_cast<double>(elem->getAttribute("_bbox_x_max"));
                  auto bbox_y_min = static_cast<double>(elem->getAttribute("_bbox_y_min"));
                  auto bbox_y_max = static_cast<double>(elem->getAttribute("_bbox_y_max"));
                  auto *bbox =
                      new BoundingObject((int)(set_elem), bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, elem);
                  cur_clicked.emplace_back(*bbox);

                  if (old_current_selection == nullptr)
                    {
                      if (elements_at_pos.size() > 1)
                        {
                          auto act = new QAction(elem->localName().c_str());
                          act->setData((int)(set_elem));
                          connect(act, &QAction::triggered, this, &GRPlotWidget::possibleElementsMenuSlot);
                          possible_elems_menu->addAction(act);
                        }
                      else if (elements_at_pos.size() == 1)
                        {
                          current_selection = bbox;
                        }
                    }
                }
              clicked = cur_clicked;
              if (elements_at_pos.size() > 1 && old_current_selection == nullptr)
                {
                  auto pos = event->pos();
                  possible_elems_menu->exec(this->mapToGlobal(pos));
                }
            }
          else
            {
              clicked.clear();
              current_selection = nullptr;
              tree_widget->updateData(grm_get_document_root());
              update();
              return;
            }
          if (current_selection == nullptr)
            {
              possible_elems_menu->clear();
              possible_elems_menu->close();
              return;
            }
          current_selection->getRef()->setAttribute("_highlighted", true);
          if (auto name = current_selection->getRef()->localName();
              name == "tick_group" || name == "tick" || name == "grid_line" || name == "text")
            {
              prev_highlighted_tick_group_elem = current_selection->getRef();
            }
          if (ctrl_key_mode)
            {
              bool removed_selection = false;
              std::unique_ptr<BoundingObject> tmp(new BoundingObject(clicked[0]));
              for (auto it = std::begin(current_selections); it != std::end(current_selections); ++it)
                {
                  if ((*it)->getRef() == tmp->getRef())
                    {
                      (*it)->getRef()->setAttribute("_selected_for_move", 0);
                      it = current_selections.erase(it);
                      removed_selection = true;
                      break;
                    }
                }
              prev_selection.reset();
              if (!removed_selection)
                {
                  if (auto elem_name = tmp->getRef()->localName();
                      (enable_advanced_editor ||
                       (elem_name == "figure" || elem_name == "plot" || elem_name == "layout_grid" ||
                        elem_name == "layout_grid_element" || elem_name == "colorbar" || elem_name == "label" ||
                        elem_name == "titles_3d" || elem_name == "text" || elem_name == "central_region" ||
                        elem_name == "side_region" || elem_name == "marginal_heatmap_plot" || elem_name == "legend" ||
                        elem_name == "text_region" || elem_name == "overlay_element")) &&
                      (elem_name != "coordinate_system" &&
                       !(elem_name == "layout_grid" && tmp->getRef()->parentElement()->localName() != "layout_grid")))
                    {
                      tmp->getRef()->setAttribute("_selected_for_move", 1);
                      set_selected_for_move = true;
                    }
                  addCurrentSelection(std::move(tmp));
                }
              else
                {
                  auto multiple_selections = grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]");
                  selection_list_widget->updateSelectionList(multiple_selections);
                }

              if (auto elem_name = current_selection->getRef()->localName();
                  enable_advanced_editor ||
                  (elem_name == "figure" || elem_name == "plot" || elem_name == "layout_grid" ||
                   elem_name == "layout_grid_element" || elem_name == "colorbar" || elem_name == "label" ||
                   elem_name == "titles_3d" || elem_name == "text" || elem_name == "central_region" ||
                   elem_name == "side_region" || elem_name == "marginal_heatmap_plot" || elem_name == "legend" ||
                   elem_name == "text_region" || elem_name == "overlay_element"))
                {
                  if (cursor_state == VERTICAL_SCALE_HOVER_MODE)
                    mouse_state.mode = MouseState::Mode::VERTICAL_SCALE_SELECTED;
                  else if (cursor_state == HORIZONTAL_SCALE_HOVER_MODE)
                    mouse_state.mode = MouseState::Mode::HORIZONTAL_SCALE_SELECTED;
                  else if (cursor_state == B_DIAGONAL_SCALE_HOVER_MODE)
                    mouse_state.mode = MouseState::Mode::B_DIAGONAL_SCALE_SELECTED;
                  else if (cursor_state == F_DIAGONAL_SCALE_HOVER_MODE)
                    mouse_state.mode = MouseState::Mode::F_DIAGONAL_SCALE_SELECTED;
                  else
                    mouse_state.mode = MouseState::Mode::MOVE_SELECTED;
                }
            }
          tree_widget->updateData(grm_get_document_root());
          tree_widget->selectItem(current_selection->getRef());
          mouse_move_selections.clear();
          if (ctrl_key_mode && set_selected_for_move)
            {
              auto multiple_selections = grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]");
              if (multiple_selections.size() > 1) attributeEditEvent(false);
              if (multiple_selections.size() > 0)
                {
                  selection_list_widget->updateSelectionList(multiple_selections);
                  selection_list_widget->show();
                  show_selection_list_widget_act->trigger();
                  ignore_resize = 2;
                }
            }
          if (!ctrl_key_mode) attributeEditEvent();
          // needed since key-release event isnt triggert after an element got selected in this menu
          if (selected_elem_via_menu) ctrl_key_mode = false;
        }
      else
        {
          if (cursor_state == LEGEND_ELEMENT_HOVER_MODE) mouse_state.mode = MouseState::Mode::LEGEND_SELECTED;

          cursorHandler(x, y);
        }

      // mark the last clicked plot to adjust the plot type menu
      if (auto global_root = grm_get_document_root();
          auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid"))
        {
          int width, height;
          double viewport[4];
          GRM::getFigureSize(&width, &height, nullptr, nullptr);
          auto max_width_height = std::max(width, height);
          auto ndc_x = (double)x / max_width_height;
          auto ndc_y = (double)(height - y) / max_width_height;

          for (const auto &plot_elem : layout_grid->querySelectorsAll("plot"))
            {
              if (!GRM::Render::getViewport(plot_elem, &viewport[0], &viewport[1], &viewport[2], &viewport[3]))
                throw NotFoundError("Plot doesn't have a viewport but it should.\n");
              if (viewport[0] <= ndc_x && ndc_x <= viewport[1] && viewport[2] <= ndc_y && ndc_y <= viewport[3])
                {
                  for (const auto &selected : global_root->querySelectorsAll("[_selected_for_menu]"))
                    {
                      selected->removeAttribute("_selected_for_menu");
                    }
                  plot_elem->setAttribute("_selected_for_menu", true);
                  plot_elem->setAttribute("_active", true);
                  adjustPlotTypeMenu(plot_elem);
                }
            }
        }
    }
}

void GRPlotWidget::mouseReleaseEvent(QMouseEvent *event)
{
  grm_args_t *args = grm_args_new();
  int x, y;
  bool was_pan = false;
  getMousePos(event, &x, &y);

  if (mouse_state.mode == MouseState::Mode::BOXZOOM)
    {
      rubber_band->hide();
      if (std::abs(x - mouse_state.pressed.x()) >= 5 && std::abs(y - mouse_state.pressed.y()) >= 5)
        {
          grm_args_push(args, "keep_aspect_ratio", "i", event->modifiers() & Qt::ShiftModifier);
          grm_args_push(args, "x1", "i", mouse_state.pressed.x());
          grm_args_push(args, "y1", "i", mouse_state.pressed.y());
          grm_args_push(args, "x2", "i", x);
          grm_args_push(args, "y2", "i", y);
        }
    }
  else if (mouse_state.mode == MouseState::Mode::PAN)
    {
      was_pan = true;
      mouse_state.mode = MouseState::Mode::NORMAL;
    }
  else if (mouse_state.mode == MouseState::Mode::MOVABLE_XFORM)
    {
      mouse_state.mode = MouseState::Mode::NORMAL;

      cursorHandler(x, y);
    }
  else if (mouse_state.mode == MouseState::Mode::MOVE_SELECTED)
    {
      mouse_state.mode = MouseState::Mode::NORMAL;
    }
  else if (mouse_state.mode == MouseState::Mode::HORIZONTAL_SCALE_SELECTED)
    {
      mouse_state.mode = MouseState::Mode::NORMAL;
    }
  else if (mouse_state.mode == MouseState::Mode::VERTICAL_SCALE_SELECTED)
    {
      mouse_state.mode = MouseState::Mode::NORMAL;
    }
  else if (mouse_state.mode == MouseState::Mode::B_DIAGONAL_SCALE_SELECTED)
    {
      mouse_state.mode = MouseState::Mode::NORMAL;
    }
  else if (mouse_state.mode == MouseState::Mode::F_DIAGONAL_SCALE_SELECTED)
    {
      mouse_state.mode = MouseState::Mode::NORMAL;
    }
  else if (mouse_state.mode == MouseState::Mode::LEGEND_SELECTED)
    {
      mouse_state.mode = MouseState::Mode::NORMAL;

      grm_args_push(args, "x", "i", x);
      grm_args_push(args, "y", "i", y);
      grm_args_push(args, "movable_state", "i", LEGEND_ELEMENT_HOVER_MODE);

      cursorHandler(x, y);
    }

  grm_input(args);
  grm_args_delete(args);

  if (!was_pan) redraw();
}

void GRPlotWidget::resizeEvent(QResizeEvent *event)
{
  const auto global_root = grm_get_document_root();
  if (auto figure = global_root->querySelectors("figure[active=1]"); figure != nullptr)
    {
      figure->setAttribute("size_x", (double)event->size().width());
      figure->setAttribute("size_y", (double)event->size().height());
    }
  else
    {
      grm_args_push(args_, "size", "dd", (double)event->size().width(), (double)event->size().height());
      grm_merge(args_);
    }

  prev_selection.reset();
  mouse_move_selections.clear();
  clicked.clear();
  tooltips.clear();
  current_selections.clear();
  resetPixmap();
  if (ignore_resize == 0)
    {
      referred_elements_highlights_cache.clear();
      mask_highlights_cache.clear();
    }
}

void GRPlotWidget::wheelEvent(QWheelEvent *event)
{
  if (event->angleDelta().y() != 0 && !enable_editor)
    {
      int x, y;
      getWheelPos(event, &x, &y);

      grm_args_t *args = grm_args_new();
      grm_args_push(args, "x", "i", x);
      grm_args_push(args, "y", "i", y);
      grm_args_push(args, "angle_delta", "d", static_cast<double>(event->angleDelta().y()));
      grm_input(args);
      grm_args_delete(args);

      redraw();
    }
}

void GRPlotWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
  if (enable_editor)
    {
      if (event->button() == Qt::MouseButton::LeftButton)
        {
          attributeEditEvent();
        }
    }
  else
    {
      GR_UNUSED(event);
      grm_args_t *args = grm_args_new();
      const QPoint pos = mapFromGlobal(QCursor::pos());
      grm_args_push(args, "key", "s", "r");
      grm_args_push(args, "x", "i", pos.x());
      grm_args_push(args, "y", "i", pos.y());
      grm_input(args);
      grm_args_delete(args);

      redraw();
    }
}

void GRPlotWidget::heatmap()
{
  hide_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"marginal_heatmap_plot", "series_surface",  "series_wireframe",
                                                       "series_contour",        "series_contourf", "series_imshow"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "heatmap");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    {
      current_selection->getRef()->removeAttribute("_highlighted");
      prev_highlighted_tick_group_elem.reset();
    }
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::marginalHeatmapAll()
{
  show_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_heatmap", "series_surface",  "series_wireframe",
                                                       "series_contour", "series_contourf", "series_imshow"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          if (series_elem->parentElement()->parentElement()->localName() != "marginal_heatmap_plot")
            series_elem->setAttribute("kind", "marginal_heatmap");
        }
    }
  for (const auto &series_elem : plot_elem->querySelectorsAll("marginal_heatmap_plot"))
    {
      series_elem->setAttribute("marginal_heatmap_kind", "all");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    {
      current_selection->getRef()->removeAttribute("_highlighted");
      prev_highlighted_tick_group_elem.reset();
    }
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::marginalHeatmapLine()
{
  show_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_heatmap", "series_surface",  "series_wireframe",
                                                       "series_contour", "series_contourf", "series_imshow"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          if (series_elem->parentElement()->parentElement()->localName() != "marginal_heatmap_plot")
            series_elem->setAttribute("kind", "marginal_heatmap");
        }
    }
  for (const auto &series_elem : plot_elem->querySelectorsAll("marginal_heatmap_plot"))
    {
      series_elem->setAttribute("marginal_heatmap_kind", "line");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    {
      current_selection->getRef()->removeAttribute("_highlighted");
      prev_highlighted_tick_group_elem.reset();
    }
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::line()
{
  bool update;
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_scatter"))
    {
      elem->setAttribute("kind", "line");
    }

  // to get the same lines then before all lines have to exist during render call so that the line_spec work properly
  grm_get_render()->getAutoUpdate(&update);
  grm_get_render()->setAutoUpdate(false);
  for (const auto &elem : plot_elem->querySelectorsAll("series_line"))
    {
      elem->removeAttribute("line_color_ind");
      elem->setAttribute("_update_required", true);
    }
  grm_get_render()->setAutoUpdate(update);
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    {
      current_selection->getRef()->removeAttribute("_highlighted");
      prev_highlighted_tick_group_elem.reset();
    }
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::sumAlgorithm()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  max_act->setChecked(false);
  for (const auto &elem : plot_elem->querySelectorsAll("marginal_heatmap_plot"))
    {
      elem->setAttribute("algorithm", "sum");
    }
  redraw();
}

void GRPlotWidget::maxAlgorithm()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  sum_act->setChecked(false);
  for (const auto &elem : plot_elem->querySelectorsAll("marginal_heatmap_plot"))
    {
      elem->setAttribute("algorithm", "max");
    }
  redraw();
}

void GRPlotWidget::volume()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_isosurface"))
    {
      elem->setAttribute("kind", "volume");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}
void GRPlotWidget::isosurface()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_volume"))
    {
      elem->setAttribute("kind", "isosurface");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::surface()
{
  hide_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"marginal_heatmap_plot", "series_heatmap",  "series_wireframe",
                                                       "series_contour",        "series_contourf", "series_imshow"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "surface");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}
void GRPlotWidget::wireframe()
{
  hide_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"marginal_heatmap_plot", "series_surface",  "series_heatmap",
                                                       "series_contour",        "series_contourf", "series_imshow"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "wireframe");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::contour()
{
  hide_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"marginal_heatmap_plot", "series_surface",  "series_wireframe",
                                                       "series_heatmap",        "series_contourf", "series_imshow"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "contour");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::imshow()
{
  hide_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"marginal_heatmap_plot", "series_surface",  "series_wireframe",
                                                       "series_contour",        "series_contourf", "series_heatmap"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "imshow");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::line3()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_scatter3", "series_tricontour", "series_trisurface",
                                                       "series_scatter"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "line3");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::contourf()
{
  hide_algo_menu_act->trigger();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"marginal_heatmap_plot", "series_surface", "series_wireframe",
                                                       "series_contour",        "series_heatmap", "series_imshow"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "contourf");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::trisurf()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_scatter3", "series_tricontour", "series_line3",
                                                       "series_scatter"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "trisurface");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::tricont()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_scatter3", "series_line3", "series_trisurface",
                                                       "series_scatter"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "tricontour");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::scatter3()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_line3", "series_tricontour", "series_trisurface",
                                                       "series_scatter"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "scatter3");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::scatter()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_line"))
    {
      elem->setAttribute("kind", "scatter");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::barplot()
{
  bool update;
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_stem", "series_stairs"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "barplot");
        }
    }

  // to get the same bars then before all bars have to exist during render call so that the line_spec work properly
  grm_get_render()->getAutoUpdate(&update);
  grm_get_render()->setAutoUpdate(false);
  for (const auto &elem : plot_elem->querySelectorsAll("series_barplot"))
    {
      elem->removeAttribute("fill_color_ind");
      elem->setAttribute("_update_required", true);
    }
  grm_get_render()->setAutoUpdate(update);
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::stairs()
{
  bool update;
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_barplot", "series_stem"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "stairs");
        }
    }

  // to get the same lines then before all lines have to exist during render call so that the line_spec work properly
  grm_get_render()->getAutoUpdate(&update);
  grm_get_render()->setAutoUpdate(false);
  for (const auto &elem : plot_elem->querySelectorsAll("series_stairs"))
    {
      elem->setAttribute("_update_required", true);
      elem->removeAttribute("line_color_ind");
    }
  grm_get_render()->setAutoUpdate(update);
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::stem()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");
  const std::vector<std::string> valid_series_names = {"series_barplot", "series_stairs"};

  for (const auto &name : valid_series_names)
    {
      auto series_elements = plot_elem->querySelectorsAll(name);
      for (const auto &series_elem : series_elements)
        {
          series_elem->setAttribute("kind", "stem");
        }
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::shade()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_hexbin"))
    {
      elem->setAttribute("kind", "shade");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::hexbin()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_shade"))
    {
      elem->setAttribute("kind", "hexbin");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::polarLine()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_polar_scatter"))
    {
      elem->setAttribute("kind", "polar_line");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::polarScatter()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto plot_elem = (layout_grid != nullptr) ? layout_grid->querySelectors("[_selected_for_menu]")
                                                  : global_root->querySelectors("figure[active=1]");

  for (const auto &elem : plot_elem->querySelectorsAll("series_polar_line"))
    {
      elem->setAttribute("kind", "polar_scatter");
    }
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("highlighted");
  current_selection = nullptr;
  current_selections.clear();
  edit_element_widget->hide();
  hide_add_element_act->trigger();
  redraw();
}

void GRPlotWidget::exportGraphics(const QString &file_type, const QString &file_ext)
{
  QFileDialog file_dialog(this, "Save " + file_type, QDir::currentPath(), file_type + " files (*." + file_ext + ")");
  file_dialog.selectFile((input_data_name + file_ext.toStdString()).c_str());
  file_dialog.setDefaultSuffix("." + file_ext);
  file_dialog.setAcceptMode(QFileDialog::AcceptSave);
  if (!file_dialog.exec()) return;
  auto save_file_name = file_dialog.selectedFiles().front().toStdString();
  if (save_file_name.empty()) return;
  file_export = save_file_name;
  redraw();
}

void GRPlotWidget::pdf()
{
  exportGraphics("PDF", "xml.pdf");
}

void GRPlotWidget::png()
{
  exportGraphics("PNG", "xml.png");
}

void GRPlotWidget::jpeg()
{
  exportGraphics("JPEG", "jpg");
}

void GRPlotWidget::svg()
{
  exportGraphics("SVG", "xml.svg");
}

void GRPlotWidget::moveableMode()
{
  disable_movable_xform = !disable_movable_xform;
  moveable_mode_act->setChecked(disable_movable_xform);
}

void GRPlotWidget::xLogSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool x_log = plot_elem->hasAttribute("x_log") && static_cast<int>(plot_elem->getAttribute("x_log"));
  plot_elem->setAttribute("x_log", !x_log);
  x_log_act->setChecked(!x_log);

  redraw();
}

void GRPlotWidget::yLogSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool y_log = plot_elem->hasAttribute("y_log") && static_cast<int>(plot_elem->getAttribute("y_log"));
  plot_elem->setAttribute("y_log", !y_log);
  y_log_act->setChecked(!y_log);

  redraw();
}

void GRPlotWidget::zLogSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool z_log = plot_elem->hasAttribute("z_log") && static_cast<int>(plot_elem->getAttribute("z_log"));
  plot_elem->setAttribute("z_log", !z_log);
  z_log_act->setChecked(!z_log);

  redraw();
}

void GRPlotWidget::rLogSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool r_log = plot_elem->hasAttribute("r_log") && static_cast<int>(plot_elem->getAttribute("r_log"));
  plot_elem->setAttribute("r_log", !r_log);
  r_log_act->setChecked(!r_log);

  redraw();
}

void GRPlotWidget::xFlipSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool x_flip = plot_elem->hasAttribute("x_flip") && static_cast<int>(plot_elem->getAttribute("x_flip"));
  plot_elem->setAttribute("x_flip", !x_flip);
  x_flip_act->setChecked(!x_flip);

  redraw();
}

void GRPlotWidget::yFlipSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool y_flip = plot_elem->hasAttribute("y_flip") && static_cast<int>(plot_elem->getAttribute("y_flip"));
  plot_elem->setAttribute("y_flip", !y_flip);
  y_flip_act->setChecked(!y_flip);

  redraw();
}

void GRPlotWidget::zFlipSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool z_flip = plot_elem->hasAttribute("z_flip") && static_cast<int>(plot_elem->getAttribute("z_flip"));
  plot_elem->setAttribute("z_flip", !z_flip);
  z_flip_act->setChecked(!z_flip);

  redraw();
}

void GRPlotWidget::thetaFlipSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool theta_flip = plot_elem->hasAttribute("theta_flip") && static_cast<int>(plot_elem->getAttribute("theta_flip"));
  plot_elem->setAttribute("theta_flip", !theta_flip);
  theta_flip_act->setChecked(!theta_flip);

  redraw();
}

void GRPlotWidget::useGR3Slot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto series_elements = figure_elem->querySelectorsAll("series_surface");
  for (const auto &series_elem : series_elements)
    {
      bool use_gr3 = static_cast<int>(series_elem->getAttribute("use_gr3"));
      series_elem->setAttribute("use_gr3", !use_gr3);
    }

  redraw();
}

void GRPlotWidget::polarWithPanSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  bool polar_with_pan =
      plot_elem->hasAttribute("polar_with_pan") && static_cast<int>(plot_elem->getAttribute("polar_with_pan"));
  plot_elem->setAttribute("polar_with_pan", !polar_with_pan);

  redraw();
}

void GRPlotWidget::keepWindowSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto central_region = figure_elem->querySelectors("central_region");

  bool keep_window =
      central_region->hasAttribute("keep_window") && static_cast<int>(central_region->getAttribute("keep_window"));
  central_region->setAttribute("keep_window", !keep_window);
  keep_window_act->setChecked(!keep_window);

  redraw();
}

void GRPlotWidget::colormapSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }

  auto colormap_old = GRM::colormapIntToString(static_cast<int>(plot_elem->getAttribute("colormap")));
  auto colormap_inverted =
      plot_elem->hasAttribute("colormap_inverted") && static_cast<int>(plot_elem->getAttribute("colormap_inverted"));

  QList<QRadioButton *> radio_buttons;
  QDialog dialog(this);
  QString title("Colormaps");
  dialog.setWindowTitle(title);
  dialog.setMinimumWidth(300);
  auto form = new QFormLayout;
  auto colormap_names = GRM::getColormaps();

  // needed information to generate linear data for the context
  for (const auto &name : colormap_names)
    {
      QString tooltip_string = name.c_str();
      auto colormap = QPixmap((":/preview_images/colormaps/" + name + ".png").c_str());
      colormap.setDevicePixelRatio(10);
      if (colormap_inverted) colormap = colormap.transformed(QTransform().rotate(180));
      auto label_pix = new QLabel();
      label_pix->setPixmap(colormap);
      label_pix->setToolTip(tooltip_string);
      auto button = new QRadioButton(this);
      if (name == colormap_old) button->setChecked(true);

      form->addRow(label_pix, button);
      radio_buttons << button;
    }

  auto checkbox = new QCheckBox(this);
  checkbox->setToolTip("If checked flip the selected colormap");
  if (colormap_inverted) checkbox->setChecked(true);
  auto text_label = QString("colormap_inverted");
  form->addRow(text_label, checkbox);

  auto scroll_area_content = new QWidget;
  scroll_area_content->setLayout(form);
  auto scroll_area = new QScrollArea;
  scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  scroll_area->setWidgetResizable(true);
  scroll_area->setWidget(scroll_area_content);

  auto group_box_layout = new QVBoxLayout;
  group_box_layout->addWidget(scroll_area);
  QDialogButtonBox button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
  group_box_layout->addWidget(&button_box);
  QObject::connect(&button_box, SIGNAL(accepted()), &dialog, SLOT(accept()));
  QObject::connect(&button_box, SIGNAL(rejected()), &dialog, SLOT(reject()));
  dialog.setLayout(group_box_layout);

  if (dialog.exec() == QDialog::Accepted)
    {
      std::vector<std::string> values;
      std::vector<double> data_vec;
      std::shared_ptr<GRM::Context> context = grm_get_render()->getContext();

      if (enable_editor) createHistoryElement();

      for (int i = 0; i < colormap_names.size(); i++)
        {
          if (radio_buttons[i]->isChecked())
            {
              plot_elem->setAttribute("colormap", GRM::colormapStringToInt(colormap_names[i]));
              auto colormap = QPixmap((":/preview_images/colormaps/" + colormap_names[i] + ".png").c_str());
              colormap = colormap.scaled(20, 20);
              if (checkbox->isChecked()) colormap = colormap.transformed(QTransform().rotate(180));
              colormap_act->setIcon(colormap);
            }
        }

      plot_elem->setAttribute("colormap_inverted", checkbox->isChecked());
    }

  redraw();
}

void GRPlotWidget::selectableGridSlot()
{
  hide_grid_bbox = !hide_grid_bbox;
  selectable_grid_act->setChecked(!hide_grid_bbox);
}

void GRPlotWidget::xLimSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;
  const auto coordinate_system = plot_elem->querySelectors("coordinate_system");
  bool timestamp = coordinate_system != nullptr && coordinate_system->hasAttribute("_time_axis") &&
                   static_cast<int>(coordinate_system->getAttribute("_time_axis"));

  QList<QLineEdit *> fields;
  QDialog dialog(this);
  dialog.setWindowTitle("Set X Lim:");
  auto form = new QFormLayout;

  auto lim_min_label = new QLabel(QString("Min:"));
  auto line_edit = new QLineEdit();
  if (plot_elem->hasAttribute("x_lim_min"))
    {
      auto lim_min_text = static_cast<std::string>(plot_elem->getAttribute("x_lim_min"));
      if (timestamp)
        {
          time_t t = std::stoi(lim_min_text);
          struct tm tm = *localtime(&t);
          char out[50];
          strftime(out, 50, "%Y-%m-%dT%H:%M:%S", &tm);
          lim_min_text = out;
        }
      line_edit->setText(lim_min_text.c_str());
    }
  form->addRow(lim_min_label, line_edit);
  fields << line_edit;

  auto lim_max_label = new QLabel(QString("Max:"));
  line_edit = new QLineEdit();
  if (plot_elem->hasAttribute("x_lim_max"))
    {
      auto lim_max_text = static_cast<std::string>(plot_elem->getAttribute("x_lim_max"));
      if (timestamp)
        {
          time_t t = std::stoi(lim_max_text);
          struct tm tm = *localtime(&t);
          char out[50];
          strftime(out, 50, "%Y-%m-%dT%H:%M:%S", &tm);
          lim_max_text = out;
        }
      line_edit->setText(lim_max_text.c_str());
    }
  form->addRow(lim_max_label, line_edit);
  fields << line_edit;

  QDialogButtonBox button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
  form->addRow(&button_box);
  QObject::connect(&button_box, SIGNAL(accepted()), &dialog, SLOT(accept()));
  QObject::connect(&button_box, SIGNAL(rejected()), &dialog, SLOT(reject()));

  dialog.setLayout(form);

  if (dialog.exec() == QDialog::Accepted)
    {
      std::shared_ptr<GRM::Context> context = grm_get_render()->getContext();

      if (enable_editor) createHistoryElement();

      if (fields[0]->isModified())
        {
          if (!fields[0]->text().isEmpty())
            {
              if (timestamp)
                {
                  struct tm timestamp_tm;
                  if (util::parseIso8601WithoutTimezone(fields[0]->text().toStdString(), timestamp_tm))
                    {
                      plot_elem->setAttribute("x_lim_min", (int)mktime(&timestamp_tm));
                    }
                }
              else
                plot_elem->setAttribute("x_lim_min", std::stod(fields[0]->text().toStdString()));
            }
          else if (plot_elem->hasAttribute("x_lim_min"))
            plot_elem->removeAttribute("x_lim_min");
        }
      if (fields[1]->isModified())
        {
          if (!fields[1]->text().isEmpty())
            {
              if (timestamp)
                {
                  struct tm timestamp_tm;
                  if (util::parseIso8601WithoutTimezone(fields[1]->text().toStdString(), timestamp_tm))
                    {
                      plot_elem->setAttribute("x_lim_max", (int)mktime(&timestamp_tm));
                    }
                }
              else
                plot_elem->setAttribute("x_lim_max", std::stod(fields[1]->text().toStdString()));
            }
          else if (plot_elem->hasAttribute("x_lim_max"))
            plot_elem->removeAttribute("x_lim_max");
        }
    }

  redraw();
}

void GRPlotWidget::yLimSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  QList<QLineEdit *> fields;
  QDialog dialog(this);
  dialog.setWindowTitle("Set Y Lim:");
  auto form = new QFormLayout;

  auto lim_min_label = new QLabel(QString("Min:"));
  auto line_edit = new QLineEdit();
  if (plot_elem->hasAttribute("y_lim_min"))
    line_edit->setText(static_cast<std::string>(plot_elem->getAttribute("y_lim_min")).c_str());
  form->addRow(lim_min_label, line_edit);
  fields << line_edit;

  auto lim_max_label = new QLabel(QString("Max:"));
  line_edit = new QLineEdit();
  if (plot_elem->hasAttribute("y_lim_max"))
    line_edit->setText(static_cast<std::string>(plot_elem->getAttribute("y_lim_max")).c_str());
  form->addRow(lim_max_label, line_edit);
  fields << line_edit;

  QDialogButtonBox button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
  form->addRow(&button_box);
  QObject::connect(&button_box, SIGNAL(accepted()), &dialog, SLOT(accept()));
  QObject::connect(&button_box, SIGNAL(rejected()), &dialog, SLOT(reject()));

  dialog.setLayout(form);

  if (dialog.exec() == QDialog::Accepted)
    {
      std::shared_ptr<GRM::Context> context = grm_get_render()->getContext();

      if (enable_editor) createHistoryElement();

      if (fields[0]->isModified())
        {
          if (!fields[0]->text().isEmpty())
            plot_elem->setAttribute("y_lim_min", std::stod(fields[0]->text().toStdString()));
          else if (plot_elem->hasAttribute("y_lim_min"))
            plot_elem->removeAttribute("y_lim_min");
        }
      if (fields[1]->isModified())
        {
          if (!fields[1]->text().isEmpty())
            plot_elem->setAttribute("y_lim_max", std::stod(fields[1]->text().toStdString()));
          else if (plot_elem->hasAttribute("y_lim_max"))
            plot_elem->removeAttribute("y_lim_max");
        }
    }

  redraw();
}

void GRPlotWidget::zLimSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }
  if (plot_elem == nullptr) return;

  QList<QLineEdit *> fields;
  QDialog dialog(this);
  dialog.setWindowTitle("Set Z Lim:");
  auto form = new QFormLayout;

  auto lim_min_label = new QLabel(QString("Min:"));
  auto line_edit = new QLineEdit();
  if (plot_elem->hasAttribute("z_lim_min"))
    line_edit->setText(static_cast<std::string>(plot_elem->getAttribute("z_lim_min")).c_str());
  form->addRow(lim_min_label, line_edit);
  fields << line_edit;

  auto lim_max_label = new QLabel(QString("Max:"));
  line_edit = new QLineEdit();
  if (plot_elem->hasAttribute("z_lim_max"))
    line_edit->setText(static_cast<std::string>(plot_elem->getAttribute("z_lim_max")).c_str());
  form->addRow(lim_max_label, line_edit);
  fields << line_edit;

  QDialogButtonBox button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
  form->addRow(&button_box);
  QObject::connect(&button_box, SIGNAL(accepted()), &dialog, SLOT(accept()));
  QObject::connect(&button_box, SIGNAL(rejected()), &dialog, SLOT(reject()));

  dialog.setLayout(form);

  if (dialog.exec() == QDialog::Accepted)
    {
      std::shared_ptr<GRM::Context> context = grm_get_render()->getContext();

      if (enable_editor) createHistoryElement();

      if (fields[0]->isModified()) plot_elem->setAttribute("z_lim_min", std::stod(fields[0]->text().toStdString()));
      if (fields[1]->isModified()) plot_elem->setAttribute("z_lim_max", std::stod(fields[1]->text().toStdString()));
    }

  redraw();
}

void GRPlotWidget::colorRGBPopUp(const std::string &attribute_name, const std::shared_ptr<GRM::Element> &element)
{
  color_picker_rgb->show();
  color_picker_rgb->start(attribute_name, element);
  if (color_picker_rgb->exec() == QDialog::Accepted) redraw();
}

void GRPlotWidget::colorIndexHelper(const std::shared_ptr<GRM::Element> &plot_elem, int current_index,
                                    QGridLayout *grid_layout, QList<QRadioButton *> &radio_buttons, int max_index,
                                    int index_name_start)
{
  int col = 0;
  for (int index = 0; index < max_index; index++)
    {
      int errind;
      double r, g, b;
      QImage image(1, 1, QImage::Format_RGB32);

      if (plot_elem->hasAttribute("colormap"))
        {
          auto colormap = static_cast<int>(plot_elem->getAttribute("colormap"));
          gr_setcolormap(colormap);
        }
      gks_inq_color_rep(-1, index_name_start, -1, &errind, &r, &g, &b);
      QRgb value = qRgb(255 * r, 255 * g, 255 * b);
      image.setPixel(0, 0, value);

      auto label_pix = new QLabel();
      auto color_pic = QPixmap::fromImage(image);
      color_pic = color_pic.scaled(20, 20);
      label_pix->setPixmap(color_pic);
      auto button = new QRadioButton(this);
      auto label = new QLabel(std::to_string(index_name_start).c_str());

      if (index_name_start == current_index)
        {
          button->setChecked(true);
          last_checked_radio_button = button;
        }

      grid_layout->addWidget(button, index / 6, col++ % 18);
      grid_layout->addWidget(label_pix, index / 6, col++ % 18);
      grid_layout->addWidget(label, index / 6, col++ % 18);

      connect(button, SIGNAL(clicked()), this, SLOT(multipleRadioButtonGroupsListener()));
      radio_buttons << button;
      index_name_start += 1;
    }
}
void GRPlotWidget::colorIndexPopUp(const std::string &attribute_name, int current_index,
                                   const std::shared_ptr<GRM::Element> &element)
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }

  QList<QRadioButton *> radio_buttons;
  QDialog dialog(this);
  dialog.setWindowTitle(attribute_name.c_str());
  auto form = new QVBoxLayout;

  auto grid_layout_ansi = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_ansi, radio_buttons, 8, 0);
  auto horizontal_group_box_ansi = new QGroupBox(tr("Ansi colors"));
  horizontal_group_box_ansi->setLayout(grid_layout_ansi);

  auto grid_layout_cmap_reduced = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_cmap_reduced, radio_buttons, 80 - 8, 8);
  auto horizontal_group_box_cmap_reduced = new QGroupBox(tr("Reduced colormap"));
  horizontal_group_box_cmap_reduced->setLayout(grid_layout_cmap_reduced);

  auto grid_layout_diff_colors = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_diff_colors, radio_buttons, 257 - 80, 80);
  auto horizontal_group_box_diff_colors = new QGroupBox(tr("Mixed colors"));
  horizontal_group_box_diff_colors->setLayout(grid_layout_diff_colors);

  auto grid_layout_rainbow = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_rainbow, radio_buttons, 588 - 257, 257);
  auto horizontal_group_box_rainbow = new QGroupBox(tr("Rainbow"));
  horizontal_group_box_rainbow->setLayout(grid_layout_rainbow);

  auto grid_layout_gray = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_gray, radio_buttons, 644 - 588, 588);
  auto horizontal_group_box_gray = new QGroupBox(tr("Gray"));
  horizontal_group_box_gray->setLayout(grid_layout_gray);

  auto grid_layout_blue = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_blue, radio_buttons, 700 - 644, 644);
  auto horizontal_group_box_blue = new QGroupBox(tr("Blue"));
  horizontal_group_box_blue->setLayout(grid_layout_blue);

  auto grid_layout_magenta = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_magenta, radio_buttons, 756 - 700, 700);
  auto horizontal_group_box_magenta = new QGroupBox(tr("Magenta"));
  horizontal_group_box_magenta->setLayout(grid_layout_magenta);

  auto grid_layout_red = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_red, radio_buttons, 812 - 756, 756);
  auto horizontal_group_box_red = new QGroupBox(tr("Red"));
  horizontal_group_box_red->setLayout(grid_layout_red);

  auto grid_layout_yellow = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_yellow, radio_buttons, 868 - 812, 812);
  auto horizontal_group_box_yellow = new QGroupBox(tr("Yellow"));
  horizontal_group_box_yellow->setLayout(grid_layout_yellow);

  auto grid_layout_green = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_green, radio_buttons, 924 - 868, 868);
  auto horizontal_group_box_green = new QGroupBox(tr("Green"));
  horizontal_group_box_green->setLayout(grid_layout_green);

  auto grid_layout_cian = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_cian, radio_buttons, 980 - 924, 924);
  auto horizontal_group_box_cian = new QGroupBox(tr("Cian"));
  horizontal_group_box_cian->setLayout(grid_layout_cian);

  auto grid_layout_high_diff_colors = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_high_diff_colors, radio_buttons, 1000 - 980, 980);
  auto horizontal_group_box_high_diff_colors = new QGroupBox(tr("Easily distinguishable colors"));
  horizontal_group_box_high_diff_colors->setLayout(grid_layout_high_diff_colors);

  auto grid_layout_cmap = new QGridLayout;
  colorIndexHelper(plot_elem, current_index, grid_layout_cmap, radio_buttons, 1255 - 1000, 1000);
  auto horizontal_group_box_cmap = new QGroupBox(tr("Colormap"));
  horizontal_group_box_cmap->setLayout(grid_layout_cmap);

  form->addWidget(horizontal_group_box_high_diff_colors);
  form->addWidget(horizontal_group_box_ansi);
  form->addWidget(horizontal_group_box_cmap);
  form->addWidget(horizontal_group_box_diff_colors);
  form->addWidget(horizontal_group_box_rainbow);
  form->addWidget(horizontal_group_box_gray);
  form->addWidget(horizontal_group_box_blue);
  form->addWidget(horizontal_group_box_magenta);
  form->addWidget(horizontal_group_box_red);
  form->addWidget(horizontal_group_box_yellow);
  form->addWidget(horizontal_group_box_green);
  form->addWidget(horizontal_group_box_cian);
  form->addWidget(horizontal_group_box_cmap_reduced);

  auto scroll_area_content = new QWidget;
  scroll_area_content->setLayout(form);
  auto scroll_area = new QScrollArea;
  scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  scroll_area->setWidgetResizable(true);
  scroll_area->setWidget(scroll_area_content);

  auto group_box_layout = new QVBoxLayout;
  group_box_layout->addWidget(scroll_area);
  QDialogButtonBox button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
  group_box_layout->addWidget(&button_box);
  QObject::connect(&button_box, SIGNAL(accepted()), &dialog, SLOT(accept()));
  QObject::connect(&button_box, SIGNAL(rejected()), &dialog, SLOT(reject()));
  dialog.setLayout(group_box_layout);

  if (dialog.exec() == QDialog::Accepted)
    {
      std::vector<std::string> values;
      std::vector<double> data_vec;
      std::shared_ptr<GRM::Context> context = grm_get_render()->getContext();

      if (enable_editor) createHistoryElement();

      for (int i = 0; i < 1255; i++)
        {
          if (radio_buttons[i]->isChecked()) element->setAttribute(attribute_name, i);
        }
    }

  redraw();
}

void GRPlotWidget::createHistoryElement(std::string flag)
{
  if (grm_get_render() == nullptr)
    {
      QApplication::beep();
      return;
    }

  // keep history element number at <= 10
  if (history_count > 10)
    {
      std::string file_name = std::string(grm_tmp_dir) + "_history" + std::to_string(history_count - 10);
      std::ofstream open_file_stream(file_name);
      if (open_file_stream) std::remove(file_name.c_str());
    }

  std::string save_file_name;
  if (flag == "_forward")
    save_file_name = std::string(grm_tmp_dir) + flag + "_history" + std::to_string(history_forward_count++);
  else
    save_file_name = std::string(grm_tmp_dir) + "_history" + std::to_string(history_count++);
  std::ofstream save_file_stream(save_file_name);
  if (!save_file_stream)
    {
      std::stringstream text_stream;
      text_stream << "Could not create history entry \"" << save_file_name << "\".";
      QMessageBox::critical(this, "History creation not possible", QString::fromStdString(text_stream.str()));
      return;
    }
  auto graphics_tree_str = std::unique_ptr<char, decltype(&std::free)>(grm_dump_graphics_tree_str(), std::free);
  save_file_stream << graphics_tree_str.get() << std::endl;
  save_file_stream.close();
  undo_action->setVisible(true);
  redo_action->setVisible(false);
}

void GRPlotWidget::removeHistoryElement()
{
  if (grm_get_render() == nullptr)
    {
      QApplication::beep();
      return;
    }

  std::string file_name = std::string(grm_tmp_dir) + "_history" + std::to_string(--history_count);
  std::ofstream open_file_stream(file_name);
  if (open_file_stream) std::remove(file_name.c_str());
}

void GRPlotWidget::keepAspectRatioSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }

  bool keep_aspect_ratio =
      plot_elem->hasAttribute("keep_aspect_ratio") && static_cast<int>(plot_elem->getAttribute("keep_aspect_ratio"));
  plot_elem->setAttribute("keep_aspect_ratio", !keep_aspect_ratio);
  keep_aspect_ratio_act->setChecked(!keep_aspect_ratio);

  redraw();
}

void GRPlotWidget::onlySquareAspectRatioSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }

  bool only_square_aspect_ratio = plot_elem->hasAttribute("only_square_aspect_ratio") &&
                                  static_cast<int>(plot_elem->getAttribute("only_square_aspect_ratio"));
  plot_elem->setAttribute("only_square_aspect_ratio", !only_square_aspect_ratio);
  only_square_aspect_ratio_act->setChecked(!only_square_aspect_ratio);

  redraw();
}

void GRPlotWidget::verticalOrientationSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto central_region = figure_elem->querySelectors("central_region");

  central_region->setAttribute("orientation", "vertical");
  vertical_orientation_act->setChecked(true);
  horizontal_orientation_act->setChecked(false);

  redraw();
}

void GRPlotWidget::horizontalOrientationSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto central_region = figure_elem->querySelectors("central_region");

  central_region->setAttribute("orientation", "horizontal");
  horizontal_orientation_act->setChecked(true);
  vertical_orientation_act->setChecked(false);

  redraw();
}

void GRPlotWidget::legendSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto legend_elem = figure_elem->querySelectors("legend"))
    {
      auto bbox_id = static_cast<int>(legend_elem->getAttribute("_bbox_id"));
      auto bbox_x_min = static_cast<double>(legend_elem->getAttribute("_bbox_x_min"));
      auto bbox_x_max = static_cast<double>(legend_elem->getAttribute("_bbox_x_max"));
      auto bbox_y_min = static_cast<double>(legend_elem->getAttribute("_bbox_y_min"));
      auto bbox_y_max = static_cast<double>(legend_elem->getAttribute("_bbox_y_max"));
      auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, legend_elem);

      editor_action->trigger(); // open the editor view to update the location
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = bbox;
      current_selection->getRef()->setAttribute("_highlighted", true);
      attributeEditEvent(true);
      called_by_location_change = true;
      editor_action->trigger(); // close the editor view again
      called_by_location_change = false;

      redraw();
    }
}

void GRPlotWidget::colorbarSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto colorbar = figure_elem->querySelectors("colorbar"))
    {
      auto bbox_id = static_cast<int>(colorbar->parentElement()->getAttribute("_bbox_id"));
      auto bbox_x_min = static_cast<double>(colorbar->parentElement()->getAttribute("_bbox_x_min"));
      auto bbox_x_max = static_cast<double>(colorbar->parentElement()->getAttribute("_bbox_x_max"));
      auto bbox_y_min = static_cast<double>(colorbar->parentElement()->getAttribute("_bbox_y_min"));
      auto bbox_y_max = static_cast<double>(colorbar->parentElement()->getAttribute("_bbox_y_max"));
      auto *bbox =
          new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, colorbar->parentElement());

      editor_action->trigger(); // open the editor view to update the location
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = bbox;
      current_selection->getRef()->setAttribute("_highlighted", true);
      attributeEditEvent(true);
      called_by_location_change = true;
      editor_action->trigger(); // close the editor view again
      called_by_location_change = false;

      redraw();
    }
}

void GRPlotWidget::leftAxisSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto side_region = figure_elem->querySelectors("side_region[location=\"left\"]"))
    {
      const auto side_plot_region = side_region->querySelectors("side_plot_region");

      for (const auto &axis : side_plot_region->children())
        {
          if (axis->localName() == "axis")
            {
              auto bbox_id = static_cast<int>(axis->getAttribute("_bbox_id"));
              auto bbox_x_min = static_cast<double>(axis->getAttribute("_bbox_x_min"));
              auto bbox_x_max = static_cast<double>(axis->getAttribute("_bbox_x_max"));
              auto bbox_y_min = static_cast<double>(axis->getAttribute("_bbox_y_min"));
              auto bbox_y_max = static_cast<double>(axis->getAttribute("_bbox_y_max"));
              auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, axis);

              editor_action->trigger(); // open the editor view to update the location
              if (current_selection != nullptr && current_selection->getRef() != nullptr)
                {
                  current_selection->getRef()->removeAttribute("_highlighted");
                  prev_highlighted_tick_group_elem.reset();
                }
              current_selection = bbox;
              current_selection->getRef()->setAttribute("_highlighted", true);
              attributeEditEvent();
              called_by_location_change = true;
              editor_action->trigger(); // close the editor view again
              called_by_location_change = false;
            }
        }

      redraw();
    }
}

void GRPlotWidget::rightAxisSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto side_region = figure_elem->querySelectors("side_region[location=\"right\"]"))
    {
      const auto side_plot_region = side_region->querySelectors("side_plot_region");

      for (const auto &axis : side_plot_region->children())
        {
          if (axis->localName() == "axis")
            {
              auto bbox_id = static_cast<int>(axis->getAttribute("_bbox_id"));
              auto bbox_x_min = static_cast<double>(axis->getAttribute("_bbox_x_min"));
              auto bbox_x_max = static_cast<double>(axis->getAttribute("_bbox_x_max"));
              auto bbox_y_min = static_cast<double>(axis->getAttribute("_bbox_y_min"));
              auto bbox_y_max = static_cast<double>(axis->getAttribute("_bbox_y_max"));
              auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, axis);

              editor_action->trigger(); // open the editor view to update the location
              if (current_selection != nullptr && current_selection->getRef() != nullptr)
                {
                  current_selection->getRef()->removeAttribute("_highlighted");
                  prev_highlighted_tick_group_elem.reset();
                }
              current_selection = bbox;
              current_selection->getRef()->setAttribute("_highlighted", true);
              attributeEditEvent();
              called_by_location_change = true;
              editor_action->trigger(); // close the editor view again
              called_by_location_change = false;
            }
        }

      redraw();
    }
}

void GRPlotWidget::bottomAxisSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto side_region = figure_elem->querySelectors("side_region[location=\"bottom\"]"))
    {
      const auto side_plot_region = side_region->querySelectors("side_plot_region");

      for (const auto &axis : side_plot_region->children())
        {
          if (axis->localName() == "axis")
            {
              auto bbox_id = static_cast<int>(axis->getAttribute("_bbox_id"));
              auto bbox_x_min = static_cast<double>(axis->getAttribute("_bbox_x_min"));
              auto bbox_x_max = static_cast<double>(axis->getAttribute("_bbox_x_max"));
              auto bbox_y_min = static_cast<double>(axis->getAttribute("_bbox_y_min"));
              auto bbox_y_max = static_cast<double>(axis->getAttribute("_bbox_y_max"));
              auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, axis);

              editor_action->trigger(); // open the editor view to update the location
              if (current_selection != nullptr && current_selection->getRef() != nullptr)
                {
                  current_selection->getRef()->removeAttribute("_highlighted");
                  prev_highlighted_tick_group_elem.reset();
                }
              current_selection = bbox;
              current_selection->getRef()->setAttribute("_highlighted", true);
              attributeEditEvent();
              called_by_location_change = true;
              editor_action->trigger(); // close the editor view again
              called_by_location_change = false;
            }
        }

      redraw();
    }
}

void GRPlotWidget::topAxisSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto side_region = figure_elem->querySelectors("side_region[location=\"top\"]"))
    {
      const auto side_plot_region = side_region->querySelectors("side_plot_region");

      for (const auto &axis : side_plot_region->children())
        {
          if (axis->localName() == "axis")
            {
              auto bbox_id = static_cast<int>(axis->getAttribute("_bbox_id"));
              auto bbox_x_min = static_cast<double>(axis->getAttribute("_bbox_x_min"));
              auto bbox_x_max = static_cast<double>(axis->getAttribute("_bbox_x_max"));
              auto bbox_y_min = static_cast<double>(axis->getAttribute("_bbox_y_min"));
              auto bbox_y_max = static_cast<double>(axis->getAttribute("_bbox_y_max"));
              auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, axis);

              editor_action->trigger(); // open the editor view to update the location
              if (current_selection != nullptr && current_selection->getRef() != nullptr)
                {
                  current_selection->getRef()->removeAttribute("_highlighted");
                  prev_highlighted_tick_group_elem.reset();
                }
              current_selection = bbox;
              current_selection->getRef()->setAttribute("_highlighted", true);
              attributeEditEvent();
              called_by_location_change = true;
              editor_action->trigger(); // close the editor view again
              called_by_location_change = false;
            }
        }

      redraw();
    }
}

void GRPlotWidget::twinXAxisSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto coordinate_system = figure_elem->querySelectors("coordinate_system"))
    {
      const auto axis = coordinate_system->querySelectors("axis[location=\"twin_x\"]");

      auto bbox_id = static_cast<int>(axis->getAttribute("_bbox_id"));
      auto bbox_x_min = static_cast<double>(axis->getAttribute("_bbox_x_min"));
      auto bbox_x_max = static_cast<double>(axis->getAttribute("_bbox_x_max"));
      auto bbox_y_min = static_cast<double>(axis->getAttribute("_bbox_y_min"));
      auto bbox_y_max = static_cast<double>(axis->getAttribute("_bbox_y_max"));
      auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, axis);

      editor_action->trigger(); // open the editor view to update the location
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = bbox;
      current_selection->getRef()->setAttribute("_highlighted", true);
      attributeEditEvent();
      called_by_location_change = true;
      editor_action->trigger(); // close the editor view again
      called_by_location_change = false;

      redraw();
    }
}

void GRPlotWidget::twinYAxisSlot()
{
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (const auto coordinate_system = figure_elem->querySelectors("coordinate_system"))
    {
      const auto axis = coordinate_system->querySelectors("axis[location=\"twin_y\"]");

      auto bbox_id = static_cast<int>(axis->getAttribute("_bbox_id"));
      auto bbox_x_min = static_cast<double>(axis->getAttribute("_bbox_x_min"));
      auto bbox_x_max = static_cast<double>(axis->getAttribute("_bbox_x_max"));
      auto bbox_y_min = static_cast<double>(axis->getAttribute("_bbox_y_min"));
      auto bbox_y_max = static_cast<double>(axis->getAttribute("_bbox_y_max"));
      auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, axis);

      editor_action->trigger(); // open the editor view to update the location
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = bbox;
      current_selection->getRef()->setAttribute("_highlighted", true);
      attributeEditEvent();
      called_by_location_change = true;
      editor_action->trigger(); // close the editor view again
      called_by_location_change = false;

      redraw();
    }
}

void GRPlotWidget::extractBoundingBoxesFromGRM(QPainter &painter)
{
  auto global_root = grm_get_document_root();

  if (enable_editor)
    {
      auto old_pen = painter.pen();
      painter.setPen(QPen(QColor(255, 0, 0, 100)));

      for (const auto &cur_child : global_root->querySelectorsAll("[_bbox_id]"))
        {
          auto id = static_cast<int>(cur_child->getAttribute("_bbox_id"));
          auto xmin = static_cast<double>(cur_child->getAttribute("_bbox_x_min"));
          auto xmax = static_cast<double>(cur_child->getAttribute("_bbox_x_max"));
          auto ymin = static_cast<double>(cur_child->getAttribute("_bbox_y_min"));
          auto ymax = static_cast<double>(cur_child->getAttribute("_bbox_y_max"));

          if (xmin == DBL_MAX || xmax == -DBL_MAX || ymin == DBL_MAX || ymax == -DBL_MAX)
            {
              if (util::isEnvVariableEnabled("GRM_DEBUG")) qDebug() << "skipping" << cur_child->localName().c_str();
            }
          else
            {
              auto b = BoundingObject(id, xmin, xmax, ymin, ymax, cur_child);
              bounding_logic->addBoundingObject(b);
            }
        }
      painter.setPen(old_pen);
    }
}

void GRPlotWidget::highlightCurrentSelection(QPainter &painter)
{
  if (enable_editor)
    {
      // only highlight current_selection when ctrl isn't pressed, else an element doesn't get visually removed from the
      // list cause it still gets highlighted
      if (!ctrl_key_mode)
        {
          if (current_selection != nullptr)
            {
              if (current_selection->getRef() != nullptr)
                {
                  int x_pos = 5, y_pos = 10;
                  if (current_selection->getRef()->localName() == "text") y_pos = 0;
                  if (enable_advanced_editor)
                    painter.drawText(current_selection->boundingRect().topLeft() + QPointF(x_pos, y_pos),
                                     current_selection->getRef()->localName().c_str());
                  current_selection->getRef()->setAttribute("_highlighted", true);
                  if (auto name = current_selection->getRef()->localName();
                      name == "tick_group" || name == "tick" || name == "grid_line" || name == "text")
                    {
                      prev_highlighted_tick_group_elem = current_selection->getRef();
                    }
                  if (prev_selection.lock() == nullptr || prev_selection.lock() != current_selection->getRef())
                    redraw();
                  prev_selection = current_selection->getRef();
                }
            }
          else if (!mouse_move_selections.empty())
            {
              const auto global_root = grm_get_document_root();
              const auto render = grm_get_render();
              for (const auto &id : mouse_move_selections)
                {
                  auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(id) + "\"]");
                  if (elem == nullptr) continue;
                  if ((elem->localName() == "side_region" || elem->localName() == "central_region" ||
                       elem->localName() == "figure" || elem->localName() == "plot" ||
                       elem->localName() == "layout_grid_element" || elem->localName() == "layout_grid" ||
                       elem->localName() == "overlay") &&
                      !elem->hasChildNodes())
                    continue;
                  bool contains_id = false;
                  for (const auto &mask : mask_highlights_cache)
                    {
                      if (mask.id == id)
                        {
                          contains_id = true;
                          mask_highlights.emplace_back(mask);
                          break;
                        }
                    }
                  if (!contains_id) render->renderMaskHighlight(elem, processPartialDrawing);
                }

              if (!mask_highlights.empty())
                {
                  for (const auto &mask : mask_highlights)
                    {
                      auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(mask.id) + "\"]");

                      // don't highlight figure and plot if they have the same mask (equals just 1 plot in the window)
                      if (elem->localName() == "figure" && global_root->querySelectors("layout_grid") == nullptr)
                        continue;
                      if (elem->localName() == "layout_grid") continue;
                      // maybe add special case for layout_grid_element and plot, which will require a more tricky check
                      painter.setOpacity(0.2);
                      auto rect =
                          QRect(mask.x / this->devicePixelRatioF(), mask.y / this->devicePixelRatioF(),
                                mask.width / this->devicePixelRatioF(), mask.height / this->devicePixelRatioF());
                      auto image = QImage(mask.image.data(), mask.width, mask.height, QImage::Format_ARGB32);
                      painter.drawImage(rect, image);
                      painter.setOpacity(1.0);

                      if (elem != nullptr && enable_advanced_editor)
                        {
                          int x_pos = 5, y_pos = 10;
                          if (elem->localName() == "text") y_pos = 0;
                          painter.drawText(rect.topLeft() + QPointF(x_pos, y_pos), elem->localName().c_str());
                        }
                    }
                }
              mask_highlights.clear();
            }
        }
      if (!tree_selections.empty())
        {
          const auto global_root = grm_get_document_root();
          const auto render = grm_get_render();
          for (const auto &id : tree_selections)
            {
              auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(id) + "\"]");
              if (elem == nullptr) continue;
              if ((elem->localName() == "side_region" || elem->localName() == "central_region" ||
                   elem->localName() == "figure" || elem->localName() == "plot" ||
                   elem->localName() == "layout_grid_element" || elem->localName() == "layout_grid" ||
                   elem->localName() == "overlay") &&
                  !elem->hasChildNodes())
                continue;
              bool contains_id = false;
              for (const auto &mask : mask_highlights_cache)
                {
                  if (mask.id == id)
                    {
                      contains_id = true;
                      mask_highlights.emplace_back(mask);
                      break;
                    }
                }
              if (!contains_id) render->renderMaskHighlight(elem, processPartialDrawing);
            }

          if (!mask_highlights.empty())
            {
              for (const auto &mask : mask_highlights)
                {
                  auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(mask.id) + "\"]");

                  // don't highlight figure and plot if they have the same mask (equals just 1 plot in the window)
                  if (elem->localName() == "figure" && global_root->querySelectors("layout_grid") == nullptr) continue;
                  if (elem->localName() == "layout_grid") continue;
                  // maybe add special case for layout_grid_element and plot, which will require a more tricky check
                  painter.setOpacity(0.2);
                  auto rect = QRect(mask.x / this->devicePixelRatioF(), mask.y / this->devicePixelRatioF(),
                                    mask.width / this->devicePixelRatioF(), mask.height / this->devicePixelRatioF());
                  auto image = QImage(mask.image.data(), mask.width, mask.height, QImage::Format_ARGB32);
                  painter.drawImage(rect, image);
                  painter.setOpacity(1.0);

                  if (elem != nullptr && enable_advanced_editor)
                    {
                      int x_pos = 5, y_pos = 10;
                      if (elem->localName() == "text") y_pos = 0;
                      painter.drawText(rect.topLeft() + QPointF(x_pos, y_pos), elem->localName().c_str());
                    }
                }
            }
          mask_highlights.clear();
        }
      if (selected_parent != nullptr)
        {
          auto render = grm_get_render();
          auto global_root = grm_get_document_root();
          if (selected_parent->getRef() != nullptr)
            {
              auto id = selected_parent->getId();
              bool contains_id = false;
              color = 0xFFFF0000;
              auto name = selected_parent->getRef()->localName();
              if ((name != "side_region" && name != "central_region" && name != "figure" || name != "plot" ||
                   name != "layout_grid_element" || name != "layout_grid" || name != "overlay") ||
                  selected_parent->getRef()->hasChildNodes())
                {
                  for (const auto &mask : mask_highlights_cache)
                    {
                      if (mask.id == id)
                        {
                          contains_id = true;
                          mask_highlights.emplace_back(mask);
                          break;
                        }
                    }
                  if (!contains_id) render->renderMaskHighlight(selected_parent->getRef(), processPartialDrawing);

                  for (const auto &mask : mask_highlights)
                    {
                      auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(mask.id) + "\"]");

                      painter.setOpacity(0.2);
                      auto rect =
                          QRect(mask.x / this->devicePixelRatioF(), mask.y / this->devicePixelRatioF(),
                                mask.width / this->devicePixelRatioF(), mask.height / this->devicePixelRatioF());
                      auto image = QImage(mask.image.data(), mask.width, mask.height, QImage::Format_ARGB32);
                      painter.drawImage(rect, image);
                      painter.setOpacity(1.0);

                      if (elem != nullptr && enable_advanced_editor)
                        {
                          int x_pos = 5, y_pos = 10;
                          if (elem->localName() == "text") y_pos = 0;
                          painter.drawText(rect.topLeft() + QPointF(x_pos, y_pos), elem->localName().c_str());
                        }
                    }
                  mask_highlights.clear();
                }
              color = 0xFF0202F0;
            }
        }
      if (!referenced_elements.empty())
        {
          auto global_root = grm_get_document_root();
          auto render = grm_get_render();
          color = 0xFFF3E03B;
          for (const auto &ref_elem : referenced_elements)
            {
              auto id = static_cast<int>(ref_elem.getRef()->getAttribute("_bbox_id"));
              auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(id) + "\"]");
              if (elem == nullptr || (elem->hasAttribute("_selected_for_move") &&
                                      static_cast<int>(elem->getAttribute("_selected_for_move")) == 0))
                continue;
              if ((elem->localName() == "side_region" || elem->localName() == "central_region" ||
                   elem->localName() == "figure" || elem->localName() == "plot" ||
                   elem->localName() == "layout_grid_element" || elem->localName() == "layout_grid" ||
                   elem->localName() == "overlay") &&
                  !elem->hasChildNodes())
                continue;
              bool contains_id = false;
              for (const auto &mask : referred_elements_highlights_cache)
                {
                  if (mask.id == id)
                    {
                      contains_id = true;
                      mask_highlights.emplace_back(mask);
                      break;
                    }
                }
              if (!contains_id) render->renderMaskHighlight(elem, processPartialDrawing);
            }
          if (!mask_highlights.empty())
            {
              for (const auto &mask : mask_highlights)
                {
                  auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(mask.id) + "\"]");

                  painter.setOpacity(0.4);
                  auto rect = QRect(mask.x / this->devicePixelRatioF(), mask.y / this->devicePixelRatioF(),
                                    mask.width / this->devicePixelRatioF(), mask.height / this->devicePixelRatioF());
                  auto image = QImage(mask.image.data(), mask.width, mask.height, QImage::Format_ARGB32);
                  painter.drawImage(rect, image);
                  painter.setOpacity(1.0);

                  if (elem != nullptr && enable_advanced_editor)
                    {
                      int x_pos = 5, y_pos = 10;
                      if (elem->localName() == "text") y_pos = 0;
                      painter.drawText(rect.topLeft() + QPointF(x_pos, y_pos), elem->localName().c_str());
                    }
                }
              mask_highlights.clear();
            }
          color = 0xFF0202F0;
        }
      // special case for hovering over elements of move_to_plot combobox to see which plot has that id
      if (plot_id != "")
        {
          auto global_root = grm_get_document_root();
          auto render = grm_get_render();
          auto plot = global_root->querySelectors("plot[plot_id=\"" + plot_id + "\"]");
          auto id = static_cast<int>(plot->getAttribute("_bbox_id"));

          color = 0xFFF3E03B;
          bool contains_id = false;
          for (const auto &mask : referred_elements_highlights_cache)
            {
              if (mask.id == id)
                {
                  contains_id = true;
                  mask_highlights.emplace_back(mask);
                  break;
                }
            }
          if (!contains_id) render->renderMaskHighlight(plot, processPartialDrawing);

          for (const auto &mask : mask_highlights)
            {
              auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(mask.id) + "\"]");

              painter.setOpacity(0.4);
              auto rect = QRect(mask.x / this->devicePixelRatioF(), mask.y / this->devicePixelRatioF(),
                                mask.width / this->devicePixelRatioF(), mask.height / this->devicePixelRatioF());
              auto image = QImage(mask.image.data(), mask.width, mask.height, QImage::Format_ARGB32);
              painter.drawImage(rect, image);
              painter.setOpacity(1.0);
            }
          mask_highlights.clear();
          color = 0xFF0202F0;
        }

      if (auto global_root = grm_get_document_root(); global_root->querySelectors("[_selected_for_move=\"1\"]"))
        {
          auto render = grm_get_render();
          for (const auto selection : global_root->querySelectorsAll("[_selected_for_move=\"1\"]"))
            {
              auto id = static_cast<int>(selection->getAttribute("_bbox_id"));
              auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(id) + "\"]");
              if (elem == nullptr) continue;
              if ((elem->localName() == "side_region" || elem->localName() == "central_region" ||
                   elem->localName() == "figure" || elem->localName() == "plot" ||
                   elem->localName() == "layout_grid_element" || elem->localName() == "layout_grid" ||
                   elem->localName() == "overlay") &&
                  !elem->hasChildNodes())
                continue;
              bool contains_id = false;
              // tooltip at mouse-pos with name? or name somewhere else? figure and plot are highlighted together,
              // how is that visible understandable
              for (const auto &mask : mask_highlights_cache)
                {
                  if (mask.id == id)
                    {
                      contains_id = true;
                      mask_highlights.emplace_back(mask);
                      break;
                    }
                }
              if (!contains_id) render->renderMaskHighlight(elem, processPartialDrawing);
            }
          if (!mask_highlights.empty())
            {
              for (const auto &mask : mask_highlights)
                {
                  auto elem = global_root->querySelectors("[_bbox_id=\"" + std::to_string(mask.id) + "\"]");

                  // don't highlight figure and plot if they have the same mask (equals just 1 plot in the window)
                  if (elem->localName() == "figure" && global_root->querySelectors("layout_grid") == nullptr) continue;
                  if (elem->localName() == "layout_grid") continue;
                  // maybe add special case for layout_grid_element and plot, which will require a more tricky check
                  painter.setOpacity(0.2);
                  auto rect = QRect(mask.x / this->devicePixelRatioF(), mask.y / this->devicePixelRatioF(),
                                    mask.width / this->devicePixelRatioF(), mask.height / this->devicePixelRatioF());
                  auto image = QImage(mask.image.data(), mask.width, mask.height, QImage::Format_ARGB32);
                  painter.drawImage(rect, image);
                  painter.setOpacity(1.0);

                  if (elem != nullptr && enable_advanced_editor)
                    {
                      int x_pos = 5, y_pos = 10;
                      if (elem->localName() == "text") y_pos = 0;
                      painter.drawText(rect.topLeft() + QPointF(x_pos, y_pos), elem->localName().c_str());
                    }
                  else if (elem != nullptr)
                    {
                      // atleast 1 element is selected to be moved or scaled -> show the viewport bbox so the user can
                      // actually see the borders amd know where to drag to scale the element
                      int width, height;
                      double mwidth, mheight;
                      GRM::getFigureSize(&width, &height, &mwidth, &mheight);
                      auto aspect_r = mwidth / mheight;

                      if (elem->hasAttribute("viewport_x_min") && elem->hasAttribute("viewport_x_max") &&
                          elem->hasAttribute("viewport_y_min") && elem->hasAttribute("viewport_y_max"))
                        {
                          auto vp_x_min = static_cast<double>(elem->getAttribute("viewport_x_min"));
                          auto vp_x_max = static_cast<double>(elem->getAttribute("viewport_x_max"));
                          auto vp_y_min = static_cast<double>(elem->getAttribute("viewport_y_min"));
                          auto vp_y_max = static_cast<double>(elem->getAttribute("viewport_y_max"));

                          vp_x_min *= width;
                          vp_x_max *= width;
                          vp_y_min *= height;
                          vp_y_max *= height;

                          if (aspect_r > 1)
                            {
                              vp_y_min *= aspect_r;
                              vp_y_max *= aspect_r;
                            }
                          else
                            {
                              vp_x_min /= aspect_r;
                              vp_x_max /= aspect_r;
                            }

                          auto old_pen = painter.pen();
                          painter.setPen(QPen(QColor(0, 0, 225, 100), 1.5, Qt::DashLine));

                          painter.drawRect(vp_x_min, std::max(0.0, height - vp_y_max), abs(vp_x_max - vp_x_min),
                                           abs(vp_y_max - vp_y_min));
                          painter.setPen(old_pen);
                        }
                    }
                }
              mask_highlights.clear();
            }
        }
    }
  else
    {
      const auto global_root = grm_get_document_root();
      if (auto plot_elem = global_root->querySelectors("[_selected_for_menu=1]"); plot_elem != nullptr)
        {
          int width, height;
          double mwidth, mheight;
          double vp_x_min, vp_x_max, vp_y_min, vp_y_max;
          GRM::getFigureSize(&width, &height, &mwidth, &mheight);
          auto aspect_r = mwidth / mheight;

          if (global_root->querySelectorsAll("layout_grid_element").size() > 1)
            {
              if (!GRM::Render::getViewport(plot_elem, &vp_x_min, &vp_x_max, &vp_y_min, &vp_y_max))
                throw NotFoundError(plot_elem->localName() + " doesn't have a viewport but it should.\n");
              vp_x_min *= width;
              vp_x_max *= width;
              vp_y_min *= height;
              vp_y_max *= height;

              if (aspect_r > 1)
                {
                  vp_y_min *= aspect_r;
                  vp_y_max *= aspect_r;
                }
              else
                {
                  vp_x_min /= aspect_r;
                  vp_x_max /= aspect_r;
                }

              painter.drawRect(vp_x_min, std::max(0.0, height - vp_y_max), abs(vp_x_max - vp_x_min),
                               abs(vp_y_max - vp_y_min));
            }
        }
    }
}

void GRPlotWidget::leaveEvent(QEvent *event)
{
  // Prevent highlighting if mouse leaves the widget without a mouseMoveEvent
  if (enable_editor)
    {
      mouse_move_selections.clear();
      update();
    }
}

void GRPlotWidget::resetPixmap()
{
  redraw_pixmap = RedrawType::FULL;
  current_selection = nullptr;
  prev_selection.reset();
  current_selections.clear();
  update();
}

void GRPlotWidget::loadFileSlot()
{
  if (!getenv("GRDISPLAY") || (getenv("GRDISPLAY") && strcmp(getenv("GRDISPLAY"), "view") != 0))
    {
#ifndef NO_XERCES_C
      std::string path =
          QFileDialog::getOpenFileName(this, "Open XML", QDir::currentPath(),
                                       "XML (*.xml*);;XML-PNG (*.xml.png);;XML-PDF (*.xml.pdf);;XML-SVG (*.xml.svg)")
              .toStdString();
      if (path.empty()) return;

      auto file = fopen(path.c_str(), "r");
      if (!file)
        {
          std::stringstream text_stream;
          text_stream << "Could not open the XML file \"" << path << "\".";
          QMessageBox::critical(this, "File open not possible", QString::fromStdString(text_stream.str()));
          return;
        }
      if (enable_editor) createHistoryElement();

      grm_load_graphics_tree(file);
      redraw();
      if (table_widget->isVisible()) table_widget->updateData(grm_get_render()->getContext());
#else
      std::stringstream text_stream;
      text_stream << "XML support not compiled in. Please recompile GRPlot with libxml2 support.";
      QMessageBox::critical(this, "File open not possible", QString::fromStdString(text_stream.str()));
      return;
#endif
    }
}

void GRPlotWidget::showContainerSlot()
{
  if (enable_editor)
    {
      if (show_container_action->isChecked())
        {
          tree_widget->show();
          tree_widget->updateData(grm_get_document_root());
          show_tree_widget_act->trigger();
          ignore_resize = 2;
        }
      else
        {
          tree_widget->hide();
          tree_widget->clearContractElements();
          hide_tree_widget_act->trigger();
          ignore_resize = 1;
        }
    }
}

void GRPlotWidget::enableEditorFunctions()
{
  if (editor_action->text() == "&Enable Editorview")
    {
      auto root = grm_get_document_root();
      auto render = grm_get_render();

      enable_editor = true;
      render->setEnableEditor(true);
      grm_tmp_dir = render->initializeHistory();
      if (enable_advanced_editor) add_element_action->setVisible(true);
      advanced_editor_act->setVisible(true);
      show_container_action->setVisible(true);
      show_container_action->setChecked(false);
      editor_action->setText(tr("&Disable Editorview"));

      // dirty reset of screen position
      grm_args_t *args = grm_args_new();
      QPoint pos = mapFromGlobal(QCursor::pos());
      grm_args_push(args, "key", "s", "r");
      grm_args_push(args, "x", "i", pos.x());
      grm_args_push(args, "y", "i", pos.y());
      grm_input(args);
      grm_args_delete(args);

      // to fix a strange behaviour on macos
      for (const auto &plot_elems : root->querySelectorsAll("plot[_active=\"1\"]"))
        {
          plot_elems->removeAttribute("_active");
        }
      for (const auto &plot_elems : root->querySelectorsAll("plot[_active_through_update=\"1\"]"))
        {
          plot_elems->removeAttribute("_active_through_update");
        }

      changeIconBarVisibility(false);
      redraw(true);
    }
  else
    {
      enable_editor = false;
      grm_get_render()->setEnableEditor(false);
      enable_advanced_editor = false;
      advanced_editor_act->setVisible(false);
      add_element_action->setVisible(false);
      show_container_action->setVisible(false);
      show_container_action->setChecked(false);
      tree_widget->hide();
      hide_tree_widget_act->trigger();
      if (!called_by_location_change) hide_edit_element_act->trigger();
      hide_preview_text_act->trigger();
      preview_text_widget->hide();
      if (!called_by_location_change) edit_element_widget->hide();
      add_element_widget->hide();
      editor_action->setText(tr("&Enable Editorview"));
      hide_selection_list_widget_act->trigger();
      ignore_resize = 1;
      selection_list_widget->hide();

      // if the editor gets turned off everything needs to be reseted
      if (!called_by_location_change)
        {
          for (const auto &elem : grm_get_document_root()->querySelectorsAll("[_highlighted=\"1\"]"))
            {
              elem->removeAttribute("_highlighted");
            }
        }
      prev_highlighted_tick_group_elem.reset();
      current_selection = nullptr;
      mouse_move_selections.clear();
      clicked.clear();

      for (const auto &selection : grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]"))
        {
          selection->setAttribute("_selected_for_move", 0);
        }
      prev_selection.reset();
      current_selections.clear();
      changeIconBarVisibility(true);
      redraw();
    }
}

void GRPlotWidget::advancedEditorSlot()
{
  if (advanced_editor_act->text() == "&Enable Advanced Editor")
    {
      advanced_editor_act->setText(tr("&Disable Advanced Editor"));
      enable_advanced_editor = true;
      add_element_action->setVisible(true);
    }
  else
    {
      enable_advanced_editor = false;
      add_element_action->setVisible(false);
    }
}

void GRPlotWidget::addElementSlot()
{
  if (enable_editor)
    {
      if (enable_advanced_editor)
        {
          if (add_element_action->isChecked())
            {
              add_element_widget->show();
              show_add_element_act->trigger();
              ignore_resize = 2;
            }
          else
            {
              add_element_widget->hide();
              hide_add_element_act->trigger();
              ignore_resize = 1;
            }
          add_element_widget->resize(400, height());
          add_element_widget->move(this->pos().x() + this->width() + add_element_widget->width(),
                                   this->pos().y() + add_element_widget->geometry().y() - 28);
        }
    }
}

void GRPlotWidget::received(ArgsWrapper args)
{
  if (!isVisible()) window()->show();
  if (args_) grm_args_delete(args_);
  grm_switch(1);
  args_ = args.getWrapper();
  grm_merge(args_);

  redraw();
}

void GRPlotWidget::closeEvent(QCloseEvent *event)
{
  event->ignore();
  hide();
  if (args_)
    {
      grm_args_delete(args_);
      args_ = nullptr;
    }
}

void GRPlotWidget::showEvent(QShowEvent *)
{
  QObject::connect(window()->windowHandle(), SIGNAL(screenChanged(QScreen *)), this, SLOT(screenChanged()));
}

QSize GRPlotWidget::sizeHint() const
{
  if (size_hint.isValid()) return size_hint;
  return QWidget::sizeHint();
}

void GRPlotWidget::screenChanged()
{
  gr_configurews();
  redraw();
}

void GRPlotWidget::showContextSlot()
{
  if (show_context_action->isChecked())
    {
      auto context = grm_get_render()->getContext();
      table_widget->updateData(context);
      table_widget->show();
      show_table_widget_act->trigger();
      ignore_resize = 2;
    }
  else
    {
      table_widget->hide();
      hide_table_widget_act->trigger();
      ignore_resize = 1;
    }
}

void GRPlotWidget::addContextSlot()
{
  std::string path =
      QFileDialog::getOpenFileName(this, "Open column data file", QDir::currentPath(), "(*.dat *.csv *.xyz)")
          .toStdString();
  if (path.empty()) return;

  // convert the data
  auto context = grm_get_render()->getContext();
  if (!grm_context_data_from_file(context, path))
    {
      fprintf(stderr, "Could not interpret the file to context data\n");
      return;
    }
  table_widget->updateData(context);
}

void GRPlotWidget::addGRPlotDataContextSlot()
{
  std::string path = QFileDialog::getOpenFileName(this, "Interpret matrix as 1 column data", QDir::currentPath(),
                                                  "(*.dat *.csv *.xyz)")
                         .toStdString();
  if (path.empty()) return;

  // convert the data
  auto context = grm_get_render()->getContext();
  if (!grm_context_data_from_file(context, path, true))
    {
      fprintf(stderr, "Could not interpret the file to context data\n");
      return;
    }
  table_widget->updateData(context);
}

void GRPlotWidget::generateLinearContextSlot()
{
  QList<QWidget *> fields;
  std::vector<std::string> label = {"Context-Data key:", "Min value:", "Max value", "Number of data"};
  QDialog dialog(this);
  dialog.setWindowTitle("Generate linear context entry");
  auto form = new QFormLayout;

  // needed information to generate linear data for the context
  for (int i = 0; i < 4; i++)
    {
      auto line_edit = new QLineEdit(&dialog);
      line_edit->setText("");
      auto text_label = QString(label[i].c_str());
      form->addRow(text_label, line_edit);
      fields << line_edit;
    }

  auto scroll_area_content = new QWidget;
  scroll_area_content->setLayout(form);
  auto scroll_area = new QScrollArea;
  scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  scroll_area->setWidgetResizable(true);
  scroll_area->setWidget(scroll_area_content);

  auto group_box_layout = new QVBoxLayout;
  group_box_layout->addWidget(scroll_area);
  QDialogButtonBox button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
  group_box_layout->addWidget(&button_box);
  QObject::connect(&button_box, SIGNAL(accepted()), &dialog, SLOT(accept()));
  QObject::connect(&button_box, SIGNAL(rejected()), &dialog, SLOT(reject()));
  dialog.setLayout(group_box_layout);

  if (dialog.exec() == QDialog::Accepted)
    {
      std::vector<std::string> values;
      std::vector<double> data_vec;
      std::shared_ptr<GRM::Context> context = grm_get_render()->getContext();

      for (int i = 0; i < 4; i++)
        {
          auto value = static_cast<QLineEdit *>(fields[i])->text().toStdString();
          if (value.empty())
            {
              fprintf(stderr, "All fields must be filled to generate linear context data\n");
              return;
            }
          values.push_back(value);
        }

      // convert entries into linear data vec
      try
        {
          auto start = std::stod(values[1]);
          auto end = std::stod(values[2]);
          auto n = std::stoi(values[3]);
          for (int i = 0; i < n; i++) data_vec.push_back(start + i * (end - start) / (n - 1));

          (*context)[values[0]] = data_vec;
          table_widget->updateData(context);
        }
      catch (std::invalid_argument &e)
        {
          fprintf(stderr, "Invalid argument for generate linear context parameter\n");
        }
    }
}

void GRPlotWidget::undoSlot()
{
  if (!getenv("GRDISPLAY") || (getenv("GRDISPLAY") && strcmp(getenv("GRDISPLAY"), "view") != 0))
    {
#ifndef NO_XERCES_C
      std::string path = std::string(grm_tmp_dir) + "_history" + std::to_string(--history_count);

      auto file = fopen(path.c_str(), "r");
      if (!file)
        {
          std::stringstream text_stream;
          text_stream << "Could not go back in history";
          QMessageBox::critical(this, "Going back in history not possible", QString::fromStdString(text_stream.str()));
          undo_action->setVisible(false);
          return;
        }
      createHistoryElement("_forward");
      grm_load_graphics_tree(file);
      redraw();
      if (table_widget->isVisible()) table_widget->updateData(grm_get_render()->getContext());

      if (edit_element_widget->isVisible())
        {
          edit_element_widget->hide();
          hide_edit_element_act->trigger();
          ignore_resize = 1;
        }
      current_selections.clear();
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = nullptr;
      if (add_element_widget->isVisible()) add_element_widget->hide();
      redo_action->setVisible(true);
      if (history_count == 0) undo_action->setVisible(false);
#else
      std::stringstream text_stream;
      text_stream << "XML support not compiled in. Please recompile GRPlot with libxml2 support.";
      QMessageBox::critical(this, "File open not possible", QString::fromStdString(text_stream.str()));

      if (edit_element_widget->isVisible())
        {
          edit_element_widget->hide();
          hide_edit_element_act->trigger();
          ignore_resize = 1;
        }
      current_selections.clear();
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = nullptr;
      if (add_element_widget->isVisible()) add_element_widget->hide();
      redo_action->setVisible(true);
      if (history_count == 0) undo_action->setVisible(false);
      return;
#endif
    }
}

void GRPlotWidget::redoSlot()
{
  if (!getenv("GRDISPLAY") || (getenv("GRDISPLAY") && strcmp(getenv("GRDISPLAY"), "view") != 0))
    {
#ifndef NO_XERCES_C
      std::string path = std::string(grm_tmp_dir) + "_forward_history" + std::to_string(--history_forward_count);

      auto file = fopen(path.c_str(), "r");
      if (!file)
        {
          std::stringstream text_stream;
          text_stream << "Could not go forward in history";
          QMessageBox::critical(this, "Going forward in history not possible",
                                QString::fromStdString(text_stream.str()));
          redo_action->setVisible(false);
          return;
        }
      grm_load_graphics_tree(file);
      redraw();
      if (table_widget->isVisible()) table_widget->updateData(grm_get_render()->getContext());

      if (edit_element_widget->isVisible())
        {
          edit_element_widget->hide();
          hide_edit_element_act->trigger();
          ignore_resize = 1;
        }
      current_selections.clear();
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = nullptr;
      if (add_element_widget->isVisible()) add_element_widget->hide();
      if (history_forward_count == 0) redo_action->setVisible(false);
      undo_action->setVisible(true);
      history_count += 1;
#else
      std::stringstream text_stream;
      text_stream << "XML support not compiled in. Please recompile GRPlot with libxml2 support.";
      QMessageBox::critical(this, "File open not possible", QString::fromStdString(text_stream.str()));

      if (edit_element_widget->isVisible())
        {
          edit_element_widget->hide();
          hide_edit_element_act->trigger();
          ignore_resize = 1;
        }
      current_selections.clear();
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = nullptr;
      if (add_element_widget->isVisible()) add_element_widget->hide();
      if (history_forward_count == 0) redo_action->setVisible(false);
      undo_action->setVisible(true);
      history_count += 1;
      return;
#endif
    }
}

void GRPlotWidget::addTextSlot()
{
  const auto render = grm_get_render();
  const auto creator = grm_get_creator();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  auto overlay = figure_elem->querySelectors("overlay");

  if (overlay == nullptr)
    {
      overlay = creator->createOverlay();
      figure_elem->appendChild(overlay);
    }

  int width = this->size().width(), height = this->size().height();
  GRM::getFigureSize(&width, &height, nullptr, nullptr);
  auto max_width_height = std::max(width, height);
  auto ndc_x = static_cast<double>(add_pos_x) / max_width_height;
  auto ndc_y = static_cast<double>(height - add_pos_y) / max_width_height;
  bool auto_update;
  render->getAutoUpdate(&auto_update);
  render->setAutoUpdate(false);
  auto overlay_element = creator->createOverlayElement(ndc_x, ndc_y, "text");
  overlay_element->setAttribute("z_index", 10);
  overlay->appendChild(overlay_element);
  render->setAutoUpdate(auto_update);

  overlay_element_edit = true;
  redraw();
}

void GRPlotWidget::addImageSlot()
{
  auto render = grm_get_render();
  auto creator = grm_get_creator();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  auto overlay = figure_elem->querySelectors("overlay");
  auto context = render->getContext();

  if (overlay == nullptr)
    {
      overlay = creator->createOverlay();
      figure_elem->appendChild(overlay);
    }

  QString filename =
      QFileDialog::getOpenFileName(this, tr("Load Image"), QDir::currentPath(), tr("Images (*.png *.jpg)"));
  if (filename.isEmpty()) return;
  QPixmap p(filename);
  auto image = p.toImage();
  std::vector<int> color_array;

  for (int j = 0; j < image.height(); j++)
    {
      for (int i = 0; i < image.width(); i++)
        {
          auto c = image.pixel(i, j);
          color_array.push_back((qRed(c) & 0xff) | (qGreen(c) & 0xff) << 8 | (qBlue(c) & 0xff) << 16 |
                                (qAlpha(c) & 0xff) << 24);
        }
    }
  auto data_key = "data" + std::to_string(static_cast<int>(global_root->getAttribute("_id")));
  (*context)[data_key] = color_array;

  int width = this->size().width(), height = this->size().height();
  GRM::getFigureSize(&width, &height, nullptr, nullptr);
  auto max_width_height = std::max(width, height);
  auto ndc_x = static_cast<double>(add_pos_x) / max_width_height;
  auto ndc_y = static_cast<double>(height - add_pos_y) / max_width_height;
  auto max_image_width_height = std::max(image.width(), image.height());
  bool auto_update;
  render->getAutoUpdate(&auto_update);
  render->setAutoUpdate(false);
  auto overlay_element = creator->createOverlayElement(ndc_x, ndc_y, "image");

  overlay_element->setAttribute("data", data_key);
  overlay_element->setAttribute("_width", image.width());
  overlay_element->setAttribute("_height", image.height());
  overlay_element->setAttribute("width_abs", 0.2 * image.width() / max_image_width_height);
  overlay_element->setAttribute("height_abs", 0.2 * image.height() / max_image_width_height);
  overlay_element->setAttribute("z_index", 10);
  overlay->appendChild(overlay_element);
  render->setAutoUpdate(auto_update);

  overlay_element_edit = true;
  redraw();
  this->activateWindow(); // needed so that the grplot widget is the active one again and events are tracked
}

void GRPlotWidget::consecutiveColorbarsSlot()
{
  auto render = grm_get_render();
  const auto global_root = grm_get_document_root();
  const auto figure_elem = global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  bool consecutive_colorbars = false;

  if (figure_elem->hasAttribute("consecutive_colorbars"))
    consecutive_colorbars = static_cast<int>(figure_elem->getAttribute("consecutive_colorbars"));
  figure_elem->setAttribute("consecutive_colorbars", !consecutive_colorbars);
  consecutive_colorbars_act->setChecked(!consecutive_colorbars);
  redraw();
}

void GRPlotWidget::flipLayoutSlot()
{
  auto render = grm_get_render();
  const auto global_root = grm_get_document_root();
  const auto figure_elem = global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  if (auto layout_grid = figure_elem->querySelectors("layout_grid"); layout_grid != nullptr)
    layout_grid->setAttribute("flip_col_and_row", true);
  redraw();
}

void GRPlotWidget::multipleRadioButtonGroupsListener()
{
  auto checked_rb = static_cast<QRadioButton *>(sender());
  if (last_checked_radio_button != nullptr)
    {
      last_checked_radio_button->setAutoExclusive(false);
      last_checked_radio_button->setChecked(false);
      last_checked_radio_button->setAutoExclusive(true);
    }
  last_checked_radio_button = checked_rb;
}

void GRPlotWidget::colorIndexSlot()
{
  auto render = grm_get_render();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  const auto plot_elems = figure_elem->querySelectorsAll("plot");
  std::shared_ptr<GRM::Element> plot_elem;

  if (plot_elems.size() > 1)
    {
      plot_elem = figure_elem->querySelectors("plot[_selected_for_menu=\"1\"]");
    }
  else
    {
      plot_elem = plot_elems[0];
    }

  int index = 1;
  if (plot_elem->hasAttribute("text_color_ind")) index = static_cast<int>(plot_elem->getAttribute("text_color_ind"));
  colorIndexPopUp("text_color_ind", index, plot_elem);
}

void GRPlotWidget::textScaleSlot()
{
  auto render = grm_get_render();
  const auto global_root = grm_get_document_root();
  const auto figure_elem = global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;

  QList<QLineEdit *> fields;
  QDialog dialog(this);
  dialog.setWindowTitle("Set Text Scale:");
  auto form = new QFormLayout;

  auto text_scale_label = new QLabel(QString("Text Scale:"));
  auto line_edit = new QLineEdit();
  if (figure_elem->hasAttribute("text_scale"))
    line_edit->setText(static_cast<std::string>(figure_elem->getAttribute("text_scale")).c_str());
  else
    line_edit->setText("1.0");
  form->addRow(text_scale_label, line_edit);
  fields << line_edit;

  QDialogButtonBox button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
  form->addRow(&button_box);
  QObject::connect(&button_box, SIGNAL(accepted()), &dialog, SLOT(accept()));
  QObject::connect(&button_box, SIGNAL(rejected()), &dialog, SLOT(reject()));

  dialog.setLayout(form);

  if (dialog.exec() == QDialog::Accepted)
    {
      std::shared_ptr<GRM::Context> context = grm_get_render()->getContext();

      if (enable_editor) createHistoryElement();

      if (fields[0]->isModified()) figure_elem->setAttribute("text_scale", std::stod(fields[0]->text().toStdString()));
    }

  redraw();
}

void GRPlotWidget::sizeCallback(const grm_event_t *new_size_object)
{
  // TODO: Get Plot ID
  if (auto event_size = QSize(new_size_object->size_event.width, new_size_object->size_event.height);
      this->size() != event_size)
    {
      size_hint = event_size;
      setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
      window()->adjustSize();
    }
}

void GRPlotWidget::cmdCallback(const grm_request_event_t *event)
{
  if (strcmp(event->request_string, "close") == 0) QApplication::quit();
}

Qt::KeyboardModifiers GRPlotWidget::queryKeyboardModifiers()
{
  return modifiers | QApplication::queryKeyboardModifiers();
}

void GRPlotWidget::processTestCommandsFile()
{
  if (!draw_called_at_least_once)
    {
      QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile);
      return;
    }
  while (test_commands_stream && !test_commands_stream->atEnd())
    {
      QString line = test_commands_stream->readLine();
      if (line.startsWith("#")) continue;
      if (QStringList words = line.split(","); !words.empty())
        {
          if (words[0] == "keyPressEvent" && words.size() == 2 && words[1].size() == 1 && words[1][0] >= 'A' &&
              words[1][0] <= 'Z')
            {
              QKeyEvent event(QEvent::KeyPress, words[1][0].unicode(), modifiers);
              keyPressEvent(&event);

              QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile);
              return;
            }
          if (words[0] == "setArg" && words.size() >= 4)
            {
              int n = words.size();
              std::string element_name;
              for (int k = 1; k < n - 2; k++)
                {
                  element_name += words[k].toUtf8().constData();
                  if (k < n - 3) element_name += " ";
                }
              auto global_root = grm_get_document_root();
              auto series_elements = global_root->querySelectorsAll(element_name);
              for (const auto &elem : series_elements)
                {
                  elem->setAttribute(words[n - 2].toUtf8().constData(), words[n - 1].toUtf8().constData());
                }
              auto val_utf8 = words[n - 1].toUtf8();
              auto value = val_utf8.constData();
              if (strcmp(value, "hist") == 0) value = "histogram";
              if (strcmp(value, "plot3") == 0) value = "line3";

              if (strcmp(value, "line") == 0)
                {
                  // to get the same lines then before all lines have to exist during render call so that the line_spec
                  // work properly
                  bool update;
                  grm_get_render()->getAutoUpdate(&update);
                  grm_get_render()->setAutoUpdate(false);
                  for (const auto &elem : global_root->querySelectorsAll("series_line"))
                    {
                      elem->setAttribute("_update_required", true);
                      elem->removeAttribute("line_color_ind");
                    }
                  grm_get_render()->setAutoUpdate(update);
                }
              if (strcmp(value, "barplot") == 0 || strcmp(value, "stairs") == 0)
                {
                  // to get the same barplots then before all lines have to exist during render call so that the
                  // line_spec work properly
                  bool update;
                  grm_get_render()->getAutoUpdate(&update);
                  grm_get_render()->setAutoUpdate(false);
                  for (const auto &elem : global_root->querySelectorsAll("series_" + std::string(value)))
                    {
                      if (strcmp(value, "barplot") == 0)
                        elem->removeAttribute("fill_color_ind");
                      else
                        elem->removeAttribute("line_color_ind");
                      elem->setAttribute("_update_required", true);
                    }
                  grm_get_render()->setAutoUpdate(update);
                }

              redraw();
            }
          else if (words[0] == "mouseMoveEvent" && words.size() == 3)
            {
              bool x_flag;
              bool y_flag;
              int x = words[1].toInt(&x_flag);
              int y = words[2].toInt(&y_flag);
              if (x_flag && y_flag)
                {
                  QPoint local_position(x, y);
                  QPoint global_position = mapToGlobal(local_position);
                  QMouseEvent event(QEvent::MouseMove, local_position, global_position, Qt::NoButton, Qt::NoButton,
                                    modifiers);
                  QCursor::setPos(global_position.x(), global_position.y());
                  mouseMoveEvent(&event);

                  QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile);
                  return;
                }
              std::cerr << "Failed to parse mouseMoveEvent: " << line.toStdString() << std::endl;
              break;
            }
          else if (words[0] == "mousePressEvent" && words.size() == 2)
            {
              Qt::MouseButton button;
              if (words[1] == "left")
                {
                  button = Qt::MouseButton::LeftButton;
                }
              else if (words[1] == "right")
                {
                  button = Qt::MouseButton::RightButton;
                }
              else
                {
                  std::cerr << "Failed to parse mousePressEvent: " << line.toStdString() << std::endl;
                  break;
                }
              QPoint global_position = QCursor::pos();
              QPoint local_position = mapFromGlobal(global_position);
              QMouseEvent event(QEvent::MouseButtonPress, local_position, global_position, button, Qt::NoButton,
                                modifiers);
              mousePressEvent(&event);
            }
          else if (words[0] == "mouseReleaseEvent" && words.size() == 2)
            {
              Qt::MouseButton button;
              if (words[1] == "left")
                {
                  button = Qt::MouseButton::LeftButton;
                }
              else if (words[1] == "right")
                {
                  button = Qt::MouseButton::RightButton;
                }
              else
                {
                  std::cerr << "Failed to parse mousePressEvent: " << line.toStdString() << std::endl;
                  break;
                }
              QPoint global_position = QCursor::pos();
              QPoint local_position = mapFromGlobal(global_position);
              QMouseEvent event(QEvent::MouseButtonRelease, local_position, global_position, button, Qt::NoButton,
                                modifiers);
              mouseReleaseEvent(&event);
            }
          else if (words[0] == "wheelEvent" && words.size() == 2)
            {
              bool delta_flag;
              int delta = words[1].toInt(&delta_flag);
              if (delta_flag)
                {
                  QPoint global_position = QCursor::pos();
                  QPoint local_position = mapFromGlobal(global_position);
#if QT_VERSION >= 0x060000
                  QWheelEvent event(local_position, global_position, QPoint{}, QPoint{0, delta}, Qt::NoButton,
                                    modifiers, Qt::NoScrollPhase, false, Qt::MouseEventSynthesizedByApplication);
#else
                  QWheelEvent event(local_position, global_position, QPoint{}, QPoint{0, delta}, delta, Qt::Vertical,
                                    Qt::NoButton, modifiers, Qt::NoScrollPhase, Qt::MouseEventSynthesizedByApplication,
                                    false);
#endif
                  wheelEvent(&event);

                  QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile);
                  return;
                }
              std::cerr << "Failed to parse wheelEvent: " << line.toStdString() << std::endl;
              break;
            }
          else if (words[0] == "modifiers")
            {
              bool parsing_failed = false;
              modifiers = Qt::NoModifier;
              for (int i = 1; i < words.size(); i++)
                {
                  if (words[i] == "shift")
                    {
                      modifiers |= Qt::ShiftModifier;
                    }
                  else if (words[i] == "alt")
                    {
                      modifiers |= Qt::AltModifier;
                    }
                  else if (words[i] == "control")
                    {
                      modifiers |= Qt::ControlModifier;
                    }
                  else
                    {
                      parsing_failed = true;
                    }
                }
              if (parsing_failed)
                {
                  std::cerr << "Failed to parse modifiers: " << line.toStdString() << std::endl;
                  break;
                }
            }
          else if (words[0] == "sleep" && words.size() == 2)
            {
              bool ms_flag;
              int ms = words[1].toInt(&ms_flag);
              if (ms_flag)
                {
                  QTimer::singleShot(ms, this, &GRPlotWidget::processTestCommandsFile);
                  return;
                }
              std::cerr << "Failed to parse sleep: " << line.toStdString() << std::endl;
              break;
            }
          else if (words[0] == "resize" && words.size() == 3)
            {
              bool width_flag, height_flag;
              int width = words[1].toInt(&width_flag);
              int height = words[2].toInt(&height_flag);
              if (width_flag && height_flag)
                {
                  this->resize(width, height);
                  this->setMinimumSize(width, height);
                  window()->adjustSize();
                  this->setMinimumSize(0, 0);
                  tooltips.clear();

                  QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile);
                  return;
                }
              std::cerr << "Failed to parse resize: " << line.toStdString() << std::endl;
              break;
            }
          else if (words[0] == "widgetToPNG" && words.size() == 2)
            {
              QPixmap pixmap(size());
              pixmap.fill(Qt::transparent);
              paint(&pixmap);
              QFile file(words[1]);
              if (file.open(QIODevice::WriteOnly))
                {
                  pixmap.save(&file, "PNG");
                }
              else
                {
                  std::cerr << "Failed to open: " << words[1].toStdString() << std::endl;
                  break;
                }
            }
          else if (words[0] == "openXML" && words.size() == 2)
            {
#ifndef NO_XERCES_C
              if (auto file = fopen(words[1].toStdString().c_str(), "r"))
                {
                  grm_load_graphics_tree(file);
                  tooltips.clear();
                  redraw();
                  QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile);
                  return;
                }
#else
              std::cerr << "Xerces-C++ support not compiled in. XML files cannot be openend." << std::endl;
#endif
            }
          else if (words[0] == "saveXML" && words.size() == 2)
            {
              if (std::ofstream save_file_stream(words[1].toStdString()); save_file_stream)
                {
                  auto graphics_tree_str =
                      std::unique_ptr<char, decltype(&std::free)>(grm_dump_graphics_tree_str(), std::free);
                  save_file_stream << graphics_tree_str.get() << std::endl;
                }
            }
          else
            {
              std::cerr << "Unknown test event: " << line.toStdString() << std::endl;
              break;
            }
        }
    }
  test_commands_file->close();
  QApplication::quit();
}

std::shared_ptr<GRM::Document> GRPlotWidget::getSchemaTree()
{
  return this->schema_tree;
}

void GRPlotWidget::setSelectedParent(BoundingObject *parent)
{
  this->selected_parent = parent;
}

BoundingObject *GRPlotWidget::getSelectedParent()
{
  return this->selected_parent;
}

void GRPlotWidget::setCurrentSelection(BoundingObject *p_current_selection)
{
  if (current_selection != nullptr && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("_highlighted");
  this->current_selection = p_current_selection;
  current_selection->getRef()->setAttribute("_highlighted", true);

  auto name = current_selection->getRef()->localName();
  if (name == "figure" || name == "layout_grid" || name == "layout_grid_element" || name == "draw_graphics" ||
      name == "overlay" || name == "overlay_element")
    return;

  const auto global_root = grm_get_document_root();
  const auto render = grm_get_render();
  if (name == "tick_group" || name == "tick" || name == "grid_line" || name == "text")
    {
      prev_highlighted_tick_group_elem = current_selection->getRef();
    }
  bool auto_update;
  render->getAutoUpdate(&auto_update);
  render->setAutoUpdate(false);
  for (const auto &selected : global_root->querySelectorsAll("[_selected_for_menu]"))
    {
      selected->removeAttribute("_selected_for_menu");
    }

  // mark the last clicked plot to adjust the plot type menu
  auto plot_parent = current_selection->getRef();
  while (plot_parent->localName() != "plot")
    {
      if (plot_parent->parentElement() == nullptr) break;
      plot_parent = plot_parent->parentElement();
    }
  plot_parent->setAttribute("_selected_for_menu", true);
  plot_parent->setAttribute("_active", true);
  render->setAutoUpdate(auto_update);
}

BoundingObject **GRPlotWidget::getCurrentSelection()
{
  return &(this->current_selection);
}

const QStringList &GRPlotWidget::getCheckBoxAttributes()
{
  return check_box_attr;
}

const QStringList &GRPlotWidget::getComboBoxAttributes()
{
  return combo_box_attr;
}

const QStringList &GRPlotWidget::getColorIndAttributes()
{
  return color_ind_attr;
}

const QStringList &GRPlotWidget::getColorRGBAttributes()
{
  return color_rgb_attr;
}

const QStringList &GRPlotWidget::getSliderAttributes()
{
  return slider_attr;
}

void GRPlotWidget::addCurrentSelection(std::unique_ptr<BoundingObject> curr_selection)
{
  current_selections.emplace_back(std::move(curr_selection));
}

std::list<std::unique_ptr<BoundingObject>>::iterator
GRPlotWidget::eraseCurrentSelection(std::list<std::unique_ptr<BoundingObject>>::const_iterator current_selection)
{
  return current_selections.erase(current_selection);
}

const std::list<std::unique_ptr<BoundingObject>> &GRPlotWidget::getCurrentSelections() const
{
  return current_selections;
}

void GRPlotWidget::setTreeUpdate(bool status)
{
  this->tree_update = status;
}

void GRPlotWidget::editElementAccepted(bool highlight_location)
{
  if (current_selection && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("_highlighted");
  prev_highlighted_tick_group_elem.reset();
  current_selection = nullptr;
  mouse_move_selections.clear();
  clicked.clear();
  mask_highlights_cache.clear();
  referred_elements_highlights_cache.clear();

  for (const auto &selection : current_selections)
    {
      selection->getRef()->setAttribute("_selected_for_move", 0);
    }
  prev_selection.reset();
  current_selections.clear();
  selection_list_widget->hide();
  hide_selection_list_widget_act->trigger();
  hide_preview_text_act->trigger();
  if (highlight_location) hide_edit_element_act->trigger();
  plot_id = "";

  auto root = grm_get_document_root();
  for (const auto &plot_elems : root->querySelectorsAll("plot[_active=\"1\"]"))
    {
      plot_elems->removeAttribute("_active");
    }
  for (const auto &plot_elems : root->querySelectorsAll("plot[_active_through_update=\"1\"]"))
    {
      plot_elems->removeAttribute("_active_through_update");
    }

  redraw(move_to_plot);
  if (move_to_plot)
    {
      move_to_plot = false;
      for (const auto &plot :
           grm_get_render()->getActiveFigure()->querySelectorsAll("plot[_active_through_update=\"1\"]"))
        {
          plot->removeAttribute("_active_through_update");
        }
    }

  // needed for some reason to fix strange behaviour after editElementAccepted where the prev element couldn't be
  // selected again
  redraw();
}

void GRPlotWidget::editElementRejected()
{
  if (current_selection && current_selection->getRef() != nullptr)
    current_selection->getRef()->removeAttribute("_highlighted");
  prev_highlighted_tick_group_elem.reset();
  current_selection = nullptr;
  mouse_move_selections.clear();
  clicked.clear();

  for (const auto &selection : current_selections)
    {
      selection->getRef()->setAttribute("_selected_for_move", 0);
    }
  prev_selection.reset();
  current_selections.clear();
  selection_list_widget->hide();
  hide_selection_list_widget_act->trigger();
  hide_preview_text_act->trigger();
  hide_edit_element_act->trigger();
  ignore_resize = 1;
  redraw();
}

void GRPlotWidget::setReferencedElements(const std::vector<BoundingObject> &referenced_elements)
{
  this->referenced_elements = referenced_elements;
}

void GRPlotWidget::adjustPlotTypeMenu(const std::shared_ptr<GRM::Element> &plot_parent)
{
  const auto global_root = grm_get_document_root();
  auto error = false;
  bool edit_enabled = !getenv("GRDISPLAY") || (getenv("GRDISPLAY") && strcmp(getenv("GRDISPLAY"), "view") != 0);

  if (plot_parent == nullptr) return;
  const auto central_region = plot_parent->querySelectors("central_region");
  if (central_region == nullptr) return;
  if (enable_editor) return;

  // hide all menu elements
  if (tree_widget != nullptr) // dummy elem which only exist in default grplot case
    {
      std::vector<std::string> valid_series_names = {
          "marginal_heatmap_plot",
          "series_barplot",
          "series_contour",
          "series_contourf",
          "series_heatmap",
          "series_hexbin",
          "series_histogram",
          "series_imshow",
          "series_isosurface",
          "series_line",
          "series_molecule",
          "series_nonuniform_heatmap",
          "series_nonuniform_polar_heatmap",
          "series_pie",
          "series_line3",
          "series_polar_heatmap",
          "series_polar_histogram",
          "series_polar_line",
          "series_polar_scatter",
          "series_quiver",
          "series_scatter",
          "series_scatter3",
          "series_shade",
          "series_stairs",
          "series_stem",
          "series_surface",
          "series_tricontour",
          "series_trisurface",
          "series_volume",
          "series_wireframe",
      };

      hidePlotTypeMenuElements();
      hide_marginal_sub_menu_act->trigger();
      hide_algo_menu_act->trigger();
      hide_multiplot_sub_menu_act->trigger();
      use_gr3_act->setVisible(false);
      polar_with_pan_act->setVisible(false);
      z_flip_act->setVisible(false);
      z_log_act->setVisible(false);
      r_log_act->setVisible(false);
      theta_flip_act->setVisible(false);
      legend_act->setVisible(false);
      colorbar_act->setVisible(false);
      left_axis_act->setVisible(false);
      right_axis_act->setVisible(false);
      bottom_axis_act->setVisible(false);
      top_axis_act->setVisible(false);
      twin_x_axis_act->setVisible(false);
      twin_y_axis_act->setVisible(false);
      z_lim_act->setVisible(false);
      x_lim_act->setVisible(true);
      y_lim_act->setVisible(true);
      x_log_act->setVisible(true);
      y_log_act->setVisible(true);
      x_flip_act->setVisible(true);
      y_flip_act->setVisible(true);
      disable_grid_act->setVisible(true);
      only_square_aspect_ratio_act->setVisible(true);
      if (edit_enabled)
        {
          hide_orientation_sub_menu_act->trigger();
          hide_aspect_ratio_sub_menu_act->trigger();
          show_log_sub_menu_act->trigger();
          show_flip_sub_menu_act->trigger();
          show_lim_sub_menu_act->trigger();
          show_plot_type_sub_menu_act->trigger();
          hide_location_sub_menu_act->trigger();
          if (global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid") != nullptr)
            show_multiplot_sub_menu_act->trigger();
        }
      if (colormap_act->isVisible() && plot_parent->hasAttribute("colormap"))
        {
          std::string colormap_string;
          if (plot_parent->getAttribute("colormap").isInt())
            colormap_string = GRM::colormapIntToString(static_cast<int>(plot_parent->getAttribute("colormap")));
          else
            colormap_string = static_cast<std::string>(plot_parent->getAttribute("colormap"));
          auto colormap = QPixmap((":/preview_images/colormaps/" + colormap_string + ".png").c_str());
          colormap_act->setIcon(colormap.scaled(20, 20));
        }

      if (grm_args_contains(args_, "error"))
        {
          error = true;
          fprintf(stderr, "Plot types are not compatible with error-bars. The menu got disabled\n");
        }

      auto coordinate_system = plot_parent->querySelectors("coordinate_system");
      if (coordinate_system != nullptr)
        {
          if (auto plot_type = static_cast<std::string>(coordinate_system->getAttribute("plot_type"));
              plot_type == "2d")
            show_aspect_ratio_sub_menu_act->trigger();
          else if (plot_type != "2d")
            disable_grid_act->setVisible(false);
        }

      if (coordinate_system && coordinate_system->querySelectors("axis[location=\"twin_x\"]"))
        {
          if (edit_enabled) show_location_sub_menu_act->trigger();
          twin_x_axis_act->setVisible(true);
        }
      if (coordinate_system && coordinate_system->querySelectors("axis[location=\"twin_y\"]"))
        {
          if (edit_enabled) show_location_sub_menu_act->trigger();
          twin_y_axis_act->setVisible(true);
        }
      if (plot_parent->querySelectors("legend") && coordinate_system)
        {
          // check for coordinate_system cause pie legends location cant be changed
          if (edit_enabled) show_location_sub_menu_act->trigger();
          legend_act->setVisible(true);
        }
      if (plot_parent->querySelectors("colorbar"))
        {
          if (edit_enabled) show_location_sub_menu_act->trigger();
          colorbar_act->setVisible(true);
          z_lim_act->setVisible(true); // to manipulate the color limits of the plot
        }
      if (plot_parent->querySelectors("side_plot_region"))
        {
          auto side_plot_region = plot_parent->querySelectors("side_plot_region");
          auto location = static_cast<std::string>(side_plot_region->getAttribute("location"));
          if (location.empty())
            location = static_cast<std::string>(side_plot_region->parentElement()->getAttribute("location"));

          if (!side_plot_region->querySelectors("colorbar") &&
              !side_plot_region->parentElement()->hasAttribute("marginal_heatmap_side_plot"))
            {
              if (location == "left")
                left_axis_act->setVisible(true);
              else if (location == "right")
                right_axis_act->setVisible(true);
              else if (location == "top")
                top_axis_act->setVisible(true);
              else if (location == "bottom")
                bottom_axis_act->setVisible(true);
              if (edit_enabled) show_location_sub_menu_act->trigger();
            }
        }

      // reset visible checkstate in multiplot case because it could be the state from the previous plot
      if (plot_parent->parentElement()->localName() != "figure")
        {
          if (plot_parent->hasAttribute("x_log"))
            x_log_act->setChecked(static_cast<int>(plot_parent->getAttribute("x_log")));
          if (plot_parent->hasAttribute("y_log"))
            y_log_act->setChecked(static_cast<int>(plot_parent->getAttribute("y_log")));
          if (plot_parent->hasAttribute("z_log"))
            z_log_act->setChecked(static_cast<int>(plot_parent->getAttribute("z_log")));
          if (plot_parent->hasAttribute("r_log"))
            r_log_act->setChecked(static_cast<int>(plot_parent->getAttribute("r_log")));
          if (plot_parent->hasAttribute("x_flip"))
            x_flip_act->setChecked(static_cast<int>(plot_parent->getAttribute("x_flip")));
          if (plot_parent->hasAttribute("y_flip"))
            y_flip_act->setChecked(static_cast<int>(plot_parent->getAttribute("y_flip")));
          if (plot_parent->hasAttribute("z_flip"))
            z_flip_act->setChecked(static_cast<int>(plot_parent->getAttribute("z_flip")));
          if (plot_parent->hasAttribute("theta_flip"))
            theta_flip_act->setChecked(static_cast<int>(plot_parent->getAttribute("theta_flip")));
          if (plot_parent->hasAttribute("keep_aspect_ratio"))
            keep_aspect_ratio_act->setChecked(static_cast<int>(plot_parent->getAttribute("keep_aspect_ratio")));
          if (plot_parent->hasAttribute("only_square_aspect_ratio"))
            only_square_aspect_ratio_act->setChecked(
                static_cast<int>(plot_parent->getAttribute("only_square_aspect_ratio")));

          if (central_region->hasAttribute("orientation"))
            {
              auto orientation = static_cast<std::string>(central_region->getAttribute("orientation"));
              if (orientation == "vertical")
                {
                  vertical_orientation_act->setChecked(true);
                  horizontal_orientation_act->setChecked(false);
                }
              else
                {
                  vertical_orientation_act->setChecked(false);
                  horizontal_orientation_act->setChecked(true);
                }
            }
          if (central_region->hasAttribute("keep_window"))
            keep_window_act->setChecked(static_cast<int>(central_region->getAttribute("keep_window")));

          auto marginal_heatmap_plot = plot_parent->querySelectors("marginal_heatmap_plot");
          if (marginal_heatmap_plot != nullptr)
            {
              auto algo = static_cast<std::string>(marginal_heatmap_plot->getAttribute("algorithm"));
              if (algo == "sum")
                {
                  sum_act->setChecked(true);
                  max_act->setChecked(false);
                }
              else
                {
                  sum_act->setChecked(false);
                  max_act->setChecked(true);
                }
            }
        }
      if (global_root->querySelectors("figure[active=1]")->hasAttribute("consecutive_colorbars"))
        consecutive_colorbars_act->setChecked(static_cast<int>(global_root->getAttribute("consecutive_colorbars")));

      if (global_root->querySelectorsAll("layout_grid_element").size() <= 1)
        flip_layout_act->setVisible(false);
      else
        flip_layout_act->setVisible(true);

      for (const auto &name : valid_series_names)
        {
          std::vector<std::shared_ptr<GRM::Element>> series_elements;
          if (name == "marginal_heatmap_plot")
            series_elements = plot_parent->querySelectorsAll(name);
          else
            series_elements = central_region->querySelectorsAll(name);
          for (const auto &series : series_elements)
            {
              int z, z_length;

              if (auto kind = static_cast<std::string>(series->getAttribute("kind"));
                  kind == "contour" || kind == "heatmap" || kind == "imshow" || kind == "marginal_heatmap" ||
                  kind == "surface" || kind == "wireframe" || kind == "contourf")
                {
                  if (kind == "marginal_heatmap") show_algo_menu_act->trigger();
                  if (kind == "surface") use_gr3_act->setVisible(true);
                  if (kind == "surface" || kind == "wireframe")
                    {
                      z_flip_act->setVisible(true);
                      z_log_act->setVisible(true);
                      z_lim_act->setVisible(true);
                    }
                  if (kind == "wireframe")
                    colormap_act->setVisible(false);
                  else
                    colormap_act->setVisible(true);

                  heatmap_act->setVisible(true);
                  surface_act->setVisible(true);
                  wireframe_act->setVisible(true);
                  contour_act->setVisible(true);
                  contourf_act->setVisible(true);
                  imshow_act->setVisible(true);
                  show_marginal_sub_menu_act->trigger();
                }
              else if (kind == "line" || (kind == "scatter" && !grm_args_values(args_, "z", "D", &z, &z_length)))
                {
                  line_act->setVisible(true);
                  scatter_act->setVisible(true);
                  show_orientation_sub_menu_act->trigger();
                }
              else if (kind == "volume" || kind == "isosurface")
                {
                  volume_act->setVisible(true);
                  isosurface_act->setVisible(true);
                  z_lim_act->setVisible(true);
                  z_flip_act->setVisible(true);
                  hide_log_sub_menu_act->trigger();
                }
              else if (kind == "line3" || kind == "trisurface" || kind == "tricontour" || kind == "scatter3" ||
                       kind == "scatter")
                {
                  if (kind != "tricontour")
                    {
                      z_flip_act->setVisible(true);
                      z_log_act->setVisible(true);
                      z_lim_act->setVisible(true);
                    }
                  line3_act->setVisible(true);
                  trisurf_act->setVisible(true);
                  tricont_act->setVisible(true);
                  scatter3_act->setVisible(true);
                  scatter_act->setVisible(true);
                }
              else if ((kind == "barplot" || kind == "stairs" || kind == "stem") && !error)
                {
                  barplot_act->setVisible(true);
                  stairs_act->setVisible(true);
                  stem_act->setVisible(true);
                  show_orientation_sub_menu_act->trigger();
                }
              else if (kind == "shade" || kind == "hexbin")
                {
                  shade_act->setVisible(true);
                  hexbin_act->setVisible(true);
                  if (kind == "hexbin")
                    hide_log_sub_menu_act->trigger();
                  else
                    show_log_sub_menu_act->trigger();
                }
              else if (kind == "polar_line" || kind == "polar_scatter")
                {
                  polar_line_act->setVisible(true);
                  polar_scatter_act->setVisible(true);
                  polar_with_pan_act->setVisible(true);
                  theta_flip_act->setVisible(true);
                  r_log_act->setVisible(true);
                  x_lim_act->setVisible(false);
                  y_lim_act->setVisible(false);
                  x_log_act->setVisible(false);
                  y_log_act->setVisible(false);
                  hide_lim_sub_menu_act->trigger();
                }
              else if (kind == "polar_heatmap" || kind == "polar_histogram")
                {
                  polar_with_pan_act->setVisible(true);
                  theta_flip_act->setVisible(true);
                  r_log_act->setVisible(true);
                  x_lim_act->setVisible(false);
                  y_lim_act->setVisible(false);
                  x_log_act->setVisible(false);
                  y_log_act->setVisible(false);
                  hide_lim_sub_menu_act->trigger();
                  hide_plot_type_sub_menu_act->trigger();
                }
              else if (kind == "histogram")
                {
                  show_orientation_sub_menu_act->trigger();
                }
              else if (kind == "pie")
                {
                  x_log_act->setVisible(false);
                  y_log_act->setVisible(false);
                  x_flip_act->setVisible(false);
                  y_flip_act->setVisible(false);
                  hide_log_sub_menu_act->trigger();
                  hide_flip_sub_menu_act->trigger();
                  hide_lim_sub_menu_act->trigger();
                  hide_plot_type_sub_menu_act->trigger();
                }
              else if (kind == "quiver")
                {
                  only_square_aspect_ratio_act->setVisible(false);
                  hide_plot_type_sub_menu_act->trigger();
                }
              add_seperator_act->trigger();
            }
        }
    }
}

void GRPlotWidget::hidePlotTypeMenuElements()
{
  heatmap_act->setVisible(false);
  surface_act->setVisible(false);
  wireframe_act->setVisible(false);
  contour_act->setVisible(false);
  contourf_act->setVisible(false);
  imshow_act->setVisible(false);
  line_act->setVisible(false);
  scatter_act->setVisible(false);
  volume_act->setVisible(false);
  isosurface_act->setVisible(false);
  line3_act->setVisible(false);
  trisurf_act->setVisible(false);
  tricont_act->setVisible(false);
  scatter3_act->setVisible(false);
  barplot_act->setVisible(false);
  stairs_act->setVisible(false);
  stem_act->setVisible(false);
  shade_act->setVisible(false);
  hexbin_act->setVisible(false);
  polar_line_act->setVisible(false);
  polar_scatter_act->setVisible(false);
}

QAction *GRPlotWidget::getPdfAct()
{
  return pdf_act;
}

QAction *GRPlotWidget::getPngAct()
{
  return png_act;
}

QAction *GRPlotWidget::getJpegAct()
{
  return jpeg_act;
}

QAction *GRPlotWidget::getSvgAct()
{
  return svg_act;
}

QAction *GRPlotWidget::getLine3Act()
{
  return line3_act;
}

QAction *GRPlotWidget::getTrisurfAct()
{
  return trisurf_act;
}

QAction *GRPlotWidget::getTricontAct()
{
  return tricont_act;
}

QAction *GRPlotWidget::getScatter3Act()
{
  return scatter3_act;
}

QAction *GRPlotWidget::getBarplotAct()
{
  return barplot_act;
}

QAction *GRPlotWidget::getStairsAct()
{
  return stairs_act;
}

QAction *GRPlotWidget::getStemAct()
{
  return stem_act;
}

QAction *GRPlotWidget::getShadeAct()
{
  return shade_act;
}

QAction *GRPlotWidget::getHexbinAct()
{
  return hexbin_act;
}

QAction *GRPlotWidget::getPolarLineAct()
{
  return polar_line_act;
}

QAction *GRPlotWidget::getPolarScatterAct()
{
  return polar_scatter_act;
}

QAction *GRPlotWidget::getLineAct()
{
  return line_act;
}

QAction *GRPlotWidget::getScatterAct()
{
  return scatter_act;
}

QAction *GRPlotWidget::getVolumeAct()
{
  return volume_act;
}

QAction *GRPlotWidget::getIsosurfaceAct()
{
  return isosurface_act;
}

QAction *GRPlotWidget::getHeatmapAct()
{
  return heatmap_act;
}

QAction *GRPlotWidget::getSurfaceAct()
{
  return surface_act;
}

QAction *GRPlotWidget::getWireframeAct()
{
  return wireframe_act;
}

QAction *GRPlotWidget::getContourAct()
{
  return contour_act;
}

QAction *GRPlotWidget::getImshowAct()
{
  return imshow_act;
}

QAction *GRPlotWidget::getContourfAct()
{
  return contourf_act;
}

QAction *GRPlotWidget::getSumAct()
{
  return sum_act;
}

QAction *GRPlotWidget::getMaxAct()
{
  return max_act;
}

QAction *GRPlotWidget::getMarginalHeatmapAllAct()
{
  return marginal_heatmap_all_act;
}

QAction *GRPlotWidget::getMarginalHeatmapLineAct()
{
  return marginal_heatmap_line_act;
}

QAction *GRPlotWidget::getMovableModeAct()
{
  return moveable_mode_act;
}

QAction *GRPlotWidget::getEditorAct()
{
  return editor_action;
}

QAction *GRPlotWidget::getLoadFileAct()
{
  return load_file_action;
}

QAction *GRPlotWidget::getShowContainerAct()
{
  return show_container_action;
}

QAction *GRPlotWidget::getAddElementAct()
{
  return add_element_action;
}

QAction *GRPlotWidget::getShowContextAct()
{
  return show_context_action;
}

QAction *GRPlotWidget::getAddContextAct()
{
  return add_context_action;
}

QAction *GRPlotWidget::getAddGRplotDataContextAct()
{
  return add_grplot_data_context;
}

QAction *GRPlotWidget::getGenerateLinearContextAct()
{
  return generate_linear_context_action;
}

QTextStream *GRPlotWidget::getTestCommandsStream()
{
  return test_commands_stream;
}

QAction *GRPlotWidget::getHideAlgoMenuAct()
{
  return hide_algo_menu_act;
}

QAction *GRPlotWidget::getShowAlgoMenuAct()
{
  return show_algo_menu_act;
}

QAction *GRPlotWidget::getHideMarginalSubMenuAct()
{
  return hide_marginal_sub_menu_act;
}

QAction *GRPlotWidget::getShowMarginalSubMenuAct()
{
  return show_marginal_sub_menu_act;
}

QAction *GRPlotWidget::getHideOrientationSubMenuAct()
{
  return hide_orientation_sub_menu_act;
}

QAction *GRPlotWidget::getShowOrientationSubMenuAct()
{
  return show_orientation_sub_menu_act;
}

QAction *GRPlotWidget::getHideAspectRatioSubMenuAct()
{
  return hide_aspect_ratio_sub_menu_act;
}

QAction *GRPlotWidget::getShowAspectRatioSubMenuAct()
{
  return show_aspect_ratio_sub_menu_act;
}

QAction *GRPlotWidget::getHideLocationSubMenuAct()
{
  return hide_location_sub_menu_act;
}

QAction *GRPlotWidget::getShowLocationSubMenuAct()
{
  return show_location_sub_menu_act;
}

QAction *GRPlotWidget::getHideLimSubMenuAct()
{
  return hide_lim_sub_menu_act;
}

QAction *GRPlotWidget::getShowLimSubMenuAct()
{
  return show_lim_sub_menu_act;
}

QAction *GRPlotWidget::getHideLogSubMenuAct()
{
  return hide_log_sub_menu_act;
}

QAction *GRPlotWidget::getShowLogSubMenuAct()
{
  return show_log_sub_menu_act;
}

QAction *GRPlotWidget::getHideFlipSubMenuAct()
{
  return hide_flip_sub_menu_act;
}

QAction *GRPlotWidget::getShowFlipSubMenuAct()
{
  return show_flip_sub_menu_act;
}

QAction *GRPlotWidget::getHidePlotTypeSubMenuAct()
{
  return hide_plot_type_sub_menu_act;
}

QAction *GRPlotWidget::getShowPlotTypeSubMenuAct()
{
  return show_plot_type_sub_menu_act;
}

QAction *GRPlotWidget::getHideMultiplotSubMenuAct()
{
  return hide_multiplot_sub_menu_act;
}

QAction *GRPlotWidget::getShowMultiplotSubMenuAct()
{
  return show_multiplot_sub_menu_act;
}

QAction *GRPlotWidget::getAddSeperatorAct()
{
  return add_seperator_act;
}

QAction *GRPlotWidget::getXLogAct()
{
  return x_log_act;
}

QAction *GRPlotWidget::getYLogAct()
{
  return y_log_act;
}

QAction *GRPlotWidget::getZLogAct()
{
  return z_log_act;
}

QAction *GRPlotWidget::getRLogAct()
{
  return r_log_act;
}

QAction *GRPlotWidget::getXFlipAct()
{
  return x_flip_act;
}

QAction *GRPlotWidget::getYFlipAct()
{
  return y_flip_act;
}

QAction *GRPlotWidget::getZFlipAct()
{
  return z_flip_act;
}

QAction *GRPlotWidget::getThetaFlipAct()
{
  return theta_flip_act;
}

QAction *GRPlotWidget::getUseGR3Act()
{
  return use_gr3_act;
}

QAction *GRPlotWidget::getPolarWithPanAct()
{
  return polar_with_pan_act;
}

QAction *GRPlotWidget::getKeepWindowAct()
{
  return keep_window_act;
}

QAction *GRPlotWidget::getKeepAspectRatioAct()
{
  return keep_aspect_ratio_act;
}

QAction *GRPlotWidget::getOnlySquareAspectRatioAct()
{
  return only_square_aspect_ratio_act;
}

QAction *GRPlotWidget::getVerticalOrientationAct()
{
  return vertical_orientation_act;
}

QAction *GRPlotWidget::getHorizontalOrientationAct()
{
  return horizontal_orientation_act;
}

QAction *GRPlotWidget::getLegendAct()
{
  return legend_act;
}

QAction *GRPlotWidget::getColorbarAct()
{
  return colorbar_act;
}
QAction *GRPlotWidget::getLeftAxisAct()
{
  return left_axis_act;
}

QAction *GRPlotWidget::getRightAxisAct()
{
  return right_axis_act;
}

QAction *GRPlotWidget::getBottomAxisAct()
{
  return bottom_axis_act;
}

QAction *GRPlotWidget::getTopAxisAct()
{
  return top_axis_act;
}

QAction *GRPlotWidget::getTwinXAxisAct()
{
  return twin_x_axis_act;
}

QAction *GRPlotWidget::getTwinYAxisAct()
{
  return twin_y_axis_act;
}

QAction *GRPlotWidget::getColormapAct()
{
  return colormap_act;
}

QAction *GRPlotWidget::getUndoAct()
{
  return undo_action;
}

QAction *GRPlotWidget::getRedoAct()
{
  return redo_action;
}

QAction *GRPlotWidget::getSelectableGridAct()
{
  return selectable_grid_act;
}

QAction *GRPlotWidget::getAdvancedEditorAct()
{
  return advanced_editor_act;
}

QAction *GRPlotWidget::getShowEditElementAct()
{
  return show_edit_element_act;
}

QAction *GRPlotWidget::getShowTreeWidgetAct()
{
  return show_tree_widget_act;
}

QAction *GRPlotWidget::getShowTableWidgetAct()
{
  return show_table_widget_act;
}

QAction *GRPlotWidget::getShowTextPreviewAct()
{
  return show_preview_text_act;
}

QAction *GRPlotWidget::getShowSelectionListWidgetAct()
{
  return show_selection_list_widget_act;
}

QAction *GRPlotWidget::getShowIconBarAct()
{
  return show_icon_bar_widget_act;
}

QAction *GRPlotWidget::getShowAddElementAct()
{
  return show_add_element_act;
}

QAction *GRPlotWidget::getHideEditElementAct()
{
  return hide_edit_element_act;
}

QAction *GRPlotWidget::getHideTreeWidgetAct()
{
  return hide_tree_widget_act;
}

QAction *GRPlotWidget::getHideTableWidgetAct()
{
  return hide_table_widget_act;
}

QAction *GRPlotWidget::getHideTextPreviewAct()
{
  return hide_preview_text_act;
}

QAction *GRPlotWidget::getHideSelectionListWidgetAct()
{
  return hide_selection_list_widget_act;
}

QAction *GRPlotWidget::getHideIconBarAct()
{
  return hide_icon_bar_widget_act;
}

QAction *GRPlotWidget::getHideAddElementAct()
{
  return hide_add_element_act;
}

QAction *GRPlotWidget::getXLimAct()
{
  return x_lim_act;
}

QAction *GRPlotWidget::getYLimAct()
{
  return y_lim_act;
}

QAction *GRPlotWidget::getZLimAct()
{
  return z_lim_act;
}

QAction *GRPlotWidget::getIconBarAct()
{
  return icon_bar_act;
}

QAction *GRPlotWidget::getTextColorIndAct()
{
  return text_color_ind_act;
}

QAction *GRPlotWidget::getDisableGridAct()
{
  return disable_grid_act;
}

QAction *GRPlotWidget::getUpdateEditElementTitleAct()
{
  return update_edit_element_title_act;
}

QAction *GRPlotWidget::getConsecutiveColorbarsAct()
{
  return consecutive_colorbars_act;
}

QAction *GRPlotWidget::getFlipLayoutAct()
{
  return flip_layout_act;
}

QAction *GRPlotWidget::getTextScaleAct()
{
  return text_scale_act;
}

QWidget *GRPlotWidget::getEditElementWidget()
{
  return edit_element_widget;
}

QWidget *GRPlotWidget::getTreeWidget()
{
  return tree_widget;
}

QWidget *GRPlotWidget::getTableWidget()
{
  return table_widget;
}

QWidget *GRPlotWidget::getTextPreviewWidget()
{
  return preview_text_widget;
}

QWidget *GRPlotWidget::getSelectionListWidget()
{
  return selection_list_widget;
}

QWidget *GRPlotWidget::getIconBarWidget()
{
  return icon_bar_widget;
}

QWidget *GRPlotWidget::getAddElementWidget()
{
  return add_element_widget;
}

void GRPlotWidget::cursorHandler(int x, int y)
{
  int cursor_state = grm_get_hover_mode(x, y, disable_movable_xform);
  if (!enable_editor)
    {
      if (cursor_state == DEFAULT_HOVER_MODE)
        {
          csr->setShape(Qt::ArrowCursor);
        }
      else if (cursor_state == MOVABLE_HOVER_MODE)
        {
          csr->setShape(Qt::OpenHandCursor);
        }
      else if (cursor_state == INTEGRAL_SIDE_HOVER_MODE)
        {
          csr->setShape(Qt::SizeHorCursor);
        }
      else if (cursor_state == LEGEND_ELEMENT_HOVER_MODE)
        {
          csr->setShape(Qt::PointingHandCursor);
        }
    }
  else
    {
      if (current_selection != nullptr && current_selection->getRef() != nullptr)
        {
          if (auto elem_name = current_selection->getRef()->localName();
              (enable_advanced_editor ||
               (elem_name == "figure" || elem_name == "plot" || elem_name == "layout_grid" ||
                elem_name == "layout_grid_element" || elem_name == "colorbar" || elem_name == "label" ||
                elem_name == "titles_3d" || elem_name == "text" || elem_name == "central_region" ||
                elem_name == "side_region" || elem_name == "marginal_heatmap_plot" || elem_name == "legend" ||
                elem_name == "text_region" || elem_name == "overlay_element")))
            {
              if (cursor_state == DEFAULT_HOVER_MODE)
                {
                  csr->setShape(Qt::ArrowCursor);
                }
              else if (cursor_state == VERTICAL_SCALE_HOVER_MODE)
                {
                  csr->setShape(Qt::SizeVerCursor);
                }
              else if (cursor_state == HORIZONTAL_SCALE_HOVER_MODE)
                {
                  csr->setShape(Qt::SizeHorCursor);
                }
              else if (cursor_state == MOVE_HOVER_MODE)
                {
                  csr->setShape(Qt::SizeAllCursor);
                }
              else if (cursor_state == B_DIAGONAL_SCALE_HOVER_MODE)
                {
                  csr->setShape(Qt::SizeBDiagCursor);
                }
              else if (cursor_state == F_DIAGONAL_SCALE_HOVER_MODE)
                {
                  csr->setShape(Qt::SizeFDiagCursor);
                }
            }
          else if (mouse_state.mode == MouseState::Mode::NORMAL)
            {
              if (cursor_state == DEFAULT_HOVER_MODE) csr->setShape(Qt::ArrowCursor);
            }
        }
      else if (cursor_state == DEFAULT_HOVER_MODE)
        {
          csr->setShape(Qt::ArrowCursor);
        }
    }
  setCursor(*csr);
}

bool GRPlotWidget::getEnableAdvancedEditor()
{
  return enable_advanced_editor;
}

void GRPlotWidget::highlightTableWidgetAt(std::string column_name)
{
  const auto context = grm_get_render()->getContext();
  table_widget->updateData(context);
  table_widget->show();
  show_table_widget_act->trigger();
  ignore_resize = 2;
  show_context_action->setChecked(true);
  for (const auto &item : table_widget->findItems(column_name.c_str(), Qt::MatchExactly))
    {
      table_widget->selectColumn(item->column());
    }
}

void GRPlotWidget::overlayElementEdit()
{
  auto render = grm_get_render();
  const auto global_root = grm_get_document_root();
  const auto layout_grid = global_root->querySelectors("figure[active=1]")->querySelectors("layout_grid");
  const auto figure_elem = (layout_grid != nullptr && layout_grid->querySelectorsAll("layout_grid_element").size() > 1)
                               ? layout_grid->querySelectors("[_selected_for_menu]")
                               : global_root->querySelectors("figure[active=1]");
  if (figure_elem == nullptr) return;
  auto overlay = figure_elem->querySelectors("overlay");
  auto tmp = overlay->querySelectorsAll("overlay_element");
  auto overlay_element = tmp[tmp.size() - 1];

  if (auto text = overlay_element->querySelectors("text"); text != nullptr)
    {
      auto bbox_id = static_cast<int>(text->getAttribute("_bbox_id"));
      auto bbox_x_min = static_cast<double>(text->getAttribute("_bbox_x_min"));
      auto bbox_x_max = static_cast<double>(text->getAttribute("_bbox_x_max"));
      auto bbox_y_min = static_cast<double>(text->getAttribute("_bbox_y_min"));
      auto bbox_y_max = static_cast<double>(text->getAttribute("_bbox_y_max"));
      auto *bbox = new BoundingObject(bbox_id, bbox_x_min, bbox_x_max, bbox_y_min, bbox_y_max, text);
      if (current_selection != nullptr)
        {
          current_selection->getRef()->removeAttribute("_highlighted");
          prev_highlighted_tick_group_elem.reset();
        }
      current_selection = bbox;
      current_selection->getRef()->setAttribute("_highlighted", true);
      attributeEditEvent(false);
    }
  overlay_element_edit = false;
}

void GRPlotWidget::setUpPreviewTextWidget(const std::string &text, int scientific_format, int text_color,
                                          int font_precision, int font, int width, int height)
{
  // 250 based on the fixed size of the right dock widget area, even if width would be the real size of the text
  // 15 so empty text fields have a decent preview if beeing edited
  preview_text_widget->initialize(text, scientific_format, text_color, font_precision, font, std::max(250, width),
                                  std::max(15, height));
  preview_text_widget->show();
  preview_text_widget->update();
  show_preview_text_act->trigger();
  ignore_resize = 2;
}

void GRPlotWidget::listItemCheckStatusChanged(QListWidgetItem *item)
{
  if (item->checkState() == Qt::Unchecked)
    {
      static_cast<CustomQListWidgetItem *>(item)->getElementRef()->removeAttribute("_selected_for_move");

      auto multiple_selections = grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]");
      selection_list_widget->updateSelectionList(multiple_selections);
      if (multiple_selections.empty())
        {
          if (!current_selections.empty()) current_selections.clear();
          for (const auto &selection : grm_get_document_root()->querySelectorsAll("[_selected_for_move=\"1\"]"))
            {
              selection->setAttribute("_selected_for_move", 0);
            }
          if (current_selection && current_selection->getRef() != nullptr)
            {
              current_selection->getRef()->removeAttribute("_highlighted");
              prev_highlighted_tick_group_elem.reset();
            }
          ctrl_key_mode = false;
          hide_selection_list_widget_act->trigger();
          current_selection = nullptr;
          mouse_move_selections.clear();
          prev_selection.reset();
          ignore_resize = 0;
          this->cursor().setShape(Qt::ArrowCursor);
        }
      redraw();
    }
}

void GRPlotWidget::listItemPressed(QListWidgetItem *item)
{
  auto selection = static_cast<CustomQListWidgetItem *>(item)->getElementRef();
  auto bbox_xmin = static_cast<double>(selection->getAttribute("_bbox_x_min"));
  auto bbox_xmax = static_cast<double>(selection->getAttribute("_bbox_x_max"));
  auto bbox_ymin = static_cast<double>(selection->getAttribute("_bbox_y_min"));
  auto bbox_ymax = static_cast<double>(selection->getAttribute("_bbox_y_max"));
  auto bbox_id = static_cast<int>(selection->getAttribute("_bbox_id"));
  setCurrentSelection(new BoundingObject(bbox_id, bbox_xmin, bbox_xmax, bbox_ymin, bbox_ymax, selection));

  redraw();
}

void GRPlotWidget::showIconBarSlot()
{
  if (icon_bar_act->isChecked())
    {
      icon_bar_widget->setVisible(true);
      show_icon_bar_widget_act->trigger();
    }
  else
    {
      icon_bar_widget->setVisible(false);
      hide_icon_bar_widget_act->trigger();
    }
}

void GRPlotWidget::addTreeSelection(int id)
{
  tree_selections.clear();
  this->tree_selections.emplace(id);
}

void GRPlotWidget::setMoveToPlot(bool status)
{
  move_to_plot = status;
}

void GRPlotWidget::changeIconBarVisibility(bool visibility)
{
  if (visibility)
    {
      show_plot_type_sub_menu_act->trigger();
      show_marginal_sub_menu_act->trigger();
      show_log_sub_menu_act->trigger();
      show_flip_sub_menu_act->trigger();
      show_lim_sub_menu_act->trigger();
      show_orientation_sub_menu_act->trigger();
      show_aspect_ratio_sub_menu_act->trigger();
      show_location_sub_menu_act->trigger();
    }
  else
    {
      hide_plot_type_sub_menu_act->trigger();
      hide_marginal_sub_menu_act->trigger();
      hide_log_sub_menu_act->trigger();
      hide_flip_sub_menu_act->trigger();
      hide_lim_sub_menu_act->trigger();
      hide_orientation_sub_menu_act->trigger();
      hide_aspect_ratio_sub_menu_act->trigger();
      hide_location_sub_menu_act->trigger();
    }
  use_gr3_act->setVisible(visibility);
  polar_with_pan_act->setVisible(visibility);
  colormap_act->setVisible(visibility);
  text_color_ind_act->setVisible(visibility);
  disable_grid_act->setVisible(visibility);
}
