gtkrc.c 129 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27
#include <config.h>
28 29 30

#include <locale.h>
#ifdef HAVE_UNISTD_H
31
#include <unistd.h>
32
#endif
33
#include <sys/stat.h>
34
#ifdef HAVE_SYS_PARAM_H
35
#include <sys/param.h>
36
#endif
37
#include <fcntl.h>
Elliot Lee's avatar
Elliot Lee committed
38
#include <string.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41

42

Owen Taylor's avatar
Owen Taylor committed
43
#include <glib.h>
44
#include <glib/gstdio.h>
Owen Taylor's avatar
Owen Taylor committed
45 46
#include "gdkconfig.h"

47
#include "gtkversion.h"
Elliot Lee's avatar
Elliot Lee committed
48
#include "gtkrc.h"
Tim Janik's avatar
Tim Janik committed
49
#include "gtkbindings.h"
50
#include "gtkthemes.h"
51
#include "gtkintl.h"
52
#include "gtkiconfactory.h"
53
#include "gtkmain.h"
54
#include "gtkmodules.h"
55
#include "gtkprivate.h"
56
#include "gtksettings.h"
57
#include "gtkwindow.h"
Elliot Lee's avatar
Elliot Lee committed
58

59 60
#include "gtkalias.h"

Tor Lillqvist's avatar
Tor Lillqvist committed
61 62 63 64
#ifdef G_OS_WIN32
#include <io.h>
#endif

Elliot Lee's avatar
Elliot Lee committed
65
typedef struct _GtkRcSet    GtkRcSet;
66
typedef struct _GtkRcNode   GtkRcNode;
67
typedef struct _GtkRcFile   GtkRcFile;
68

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
enum 
{
  PATH_ELT_PSPEC,
  PATH_ELT_UNRESOLVED,
  PATH_ELT_TYPE
};

typedef struct
{
  gint type;
  union 
  {
    GType         class_type;
    gchar        *class_name;
    GPatternSpec *pspec;
  } elt;
} PathElt;

Elliot Lee's avatar
Elliot Lee committed
87 88
struct _GtkRcSet
{
89 90
  GtkPathType   type;

91
  GPatternSpec *pspec;
92 93 94 95
  GSList       *path;

  GtkRcStyle   *rc_style;
  gint          priority;
Elliot Lee's avatar
Elliot Lee committed
96 97
};

98 99 100 101 102
struct _GtkRcFile
{
  time_t mtime;
  gchar *name;
  gchar *canonical_name;
103
  gchar *directory;
104 105
  guint  reload    : 1;
  guint  is_string : 1;	/* If TRUE, name is a string to parse with gtk_rc_parse_string() */
106
};
Elliot Lee's avatar
Elliot Lee committed
107

108 109 110 111 112 113 114 115 116 117 118 119 120 121

struct _GtkRcContext
{
  GHashTable *rc_style_ht;
  GtkSettings *settings;
  GSList *rc_sets_widget;
  GSList *rc_sets_widget_class;
  GSList *rc_sets_class;

  /* The files we have parsed, to reread later if necessary */
  GSList *rc_files;

  gchar *theme_name;
  gchar *key_theme_name;
122
  gchar *font_name;
123
  
124
  gchar **pixmap_path;
125 126

  gint default_priority;
127
  GtkStyle *default_style;
128 129

  GHashTable *color_hash;
130 131

  guint reloading : 1;
132 133 134 135 136 137 138 139 140
};

#define GTK_RC_STYLE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RC_STYLE, GtkRcStylePrivate))

typedef struct _GtkRcStylePrivate GtkRcStylePrivate;

struct _GtkRcStylePrivate
{
  GSList *color_hashes;
141 142 143 144
};

static GtkRcContext *gtk_rc_context_get              (GtkSettings     *settings);

145 146 147
static guint       gtk_rc_style_hash                 (const gchar     *name);
static gboolean    gtk_rc_style_equal                (const gchar     *a,
                                                      const gchar     *b);
148
static guint       gtk_rc_styles_hash                (const GSList    *rc_styles);
149
static gboolean    gtk_rc_styles_equal               (const GSList    *a,
150
                                                      const GSList    *b);
151 152
static GtkRcStyle* gtk_rc_style_find                 (GtkRcContext    *context,
						      const gchar     *name);
153 154 155
static GSList *    gtk_rc_styles_match               (GSList          *rc_styles,
                                                      GSList          *sets,
                                                      guint            path_length,
156 157
                                                      gchar           *path,
                                                      gchar           *path_reversed);
158 159 160 161
static GtkStyle *  gtk_rc_style_to_style             (GtkRcContext    *context,
						      GtkRcStyle      *rc_style);
static GtkStyle*   gtk_rc_init_style                 (GtkRcContext    *context,
						      GSList          *rc_styles);
162 163 164 165
static void        gtk_rc_parse_default_files        (GtkRcContext    *context);
static void        gtk_rc_parse_named                (GtkRcContext    *context,
						      const gchar     *name,
						      const gchar     *type);
Owen Taylor's avatar
Owen Taylor committed
166
static void        gtk_rc_context_parse_file         (GtkRcContext    *context,
167 168
						      const gchar     *filename,
						      gint             priority,
169
                                                      gboolean         reload);
170 171
static void        gtk_rc_parse_any                  (GtkRcContext    *context,
						      const gchar     *input_name,
172 173
                                                      gint             input_fd,
                                                      const gchar     *input_string);
174 175 176 177
static guint       gtk_rc_parse_statement            (GtkRcContext    *context,
						      GScanner        *scanner);
static guint       gtk_rc_parse_style                (GtkRcContext    *context,
						      GScanner        *scanner);
178
static guint       gtk_rc_parse_assignment           (GScanner        *scanner,
179
                                                      GtkRcStyle      *style,
180
						      GtkRcProperty   *prop);
181 182 183 184 185 186
static guint       gtk_rc_parse_bg                   (GScanner        *scanner,
                                                      GtkRcStyle      *style);
static guint       gtk_rc_parse_fg                   (GScanner        *scanner,
                                                      GtkRcStyle      *style);
static guint       gtk_rc_parse_text                 (GScanner        *scanner,
                                                      GtkRcStyle      *style);
187 188 189 190 191 192
static guint       gtk_rc_parse_base                 (GScanner        *scanner,
                                                      GtkRcStyle      *style);
static guint       gtk_rc_parse_xthickness           (GScanner        *scanner,
                                                      GtkRcStyle      *style);
static guint       gtk_rc_parse_ythickness           (GScanner        *scanner,
                                                      GtkRcStyle      *style);
193 194
static guint       gtk_rc_parse_bg_pixmap            (GtkRcContext    *context,
						      GScanner        *scanner,
195 196 197 198 199 200 201
                                                      GtkRcStyle      *rc_style);
static guint       gtk_rc_parse_font                 (GScanner        *scanner,
                                                      GtkRcStyle      *rc_style);
static guint       gtk_rc_parse_fontset              (GScanner        *scanner,
                                                      GtkRcStyle      *rc_style);
static guint       gtk_rc_parse_font_name            (GScanner        *scanner,
                                                      GtkRcStyle      *rc_style);
202 203
static guint       gtk_rc_parse_engine               (GtkRcContext    *context,
						      GScanner        *scanner,
204
                                                      GtkRcStyle     **rc_style);
205 206 207 208 209
static guint       gtk_rc_parse_pixmap_path          (GtkRcContext    *context,
						      GScanner        *scanner);
static void        gtk_rc_parse_pixmap_path_string   (GtkRcContext    *context,
						      GScanner        *scanner,
						      const gchar     *pix_path);
210
static guint       gtk_rc_parse_module_path          (GScanner        *scanner);
Owen Taylor's avatar
Owen Taylor committed
211
static guint       gtk_rc_parse_im_module_file       (GScanner        *scanner);
212 213 214 215
static guint       gtk_rc_parse_path_pattern         (GtkRcContext    *context,
						      GScanner        *scanner);
static guint       gtk_rc_parse_stock                (GtkRcContext    *context,
						      GScanner        *scanner,
216 217
                                                      GtkRcStyle      *rc_style,
                                                      GtkIconFactory  *factory);
218 219 220 221
static guint       gtk_rc_parse_logical_color        (GScanner        *scanner,
                                                      GtkRcStyle      *rc_style,
                                                      GHashTable      *hash);

222 223 224
static void        gtk_rc_clear_hash_node            (gpointer         key,
                                                      gpointer         data,
                                                      gpointer         user_data);
225
static void        gtk_rc_clear_styles               (GtkRcContext    *context);
226
static void        gtk_rc_add_initial_default_files  (void);
227

228 229 230
static void        gtk_rc_style_finalize             (GObject         *object);
static void        gtk_rc_style_real_merge           (GtkRcStyle      *dest,
                                                      GtkRcStyle      *src);
231 232
static GtkRcStyle* gtk_rc_style_real_create_rc_style (GtkRcStyle      *rc_style);
static GtkStyle*   gtk_rc_style_real_create_style    (GtkRcStyle      *rc_style);
233 234 235
static void        gtk_rc_style_copy_icons_and_colors(GtkRcStyle      *rc_style,
                                                      GtkRcStyle      *src_style,
                                                      GtkRcContext    *context);
236 237
static gint	   gtk_rc_properties_cmp	     (gconstpointer    bsearch_node1,
						      gconstpointer    bsearch_node2);
238
static void        gtk_rc_set_free                   (GtkRcSet        *rc_set);
239

240 241 242 243
static void	   insert_rc_property		     (GtkRcStyle      *style,
						      GtkRcProperty   *property,
						      gboolean         replace);

244

245
static const GScannerConfig gtk_rc_scanner_config =
246 247
{
  (
248
   " \t\r\n"
249 250 251
   )			/* cset_skip_characters */,
  (
   "_"
252
   G_CSET_a_2_z
253 254 255
   G_CSET_A_2_Z
   )			/* cset_identifier_first */,
  (
256 257
   G_CSET_DIGITS
   "-_"
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
   G_CSET_a_2_z
   G_CSET_A_2_Z
   )			/* cset_identifier_nth */,
  ( "#\n" )		/* cpair_comment_single */,
  
  TRUE			/* case_sensitive */,
  
  TRUE			/* skip_comment_multi */,
  TRUE			/* skip_comment_single */,
  TRUE			/* scan_comment_multi */,
  TRUE			/* scan_identifier */,
  FALSE			/* scan_identifier_1char */,
  FALSE			/* scan_identifier_NULL */,
  TRUE			/* scan_symbols */,
  TRUE			/* scan_binary */,
  TRUE			/* scan_octal */,
  TRUE			/* scan_float */,
  TRUE			/* scan_hex */,
  TRUE			/* scan_hex_dollar */,
277
  TRUE			/* scan_string_sq */,
278 279 280
  TRUE			/* scan_string_dq */,
  TRUE			/* numbers_2_int */,
  FALSE			/* int_2_float */,
281
  FALSE			/* identifier_2_string */,
282 283
  TRUE			/* char_2_token */,
  TRUE			/* symbol_2_token */,
284
  FALSE			/* scope_0_fallback */,
285
};
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
 
static const gchar symbol_names[] = 
  "include\0"
  "NORMAL\0"
  "ACTIVE\0"
  "PRELIGHT\0"
  "SELECTED\0"
  "INSENSITIVE\0"
  "fg\0"
  "bg\0"
  "text\0"
  "base\0"
  "xthickness\0"
  "ythickness\0"
  "font\0"
  "fontset\0"
  "font_name\0"
  "bg_pixmap\0"
  "pixmap_path\0"
  "style\0"
  "binding\0"
  "bind\0"
  "widget\0"
  "widget_class\0"
  "class\0"
  "lowest\0"
  "gtk\0"
  "application\0"
  "theme\0"
  "rc\0"
  "highest\0"
  "engine\0"
  "module_path\0"
  "stock\0"
  "im_module_file\0"
  "LTR\0"
  "RTL\0"
323 324
  "color\0"
  "unbind\0";
325

326
static const struct
Elliot Lee's avatar
Elliot Lee committed
327
{
328
  guint name_offset;
329
  guint token;
330
} symbols[] = {
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
  {   0, GTK_RC_TOKEN_INCLUDE },
  {   8, GTK_RC_TOKEN_NORMAL },
  {  15, GTK_RC_TOKEN_ACTIVE },
  {  22, GTK_RC_TOKEN_PRELIGHT },
  {  31, GTK_RC_TOKEN_SELECTED },
  {  40, GTK_RC_TOKEN_INSENSITIVE },
  {  52, GTK_RC_TOKEN_FG },
  {  55, GTK_RC_TOKEN_BG },
  {  58, GTK_RC_TOKEN_TEXT },
  {  63, GTK_RC_TOKEN_BASE },
  {  68, GTK_RC_TOKEN_XTHICKNESS },
  {  79, GTK_RC_TOKEN_YTHICKNESS },
  {  90, GTK_RC_TOKEN_FONT },
  {  95, GTK_RC_TOKEN_FONTSET },
  { 103, GTK_RC_TOKEN_FONT_NAME },
  { 113, GTK_RC_TOKEN_BG_PIXMAP },
  { 123, GTK_RC_TOKEN_PIXMAP_PATH },
  { 135, GTK_RC_TOKEN_STYLE },
  { 141, GTK_RC_TOKEN_BINDING },
  { 149, GTK_RC_TOKEN_BIND },
  { 154, GTK_RC_TOKEN_WIDGET },
  { 161, GTK_RC_TOKEN_WIDGET_CLASS },
  { 174, GTK_RC_TOKEN_CLASS },
  { 180, GTK_RC_TOKEN_LOWEST },
  { 187, GTK_RC_TOKEN_GTK },
  { 191, GTK_RC_TOKEN_APPLICATION },
  { 203, GTK_RC_TOKEN_THEME },
  { 209, GTK_RC_TOKEN_RC },
  { 212, GTK_RC_TOKEN_HIGHEST },
  { 220, GTK_RC_TOKEN_ENGINE },
  { 227, GTK_RC_TOKEN_MODULE_PATH },
  { 239, GTK_RC_TOKEN_STOCK },
  { 245, GTK_RC_TOKEN_IM_MODULE_FILE },
  { 260, GTK_RC_TOKEN_LTR },
  { 264, GTK_RC_TOKEN_RTL },
366 367
  { 268, GTK_RC_TOKEN_COLOR },
  { 274, GTK_RC_TOKEN_UNBIND }
368
};
369

370
static GHashTable *realized_style_ht = NULL;
Elliot Lee's avatar
Elliot Lee committed
371

Owen Taylor's avatar
Owen Taylor committed
372 373
static gchar *im_module_file = NULL;

374 375
static gint    max_default_files = 0;
static gchar **gtk_rc_default_files = NULL;
376

377 378 379
/* A stack of information of RC files we are parsing currently.
 * The directories for these files are implicitely added to the end of
 * PIXMAP_PATHS.
380
 */
381
static GSList *current_files_stack = NULL;
382

Owen Taylor's avatar
Owen Taylor committed
383 384 385 386 387 388 389 390
/* RC files and strings that are parsed for every context
 */
static GSList *global_rc_files = NULL;

/* Keep list of all current RC contexts for convenience
 */
static GSList *rc_contexts;

391 392
/* RC file handling */

Owen Taylor's avatar
Owen Taylor committed
393 394
static gchar *
gtk_rc_make_default_dir (const gchar *type)
395
{
396 397
  const gchar *var;
  gchar *path;
398

Matthias Clasen's avatar
Matthias Clasen committed
399
  var = g_getenv ("GTK_EXE_PREFIX");
400

401
  if (var)
402
    path = g_build_filename (var, "lib", "gtk-2.0", GTK_BINARY_VERSION, type, NULL);
403
  else
404
    path = g_build_filename (GTK_LIBDIR, "gtk-2.0", GTK_BINARY_VERSION, type, NULL);
405

406 407 408
  return path;
}

Matthias Clasen's avatar
Matthias Clasen committed
409 410 411 412 413 414
/**
 * gtk_rc_get_im_module_path:
 * @returns: a newly-allocated string containing the path in which to 
 *    look for IM modules.
 *
 * Obtains the path in which to look for IM modules. See the documentation
415 416 417 418
 * of the <link linkend="im-module-path"><envar>GTK_PATH</envar></link>
 * environment variable for more details about looking up modules. This
 * function is useful solely for utilities supplied with GTK+ and should
 * not be used by applications under normal circumstances.
Matthias Clasen's avatar
Matthias Clasen committed
419
 */
420
gchar *
Owen Taylor's avatar
Owen Taylor committed
421 422
gtk_rc_get_im_module_path (void)
{
423 424 425
  gchar **paths = _gtk_get_module_path ("immodules");
  gchar *result = g_strjoinv (G_SEARCHPATH_SEPARATOR_S, paths);
  g_strfreev (paths);
Owen Taylor's avatar
Owen Taylor committed
426

427
  return result;
Owen Taylor's avatar
Owen Taylor committed
428 429
}

Matthias Clasen's avatar
Matthias Clasen committed
430 431 432
/**
 * gtk_rc_get_im_module_file:
 * @returns: a newly-allocated string containing the name of the file
433
 * listing the IM modules available for loading
Matthias Clasen's avatar
Matthias Clasen committed
434 435 436 437
 *
 * Obtains the path to the IM modules file. See the documentation
 * of the <link linkend="im-module-file"><envar>GTK_IM_MODULE_FILE</envar></link>
 * environment variable for more details.
jacob berkman's avatar
jacob berkman committed
438
 */
Owen Taylor's avatar
Owen Taylor committed
439 440 441
gchar *
gtk_rc_get_im_module_file (void)
{
442 443 444 445 446
  const gchar *var = g_getenv ("GTK_IM_MODULE_FILE");
  gchar *result = NULL;

  if (var)
    result = g_strdup (var);
Owen Taylor's avatar
Owen Taylor committed
447 448 449 450

  if (!result)
    {
      if (im_module_file)
Tor Lillqvist's avatar
Tor Lillqvist committed
451
	result = g_strdup (im_module_file);
Owen Taylor's avatar
Owen Taylor committed
452
      else
453
	result = g_build_filename (GTK_SYSCONFDIR, "gtk-2.0", "gtk.immodules", NULL);
Owen Taylor's avatar
Owen Taylor committed
454 455
    }

Tor Lillqvist's avatar
Tor Lillqvist committed
456
  return result;
Owen Taylor's avatar
Owen Taylor committed
457 458 459
}

gchar *
Hans Breuer's avatar
Hans Breuer committed
460
gtk_rc_get_theme_dir (void)
461
{
462 463
  const gchar *var;
  gchar *path;
464

Matthias Clasen's avatar
Matthias Clasen committed
465
  var = g_getenv ("GTK_DATA_PREFIX");
466

467
  if (var)
468
    path = g_build_filename (var, "share", "themes", NULL);
469
  else
470
    path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
471

472 473 474
  return path;
}

475 476 477 478 479
/**
 * gtk_rc_get_module_dir:
 * 
 * Returns a directory in which GTK+ looks for theme engines.
 * For full information about the search for theme engines,
Matthias Clasen's avatar
Matthias Clasen committed
480 481
 * see the docs for <envar>GTK_PATH</envar> in
 * <xref linkend="gtk-running"/>.
482 483 484
 * 
 * return value: the directory. (Must be freed with g_free())
 **/
Owen Taylor's avatar
Owen Taylor committed
485
gchar *
Hans Breuer's avatar
Hans Breuer committed
486
gtk_rc_get_module_dir (void)
Owen Taylor's avatar
Owen Taylor committed
487 488 489 490
{
  return gtk_rc_make_default_dir ("engines");
}

491 492 493 494
static void
gtk_rc_add_initial_default_files (void)
{
  static gint init = FALSE;
495 496
  const gchar *var;
  gchar *str;
497 498 499 500 501
  gchar **files;
  gint i;

  if (init)
    return;
502 503 504 505
 
  gtk_rc_default_files = g_new (gchar*, 10);
  max_default_files = 10;

506 507 508
  gtk_rc_default_files[0] = NULL;
  init = TRUE;

509
  var = g_getenv ("GTK2_RC_FILES");
510

511 512
  if (var)
    {
513
      files = g_strsplit (var, G_SEARCHPATH_SEPARATOR_S, -1);
514 515 516 517 518 519
      i=0;
      while (files[i])
	{
	  gtk_rc_add_default_file (files[i]);
	  i++;
	}
520
      g_strfreev (files);
521 522 523
    }
  else
    {
524
      const gchar *home;
525
      str = g_build_filename (GTK_SYSCONFDIR, "gtk-2.0", "gtkrc", NULL);
526

527
      gtk_rc_add_default_file (str);
528
      g_free (str);
529

530 531
      home = g_get_home_dir ();
      if (home)
532
	{
533
	  str = g_build_filename (home, ".gtkrc-2.0", NULL);
534 535 536
	  gtk_rc_add_default_file (str);
	  g_free (str);
	}
537 538 539
    }
}

540 541
/**
 * gtk_rc_add_default_file:
542 543
 * @filename: the pathname to the file. If @filename is not absolute, it
 *    is searched in the current directory.
544 545 546 547
 * 
 * Adds a file to the list of files to be parsed at the
 * end of gtk_init().
 **/
548
void
549
gtk_rc_add_default_file (const gchar *filename)
550 551 552 553 554
{
  guint n;
  
  gtk_rc_add_initial_default_files ();

555 556 557 558 559 560 561 562 563 564 565
  for (n = 0; n < max_default_files; n++) 
    {
      if (gtk_rc_default_files[n] == NULL)
	break;
    }

  if (n == max_default_files)
    {
      max_default_files += 10;
      gtk_rc_default_files = g_renew (gchar*, gtk_rc_default_files, max_default_files);
    }
566
  
567
  gtk_rc_default_files[n++] = g_strdup (filename);
568 569 570
  gtk_rc_default_files[n] = NULL;
}

571 572
/**
 * gtk_rc_set_default_files:
Matthias Clasen's avatar
Matthias Clasen committed
573
 * @filenames: A %NULL-terminated list of filenames.
574 575
 * 
 * Sets the list of files that GTK+ will read at the
Matthias Clasen's avatar
Matthias Clasen committed
576
 * end of gtk_init().
577
 **/
578
void
579
gtk_rc_set_default_files (gchar **filenames)
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
{
  gint i;

  gtk_rc_add_initial_default_files ();

  i = 0;
  while (gtk_rc_default_files[i])
    {
      g_free (gtk_rc_default_files[i]);
      i++;
    }
    
  gtk_rc_default_files[0] = NULL;

  i = 0;
595
  while (filenames[i] != NULL)
596
    {
597
      gtk_rc_add_default_file (filenames[i]);
598 599 600 601
      i++;
    }
}

602 603 604 605
/**
 * gtk_rc_get_default_files:
 * 
 * Retrieves the current list of RC files that will be parsed
Matthias Clasen's avatar
Matthias Clasen committed
606
 * at the end of gtk_init().
607
 * 
Matthias Clasen's avatar
Matthias Clasen committed
608
 * Return value: A %NULL-terminated array of filenames. This memory
609
 * is owned by GTK+ and must not be freed by the application.
Matthias Clasen's avatar
Matthias Clasen committed
610
 * If you want to store this information, you should make a copy.
611
 **/
612 613 614 615 616 617 618 619
gchar **
gtk_rc_get_default_files (void)
{
  gtk_rc_add_initial_default_files ();

  return gtk_rc_default_files;
}

620 621 622 623 624 625 626 627
static void
gtk_rc_settings_changed (GtkSettings  *settings,
			 GParamSpec   *pspec,
			 GtkRcContext *context)
{
  gchar *new_theme_name;
  gchar *new_key_theme_name;

628 629 630
  if (context->reloading)
    return;

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
  g_object_get (settings,
		"gtk-theme-name", &new_theme_name,
		"gtk-key-theme-name", &new_key_theme_name,
		NULL);

  if ((new_theme_name != context->theme_name && 
       !(new_theme_name && context->theme_name && strcmp (new_theme_name, context->theme_name) == 0)) ||
      (new_key_theme_name != context->key_theme_name &&
       !(new_key_theme_name && context->key_theme_name && strcmp (new_key_theme_name, context->key_theme_name) == 0)))
    {
      gtk_rc_reparse_all_for_settings (settings, TRUE);
    }

  g_free (new_theme_name);
  g_free (new_key_theme_name);
}

648 649 650 651 652
static void
gtk_rc_font_name_changed (GtkSettings  *settings,
                          GParamSpec   *pspec,
                          GtkRcContext *context)
{
653 654
  if (!context->reloading)
    _gtk_rc_context_get_default_font_name (settings);
655 656
}

657
static void
658 659 660
gtk_rc_color_hash_changed (GtkSettings  *settings,
			   GParamSpec   *pspec,
			   GtkRcContext *context)
661
{
662 663 664 665
  if (context->color_hash)
    g_hash_table_unref (context->color_hash);
  
  g_object_get (settings, "color-hash", &context->color_hash, NULL);
666

667
  gtk_rc_reparse_all_for_settings (settings, TRUE);
668 669
}

670 671 672 673 674 675 676 677 678 679 680 681 682
static GtkRcContext *
gtk_rc_context_get (GtkSettings *settings)
{
  if (!settings->rc_context)
    {
      GtkRcContext *context = settings->rc_context = g_new (GtkRcContext, 1);

      context->settings = settings;
      context->rc_style_ht = NULL;
      context->rc_sets_widget = NULL;
      context->rc_sets_widget_class = NULL;
      context->rc_sets_class = NULL;
      context->rc_files = NULL;
683
      context->default_style = NULL;
684
      context->reloading = FALSE;
685 686 687 688

      g_object_get (settings,
		    "gtk-theme-name", &context->theme_name,
		    "gtk-key-theme-name", &context->key_theme_name,
689
		    "gtk-font-name", &context->font_name,
690
		    "color-hash", &context->color_hash,
691 692 693 694 695 696 697 698 699 700
		    NULL);

      g_signal_connect (settings,
			"notify::gtk-theme-name",
			G_CALLBACK (gtk_rc_settings_changed),
			context);
      g_signal_connect (settings,
			"notify::gtk-key-theme-name",
			G_CALLBACK (gtk_rc_settings_changed),
			context);
701 702 703 704
      g_signal_connect (settings,
			"notify::gtk-font-name",
			G_CALLBACK (gtk_rc_font_name_changed),
			context);
705
      g_signal_connect (settings,
706 707
			"notify::color-hash",
			G_CALLBACK (gtk_rc_color_hash_changed),
708
			context);
709

710
      context->pixmap_path = NULL;
711 712

      context->default_priority = GTK_PATH_PRIO_RC;
Owen Taylor's avatar
Owen Taylor committed
713 714

      rc_contexts = g_slist_prepend (rc_contexts, settings->rc_context);
715 716 717 718 719
    }

  return settings->rc_context;
}

720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
static void 
gtk_rc_clear_rc_files (GtkRcContext *context)
{
  GSList *list;

  list = context->rc_files;
  while (list)
    {
      GtkRcFile *rc_file = list->data;
      
      if (rc_file->canonical_name != rc_file->name)
	g_free (rc_file->canonical_name);
      g_free (rc_file->directory);
      g_free (rc_file->name);
      g_free (rc_file);
      
      list = list->next;
    }
  
  g_slist_free (context->rc_files);
  context->rc_files = NULL;
}

void
_gtk_rc_context_destroy (GtkSettings *settings)
{
  GtkRcContext *context;

  g_return_if_fail (GTK_IS_SETTINGS (settings));

  context = settings->rc_context;
  if (!context)
    return;

  _gtk_settings_reset_rc_values (context->settings);
  gtk_rc_clear_styles (context);
  gtk_rc_clear_rc_files (context);

  if (context->default_style)
    g_object_unref (context->default_style);

  g_strfreev (context->pixmap_path);

  g_free (context->theme_name);
  g_free (context->key_theme_name);
  g_free (context->font_name);

  if (context->color_hash)
    g_hash_table_unref (context->color_hash);

  g_signal_handlers_disconnect_by_func (settings,
					gtk_rc_settings_changed, context);
  g_signal_handlers_disconnect_by_func (settings,
					gtk_rc_font_name_changed, context);
  g_signal_handlers_disconnect_by_func (settings,
					gtk_rc_color_hash_changed, context);

  rc_contexts = g_slist_remove (rc_contexts, context);

  g_free (context);

  settings->rc_context = NULL;
}

784 785 786 787 788 789
static void
gtk_rc_parse_named (GtkRcContext *context,
		    const gchar  *name,
		    const gchar  *type)
{
  gchar *path = NULL;
790
  const gchar *home_dir;
791 792 793
  gchar *subpath;

  if (type)
794
    subpath = g_strconcat ("gtk-2.0-", type,
795 796 797
			   G_DIR_SEPARATOR_S "gtkrc",
			   NULL);
  else
798
    subpath = g_strdup ("gtk-2.0" G_DIR_SEPARATOR_S "gtkrc");
799 800 801 802 803 804
  
  /* First look in the users home directory
   */
  home_dir = g_get_home_dir ();
  if (home_dir)
    {
805
      path = g_build_filename (home_dir, ".themes", name, subpath, NULL);
806 807 808 809 810 811 812
      if (!g_file_test (path, G_FILE_TEST_EXISTS))
	{
	  g_free (path);
	  path = NULL;
	}
    }

813
  if (!path)
814 815
    {
      gchar *theme_dir = gtk_rc_get_theme_dir ();
816
      path = g_build_filename (theme_dir, name, subpath, NULL);
817 818 819 820 821 822 823 824 825 826
      g_free (theme_dir);
      
      if (!g_file_test (path, G_FILE_TEST_EXISTS))
	{
	  g_free (path);
	  path = NULL;
	}
    }

  if (path)
827
    {
Owen Taylor's avatar
Owen Taylor committed
828
      gtk_rc_context_parse_file (context, path, GTK_PATH_PRIO_THEME, FALSE);
829 830 831 832 833 834 835 836 837
      g_free (path);
    }

  g_free (subpath);
}

static void
gtk_rc_parse_default_files (GtkRcContext *context)
{
838
  gint i;
839

840
  for (i = 0; gtk_rc_default_files[i] != NULL; i++)
Owen Taylor's avatar
Owen Taylor committed
841
    gtk_rc_context_parse_file (context, gtk_rc_default_files[i], GTK_PATH_PRIO_RC, FALSE);
842
}
Elliot Lee's avatar
Elliot Lee committed
843

844 845 846 847 848 849 850 851 852 853 854 855
void
_gtk_rc_init (void)
{
  static gboolean initialized = FALSE;

  if (!initialized)
    {
      initialized = TRUE;
      
      gtk_rc_add_initial_default_files ();
    }
  
856 857
  /* Default RC string */
  gtk_rc_parse_string ("style \"gtk-default-tooltips-style\" {\n"
858
		       "  bg[NORMAL] = \"#eee1b3\"\n"
859 860 861
		       "  fg[NORMAL] = \"#000000\"\n"
		       "}\n"
		       "\n"
862 863 864
		       "style \"gtk-default-progress-bar-style\" {\n"
		       "  bg[PRELIGHT] = \"#4b6983\"\n"
		       "  fg[PRELIGHT] = \"#ffffff\"\n"
865
		       "  bg[NORMAL]   = \"#c4c2bd\"\n"
866 867
		       "}\n"
		       "\n"
868 869 870 871
		       "style \"gtk-default-menu-bar-item-style\" {\n"
		       "  GtkMenuItem::horizontal_padding = 5\n"
		       "}\n"
		       "\n"
872 873 874 875 876 877 878
		       "style \"gtk-default-menu-item-style\" {\n"
		       "  bg[PRELIGHT] = \"#4b6983\"\n"
		       "  fg[PRELIGHT] = \"#ffffff\"\n"
		       "  base[PRELIGHT] = \"#4b6983\"\n"
		       "  text[PRELIGHT] = \"#ffffff\"\n"
		       "}\n"
		       "\n"
Matthias Clasen's avatar
Matthias Clasen committed
879
                       /* Make transparent tray icons work */
880 881 882 883
		       "style \"gtk-default-tray-icon-style\" {\n"
		       "  bg_pixmap[NORMAL] = \"<parent>\"\n"
		       "}\n"
		       "\n"
Matthias Clasen's avatar
Matthias Clasen committed
884 885 886 887 888
                       /* Work around clipping of accelerator underlines */
                       "style \"gtk-default-label-style\" {\n"
                       "  GtkWidget::draw-border = {0,0,0,1}\n"
                       "}\n"
                       "\n"    
889
		       "class \"GtkProgressBar\" style : gtk \"gtk-default-progress-bar-style\"\n"
890
		       "class \"GtkTrayIcon\" style : gtk \"gtk-default-tray-icon-style\"\n"
Kristian Rietveld's avatar
Kristian Rietveld committed
891
		       "widget \"gtk-tooltip*\" style : gtk \"gtk-default-tooltips-style\"\n"
892 893
		       "widget_class \"*<GtkMenuItem>*\" style : gtk \"gtk-default-menu-item-style\"\n"
		       "widget_class \"*<GtkMenuBar>*<GtkMenuItem>\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
Matthias Clasen's avatar
Matthias Clasen committed
894
                       "class \"GtkLabel\" style : gtk \"gtk-default-label-style\"\n"
895
      );
896 897
}
  
Owen Taylor's avatar
Owen Taylor committed
898 899 900 901 902 903 904
static void
gtk_rc_context_parse_string (GtkRcContext *context,
			     const gchar  *rc_string)
{
  gtk_rc_parse_any (context, "-", -1, rc_string);
}

Elliot Lee's avatar
Elliot Lee committed
905
void
906
gtk_rc_parse_string (const gchar *rc_string)
Elliot Lee's avatar
Elliot Lee committed
907
{
908
  GtkRcFile *rc_file;
Owen Taylor's avatar
Owen Taylor committed
909
  GSList *tmp_list;
910
      
911
  g_return_if_fail (rc_string != NULL);
Elliot Lee's avatar
Elliot Lee committed
912

913 914 915 916
  rc_file = g_new (GtkRcFile, 1);
  rc_file->is_string = TRUE;
  rc_file->name = g_strdup (rc_string);
  rc_file->canonical_name = NULL;
917
  rc_file->directory = NULL;
918 919
  rc_file->mtime = 0;
  rc_file->reload = TRUE;
920
  
Owen Taylor's avatar
Owen Taylor committed
921
  global_rc_files = g_slist_append (global_rc_files, rc_file);
922

Owen Taylor's avatar
Owen Taylor committed
923 924
  for (tmp_list = rc_contexts; tmp_list; tmp_list = tmp_list->next)
    gtk_rc_context_parse_string (tmp_list->data, rc_string);
925
}
Elliot Lee's avatar
Elliot Lee committed
926

Owen Taylor's avatar
Owen Taylor committed
927 928 929 930
static GtkRcFile *
add_to_rc_file_list (GSList     **rc_file_list,
		     const char  *filename,
		     gboolean     reload)
931
{
932
  GSList *tmp_list;
Owen Taylor's avatar
Owen Taylor committed
933
  GtkRcFile *rc_file;
934
  
Owen Taylor's avatar
Owen Taylor committed
935
  tmp_list = *rc_file_list;
936 937 938 939
  while (tmp_list)
    {
      rc_file = tmp_list->data;
      if (!strcmp (rc_file->name, filename))
Owen Taylor's avatar
Owen Taylor committed
940
	return rc_file;
941 942 943 944
      
      tmp_list = tmp_list->next;
    }

Owen Taylor's avatar
Owen Taylor committed
945 946 947 948
  rc_file = g_new (GtkRcFile, 1);
  rc_file->is_string = FALSE;
  rc_file->name = g_strdup (filename);
  rc_file->canonical_name = NULL;
949
  rc_file->directory = NULL;
Owen Taylor's avatar
Owen Taylor committed
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966
  rc_file->mtime = 0;
  rc_file->reload = reload;
  
  *rc_file_list = g_slist_append (*rc_file_list, rc_file);
  
  return rc_file;
}

static void
gtk_rc_context_parse_one_file (GtkRcContext *context,
			       const gchar  *filename,
			       gint          priority,
			       gboolean      reload)
{
  GtkRcFile *rc_file;
  struct stat statbuf;
  gint saved_priority;
967

Owen Taylor's avatar
Owen Taylor committed
968 969 970 971 972 973
  g_return_if_fail (filename != NULL);

  saved_priority = context->default_priority;
  context->default_priority = priority;

  rc_file = add_to_rc_file_list (&context->rc_files, filename, reload);
974

975 976 977 978
  if (!rc_file->canonical_name)
    {
      /* Get the absolute pathname */

979
      if (g_path_is_absolute (rc_file->name))
980 981 982
	rc_file->canonical_name = rc_file->name;
      else
	{
983 984 985
	  gchar *cwd;

	  cwd = g_get_current_dir ();
986
	  rc_file->canonical_name = g_build_filename (cwd, rc_file->name, NULL);
987
	  g_free (cwd);
988
	}
989 990
      
      rc_file->directory = g_path_get_dirname (rc_file->canonical_name);
991 992
    }

993 994 995 996 997
  /* If the file is already being parsed (recursion), do nothing
   */
  if (g_slist_find (current_files_stack, rc_file))
    return;

998
  if (!g_lstat (rc_file->canonical_name, &statbuf))
999 1000
    {
      gint fd;
1001
      
1002
      rc_file->mtime = statbuf.st_mtime;
1003

1004
      fd = g_open (rc_file->canonical_name, O_RDONLY, 0);
1005
      if (fd < 0)
1006
	goto out;
Elliot Lee's avatar
Elliot Lee committed
1007

1008 1009
      /* Temporarily push information for this file on
       * a stack of current files while parsing it.
1010
       */
1011
      current_files_stack = g_slist_prepend (current_files_stack, rc_file);
1012
      gtk_rc_parse_any (context, filename, fd, NULL);
1013 1014
      current_files_stack = g_slist_delete_link (current_files_stack,
						 current_files_stack);
Elliot Lee's avatar
Elliot Lee committed
1015

1016 1017
      close (fd);
    }
1018 1019 1020

 out:
  context->default_priority = saved_priority;
Elliot Lee's avatar
Elliot Lee committed
1021 1022
}

1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
static gchar *
strchr_len (const gchar *str, gint len, char c)
{
  while (len--)
    {
      if (*str == c)
	return (gchar *)str;

      str++;
    }

  return NULL;
}

static void
Owen Taylor's avatar
Owen Taylor committed
1038 1039 1040 1041
gtk_rc_context_parse_file (GtkRcContext *context,
			   const gchar  *filename,
			   gint          priority,
			   gboolean      reload)
1042 1043 1044 1045
{
  gchar *locale_suffixes[2];
  gint n_locale_suffixes = 0;
  gchar *p;
1046
  gchar *locale;
1047 1048 1049
  gint length, j;
  gboolean found = FALSE;

1050
  locale = _gtk_get_lc_ctype ();
1051 1052 1053

  if (strcmp (locale, "C") && strcmp (locale, "POSIX"))
    {
1054
      /* Determine locale-specific suffixes for RC files.
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
       */
      length = strlen (locale);
      
      p = strchr (locale, '@');
      if (p)
	length = p - locale;

      p = strchr_len (locale, length, '.');
      if (p)
	length = p - locale;
      
      locale_suffixes[n_locale_suffixes++] = g_strndup (locale, length);
      
      p = strchr_len (locale, length, '_');
      if (p)
	{
	  length = p - locale;
	  locale_suffixes[n_locale_suffixes++] = g_strndup (locale, length);
	}
    }
1075 1076

  g_free (locale);
1077
  
Owen Taylor's avatar
Owen Taylor committed
1078
  gtk_rc_context_parse_one_file (context, filename, priority, reload);
1079 1080 1081 1082 1083 1084 1085
  for (j = 0; j < n_locale_suffixes; j++)
    {
      if (!found)
	{
	  gchar *name = g_strconcat (filename, ".", locale_suffixes[j], NULL);
	  if (g_file_test (name, G_FILE_TEST_EXISTS))
	    {
Owen Taylor's avatar
Owen Taylor committed
1086
	      gtk_rc_context_parse_one_file (context, name, priority, FALSE);
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
	      found = TRUE;
	    }
	      
	  g_free (name);
	}
      
      g_free (locale_suffixes[j]);
    }
}

1097
void
1098 1099
gtk_rc_parse (const gchar *filename)
{
Owen Taylor's avatar
Owen Taylor committed
1100 1101
  GSList *tmp_list;
  
1102 1103
  g_return_if_fail (filename != NULL);

Owen Taylor's avatar
Owen Taylor committed
1104 1105 1106 1107
  add_to_rc_file_list (&global_rc_files, filename, TRUE);
  
  for (tmp_list = rc_contexts; tmp_list; tmp_list = tmp_list->next)
    gtk_rc_context_parse_file (tmp_list->data, filename, GTK_PATH_PRIO_RC, TRUE);
1108 1109
}

1110 1111
/* Handling of RC styles */

Matthias Clasen's avatar
Matthias Clasen committed
1112
G_DEFINE_TYPE (GtkRcStyle, gtk_rc_style, G_TYPE_OBJECT)
1113

1114 1115 1116
static void
gtk_rc_style_init (GtkRcStyle *style)
{
1117
  GtkRcStylePrivate *priv = GTK_RC_STYLE_GET_PRIVATE (style);
1118 1119 1120
  guint i;

  style->name = NULL;
1121 1122
  style->font_desc = NULL;

1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
  for (i = 0; i < 5; i++)
    {
      static const GdkColor init_color = { 0, 0, 0, 0, };

      style->bg_pixmap_name[i] = NULL;
      style->color_flags[i] = 0;
      style->fg[i] = init_color;
      style->bg[i] = init_color;
      style->text[i] = init_color;
      style->base[i] = init_color;
    }
  style->xthickness = -1;
  style->ythickness = -1;
1136
  style->rc_properties = NULL;
1137

1138
  style->rc_style_lists = NULL;
1139
  style->icon_factories = NULL;
1140 1141

  priv->color_hashes = NULL;
1142
}
1143

1144 1145
static void
gtk_rc_style_class_init (GtkRcStyleClass *klass)
1146
{
1147 1148 1149
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  
  object_class->finalize = gtk_rc_style_finalize;
1150 1151

  klass->parse = NULL;
1152
  klass->create_rc_style = gtk_rc_style_real_create_rc_style;
1153 1154
  klass->merge = gtk_rc_style_real_merge;
  klass->create_style = gtk_rc_style_real_create_style;
1155 1156

  g_type_class_add_private (object_class, sizeof (GtkRcStylePrivate));
1157 1158
}

1159 1160
static void
gtk_rc_style_finalize (GObject *object)
1161
{
1162 1163
  GSList *tmp_list1, *tmp_list2;
  GtkRcStyle *rc_style;
1164
  GtkRcStylePrivate *rc_priv;
1165
  gint i;
1166

1167
  rc_style = GTK_RC_STYLE (object);
1168 1169
  rc_priv = GTK_RC_STYLE_GET_PRIVATE (rc_style);

1170
  g_free (rc_style->name);
1171 1172
  if (rc_style->font_desc)
    pango_font_description_free (rc_style->font_desc);
1173
      
1174
  for (i = 0; i < 5; i++)
1175
    g_free (rc_style->bg_pixmap_name[i]);
1176 1177 1178 1179 1180 1181 1182 1183 1184
  
  /* Now remove all references to this rc_style from
   * realized_style_ht
   */
  tmp_list1 = rc_style->rc_style_lists;
  while (tmp_list1)
    {
      GSList *rc_styles = tmp_list1->data;
      GtkStyle *style = g_hash_table_lookup (realized_style_ht, rc_styles);
Manish Singh's avatar
Manish Singh committed
1185
      g_object_unref (style);
1186 1187 1188

      /* Remove the list of styles from the other rc_styles
       * in the list
1189
       */
1190 1191 1192 1193 1194 1195
      tmp_list2 = rc_styles;
      while (tmp_list2)
        {
          GtkRcStyle *other_style = tmp_list2->data;

          if (other_style != rc_style)
1196 1197
            other_style->rc_style_lists = g_slist_remove_all (other_style->rc_style_lists,
							      rc_styles);
1198 1199
          tmp_list2 = tmp_list2->next;
        }
1200

1201 1202 1203 1204
      /* And from the hash table itself
       */
      g_hash_table_remove (realized_style_ht, rc_styles);
      g_slist_free (rc_styles);
1205

1206 1207 1208
      tmp_list1 = tmp_list1->next;
    }
  g_slist_free (rc_style->rc_style_lists);
1209

1210 1211 1212 1213
  if (rc_style->rc_properties)
    {
      guint i;

1214
      for (i = 0; i < rc_style->rc_properties->len; i++)
1215
	{
1216
	  GtkRcProperty *node = &g_array_index (rc_style->rc_properties, GtkRcProperty, i);