~kroq-gar78/ubuntu/precise/gnome-control-center/fix-885947

« back to all changes in this revision

Viewing changes to panels/user-accounts/um-crop-area.c

  • Committer: Bazaar Package Importer
  • Author(s): Rodrigo Moya
  • Date: 2011-05-17 10:47:27 UTC
  • mfrom: (0.1.11 experimental) (1.1.45 upstream)
  • Revision ID: james.westby@ubuntu.com-20110517104727-lqel6m8vhfw5jby1
Tags: 1:3.0.1.1-1ubuntu1
* Rebase on Debian, remaining Ubuntu changes:
* debian/control:
  - Build-Depend on hardening-wrapper, dpkg-dev and dh-autoreconf
  - Add dependency on ubuntu-system-service
  - Remove dependency on gnome-icon-theme-symbolic
  - Move dependency on apg, gnome-icon-theme-symbolic and accountsservice to
    be a Recommends: until we get them in main
* debian/rules:
  - Use autoreconf
  - Add binary-post-install rule for gnome-control-center-data
  - Run dh-autoreconf
* debian/gnome-control-center.dirs:
* debian/gnome-control-center.links:
  - Add a link to the control center shell for indicators
* debian/patches/00_disable-nm.patch:
  - Temporary patch to disable building with NetworkManager until we get
    the new one in the archive
* debian/patches/01_git_remove_gettext_calls.patch:
  - Remove calls to AM_GNU_GETTEXT, IT_PROG_INTLTOOL should be enough
* debian/patches/01_git_kill_warning.patch:
  - Kill warning
* debian/patches/50_ubuntu_systemwide_prefs.patch:
  - Ubuntu specific proxy preferences
* debian/patches/51_ubuntu_system_keyboard.patch:
  - Implement the global keyboard spec at https://wiki.ubuntu.com/DefaultKeyboardSettings

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright 2009  Red Hat, Inc,
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 3 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
18
 *
 
19
 * Written by: Matthias Clasen <mclasen@redhat.com>
 
20
 */
 
21
 
 
22
#include "config.h"
 
23
 
 
24
#include <stdlib.h>
 
25
 
 
26
#include <glib.h>
 
27
#include <glib/gi18n.h>
 
28
#include <gtk/gtk.h>
 
29
 
 
30
#include "um-crop-area.h"
 
31
 
 
32
struct _UmCropAreaPrivate {
 
33
        GdkPixbuf *browse_pixbuf;
 
34
        GdkPixbuf *pixbuf;
 
35
        GdkPixbuf *color_shifted;
 
36
        gdouble scale;
 
37
        GdkRectangle image;
 
38
        GdkCursorType current_cursor;
 
39
        GdkRectangle crop;
 
40
        gint active_region;
 
41
        gint last_press_x;
 
42
        gint last_press_y;
 
43
        gint base_width;
 
44
        gint base_height;
 
45
        gdouble aspect;
 
46
};
 
47
 
 
48
G_DEFINE_TYPE (UmCropArea, um_crop_area, GTK_TYPE_DRAWING_AREA);
 
49
 
 
50
static inline guchar
 
51
shift_color_byte (guchar b,
 
52
                  int    shift)
 
53
{
 
54
        return CLAMP(b + shift, 0, 255);
 
55
}
 
56
 
 
57
static void
 
58
shift_colors (GdkPixbuf *pixbuf,
 
59
              gint       red,
 
60
              gint       green,
 
61
              gint       blue,
 
62
              gint       alpha)
 
63
{
 
64
        gint x, y, offset, y_offset, rowstride, width, height;
 
65
        guchar *pixels;
 
66
        gint channels;
 
67
 
 
68
        width = gdk_pixbuf_get_width (pixbuf);
 
69
        height = gdk_pixbuf_get_height (pixbuf);
 
70
        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
 
71
        pixels = gdk_pixbuf_get_pixels (pixbuf);
 
72
        channels = gdk_pixbuf_get_n_channels (pixbuf);
 
73
 
 
74
        for (y = 0; y < height; y++) {
 
75
                y_offset = y * rowstride;
 
76
                for (x = 0; x < width; x++) {
 
77
                        offset = y_offset + x * channels;
 
78
                        if (red != 0)
 
79
                                pixels[offset] = shift_color_byte (pixels[offset], red);
 
80
                        if (green != 0)
 
81
                                pixels[offset + 1] = shift_color_byte (pixels[offset + 1], green);
 
82
                        if (blue != 0)
 
83
                                pixels[offset + 2] = shift_color_byte (pixels[offset + 2], blue);
 
84
                        if (alpha != 0 && channels >= 4)
 
85
                                pixels[offset + 3] = shift_color_byte (pixels[offset + 3], blue);
 
86
                }
 
87
        }
 
88
}
 
89
 
 
90
static void
 
91
update_pixbufs (UmCropArea *area)
 
92
{
 
93
        gint width;
 
94
        gint height;
 
95
        GtkAllocation allocation;
 
96
        gdouble scale;
 
97
        GdkRGBA color;
 
98
        guint32 pixel;
 
99
        gint dest_x, dest_y, dest_width, dest_height;
 
100
        GtkWidget *widget;
 
101
        GtkStyleContext *context;
 
102
 
 
103
        widget = GTK_WIDGET (area);
 
104
        gtk_widget_get_allocation (widget, &allocation);
 
105
        context = gtk_widget_get_style_context (widget);
 
106
 
 
107
        if (area->priv->pixbuf == NULL ||
 
108
            gdk_pixbuf_get_width (area->priv->pixbuf) != allocation.width ||
 
109
            gdk_pixbuf_get_height (area->priv->pixbuf) != allocation.height) {
 
110
                if (area->priv->pixbuf != NULL)
 
111
                        g_object_unref (area->priv->pixbuf);
 
112
                area->priv->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
 
113
                                                     gdk_pixbuf_get_has_alpha (area->priv->browse_pixbuf),
 
114
                                                     8,
 
115
                                                     allocation.width, allocation.height);
 
116
 
 
117
                gtk_style_context_get_background_color (context, gtk_style_context_get_state (context), &color);
 
118
                pixel = (((gint)(color.red * 1.0)) << 16) |
 
119
                        (((gint)(color.green * 1.0)) << 8) |
 
120
                         ((gint)(color.blue * 1.0));
 
121
                gdk_pixbuf_fill (area->priv->pixbuf, pixel);
 
122
 
 
123
                width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
 
124
                height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
 
125
 
 
126
                scale = allocation.height / (gdouble)height;
 
127
                if (scale * width > allocation.width)
 
128
                    scale = allocation.width / (gdouble)width;
 
129
 
 
130
                dest_width = width * scale;
 
131
                dest_height = height * scale;
 
132
                dest_x = (allocation.width - dest_width) / 2;
 
133
                dest_y = (allocation.height - dest_height) / 2,
 
134
 
 
135
                gdk_pixbuf_scale (area->priv->browse_pixbuf,
 
136
                                  area->priv->pixbuf,
 
137
                                  dest_x, dest_y,
 
138
                                  dest_width, dest_height,
 
139
                                  dest_x, dest_y,
 
140
                                  scale, scale,
 
141
                                  GDK_INTERP_BILINEAR);
 
142
 
 
143
                if (area->priv->color_shifted)
 
144
                        g_object_unref (area->priv->color_shifted);
 
145
                area->priv->color_shifted = gdk_pixbuf_copy (area->priv->pixbuf);
 
146
                shift_colors (area->priv->color_shifted, -32, -32, -32, 0);
 
147
 
 
148
                if (area->priv->scale == 0.0) {
 
149
                        area->priv->crop.width = 2 * area->priv->base_width / scale;
 
150
                        area->priv->crop.height = 2 * area->priv->base_height / scale;
 
151
                        area->priv->crop.x = (gdk_pixbuf_get_width (area->priv->browse_pixbuf) - area->priv->crop.width) / 2;
 
152
                        area->priv->crop.y = (gdk_pixbuf_get_height (area->priv->browse_pixbuf) - area->priv->crop.height) / 2;
 
153
                }
 
154
 
 
155
                area->priv->scale = scale;
 
156
                area->priv->image.x = dest_x;
 
157
                area->priv->image.y = dest_y;
 
158
                area->priv->image.width = dest_width;
 
159
                area->priv->image.height = dest_height;
 
160
        }
 
161
}
 
162
 
 
163
static void
 
164
crop_to_widget (UmCropArea    *area,
 
165
                GdkRectangle  *crop)
 
166
{
 
167
        crop->x = area->priv->image.x + area->priv->crop.x * area->priv->scale;
 
168
        crop->y = area->priv->image.y + area->priv->crop.y * area->priv->scale;
 
169
        crop->width = area->priv->crop.width * area->priv->scale;
 
170
        crop->height = area->priv->crop.height * area->priv->scale;
 
171
}
 
172
 
 
173
typedef enum {
 
174
        OUTSIDE,
 
175
        INSIDE,
 
176
        TOP,
 
177
        TOP_LEFT,
 
178
        TOP_RIGHT,
 
179
        BOTTOM,
 
180
        BOTTOM_LEFT,
 
181
        BOTTOM_RIGHT,
 
182
        LEFT,
 
183
        RIGHT
 
184
} Location;
 
185
 
 
186
static gboolean
 
187
um_crop_area_draw (GtkWidget *widget,
 
188
                   cairo_t   *cr)
 
189
{
 
190
        GdkRectangle crop;
 
191
        gint width, height;
 
192
        UmCropArea *uarea = UM_CROP_AREA (widget);
 
193
 
 
194
        if (uarea->priv->browse_pixbuf == NULL)
 
195
                return FALSE;
 
196
 
 
197
        update_pixbufs (uarea);
 
198
 
 
199
        width = gdk_pixbuf_get_width (uarea->priv->pixbuf);
 
200
        height = gdk_pixbuf_get_height (uarea->priv->pixbuf);
 
201
        crop_to_widget (uarea, &crop);
 
202
 
 
203
        gdk_cairo_set_source_pixbuf (cr, uarea->priv->color_shifted, 0, 0);
 
204
        cairo_rectangle (cr, 0, 0, width, crop.y);
 
205
        cairo_rectangle (cr, 0, crop.y, crop.x, crop.height);
 
206
        cairo_rectangle (cr, crop.x + crop.width, crop.y, width - crop.x - crop.width, crop.height);
 
207
        cairo_rectangle (cr, 0, crop.y + crop.height, width, height - crop.y - crop.height);
 
208
        cairo_fill (cr);
 
209
 
 
210
        gdk_cairo_set_source_pixbuf (cr, uarea->priv->pixbuf, 0, 0);
 
211
        cairo_rectangle (cr, crop.x, crop.y, crop.width, crop.height);
 
212
        cairo_fill (cr);
 
213
 
 
214
        if (uarea->priv->active_region != OUTSIDE) {
 
215
                gint x1, x2, y1, y2;
 
216
                cairo_set_source_rgb (cr, 1, 1, 1);
 
217
                cairo_set_line_width (cr, 1.0);
 
218
                x1 = crop.x + crop.width / 3.0;
 
219
                x2 = crop.x + 2 * crop.width / 3.0;
 
220
                y1 = crop.y + crop.height / 3.0;
 
221
                y2 = crop.y + 2 * crop.height / 3.0;
 
222
 
 
223
                cairo_move_to (cr, x1 + 0.5, crop.y);
 
224
                cairo_line_to (cr, x1 + 0.5, crop.y + crop.height);
 
225
 
 
226
                cairo_move_to (cr, x2 + 0.5, crop.y);
 
227
                cairo_line_to (cr, x2 + 0.5, crop.y + crop.height);
 
228
 
 
229
                cairo_move_to (cr, crop.x, y1 + 0.5);
 
230
                cairo_line_to (cr, crop.x + crop.width, y1 + 0.5);
 
231
 
 
232
                cairo_move_to (cr, crop.x, y2 + 0.5);
 
233
                cairo_line_to (cr, crop.x + crop.width, y2 + 0.5);
 
234
                cairo_stroke (cr);
 
235
        }
 
236
 
 
237
        cairo_set_source_rgb (cr,  0, 0, 0);
 
238
        cairo_set_line_width (cr, 1.0);
 
239
        cairo_rectangle (cr,
 
240
                         crop.x + 0.5,
 
241
                         crop.y + 0.5,
 
242
                         crop.width - 1.0,
 
243
                         crop.height - 1.0);
 
244
        cairo_stroke (cr);
 
245
 
 
246
        cairo_set_source_rgb (cr, 1, 1, 1);
 
247
        cairo_set_line_width (cr, 2.0);
 
248
        cairo_rectangle (cr,
 
249
                         crop.x + 2.0,
 
250
                         crop.y + 2.0,
 
251
                         crop.width - 4.0,
 
252
                         crop.height - 4.0);
 
253
        cairo_stroke (cr);
 
254
 
 
255
        return FALSE;
 
256
}
 
257
 
 
258
typedef enum {
 
259
        BELOW,
 
260
        LOWER,
 
261
        BETWEEN,
 
262
        UPPER,
 
263
        ABOVE
 
264
} Range;
 
265
 
 
266
static Range
 
267
find_range (gint x,
 
268
            gint min,
 
269
            gint max)
 
270
{
 
271
        gint tolerance = 12;
 
272
 
 
273
        if (x < min - tolerance)
 
274
                return BELOW;
 
275
        if (x <= min + tolerance)
 
276
                return LOWER;
 
277
        if (x < max - tolerance)
 
278
                return BETWEEN;
 
279
        if (x <= max + tolerance)
 
280
                return UPPER;
 
281
        return ABOVE;
 
282
}
 
283
 
 
284
static Location
 
285
find_location (GdkRectangle *rect,
 
286
               gint          x,
 
287
               gint          y)
 
288
{
 
289
        Range x_range, y_range;
 
290
        Location location[5][5] = {
 
291
                { OUTSIDE, OUTSIDE,     OUTSIDE, OUTSIDE,      OUTSIDE },
 
292
                { OUTSIDE, TOP_LEFT,    TOP,     TOP_RIGHT,    OUTSIDE },
 
293
                { OUTSIDE, LEFT,        INSIDE,  RIGHT,        OUTSIDE },
 
294
                { OUTSIDE, BOTTOM_LEFT, BOTTOM,  BOTTOM_RIGHT, OUTSIDE },
 
295
                { OUTSIDE, OUTSIDE,     OUTSIDE, OUTSIDE,      OUTSIDE }
 
296
        };
 
297
 
 
298
        x_range = find_range (x, rect->x, rect->x + rect->width);
 
299
        y_range = find_range (y, rect->y, rect->y + rect->height);
 
300
 
 
301
        return location[y_range][x_range];
 
302
}
 
303
 
 
304
static void
 
305
update_cursor (UmCropArea *area,
 
306
               gint           x,
 
307
               gint           y)
 
308
{
 
309
        gint cursor_type;
 
310
        GdkRectangle crop;
 
311
        gint region;
 
312
 
 
313
        region = area->priv->active_region;
 
314
        if (region == OUTSIDE) {
 
315
                crop_to_widget (area, &crop);
 
316
                region = find_location (&crop, x, y);
 
317
        }
 
318
 
 
319
        switch (region) {
 
320
        case OUTSIDE:
 
321
                cursor_type = GDK_LEFT_PTR;
 
322
                break;
 
323
        case TOP_LEFT:
 
324
                cursor_type = GDK_TOP_LEFT_CORNER;
 
325
                break;
 
326
        case TOP:
 
327
                cursor_type = GDK_TOP_SIDE;
 
328
                break;
 
329
        case TOP_RIGHT:
 
330
                cursor_type = GDK_TOP_RIGHT_CORNER;
 
331
                break;
 
332
        case LEFT:
 
333
                cursor_type = GDK_LEFT_SIDE;
 
334
                break;
 
335
        case INSIDE:
 
336
                cursor_type = GDK_FLEUR;
 
337
                break;
 
338
        case RIGHT:
 
339
                cursor_type = GDK_RIGHT_SIDE;
 
340
                break;
 
341
        case BOTTOM_LEFT:
 
342
                cursor_type = GDK_BOTTOM_LEFT_CORNER;
 
343
                break;
 
344
        case BOTTOM:
 
345
                cursor_type = GDK_BOTTOM_SIDE;
 
346
                break;
 
347
        case BOTTOM_RIGHT:
 
348
                cursor_type = GDK_BOTTOM_RIGHT_CORNER;
 
349
                break;
 
350
        default:
 
351
                g_assert_not_reached ();
 
352
        }
 
353
 
 
354
        if (cursor_type != area->priv->current_cursor) {
 
355
                GdkCursor *cursor = gdk_cursor_new (cursor_type);
 
356
                gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (area)), cursor);
 
357
                g_object_unref (cursor);
 
358
                area->priv->current_cursor = cursor_type;
 
359
        }
 
360
}
 
361
 
 
362
static int
 
363
eval_radial_line (gdouble center_x, gdouble center_y,
 
364
                  gdouble bounds_x, gdouble bounds_y,
 
365
                  gdouble user_x)
 
366
{
 
367
        gdouble decision_slope;
 
368
        gdouble decision_intercept;
 
369
 
 
370
        decision_slope = (bounds_y - center_y) / (bounds_x - center_x);
 
371
        decision_intercept = -(decision_slope * bounds_x);
 
372
 
 
373
        return (int) (decision_slope * user_x + decision_intercept);
 
374
}
 
375
 
 
376
static gboolean
 
377
um_crop_area_motion_notify_event (GtkWidget      *widget,
 
378
                                  GdkEventMotion *event)
 
379
{
 
380
        UmCropArea *area = UM_CROP_AREA (widget);
 
381
        gint x, y;
 
382
        gint delta_x, delta_y;
 
383
        gint width, height;
 
384
        gint adj_width, adj_height;
 
385
        gint pb_width, pb_height;
 
386
        GdkRectangle damage;
 
387
        gint left, right, top, bottom;
 
388
        gdouble new_width, new_height;
 
389
        gdouble center_x, center_y;
 
390
        gint min_width, min_height;
 
391
 
 
392
        if (area->priv->browse_pixbuf == NULL)
 
393
                return FALSE;
 
394
 
 
395
        update_cursor (area, event->x, event->y);
 
396
 
 
397
        crop_to_widget (area, &damage);
 
398
        gtk_widget_queue_draw_area (widget,
 
399
                                    damage.x - 1, damage.y - 1,
 
400
                                    damage.width + 2, damage.height + 2);
 
401
 
 
402
        pb_width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
 
403
        pb_height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
 
404
 
 
405
        x = (event->x - area->priv->image.x) / area->priv->scale;
 
406
        y = (event->y - area->priv->image.y) / area->priv->scale;
 
407
 
 
408
        delta_x = x - area->priv->last_press_x;
 
409
        delta_y = y - area->priv->last_press_y;
 
410
        area->priv->last_press_x = x;
 
411
        area->priv->last_press_y = y;
 
412
 
 
413
        left = area->priv->crop.x;
 
414
        right = area->priv->crop.x + area->priv->crop.width - 1;
 
415
        top = area->priv->crop.y;
 
416
        bottom = area->priv->crop.y + area->priv->crop.height - 1;
 
417
 
 
418
        center_x = (left + right) / 2.0;
 
419
        center_y = (top + bottom) / 2.0;
 
420
 
 
421
        switch (area->priv->active_region) {
 
422
        case INSIDE:
 
423
                width = right - left + 1;
 
424
                height = bottom - top + 1;
 
425
 
 
426
                left += delta_x;
 
427
                right += delta_x;
 
428
                top += delta_y;
 
429
                bottom += delta_y;
 
430
 
 
431
                if (left < 0)
 
432
                        left = 0;
 
433
                if (top < 0)
 
434
                        top = 0;
 
435
                if (right > pb_width)
 
436
                        right = pb_width;
 
437
                if (bottom > pb_height)
 
438
                        bottom = pb_height;
 
439
 
 
440
                adj_width = right - left + 1;
 
441
                adj_height = bottom - top + 1;
 
442
                if (adj_width != width) {
 
443
                        if (delta_x < 0)
 
444
                                right = left + width - 1;
 
445
                        else
 
446
                                left = right - width + 1;
 
447
                }
 
448
                if (adj_height != height) {
 
449
                        if (delta_y < 0)
 
450
                                bottom = top + height - 1;
 
451
                        else
 
452
                                top = bottom - height + 1;
 
453
                }
 
454
 
 
455
                break;
 
456
 
 
457
        case TOP_LEFT:
 
458
                if (area->priv->aspect < 0) {
 
459
                        top = y;
 
460
                        left = x;
 
461
                }
 
462
                else if (y < eval_radial_line (center_x, center_y, left, top, x)) {
 
463
                        top = y;
 
464
                        new_width = (bottom - top) * area->priv->aspect;
 
465
                        left = right - new_width;
 
466
                }
 
467
                else {
 
468
                        left = x;
 
469
                        new_height = (right - left) / area->priv->aspect;
 
470
                        top = bottom - new_height;
 
471
                }
 
472
                break;
 
473
 
 
474
        case TOP:
 
475
                top = y;
 
476
                if (area->priv->aspect > 0) {
 
477
                        new_width = (bottom - top) * area->priv->aspect;
 
478
                        right = left + new_width;
 
479
                }
 
480
                break;
 
481
 
 
482
        case TOP_RIGHT:
 
483
                if (area->priv->aspect < 0) {
 
484
                        top = y;
 
485
                        right = x;
 
486
                }
 
487
                else if (y < eval_radial_line (center_x, center_y, right, top, x)) {
 
488
                        top = y;
 
489
                        new_width = (bottom - top) * area->priv->aspect;
 
490
                        right = left + new_width;
 
491
                }
 
492
                else {
 
493
                        right = x;
 
494
                        new_height = (right - left) / area->priv->aspect;
 
495
                        top = bottom - new_height;
 
496
                }
 
497
                break;
 
498
 
 
499
        case LEFT:
 
500
                left = x;
 
501
                if (area->priv->aspect > 0) {
 
502
                        new_height = (right - left) / area->priv->aspect;
 
503
                        bottom = top + new_height;
 
504
                }
 
505
                break;
 
506
 
 
507
        case BOTTOM_LEFT:
 
508
                if (area->priv->aspect < 0) {
 
509
                        bottom = y;
 
510
                        left = x;
 
511
                }
 
512
                else if (y < eval_radial_line (center_x, center_y, left, bottom, x)) {
 
513
                        left = x;
 
514
                        new_height = (right - left) / area->priv->aspect;
 
515
                        bottom = top + new_height;
 
516
                }
 
517
                else {
 
518
                        bottom = y;
 
519
                        new_width = (bottom - top) * area->priv->aspect;
 
520
                        left = right - new_width;
 
521
                }
 
522
                break;
 
523
 
 
524
        case RIGHT:
 
525
                right = x;
 
526
                if (area->priv->aspect > 0) {
 
527
                        new_height = (right - left) / area->priv->aspect;
 
528
                        bottom = top + new_height;
 
529
                }
 
530
                break;
 
531
 
 
532
        case BOTTOM_RIGHT:
 
533
                if (area->priv->aspect < 0) {
 
534
                        bottom = y;
 
535
                        right = x;
 
536
                }
 
537
                else if (y < eval_radial_line (center_x, center_y, right, bottom, x)) {
 
538
                        right = x;
 
539
                        new_height = (right - left) / area->priv->aspect;
 
540
                        bottom = top + new_height;
 
541
                }
 
542
                else {
 
543
                        bottom = y;
 
544
                        new_width = (bottom - top) * area->priv->aspect;
 
545
                        right = left + new_width;
 
546
                }
 
547
                break;
 
548
 
 
549
        case BOTTOM:
 
550
                bottom = y;
 
551
                if (area->priv->aspect > 0) {
 
552
                        new_width = (bottom - top) * area->priv->aspect;
 
553
                        right= left + new_width;
 
554
                }
 
555
                break;
 
556
 
 
557
        default:
 
558
                return FALSE;
 
559
        }
 
560
 
 
561
        min_width = area->priv->base_width / area->priv->scale;
 
562
        min_height = area->priv->base_height / area->priv->scale;
 
563
 
 
564
        width = right - left + 1;
 
565
        height = bottom - top + 1;
 
566
        if (area->priv->aspect < 0) {
 
567
                if (left < 0)
 
568
                        left = 0;
 
569
                if (top < 0)
 
570
                        top = 0;
 
571
                if (right > pb_width)
 
572
                        right = pb_width;
 
573
                if (bottom > pb_height)
 
574
                        bottom = pb_height;
 
575
 
 
576
                width = right - left + 1;
 
577
                height = bottom - top + 1;
 
578
 
 
579
                switch (area->priv->active_region) {
 
580
                case LEFT:
 
581
                case TOP_LEFT:
 
582
                case BOTTOM_LEFT:
 
583
                        if (width < min_width)
 
584
                                left = right - min_width;
 
585
                        break;
 
586
                case RIGHT:
 
587
                case TOP_RIGHT:
 
588
                case BOTTOM_RIGHT:
 
589
                        if (width < min_width)
 
590
                                right = left + min_width;
 
591
                        break;
 
592
 
 
593
                default: ;
 
594
                }
 
595
 
 
596
                switch (area->priv->active_region) {
 
597
                case TOP:
 
598
                case TOP_LEFT:
 
599
                case TOP_RIGHT:
 
600
                        if (height < min_height)
 
601
                                top = bottom - min_height;
 
602
                        break;
 
603
                case BOTTOM:
 
604
                case BOTTOM_LEFT:
 
605
                case BOTTOM_RIGHT:
 
606
                        if (height < min_height)
 
607
                                bottom = top + min_height;
 
608
                        break;
 
609
 
 
610
                default: ;
 
611
                }
 
612
        }
 
613
        else {
 
614
                if (left < 0 || top < 0 ||
 
615
                    right > pb_width || bottom > pb_height ||
 
616
                    width < min_width || height < min_height) {
 
617
                        left = area->priv->crop.x;
 
618
                        right = area->priv->crop.x + area->priv->crop.width - 1;
 
619
                        top = area->priv->crop.y;
 
620
                        bottom = area->priv->crop.y + area->priv->crop.height - 1;
 
621
                }
 
622
        }
 
623
 
 
624
        area->priv->crop.x = left;
 
625
        area->priv->crop.y = top;
 
626
        area->priv->crop.width = right - left + 1;
 
627
        area->priv->crop.height = bottom - top + 1;
 
628
 
 
629
        crop_to_widget (area, &damage);
 
630
        gtk_widget_queue_draw_area (widget,
 
631
                                    damage.x - 1, damage.y - 1,
 
632
                                    damage.width + 2, damage.height + 2);
 
633
 
 
634
        return FALSE;
 
635
}
 
636
 
 
637
static gboolean
 
638
um_crop_area_button_press_event (GtkWidget      *widget,
 
639
                                 GdkEventButton *event)
 
640
{
 
641
        UmCropArea *area = UM_CROP_AREA (widget);
 
642
        GdkRectangle crop;
 
643
 
 
644
        if (area->priv->browse_pixbuf == NULL)
 
645
                return FALSE;
 
646
 
 
647
        crop_to_widget (area, &crop);
 
648
 
 
649
        area->priv->last_press_x = (event->x - area->priv->image.x) / area->priv->scale;
 
650
        area->priv->last_press_y = (event->y - area->priv->image.y) / area->priv->scale;
 
651
        area->priv->active_region = find_location (&crop, event->x, event->y);
 
652
 
 
653
        gtk_widget_queue_draw_area (widget,
 
654
                                    crop.x - 1, crop.y - 1,
 
655
                                    crop.width + 2, crop.height + 2);
 
656
 
 
657
        return FALSE;
 
658
}
 
659
 
 
660
static gboolean
 
661
um_crop_area_button_release_event (GtkWidget      *widget,
 
662
                                   GdkEventButton *event)
 
663
{
 
664
        UmCropArea *area = UM_CROP_AREA (widget);
 
665
        GdkRectangle crop;
 
666
 
 
667
        if (area->priv->browse_pixbuf == NULL)
 
668
                return FALSE;
 
669
 
 
670
        crop_to_widget (area, &crop);
 
671
 
 
672
        area->priv->last_press_x = -1;
 
673
        area->priv->last_press_y = -1;
 
674
        area->priv->active_region = OUTSIDE;
 
675
 
 
676
        gtk_widget_queue_draw_area (widget,
 
677
                                    crop.x - 1, crop.y - 1,
 
678
                                    crop.width + 2, crop.height + 2);
 
679
 
 
680
        return FALSE;
 
681
}
 
682
 
 
683
static void
 
684
um_crop_area_finalize (GObject *object)
 
685
{
 
686
        UmCropArea *area = UM_CROP_AREA (object);
 
687
 
 
688
        if (area->priv->browse_pixbuf) {
 
689
                g_object_unref (area->priv->browse_pixbuf);
 
690
                area->priv->browse_pixbuf = NULL;
 
691
        }
 
692
        if (area->priv->pixbuf) {
 
693
                g_object_unref (area->priv->pixbuf);
 
694
                area->priv->pixbuf = NULL;
 
695
        }
 
696
        if (area->priv->color_shifted) {
 
697
                g_object_unref (area->priv->color_shifted);
 
698
                area->priv->color_shifted = NULL;
 
699
        }
 
700
}
 
701
 
 
702
static void
 
703
um_crop_area_class_init (UmCropAreaClass *klass)
 
704
{
 
705
        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
706
        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
707
 
 
708
        object_class->finalize = um_crop_area_finalize;
 
709
        widget_class->draw = um_crop_area_draw;
 
710
        widget_class->button_press_event = um_crop_area_button_press_event;
 
711
        widget_class->button_release_event = um_crop_area_button_release_event;
 
712
        widget_class->motion_notify_event = um_crop_area_motion_notify_event;
 
713
 
 
714
        g_type_class_add_private (klass, sizeof (UmCropAreaPrivate));
 
715
}
 
716
 
 
717
static void
 
718
um_crop_area_init (UmCropArea *area)
 
719
{
 
720
        area->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((area), UM_TYPE_CROP_AREA,
 
721
                                                   UmCropAreaPrivate));
 
722
 
 
723
        gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_MASK |
 
724
                               GDK_BUTTON_PRESS_MASK |
 
725
                               GDK_BUTTON_RELEASE_MASK);
 
726
 
 
727
        area->priv->scale = 0.0;
 
728
        area->priv->image.x = 0;
 
729
        area->priv->image.y = 0;
 
730
        area->priv->image.width = 0;
 
731
        area->priv->image.height = 0;
 
732
        area->priv->active_region = OUTSIDE;
 
733
        area->priv->base_width = 48;
 
734
        area->priv->base_height = 48;
 
735
        area->priv->aspect = 1;
 
736
}
 
737
 
 
738
GtkWidget *
 
739
um_crop_area_new (void)
 
740
{
 
741
        return g_object_new (UM_TYPE_CROP_AREA, NULL);
 
742
}
 
743
 
 
744
GdkPixbuf *
 
745
um_crop_area_get_picture (UmCropArea *area)
 
746
{
 
747
        gint width, height;
 
748
 
 
749
        width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
 
750
        height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
 
751
        width = MIN (area->priv->crop.width, width - area->priv->crop.x);
 
752
        height = MIN (area->priv->crop.height, height - area->priv->crop.y);
 
753
 
 
754
        return gdk_pixbuf_new_subpixbuf (area->priv->browse_pixbuf,
 
755
                                         area->priv->crop.x,
 
756
                                         area->priv->crop.y,
 
757
                                         width, height);
 
758
}
 
759
 
 
760
void
 
761
um_crop_area_set_picture (UmCropArea *area,
 
762
                          GdkPixbuf  *pixbuf)
 
763
{
 
764
        int width;
 
765
        int height;
 
766
 
 
767
        if (area->priv->browse_pixbuf) {
 
768
                g_object_unref (area->priv->browse_pixbuf);
 
769
                area->priv->browse_pixbuf = NULL;
 
770
        }
 
771
        if (pixbuf) {
 
772
                area->priv->browse_pixbuf = g_object_ref (pixbuf);
 
773
                width = gdk_pixbuf_get_width (pixbuf);
 
774
                height = gdk_pixbuf_get_height (pixbuf);
 
775
        } else {
 
776
                width = 0;
 
777
                height = 0;
 
778
        }
 
779
 
 
780
        area->priv->crop.width = 2 * area->priv->base_width;
 
781
        area->priv->crop.height = 2 * area->priv->base_height;
 
782
        area->priv->crop.x = (width - area->priv->crop.width) / 2;
 
783
        area->priv->crop.y = (height - area->priv->crop.height) / 2;
 
784
 
 
785
        area->priv->scale = 0.0;
 
786
        area->priv->image.x = 0;
 
787
        area->priv->image.y = 0;
 
788
        area->priv->image.width = 0;
 
789
        area->priv->image.height = 0;
 
790
 
 
791
        gtk_widget_queue_draw (GTK_WIDGET (area));
 
792
}
 
793
 
 
794
void
 
795
um_crop_area_set_min_size (UmCropArea *area,
 
796
                           gint        width,
 
797
                           gint        height)
 
798
{
 
799
        area->priv->base_width = width;
 
800
        area->priv->base_height = height;
 
801
 
 
802
        if (area->priv->aspect > 0) {
 
803
                area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
 
804
        }
 
805
}
 
806
 
 
807
void
 
808
um_crop_area_set_constrain_aspect (UmCropArea *area,
 
809
                                   gboolean    constrain)
 
810
{
 
811
        if (constrain) {
 
812
                area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
 
813
        }
 
814
        else {
 
815
                area->priv->aspect = -1;
 
816
        }
 
817
}
 
818