Ejemplo n.º 1
0
int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
{
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
  if(g->dragging > 0)
  {
    // dt_iop_graduatednd_params_t *p   = (dt_iop_graduatednd_params_t *)self->params;
    // float wd = self->dev->preview_pipe->backbuf_width;
    // float ht = self->dev->preview_pipe->backbuf_height;
    float pzx, pzy;
    dt_dev_get_pointer_zoom_pos(self->dev, x, y, &pzx, &pzy);
    pzx += 0.5f;
    pzy += 0.5f;

    float r = 0.0, o = 0.0;
    // float pts[4];
    // dt_dev_distort_backtransform(self->dev,pts,2);
    set_grad_from_points(self, g->xa, g->ya, g->xb, g->yb, &r, &o);

    // if this is a "line dragging, we reset extremities, to be sure they are not outside the image
    if(g->dragging == 3)
    {
      /*
       * whole line dragging should not change rotation, so we should reuse
       * old rotation to avoid rounding issues
       */
      r = p->rotation;
      set_points_from_grad(self, &g->xa, &g->ya, &g->xb, &g->yb, r, o);
    }
    self->dt->gui->reset = 1;
    dt_bauhaus_slider_set(g->scale3, r);
    // dt_bauhaus_slider_set(g->scale4,o);
    self->dt->gui->reset = 0;
    p->rotation = r;
    p->offset = o;
    g->dragging = 0;
    dt_dev_add_history_item(darktable.develop, self, TRUE);
  }

  g->dragging = 0;
  return 0;
}
Ejemplo n.º 2
0
static void
mode_callback(GtkWidget *combo, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;

  if(darktable.gui->reset) return;

  dt_iop_exposure_gui_data_t *g = (dt_iop_exposure_gui_data_t *)self->gui_data;
  dt_iop_exposure_params_t *p = (dt_iop_exposure_params_t *)self->params;

  const dt_iop_exposure_mode_t new_mode = GPOINTER_TO_UINT(g_list_nth_data(g->modes, dt_bauhaus_combobox_get(combo)));

  free(g->deflicker_histogram);
  g->deflicker_histogram = NULL;

  switch(new_mode)
  {
    case EXPOSURE_MODE_DEFLICKER:
      autoexp_disable(self);
      if(!dt_image_is_raw(&self->dev->image_storage))
      {
        dt_bauhaus_combobox_set(g->mode, g_list_index(g->modes, GUINT_TO_POINTER(EXPOSURE_MODE_MANUAL)));
        gtk_widget_hide(GTK_WIDGET(g->mode));
        break;
      }
      p->mode = EXPOSURE_MODE_DEFLICKER;
      gtk_widget_hide(GTK_WIDGET(g->vbox_manual));
      gtk_widget_show(GTK_WIDGET(g->vbox_deflicker));
      if(p->deflicker_histogram_source == DEFLICKER_HISTOGRAM_SOURCE_SOURCEFILE)
        deflicker_prepare_histogram(self, &g->deflicker_histogram,
                                    &g->deflicker_histogram_stats);
      break;
    case EXPOSURE_MODE_MANUAL:
    default:
      p->mode = EXPOSURE_MODE_MANUAL;
      gtk_widget_hide(GTK_WIDGET(g->vbox_deflicker));
      gtk_widget_show(GTK_WIDGET(g->vbox_manual));
      break;
  }

  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 3
0
static void colorpick_callback(GtkColorButton *widget, dt_iop_module_t *self)
{
  if(self->dt->gui->reset) return;

  dt_iop_splittoning_gui_data_t *g = (dt_iop_splittoning_gui_data_t *)self->gui_data;

  float color[3], h, s, l;

  GdkRGBA c;
  gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
  color[0] = c.red;
  color[1] = c.green;
  color[2] = c.blue;
  rgb2hsl(color, &h, &s, &l);

  dt_bauhaus_slider_set((GTK_WIDGET(widget) == g->colorpick1) ? g->gslider1 : g->gslider3, h);
  dt_bauhaus_slider_set((GTK_WIDGET(widget) == g->colorpick1) ? g->gslider2 : g->gslider4, s);

  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 4
0
static gboolean dt_iop_zonesystem_bar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
{
  dt_iop_zonesystem_params_t *p = (dt_iop_zonesystem_params_t *)self->params;
  int cs = CLAMP(p->size, 4, MAX_ZONE_SYSTEM_SIZE);

  if(event->direction == GDK_SCROLL_UP)
    p->size += 1;
  else if(event->direction == GDK_SCROLL_DOWN)
    p->size -= 1;

  /* sanity checks */
  p->size = CLAMP(p->size, 4, MAX_ZONE_SYSTEM_SIZE);

  p->zone[cs] = -1;
  dt_dev_add_history_item(darktable.develop, self, TRUE);

  gtk_widget_queue_draw(widget);

  return TRUE;
}
Ejemplo n.º 5
0
static gboolean scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_basecurve_params_t *p = (dt_iop_basecurve_params_t *)self->params;
  dt_iop_basecurve_gui_data_t *c = (dt_iop_basecurve_gui_data_t *)self->gui_data;

  int ch = 0;
  dt_iop_basecurve_node_t *basecurve = p->basecurve[ch];

  if(c->selected >= 0)
  {
    if(event->direction == GDK_SCROLL_UP)
      basecurve[c->selected].y = MAX(0.0f, basecurve[c->selected].y + 0.001f);
    if(event->direction == GDK_SCROLL_DOWN)
      basecurve[c->selected].y = MIN(1.0f, basecurve[c->selected].y - 0.001f);
    dt_dev_add_history_item(darktable.develop, self, TRUE);
    gtk_widget_queue_draw(widget);
  }
  return TRUE;
}
Ejemplo n.º 6
0
static void output_profile_changed(GtkWidget *widget, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;
  dt_iop_colorout_params_t *p = (dt_iop_colorout_params_t *)self->params;
  int pos = dt_bauhaus_combobox_get(widget);

  for(GList *profiles = darktable.color_profiles->profiles; profiles; profiles = g_list_next(profiles))
  {
    dt_colorspaces_color_profile_t *pp = (dt_colorspaces_color_profile_t *)profiles->data;
    if(pp->out_pos == pos)
    {
      p->type = pp->type;
      g_strlcpy(p->filename, pp->filename, sizeof(p->filename));
      dt_dev_add_history_item(darktable.develop, self, TRUE);
      return;
    }
  }

  fprintf(stderr, "[colorout] color profile %s seems to have disappeared!\n", dt_colorspaces_get_name(p->type, p->filename));
}
Ejemplo n.º 7
0
static void target_C_callback(GtkWidget *slider, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_colorchecker_params_t *p = (dt_iop_colorchecker_params_t *)self->params;
  dt_iop_colorchecker_gui_data_t *g = (dt_iop_colorchecker_gui_data_t *)self->gui_data;
  const float Cin = sqrtf(
      p->source_a[g->patch]*p->source_a[g->patch] +
      p->source_b[g->patch]*p->source_b[g->patch]);
  const float Cout = MAX(1e-4f, sqrtf(
      p->target_a[g->patch]*p->target_a[g->patch]+
      p->target_b[g->patch]*p->target_b[g->patch]));
  const float Cnew = CLAMP(Cin + dt_bauhaus_slider_get(slider), 0.01, 128.0);
  p->target_a[g->patch] = CLAMP(p->target_a[g->patch]*Cnew/Cout, -128.0, 128.0);
  p->target_b[g->patch] = CLAMP(p->target_b[g->patch]*Cnew/Cout, -128.0, 128.0);
  const int reset = darktable.gui->reset;
  darktable.gui->reset = 1; // avoid history item
  dt_bauhaus_slider_set(g->scale_a, p->target_a[g->patch] - p->source_a[g->patch]);
  dt_bauhaus_slider_set(g->scale_b, p->target_b[g->patch] - p->source_b[g->patch]);
  darktable.gui->reset = reset;
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 8
0
static void
frame_colorpick_callback (GtkDarktableButton *button, dt_iop_module_t *self)
{
  if(self->dt->gui->reset) return;
  dt_iop_borders_gui_data_t *g = (dt_iop_borders_gui_data_t *)self->gui_data;
  dt_iop_borders_params_t *p = (dt_iop_borders_params_t *)self->params;

  // turn off the other color picker so that this tool actually works ...
  gtk_toggle_button_set_active(g->frame_picker, FALSE);
  gtk_toggle_button_set_active(g->border_picker, FALSE);

  GtkColorSelectionDialog  *csd = GTK_COLOR_SELECTION_DIALOG(gtk_color_selection_dialog_new(_("select frame line color")));
  gtk_window_set_transient_for(GTK_WINDOW(csd), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)));

  GtkWidget *okButton, *cancelButton = 0;
  g_object_get(G_OBJECT(csd), "ok-button", &okButton, NULL);
  g_object_get(G_OBJECT(csd), "cancel-button", &cancelButton, NULL);

  g_signal_connect (G_OBJECT (okButton), "clicked",
                    G_CALLBACK (colorpick_button_callback), csd);
  g_signal_connect (G_OBJECT (cancelButton), "clicked",
                    G_CALLBACK (colorpick_button_callback), csd);

  GtkColorSelection *cs = GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(csd));
  GdkColor c;
  c.red   = 65535 * p->frame_color[0];
  c.green = 65535 * p->frame_color[1];
  c.blue  = 65535 * p->frame_color[2];
  gtk_color_selection_set_current_color(cs, &c);
  if(gtk_dialog_run(GTK_DIALOG(csd)) == GTK_RESPONSE_ACCEPT)
  {
    gtk_color_selection_get_current_color(cs, &c);
    p->frame_color[0] = c.red  /65535.0;
    p->frame_color[1] = c.green/65535.0;
    p->frame_color[2] = c.blue /65535.0;
    gtk_widget_modify_fg(GTK_WIDGET(g->frame_colorpick), GTK_STATE_NORMAL, &c);
  }
  gtk_widget_destroy(GTK_WIDGET(csd));
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 9
0
static void
hue_callback(GtkWidget *slider, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_graduatednd_params_t *p = (dt_iop_graduatednd_params_t *)self->params;
  dt_iop_graduatednd_gui_data_t *g = (dt_iop_graduatednd_gui_data_t *)self->gui_data;

  const float hue = dt_bauhaus_slider_get(g->gslider1);
  //fprintf(stderr," hue: %f, saturation: %f\n",hue,dtgtk_gradient_slider_get_value(g->gslider2));
  float saturation = 1.0f;
  float color[3];
  hsl2rgb(color, hue, saturation, 0.5f);

  dt_bauhaus_slider_set_stop(g->gslider2, 1.0f, color[0], color[1], color[2]);  // Update saturation end color

  if(self->dt->gui->reset)
    return;
  gtk_widget_draw(GTK_WIDGET(g->gslider2),NULL);

  p->hue = hue;
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 10
0
static void callback(GtkWidget *widget, gpointer *user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  if(self->dt->gui->reset) return;

  dt_iop_rawprepare_gui_data_t *g = (dt_iop_rawprepare_gui_data_t *)self->gui_data;
  dt_iop_rawprepare_params_t *p = (dt_iop_rawprepare_params_t *)self->params;

  for(int i = 0; i < 4; i++)
    p->raw_black_level_separate[i] = dt_bauhaus_slider_get(g->black_level_separate[i]);
  p->raw_white_point = dt_bauhaus_slider_get(g->white_point);

  if(dt_conf_get_bool("plugins/darkroom/rawprepare/allow_editing_crop"))
  {
    for(int i = 0; i < 4; i++)
    {
      p->crop.array[i] = dt_bauhaus_slider_get(g->crop[i]);
    }
  }

  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 11
0
static void dt_iop_exposure_set_white(struct dt_iop_module_t *self, const float white)
{
  dt_iop_exposure_params_t *p = (dt_iop_exposure_params_t *)self->params;

  if(p->mode == EXPOSURE_MODE_DEFLICKER)
  {
    dt_iop_exposure_gui_data_t *g = (dt_iop_exposure_gui_data_t *)self->gui_data;

    p->deflicker_target_level = white;

    darktable.gui->reset = 1;
    dt_bauhaus_slider_set(g->deflicker_target_level, p->deflicker_target_level);
    darktable.gui->reset = 0;

    dt_dev_add_history_item(darktable.develop, self, TRUE);
  }
  else
  {
    exposure_set_white(self, white);
    autoexp_disable(self);
  }
}
Ejemplo n.º 12
0
static gboolean
expose (GtkWidget *widget, GdkEventExpose *event, dt_iop_module_t *self)
{
    // capture gui color picked event.
    if(darktable.gui->reset) return FALSE;
    if(self->picked_color_max[0] < self->picked_color_min[0]) return FALSE;
    if(!self->request_color_pick) return FALSE;
    static float old[3] = {0, 0, 0};
    const float *grayrgb = self->picked_color;
    if(grayrgb[0] == old[0] && grayrgb[1] == old[1] && grayrgb[2] == old[2]) return FALSE;
    for(int k=0; k<3; k++) old[k] = grayrgb[k];
    dt_iop_temperature_params_t *p = (dt_iop_temperature_params_t *)self->params;
    for(int k=0; k<3; k++) p->coeffs[k] = (grayrgb[k] > 0.001f) ? 1.0f/grayrgb[k] : 1.0f;
    // normalize green:
    p->coeffs[0] /= p->coeffs[1];
    p->coeffs[2] /= p->coeffs[1];
    p->coeffs[1] = 1.0;
    for(int k=0; k<3; k++) p->coeffs[k] = fmaxf(0.0f, fminf(8.0f, p->coeffs[k]));
    gui_update_from_coeffs(self);
    dt_dev_add_history_item(darktable.develop, self, TRUE);
    return FALSE;
}
Ejemplo n.º 13
0
static gboolean scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_tonecurve_params_t *p = (dt_iop_tonecurve_params_t *)self->params;
  dt_iop_tonecurve_gui_data_t *c = (dt_iop_tonecurve_gui_data_t *)self->gui_data;

  int ch = c->channel;
  dt_iop_tonecurve_node_t *tonecurve = p->tonecurve[ch];
  int autoscale_ab = p->tonecurve_autoscale_ab;

  // if autoscale_ab is on: do not modify a and b curves
  if (autoscale_ab && ch != ch_L) return TRUE;

  if(c->selected >= 0)
  {
    if(event->direction == GDK_SCROLL_UP  ) tonecurve[c->selected].y = MAX(0.0f, tonecurve[c->selected].y + 0.001f);
    if(event->direction == GDK_SCROLL_DOWN) tonecurve[c->selected].y = MIN(1.0f, tonecurve[c->selected].y - 0.001f);
    dt_dev_add_history_item(darktable.develop, self, TRUE);
    gtk_widget_queue_draw(widget);
  }
  return TRUE;
}
Ejemplo n.º 14
0
static void
_blendop_mode_callback (GtkWidget *combo, dt_iop_gui_blend_data_t *data)
{
  data->module->blend_params->mode = data->modes[dt_bauhaus_combobox_get(data->blend_modes_combo)].mode;
  if(data->module->blend_params->mode != DEVELOP_BLEND_DISABLED)
  {
    gtk_widget_show(data->opacity_slider);
    if(data->blendif_support)
    {
      gtk_widget_show(data->blendif_enable);
      if(dt_bauhaus_combobox_get(data->blendif_enable))
        gtk_widget_show(GTK_WIDGET(data->blendif_box));
    }
  }
  else
  {
    gtk_widget_hide(data->opacity_slider);
    if(data->blendif_support)
    {
      /* switch off color picker if it was requested by blendif */
      if(data->module->request_color_pick < 0)
      {
        data->module->request_color_pick = 0;
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->colorpicker), 0);
      }

      data->module->request_mask_display = 0;
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->showmask), 0);
      data->module->suppress_mask = 0;
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->suppress), 0);


      gtk_widget_hide(GTK_WIDGET(data->blendif_enable));
      gtk_widget_hide(GTK_WIDGET(data->blendif_box));
    }
  }
  dt_dev_add_history_item(darktable.develop, data->module, TRUE);
}
Ejemplo n.º 15
0
static void
hue_callback(GtkWidget *slider, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_splittoning_params_t *p = (dt_iop_splittoning_params_t *)self->params;
  dt_iop_splittoning_gui_data_t *g = (dt_iop_splittoning_gui_data_t *)self->gui_data;

  double hue=0;
  double saturation=0;
  GtkWidget* colorpicker;
  GtkWidget* sat_slider=NULL;
  if( slider == g->gslider1 )
  {
    // Shadows
    hue = p->shadow_hue = dt_bauhaus_slider_get(slider);
    saturation = p->shadow_saturation;
    colorpicker = GTK_WIDGET(g->colorpick1);
    sat_slider = g->gslider2;
    update_balance_slider_colors(g->scale1, -1, hue);
  }
  else
  {
    hue = p->highlight_hue = dt_bauhaus_slider_get(slider);
    saturation=p->highlight_saturation;
    colorpicker=GTK_WIDGET(g->colorpick2);
    sat_slider=g->gslider4;
    update_balance_slider_colors(g->scale1, hue, -1);
  }

  update_colorpicker_fg(colorpicker, hue, saturation);
  update_saturation_slider_end_color(sat_slider, hue);

  if(self->dt->gui->reset) return;

  gtk_widget_queue_draw(GTK_WIDGET(sat_slider));

  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 16
0
static void autoexp_callback(GtkToggleButton *button, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_exposure_gui_data_t *g = (dt_iop_exposure_gui_data_t *)self->gui_data;
  if(self->dt->gui->reset) return;

  self->request_color_pick = gtk_toggle_button_get_active(button) ? DT_REQUEST_COLORPICK_MODULE
                                                                  : DT_REQUEST_COLORPICK_OFF;

  dt_iop_request_focus(self);

  if(self->request_color_pick == DT_REQUEST_COLORPICK_MODULE)
  {
    dt_lib_colorpicker_set_area(darktable.lib, 0.99);
    dt_dev_reprocess_all(self->dev);
  }
  else
    dt_control_queue_redraw();

  gtk_widget_set_sensitive(GTK_WIDGET(g->autoexpp), gtk_toggle_button_get_active(button));

  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 17
0
static void
deflicker_disable(dt_iop_module_t *self)
{
  dt_iop_exposure_gui_data_t *g = (dt_iop_exposure_gui_data_t *)self->gui_data;
  dt_iop_exposure_params_t *p = (dt_iop_exposure_params_t *)self->params;

  gulong signal_id = g_signal_lookup("toggled", GTK_TYPE_CHECK_BUTTON);
  gulong handler_id = g_signal_handler_find(G_OBJECT(g->deflicker),
                                      G_SIGNAL_MATCH_ID,
                                      signal_id,
                                      0, NULL, NULL, NULL);

  g_signal_handler_block(G_OBJECT (g->deflicker), handler_id);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->deflicker), FALSE);
  g_signal_handler_unblock(G_OBJECT (g->deflicker), handler_id);

  gtk_widget_set_sensitive(GTK_WIDGET(g->deflicker_percentile), FALSE);
  gtk_widget_set_sensitive(GTK_WIDGET(g->deflicker_level), FALSE);

  p->deflicker = FALSE;

  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 18
0
static gboolean key_softproof_callback(GtkAccelGroup *accel_group,
                                   GObject *acceleratable,
                                   guint keyval, GdkModifierType modifier,
                                   gpointer data)
{
  dt_iop_module_t* self = (dt_iop_module_t*)data;
  dt_iop_colorout_gui_data_t *g = (dt_iop_colorout_gui_data_t *)self->gui_data;
  dt_iop_colorout_params_t *p = (dt_iop_colorout_params_t *)self->params;

  /* toggle softproofing on/off */
  g->softproof_enabled = p->softproof_enabled = !p->softproof_enabled;
  if(p->softproof_enabled)
  {
    int pos = dt_bauhaus_combobox_get(g->cbox5);
    gchar *filename = _get_profile_from_pos(g->profiles, pos);
    if (filename)
      g_strlcpy(p->softproofprofile, filename, sizeof(p->softproofprofile));
  }

  dt_dev_add_history_item(darktable.develop, self, TRUE);
  dt_control_queue_redraw_center();
  return TRUE;
}
Ejemplo n.º 19
0
static void
saturation_callback(GtkDarktableGradientSlider *slider, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_splittoning_params_t *p = (dt_iop_splittoning_params_t *)self->params;
  dt_iop_splittoning_gui_data_t *g = (dt_iop_splittoning_gui_data_t *)self->gui_data;

  double hue=0;
  double saturation=0;
  float color[3];
  GtkWidget *preview;
  if( slider == g->gslider2 )
  {
    // Shadows
    hue=dtgtk_gradient_slider_get_value(g->gslider1);
    p->shadow_saturation=saturation=dtgtk_gradient_slider_get_value(slider);
    preview=GTK_WIDGET(g->colorpick1);
  }
  else
  {
    hue=dtgtk_gradient_slider_get_value(g->gslider3);
    p->highlight_saturation=saturation=dtgtk_gradient_slider_get_value(slider);
    preview=GTK_WIDGET(g->colorpick2);
  }

  hsl2rgb(color,hue,saturation,0.5);

  GdkColor c;
  c.red=color[0]*65535.0;
  c.green=color[1]*65535.0;
  c.blue=color[2]*65535.0;

  gtk_widget_modify_fg(preview,GTK_STATE_NORMAL,&c); // Update color preview

  if(self->dt->gui->reset) return;
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 20
0
static gboolean dt_iop_levels_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_levels_gui_data_t *c = (dt_iop_levels_gui_data_t *)self->gui_data;
  dt_iop_levels_params_t *p = (dt_iop_levels_params_t *)self->params;

  const float interval = 0.002; // Distance moved for each scroll event
  gboolean updated = FALSE;
  float new_position = 0;

  if (c->dragging)
  {
    return FALSE;
  }

  if(event->direction == GDK_SCROLL_UP)
  {
    new_position = p->levels[c->handle_move] + interval;
    updated = TRUE;
  }
  else if(event->direction == GDK_SCROLL_DOWN)
  {
    new_position = p->levels[c->handle_move] - interval;
    updated = TRUE;
  }

  if (updated)
  {
    dt_iop_levels_move_handle(self, c->handle_move, new_position,
                              p->levels, c->drag_start_percentage);
    dt_dev_add_history_item(darktable.develop, self, TRUE);
    return TRUE;
  }

  return FALSE;
}
Ejemplo n.º 21
0
static void
hue_callback(GtkDarktableGradientSlider *slider, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_colorize_params_t *p = (dt_iop_colorize_params_t *)self->params;
  dt_iop_colorize_gui_data_t *g = (dt_iop_colorize_gui_data_t *)self->gui_data;

  float color[3];
  GtkWidget *preview;
  GtkDarktableGradientSlider *sslider=NULL;


  p->hue = dtgtk_gradient_slider_get_value(slider);
  preview = GTK_WIDGET(g->colorpick1);
  sslider = g->gslider2;

  /* convert to rgb */
  hsl2rgb(color,p->hue,p->saturation,0.5);

  /* update preview color */
  GdkColor c;
  c.red=color[0]*65535.0;
  c.green=color[1]*65535.0;
  c.blue=color[2]*65535.0;

  dtgtk_gradient_slider_set_stop(sslider,1.0,c); 

  gtk_widget_modify_fg(preview,GTK_STATE_NORMAL,&c);

  if (self->dt->gui->reset) return;

  gtk_widget_draw(GTK_WIDGET(sslider),NULL);

  if (dtgtk_gradient_slider_is_dragging(slider)==FALSE) 
    dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 22
0
static void
position_v_changed (GtkWidget *combo, dt_iop_module_t *self)
{
  dt_iop_borders_gui_data_t *g = (dt_iop_borders_gui_data_t *)self->gui_data;
  dt_iop_borders_params_t *p = (dt_iop_borders_params_t *)self->params;
  int which = dt_bauhaus_combobox_get(combo);
  const char* text = dt_bauhaus_combobox_get_text(combo);
  if (which < 0)
  {
    p->pos_v = 0.5f; // center
    if(text)
    {
      const char *c = text;
      while(*c != ':' && *c != '/' && c < text + strlen(text)) c++;
      if(c < text + strlen(text) - 1)
      {
        // *c = '\0'; // not needed, atof will stop there.
        c++;
        p->pos_v = atof(text) / atof(c);
      }
      else
      {
        p->pos_v = atof(text);
      }
      g_strlcpy(p->pos_v_text, text, sizeof(p->pos_v_text));
      p->pos_v = MAX(p->pos_v, 0);
      p->pos_v = MIN(p->pos_v, 1);
    }
  }
  else if (which < DT_IOP_BORDERS_POSITION_H_COUNT)
  {
    g_strlcpy(p->pos_v_text, text, sizeof(p->pos_v_text));
    p->pos_v = g->pos_h_ratios[which];
  }
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 23
0
static void
mode_changed (GtkWidget *combo, dt_iop_module_t *self)
{
  dt_iop_highlights_params_t *p = (dt_iop_highlights_params_t *)self->params;
  dt_iop_highlights_gui_data_t *g = (dt_iop_highlights_gui_data_t *)self->gui_data;
  int active = dt_bauhaus_combobox_get(combo);

  switch(active)
  {
    case DT_IOP_HIGHLIGHTS_CLIP:
      p->mode = DT_IOP_HIGHLIGHTS_CLIP;
      gtk_widget_set_visible(GTK_WIDGET(g->slider_box), FALSE);
      break;
    default:
    case DT_IOP_HIGHLIGHTS_LCH:
      p->mode = DT_IOP_HIGHLIGHTS_LCH;
      gtk_widget_set_visible(GTK_WIDGET(g->slider_box), TRUE);
      gtk_widget_set_no_show_all(GTK_WIDGET(g->slider_box), FALSE);
      gtk_widget_show_all(GTK_WIDGET(g->slider_box));
      gtk_widget_set_no_show_all(GTK_WIDGET(g->slider_box), TRUE);
      break;
  }
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 24
0
static gboolean dt_iop_tonecurve_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_tonecurve_gui_data_t *c = (dt_iop_tonecurve_gui_data_t *)self->gui_data;
  dt_iop_tonecurve_params_t *p = (dt_iop_tonecurve_params_t *)self->params;

  int ch = c->channel;
  int nodes = p->tonecurve_nodes[ch];
  dt_iop_tonecurve_node_t *tonecurve = p->tonecurve[ch];
  int autoscale_ab = p->tonecurve_autoscale_ab;

  // if autoscale_ab is on: do not modify a and b curves
  if (autoscale_ab && ch != ch_L) goto finally;

  const int inset = DT_GUI_CURVE_EDITOR_INSET;
  int height = widget->allocation.height - 2*inset, width = widget->allocation.width - 2*inset;
  c->mouse_x = CLAMP(event->x - inset, 0, width);
  c->mouse_y = CLAMP(event->y - inset, 0, height);

  const float mx = c->mouse_x/(float)width;
  const float my = 1.0f - c->mouse_y/(float)height;

  if(event->state & GDK_BUTTON1_MASK)
  {
    // got a vertex selected:
    if(c->selected >= 0)
    {
      tonecurve[c->selected].x = mx;
      tonecurve[c->selected].y = my;

      // delete vertex if order has changed:
      if(nodes > 2)
        if((c->selected > 0 && tonecurve[c->selected-1].x >= mx) ||
            (c->selected < nodes-1 && tonecurve[c->selected+1].x <= mx))
        {
          for(int k=c->selected; k<nodes-1; k++)
          {
            tonecurve[k].x = tonecurve[k+1].x;
            tonecurve[k].y = tonecurve[k+1].y;
          }
          c->selected = -2; // avoid re-insertion of that point immediately after this
          p->tonecurve_nodes[ch] --;
        }
      dt_dev_add_history_item(darktable.develop, self, TRUE);
    }
    else if(nodes < 20 && c->selected >= -1)
    {
      // no vertex was close, create a new one!
      if(tonecurve[0].x > mx)
        c->selected = 0;
      else for(int k=1; k<nodes; k++)
        {
          if(tonecurve[k].x > mx)
          {
            c->selected = k;
            break;
          }
        }
      if(c->selected == -1) c->selected = nodes;
      for(int i=nodes; i>c->selected; i--)
      {
        tonecurve[i].x = tonecurve[i-1].x;
        tonecurve[i].y = tonecurve[i-1].y;
      }
      // found a new point
      tonecurve[c->selected].x = mx;
      tonecurve[c->selected].y = my;
      p->tonecurve_nodes[ch] ++;
      dt_dev_add_history_item(darktable.develop, self, TRUE);
    }
  }
  else
  {
    // minimum area around the node to select it:
    float min = .04f;
    min *= min; // comparing against square
    int nearest = -1;
    for(int k=0; k<nodes; k++)
    {
      float dist = (my - tonecurve[k].y)*(my - tonecurve[k].y)
                   + (mx - tonecurve[k].x)*(mx - tonecurve[k].x);
      if(dist < min)
      {
        min = dist;
        nearest = k;
      }
    }
    c->selected = nearest;
  }
finally:
  gtk_widget_queue_draw(widget);
  return TRUE;
}
Ejemplo n.º 25
0
static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_levels_gui_data_t *c = (dt_iop_levels_gui_data_t *)self->gui_data;
  dt_iop_levels_params_t *p = (dt_iop_levels_params_t *)self->params;
  const int inset = DT_GUI_CURVE_EDITOR_INSET;
  int height = widget->allocation.height - 2*inset, width = widget->allocation.width - 2*inset;
  if(!c->dragging)
  {
    c->mouse_x = CLAMP(event->x - inset, 0, width);
    c->drag_start_percentage = (p->levels[1] - p->levels[0])
                               / (p->levels[2] - p->levels[0]);
  }
  c->mouse_y = CLAMP(event->y - inset, 0, height);

  if(c->dragging)
  {
    if(c->handle_move >= 0 && c->handle_move < 3)
    {
      const float mx = (CLAMP(event->x - inset, 0, width)) / (float)width;

      float min_x = 0;
      float max_x = 1;

      // Determining the minimum and maximum bounds for the drag handles
      switch(c->handle_move)
      {
      case 0:
        max_x = fminf(p->levels[2] - (0.05 / c->drag_start_percentage),
                      1);
        max_x = fminf((p->levels[2] * (1 - c->drag_start_percentage) - 0.05)
                      / (1 - c->drag_start_percentage),
                      max_x);
        break;

      case 1:
        min_x = p->levels[0] + 0.05;
        max_x = p->levels[2] - 0.05;
        break;

      case 2:
        min_x = fmaxf((0.05 / c->drag_start_percentage) + p->levels[0],
                      0);
        min_x = fmaxf((p->levels[0] * (1 - c->drag_start_percentage) + 0.05)
                      / (1 - c->drag_start_percentage),
                      min_x);
        break;
      }

      p->levels[c->handle_move] =
          fminf(max_x, fmaxf(min_x, mx));

      if(c->handle_move != 1)
        p->levels[1] = p->levels[0] + (c->drag_start_percentage
                                       * (p->levels[2] - p->levels[0]));
    }
    dt_dev_add_history_item(darktable.develop, self, TRUE);
  }
  else
  {
    c->handle_move = 0;
    const float mx = CLAMP(event->x - inset, 0, width)/(float)width;
    float dist = fabsf(p->levels[0] - mx);
    for(int k=1; k<3; k++)
    {
      float d2 = fabsf(p->levels[k] - mx);
      if(d2 < dist)
      {
        c->handle_move = k;
        dist = d2;
      }
    }
  }
  gtk_widget_queue_draw(widget);

  gint x, y;
  gdk_window_get_pointer(event->window, &x, &y, NULL);
  return TRUE;
}
Ejemplo n.º 26
0
static gboolean dt_iop_levels_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_levels_gui_data_t *c = (dt_iop_levels_gui_data_t *)self->gui_data;
  dt_iop_levels_params_t *p = (dt_iop_levels_params_t *)self->params;
  const int inset = DT_GUI_CURVE_EDITOR_INSET;
  int width = widget->allocation.width, height = widget->allocation.height;
  cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
  cairo_t *cr = cairo_create(cst);

  float mean_picked_color = *self->picked_color / 100.0;

  /* we need to save the last picked color to prevent flickering when
   * changing from one picker to another, as the picked_color value does not
   * update as rapidly */
  if(self->request_color_pick && mean_picked_color != c->last_picked_color)
  {
    float previous_color[3];
    previous_color[0] = p->levels[0];
    previous_color[1] = p->levels[1];
    previous_color[2] = p->levels[2];

    c->last_picked_color = mean_picked_color;

    if (BLACK == c->current_pick)
    {
      if (mean_picked_color > p->levels[1])
      {
        p->levels[0] = p->levels[1]-FLT_EPSILON;
      }
      else
      {
        p->levels[0] = mean_picked_color;
      }
      c->pick_xy_positions[0][0] = self->color_picker_point[0];
      c->pick_xy_positions[0][1] = self->color_picker_point[1];
    }
    else if (GREY == c->current_pick)
    {
      if (mean_picked_color < p->levels[0] || mean_picked_color > p->levels[2])
      {
        p->levels[1] = p->levels[1];
      }
      else
      {
        p->levels[1] = mean_picked_color;
      }
      c->pick_xy_positions[1][0] = self->color_picker_point[0];
      c->pick_xy_positions[1][1] = self->color_picker_point[1];
    }
    else if (WHITE == c->current_pick)
    {
      if (mean_picked_color < p->levels[1])
      {
        p->levels[2] = p->levels[1]+FLT_EPSILON;
      }
      else
      {
        p->levels[2] = mean_picked_color;
      }
      c->pick_xy_positions[2][0] = self->color_picker_point[0];
      c->pick_xy_positions[2][1] = self->color_picker_point[1];
    }

    if (   previous_color[0] != p->levels[0]
        || previous_color[1] != p->levels[1]
        || previous_color[2] != p->levels[2] )
    {
      dt_dev_add_history_item(darktable.develop, self, TRUE);
    }
  }

  // clear bg
  cairo_set_source_rgb (cr, .2, .2, .2);
  cairo_paint(cr);

  cairo_translate(cr, inset, inset);
  width -= 2*inset;
  height -= 2*inset;

  cairo_set_line_width(cr, 1.0);
  cairo_set_source_rgb (cr, .1, .1, .1);
  cairo_rectangle(cr, 0, 0, width, height);
  cairo_stroke(cr);

  cairo_set_source_rgb (cr, .3, .3, .3);
  cairo_rectangle(cr, 0, 0, width, height);
  cairo_fill(cr);

  // draw grid
  cairo_set_line_width(cr, .4);
  cairo_set_source_rgb (cr, .1, .1, .1);
  dt_draw_vertical_lines(cr, 4, 0, 0, width, height);

  // Drawing the vertical line indicators
  cairo_set_line_width(cr, 2.);

  for(int k = 0; k < 3; k++)
  {
    if(k == c->handle_move && c->mouse_x > 0)
      cairo_set_source_rgb(cr, 1, 1, 1);
    else
      cairo_set_source_rgb(cr, .7, .7, .7);

    cairo_move_to(cr, width*p->levels[k], height);
    cairo_rel_line_to(cr, 0, -height);
    cairo_stroke(cr);
  }

  // draw x positions
  cairo_set_line_width(cr, 1.);
  const float arrw = 7.0f;
  for(int k=0; k<3; k++)
  {
    switch(k)
    {
      case 0:
        cairo_set_source_rgb(cr, 0, 0, 0);
        break;

      case 1:
        cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
        break;

      default:
        cairo_set_source_rgb(cr, 1, 1, 1);
        break;
    }

    cairo_move_to(cr, width*p->levels[k], height+inset-1);
    cairo_rel_line_to(cr, -arrw*.5f, 0);
    cairo_rel_line_to(cr, arrw*.5f, -arrw);
    cairo_rel_line_to(cr, arrw*.5f, arrw);
    cairo_close_path(cr);
    if(c->handle_move == k && c->mouse_x > 0)
      cairo_fill(cr);
    else
      cairo_stroke(cr);
  }

  cairo_translate(cr, 0, height);

  // draw lum histogram in background
  // only if the module is enabled
  if (self->enabled)
  {
    dt_develop_t *dev = darktable.develop;
    float *hist, hist_max;
    hist = dev->histogram_pre_levels;
    hist_max = dev->histogram_linear?dev->histogram_pre_levels_max:logf(1.0 + dev->histogram_pre_levels_max);
    if(hist_max > 0)
    {
      cairo_save(cr);
      cairo_scale(cr, width/63.0, -(height-5)/(float)hist_max);
      cairo_set_source_rgba(cr, .2, .2, .2, 0.5);
      dt_draw_histogram_8(cr, hist, 3);
      cairo_restore(cr);
    }
  }

  // Cleaning up
  cairo_destroy(cr);
  cairo_t *cr_pixmap = gdk_cairo_create(gtk_widget_get_window(widget));
  cairo_set_source_surface (cr_pixmap, cst, 0, 0);
  cairo_paint(cr_pixmap);
  cairo_destroy(cr_pixmap);
  cairo_surface_destroy(cst);
  return TRUE;
}
Ejemplo n.º 27
0
static gboolean borders_draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
{
  if(darktable.gui->reset) return FALSE;
  if(self->picked_output_color_max[0] < 0) return FALSE;
  if(self->request_color_pick == DT_REQUEST_COLORPICK_OFF) return FALSE;
  dt_iop_borders_gui_data_t *g = (dt_iop_borders_gui_data_t *)self->gui_data;
  dt_iop_borders_params_t *p = (dt_iop_borders_params_t *)self->params;

  // interrupt if no valid color reading
  if(self->picked_color_min[0] == INFINITY) return FALSE;

  if(fabsf(p->color[0] - self->picked_color[0]) < 0.0001f
     && fabsf(p->color[1] - self->picked_color[1]) < 0.0001f
     && fabsf(p->color[2] - self->picked_color[2]) < 0.0001f)
  {
    // interrupt infinite loops
    return FALSE;
  }

  if(fabsf(p->frame_color[0] - self->picked_color[0]) < 0.0001f
     && fabsf(p->frame_color[1] - self->picked_color[1]) < 0.0001f
     && fabsf(p->frame_color[2] - self->picked_color[2]) < 0.0001f)
  {
    // interrupt infinite loops
    return FALSE;
  }

  GdkRGBA c = (GdkRGBA){.red = self->picked_color[0],
                        .green = self->picked_color[1],
                        .blue = self->picked_color[2],
                        .alpha = 1.0 };
  if(g->active_colorpick == g->frame_colorpick)
  {
    p->frame_color[0] = self->picked_color[0];
    p->frame_color[1] = self->picked_color[1];
    p->frame_color[2] = self->picked_color[2];
    gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->frame_colorpick), &c);
  }
  else
  {
    p->color[0] = self->picked_color[0];
    p->color[1] = self->picked_color[1];
    p->color[2] = self->picked_color[2];
    gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &c);
  }

  dt_dev_add_history_item(darktable.develop, self, TRUE);
  return FALSE;
}

static void aspect_changed(GtkWidget *combo, dt_iop_module_t *self)
{
  dt_iop_borders_gui_data_t *g = (dt_iop_borders_gui_data_t *)self->gui_data;
  dt_iop_borders_params_t *p = (dt_iop_borders_params_t *)self->params;
  int which = dt_bauhaus_combobox_get(combo);
  const char *text = dt_bauhaus_combobox_get_text(combo);
  if(which < 0)
  {
    p->aspect = DT_IOP_BORDERS_ASPECT_CONSTANT_VALUE;
    if(text)
    {
      const char *c = text;
      const char *end = text + strlen(text);
      while(*c != ':' && *c != '/' && c < end) c++;
      if(c < end - 1)
      {
        // *c = '\0'; // not needed, atof will stop there.
        c++;
        p->aspect = atof(text) / atof(c);
        g_strlcpy(p->aspect_text, text, sizeof(p->aspect_text));
      }
    }
  }
  else if(which < DT_IOP_BORDERS_ASPECT_COUNT)
  {
    g_strlcpy(p->aspect_text, text, sizeof(p->aspect_text));
    p->aspect = g->aspect_ratios[which];
  }
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 28
0
static void detail_callback(GtkWidget *w, dt_iop_module_t *self)
{
  dt_iop_bilat_params_t *p = (dt_iop_bilat_params_t *)self->params;
  p->detail = dt_bauhaus_slider_get(w);
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Ejemplo n.º 29
0
static gboolean lowlight_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_lowlight_gui_data_t *c = (dt_iop_lowlight_gui_data_t *)self->gui_data;
  dt_iop_lowlight_params_t *p = (dt_iop_lowlight_params_t *)self->params;
  const int inset = DT_IOP_LOWLIGHT_INSET;
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
  if(!c->dragging) c->mouse_x = CLAMP(event->x - inset, 0, width) / (float)width;
  c->mouse_y = 1.0 - CLAMP(event->y - inset, 0, height) / (float)height;
  if(c->dragging)
  {
    *p = c->drag_params;
    if(c->x_move >= 0)
    {
      const float mx = CLAMP(event->x - inset, 0, width) / (float)width;
      if(c->x_move > 0 && c->x_move < DT_IOP_LOWLIGHT_BANDS - 1)
      {
        const float minx = p->transition_x[c->x_move - 1] + 0.001f;
        const float maxx = p->transition_x[c->x_move + 1] - 0.001f;
        p->transition_x[c->x_move] = fminf(maxx, fmaxf(minx, mx));
      }
    }
    else
    {
      dt_iop_lowlight_get_params(p, c->mouse_x, c->mouse_y + c->mouse_pick, c->mouse_radius);
    }
    dt_dev_add_history_item(darktable.develop, self, TRUE);
  }
  else if(event->y > height)
  {
    c->x_move = 0;
    float dist = fabs(p->transition_x[0] - c->mouse_x);
    for(int k = 1; k < DT_IOP_LOWLIGHT_BANDS; k++)
    {
      float d2 = fabs(p->transition_x[k] - c->mouse_x);
      if(d2 < dist)
      {
        c->x_move = k;
        dist = d2;
      }
    }
  }
  else
  {
    c->x_move = -1;
  }
  gtk_widget_queue_draw(widget);
  gint x, y;
#if GTK_CHECK_VERSION(3, 20, 0)
  gdk_window_get_device_position(event->window,
      gdk_seat_get_pointer(gdk_display_get_default_seat(
          gdk_window_get_display(event->window))),
      &x, &y, 0);
#else
  gdk_window_get_device_position(event->window,
                                 gdk_device_manager_get_client_pointer(
                                     gdk_display_get_device_manager(gdk_window_get_display(event->window))),
                                 &x, &y, NULL);
#endif
  return TRUE;
}
Ejemplo n.º 30
0
static gboolean dt_iop_basecurve_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
  dt_iop_module_t *self = (dt_iop_module_t *)user_data;
  dt_iop_basecurve_gui_data_t *c = (dt_iop_basecurve_gui_data_t *)self->gui_data;
  dt_iop_basecurve_params_t *p = (dt_iop_basecurve_params_t *)self->params;
  int ch = 0;
  int nodes = p->basecurve_nodes[ch];
  dt_iop_basecurve_node_t *basecurve = p->basecurve[ch];

  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  const int inset = DT_GUI_CURVE_EDITOR_INSET;
  int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
  c->mouse_x = CLAMP(event->x - inset, 0, width);
  c->mouse_y = CLAMP(event->y - inset, 0, height);

  const float mx = c->mouse_x / (float)width;
  const float my = 1.0f - c->mouse_y / (float)height;
  const float linx = to_lin(mx, c->loglogscale), liny = to_lin(my, c->loglogscale);

  if(event->state & GDK_BUTTON1_MASK)
  {
    // got a vertex selected:
    if(c->selected >= 0)
    {
      basecurve[c->selected].x = linx;
      basecurve[c->selected].y = liny;

      // delete vertex if order has changed:
      if(nodes > 2)
        if((c->selected > 0 && basecurve[c->selected - 1].x >= linx)
           || (c->selected < nodes - 1 && basecurve[c->selected + 1].x <= linx))
        {
          for(int k = c->selected; k < nodes - 1; k++)
          {
            basecurve[k].x = basecurve[k + 1].x;
            basecurve[k].y = basecurve[k + 1].y;
          }
          c->selected = -2; // avoid re-insertion of that point immediately after this
          p->basecurve_nodes[ch]--;
        }
      dt_dev_add_history_item(darktable.develop, self, TRUE);
    }
    else if(nodes < 20 && c->selected >= -1)
    {
      // no vertex was close, create a new one!
      if(basecurve[0].x > linx)
        c->selected = 0;
      else
        for(int k = 1; k < nodes; k++)
        {
          if(basecurve[k].x > linx)
          {
            c->selected = k;
            break;
          }
        }
      if(c->selected == -1) c->selected = nodes;
      for(int i = nodes; i > c->selected; i--)
      {
        basecurve[i].x = basecurve[i - 1].x;
        basecurve[i].y = basecurve[i - 1].y;
      }
      // found a new point
      basecurve[c->selected].x = linx;
      basecurve[c->selected].y = liny;
      p->basecurve_nodes[ch]++;
      dt_dev_add_history_item(darktable.develop, self, TRUE);
    }
  }
  else
  {
    // minimum area around the node to select it:
    float min = .04f;
    min *= min; // comparing against square
    int nearest = -1;
    for(int k = 0; k < nodes; k++)
    {
      float dist
          = (my - to_log(basecurve[k].y, c->loglogscale)) * (my - to_log(basecurve[k].y, c->loglogscale))
            + (mx - to_log(basecurve[k].x, c->loglogscale)) * (mx - to_log(basecurve[k].x, c->loglogscale));
      if(dist < min)
      {
        min = dist;
        nearest = k;
      }
    }
    c->selected = nearest;
  }
  gtk_widget_queue_draw(widget);
  return TRUE;
}