]> git.sur5r.net Git - glabels/blob - glabels2/src/util.c
2008-10-19 Jim Evins <evins@snaught.com>
[glabels] / glabels2 / src / util.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
3 /*
4  *  (GLABELS) Label and Business Card Creation program for GNOME
5  *
6  *  util.c:  various small utility functions
7  *
8  *  Copyright (C) 2001  Jim Evins <evins@snaught.com>.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
23  */
24
25 #include <config.h>
26
27 #include "util.h"
28
29 #include <string.h>
30 #include <glib.h>
31 #include <math.h>
32 #include <gtk/gtkliststore.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <gtk/gtkcelllayout.h>
35 #include <libglabels/str.h>
36
37 #define FRAC_EPSILON 0.00005
38
39 \f
40 /****************************************************************************/
41 /* Append ".glabels" extension to filename if needed.                       */
42 /****************************************************************************/
43 gchar *
44 gl_util_add_extension (const gchar *orig_filename)
45 {
46         gchar *new_filename, *extension;
47
48         extension = strrchr (orig_filename, '.');
49         if (extension == NULL) {
50                 new_filename = g_strconcat (orig_filename, ".glabels", NULL);
51         } else {
52                 if (g_strcasecmp (extension, ".glabels") != 0) {
53                         new_filename =
54                             g_strconcat (orig_filename, ".glabels", NULL);
55                 } else {
56                         new_filename = g_strdup (orig_filename);
57                 }
58         }
59
60         return new_filename;
61 }
62
63 /****************************************************************************/
64 /* Remove ".glabels" extension from filename if needed.                     */
65 /****************************************************************************/
66 gchar *
67 gl_util_remove_extension (const gchar *orig_filename)
68 {
69         gchar *new_filename, *extension;
70
71         new_filename = g_strdup (orig_filename);
72
73         extension = strrchr (new_filename, '.');
74         if (extension != NULL) {
75                 if (g_strcasecmp (extension, ".glabels") == 0) {
76                         *extension = 0; /* truncate string, rm extension */
77                 }
78         }
79
80         return new_filename;
81 }
82
83 /****************************************************************************/
84 /* Make sure we have an absolute path to filename.                          */
85 /****************************************************************************/
86 gchar *
87 gl_util_make_absolute (const gchar *filename)
88 {
89         gchar *pwd, *absolute_filename;
90
91         if (g_path_is_absolute (filename)) {
92                 absolute_filename = g_strdup (filename);
93         } else {
94                 pwd = g_get_current_dir ();
95                 absolute_filename = g_build_filename (pwd, filename, NULL);
96                 g_free (pwd);
97         }
98
99         return absolute_filename;
100 }
101
102 /****************************************************************************/
103 /* Create fractional representation of number, if possible.                 */
104 /****************************************************************************/
105 gchar *
106 gl_util_fraction (gdouble x)
107 {
108         static gdouble denom[] = { 1., 2., 3., 4., 8., 16., 32., 0. };
109         gint i;
110         gdouble product, remainder;
111         gint n, d;
112
113         for ( i=0; denom[i] != 0.0; i++ ) {
114                 product = x * denom[i];
115                 remainder = fabs(product - ((gint)(product+0.5)));
116                 if ( remainder < FRAC_EPSILON ) break;
117         }
118
119         if ( denom[i] == 0.0 ) {
120                 /* None of our denominators work. */
121                 return g_strdup_printf ("%.5g", x);
122         }
123         if ( denom[i] == 1.0 ) {
124                 /* Simple integer. */
125                 return g_strdup_printf ("%d", (gint)x);
126         }
127         n = (gint)( x * denom[i] + 0.5 );
128         d = (gint)denom[i];
129         if ( n > d ) {
130                 return g_strdup_printf ("%d_%d/%d", (n/d), (n%d), d);
131         } else {
132                 return g_strdup_printf ("%d/%d", (n%d), d);
133         }
134 }
135
136 /****************************************************************************/
137 /* Utilities to deal with PangoAlignment types.                             */
138 /****************************************************************************/
139 const gchar *
140 gl_util_align_to_string (PangoAlignment align)
141 {
142         switch (align) {
143         case PANGO_ALIGN_LEFT:
144                 return "Left";
145         case PANGO_ALIGN_CENTER:
146                 return "Center";
147         case PANGO_ALIGN_RIGHT:
148                 return "Right";
149         default:
150                 return "?";
151         }
152 }
153
154 PangoAlignment
155 gl_util_string_to_align (const gchar *string)
156 {
157
158         if (g_strcasecmp (string, "Left") == 0) {
159                 return PANGO_ALIGN_LEFT;
160         } else if (g_strcasecmp (string, "Center") == 0) {
161                 return PANGO_ALIGN_CENTER;
162         } else if (g_strcasecmp (string, "Right") == 0) {
163                 return PANGO_ALIGN_RIGHT;
164         } else {
165                 return PANGO_ALIGN_LEFT;
166         }
167
168 }
169
170 /****************************************************************************/
171 /* Utilities to deal with PangoWeight types                                 */
172 /****************************************************************************/
173 const gchar *
174 gl_util_weight_to_string (PangoWeight weight)
175 {
176         switch (weight) {
177         case PANGO_WEIGHT_NORMAL:
178                 return "Regular";
179         case PANGO_WEIGHT_BOLD:
180                 return "Bold";
181         default:
182                 return "?";
183         }
184 }
185
186 PangoWeight
187 gl_util_string_to_weight (const gchar *string)
188 {
189
190         if (g_strcasecmp (string, "Regular") == 0) {
191                 return PANGO_WEIGHT_NORMAL;
192         } else if (g_strcasecmp (string, "Bold") == 0) {
193                 return PANGO_WEIGHT_BOLD;
194         } else {
195                 return PANGO_WEIGHT_NORMAL;
196         }
197
198 }
199
200 /****************************************************************************/
201 /* Convienience function to set strings in a text combo_box from a GList    */
202 /****************************************************************************/
203 void
204 gl_util_combo_box_set_strings (GtkComboBox       *combo,
205                                GList             *list)
206 {
207         GtkTreeModel *model;
208         GList        *p;
209
210         g_return_if_fail (list);
211
212         model = gtk_combo_box_get_model(combo);
213         gtk_list_store_clear (GTK_LIST_STORE (model));
214
215         for (p=list; p!=NULL; p=p->next) {
216                 if (p->data) {
217                         gtk_combo_box_append_text (combo, p->data);
218                 }
219         }
220 }
221
222 /*---------------------------------------------------------------------------*/
223 /* PRIVATE.  gl_util_combo_box_set_active_text support.                      */
224 /*---------------------------------------------------------------------------*/
225
226 typedef struct {
227   const gchar *text;
228   GtkTreeIter  iter;
229   gboolean     found;
230 } TextSearchData;
231
232 static gboolean
233 search_text_func (GtkTreeModel *model,
234                   GtkTreePath  *path,
235                   GtkTreeIter  *iter,
236                   gpointer      data)
237 {
238   TextSearchData *search_data = (TextSearchData *)data;
239   gchar          *text = NULL;
240
241   gtk_tree_model_get (model, iter, 0, &text, -1);
242
243   if (strcmp (text,search_data->text) == 0) {
244     search_data->found = TRUE;
245     search_data->iter  = *iter;
246   }
247
248   g_free (text);
249   
250   return FALSE;
251 }
252
253 /****************************************************************************/
254 /* Convienience function to set active text in a text combo_box from text   */
255 /****************************************************************************/
256 void
257 gl_util_combo_box_set_active_text (GtkComboBox       *combo,
258                                    const gchar       *text)
259 {
260         GtkTreeModel   *model = gtk_combo_box_get_model(combo);
261
262         g_return_if_fail (GTK_IS_LIST_STORE (model));
263
264         if (!text) {
265
266                 gtk_combo_box_set_active (combo, -1);
267
268         } else {
269                 TextSearchData  search_data;
270
271                 search_data.text        = text;
272                 search_data.found       = FALSE;
273
274                 gtk_tree_model_foreach (model, search_text_func, &search_data);
275                 if (search_data.found) {
276                         gtk_combo_box_set_active_iter (combo,
277                                                        &search_data.iter);
278                 } else {
279                         gtk_combo_box_set_active (combo, -1);
280                 }    
281
282         }
283
284 }
285
286 /****************************************************************************/
287 /* Convienience function to add a simple text model to an existing          */
288 /* combo_box.  This is needed since combo_boxes created with glade do not   */
289 /* use the gtk_combo_box_new_text() constructor.                            */
290 /****************************************************************************/
291 void
292 gl_util_combo_box_add_text_model (GtkComboBox       *combo)
293 {
294         GtkCellRenderer *cell;
295         GtkListStore *store;
296
297         store = gtk_list_store_new (1, G_TYPE_STRING);
298         gtk_combo_box_set_model (combo, GTK_TREE_MODEL (store));
299         g_object_unref (store);
300
301         cell = gtk_cell_renderer_text_new ();
302         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
303         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
304                                         "text", 0,
305                                         NULL);
306 }
307
308
309 /****************************************************************************/
310 /* Get list of available font families.                                     */
311 /****************************************************************************/
312 GList  *
313 gl_util_get_font_family_list (void)
314 {
315         GList                *list = NULL;
316         PangoFontMap         *fontmap;
317         PangoContext         *context;
318         PangoFontFamily     **families;
319         gint                  n;
320         gint                  i;
321         gchar                *name;
322
323         fontmap = pango_cairo_font_map_new ();
324         context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
325
326         pango_context_list_families (context, &families, &n);
327
328         for ( i=0; i<n; i++ )
329         {
330                 name = g_strdup (pango_font_family_get_name (families[i]));
331                 list = g_list_insert_sorted (list, name,
332                                              (GCompareFunc)lgl_str_utf8_casecmp);
333         }
334
335         g_free (families);
336
337         g_object_unref (context);
338         g_object_unref (fontmap);
339
340         return list;
341 }
342
343 /****************************************************************************/
344 /* Free previosly allocated list of font families.                          */
345 /****************************************************************************/
346 void    gl_util_font_family_list_free (GList *list)
347 {
348         GList *p;
349
350         for (p = list; p != NULL; p = p->next) {
351                 g_free (p->data);
352                 p->data = NULL;
353         }
354
355         g_list_free (list);
356 }
357
358
359 /****************************************************************************/
360 /* Convert cairo surface to GdkPixbuf.                                      */
361 /* from http://davyd.livejournal.com/240469.html                            */
362 /****************************************************************************/
363 static inline guint8
364 convert_color_channel (guint8 src, guint8 alpha)
365 {
366         return alpha ? ((src << 8) - src) / alpha : 0;
367 }
368
369 /**
370  * gl_util_cairo_convert_to_pixbuf:
371  * Converts from a Cairo image surface to a GdkPixbuf. Why does GTK+ not
372  * implement this?
373  */
374 GdkPixbuf *
375 gl_util_cairo_convert_to_pixbuf (cairo_surface_t *surface)
376 {
377         GdkPixbuf *pixbuf;
378         int width, height;
379         int srcstride, dststride;
380         guchar *srcpixels, *dstpixels;
381         guchar *srcpixel, *dstpixel;
382         int n_channels;
383         int x, y;
384
385         switch (cairo_image_surface_get_format (surface))
386         {
387                 case CAIRO_FORMAT_ARGB32:
388                 case CAIRO_FORMAT_RGB24:
389                         break;
390
391                 default:
392                         g_critical ("This Cairo surface format not supported");
393                         return NULL;
394                         break;
395         }
396
397         width = cairo_image_surface_get_width (surface);
398         height = cairo_image_surface_get_height (surface);
399         srcstride = cairo_image_surface_get_stride (surface);
400         srcpixels = cairo_image_surface_get_data (surface);
401
402         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
403                         width, height);
404         dststride = gdk_pixbuf_get_rowstride (pixbuf);
405         dstpixels = gdk_pixbuf_get_pixels (pixbuf);
406         n_channels = gdk_pixbuf_get_n_channels (pixbuf);
407
408         for (y = 0; y < height; y++)
409         {
410                 for (x = 0; x < width; x++)
411                 {
412                         srcpixel = srcpixels + y * srcstride + x * 4;
413                         dstpixel = dstpixels + y * dststride + x * n_channels;
414
415                         dstpixel[0] = convert_color_channel (srcpixel[2],
416                                                              srcpixel[3]);
417                         dstpixel[1] = convert_color_channel (srcpixel[1],
418                                                              srcpixel[3]);
419                         dstpixel[2] = convert_color_channel (srcpixel[0],
420                                                              srcpixel[3]);
421                         dstpixel[3] = srcpixel[3];
422                 }
423         }
424
425         return pixbuf;
426 }
427
428
429 /****************************************************************************/
430 /* Get widgets from GtkBuilder "en masse."                                  */
431 /****************************************************************************/
432 void gl_util_get_builder_widgets (GtkBuilder *builder,
433                                   gchar      *first_name,
434                                   ...)
435 {
436         va_list     args;
437         gchar      *name;
438         GtkWidget **p_widget;
439
440         va_start (args, first_name);
441
442         for ( name = first_name; name; name = va_arg (args, gchar *) )
443         {
444                 p_widget = va_arg (args, GtkWidget **);
445
446                 *p_widget = GTK_WIDGET (gtk_builder_get_object (builder, name));
447
448                 if (!*p_widget)
449                 {
450                         g_critical ("Could not load widget \"%s\".\n\ngLabels may not be installed correctly!",
451                                     name);
452                         break;
453                 }
454         }
455
456         va_end (args);
457 }
458