"Fossies" - the Fresh Open Source Software Archive

Member "gambas-3.16.3/gb.gtk/src/gtools.cpp" (7 Sep 2021, 53034 Bytes) of package /linux/misc/gambas-3.16.3.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gtools.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.16.2_vs_3.16.3.

    1 /***************************************************************************
    2 
    3   gtools.cpp
    4 
    5   (c) 2004-2006 - Daniel Campos Fernández <dcamposf@gmail.com>
    6 
    7   This program is free software; you can redistribute it and/or modify
    8   it under the terms of the GNU General Public License as published by
    9   the Free Software Foundation; either version 2, or (at your option)
   10   any later version.
   11 
   12   This program is distributed in the hope that it will be useful,
   13   but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15   GNU General Public License for more details.
   16 
   17   You should have received a copy of the GNU General Public License
   18   along with this program; if not, write to the Free Software
   19   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   20   MA 02110-1301, USA.
   21 
   22 ***************************************************************************/
   23 
   24 #include "widgets.h"
   25 #include "gcolor.h"
   26 #include "gdesktop.h"
   27 #include "gtools.h"
   28 #include "gpicture.h"
   29 #include "gapplication.h"
   30 
   31 // HTML character entities
   32 #include "kentities.h"
   33 
   34 #include "CKey.h"
   35 
   36 void stub(const char *function)
   37 {
   38     printf("gb.gtk: warning: %s not yet implemented\n", function);
   39 }
   40 
   41 /*******************************************************************
   42 
   43 Conversion between GDK and long type colors
   44 
   45 ********************************************************************/
   46 
   47 #define SCALE(i) ((int)(i * 255.0 / 65535.0 + 0.5))
   48 #define UNSCALE(d) ((int)(d / 255.0 * 65535.0 + 0.5))
   49 
   50 #ifndef GTK3
   51 static GtkStateType _color_style_bg[] = { GTK_STATE_INSENSITIVE, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_NORMAL };
   52 static GtkStateType _color_style_fg[] = { GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_NORMAL };
   53 #endif
   54 
   55 #ifdef GTK3
   56 void fill_gdk_color(GdkColor *gcol, gColor color)
   57 {
   58     int r, g, b;
   59 
   60     gt_color_to_rgb(color, &r, &g, &b);
   61 
   62     gcol->red = UNSCALE(r);
   63     gcol->green = UNSCALE(g);
   64     gcol->blue = UNSCALE(b);
   65 }
   66 #else
   67 void fill_gdk_color(GdkColor *gcol, gColor color, GdkColormap *cmap)
   68 {
   69     int r, g, b;
   70 
   71     if (!cmap)
   72         cmap = gdk_colormap_get_system();
   73 
   74     gt_color_to_rgb(color, &r, &g, &b);
   75 
   76     gcol->red = UNSCALE(r);
   77     gcol->green = UNSCALE(g);
   78     gcol->blue = UNSCALE(b);
   79 
   80     gdk_colormap_alloc_color(cmap, gcol, TRUE, TRUE);
   81 }
   82 #endif
   83 
   84 gColor gt_gdkcolor_to_color(GdkColor *gcol)
   85 {
   86     return gt_rgb_to_color(SCALE(gcol->red), SCALE(gcol->green), SCALE(gcol->blue));
   87 }
   88 
   89 #ifndef GTK3
   90 
   91 static void set_color(GtkWidget *wid, gColor color, void (*func)(GtkWidget *, GtkStateType, const GdkColor *), bool fg)
   92 {
   93     GdkColor gcol;
   94     GdkColor *pcol;
   95     int i;
   96     GtkStateType st;
   97 
   98     if (color == COLOR_DEFAULT)
   99     {
  100         pcol = NULL;
  101     }
  102     else
  103     {
  104         fill_gdk_color(&gcol, color);
  105         pcol = &gcol;
  106     }
  107     
  108     for (i = 0;; i++)
  109     {
  110         st = fg ? _color_style_fg[i] : _color_style_bg[i];
  111         (*func)(wid, st, pcol);
  112         if (st == GTK_STATE_NORMAL)
  113             break;
  114     }
  115 }
  116 
  117 gColor get_gdk_fg_color(GtkWidget *wid, bool enabled)
  118 {
  119     GtkStyle* st;
  120 
  121     st=gtk_widget_get_style(wid);
  122     return gt_gdkcolor_to_color(&st->fg[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]);
  123 }
  124 
  125 void set_gdk_fg_color(GtkWidget *wid, gColor color)
  126 {
  127     set_color(wid, color, gtk_widget_modify_fg, true);
  128 }
  129 
  130 gColor get_gdk_bg_color(GtkWidget *wid, bool enabled)
  131 {
  132     GtkStyle* st;
  133 
  134     st=gtk_widget_get_style(wid);   
  135     return gt_gdkcolor_to_color(&st->bg[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]);
  136 }
  137 
  138 void set_gdk_bg_color(GtkWidget *wid,gColor color)
  139 {
  140     set_color(wid, color, gtk_widget_modify_bg, false);
  141 }
  142 
  143 gColor get_gdk_text_color(GtkWidget *wid, bool enabled)
  144 {
  145     GtkStyle* st;
  146 
  147     st=gtk_widget_get_style(wid);   
  148     return gt_gdkcolor_to_color(&st->text[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]);
  149 }
  150 
  151 void set_gdk_text_color(GtkWidget *wid,gColor color)
  152 {
  153     set_color(wid, color, gtk_widget_modify_text, true);
  154 }
  155 
  156 gColor get_gdk_base_color(GtkWidget *wid, bool enabled)
  157 {
  158     GtkStyle* st;
  159 
  160     st=gtk_widget_get_style(wid);
  161     return gt_gdkcolor_to_color(&st->base[enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE]);
  162 }
  163 
  164 void set_gdk_base_color(GtkWidget *wid,gColor color)
  165 {
  166     set_color(wid, color, gtk_widget_modify_base, false);
  167 }
  168 #endif
  169 
  170 void gt_color_to_rgb(gColor color, int *r, int *g, int *b)
  171 {
  172     *b = color & 0xFF;
  173     *g = (color >> 8) & 0xFF;
  174     *r = (color >> 16) & 0xFF;
  175 }
  176 
  177 gColor gt_rgb_to_color(int r, int g, int b)
  178 {
  179     return (gColor)(b | (g << 8) | (r << 16));
  180 }
  181 
  182 void gt_color_to_rgba(gColor color, int *r, int *g, int *b, int *a)
  183 {
  184     *b = color & 0xFF;
  185     *g = (color >> 8) & 0xFF;
  186     *r = (color >> 16) & 0xFF;
  187     *a = 255 - ((color >> 24) & 0xFF);
  188 }
  189 
  190 gColor gt_rgba_to_color(int r, int g, int b, int a)
  191 {
  192     return (gColor)(b | (g << 8) | (r << 16) | ((255 - a) << 24));
  193 }
  194 
  195 void gt_color_to_frgba(gColor color, double *r, double *g, double *b, double *a)
  196 {
  197     *b = (color & 0xFF) / 255.0;
  198     *g = ((color >> 8) & 0xFF) / 255.0;
  199     *r = ((color >> 16) & 0xFF) / 255.0;
  200     *a = 1 - ((color >> 24) & 0xFF) / 255.0;
  201 }
  202 
  203 #ifdef GTK3
  204 
  205 void gt_from_color(gColor color, GdkRGBA *rgba)
  206 {
  207     gt_color_to_frgba(color, &rgba->red, &rgba->green, &rgba->blue, &rgba->alpha);
  208 }
  209 
  210 gColor gt_to_color(GdkRGBA *rgba)
  211 {
  212     return gt_frgba_to_color(rgba->red, rgba->green, rgba->blue, rgba->alpha);
  213 }
  214 
  215 void gt_to_css_color(char *css, gColor color)
  216 {
  217     int r, g, b, a;
  218     char buffer[8];
  219     
  220     gt_color_to_rgba(color, &r, &g, &b, &a);
  221     if (a == 255)
  222         sprintf(css, "#%02X%02X%02X", r, g, b);
  223     else
  224     {
  225         sprintf(buffer, "%.3f", a / 255.0);
  226         sprintf(css, "rgba(%d,%d,%d,0.%s)", r, g, b, &buffer[2]);
  227     }
  228 }
  229 
  230 #endif
  231 
  232 gColor gt_frgba_to_color(double r, double g, double b, double a)
  233 {
  234     return gt_rgba_to_color((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0));
  235 }
  236 
  237 void gt_rgb_to_hsv(int r, int g, int b, int *H, int *S,int *V )
  238 {
  239     float R = (float)r, G = (float)g, B = (float)b;
  240     float v, x, f;
  241     int i;
  242 
  243     R/=255;
  244     G/=255;
  245     B/=255;
  246 
  247     x=R;
  248     if (G<x) x=G;
  249     if (B<x) x=B;
  250 
  251     v=R;
  252     if (G>v) v=G;
  253     if (B>v) v=B;
  254 
  255 
  256     if(v == x) {
  257         *H=-1;
  258         *S=0;
  259         *V = (int)(v * 255);
  260         return;
  261     }
  262 
  263     f = (R == x) ? G - B : ((G == x) ? B - R : R - G);
  264     i = (R == x) ? 3 : ((G == x) ? 5 : 1);
  265     *H=(int)((i - f /(v - x))*60);
  266     *S=(int)(((v - x)/v)*255);
  267     *V=(int)(v*255);
  268 }
  269 
  270 void gt_hsv_to_rgb(int h, int s, int v, int *R, int *G, int *B)
  271 {
  272      double H,S,V;
  273      double var_h,var_i,var_1,var_2,var_3,tmp_r,tmp_g,tmp_b;
  274 
  275     if (h < 0)
  276         h = 360 - ((-h) % 360);
  277     else
  278         h = h % 360;
  279 
  280      H=((double)h)/360;
  281      S=((double)s)/255;
  282      V=((double)v)/255;
  283 
  284     if ( S == 0 )
  285     {
  286         *R = (int)(V * 255);
  287         *G = (int)(V * 255);
  288         *B = (int)(V * 255);
  289     }
  290     else
  291     {
  292         var_h = H * 6;
  293         var_i = (int)var_h;
  294         var_1 = V * ( 1 - S );
  295         var_2 = V * ( 1 - S * ( var_h - var_i ) );
  296         var_3 = V * ( 1 - S * ( 1 - ( var_h - var_i ) ) );
  297 
  298         switch ((int)var_i)
  299         {
  300             case 0:
  301                 tmp_r = V;
  302                 tmp_g = var_3;
  303                 tmp_b = var_1;
  304                 break;
  305 
  306             case 1:
  307                 tmp_r = var_2;
  308                 tmp_g = V;
  309                 tmp_b = var_1;
  310                 break;
  311 
  312             case 2:
  313                 tmp_r = var_1;
  314                 tmp_g = V;
  315                 tmp_b = var_3;
  316                 break;
  317 
  318             case 3:
  319                 tmp_r = var_1;
  320                 tmp_g = var_2;
  321                 tmp_b = V;
  322                 break;
  323 
  324             case 4:
  325                 tmp_r = var_3;
  326                 tmp_g = var_1;
  327                 tmp_b = V;
  328                 break;
  329 
  330             default:
  331                 tmp_r = V;
  332                 tmp_g = var_1;
  333                 tmp_b = var_2;
  334                 break;
  335         }
  336 
  337         *R = (int)(tmp_r * 255);
  338         *G = (int)(tmp_g * 255);
  339         *B = (int)(tmp_b * 255);
  340 
  341     }
  342 }
  343 
  344 
  345 #if 0
  346 /*******************************************************************
  347 
  348  Border style in gWidgets that use a Frame to show it
  349  
  350  *******************************************************************/
  351 int Frame_getBorder(GtkFrame *fr)
  352 {
  353     switch (gtk_frame_get_shadow_type(fr))
  354     {
  355         case GTK_SHADOW_NONE: return BORDER_NONE;
  356         case GTK_SHADOW_ETCHED_OUT: return BORDER_PLAIN;
  357         case GTK_SHADOW_IN: return BORDER_SUNKEN;
  358         case GTK_SHADOW_OUT: return BORDER_RAISED;
  359         case GTK_SHADOW_ETCHED_IN: return BORDER_ETCHED;
  360     }
  361     
  362     return BORDER_NONE;
  363 }
  364 
  365 void Frame_setBorder(GtkFrame *fr,int vl)
  366 {
  367     switch(vl)
  368     {
  369         case BORDER_NONE:
  370             gtk_frame_set_shadow_type(fr,GTK_SHADOW_NONE);
  371             break;
  372         case BORDER_PLAIN:
  373             gtk_frame_set_shadow_type(fr,GTK_SHADOW_ETCHED_OUT);
  374             break;
  375         case BORDER_SUNKEN:
  376             gtk_frame_set_shadow_type(fr,GTK_SHADOW_IN);
  377             break;
  378         case BORDER_RAISED:
  379             gtk_frame_set_shadow_type(fr,GTK_SHADOW_OUT);
  380             break;
  381         case BORDER_ETCHED:
  382             gtk_frame_set_shadow_type(fr,GTK_SHADOW_ETCHED_IN);
  383             break;
  384         default: return;
  385     }
  386 }
  387 #endif
  388 
  389 /*************************************************************
  390 
  391  Takes a snapshot of a GdkWindow
  392  
  393  *************************************************************/
  394 
  395 gPicture *gt_grab_window(GdkWindow *win, int x, int y, int w, int h)
  396 {
  397     int ow, oh;
  398     int ww, hh;
  399     int dx, dy;
  400     GdkPixbuf *buf = NULL;
  401     gPicture *pic;
  402     
  403 #ifdef GTK3
  404     gdk_window_get_geometry(win, NULL, NULL, &ww, &hh);
  405 #else
  406     gdk_window_get_geometry(win, 0, 0, &ww, &hh, 0);
  407 #endif
  408     
  409     if (w <= 0 || h <= 0)
  410     {
  411         w = ww;
  412         h = hh;
  413     }
  414     
  415     dx = dy = 0;
  416     ow = w;
  417     oh = h;
  418     
  419     if (x < 0)
  420     {
  421         w += x;
  422         dx = -x;
  423         x = 0;
  424     }
  425     
  426     if (y < 0)
  427     {
  428         h += y;
  429         dy = -y;
  430         y = 0;
  431     }
  432     
  433     if ((x + w) > ww)
  434         w = ww - x;
  435     
  436     if ((y + h) > hh)
  437         h = hh - y;
  438     
  439     if (w > 0 && h > 0)
  440     {
  441 #ifdef GTK3
  442         buf = gdk_pixbuf_get_from_window(win, x, y, w, h);
  443 #else
  444         buf = gdk_pixbuf_get_from_drawable(NULL, win, NULL, x, y, 0, 0, w, h);
  445 #endif
  446     }
  447     
  448     if (w == ow && h == oh)
  449         return new gPicture(buf);
  450         
  451     pic = new gPicture(gPicture::PIXBUF, ow, oh, false);
  452     pic->fill(0);
  453     
  454     if (w <= 0 || h <= 0)
  455         return pic;
  456     
  457     gdk_pixbuf_copy_area(buf, 0, 0, w, h, pic->getPixbuf(), dx, dy);
  458     return pic;
  459 }
  460 
  461 /*************************************************************
  462 
  463  GtkLabel with accelerators
  464  
  465  *************************************************************/
  466 
  467 void gMnemonic_correctText(char *st,char **buf)
  468 {
  469     int bucle,b2;
  470     
  471     if (!st || !*st)
  472     {
  473         *buf = g_strdup("");
  474         return;
  475     }
  476     
  477     int len = strlen(st);
  478     int len_in = len;
  479     
  480     for (bucle=0;bucle<len_in;bucle++)
  481     {
  482         if (st[bucle]=='&')
  483         {
  484             if (bucle<(len_in-1)) 
  485                 if (st[bucle+1]=='&')
  486                     len--;
  487                 
  488         }
  489         else if (st[bucle]=='_') len++;
  490     }
  491     
  492     *buf=(char*)g_malloc(sizeof(char)*(len+1));
  493     b2=0;
  494     
  495     for (bucle=0;bucle<len_in;bucle++)
  496     {
  497         if (st[bucle]=='&')
  498         {
  499             if (bucle<(len_in-1))
  500             {
  501                 if (st[bucle+1]=='&')
  502                 {
  503                     (*buf)[b2++]='&';
  504                     bucle++;
  505                 }
  506                 else
  507                 {
  508                     (*buf)[b2++]='_';
  509                 }
  510             }
  511             else
  512             {
  513                 (*buf)[b2]=' ';
  514                 b2++;
  515             }
  516         }
  517         else if (st[bucle]=='_')
  518         {
  519             (*buf)[b2++]='_';
  520             (*buf)[b2++]='_';
  521         }
  522         else
  523         {
  524             (*buf)[b2++]=st[bucle];
  525         }   
  526         (*buf)[b2]=0;
  527     }
  528 }
  529 
  530 guint gMnemonic_correctMarkup(char *st,char **buf)
  531 {
  532     int bucle,b2;
  533     guint retval=0; 
  534 
  535     if (!st || !*st)
  536     {
  537         *buf = g_strdup("");
  538         return retval;
  539     }
  540     
  541     int len = strlen(st);
  542     int len_in = len;
  543     
  544     for (bucle=0;bucle<len_in;bucle++)
  545     {
  546         if (st[bucle]=='&')
  547         {
  548             if (bucle<(len_in-1)) 
  549             {
  550                 if (st[bucle+1]=='&') len+-3;
  551                 else                  len+=6;
  552             }
  553             else
  554             {
  555                 len+=4;
  556             }
  557                 
  558         }
  559         else if (st[bucle]=='<') 
  560         {
  561             len+=3;
  562         }
  563         else if (st[bucle]=='>')
  564         {
  565             len+=3;
  566         }
  567     }
  568     
  569     *buf=(char*)g_malloc(sizeof(char)*(len+1));
  570     (*buf[0])=0;
  571     b2=0;
  572     
  573     for (bucle=0;bucle<len_in;bucle++)
  574     {
  575         if (st[bucle]=='&')
  576         {
  577             if (bucle<(len_in-1))
  578             {
  579                 if (st[bucle+1]=='&')
  580                 {
  581                     (*buf)[b2++]='&';
  582                     (*buf)[b2++]='a';
  583                     (*buf)[b2++]='m';
  584                     (*buf)[b2++]='p';
  585                     (*buf)[b2++]=';';
  586                     bucle++;
  587                 }
  588                 else
  589                 {
  590                     bucle++;
  591                     retval=(guint)st[bucle];
  592                     (*buf)[b2++]='<';
  593                     (*buf)[b2++]='u';
  594                     (*buf)[b2++]='>';
  595                     (*buf)[b2++]=st[bucle];
  596                     (*buf)[b2++]='<';
  597                     (*buf)[b2++]='/';
  598                     (*buf)[b2++]='u';
  599                     (*buf)[b2++]='>';
  600                 }
  601             }
  602             else
  603             {
  604                 (*buf)[b2++]='&';
  605                 (*buf)[b2++]='a';
  606                 (*buf)[b2++]='m';
  607                 (*buf)[b2++]='p';
  608                 (*buf)[b2++]=';';
  609             }
  610         }
  611         else if (st[bucle]=='<')
  612         {
  613             (*buf)[b2++]='&';
  614             (*buf)[b2++]='l';
  615             (*buf)[b2++]='t';
  616             (*buf)[b2++]=';';
  617         }
  618         else if (st[bucle]=='>')
  619         {
  620             (*buf)[b2++]='&';
  621             (*buf)[b2++]='g';
  622             (*buf)[b2++]='t';
  623             (*buf)[b2++]=';';
  624         }
  625         else
  626         {
  627             (*buf)[b2++]=st[bucle];
  628         }   
  629         (*buf)[b2]=0;
  630     }
  631 
  632     return retval;
  633 }
  634 
  635 void gMnemonic_returnText(char *st,char **buf)
  636 {
  637     int bucle,b2;
  638     
  639     if (!st || !*st)
  640     {
  641         *buf = g_strdup("");
  642         return;
  643     }
  644         
  645     int len = strlen(st);
  646     int len_in = len;
  647     
  648     for (bucle=0;bucle<len_in;bucle++)
  649     {
  650         if (st[bucle]=='_')
  651         {
  652             if (bucle<(len_in-1)) 
  653                 if (st[bucle+1]=='_')
  654                     len--;
  655                 
  656         }
  657         else if (st[bucle]=='&') len++;
  658     }
  659     
  660     *buf=(char*)g_malloc(sizeof(char)*(len+1));
  661     b2=0;
  662     
  663     for (bucle=0;bucle<len_in;bucle++)
  664     {
  665         if (st[bucle]=='_')
  666         {
  667             if (bucle<(len_in-1))
  668             {
  669                 if (st[bucle+1]=='_')
  670                 {
  671                     (*buf)[b2++]='&';
  672                     bucle++;
  673                 }
  674                 else
  675                 {
  676                     (*buf)[b2++]='_';
  677                 }
  678             }
  679             else
  680             {
  681                 (*buf)[b2]=' ';
  682                 b2++;
  683             }
  684         }
  685         else if (st[bucle]=='&')
  686         {
  687             (*buf)[b2++]='&';
  688             (*buf)[b2++]='&';
  689         }
  690         else
  691         {
  692             (*buf)[b2++]=st[bucle];
  693         }   
  694         (*buf)[b2]=0;
  695     }
  696 }
  697 
  698 void g_stradd(gchar **res, const gchar *s)
  699 {
  700   if (!*res)
  701     *res = g_strdup(s);
  702   else
  703   {
  704     gchar *old = *res;
  705     *res = g_strconcat(*res, s, (void *)NULL);
  706     g_free(old);
  707   }
  708 }
  709 
  710 void gt_shortcut_parse(char *shortcut, guint *key, GdkModifierType *mods)
  711 {
  712     char **tokens;
  713     char *token;
  714     int i;
  715     int m;
  716     
  717     *key = 0;
  718     *mods = (GdkModifierType)0;
  719     
  720     m = 0;
  721     
  722     if (!shortcut || !*shortcut)
  723         return;
  724     
  725     tokens = g_strsplit(shortcut, "+", 0);
  726     
  727     i = 0;
  728     while (tokens[i])
  729     {
  730         g_strstrip(tokens[i]);
  731         i++;
  732     }
  733     
  734     for (i = 0; (token = tokens[i]); i++)
  735     {
  736         if (!strcasecmp(token, "ctrl") || !strcasecmp(token, "control"))
  737             m |= GDK_CONTROL_MASK;
  738         else if (!strcasecmp(token, "shift"))
  739             m |= GDK_SHIFT_MASK;
  740         else if (!strcasecmp(token, "alt"))
  741             m |= GDK_MOD1_MASK;
  742         else
  743         {
  744           *key = KEY_get_keyval_from_name(token);
  745             *mods = (GdkModifierType)m;
  746             break;
  747         }
  748     }
  749     
  750     g_strfreev(tokens);
  751     
  752     //fprintf(stderr, "gt_shortcut_parse: %s -> %d / %d\n", shortcut, *key, m);
  753 }
  754 
  755 #define MAX_FREE_LATER 16
  756 
  757 static char *_free_later_ptr[MAX_FREE_LATER] = { 0 };
  758 static int _free_later_index = 0;
  759 
  760 char *gt_free_later(char *ptr)
  761 {
  762     if (_free_later_ptr[_free_later_index])
  763         g_free(_free_later_ptr[_free_later_index]);
  764     
  765     _free_later_ptr[_free_later_index] = ptr;
  766     
  767     _free_later_index++;
  768     
  769     if (_free_later_index >= MAX_FREE_LATER)
  770         _free_later_index = 0;
  771         
  772     return ptr;
  773 }
  774 
  775 static void free_all_later()
  776 {
  777     int i;
  778     char *p;
  779     
  780     for (i = 0; i < MAX_FREE_LATER; i++)
  781     {
  782         p = _free_later_ptr[i];
  783         if (p)
  784         {
  785             g_free(p);
  786             _free_later_ptr[i] = NULL;
  787         }
  788     }
  789 }
  790 
  791 void gt_exit()
  792 {
  793     free_all_later();
  794 }
  795 
  796 static const char *html_entity(char c)
  797 {
  798     static char same[2];
  799     
  800     if (c == '<')
  801         return "&lt;";
  802     else if (c == '>')
  803         return "&gt;";
  804     else if (c == '&')
  805         return "&amp;";
  806     
  807     same[0] = c;
  808     same[1] = 0;
  809     return (const char *)same;
  810 }
  811 
  812 static void add_space(GString *str)
  813 {
  814     char c;
  815     
  816     if (str->len == 0)
  817         return;
  818         
  819     c = str->str[str->len - 1];
  820     if (c != ' ' && c != '\n')
  821         g_string_append_c(str, ' ');
  822 }
  823 
  824 static void add_paragraph_break(GString *str)
  825 {
  826     int i;
  827     char c;
  828     bool markup = false;
  829     int nl = 0;
  830     
  831     for (i = str->len - 1; i >= 0; i--)
  832     {
  833         c = str->str[i];
  834         if (markup)
  835         {
  836             if (c == '<')
  837                 markup = false;
  838             continue;
  839         }
  840         if (c == '>')
  841         {
  842             markup = true;
  843             continue;
  844         }
  845         if (c != '\n')
  846             break;
  847         nl++;
  848         if (nl >= 2)
  849             break;
  850     }
  851     
  852     if (nl == 2 || i < 0)
  853         return;
  854     
  855     while (nl < 2)
  856     {
  857         g_string_append_c(str, '\n');
  858         nl++;
  859     }
  860 }
  861 
  862 static void add_attr(GString *pango, const char *attr, const char *value)
  863 {
  864     bool add_quote = *value != '"' && *value !='\'';
  865     
  866     g_string_append_c(pango, ' ');
  867     g_string_append(pango, attr);
  868     g_string_append_c(pango, '=');
  869     if (add_quote) g_string_append_c(pango, '"');
  870     g_string_append(pango, value);
  871     if (add_quote) g_string_append_c(pango, '"');
  872 }
  873 
  874 char *gt_html_to_pango_string(const char *html, int len_html, bool newline_are_break)
  875 {
  876     static const char *title[] =
  877     {
  878         "h1", "size=\"xx-large\"",
  879         "h2", "size=\"x-large\"",
  880         "h3", "size=\"large\"",
  881         "h4", "size=\"medium\"",
  882         "h5", "size=\"small\"",
  883         "h6", "size=\"x-small\"",
  884         NULL, NULL
  885     };
  886     
  887     static const char *accept[] = { "b", "big", "i", "s", "sub", "sup", "small", "tt", "u", NULL };
  888     
  889     static const char *size_name[] = { "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" };
  890     static const int size_stack_len = 16;
  891     
  892     GString *pango = g_string_new("");
  893     const char *p, *p_end, *p_markup;
  894     char c;
  895     const char *token;
  896     const char *markup;
  897     GB_ARRAY attr_array;
  898     char **attr;
  899     int attr_count;
  900     const char *pp;
  901     gsize len;
  902     bool start_token = false;
  903     bool end_token = false;
  904     const char **pt;
  905     int size = 3; // medium
  906     char size_stack[size_stack_len];
  907     int size_stack_ptr = 0;
  908     int i;
  909     int mode_pre = 0;
  910     
  911     //fprintf(stderr, "gt_html_to_pango_string: %.*s\n", len_html, html);
  912     
  913     p_end = &html[len_html < 0 ? strlen(html) : len_html];
  914     p_markup = NULL;
  915     
  916     if (len_html == 0)
  917         goto RETURN_STRING;
  918     
  919 #if PANGO_VERSION_CHECK(1, 41, 0)
  920 #else
  921     // Sometimes the first markup is not taken into account.
  922     // This is a workaround for this bug:
  923     g_string_append_unichar(pango, 0xFEFF);
  924 #endif
  925     
  926     for (p = html;; p++)
  927     {
  928         c = *p;
  929         if (!c)
  930             break;
  931             
  932         if (c == '<')
  933         {
  934             if (p[1] == '/')
  935             {
  936                 p_markup = &p[2];
  937                 start_token = false;
  938                 end_token = true;
  939             }
  940             else
  941             {
  942                 p_markup = &p[1];
  943                 start_token = true;
  944                 end_token = false;
  945             }
  946             continue;
  947         }
  948         
  949         if (c == '>')
  950         {
  951             if (!p_markup)
  952             {
  953                 g_string_append(pango, "&gt;");
  954                 continue;
  955             }
  956 
  957             len = p - p_markup;
  958 
  959             if (p[-1] == '/')
  960             {
  961                 if (len > 0)
  962                     len--;
  963                 end_token = true;
  964             }
  965 
  966             if (len <= 0)
  967             {
  968                 g_string_append(pango, "&lt;");
  969                 if (end_token) g_string_append_c(pango, '/');
  970                 g_string_append(pango, "&gt;");
  971                 p_markup = NULL;
  972                 continue;
  973             }
  974             
  975             markup = g_strndup(p_markup, len);
  976             
  977             attr_array = GB.String.Split(markup, len, " ", 1, "\"", 1, FALSE, TRUE);
  978             attr_count = GB.Array.Count(attr_array);
  979             attr = (char **)GB.Array.Get(attr_array, 0);
  980             token = NULL;
  981             
  982             for (i = 0; i < attr_count; i++)
  983             {
  984                 if (attr[i][0])
  985                 {
  986                     token = attr[i];
  987                     break;
  988                 }
  989             }
  990             
  991             if (!token)
  992                 goto __FOUND_TOKEN;
  993             
  994             for (pt = title; *pt; pt += 2)
  995             {
  996                 if (!strcasecmp(*pt, token))
  997                 {
  998                     if (start_token && !end_token)
  999                     {
 1000                         add_paragraph_break(pango);
 1001                         g_string_append_printf(pango, "<span %s><b>", pt[1]);
 1002                     }
 1003                     else if (end_token && !start_token)
 1004                     {
 1005                         g_string_append_printf(pango, "</b></span>");
 1006                         add_paragraph_break(pango);
 1007                         //g_string_append(pango, "<span size=\"smaller\">\n</span>");
 1008                     }
 1009                     goto __FOUND_TOKEN;
 1010                 }
 1011             }
 1012             
 1013             for (pt = accept; *pt; pt++)
 1014             {
 1015                 if (!strcasecmp(*pt, token))
 1016                 {
 1017                     if (start_token != end_token)
 1018                         g_string_append_printf(pango, "<%s%s>", end_token ? "/" : "", *pt);
 1019                     goto __FOUND_TOKEN;
 1020                 }
 1021             }
 1022             
 1023             if (!strcasecmp(token, "br") || !strcasecmp(token, "hr"))
 1024             {
 1025                 if (start_token)
 1026                 {
 1027                     g_string_append_c(pango, '\n');
 1028                 }
 1029                 goto __FOUND_TOKEN;
 1030             }
 1031             
 1032             if (!strcasecmp(token, "p"))
 1033             {
 1034                 add_paragraph_break(pango);
 1035                 goto __FOUND_TOKEN;
 1036             }
 1037             
 1038             if (!strcasecmp(token, "font"))
 1039             {
 1040                 if (end_token && !start_token)
 1041                 {
 1042                     if (size_stack_ptr > 0)
 1043                     {
 1044                         size_stack_ptr--;
 1045                         if (size_stack_ptr < size_stack_len)
 1046                             size = size_stack[size_stack_ptr];
 1047                     }
 1048                     g_string_append(pango, "</span>");
 1049                 }
 1050                 else if (start_token && !end_token)
 1051                 {
 1052                     if (size_stack_ptr < size_stack_len)
 1053                         size_stack[size_stack_ptr] = size;
 1054                     size_stack_ptr++;
 1055 
 1056                     g_string_append(pango, "<span");
 1057                     for (i = 0; i < attr_count; i++)
 1058                     {
 1059                         if (!strncasecmp(attr[i], "color=", 6))
 1060                         {
 1061                             add_attr(pango, "foreground", attr[i] + 6);
 1062                         }
 1063                         else if (!strncasecmp(attr[i], "face=", 5))
 1064                         {
 1065                             add_attr(pango, "face", attr[i] + 5);
 1066                         }
 1067                         else if (!strncasecmp(attr[i], "size=", 5))
 1068                         {
 1069                             g_string_append(pango, " size=");
 1070                             pp = attr[i] + 6;
 1071                             if (*pp == '"')
 1072                                 pp++;
 1073                             if (isdigit(*pp))
 1074                             {
 1075                                 size = *pp - '1';
 1076                                 pp++;
 1077                             }
 1078                             else if (*pp == '+' && isdigit(pp[1]))
 1079                             {
 1080                                 size += pp[1] - '0';
 1081                                 pp += 2;
 1082                             }
 1083                             else if (*pp == '-' && isdigit(pp[1]))
 1084                             {
 1085                                 size -= pp[1] - '0';
 1086                                 pp += 2;
 1087                             }
 1088                             
 1089                             if (size < 0)
 1090                                 size = 0;
 1091                             else if (size > 6)
 1092                                 size = 6;
 1093                             
 1094                             g_string_append_c(pango, '"');
 1095                             g_string_append(pango, size_name[size]);
 1096                             g_string_append_c(pango, '"');
 1097                         }
 1098                     }
 1099                     g_string_append_c(pango, '>');
 1100                 }
 1101                 goto __FOUND_TOKEN;
 1102             }
 1103             
 1104             if (!strcasecmp(token, "a") || !strncasecmp(token, "a href", 6))
 1105             {
 1106                 if (start_token && !end_token)
 1107                     g_string_append(pango, "<span foreground=\"blue\"><u>");
 1108                 else if (end_token && !start_token)
 1109                     g_string_append(pango, "</u></span>");
 1110                 goto __FOUND_TOKEN;
 1111             }
 1112             
 1113             if (!strcasecmp(token, "code"))
 1114             {
 1115                 if (start_token && !end_token)
 1116                     g_string_append(pango, "<tt>");
 1117                 else if (end_token && !start_token)
 1118                     g_string_append(pango, "</tt>");
 1119                 goto __FOUND_TOKEN;
 1120             }
 1121             
 1122             if (!strcasecmp(token, "pre"))
 1123             {
 1124                 if (start_token && !end_token)
 1125                 {
 1126                     add_paragraph_break(pango);
 1127                     g_string_append(pango, "<tt>");
 1128                     mode_pre++;
 1129                 }
 1130                 else if (end_token && !start_token)
 1131                 {
 1132                     mode_pre--;
 1133                     g_string_append(pango, "</tt>");
 1134                     add_paragraph_break(pango);
 1135                 }
 1136                 goto __FOUND_TOKEN;
 1137             }
 1138             
 1139             g_string_append(pango, "&lt;");
 1140             if (end_token) g_string_append(pango, "/");
 1141             while (p_markup < p)
 1142             {
 1143                 g_string_append(pango, html_entity(*p_markup));
 1144                 p_markup++;
 1145             }
 1146             g_string_append(pango, "&gt;");
 1147 
 1148         __FOUND_TOKEN:
 1149         
 1150             GB.Unref(POINTER(&attr_array));
 1151             p_markup = NULL;
 1152             continue;   
 1153         }
 1154         
 1155         if (c == '&')
 1156         {
 1157             const char *entity_start = ++p;
 1158             int entity_len;
 1159             int entity_unicode;
 1160             char *endptr;
 1161             
 1162 //          if ((p_end - p) >= 6 && !strncasecmp(p, "&nbsp;", 6))
 1163 //          {
 1164 //              g_string_append(pango, " ");
 1165 //              p += 5;
 1166 //              continue;
 1167 //          }
 1168             while (p < p_end && *p != ';' && ((uchar)*p) > ' ')
 1169                 p++;
 1170                 
 1171             entity_len = p - entity_start;
 1172             if (*p != ';')
 1173                 entity_len = 0;
 1174                 
 1175             if (entity_len > 1)
 1176             {
 1177                 if ((entity_len == 3 && !strncasecmp(entity_start, "amp", 3))
 1178                       || (entity_len == 2 && (!strncasecmp(entity_start, "lt", 2) || !strncasecmp(entity_start, "gt", 2))))
 1179                 {
 1180                     g_string_append_len(pango, entity_start - 1, entity_len + 2);
 1181                     continue;
 1182                 }
 1183                 else
 1184                 {
 1185                     entity_unicode = -1;
 1186                     endptr = NULL;
 1187                     errno = 0;
 1188 
 1189                     if (*entity_start == '#')
 1190                     {
 1191                         if (entity_start[1] == 'x')
 1192                         {
 1193                             entity_unicode = strtol(&entity_start[2], &endptr, 16);
 1194                             if (entity_unicode <= 0)
 1195                                 entity_unicode = -1;
 1196                         }
 1197                         else if (isdigit(entity_start[1]))
 1198                         {
 1199                             entity_unicode = strtol(&entity_start[1], &endptr, 10);
 1200                             if (entity_unicode <= 0)
 1201                                 entity_unicode = -1;
 1202                         }
 1203                     }
 1204                     else
 1205                     {
 1206                         const struct entity *e = kde_findEntity(entity_start, entity_len);
 1207                         if (e)
 1208                             entity_unicode = e->code;
 1209                     }
 1210 
 1211                     if (entity_unicode >= 0)
 1212                     {
 1213                         g_string_append_unichar(pango, entity_unicode);
 1214                         continue;
 1215                     }
 1216                 }
 1217             }
 1218             
 1219             g_string_append(pango, "&amp;");
 1220             p = entity_start - 1;
 1221             continue;
 1222         }
 1223     
 1224         if (!p_markup)
 1225         {
 1226             if (c == '\n' && !newline_are_break && mode_pre == 0)
 1227             {
 1228                 add_space(pango);
 1229                 continue;
 1230             }
 1231             
 1232             if (c == '\r')
 1233             {
 1234                 add_space(pango);
 1235                 continue;
 1236             }
 1237         
 1238             if (c == ' ')
 1239             {
 1240                 if (mode_pre)
 1241                     g_string_append_unichar(pango, 160);
 1242                 else
 1243                     add_space(pango);
 1244                 continue;
 1245             }
 1246             
 1247             g_string_append(pango, html_entity(*p));
 1248         }
 1249     }
 1250     
 1251 RETURN_STRING:
 1252     
 1253     p = g_string_free(pango, false);
 1254     //fprintf(stderr, "==> '%s'\n", p);
 1255     return (char *)p;
 1256 }
 1257 
 1258 int gt_to_alignment(double halign, double valign)
 1259 {
 1260     int align = 0;
 1261     
 1262     if (halign == 0.0)
 1263         align += 0x1;
 1264     else if (halign == 0.5)
 1265         align += 0x2;
 1266     else if (halign == 1.0)
 1267         align += 0x3;
 1268         
 1269     if (valign == 0.0)
 1270         align += 0x10;
 1271     else if (valign == 1.0)
 1272         align += 0x20;
 1273         
 1274     return align;
 1275 }
 1276 
 1277 double gt_from_alignment(int align, bool vertical)
 1278 {
 1279     if (vertical)
 1280     {
 1281         switch(align & 0xF0)
 1282         {
 1283             case 0x10: return 0.0;
 1284             case 0x20: return 1.0;
 1285             default: return 0.5;
 1286         }
 1287     }
 1288     else
 1289     {
 1290         switch (align & 0x0F)
 1291         {
 1292             case 1: return 0.0;
 1293             case 2: return 1.0;
 1294             case 3: return 0.5;
 1295             default: return gDesktop::rightToLeft() ? 1.0 : 0.0;
 1296         }
 1297     }
 1298 }
 1299 
 1300 
 1301 void gt_ensure_visible(GtEnsureVisible *arg, int x, int y, int w, int h)
 1302 {
 1303     w = (w + 1) / 2;
 1304     h = (h + 1) / 2;
 1305     x += w;
 1306     y += h;
 1307 
 1308     int pw = arg->clientWidth;
 1309     int ph = arg->clientHeight;
 1310 
 1311     int cx = -arg->scrollX;
 1312     int cy = -arg->scrollY;
 1313     int cw = arg->scrollWidth;
 1314     int ch = arg->scrollHeight;
 1315 
 1316     if ( pw < w*2 )
 1317             w=pw/2;
 1318     if ( ph < h*2 )
 1319             h=ph/2;
 1320 
 1321     if ( cw <= pw ) {
 1322             w=0;
 1323             cx=0;
 1324     }
 1325     if ( ch <= ph ) {
 1326             h=0;
 1327             cy=0;
 1328     }
 1329 
 1330     if ( x < -cx+w )
 1331             cx = -x+w;
 1332     else if ( x >= -cx+pw-w )
 1333             cx = -x+pw-w;
 1334 
 1335     if ( y < -cy+h )
 1336             cy = -y+h;
 1337     else if ( y >= -cy+ph-h )
 1338             cy = -y+ph-h;
 1339 
 1340     if ( cx > 0 )
 1341             cx=0;
 1342     else if ( cx < pw-cw && cw>pw )
 1343             cx=pw-cw;
 1344 
 1345     if ( cy > 0 )
 1346             cy=0;
 1347     else if ( cy < ph-ch && ch>ph )
 1348             cy=ph-ch;
 1349 
 1350     arg->scrollX = -cx;
 1351     arg->scrollY = -cy;
 1352 }
 1353 
 1354 #ifdef GTK3
 1355 #else
 1356 void gt_pixmap_fill(GdkPixmap *pix, gColor col, GdkGC *gc)
 1357 {
 1358     GdkColor color;
 1359     int w, h;
 1360     bool free_gc = false;
 1361     
 1362     fill_gdk_color(&color, col);
 1363     
 1364     #if GTK_CHECK_VERSION(2, 24, 0)
 1365     gdk_pixmap_get_size(pix, &w, &h);
 1366     #else
 1367     gdk_drawable_get_size(GDK_DRAWABLE(pix), &w, &h);
 1368     #endif
 1369     
 1370     if (!gc)
 1371     {
 1372         gc = gdk_gc_new(pix);
 1373         free_gc = true;
 1374     }
 1375     
 1376     gdk_gc_set_foreground(gc, &color);
 1377     gdk_gc_set_background(gc, &color);
 1378     gdk_draw_rectangle(pix, gc, true, 0, 0, w, h);
 1379     
 1380     if (free_gc)
 1381         g_object_unref(gc);
 1382 }
 1383 
 1384 void gt_pixbuf_render_pixmap_and_mask(GdkPixbuf *pixbuf, GdkPixmap **pixmap_return, GdkBitmap **mask_return, int alpha_threshold)
 1385 {
 1386     GdkColormap *colormap = gdk_rgb_get_colormap();
 1387   GdkScreen *screen;
 1388 
 1389   screen = gdk_colormap_get_screen (colormap);
 1390   
 1391   if (pixmap_return)
 1392     {
 1393         GdkGC *gc;
 1394         *pixmap_return = gdk_pixmap_new (gdk_screen_get_root_window (screen),
 1395                             gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
 1396                             gdk_colormap_get_visual (colormap)->depth);
 1397         
 1398         gdk_drawable_set_colormap (GDK_DRAWABLE (*pixmap_return), colormap);
 1399         
 1400         gc = gdk_gc_new(*pixmap_return);
 1401         
 1402         gt_pixmap_fill(*pixmap_return, 0, gc);
 1403         
 1404         gdk_draw_pixbuf (*pixmap_return, gc, pixbuf, 
 1405                     0, 0, 0, 0,
 1406                     gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
 1407                     GDK_RGB_DITHER_NORMAL,
 1408                     0, 0);
 1409         
 1410         g_object_unref(gc);
 1411     }
 1412   
 1413   if (mask_return)
 1414     {
 1415         if (gdk_pixbuf_get_has_alpha (pixbuf))
 1416         {
 1417             *mask_return = gdk_pixmap_new (gdk_screen_get_root_window (screen),
 1418                             gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), 1);
 1419         
 1420             gdk_pixbuf_render_threshold_alpha (pixbuf, *mask_return,
 1421                                     0, 0, 0, 0,
 1422                                     gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
 1423                                     alpha_threshold);
 1424         }
 1425         else
 1426             *mask_return = NULL;
 1427     }
 1428 }
 1429 #endif
 1430 
 1431 #if 0
 1432 void gt_pixbuf_replace_color(GdkPixbuf *pixbuf, gColor src, gColor dst, bool noteq)
 1433 {
 1434     guint32 *p;
 1435     int i, n;
 1436     
 1437     p = (guint32 *)gdk_pixbuf_get_pixels(pixbuf);
 1438     n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf);
 1439 
 1440     src ^= 0xFF000000;
 1441     dst ^= 0xFF000000;
 1442     
 1443     if (noteq)
 1444     {
 1445         for (i = 0; i < n; i++, p ++)
 1446         {
 1447             if (*p != src)
 1448                 *p = dst;
 1449         }
 1450     }
 1451     else
 1452     {
 1453         for (i = 0; i < n; i++, p ++)
 1454         {
 1455             if (*p == src)
 1456                 *p = dst;
 1457         }
 1458     }
 1459 }
 1460 #endif
 1461 
 1462 // Comes from the GIMP
 1463 
 1464 typedef
 1465     struct {
 1466         float r;
 1467         float b;
 1468         float g;
 1469         float a;
 1470         }
 1471     RGB;
 1472 
 1473 static void color_to_alpha(RGB *src, const RGB *color)
 1474 {
 1475   RGB alpha;
 1476 
 1477   alpha.a = src->a;
 1478 
 1479   if (color->r < 0.0001)
 1480     alpha.r = src->r;
 1481   else if (src->r > color->r)
 1482     alpha.r = (src->r - color->r) / (1.0 - color->r);
 1483   else if (src->r < color->r)
 1484     alpha.r = (color->r - src->r) / color->r;
 1485   else alpha.r = 0.0;
 1486 
 1487   if (color->g < 0.0001)
 1488     alpha.g = src->g;
 1489   else if (src->g > color->g)
 1490     alpha.g = (src->g - color->g) / (1.0 - color->g);
 1491   else if (src->g < color->g)
 1492     alpha.g = (color->g - src->g) / (color->g);
 1493   else alpha.g = 0.0;
 1494 
 1495   if (color->b < 0.0001)
 1496     alpha.b = src->b;
 1497   else if (src->b > color->b)
 1498     alpha.b = (src->b - color->b) / (1.0 - color->b);
 1499   else if (src->b < color->b)
 1500     alpha.b = (color->b - src->b) / (color->b);
 1501   else alpha.b = 0.0;
 1502 
 1503   if (alpha.r > alpha.g)
 1504     {
 1505       if (alpha.r > alpha.b)
 1506         {
 1507           src->a = alpha.r;
 1508         }
 1509       else
 1510         {
 1511           src->a = alpha.b;
 1512         }
 1513     }
 1514   else if (alpha.g > alpha.b)
 1515     {
 1516       src->a = alpha.g;
 1517     }
 1518   else
 1519     {
 1520       src->a = alpha.b;
 1521     }
 1522 
 1523   if (src->a < 0.0001)
 1524     return;
 1525 
 1526   src->r = (src->r - color->r) / src->a + color->r;
 1527   src->g = (src->g - color->g) / src->a + color->g;
 1528   src->b = (src->b - color->b) / src->a + color->b;
 1529 
 1530   src->a *= alpha.a;
 1531 }
 1532 
 1533 void gt_pixbuf_make_alpha(GdkPixbuf *pixbuf, gColor color)
 1534 {
 1535     guchar *p;
 1536     int i, n;
 1537     RGB rgb_color, rgb_src;
 1538     int r, g, b;
 1539     
 1540     p = gdk_pixbuf_get_pixels(pixbuf);
 1541     n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf);
 1542 
 1543     gt_color_to_rgb(color, &r, &g, &b);
 1544 
 1545     rgb_color.b = b / 255.0;
 1546     rgb_color.g = g / 255.0;
 1547     rgb_color.r = r / 255.0;
 1548     rgb_color.a = 1.0;
 1549 
 1550     for (i = 0; i < n; i++, p += 4)
 1551     {
 1552         rgb_src.r = p[0] / 255.0;
 1553         rgb_src.g = p[1] / 255.0;
 1554         rgb_src.b = p[2] / 255.0;
 1555         rgb_src.a = p[3] / 255.0;
 1556         
 1557         color_to_alpha(&rgb_src, &rgb_color);
 1558         
 1559         p[0] = rgb_src.r * 255.0 + 0.5;
 1560         p[1] = rgb_src.g * 255.0 + 0.5;
 1561         p[2] = rgb_src.b * 255.0 + 0.5;
 1562         //p[3] = 0xFF ^ (uchar)(rgb_src.a * 255.0 + 0.5);
 1563         p[3] = rgb_src.a * 255.0 + 0.5;
 1564     }
 1565 }
 1566 
 1567 void gt_pixbuf_make_gray(GdkPixbuf *pixbuf)
 1568 {
 1569     guchar *p;
 1570     int i, n;
 1571     
 1572     p = gdk_pixbuf_get_pixels(pixbuf);
 1573     n = gdk_pixbuf_get_width(pixbuf) * gdk_pixbuf_get_height(pixbuf);
 1574 
 1575     for (i = 0; i < n; i++, p += 4)
 1576         p[0] = p[1] = p[2] = (p[0] * 11 + p[1] * 16 + p[2] * 5) / 32;
 1577 }
 1578 
 1579 GdkPixbuf *gt_pixbuf_create_disabled(GdkPixbuf *img)
 1580 {
 1581   gint w, h;
 1582   guchar *r, *g, *b, *end;
 1583   GdkPixbuf *dimg;
 1584 
 1585   dimg = gdk_pixbuf_copy(img);
 1586   w = gdk_pixbuf_get_width(dimg);
 1587   h = gdk_pixbuf_get_height(dimg);
 1588   r = gdk_pixbuf_get_pixels(dimg);
 1589   g = r + 1;
 1590   b = r + 2;
 1591   end = r + w * h * gdk_pixbuf_get_n_channels(img);
 1592   
 1593     while (r != end) 
 1594     {
 1595         *r = *g = *b = (*r * 11 + *g * 16 + *b * 5) / 32;
 1596     r += 4;
 1597     g += 4;
 1598     b += 4;
 1599     }
 1600     
 1601     return dimg;
 1602 }
 1603 
 1604 
 1605 
 1606 static void disabled_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer data)
 1607 {
 1608     // Print only debugging messages
 1609     //if (log_level & G_LOG_LEVEL_DEBUG)
 1610     //  g_log_default_handler(log_domain, log_level, message, data);
 1611 }
 1612 
 1613 static GLogFunc old_handler = NULL;
 1614 
 1615 void gt_disable_warnings(bool disable)
 1616 {
 1617     //fprintf(stderr, "disable warnings\n");
 1618     if (disable)
 1619         old_handler = g_log_set_default_handler(disabled_handler, NULL);        
 1620     else
 1621         g_log_set_default_handler(old_handler, NULL);
 1622     //fprintf(stderr, "enable warnings\n");
 1623 }
 1624 
 1625 void gt_set_cell_renderer_text_from_font(GtkCellRendererText *cell, gFont *font)
 1626 {
 1627     g_object_set(G_OBJECT(cell),
 1628         "font-desc", font->desc(),
 1629         "underline", font->underline() ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE,
 1630         "strikethrough", font->strikeout(),
 1631         (void *)NULL);  
 1632 }
 1633 
 1634 static void set_layout_from_font(PangoLayout *layout, gFont *font, bool add, int dpi)
 1635 {
 1636     PangoFontDescription *desc;
 1637     PangoAttrList *attrs;
 1638     PangoAttribute *attr;
 1639     bool copy = false;
 1640     
 1641     desc = pango_context_get_font_description(font->ct);
 1642 
 1643     /*if ((dpi && dpi != gDesktop::resolution()))
 1644     {
 1645         double size = pango_font_description_get_size(desc);
 1646         desc = pango_font_description_copy(desc);
 1647         copy = true;
 1648         
 1649         size *= dpi / gDesktop::resolution();
 1650         
 1651         pango_font_description_set_size(desc, (int)size);
 1652     }*/
 1653     
 1654     pango_layout_set_font_description(layout, desc);
 1655     
 1656     if (add)
 1657     {
 1658         attrs = pango_layout_get_attributes(layout);
 1659         if (!attrs)
 1660         {
 1661             attrs = pango_attr_list_new();
 1662             add = false;
 1663         }
 1664     }
 1665     else
 1666         attrs = pango_attr_list_new();
 1667     
 1668     if (font->underline())
 1669     {
 1670         attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
 1671         pango_attr_list_insert(attrs, attr);
 1672     }
 1673     
 1674     if (font->strikeout())
 1675     {
 1676         attr = pango_attr_strikethrough_new(true);
 1677         pango_attr_list_insert(attrs, attr);
 1678     }
 1679     
 1680     if (font->mustFixSpacing())
 1681     {
 1682         attr = pango_attr_letter_spacing_new(PANGO_SCALE);
 1683         pango_attr_list_insert(attrs, attr);
 1684     }
 1685     
 1686     pango_layout_set_attributes(layout, attrs);
 1687     
 1688     if (!add)
 1689         pango_attr_list_unref(attrs);
 1690     
 1691     pango_layout_context_changed(layout);
 1692     
 1693     if (copy)
 1694         pango_font_description_free(desc);
 1695 }
 1696 
 1697 void gt_set_layout_from_font(PangoLayout *layout, gFont *font, int dpi)
 1698 {
 1699     set_layout_from_font(layout, font, false, dpi);
 1700 }
 1701 
 1702 void gt_add_layout_from_font(PangoLayout *layout, gFont *font, int dpi)
 1703 {
 1704     set_layout_from_font(layout, font, true, dpi);
 1705 }
 1706 
 1707 void gt_layout_alignment(PangoLayout *layout, float w, float h, float *tw, float *th, int align, float *offX, float *offY)
 1708 {
 1709     int ptw, pth;
 1710     gt_layout_get_extents(layout, &ptw, &pth, FALSE);
 1711     *tw = (float)ptw / PANGO_SCALE;
 1712     *th = (float)pth / PANGO_SCALE;
 1713     
 1714     if (w < 0) w = *tw;
 1715     if (h < 0) h = *th;
 1716 
 1717     switch (align)
 1718     {
 1719         case ALIGN_BOTTOM_NORMAL:
 1720             align = gDesktop::rightToLeft() ? ALIGN_BOTTOM_RIGHT : ALIGN_BOTTOM_LEFT;
 1721             break;
 1722         
 1723         case ALIGN_NORMAL:
 1724             align = gDesktop::rightToLeft() ? ALIGN_RIGHT : ALIGN_LEFT;
 1725             break;
 1726             
 1727         case ALIGN_TOP_NORMAL:
 1728             align = gDesktop::rightToLeft() ? ALIGN_TOP_RIGHT : ALIGN_TOP_LEFT;
 1729             break;
 1730     }
 1731     
 1732     switch (align)
 1733     {
 1734         case ALIGN_BOTTOM:   
 1735             pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); 
 1736             *offX = (w - *tw) / 2;
 1737             *offY = h - *th; 
 1738             break;
 1739         
 1740         case ALIGN_BOTTOM_LEFT:         
 1741             pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
 1742             *offX = 0;
 1743             *offY = h - *th;
 1744             break;
 1745             
 1746         case ALIGN_BOTTOM_RIGHT: 
 1747             pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
 1748             *offX = w - *tw;
 1749             *offY = h - *th;
 1750             break;
 1751             
 1752         case ALIGN_CENTER: 
 1753             pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
 1754             *offX = (w - *tw) / 2;
 1755             *offY = (h - *th) / 2;
 1756             break;
 1757             
 1758         case ALIGN_LEFT:
 1759             pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
 1760             *offX = 0;
 1761             *offY = (h - *th) / 2;
 1762             break;
 1763             
 1764         case ALIGN_RIGHT:
 1765             pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
 1766             *offX = w - *tw;
 1767             *offY = (h - *th) / 2;
 1768             break;
 1769             
 1770         case ALIGN_TOP:
 1771             pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
 1772             *offX = (w - *tw) / 2;
 1773             *offY = 0;
 1774             break;
 1775             
 1776         case ALIGN_TOP_LEFT:
 1777             pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
 1778             *offX = 0;
 1779             *offY = 0;
 1780             break;
 1781             
 1782         case ALIGN_TOP_RIGHT:
 1783             pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
 1784             *offX = w - *tw;
 1785             *offY = 0; 
 1786             break;
 1787     }
 1788 }
 1789 
 1790 #if GTK_CHECK_VERSION(2, 18, 0)
 1791 #else
 1792 void
 1793 gtk_widget_set_can_focus (GtkWidget *widget,
 1794                           gboolean   can_focus)
 1795 {
 1796   g_return_if_fail (GTK_IS_WIDGET (widget));
 1797 
 1798   if (can_focus != GTK_WIDGET_CAN_FOCUS (widget))
 1799     {
 1800       if (can_focus)
 1801         GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
 1802       else
 1803         GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS);
 1804 
 1805       gtk_widget_queue_resize (widget);
 1806       g_object_notify (G_OBJECT (widget), "can-focus");
 1807     }
 1808 }
 1809 
 1810 void gtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation)
 1811 {
 1812     *allocation = widget->allocation;
 1813 }
 1814 #endif
 1815 
 1816 static void add_again(GtkWidget *widget, GtkWidget *ignore)
 1817 {
 1818     int x, y;
 1819     GtkContainer *parent;
 1820     gControl *control;
 1821     gContainer *parent_control;
 1822 
 1823     if (widget == ignore)
 1824         return;
 1825     
 1826     parent = GTK_CONTAINER(gtk_widget_get_parent(ignore));
 1827     control = gt_get_control(widget);
 1828     parent_control = (gContainer *)gt_get_control(parent);
 1829     
 1830     if (control && parent_control)
 1831     {
 1832         x = control->x();
 1833         y = control->y();
 1834     }
 1835     
 1836     g_object_ref(G_OBJECT(widget));
 1837     gtk_container_remove(parent, widget);
 1838     gtk_container_add(parent, widget);
 1839     g_object_unref(G_OBJECT(widget));
 1840     
 1841     if (control && parent_control)
 1842         parent_control->moveChild(control, x, y);
 1843 }
 1844 
 1845 void gt_lower_widget(GtkWidget *widget)
 1846 {
 1847     GtkContainer *parent;
 1848     
 1849     parent = GTK_CONTAINER(gtk_widget_get_parent(widget));
 1850     if (parent)
 1851         gtk_container_foreach(parent, (GtkCallback)add_again, widget);
 1852 }
 1853 
 1854 #if GTK_CHECK_VERSION(2, 22, 0)
 1855 #else
 1856 int gdk_device_get_source(GdkDevice *device)
 1857 {
 1858     return device->source;
 1859 }
 1860 
 1861 //GtkWidget *_gtk_window_group_get_current_grab(GtkWindowGroup *window_group);
 1862 
 1863 GtkWidget *gtk_window_group_get_current_grab(GtkWindowGroup *window_group)
 1864 {
 1865   if (window_group->grabs)
 1866     return GTK_WIDGET(window_group->grabs->data);
 1867   return NULL;
 1868 }
 1869 #endif
 1870 
 1871 void gt_cairo_set_source_color(cairo_t *cr, GB_COLOR color)
 1872 {
 1873     int r, g, b, a;
 1874     
 1875     GB_COLOR_SPLIT(color, r, g, b, a);
 1876     cairo_set_source_rgba(cr, r / 255.0, g / 255.0, b / 255.0, a / 255.0);
 1877 }
 1878 
 1879 void gt_cairo_draw_rect(cairo_t *cr, int x, int y, int w, int h, GB_COLOR color)
 1880 {
 1881     gt_cairo_set_source_color(cr, color);
 1882     
 1883     cairo_rectangle(cr, x, y, w, 1);
 1884     cairo_fill(cr);
 1885     if (h <= 1)
 1886         return;
 1887     cairo_rectangle(cr, x, y + h - 1, w, 1);
 1888     cairo_fill(cr);
 1889     if (h <= 2)
 1890         return;
 1891     cairo_rectangle(cr, x, y + 1, 1, h - 2);
 1892     cairo_fill(cr);
 1893     cairo_rectangle(cr, x + w - 1, y + 1, 1, h - 2);
 1894     cairo_fill(cr);
 1895 }
 1896 
 1897 // Function partially taken from the GTK+ source code.
 1898 cairo_surface_t *gt_cairo_create_surface_from_pixbuf(const GdkPixbuf *pixbuf)
 1899 {
 1900     gint width = gdk_pixbuf_get_width (pixbuf);
 1901     gint height = gdk_pixbuf_get_height (pixbuf);
 1902     guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
 1903     int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
 1904     int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
 1905     int cairo_stride;
 1906     guchar *cairo_pixels;
 1907     cairo_format_t format;
 1908     cairo_surface_t *surface;
 1909     static const cairo_user_data_key_t key = { 0 };
 1910     int j;
 1911 
 1912     //fprintf(stderr, "gt_cairo_create_surface_from_pixbuf: %d %d / %d\n", width, height, n_channels);
 1913     
 1914     if (n_channels == 3)
 1915         format = CAIRO_FORMAT_RGB24;
 1916     else
 1917         format = CAIRO_FORMAT_ARGB32;
 1918 
 1919     cairo_stride = cairo_format_stride_for_width (format, width);
 1920     cairo_pixels = (uchar *)g_malloc_n (height, cairo_stride);
 1921     surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels,
 1922                                                                                                 format,
 1923                                                                                                 width, height, cairo_stride);
 1924 
 1925     cairo_surface_set_user_data (surface, &key,
 1926                         cairo_pixels, (cairo_destroy_func_t)g_free);
 1927 
 1928     for (j = height; j; j--)
 1929     {
 1930         guchar *p = gdk_pixels;
 1931         guchar *q = cairo_pixels;
 1932 
 1933         if (n_channels == 3)
 1934         {
 1935             guchar *end = p + 3 * width;
 1936             
 1937             while (p < end)
 1938                 {
 1939                     #if G_BYTE_ORDER == G_LITTLE_ENDIAN
 1940                     q[0] = p[2];
 1941                     q[1] = p[1];
 1942                     q[2] = p[0];
 1943                     #else
 1944                     q[1] = p[0];
 1945                     q[2] = p[1];
 1946                     q[3] = p[2];
 1947                     #endif
 1948                     p += 3;
 1949                     q += 4;
 1950                 }
 1951         }
 1952         else
 1953         {
 1954             guchar *end = p + 4 * width;
 1955             guint t1,t2,t3;
 1956                 
 1957             #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
 1958 
 1959             while (p < end)
 1960                 {
 1961                     #if G_BYTE_ORDER == G_LITTLE_ENDIAN
 1962                     MULT(q[0], p[2], p[3], t1);
 1963                     MULT(q[1], p[1], p[3], t2);
 1964                     MULT(q[2], p[0], p[3], t3);
 1965                     q[3] = p[3];
 1966                     #else     
 1967                     q[0] = p[3];
 1968                     MULT(q[1], p[0], p[3], t1);
 1969                     MULT(q[2], p[1], p[3], t2);
 1970                     MULT(q[3], p[2], p[3], t3);
 1971                     #endif
 1972                     
 1973                     p += 4;
 1974                     q += 4;
 1975                 }
 1976             
 1977             #undef MULT
 1978         }
 1979 
 1980         gdk_pixels += gdk_rowstride;
 1981         cairo_pixels += cairo_stride;
 1982     }
 1983 
 1984     return surface;
 1985 }
 1986 
 1987 void gt_cairo_draw_pixbuf(cairo_t *cr, GdkPixbuf *pixbuf, float x, float y, float w, float h, float opacity, GB_RECT *source)
 1988 {
 1989     cairo_surface_t *surface;
 1990     cairo_pattern_t *pattern = NULL, *save;
 1991     cairo_matrix_t matrix;
 1992     
 1993     cairo_save(cr);
 1994     
 1995     save = cairo_get_source(cr);
 1996     cairo_pattern_reference(save);
 1997     
 1998     if (source)
 1999         pixbuf = gdk_pixbuf_new_subpixbuf(pixbuf, source->x, source->y, source->w, source->h);
 2000     
 2001     if (w < 0 || h < 0)
 2002     {
 2003         //fprintf(stderr, "gdk_cairo_set_source_pixbuf: %d %d\n", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
 2004         gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
 2005         cairo_rectangle(cr, x, y, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
 2006     }
 2007     else
 2008     {
 2009         surface = gt_cairo_create_surface_from_pixbuf(pixbuf);
 2010         pattern = cairo_pattern_create_for_surface(surface);
 2011         cairo_surface_destroy(surface);
 2012         cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
 2013         
 2014         if (source && w >= source->w && h >= source->h && w == (int)w && h == (int)h && ((int)w % source->w) == 0 && ((int)h % source->h) == 0)
 2015             cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
 2016         
 2017         //gdk_cairo_set_source_pixbuf(cr, picture->getPixbuf(), x, y);
 2018         
 2019         cairo_matrix_init_identity(&matrix);
 2020         cairo_matrix_translate(&matrix, x, y);
 2021         cairo_matrix_scale(&matrix, w / gdk_pixbuf_get_width(pixbuf), h / gdk_pixbuf_get_height(pixbuf));
 2022         cairo_matrix_invert(&matrix);
 2023         cairo_pattern_set_matrix(pattern, &matrix);
 2024         cairo_set_source(cr, pattern);
 2025         
 2026         cairo_rectangle(cr, x, y, w, h);
 2027     }
 2028     
 2029     if (opacity == 1.0)
 2030     {
 2031         cairo_fill(cr);
 2032     }
 2033     else
 2034     {
 2035         cairo_clip(cr);
 2036         cairo_paint_with_alpha(cr, opacity);
 2037     }
 2038     
 2039     cairo_set_source(cr, save);
 2040     cairo_pattern_destroy(save);
 2041     
 2042     cairo_restore(cr);
 2043     
 2044     if (pattern)
 2045         cairo_pattern_destroy(pattern);
 2046     
 2047     if (source)
 2048         g_object_unref(pixbuf);
 2049 }
 2050 
 2051 
 2052 // Style management
 2053 
 2054 #define NUM_STYLES 12
 2055 
 2056 #ifdef GTK3
 2057 static GtkStyleContext *_style[NUM_STYLES] = { NULL };
 2058 #else
 2059 static int _style_loaded = 0;
 2060 static GtkStyle *_style[NUM_STYLES];
 2061 #endif
 2062 
 2063 static int type_to_index(GType type)
 2064 {
 2065     if (type == GTK_TYPE_ENTRY)
 2066         return 1;
 2067     else if (type == GTK_TYPE_LAYOUT)
 2068         return 2;
 2069     else if (type == GTK_TYPE_TOOLTIP)
 2070         return 3;
 2071     else if (type == GTK_TYPE_SCROLLBAR)
 2072         return 4;
 2073     else if (type == GTK_TYPE_SCROLLED_WINDOW)
 2074         return 5;
 2075     else if (type == GTK_TYPE_CHECK_BUTTON)
 2076         return 6;
 2077     else if (type == GTK_TYPE_RADIO_BUTTON)
 2078         return 7;
 2079     else if (type == GTK_TYPE_FRAME)
 2080         return 8;
 2081     else if (type == GTK_TYPE_LABEL)
 2082         return 9;
 2083     else if (type == GTK_TYPE_BUTTON)
 2084         return 10;
 2085     else if (type == GTK_TYPE_WINDOW)
 2086         return 11;
 2087     else
 2088         return 0;
 2089 }
 2090 
 2091 #ifdef GTK3
 2092 
 2093 const char *gt_get_style_class(GType type)
 2094 {
 2095     static const char *_class[] = {
 2096         GTK_STYLE_CLASS_DEFAULT, GTK_STYLE_CLASS_ENTRY, GTK_STYLE_CLASS_BACKGROUND, GTK_STYLE_CLASS_TOOLTIP,
 2097         GTK_STYLE_CLASS_SCROLLBAR, GTK_STYLE_CLASS_DEFAULT, GTK_STYLE_CLASS_CHECK, GTK_STYLE_CLASS_RADIO,
 2098         GTK_STYLE_CLASS_FRAME, GTK_STYLE_CLASS_BACKGROUND, GTK_STYLE_CLASS_BUTTON, GTK_STYLE_CLASS_BACKGROUND
 2099     };
 2100 
 2101     int index = type_to_index(type);
 2102     if (index < 0)
 2103         return NULL;
 2104     else
 2105         return _class[index];
 2106 }
 2107 
 2108 GtkStyleContext *gt_get_style(GType type, const char *node, const char *more_klass)
 2109 {
 2110     int index;
 2111     GtkStyleContext *style;
 2112 
 2113     if (!node && !more_klass)
 2114     {
 2115         index = type_to_index(type);
 2116         style = _style[index];
 2117         if (style)
 2118             return style;
 2119     }
 2120     
 2121     GtkWidgetPath *path = gtk_widget_path_new();
 2122     const char *klass = gt_get_style_class(type);
 2123 
 2124     style = gtk_style_context_new();
 2125     
 2126     if (klass)
 2127         gtk_style_context_add_class(style, klass);
 2128 
 2129     if (more_klass)
 2130         gtk_style_context_add_class(style, more_klass);
 2131 
 2132     gtk_widget_path_append_type(path, type);
 2133 
 2134     #if GTK_CHECK_VERSION(3, 20, 0)
 2135     gtk_widget_path_iter_set_object_name(path, -1, klass);
 2136     if (node)
 2137     {
 2138         gtk_widget_path_append_type(path, type);
 2139         gtk_widget_path_iter_set_object_name(path, 1, node);
 2140     }
 2141 #endif
 2142 
 2143     gtk_style_context_set_path(style, path);
 2144 
 2145     if (!node && !more_klass)
 2146         _style[index] = style;
 2147     
 2148     return style;
 2149 }
 2150 
 2151 #else
 2152 
 2153 static GtkStyle *get_style(const char *name, GType type)
 2154 {
 2155     GtkSettings *set = gtk_settings_get_default();
 2156     GtkStyle* st = gtk_rc_get_style_by_paths(set, NULL, name, type);
 2157     if (!st) st = gtk_widget_get_default_style();
 2158     return st;
 2159 }
 2160 
 2161 static GtkStyle *get_widget_style(const char *name)
 2162 {
 2163     GtkSettings *set = gtk_settings_get_default();
 2164     GtkStyle* st = gtk_rc_get_style_by_paths(set, name, NULL, G_TYPE_NONE);
 2165     if (!st) st = gtk_widget_get_default_style();
 2166     return st;
 2167 }
 2168 
 2169 GtkStyle *gt_get_style(GType type)
 2170 {
 2171     int index = type_to_index(type);
 2172     if (index < 0)
 2173         return NULL;
 2174 
 2175     if ((_style_loaded & (1 << index)) == 0)
 2176     {
 2177         if (type == GTK_TYPE_TOOLTIP)
 2178             _style[index] = get_widget_style("gtk-tooltip");
 2179         else
 2180             _style[index] = get_style(g_type_name(type), type);
 2181 
 2182         _style_loaded |= (1 << index);
 2183     }
 2184 
 2185     return _style[index];
 2186 }
 2187 
 2188 #endif
 2189 
 2190 void gt_get_style_property(GType type, const char *name, void *pvalue)
 2191 {
 2192 #ifdef GTK3
 2193     gtk_style_context_get_style(gt_get_style(type), name, pvalue, NULL);
 2194 #else
 2195     gtk_style_get(gt_get_style(type), type, name, pvalue, (char *)NULL);
 2196 #endif
 2197 }
 2198 
 2199 
 2200 #ifdef GTK3
 2201 
 2202 // Draw a styled border
 2203 
 2204 void gt_draw_border(cairo_t *cr, GtkStyleContext *st, GtkStateFlags state, int border, gColor color, int x, int y, int w, int h, bool bg)
 2205 {
 2206     if (border == BORDER_NONE)
 2207         return;
 2208 
 2209     if (w < 2 || h < 2)
 2210         return;
 2211 
 2212     if (border == BORDER_PLAIN)
 2213     {
 2214         gt_cairo_draw_rect(cr, x, y, w, h, color);
 2215         return;
 2216     }
 2217 
 2218     if (strcmp(gApplication::getStyleName(), "oxygen-gtk") == 0)
 2219     {
 2220         x -= 3;
 2221         w += 6;
 2222     }
 2223 
 2224     gboolean interior_focus;
 2225 
 2226     if (border == BORDER_RAISED)
 2227     {
 2228         st = gt_get_style(GTK_TYPE_BUTTON);
 2229         gtk_style_context_set_state(st, state);
 2230         //gtk_style_context_add_class(st, GTK_STYLE_CLASS_BUTTON);
 2231         gtk_render_frame(st, cr, x, y, w, h);
 2232     }
 2233     else if (border == BORDER_SUNKEN)
 2234     {
 2235         st = gt_get_style(GTK_TYPE_ENTRY);
 2236         gtk_style_context_set_state(st, state);
 2237         //gtk_style_context_add_class(st, GTK_STYLE_CLASS_ENTRY);
 2238         if (bg)
 2239             gtk_render_background(st, cr, x, y, w, h);
 2240         gtk_render_frame(st, cr, x, y, w, h);
 2241     }
 2242     else if (border == BORDER_ETCHED)
 2243     {
 2244         st = gt_get_style(GTK_TYPE_FRAME);
 2245         gtk_style_context_set_state(st, state);
 2246         //gtk_style_context_add_class(st, GTK_STYLE_CLASS_FRAME);
 2247         gtk_render_frame(st, cr, x, y, w, h);
 2248     }
 2249 
 2250     if (state & GTK_STATE_FLAG_FOCUSED)
 2251     {
 2252         gtk_style_context_get_style (st, "interior-focus", &interior_focus, NULL);
 2253         if (!interior_focus)
 2254             gtk_render_focus(st, cr, x, y, w, h);
 2255     }
 2256 }
 2257 #endif
 2258 
 2259 bool gt_grab(GtkWidget *widget, bool owner_event, guint32 time)
 2260 {
 2261     GdkWindow *win = gtk_widget_get_window(widget);
 2262     int ret;
 2263 
 2264 #if GDK_MAJOR_VERSION > 3 || (GDK_MAJOR_VERSION == 3 && GDK_MINOR_VERSION >= 20)
 2265 
 2266     GdkSeat *seat = gdk_display_get_default_seat(gdk_display_get_default());
 2267 
 2268     ret = gdk_seat_grab(seat, win, GDK_SEAT_CAPABILITY_ALL, owner_event, gdk_window_get_cursor(win), NULL, NULL, NULL);
 2269     if (ret == GDK_GRAB_SUCCESS)
 2270         return FALSE;
 2271 
 2272 #elif defined(GTK3)
 2273 
 2274     GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default()));
 2275     GdkDevice *keyboard = gdk_device_get_associated_device(pointer);
 2276 
 2277     ret = gdk_device_grab(pointer, win, GDK_OWNERSHIP_APPLICATION, owner_event,
 2278         (GdkEventMask)(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK),
 2279         gdk_window_get_cursor(win),
 2280         time);
 2281 
 2282     if (ret == GDK_GRAB_SUCCESS)
 2283     {
 2284         ret = gdk_device_grab(keyboard, win, GDK_OWNERSHIP_APPLICATION, owner_event,
 2285             (GdkEventMask)(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK),
 2286             NULL, time);
 2287 
 2288         if (ret == GDK_GRAB_SUCCESS)
 2289             return FALSE;
 2290 
 2291         gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
 2292     }
 2293 
 2294 #else
 2295 
 2296     ret = gdk_pointer_grab(win, owner_event,
 2297         (GdkEventMask)(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK),
 2298         NULL,
 2299 #if GTK_CHECK_VERSION(2, 18, 0)
 2300         gdk_window_get_cursor(win),
 2301 #else
 2302         NULL,
 2303 #endif
 2304         time);
 2305 
 2306     if (ret == GDK_GRAB_SUCCESS)
 2307     {
 2308         ret = gdk_keyboard_grab(win, owner_event, time);
 2309 
 2310         if (ret == GDK_GRAB_SUCCESS)
 2311             return FALSE;
 2312 
 2313         gdk_pointer_ungrab(GDK_CURRENT_TIME);
 2314     }
 2315 
 2316 #endif
 2317 
 2318     fprintf(stderr, "gb.gtk: warning: grab failed: %d\n", ret);
 2319     return TRUE;
 2320 }
 2321 
 2322 void gt_ungrab(void)
 2323 {
 2324 #if GDK_MAJOR_VERSION > 3 || (GDK_MAJOR_VERSION == 3 && GDK_MINOR_VERSION >= 20)
 2325 
 2326     GdkSeat *seat = gdk_display_get_default_seat(gdk_display_get_default());
 2327     gdk_seat_ungrab(seat);
 2328 
 2329 #elif defined(GTK3)
 2330     
 2331     GdkDevice *pointer = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default()));
 2332     GdkDevice *keyboard = gdk_device_get_associated_device(pointer);
 2333 
 2334     gdk_device_ungrab(pointer, GDK_CURRENT_TIME);
 2335     gdk_device_ungrab(keyboard, GDK_CURRENT_TIME);
 2336     
 2337 #else
 2338     
 2339     gdk_pointer_ungrab(GDK_CURRENT_TIME);
 2340     gdk_keyboard_ungrab(GDK_CURRENT_TIME);
 2341     
 2342 #endif
 2343 }
 2344 
 2345 #ifdef GTK3
 2346 void gt_widget_reparent(GtkWidget *widget, GtkWidget *new_parent)
 2347 {
 2348   GtkWidget *parent = gtk_widget_get_parent(widget);
 2349   
 2350   if (parent == new_parent)
 2351     return;
 2352   
 2353   g_object_ref(widget);
 2354   gtk_container_remove(GTK_CONTAINER(parent), widget);
 2355   gtk_container_add(GTK_CONTAINER(new_parent), widget);
 2356   g_object_unref(widget);
 2357 }
 2358 #endif
 2359 
 2360 void gt_on_theme_change()
 2361 {
 2362     int i;
 2363     
 2364     for (i = 0; i < NUM_STYLES; i++)
 2365     {
 2366         if (_style[i])
 2367             g_object_unref(G_OBJECT(_style[i]));
 2368         _style[i] = NULL;
 2369     }
 2370 }
 2371 
 2372 #if GTK_CHECK_VERSION(3, 22, 0)
 2373 int gt_find_monitor(GdkMonitor *monitor)
 2374 {
 2375     GdkDisplay *display = gdk_display_get_default();
 2376     int i;
 2377     
 2378     for (i = 0; i < gdk_display_get_n_monitors(display); i++)
 2379     {
 2380         if (gdk_display_get_monitor(display, i) == monitor)
 2381             return i;
 2382     }
 2383     
 2384     return -1;
 2385 }
 2386 #endif
 2387 
 2388 #ifdef GTK3
 2389 const char *gt_widget_set_name(GtkWidget *widget, const char *suffix)
 2390 {
 2391     static int count = 0;
 2392     
 2393     char buffer[256];
 2394     const char *name;
 2395     
 2396     name = gtk_widget_get_name(widget);
 2397     if (name && name[0] == 'g')
 2398         return name;
 2399     
 2400     count++;
 2401     sprintf(buffer, "g%d_%s", count, suffix ? suffix : "");
 2402     gtk_widget_set_name(widget, buffer);
 2403     return gtk_widget_get_name(widget);
 2404 }
 2405 
 2406 void gt_css_add_color(GString *css, gColor bg, gColor fg)
 2407 {
 2408     char buffer[32];
 2409 
 2410     if (bg != COLOR_DEFAULT)
 2411     {
 2412         gt_to_css_color(buffer, bg);
 2413         g_string_append(css, "background-color:");
 2414         g_string_append(css, buffer);
 2415         g_string_append(css, ";\nbackground-image:none;\n");
 2416     }
 2417 
 2418     if (fg != COLOR_DEFAULT)
 2419     {
 2420         gt_to_css_color(buffer, fg);
 2421         g_string_append(css, "color:");
 2422         g_string_append(css, buffer);
 2423         g_string_append(css, ";\n");
 2424     }
 2425 }
 2426 
 2427 void gt_css_add_font(GString *css, gFont *font)
 2428 {
 2429     int s;
 2430     char buffer[32];
 2431     
 2432     if (!font)
 2433         return;
 2434     
 2435     if (font->_name_set)
 2436     {
 2437         g_string_append(css, "font-family:\"");
 2438         g_string_append(css, font->name());
 2439         g_string_append(css, "\";\n");
 2440     }
 2441 
 2442     if (font->_size_set)
 2443     {
 2444         s = (int)(font->size() * 10 + 0.5);
 2445         sprintf(buffer, "%dpt;\n", s / 10); //, s % 10);
 2446         g_string_append(css, "font-size:");
 2447         g_string_append(css, buffer);
 2448     }
 2449 
 2450     if (font->_bold_set)
 2451     {
 2452         g_string_append(css, "font-weight:");
 2453         g_string_append(css, font->bold() ? "bold" : "normal");
 2454         g_string_append(css, ";\n");
 2455     }
 2456 
 2457     if (font->_italic_set)
 2458     {
 2459         g_string_append(css, "font-style:");
 2460         g_string_append(css, font->italic() ? "italic" : "normal");
 2461         g_string_append(css, ";\n");
 2462     }
 2463 
 2464     if (font->_underline_set || font->_strikeout_set)
 2465     {
 2466         g_string_append(css, "text-decoration-line:");
 2467         if (font->strikeout())
 2468             g_string_append(css, "line-through");
 2469         else if (font->underline())
 2470             g_string_append(css, "underline");
 2471         else
 2472             g_string_append(css, "none");
 2473         g_string_append(css, ";\n");
 2474     }
 2475     
 2476     if (font->mustFixSpacing())
 2477     {
 2478         g_string_append(css, "letter-spacing:1px;\n");
 2479     }
 2480 }
 2481 
 2482 void gt_widget_update_css(GtkWidget *widget, gFont *font, gColor bg, gColor fg)
 2483 {
 2484     GtkStyleContext *context;
 2485     GtkStyleProvider *css_provider;
 2486     const char *name;
 2487     GString *css;
 2488     char *css_str;
 2489     
 2490     context = gtk_widget_get_style_context(widget);
 2491     name = gt_widget_set_name(widget, NULL);
 2492     
 2493     css = g_string_new(NULL);
 2494     
 2495     if (font || bg != COLOR_DEFAULT || fg != COLOR_DEFAULT)
 2496     {
 2497         g_string_append_printf(css, "#%s {\ntransition:none;\n", name);
 2498         gt_css_add_color(css, bg, fg);
 2499         gt_css_add_font(css, font);
 2500         g_string_append(css, "}\n");
 2501     }
 2502     
 2503     css_provider = (GtkStyleProvider *)g_object_get_data(G_OBJECT(widget), "gambas-css");
 2504     
 2505     if (css->len)
 2506     {
 2507         if (!css_provider)
 2508         {
 2509             css_provider = GTK_STYLE_PROVIDER(gtk_css_provider_new());
 2510             g_object_set_data_full(G_OBJECT(widget), "gambas-css", (gpointer)css_provider, g_object_unref);
 2511         }
 2512         css_str = g_string_free(css, FALSE);
 2513         //fprintf(stderr, "==== %s\n%s", name, css_str);
 2514         gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(css_provider), css_str, -1, NULL);
 2515         g_free(css_str);
 2516 
 2517         gtk_style_context_add_provider(context, css_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 2518     }
 2519     else
 2520     {
 2521         if (css_provider)
 2522         {
 2523             gtk_style_context_remove_provider(context, css_provider);
 2524             g_object_set_data(G_OBJECT(widget), "gambas-css", NULL);
 2525         }
 2526     }
 2527 }
 2528 
 2529 void gt_define_style_sheet(GtkStyleProvider **provider, GString *css)
 2530 {
 2531     GdkScreen *screen = gdk_screen_get_default();
 2532     char *css_str;
 2533     
 2534     if (css && css->len)
 2535     {
 2536         if (!*provider)
 2537             *provider = GTK_STYLE_PROVIDER(gtk_css_provider_new());
 2538             
 2539         css_str = g_string_free(css, FALSE);
 2540         gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(*provider), css_str, -1, NULL);
 2541         g_free(css_str);
 2542         gtk_style_context_add_provider_for_screen(screen, *provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 2543     }
 2544     else
 2545     {
 2546         if (*provider)
 2547         {
 2548             gtk_style_context_remove_provider_for_screen(screen, *provider);
 2549             *provider = NULL;
 2550         }
 2551     }
 2552 }
 2553 
 2554 #endif
 2555 
 2556 void gt_layout_get_extents(PangoLayout *layout, int *w, int *h, bool pixels)
 2557 {
 2558     PangoRectangle ink_rect, log_rect;
 2559     pango_layout_get_extents(layout, &ink_rect, &log_rect);
 2560     
 2561     *w = Max(ink_rect.width, log_rect.width);
 2562     *h = Max(ink_rect.height, log_rect.height);
 2563     
 2564     if (pixels)
 2565     {
 2566         *w = gt_pango_to_pixel(*w); 
 2567         *h = gt_pango_to_pixel(*h); 
 2568     }
 2569     
 2570 }
 2571 
 2572 void gt_widget_set_inverted(GtkWidget *widget, bool inverted)
 2573 {
 2574     GtkTextDirection dir = gtk_widget_get_default_direction();
 2575     
 2576     if (inverted)
 2577         gtk_widget_set_direction(widget, dir == GTK_TEXT_DIR_LTR ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
 2578     else
 2579         gtk_widget_set_direction(widget, dir);
 2580 }