Esempio n. 1
0
static void
filter(GwyGraph *graph)
{
    GwyContainer *data;
    GwyGraphCurveModel *cmodel, *cmodelnew;
    GwyGraphModel *model;
    const gdouble *xdata, *ydata;
    gdouble *newydata;
    gint i, ncurves, ndata;
    GQuark quark;

    gwy_app_data_browser_get_current(GWY_APP_CONTAINER, &data,
                                     GWY_APP_GRAPH_MODEL_KEY, &quark,
                                     0);
    gwy_app_undo_qcheckpointv(data, 1, &quark);

    model = gwy_graph_get_model(graph);
    ncurves = gwy_graph_model_get_n_curves(model);
    for (i = 0; i < ncurves; i++) {
        cmodel = gwy_graph_model_get_curve(model, i);
        cmodelnew = gwy_graph_curve_model_new_alike(cmodel);
        xdata = gwy_graph_curve_model_get_xdata(cmodel);
        ydata = gwy_graph_curve_model_get_ydata(cmodel);
        ndata = gwy_graph_curve_model_get_ndata(cmodel);
        newydata = g_new(gdouble, ndata);
        filter_do(ydata, newydata, ndata);
        gwy_graph_curve_model_set_data(cmodelnew, xdata,
                                       newydata, ndata);
        g_free(newydata);
        gwy_graph_model_remove_curve(gwy_graph_get_model(graph), i);
        gwy_graph_model_add_curve(model, cmodelnew);
        g_object_unref(cmodelnew);
    }
}
Esempio n. 2
0
static void
gwy_graph_window_y_log(GwyGraphWindow *graphwindow)
{
    GwyGraphModel *model;
    gboolean state;

    model = gwy_graph_get_model(GWY_GRAPH(graphwindow->graph));
    g_object_get(model, "y-logarithmic", &state, NULL);
    g_object_set(model, "y-logarithmic", !state, NULL);
}
Esempio n. 3
0
static void
zoom_in_changed(EntropyControls *controls, GtkToggleButton *check)
{
    GwyGraphModel *gmodel = gwy_graph_get_model(GWY_GRAPH(controls->graph));
    GwyGraphCurveModel *gcmodel;
    const gdouble *xdata, *ydata;
    gdouble S;
    guint ndata, i;

    g_object_set(gmodel,
                 "x-min-set", FALSE,
                 "x-max-set", FALSE,
                 "y-min-set", FALSE,
                 "y-max-set", FALSE,
                 NULL);

    if (!(controls->args->zoom_in = gtk_toggle_button_get_active(check))
        || (gwy_graph_model_get_n_curves(gmodel) < 2)) {
        return;
    }

    gcmodel = gwy_graph_model_get_curve(gmodel, 1);
    ydata = gwy_graph_curve_model_get_ydata(gcmodel);
    S = ydata[0];

    gcmodel = gwy_graph_model_get_curve(gmodel, 0);
    ndata = gwy_graph_curve_model_get_ndata(gcmodel);
    if (ndata < 5)
        return;

    xdata = gwy_graph_curve_model_get_xdata(gcmodel);
    ydata = gwy_graph_curve_model_get_ydata(gcmodel);
    for (i = 1; i+1 < ndata; i++) {
        if (ydata[i] > S - G_LN2) {
            g_object_set(gmodel,
                         "x-min", xdata[i-1],
                         "x-min-set", TRUE,
                         "y-min", ydata[i-1],
                         "y-min-set", TRUE,
                         NULL);
            break;
        }
    }
    for (i = ndata-2; i; i--) {
        if (ydata[i] < S + G_LN2) {
            g_object_set(gmodel,
                         "x-max", xdata[i+1],
                         "x-max-set", TRUE,
                         "y-max", ydata[i+1],
                         "y-max-set", TRUE,
                         NULL);
            break;
        }
    }
}
Esempio n. 4
0
static gboolean
filter_target_graphs(GwyContainer *data, gint id, gpointer user_data)
{
    GrainCrossControls *controls = (GrainCrossControls*)user_data;
    GwyGraphModel *gmodel, *targetgmodel;
    GQuark quark = gwy_app_get_graph_key_for_id(id);

    gmodel = gwy_graph_get_model(GWY_GRAPH(controls->graph));
    g_return_val_if_fail(GWY_IS_GRAPH_MODEL(gmodel), FALSE);
    return (gwy_container_gis_object(data, quark, (GObject**)&targetgmodel)
            && gwy_graph_model_units_are_compatible(gmodel, targetgmodel));
}
Esempio n. 5
0
static void
gwy_graph_window_zoom_to_fit(GwyGraphWindow *graphwindow)
{
    GwyGraph *graph;
    GwyGraphModel *model;

    graph = GWY_GRAPH(graphwindow->graph);
    model = gwy_graph_get_model(graph);
    g_object_set(model,
                 "x-min-set", FALSE, "x-max-set", FALSE,
                 "y-min-set", FALSE, "y-max-set", FALSE,
                 NULL);
}
Esempio n. 6
0
static void
graph_title_changed(GwyGraphWindow *graphwindow)
{
    GwyGraphModel *gmodel;
    gchar *title;

    gmodel = gwy_graph_get_model(GWY_GRAPH(graphwindow->graph));
    g_object_get(gmodel, "title", &title, NULL);

    /* FIXME: Can it be NULL? */
    if (title)
        gtk_window_set_title(GTK_WINDOW(graphwindow), title);
    else
        gtk_window_set_title(GTK_WINDOW(graphwindow), _("Untitled"));

    g_free(title);
}
Esempio n. 7
0
/**
 * gwy_graph_window_new:
 * @graph: A GwyGraph object containing the graph.
 *
 * Creates a new window showing @graph.
 *
 * Returns: A newly created graph window as #GtkWidget.
 **/
GtkWidget*
gwy_graph_window_new(GwyGraph *graph)
{
    GwyGraphWindow *graphwindow;
    GwyGraphArea *area;
    GwySelection *selection;
    GtkScrolledWindow *swindow;
    GtkWidget *vbox, *hbox;
    GtkWidget *label;

    gwy_debug("");
    g_return_val_if_fail(GWY_IS_GRAPH(graph), NULL);

    graphwindow = (GwyGraphWindow*)g_object_new(GWY_TYPE_GRAPH_WINDOW, NULL);
    gtk_window_set_wmclass(GTK_WINDOW(graphwindow), "data",
                           g_get_application_name());
    gtk_window_set_resizable(GTK_WINDOW(graphwindow), TRUE);

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(GTK_WINDOW(graphwindow)), vbox);

    graphwindow->graph = GTK_WIDGET(graph);
    graphwindow->last_status = gwy_graph_get_status(graph);

    /*add notebook with graph and text matrix*/
    graphwindow->notebook = gtk_notebook_new();

    graph_title_changed(graphwindow);

    label = gtk_label_new(_("Graph"));
    gtk_notebook_append_page(GTK_NOTEBOOK(graphwindow->notebook),
                             GTK_WIDGET(graph),
                             label);


    swindow = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
    graphwindow->data = gwy_graph_data_new(gwy_graph_get_model(graph));
    gtk_container_add(GTK_CONTAINER(swindow), graphwindow->data);

    label = gtk_label_new(_("Data"));
    gtk_notebook_append_page(GTK_NOTEBOOK(graphwindow->notebook),
                             GTK_WIDGET(swindow),
                             label);


    gtk_container_add(GTK_CONTAINER(vbox), graphwindow->notebook);
    /*add buttons*/

    hbox = gtk_hbox_new(FALSE, 0);

    graphwindow->button_measure_points = gtk_toggle_button_new();
    gtk_container_add(GTK_CONTAINER(graphwindow->button_measure_points),
                      gtk_image_new_from_stock(GWY_STOCK_GRAPH_MEASURE,
                                               GTK_ICON_SIZE_LARGE_TOOLBAR));
    gtk_box_pack_start(GTK_BOX(hbox), graphwindow->button_measure_points,
                       FALSE, FALSE, 0);
    gwy_graph_window_set_tooltip(graphwindow->button_measure_points,
                                 _("Measure distances in graph"));
    g_signal_connect_swapped(graphwindow->button_measure_points, "clicked",
                             G_CALLBACK(gwy_graph_window_measure),
                             graphwindow);

    graphwindow->button_zoom_in = gtk_toggle_button_new();
    gtk_container_add(GTK_CONTAINER(graphwindow->button_zoom_in),
                      gtk_image_new_from_stock(GWY_STOCK_GRAPH_ZOOM_IN,
                                               GTK_ICON_SIZE_LARGE_TOOLBAR));
    gtk_box_pack_start(GTK_BOX(hbox), graphwindow->button_zoom_in,
                       FALSE, FALSE, 0);
    gwy_graph_window_set_tooltip(graphwindow->button_zoom_in,
                                 _("Zoom in by mouse selection"));
    g_signal_connect_swapped(graphwindow->button_zoom_in, "toggled",
                             G_CALLBACK(gwy_graph_window_zoom_in),
                             graphwindow);

    graphwindow->button_zoom_to_fit = gtk_button_new();
    gtk_container_add(GTK_CONTAINER(graphwindow->button_zoom_to_fit),
                      gtk_image_new_from_stock(GWY_STOCK_GRAPH_ZOOM_FIT,
                                               GTK_ICON_SIZE_LARGE_TOOLBAR));
    gtk_box_pack_start(GTK_BOX(hbox), graphwindow->button_zoom_to_fit,
                       FALSE, FALSE, 0);
    gwy_graph_window_set_tooltip(graphwindow->button_zoom_to_fit,
                                 _("Zoom out to full curve"));
    g_signal_connect_swapped(graphwindow->button_zoom_to_fit, "clicked",
                             G_CALLBACK(gwy_graph_window_zoom_to_fit),
                             graphwindow);

    graphwindow->button_x_log = gtk_toggle_button_new();
    gtk_container_add(GTK_CONTAINER(graphwindow->button_x_log),
                      gtk_image_new_from_stock(GWY_STOCK_LOGSCALE_HORIZONTAL,
                                               GTK_ICON_SIZE_LARGE_TOOLBAR));
    gtk_box_pack_start(GTK_BOX(hbox), graphwindow->button_x_log,
                       FALSE, FALSE, 0);
    gwy_graph_window_set_tooltip(graphwindow->button_x_log,
                                 _("Toggle logarithmic x axis"));
    g_signal_connect_swapped(graphwindow->button_x_log, "clicked",
                             G_CALLBACK(gwy_graph_window_x_log),
                             graphwindow);

    gtk_widget_set_sensitive(graphwindow->button_x_log,
                gwy_graph_model_x_data_can_be_logarithmed(graph->graph_model));

    graphwindow->button_y_log = gtk_toggle_button_new();
    gtk_container_add(GTK_CONTAINER(graphwindow->button_y_log),
                      gtk_image_new_from_stock(GWY_STOCK_LOGSCALE_VERTICAL,
                                               GTK_ICON_SIZE_LARGE_TOOLBAR));
    gtk_box_pack_start(GTK_BOX(hbox), graphwindow->button_y_log,
                       FALSE, FALSE, 0);
    gwy_graph_window_set_tooltip(graphwindow->button_x_log,
                                 _("Toggle logarithmic y axis"));
    g_signal_connect_swapped(graphwindow->button_y_log, "clicked",
                             G_CALLBACK(gwy_graph_window_y_log),
                             graphwindow);

    gtk_widget_set_sensitive(graphwindow->button_y_log,
                gwy_graph_model_y_data_can_be_logarithmed(graph->graph_model));


    graphwindow->statusbar = gwy_statusbar_new();
    gtk_widget_set_name(graphwindow->statusbar, "gwyflatstatusbar");
    gtk_box_pack_start(GTK_BOX(hbox), graphwindow->statusbar, TRUE, TRUE, 0);

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

    graphwindow->measure_dialog = _gwy_graph_window_measure_dialog_new(graph);
    g_signal_connect_swapped(graphwindow->measure_dialog, "response",
                             G_CALLBACK(gwy_graph_window_measure_finished),
                             graphwindow);

    g_signal_connect_swapped(gwy_graph_get_area(graph), "motion-notify-event",
                             G_CALLBACK(gwy_graph_cursor_motion), graphwindow);

    area = GWY_GRAPH_AREA(gwy_graph_get_area(graph));
    selection = gwy_graph_area_get_selection(area, GWY_GRAPH_STATUS_ZOOM);
    g_signal_connect_swapped(selection, "finished",
                             G_CALLBACK(gwy_graph_window_zoom_finished),
                             graphwindow);


    if (gwy_graph_get_model(GWY_GRAPH(graphwindow->graph)))
        g_signal_connect_swapped(gwy_graph_get_model(graph), "notify::title",
                                 G_CALLBACK(graph_title_changed), graphwindow);

    return GTK_WIDGET(graphwindow);
}
Esempio n. 8
0
static void
preview(EntropyControls *controls)
{
    EntropyArgs *args = controls->args;
    GwyDataField *dfield = controls->dfield;
    GwyDataField *mfield = controls->mfield;
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *gcmodel;
    GwyDataLine *ecurve;
    gchar buf[24];
    gdouble S, s, Smax = 0.0;

    ecurve = gwy_data_line_new(1, 1.0, FALSE);
    if (args->mode == ENTROPY_VALUES) {
        S = gwy_data_field_area_get_entropy_at_scales(dfield, ecurve,
                                                      mfield, args->masking,
                                                      0, 0,
                                                      dfield->xres,
                                                      dfield->yres,
                                                      0);
        s = gwy_data_field_area_get_rms_mask(dfield,
                                             mfield, args->masking,
                                             0, 0, dfield->xres, dfield->yres);
        Smax = ENTROPY_NORMAL + log(s);
    }
    else {
        GwyDataField *xder = gwy_data_field_new_alike(dfield, FALSE);
        GwyDataField *yder = gwy_data_field_new_alike(dfield, FALSE);

        compute_slopes(controls->dfield,
                       args->fit_plane ? args->kernel_size : 0, xder, yder);
        xder = fake_mask(xder, mfield, args->masking);
        yder = fake_mask(yder, mfield, args->masking);
        if (args->mode == ENTROPY_ANGLES)
            transform_to_sphere(xder, yder);

        S = gwy_data_field_get_entropy_2d_at_scales(xder, yder, ecurve, 0);
        if (args->mode == ENTROPY_SLOPES) {
            s = calculate_sigma2_2d(xder, yder);
            Smax = ENTROPY_NORMAL_2D + log(s);
        }

        g_object_unref(xder);
        g_object_unref(yder);
    }

    g_snprintf(buf, sizeof(buf), "%g", S);
    gtk_label_set_text(GTK_LABEL(controls->entropy), buf);

    if (args->mode != ENTROPY_ANGLES) {
        g_snprintf(buf, sizeof(buf), "%g", Smax - S);
        gtk_label_set_text(GTK_LABEL(controls->entropydef), buf);
    }
    else
        gtk_label_set_text(GTK_LABEL(controls->entropydef), _("N.A."));

    gmodel = gwy_graph_get_model(GWY_GRAPH(controls->graph));
    gwy_graph_model_remove_all_curves(gmodel);
    g_object_set(gmodel,
                 "axis-label-bottom", "log h",
                 "axis-label-left", "S",
                 "label-position", GWY_GRAPH_LABEL_NORTHWEST,
                 NULL);

    if (gwy_data_line_get_min(ecurve) > -0.5*G_MAXDOUBLE) {
        gcmodel = gwy_graph_curve_model_new();
        g_object_set(gcmodel,
                     "description", _("Entropy at scales"),
                     "mode", GWY_GRAPH_CURVE_LINE_POINTS,
                     "color", gwy_graph_get_preset_color(0),
                     NULL);
        gwy_graph_curve_model_set_data_from_dataline(gcmodel, ecurve, 0, 0);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
    }

    if (S > -0.5*G_MAXDOUBLE) {
        GwyDataLine *best = gwy_data_line_duplicate(ecurve);
        gdouble *ydata = gwy_data_line_get_data(best);
        guint i, res = gwy_data_line_get_res(best);

        for (i = 0; i < res; i++)
            ydata[i] = S;

        gcmodel = gwy_graph_curve_model_new();
        g_object_set(gcmodel,
                     "description", _("Best estimate"),
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", gwy_graph_get_preset_color(1),
                     NULL);
        gwy_graph_curve_model_set_data_from_dataline(gcmodel, best, 0, 0);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
        g_object_unref(best);
    }

    g_object_unref(ecurve);

    zoom_in_changed(controls, GTK_TOGGLE_BUTTON(controls->zoom_in));
}
Esempio n. 9
0
static void
fit_dialog(FitArgs *args)
{
    GtkWidget *label, *dialog, *hbox, *hbox2, *table, *align, *expander, *scroll;
    GtkTable *table2;
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *cmodel;
    GwyGraphArea *area;
    GwySelection *selection;
    GwySIUnit *siunit;
    FitControls controls;
    gint response, row;
    GString *report;
    gdouble xmin, xmax;

    controls.args = args;
    controls.in_update = TRUE;
    controls.param = g_array_new(FALSE, TRUE, sizeof(FitParamControl));

    gmodel = gwy_graph_get_model(GWY_GRAPH(args->parent_graph));
    gwy_graph_model_get_x_range(gmodel, &xmin, &xmax);
    g_object_get(gmodel, "si-unit-x", &siunit, NULL);
    args->abscissa_vf
        = gwy_si_unit_get_format_with_digits(siunit,
                                             GWY_SI_UNIT_FORMAT_VFMARKUP,
                                             MAX(fabs(xmin), fabs(xmax)), 4,
                                             NULL);
    g_object_unref(siunit);

    dialog = gtk_dialog_new_with_buttons(_("Fit FD Curve"), NULL, 0, NULL);
    controls.dialog = dialog;
    gtk_dialog_add_action_widget(GTK_DIALOG(dialog),
                                 gwy_stock_like_button_new(gwy_sgettext("verb|_Fit"),
                                                           GTK_STOCK_EXECUTE),
                                 RESPONSE_FIT);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          gwy_sgettext("verb|_Estimate"), RESPONSE_ESTIMATE);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          _("_Plot Inits"), RESPONSE_PLOT);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          GTK_STOCK_SAVE, RESPONSE_SAVE);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          GTK_STOCK_OK, GTK_RESPONSE_OK);
    gwy_help_add_to_graph_dialog(GTK_DIALOG(dialog), GWY_HELP_DEFAULT);
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);

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

    /* Graph */
    args->graph_model = gwy_graph_model_new_alike(gmodel);
    controls.graph = gwy_graph_new(args->graph_model);
    g_object_unref(args->graph_model);
    gtk_widget_set_size_request(controls.graph, 400, 300);

    gwy_graph_enable_user_input(GWY_GRAPH(controls.graph), FALSE);
    gtk_box_pack_start(GTK_BOX(hbox), controls.graph, TRUE, TRUE, 0);
    gwy_graph_set_status(GWY_GRAPH(controls.graph), GWY_GRAPH_STATUS_XSEL);

    area = GWY_GRAPH_AREA(gwy_graph_get_area(GWY_GRAPH(controls.graph)));
    selection = gwy_graph_area_get_selection(area, GWY_GRAPH_STATUS_XSEL);
    gwy_selection_set_max_objects(selection, 1);
    g_signal_connect(selection, "changed",
                     G_CALLBACK(graph_selected), &controls);

    gwy_graph_model_add_curve(controls.args->graph_model,
                              gwy_graph_model_get_curve(gmodel, args->curve));
    args->fitfunc = NULL;

    /* Controls */
    align = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
    gtk_box_pack_start(GTK_BOX(hbox), align, FALSE, FALSE, 0);
    g_signal_connect(align, "size-request", G_CALLBACK(grow_width), NULL);

    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_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    row = 0;

    /* Curve to fit */
    label = gtk_label_new_with_mnemonic(_("_Graph curve:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 1, row, row+1, GTK_FILL, 0, 0, 0);

    controls.curve = curve_selector_new(gmodel,
                                        G_CALLBACK(curve_changed), &controls,
                                        args->curve);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), controls.curve);
    gtk_table_attach(GTK_TABLE(table), controls.curve,
                     1, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    /* Fitted function */
    label = gtk_label_new_with_mnemonic(_("F_unction:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 1, row, row+1, GTK_FILL, 0, 0, 0);

    controls.function = function_selector_new(G_CALLBACK(function_changed),
                                              &controls, args->function_type);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), controls.function);
    
    gtk_table_attach(GTK_TABLE(table), controls.function,
                     1, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    controls.formula = gtk_label_new("f(x) =");
    gtk_misc_set_alignment(GTK_MISC(controls.formula), 0.0, 0.5);
    gtk_label_set_selectable(GTK_LABEL(controls.formula), TRUE);
    
    scroll = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), controls.formula);
    
    gtk_table_attach(GTK_TABLE(table), scroll,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 8);
    row++;

    /* Parameters sought */
    controls.param_table = gtk_table_new(1, 10, FALSE);
    table2 = GTK_TABLE(controls.param_table);
    gtk_table_set_row_spacing(table2, 0, 2);
    gtk_table_set_col_spacings(table2, 2);
    gtk_table_set_col_spacing(table2, 0, 6);
    gtk_table_set_col_spacing(table2, 4, 6);
    gtk_table_set_col_spacing(table2, 5, 6);
    gtk_table_set_col_spacing(table2, 7, 6);
    gtk_table_set_col_spacing(table2, 8, 6);
    gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(table2),
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    gtk_table_attach(table2, gwy_label_new_header(_("Fix")),
                     0, 1, 0, 1, GTK_FILL, 0, 0, 0);
    gtk_table_attach(table2, gwy_label_new_header(_("Parameter")),
                     1, 5, 0, 1, GTK_FILL, 0, 0, 0);
    gtk_table_attach(table2, gwy_label_new_header(_("Error")),
                     6, 8, 0, 1, GTK_FILL, 0, 0, 0);
    gtk_table_attach(table2, gwy_label_new_header(_("Initial")),
                     9, 10, 0, 1, GTK_FILL, 0, 0, 0);

    /* Make space for 4 parameters */
#if 0
    for (i = 0; i < 4; i++)
        fit_param_row_create(&controls, i, table2, i+1);
#endif

    /* Chi^2 */
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), _("χ<sup>2</sup> result:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 1, row, row+1, GTK_FILL, 0, 0, 0);

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

    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    /* Correlation matrix */
    expander = gtk_expander_new(NULL);
    gtk_expander_set_label_widget(GTK_EXPANDER(expander),
                                 gwy_label_new_header(_("Correlation Matrix")));
    gtk_table_attach(GTK_TABLE(table), expander,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    align = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
    gtk_container_add(GTK_CONTAINER(expander), align);
    row++;

    controls.covar = g_array_new(FALSE, TRUE, sizeof(GtkWidget*));
    controls.covar_table = gtk_table_new(1, 1, TRUE);
    table2 = GTK_TABLE(controls.covar_table);
    gtk_table_set_col_spacings(table2, 6);
    gtk_table_set_row_spacings(table2, 2);
    gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(table2));

    /* Fit area */
    hbox2 = gtk_hbox_new(FALSE, 6);
    gtk_table_attach(GTK_TABLE(table), hbox2,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    label = gtk_label_new(_("Range:"));
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    controls.from = gtk_entry_new();
    g_object_set_data(G_OBJECT(controls.from), "id", (gpointer)"from");
    gtk_entry_set_width_chars(GTK_ENTRY(controls.from), 8);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.from, FALSE, FALSE, 0);
    g_signal_connect(controls.from, "activate",
                     G_CALLBACK(range_changed), &controls);
    gwy_widget_set_activate_on_unfocus(controls.from, TRUE);

    label = gtk_label_new(gwy_sgettext("range|to"));
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    controls.to = gtk_entry_new();
    g_object_set_data(G_OBJECT(controls.to), "id", (gpointer)"to");
    gtk_entry_set_width_chars(GTK_ENTRY(controls.to), 8);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.to, FALSE, FALSE, 0);
    g_signal_connect(controls.to, "activate",
                     G_CALLBACK(range_changed), &controls);
    gwy_widget_set_activate_on_unfocus(controls.to, TRUE);

    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), args->abscissa_vf->units);
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    /* Auto-update */
    hbox2 = gtk_hbox_new(FALSE, 6);
    gtk_table_attach(GTK_TABLE(table), hbox2,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    label = gtk_label_new(_("Instant:"));
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    controls.auto_estimate = gtk_check_button_new_with_mnemonic(_("e_stimate"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.auto_estimate),
                                 args->auto_estimate);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.auto_estimate, FALSE, FALSE, 0);
    g_signal_connect(controls.auto_estimate, "toggled",
                     G_CALLBACK(auto_estimate_changed), &controls);

    controls.auto_plot = gtk_check_button_new_with_mnemonic(_("p_lot"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.auto_plot),
                                 args->auto_plot);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.auto_plot, FALSE, FALSE, 0);
    g_signal_connect(controls.auto_plot, "toggled",
                     G_CALLBACK(auto_plot_changed), &controls);

    function_changed(GTK_COMBO_BOX(controls.function), &controls);
    graph_selected(selection, -1, &controls);

    controls.in_update = FALSE;
    gtk_widget_show_all(dialog);

    do {
        response = gtk_dialog_run(GTK_DIALOG(dialog));
        fit_fetch_entry(&controls);
        switch (response) {
            case GTK_RESPONSE_CANCEL:
            case GTK_RESPONSE_DELETE_EVENT:
            gtk_widget_destroy(dialog);
            fit_controls_free(&controls);
            return;
            break;

            case GTK_RESPONSE_OK:
            if (args->is_estimated) {
                cmodel = gwy_graph_model_get_curve(args->graph_model, 1);
                gwy_graph_model_add_curve(gmodel, cmodel);
            }
            gtk_widget_destroy(dialog);
            break;

            case RESPONSE_SAVE:
            report = create_fit_report(args);
            gwy_save_auxiliary_data(_("Save Fit Report"), GTK_WINDOW(dialog),
                                    -1, report->str);
            g_string_free(report, TRUE);
            break;

            case RESPONSE_ESTIMATE:
            fit_estimate(&controls);
            break;

            case RESPONSE_PLOT:
            fit_set_state(&controls, FALSE, TRUE);
            fit_plot_curve(args);
            break;

            case RESPONSE_FIT:
            fit_do(&controls);
            break;

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