Exemple #1
0
static void
grain_dist_dialog_update_sensitivity(GrainDistControls *controls,
                                     GrainDistArgs *args)
{
    GtkWidget *check, *w;

    check = gwy_table_hscale_get_check(controls->resolution);
    switch (args->mode) {
        case MODE_GRAPH:
        gtk_widget_set_sensitive(check, TRUE);
        gtk_toggle_button_toggled(GTK_TOGGLE_BUTTON(check));
        gtk_toggle_button_toggled(GTK_TOGGLE_BUTTON(check));
        break;

        case MODE_RAW:
        gtk_widget_set_sensitive(check, FALSE);
        w = gwy_table_hscale_get_scale(controls->resolution);
        gtk_widget_set_sensitive(w, FALSE);
        w = gwy_table_hscale_get_middle_widget(controls->resolution);
        gtk_widget_set_sensitive(w, FALSE);
        break;

        default:
        g_assert_not_reached();
        break;
    }

    gtk_widget_set_sensitive(controls->ok, args->selected & args->bitmask);
}
Exemple #2
0
static void
tip_blind_dialog(TipBlindArgs *args)
{
    GtkWidget *dialog, *table, *hbox, *vbox, *label;
    GwyContainer *data;
    GwyGraphModel *gmodel;
    GwyGraphArea *area;
    TipBlindControls controls;
    GwyPixmapLayer *layer;
    GwyDataField *dfield;
    GQuark quark;
    GwySIUnit *unit;
    gint response, row;

    dialog = gtk_dialog_new_with_buttons(_("Blind Tip Estimation"), NULL, 0,
                                         _("Run _Partial"), RESPONSE_ESTIMATE,
                                         _("Run _Full"), RESPONSE_REFINE,
                                         _("_Reset Tip"), 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.args = args;
    controls.in_update = TRUE;
    controls.good_tip = FALSE;
    controls.dialog = dialog;
    gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK,
                                      controls.good_tip);

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

    controls.vxres = 240;
    controls.vyres = 240;
    controls.oldnstripes = args->nstripes;

    /* set initial tip properties */
    data = gwy_app_data_browser_get(args->source.datano);
    quark = gwy_app_get_data_key_for_id(args->source.id);
    dfield = GWY_DATA_FIELD(gwy_container_get_object(data, quark));

    controls.tip = gwy_data_field_new_alike(dfield, TRUE);
    gwy_data_field_resample(controls.tip, args->xres, args->yres,
                            GWY_INTERPOLATION_NONE);
    gwy_data_field_clear(controls.tip);

    /* set up data of rescaled image of the tip */
    controls.vtip = gwy_container_new();
    gwy_app_sync_data_items(data, controls.vtip,
                            args->source.id, 0, FALSE,
                            GWY_DATA_ITEM_PALETTE,
                            0);

    dfield = gwy_data_field_new_alike(controls.tip, TRUE);
    gwy_data_field_resample(dfield, controls.vxres, controls.vyres,
                            GWY_INTERPOLATION_ROUND);
    gwy_container_set_object_by_name(controls.vtip, "/0/data", dfield);
    g_object_unref(dfield);

    /* set up rescaled image of the tip */
    vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 4);

    controls.view = gwy_data_view_new(controls.vtip);
    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.view), layer);

    /* set up tip estimation controls */
    gtk_box_pack_start(GTK_BOX(vbox), controls.view, FALSE, FALSE, 0);

    gmodel = gwy_graph_model_new();
    controls.graph = gwy_graph_new(gmodel);
    g_object_unref(gmodel);
    gwy_axis_set_visible(gwy_graph_get_axis(GWY_GRAPH(controls.graph),
                                            GTK_POS_LEFT),
                         FALSE);
    gwy_axis_set_visible(gwy_graph_get_axis(GWY_GRAPH(controls.graph),
                                            GTK_POS_BOTTOM),
                         FALSE);
    area = GWY_GRAPH_AREA(gwy_graph_get_area(GWY_GRAPH(controls.graph)));
    gtk_widget_set_no_show_all(gwy_graph_area_get_label(area), TRUE);
    g_signal_connect_after(gwy_graph_area_get_label(area), "map",
                           G_CALLBACK(gtk_widget_hide), NULL);
    gtk_box_pack_start(GTK_BOX(vbox), controls.graph, TRUE, TRUE, 0);

    table = gtk_table_new(13, 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(hbox), table, FALSE, FALSE, 4);
    row = 0;

    controls.data = gwy_data_chooser_new_channels();
    gwy_data_chooser_set_filter(GWY_DATA_CHOOSER(controls.data),
                                tip_blind_source_filter, &args->orig, NULL);
    gwy_data_chooser_set_active_id(GWY_DATA_CHOOSER(controls.data),
                                   &args->source);
    g_signal_connect(controls.data, "changed",
                     G_CALLBACK(data_changed), &args->source);
    gwy_table_attach_hscale(table, row, _("Related _data:"), NULL,
                            GTK_OBJECT(controls.data), GWY_HSCALE_WIDGET);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    label = gtk_label_new(_("Estimated Tip Size"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 4, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    controls.xres = gtk_adjustment_new(args->xres, MIN_RES, MAX_RES, 1, 10, 0);
    gwy_table_attach_hscale(table, row, _("_Width:"), "px", controls.xres, 0);
    g_object_set_data(G_OBJECT(controls.xres), "controls", &controls);
    g_signal_connect(controls.xres, "value-changed",
                     G_CALLBACK(width_changed), &controls);
    row++;

    controls.yres = gtk_adjustment_new(args->yres, MIN_RES, MAX_RES, 1, 10, 0);
    gwy_table_attach_hscale(table, row, _("_Height:"), "px", controls.yres, 0);
    g_object_set_data(G_OBJECT(controls.yres), "controls", &controls);
    g_signal_connect(controls.yres, "value-changed",
                     G_CALLBACK(height_changed), &controls);
    row++;

    controls.same_resolution
        = gtk_check_button_new_with_mnemonic(_("_Same resolution"));
    gtk_table_attach(GTK_TABLE(table), controls.same_resolution,
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.same_resolution),
                                 args->same_resolution);
    g_signal_connect(controls.same_resolution, "toggled",
                     G_CALLBACK(same_resolution_changed), &controls);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    gtk_table_attach(GTK_TABLE(table), gwy_label_new_header(_("Options")),
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    controls.threshold = gtk_adjustment_new(1.0, 0.01, 1000.0, 0.01, 1.0, 0.0);
    controls.threshold_spin
        = gtk_spin_button_new(GTK_ADJUSTMENT(controls.threshold), 0.1, 2);
    gtk_table_attach(GTK_TABLE(table), controls.threshold_spin,
                     2, 3, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    label = gtk_label_new_with_mnemonic(_("Noise suppression t_hreshold:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), controls.threshold_spin);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    unit = gwy_data_field_get_si_unit_z(dfield);
    controls.threshold_unit
        = gwy_combo_box_metric_unit_new(G_CALLBACK(thresh_changed),
                                        &controls,
                                        -12, -3, unit, -9);
    gtk_table_attach(GTK_TABLE(table), controls.threshold_unit,
                     3, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    g_signal_connect(controls.threshold, "value-changed",
                     G_CALLBACK(thresh_changed), &controls);
    sci_entry_set_value(GTK_ADJUSTMENT(controls.threshold),
                        GTK_COMBO_BOX(controls.threshold_unit),
                        args->thresh);
    row++;

    controls.boundaries
                    = gtk_check_button_new_with_mnemonic(_("Use _boundaries"));
    gtk_table_attach(GTK_TABLE(table), controls.boundaries,
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.boundaries),
                                                 args->use_boundaries);
    g_signal_connect(controls.boundaries, "toggled",
                     G_CALLBACK(bound_changed), args);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    gtk_table_attach(GTK_TABLE(table), gwy_label_new_header(_("Stripes")),
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    controls.nstripes = gtk_adjustment_new(args->nstripes,
                                           MIN_STRIPES, MAX_STRIPES, 1, 10, 0);
    gwy_table_attach_hscale(table, row, _("_Split to stripes:"), NULL,
                            controls.nstripes, GWY_HSCALE_CHECK);
    controls.split_to_stripes = gwy_table_hscale_get_check(controls.nstripes);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.split_to_stripes),
                                 !args->split_to_stripes);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.split_to_stripes),
                                 args->split_to_stripes);
    g_signal_connect(controls.split_to_stripes, "toggled",
                     G_CALLBACK(split_to_stripes_changed), &controls);
    g_signal_connect(controls.nstripes, "value-changed",
                     G_CALLBACK(nstripes_changed), &controls);
    row++;

    controls.stripeno = gtk_adjustment_new(1, 1, args->nstripes, 1, 10, 0);
    gwy_table_attach_hscale(table, row, _("_Preview stripe:"), NULL,
                            controls.stripeno, GWY_HSCALE_DEFAULT);
    g_signal_connect(controls.stripeno, "value-changed",
                     G_CALLBACK(stripeno_changed), &controls);
    row++;

    controls.plot_size_graph
        = gtk_check_button_new_with_mnemonic(_("Plot size _graph"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.plot_size_graph),
                                 args->plot_size_graph);
    gtk_table_attach(GTK_TABLE(table), controls.plot_size_graph,
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    g_signal_connect(controls.plot_size_graph, "toggled",
                     G_CALLBACK(plot_size_graph_changed), &controls);
    row++;

    controls.create_images
        = gtk_check_button_new_with_mnemonic(_("Create tip i_mages"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.create_images),
                                 args->create_images);
    gtk_table_attach(GTK_TABLE(table), controls.create_images,
                     0, 4, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    g_signal_connect(controls.create_images, "toggled",
                     G_CALLBACK(create_images_changed), &controls);
    row++;

    controls.tipdone = FALSE;
    controls.in_update = FALSE;
    split_to_stripes_changed(GTK_TOGGLE_BUTTON(controls.split_to_stripes),
                             &controls);
    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:
            tip_blind_dialog_abandon(&controls);
            tip_blind_save_args(gwy_app_settings_get(), args);
            return;
            break;

            case GTK_RESPONSE_OK:
            tip_blind_save_args(gwy_app_settings_get(), args);
            tip_blind_do(&controls, args);
            break;

            case RESPONSE_RESET:
            reset(&controls, args);
            break;

            case RESPONSE_ESTIMATE:
            tip_blind_run(&controls, args, FALSE);
            break;

            case RESPONSE_REFINE:
            tip_blind_run(&controls, args, TRUE);
            break;

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

    gtk_widget_destroy(dialog);
    tip_blind_dialog_abandon(&controls);

    return;
}
Exemple #3
0
static void
prof_dialog(ProfArgs *args,
            GwyContainer *data,
            GwyDataField *dfield,
            gint id)
{
    GtkWidget *hbox, *hbox2, *vbox, *label;
    GwyDataChooser *chooser;
    GtkTable *table;
    GtkDialog *dialog;
    GwyGraph *graph;
    GwyGraphArea *area;
    ProfControls controls;
    gint response;
    gint row;

    controls.args = args;
    controls.psdffield = prof_psdf(dfield, args);
    controls.modfield = prof_sqrt(controls.psdffield);
    controls.hx = gwy_data_field_get_xmeasure(dfield)/(2*G_PI);
    controls.hy = gwy_data_field_get_xmeasure(dfield)/(2*G_PI);

    controls.dialog = gtk_dialog_new_with_buttons(_("Radial PSDF Section"),
                                                  NULL, 0,
                                                  GTK_STOCK_CLEAR,
                                                  RESPONSE_CLEAR,
                                                  GTK_STOCK_CANCEL,
                                                  GTK_RESPONSE_CANCEL,
                                                  GTK_STOCK_OK,
                                                  GTK_RESPONSE_OK,
                                                  NULL);
    dialog = GTK_DIALOG(controls.dialog);
    gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
    gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_OK, FALSE);
    gwy_help_add_to_proc_dialog(GTK_DIALOG(dialog), GWY_HELP_DEFAULT);

    hbox = gtk_hbox_new(FALSE, 2);

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

    controls.mydata = gwy_container_new();
    gwy_container_set_object_by_name(controls.mydata, "/0/data",
                                     controls.modfield);
    g_object_unref(controls.modfield);
    gwy_container_set_string_by_name(controls.mydata, "/0/base/palette",
                                     g_strdup("DFit"));
    gwy_container_set_enum_by_name(controls.mydata, "/0/base/range-type",
                                   GWY_LAYER_BASIC_RANGE_AUTO);
    gwy_app_sync_data_items(data, controls.mydata, id, 0, FALSE,
                            GWY_DATA_ITEM_REAL_SQUARE,
                            0);
    controls.view = create_preview(controls.mydata, 0, PREVIEW_SIZE, FALSE);
    controls.selection = create_vector_layer(GWY_DATA_VIEW(controls.view), 0,
                                             "Point", TRUE);
    gwy_selection_set_max_objects(controls.selection, NLINES);
    g_object_set(gwy_data_view_get_top_layer(GWY_DATA_VIEW(controls.view)),
                 "draw-as-vector", TRUE,
                 NULL);
    g_signal_connect_swapped(controls.selection, "changed",
                             G_CALLBACK(prof_selection_changed), &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, 4);

    controls.gmodel = gwy_graph_model_new();
    g_object_set(controls.gmodel,
                 "title", _("PSDF Section"),
                 "axis-label-bottom", "k",
                 "axis-label-left", "W",
                 NULL);
    init_graph_model_units(controls.gmodel, controls.psdffield);
    controls.line = gwy_data_line_new(1, 1.0, FALSE);

    controls.graph = gwy_graph_new(controls.gmodel);
    graph = GWY_GRAPH(controls.graph);
    gtk_widget_set_size_request(controls.graph, GRAPH_WIDTH, -1);
    gwy_graph_set_axis_visible(graph, GTK_POS_LEFT, FALSE);
    gwy_graph_set_axis_visible(graph, GTK_POS_RIGHT, FALSE);
    gwy_graph_set_axis_visible(graph, GTK_POS_TOP, FALSE);
    gwy_graph_set_axis_visible(graph, GTK_POS_BOTTOM, FALSE);
    gwy_graph_enable_user_input(graph, FALSE);
    area = GWY_GRAPH_AREA(gwy_graph_get_area(graph));
    gwy_graph_area_enable_user_input(area, FALSE);
    gtk_box_pack_start(GTK_BOX(vbox), controls.graph, TRUE, TRUE, 0);

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

    controls.resolution = gtk_adjustment_new(controls.args->resolution,
                                             MIN_RESOLUTION, MAX_RESOLUTION,
                                             1, 10, 0);
    gwy_table_attach_hscale(GTK_WIDGET(table), row, _("_Fix res.:"), NULL,
                            controls.resolution,
                            GWY_HSCALE_CHECK | GWY_HSCALE_SQRT);
    g_signal_connect_swapped(controls.resolution, "value-changed",
                             G_CALLBACK(prof_resolution_changed), &controls);
    controls.fixres = gwy_table_hscale_get_check(controls.resolution);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.fixres),
                                 controls.args->fixres);
    g_signal_connect_swapped(controls.fixres, "toggled",
                             G_CALLBACK(prof_fixres_changed), &controls);
    gwy_table_hscale_set_sensitive(controls.resolution, controls.args->fixres);
    row++;

    controls.separate = gtk_check_button_new_with_mnemonic(_("_Separate curves"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.separate),
                                 args->separate);
    gtk_table_attach(table, controls.separate,
                     0, 3, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    g_signal_connect_swapped(controls.separate, "toggled",
                             G_CALLBACK(prof_separate_changed), &controls);
    row++;

    hbox2 = gtk_hbox_new(FALSE, 6);
    gtk_table_attach(table, hbox2,
                     0, 3, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);

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

    controls.interpolation
        = gwy_enum_combo_box_new(gwy_interpolation_type_get_enum(), -1,
                                 G_CALLBACK(prof_interpolation_changed),
                                 &controls,
                                 controls.args->interpolation, TRUE);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), controls.interpolation);
    gtk_box_pack_end(GTK_BOX(hbox2), controls.interpolation, FALSE, FALSE, 0);
    row++;

    controls.target_hbox = hbox2 = gtk_hbox_new(FALSE, 6);
    gtk_widget_set_sensitive(hbox2, !args->separate);
    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++;

    gtk_widget_show_all(controls.dialog);
    do {
        response = gtk_dialog_run(dialog);
        switch (response) {
            case GTK_RESPONSE_CANCEL:
            case GTK_RESPONSE_DELETE_EVENT:
            gtk_widget_destroy(controls.dialog);
            case GTK_RESPONSE_NONE:
            goto finalize;
            break;

            case GTK_RESPONSE_OK:
            break;

            case RESPONSE_CLEAR:
            gwy_selection_clear(controls.selection);
            break;

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

    gtk_widget_destroy(controls.dialog);
    prof_execute(&controls, data);
finalize:
    g_object_unref(controls.psdffield);
    g_object_unref(controls.mydata);
    g_object_unref(controls.gmodel);
    g_object_unref(controls.line);
}
Exemple #4
0
static void
grain_dist_dialog(GrainDistArgs *args,
                  GwyContainer *data,
                  GwyDataField *dfield,
                  GwyDataField *mfield)
{
    static const GwyEnum quantities_area[] = {
        { N_("_Projected area"),         GWY_GRAIN_VALUE_PROJECTED_AREA,    },
        { N_("Equivalent _square side"), GWY_GRAIN_VALUE_EQUIV_SQUARE_SIDE, },
        { N_("Equivalent disc _radius"), GWY_GRAIN_VALUE_EQUIV_DISC_RADIUS, },
        { N_("S_urface area"),           GWY_GRAIN_VALUE_SURFACE_AREA,      },
    };
    static const GwyEnum quantities_value[] = {
        { N_("Ma_ximum"), GWY_GRAIN_VALUE_MAXIMUM, },
        { N_("M_inimum"), GWY_GRAIN_VALUE_MINIMUM, },
        { N_("_Mean"),    GWY_GRAIN_VALUE_MEAN,    },
        { N_("Me_dian"),  GWY_GRAIN_VALUE_MEDIAN,  },
    };
    static const GwyEnum quantities_boundary[] = {
        { N_("Projected _boundary length"),
            GWY_GRAIN_VALUE_FLAT_BOUNDARY_LENGTH, },
        { N_("Minimum bounding size"),
            GWY_GRAIN_VALUE_MINIMUM_BOUND_SIZE, },
        { N_("Minimum bounding direction"),
            GWY_GRAIN_VALUE_MINIMUM_BOUND_ANGLE, },
        { N_("Maximum bounding size"),
            GWY_GRAIN_VALUE_MAXIMUM_BOUND_SIZE, },
        { N_("Maximum bounding direction"),
            GWY_GRAIN_VALUE_MAXIMUM_BOUND_ANGLE, },
    };
    static const GwyEnum quantities_volume[] = {
        { N_("_Zero basis"),                 GWY_GRAIN_VALUE_VOLUME_0,       },
        { N_("_Grain minimum basis"),        GWY_GRAIN_VALUE_VOLUME_MIN,     },
        { N_("_Laplacian background basis"), GWY_GRAIN_VALUE_VOLUME_LAPLACE, },
    };
    static const GwyEnum modes[] = {
        { N_("_Export raw data"), MODE_RAW,   },
        { N_("Plot _graphs"),     MODE_GRAPH, },
    };

    GrainDistControls controls;
    GtkWidget *dialog;
    GtkTable *table;
    gint row, response;
    GSList *l;

    controls.args = args;

    dialog = gtk_dialog_new_with_buttons(_("Grain Distributions"), NULL, 0,
                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                         NULL);
    controls.ok = gtk_dialog_add_button(GTK_DIALOG(dialog),
                                        GTK_STOCK_OK, GTK_RESPONSE_OK);
    gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);

    /* Output type */
    table = GTK_TABLE(gtk_table_new(2, 2, FALSE));
    gtk_table_set_row_spacings(table, 8);
    gtk_table_set_col_spacings(table, 12);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(table),
                       FALSE, FALSE, 0);

    controls.qlist = append_checkbox_list(table, 0, 0, _("Value"),
                                          NULL,
                                          G_N_ELEMENTS(quantities_value),
                                          quantities_value,
                                          args->selected,
                                          args->bitmask);
    controls.qlist = append_checkbox_list(table, 1, 0, _("Area"),
                                          controls.qlist,
                                          G_N_ELEMENTS(quantities_area),
                                          quantities_area,
                                          args->selected,
                                          args->bitmask);
    controls.qlist = append_checkbox_list(table, 0, 1, _("Boundary"),
                                          controls.qlist,
                                          G_N_ELEMENTS(quantities_boundary),
                                          quantities_boundary,
                                          args->selected,
                                          args->bitmask);
    controls.qlist = append_checkbox_list(table, 1, 1, _("Volume"),
                                          controls.qlist,
                                          G_N_ELEMENTS(quantities_volume),
                                          quantities_volume,
                                          args->selected,
                                          args->bitmask);

    for (l = controls.qlist; l; l = g_slist_next(l))
        g_signal_connect_swapped(l->data, "toggled",
                                 G_CALLBACK(selected_changed_cb), &controls);

    /* Options */
    table = GTK_TABLE(gtk_table_new(4, 4, FALSE));
    gtk_table_set_row_spacings(table, 2);
    gtk_table_set_col_spacings(table, 6);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(table),
                       FALSE, FALSE, 0);
    row = 0;

    controls.mode = gwy_radio_buttons_create(modes, G_N_ELEMENTS(modes),
                                             G_CALLBACK(mode_changed_cb),
                                             &controls,
                                             args->mode);

    gtk_table_attach(table, gwy_label_new_header(_("Options")),
                     0, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    row = gwy_radio_buttons_attach_to_table(controls.mode, table, 4, row);

    controls.resolution = gtk_adjustment_new(args->resolution,
                                             MIN_RESOLUTION, MAX_RESOLUTION,
                                             1, 10, 0);
    gwy_table_attach_hscale(GTK_WIDGET(table), row, _("_Fix res.:"), NULL,
                            controls.resolution, GWY_HSCALE_CHECK);
    controls.fixres = gwy_table_hscale_get_check(controls.resolution);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.fixres),
                                 args->fixres);

    gtk_widget_show_all(dialog);
    grain_dist_dialog_update_sensitivity(&controls, args);

    do {
        response = gtk_dialog_run(GTK_DIALOG(dialog));
        switch (response) {
            case GTK_RESPONSE_CANCEL:
            case GTK_RESPONSE_DELETE_EVENT:
            grain_dist_dialog_update_values(&controls, args);
            gtk_widget_destroy(dialog);
            case GTK_RESPONSE_NONE:
            return;
            break;

            case GTK_RESPONSE_OK:
            break;

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

    grain_dist_dialog_update_values(&controls, args);
    gtk_widget_destroy(dialog);

    grain_dist_run(args, data, dfield, mfield);
}