script-fu-scripts.c 28.6 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10 11 12 13 14
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16 17
 */

Tor Lillqvist's avatar
Tor Lillqvist committed
18 19
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
20
#include <string.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
21

22
#include <glib.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
23

24 25 26 27 28
#ifdef G_OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

29 30
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
31

32
#include "tinyscheme/scheme-private.h"
33

34
#include "scheme-wrapper.h"
35

36 37 38
#include "script-fu-types.h"

#include "script-fu-interface.h"
39
#include "script-fu-script.h"
Elliot Lee's avatar
Elliot Lee committed
40
#include "script-fu-scripts.h"
41
#include "script-fu-utils.h"
Elliot Lee's avatar
Elliot Lee committed
42

43
#include "script-fu-intl.h"
44

45

46 47
typedef struct
{
48 49
  SFScript *script;
  gchar    *menu_path;
50 51 52
} SFMenu;


Elliot Lee's avatar
Elliot Lee committed
53 54 55 56
/*
 *  Local Functions
 */

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
static gboolean  script_fu_run_command    (const gchar      *command,
                                           GError          **error);
static void      script_fu_load_directory (GFile            *directory);
static void      script_fu_load_script    (GFile            *file);
static gboolean  script_fu_install_script (gpointer          foo,
                                           GList            *scripts,
                                           gpointer          bar);
static void      script_fu_install_menu   (SFMenu           *menu);
static gboolean  script_fu_remove_script  (gpointer          foo,
                                           GList            *scripts,
                                           gpointer          bar);
static void      script_fu_script_proc    (const gchar      *name,
                                           gint              nparams,
                                           const GimpParam  *params,
                                           gint             *nreturn_vals,
                                           GimpParam       **return_vals);

static SFScript *script_fu_find_script    (const gchar      *name);

static gchar *   script_fu_menu_map       (const gchar      *menu_path);
static gint      script_fu_menu_compare   (gconstpointer     a,
                                           gconstpointer     b);
79

Elliot Lee's avatar
Elliot Lee committed
80 81 82 83 84

/*
 *  Local variables
 */

85
static GTree *script_tree      = NULL;
86
static GList *script_menu_list = NULL;
87

Elliot Lee's avatar
Elliot Lee committed
88 89 90 91 92 93

/*
 *  Function definitions
 */

void
94
script_fu_find_scripts (GList *path)
Elliot Lee's avatar
Elliot Lee committed
95
{
96 97
  GList *list;

Elliot Lee's avatar
Elliot Lee committed
98
  /*  Make sure to clear any existing scripts  */
99
  if (script_tree != NULL)
Elliot Lee's avatar
Elliot Lee committed
100
    {
101
      g_tree_foreach (script_tree,
102
                      (GTraverseFunc) script_fu_remove_script,
103
                      NULL);
104
      g_tree_destroy (script_tree);
Elliot Lee's avatar
Elliot Lee committed
105 106
    }

107
  if (! path)
108
    return;
Elliot Lee's avatar
Elliot Lee committed
109

110
  script_tree = g_tree_new ((GCompareFunc) g_utf8_collate);
111

112 113 114 115
  for (list = path; list; list = g_list_next (list))
    {
      script_fu_load_directory (list->data);
    }
116

117
  /*  Now that all scripts are read in and sorted, tell gimp about them  */
118
  g_tree_foreach (script_tree,
119
                  (GTraverseFunc) script_fu_install_script,
120
                  NULL);
121 122 123 124

  script_menu_list = g_list_sort (script_menu_list,
                                  (GCompareFunc) script_fu_menu_compare);

125 126 127
  /*  Install and nuke the list of menu entries  */
  g_list_free_full (script_menu_list,
                    (GDestroyNotify) script_fu_install_menu);
128
  script_menu_list = NULL;
Elliot Lee's avatar
Elliot Lee committed
129 130
}

131
pointer
132 133
script_fu_add_script (scheme  *sc,
                      pointer  a)
Elliot Lee's avatar
Elliot Lee committed
134
{
135 136
  SFScript    *script;
  const gchar *name;
137
  const gchar *menu_label;
138 139 140 141 142 143 144
  const gchar *blurb;
  const gchar *author;
  const gchar *copyright;
  const gchar *date;
  const gchar *image_types;
  gint         n_args;
  gint         i;
Elliot Lee's avatar
Elliot Lee committed
145 146

  /*  Check the length of a  */
147
  if (sc->vptr->list_length (sc, a) < 7)
148 149 150 151
    {
      g_message (_("Too few arguments to 'script-fu-register' call"));
      return sc->NIL;
    }
Elliot Lee's avatar
Elliot Lee committed
152 153

  /*  Find the script name  */
154
  name = sc->vptr->string_value (sc->vptr->pair_car (a));
155
  a = sc->vptr->pair_cdr (a);
Elliot Lee's avatar
Elliot Lee committed
156

157 158
  /*  Find the script menu_label  */
  menu_label = sc->vptr->string_value (sc->vptr->pair_car (a));
159
  a = sc->vptr->pair_cdr (a);
Elliot Lee's avatar
Elliot Lee committed
160

161
  /*  Find the script blurb  */
162
  blurb = sc->vptr->string_value (sc->vptr->pair_car (a));
163
  a = sc->vptr->pair_cdr (a);
Elliot Lee's avatar
Elliot Lee committed
164 165

  /*  Find the script author  */
166
  author = sc->vptr->string_value (sc->vptr->pair_car (a));
167
  a = sc->vptr->pair_cdr (a);
Elliot Lee's avatar
Elliot Lee committed
168 169

  /*  Find the script copyright  */
170
  copyright = sc->vptr->string_value (sc->vptr->pair_car (a));
171
  a = sc->vptr->pair_cdr (a);
Elliot Lee's avatar
Elliot Lee committed
172 173

  /*  Find the script date  */
174
  date = sc->vptr->string_value (sc->vptr->pair_car (a));
175
  a = sc->vptr->pair_cdr (a);
Elliot Lee's avatar
Elliot Lee committed
176 177

  /*  Find the script image types  */
178
  if (sc->vptr->is_pair (a))
Elliot Lee's avatar
Elliot Lee committed
179
    {
180
      image_types = sc->vptr->string_value (sc->vptr->pair_car (a));
181
      a = sc->vptr->pair_cdr (a);
Elliot Lee's avatar
Elliot Lee committed
182 183 184
    }
  else
    {
185
      image_types = sc->vptr->string_value (a);
186
      a = sc->NIL;
Elliot Lee's avatar
Elliot Lee committed
187 188 189
    }

  /*  Check the supplied number of arguments  */
190
  n_args = sc->vptr->list_length (sc, a) / 3;
Elliot Lee's avatar
Elliot Lee committed
191

192 193
  /*  Create a new script  */
  script = script_fu_script_new (name,
194
                                 menu_label,
195 196 197 198 199 200 201 202 203 204
                                 blurb,
                                 author,
                                 copyright,
                                 date,
                                 image_types,
                                 n_args);

  for (i = 0; i < script->n_args; i++)
    {
      SFArg *arg = &script->args[i];
205

206 207 208 209
      if (a != sc->NIL)
        {
          if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
            return foreign_error (sc, "script-fu-register: argument types must be integer values", 0);
210

211 212 213 214 215
          arg->type = sc->vptr->ivalue (sc->vptr->pair_car (a));
          a = sc->vptr->pair_cdr (a);
        }
      else
        return foreign_error (sc, "script-fu-register: missing type specifier", 0);
216

217 218 219 220 221 222 223 224 225 226 227 228
      if (a != sc->NIL)
        {
          if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
            return foreign_error (sc, "script-fu-register: argument labels must be strings", 0);

          arg->label = g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
          a = sc->vptr->pair_cdr (a);
        }
      else
        return foreign_error (sc, "script-fu-register: missing arguments label", 0);

      if (a != sc->NIL)
229
        {
230
          switch (arg->type)
231
            {
232 233 234 235 236 237
            case SF_IMAGE:
            case SF_DRAWABLE:
            case SF_LAYER:
            case SF_CHANNEL:
            case SF_VECTORS:
            case SF_DISPLAY:
238
              if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
239
                return foreign_error (sc, "script-fu-register: default IDs must be integer values", 0);
240

241 242 243
              arg->default_value.sfa_image =
                sc->vptr->ivalue (sc->vptr->pair_car (a));
              break;
244

245 246 247 248 249 250 251
            case SF_COLOR:
              if (sc->vptr->is_string (sc->vptr->pair_car (a)))
                {
                  if (! gimp_rgb_parse_css (&arg->default_value.sfa_color,
                                            sc->vptr->string_value (sc->vptr->pair_car (a)),
                                            -1))
                    return foreign_error (sc, "script-fu-register: invalid default color name", 0);
252

253 254 255 256
                  gimp_rgb_set_alpha (&arg->default_value.sfa_color, 1.0);
                }
              else if (sc->vptr->is_list (sc, sc->vptr->pair_car (a)) &&
                       sc->vptr->list_length(sc, sc->vptr->pair_car (a)) == 3)
257
                {
258 259
                  pointer color_list;
                  guchar  r, g, b;
260

261 262 263 264 265 266
                  color_list = sc->vptr->pair_car (a);
                  r = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
                  color_list = sc->vptr->pair_cdr (color_list);
                  g = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
                  color_list = sc->vptr->pair_cdr (color_list);
                  b = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
267

268 269 270 271 272 273 274
                  gimp_rgb_set_uchar (&arg->default_value.sfa_color, r, g, b);
                }
              else
                {
                  return foreign_error (sc, "script-fu-register: color defaults must be a list of 3 integers or a color name", 0);
                }
              break;
275

276 277 278
            case SF_TOGGLE:
              if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: toggle default must be an integer value", 0);
279

280 281 282
              arg->default_value.sfa_toggle =
                (sc->vptr->ivalue (sc->vptr->pair_car (a))) ? TRUE : FALSE;
              break;
283

284 285 286
            case SF_VALUE:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: value defaults must be string values", 0);
Elliot Lee's avatar
Elliot Lee committed
287

288 289 290
              arg->default_value.sfa_value =
                g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
              break;
Elliot Lee's avatar
Elliot Lee committed
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 323 324 325 326 327 328 329 330
            case SF_STRING:
            case SF_TEXT:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: string defaults must be string values", 0);

              arg->default_value.sfa_value =
                g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
              break;

            case SF_ADJUSTMENT:
              {
                pointer adj_list;

                if (!sc->vptr->is_list (sc, a))
                  return foreign_error (sc, "script-fu-register: adjustment defaults must be a list", 0);

                adj_list = sc->vptr->pair_car (a);
                arg->default_value.sfa_adjustment.value =
                  sc->vptr->rvalue (sc->vptr->pair_car (adj_list));

                adj_list = sc->vptr->pair_cdr (adj_list);
                arg->default_value.sfa_adjustment.lower =
                  sc->vptr->rvalue (sc->vptr->pair_car (adj_list));

                adj_list = sc->vptr->pair_cdr (adj_list);
                arg->default_value.sfa_adjustment.upper =
                  sc->vptr->rvalue (sc->vptr->pair_car (adj_list));

                adj_list = sc->vptr->pair_cdr (adj_list);
                arg->default_value.sfa_adjustment.step =
                  sc->vptr->rvalue (sc->vptr->pair_car (adj_list));

                adj_list = sc->vptr->pair_cdr (adj_list);
                arg->default_value.sfa_adjustment.page =
                  sc->vptr->rvalue (sc->vptr->pair_car (adj_list));

                adj_list = sc->vptr->pair_cdr (adj_list);
                arg->default_value.sfa_adjustment.digits =
                  sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
331

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
                adj_list = sc->vptr->pair_cdr (adj_list);
                arg->default_value.sfa_adjustment.type =
                  sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
              }
              break;

            case SF_FILENAME:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: filename defaults must be string values", 0);
              /* fallthrough */

            case SF_DIRNAME:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: dirname defaults must be string values", 0);

              arg->default_value.sfa_file.filename =
                g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
Tor Lillqvist's avatar
Tor Lillqvist committed
349

350
#ifdef G_OS_WIN32
351 352 353 354 355
              {
                /* Replace POSIX slashes with Win32 backslashes. This
                 * is just so script-fus can be written with only
                 * POSIX directory separators.
                 */
Tor Lillqvist's avatar
Tor Lillqvist committed
356
                gchar *filename = arg->default_value.sfa_file.filename;
357

358 359 360 361
                while (*filename)
                  {
                    if (*filename == '/')
                      *filename = G_DIR_SEPARATOR;
362

363
                    filename++;
364
                  }
365
              }
Tor Lillqvist's avatar
Tor Lillqvist committed
366
#endif
367
              break;
368

369 370 371
            case SF_FONT:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: font defaults must be string values", 0);
372

373 374 375
              arg->default_value.sfa_font =
                g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
              break;
376

377 378 379
            case SF_PALETTE:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: palette defaults must be string values", 0);
380

381 382 383
              arg->default_value.sfa_palette =
                g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
              break;
384

385 386 387
            case SF_PATTERN:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: pattern defaults must be string values", 0);
388

389 390 391
              arg->default_value.sfa_pattern =
                g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
              break;
392

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
            case SF_BRUSH:
              {
                pointer brush_list;

                if (!sc->vptr->is_list (sc, a))
                  return foreign_error (sc, "script-fu-register: brush defaults must be a list", 0);

                brush_list = sc->vptr->pair_car (a);
                arg->default_value.sfa_brush.name =
                  g_strdup (sc->vptr->string_value (sc->vptr->pair_car (brush_list)));

                brush_list = sc->vptr->pair_cdr (brush_list);
                arg->default_value.sfa_brush.opacity =
                  sc->vptr->rvalue (sc->vptr->pair_car (brush_list));

                brush_list = sc->vptr->pair_cdr (brush_list);
                arg->default_value.sfa_brush.spacing =
                  sc->vptr->ivalue (sc->vptr->pair_car (brush_list));

                brush_list = sc->vptr->pair_cdr (brush_list);
                arg->default_value.sfa_brush.paint_mode =
                  sc->vptr->ivalue (sc->vptr->pair_car (brush_list));
              }
              break;
417

418 419 420
            case SF_GRADIENT:
              if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
                return foreign_error (sc, "script-fu-register: gradient defaults must be string values", 0);
421

422 423 424
              arg->default_value.sfa_gradient =
                g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
              break;
425

426 427 428
            case SF_OPTION:
              {
                pointer option_list;
Elliot Lee's avatar
Elliot Lee committed
429

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
                if (!sc->vptr->is_list (sc, a))
                  return foreign_error (sc, "script-fu-register: option defaults must be a list", 0);

                for (option_list = sc->vptr->pair_car (a);
                     option_list != sc->NIL;
                     option_list = sc->vptr->pair_cdr (option_list))
                  {
                    arg->default_value.sfa_option.list =
                      g_slist_append (arg->default_value.sfa_option.list,
                                      g_strdup (sc->vptr->string_value
                                                (sc->vptr->pair_car (option_list))));
                  }
              }
              break;

            case SF_ENUM:
              {
                pointer      option_list;
                const gchar *val;
                gchar       *type_name;
                GEnumValue  *enum_value;
                GType        enum_type;

                if (!sc->vptr->is_list (sc, a))
                  return foreign_error (sc, "script-fu-register: enum defaults must be a list", 0);

                option_list = sc->vptr->pair_car (a);
                if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
                  return foreign_error (sc, "script-fu-register: first element in enum defaults must be a type-name", 0);

                val = sc->vptr->string_value (sc->vptr->pair_car (option_list));

                if (g_str_has_prefix (val, "Gimp"))
                  type_name = g_strdup (val);
                else
                  type_name = g_strconcat ("Gimp", val, NULL);

                enum_type = g_type_from_name (type_name);
                if (! G_TYPE_IS_ENUM (enum_type))
                  {
                    g_free (type_name);
                    return foreign_error (sc, "script-fu-register: first element in enum defaults must be the name of a registered type", 0);
                  }

                arg->default_value.sfa_enum.type_name = type_name;

                option_list = sc->vptr->pair_cdr (option_list);
                if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
                  return foreign_error (sc, "script-fu-register: second element in enum defaults must be a string", 0);

                enum_value =
                  g_enum_get_value_by_nick (g_type_class_peek (enum_type),
                                            sc->vptr->string_value (sc->vptr->pair_car (option_list)));
                if (enum_value)
484
                  arg->default_value.sfa_enum.history = enum_value->value;
485 486
              }
              break;
487
            }
488 489 490 491 492 493

          a = sc->vptr->pair_cdr (a);
        }
      else
        {
          return foreign_error (sc, "script-fu-register: missing default argument", 0);
494
        }
Elliot Lee's avatar
Elliot Lee committed
495 496
    }

497 498 499
  /*  fill all values from defaults  */
  script_fu_script_reset (script, TRUE);

500
  if (script->menu_label[0] == '<')
501
    {
502 503 504 505 506 507 508
      gchar *mapped = script_fu_menu_map (script->menu_label);

      if (mapped)
        {
          g_free (script->menu_label);
          script->menu_label = mapped;
        }
509
    }
510

511
  {
512
    GList *list = g_tree_lookup (script_tree, script->menu_label);
513

514 515
    g_tree_insert (script_tree, (gpointer) script->menu_label,
                    g_list_append (list, script));
516
  }
Elliot Lee's avatar
Elliot Lee committed
517

518
  return sc->NIL;
Elliot Lee's avatar
Elliot Lee committed
519 520
}

521
pointer
522 523
script_fu_add_menu (scheme  *sc,
                    pointer  a)
524
{
525 526 527
  SFScript    *script;
  SFMenu      *menu;
  const gchar *name;
528
  const gchar *path;
529 530

  /*  Check the length of a  */
531
  if (sc->vptr->list_length (sc, a) != 2)
532
    return foreign_error (sc, "Incorrect number of arguments for script-fu-menu-register", 0);
533 534

  /*  Find the script PDB entry name  */
535 536
  name = sc->vptr->string_value (sc->vptr->pair_car (a));
  a = sc->vptr->pair_cdr (a);
537

538
  script = script_fu_find_script (name);
539 540

  if (! script)
541
    {
542 543
      g_message ("Procedure %s in script-fu-menu-register does not exist",
                 name);
544 545
      return sc->NIL;
    }
546 547

  /*  Create a new list of menus  */
548
  menu = g_slice_new0 (SFMenu);
549 550 551

  menu->script = script;

552
  /*  Find the script menu path  */
553 554 555 556 557 558
  path = sc->vptr->string_value (sc->vptr->pair_car (a));

  menu->menu_path = script_fu_menu_map (path);

  if (! menu->menu_path)
    menu->menu_path = g_strdup (path);
559

560
  script_menu_list = g_list_prepend (script_menu_list, menu);
561

562
  return sc->NIL;
563 564
}

565

566 567
/*  private functions  */

568
static gboolean
569 570
script_fu_run_command (const gchar  *command,
                       GError      **error)
571
{
Sven Neumann's avatar
Sven Neumann committed
572
  GString  *output;
573 574
  gboolean  success = FALSE;

Sven Neumann's avatar
Sven Neumann committed
575
  output = g_string_new (NULL);
576 577 578 579
  ts_register_output_func (ts_gstring_output_func, output);

  if (ts_interpret_string (command))
    {
580
      g_set_error (error, 0, 0, "%s", output->str);
581 582 583 584 585 586 587 588 589 590 591
    }
  else
    {
      success = TRUE;
    }

  g_string_free (output, TRUE);

  return success;
}

592
static void
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
script_fu_load_directory (GFile *directory)
{
  GFileEnumerator *enumerator;

  enumerator = g_file_enumerate_children (directory,
                                          G_FILE_ATTRIBUTE_STANDARD_NAME ","
                                          G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
                                          G_FILE_ATTRIBUTE_STANDARD_TYPE,
                                          G_FILE_QUERY_INFO_NONE,
                                          NULL, NULL);

  if (enumerator)
    {
      GFileInfo *info;

      while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
        {
          GFileType file_type = g_file_info_get_file_type (info);

612 613 614
          if ((file_type == G_FILE_TYPE_REGULAR ||
               file_type == G_FILE_TYPE_DIRECTORY) &&
              ! g_file_info_get_is_hidden (info))
615 616 617
            {
              GFile *child = g_file_enumerator_get_child (enumerator, info);

618 619 620 621
              if (file_type == G_FILE_TYPE_DIRECTORY)
                script_fu_load_directory (child);
              else
                script_fu_load_script (child);
622 623 624 625 626 627 628 629 630 631 632 633 634

              g_object_unref (child);
            }

          g_object_unref (info);
        }

      g_object_unref (enumerator);
    }
}

static void
script_fu_load_script (GFile *file)
635
{
636
  if (gimp_file_has_extension (file, ".scm"))
637
    {
638 639
      gchar  *path    = g_file_get_path (file);
      gchar  *escaped = script_fu_strescape (path);
640 641
      gchar  *command;
      GError *error   = NULL;
642

643 644
      command = g_strdup_printf ("(load \"%s\")", escaped);
      g_free (escaped);
645

646 647
      if (! script_fu_run_command (command, &error))
        {
648 649
          gchar *message = g_strdup_printf (_("Error while loading %s:"),
                                            gimp_file_get_utf8_name (file));
Sven Neumann's avatar
Sven Neumann committed
650 651

          g_message ("%s\n\n%s", message, error->message);
652

653
          g_clear_error (&error);
Sven Neumann's avatar
Sven Neumann committed
654
          g_free (message);
655
        }
656 657

#ifdef G_OS_WIN32
658
      /* No, I don't know why, but this is
659 660
       * necessary on NT 4.0.
       */
661
      Sleep (0);
662 663 664
#endif

      g_free (command);
665
      g_free (path);
666 667 668
    }
}

669
/*
670
 *  The following function is a GTraverseFunction.
671
 */
672
static gboolean
673
script_fu_install_script (gpointer  foo G_GNUC_UNUSED,
674 675
                          GList    *scripts,
                          gpointer  bar G_GNUC_UNUSED)
676
{
677 678 679
  GList *list;

  for (list = scripts; list; list = g_list_next (list))
680 681 682
    {
      SFScript *script = list->data;

Sven Neumann's avatar
Sven Neumann committed
683
      script_fu_script_install_proc (script, script_fu_script_proc);
684
    }
685 686 687 688

  return FALSE;
}

689
static void
690
script_fu_install_menu (SFMenu *menu)
691
{
692
  gimp_plugin_menu_register (menu->script->name, menu->menu_path);
693 694

  g_free (menu->menu_path);
695
  g_slice_free (SFMenu, menu);
696 697
}

698
/*
699 700
 *  The following function is a GTraverseFunction.
 */
701
static gboolean
702
script_fu_remove_script (gpointer  foo G_GNUC_UNUSED,
703 704
                         GList    *scripts,
                         gpointer  bar G_GNUC_UNUSED)
705
{
706 707 708
  GList *list;

  for (list = scripts; list; list = g_list_next (list))
709 710 711 712 713 714
    {
      SFScript *script = list->data;

      script_fu_script_uninstall_proc (script);
      script_fu_script_free (script);
    }
715

716
  g_list_free (scripts);
717 718 719 720

  return FALSE;
}

Elliot Lee's avatar
Elliot Lee committed
721
static void
722
script_fu_script_proc (const gchar      *name,
723 724 725 726
                       gint              nparams,
                       const GimpParam  *params,
                       gint             *nreturn_vals,
                       GimpParam       **return_vals)
Elliot Lee's avatar
Elliot Lee committed
727
{
728
  static GimpParam   values[2] = { { 0, }, { 0, } };
Sven Neumann's avatar
Sven Neumann committed
729
  GimpPDBStatusType  status    = GIMP_PDB_SUCCESS;
730
  SFScript          *script;
Sven Neumann's avatar
Sven Neumann committed
731 732 733 734 735 736 737
  GError            *error     = NULL;

  if (values[1].type == GIMP_PDB_STRING && values[1].data.d_string)
    {
      g_free (values[1].data.d_string);
      values[1].data.d_string = NULL;
    }
Elliot Lee's avatar
Elliot Lee committed
738

739 740 741 742 743
  *nreturn_vals = 1;
  *return_vals  = values;

  values[0].type = GIMP_PDB_STATUS;

744 745 746 747
  script = script_fu_find_script (name);

  if (! script)
    status = GIMP_PDB_CALLING_ERROR;
Elliot Lee's avatar
Elliot Lee committed
748

749
  if (status == GIMP_PDB_SUCCESS)
Elliot Lee's avatar
Elliot Lee committed
750
    {
751
      GimpRunMode run_mode = params[0].data.d_int32;
752

753
      ts_set_run_mode (run_mode);
754

Elliot Lee's avatar
Elliot Lee committed
755
      switch (run_mode)
756 757
        {
        case GIMP_RUN_INTERACTIVE:
758 759 760 761
          {
            gint min_args = 0;

            /*  First, try to collect the standard script arguments...  */
762 763
            min_args = script_fu_script_collect_standard_args (script,
                                                               nparams, params);
764 765

            /*  ...then acquire the rest of arguments (if any) with a dialog  */
766
            if (script->n_args > min_args)
767
              {
768
                status = script_fu_interface (script, min_args);
769 770 771 772 773 774
                break;
              }
            /*  otherwise (if the script takes no more arguments), skip
             *  this part and run the script directly (fallthrough)
             */
          }
775 776

        case GIMP_RUN_NONINTERACTIVE:
777
          /*  Make sure all the arguments are there  */
778
          if (nparams != (script->n_args + 1))
779 780
            status = GIMP_PDB_CALLING_ERROR;

781 782
          if (status == GIMP_PDB_SUCCESS)
            {
783
              gchar *command;
784

785 786
              command = script_fu_script_get_command_from_params (script,
                                                                  params);
Elliot Lee's avatar
Elliot Lee committed
787

788
              /*  run the command through the interpreter  */
789 790 791 792
              if (! script_fu_run_command (command, &error))
                {
                  status                  = GIMP_PDB_EXECUTION_ERROR;
                  *nreturn_vals           = 2;
793 794
                  values[1].type          = GIMP_PDB_STRING;
                  values[1].data.d_string = error->message;
Sven Neumann's avatar
Sven Neumann committed
795 796 797

                  error->message = NULL;
                  g_error_free (error);
798
                }
799

800 801 802
              g_free (command);
            }
          break;
Elliot Lee's avatar
Elliot Lee committed
803

804 805
        case GIMP_RUN_WITH_LAST_VALS:
          {
806
            gchar *command;
807 808

            /*  First, try to collect the standard script arguments  */
809
            script_fu_script_collect_standard_args (script, nparams, params);
810

811
            command = script_fu_script_get_command (script);
812 813

            /*  run the command through the interpreter  */
814 815 816 817
            if (! script_fu_run_command (command, &error))
              {
                status                  = GIMP_PDB_EXECUTION_ERROR;
                *nreturn_vals           = 2;
818 819
                values[1].type          = GIMP_PDB_STRING;
                values[1].data.d_string = error->message;
Sven Neumann's avatar
Sven Neumann committed
820 821 822

                error->message = NULL;
                g_error_free (error);
823
              }
824 825 826 827 828

            g_free (command);
          }
          break;

829 830 831
        default:
          break;
        }
Elliot Lee's avatar
Elliot Lee committed
832 833 834 835 836
    }

  values[0].data.d_status = status;
}

837
/* this is a GTraverseFunction */
838
static gboolean
839
script_fu_lookup_script (gpointer      *foo G_GNUC_UNUSED,
840 841
                         GList         *scripts,
                         gconstpointer *name)
Elliot Lee's avatar
Elliot Lee committed
842
{
843 844 845
  GList *list;

  for (list = scripts; list; list = g_list_next (list))
846
    {
847 848
      SFScript *script = list->data;

849
      if (strcmp (script->name, *name) == 0)
850 851 852 853 854
        {
          /* store the script in the name pointer and stop the traversal */
          *name = script;
          return TRUE;
        }
Elliot Lee's avatar
Elliot Lee committed
855
    }
856 857

  return FALSE;
858
}
Elliot Lee's avatar
Elliot Lee committed
859

860
static SFScript *
861
script_fu_find_script (const gchar *name)
862
{
863
  gconstpointer script = name;
864

865
  g_tree_foreach (script_tree,
866
                  (GTraverseFunc) script_fu_lookup_script,
867
                  &script);
868

869
  if (script == name)
870
    return NULL;
871 872

  return (SFScript *) script;
Elliot Lee's avatar
Elliot Lee committed
873 874
}

875 876
static gchar *
script_fu_menu_map (const gchar *menu_path)
877 878 879 880 881 882 883
{
  /*  for backward compatibility, we fiddle with some menu paths  */
  const struct
  {
    const gchar *old;
    const gchar *new;
  } mapping[] = {
884 885 886 887 888 889 890 891
    { "<Image>/Script-Fu/Alchemy",       "<Image>/Filters/Artistic"            },
    { "<Image>/Script-Fu/Alpha to Logo", "<Image>/Filters/Alpha to Logo"       },
    { "<Image>/Script-Fu/Animators",     "<Image>/Filters/Animation/Animators" },
    { "<Image>/Script-Fu/Decor",         "<Image>/Filters/Decor"               },
    { "<Image>/Script-Fu/Render",        "<Image>/Filters/Render"              },
    { "<Image>/Script-Fu/Selection",     "<Image>/Select/Modify"               },
    { "<Image>/Script-Fu/Shadow",        "<Image>/Filters/Light and Shadow/Shadow" },
    { "<Image>/Script-Fu/Stencil Ops",   "<Image>/Filters/Decor"               }
892 893 894 895 896 897
  };

  gint i;

  for (i = 0; i < G_N_ELEMENTS (mapping); i++)
    {
898
      if (g_str_has_prefix (menu_path, mapping[i].old))
899
        {
900
          const gchar *suffix = menu_path + strlen (mapping[i].old);
901

Simon Budig's avatar
Simon Budig committed
902
          if (*suffix != '/')
903 904
            continue;

905
          return g_strconcat (mapping[i].new, suffix, NULL);
906 907
        }
    }
908 909

  return NULL;
910 911
}

912 913 914 915
static gint
script_fu_menu_compare (gconstpointer a,
                        gconstpointer b)
{
916 917
  const SFMenu *menu_a = a;
  const SFMenu *menu_b = b;
918
  gint          retval = 0;
919 920

  if (menu_a->menu_path && menu_b->menu_path)
921
    {
922 923
      retval = g_utf8_collate (menu_a->menu_path,
                               menu_b->menu_path);
924 925

      if (retval == 0 &&
926
          menu_a->script->menu_label && menu_b->script->menu_label)
927
        {
928 929
          retval = g_utf8_collate (menu_a->script->menu_label,
                                   menu_b->script->menu_label);
930
        }
931
    }
932

933
  return retval;
934
}