gtkrc.c 101 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 43
#include "gtkalias.h"

Tor Lillqvist's avatar
Tor Lillqvist committed
44 45 46 47
#ifndef HAVE_LSTAT
#define lstat stat
#endif

Owen Taylor's avatar
Owen Taylor committed
48 49 50
#include <glib.h>
#include "gdkconfig.h"

51
#include "gtkversion.h"
Elliot Lee's avatar
Elliot Lee committed
52
#include "gtkrc.h"
Tim Janik's avatar
Tim Janik committed
53
#include "gtkbindings.h"
54
#include "gtkthemes.h"
55
#include "gtkintl.h"
56
#include "gtkiconfactory.h"
57
#include "gtkmain.h"
58
#include "gtkprivate.h"
59
#include "gtksettings.h"
60
#include "gtkwindow.h"
Elliot Lee's avatar
Elliot Lee committed
61

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

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

Elliot Lee's avatar
Elliot Lee committed
70 71
struct _GtkRcSet
{
72
  GPatternSpec *pspec;
73 74
  GtkRcStyle *rc_style;
  gint priority;
Elliot Lee's avatar
Elliot Lee committed
75 76
};

77 78
struct _GtkRcFile
{
79
  gboolean is_string;		/* If TRUE, name is a string to parse with gtk_rc_parse_string() */
80 81 82
  time_t mtime;
  gchar *name;
  gchar *canonical_name;
83
  gchar *directory;
84
  guint reload;
85
};
Elliot Lee's avatar
Elliot Lee committed
86

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
#define GTK_RC_MAX_PIXMAP_PATHS 128

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;
102
  gchar *font_name;
103 104 105 106
  
  gchar *pixmap_path[GTK_RC_MAX_PIXMAP_PATHS];

  gint default_priority;
107
  GtkStyle *default_style;
108 109 110 111
};

static GtkRcContext *gtk_rc_context_get              (GtkSettings     *settings);

112 113 114
static guint       gtk_rc_style_hash                 (const gchar     *name);
static gboolean    gtk_rc_style_equal                (const gchar     *a,
                                                      const gchar     *b);
115
static guint       gtk_rc_styles_hash                (const GSList    *rc_styles);
116
static gboolean    gtk_rc_styles_equal               (const GSList    *a,
117
                                                      const GSList    *b);
118 119
static GtkRcStyle* gtk_rc_style_find                 (GtkRcContext    *context,
						      const gchar     *name);
120 121 122
static GSList *    gtk_rc_styles_match               (GSList          *rc_styles,
                                                      GSList          *sets,
                                                      guint            path_length,
123 124
                                                      const gchar     *path,
                                                      const gchar     *path_reversed);
125 126 127 128
static GtkStyle *  gtk_rc_style_to_style             (GtkRcContext    *context,
						      GtkRcStyle      *rc_style);
static GtkStyle*   gtk_rc_init_style                 (GtkRcContext    *context,
						      GSList          *rc_styles);
129 130 131 132
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
133
static void        gtk_rc_context_parse_file         (GtkRcContext    *context,
134 135
						      const gchar     *filename,
						      gint             priority,
136
                                                      gboolean         reload);
137 138
static void        gtk_rc_parse_any                  (GtkRcContext    *context,
						      const gchar     *input_name,
139 140
                                                      gint             input_fd,
                                                      const gchar     *input_string);
141 142 143 144
static guint       gtk_rc_parse_statement            (GtkRcContext    *context,
						      GScanner        *scanner);
static guint       gtk_rc_parse_style                (GtkRcContext    *context,
						      GScanner        *scanner);
145 146
static guint       gtk_rc_parse_assignment           (GScanner        *scanner,
						      GtkRcProperty   *prop);
147 148 149 150 151 152
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);
153 154 155 156 157 158
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);
159 160
static guint       gtk_rc_parse_bg_pixmap            (GtkRcContext    *context,
						      GScanner        *scanner,
161 162 163 164 165 166 167
                                                      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);
168 169
static guint       gtk_rc_parse_engine               (GtkRcContext    *context,
						      GScanner        *scanner,
170
                                                      GtkRcStyle     **rc_style);
171 172 173 174 175
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);
176
static guint       gtk_rc_parse_module_path          (GScanner        *scanner);
Owen Taylor's avatar
Owen Taylor committed
177
static guint       gtk_rc_parse_im_module_file       (GScanner        *scanner);
178 179 180 181
static guint       gtk_rc_parse_path_pattern         (GtkRcContext    *context,
						      GScanner        *scanner);
static guint       gtk_rc_parse_stock                (GtkRcContext    *context,
						      GScanner        *scanner,
182 183
                                                      GtkRcStyle      *rc_style,
                                                      GtkIconFactory  *factory);
184 185 186
static void        gtk_rc_clear_hash_node            (gpointer         key,
                                                      gpointer         data,
                                                      gpointer         user_data);
187
static void        gtk_rc_clear_styles               (GtkRcContext    *context);
188
static void        gtk_rc_add_initial_default_files  (void);
189

190 191 192 193 194
static void        gtk_rc_style_init                 (GtkRcStyle      *style);
static void        gtk_rc_style_class_init           (GtkRcStyleClass *klass);
static void        gtk_rc_style_finalize             (GObject         *object);
static void        gtk_rc_style_real_merge           (GtkRcStyle      *dest,
                                                      GtkRcStyle      *src);
195 196 197 198
static GtkRcStyle* gtk_rc_style_real_create_rc_style (GtkRcStyle      *rc_style);
static GtkStyle*   gtk_rc_style_real_create_style    (GtkRcStyle      *rc_style);
static gint	   gtk_rc_properties_cmp	     (gconstpointer    bsearch_node1,
						      gconstpointer    bsearch_node2);
199 200

static gpointer parent_class = NULL;
201

202
static const GScannerConfig gtk_rc_scanner_config =
203 204
{
  (
205
   " \t\r\n"
206 207 208 209 210 211 212 213
   )			/* cset_skip_characters */,
  (
   G_CSET_a_2_z
   "_"
   G_CSET_A_2_Z
   )			/* cset_identifier_first */,
  (
   G_CSET_a_2_z
214
   "_-0123456789"
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
   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 */,
233
  TRUE			/* scan_string_sq */,
234 235 236
  TRUE			/* scan_string_dq */,
  TRUE			/* numbers_2_int */,
  FALSE			/* int_2_float */,
237
  FALSE			/* identifier_2_string */,
238 239
  TRUE			/* char_2_token */,
  TRUE			/* symbol_2_token */,
240
  FALSE			/* scope_0_fallback */,
241 242
};

243
static const struct
Elliot Lee's avatar
Elliot Lee committed
244
{
245
  gchar *name;
246
  guint token;
247
} symbols[] = {
248 249 250 251 252 253 254 255 256
  { "include", GTK_RC_TOKEN_INCLUDE },
  { "NORMAL", GTK_RC_TOKEN_NORMAL },
  { "ACTIVE", GTK_RC_TOKEN_ACTIVE },
  { "PRELIGHT", GTK_RC_TOKEN_PRELIGHT },
  { "SELECTED", GTK_RC_TOKEN_SELECTED },
  { "INSENSITIVE", GTK_RC_TOKEN_INSENSITIVE },
  { "fg", GTK_RC_TOKEN_FG },
  { "bg", GTK_RC_TOKEN_BG },
  { "text", GTK_RC_TOKEN_TEXT },
257 258 259
  { "base", GTK_RC_TOKEN_BASE },
  { "xthickness", GTK_RC_TOKEN_XTHICKNESS },
  { "ythickness", GTK_RC_TOKEN_YTHICKNESS },
260 261
  { "font", GTK_RC_TOKEN_FONT },
  { "fontset", GTK_RC_TOKEN_FONTSET },
262
  { "font_name", GTK_RC_TOKEN_FONT_NAME },
263 264 265 266 267 268 269 270 271 272 273
  { "bg_pixmap", GTK_RC_TOKEN_BG_PIXMAP },
  { "pixmap_path", GTK_RC_TOKEN_PIXMAP_PATH },
  { "style", GTK_RC_TOKEN_STYLE },
  { "binding", GTK_RC_TOKEN_BINDING },
  { "bind", GTK_RC_TOKEN_BIND },
  { "widget", GTK_RC_TOKEN_WIDGET },
  { "widget_class", GTK_RC_TOKEN_WIDGET_CLASS },
  { "class", GTK_RC_TOKEN_CLASS },
  { "lowest", GTK_RC_TOKEN_LOWEST },
  { "gtk", GTK_RC_TOKEN_GTK },
  { "application", GTK_RC_TOKEN_APPLICATION },
274
  { "theme", GTK_RC_TOKEN_THEME },
275 276
  { "rc", GTK_RC_TOKEN_RC },
  { "highest", GTK_RC_TOKEN_HIGHEST },
277 278
  { "engine", GTK_RC_TOKEN_ENGINE },
  { "module_path", GTK_RC_TOKEN_MODULE_PATH },
279
  { "stock", GTK_RC_TOKEN_STOCK },
Owen Taylor's avatar
Owen Taylor committed
280
  { "im_module_file", GTK_RC_TOKEN_IM_MODULE_FILE },
281 282
  { "LTR", GTK_RC_TOKEN_LTR },
  { "RTL", GTK_RC_TOKEN_RTL }
283
};
284

285
static GHashTable *realized_style_ht = NULL;
Elliot Lee's avatar
Elliot Lee committed
286

Owen Taylor's avatar
Owen Taylor committed
287 288
static gchar *im_module_file = NULL;

289 290 291
#define GTK_RC_MAX_DEFAULT_FILES 128
static gchar *gtk_rc_default_files[GTK_RC_MAX_DEFAULT_FILES];

292 293 294
/* 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.
295
 */
296
static GSList *current_files_stack = NULL;
297

Owen Taylor's avatar
Owen Taylor committed
298 299 300 301 302 303 304 305
/* 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;

306 307
/* RC file handling */

Owen Taylor's avatar
Owen Taylor committed
308 309
static gchar *
gtk_rc_make_default_dir (const gchar *type)
310
{
311 312
  const gchar *var;
  gchar *path;
313

Matthias Clasen's avatar
Matthias Clasen committed
314
  var = g_getenv ("GTK_EXE_PREFIX");
315
  if (var)
316
    path = g_build_filename (var, "lib", "gtk-2.0", GTK_BINARY_VERSION, type, NULL);
317
  else
318
    path = g_build_filename (GTK_LIBDIR, "gtk-2.0", GTK_BINARY_VERSION, type, NULL);
319

320 321 322
  return path;
}

Matthias Clasen's avatar
Matthias Clasen committed
323 324 325 326 327 328
/**
 * 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
329 330 331 332
 * 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
333
 */
334
gchar *
Owen Taylor's avatar
Owen Taylor committed
335 336
gtk_rc_get_im_module_path (void)
{
337 338 339
  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
340

341
  return result;
Owen Taylor's avatar
Owen Taylor committed
342 343
}

Matthias Clasen's avatar
Matthias Clasen committed
344 345 346
/**
 * gtk_rc_get_im_module_file:
 * @returns: a newly-allocated string containing the name of the file
347
 * listing the IM modules available for loading
Matthias Clasen's avatar
Matthias Clasen committed
348 349 350 351
 *
 * 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
352
 */
Owen Taylor's avatar
Owen Taylor committed
353 354 355
gchar *
gtk_rc_get_im_module_file (void)
{
356
  gchar *result = g_strdup (g_getenv ("GTK_IM_MODULE_FILE"));
Owen Taylor's avatar
Owen Taylor committed
357 358 359 360

  if (!result)
    {
      if (im_module_file)
Tor Lillqvist's avatar
Tor Lillqvist committed
361
	result = g_strdup (im_module_file);
Owen Taylor's avatar
Owen Taylor committed
362
      else
363
	result = g_build_filename (GTK_SYSCONFDIR, "gtk-2.0", "gtk.immodules", NULL);
Owen Taylor's avatar
Owen Taylor committed
364 365
    }

Tor Lillqvist's avatar
Tor Lillqvist committed
366
  return result;
Owen Taylor's avatar
Owen Taylor committed
367 368 369
}

gchar *
Hans Breuer's avatar
Hans Breuer committed
370
gtk_rc_get_theme_dir (void)
371
{
372 373
  const gchar *var;
  gchar *path;
374

Matthias Clasen's avatar
Matthias Clasen committed
375
  var = g_getenv ("GTK_DATA_PREFIX");
376
  if (var)
377
    path = g_build_filename (var, "share", "themes", NULL);
378
  else
379
    path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
380

381 382 383
  return path;
}

384 385 386 387 388
/**
 * 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
389 390
 * see the docs for <envar>GTK_PATH</envar> in
 * <xref linkend="gtk-running"/>.
391 392 393
 * 
 * return value: the directory. (Must be freed with g_free())
 **/
Owen Taylor's avatar
Owen Taylor committed
394
gchar *
Hans Breuer's avatar
Hans Breuer committed
395
gtk_rc_get_module_dir (void)
Owen Taylor's avatar
Owen Taylor committed
396 397 398 399
{
  return gtk_rc_make_default_dir ("engines");
}

400 401 402 403
static void
gtk_rc_add_initial_default_files (void)
{
  static gint init = FALSE;
404 405
  const gchar *var;
  gchar *str;
406 407 408 409 410 411 412 413 414
  gchar **files;
  gint i;

  if (init)
    return;
  
  gtk_rc_default_files[0] = NULL;
  init = TRUE;

415
  var = g_getenv ("GTK2_RC_FILES");
416 417
  if (var)
    {
418
      files = g_strsplit (var, G_SEARCHPATH_SEPARATOR_S, 128);
419 420 421 422 423 424
      i=0;
      while (files[i])
	{
	  gtk_rc_add_default_file (files[i]);
	  i++;
	}
425
      g_strfreev (files);
426 427 428
    }
  else
    {
429
      str = g_build_filename (GTK_SYSCONFDIR, "gtk-2.0", "gtkrc", NULL);
430

431
      gtk_rc_add_default_file (str);
432
      g_free (str);
433

434 435 436
      var = g_get_home_dir ();
      if (var)
	{
437
	  str = g_build_filename (var, ".gtkrc-2.0", NULL);
438 439 440
	  gtk_rc_add_default_file (str);
	  g_free (str);
	}
441 442 443
    }
}

444 445
/**
 * gtk_rc_add_default_file:
446 447
 * @filename: the pathname to the file. If @filename is not absolute, it
 *    is searched in the current directory.
448 449 450 451
 * 
 * Adds a file to the list of files to be parsed at the
 * end of gtk_init().
 **/
452
void
453
gtk_rc_add_default_file (const gchar *filename)
454 455 456 457 458 459 460 461 462
{
  guint n;
  
  gtk_rc_add_initial_default_files ();

  for (n = 0; gtk_rc_default_files[n]; n++) ;
  if (n >= GTK_RC_MAX_DEFAULT_FILES - 1)
    return;
  
463
  gtk_rc_default_files[n++] = g_strdup (filename);
464 465 466
  gtk_rc_default_files[n] = NULL;
}

467 468
/**
 * gtk_rc_set_default_files:
Matthias Clasen's avatar
Matthias Clasen committed
469
 * @filenames: A %NULL-terminated list of filenames.
470 471
 * 
 * Sets the list of files that GTK+ will read at the
Matthias Clasen's avatar
Matthias Clasen committed
472
 * end of gtk_init().
473
 **/
474
void
475
gtk_rc_set_default_files (gchar **filenames)
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
{
  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;
491
  while (filenames[i] != NULL)
492
    {
493
      gtk_rc_add_default_file (filenames[i]);
494 495 496 497
      i++;
    }
}

498 499 500 501
/**
 * gtk_rc_get_default_files:
 * 
 * Retrieves the current list of RC files that will be parsed
Matthias Clasen's avatar
Matthias Clasen committed
502
 * at the end of gtk_init().
503
 * 
Matthias Clasen's avatar
Matthias Clasen committed
504
 * Return value: A %NULL-terminated array of filenames. This memory
505
 * is owned by GTK+ and must not be freed by the application.
Matthias Clasen's avatar
Matthias Clasen committed
506
 * If you want to store this information, you should make a copy.
507
 **/
508 509 510 511 512 513 514 515
gchar **
gtk_rc_get_default_files (void)
{
  gtk_rc_add_initial_default_files ();

  return gtk_rc_default_files;
}

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
static void
gtk_rc_settings_changed (GtkSettings  *settings,
			 GParamSpec   *pspec,
			 GtkRcContext *context)
{
  gchar *new_theme_name;
  gchar *new_key_theme_name;

  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);
}

541 542 543 544 545 546 547 548
static void
gtk_rc_font_name_changed (GtkSettings  *settings,
                          GParamSpec   *pspec,
                          GtkRcContext *context)
{
  _gtk_rc_context_get_default_font_name (settings);
}

549 550 551 552 553 554 555 556 557 558 559 560 561
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;
562
      context->default_style = NULL;
563 564 565 566

      g_object_get (settings,
		    "gtk-theme-name", &context->theme_name,
		    "gtk-key-theme-name", &context->key_theme_name,
567
		    "gtk-font-name", &context->font_name,
568 569 570 571 572 573 574 575 576 577
		    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);
578 579 580 581 582
      g_signal_connect (settings,
			"notify::gtk-font-name",
			G_CALLBACK (gtk_rc_font_name_changed),
			context);

583 584 585 586
      
      context->pixmap_path[0] = NULL;

      context->default_priority = GTK_PATH_PRIO_RC;
Owen Taylor's avatar
Owen Taylor committed
587 588

      rc_contexts = g_slist_prepend (rc_contexts, settings->rc_context);
589 590 591 592 593 594 595 596 597 598 599
    }

  return settings->rc_context;
}

static void
gtk_rc_parse_named (GtkRcContext *context,
		    const gchar  *name,
		    const gchar  *type)
{
  gchar *path = NULL;
600
  const gchar *home_dir;
601 602 603
  gchar *subpath;

  if (type)
604
    subpath = g_strconcat ("gtk-2.0-", type,
605 606 607
			   G_DIR_SEPARATOR_S "gtkrc",
			   NULL);
  else
608
    subpath = g_strdup ("gtk-2.0" G_DIR_SEPARATOR_S "gtkrc");
609 610 611 612 613 614
  
  /* First look in the users home directory
   */
  home_dir = g_get_home_dir ();
  if (home_dir)
    {
615
      path = g_build_filename (home_dir, ".themes", name, subpath, NULL);
616 617 618 619 620 621 622
      if (!g_file_test (path, G_FILE_TEST_EXISTS))
	{
	  g_free (path);
	  path = NULL;
	}
    }

623
  if (!path)
624 625
    {
      gchar *theme_dir = gtk_rc_get_theme_dir ();
626
      path = g_build_filename (theme_dir, name, subpath, NULL);
627 628 629 630 631 632 633 634 635 636
      g_free (theme_dir);
      
      if (!g_file_test (path, G_FILE_TEST_EXISTS))
	{
	  g_free (path);
	  path = NULL;
	}
    }

  if (path)
637
    {
Owen Taylor's avatar
Owen Taylor committed
638
      gtk_rc_context_parse_file (context, path, GTK_PATH_PRIO_THEME, FALSE);
639 640 641 642 643 644 645 646 647
      g_free (path);
    }

  g_free (subpath);
}

static void
gtk_rc_parse_default_files (GtkRcContext *context)
{
648
  gint i;
649

650
  for (i = 0; gtk_rc_default_files[i] != NULL; i++)
Owen Taylor's avatar
Owen Taylor committed
651
    gtk_rc_context_parse_file (context, gtk_rc_default_files[i], GTK_PATH_PRIO_RC, FALSE);
652
}
Elliot Lee's avatar
Elliot Lee committed
653

654 655 656 657 658 659 660 661 662 663 664 665
void
_gtk_rc_init (void)
{
  static gboolean initialized = FALSE;

  if (!initialized)
    {
      initialized = TRUE;
      
      gtk_rc_add_initial_default_files ();
    }
  
666 667
  /* Default RC string */
  gtk_rc_parse_string ("style \"gtk-default-tooltips-style\" {\n"
668
		       "  bg[NORMAL] = \"#eee1b3\"\n"
669 670 671
		       "  fg[NORMAL] = \"#000000\"\n"
		       "}\n"
		       "\n"
672 673 674
		       "style \"gtk-default-progress-bar-style\" {\n"
		       "  bg[PRELIGHT] = \"#4b6983\"\n"
		       "  fg[PRELIGHT] = \"#ffffff\"\n"
675
		       "  bg[NORMAL]   = \"#c4c2bd\"\n"
676 677
		       "}\n"
		       "\n"
678 679 680 681
		       "style \"gtk-default-menu-bar-item-style\" {\n"
		       "  GtkMenuItem::horizontal_padding = 5\n"
		       "}\n"
		       "\n"
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
		       "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"
		       "class \"GtkProgressBar\" style : gtk \"gtk-default-progress-bar-style\"\n"
		       "widget \"gtk-tooltips*\" style : gtk \"gtk-default-tooltips-style\"\n"
		       "class \"GtkMenuItem\" style : gtk \"gtk-default-menu-item-style\"\n"
		       "widget_class \"*.GtkMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
		       "widget_class \"*.GtkAccelMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
		       "widget_class \"*.GtkRadioMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
		       "widget_class \"*.GtkCheckMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
		       "widget_class \"*.GtkImageMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
		       "widget_class \"*.GtkSeparatorMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
698
		       "widget_class \"*.GtkCellViewMenuItem.*\" style : gtk \"gtk-default-menu-item-style\"\n"
699 700 701 702 703
		       "widget_class \"*GtkMenuBar*GtkMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
		       "widget_class \"*GtkMenuBar*GtkAccelMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
		       "widget_class \"*GtkMenuBar*GtkRadioMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
		       "widget_class \"*GtkMenuBar*GtkCheckMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
		       "widget_class \"*GtkMenuBar*GtkImageMenuItem\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
704
      );
705 706
}
  
Owen Taylor's avatar
Owen Taylor committed
707 708 709 710 711 712 713
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
714
void
715
gtk_rc_parse_string (const gchar *rc_string)
Elliot Lee's avatar
Elliot Lee committed
716
{
717
  GtkRcFile *rc_file;
Owen Taylor's avatar
Owen Taylor committed
718
  GSList *tmp_list;
719
      
720
  g_return_if_fail (rc_string != NULL);
Elliot Lee's avatar
Elliot Lee committed
721

722 723 724 725
  rc_file = g_new (GtkRcFile, 1);
  rc_file->is_string = TRUE;
  rc_file->name = g_strdup (rc_string);
  rc_file->canonical_name = NULL;
726
  rc_file->directory = NULL;
727 728
  rc_file->mtime = 0;
  rc_file->reload = TRUE;
729
  
Owen Taylor's avatar
Owen Taylor committed
730
  global_rc_files = g_slist_append (global_rc_files, rc_file);
731

Owen Taylor's avatar
Owen Taylor committed
732 733
  for (tmp_list = rc_contexts; tmp_list; tmp_list = tmp_list->next)
    gtk_rc_context_parse_string (tmp_list->data, rc_string);
734
}
Elliot Lee's avatar
Elliot Lee committed
735

Owen Taylor's avatar
Owen Taylor committed
736 737 738 739
static GtkRcFile *
add_to_rc_file_list (GSList     **rc_file_list,
		     const char  *filename,
		     gboolean     reload)
740
{
741
  GSList *tmp_list;
Owen Taylor's avatar
Owen Taylor committed
742
  GtkRcFile *rc_file;
743
  
Owen Taylor's avatar
Owen Taylor committed
744
  tmp_list = *rc_file_list;
745 746 747 748
  while (tmp_list)
    {
      rc_file = tmp_list->data;
      if (!strcmp (rc_file->name, filename))
Owen Taylor's avatar
Owen Taylor committed
749
	return rc_file;
750 751 752 753
      
      tmp_list = tmp_list->next;
    }

Owen Taylor's avatar
Owen Taylor committed
754 755 756 757
  rc_file = g_new (GtkRcFile, 1);
  rc_file->is_string = FALSE;
  rc_file->name = g_strdup (filename);
  rc_file->canonical_name = NULL;
758
  rc_file->directory = NULL;
Owen Taylor's avatar
Owen Taylor committed
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
  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;
776

Owen Taylor's avatar
Owen Taylor committed
777 778 779 780 781 782
  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);
783

784 785 786 787
  if (!rc_file->canonical_name)
    {
      /* Get the absolute pathname */

788
      if (g_path_is_absolute (rc_file->name))
789 790 791
	rc_file->canonical_name = rc_file->name;
      else
	{
792 793 794
	  gchar *cwd;

	  cwd = g_get_current_dir ();
795
	  rc_file->canonical_name = g_build_filename (cwd, rc_file->name, NULL);
796
	  g_free (cwd);
797
	}
798 799
      
      rc_file->directory = g_path_get_dirname (rc_file->canonical_name);
800 801
    }

802 803 804 805 806
  /* If the file is already being parsed (recursion), do nothing
   */
  if (g_slist_find (current_files_stack, rc_file))
    return;

807
  if (!lstat (rc_file->canonical_name, &statbuf))
808 809
    {
      gint fd;
810
      
811
      rc_file->mtime = statbuf.st_mtime;
812

813 814
      fd = open (rc_file->canonical_name, O_RDONLY);
      if (fd < 0)
815
	goto out;
Elliot Lee's avatar
Elliot Lee committed
816

817 818
      /* Temporarily push information for this file on
       * a stack of current files while parsing it.
819
       */
820
      current_files_stack = g_slist_prepend (current_files_stack, rc_file);
821
      gtk_rc_parse_any (context, filename, fd, NULL);
822 823
      current_files_stack = g_slist_delete_link (current_files_stack,
						 current_files_stack);
Elliot Lee's avatar
Elliot Lee committed
824

825 826
      close (fd);
    }
827 828 829

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

832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
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
847 848 849 850
gtk_rc_context_parse_file (GtkRcContext *context,
			   const gchar  *filename,
			   gint          priority,
			   gboolean      reload)
851 852 853 854
{
  gchar *locale_suffixes[2];
  gint n_locale_suffixes = 0;
  gchar *p;
855
  gchar *locale;
856 857 858
  gint length, j;
  gboolean found = FALSE;

859
  locale = _gtk_get_lc_ctype ();
860 861 862

  if (strcmp (locale, "C") && strcmp (locale, "POSIX"))
    {
863
      /* Determine locale-specific suffixes for RC files.
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
       */
      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);
	}
    }
884 885

  g_free (locale);
886
  
Owen Taylor's avatar
Owen Taylor committed
887
  gtk_rc_context_parse_one_file (context, filename, priority, reload);
888 889 890 891 892 893 894
  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
895
	      gtk_rc_context_parse_one_file (context, name, priority, FALSE);
896 897 898 899 900 901 902 903 904 905
	      found = TRUE;
	    }
	      
	  g_free (name);
	}
      
      g_free (locale_suffixes[j]);
    }
}

906
void
907 908
gtk_rc_parse (const gchar *filename)
{
Owen Taylor's avatar
Owen Taylor committed
909 910
  GSList *tmp_list;
  
911 912
  g_return_if_fail (filename != NULL);

Owen Taylor's avatar
Owen Taylor committed
913 914 915 916
  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);
917 918
}

919 920
/* Handling of RC styles */

921 922
GType
gtk_rc_style_get_type (void)
923
{
Manish Singh's avatar
Manish Singh committed
924
  static GType rc_style_type = 0;
925

Manish Singh's avatar
Manish Singh committed
926
  if (!rc_style_type)
927
    {
Manish Singh's avatar
Manish Singh committed
928
      static const GTypeInfo rc_style_info =
929 930 931 932 933 934 935 936 937 938 939 940
      {
        sizeof (GtkRcStyleClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) gtk_rc_style_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (GtkRcStyle),
        0,              /* n_preallocs */
        (GInstanceInitFunc) gtk_rc_style_init,
      };
      
Manish Singh's avatar
Manish Singh committed
941 942
      rc_style_type = g_type_register_static (G_TYPE_OBJECT, "GtkRcStyle",
					      &rc_style_info, 0);
943 944
    }
  
Manish Singh's avatar
Manish Singh committed
945
  return rc_style_type;
946
}
947

948 949 950
static void
gtk_rc_style_init (GtkRcStyle *style)
{
951 952 953
  guint i;

  style->name = NULL;
954 955
  style->font_desc = NULL;

956 957 958 959 960 961 962 963 964 965 966 967 968
  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;
969
  style->rc_properties = NULL;
970

971
  style->rc_style_lists = NULL;
972
  style->icon_factories = NULL;
973
}
974

975 976
static void
gtk_rc_style_class_init (GtkRcStyleClass *klass)
977
{
978 979 980
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  
  parent_class = g_type_class_peek_parent (klass);
981

982
  object_class->finalize = gtk_rc_style_finalize;
983 984

  klass->parse = NULL;
985
  klass->create_rc_style = gtk_rc_style_real_create_rc_style;
986 987
  klass->merge = gtk_rc_style_real_merge;
  klass->create_style = gtk_rc_style_real_create_style;
988 989
}

990 991
static void
gtk_rc_style_finalize (GObject *object)
992
{
993 994
  GSList *tmp_list1, *tmp_list2;
  GtkRcStyle *rc_style;
995
  gint i;
996

997 998 999 1000 1001 1002
  rc_style = GTK_RC_STYLE (object);
  
  if (rc_style->name)
    g_free (rc_style->name);
  if (rc_style->font_desc)
    pango_font_description_free (rc_style->font_desc);
1003
      
1004
  for (i = 0; i < 5; i++)
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
    if (rc_style->bg_pixmap_name[i])
      g_free (rc_style->bg_pixmap_name[i]);
  
  /* 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
1016
      g_object_unref (style);
1017 1018 1019

      /* Remove the list of styles from the other rc_styles
       * in the list
1020
       */
1021 1022 1023 1024 1025 1026
      tmp_list2 = rc_styles;
      while (tmp_list2)
        {
          GtkRcStyle *other_style = tmp_list2->data;

          if (other_style != rc_style)
1027 1028
            other_style->rc_style_lists = g_slist_remove_all (other_style->rc_style_lists,
							      rc_styles);
1029 1030
          tmp_list2 = tmp_list2->next;
        }
1031

1032 1033 1034 1035
      /* And from the hash table itself
       */
      g_hash_table_remove (realized_style_ht, rc_styles);
      g_slist_free (rc_styles);
1036

1037 1038 1039
      tmp_list1 = tmp_list1->next;
    }
  g_slist_free (rc_style->rc_style_lists);
1040

1041 1042 1043 1044
  if (rc_style->rc_properties)
    {
      guint i;

1045
      for (i = 0; i < rc_style->rc_properties->len; i++)
1046
	{
1047
	  GtkRcProperty *node = &g_array_index (rc_style->rc_properties, GtkRcProperty, i);
1048 1049 1050 1051

	  g_free (node->origin);
	  g_value_unset (&node->value);
	}
1052
      g_array_free (rc_style->rc_properties, TRUE);
1053 1054 1055
      rc_style->rc_properties = NULL;
    }

1056 1057 1058
  tmp_list1 = rc_style->icon_factories;
  while (tmp_list1)
    {
Manish Singh's avatar
Manish Singh committed
1059
      g_object_unref (tmp_list1->data);
1060 1061 1062 1063
      tmp_list1 = tmp_list1->next;
    }
  g_slist_free (rc_style->icon_factories);
  
1064 1065
  G_OBJECT_CLASS (parent_class)->finalize (object);
}
1066

1067
GtkRcStyle *
1068
gtk_rc_style_new (void)
1069 1070 1071
{
  GtkRcStyle *style;
  
1072
  style = g_object_new (GTK_TYPE_RC_STYLE, NULL);
1073 1074 1075
  
  return style;
}
1076

1077 1078 1079 1080
/**
 * gtk_rc_style_copy:
 * @orig: the style to copy
 * 
Matthias Clasen's avatar
Matthias Clasen committed
1081
 * Makes a copy of the specified #GtkRcStyle. This function
Matthias Clasen's avatar
Matthias Clasen committed
1082
 * will correctly copy an RC style that is a member of a class
1083 1084 1085 1086 1087 1088 1089 1090 1091
 * derived from #GtkRcStyle.
 * 
 * Return value: the resulting #GtkRcStyle
 **/
GtkRcStyle *
gtk_rc_style_copy (GtkRcStyle *orig)
{
  GtkRcStyle *style;

1092
  g_return_val_if_fail (GTK_IS_RC_STYLE (orig), NULL);
1093
  
1094
  style = GTK_RC_STYLE_GET_CLASS (orig)->create_rc_style (orig);
1095 1096 1097 1098 1099
  GTK_RC_STYLE_GET_CLASS (style)->merge (style, orig);

  return style;
}

1100
void      
Manish Singh's avatar
Manish Singh committed
1101
gtk_rc_style_ref (GtkRcStyle *rc_style)
1102 1103 1104
{
  g_return_if_fail (GTK_IS_RC_STYLE (rc_style));

Manish Singh's avatar
Manish Singh committed
1105
  g_object_ref (rc_style);
1106 1107 1108
}

void      
Manish Singh's avatar
Manish Singh committed
1109
gtk_rc_style_unref (GtkRcStyle *rc_style)
1110 1111 1112
{
  g_return_if_fail (GTK_IS_RC_STYLE (rc_style));

Manish Singh's avatar
Manish Singh committed
1113
  g_object_unref (rc_style);
1114 1115
}

1116
static GtkRcStyle *
1117
gtk_rc_style_real_create_rc_style (GtkRcStyle *style)
1118 1119 1120 1121
{
  return GTK_RC_STYLE (g_object_new (G_OBJECT_TYPE (style), NULL));
}

1122 1123 1124 1125 1126 1127 1128
static gint
gtk_rc_properties_cmp (gconstpointer bsearch_node1,
		       gconstpointer bsearch_node2)
{
  const GtkRcProperty *prop1 = bsearch_node1;
  const GtkRcProperty *prop2 = bsearch_node2;

1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
  if (prop1->type_name == prop2->type_name)
    return prop1->property_name < prop2->property_name ? -1 : prop1->property_name == prop2->property_name ? 0 : 1;
  else
    return prop1->type_name < prop2->type_name ? -1 : 1;
}

static void
insert_rc_property (GtkRcStyle    *style,
		    GtkRcProperty *property,
		    gboolean       replace)
{
  guint i;
  GtkRcProperty *new_property = NULL;
  GtkRcProperty key = { 0, 0, NULL, { 0, }, };

  key.type_name = property->type_name;
  key.property_name = property->property_name;

  if (!style->rc_properties)
    style->rc_properties = g_array_new (FALSE, FALSE, sizeof (GtkRcProperty));

  i = 0;
  while (i < style->rc_properties->len)
    {
      gint cmp = gtk_rc_properties_cmp (&key, &g_array_index (style->rc_properties, GtkRcProperty, i));
1154

1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
      if (cmp == 0)
	{
	  if (replace)
	    {
	      new_property = &g_array_index (style->rc_properties, GtkRcProperty, i);
	      
	      g_free (new_property->origin);
	      g_value_unset (&new_property->value);
	      
	      *new_property = key;
	      break;
	    }
	  else
	    return;
	}
1170
      else if (cmp < 0)
1171
	break;
1172

1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
      i++;
    }

  if (!new_property)
    {
      g_array_insert_val (style->rc_properties, i, key);
      new_property = &g_array_index (style->rc_properties, GtkRcProperty, i);
    }

  new_property->origin = g_strdup (property->origin);
  g_value_init (&new_property->value, G_VALUE_TYPE (&property->value));
  g_value_copy (&property->value, &new_property->value);
1185 1186
}

1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221