~twright-tdw/simple-scan/toolbar-improvements

« back to all changes in this revision

Viewing changes to src/ui.c

  • Committer: robert.ancell at gmail
  • Date: 2009-11-13 07:15:15 UTC
  • Revision ID: robert.ancell@gmail.com-20091113071515-dwpsirvyv9rn6um0
Initial simple-scan release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <string.h>
 
2
#include <gtk/gtk.h>
 
3
#include <gconf/gconf-client.h>
 
4
 
 
5
#include "ui.h"
 
6
 
 
7
 
 
8
enum {
 
9
    RENDER_PREVIEW,
 
10
    START_SCAN,
 
11
    STOP_SCAN,
 
12
    SAVE,
 
13
    PRINT,
 
14
    LAST_SIGNAL
 
15
};
 
16
static guint signals[LAST_SIGNAL] = { 0, };
 
17
 
 
18
 
 
19
struct SimpleScanPrivate
 
20
{
 
21
    GConfClient *client;
 
22
 
 
23
    GtkWidget *window;
 
24
    GtkWidget *scan_label;
 
25
    GtkWidget *actions_box;
 
26
    GtkWidget *device_combo, *mode_combo;
 
27
    GtkWidget *preview_area;
 
28
 
 
29
    gboolean scanning;
 
30
    Orientation orientation;
 
31
};
 
32
 
 
33
G_DEFINE_TYPE (SimpleScan, ui, G_TYPE_OBJECT);
 
34
 
 
35
 
 
36
static gboolean
 
37
find_scan_device (SimpleScan *ui, const char *device, GtkTreeIter *iter)
 
38
{
 
39
    GtkTreeModel *model;
 
40
    gboolean have_iter = FALSE;
 
41
 
 
42
    model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->priv->device_combo));
 
43
    if (gtk_tree_model_get_iter_first (model, iter)) {
 
44
        do {
 
45
            gchar *d;
 
46
            gtk_tree_model_get (model, iter, 0, &d, -1);
 
47
            if (strcmp (d, device) == 0)
 
48
                have_iter = TRUE;
 
49
            g_free (d);
 
50
        } while (!have_iter && gtk_tree_model_iter_next (model, iter));
 
51
    }
 
52
    
 
53
    return have_iter;
 
54
}
 
55
 
 
56
 
 
57
static gchar *
 
58
get_selected_device (SimpleScan *ui)
 
59
{
 
60
    GtkTreeIter iter;
 
61
 
 
62
    if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (ui->priv->device_combo), &iter)) {
 
63
        GtkTreeModel *model;
 
64
        gchar *device;
 
65
 
 
66
        model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->priv->device_combo));
 
67
        gtk_tree_model_get (model, &iter, 0, &device, -1);
 
68
        return device;
 
69
    }
 
70
 
 
71
    return NULL;
 
72
}
 
73
 
 
74
 
 
75
void
 
76
ui_mark_devices_undetected (SimpleScan *ui)
 
77
{
 
78
    GtkTreeModel *model;
 
79
    GtkTreeIter iter;
 
80
    
 
81
    model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->priv->device_combo));
 
82
    if (gtk_tree_model_get_iter_first (model, &iter)) {
 
83
        do {
 
84
            gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, FALSE, -1);
 
85
        } while (gtk_tree_model_iter_next (model, &iter));
 
86
    }
 
87
}
 
88
 
 
89
 
 
90
void
 
91
ui_add_scan_device (SimpleScan *ui, const gchar *device, const gchar *label)
 
92
{
 
93
    GtkTreeIter iter;
 
94
    GtkTreeModel *model;
 
95
 
 
96
    model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->priv->device_combo));
 
97
    
 
98
    if (!find_scan_device (ui, device, &iter)) {
 
99
        gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 
100
        gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, device, -1);
 
101
    }
 
102
 
 
103
    gtk_list_store_set (GTK_LIST_STORE (model), &iter, 1, label, 2, TRUE, -1);
 
104
    
 
105
    /* Select this device if none selected */
 
106
    if (gtk_combo_box_get_active (GTK_COMBO_BOX (ui->priv->device_combo)) == -1)
 
107
        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (ui->priv->device_combo), &iter);
 
108
}
 
109
 
 
110
 
 
111
static void
 
112
get_document_hint (SimpleScan *ui)
 
113
{
 
114
    GtkTreeIter iter;
 
115
 
 
116
    if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (ui->priv->mode_combo), &iter)) {
 
117
        GtkTreeModel *model;
 
118
        gchar *mode;
 
119
 
 
120
        model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->priv->mode_combo));
 
121
        gtk_tree_model_get (model, &iter, 0, &mode, -1);
 
122
        g_free (mode);
 
123
    }
 
124
}
 
125
 
 
126
 
 
127
G_MODULE_EXPORT
 
128
gboolean
 
129
preview_area_expose_event_cb (GtkWidget *widget, GdkEventExpose *event, SimpleScan *ui)
 
130
{
 
131
    cairo_t *context;
 
132
    double width, height;
 
133
    
 
134
    context = gdk_cairo_create (widget->window);
 
135
    
 
136
    width = widget->allocation.width;
 
137
    height = widget->allocation.height;
 
138
    g_signal_emit (G_OBJECT (ui), signals[RENDER_PREVIEW], 0, context, width, height);
 
139
 
 
140
    cairo_destroy (context);
 
141
 
 
142
    return FALSE;
 
143
}
 
144
 
 
145
 
 
146
G_MODULE_EXPORT
 
147
void
 
148
scan_button_clicked_cb (GtkWidget *widget, SimpleScan *ui)
 
149
{
 
150
    if (ui->priv->scanning) {
 
151
        g_signal_emit (G_OBJECT (ui), signals[STOP_SCAN], 0);
 
152
    } else {
 
153
        gchar *device;
 
154
        device = get_selected_device (ui);
 
155
        if (device) {
 
156
            g_signal_emit (G_OBJECT (ui), signals[START_SCAN], 0, device);
 
157
            g_free (device);
 
158
        }
 
159
    }
 
160
}
 
161
 
 
162
 
 
163
G_MODULE_EXPORT
 
164
void
 
165
rotate_button_clicked_cb (GtkWidget *widget, SimpleScan *ui)
 
166
{
 
167
    ui->priv->orientation++;
 
168
    if (ui->priv->orientation > RIGHT_TO_LEFT)
 
169
        ui->priv->orientation = TOP_TO_BOTTOM;
 
170
    ui_redraw_preview (ui);
 
171
}
 
172
 
 
173
 
 
174
G_MODULE_EXPORT
 
175
void
 
176
save_file_button_clicked_cb (GtkWidget *widget, SimpleScan *ui)
 
177
{
 
178
    GtkWidget *dialog;
 
179
    gint response;
 
180
 
 
181
    dialog = gtk_file_chooser_dialog_new ("Save As...", GTK_WINDOW (ui->priv->window),
 
182
                                          GTK_FILE_CHOOSER_ACTION_SAVE,
 
183
                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
184
                                          GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
 
185
                                          NULL);
 
186
    gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
 
187
    gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
 
188
    gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "Scanned Document.pdf");
 
189
    
 
190
    response = gtk_dialog_run (GTK_DIALOG (dialog));
 
191
    if (response == GTK_RESPONSE_ACCEPT) {
 
192
        gchar *uri;
 
193
        
 
194
        uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
 
195
        g_signal_emit (G_OBJECT (ui), signals[SAVE], 0, uri);
 
196
 
 
197
        g_free (uri);
 
198
    }
 
199
    gtk_widget_destroy (dialog);
 
200
}
 
201
 
 
202
 
 
203
static void
 
204
draw_page (GtkPrintOperation *operation,
 
205
           GtkPrintContext   *print_context,
 
206
           gint               page_number,
 
207
           SimpleScan                *ui)
 
208
{
 
209
    cairo_t *context;
 
210
 
 
211
    context = gtk_print_context_get_cairo_context (print_context);
 
212
 
 
213
    g_signal_emit (G_OBJECT (ui), signals[PRINT], 0, context);
 
214
 
 
215
    //For some reason can't destroy until job complete
 
216
    //cairo_destroy (context);
 
217
}
 
218
 
 
219
 
 
220
G_MODULE_EXPORT
 
221
void
 
222
print_button_clicked_cb (GtkWidget *widget, SimpleScan *ui)
 
223
{
 
224
    GtkPrintOperation *print;
 
225
    GtkPrintOperationResult result;
 
226
    GError *error = NULL;
 
227
    
 
228
    print = gtk_print_operation_new ();
 
229
    gtk_print_operation_set_n_pages (print, 1);
 
230
    gtk_print_operation_set_use_full_page (print, TRUE);
 
231
    // FIXME: Auto portrait, landscape
 
232
    g_signal_connect (print, "draw-page", G_CALLBACK (draw_page), ui);
 
233
 
 
234
    result = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
 
235
                                      GTK_WINDOW (ui->priv->window), &error);
 
236
 
 
237
    g_object_unref (print);
 
238
}
 
239
 
 
240
 
 
241
static void
 
242
load_device_cache (SimpleScan *ui)
 
243
{
 
244
    gchar *filename;
 
245
    GKeyFile *key_file;
 
246
    gboolean result;
 
247
    GError *error = NULL;
 
248
    
 
249
    filename = g_build_filename (g_get_user_cache_dir (), "simple-scan", "device_cache", NULL);
 
250
    
 
251
    key_file = g_key_file_new ();
 
252
    result = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error);
 
253
    if (error) {
 
254
        g_warning ("Error loading device cache file: %s", error->message);
 
255
        g_error_free (error);
 
256
        error = NULL;
 
257
    }
 
258
    if (result) {
 
259
        gchar **groups, **group_iter;
 
260
 
 
261
        groups = g_key_file_get_groups (key_file, NULL);
 
262
        for (group_iter = groups; *group_iter; group_iter++) {
 
263
            gchar *label, *device;
 
264
 
 
265
            label = *group_iter;
 
266
            device = g_key_file_get_value (key_file, label, "device", &error);
 
267
            if (error) {
 
268
                g_warning ("Error getting device name for label '%s': %s", label, error->message);
 
269
                g_error_free (error);
 
270
                error = NULL;
 
271
            }
 
272
            
 
273
            if (device)
 
274
                ui_add_scan_device (ui, device, label);
 
275
 
 
276
            g_free (device);
 
277
        }
 
278
 
 
279
        g_strfreev (groups);
 
280
    }
 
281
 
 
282
    g_free (filename);
 
283
    g_key_file_free (key_file);
 
284
}
 
285
 
 
286
 
 
287
static void
 
288
save_device_cache (SimpleScan *ui)
 
289
{
 
290
    GtkTreeModel *model;
 
291
    GtkTreeIter iter;
 
292
 
 
293
    g_debug ("Saving device cache");
 
294
 
 
295
    model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->priv->device_combo));
 
296
    if (gtk_tree_model_get_iter_first (model, &iter)) {
 
297
        GKeyFile *key_file;
 
298
        gchar *data;
 
299
        gsize data_length;
 
300
        GError *error = NULL;
 
301
 
 
302
        key_file = g_key_file_new ();
 
303
        do {
 
304
            gchar *name, *label;
 
305
            gboolean detected;
 
306
            
 
307
            gtk_tree_model_get (model, &iter, 0, &name, 1, &label, 2, &detected, -1);
 
308
            
 
309
            if (detected) {
 
310
                g_debug ("Storing device '%s' in cache", name);
 
311
                g_key_file_set_value (key_file, label, "device", name);
 
312
            }
 
313
 
 
314
            g_free (name);
 
315
            g_free (label);
 
316
        } while (gtk_tree_model_iter_next (model, &iter));
 
317
        
 
318
        data = g_key_file_to_data (key_file, &data_length, &error);
 
319
        if (data) {
 
320
            gchar *dir, *filename;
 
321
            GFile *file;
 
322
            GFileOutputStream *stream;
 
323
            GError *error = NULL;
 
324
 
 
325
            dir = g_build_filename (g_get_user_cache_dir (), "simple-scan", NULL);
 
326
            g_mkdir_with_parents (dir, 0700);
 
327
            filename = g_build_filename (dir, "device_cache", NULL);
 
328
 
 
329
            file = g_file_new_for_path (filename);
 
330
            stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
 
331
            if (error) {
 
332
                g_warning ("Error writing device cache: %s", error->message);
 
333
                g_error_free (error);
 
334
                error = NULL;
 
335
            }
 
336
            if (stream) {
 
337
                g_output_stream_write_all (G_OUTPUT_STREAM (stream), data, data_length, NULL, NULL, &error);
 
338
                if (error) {
 
339
                    g_warning ("Error writing device cache: %s", error->message);
 
340
                    g_error_free (error);
 
341
                    error = NULL;
 
342
                }
 
343
                g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, NULL);
 
344
            }
 
345
            g_free (data);
 
346
 
 
347
            g_free (filename);
 
348
            g_free (dir);        
 
349
        }
 
350
 
 
351
        g_key_file_free (key_file);
 
352
    }
 
353
}
 
354
 
 
355
 
 
356
G_MODULE_EXPORT
 
357
gboolean
 
358
window_delete_event_cb (GtkWidget *widget, GdkEvent *event, SimpleScan *ui)
 
359
{
 
360
    char *device;
 
361
    save_device_cache (ui);
 
362
    device = get_selected_device (ui);
 
363
    if (device) {
 
364
        gconf_client_set_string(ui->priv->client, "/apps/simple-scan/selected_device", device, NULL);
 
365
        g_free (device);
 
366
    }
 
367
    gtk_main_quit ();
 
368
    return TRUE;
 
369
}
 
370
 
 
371
 
 
372
static gboolean
 
373
ui_load (SimpleScan *ui)
 
374
{
 
375
    GtkBuilder *builder;
 
376
    GError *error = NULL;
 
377
    GtkCellRenderer *renderer;
 
378
    GtkTreeIter iter;
 
379
    gchar *device;
 
380
 
 
381
    builder = gtk_builder_new ();
 
382
    gtk_builder_add_from_file (builder, UI_DIR "simple-scan.ui", &error);
 
383
    if (error) {
 
384
        g_critical ("Unable to load UI: %s\n", error->message);
 
385
        return FALSE;
 
386
    }
 
387
    gtk_builder_connect_signals (builder, ui);
 
388
 
 
389
    ui->priv->window = GTK_WIDGET (gtk_builder_get_object (builder, "simple_scan_window"));
 
390
    ui->priv->scan_label = GTK_WIDGET (gtk_builder_get_object (builder, "scan_label"));
 
391
    ui->priv->actions_box = GTK_WIDGET (gtk_builder_get_object (builder, "actions_box"));
 
392
    ui->priv->device_combo = GTK_WIDGET (gtk_builder_get_object (builder, "device_combo"));
 
393
    ui->priv->mode_combo = GTK_WIDGET (gtk_builder_get_object (builder, "mode_combo"));
 
394
    ui->priv->preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "preview_area"));
 
395
 
 
396
    renderer = gtk_cell_renderer_text_new();
 
397
    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ui->priv->device_combo), renderer, TRUE);
 
398
    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (ui->priv->device_combo), renderer, "text", 1);
 
399
 
 
400
    renderer = gtk_cell_renderer_text_new();
 
401
    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ui->priv->mode_combo), renderer, TRUE);
 
402
    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (ui->priv->mode_combo), renderer, "text", 1);
 
403
    gtk_combo_box_set_active (GTK_COMBO_BOX (ui->priv->mode_combo), 0);
 
404
 
 
405
    /* Load previously detected scanners and select the last used one */
 
406
    load_device_cache (ui);
 
407
    device = gconf_client_get_string(ui->priv->client, "/apps/simple-scan/selected_device", NULL);
 
408
    if (device && find_scan_device (ui, device, &iter)) {
 
409
        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (ui->priv->device_combo), &iter);
 
410
    }
 
411
 
 
412
    return TRUE;
 
413
}
 
414
 
 
415
 
 
416
SimpleScan *
 
417
ui_new ()
 
418
{
 
419
    return g_object_new (SIMPLE_SCAN_TYPE, NULL);
 
420
}
 
421
 
 
422
 
 
423
void
 
424
ui_set_scanning (SimpleScan *ui, gboolean scanning)
 
425
{
 
426
    ui->priv->scanning = scanning;
 
427
    if (ui->priv->scanning)
 
428
        gtk_label_set_label (GTK_LABEL (ui->priv->scan_label), "_Cancel");
 
429
    else
 
430
        gtk_label_set_label (GTK_LABEL (ui->priv->scan_label), "_Scan");
 
431
}
 
432
 
 
433
 
 
434
void
 
435
ui_set_have_scan (SimpleScan *ui, gboolean have_scan)
 
436
{
 
437
    gtk_widget_set_sensitive (ui->priv->actions_box, have_scan);
 
438
}
 
439
 
 
440
 
 
441
Orientation ui_get_orientation (SimpleScan *ui)
 
442
{
 
443
    return ui->priv->orientation;
 
444
}
 
445
 
 
446
 
 
447
void
 
448
ui_redraw_preview (SimpleScan *ui)
 
449
{
 
450
    gtk_widget_queue_draw (ui->priv->preview_area);    
 
451
}
 
452
 
 
453
 
 
454
static void
 
455
g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE (GClosure     *closure,
 
456
                                                     GValue       *return_value G_GNUC_UNUSED,
 
457
                                                     guint         n_param_values,
 
458
                                                     const GValue *param_values,
 
459
                                                     gpointer      invocation_hint G_GNUC_UNUSED,
 
460
                                                     gpointer      marshal_data)
 
461
{
 
462
    typedef void (*GMarshalFunc_VOID__POINTER_DOUBLE_DOUBLE) (gpointer     data1,
 
463
                                                              gpointer     arg_1,
 
464
                                                              gdouble      arg_2,
 
465
                                                              gdouble      arg_3,
 
466
                                                              gpointer     data2);
 
467
    register GMarshalFunc_VOID__POINTER_DOUBLE_DOUBLE callback;
 
468
    register GCClosure *cc = (GCClosure*) closure;
 
469
    register gpointer data1, data2;
 
470
    
 
471
    g_return_if_fail (n_param_values == 4);
 
472
    
 
473
    if (G_CCLOSURE_SWAP_DATA (closure))
 
474
    {
 
475
        data1 = closure->data;
 
476
        data2 = g_value_peek_pointer (param_values + 0);
 
477
    }
 
478
    else
 
479
    {
 
480
        data1 = g_value_peek_pointer (param_values + 0);
 
481
        data2 = closure->data;
 
482
    }
 
483
    callback = (GMarshalFunc_VOID__POINTER_DOUBLE_DOUBLE) (marshal_data ? marshal_data : cc->callback);
 
484
    
 
485
    callback (data1,
 
486
              g_value_get_pointer (param_values + 1),
 
487
              g_value_get_double (param_values + 2),
 
488
              g_value_get_double (param_values + 3),
 
489
              data2);
 
490
}
 
491
 
 
492
 
 
493
static void
 
494
ui_class_init (SimpleScanClass *klass)
 
495
{
 
496
    signals[RENDER_PREVIEW] =
 
497
        g_signal_new ("render-preview",
 
498
                      G_TYPE_FROM_CLASS (klass),
 
499
                      G_SIGNAL_RUN_LAST,
 
500
                      G_STRUCT_OFFSET (SimpleScanClass, render_preview),
 
501
                      NULL, NULL,
 
502
                      g_cclosure_user_marshal_VOID__POINTER_DOUBLE_DOUBLE,
 
503
                      G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
 
504
    signals[START_SCAN] =
 
505
        g_signal_new ("start-scan",
 
506
                      G_TYPE_FROM_CLASS (klass),
 
507
                      G_SIGNAL_RUN_LAST,
 
508
                      G_STRUCT_OFFSET (SimpleScanClass, start_scan),
 
509
                      NULL, NULL,
 
510
                      g_cclosure_marshal_VOID__INT,
 
511
                      G_TYPE_NONE, 1, G_TYPE_INT);
 
512
    signals[STOP_SCAN] =
 
513
        g_signal_new ("stop-scan",
 
514
                      G_TYPE_FROM_CLASS (klass),
 
515
                      G_SIGNAL_RUN_LAST,
 
516
                      G_STRUCT_OFFSET (SimpleScanClass, stop_scan),
 
517
                      NULL, NULL,
 
518
                      g_cclosure_marshal_VOID__VOID,
 
519
                      G_TYPE_NONE, 0);
 
520
    signals[SAVE] =
 
521
        g_signal_new ("save",
 
522
                      G_TYPE_FROM_CLASS (klass),
 
523
                      G_SIGNAL_RUN_LAST,
 
524
                      G_STRUCT_OFFSET (SimpleScanClass, save),
 
525
                      NULL, NULL,
 
526
                      g_cclosure_marshal_VOID__POINTER,
 
527
                      G_TYPE_NONE, 1, G_TYPE_POINTER);
 
528
    signals[PRINT] =
 
529
        g_signal_new ("print",
 
530
                      G_TYPE_FROM_CLASS (klass),
 
531
                      G_SIGNAL_RUN_LAST,
 
532
                      G_STRUCT_OFFSET (SimpleScanClass, print),
 
533
                      NULL, NULL,
 
534
                      g_cclosure_marshal_VOID__POINTER,
 
535
                      G_TYPE_NONE, 1, G_TYPE_POINTER);
 
536
 
 
537
    g_type_class_add_private (klass, sizeof (SimpleScanPrivate));
 
538
}
 
539
 
 
540
 
 
541
static void
 
542
ui_init (SimpleScan *ui)
 
543
{
 
544
    ui->priv = G_TYPE_INSTANCE_GET_PRIVATE (ui, SIMPLE_SCAN_TYPE, SimpleScanPrivate);
 
545
 
 
546
    ui->priv->client = gconf_client_get_default();
 
547
    gconf_client_add_dir(ui->priv->client, "/apps/simple-scan", GCONF_CLIENT_PRELOAD_NONE, NULL);
 
548
    
 
549
    ui->priv->scanning = FALSE;
 
550
    ui->priv->orientation = TOP_TO_BOTTOM;
 
551
 
 
552
    ui_load (ui);
 
553
}