Пример #1
0
static void
gwy_vector_layer_get_property(GObject *object,
                              guint prop_id,
                              GValue *value,
                              GParamSpec *pspec)
{
    GwyVectorLayer *vector_layer = GWY_VECTOR_LAYER(object);

    switch (prop_id) {
        case PROP_SELECTION_KEY:
        g_value_set_static_string(value,
                                  g_quark_to_string(vector_layer->selection_key));
        break;

        case PROP_EDITABLE:
        g_value_set_boolean(value, vector_layer->editable);
        break;

        case PROP_FOCUS:
        g_value_set_int(value, vector_layer->focus);
        break;

        default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
}
Пример #2
0
/**
 * gwy_data_view_get_top_layer:
 * @data_view: A #GwyDataView.
 *
 * Returns the top layer this data view currently uses, or %NULL if none
 * is present.
 *
 * Returns: The currently used top layer.
 **/
GwyVectorLayer*
gwy_data_view_get_top_layer(GwyDataView *data_view)
{
    g_return_val_if_fail(GWY_IS_DATA_VIEW(data_view), NULL);

    return GWY_VECTOR_LAYER(data_view->top_layer);
}
Пример #3
0
static void
gwy_vector_layer_real_updated(GwyDataViewLayer *layer)
{
    GwyVectorLayer *vector_layer;

    vector_layer = GWY_VECTOR_LAYER(layer);
    if (vector_layer->timer) {
        gtk_timeout_remove(vector_layer->timer);
        vector_layer->timer = 0;
    }
}
Пример #4
0
static void
gwy_vector_layer_destroy(GtkObject *object)
{
    GwyVectorLayer *layer;

    layer = GWY_VECTOR_LAYER(object);
    gwy_object_unref(layer->gc);
    gwy_object_unref(layer->layout);

    GTK_OBJECT_CLASS(gwy_vector_layer_parent_class)->destroy(object);
}
Пример #5
0
static gboolean
gwy_data_view_expose(GtkWidget *widget,
                     GdkEventExpose *event)
{
    GwyDataView *data_view;
    gint xs, ys, xe, ye, w, h;
    GdkRectangle rect;
    gboolean emit_redrawn = FALSE;

    data_view = GWY_DATA_VIEW(widget);

    gdk_region_get_clipbox(event->region, &rect);
    gwy_debug("bbox = %dx%d  at (%d,%d)",
              rect.width, rect.height, rect.x, rect.y);
    w = gdk_pixbuf_get_width(data_view->pixbuf);
    h = gdk_pixbuf_get_height(data_view->pixbuf);
    xs = MAX(rect.x, data_view->xoff) - data_view->xoff;
    ys = MAX(rect.y, data_view->yoff) - data_view->yoff;
    xe = MIN(rect.x + rect.width, data_view->xoff + w) - data_view->xoff;
    ye = MIN(rect.y + rect.height, data_view->yoff + h) - data_view->yoff;
    gwy_debug("going to draw: %dx%d  at (%d,%d)",
              xe - xs, ye - ys, xs, ys);
    if (xs >= xe || ys >= ye)
        return FALSE;

    /* FIXME: ask the layers, if they want to repaint themselves */
    if (data_view->force_update
        || gwy_data_view_layer_wants_repaint(data_view->base_layer)
        || gwy_data_view_layer_wants_repaint(data_view->alpha_layer)) {
        gwy_data_view_paint(data_view);
        emit_redrawn = TRUE;
    }
    data_view->force_update = FALSE;

    gdk_draw_pixbuf(widget->window,
                    NULL,
                    data_view->pixbuf,
                    xs, ys,
                    xs + data_view->xoff, ys + data_view->yoff,
                    xe - xs, ye - ys,
                    GDK_RGB_DITHER_NORMAL,
                    0, 0);

    if (data_view->top_layer)
        gwy_vector_layer_draw(GWY_VECTOR_LAYER(data_view->top_layer),
                              widget->window);

    if (emit_redrawn)
        g_signal_emit(data_view, data_view_signals[REDRAWN], 0);

    return FALSE;
}
Пример #6
0
static gboolean
gwy_data_view_key_release(GtkWidget *widget,
                          GdkEventKey *event)
{
    GwyDataView *data_view;

    g_return_val_if_fail(event, FALSE);

    data_view = GWY_DATA_VIEW(widget);
    if (!data_view->top_layer)
        return FALSE;

    return gwy_vector_layer_key_release(GWY_VECTOR_LAYER(data_view->top_layer), event);
}
Пример #7
0
static gboolean
gwy_data_view_button_press(GtkWidget *widget,
                           GdkEventButton *event)
{
    GwyDataView *data_view;

    gwy_debug(" ");
    g_return_val_if_fail(event, FALSE);

    data_view = GWY_DATA_VIEW(widget);
    if (!data_view->top_layer)
        return FALSE;

    return gwy_vector_layer_button_press(GWY_VECTOR_LAYER(data_view->top_layer), event);
}
Пример #8
0
static void
gwy_vector_layer_finalize(GObject *object)
{
    GwyVectorLayer *layer;

    gwy_debug(" ");

    g_return_if_fail(GWY_IS_VECTOR_LAYER(object));

    layer = GWY_VECTOR_LAYER(object);

    gwy_object_unref(layer->gc);
    gwy_object_unref(layer->layout);

    G_OBJECT_CLASS(parent_class)->finalize(object);
}
Пример #9
0
static void
gwy_vector_layer_get_property(GObject*object,
                              guint prop_id,
                              GValue *value,
                              GParamSpec *pspec)
{
    GwyVectorLayer *layer = GWY_VECTOR_LAYER(object);

    switch (prop_id) {
        case PROP_UPDATE_POLICY:
        g_value_set_enum(value, layer->update_policy);
        break;

        default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
        break;
    }
}
Пример #10
0
static void
gwy_vector_layer_unplugged(GwyDataViewLayer *layer)
{
    GwyVectorLayer *vector_layer;

    gwy_debug(" ");

    vector_layer = GWY_VECTOR_LAYER(layer);
    gwy_object_unref(vector_layer->gc);
    if (vector_layer->timer) {
        gtk_timeout_remove(vector_layer->timer);
        vector_layer->timer = 0;
    }
    g_signal_handlers_disconnect_matched(layer->parent,
                                         G_SIGNAL_MATCH_FUNC
                                            | G_SIGNAL_MATCH_DATA,
                                         0, 0, NULL,
                                         gwy_vector_layer_update_context,
                                         layer);
    gwy_object_unref(vector_layer->layout);

    GWY_DATA_VIEW_LAYER_CLASS(parent_class)->unplugged(layer);
}
Пример #11
0
static void
facets_dialog(FacetsArgs *args,
              GwyContainer *data,
              GwyContainer *fdata,
              GwyDataField *dfield,
              GwyDataField *mfield,
              gint id,
              GQuark mquark)
{
    GtkWidget *dialog, *table, *hbox, *hbox2, *vbox, *label, *scale, *button;
    GtkWidget *spin;
    FacetsControls controls;
    enum {
        RESPONSE_RESET = 1,
        RESPONSE_PREVIEW = 2
    };
    gint response;
    GwyPixmapLayer *layer;
    GwyVectorLayer *vlayer;
    GwySelection *selection;
    gint row;

    memset(&controls, 0, sizeof(FacetsControls));
    controls.args = args;
    dialog = gtk_dialog_new_with_buttons(_("Mark Facets"),
                                         NULL,
                                         GTK_DIALOG_DESTROY_WITH_PARENT,
                                         _("_Mark"), RESPONSE_PREVIEW,
                                         _("_Reset"), RESPONSE_RESET,
                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                         GTK_STOCK_OK, GTK_RESPONSE_OK,
                                         NULL);
    gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
    controls.dialog = dialog;

    /* Shallow-copy stuff to temporary container */
    controls.fdata = fdata;
    controls.mydata = gwy_container_new();
    gwy_container_set_object_by_name(controls.mydata, "/0/data", dfield);
    gwy_app_sync_data_items(data, controls.mydata, id, 0, FALSE,
                            GWY_DATA_ITEM_PALETTE,
                            GWY_DATA_ITEM_RANGE,
                            GWY_DATA_ITEM_MASK_COLOR,
                            GWY_DATA_ITEM_REAL_SQUARE,
                            0);

    hbox = gtk_hbox_new(FALSE, 2);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,
                       FALSE, FALSE, 4);

    controls.view = gwy_data_view_new(controls.mydata);
    layer = gwy_layer_basic_new();
    g_object_set(layer,
                 "data-key", "/0/data",
                 "gradient-key", "/0/base/palette",
                 "range-type-key", "/0/base/range-type",
                 "min-max-key", "/0/base",
                 NULL);
    gwy_data_view_set_data_prefix(GWY_DATA_VIEW(controls.view), "/0/data");
    gwy_data_view_set_base_layer(GWY_DATA_VIEW(controls.view), layer);
    gwy_set_data_preview_size(GWY_DATA_VIEW(controls.view), PREVIEW_SIZE);

    vlayer = g_object_new(g_type_from_name("GwyLayerPoint"), NULL);
    gwy_vector_layer_set_selection_key(vlayer, "/0/select/pointer");
    gwy_data_view_set_top_layer(GWY_DATA_VIEW(controls.view), vlayer);
    selection = gwy_vector_layer_ensure_selection(vlayer);
    g_signal_connect(selection, "changed",
                     G_CALLBACK(preview_selection_updated), &controls);

    gtk_box_pack_start(GTK_BOX(hbox), controls.view, FALSE, FALSE, 4);

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);

    hbox2 = gtk_hbox_new(FALSE, 4);
    gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0);

    /* Slope view */
    controls.fview = gwy_data_view_new(controls.fdata);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.fview, FALSE, FALSE, 0);

    layer = gwy_layer_basic_new();
    gwy_pixmap_layer_set_data_key(layer, "/0/data");
    gwy_layer_basic_set_gradient_key(GWY_LAYER_BASIC(layer), "/0/base/palette");
    gwy_data_view_set_base_layer(GWY_DATA_VIEW(controls.fview),
                                 layer);

    vlayer = g_object_new(g_type_from_name("GwyLayerPoint"), NULL);
    gwy_vector_layer_set_selection_key(vlayer, "/0/select/pointer");
    gwy_data_view_set_top_layer(GWY_DATA_VIEW(controls.fview),
                                GWY_VECTOR_LAYER(vlayer));
    selection = gwy_vector_layer_ensure_selection(vlayer);
    g_signal_connect(selection, "changed",
                     G_CALLBACK(facet_view_selection_updated), &controls);

    /* Info table */
    table = gtk_table_new(7, 2, FALSE);
    gtk_table_set_row_spacings(GTK_TABLE(table), 2);
    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    gtk_box_pack_start(GTK_BOX(hbox2), table, TRUE, TRUE, 4);
    row = 0;

    label = gwy_label_new_header(gwy_sgettext("noun|Normal"));
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    controls.theta_label = add_angle_label(table, _("θ:"), &row);
    controls.phi_label = add_angle_label(table, _("φ:"), &row);

    button = gtk_button_new_with_mnemonic(_("_Find Maximum"));
    gtk_table_attach(GTK_TABLE(table), button,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    g_signal_connect_swapped(button, "clicked",
                             G_CALLBACK(facet_view_reset_maximum), &controls);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    label = gwy_label_new_header(_("Mean Normal"));
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    controls.mtheta_label = add_angle_label(table, _("θ:"), &row);
    controls.mphi_label = add_angle_label(table, _("φ:"), &row);
    gtk_table_set_row_spacing(GTK_TABLE(table), row-1, 8);

    label = gtk_label_new_with_mnemonic(_("Facet plane size:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    controls.kernel_size = gtk_adjustment_new(args->kernel_size,
                                              0.0, MAX_PLANE_SIZE, 1.0, 1.0, 0);
    spin = gtk_spin_button_new(GTK_ADJUSTMENT(controls.kernel_size), 0.0, 0);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), spin);
    gtk_table_attach(GTK_TABLE(table), spin,
                     0, 1, row, row+1, 0, 0, 0, 0);
    g_signal_connect(controls.kernel_size, "value-changed",
                     G_CALLBACK(facet_view_recompute), &controls);
    row++;

    table = gtk_table_new(9, 4, FALSE);
    gtk_table_set_row_spacings(GTK_TABLE(table), 2);
    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 4);
    row = 0;

    controls.tolerance = gtk_adjustment_new(args->tolerance*180.0/G_PI,
                                            0.0, 15.0, 0.01, 0.1, 0);
    scale = gwy_table_attach_hscale(table, row++, _("_Tolerance:"), _("deg"),
                                    controls.tolerance, 0);
    gtk_spin_button_set_digits(GTK_SPIN_BUTTON(scale), 3);
    g_signal_connect(controls.tolerance, "value-changed",
                     G_CALLBACK(facets_tolerance_changed), &controls);

    controls.color_button = gwy_color_button_new();
    gwy_color_button_set_use_alpha(GWY_COLOR_BUTTON(controls.color_button),
                                   TRUE);
    load_mask_color(controls.color_button,
                    gwy_data_view_get_data(GWY_DATA_VIEW(controls.view)));
    gwy_table_attach_hscale(table, row++, _("_Mask color:"), NULL,
                            GTK_OBJECT(controls.color_button),
                            GWY_HSCALE_WIDGET_NO_EXPAND);
    g_signal_connect(controls.color_button, "clicked",
                     G_CALLBACK(mask_color_change_cb), &controls);

    if (!gwy_si_unit_equal(gwy_data_field_get_si_unit_xy(dfield),
                           gwy_data_field_get_si_unit_z(dfield))) {
        gtk_table_set_row_spacing(GTK_TABLE(table), row-1, 8);
        label = gtk_label_new(_("Warning: Lateral and value units differ. "
                                "Angles are not physically meaningful."));
        gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
        gtk_table_attach(GTK_TABLE(table), label,
                         0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
        row++;
    }

    facets_invalidate(&controls);
    gtk_widget_show_all(dialog);
    facet_view_select_angle(&controls, args->theta0, args->phi0);

    do {
        response = gtk_dialog_run(GTK_DIALOG(dialog));
        switch (response) {
            case GTK_RESPONSE_CANCEL:
            case GTK_RESPONSE_DELETE_EVENT:
            gtk_widget_destroy(dialog);
            case GTK_RESPONSE_NONE:
            g_object_unref(controls.mydata);
            return;
            break;

            case GTK_RESPONSE_OK:
            break;

            case RESPONSE_RESET:
            args->tolerance = facets_defaults.tolerance;
            args->kernel_size = facets_defaults.kernel_size;
            facets_dialog_update_controls(&controls, args);
            break;

            case RESPONSE_PREVIEW:
            preview(&controls, args);
            update_average_angle(&controls, args);
            break;

            default:
            g_assert_not_reached();
            break;
        }
    } while (response != GTK_RESPONSE_OK);

    gwy_app_sync_data_items(controls.mydata, data, 0, id, FALSE,
                            GWY_DATA_ITEM_MASK_COLOR,
                            0);
    gtk_widget_destroy(dialog);

    if (controls.computed) {
        mfield = gwy_container_get_object_by_name(controls.mydata, "/0/mask");
        gwy_app_undo_qcheckpointv(data, 1, &mquark);
        gwy_container_set_object(data, mquark, mfield);
        g_object_unref(controls.mydata);
    }
    else {
        g_object_unref(controls.mydata);
        run_noninteractive(args, data, fdata, dfield, mfield, mquark);
    }
}
Пример #12
0
static void
selection_finished_cb(GwyUnitoolState *state)
{
    GwyContainer *data;
    GObject *dfield;
    GwyDataField *mask = NULL;
    GwyDataViewLayer *layer;
    ToolControls *controls;
    GwySIUnit *siunit;
    gint isel[4];

    controls = (ToolControls*)state->user_data;
    layer = GWY_DATA_VIEW_LAYER(state->layer);
    data = gwy_data_view_get_data(GWY_DATA_VIEW(layer->parent));
    dfield = gwy_container_get_object_by_name(data, "/0/data");
    gwy_container_gis_object_by_name(data, "/0/mask", (GObject**)&mask);
    gwy_unitool_rect_info_table_fill(state, &controls->labels, NULL, isel);

    switch (controls->mode) {
        case MASK_EDIT_SET:
        gwy_app_undo_checkpoint(data, "/0/mask", NULL);
        if (!mask) {
            mask = GWY_DATA_FIELD(gwy_serializable_duplicate(dfield));
            siunit = GWY_SI_UNIT(gwy_si_unit_new(""));
            gwy_data_field_set_si_unit_z(mask, siunit);
            g_object_unref(siunit);
            gwy_container_set_object_by_name(data, "/0/mask", (GObject*)mask);
            g_object_unref(mask);
        }
        gwy_data_field_fill(mask, 0.0);
        gwy_data_field_area_fill(mask, isel[0], isel[1], isel[2], isel[3],
                                 1.0);
        break;

        case MASK_EDIT_ADD:
        gwy_app_undo_checkpoint(data, "/0/mask", NULL);
        if (!mask) {
            mask = GWY_DATA_FIELD(gwy_serializable_duplicate(dfield));
            siunit = GWY_SI_UNIT(gwy_si_unit_new(""));
            gwy_data_field_set_si_unit_z(mask, siunit);
            g_object_unref(siunit);
            gwy_container_set_object_by_name(data, "/0/mask", (GObject*)mask);
            g_object_unref(mask);
            gwy_data_field_fill(mask, 0.0);
        }
        gwy_data_field_area_fill(mask, isel[0], isel[1], isel[2], isel[3],
                                 1.0);
        break;

        case MASK_EDIT_REMOVE:
        if (mask) {
            gwy_app_undo_checkpoint(data, "/0/mask", NULL);
            gwy_data_field_area_fill(mask, isel[0], isel[1], isel[2], isel[3],
                                     0.0);
        }
        break;

        case MASK_EDIT_INTERSECT:
        if (mask) {
            gwy_app_undo_checkpoint(data, "/0/mask", NULL);
            gwy_data_field_clamp(mask, 0.0, 1.0);
            gwy_data_field_area_add(mask, isel[0], isel[1], isel[2], isel[3],
                                    1.0);
            gwy_data_field_add(mask, -1.0);
            gwy_data_field_clamp(mask, 0.0, 1.0);
        }
        break;

        default:
        break;
    }
    gwy_vector_layer_unselect(GWY_VECTOR_LAYER(layer));
    gwy_app_data_view_update(layer->parent);
}
Пример #13
0
static gboolean
curvature_dialog(CurvatureArgs *args,
                 GwyContainer *data,
                 GwyDataField *dfield,
                 GwyDataField *mfield,
                 gint id)
{
    enum { RESPONSE_RESET = 1 };
    GtkWidget *dialog, *table, *label, *hbox, *vbox, *treeview, *button, *hbox2;
    GwyDataChooser *chooser;
    GtkTreeSelection *selection;
    GtkTreeViewColumn *column;
    GtkCellRenderer *renderer;
    GwyPixmapLayer *player;
    GwyVectorLayer *vlayer;
    CurvatureControls controls;
    gint response;
    gint row;

    controls.args = args;
    controls.unit = gwy_data_field_get_si_unit_xy(dfield);
    gwy_clear(controls.params, PARAM_NPARAMS);

    controls.gmodel = gwy_graph_model_new();
    init_graph_model_units(controls.gmodel, dfield);

    dialog = gtk_dialog_new_with_buttons(_("Curvature"),
                                         NULL, 0,
                                         _("_Reset"), RESPONSE_RESET,
                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                         GTK_STOCK_OK, GTK_RESPONSE_OK,
                                         NULL);
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
    gwy_help_add_to_proc_dialog(GTK_DIALOG(dialog), GWY_HELP_DEFAULT);
    controls.dialog = dialog;

    hbox = gtk_hbox_new(FALSE, 8);
    gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,
                       FALSE, FALSE, 0);

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);

    controls.data = gwy_container_new();
    gwy_container_set_object_by_name(controls.data, "/0/data", dfield);
    if (mfield)
        gwy_container_set_object_by_name(controls.data, "/0/mask", mfield);
    gwy_app_sync_data_items(data, controls.data, id, 0, FALSE,
                            GWY_DATA_ITEM_PALETTE,
                            GWY_DATA_ITEM_MASK_COLOR,
                            GWY_DATA_ITEM_RANGE,
                            GWY_DATA_ITEM_REAL_SQUARE,
                            0);
    controls.view = gwy_data_view_new(controls.data);
    player = gwy_layer_basic_new();
    g_object_set(player,
                 "data-key", "/0/data",
                 "gradient-key", "/0/base/palette",
                 "range-type-key", "/0/base/range-type",
                 "min-max-key", "/0/base",
                 NULL);
    gwy_data_view_set_data_prefix(GWY_DATA_VIEW(controls.view), "/0/data");
    gwy_data_view_set_base_layer(GWY_DATA_VIEW(controls.view), player);
    gwy_set_data_preview_size(GWY_DATA_VIEW(controls.view), PREVIEW_SIZE);

    vlayer = g_object_new(g_type_from_name("GwyLayerLine"), NULL);
    gwy_vector_layer_set_selection_key(vlayer, "/0/select/line");
    gwy_vector_layer_set_editable(vlayer, FALSE);
    gwy_data_view_set_top_layer(GWY_DATA_VIEW(controls.view),
                                GWY_VECTOR_LAYER(vlayer));
    controls.selection = gwy_vector_layer_ensure_selection(vlayer);
    g_object_set(controls.selection, "max-objects", 2, NULL);

    gtk_box_pack_start(GTK_BOX(vbox), controls.view, FALSE, FALSE, 4);

    table = gtk_table_new(5 + (mfield ? 4 : 0), 4, FALSE);
    gtk_table_set_row_spacings(GTK_TABLE(table), 2);
    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
    gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
    row = 0;

    label = gwy_label_new_header(_("Output Type"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    controls.set_selection
        = gtk_check_button_new_with_mnemonic(_("_Set selection"));
    gtk_table_attach(GTK_TABLE(table), controls.set_selection,
                     0, 4, row, row+1, GTK_FILL, 0, 0, 0);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.set_selection),
                                 args->set_selection);
    g_signal_connect(controls.set_selection, "toggled",
                     G_CALLBACK(curvature_set_selection_changed), &controls);
    row++;

    controls.plot_graph
        = gtk_check_button_new_with_mnemonic(_("_Plot graph"));
    gtk_table_attach(GTK_TABLE(table), controls.plot_graph,
                     0, 4, row, row+1, GTK_FILL, 0, 0, 0);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.plot_graph),
                                 args->plot_graph);
    g_signal_connect(controls.plot_graph, "toggled",
                     G_CALLBACK(curvature_plot_graph_changed), &controls);
    row++;

    gtk_table_set_row_spacing(GTK_TABLE(table), row-1, 8);
    controls.target_hbox = hbox2 = gtk_hbox_new(FALSE, 6);
    gtk_widget_set_sensitive(controls.target_hbox, args->plot_graph);
    gtk_table_attach(GTK_TABLE(table), hbox2,
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);

    label = gtk_label_new_with_mnemonic(_("Target _graph:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    controls.target_graph = gwy_data_chooser_new_graphs();
    chooser = GWY_DATA_CHOOSER(controls.target_graph);
    gwy_data_chooser_set_none(chooser, _("New graph"));
    gwy_data_chooser_set_active(chooser, NULL, -1);
    gwy_data_chooser_set_filter(chooser, filter_target_graphs, &controls, NULL);
    gwy_data_chooser_set_active_id(chooser, &args->target_graph);
    gwy_data_chooser_get_active_id(chooser, &args->target_graph);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), controls.target_graph);
    gtk_box_pack_end(GTK_BOX(hbox2), controls.target_graph, FALSE, FALSE, 0);
    g_signal_connect_swapped(controls.target_graph, "changed",
                             G_CALLBACK(target_graph_changed), &controls);
    row++;

    if (mfield) {
        gtk_table_set_row_spacing(GTK_TABLE(table), row-1, 8);
        label = gwy_label_new_header(_("Masking Mode"));
        gtk_table_attach(GTK_TABLE(table), label,
                        0, 3, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
        row++;

        controls.masking_group
            = gwy_radio_buttons_create(gwy_masking_type_get_enum(), -1,
                                       G_CALLBACK(curvature_masking_changed),
                                       &controls, args->masking);
        row = gwy_radio_buttons_attach_to_table(controls.masking_group,
                                                GTK_TABLE(table), 3, row);
        gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    }
    else
        controls.masking_group = NULL;

    controls.warning = gtk_label_new(NULL);
    gtk_misc_set_alignment(GTK_MISC(controls.warning), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), controls.warning,
                     0, 3, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);

    controls.graph = gwy_graph_new(controls.gmodel);
    gtk_widget_set_size_request(controls.graph, 320, 260);
    g_object_unref(controls.gmodel);

    gtk_box_pack_start(GTK_BOX(vbox), controls.graph, TRUE, TRUE, 4);

    controls.paramstore = gwy_null_store_new(PARAM_NPARAMS);
    treeview
        = gtk_tree_view_new_with_model(GTK_TREE_MODEL(controls.paramstore));
    g_object_unref(controls.paramstore);
    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
    gtk_box_pack_start(GTK_BOX(vbox), treeview, FALSE, FALSE, 4);

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
    gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes(_("Parameter"), renderer,
                                                      NULL);
    gtk_tree_view_column_set_cell_data_func(column, renderer,
                                            render_name, param_names, NULL);
    gtk_tree_view_column_set_expand(column, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes(_("Symbol"), renderer,
                                                      NULL);
    gtk_tree_view_column_set_cell_data_func(column, renderer,
                                            render_symbol, param_symbols, NULL);
    gtk_tree_view_column_set_expand(column, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    renderer = gtk_cell_renderer_text_new();
    g_object_set(renderer, "xalign", 1.0, NULL);
    column = gtk_tree_view_column_new_with_attributes(_("Value"), renderer,
                                                      NULL);
    gtk_tree_view_column_set_cell_data_func(column, renderer,
                                            render_value, &controls, NULL);
    gtk_tree_view_column_set_expand(column, TRUE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);

    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

    button = curvature_add_aux_button(hbox, GTK_STOCK_SAVE,
                                      _("Save table to a file"));
    g_signal_connect_swapped(button, "clicked",
                             G_CALLBACK(curvature_save), &controls);

    button = curvature_add_aux_button(hbox, GTK_STOCK_COPY,
                                      _("Copy table to clipboard"));
    g_signal_connect_swapped(button, "clicked",
                             G_CALLBACK(curvature_copy), &controls);

    curvature_update_preview(&controls, args);

    gtk_widget_show_all(dialog);
    do {
        response = gtk_dialog_run(GTK_DIALOG(dialog));
        switch (response) {
            case GTK_RESPONSE_CANCEL:
            case GTK_RESPONSE_DELETE_EVENT:
            gtk_widget_destroy(dialog);
            case GTK_RESPONSE_NONE:
            return FALSE;
            break;

            case GTK_RESPONSE_OK:
            break;

            case RESPONSE_RESET:
            *args = curvature_defaults;
            curvature_dialog_update(&controls, args);
            break;

            default:
            g_assert_not_reached();
            break;
        }
    } while (response != GTK_RESPONSE_OK);

    gtk_widget_destroy(dialog);

    return TRUE;
}