~ubuntu-branches/ubuntu/jaunty/gimp/jaunty-security

« back to all changes in this revision

Viewing changes to plug-ins/pygimp/gimpfu.py

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2007-05-02 16:33:03 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20070502163303-bvzhjzbpw8qglc4y
Tags: 2.3.16-1ubuntu1
* Resynchronized with Debian, remaining Ubuntu changes:
  - debian/rules: i18n magic.
* debian/control.in:
  - Maintainer: Ubuntu Core Developers <ubuntu-devel@lists.ubuntu.com>
* debian/patches/02_help-message.patch,
  debian/patches/03_gimp.desktop.in.in.patch,
  debian/patches/10_dont_show_wizard.patch: updated.
* debian/patches/04_composite-signedness.patch,
  debian/patches/05_add-letter-spacing.patch: dropped, used upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#   Gimp-Python - allows the writing of Gimp plugins in Python.
 
1
#   Gimp-Python - allows the writing of GIMP plug-ins in Python.
2
2
#   Copyright (C) 1997  James Henstridge <james@daa.com.au>
3
3
#
4
4
#   This program is free software; you can redistribute it and/or modify
15
15
#   along with this program; if not, write to the Free Software
16
16
#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
17
 
18
 
'''Simple interface to writing GIMP plugins in python.
 
18
'''Simple interface to writing GIMP plug-ins in Python.
19
19
 
20
20
Instead of worrying about all the user interaction, saving last used values
21
21
and everything, the gimpfu module can take care of it for you.  It provides
22
 
a simple register() function that will register your plugin if needed, and
23
 
cause your plugin function to be called when needed.
 
22
a simple register() function that will register your plug-in if needed, and
 
23
cause your plug-in function to be called when needed.
24
24
 
25
 
Gimpfu will also handle showing a user interface for editing plugin parameters
26
 
if the plugin is called interactively, and will also save the last used
 
25
Gimpfu will also handle showing a user interface for editing plug-in parameters
 
26
if the plug-in is called interactively, and will also save the last used
27
27
parameters, so the RUN_WITH_LAST_VALUES run_type will work correctly.  It
28
 
will also make sure that the displays are flushed on completion if the plugin
 
28
will also make sure that the displays are flushed on completion if the plug-in
29
29
was run interactively.
30
30
 
31
 
When registering the plugin, you do not need to worry about specifying
32
 
the run_type parameter.  And if the plugin is an image plugin (the menu
 
31
When registering the plug-in, you do not need to worry about specifying
 
32
the run_type parameter.  And if the plug-in is an image plug-in (the menu
33
33
path starts with <Image>/), the image and drawable parameters are also
34
34
automatically added.
35
35
 
36
 
A typical gimpfu plugin would look like this:
 
36
A typical gimpfu plug-in would look like this:
37
37
  from gimpfu import *
38
38
 
39
39
  def plugin_func(image, drawable, args):
45
45
              "author",
46
46
              "copyright",
47
47
              "year",
48
 
              "<Image>/Somewhere/My plugin",
 
48
              "My plug-in",
49
49
              "*",
50
50
              [(PF_STRING, "arg", "The argument", "default-value")],
51
51
              [],
52
 
              plugin_func)
 
52
              plugin_func, menu="<Image>/Somewhere")
53
53
  main()
54
54
 
55
55
The call to "from gimpfu import *" will import all the gimp constants into
56
 
the plugin namespace, and also import the symbols gimp, pdb, register and
57
 
main.  This should be just about all any plugin needs.  You can use any
58
 
of the PF_* constants below as parameter types, and an appropriate user
59
 
interface element will be displayed when the plugin is run in interactive
60
 
mode.  Note that the the PF_SPINNER and PF_SLIDER types expect a fifth
61
 
element in their description tuple -- a 3-tuple of the form (lower,upper,step),
62
 
which defines the limits for the slider or spinner.'''
 
56
the plug-in namespace, and also import the symbols gimp, pdb, register and
 
57
main.  This should be just about all any plug-in needs.
 
58
 
 
59
You can use any of the PF_* constants below as parameter types, and an
 
60
appropriate user interface element will be displayed when the plug-in is
 
61
run in interactive mode.  Note that the the PF_SPINNER and PF_SLIDER types
 
62
expect a fifth element in their description tuple -- a 3-tuple of the form
 
63
(lower,upper,step), which defines the limits for the slider or spinner.
 
64
 
 
65
If want to localize your plug-in, add an optional domain parameter to the
 
66
register call. It can be the name of the translation domain or a tuple that
 
67
consists of the translation domain and the directory where the translations
 
68
are installed.
 
69
'''
63
70
 
64
71
import string as _string
65
72
import gimp
66
73
from gimpenums import *
67
74
pdb = gimp.pdb
68
75
 
 
76
import gettext
 
77
t = gettext.translation('gimp20-python', gimp.locale_directory, fallback=True)
 
78
_ = t.ugettext
 
79
 
69
80
class error(RuntimeError):pass
70
81
class CancelError(RuntimeError):pass
71
82
 
90
101
PF_LAYER       = PDB_LAYER
91
102
PF_CHANNEL     = PDB_CHANNEL
92
103
PF_DRAWABLE    = PDB_DRAWABLE
 
104
PF_VECTORS     = PDB_VECTORS
93
105
#PF_SELECTION   = PDB_SELECTION
94
106
#PF_BOUNDARY    = PDB_BOUNDARY
95
107
#PF_PATH        = PDB_PATH
109
121
PF_RADIO       = 1008
110
122
PF_TEXT        = 1009
111
123
PF_PALETTE     = 1010
 
124
PF_FILENAME    = 1011
 
125
PF_DIRNAME     = 1012
112
126
 
113
127
_type_mapping = {
114
128
    PF_INT8        : PDB_INT8,
127
141
    PF_LAYER       : PDB_LAYER,
128
142
    PF_CHANNEL     : PDB_CHANNEL,
129
143
    PF_DRAWABLE    : PDB_DRAWABLE,
 
144
    PF_VECTORS     : PDB_VECTORS,
130
145
 
131
146
    PF_TOGGLE      : PDB_INT32,
132
147
    PF_SLIDER      : PDB_FLOAT,
140
155
    PF_RADIO       : PDB_STRING,
141
156
    PF_TEXT        : PDB_STRING,
142
157
    PF_PALETTE     : PDB_STRING,
 
158
    PF_FILENAME    : PDB_STRING,
 
159
    PF_DIRNAME     : PDB_STRING,
143
160
}
144
161
 
145
162
_registered_plugins_ = {}
146
163
 
147
 
def register(func_name, blurb, help, author, copyright, date, menupath,
 
164
def register(proc_name, blurb, help, author, copyright, date, label,
148
165
                 imagetypes, params, results, function,
149
 
                 on_query=None, on_run=None):
150
 
    '''This is called to register a new plugin.'''
 
166
                 menu=None, domain=None, on_query=None, on_run=None):
 
167
    '''This is called to register a new plug-in.'''
 
168
 
151
169
    # First perform some sanity checks on the data
152
170
    def letterCheck(str):
153
 
        allowed = _string.letters + _string.digits + '_'
 
171
        allowed = _string.letters + _string.digits + '_' + '-'
154
172
        for ch in str:
155
173
            if not ch in allowed:
156
 
                    return 0
 
174
                return 0
157
175
        else:
158
176
            return 1
159
 
    if not letterCheck(func_name):
160
 
        raise error, "function name contains illegal characters"
 
177
 
 
178
    if not letterCheck(proc_name):
 
179
        raise error, "procedure name contains illegal characters"
 
180
 
161
181
    for ent in params:
162
182
        if len(ent) < 4:
163
183
            raise error, ("parameter definition must contain at least 4 "
164
 
                        "elements (%s given: %s)" % (len(ent), ent))
165
 
        if type(ent[0]) != type(42):
 
184
                          "elements (%s given: %s)" % (len(ent), ent))
 
185
 
 
186
        if type(ent[0]) != int:
166
187
            raise error, "parameter types must be integers"
 
188
 
167
189
        if not letterCheck(ent[1]):
168
190
            raise error, "parameter name contains illegal characters"
 
191
 
169
192
    for ent in results:
170
193
        if len(ent) < 3:
171
194
            raise error, ("result definition must contain at least 3 elements "
172
 
                        "(%s given: %s)" % (len(ent), ent))
 
195
                          "(%s given: %s)" % (len(ent), ent))
 
196
 
173
197
        if type(ent[0]) != type(42):
174
198
            raise error, "result types must be integers"
 
199
 
175
200
        if not letterCheck(ent[1]):
176
201
            raise error, "result name contains illegal characters"
177
 
    if menupath[:8] == '<Image>/' or \
178
 
       menupath[:7] == '<Load>/' or \
179
 
       menupath[:7] == '<Save>/' or \
180
 
       menupath[:10] == '<Toolbox>/':
181
 
        plugin_type = PLUGIN
182
 
    else:
183
 
        raise error, "Invalid menu path"
184
 
 
185
 
    if not func_name[:7] == 'python_' and \
186
 
       not func_name[:10] == 'extension_' and \
187
 
       not func_name[:8] == 'plug_in_' and \
188
 
       not func_name[:5] == 'file_':
189
 
           func_name = 'python_fu_' + func_name
190
 
 
191
 
    _registered_plugins_[func_name] = (blurb, help, author, copyright,
192
 
                                       date, menupath, imagetypes,
 
202
 
 
203
    plugin_type = PLUGIN
 
204
 
 
205
    if not proc_name[:7] == 'python-' and \
 
206
       not proc_name[:7] == 'python_' and \
 
207
       not proc_name[:10] == 'extension-' and \
 
208
       not proc_name[:10] == 'extension_' and \
 
209
       not proc_name[:8] == 'plug-in-' and \
 
210
       not proc_name[:8] == 'plug_in_' and \
 
211
       not proc_name[:5] == 'file-' and \
 
212
       not proc_name[:5] == 'file_':
 
213
           proc_name = 'python-fu-' + proc_name
 
214
 
 
215
    # if menu is not given, derive it from label
 
216
    if menu is None and label:
 
217
        fields = _string.split(label, '/')
 
218
        if fields:
 
219
            label = fields.pop()
 
220
            menu = _string.join(fields, '/')
 
221
 
 
222
    _registered_plugins_[proc_name] = (blurb, help, author, copyright,
 
223
                                       date, label, imagetypes,
193
224
                                       plugin_type, params, results,
194
 
                                       function, on_query, on_run)
 
225
                                       function, menu, domain, on_query, on_run)
195
226
 
196
227
file_params = [(PDB_STRING, "filename", "The name of the file"),
197
 
               (PDB_STRING, "raw_filename", "The name of the file")]
 
228
               (PDB_STRING, "raw-filename", "The name of the file")]
198
229
 
199
230
def _query():
200
231
    for plugin in _registered_plugins_.keys():
201
232
        (blurb, help, author, copyright, date,
202
 
         menupath, imagetypes, plugin_type,
203
 
         params, results, function,
 
233
         label, imagetypes, plugin_type,
 
234
         params, results, function, menu, domain,
204
235
         on_query, on_run) = _registered_plugins_[plugin]
205
236
 
206
 
        fn = lambda x: (_type_mapping[x[0]], x[1], x[2])
207
 
        params = map(fn, params)
 
237
        def make_params(params):
 
238
            return [(_type_mapping[x[0]], x[1], x[2]) for x in params]
 
239
 
 
240
        params = make_params(params)
208
241
        # add the run mode argument ...
209
 
        params.insert(0, (PDB_INT32, "run_mode",
210
 
                                    "Interactive, Non-Interactive"))
 
242
        params.insert(0, (PDB_INT32, "run-mode",
 
243
                                     "Interactive, Non-Interactive"))
 
244
 
211
245
        if plugin_type == PLUGIN:
212
 
            if menupath[:7] == '<Load>/':
 
246
            if menu is None:
 
247
                pass
 
248
            elif menu[:6] == '<Load>':
213
249
                params[1:1] = file_params
214
 
            elif menupath[:10] != '<Toolbox>/':
 
250
            elif menu[:7] == '<Image>' or menu[:6] == '<Save>':
215
251
                params.insert(1, (PDB_IMAGE, "image",
216
252
                                  "The image to work on"))
217
253
                params.insert(2, (PDB_DRAWABLE, "drawable",
218
254
                                  "The drawable to work on"))
219
 
                if menupath[:7] == '<Save>/':
 
255
                if menu[:6] == '<Save>':
220
256
                    params[3:3] = file_params
221
257
 
222
 
        results = map(fn, results)
 
258
        results = make_params(results)
 
259
 
 
260
        if domain:
 
261
            try:
 
262
                (domain, locale_dir) = domain
 
263
                gimp.domain_register(domain, locale_dir)
 
264
            except ValueError:
 
265
                gimp.domain_register(domain)
 
266
 
223
267
        gimp.install_procedure(plugin, blurb, help, author, copyright,
224
 
                               date, menupath, imagetypes, plugin_type,
 
268
                               date, label, imagetypes, plugin_type,
225
269
                               params, results)
 
270
 
 
271
        if menu:
 
272
            gimp.menu_register(plugin, menu)
226
273
        if on_query:
227
274
            on_query()
228
275
 
229
 
def _get_defaults(func_name):
 
276
def _get_defaults(proc_name):
230
277
    import gimpshelf
231
278
    (blurb, help, author, copyright, date,
232
 
     menupath, imagetypes, plugin_type,
233
 
     params, results, function,
234
 
     on_query, on_run) = _registered_plugins_[func_name]
235
 
 
236
 
    key = "python-fu-save--" + func_name
 
279
     label, imagetypes, plugin_type,
 
280
     params, results, function, menu, domain,
 
281
     on_query, on_run) = _registered_plugins_[proc_name]
 
282
 
 
283
    key = "python-fu-save--" + proc_name
 
284
 
237
285
    if gimpshelf.shelf.has_key(key):
238
286
        return gimpshelf.shelf[key]
239
287
    else:
240
288
        # return the default values
241
 
        return map(lambda x: x[3], params)
 
289
        return [x[3] for x in params]
242
290
 
243
 
def _set_defaults(func_name, defaults):
 
291
def _set_defaults(proc_name, defaults):
244
292
    import gimpshelf
245
293
 
246
 
    key = "python-fu-save--" + func_name
 
294
    key = "python-fu-save--" + proc_name
247
295
    gimpshelf.shelf[key] = defaults
248
296
 
249
 
def _interact(func_name, start_params):
 
297
def _interact(proc_name, start_params):
250
298
    (blurb, help, author, copyright, date,
251
 
     menupath, imagetypes, plugin_type,
252
 
     params, results, function,
253
 
     on_query, on_run) = _registered_plugins_[func_name]
 
299
     label, imagetypes, plugin_type,
 
300
     params, results, function, menu, domain,
 
301
     on_query, on_run) = _registered_plugins_[proc_name]
254
302
 
255
303
    def run_script(run_params):
256
304
        params = start_params + tuple(run_params)
263
311
    import pygtk
264
312
    pygtk.require('2.0')
265
313
 
 
314
    import gimpui
266
315
    import gtk
267
 
    import gimpui
268
 
 
269
 
    gtk.rc_parse(gimp.gtkrc())
270
 
 
271
 
    defaults = _get_defaults(func_name)
 
316
#    import pango
 
317
 
 
318
    defaults = _get_defaults(proc_name)
272
319
 
273
320
    class EntryValueError(Exception):
274
321
        pass
275
322
 
276
 
    def error_dialog(parent, msg):
 
323
    def warning_dialog(parent, primary, secondary=None):
277
324
        dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
278
325
                                        gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE,
279
 
                                        msg)
 
326
                                        primary)
 
327
        if secondary:
 
328
            dlg.format_secondary_text(secondary)
280
329
        dlg.run()
281
330
        dlg.destroy()
282
331
 
 
332
    def error_dialog(parent, proc_name):
 
333
        import sys, traceback
 
334
 
 
335
        exc_str = exc_only_str = _('Missing exception information')
 
336
 
 
337
        try:
 
338
            etype, value, tb = sys.exc_info()        
 
339
            exc_str = ''.join(traceback.format_exception(etype, value, tb))
 
340
            exc_only_str = ''.join(traceback.format_exception_only(etype, value))
 
341
        finally:
 
342
            etype = value = tb = None
 
343
 
 
344
        title = _("An error occured running %s") % proc_name
 
345
        dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
 
346
                                        gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
 
347
                                        title)
 
348
        dlg.format_secondary_text(exc_only_str)
 
349
 
 
350
        alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
 
351
        alignment.set_padding(0, 0, 12, 12)
 
352
        dlg.vbox.pack_start(alignment)
 
353
        alignment.show()
 
354
 
 
355
        expander = gtk.Expander(_("_More Information"));
 
356
        expander.set_use_underline(True)
 
357
        expander.set_spacing(6)
 
358
        alignment.add(expander)
 
359
        expander.show()
 
360
 
 
361
        scrolled = gtk.ScrolledWindow()
 
362
        scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
363
        scrolled.set_size_request(-1, 200)
 
364
        expander.add(scrolled)
 
365
        scrolled.show()
 
366
 
 
367
        
 
368
        label = gtk.Label(exc_str)
 
369
        label.set_alignment(0.0, 0.0)
 
370
        label.set_padding(6, 6)
 
371
        label.set_selectable(True)
 
372
        scrolled.add_with_viewport(label)
 
373
        label.show()
 
374
 
 
375
        def response(widget, id):
 
376
            widget.destroy()
 
377
 
 
378
        dlg.connect("response", response)
 
379
        dlg.set_resizable(True)
 
380
        dlg.show()
 
381
 
283
382
    # define a mapping of param types to edit objects ...
284
383
    class StringEntry(gtk.Entry):
285
384
        def __init__(self, default=''):
286
385
            gtk.Entry.__init__(self)
287
386
            self.set_text(str(default))
 
387
 
288
388
        def get_value(self):
289
389
            return self.get_text()
290
390
 
304
404
 
305
405
            self.set_value(str(default))
306
406
            
307
 
        def set_value (self, text):
 
407
        def set_value(self, text):
308
408
            self.buffer.set_text(text)
 
409
 
309
410
        def get_value(self):
310
411
            return self.buffer.get_text(self.buffer.get_start_iter(),
311
412
                                        self.buffer.get_end_iter())
316
417
                return int(self.get_text())
317
418
            except ValueError, e:
318
419
                raise EntryValueError, e.args
 
420
 
319
421
    class FloatEntry(StringEntry):
320
422
            def get_value(self):
321
423
                try:
322
424
                    return float(self.get_text())
323
425
                except ValueError, e:
324
426
                    raise EntryValueError, e.args
 
427
 
325
428
#    class ArrayEntry(StringEntry):
326
429
#            def get_value(self):
327
430
#                return eval(self.get_text(), {}, {})
 
431
 
328
432
    class SliderEntry(gtk.HScale):
329
433
        # bounds is (upper, lower, step)
330
434
        def __init__(self, default=0, bounds=(0, 100, 5)):
332
436
                                      bounds[1], bounds[2],
333
437
                                      bounds[2], 0)
334
438
            gtk.HScale.__init__(self, self.adj)
 
439
 
335
440
        def get_value(self):
336
441
            return self.adj.value
 
442
 
337
443
    class SpinnerEntry(gtk.SpinButton):
338
444
        # bounds is (upper, lower, step)
339
445
        def __init__(self, default=0, bounds=(0, 100, 5)):
341
447
                                        bounds[1], bounds[2],
342
448
                                        bounds[2], 0)
343
449
            gtk.SpinButton.__init__(self, self.adj, 1, 0)
 
450
 
344
451
        def get_value(self):
345
452
            try:
346
453
                return int(self.get_text())
347
454
            except ValueError, e:
348
455
                raise EntryValueError, e.args
 
456
 
349
457
    class ToggleEntry(gtk.ToggleButton):
350
458
        def __init__(self, default=0):
351
459
            gtk.ToggleButton.__init__(self)
352
 
            self.label = gtk.Label("No")
 
460
 
 
461
            self.label = gtk.Label(_("No"))
353
462
            self.add(self.label)
354
463
            self.label.show()
 
464
 
355
465
            self.connect("toggled", self.changed)
 
466
 
356
467
            self.set_active(default)
 
468
 
357
469
        def changed(self, tog):
358
470
            if tog.get_active():
359
 
                self.label.set_text("Yes")
 
471
                self.label.set_text(_("Yes"))
360
472
            else:
361
 
                self.label.set_text("No")
 
473
                self.label.set_text(_("No"))
 
474
 
362
475
        def get_value(self):
363
476
            return self.get_active()
 
477
 
364
478
    class RadioEntry(gtk.Frame):
365
 
        def __init__(self, default=0, items=(("Yes", 1), ("No", 0))):
 
479
        def __init__(self, default=0, items=((_("Yes"), 1), (_("No"), 0))):
366
480
            gtk.Frame.__init__(self)
367
 
            box = gtk.VBox(gtk.FALSE, 5)
 
481
 
 
482
            box = gtk.VBox(False, 6)
368
483
            self.add(box)
369
484
            box.show()
 
485
 
370
486
            button = None
 
487
 
371
488
            for (label, value) in items:
372
489
                button = gtk.RadioButton(button, label)
373
 
                button.connect("toggled", self.changed, value)
374
490
                box.pack_start(button)
375
491
                button.show()
 
492
 
 
493
                button.connect("toggled", self.changed, value)
 
494
 
376
495
                if value == default:
377
 
                    button.set_active(gtk.TRUE)
 
496
                    button.set_active(True)
378
497
                    self.active_value = value
 
498
 
379
499
        def changed(self, radio, value):
380
500
            if radio.get_active():
381
501
                self.active_value = value
 
502
 
382
503
        def get_value(self):
383
504
            return self.active_value
384
505
 
 
506
    def FileSelector(default=''):
 
507
       if default and default.endswith('/'):
 
508
           selector = DirnameSelector
 
509
           if default == '/': default = ''
 
510
       else:
 
511
           selector = FilenameSelector
 
512
       return selector(default)
 
513
 
 
514
    class FilenameSelector(gtk.FileChooserButton):
 
515
        def __init__(self, default='', save_mode=False):
 
516
            gtk.FileChooserButton.__init__(self,
 
517
                                           _("Python-Fu File Selection"))
 
518
            self.set_action(gtk.FILE_CHOOSER_ACTION_OPEN)
 
519
            if default:
 
520
                self.set_filename(default)
 
521
 
 
522
        def get_value(self):
 
523
            return self.get_filename()
 
524
 
 
525
    class DirnameSelector(gtk.FileChooserButton):
 
526
        def __init__(self, default=''):
 
527
            gtk.FileChooserButton.__init__(self,
 
528
                                           _("Python-Fu Folder Selection"))
 
529
            self.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
 
530
            if default:
 
531
                self.set_filename(default)
 
532
 
 
533
        def get_value(self):
 
534
            return self.get_filename()
 
535
 
385
536
    _edit_mapping = {
386
537
            PF_INT8        : IntEntry,
387
538
            PF_INT16       : IntEntry,
393
544
            #PF_INT32ARRAY  : ArrayEntry,
394
545
            #PF_FLOATARRAY  : ArrayEntry,
395
546
            #PF_STRINGARRAY : ArrayEntry,
396
 
            PF_COLOUR      : gimpui.ColourSelector,
 
547
            PF_COLOUR      : gimpui.ColorSelector,
397
548
            PF_REGION      : IntEntry,  # should handle differently ...
398
549
            PF_IMAGE       : gimpui.ImageSelector,
399
550
            PF_LAYER       : gimpui.LayerSelector,
400
551
            PF_CHANNEL     : gimpui.ChannelSelector,
401
552
            PF_DRAWABLE    : gimpui.DrawableSelector,
 
553
            PF_VECTORS     : gimpui.VectorsSelector,
402
554
 
403
555
            PF_TOGGLE      : ToggleEntry,
404
556
            PF_SLIDER      : SliderEntry,
406
558
            PF_RADIO       : RadioEntry,
407
559
 
408
560
            PF_FONT        : gimpui.FontSelector,
409
 
            PF_FILE        : gimpui.FileSelector,
 
561
            PF_FILE        : FileSelector,
 
562
            PF_FILENAME    : FilenameSelector,
 
563
            PF_DIRNAME     : DirnameSelector,
410
564
            PF_BRUSH       : gimpui.BrushSelector,
411
565
            PF_PATTERN     : gimpui.PatternSelector,
412
566
            PF_GRADIENT    : gimpui.GradientSelector,
417
571
    if on_run:
418
572
        on_run()
419
573
 
420
 
    need_progress = menupath[:8] != '<Image>/'
421
 
 
422
574
    tooltips = gtk.Tooltips()
423
575
 
424
 
    dialog = gtk.Dialog(func_name, None, 0,
425
 
                        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
426
 
                        gtk.STOCK_OK, gtk.RESPONSE_OK))
427
 
 
428
 
    hbox = gtk.HBox(gtk.FALSE, 5)
429
 
    hbox.set_border_width(5)
430
 
    dialog.vbox.pack_start(hbox, expand=gtk.FALSE)
431
 
    hbox.show()
432
 
 
433
 
    table = gtk.Table(len(params), 2, gtk.FALSE)
434
 
    table.set_border_width(5)
435
 
    table.set_row_spacings(4)
436
 
    table.set_col_spacings(10)
437
 
    hbox.pack_end(table, expand=gtk.FALSE)
 
576
    dialog = gimpui.Dialog(proc_name, 'python-fu', None, 0, None, proc_name,
 
577
                           (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
 
578
                            gtk.STOCK_OK, gtk.RESPONSE_OK))
 
579
 
 
580
    dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL))
 
581
 
 
582
    dialog.set_transient()
 
583
 
 
584
    vbox = gtk.VBox(False, 12)
 
585
    vbox.set_border_width(12)
 
586
    dialog.vbox.pack_start(vbox)
 
587
    vbox.show()
 
588
 
 
589
    if blurb:
 
590
        if domain:
 
591
            try:
 
592
                (domain, locale_dir) = domain
 
593
                trans = gettext.translation(domain, locale_dir, fallback=True)
 
594
            except ValueError:
 
595
                trans = gettext.translation(domain, fallback=True)
 
596
            blurb = trans.ugettext(blurb)
 
597
        box = gimpui.HintBox(blurb)
 
598
        vbox.pack_start(box, expand=False)
 
599
        box.show()
 
600
 
 
601
    table = gtk.Table(len(params), 2, False)
 
602
    table.set_row_spacings(6)
 
603
    table.set_col_spacings(6)
 
604
    vbox.pack_start(table, expand=False)
438
605
    table.show()
439
606
 
440
 
    vbox = gtk.VBox(gtk.FALSE, 10)
441
 
    hbox.pack_start(vbox, expand=gtk.FALSE)
442
 
    vbox.show()
443
 
 
444
 
    pix = _get_logo()
445
 
    if pix:
446
 
        vbox.pack_start(pix, expand=gtk.FALSE)
447
 
        pix.show()
448
 
 
449
 
    label = gtk.Label(blurb)
450
 
    label.set_line_wrap(gtk.TRUE)
451
 
    label.set_justify(gtk.JUSTIFY_LEFT)
452
 
    label.set_size_request(100, -1)
453
 
    vbox.pack_start(label, expand=gtk.FALSE)
454
 
    label.show()
455
 
 
456
 
    progress_callback = None
457
 
 
458
607
    def response(dlg, id):
459
608
        if id == gtk.RESPONSE_OK:
 
609
            dlg.set_response_sensitive(gtk.RESPONSE_OK, False)
 
610
            dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, False)
 
611
 
460
612
            params = []
461
613
 
462
614
            try:
463
615
                for wid in edit_wids:
464
616
                    params.append(wid.get_value())
465
617
            except EntryValueError:
466
 
                error_dialog(dialog, 'Invalid input for "%s"' % wid.desc)
 
618
                warning_dialog(dialog, _("Invalid input for '%s'") % wid.desc)
467
619
            else:
468
 
                _set_defaults(func_name, params)
469
 
                dialog.res = run_script(params)
470
 
 
471
 
        if progress_callback:
472
 
            gimp.progress_uninstall(progress_callback)
 
620
                _set_defaults(proc_name, params)
 
621
                try:
 
622
                    dialog.res = run_script(params)
 
623
                except Exception:
 
624
                    dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, True)
 
625
                    error_dialog(dialog, proc_name)
 
626
                    raise
473
627
 
474
628
        gtk.main_quit()
475
629
 
483
637
        def_val = defaults[i]
484
638
 
485
639
        label = gtk.Label(desc)
486
 
        label.set_alignment(1.0, 0.5)
487
 
        table.attach(label, 1,2, i,i+1, xoptions=gtk.FILL)
 
640
        label.set_use_underline(True)
 
641
        label.set_alignment(0.0, 0.5)
 
642
        table.attach(label, 1, 2, i, i+1, xoptions=gtk.FILL)
488
643
        label.show()
489
644
 
490
645
        if pf_type in (PF_SPINNER, PF_SLIDER, PF_RADIO):
491
646
            wid = _edit_mapping[pf_type](def_val, params[i][4])
492
647
        else:
493
648
            wid = _edit_mapping[pf_type](def_val)
494
 
        
 
649
 
 
650
        label.set_mnemonic_widget(wid)
 
651
 
495
652
        table.attach(wid, 2,3, i,i+1, yoptions=0)
 
653
 
496
654
        if pf_type != PF_TEXT:
497
655
            tooltips.set_tip(wid, desc, None)         
498
656
        else:
503
661
        wid.desc = desc
504
662
        edit_wids.append(wid)
505
663
 
506
 
    if need_progress:
507
 
        frame = gtk.Frame("Script Progress")
508
 
        frame.set_border_width(5)
509
 
        dialog.vbox.pack_start(frame)
510
 
        frame.show()
511
 
 
512
 
        vbox = gtk.VBox(gtk.FALSE, 5)
513
 
        vbox.set_border_width(5)
514
 
        frame.add(vbox)
515
 
        vbox.show()
516
 
 
517
 
        progress_label = gtk.Label("(none)")
518
 
        progress_label.set_alignment(0.0, 0.5)
519
 
        vbox.pack_start(progress_label)
520
 
        progress_label.show()
521
 
 
522
 
        progress = gtk.ProgressBar()
523
 
        progress.set_text(" ")
524
 
        vbox.pack_start(progress)
525
 
        progress.show()
526
 
 
527
 
        def progress_update(message=-1, fraction=None):
528
 
            if message == -1:
529
 
                pass
530
 
            elif message:
531
 
                progress.set_text(message)
532
 
            else:
533
 
                progress.set_text(" ")
534
 
 
535
 
            if fraction is not None:
536
 
                progress.set_fraction(fraction)
537
 
 
538
 
            while gtk.events_pending():
539
 
                gtk.main_iteration()
540
 
 
541
 
        def progress_start(message, cancelable):
542
 
            progress_update(message, 0.0)
543
 
 
544
 
        def progress_end():
545
 
            progress_update(None, 0.0)
546
 
 
547
 
        def progress_text(message):
548
 
            progress_update(message)
549
 
 
550
 
        def progress_value(percentage):
551
 
            progress_update(fraction=percentage)
552
 
 
553
 
        progress_callback = gimp.progress_install(progress_start, progress_end,
554
 
                                                  progress_text, progress_value)
 
664
    progress_vbox = gtk.VBox(False, 6)
 
665
    vbox.pack_end(progress_vbox, expand=False)
 
666
    progress_vbox.show()
 
667
 
 
668
    progress = gimpui.ProgressBar()
 
669
    progress_vbox.pack_start(progress)
 
670
    progress.show()
 
671
 
 
672
#    progress_label = gtk.Label()
 
673
#    progress_label.set_alignment(0.0, 0.5)
 
674
#    progress_label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
 
675
 
 
676
#    attrs = pango.AttrList()
 
677
#    attrs.insert(pango.AttrStyle(pango.STYLE_ITALIC, 0, -1))
 
678
#    progress_label.set_attributes(attrs)
 
679
 
 
680
#    progress_vbox.pack_start(progress_label)
 
681
#    progress_label.show()
555
682
 
556
683
    tooltips.enable()
557
684
    dialog.show()
566
693
        dialog.destroy()
567
694
        raise CancelError
568
695
 
569
 
def _run(func_name, params):
 
696
def _run(proc_name, params):
570
697
    run_mode = params[0]
571
 
    plugin_type = _registered_plugins_[func_name][7]
572
 
    menupath = _registered_plugins_[func_name][5]
573
 
    func = _registered_plugins_[func_name][10]
574
 
 
575
 
    if plugin_type == PLUGIN and menupath[:10] != '<Toolbox>/':
576
 
        if menupath[:7] == '<Save>/':
577
 
            end = 5
578
 
        else:
579
 
            end = 3
580
 
 
 
698
    plugin_type = _registered_plugins_[proc_name][7]
 
699
    func = _registered_plugins_[proc_name][10]
 
700
    menu = _registered_plugins_[proc_name][11]
 
701
 
 
702
    if plugin_type == PLUGIN and menu[:7] == '<Image>':
 
703
        end = 3
 
704
        start_params = params[1:end]
 
705
        extra_params = params[end:]
 
706
    elif plugin_type == PLUGIN and menu[:6] == '<Save>':
 
707
        end = 5
581
708
        start_params = params[1:end]
582
709
        extra_params = params[end:]
583
710
    else:
586
713
 
587
714
    if run_mode == RUN_INTERACTIVE:
588
715
        try:
589
 
            res = _interact(func_name, start_params)
 
716
            res = _interact(proc_name, start_params)
590
717
        except CancelError:
591
718
            return
592
719
    else:
593
720
        if run_mode == RUN_WITH_LAST_VALS:
594
 
            extra_params = _get_defaults(func_name)
 
721
            extra_params = _get_defaults(proc_name)
595
722
 
596
723
        params = start_params + tuple(extra_params)
597
724
        res = apply(func, params)
602
729
    return res
603
730
 
604
731
def main():
605
 
    '''This should be called after registering the plugin.'''
 
732
    '''This should be called after registering the plug-in.'''
606
733
    gimp.main(None, None, _query, _run)
607
734
 
608
735
def fail(msg):
610
737
    gimp.message(msg)
611
738
    raise error, msg
612
739
 
613
 
def _get_logo():
614
 
    import gtk, gobject
615
 
 
616
 
    import os.path
617
 
    logofile = os.path.join(os.path.dirname(__file__), 'pygimp-logo.png')
618
 
 
619
 
    try:
620
 
        pixbuf = gtk.gdk.pixbuf_new_from_file(logofile)
621
 
    except gobject.GError, e:
622
 
        return None
623
 
 
624
 
    image = gtk.Image()
625
 
    image.set_from_pixbuf(pixbuf)
626
 
 
627
 
    return image
 
740
def N_(message):
 
741
    return message