]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkpixmap-x11.c
- Show selection correctly when starting selection with arrows
[~andy/gtk] / gdk / x11 / gdkpixmap-x11.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "../config.h"
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 /* Needed for SEEK_END in SunOS */
23 #include <unistd.h>
24 #include <X11/Xlib.h>
25
26 #include "gdk.h"
27 #include "gdkprivate.h"
28
29 typedef struct
30 {
31   gchar *color_string;
32   GdkColor color;
33   gint transparent;
34 } _GdkPixmapColor;
35
36 GdkPixmap*
37 gdk_pixmap_new (GdkWindow *window,
38                 gint       width,
39                 gint       height,
40                 gint       depth)
41 {
42   GdkPixmap *pixmap;
43   GdkWindowPrivate *private;
44   GdkWindowPrivate *window_private;
45
46   g_return_val_if_fail ((window != NULL) || (depth != -1), NULL);
47
48   if (!window)
49     window = (GdkWindow*) &gdk_root_parent;
50
51   window_private = (GdkWindowPrivate*) window;
52   if (window_private->destroyed)
53     return NULL;
54
55   if (depth == -1)
56     gdk_window_get_geometry (window, NULL, NULL, NULL, NULL, &depth);
57
58   private = g_new (GdkWindowPrivate, 1);
59   pixmap = (GdkPixmap*) private;
60
61   private->xdisplay = window_private->xdisplay;
62   private->window_type = GDK_WINDOW_PIXMAP;
63   private->xwindow = XCreatePixmap (private->xdisplay, window_private->xwindow,
64                                     width, height, depth);
65   private->parent = NULL;
66   private->x = 0;
67   private->y = 0;
68   private->width = width;
69   private->height = height;
70   private->resize_count = 0;
71   private->ref_count = 1;
72   private->destroyed = 0;
73
74   gdk_xid_table_insert (&private->xwindow, pixmap);
75
76   return pixmap;
77 }
78
79 GdkPixmap *
80 gdk_bitmap_create_from_data (GdkWindow *window,
81                              gchar     *data,
82                              gint       width,
83                              gint       height)
84 {
85   GdkPixmap *pixmap;
86   GdkWindowPrivate *private;
87   GdkWindowPrivate *window_private;
88
89   g_return_val_if_fail (data != NULL, NULL);
90
91   if (!window)
92     window = (GdkWindow*) &gdk_root_parent;
93
94   window_private = (GdkWindowPrivate*) window;
95   if (window_private->destroyed)
96     return NULL;
97
98   private = g_new (GdkWindowPrivate, 1);
99   pixmap = (GdkPixmap*) private;
100
101   private->parent = NULL;
102   private->xdisplay = window_private->xdisplay;
103   private->window_type = GDK_WINDOW_PIXMAP;
104   private->x = 0;
105   private->y = 0;
106   private->width = width;
107   private->height = height;
108   private->resize_count = 0;
109   private->ref_count = 1;
110   private->destroyed = FALSE;
111
112   private->xwindow = XCreateBitmapFromData (private->xdisplay,
113                                             window_private->xwindow,
114                                             data, width, height);
115
116   gdk_xid_table_insert (&private->xwindow, pixmap);
117
118   return pixmap;
119 }
120
121 GdkPixmap*
122 gdk_pixmap_create_from_data (GdkWindow *window,
123                              gchar     *data,
124                              gint       width,
125                              gint       height,
126                              gint       depth,
127                              GdkColor  *fg,
128                              GdkColor  *bg)
129 {
130   GdkPixmap *pixmap;
131   GdkWindowPrivate *private;
132   GdkWindowPrivate *window_private;
133
134   g_return_val_if_fail (data != NULL, NULL);
135   g_return_val_if_fail (fg != NULL, NULL);
136   g_return_val_if_fail (bg != NULL, NULL);
137   g_return_val_if_fail ((window != NULL) || (depth != -1), NULL);
138
139   if (!window)
140     window = (GdkWindow*) &gdk_root_parent;
141
142   window_private = (GdkWindowPrivate*) window;
143   if (window_private->destroyed)
144     return NULL;
145
146   if (depth == -1)
147     gdk_window_get_geometry (window, NULL, NULL, NULL, NULL, &depth);
148
149   private = g_new (GdkWindowPrivate, 1);
150   pixmap = (GdkPixmap*) private;
151
152   private->parent = NULL;
153   private->xdisplay = window_private->xdisplay;
154   private->window_type = GDK_WINDOW_PIXMAP;
155   private->x = 0;
156   private->y = 0;
157   private->width = width;
158   private->height = height;
159   private->resize_count = 0;
160   private->ref_count = 1;
161   private->destroyed = FALSE;
162
163   private->xwindow = XCreatePixmapFromBitmapData (private->xdisplay,
164                                                   window_private->xwindow,
165                                                   data, width, height,
166                                                   fg->pixel, bg->pixel, depth);
167
168   gdk_xid_table_insert (&private->xwindow, pixmap);
169
170   return pixmap;
171 }
172
173 gint
174 gdk_pixmap_seek_string (FILE  *infile,
175                         const gchar *str,
176                         gint   skip_comments)
177 {
178   char instr[1024];
179
180   while (!feof (infile))
181     {
182       fscanf (infile, "%s", instr);
183       if (skip_comments == TRUE && strcmp (instr, "/*") == 0)
184         {
185           fscanf (infile, "%s", instr);
186           while (!feof (infile) && strcmp (instr, "*/") != 0)
187             fscanf (infile, "%s", instr);
188           fscanf(infile, "%s", instr);
189         }
190       if (strcmp (instr, str)==0)
191         return TRUE;
192     }
193
194   return FALSE;
195 }
196
197 gint
198 gdk_pixmap_seek_char (FILE  *infile,
199                       gchar  c)
200 {
201   gchar b, oldb;
202
203   while (!feof (infile))
204     {
205       fscanf(infile, "%c", &b);
206       if (c != b && b == '/')
207         {
208           fscanf (infile, "%c", &b);
209           if (b == '*')
210             {
211               oldb = b;
212               while (!feof (infile) && !(oldb == '*' && b == '/'))
213                 {
214                   oldb = b;
215                   fscanf (infile, "%c", &b);
216                 }
217               fscanf (infile, "%c", &b);
218             }
219         }
220       if (c == b)
221         return TRUE;
222     }
223
224   return FALSE;
225 }
226
227 gint
228 gdk_pixmap_read_string (FILE  *infile,
229                         gchar **buffer,
230                         guint *buffer_size)
231 {
232   gchar c;
233   guint cnt = 0;
234
235   if ((*buffer) == NULL)
236     {
237       (*buffer_size) = 10 * sizeof (gchar);
238       (*buffer) = (gchar *) malloc (*buffer_size);
239     }
240
241   do
242     fscanf (infile, "%c", &c);
243   while (!feof (infile) && c != '"');
244
245   if (c != '"')
246     return FALSE;
247
248   while (!feof (infile))
249     {
250       fscanf (infile, "%c", &c);
251
252       if (cnt == (*buffer_size))
253         {
254           (*buffer_size) *= 2;
255           (*buffer) = (gchar *) realloc ((*buffer), *buffer_size);        
256         }
257
258       if (c != '"')
259         (*buffer)[cnt++] = c;
260       else
261         {
262           (*buffer)[cnt++] = 0;
263           return TRUE;
264         }
265     }
266
267   return FALSE;
268 }
269
270 gchar*
271 gdk_pixmap_skip_whitespaces (gchar *buffer)
272 {
273   gint32 index = 0;
274
275   while (buffer[index] != 0 && (buffer[index] == 0x20 || buffer[index] == 0x09))
276     index++;
277
278   return &buffer[index];
279 }
280
281 gchar*
282 gdk_pixmap_skip_string (gchar *buffer)
283 {
284   gint32 index = 0;
285
286   while (buffer[index] != 0 && buffer[index] != 0x20 && buffer[index] != 0x09)
287     index++;
288
289   return &buffer[index];
290 }
291
292 gchar*
293 gdk_pixmap_extract_color (gchar *buffer)
294 {
295   gint counter, finished = FALSE, numnames;
296   gchar *ptr = NULL, ch, temp[128];
297   gchar color[128], *retcol;
298
299   counter = 0;
300   while (ptr == NULL)
301     {
302       if (buffer[counter] == 'c')
303         {
304           ch = buffer[counter + 1];
305           if (ch == 0x20 || ch == 0x09)
306             ptr = &buffer[counter + 1];
307         }
308       else if (buffer[counter] == 0)
309         return NULL;
310
311       counter++;
312     }
313
314   if (ptr == NULL)
315     return NULL;
316
317   ptr = gdk_pixmap_skip_whitespaces (ptr);
318
319   if (ptr[0] == 0)
320     return NULL;
321   else if (ptr[0] == '#')
322     {
323       retcol = g_strdup (ptr);
324       return retcol;
325     }
326
327   color[0] = 0;
328   numnames = 0;
329
330   while (finished == FALSE)
331     {
332       sscanf (ptr, "%s", temp);
333
334       if ((gint)ptr[0] == 0 || strcmp ("s", temp) == 0 || strcmp ("m", temp) == 0 ||
335           strcmp ("g", temp) == 0 || strcmp ("g4", temp) == 0)
336        finished = TRUE;
337       else
338         {
339           if (numnames > 0)
340             strcat (color, " ");
341           strcat (color, temp);
342           ptr = gdk_pixmap_skip_string (ptr);
343           ptr = gdk_pixmap_skip_whitespaces (ptr);
344           numnames++;
345         }
346     }
347
348   retcol = g_strdup (color);
349   return retcol;
350 }
351
352
353 GdkPixmap*
354 gdk_pixmap_colormap_create_from_xpm (GdkWindow   *window,
355                                      GdkColormap *colormap,
356                                      GdkBitmap  **mask,
357                                      GdkColor    *transparent_color,
358                                      const gchar *filename)
359 {
360   FILE *infile = NULL;
361   GdkPixmap *pixmap = NULL;
362   GdkImage *image = NULL;
363   GdkVisual *visual;
364   GdkGC *gc;
365   GdkColor tmp_color;
366   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt;
367   gchar *buffer = NULL, pixel_str[32];
368   guint buffer_size = 0;
369   _GdkPixmapColor *colors = NULL, *color = NULL;
370   gulong index;
371
372   if ((window == NULL) && (colormap == NULL))
373     g_warning ("Creating pixmap from xpm with NULL window and colormap");
374
375   if (window == NULL)
376     window = (GdkWindow *)&gdk_root_parent;
377
378   if (colormap == NULL)
379     {
380       colormap = gdk_window_get_colormap (window);
381       visual = gdk_window_get_visual (window);
382     }
383   else
384     visual = ((GdkColormapPrivate *)colormap)->visual;
385
386   infile = fopen (filename, "rb");
387   if (infile != NULL)
388     {
389       if (gdk_pixmap_seek_string (infile, "XPM", FALSE) == TRUE)
390         {
391           if (gdk_pixmap_seek_char (infile,'{') == TRUE)
392             {
393               gdk_pixmap_seek_char (infile, '"');
394               fseek (infile, -1, SEEK_CUR);
395               gdk_pixmap_read_string (infile, &buffer, &buffer_size);
396
397               sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
398
399               colors = g_new(_GdkPixmapColor, num_cols);
400
401               if (transparent_color == NULL) 
402                 {
403                   gdk_color_white (colormap, &tmp_color);
404                   transparent_color = &tmp_color;
405                 }
406
407               for (cnt = 0; cnt < num_cols; cnt++)
408                 {
409                   gchar *color_name;
410
411                   gdk_pixmap_seek_char (infile, '"');
412                   fseek (infile, -1, SEEK_CUR);
413                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
414
415                   colors[cnt].color_string = g_new(gchar, cpp + 1);
416                   for (n = 0; n < cpp; n++)
417                     colors[cnt].color_string[n] = buffer[n];
418                   colors[cnt].color_string[n] = 0;
419                   colors[cnt].transparent = FALSE;
420
421                   color_name = gdk_pixmap_extract_color (&buffer[cpp]);
422
423                   if (color_name != NULL)
424                     {
425                       if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
426                         {
427                           colors[cnt].color = *transparent_color;
428                           colors[cnt].transparent = TRUE;
429                         }
430                     }
431                   else
432                     {
433                       colors[cnt].color = *transparent_color;
434                       colors[cnt].transparent = TRUE;
435                     }
436
437                   g_free (color_name);
438
439                   gdk_color_alloc (colormap, &colors[cnt].color);
440                 }
441
442               index = 0;
443               image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
444
445               gc = NULL;
446               if (mask)
447                 {
448                   /* The pixmap mask is just a bits pattern.
449                    * Color 0 is used for background and 1 for foreground.
450                    * We don't care about the colormap, we just need 0 and 1.
451                    */
452                   GdkColor mask_pattern;
453                   
454                   *mask = gdk_pixmap_new (window, width, height, 1);
455                   gc = gdk_gc_new (*mask);
456                   
457                   mask_pattern.pixel = 0;
458                   gdk_gc_set_foreground (gc, &mask_pattern);
459                   gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
460                   
461                   mask_pattern.pixel = 1;
462                   gdk_gc_set_foreground (gc, &mask_pattern);
463                 }
464
465               for (ycnt = 0; ycnt < height; ycnt++)
466                 {
467                   gdk_pixmap_read_string (infile, &buffer, &buffer_size);
468
469                   for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
470                     {
471                       strncpy (pixel_str, &buffer[n], cpp);
472                       pixel_str[cpp] = 0;
473                       color = NULL;
474                       ns = 0;
475
476                       while (color == NULL)
477                         {
478                           if (strcmp (pixel_str, colors[ns].color_string) == 0)
479                             color = &colors[ns];
480                           else
481                             ns++;
482                         }
483
484                       gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
485
486                       if (mask && color->transparent)
487                         {
488                           if (cnt < xcnt)
489                             gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
490                           cnt = xcnt + 1;
491                         }
492                     }
493
494                   if (mask && (cnt < xcnt))
495                     gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
496                 }
497
498               if (mask)
499                 gdk_gc_destroy (gc);
500
501               pixmap = gdk_pixmap_new (window, width, height, visual->depth);
502
503               gc = gdk_gc_new (pixmap);
504               gdk_gc_set_foreground (gc, transparent_color);
505               gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
506               gdk_gc_destroy (gc);
507               gdk_image_destroy (image);
508             }
509         }
510
511       fclose (infile);
512       free (buffer);
513
514       if (colors != NULL)
515         {
516           for (cnt = 0; cnt < num_cols; cnt++)
517             g_free (colors[cnt].color_string);
518           g_free (colors);
519         }
520     }
521
522   return pixmap;
523 }
524
525 GdkPixmap*
526 gdk_pixmap_create_from_xpm (GdkWindow  *window,
527                             GdkBitmap **mask,
528                             GdkColor   *transparent_color,
529                             const gchar *filename)
530 {
531   return gdk_pixmap_colormap_create_from_xpm (window, NULL, mask, 
532                                        transparent_color, filename);
533 }
534
535
536 GdkPixmap*
537 gdk_pixmap_colormap_create_from_xpm_d (GdkWindow  *window,
538                                        GdkColormap *colormap,
539                                        GdkBitmap **mask,
540                                        GdkColor   *transparent_color,
541                                        gchar     **data)
542 {
543   GdkPixmap *pixmap = NULL;
544   GdkImage *image = NULL;
545   GdkVisual *visual;
546   GdkGC *gc;
547   GdkColor tmp_color;
548   gint width, height, num_cols, cpp, cnt, n, ns, xcnt, ycnt, i;
549   gchar *buffer, pixel_str[32];
550   _GdkPixmapColor *colors = NULL, *color = NULL;
551   gulong index;
552
553   if ((window == NULL) && (colormap == NULL))
554     g_warning ("Creating pixmap from xpm with NULL window and colormap");
555
556   if (window == NULL)
557     window = (GdkWindow *)&gdk_root_parent;
558
559   if (colormap == NULL)
560     {
561       colormap = gdk_window_get_colormap (window);
562       visual = gdk_window_get_visual (window);
563     }
564   else
565     visual = ((GdkColormapPrivate *)colormap)->visual;
566
567   i = 0;
568   buffer = data[i++];
569   sscanf (buffer,"%d %d %d %d", &width, &height, &num_cols, &cpp);
570
571   colors = g_new(_GdkPixmapColor, num_cols);
572
573   if (transparent_color == NULL) 
574     {
575       gdk_color_white (colormap, &tmp_color);
576       transparent_color = &tmp_color;
577     }
578
579   for (cnt = 0; cnt < num_cols; cnt++)
580     {
581       gchar *color_name;
582
583       buffer = data[i++];
584
585       colors[cnt].color_string = g_new(gchar, cpp + 1);
586       for (n = 0; n < cpp; n++)
587         colors[cnt].color_string[n] = buffer[n];
588       colors[cnt].color_string[n] = 0;
589       colors[cnt].transparent = FALSE;
590
591       color_name = gdk_pixmap_extract_color (&buffer[cpp]);
592
593       if (color_name != NULL)
594         {
595           if (gdk_color_parse (color_name, &colors[cnt].color) == FALSE)
596             {
597               colors[cnt].color = *transparent_color;
598               colors[cnt].transparent = TRUE;
599             }
600         }
601       else
602         {
603           colors[cnt].color = *transparent_color;
604           colors[cnt].transparent = TRUE;
605         }
606
607       g_free (color_name);
608
609       gdk_color_alloc (colormap, &colors[cnt].color);
610     }
611
612   index = 0;
613   image = gdk_image_new (GDK_IMAGE_FASTEST, visual, width, height);
614
615   gc = NULL;
616   if (mask)
617     {
618       /* The pixmap mask is just a bits pattern.
619        * Color 0 is used for background and 1 for foreground.
620        * We don't care about the colormap, we just need 0 and 1.
621        */
622       GdkColor mask_pattern;
623
624       *mask = gdk_pixmap_new (window, width, height, 1);
625       gc = gdk_gc_new (*mask);
626
627       mask_pattern.pixel = 0;
628       gdk_gc_set_foreground (gc, &mask_pattern);
629       gdk_draw_rectangle (*mask, gc, TRUE, 0, 0, -1, -1);
630
631       mask_pattern.pixel = 1;
632       gdk_gc_set_foreground (gc, &mask_pattern);
633     }
634
635   for (ycnt = 0; ycnt < height; ycnt++)
636     {
637       buffer = data[i++];
638
639       for (n = 0, cnt = 0, xcnt = 0; n < (width * cpp); n += cpp, xcnt++)
640         {
641           strncpy (pixel_str, &buffer[n], cpp);
642           pixel_str[cpp] = 0;
643           color = NULL;
644           ns = 0;
645
646           while (color == NULL)
647             {
648               if (strcmp (pixel_str, colors[ns].color_string) == 0)
649                 color = &colors[ns];
650               else
651                 ns++;
652             }
653
654           gdk_image_put_pixel (image, xcnt, ycnt, color->color.pixel);
655
656           if (mask && color->transparent)
657             {
658               if (cnt < xcnt)
659                 gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
660               cnt = xcnt + 1;
661             }
662         }
663
664       if (mask && (cnt < xcnt))
665         gdk_draw_line (*mask, gc, cnt, ycnt, xcnt - 1, ycnt);
666     }
667
668   if (mask)
669     gdk_gc_destroy (gc);
670
671   pixmap = gdk_pixmap_new (window, width, height, visual->depth);
672
673   gc = gdk_gc_new (pixmap);
674   gdk_gc_set_foreground (gc, transparent_color);
675   gdk_draw_image (pixmap, gc, image, 0, 0, 0, 0, image->width, image->height);
676   gdk_gc_destroy (gc);
677   gdk_image_destroy (image);
678
679   if (colors != NULL)
680     {
681       for (cnt = 0; cnt < num_cols; cnt++)
682         g_free (colors[cnt].color_string);
683       g_free (colors);
684     }
685
686   return pixmap;
687 }
688
689 GdkPixmap*
690 gdk_pixmap_create_from_xpm_d (GdkWindow  *window,
691                               GdkBitmap **mask,
692                               GdkColor   *transparent_color,
693                               gchar     **data)
694 {
695   return gdk_pixmap_colormap_create_from_xpm_d (window, NULL, mask,
696                                                 transparent_color, data);
697 }
698
699 GdkPixmap*
700 gdk_pixmap_ref (GdkPixmap *pixmap)
701 {
702   GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
703   g_return_val_if_fail (pixmap != NULL, NULL);
704
705   private->ref_count += 1;
706   return pixmap;
707 }
708
709 void
710 gdk_pixmap_unref (GdkPixmap *pixmap)
711 {
712   GdkWindowPrivate *private = (GdkWindowPrivate *)pixmap;
713   g_return_if_fail(pixmap != NULL);
714
715   private->ref_count -= 1;
716   if (private->ref_count == 0)
717     {
718       XFreePixmap (private->xdisplay, private->xwindow);
719       gdk_xid_table_remove (private->xwindow);
720       g_free (private);
721     }
722 }
723
724 GdkBitmap *
725 gdk_bitmap_ref (GdkBitmap *bitmap)
726 {
727   return (GdkBitmap *)gdk_pixmap_ref ((GdkPixmap *)bitmap);
728 }
729
730 void
731 gdk_bitmap_unref (GdkBitmap *bitmap)
732 {
733   gdk_pixmap_unref ((GdkPixmap *)bitmap);
734 }