gtkquartz.c 11.4 KB
Newer Older
Anders Carlsson's avatar
Anders Carlsson committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* gtkquartz.c: Utility functions used by the Quartz port
 *
 * Copyright (C) 2006 Imendio AB
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Anders Carlsson's avatar
Anders Carlsson committed
17 18
 */

19
#include "config.h"
Anders Carlsson's avatar
Anders Carlsson committed
20 21

#include "gtkquartz.h"
22
#include "gtkselectionprivate.h"
23
#include <gdk/quartz/gdkquartz.h>
Anders Carlsson's avatar
Anders Carlsson committed
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

static gboolean
_cairo_surface_extents (cairo_surface_t *surface,
                            GdkRectangle *extents)
{
  double x1, x2, y1, y2;
  cairo_t *cr;

  g_return_val_if_fail (surface != NULL, FALSE);
  g_return_val_if_fail (extents != NULL, FALSE);

  cr = cairo_create (surface);
  cairo_clip_extents (cr, &x1, &y1, &x2, &y2);

  x1 = floor (x1);
  y1 = floor (y1);
  x2 = ceil (x2);
  y2 = ceil (y2);
  x2 -= x1;
  y2 -= y1;

  if (x1 < G_MININT || x1 > G_MAXINT ||
      y1 < G_MININT || y1 > G_MAXINT ||
      x2 > G_MAXINT || y2 > G_MAXINT)
    {
      extents->x = extents->y = extents->width = extents->height = 0;
      return FALSE;
    }

  extents->x = x1;
  extents->y = y1;
  extents->width = x2;
  extents->height = y2;

  return TRUE;
}

static void
_data_provider_release_cairo_surface (void* info, const void* data, size_t size)
{
  cairo_surface_destroy ((cairo_surface_t *)info);
}

/* Returns a new NSImage or %NULL in case of an error.
 * The device scale factor will be transfered to the NSImage (hidpi)
 */
NSImage *
_gtk_quartz_create_image_from_surface (cairo_surface_t *surface)
{
  CGColorSpaceRef colorspace;
  CGDataProviderRef data_provider;
  CGImageRef image;
  void *data;
  NSImage *nsimage;
  double sx, sy;
  cairo_t *cr;
  cairo_surface_t *img_surface;
  cairo_rectangle_int_t extents;
  int width, height, rowstride;

  if (!_cairo_surface_extents (surface, &extents))
    return NULL;

  cairo_surface_get_device_scale (surface, &sx, &sy);
  width = extents.width * sx;
  height = extents.height * sy;

  img_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
  cr = cairo_create (img_surface);
  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  cairo_scale (cr, sx, sy);
  cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
  cairo_paint (cr);
  cairo_destroy (cr);

  cairo_surface_flush (img_surface);
  rowstride = cairo_image_surface_get_stride (img_surface);
  data = cairo_image_surface_get_data (img_surface);

  colorspace = CGColorSpaceCreateDeviceRGB ();
  /* Note: the release callback will only be called after NSImage below dies */
  data_provider = CGDataProviderCreateWithData (surface, data, height * rowstride,
                                                _data_provider_release_cairo_surface);

  image = CGImageCreate (width, height, 8,
                         32, rowstride,
                         colorspace,
                         /* XXX: kCGBitmapByteOrderDefault gives wrong colors..?? */
                         kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst,
                         data_provider, NULL, FALSE,
                         kCGRenderingIntentDefault);
  CGDataProviderRelease (data_provider);
  CGColorSpaceRelease (colorspace);

  nsimage = [[NSImage alloc] initWithCGImage:image size:NSMakeSize (extents.width, extents.height)];
  CGImageRelease (image);

  return nsimage;
}

125
NSSet *
Anders Carlsson's avatar
Anders Carlsson committed
126 127 128 129 130 131 132 133
_gtk_quartz_target_list_to_pasteboard_types (GtkTargetList *target_list)
{
  NSMutableSet *set = [[NSMutableSet alloc] init];
  GList *list;

  for (list = target_list->list; list; list = list->next)
    {
      GtkTargetPair *pair = list->data;
134
      g_return_val_if_fail (pair->flags < 16, NULL);
135
      [set addObject:gdk_quartz_atom_to_pasteboard_type_libgtk_only (pair->target)];
Anders Carlsson's avatar
Anders Carlsson committed
136 137
    }

138
  return set;
Anders Carlsson's avatar
Anders Carlsson committed
139 140
}

141
NSSet *
Anders Carlsson's avatar
Anders Carlsson committed
142 143 144 145 146 147 148 149
_gtk_quartz_target_entries_to_pasteboard_types (const GtkTargetEntry *targets,
						guint                 n_targets)
{
  NSMutableSet *set = [[NSMutableSet alloc] init];
  int i;

  for (i = 0; i < n_targets; i++)
    {
150
      [set addObject:gdk_quartz_target_to_pasteboard_type_libgtk_only (targets[i].target)];
Anders Carlsson's avatar
Anders Carlsson committed
151 152
    }

153
  return set;
Anders Carlsson's avatar
Anders Carlsson committed
154 155 156 157 158 159 160 161 162 163 164 165 166
}

GList *
_gtk_quartz_pasteboard_types_to_atom_list (NSArray *array)
{
  GList *result = NULL;
  int i;
  int count;

  count = [array count];

  for (i = 0; i < count; i++) 
    {
167
      GdkAtom atom = gdk_quartz_pasteboard_type_to_atom_libgtk_only ([array objectAtIndex:i]);
Anders Carlsson's avatar
Anders Carlsson committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181

      result = g_list_prepend (result, GDK_ATOM_TO_POINTER (atom));
    }

  return result;
}

GtkSelectionData *
_gtk_quartz_get_selection_data_from_pasteboard (NSPasteboard *pasteboard,
						GdkAtom       target,
						GdkAtom       selection)
{
  GtkSelectionData *selection_data = NULL;

182
  selection_data = g_slice_new0 (GtkSelectionData);
Anders Carlsson's avatar
Anders Carlsson committed
183 184
  selection_data->selection = selection;
  selection_data->target = target;
185 186
  if (!selection_data->display)
    selection_data->display = gdk_display_get_default ();
Anders Carlsson's avatar
Anders Carlsson committed
187 188 189 190 191 192
  if (target == gdk_atom_intern_static_string ("UTF8_STRING"))
    {
      NSString *s = [pasteboard stringForType:NSStringPboardType];

      if (s)
	{
193
          const char *utf8_string = [s UTF8String];
194

195 196 197
          gtk_selection_data_set (selection_data,
                                  target, 8,
                                  (guchar *)utf8_string, strlen (utf8_string));
Anders Carlsson's avatar
Anders Carlsson committed
198 199 200 201
	}
    }
  else if (target == gdk_atom_intern_static_string ("application/x-color"))
    {
202 203
      NSColor *nscolor = [[NSColor colorFromPasteboard:pasteboard]
                          colorUsingColorSpaceName:NSDeviceRGBColorSpace];
Anders Carlsson's avatar
Anders Carlsson committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217
      
      guint16 color[4];
      
      selection_data->target = target;

      color[0] = 0xffff * [nscolor redComponent];
      color[1] = 0xffff * [nscolor greenComponent];
      color[2] = 0xffff * [nscolor blueComponent];
      color[3] = 0xffff * [nscolor alphaComponent];

      gtk_selection_data_set (selection_data, target, 16, (guchar *)color, 8);
    }
  else if (target == gdk_atom_intern_static_string ("text/uri-list"))
    {
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
      if ([[pasteboard types] containsObject:NSFilenamesPboardType])
        {
           gchar **uris;
           NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
           int n_files = [files count];
           int i;

           selection_data->target = gdk_atom_intern_static_string ("text/uri-list");

           uris = (gchar **) g_malloc (sizeof (gchar*) * (n_files + 1));
           for (i = 0; i < n_files; ++i)
             {
               NSString* uriString = [files objectAtIndex:i];
               uriString = [@"file://" stringByAppendingString:uriString];
               uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
               uris[i] = (gchar *) [uriString cStringUsingEncoding:NSUTF8StringEncoding];
             }
           uris[i] = NULL;

           gtk_selection_data_set_uris (selection_data, uris);
           g_free (uris);
         }
      else if ([[pasteboard types] containsObject:NSURLPboardType])
        {
          gchar *uris[2];
          NSURL *url = [NSURL URLFromPasteboard:pasteboard];

          selection_data->target = gdk_atom_intern_static_string ("text/uri-list");
246

247
          uris[0] = (gchar *) [[url description] UTF8String];
Richard Hult's avatar
Richard Hult committed
248

249 250 251
          uris[1] = NULL;
          gtk_selection_data_set_uris (selection_data, uris);
        }
Anders Carlsson's avatar
Anders Carlsson committed
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
    }
  else
    {
      NSData *data;
      gchar *name;

      name = gdk_atom_name (target);

      if (strcmp (name, "image/tiff") == 0)
	data = [pasteboard dataForType:NSTIFFPboardType];
      else
	data = [pasteboard dataForType:[NSString stringWithUTF8String:name]];

      g_free (name);

      if (data)
	{
269 270 271
	  gtk_selection_data_set (selection_data,
                                  target, 8,
                                  [data bytes], [data length]);
Anders Carlsson's avatar
Anders Carlsson committed
272 273 274 275 276 277 278
	}
    }

  return selection_data;
}

void
279
_gtk_quartz_set_selection_data_for_pasteboard (NSPasteboard     *pasteboard,
Anders Carlsson's avatar
Anders Carlsson committed
280 281 282
					       GtkSelectionData *selection_data)
{
  NSString *type;
283 284 285
  GdkDisplay *display;
  gint format;
  const guchar *data;
Kristian Rietveld's avatar
Kristian Rietveld committed
286
  NSUInteger length;
287 288 289

  display = gtk_selection_data_get_display (selection_data);
  format = gtk_selection_data_get_format (selection_data);
290 291
  data = gtk_selection_data_get_data (selection_data);
  length = gtk_selection_data_get_length (selection_data);
Anders Carlsson's avatar
Anders Carlsson committed
292

293
  type = gdk_quartz_atom_to_pasteboard_type_libgtk_only (gtk_selection_data_get_target (selection_data));
Richard Hult's avatar
Richard Hult committed
294

Anders Carlsson's avatar
Anders Carlsson committed
295
  if ([type isEqualTo:NSStringPboardType]) 
296
    [pasteboard setString:[NSString stringWithUTF8String:(const char *)data]
Anders Carlsson's avatar
Anders Carlsson committed
297 298 299
                  forType:type];
  else if ([type isEqualTo:NSColorPboardType])
    {
300
      guint16 *color = (guint16 *)data;
Anders Carlsson's avatar
Anders Carlsson committed
301
      float red, green, blue, alpha;
Richard Hult's avatar
Richard Hult committed
302
      NSColor *nscolor;
Anders Carlsson's avatar
Anders Carlsson committed
303 304 305 306 307

      red   = (float)color[0] / 0xffff;
      green = (float)color[1] / 0xffff;
      blue  = (float)color[2] / 0xffff;
      alpha = (float)color[3] / 0xffff;
308

Richard Hult's avatar
Richard Hult committed
309
      nscolor = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];
Anders Carlsson's avatar
Anders Carlsson committed
310 311 312 313
      [nscolor writeToPasteboard:pasteboard];
    }
  else if ([type isEqualTo:NSURLPboardType])
    {
314
      gchar **uris;
Anders Carlsson's avatar
Anders Carlsson committed
315

316 317
      uris = gtk_selection_data_get_uris (selection_data);
      if (uris != NULL)
318
        {
Richard Hult's avatar
Richard Hult committed
319 320
          NSURL *url;

321
          url = [NSURL URLWithString:[NSString stringWithUTF8String:uris[0]]];
322 323
          [url writeToPasteboard:pasteboard];
        }
324
      g_strfreev (uris);
Anders Carlsson's avatar
Anders Carlsson committed
325 326
    }
  else
Kristian Rietveld's avatar
Kristian Rietveld committed
327 328 329 330
    [pasteboard setData:[NSData dataWithBytesNoCopy:(void *)data
                                             length:length
                                       freeWhenDone:NO]
                                            forType:type];
Anders Carlsson's avatar
Anders Carlsson committed
331
}
332

333 334 335
#ifdef QUARTZ_RELOCATION

/* Bundle-based functions for various directories. These almost work
336
 * even when the application isn’t in a bundle, becuase mainBundle
337
 * paths point to the bin directory in that case. It’s a simple matter
338 339 340
 * to test for that and remove the last element.
 */

341 342
static const gchar *
get_bundle_path (void)
343
{
344
  static gchar *path = NULL;
345

346 347 348 349
  if (path == NULL)
    {
      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
      gchar *resource_path = g_strdup ([[[NSBundle mainBundle] resourcePath] UTF8String]);
350
      gchar *base;
351
      [pool drain];
352

353 354 355 356 357
      base = g_path_get_basename (resource_path);
      if (strcmp (base, "bin") == 0)
	path = g_path_get_dirname (resource_path);
      else
	path = strdup (resource_path);
358

359 360 361
      g_free (resource_path);
      g_free (base);
    }
362

363 364 365 366 367 368
  return path;
}

const gchar *
_gtk_get_datadir (void)
{
369
  static gchar *path = NULL;
370

371
  if (path == NULL)
372 373
    path = g_build_filename (get_bundle_path (), "share", NULL);

374
  return path;
375 376 377 378 379
}

const gchar *
_gtk_get_libdir (void)
{
380
  static gchar *path = NULL;
381

382
  if (path == NULL)
383 384
    path = g_build_filename (get_bundle_path (), "lib", NULL);

385
  return path;
386 387 388 389 390
}

const gchar *
_gtk_get_localedir (void)
{
391
  static gchar *path = NULL;
392

393
  if (path == NULL)
394 395
    path = g_build_filename (get_bundle_path (), "share", "locale", NULL);

396
  return path;
397 398 399 400 401
}

const gchar *
_gtk_get_sysconfdir (void)
{
402
  static gchar *path = NULL;
403

404
  if (path == NULL)
405 406
    path = g_build_filename (get_bundle_path (), "etc", NULL);

407
  return path;
408 409 410 411 412
}

const gchar *
_gtk_get_data_prefix (void)
{
413
  return get_bundle_path ();
414
}
415 416

#endif /* QUARTZ_RELOCATION */